render board
This commit is contained in:
parent
db57450452
commit
63a8c0bd78
@ -1,7 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
pub enum Controls {
|
||||
pub enum Action {
|
||||
Left,
|
||||
Right,
|
||||
SoftDrop,
|
||||
@ -11,17 +11,20 @@ pub enum Controls {
|
||||
RotateCcw,
|
||||
}
|
||||
|
||||
pub struct ControlsHeld(HashMap<Controls, usize>);
|
||||
pub struct ActionsHeld(HashMap<Action, usize>);
|
||||
|
||||
impl ControlsHeld {
|
||||
pub fn just_pressed(&self, ticks: usize, control: &Controls) -> bool {
|
||||
impl ActionsHeld {
|
||||
pub fn new() -> Self {
|
||||
Self(HashMap::new())
|
||||
}
|
||||
pub fn just_pressed(&self, ticks: usize, control: &Action) -> bool {
|
||||
self.held_for(ticks, control, |held_for| held_for == 0)
|
||||
}
|
||||
|
||||
pub fn held_for<F: Fn(usize) -> bool>(
|
||||
&self,
|
||||
ticks: usize,
|
||||
control: &Controls,
|
||||
control: &Action,
|
||||
functor: F,
|
||||
) -> bool {
|
||||
self.get(control)
|
||||
@ -30,15 +33,15 @@ impl ControlsHeld {
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for ControlsHeld {
|
||||
type Target = HashMap<Controls, usize>;
|
||||
impl std::ops::Deref for ActionsHeld {
|
||||
type Target = HashMap<Action, usize>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for ControlsHeld {
|
||||
impl std::ops::DerefMut for ActionsHeld {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
|
66
src/game.rs
66
src/game.rs
@ -1,4 +1,4 @@
|
||||
use crate::actions::{Controls, ControlsHeld};
|
||||
use crate::actions::{Action, ActionsHeld};
|
||||
use crate::board::Board;
|
||||
use crate::tetromino::{Direction, DirectionDiff, Tetromino};
|
||||
|
||||
@ -21,14 +21,14 @@ impl CurrentTetromino {
|
||||
}
|
||||
}
|
||||
|
||||
struct Game {
|
||||
board: Board,
|
||||
next_tetrominos: [Tetromino; 3],
|
||||
current_tetromino: CurrentTetromino,
|
||||
held_tetromino: Option<Tetromino>,
|
||||
pub struct Game {
|
||||
pub board: Board,
|
||||
pub next_tetrominos: [Tetromino; 3],
|
||||
pub current_tetromino: CurrentTetromino,
|
||||
pub held_tetromino: Option<Tetromino>,
|
||||
has_swapped_held: bool,
|
||||
score: Score,
|
||||
ticks: usize,
|
||||
pub score: Score,
|
||||
pub ticks: usize,
|
||||
}
|
||||
|
||||
struct Score {
|
||||
@ -79,6 +79,17 @@ impl Score {
|
||||
}
|
||||
|
||||
impl Game {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
board: Board::new(),
|
||||
next_tetrominos: std::array::from_fn(|_| Tetromino::random()),
|
||||
current_tetromino: CurrentTetromino::new(Tetromino::random()),
|
||||
held_tetromino: None,
|
||||
has_swapped_held: false,
|
||||
score: Score::new(),
|
||||
ticks: 0,
|
||||
}
|
||||
}
|
||||
fn take_next_in_bag(&mut self, mut last: Tetromino) -> Tetromino {
|
||||
for value in self.next_tetrominos.iter_mut().rev() {
|
||||
std::mem::swap(value, &mut last)
|
||||
@ -86,8 +97,8 @@ impl Game {
|
||||
last
|
||||
}
|
||||
|
||||
fn hard_drop(&mut self, controls: &ControlsHeld) {
|
||||
if !controls.just_pressed(self.ticks, &Controls::HardDrop) {
|
||||
fn hard_drop(&mut self, actions: &ActionsHeld) {
|
||||
if !actions.just_pressed(self.ticks, &Action::HardDrop) {
|
||||
return;
|
||||
}
|
||||
let start_y = self.current_tetromino.y;
|
||||
@ -104,9 +115,9 @@ impl Game {
|
||||
}
|
||||
}
|
||||
|
||||
fn soft_drop(&mut self, controls: &ControlsHeld) {
|
||||
fn soft_drop(&mut self, actions: &ActionsHeld) {
|
||||
let mut delay = 32 - self.score.level * 2;
|
||||
if controls.contains_key(&Controls::SoftDrop) {
|
||||
if actions.contains_key(&Action::SoftDrop) {
|
||||
delay /= 10;
|
||||
}
|
||||
|
||||
@ -119,21 +130,21 @@ impl Game {
|
||||
self.current_tetromino.y -= 1;
|
||||
self.place_current_tetromino();
|
||||
self.check_line_clears();
|
||||
} else if controls.contains_key(&Controls::SoftDrop) {
|
||||
} else if actions.contains_key(&Action::SoftDrop) {
|
||||
self.score.points += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn move_horizontally(&mut self, controls: &ControlsHeld) {
|
||||
for key in [Controls::Left, Controls::Right] {
|
||||
let just_pressed = controls.just_pressed(self.ticks, &key);
|
||||
let long_press = controls.held_for(self.ticks, &key, |held_for| held_for > 15);
|
||||
fn move_horizontally(&mut self, actions: &ActionsHeld) {
|
||||
for key in [Action::Left, Action::Right] {
|
||||
let just_pressed = actions.just_pressed(self.ticks, &key);
|
||||
let long_press = actions.held_for(self.ticks, &key, |held_for| held_for > 15);
|
||||
if !just_pressed && !long_press {
|
||||
continue;
|
||||
}
|
||||
let offset = match key {
|
||||
Controls::Left => -1,
|
||||
Controls::Right => 1,
|
||||
Action::Left => -1,
|
||||
Action::Right => 1,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
self.current_tetromino.x += offset;
|
||||
@ -173,21 +184,20 @@ impl Game {
|
||||
}
|
||||
}
|
||||
|
||||
fn step(&mut self, controls: &ControlsHeld) {
|
||||
// TODO: ensure game is running at 60 ticks per second? (`if !check_update_time(context, 60) { return; }`)
|
||||
self.hard_drop(controls);
|
||||
self.soft_drop(controls);
|
||||
self.move_horizontally(controls);
|
||||
pub fn step(&mut self, actions: &ActionsHeld) {
|
||||
self.hard_drop(actions);
|
||||
self.soft_drop(actions);
|
||||
self.move_horizontally(actions);
|
||||
|
||||
if controls.just_pressed(self.ticks, &Controls::Swap) {
|
||||
if actions.just_pressed(self.ticks, &Action::Swap) {
|
||||
self.try_swap_tetromino();
|
||||
}
|
||||
|
||||
for (control, direction) in [
|
||||
(Controls::RotateCw, DirectionDiff::Cw),
|
||||
(Controls::RotateCcw, DirectionDiff::Ccw),
|
||||
(Action::RotateCw, DirectionDiff::Cw),
|
||||
(Action::RotateCcw, DirectionDiff::Ccw),
|
||||
] {
|
||||
if !controls.just_pressed(self.ticks, &control) {
|
||||
if !actions.just_pressed(self.ticks, &control) {
|
||||
continue;
|
||||
}
|
||||
self.try_rotate(direction);
|
||||
|
@ -3,6 +3,7 @@ use tetromino::Tetromino;
|
||||
mod actions;
|
||||
mod board;
|
||||
mod game;
|
||||
mod sdl_impl;
|
||||
mod tetromino;
|
||||
|
||||
struct Rgb(u8, u8, u8);
|
||||
@ -21,4 +22,6 @@ impl Rgb {
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
fn main() {
|
||||
sdl_impl::start_game();
|
||||
}
|
||||
|
146
src/sdl_impl.rs
Normal file
146
src/sdl_impl.rs
Normal file
@ -0,0 +1,146 @@
|
||||
use crate::actions::{Action, ActionsHeld};
|
||||
use crate::board::Board;
|
||||
use crate::game::Game;
|
||||
use crate::Rgb;
|
||||
use sdl2::event::Event;
|
||||
use sdl2::keyboard::Keycode;
|
||||
use sdl2::pixels::Color;
|
||||
use sdl2::rect::Rect;
|
||||
use sdl2::render::WindowCanvas;
|
||||
use std::time::Duration;
|
||||
|
||||
fn draw_board(canvas: &mut WindowCanvas, width: usize, height: usize, color: Rgb) {
|
||||
canvas.set_draw_color(Color::RGB(color.0, color.1, color.2));
|
||||
canvas
|
||||
.fill_rect(Rect::new(
|
||||
(800 - 24 * Board::WIDTH as i32) / 2 + width as i32 * 24,
|
||||
(600 - 24 * Board::HEIGHT as i32) / 2 + height as i32 * 24,
|
||||
24,
|
||||
24,
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn start_game() {
|
||||
let mut game = Game::new();
|
||||
let mut actions = ActionsHeld::new();
|
||||
|
||||
let sdl_context = sdl2::init().unwrap();
|
||||
let video_subsystem = sdl_context.video().unwrap();
|
||||
|
||||
let window = video_subsystem
|
||||
.window("reimtris2", 800, 600)
|
||||
.position_centered()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let mut canvas = window.into_canvas().build().unwrap();
|
||||
let mut event_pump = sdl_context.event_pump().unwrap();
|
||||
'running: loop {
|
||||
canvas.set_draw_color(Color::RGB(16, 16, 16));
|
||||
canvas.clear();
|
||||
for event in event_pump.poll_iter() {
|
||||
match event {
|
||||
Event::Quit { .. }
|
||||
| Event::KeyDown {
|
||||
keycode: Some(Keycode::Escape),
|
||||
..
|
||||
} => break 'running,
|
||||
Event::KeyDown {
|
||||
keycode: Some(keycode),
|
||||
..
|
||||
} => {
|
||||
let keycode = match keycode {
|
||||
Keycode::Left | Keycode::A => Action::Left,
|
||||
Keycode::Right | Keycode::D => Action::Right,
|
||||
Keycode::Down | Keycode::S => Action::SoftDrop,
|
||||
Keycode::Space => Action::HardDrop,
|
||||
Keycode::Z => Action::RotateCcw,
|
||||
Keycode::X => Action::RotateCw,
|
||||
Keycode::C => Action::Swap,
|
||||
_ => continue,
|
||||
};
|
||||
actions.insert(keycode, game.ticks);
|
||||
}
|
||||
Event::KeyUp {
|
||||
keycode: Some(keycode),
|
||||
..
|
||||
} => {
|
||||
let keycode = match keycode {
|
||||
Keycode::Left | Keycode::A => Action::Left,
|
||||
Keycode::Right | Keycode::D => Action::Right,
|
||||
Keycode::Down | Keycode::S => Action::SoftDrop,
|
||||
Keycode::Space => Action::HardDrop,
|
||||
Keycode::Z => Action::RotateCcw,
|
||||
Keycode::X => Action::RotateCw,
|
||||
Keycode::C => Action::Swap,
|
||||
_ => continue,
|
||||
};
|
||||
actions.remove(&keycode);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
canvas.set_draw_color(Color::WHITE);
|
||||
canvas
|
||||
.draw_rect(Rect::new(
|
||||
(800 - 24 * Board::WIDTH as i32) / 2 - 1,
|
||||
(600 - 24 * Board::HEIGHT as i32) / 2 - 1,
|
||||
24 * Board::WIDTH as u32 + 2,
|
||||
24 * Board::HEIGHT as u32 + 2,
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
for (y, row) in game.board.iter().enumerate() {
|
||||
for (x, piece) in row.iter().enumerate() {
|
||||
let color = match piece {
|
||||
Some(t) => Rgb::from_tetromino(t),
|
||||
None => Rgb(0, 0, 0),
|
||||
};
|
||||
draw_board(&mut canvas, x, y, color)
|
||||
}
|
||||
}
|
||||
|
||||
let pattern = game
|
||||
.current_tetromino
|
||||
.tetromino
|
||||
.direction_pattern(&game.current_tetromino.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 + game.current_tetromino.x;
|
||||
let y = y as i8 + game.current_tetromino.y;
|
||||
|
||||
if y < 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if y >= Board::HEIGHT as i8 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if x < 0 || x >= Board::WIDTH as i8 {
|
||||
continue;
|
||||
}
|
||||
|
||||
draw_board(
|
||||
&mut canvas,
|
||||
x as usize,
|
||||
y as usize,
|
||||
Rgb::from_tetromino(&game.current_tetromino.tetromino),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
game.step(&actions);
|
||||
|
||||
canvas.present();
|
||||
::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user