tinybit/
lib.rs

1// #![deny(missing_docs)]
2//! Terminal game engine
3//! ```
4//! use tinybit::events::{events, Event, KeyCode, KeyEvent, EventModel};
5//! use tinybit::{
6//!     term_size, Camera, Renderer, ScreenPos, ScreenSize, StdoutTarget, Viewport,
7//!     WorldPos, WorldSize, Pixel
8//! };
9//!
10//! fn main() {
11//!     let (width, height) = term_size().expect("Can't get the term size? Can't play the game!");
12//!
13//!     // Viewport
14//!     let viewport_size = ScreenSize::new(width / 2, height / 2);
15//!     let mut viewport = Viewport::new(ScreenPos::new(0, 4), viewport_size);
16//!
17//!     // Camera
18//!     let (width, height) = (width as i64, height as i64);
19//!     let camera_size = WorldSize::new(width / 2, height / 2);
20//!     let camera_pos = WorldPos::new(width, height);
21//!     let mut camera = Camera::new(camera_pos, camera_size);
22//!
23//!     // Renderer
24//!     let stdout_renderer = StdoutTarget::new().expect("Failed to enter raw mode");
25//!     let mut renderer = Renderer::new(stdout_renderer);
26//!
27//!     // Player
28//!     let mut player = ('@', camera_pos);
29//!
30//!     let (_tx, events) = events::<()> (EventModel::Fps(20)); 
31//!     for event in events {
32//!         match event {
33//!             Event::User(()) => {}
34//!             Event::Tick => {
35//!                 let pixel = Pixel::new(player.0, camera.to_screen(player.1), None, None);
36//!                 viewport.draw_pixel(pixel);
37//!                 let _ = renderer.render(&mut viewport);
38//!                 viewport.swap_buffers();
39//! #               break
40//!             }
41//!             Event::Key(KeyEvent { code: KeyCode::Esc, ..  }) => break,
42//!             Event::Key(KeyEvent { code: kc, .. }) => {
43//!                 match kc {
44//!                     KeyCode::Left => { player.1.x -= 1; }
45//!                     KeyCode::Right => { player.1.x += 1; }
46//!                     KeyCode::Up => { player.1.y -= 1; }
47//!                     KeyCode::Down => { player.1.y += 1; }
48//!                     _ => {}
49//!                 }
50//!             }
51//!             Event::Resize(w, h) => {}
52//!         }
53//!     }
54//! }
55//! ```
56
57#[cfg(feature = "derive")]
58use serde::{Deserialize, Serialize};
59
60mod pixelbuffer;
61mod viewport;
62
63pub mod camera;
64pub mod events;
65pub mod render;
66pub mod widgets;
67
68/// A character at a position, with a colour
69#[derive(Debug, Copy, Clone, PartialEq)]
70#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
71pub struct Pixel {
72    pub glyph: char,
73    pub pos: ScreenPos,
74    pub fg_color: Option<Color>,
75    pub bg_color: Option<Color>,
76}
77
78impl Pixel {
79    pub fn new(
80        glyph: char,
81        pos: ScreenPos,
82        fg_color: Option<Color>,
83        bg_color: Option<Color>,
84    ) -> Self {
85        Self { glyph, pos, fg_color, bg_color }
86    }
87
88    pub fn white(c: char, pos: ScreenPos) -> Self {
89        Self::new(c, pos, None, None)
90    }
91}
92
93// -----------------------------------------------------------------------------
94//     - Reexports -
95// -----------------------------------------------------------------------------
96pub use camera::Camera;
97pub use crossterm::style::{Color, Colored};
98pub use crossterm::terminal::size as term_size;
99pub use crossterm::ErrorKind as CrosstermError;
100pub use pixelbuffer::PixelBuffer;
101pub use render::{Renderer, StdoutTarget};
102pub use viewport::Viewport;
103
104// -----------------------------------------------------------------------------
105//     - Euclid -
106// -----------------------------------------------------------------------------
107/// A position on screen, where 0,0 is the top left corner
108#[derive(Debug, Copy, Clone, PartialEq)]
109#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
110pub struct ScreenPos {
111    pub x: u16,
112    pub y: u16,
113}
114
115impl ScreenPos {
116    pub fn new(x: u16, y: u16) -> Self {
117        Self { x, y }
118    }
119
120    pub fn zero() -> Self {
121        Self::new(0, 0)
122    }
123}
124
125/// A position in the world
126#[derive(Debug, Copy, Clone, PartialEq)]
127#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
128pub struct WorldPos {
129    pub x: i64,
130    pub y: i64,
131}
132
133impl WorldPos {
134    pub fn new(x: i64, y: i64) -> Self {
135        Self { x, y }
136    }
137
138    pub fn zero() -> Self {
139        Self::new(0, 0)
140    }
141}
142
143/// A rect on screen
144#[derive(Debug, Copy, Clone)]
145#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
146pub struct ScreenRect {
147    pub origin: ScreenPos,
148    pub size: ScreenSize,
149}
150
151impl ScreenRect {
152    pub fn new(origin: ScreenPos, size: ScreenSize) -> Self {
153        Self { origin, size }
154    }
155}
156
157/// A rect in the world
158#[derive(Debug, Copy, Clone)]
159#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
160pub struct WorldRect {
161    pub origin: WorldPos,
162    pub size: WorldSize,
163}
164
165impl WorldRect {
166    pub fn new(origin: WorldPos, size: WorldSize) -> Self {
167        Self { origin, size }
168    }
169
170    pub fn min_x(&self) -> i64 {
171        self.origin.x
172    }
173
174    pub fn min_y(&self) -> i64 {
175        self.origin.y
176    }
177
178    pub fn max_x(&self) -> i64 {
179        self.origin.x + self.size.width
180    }
181
182    pub fn max_y(&self) -> i64 {
183        self.origin.y + self.size.height
184    }
185}
186
187/// A size on screen
188#[derive(Debug, Copy, Clone)]
189#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
190pub struct ScreenSize {
191    pub width: u16,
192    pub height: u16,
193}
194
195impl ScreenSize {
196    pub fn new(width: u16, height: u16) -> Self {
197        Self { width, height }
198    }
199}
200
201/// A size in the world
202#[derive(Debug, Copy, Clone, PartialEq)]
203#[cfg_attr(feature = "derive", derive(Serialize, Deserialize))]
204pub struct WorldSize {
205    pub width: i64,
206    pub height: i64,
207}
208
209impl WorldSize {
210    pub fn new(width: i64, height: i64) -> Self {
211        Self { width, height }
212    }
213}