commit ac5ff9301b04d3e01e2174a892e11bdb686c2415 Author: Max Bradbury Date: Wed Sep 2 17:50:13 2020 +0100 wip diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7d11246 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +/Cargo.lock +/.idea/ diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..79ea96c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "game-engine" +version = "0.1.0" +authors = ["Max Bradbury "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +toml = "^0.5.6" +serde = "^1.0.114" +serde_derive = "^1.0.114" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..b518327 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,504 @@ +use std::collections::HashMap; +use std::fs; +use serde_derive::{Serialize, Deserialize}; + +#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct Colour { + red: u8, + green: u8, + blue: u8, + alpha: u8, +} + +impl Colour { + pub fn from(colours: Vec) -> Colour { + const ZERO: u8 = 0; + Colour { + red: *colours.get(0).unwrap_or(&ZERO), + green: *colours.get(1).unwrap_or(&ZERO), + blue: *colours.get(2).unwrap_or(&ZERO), + alpha: *colours.get(3).unwrap_or(&255), + } + } + + pub fn to_vec(&self) -> Vec { + vec![self.red, self.green, self.blue, self.alpha] + } +} + +#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct Palette { + name: String, + colours: Vec, +} + +impl Palette { + pub fn from(name: &str, toml: &str) -> Self { + let intermediate: IntermediatePalette = toml::from_str(toml).unwrap(); + + println!("palette name: {}", name); + + for colour in &intermediate.colours { + println!("palette colour: {}{}{}", colour[0], colour[1], colour[2]); + } + + Palette { + name: name.to_string(), + colours: intermediate.colours.iter().map(|vec| { + Colour::from(vec.clone()) + }).collect(), + } + } + + pub fn from_file(path: String) -> Self { + // todo get name without extension + let name = "blah"; + let toml = fs::read_to_string(path).unwrap(); + Self::from(name, &toml) + } +} + +/// for toml purposes +#[derive(Serialize, Deserialize)] +struct IntermediatePalette { + colours: Vec>, +} + +/// for toml purposes +#[derive(Serialize, Deserialize)] +struct IntermediatePalettes { + /// singular so each palette section is named "palette" instead of "palettes" in toml + palette: Vec, +} + +impl IntermediatePalettes { + pub fn from_dir() -> Self { + Self { + palette: vec![] + } + } +} + +#[derive(Eq, Hash, PartialEq)] +pub struct Position { + x: u8, + y: u8, +} + +#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct Image { + name: String, + /// colour indexes + pixels: Vec, +} + +impl Image { + fn from(intermediate: IntermediateImage) -> Image { + Image { + name: intermediate.name.to_owned(), + pixels: intermediate.pixels.split_whitespace().collect::().chars().map( + |char|char as u8 + ).collect() + } + } +} + +/// for toml purposes +#[derive(Debug, Serialize, Deserialize)] +struct IntermediateImages { + /// singular so each image is named "image" instead of "images" in toml + image: Vec, +} + +impl IntermediateImages { + fn to_images(&self) -> Vec { + self.image.iter().map(|intermediate| + Image::from(intermediate.clone()) + ).collect() + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +struct IntermediateImage { + name: String, + pixels: String, +} + +impl IntermediateImage { + // todo refactor + fn from(image: Image) -> IntermediateImage { + let mut string = "\n".to_string(); + + let sqrt = (image.pixels.len() as f64).sqrt() as usize; + for line in image.pixels.chunks(sqrt) { + for pixel in line { + string.push_str(&format!("{}", *pixel)); + } + string.push('\n'); + } + + IntermediateImage { + name: image.name.to_owned(), + /// todo wtf? I guess this crate doesn't handle multiline strings correctly + pixels: format!("\"\"{}\"\"", string), + } + } +} + +// #[derive(Serialize, Deserialize)] +// pub struct Thing { +// name: Option, +// /// image name +// image: String, +// } +// + +pub struct Room { + name: String, + width: u8, + height: u8, + /// thing names and their positions + background: HashMap, + foreground: HashMap, +} + +#[derive(Serialize, Deserialize)] +struct IntermediateRoom { + name: String, + background: Vec, + foreground: Vec, +} + +impl IntermediateRoom { + fn from(room: Room) -> IntermediateRoom { + fn hashmap_to_vec(hash: HashMap, width: u8, height: u8) -> Vec { + let mut thing_ids = Vec::new(); + + while thing_ids.len() < (width * height) as usize { + thing_ids.push(String::new()); + } + + thing_ids + } + + IntermediateRoom { + name: "".to_string(), + background: vec![], + foreground: vec![] + } + } +} + +// #[derive(Serialize, Deserialize)] +// pub enum DataType { +// Image, +// Integer, +// Palette, +// Room, +// Script, +// String, +// Thing, +// Variable, +// } +// +// /// todo refactor, this is stupid +// #[derive(Serialize, Deserialize)] +// pub struct Value { +// data_type: DataType, +// image: Option, +// integer: Option, +// palette: Option, +// room: Option, +// script: Option, +// string: Option, +// thing: Option, +// variable: Option, +// } +// +// #[derive(Serialize, Deserialize)] +// pub struct Variable { +// name: String, +// data_type: DataType, +// default: Value, +// } +// +// #[derive(Serialize, Deserialize)] +// pub struct Parameter { +// name: String, +// data_type: DataType, +// default: Value, +// } +// +// #[derive(Serialize, Deserialize)] +// pub struct Script { +// name: Option, +// params: Vec, +// script: String, +// } +// +// #[derive(Serialize, Deserialize)] +// pub struct ScriptInstance { +// script: String, +// params: HashMap, +// } +// +// #[derive(Serialize, Deserialize)] +// pub struct ScriptCollection { +// scripts: Vec, +// /// as well as many named scripts, a trigger can have one anonymous script +// anonymous: Option, +// } +// +// #[derive(Serialize, Deserialize)] +// pub struct Version { +// major: u8, +// minor: u8, +// } +// +// impl Version { +// pub fn default() -> Version { +// Version { major: 0, minor: 1 } +// } +// } + +#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct Config { + /// used in the window title bar + name: Option, + width: u8, + height: u8, + /// animation rate in milliseconds + tick: u64, + /// if this is not specified, the game will pick the first room it finds + starting_room: Option, +} + +#[derive(Serialize, Deserialize)] +pub struct Game { + config: Config, +// palettes: Vec, +// variables: Vec, +// triggers: HashMap, +} +// +// #[derive(Debug)] +// pub struct GameParseError; +// +// impl Game { +// pub fn from(s: &str) -> Result { +// let result = toml::from_str(s); +// if result.is_ok() { +// Ok(result.unwrap()) +// } else { +// Err(GameParseError) +// } +// } +// } +// + +mod mock { + pub(crate) mod image { + use crate::Image; + + pub fn bg() -> Image { + Image { + name: "bg".to_string(), + pixels: vec![ + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + ] + } + } + + pub fn block() -> Image { + Image { + name: "block".to_string(), + pixels: vec![ + 1,1,1,1,1,1,1,1, + 1,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,1, + 1,0,0,1,1,0,0,1, + 1,0,0,1,1,0,0,1, + 1,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,1, + 1,1,1,1,1,1,1,1, + ] + } + } + + pub fn avatar() -> Image { + Image { + name: "avatar".to_string(), + pixels: vec![ + 0,0,0,2,2,0,0,0, + 0,0,0,2,2,0,0,0, + 0,0,0,2,2,0,0,0, + 0,0,2,2,2,2,0,0, + 0,2,2,2,2,2,2,0, + 2,0,2,2,2,2,0,2, + 0,0,2,0,0,2,0,0, + 0,0,2,0,0,2,0,0, + ] + } + } + + pub fn cat() -> Image { + Image { + name: "cat".to_string(), + pixels: vec![ + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,2,0,2,0,0,0,2, + 0,2,2,2,0,0,0,2, + 0,2,2,2,0,0,2,0, + 0,2,2,2,2,2,0,0, + 0,0,2,2,2,2,0,0, + 0,0,2,0,0,2,0,0, + ] + } + } + } + + pub(crate) mod palette { + use crate::{Palette, Colour, IntermediatePalette}; + + pub(crate) fn intermediate() -> IntermediatePalette { + IntermediatePalette { + colours: vec![ + vec![0,0,0,0], + vec![0,81,104,255], + vec![118,159,155,255], + vec![155,155,155,255], + ] + } + } + + pub(crate) fn default() -> Palette { + Palette { + name: "blueprint".to_string(), + colours: vec![ + Colour { red: 0, green: 0, blue: 0, alpha: 0 }, + Colour { red: 0, green: 81, blue: 104, alpha: 255 }, + Colour { red: 118, green: 159, blue: 155, alpha: 255 }, + Colour { red: 155, green: 155, blue: 155, alpha: 255 }, + ], + } + } + } +} + +#[cfg(test)] +mod test { + use crate::{Game, Config, Palette, Colour, Image, IntermediatePalettes, IntermediateImage, IntermediateImages}; + + #[test] + fn test_config_from_toml() { + let output: Config = toml::from_str(include_str!("test-resources/basic/game.toml")).unwrap(); + let expected = Config { + name: Some("Write your game's title here".to_string()), + width: 16, + height: 9, + tick: 400, + starting_room: Some("example room".to_string()) + }; + assert_eq!(output, expected); + } + + #[test] + fn test_config_to_toml() { + let output = toml::to_string(&Config { + name: Some("Write your game's title here".to_string()), + width: 16, + height: 9, + tick: 400, + starting_room: Some("example room".to_string()) + }).unwrap(); + let expected = include_str!("test-resources/basic/game.toml"); + assert_eq!(&output, expected); + } + + #[test] + fn test_palette_from_toml() { + let output = Palette::from( + "blueprint", + include_str!("test-resources/basic/palettes/blueprint.toml") + ); + let expected = crate::mock::palette::default(); + assert_eq!(output, expected); + } + + #[test] + fn test_palette_to_toml() { + let intermediate = crate::mock::palette::intermediate(); + let output = toml::to_string(&intermediate).unwrap(); + let expected = include_str!("test-resources/basic/palettes/blueprint.toml"); + assert_eq!(&output, expected); + } + + // #[test] + // fn test_image_from_toml() { + // let str = include_str!("test-resources/basic/images.toml"); + // let output: Image = toml::from_str(str).unwrap(); + // let expected = crate::mock::image::avatar(); + // assert_eq!(output, expected); + // } + + #[test] + fn test_colour_from_intermediate() { + let output = Colour::from(vec![64, 128, 192, 255]); + let expected = Colour { red: 64, green: 128, blue: 192, alpha: 255 }; + assert_eq!(output, expected); + } + + #[test] + fn test_colour_to_intermediate() { + let output = Colour { red: 64, green: 128, blue: 192, alpha: 255 }.to_vec(); + let expected = vec![64, 128, 192, 255]; + assert_eq!(output, expected); + } + + // #[test] + // fn test_images_from_intermediate() { + // let str = include_str!("test-resources/basic/images.toml"); + // let output: Vec = toml::from_str(str).unwrap(); + // print!("{}", output.len()); + // } + + // #[test] + // fn test_images_to_toml() { + // let images = IntermediateImages { + // image: vec![ + // IntermediateImage::from(crate::mock::image::bg()), + // IntermediateImage::from(crate::mock::image::block()), + // IntermediateImage::from(crate::mock::image::avatar()), + // IntermediateImage::from(crate::mock::image::cat()), + // ] + // }; + // + // let output = toml::to_string(&images).unwrap(); + // let expected = include_str!("test-resources/basic/images.toml"); + // + // // I think this is failing because one has escaped quotation marks and one has normal ones(??) + // assert_eq!(output, expected); + // } + + // #[test] + // fn test_images_from_toml() { + // let str = include_str!("test-resources/basic/images.toml"); + // let output: IntermediateImages = toml::from_str(str).unwrap(); + // let output = output.to_images(); + // let expected = vec![ + // crate::mock::image::bg(), + // crate::mock::image::block(), + // crate::mock::image::avatar(), + // crate::mock::image::cat(), + // ]; + // assert_eq!(output, expected); + // } +} diff --git a/src/test-resources/basic/basic.toml b/src/test-resources/basic/basic.toml new file mode 100644 index 0000000..a1fb4ec --- /dev/null +++ b/src/test-resources/basic/basic.toml @@ -0,0 +1,68 @@ +[image] +name = "bg" +pixels = [ + 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1, +] + +[image] +name = "block" +pixels = [ + 2,2,2,2,2,2,2,2, + 2,1,1,1,1,1,1,2, + 2,1,1,1,1,1,1,2, + 2,1,1,2,2,1,1,2, + 2,1,1,2,2,1,1,2, + 2,1,1,1,1,1,1,2, + 2,1,1,1,1,1,1,2, + 2,2,2,2,2,2,2,2, +] + +[image] +name = "avatar" +pixels = [ + 0,0,0,3,3,0,0,0, + 0,0,0,3,3,0,0,0, + 0,0,0,3,3,0,0,0, + 0,0,3,3,3,3,0,0, + 0,3,3,3,3,3,3,0, + 3,0,3,3,3,3,0,3, + 0,0,3,0,0,3,0,0, + 0,0,3,0,0,3,0,0, +] + +[image] +name = "cat" +pixels = [ + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,3,0,3,0,0,0,3, + 0,3,3,3,0,0,0,3, + 0,3,3,3,0,0,3,0, + 0,3,3,3,3,3,0,0, + 0,0,3,3,3,3,0,0, + 0,0,3,0,0,3,0,0, +] + +[thing] +name = "avatar" +image = "avatar" +input = ["movement-wasd", "movement-arrow-keys"] + +[thing] +name = "cat" +image = "cat" +bumped = """ +say "I'm a cat" +""" + +[variable] +name = "a" +type = "int" +default = 42 diff --git a/src/test-resources/basic/game.toml b/src/test-resources/basic/game.toml new file mode 100644 index 0000000..d269e4f --- /dev/null +++ b/src/test-resources/basic/game.toml @@ -0,0 +1,5 @@ +name = "Write your game's title here" +width = 16 +height = 9 +tick = 400 +starting_room = "example room" diff --git a/src/test-resources/basic/images/avatar.toml b/src/test-resources/basic/images/avatar.toml new file mode 100644 index 0000000..7fb0a86 --- /dev/null +++ b/src/test-resources/basic/images/avatar.toml @@ -0,0 +1,10 @@ +pixels = """ +00022000 +00022000 +00022000 +00222200 +02222220 +20222202 +00200200 +00200200 +""" diff --git a/src/test-resources/basic/images/bg.toml b/src/test-resources/basic/images/bg.toml new file mode 100644 index 0000000..2c87bcd --- /dev/null +++ b/src/test-resources/basic/images/bg.toml @@ -0,0 +1,10 @@ +pixels = """ +00000000 +00000000 +00000000 +00000000 +00000000 +00000000 +00000000 +00000000 +""" diff --git a/src/test-resources/basic/images/block.toml b/src/test-resources/basic/images/block.toml new file mode 100644 index 0000000..e75fcbd --- /dev/null +++ b/src/test-resources/basic/images/block.toml @@ -0,0 +1,10 @@ +pixels = """ +11111111 +10000001 +10000001 +10011001 +10011001 +10000001 +10000001 +11111111 +""" diff --git a/src/test-resources/basic/images/cat.toml b/src/test-resources/basic/images/cat.toml new file mode 100644 index 0000000..86b80a2 --- /dev/null +++ b/src/test-resources/basic/images/cat.toml @@ -0,0 +1,10 @@ +pixels = """ +00000000 +00000000 +02020002 +02220002 +02220020 +02222200 +00222200 +00200200 +""" diff --git a/src/test-resources/basic/palettes/blueprint.toml b/src/test-resources/basic/palettes/blueprint.toml new file mode 100644 index 0000000..669d382 --- /dev/null +++ b/src/test-resources/basic/palettes/blueprint.toml @@ -0,0 +1 @@ +colours = [[0, 0, 0, 0], [0, 81, 104, 255], [118, 159, 155, 255], [155, 155, 155, 255]] diff --git a/src/test-resources/basic/rooms/example room.toml b/src/test-resources/basic/rooms/example room.toml new file mode 100644 index 0000000..544f58f --- /dev/null +++ b/src/test-resources/basic/rooms/example room.toml @@ -0,0 +1,37 @@ +background = [ + "bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg", + "bg","block","block","block","block","block","block","block","block","block","block","block","block","block","block","bg", + "bg","block","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","block","bg", + "bg","block","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","block","bg", + "bg","block","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","block","bg", + "bg","block","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","block","bg", + "bg","block","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","block","bg", + "bg","block","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","block","bg", + "bg","block","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","block","bg", + "bg","block","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","block","bg", + "bg","block","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","block","bg", + "bg","block","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","block","bg", + "bg","block","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","block","bg", + "bg","block","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","block","bg", + "bg","block","block","block","block","block","block","block","block","block","block","block","block","block","block","bg", + "bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg","bg", +] +foreground = [ + "","","","","","","","","","","","","","","","", + "","","","","","","","","","","","","","","","", + "","","","","","","","","","","","","","","","", + "","","","","","","","","","","","","","","","", + "","","","","avatar","","","","","","","","","","","", + "","","","","","","","","","","","","","","","", + "","","","","","","","","","","","","","","","", + "","","","","","","","","","","","","","","","", + "","","","","","","","","","","","","","","","", + "","","","","","","","","","","","","","","","", + "","","","","","","","","","","","","","","","", + "","","","","","","","","","","","","","","","", + "","","","","","","","","cat","","","","","","","", + "","","","","","","","","","","","","","","","", + "","","","","","","","","","","","","","","","", + "","","","","","","","","","","","","","","","", +] +palette = "blueprint"