diff --git a/Cargo.lock b/Cargo.lock index fc945a2..3918d8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,7 @@ dependencies = [ "libm", "linked_list_allocator", "log", + "num-traits", "pc-keyboard", "pic8259", "rand", diff --git a/Cargo.toml b/Cargo.toml index ac1e823..7fcaa17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ log = "0.4.20" uchan = { version = "0.1.4", default-features = false } embedded-time = "0.12.1" vga = "0.2.9" +num-traits = { version = "0.2.16", default-features = false } [dependencies.lazy_static] version = "1.0" diff --git a/hardware.sh b/hardware.sh index c5979ea..0e70d3c 100755 --- a/hardware.sh +++ b/hardware.sh @@ -1,2 +1,2 @@ cargo bootimage --release -sudo dd if=target/x86_64-CrystalOS/release/bootimage-CrystalOS.bin of=/dev/sdc +sudo dd if=target/x86_64-CrystalOS/release/bootimage-CrystalOS.bin of=/dev/sdb diff --git a/src/user/bin/asteroids/game.rs b/src/user/bin/asteroids/game.rs index 8f73e16..6c8cb86 100644 --- a/src/user/bin/asteroids/game.rs +++ b/src/user/bin/asteroids/game.rs @@ -1,23 +1,159 @@ use crate::system::std::application::Application; use async_trait::async_trait; use alloc::boxed::Box; -use alloc::string::String; +use alloc::format; +use alloc::string::{String, ToString}; use alloc::vec::Vec; +use core::any::Any; +use crate::{serial_println, std}; use crate::std::application::Error; +use crate::std::application::Error::ApplicationError; +use crate::std::frame::{Position, Dimensions, RenderError, Frame, ColouredChar}; +use crate::std::io::{Color, ColorCode, KeyStroke, Screen, Stdin}; +use crate::std::random::Random; +use crate::user::lib::libgui::cg_core::{CgComponent, Widget}; +use crate::user::lib::libgui::cg_widgets::{CgContainer, CgIndicatorWidget}; +#[derive(Clone)] pub struct Player { pub health: u32, - pub score: u32 + pub position: Position, } impl Player { pub fn new() -> Player { Player { health: 5, - score: 0 + position: Position::new(10, 12) } } } +#[derive(Clone)] pub struct Game { - pub player: Player + pub player: Player, + pub enemies: Vec, + pub score: u32, } + +#[async_trait] +impl Application for Game { + fn new() -> Self { + Self { + player: Player::new(), + enemies: Vec::new(), + score: 0 + } + } + async fn run(&mut self, args: Vec) -> Result<(), Error> { + let mut spawn_timer: i32 = 0; + + Screen::Application.set_mode().unwrap(); + + let mut container_data = CgContainer::new( + Position::new(0, 0), + Dimensions::new(80, 25), + true, + ); + + let self_ref = Widget::insert(self.clone()); + container_data.insert("app", self_ref); + let self_ref = container_data.fetch("app").unwrap(); + + loop { + std::time::wait(0.1); + + spawn_timer += 1; + + if spawn_timer >= 8 { + spawn_timer = 0; + self.enemies.push(Enemy::new(Random::int(1, 21))); + } + + self.enemies.retain(|e| {if e.position.x <= 0 {self.score += 1; false } else { true }}); + self.enemies.iter_mut().for_each(|e| e.position.x -= 1); + self.enemies.iter().for_each(|e| { if e.position.x == self.player.position.x && e.position.y == self.player.position.y { self.player.health -= 1; } }); + + if self.player.health == 0 { + break; + } + + if let Some(input_key) = Stdin::try_keystroke() { + match input_key { + KeyStroke::Char('q') => { + break; + } + KeyStroke::Char('w') => self.player.position.y -= 1, + KeyStroke::Char('s') => self.player.position.y += 1, + KeyStroke::Char('a') => self.player.position.x -= 1, + KeyStroke::Char('d') => self.player.position.x += 1, + _ => (), + } + } + self_ref.update(self.clone()); + if let Ok(frame) = container_data.render() { + frame.write_to_screen().unwrap(); + } + } + + let mut frame = Frame::new(Dimensions::new(0, 0), Dimensions::new(80, 25)).map_err(|_| ApplicationError("idk".to_string()))?; + let msg = format!("your score was: {}", self.score); + msg.chars().enumerate().for_each(|(i, c)| { + serial_println!("{}", (80 - msg.len()) / 2 + i); + frame[12][(80 - msg.len()) / 2 + i] = ColouredChar { + character: c, + colour: ColorCode::new(Color::Cyan, Color::Black), + } + }); + frame.write_to_screen().unwrap(); + + while let KeyStroke::Char(c) = Stdin::keystroke().await { + if c == 'q' { + break; + } + } + + Screen::Terminal.set_mode().unwrap(); + Ok(()) + } +} + +impl CgComponent for Game { + fn render(&self) -> Result { + let mut frame = Frame::new(Dimensions::new(1, 1), Dimensions::new(78, 23))?; + + let pos = self.player.position; + + frame[pos.y][pos.x] = ColouredChar { + character: '@', + colour: ColorCode::new(Color::Cyan, Color::Black), + }; + + for i in self.enemies.iter().map(|enemy| enemy.position).collect::>() { + frame[i.y][i.x] = ColouredChar { + character: '*', + colour: ColorCode::new(Color::Red, Color::Black), + } + } + + Ok(frame) + } + + fn as_any(&self) -> &dyn Any { + self + } +} + + +#[derive(Clone)] +pub struct Enemy { + pub position: Position +} + +impl Enemy { + pub fn new(y: usize) -> Enemy { + Enemy { + position: Position::new(75, y) + } + } +} + diff --git a/src/user/bin/asteroids/mod.rs b/src/user/bin/asteroids/mod.rs index 9f9ab08..1fc950e 100644 --- a/src/user/bin/asteroids/mod.rs +++ b/src/user/bin/asteroids/mod.rs @@ -1,2 +1,2 @@ mod render; -mod game; \ No newline at end of file +pub(crate) mod game; \ No newline at end of file diff --git a/src/user/bin/grapher.rs b/src/user/bin/grapher.rs index 6c63454..1912a76 100644 --- a/src/user/bin/grapher.rs +++ b/src/user/bin/grapher.rs @@ -2,6 +2,7 @@ use alloc::string::{String, ToString}; use alloc::{format, vec}; use alloc::vec::Vec; use alloc::boxed::Box; +use alloc::fmt::format; use alloc::sync::Arc; use core::any::Any; use async_trait::async_trait; @@ -25,6 +26,9 @@ use super::calc; const OFFSET_X: i64 = 39; const OFFSET_Y: i64 = 10; +use core::f64::consts::E; +use core::f64::consts::PI; + #[derive(Clone)] pub struct Grapher { points: Vec, @@ -155,7 +159,12 @@ impl Grapher { let x = x as f64 / 100.0; let new_eq = equation.chars().map(|c| { - if c == 'x' { format!("({})", x) } else { c.to_string() } + match c { + 'x' => format!("({})", x), + 'e' => format!("({})", E), + 'π' => format!("({})", PI), + _ => c.to_string(), + } }).collect::(); let fx = cal.calculate(new_eq); diff --git a/src/user/bin/shell.rs b/src/user/bin/shell.rs index 46577ec..968c997 100644 --- a/src/user/bin/shell.rs +++ b/src/user/bin/shell.rs @@ -125,6 +125,10 @@ async fn exec() -> Result<(), Error> { let mut game = snake::Game::new(); game.run(args).await?; } + "asteroids" => { + let mut asteroid_game = asteroids::game::Game::new(); + asteroid_game.run(args).await?; + } "serial" => { let c = Serial::reply_char('e'); println!("{}", c); @@ -184,11 +188,8 @@ async fn exec() -> Result<(), Error> { } "test_features" => { Screen::Application.set_mode().unwrap(); - setup_ui().await; - - Screen::Terminal.set_mode().unwrap() - + Screen::Terminal.set_mode().unwrap(); } _ => { return Err(Error::UnknownCommand( @@ -315,31 +316,6 @@ async fn setup_ui() { } -async fn test_new_datastore() { - let container = Widget::insert(CgContainer::new( - Position::new(0, 0), - Dimensions::new(80, 25), - true, - )); - - let textbox = Widget::insert(CgTextBox::new( - String::from("test textbox"), - String::from("dam"), - Position::new(2, 5), - Dimensions::new(40, 12), - true, - )); - - let mut c = textbox.fetch::().unwrap(); - c.content = String::from("dam2"); - textbox.update(c); - - let c = textbox.fetch::().unwrap(); - - serial_println!("{}", c.content); -} - - diff --git a/src/user/lib/libgui/cg_core.rs b/src/user/lib/libgui/cg_core.rs index a2eed7a..228adf7 100644 --- a/src/user/lib/libgui/cg_core.rs +++ b/src/user/lib/libgui/cg_core.rs @@ -96,7 +96,8 @@ impl Widget { impl Drop for Widget { fn drop(&mut self) { - GUITREE.lock().remove(self); + let removed = GUITREE.lock().remove(self); + drop(removed); } } @@ -136,9 +137,9 @@ impl DataStore { items.get(&id.id).cloned() } - fn remove(&self, id: &Widget) { + fn remove(&self, id: &Widget) -> Option>> { let mut items = self.items.lock(); - items.remove(&id.id); + items.remove(&id.id) } } diff --git a/src/user/lib/libgui/cg_inputs.rs b/src/user/lib/libgui/cg_inputs.rs index 5555da2..2f85ac7 100644 --- a/src/user/lib/libgui/cg_inputs.rs +++ b/src/user/lib/libgui/cg_inputs.rs @@ -93,31 +93,61 @@ impl CgTextEdit for CgLineEdit { #[async_trait] impl CgTextInput for CgLineEdit { async fn input(&mut self, break_condition: fn(KeyStroke) -> (KeyStroke, Exit), id: &Widget, app: &Widget) -> Result<(String, bool), RenderError> { - while let (c, Exit::None) = break_condition(Stdin::keystroke().await) { - match c { - KeyStroke::Char('\n') => break, - KeyStroke::Char('\x08') => self.backspace(), - KeyStroke::Backspace => self.backspace(), - KeyStroke::Char(c) => self.write_char(c), - KeyStroke::Left => self.move_cursor(false), - KeyStroke::Right => self.move_cursor(true), + loop { + match break_condition(Stdin::keystroke().await) { + (KeyStroke::Char('\n'), Exit::None) => { + let res = self.text.iter().collect(); + self.clear(); + id.update(self.clone()); + match app.render() { + Ok(frame) => frame.write_to_screen()?, + Err(e) => return Err(e), + } + return Ok((res, false)) + }, + (c, Exit::None) => { + match c { + KeyStroke::Char('\x08') => self.backspace(), + KeyStroke::Backspace => self.backspace(), + KeyStroke::Char(c) => self.write_char(c), + KeyStroke::Left => self.move_cursor(false), + KeyStroke::Right => self.move_cursor(true), + _ => (), + } + + id.update(self.clone()); + match app.render() { + Ok(frame) => frame.write_to_screen()?, + Err(e) => return Err(e), + } + }, + (_, Exit::Exit) => { + return Ok((String::new(), true)) + }, _ => (), } - - id.update(self.clone()); - match app.render() { - Ok(frame) => frame.write_to_screen()?, - Err(e) => return Err(e), - } - }; - let res = self.text.iter().collect(); - self.clear(); - id.update(self.clone()); - match app.render() { - Ok(frame) => frame.write_to_screen()?, - Err(e) => return Err(e), } - Ok((res, false)) + } +} + +#[derive(Debug, Clone)] +pub struct CgBoxEdit { + pub position: Position, + pub dimensions: Dimensions, + pub prompt: String, + pub text: Vec, + pub ptr: Position, +} + +impl CgBoxEdit { + pub fn new(position: Position, dimensions: Dimensions, prompt: String) -> CgBoxEdit { + CgBoxEdit { + position, + dimensions, + prompt, + text: Vec::new(), + ptr: Position::new(0, 0) + } } } @@ -137,5 +167,3 @@ impl CgTextInput for CgLineEdit { - - diff --git a/src/user/lib/libgui/cg_widgets.rs b/src/user/lib/libgui/cg_widgets.rs index a4495dd..871fbe8 100644 --- a/src/user/lib/libgui/cg_widgets.rs +++ b/src/user/lib/libgui/cg_widgets.rs @@ -58,12 +58,7 @@ impl CgOutline for CgContainer { impl CgComponent for CgContainer { fn render(&self) -> Result { - serial_println!("rendering"); - let mut result = Frame::new(self.position, self.dimensions)?; - - serial_println!("{:?}", self.elements); - for widget in &self.elements { let frame = widget.1.render()?; match result.render_bounds_check(&frame, true) { // TODO: this needs to be set to false for production