summaryrefslogtreecommitdiff
path: root/src/gui.rs
blob: b13891c8a2816c553c78918c0600c990de33d972 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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<G: Game + 'static>(mut game: G) -> std::thread::JoinHandle<()> {
        std::thread::spawn(|| Self::new().run_in_thread(game))
    }

    fn run_in_thread<G: Game + 'static>(&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<Mutex<Option<Status>>>,
    size: (u32, u32),
    jump: Arc<AtomicU32>,
}

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);
        }
    }
}