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); // } }