diff --git a/src/board.rs b/src/board.rs index 058a0d2..48de877 100644 --- a/src/board.rs +++ b/src/board.rs @@ -28,26 +28,35 @@ impl Board { Board(board) } - pub fn colliding( + pub fn lowest_y( &self, CurrentTetromino { tetromino, direction, - x: cur_x, - y: cur_y, + x, + y, }: &CurrentTetromino, - ) -> bool { + ) -> i8 { let pattern = tetromino.direction_pattern(direction); + let mut y = *y; + loop { + if self.pattern_and_position_colliding(pattern, *x, y) { + break y - 1; + } + y += 1; + } + } - for (y, row) in pattern.iter().enumerate() { - for x in row + fn pattern_and_position_colliding(&self, pattern: [[bool; 4]; 4], x: i8, y: i8) -> bool { + for (y_offset, row) in pattern.iter().enumerate() { + for x_offset in row .iter() .enumerate() .filter(|(_, exists)| **exists) .map(|(x, _)| x) { - let x = x as i8 + cur_x; - let y = y as i8 + cur_y; + let x = x_offset as i8 + x; + let y = y_offset as i8 + y; if y < 0 { continue; @@ -70,6 +79,19 @@ impl Board { false } + pub fn colliding( + &self, + CurrentTetromino { + tetromino, + direction, + x, + y, + }: &CurrentTetromino, + ) -> bool { + let pattern = tetromino.direction_pattern(direction); + self.pattern_and_position_colliding(pattern, *x, *y) + } + pub fn lines_cleared(&mut self) -> usize { let line_clears: Vec<_> = self .iter() diff --git a/src/sdl_impl.rs b/src/sdl_impl.rs index f69c391..856755c 100644 --- a/src/sdl_impl.rs +++ b/src/sdl_impl.rs @@ -1,42 +1,85 @@ use crate::actions::{Action, ActionsHeld}; use crate::board::Board; use crate::game::{CurrentTetromino, Game}; +use crate::tetromino::Tetromino; use crate::Rgb; use sdl2::event::Event; use sdl2::keyboard::Keycode; use sdl2::pixels::Color; use sdl2::rect::Rect; -use sdl2::render::{Canvas, RenderTarget, Texture, TextureCreator, WindowCanvas}; +use sdl2::render::{Texture, TextureCreator, WindowCanvas}; use sdl2::rwops::RWops; use sdl2::ttf::Sdl2TtfContext; use std::time::Duration; -fn draw_board_tile(canvas: &mut WindowCanvas, width: usize, height: usize, color: Rgb) { +const WIDTH: i32 = 1000; +const HEIGHT: i32 = 800; + +fn draw_board_tile( + canvas: &mut WindowCanvas, + width: usize, + height: usize, + color: &Rgb, + filled: bool, +) -> Result<(), String> { canvas.set_draw_color(Color::RGB(color.0, color.1, color.2)); - canvas - .fill_rect(Rect::new( - center(24 * Board::WIDTH as i32, 800) + width as i32 * 24, - center(24 * Board::HEIGHT as i32, 600) + height as i32 * 24, - 24, - 24, - )) - .unwrap(); + let rect = Rect::new( + center(24 * Board::WIDTH as i32, WIDTH) + width as i32 * 24, + center(24 * Board::HEIGHT as i32, HEIGHT) + height as i32 * 24, + 24, + 24, + ); + if filled { + canvas.fill_rect(rect) + } else { + canvas.draw_rect(rect) + } } fn center(length: i32, max: i32) -> i32 { (max - length) / 2 } -fn draw_board(canvas: &mut WindowCanvas, board: &Board, current: &CurrentTetromino) { +fn draw_tetromino_from_parts( + canvas: &mut WindowCanvas, + x: i8, + y: i8, + color: Rgb, + pattern: [[bool; 4]; 4], + filled: bool, +) -> Result<(), String> { + for (y_offset, row) in pattern.iter().enumerate() { + for x_offset in row + .iter() + .enumerate() + .filter(|(_, exists)| **exists) + .map(|(x, _)| x) + { + let x = x_offset as i8 + x; + let y = y_offset as i8 + y; + + if y < 0 { + continue; + } + + draw_board_tile(canvas, x as usize, y as usize, &color, filled)? + } + } + Ok(()) +} + +fn draw_board( + canvas: &mut WindowCanvas, + board: &Board, + current: &CurrentTetromino, +) -> Result<(), String> { canvas.set_draw_color(Color::WHITE); - canvas - .draw_rect(Rect::new( - center(24 * Board::WIDTH as i32, 800) - 1, - center(24 * Board::HEIGHT as i32, 600) - 1, - 24 * Board::WIDTH as u32 + 2, - 24 * Board::HEIGHT as u32 + 2, - )) - .unwrap(); + canvas.draw_rect(Rect::new( + center(24 * Board::WIDTH as i32, WIDTH) - 1, + center(24 * Board::HEIGHT as i32, HEIGHT) - 1, + 24 * Board::WIDTH as u32 + 2, + 24 * Board::HEIGHT as u32 + 2, + ))?; for (y, row) in board.iter().enumerate() { for (x, piece) in row.iter().enumerate() { @@ -44,53 +87,48 @@ fn draw_board(canvas: &mut WindowCanvas, board: &Board, current: &CurrentTetromi Some(t) => Rgb::from_tetromino(t), None => Rgb(0, 0, 0), }; - draw_board_tile(canvas, x, y, color) + draw_board_tile(canvas, x, y, &color, true)? } } let pattern = current.tetromino.direction_pattern(¤t.direction); - for (y, row) in pattern.iter().enumerate() { - for x in row - .iter() - .enumerate() - .filter(|(_, exists)| **exists) - .map(|(x, _)| x) - { - let x = x as i8 + current.x; - let y = y as i8 + current.y; + draw_tetromino_from_parts( + canvas, + current.x, + board.lowest_y(¤t), + Rgb(255, 255, 255), + pattern, + false, + )?; - if y < 0 { - continue; - } + draw_tetromino_from_parts( + canvas, + current.x, + current.y, + Rgb::from_tetromino(¤t.tetromino), + pattern, + true, + )?; - draw_board_tile( - canvas, - x as usize, - y as usize, - Rgb::from_tetromino(¤t.tetromino), - ) - } - } + Ok(()) } fn font_texture<'font, 'a, C>( text: &'a str, ttf_context: &'a Sdl2TtfContext, texture_creator: &'font TextureCreator, -) -> Texture<'font> { - let font = ttf_context - .load_font_from_rwops( - RWops::from_bytes(include_bytes!("res/josenfin_sans_regular.ttf")).unwrap(), - 24, - ) - .unwrap(); +) -> Result, String> { + let font = ttf_context.load_font_from_rwops( + RWops::from_bytes(include_bytes!("res/josenfin_sans_regular.ttf"))?, + 24, + )?; let game_over_text = font.render(text).solid(Color::RGB(255, 255, 255)).unwrap(); let texture = texture_creator .create_texture_from_surface(game_over_text) .unwrap(); - texture + Ok(texture) } fn draw_important_text( @@ -99,14 +137,14 @@ fn draw_important_text( ttf_context: &Sdl2TtfContext, ) -> Result<(), String> { let texture_creator = canvas.texture_creator(); - let texture = font_texture(text, &ttf_context, &texture_creator); + let texture = font_texture(text, &ttf_context, &texture_creator)?; let size = texture.query(); let width = size.width; let height = size.height; - let x = center(width as i32, 800); - let y = center(height as i32, 600); + let x = center(width as i32, WIDTH); + let y = center(height as i32, HEIGHT); canvas.set_draw_color(Color::WHITE); canvas.draw_rect(Rect::new(x - 9, y - 9, width + 18, height + 18))?; @@ -118,23 +156,23 @@ fn draw_important_text( Ok(()) } -pub fn start_game() { +pub fn start_game() -> Result<(), String> { let mut game = Game::new(); let mut actions = ActionsHeld::new(); let mut paused = false; - let sdl_context = sdl2::init().unwrap(); + let sdl_context = sdl2::init()?; let ttf_context = sdl2::ttf::init().unwrap(); - let video_subsystem = sdl_context.video().unwrap(); + let video_subsystem = sdl_context.video()?; let window = video_subsystem - .window("reimtris2", 800, 600) + .window("reimtris2", WIDTH as u32, HEIGHT as u32) .position_centered() .build() .unwrap(); let mut canvas = window.into_canvas().build().unwrap(); - let mut event_pump = sdl_context.event_pump().unwrap(); + let mut event_pump = sdl_context.event_pump()?; 'running: loop { canvas.set_draw_color(Color::RGB(16, 16, 16)); canvas.clear(); @@ -144,7 +182,7 @@ pub fn start_game() { | Event::KeyDown { keycode: Some(Keycode::Escape), .. - } => break 'running, + } => break 'running Ok(()), Event::KeyDown { keycode: Some(keycode), .. @@ -189,22 +227,20 @@ pub fn start_game() { } } - draw_board(&mut canvas, &game.board, &game.current_tetromino); + draw_board(&mut canvas, &game.board, &game.current_tetromino)?; if paused { draw_important_text( "game paused o_o... press [p] to unpause !!", &mut canvas, &ttf_context, - ) - .unwrap(); + )?; } else if game.game_over { draw_important_text( "game over T_T... press [enter] 2 restart :D", &mut canvas, &ttf_context, - ) - .unwrap(); + )?; } else { game.step(&actions); }