From ec40f6e3acc7874b82709ab81b254c741529fdff Mon Sep 17 00:00:00 2001 From: Mica White Date: Mon, 26 Oct 2026 10:52:04 -0400 Subject: Finish engine --- platforms/tortuise-script-compiled/src/buffer.rs | 296 ++++++++++++++++++++++ platforms/tortuise-script-compiled/src/entity.rs | 73 ++++++ platforms/tortuise-script-compiled/src/input.rs | 260 +++++++++++++++++++ platforms/tortuise-script-compiled/src/lib.rs | 119 +++++++++ platforms/tortuise-script-compiled/src/process.rs | 10 + platforms/tortuise-script-compiled/src/random.rs | 70 +++++ platforms/tortuise-script-compiled/src/scene.rs | 17 ++ platforms/tortuise-script-compiled/src/script.rs | 29 +++ platforms/tortuise-script-compiled/src/state.rs | 65 +++++ platforms/tortuise-script-compiled/src/storage.rs | 181 +++++++++++++ platforms/tortuise-script-compiled/src/time.rs | 132 ++++++++++ 11 files changed, 1252 insertions(+) create mode 100644 platforms/tortuise-script-compiled/src/buffer.rs create mode 100644 platforms/tortuise-script-compiled/src/entity.rs create mode 100644 platforms/tortuise-script-compiled/src/input.rs create mode 100644 platforms/tortuise-script-compiled/src/lib.rs create mode 100644 platforms/tortuise-script-compiled/src/process.rs create mode 100644 platforms/tortuise-script-compiled/src/random.rs create mode 100644 platforms/tortuise-script-compiled/src/scene.rs create mode 100644 platforms/tortuise-script-compiled/src/script.rs create mode 100644 platforms/tortuise-script-compiled/src/state.rs create mode 100644 platforms/tortuise-script-compiled/src/storage.rs create mode 100644 platforms/tortuise-script-compiled/src/time.rs (limited to 'platforms/tortuise-script-compiled/src') diff --git a/platforms/tortuise-script-compiled/src/buffer.rs b/platforms/tortuise-script-compiled/src/buffer.rs new file mode 100644 index 0000000..c60ee40 --- /dev/null +++ b/platforms/tortuise-script-compiled/src/buffer.rs @@ -0,0 +1,296 @@ +use std::time::Duration; + +use crossterm::style::ContentStyle; +use serde::{Deserialize, Serialize}; +use tortuise::script::Operation; + +use crate::ScriptContext; + +#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Serialize, Deserialize)] +pub struct Cell { + pub character: char, + pub foreground_color: Color, + pub background_color: Color, + pub underline_color: Color, + pub attributes: Attributes, + pub x: u16, + pub y: u16, + pub z: u8, +} + +#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Serialize, Deserialize)] +pub struct WindowSize { + pub rows: u16, + pub columns: u16, +} + +#[derive( + Copy, Clone, Debug, Default, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize, Deserialize, +)] +pub enum Color { + /// Resets the terminal color. + #[default] + Reset, + + /// Black color. + Black, + + /// Dark grey color. + DarkGrey, + + /// Light red color. + Red, + + /// Dark red color. + DarkRed, + + /// Light green color. + Green, + + /// Dark green color. + DarkGreen, + + /// Light yellow color. + Yellow, + + /// Dark yellow color. + DarkYellow, + + /// Light blue color. + Blue, + + /// Dark blue color. + DarkBlue, + + /// Light magenta color. + Magenta, + + /// Dark magenta color. + DarkMagenta, + + /// Light cyan color. + Cyan, + + /// Dark cyan color. + DarkCyan, + + /// White color. + White, + + /// Grey color. + Grey, + + /// An RGB color. See [RGB color model](https://en.wikipedia.org/wiki/RGB_color_model) for more info. + /// + /// Most UNIX terminals and Windows 10 supported only. + /// See [Platform-specific notes](enum.Color.html#platform-specific-notes) for more info. + Rgb { r: u8, g: u8, b: u8 }, + + /// An ANSI color. See [256 colors - cheat sheet](https://jonasjacek.github.io/colors/) for more info. + /// + /// Most UNIX terminals and Windows 10 supported only. + /// See [Platform-specific notes](enum.Color.html#platform-specific-notes) for more info. + AnsiValue(u8), +} + +#[derive( + Copy, Clone, Debug, Default, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize, Deserialize, +)] +pub struct Attributes(u32); + +pub enum Attribute { + /// Increases the text intensity. + Bold = 1, + /// Decreases the text intensity. + Dim = 2, + /// Emphasises the text. + Italic = 4, + /// Underlines the text. + Underlined = 8, + + // Other types of underlining + /// Double underlines the text. + DoubleUnderlined = 16, + /// Undercurls the text. + Undercurled = 32, + /// Underdots the text. + Underdotted = 64, + /// Underdashes the text. + Underdashed = 128, + + /// Makes the text blinking (< 150 per minute). + SlowBlink = 256, + /// Makes the text blinking (>= 150 per minute). + RapidBlink = 512, + /// Swaps foreground and background colors. + Reverse = 1024, + /// Hides the text (also known as Conceal). + Hidden = 2048, + /// Crosses the text. + CrossedOut = 4096, + /// Sets the [Fraktur](https://en.wikipedia.org/wiki/Fraktur) typeface. + /// + /// Mostly used for [mathematical alphanumeric symbols](https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols). + Fraktur = 8192, + /// Makes the text framed. + Framed = 16384, + /// Makes the text encircled. + Encircled = 32768, + /// Draws a line at the top of the text. + OverLined = 65536, +} + +impl Attributes { + pub fn none() -> Self { + Self(0) + } + + pub fn with(self, attribute: Attribute) -> Self { + Self(self.0 | attribute as u32) + } + + pub fn without(self, attribute: Attribute) -> Self { + Self(self.0 & !(attribute as u32)) + } + + pub fn set(&mut self, attribute: Attribute) { + self.0 |= attribute as u32; + } + + pub fn unset(&mut self, attribute: Attribute) { + self.0 &= !(attribute as u32) + } + + pub fn has(self, attribute: Attribute) -> bool { + self.0 & (attribute as u32) != 0 + } + + pub fn extend(&mut self, attributes: Attributes) { + self.0 |= attributes.0 + } + + pub fn is_empty(self) -> bool { + self.0 == 0 + } +} + +impl From for crossterm::style::Color { + fn from(value: Color) -> Self { + match value { + Color::Reset => crossterm::style::Color::Reset, + Color::Black => crossterm::style::Color::Black, + Color::DarkGrey => crossterm::style::Color::DarkGrey, + Color::Red => crossterm::style::Color::Red, + Color::DarkRed => crossterm::style::Color::DarkRed, + Color::Green => crossterm::style::Color::Green, + Color::DarkGreen => crossterm::style::Color::DarkGreen, + Color::Yellow => crossterm::style::Color::Yellow, + Color::DarkYellow => crossterm::style::Color::DarkYellow, + Color::Blue => crossterm::style::Color::Blue, + Color::DarkBlue => crossterm::style::Color::DarkBlue, + Color::Magenta => crossterm::style::Color::Magenta, + Color::DarkMagenta => crossterm::style::Color::DarkMagenta, + Color::Cyan => crossterm::style::Color::Cyan, + Color::DarkCyan => crossterm::style::Color::DarkCyan, + Color::White => crossterm::style::Color::White, + Color::Grey => crossterm::style::Color::Grey, + Color::Rgb { r, g, b } => crossterm::style::Color::Rgb { r, g, b }, + Color::AnsiValue(v) => crossterm::style::Color::AnsiValue(v), + } + } +} + +impl From for crossterm::style::Attributes { + fn from(value: Attributes) -> Self { + let mut new = crossterm::style::Attributes::none(); + + if value.has(Attribute::Bold) { + new.set(crossterm::style::Attribute::Bold); + } + if value.has(Attribute::Dim) { + new.set(crossterm::style::Attribute::Dim); + } + if value.has(Attribute::Italic) { + new.set(crossterm::style::Attribute::Italic); + } + if value.has(Attribute::Underlined) { + new.set(crossterm::style::Attribute::Underlined); + } + if value.has(Attribute::DoubleUnderlined) { + new.set(crossterm::style::Attribute::DoubleUnderlined); + } + if value.has(Attribute::Undercurled) { + new.set(crossterm::style::Attribute::Undercurled); + } + if value.has(Attribute::Underdotted) { + new.set(crossterm::style::Attribute::Underdotted); + } + if value.has(Attribute::Underdashed) { + new.set(crossterm::style::Attribute::Underdashed); + } + if value.has(Attribute::SlowBlink) { + new.set(crossterm::style::Attribute::SlowBlink); + } + if value.has(Attribute::RapidBlink) { + new.set(crossterm::style::Attribute::RapidBlink); + } + if value.has(Attribute::Reverse) { + new.set(crossterm::style::Attribute::Reverse); + } + if value.has(Attribute::Hidden) { + new.set(crossterm::style::Attribute::Hidden); + } + if value.has(Attribute::CrossedOut) { + new.set(crossterm::style::Attribute::CrossedOut); + } + if value.has(Attribute::Fraktur) { + new.set(crossterm::style::Attribute::Fraktur); + } + if value.has(Attribute::Framed) { + new.set(crossterm::style::Attribute::Framed); + } + if value.has(Attribute::Encircled) { + new.set(crossterm::style::Attribute::Encircled); + } + if value.has(Attribute::OverLined) { + new.set(crossterm::style::Attribute::OverLined); + } + + new + } +} + +impl ScriptContext<'_> { + pub fn add_cell(&mut self, cell: Cell) { + puffin::profile_function!(); + self.0 + .operation_queue + .push(Operation::AddCell(tortuise::buffer::Cell { + character: cell.character, + style: ContentStyle { + foreground_color: Some(cell.foreground_color.into()), + background_color: Some(cell.background_color.into()), + underline_color: Some(cell.underline_color.into()), + attributes: cell.attributes.into(), + }, + x: cell.x, + y: cell.y, + z: cell.z, + })); + } + + pub fn window_size(&self) -> WindowSize { + puffin::profile_function!(); + let size = &self.0.buffer.window_size; + WindowSize { + rows: size.rows, + columns: size.columns, + } + } + + pub fn set_framerate_limit(&mut self, limit: Option) { + puffin::profile_function!(); + let limit = limit.map(|limit| Duration::from_nanos(1_000_000_000 / limit as u64)); + self.0.operation_queue.push(Operation::SetFrameLimit(limit)); + } +} diff --git a/platforms/tortuise-script-compiled/src/entity.rs b/platforms/tortuise-script-compiled/src/entity.rs new file mode 100644 index 0000000..507690d --- /dev/null +++ b/platforms/tortuise-script-compiled/src/entity.rs @@ -0,0 +1,73 @@ +use core::ops::{Deref, DerefMut}; + +use spin::lock_api::{MappedRwLockWriteGuard, RwLock, RwLockWriteGuard}; + +use crate::ScriptContext; + +pub struct Entity { + data: RwLock>, + default: fn(&ScriptContext) -> T, +} + +pub struct EntityRef<'entity, T> { + data: MappedRwLockWriteGuard<'entity, T>, +} + +pub struct EntityMut<'entity, T> { + data: MappedRwLockWriteGuard<'entity, T>, +} + +impl Entity { + pub const fn new(_name: &'static str, default: fn(&ScriptContext) -> T) -> Self { + Self { + data: RwLock::new(None), + default, + } + } + + pub const fn from_string(name: String, default: fn(&ScriptContext) -> T) -> Self { + core::mem::forget(name); + Self { + data: RwLock::new(None), + default, + } + } + + pub fn get(&self, context: &ScriptContext) -> EntityRef<'_, T> { + puffin::profile_function!(); + let data = RwLockWriteGuard::map(self.data.write(), |data| { + data.get_or_insert_with(|| (self.default)(context)) + }); + EntityRef { data } + } + + pub fn get_mut(&self, context: &ScriptContext) -> EntityMut<'_, T> { + puffin::profile_function!(); + let data = RwLockWriteGuard::map(self.data.write(), |data| { + data.get_or_insert_with(|| (self.default)(context)) + }); + EntityMut { data } + } +} + +impl Deref for EntityRef<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.data + } +} + +impl Deref for EntityMut<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.data + } +} + +impl DerefMut for EntityMut<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.data + } +} diff --git a/platforms/tortuise-script-compiled/src/input.rs b/platforms/tortuise-script-compiled/src/input.rs new file mode 100644 index 0000000..a7d7340 --- /dev/null +++ b/platforms/tortuise-script-compiled/src/input.rs @@ -0,0 +1,260 @@ +use serde::{Deserialize, Serialize}; + +use crate::ScriptContext; + +/// Represents a key. +#[derive(Debug, PartialOrd, PartialEq, Eq, Clone, Copy, Hash, Serialize, Deserialize)] +pub enum KeyCode { + /// Backspace key (Delete on macOS, Backspace on other platforms). + Backspace, + /// Enter key. + Enter, + /// Left arrow key. + Left, + /// Right arrow key. + Right, + /// Up arrow key. + Up, + /// Down arrow key. + Down, + /// Home key. + Home, + /// End key. + End, + /// Page up key. + PageUp, + /// Page down key. + PageDown, + /// Tab key. + Tab, + /// Shift + Tab key. + BackTab, + /// Delete key. (Fn+Delete on macOS, Delete on other platforms) + Delete, + /// Insert key. + Insert, + /// F key. + /// + /// `KeyCode::F(1)` represents F1 key, etc. + F(u8), + /// A character. + /// + /// `KeyCode::Char('c')` represents `c` character, etc. + Char(char), + /// Null. + Null, + /// Escape key. + Esc, +} + +pub type Position = tortuise::input::Position; +pub type Direction = tortuise::input::Direction; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum Button { + // Action Pad + South = 1, + East = 2, + North = 4, + West = 5, + C = 3, + Z = 6, + // Triggers + LeftTrigger = 7, + LeftTrigger2 = 9, + RightTrigger = 8, + RightTrigger2 = 10, + // Menu Pad + Select = 11, + Start = 12, + Mode = 13, + // Sticks + LeftThumb = 14, + RightThumb = 15, + // D-Pad + DPadUp = 16, + DPadDown = 17, + DPadLeft = 18, + DPadRight = 19, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum Axis { + LeftStickX = 1, + LeftStickY = 2, + LeftZ = 3, + RightStickX = 4, + RightStickY = 5, + RightZ = 6, + DPadX = 7, + DPadY = 8, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum PowerInfo { + Wired, + Discharging(u8), + Charging(u8), + Charged, + Unknown, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Gamepad(usize); + +impl From