summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/snake.rs156
1 files changed, 156 insertions, 0 deletions
diff --git a/examples/snake.rs b/examples/snake.rs
new file mode 100644
index 0000000..2163acf
--- /dev/null
+++ b/examples/snake.rs
@@ -0,0 +1,156 @@
+use std::time::{Duration, SystemTime};
+
+use bytemuck::{Pod, Zeroable, bytes_of, from_bytes_mut};
+use crossterm::{event::KeyCode, style::ContentStyle};
+use tortuise::{
+ buffer::{Buffer, Cell, Event},
+ file::FileManager,
+ input::InputManager,
+ scene::switch_scenes,
+ script::{ScriptContext, ScriptDeclaration, ScriptManager},
+};
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Pod, Zeroable)]
+#[repr(C)]
+struct Position {
+ x: i16,
+ y: i16,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Pod, Zeroable)]
+#[repr(C)]
+struct GameState {
+ apple_location: Position,
+ snake_cells: [Position; 2],
+ direction: Position,
+ next_move: u128,
+}
+
+fn init_scene(_: &mut ScriptContext, state: &mut Box<[u8]>) {
+ *state = Box::from(bytes_of(&GameState {
+ apple_location: Position { x: 2, y: 2 },
+ snake_cells: [Position { x: 10, y: 5 }, Position { x: 9, y: 5 }],
+ direction: Position { x: 1, y: 0 },
+ next_move: SystemTime::now()
+ .checked_add(Duration::from_millis(200))
+ .unwrap()
+ .duration_since(SystemTime::UNIX_EPOCH)
+ .unwrap()
+ .as_millis(),
+ }))
+}
+
+fn render(ctx: &mut ScriptContext, byte_state: &mut Box<[u8]>) {
+ let state: &mut GameState = from_bytes_mut(byte_state);
+ if ctx.input.is_key_pressed_this_frame(KeyCode::Up) {
+ state.direction = Position { x: 0, y: -1 };
+ } else if ctx.input.is_key_pressed_this_frame(KeyCode::Right) {
+ state.direction = Position { x: 1, y: 0 };
+ } else if ctx.input.is_key_pressed_this_frame(KeyCode::Left) {
+ state.direction = Position { x: -1, y: 0 };
+ } else if ctx.input.is_key_pressed_this_frame(KeyCode::Down) {
+ state.direction = Position { x: 0, y: 1 };
+ }
+
+ let time = SystemTime::now()
+ .duration_since(SystemTime::UNIX_EPOCH)
+ .unwrap()
+ .as_millis();
+ if time > state.next_move {
+ state.next_move += 200;
+ state.snake_cells[1] = state.snake_cells[0];
+ state.snake_cells[0].x += state.direction.x;
+ state.snake_cells[0].y += state.direction.y;
+ }
+
+ if state.snake_cells[0] == state.apple_location {
+ let new_x = (getrandom::u32().unwrap() % ctx.buffer.window_size.columns as u32) as i16;
+ let new_y = (getrandom::u32().unwrap() % ctx.buffer.window_size.rows as u32) as i16;
+ state.apple_location = Position { x: new_x, y: new_y }
+ }
+
+ ctx.buffer.cells.clear();
+ ctx.buffer.cells.push(Cell {
+ character: '●',
+ style: ContentStyle {
+ foreground_color: Some(crossterm::style::Color::Red),
+ ..Default::default()
+ },
+ x: state.apple_location.x.unsigned_abs(),
+ y: state.apple_location.y.unsigned_abs(),
+ z: 0,
+ });
+ for cell in state.snake_cells {
+ ctx.buffer.cells.push(Cell {
+ character: '█',
+ style: ContentStyle {
+ foreground_color: Some(crossterm::style::Color::Green),
+ ..Default::default()
+ },
+ x: cell.x.unsigned_abs(),
+ y: cell.y.unsigned_abs(),
+ z: 1,
+ });
+ }
+}
+
+fn exit_on_escape(ctx: &mut ScriptContext, _: &mut Box<[u8]>) {
+ if ctx.input.is_key_pressed_this_frame(KeyCode::Esc) {
+ ctx.exit();
+ }
+}
+
+fn main() {
+ let stdout = Box::new(std::io::stdout().lock());
+ let mut buffer = Buffer::new(stdout).unwrap();
+ let mut input = InputManager::new();
+ let mut context = ScriptContext {
+ buffer: &mut buffer,
+ input: &input,
+ };
+ let scripts = [
+ ScriptDeclaration::Static {
+ start: None,
+ update: Some(exit_on_escape),
+ end: None,
+ },
+ ScriptDeclaration::Static {
+ start: Some(init_scene),
+ update: Some(render),
+ end: None,
+ },
+ ];
+ let mut scripts = ScriptManager::new(&scripts);
+
+ let mut files = FileManager::default();
+ let manifest = files
+ .load_file(Box::from("manifest.toml".as_ref()))
+ .unwrap()
+ .unwrap_manifest()
+ .clone();
+ switch_scenes(
+ &manifest.scenes,
+ &mut scripts,
+ &mut context,
+ &mut files,
+ manifest.first_scene,
+ );
+
+ buffer
+ .render_loop(|buffer, event| match event {
+ Event::Key(event) => {
+ if event.is_press() {
+ input.press_key(event.code);
+ }
+ }
+ Event::Render => {
+ let mut context = ScriptContext {
+ buffer,
+ input: &input,
+ };
+ scripts.run_scripts(&mut context);
+ input.reset_next_frame();
+ }
+ })
+ .unwrap();
+}