reimtris2/src/gui/render.rs

224 lines
6.9 KiB
Rust

use crate::actions::{Action, ActionsHeld};
use crate::board::Board;
use crate::game::{CurrentTetromino, Game};
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::pixels::Color;
use sdl2::rect::Rect;
use sdl2::render::{Texture, TextureCreator, WindowCanvas};
use sdl2::ttf::Sdl2TtfContext;
use std::time::Duration;
use super::audio::{self};
use super::ui::{Rgb, UiCtx};
const WIDTH: i32 = 1000;
const HEIGHT: i32 = 800;
fn font_texture<'font, 'a, P: AsRef<std::path::Path>, Text: AsRef<str>, C>(
font: P,
text: Text,
ttf_context: &'a Sdl2TtfContext,
texture_creator: &'font TextureCreator<C>,
) -> Result<Texture<'font>, String> {
let font = ttf_context.load_font(font, 24)?;
let game_over_text = font
.render(text.as_ref())
.solid(Color::RGB(255, 255, 255))
.map_err(|err| err.to_string())?;
let texture = texture_creator
.create_texture_from_surface(game_over_text)
.map_err(|err| err.to_string())?;
Ok(texture)
}
struct SdlUiCtx {
canvas: WindowCanvas,
ttf: Sdl2TtfContext,
}
impl SdlUiCtx {
fn present(&mut self) {
self.canvas.present();
}
}
impl UiCtx<String> for SdlUiCtx {
fn window_size(&self) -> Result<(i32, i32), String> {
let (width, height) = self.canvas.window().size();
Ok((width as i32, height as i32))
}
fn fill_rect(
&mut self,
x: i32,
y: i32,
width: i32,
height: i32,
rgb: &super::ui::Rgb,
) -> Result<(), String> {
self.canvas.set_draw_color(Color::RGB(rgb.0, rgb.1, rgb.2));
self.canvas
.fill_rect(Rect::new(x, y, width as u32, height as u32))?;
Ok(())
}
fn outline_rect(
&mut self,
x: i32,
y: i32,
width: i32,
height: i32,
rgb: &super::ui::Rgb,
) -> Result<(), String> {
self.canvas.set_draw_color(Color::RGB(rgb.0, rgb.1, rgb.2));
self.canvas
.draw_rect(Rect::new(x, y, width as u32, height as u32))?;
Ok(())
}
fn text_size<P: AsRef<std::path::Path>, Text: AsRef<str>>(
&mut self,
font: P,
text: Text,
) -> Result<(i32, i32), String> {
let texture_creator = self.canvas.texture_creator();
let texture = font_texture(font, text, &self.ttf, &texture_creator)?;
let query = texture.query();
Ok((query.width as i32, query.height as i32))
}
fn fill_text<P: AsRef<std::path::Path>, Text: AsRef<str>>(
&mut self,
font: P,
text: Text,
x: i32,
y: i32,
width: i32,
height: i32,
) -> Result<(), String> {
let texture_creator = self.canvas.texture_creator();
let texture = font_texture(font, text, &self.ttf, &texture_creator)?;
self.canvas.copy(
&texture,
None,
Some(Rect::new(x, y, width as u32, height as u32)),
)?;
Ok(())
}
fn clear(&mut self, rgb: &Rgb) -> Result<(), String> {
self.canvas.set_draw_color(Color::RGB(rgb.0, rgb.1, rgb.2));
self.canvas.clear();
Ok(())
}
}
pub fn start_game() -> Result<(), String> {
let mut game = Game::new();
let mut actions = ActionsHeld::new();
let mut paused = false;
let audio_thread = audio::audio_thread();
let sdl_context = sdl2::init()?;
let ttf_context = sdl2::ttf::init().unwrap();
let video_subsystem = sdl_context.video()?;
let window = video_subsystem
.window("reimtris2", WIDTH as u32, HEIGHT as u32)
.position_centered()
.build()
.unwrap();
let canvas = window.into_canvas().build().unwrap();
let mut ctx = SdlUiCtx {
canvas,
ttf: ttf_context,
};
let mut event_pump = sdl_context.event_pump()?;
'running: loop {
ctx.clear(&Rgb(16, 16, 16))?;
for event in event_pump.poll_iter() {
match event {
Event::Quit { .. }
| Event::KeyDown {
keycode: Some(Keycode::Escape),
..
} => break 'running Ok(()),
Event::KeyDown {
keycode: Some(keycode),
..
} => {
let keycode = match keycode {
Keycode::Return if !paused && game.game_over => {
game = Game::new();
continue;
}
Keycode::M => {
audio_thread.send(audio::Command::ToggleMuted).unwrap();
continue;
}
Keycode::P => {
paused = !paused;
continue;
}
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);
}
_ => {}
}
}
ctx.draw_board(&game.board, &game.current_tetromino)?;
if paused {
ctx.draw_important_text(
"resources/josenfin_sans_regular.ttf",
"game paused o_o... press [p] to unpause !!",
)?;
} else if game.game_over {
ctx.draw_important_text(
"resources/josenfin_sans_regular.ttf",
"game over T_T... press [enter] 2 restart :D",
)?;
} else {
let effects = game.step(&actions);
effects.into_iter().for_each(|effect| {
audio_thread
.send(audio::Command::PlayEffect(effect))
.unwrap()
});
}
ctx.present();
::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
}
}