Compare commits
3 commits
765b2655a3
...
2aa1602dc1
Author | SHA1 | Date | |
---|---|---|---|
2aa1602dc1 | |||
450900b167 | |||
d719338097 |
4 changed files with 86 additions and 31 deletions
70
src/game.rs
70
src/game.rs
|
@ -25,6 +25,9 @@ pub struct Game {
|
||||||
next_lock_tick: u64,
|
next_lock_tick: u64,
|
||||||
next_spawn_tick: u64,
|
next_spawn_tick: u64,
|
||||||
is_game_over: bool,
|
is_game_over: bool,
|
||||||
|
/// The last clear action performed, used for determining if a back-to-back
|
||||||
|
/// bonus is needed.
|
||||||
|
last_clear_action: ClearAction,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Game {
|
impl fmt::Debug for Game {
|
||||||
|
@ -47,6 +50,7 @@ impl Default for Game {
|
||||||
next_lock_tick: 0,
|
next_lock_tick: 0,
|
||||||
next_spawn_tick: 0,
|
next_spawn_tick: 0,
|
||||||
is_game_over: false,
|
is_game_over: false,
|
||||||
|
last_clear_action: ClearAction::Single, // Doesn't matter what it's initialized to
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,7 +64,6 @@ impl Tickable for Game {
|
||||||
if self.is_game_over() {
|
if self.is_game_over() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.tick += 1;
|
self.tick += 1;
|
||||||
match self.tick {
|
match self.tick {
|
||||||
t if t == self.next_spawn_tick => self.spawn_tetromino(),
|
t if t == self.next_spawn_tick => self.spawn_tetromino(),
|
||||||
|
@ -79,13 +82,26 @@ impl Tickable for Game {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ClearAction {
|
||||||
|
Single,
|
||||||
|
Double,
|
||||||
|
Triple,
|
||||||
|
Tetris,
|
||||||
|
MiniTSpin,
|
||||||
|
TSpin,
|
||||||
|
TSpinSingle,
|
||||||
|
TSpinDouble,
|
||||||
|
TSpinTriple,
|
||||||
|
}
|
||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
pub fn is_game_over(&self) -> bool {
|
pub fn is_game_over(&self) -> bool {
|
||||||
self.is_game_over || !self.playfield.is_active_piece_in_valid_position()
|
self.is_game_over || !self.playfield.is_active_piece_in_valid_position()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_gravity_tick(&mut self) {
|
fn update_gravity_tick(&mut self) {
|
||||||
self.next_gravity_tick = (-1 as i64) as u64; //self.tick + TICKS_PER_SECOND as u64;
|
// self.next_gravity_tick = (-1 as i64) as u64;
|
||||||
|
self.next_gravity_tick = self.tick + TICKS_PER_SECOND as u64;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_lock_tick(&mut self) {
|
fn update_lock_tick(&mut self) {
|
||||||
|
@ -94,14 +110,26 @@ impl Game {
|
||||||
|
|
||||||
fn spawn_tetromino(&mut self) {
|
fn spawn_tetromino(&mut self) {
|
||||||
self.playfield.spawn_tetromino();
|
self.playfield.spawn_tetromino();
|
||||||
self.playfield.tick_gravity();
|
|
||||||
self.update_gravity_tick();
|
self.update_gravity_tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns if some lines were cleared
|
/// Returns if some lines were cleared
|
||||||
fn clear_lines(&mut self) -> bool {
|
fn clear_lines(&mut self) -> usize {
|
||||||
// todo: award points based on how lines were cleared
|
let rows = self
|
||||||
return false;
|
.playfield
|
||||||
|
.active_piece
|
||||||
|
.map(|t| t.get_cur_occupied_spaces())
|
||||||
|
.map(|i| i.iter().map(|p| p.y).collect::<Vec<_>>())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let mut rows_cleared = 0;
|
||||||
|
for row in rows {
|
||||||
|
if self.playfield.try_clear_row(row as usize).is_ok() {
|
||||||
|
rows_cleared += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rows_cleared
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_lock_tetromino(&mut self) -> bool {
|
fn try_lock_tetromino(&mut self) -> bool {
|
||||||
|
@ -110,11 +138,14 @@ impl Game {
|
||||||
let positions = self.playfield.lock_active_piece();
|
let positions = self.playfield.lock_active_piece();
|
||||||
self.is_game_over =
|
self.is_game_over =
|
||||||
self.is_game_over || positions.iter().all(|Position { x: _, y }| *y < 20);
|
self.is_game_over || positions.iter().all(|Position { x: _, y }| *y < 20);
|
||||||
if self.clear_lines() {
|
|
||||||
|
if self.clear_lines() > 0 {
|
||||||
|
self.playfield.active_piece = None;
|
||||||
self.next_spawn_tick = self.tick + LINE_CLEAR_DELAY;
|
self.next_spawn_tick = self.tick + LINE_CLEAR_DELAY;
|
||||||
} else {
|
} else {
|
||||||
self.spawn_tetromino();
|
self.spawn_tetromino();
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -141,12 +172,16 @@ pub trait Controllable {
|
||||||
impl Controllable for Game {
|
impl Controllable for Game {
|
||||||
fn move_left(&mut self) {
|
fn move_left(&mut self) {
|
||||||
self.playfield.move_offset(-1, 0);
|
self.playfield.move_offset(-1, 0);
|
||||||
|
if !self.playfield.can_active_piece_move_down() {
|
||||||
self.update_lock_tick();
|
self.update_lock_tick();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fn move_right(&mut self) {
|
fn move_right(&mut self) {
|
||||||
self.playfield.move_offset(1, 0);
|
self.playfield.move_offset(1, 0);
|
||||||
|
if !self.playfield.can_active_piece_move_down() {
|
||||||
self.update_lock_tick();
|
self.update_lock_tick();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fn move_down(&mut self) {
|
fn move_down(&mut self) {
|
||||||
if self.playfield.move_offset(0, 1) {
|
if self.playfield.move_offset(0, 1) {
|
||||||
self.score += 1;
|
self.score += 1;
|
||||||
|
@ -187,23 +222,14 @@ impl Controllable for Game {
|
||||||
if !self.try_lock_tetromino() {
|
if !self.try_lock_tetromino() {
|
||||||
println!("couldn't lock tetromino despite hard dropping!");
|
println!("couldn't lock tetromino despite hard dropping!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.next_lock_tick = std::u64::MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hold(&mut self) {
|
fn hold(&mut self) {
|
||||||
// if self.can_swap_hold {
|
match self.playfield.try_swap_hold() {
|
||||||
// match self.hold_piece {
|
Ok(_) => {}
|
||||||
// None => {
|
Err(_) => (),
|
||||||
// self.hold_piece = Some(self.active_piece);
|
}
|
||||||
// self.get_new_piece();
|
|
||||||
// }
|
|
||||||
// Some(piece) => {
|
|
||||||
// self.hold_piece = Some(self.active_piece);
|
|
||||||
// self.active_piece = piece;
|
|
||||||
// self.reset_position();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// self.can_swap_hold = false;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,5 +86,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
dbg!(game);
|
dbg!(game);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,6 +134,7 @@ impl PlayField {
|
||||||
));
|
));
|
||||||
self.next_pieces.push_back(self.bag.get_tetromino());
|
self.next_pieces.push_back(self.bag.get_tetromino());
|
||||||
self.can_swap_hold = true;
|
self.can_swap_hold = true;
|
||||||
|
self.tick_gravity();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tick_gravity(&mut self) {
|
pub fn tick_gravity(&mut self) {
|
||||||
|
@ -170,19 +171,16 @@ impl PlayField {
|
||||||
self.field[*y as usize][*x as usize] = Some(active_color);
|
self.field[*y as usize][*x as usize] = Some(active_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.active_piece = None;
|
|
||||||
new_pieces
|
new_pieces
|
||||||
}
|
}
|
||||||
None => panic!("Tried to lock active piece while active piece doesn't exist!"),
|
None => vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_active_piece_in_valid_position(&self) -> bool {
|
pub fn is_active_piece_in_valid_position(&self) -> bool {
|
||||||
match self.active_piece {
|
match self.active_piece {
|
||||||
Some(active_piece) => {
|
Some(active_piece) => self.can_piece_be_at_position(&active_piece),
|
||||||
self.can_piece_be_at_position(&active_piece)
|
None => true,
|
||||||
},
|
|
||||||
None => panic!("Tried checking if active piece is in a valid position but active piece doesn't exist")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,6 +194,37 @@ impl PlayField {
|
||||||
&& self.field[*y as usize][*x as usize].is_none()
|
&& self.field[*y as usize][*x as usize].is_none()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_swap_hold(&mut self) -> Result<(), ()> {
|
||||||
|
if self.can_swap_hold {
|
||||||
|
match self.active_piece {
|
||||||
|
Some(piece) => {
|
||||||
|
match self.hold_piece {
|
||||||
|
Some(hold) => self.next_pieces.push_front(hold),
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
self.hold_piece = Some(piece.piece_type);
|
||||||
|
self.spawn_tetromino();
|
||||||
|
self.can_swap_hold = false;
|
||||||
|
}
|
||||||
|
None => return Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_clear_row(&mut self, row: usize) -> Result<(), ()> {
|
||||||
|
if self.field[row].iter().all(|cell| cell.is_some()) {
|
||||||
|
self.field[row] = [None; PLAYFIELD_WIDTH];
|
||||||
|
for y in (1..=row).rev() {
|
||||||
|
self.field[y] = self.field[y - 1];
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Renderable for PlayField {
|
impl Renderable for PlayField {
|
||||||
|
|
|
@ -45,7 +45,6 @@ impl SRS {
|
||||||
}
|
}
|
||||||
|
|
||||||
let cur_offsets = offset_data.get(&test_tetromino.rotation_state).unwrap();
|
let cur_offsets = offset_data.get(&test_tetromino.rotation_state).unwrap();
|
||||||
|
|
||||||
let mut offsets = Vec::with_capacity(cur_offsets.len());
|
let mut offsets = Vec::with_capacity(cur_offsets.len());
|
||||||
|
|
||||||
for i in 0..cur_offsets.len() {
|
for i in 0..cur_offsets.len() {
|
||||||
|
|
Loading…
Reference in a new issue