Browse Source

completed lines flash

master
Inderjit Gill 1 year ago
parent
commit
b3bff2d60e
1 changed files with 177 additions and 99 deletions
  1. +177
    -99
      src/game.rs

+ 177
- 99
src/game.rs View File

@@ -16,6 +16,13 @@ pub enum GameMode {
Paused,
}

#[derive(PartialEq)]
pub enum GameState {
Playing,
PieceIsDropping,
LinesFlashing,
}

// hacky: make sure these values match up with code in web/js/Audio.js
#[derive(Clone, Copy)]
pub enum SoundEffect {
@@ -35,6 +42,10 @@ pub struct Block {
occupied: bool,
offset: Vec2D,
colour: Col,

hue: f64,
flashing_bright_colour: Col,
flashing_dull_colour: Col,
}

impl Block {
@@ -43,6 +54,9 @@ impl Block {
occupied: false,
offset: Vec2D { x: 0.0, y: 0.0 },
colour: col,
hue: 0.0,
flashing_bright_colour: col,
flashing_dull_colour: col,
}
}

@@ -50,6 +64,35 @@ impl Block {
self.occupied = false;
self.offset = Vec2D { x: 0.0, y: 0.0 };
self.colour = *colour;
self.hue = 0.0;
self.flashing_bright_colour = *colour;
self.flashing_dull_colour = *colour;
}

pub fn flash_on(&mut self) {
self.colour = self.flashing_bright_colour;
}

pub fn flash_off(&mut self) {
self.colour = self.flashing_dull_colour;
}

pub fn set(&mut self, occupied: bool, colour: &Col, hue: f64) {
self.occupied = occupied;
self.hue = hue;
if occupied {
// set the colour straight away if it's occupied
// (unoccupied blocks will fade out, see update_block_colours)
self.colour = *colour;

// also calculate the flashing colours now
if let Ok(bright) = get_colour_from_hsl(hue, 85.0, 70.0, 1.0) {
self.flashing_bright_colour = bright;
}
if let Ok(dull) = get_colour_from_hsl(hue, 85.0, 30.0, 1.0) {
self.flashing_dull_colour = dull;
}
}
}
}

@@ -105,6 +148,20 @@ impl Board {
self.crt_update_speed = new_crt_update_speed;
}

pub fn flash_complete_lines(&mut self, lines_flashing_time: f32) {
for line in 0..self.height {
if self.is_line_filled(line) {
for x in 0..self.width {
if lines_flashing_time > 250.0 && lines_flashing_time < 350.0 {
self.board[line][x].flash_off();
} else {
self.board[line][x].flash_on();
}
}
}
}
}

