olc_rust_game_engine/
lib.rs

1//! # Rust OlcConsoleGameEngine
2//!
3//! `game_engine` is an attempt at a rust port of
4//! [Javidx9's](https://www.youtube.com/channel/UC-yuWVUplUJZvieEligKBkA)
5//! [Console Game Engine](https://github.com/OneLoneCoder/videos/blob/master/olcConsoleGameEngine.h)
6//!
7//! Better docs *definitely* coming soon 😁
8
9use crossterm::style::{Print, StyledContent, Stylize};
10use crossterm::{cursor, execute, terminal};
11use keyboard_query::{DeviceQuery, DeviceState};
12use std::io::stdout;
13
14pub use crossterm::style::Color;
15pub use crossterm::Result;
16
17pub struct ConsoleGameEngine<T: Rules> {
18    height: usize,
19    utils: Utils,
20    rules: T,
21    width: usize,
22}
23
24impl<T> ConsoleGameEngine<T>
25where
26    T: Rules,
27{
28    pub fn new(height: usize, width: usize, rules: T) -> ConsoleGameEngine<T> {
29        ConsoleGameEngine {
30            height,
31            utils: Utils::new(height, width),
32            rules,
33            width,
34        }
35    }
36
37    pub fn construct_console(&self) -> Result<()> {
38        // todo: set console title to something
39        execute!(
40            stdout(),
41            terminal::SetSize(self.width as u16, self.height as u16),
42            cursor::DisableBlinking,
43            cursor::Hide
44        )?;
45        terminal::enable_raw_mode().unwrap(); // is this necessary?
46
47        Ok(())
48    }
49
50    pub fn start(&mut self, redraw: bool) -> Result<()> {
51        self.rules.on_user_create(&mut self.utils);
52
53        let mut t_p_1 = std::time::Instant::now();
54        let mut t_p_2: std::time::Instant;
55
56        let device_state = DeviceState::new();
57
58        loop {
59            t_p_2 = std::time::Instant::now();
60            let elapsed_time = t_p_2.duration_since(t_p_1).as_secs_f64();
61            t_p_1 = t_p_2;
62
63            self.utils.keys = device_state.get_keys();
64
65            self.rules.on_user_update(&mut self.utils, elapsed_time);
66
67            if redraw {
68                self.utils.redraw_screen()?
69            } else {
70                self.utils.draw_screen()?
71            };
72        }
73    }
74}
75
76pub struct Utils {
77    diff_coords: Vec<(usize, usize)>,
78    pub height: usize,
79    pub keys: Vec<u16>,
80    screen: Vec<StyledContent<char>>,
81    pub width: usize,
82}
83
84impl Utils {
85    fn new(height: usize, width: usize) -> Utils {
86        Utils {
87            diff_coords: vec![],
88            height,
89            keys: vec![],
90            screen: vec![' '.with(Color::Black); height * width],
91            width,
92        }
93    }
94
95    pub fn redraw_screen(&mut self) -> Result<()> {
96        let scr: String = self.screen.iter().map(|&x| format!("{}", x)).collect();
97
98        execute!(stdout(), cursor::MoveTo(0, 0), Print(scr))?;
99
100        Ok(())
101    }
102
103    pub fn draw_screen(&mut self) -> Result<()> {
104        for coords in &self.diff_coords {
105            execute!(
106                stdout(),
107                cursor::MoveTo(coords.0 as u16, coords.1 as u16),
108                Print(self.screen[coords.1 * self.width + coords.0])
109            )?;
110        }
111        self.diff_coords.clear();
112
113        Ok(())
114    }
115
116    pub fn draw_string(&mut self, x: usize, y: usize, draw_str: &str, color: Color, alpha: bool) {
117        for (i, c) in draw_str.chars().enumerate() {
118            if alpha && c == ' ' {
119                continue;
120            }
121
122            self.draw(x + i, y, c, color);
123        }
124    }
125
126    pub fn draw(&mut self, x: usize, y: usize, ch: char, color: Color) {
127        if x < self.width && y < self.height && self.screen[y * self.width + x] != ch.with(color) {
128            self.screen[y * self.width + x] = ch.with(color);
129            self.diff_coords.push((x, y));
130        }
131    }
132
133    pub fn fill(&mut self, x1: usize, y1: usize, x2: usize, y2: usize, ch: char, color: Color) {
134        for x in x1..x2 {
135            for y in y1..y2 {
136                self.draw(x, y, ch, color);
137            }
138        }
139    }
140}
141
142pub trait Rules {
143    fn on_user_create(&mut self, utils: &mut Utils);
144    fn on_user_update(&mut self, utils: &mut Utils, elapsed_time: f64);
145}