summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock21
-rw-r--r--Cargo.toml1
-rw-r--r--examples/snake.rs156
-rw-r--r--src/file.rs2
-rw-r--r--src/main.rs11
-rw-r--r--src/scene.rs8
-rw-r--r--src/script.rs13
7 files changed, 201 insertions, 11 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 431f04b..78081df 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -280,6 +280,26 @@ dependencies = [
]
[[package]]
+name = "bytemuck"
+version = "1.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec"
+dependencies = [
+ "bytemuck_derive",
+]
+
+[[package]]
+name = "bytemuck_derive"
+version = "1.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1939,6 +1959,7 @@ checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
name = "tortuise"
version = "0.1.0"
dependencies = [
+ "bytemuck",
"crossterm",
"getrandom",
"mimalloc",
diff --git a/Cargo.toml b/Cargo.toml
index e4d6f1e..a13d8c4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2024"
[dependencies]
+bytemuck = { version = "1", features = ["derive"] }
crossterm = "0.29"
getrandom = "0.4"
mimalloc = "0.1"
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();
+}
diff --git a/src/file.rs b/src/file.rs
index 8e96fa9..42fecfa 100644
--- a/src/file.rs
+++ b/src/file.rs
@@ -14,7 +14,7 @@ pub struct FileManager {
cache: FxHashMap<Box<Path>, FileData>,
}
-#[derive(Debug, Serialize, Deserialize)]
+#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GameManifest {
pub name: Box<str>,
pub first_scene: usize,
diff --git a/src/main.rs b/src/main.rs
index 0cbb7c8..81fffcf 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -78,8 +78,15 @@ fn main() {
let manifest = files
.load_file(Box::from("manifest.toml".as_ref()))
.unwrap()
- .unwrap_manifest();
- switch_scenes(&manifest.scenes, &mut scripts, &mut context, 0);
+ .unwrap_manifest()
+ .clone();
+ switch_scenes(
+ &manifest.scenes,
+ &mut scripts,
+ &mut context,
+ &mut files,
+ manifest.first_scene,
+ );
buffer
.render_loop(|buffer, event| match event {
diff --git a/src/scene.rs b/src/scene.rs
index 143eb0b..8f70350 100644
--- a/src/scene.rs
+++ b/src/scene.rs
@@ -1,7 +1,10 @@
use rustc_hash::FxHashSet;
use serde::{Deserialize, Serialize};
-use crate::script::{ScriptContext, ScriptManager};
+use crate::{
+ file::FileManager,
+ script::{ScriptContext, ScriptManager},
+};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Scene {
@@ -29,13 +32,14 @@ pub fn switch_scenes(
scenes: &[Scene],
scripts: &mut ScriptManager,
context: &mut ScriptContext<'_>,
+ files: &mut FileManager,
new: usize,
) {
let active_scripts = FxHashSet::from_iter(scripts.active_scripts.iter().cloned());
let scripts_to_load = &scenes[new].scripts.required;
for script in scripts_to_load {
if !active_scripts.contains(script) {
- scripts.activate_script(*script, context);
+ scripts.activate_script(*script, context, files);
} else {
scripts.reset_script(*script, context);
}
diff --git a/src/script.rs b/src/script.rs
index dd65106..eeb943f 100644
--- a/src/script.rs
+++ b/src/script.rs
@@ -79,12 +79,13 @@ impl<'a> ScriptManager<'a> {
});
}
ScriptDeclaration::Wasm { path } => {
- let module = files.load_file(path.clone()).unwrap().unwrap_wasm_module();
- let mut store = wasmi::Store::new(&files.wasm_compiler, context);
- let instance = self
- .linker
- .instantiate_and_start(&mut store, &module)
- .unwrap();
+ todo!("wasm scripts")
+ // let module = files.load_file(path.clone()).unwrap().unwrap_wasm_module();
+ // let mut store = wasmi::Store::new(&files.wasm_compiler, context);
+ // let instance = self
+ // .linker
+ // .instantiate_and_start(&mut store, &module)
+ // .unwrap();
}
}
self.active_scripts.push(script_id);