use ggez::event::EventHandler; use crate::game::{Game, Status, Field}; use std::sync::{Mutex, Arc, atomic::AtomicU32}; pub struct Gui { ctx: ggez::Context, event_loop: ggez::event::EventsLoop, } impl Gui { pub fn new() -> Self { let (mut ctx, event_loop) = ggez::ContextBuilder::new("neo x", "natrixaeria") .window_setup(ggez::conf::WindowSetup::default() .vsync(true)) .build().unwrap(); Self { ctx, event_loop, } } pub fn run(mut game: G) -> std::thread::JoinHandle<()> { std::thread::spawn(|| Self::new().run_in_thread(game)) } fn run_in_thread(&mut self, mut game: G) { let status = Arc::new(Mutex::new(Some(game.status()))); let jump = Arc::new(AtomicU32::new(0)); let mut handler = GameEventHandler { status: Arc::clone(&status), size: game.size(), jump: Arc::clone(&jump) }; std::thread::spawn(move || { let mut speed = 85.0; while let Some(new_status) = game.update() { if jump.fetch_and(0, std::sync::atomic::Ordering::SeqCst) > 0 { game.jump(); } if let Ok(mut lock) = status.lock() { *lock = Some(new_status); } else { break; } std::thread::sleep(std::time::Duration::from_millis(speed as u64)); speed *= 0.9992; println!("{:04} | {}", game.get_points(), speed); } println!("Points: {}", game.get_points()); { *status.lock().unwrap() = None; } }); match ggez::event::run(&mut self.ctx, &mut self.event_loop, &mut handler) { Err(_) => (), _ => (), } } } struct GameEventHandler { status: Arc>>, size: (u32, u32), jump: Arc, } impl GameEventHandler { fn draw_rect(&mut self, ctx: &mut ggez::Context, translation: [f32; 4], color: ggez::graphics::Color, draw_mode: &ggez::graphics::DrawParam) -> ggez::GameResult<()> { let rect = ggez::graphics::Mesh::new_rectangle( ctx, ggez::graphics::DrawMode::fill(), ggez::graphics::Rect::new(translation[0], translation[1], translation[2], translation[3]), color )?; ggez::graphics::draw(ctx, &rect, *draw_mode) } fn draw_field(&mut self, ctx: &mut ggez::Context, col: u32, row: u32, color: ggez::graphics::Color, draw_mode: &ggez::graphics::DrawParam) -> ggez::GameResult<()> { let (w, h) = ggez::graphics::size(ctx); let (rx, ry) = self.size; let (u, v) = (w / (rx as f32), h / (ry as f32)); let (u, v) = if u < v { (u, u) } else { (v, v) }; let (x, y) = (col as f32 * u, row as f32 * v); let translation = [ x, y, u, v ]; self.draw_rect(ctx, translation, color, draw_mode) } } impl EventHandler for GameEventHandler { fn update(&mut self, ctx: &mut ggez::Context) -> ggez::GameResult<()> { Ok(()) } fn draw(&mut self, ctx: &mut ggez::Context) -> ggez::GameResult<()> { let status = { (*self.status.lock().unwrap()).clone() }; if status.is_none() { println!("exxiittt"); ggez::event::quit(ctx); return Err(ggez::error::GameError::ResourceLoadError(format!("ending app"))); } let status = status.unwrap(); let bg = ggez::graphics::BLACK; let draw_mode = ggez::graphics::DrawParam::new(); ggez::graphics::clear(ctx, bg); let (cols, rows) = self.size; for row in 0..rows { for col in 0..cols { let color = match status.fields[((rows - row - 1) + col * rows) as usize] { Field::Air => continue, Field::Wall => ggez::graphics::Color::from_rgb(100, 100, 100), Field::Spike => ggez::graphics::Color::from_rgb(200, 50, 6), }; self.draw_field(ctx, col, row, color, &draw_mode)?; } } let player_color = ggez::graphics::Color::from_rgb(0, 180, 250); let (x, y) = (status.player[0], status.player[1]); if rows >= y + 1 { self.draw_field(ctx, x, rows - y - 1, player_color, &draw_mode)?; } ggez::graphics::present(ctx) } fn key_down_event(&mut self, _ctx: &mut ggez::Context, keycode: ggez::event::KeyCode, _keymods: ggez::event::KeyMods, _repeat: bool) { if keycode == ggez::event::KeyCode::Space { self.jump.store(1, std::sync::atomic::Ordering::SeqCst); } } }