pub fn is_line_filled(&self, line_number: usize) -> bool {
if line_number >= self.height {
// should error
@@ -175,19 +232,23 @@ pub struct Piece {
pos: BoardPos,
angle: PieceAngle,
colour: Col,
hue: f64,
}

pub struct Game {
mode: GameMode,
resume_from_paused: bool,

game_state: GameState,
lines_flashing_time: f32, // ms

board: Board,
board_offset: Block2D,

shapes: HashMap<ShapeKind, Shape>,
piece: Piece, // the current piece under player control
next_piece_shape: ShapeKind,
next_piece_colour: Col,
next_piece_hue: f64,

colour_saturation: f64,
colour_lightness: f64,
@@ -209,7 +270,6 @@ pub struct Game {
tetris: bool,

time_to_next_lowering: f32, // ms
piece_is_dropping: bool,
impact_sfx_played: bool,

default_cooldown: f32, // ms
@@ -239,6 +299,9 @@ impl Game {
mode: GameMode::Playing,
resume_from_paused: false,

game_state: GameState::Playing,
lines_flashing_time: 0.0,

board: Board::new(config),
board_offset: Block2D {
x: config.board_offset_x,
@@ -251,9 +314,10 @@ impl Game {
pos: BoardPos { x: 5, y: 19 },
angle: PieceAngle::R0,
colour: Col::new(0.4, 0.8, 0.2, 0.9),
hue: 0.0,
},
next_piece_shape: ShapeKind::L,
next_piece_colour: Col::new(1.0, 1.0, 1.0, 1.0),
next_piece_hue: 1.0,

colour_saturation: config.colour_saturation,
colour_lightness: config.colour_lightness,
@@ -276,7 +340,6 @@ impl Game {
tetris: false, // was the last scoring move a tetris?

time_to_next_lowering: get_time_to_next_lowering(0),
piece_is_dropping: false,
impact_sfx_played: false,

default_cooldown: config.default_cooldown,
@@ -330,12 +393,7 @@ impl Game {

self.board.reset();
self.next_piece_shape = get_random_shape(1.0 - random)?;
self.next_piece_colour = get_colour_from_hsl(
((1.0 - random) * 360.0) as f64,
self.colour_saturation,
self.colour_lightness,
1.0,
)?;
self.next_piece_hue = ((1.0 - random) * 360.0) as f64;

use_next_shape(self, random)?;

@@ -360,12 +418,7 @@ impl Game {

pub fn init(&mut self, random: f32) -> error::Result<()> {
self.next_piece_shape = get_random_shape(1.0 - random)?;
self.next_piece_colour = get_colour_from_hsl(
((1.0 - random) * 360.0) as f64,
self.colour_saturation,
self.colour_lightness,
1.0,
)?;
self.next_piece_hue = ((1.0 - random) * 360.0) as f64;

use_next_shape(self, random)?;
Ok(())
@@ -388,7 +441,12 @@ impl Game {
render_lines(geometry, self.lines, Block2D { x, y: y + 6 }, c);
}
if let Some(shape) = self.shapes.get(&self.next_piece_shape) {
render_next_shape(geometry, &shape, Block2D { x, y }, self.next_piece_colour);
if let Ok(colour) = get_colour_from_hsl(self.next_piece_hue,
self.colour_saturation,
self.colour_lightness,
1.0) {
render_next_shape(geometry, &shape, Block2D { x, y }, colour);
}
}

// render last as this contains a full-screen alpha
@@ -459,9 +517,6 @@ impl Game {
if self.debug_mode {
self.debug_flash = !self.debug_flash;
self.debug_delta = corrected_delta;
// if self.debug_delta > 0.0 && self.debug_delta < 15.0 {
// log(&format!("{}", self.debug_delta));
// }
}

match self.mode {
@@ -499,68 +554,83 @@ impl Game {
remove_shape_from_board(&mut self.board, shape, &self.piece.pos, &self.piece.angle);
}

apply_user_input(self, controller, delta);

if self.piece_is_dropping {
if let Some(shape) = self.shapes.get(&self.piece.shape_kind) {
if !lower_piece(&self.board, shape, &mut self.piece) {
// NOTE: don't lock in the shape when it stops dropping.
//
// the game 'feels' better if the user has time to modify the
// piece's position at the bottom of the drop
self.piece_is_dropping = false;
if !self.impact_sfx_played {
play_sound(SoundEffect::Impact);
self.impact_sfx_played = true;
match self.game_state {
GameState::Playing => {
apply_user_input(self, controller, delta);
if is_time_to_lower_piece(self, delta) {
let mut piece_cant_go_lower = false;

if let Some(shape) = self.shapes.get(&self.piece.shape_kind) {
piece_cant_go_lower = !lower_piece(&self.board, shape, &mut self.piece);
}
}
}
} else if is_time_to_lower_piece(self, delta) {
let mut piece_cant_go_lower = false;

if let Some(shape) = self.shapes.get(&self.piece.shape_kind) {
piece_cant_go_lower = !lower_piece(&self.board, shape, &mut self.piece);
}
if piece_cant_go_lower {
if let Some(shape) = self.shapes.get(&self.piece.shape_kind) {
add_shape_to_board(
&mut self.board,
shape,
&self.piece
);

if any_complete_lines(&self.board) {
let num_lines = num_complete_lines(&self.board);
match num_lines {
1 => play_sound(SoundEffect::Line1),
2 => play_sound(SoundEffect::Line2),
3 => play_sound(SoundEffect::Line3),
4 => play_sound(SoundEffect::Line4),
_ => (),
};

self.lines_flashing_time = 0.0;
self.game_state = GameState::LinesFlashing;
} else if !self.impact_sfx_played {
play_sound(SoundEffect::Impact);
self.impact_sfx_played = true;
}
}

use_next_shape(self, random)?;
if check_for_game_over(self) {
play_sound(SoundEffect::GameOver);
log(&format!("i_count: {}", self.i_count));
log(&format!("j_count: {}", self.j_count));
log(&format!("l_count: {}", self.l_count));
log(&format!("o_count: {}", self.o_count));
log(&format!("s_count: {}", self.s_count));
log(&format!("t_count: {}", self.t_count));
log(&format!("z_count: {}", self.z_count));
self.is_game_over = true;
}
}
}
},
GameState::PieceIsDropping => {
apply_user_input(self, controller, delta);

if piece_cant_go_lower {
let mut num_lines = 0;
if let Some(shape) = self.shapes.get(&self.piece.shape_kind) {
add_shape_to_board(
&mut self.board,
shape,
&self.piece.pos,
&self.piece.angle,
&self.piece.colour,
);
num_lines = remove_complete_lines(&mut self.board)?;
if !lower_piece(&self.board, shape, &mut self.piece) {
// NOTE: don't lock in the shape when it stops dropping.
//
// the game 'feels' better if the user has time to modify the
// piece's position at the bottom of the drop
self.game_state = GameState::Playing;
if !self.impact_sfx_played {
play_sound(SoundEffect::Impact);
self.impact_sfx_played = true;
}
}
}
},
GameState::LinesFlashing => {
self.lines_flashing_time += delta;
self.board.flash_complete_lines(self.lines_flashing_time);
if self.lines_flashing_time > 500.0 {

if num_lines > 0 {
match num_lines {
1 => play_sound(SoundEffect::Line1),
2 => play_sound(SoundEffect::Line2),
3 => play_sound(SoundEffect::Line3),
4 => play_sound(SoundEffect::Line4),
_ => (),
};

let num_lines = remove_complete_lines(&mut self.board)?;
update_score(self, num_lines);
} else if !self.impact_sfx_played {
play_sound(SoundEffect::Impact);
self.impact_sfx_played = true;
}

use_next_shape(self, random)?;
if check_for_game_over(self) {
play_sound(SoundEffect::GameOver);
log(&format!("i_count: {}", self.i_count));
log(&format!("j_count: {}", self.j_count));
log(&format!("l_count: {}", self.l_count));
log(&format!("o_count: {}", self.o_count));
log(&format!("s_count: {}", self.s_count));
log(&format!("t_count: {}", self.t_count));
log(&format!("z_count: {}", self.z_count));
self.is_game_over = true;
self.game_state = GameState::Playing;
}
}
}
@@ -569,9 +639,7 @@ impl Game {
add_shape_to_board(
&mut self.board,
shape,
&self.piece.pos,
&self.piece.angle,
&self.piece.colour,
&self.piece
);
}

@@ -625,7 +693,8 @@ fn use_next_shape(game: &mut Game, random: f32) -> error::Result<()> {
game.piece.pos.x = ((game.board.width as i32) - shape.aabb_width) / 2;
game.piece.pos.y = (game.board.height as i32) - shape.real_height;
game.piece.angle = PieceAngle::R0;
game.piece.colour = game.next_piece_colour;
game.piece.colour = get_colour_from_hsl(game.next_piece_hue, game.colour_saturation, game.colour_lightness, 1.0)?;
game.piece.hue = game.next_piece_hue;
}

match game.piece.shape_kind {
@@ -640,12 +709,7 @@ fn use_next_shape(game: &mut Game, random: f32) -> error::Result<()> {

game.impact_sfx_played = false;
game.next_piece_shape = get_random_shape(random)?;
game.next_piece_colour = get_colour_from_hsl(
(random * 360.0) as f64,
game.colour_saturation,
game.colour_lightness,
1.0,
)?;
game.next_piece_hue = (random * 360.0) as f64;

Ok(())
}
@@ -766,7 +830,7 @@ fn apply_user_input(game: &mut Game, controller: &Controller, delta: f32) {
}

if controller.just_pressed(ControllerButton::Down) {
game.piece_is_dropping = true;
game.game_state = GameState::PieceIsDropping;
}

if can_apply_input(
@@ -869,6 +933,27 @@ fn is_colliding(board: &Board, shape: &Shape, pos: &BoardPos, angle: &PieceAngle
.any(|p| board.board[p.y as usize][p.x as usize].occupied)
}

fn any_complete_lines(board: &Board) -> bool {
for y in 0..board.height {
if board.is_line_filled(y) {
return true
}
}
false
}

fn num_complete_lines(board: &Board) -> i32 {
let mut count = 0;

for y in 0..board.height {
if board.is_line_filled(y) {
count += 1;
}
}

count
}

fn remove_complete_lines(board: &mut Board) -> error::Result<i32> {
let mut offset = 0;

@@ -885,13 +970,15 @@ fn remove_complete_lines(board: &mut Board) -> error::Result<i32> {
fn add_shape_to_board(
board: &mut Board,
shape: &Shape,
pos: &BoardPos,
angle: &PieceAngle,
colour: &Col,
piece: &Piece
) {
let pos = &piece.pos;
let angle = &piece.angle;
let colour = &piece.colour;

for pos in get_board_positions(shape, pos, angle) {
if is_valid_board_position(board, &pos) {
set_block(board, pos.x, pos.y, true, colour);
board.board[pos.y as usize][pos.x as usize].set(true, colour, piece.hue);
}
}
}
@@ -900,7 +987,7 @@ fn remove_shape_from_board(board: &mut Board, shape: &Shape, pos: &BoardPos, ang
let inactive_colour = board.inactive_colour;
for pos in get_board_positions(shape, &pos, &angle) {
if is_valid_board_position(board, &pos) {
set_block(board, pos.x, pos.y, false, &inactive_colour);
board.board[pos.y as usize][pos.x as usize].set(false, &inactive_colour, 0.0);
}
}
}
@@ -950,15 +1037,6 @@ fn get_board_positions(shape: &Shape, pos: &BoardPos, angle: &PieceAngle) -> Vec
res
}

fn set_block(board: &mut Board, x: i32, y: i32, occupied: bool, colour: &Col) {
board.board[y as usize][x as usize].occupied = occupied;
if occupied {
// set the colour straight away if it's occupied
// (unoccupied blocks will fade out, see update_block_colours)
board.board[y as usize][x as usize].colour = *colour;
}
}

fn update_block_colours(board: &mut Board) {
for mut row in &mut board.board {
for mut block in row {

Loading…
Cancel
Save