olc_rust_game_engine/
lib.rs1use 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 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(); 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}