2020-04-05 22:02:03 +00:00
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
2020-04-05 17:58:04 +00:00
|
|
|
struct Colour {
|
|
|
|
red: u8,
|
|
|
|
green: u8,
|
|
|
|
blue: u8,
|
|
|
|
}
|
|
|
|
|
2020-04-05 22:02:03 +00:00
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
2020-04-05 17:58:04 +00:00
|
|
|
struct Palette {
|
|
|
|
id: String, // base36 string (why??)
|
|
|
|
name: Option<String>,
|
|
|
|
colours: Vec<Colour>,
|
|
|
|
}
|
|
|
|
|
2020-04-05 22:02:03 +00:00
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
2020-04-05 17:58:04 +00:00
|
|
|
struct Image {
|
2020-04-05 21:28:23 +00:00
|
|
|
pixels: Vec<u8>, // 64 for SD, 256 for HD
|
2020-04-05 17:58:04 +00:00
|
|
|
}
|
|
|
|
|
2020-04-05 22:02:03 +00:00
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
2020-04-05 17:58:04 +00:00
|
|
|
struct Tile {
|
|
|
|
id: String, // base36 string
|
|
|
|
name: Option<String>,
|
|
|
|
wall: bool,
|
|
|
|
animation_frames: Vec<Image>,
|
|
|
|
}
|
|
|
|
|
2020-04-10 15:27:23 +00:00
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
2020-04-05 19:46:55 +00:00
|
|
|
struct Position {
|
|
|
|
x: u8,
|
|
|
|
y: u8,
|
|
|
|
}
|
|
|
|
|
2020-04-10 15:27:23 +00:00
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
|
|
|
struct Instance {
|
|
|
|
position: Position,
|
|
|
|
id: String, // item / ending id
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
|
|
|
struct ExitInstance {
|
|
|
|
position: Position,
|
|
|
|
exit: Exit,
|
|
|
|
}
|
|
|
|
|
2020-04-05 22:02:03 +00:00
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
2020-04-05 19:46:55 +00:00
|
|
|
struct Dialogue {
|
|
|
|
id: String,
|
|
|
|
contents: String,
|
|
|
|
}
|
|
|
|
|
2020-04-05 22:02:03 +00:00
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
2020-04-05 19:46:55 +00:00
|
|
|
struct Sprite {
|
|
|
|
id: String, // lowercase base36
|
|
|
|
name: Option<String>,
|
|
|
|
animation_frames: Vec<Image>,
|
2020-04-05 22:58:10 +00:00
|
|
|
dialogue: Option<String>, /// dialogue id
|
2020-04-06 11:27:29 +00:00
|
|
|
room: String, /// room id
|
2020-04-05 19:46:55 +00:00
|
|
|
position: Position,
|
|
|
|
}
|
|
|
|
|
2020-04-06 07:54:43 +00:00
|
|
|
/// avatar is a "sprite" in the game data but with a specific id
|
2020-04-05 22:02:03 +00:00
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
2020-04-05 19:46:55 +00:00
|
|
|
struct Avatar {
|
|
|
|
animation_frames: Vec<Image>,
|
2020-04-06 11:27:29 +00:00
|
|
|
room: String, /// room id
|
2020-04-05 19:46:55 +00:00
|
|
|
position: Position,
|
|
|
|
}
|
|
|
|
|
2020-04-05 22:02:03 +00:00
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
2020-04-05 19:46:55 +00:00
|
|
|
struct Item {
|
|
|
|
id: String,
|
|
|
|
animation_frames: Vec<Image>,
|
|
|
|
name: Option<String>,
|
2020-04-06 07:54:43 +00:00
|
|
|
dialogue: Option<String>, // dialogue id
|
2020-04-05 19:46:55 +00:00
|
|
|
}
|
|
|
|
|
2020-04-05 22:02:03 +00:00
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
2020-04-05 20:02:45 +00:00
|
|
|
struct Exit {
|
|
|
|
/// destination
|
|
|
|
room: String, /// id
|
|
|
|
position: Position,
|
|
|
|
}
|
|
|
|
|
|
|
|
// same as a dialogue basically
|
2020-04-05 22:02:03 +00:00
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
2020-04-05 20:02:45 +00:00
|
|
|
struct Ending {
|
2020-04-06 13:11:56 +00:00
|
|
|
id: String,
|
|
|
|
dialogue: String,
|
2020-04-05 20:02:45 +00:00
|
|
|
}
|
|
|
|
|
2020-04-05 22:02:03 +00:00
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
2020-04-05 20:02:45 +00:00
|
|
|
struct Room {
|
|
|
|
id: String,
|
2020-04-06 19:34:35 +00:00
|
|
|
palette: String, // id
|
2020-04-05 20:02:45 +00:00
|
|
|
name: Option<String>,
|
2020-04-06 19:34:35 +00:00
|
|
|
tiles: Vec<String>, // tile ids
|
2020-04-10 15:27:23 +00:00
|
|
|
items: Vec<Instance>,
|
|
|
|
exits: Vec<ExitInstance>,
|
|
|
|
endings: Vec<Instance>,
|
2020-04-05 20:02:45 +00:00
|
|
|
}
|
|
|
|
|
2020-04-06 13:22:35 +00:00
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
|
|
|
struct Variable {
|
|
|
|
id: String,
|
2020-04-06 18:11:41 +00:00
|
|
|
initial_value: String,
|
2020-04-06 13:22:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
2020-04-05 17:58:04 +00:00
|
|
|
struct Game {
|
|
|
|
name: String,
|
|
|
|
version: f64,
|
2020-04-06 11:27:58 +00:00
|
|
|
room_format: u8,
|
2020-04-05 17:58:04 +00:00
|
|
|
palettes: Vec<Palette>,
|
2020-04-11 21:40:26 +00:00
|
|
|
rooms: Vec<Room>,
|
|
|
|
tiles: Vec<Tile>,
|
|
|
|
avatar: Avatar,
|
|
|
|
sprites: Vec<Sprite>,
|
|
|
|
items: Vec<Item>,
|
|
|
|
dialogues: Vec<Dialogue>,
|
|
|
|
endings: Vec<Ending>,
|
2020-04-06 13:22:35 +00:00
|
|
|
variables: Vec<Variable>,
|
2020-04-05 17:58:04 +00:00
|
|
|
}
|
|
|
|
|
2020-04-11 22:04:56 +00:00
|
|
|
fn example_image_chequers_1() -> Image {
|
2020-04-06 11:26:26 +00:00
|
|
|
Image {
|
|
|
|
pixels: vec![
|
|
|
|
1,0,1,0,1,0,1,0,
|
|
|
|
0,1,0,1,0,1,0,1,
|
|
|
|
1,0,1,0,1,0,1,0,
|
|
|
|
0,1,0,1,0,1,0,1,
|
|
|
|
1,0,1,0,1,0,1,0,
|
|
|
|
0,1,0,1,0,1,0,1,
|
|
|
|
1,0,1,0,1,0,1,0,
|
|
|
|
0,1,0,1,0,1,0,1,
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-11 22:04:56 +00:00
|
|
|
fn example_image_chequers_2() -> Image {
|
2020-04-06 11:26:26 +00:00
|
|
|
Image {
|
|
|
|
pixels: vec![
|
|
|
|
0,1,0,1,0,1,0,1,
|
|
|
|
1,0,1,0,1,0,1,0,
|
|
|
|
0,1,0,1,0,1,0,1,
|
|
|
|
1,0,1,0,1,0,1,0,
|
|
|
|
0,1,0,1,0,1,0,1,
|
|
|
|
1,0,1,0,1,0,1,0,
|
|
|
|
0,1,0,1,0,1,0,1,
|
|
|
|
1,0,1,0,1,0,1,0,
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-11 22:04:56 +00:00
|
|
|
fn example_avatar() -> Avatar {
|
2020-04-11 17:17:29 +00:00
|
|
|
Avatar {
|
|
|
|
animation_frames: vec![
|
|
|
|
Image {
|
|
|
|
pixels: vec![
|
|
|
|
0,0,0,0,0,0,0,0,
|
|
|
|
0,0,1,1,1,1,0,0,
|
|
|
|
0,1,1,1,1,1,1,0,
|
|
|
|
1,1,1,0,1,1,1,0,
|
|
|
|
1,0,0,1,1,0,0,1,
|
|
|
|
1,1,1,1,1,1,1,1,
|
|
|
|
1,1,1,1,1,1,1,1,
|
|
|
|
0,1,1,1,1,1,1,0,
|
|
|
|
]
|
|
|
|
},
|
|
|
|
Image {
|
|
|
|
pixels: vec![
|
|
|
|
0,0,0,0,0,0,0,0,
|
|
|
|
0,0,0,0,0,0,0,0,
|
|
|
|
0,0,1,1,1,1,0,0,
|
|
|
|
0,1,1,1,1,1,1,0,
|
|
|
|
1,1,1,0,1,1,1,0,
|
|
|
|
1,0,0,1,1,0,0,1,
|
|
|
|
1,1,1,1,1,1,1,1,
|
|
|
|
0,1,1,1,0,1,1,0,
|
|
|
|
]
|
|
|
|
},
|
|
|
|
],
|
|
|
|
room: "0".to_string(),
|
|
|
|
position: Position { x: 2, y: 5 }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-11 22:04:56 +00:00
|
|
|
fn example_sprite() -> Sprite {
|
2020-04-06 11:26:26 +00:00
|
|
|
Sprite {
|
|
|
|
id: "a".to_string(),
|
|
|
|
name: Some("hatch".to_string()),
|
|
|
|
animation_frames: vec![
|
|
|
|
Image {
|
|
|
|
pixels: vec![
|
|
|
|
0,0,0,0,0,0,0,0,
|
|
|
|
0,1,1,1,1,0,0,0,
|
|
|
|
0,1,0,0,1,0,0,0,
|
|
|
|
0,0,1,1,1,1,0,0,
|
|
|
|
0,0,1,1,1,1,0,0,
|
|
|
|
0,1,0,1,1,1,1,0,
|
|
|
|
0,1,0,1,1,1,1,0,
|
|
|
|
0,1,1,0,1,1,1,1,
|
|
|
|
]
|
|
|
|
}
|
|
|
|
],
|
|
|
|
dialogue: Some("SPR_0".to_string()),
|
|
|
|
room: "4".to_string(),
|
|
|
|
position: Position {
|
|
|
|
x: 9,
|
|
|
|
y: 7
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-11 22:04:56 +00:00
|
|
|
fn example_item() -> Item {
|
2020-04-06 11:26:26 +00:00
|
|
|
Item {
|
|
|
|
id: "6".to_string(),
|
|
|
|
animation_frames: vec![
|
|
|
|
Image {
|
|
|
|
pixels: vec![
|
|
|
|
0,1,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,1,0,0,
|
|
|
|
0,0,1,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,1,0,
|
|
|
|
]
|
|
|
|
}
|
|
|
|
],
|
|
|
|
name: Some("door".to_string()),
|
|
|
|
dialogue: Some("ITM_2".to_string())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-11 22:04:56 +00:00
|
|
|
fn example_room() -> Room {
|
2020-04-09 07:28:08 +00:00
|
|
|
Room {
|
|
|
|
id: "a".to_string(),
|
|
|
|
palette: "9".to_string(),
|
|
|
|
name: Some("cellar 7".to_string()),
|
|
|
|
tiles: vec![
|
|
|
|
"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"1l".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),
|
|
|
|
"y".to_string(),"x".to_string(),"0".to_string(),"0".to_string(),"1j".to_string(),"0".to_string(),"0".to_string(),"1j".to_string(),"1l".to_string(),"0".to_string(),"1j".to_string(),"0".to_string(),"0".to_string(),"1j".to_string(),"0".to_string(),"0".to_string(),
|
|
|
|
"y".to_string(),"y".to_string(),"x".to_string(),"k".to_string(),"k".to_string(),"1c".to_string(),"1x".to_string(),"1y".to_string(),"1m".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),
|
|
|
|
"y".to_string(),"y".to_string(),"y".to_string(),"x".to_string(),"k".to_string(),"s".to_string(),"s".to_string(),"s".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"1g".to_string(),"1f".to_string(),"k".to_string(),
|
|
|
|
"k".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"1i".to_string(),"1u".to_string(),"1u".to_string(),"1u".to_string(),"1v".to_string(),"11".to_string(),"19".to_string(),"1b".to_string(),"1a".to_string(),"1e".to_string(),"10".to_string(),"k".to_string(),
|
|
|
|
"k".to_string(),"z".to_string(),"z".to_string(),"11".to_string(),"12".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"10".to_string(),"17".to_string(),"z".to_string(),"18".to_string(),"1e".to_string(),"12".to_string(),"k".to_string(),
|
|
|
|
"k".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"1k".to_string(),"14".to_string(),"15".to_string(),"16".to_string(),"1h".to_string(),"z".to_string(),"k".to_string(),
|
|
|
|
"k".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"10".to_string(),"1d".to_string(),"1v".to_string(),"1r".to_string(),"1s".to_string(),"1r".to_string(),"1q".to_string(),"1z".to_string(),"k".to_string(),
|
|
|
|
"k".to_string(),"z".to_string(),"z".to_string(),"12".to_string(),"10".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"1i".to_string(),"1n".to_string(),"1o".to_string(),"1o".to_string(),"1o".to_string(),"1p".to_string(),"z".to_string(),"k".to_string(),
|
|
|
|
"k".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"10".to_string(),"z".to_string(),"z".to_string(),"k".to_string(),
|
|
|
|
"k".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"11".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"k".to_string(),
|
|
|
|
"k".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"z".to_string(),"12".to_string(),"z".to_string(),"z".to_string(),"10".to_string(),"12".to_string(),"k".to_string(),
|
|
|
|
"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),"k".to_string(),
|
|
|
|
"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),
|
|
|
|
"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),
|
|
|
|
"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string()
|
|
|
|
],
|
2020-04-10 15:27:23 +00:00
|
|
|
items: vec![
|
|
|
|
Instance {position: Position { x: 11, y: 5}, id: "d".to_string()},
|
|
|
|
Instance {position: Position { x: 8, y: 3}, id: "e".to_string()},
|
|
|
|
Instance {position: Position { x: 1, y: 0}, id: "5".to_string()},
|
|
|
|
Instance {position: Position { x: 2, y: 1}, id: "6".to_string()},
|
|
|
|
Instance {position: Position { x: 3, y: 2}, id: "6".to_string()},
|
|
|
|
],
|
|
|
|
exits: vec![
|
|
|
|
ExitInstance {
|
|
|
|
position: Position { x: 3, y: 3},
|
|
|
|
exit: Exit { room: "3".to_string(), position: Position { x: 10, y: 6}}
|
|
|
|
},
|
|
|
|
],
|
|
|
|
endings: vec![
|
|
|
|
Instance{position: Position { x: 8, y: 7 }, id: "undefined".to_string()},
|
|
|
|
],
|
2020-04-09 07:28:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-11 22:04:56 +00:00
|
|
|
fn example_room_string() -> String {
|
2020-04-11 15:07:53 +00:00
|
|
|
include_str!("../test/resources/room").to_string()
|
2020-04-09 07:28:08 +00:00
|
|
|
}
|
|
|
|
|
2020-04-05 17:58:04 +00:00
|
|
|
fn image_from_string(string: String) -> Image {
|
|
|
|
let string = string.replace("\n", "");
|
|
|
|
let pixels: Vec<&str> = string.split("").collect();
|
|
|
|
// the above seems to add an extra "" at the start and end of the vec, so strip them below
|
2020-04-05 21:28:23 +00:00
|
|
|
let pixels = &pixels[1..(pixels.len() - 1)];
|
|
|
|
let pixels: Vec<u8> = pixels.iter().map(|&pixel| { pixel.parse::<u8>().unwrap() }).collect();
|
|
|
|
|
2020-04-05 17:58:04 +00:00
|
|
|
Image { pixels }
|
|
|
|
}
|
|
|
|
|
2020-04-05 21:28:23 +00:00
|
|
|
#[test]
|
|
|
|
fn test_image_from_string() {
|
|
|
|
let output = image_from_string(
|
2020-04-11 15:07:53 +00:00
|
|
|
include_str!("../test/resources/image").to_string()
|
2020-04-05 21:28:23 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
let expected = Image {
|
|
|
|
pixels: vec![
|
|
|
|
1,1,1,1,1,1,1,1,
|
|
|
|
1,1,0,0,1,1,1,1,
|
|
|
|
1,0,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,
|
|
|
|
]
|
|
|
|
};
|
|
|
|
|
|
|
|
assert_eq!(output, expected)
|
|
|
|
}
|
|
|
|
|
2020-04-05 17:58:04 +00:00
|
|
|
fn image_to_string(image: Image) -> String {
|
|
|
|
let mut string = String::new();
|
|
|
|
|
2020-04-11 21:40:38 +00:00
|
|
|
let sqrt = (image.pixels.len() as f64).sqrt() as usize; // 8 for SD, 16 for HD
|
2020-04-11 17:24:22 +00:00
|
|
|
for line in image.pixels.chunks(sqrt) {
|
2020-04-05 17:58:04 +00:00
|
|
|
for pixel in line {
|
2020-04-06 07:36:52 +00:00
|
|
|
string.push_str(&format!("{}", *pixel));
|
2020-04-05 17:58:04 +00:00
|
|
|
}
|
|
|
|
string.push('\n');
|
|
|
|
}
|
|
|
|
|
|
|
|
string.pop(); // remove trailing newline
|
|
|
|
|
|
|
|
string
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_image_to_string() {
|
2020-04-11 22:04:56 +00:00
|
|
|
let output = image_to_string(example_image_chequers_1());
|
2020-04-11 15:07:53 +00:00
|
|
|
let expected = include_str!("../test/resources/image-chequers-1").to_string();
|
2020-04-05 17:58:04 +00:00
|
|
|
|
|
|
|
assert_eq!(output, expected);
|
|
|
|
}
|
|
|
|
|
2020-04-06 06:23:20 +00:00
|
|
|
fn animation_frames_to_string(animation_frames: Vec<Image>) -> String {
|
|
|
|
let mut string = String::new();
|
|
|
|
let last_frame = animation_frames.len() - 1;
|
|
|
|
|
|
|
|
for (i, frame) in animation_frames.into_iter().enumerate() {
|
|
|
|
string.push_str(&image_to_string(frame));
|
|
|
|
|
|
|
|
if i < last_frame {
|
|
|
|
string.push_str(&"\n>\n".to_string());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
string
|
|
|
|
}
|
|
|
|
|
2020-04-05 17:58:04 +00:00
|
|
|
fn tile_from_string(string: String) -> Tile {
|
2020-04-11 17:18:20 +00:00
|
|
|
let mut lines: Vec<&str> = string.lines().collect();
|
2020-04-05 17:58:04 +00:00
|
|
|
|
|
|
|
let id = lines[0].replace("TIL ", "");
|
|
|
|
|
|
|
|
let last_line = lines.pop().unwrap();
|
|
|
|
let wall = match last_line == "WAL true" {
|
|
|
|
true => true,
|
|
|
|
false => {
|
|
|
|
lines.push(last_line);
|
|
|
|
false
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let last_line = lines.pop().unwrap();
|
|
|
|
let name = match last_line.starts_with("NAME") {
|
|
|
|
true => Some(last_line.replace("NAME ", "").to_string()),
|
|
|
|
false => {
|
|
|
|
lines.push(last_line);
|
|
|
|
None
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let animation_frames = lines[1..].join("");
|
|
|
|
let animation_frames: Vec<&str> = animation_frames.split("\n>\n").collect();
|
|
|
|
let animation_frames: Vec<Image> = animation_frames.iter().map(|&frame| {
|
|
|
|
image_from_string(frame.to_string())
|
|
|
|
}).collect();
|
|
|
|
|
|
|
|
Tile {id, name, wall, animation_frames}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_tile_from_string() {
|
2020-04-11 15:07:53 +00:00
|
|
|
let output = tile_from_string(
|
|
|
|
include_str!("../test/resources/tile").to_string()
|
|
|
|
);
|
2020-04-05 17:58:04 +00:00
|
|
|
|
|
|
|
let expected = Tile {
|
|
|
|
id: "z".to_string(),
|
|
|
|
name: Some("concrete 1".to_string()),
|
|
|
|
wall: true,
|
|
|
|
animation_frames: vec![
|
|
|
|
Image {
|
2020-04-05 21:28:23 +00:00
|
|
|
pixels: vec![1; 64]
|
2020-04-05 17:58:04 +00:00
|
|
|
}
|
|
|
|
],
|
|
|
|
};
|
|
|
|
|
|
|
|
assert_eq!(output, expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn tile_to_string(tile: Tile) -> String {
|
|
|
|
format!(
|
|
|
|
"TIL {}\n{}{}{}",
|
|
|
|
tile.id,
|
2020-04-06 06:23:20 +00:00
|
|
|
animation_frames_to_string(tile.animation_frames),
|
2020-04-05 17:58:04 +00:00
|
|
|
if tile.name.is_some() {format!("\nNAME {}", tile.name.unwrap())} else {"".to_string()},
|
|
|
|
if tile.wall {"\nWAL true"} else {""}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-04-05 19:09:03 +00:00
|
|
|
#[test]
|
|
|
|
fn test_tile_to_string() {
|
|
|
|
let output = tile_to_string(Tile {
|
|
|
|
id: "7a".to_string(),
|
|
|
|
name: Some("chequers".to_string()),
|
|
|
|
wall: false,
|
|
|
|
animation_frames: vec![
|
2020-04-11 22:04:56 +00:00
|
|
|
example_image_chequers_1(),
|
|
|
|
example_image_chequers_2(),
|
2020-04-05 19:09:03 +00:00
|
|
|
]
|
|
|
|
});
|
|
|
|
|
2020-04-11 15:07:53 +00:00
|
|
|
let expected = include_str!("../test/resources/tile-chequers").to_string();
|
2020-04-05 19:09:03 +00:00
|
|
|
|
|
|
|
assert_eq!(output, expected);
|
|
|
|
}
|
2020-04-05 17:58:04 +00:00
|
|
|
|
|
|
|
fn colour_from_string(colour: String) -> Colour {
|
|
|
|
let values: Vec<&str> = colour.split(',').collect();
|
|
|
|
|
|
|
|
let red: u8 = values[0].parse().unwrap_or(0);
|
|
|
|
let green: u8 = values[1].parse().unwrap_or(0);
|
|
|
|
let blue: u8 = values[2].parse().unwrap_or(0);
|
|
|
|
|
|
|
|
Colour { red, green, blue }
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_colour_from_string() {
|
|
|
|
assert_eq!(
|
|
|
|
colour_from_string("0,255,0".to_string()),
|
|
|
|
Colour { red: 0, green: 255, blue: 0 }
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-04-05 21:28:35 +00:00
|
|
|
fn colour_to_string(colour: Colour) -> String {
|
|
|
|
format!("{},{},{}", colour.red, colour.green, colour.blue)
|
|
|
|
}
|
|
|
|
|
2020-04-05 17:58:04 +00:00
|
|
|
#[test]
|
|
|
|
fn test_colour_to_string() {
|
|
|
|
assert_eq!(
|
|
|
|
colour_to_string(Colour { red: 22, green: 33, blue: 44 }),
|
|
|
|
"22,33,44".to_string()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn palette_from_string(palette: String) -> Palette {
|
|
|
|
let lines: Vec<&str> = palette.split('\n').collect();
|
|
|
|
|
|
|
|
let id = lines[0].replace("PAL ", "");
|
|
|
|
|
|
|
|
let name = match lines[1].starts_with("NAME") {
|
|
|
|
true => Some(lines[1].replace("NAME ", "").to_string()),
|
|
|
|
false => None,
|
|
|
|
};
|
|
|
|
|
|
|
|
let colour_start_index = if name.is_some() {2} else {1};
|
|
|
|
|
|
|
|
let colours = lines[colour_start_index..].iter().map(|&line| {
|
|
|
|
colour_from_string(line.to_string())
|
|
|
|
}).collect();
|
|
|
|
|
|
|
|
Palette { id, name, colours }
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_palette_from_string() {
|
|
|
|
let output = palette_from_string(
|
|
|
|
"PAL 1\nNAME lamplight\n45,45,59\n66,60,39\n140,94,1".to_string()
|
|
|
|
);
|
|
|
|
|
|
|
|
let expected = Palette {
|
|
|
|
id: "1".to_string(),
|
|
|
|
name: Some("lamplight".to_string()),
|
|
|
|
colours: vec![
|
|
|
|
Colour {red: 45, green: 45, blue: 59},
|
|
|
|
Colour {red: 66, green: 60, blue: 39},
|
|
|
|
Colour {red: 140, green: 94, blue: 1 },
|
|
|
|
],
|
|
|
|
};
|
|
|
|
|
|
|
|
assert_eq!(output, expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_palette_from_string_no_name() {
|
|
|
|
let output = palette_from_string(
|
|
|
|
"PAL 9\n45,45,59\n66,60,39\n140,94,1".to_string()
|
|
|
|
);
|
|
|
|
|
|
|
|
let expected = Palette {
|
|
|
|
id: "9".to_string(),
|
|
|
|
name: None,
|
|
|
|
colours: vec![
|
|
|
|
Colour {red: 45, green: 45, blue: 59},
|
|
|
|
Colour {red: 66, green: 60, blue: 39},
|
|
|
|
Colour {red: 140, green: 94, blue: 1 },
|
|
|
|
],
|
|
|
|
};
|
|
|
|
|
|
|
|
assert_eq!(output, expected);
|
|
|
|
}
|
|
|
|
|
2020-04-11 22:05:47 +00:00
|
|
|
fn palette_to_string(palette: Palette) -> String {
|
|
|
|
let name = if palette.name.is_some() {
|
|
|
|
format!("NAME {}\n", palette.name.unwrap())
|
|
|
|
} else {
|
|
|
|
"".to_string()
|
|
|
|
};
|
|
|
|
|
|
|
|
let colours: Vec<String> = palette.colours.into_iter().map(|colour| {
|
|
|
|
colour_to_string(colour)
|
|
|
|
}).collect();
|
|
|
|
let colours = colours.join("\n");
|
|
|
|
|
|
|
|
format!("PAL {}\n{}{}", palette.id, name, colours)
|
|
|
|
}
|
|
|
|
|
2020-04-05 22:00:15 +00:00
|
|
|
fn position_from_string(string: String) -> Position {
|
2020-04-06 11:27:29 +00:00
|
|
|
// e.g. "2,5"
|
|
|
|
let xy: Vec<&str> = string.split(',').collect();
|
2020-04-05 22:00:15 +00:00
|
|
|
let x = xy[0].parse().unwrap();
|
|
|
|
let y = xy[1].parse().unwrap();
|
|
|
|
|
2020-04-06 11:27:29 +00:00
|
|
|
Position {x, y}
|
2020-04-05 22:00:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_position_from_string() {
|
|
|
|
assert_eq!(
|
2020-04-06 11:27:29 +00:00
|
|
|
position_from_string("4,12".to_string()),
|
|
|
|
Position { x: 4, y: 12 }
|
2020-04-05 22:00:15 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn position_to_string(position: Position) -> String {
|
2020-04-06 11:27:29 +00:00
|
|
|
format!("{},{}", position.x, position.y)
|
2020-04-05 22:00:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_position_to_string() {
|
|
|
|
assert_eq!(
|
2020-04-06 11:27:29 +00:00
|
|
|
position_to_string(Position { x: 4, y: 12 }),
|
|
|
|
"4,12".to_string()
|
2020-04-05 22:00:15 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-04-11 17:17:29 +00:00
|
|
|
fn avatar_from_string(string: String) -> Avatar {
|
|
|
|
let string = string.replace("SPR A\n", "");
|
|
|
|
let mut lines: Vec<&str> = string.lines().collect();
|
|
|
|
let room_pos = lines.pop().unwrap().replace("POS ", "");
|
|
|
|
let room_pos: Vec<&str> = room_pos.split_whitespace().collect();
|
|
|
|
let room = room_pos[0].to_string();
|
|
|
|
let position = position_from_string(room_pos[1].to_string());
|
|
|
|
let animation_frames: String = lines.join("\n");
|
|
|
|
let animation_frames: Vec<&str> = animation_frames.split("\n>\n").collect();
|
|
|
|
let animation_frames: Vec<Image> = animation_frames.iter().map(|&frame| {
|
|
|
|
image_from_string(frame.to_string())
|
|
|
|
}).collect();
|
|
|
|
|
|
|
|
Avatar { animation_frames, room, position }
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_avatar_from_string() {
|
|
|
|
let output = avatar_from_string(
|
|
|
|
include_str!("../test/resources/avatar").to_string()
|
|
|
|
);
|
2020-04-11 22:04:56 +00:00
|
|
|
let expected = example_avatar();
|
2020-04-11 17:17:29 +00:00
|
|
|
assert_eq!(output, expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn avatar_to_string(avatar: Avatar) -> String {
|
|
|
|
format!(
|
|
|
|
"SPR A\n{}\nPOS {} {}",
|
|
|
|
animation_frames_to_string(avatar.animation_frames),
|
|
|
|
avatar.room,
|
|
|
|
position_to_string(avatar.position)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_avatar_to_string() {
|
2020-04-11 22:04:56 +00:00
|
|
|
let output = avatar_to_string(example_avatar());
|
2020-04-11 17:17:29 +00:00
|
|
|
let expected = include_str!("../test/resources/avatar");
|
|
|
|
assert_eq!(output, expected);
|
|
|
|
}
|
|
|
|
|
2020-04-05 22:58:10 +00:00
|
|
|
fn sprite_from_string(string: String) -> Sprite {
|
2020-04-11 17:18:20 +00:00
|
|
|
let mut lines: Vec<&str> = string.lines().collect();
|
2020-04-05 22:58:10 +00:00
|
|
|
|
|
|
|
let id = lines[0].replace("SPR ", "");
|
|
|
|
let mut name = None;
|
|
|
|
let mut dialogue = None;
|
2020-04-06 11:27:29 +00:00
|
|
|
let mut room: Option<String> = None;
|
2020-04-05 22:58:10 +00:00
|
|
|
let mut position: Option<Position> = None;
|
|
|
|
|
|
|
|
for _ in 0..3 {
|
|
|
|
let last_line = lines.pop().unwrap();
|
|
|
|
|
|
|
|
if last_line.starts_with("NAME") {
|
|
|
|
name = Some(last_line.replace("NAME ", "").to_string());
|
|
|
|
} else if last_line.starts_with("DLG") {
|
|
|
|
dialogue = Some(last_line.replace("DLG ", "").to_string());
|
|
|
|
} else if last_line.starts_with("POS") {
|
2020-04-06 11:27:29 +00:00
|
|
|
let last_line = last_line.replace("POS ", "");
|
|
|
|
let room_position: Vec<&str> = last_line.split(' ').collect();
|
|
|
|
room = Some(room_position[0].to_string());
|
|
|
|
position = Some(position_from_string(room_position[1].to_string()));
|
2020-04-05 22:58:10 +00:00
|
|
|
} else {
|
|
|
|
lines.push(last_line); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-06 11:27:29 +00:00
|
|
|
let room = room.unwrap();
|
2020-04-05 22:58:10 +00:00
|
|
|
let position = position.unwrap();
|
|
|
|
|
|
|
|
// todo dedupe
|
|
|
|
let animation_frames = lines[1..].join("");
|
|
|
|
let animation_frames: Vec<&str> = animation_frames.split("\n>\n").collect();
|
|
|
|
let animation_frames: Vec<Image> = animation_frames.iter().map(|&frame| {
|
|
|
|
image_from_string(frame.to_string())
|
|
|
|
}).collect();
|
|
|
|
|
2020-04-06 11:27:29 +00:00
|
|
|
Sprite { id, name, animation_frames, dialogue, room, position }
|
2020-04-05 22:58:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_sprite_from_string() {
|
2020-04-11 15:07:53 +00:00
|
|
|
let output = sprite_from_string(
|
|
|
|
include_str!("../test/resources/sprite").to_string()
|
|
|
|
);
|
|
|
|
|
2020-04-11 22:04:56 +00:00
|
|
|
let expected = example_sprite();
|
2020-04-05 22:58:10 +00:00
|
|
|
|
|
|
|
assert_eq!(output, expected);
|
|
|
|
}
|
2020-04-05 19:46:55 +00:00
|
|
|
|
2020-04-06 06:23:20 +00:00
|
|
|
fn sprite_to_string(sprite: Sprite) -> String {
|
|
|
|
format!(
|
2020-04-10 15:52:53 +00:00
|
|
|
"SPR {}\n{}{}{}\nPOS {} {}",
|
2020-04-06 06:23:20 +00:00
|
|
|
sprite.id,
|
|
|
|
animation_frames_to_string(sprite.animation_frames),
|
2020-04-06 07:27:08 +00:00
|
|
|
if sprite.name.is_some() {format!("\nNAME {}", sprite.name.unwrap())} else {"".to_string()},
|
|
|
|
if sprite.dialogue.is_some() {format!("\nDLG {}", sprite.dialogue.unwrap())} else {"".to_string()},
|
2020-04-10 15:52:53 +00:00
|
|
|
sprite.room,
|
2020-04-06 06:23:20 +00:00
|
|
|
position_to_string(sprite.position),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-04-06 07:27:08 +00:00
|
|
|
#[test]
|
|
|
|
fn test_sprite_to_string() {
|
2020-04-11 22:04:56 +00:00
|
|
|
let output = sprite_to_string(example_sprite());
|
2020-04-11 15:07:53 +00:00
|
|
|
let expected = include_str!("../test/resources/sprite").to_string();
|
2020-04-06 07:27:08 +00:00
|
|
|
|
|
|
|
assert_eq!(output, expected);
|
|
|
|
}
|
|
|
|
|
2020-04-06 07:54:43 +00:00
|
|
|
fn item_from_string(string: String) -> Item {
|
2020-04-11 17:18:20 +00:00
|
|
|
let mut lines: Vec<&str> = string.lines().collect();
|
2020-04-06 07:54:43 +00:00
|
|
|
|
|
|
|
let id = lines[0].replace("ITM ", "");
|
|
|
|
let mut name = None;
|
|
|
|
let mut dialogue = None;
|
|
|
|
|
|
|
|
for _ in 0..2 {
|
|
|
|
let last_line = lines.pop().unwrap();
|
|
|
|
|
|
|
|
if last_line.starts_with("NAME") {
|
|
|
|
name = Some(last_line.replace("NAME ", "").to_string());
|
|
|
|
} else if last_line.starts_with("DLG") {
|
|
|
|
dialogue = Some(last_line.replace("DLG ", "").to_string());
|
|
|
|
} else {
|
|
|
|
lines.push(last_line); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-06 08:02:52 +00:00
|
|
|
// todo dedupe
|
2020-04-06 07:54:43 +00:00
|
|
|
let animation_frames = lines[1..].join("");
|
|
|
|
let animation_frames: Vec<&str> = animation_frames.split("\n>\n").collect();
|
|
|
|
let animation_frames: Vec<Image> = animation_frames.iter().map(|&frame| {
|
|
|
|
image_from_string(frame.to_string())
|
|
|
|
}).collect();
|
|
|
|
|
2020-04-06 08:02:52 +00:00
|
|
|
Item { id, name, animation_frames, dialogue }
|
2020-04-06 07:54:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_item_from_string() {
|
2020-04-11 15:07:53 +00:00
|
|
|
let output = item_from_string(
|
|
|
|
include_str!("../test/resources/item").to_string()
|
|
|
|
);
|
|
|
|
|
2020-04-11 22:04:56 +00:00
|
|
|
let expected = example_item();
|
2020-04-06 08:02:52 +00:00
|
|
|
|
|
|
|
assert_eq!(output, expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn item_to_string(item: Item) -> String {
|
|
|
|
format!(
|
|
|
|
"ITM {}\n{}{}{}",
|
|
|
|
item.id,
|
|
|
|
animation_frames_to_string(item.animation_frames),
|
|
|
|
if item.name.is_some() {format!("\nNAME {}", item.name.unwrap())} else {"".to_string()},
|
|
|
|
if item.dialogue.is_some() {format!("\nDLG {}", item.dialogue.unwrap())} else {"".to_string()},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_item_to_string() {
|
2020-04-11 22:04:56 +00:00
|
|
|
let output = item_to_string(example_item());
|
2020-04-11 15:07:53 +00:00
|
|
|
let expected = include_str!("../test/resources/item").to_string();
|
2020-04-06 07:54:43 +00:00
|
|
|
|
|
|
|
assert_eq!(output, expected);
|
|
|
|
}
|
|
|
|
|
2020-04-06 12:21:59 +00:00
|
|
|
fn exit_from_string(string: String) -> Exit {
|
|
|
|
// e.g. "4 3,3"
|
|
|
|
let room_position: Vec<&str> = string.split(' ').collect();
|
|
|
|
let room = room_position[0].to_string();
|
|
|
|
let position = position_from_string(room_position[1].to_string());
|
|
|
|
|
|
|
|
Exit { room, position }
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_exit_from_string() {
|
|
|
|
assert_eq!(
|
|
|
|
exit_from_string("a 12,13".to_string()),
|
2020-04-11 17:24:43 +00:00
|
|
|
Exit { room: "a".to_string(), position: Position { x: 12, y: 13 } }
|
2020-04-06 12:21:59 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn exit_to_string(exit: Exit) -> String {
|
|
|
|
format!("{} {}", exit.room, position_to_string(exit.position))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_exit_to_string() {
|
|
|
|
assert_eq!(
|
2020-04-11 15:07:53 +00:00
|
|
|
exit_to_string(Exit { room: "8".to_string(), position: Position { x: 5, y: 6 } }),
|
2020-04-06 12:21:59 +00:00
|
|
|
"8 5,6".to_string()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn ending_from_string(string: String) -> Ending {
|
2020-04-06 13:11:56 +00:00
|
|
|
let string = string.replace("END ", "");
|
|
|
|
let id_dialogue: Vec<&str> = string.split('\n').collect();
|
|
|
|
let id = id_dialogue[0].to_string();
|
|
|
|
let dialogue = id_dialogue[1].to_string();
|
|
|
|
|
|
|
|
Ending { id, dialogue }
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_ending_from_string() {
|
|
|
|
assert_eq!(
|
2020-04-11 15:07:53 +00:00
|
|
|
ending_from_string(include_str!("../test/resources/ending").to_string()),
|
|
|
|
Ending {
|
|
|
|
id: "a".to_string(),
|
|
|
|
dialogue: "This is a long line of dialogue. Blah blah blah".to_string()
|
|
|
|
}
|
2020-04-06 13:11:56 +00:00
|
|
|
);
|
2020-04-06 12:21:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn ending_to_string(ending: Ending) -> String {
|
2020-04-06 13:11:56 +00:00
|
|
|
format!("END {}\n{}", ending.id, ending.dialogue)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_ending_to_string() {
|
|
|
|
assert_eq!(
|
|
|
|
ending_to_string(
|
|
|
|
Ending {
|
|
|
|
id: "7".to_string(),
|
|
|
|
dialogue: "This is another long ending. So long, farewell, etc.".to_string()
|
|
|
|
}
|
|
|
|
),
|
|
|
|
"END 7\nThis is another long ending. So long, farewell, etc.".to_string()
|
|
|
|
);
|
2020-04-06 12:21:59 +00:00
|
|
|
}
|
|
|
|
|
2020-04-06 16:18:17 +00:00
|
|
|
fn dialogue_from_string(string: String) -> Dialogue {
|
2020-04-11 17:18:20 +00:00
|
|
|
let lines: Vec<&str> = string.lines().collect();
|
2020-04-06 16:18:17 +00:00
|
|
|
let id = lines[0].replace("DLG ", "").to_string();
|
|
|
|
let contents = lines[1..].join("\n");
|
|
|
|
|
|
|
|
Dialogue { id, contents }
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_dialogue_from_string() {
|
|
|
|
assert_eq!(
|
|
|
|
dialogue_from_string("DLG h\nhello\ngoodbye".to_string()),
|
|
|
|
Dialogue { id: "h".to_string(), contents: "hello\ngoodbye".to_string()}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn dialogue_to_string(dialogue: Dialogue) -> String {
|
|
|
|
format!("DLG {}\n{}", dialogue.id, dialogue.contents)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_dialogue_to_string() {
|
|
|
|
assert_eq!(
|
|
|
|
dialogue_to_string(
|
|
|
|
Dialogue {
|
|
|
|
id: "y".to_string(),
|
|
|
|
contents: "This is a bit of dialogue,\nblah blah\nblah blah".to_string()
|
|
|
|
}
|
|
|
|
),
|
|
|
|
"DLG y\nThis is a bit of dialogue,\nblah blah\nblah blah".to_string()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-04-06 18:11:41 +00:00
|
|
|
fn variable_from_string(string: String) -> Variable {
|
|
|
|
let id_value: Vec<&str> = string.split('\n').collect();
|
|
|
|
let id = id_value[0].replace("VAR ", "").to_string();
|
|
|
|
let initial_value = id_value[1].to_string();
|
|
|
|
|
|
|
|
Variable { id, initial_value }
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_variable_from_string() {
|
|
|
|
assert_eq!(
|
|
|
|
variable_from_string("VAR a\n42".to_string()),
|
|
|
|
Variable { id: "a".to_string(), initial_value: "42".to_string()}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn variable_to_string(variable: Variable) -> String {
|
|
|
|
format!("VAR {}\n{}", variable.id, variable.initial_value)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_variable_to_string() {
|
|
|
|
let output = variable_to_string(
|
|
|
|
Variable { id: "c".to_string(), initial_value: "57".to_string() }
|
|
|
|
);
|
|
|
|
let expected = "VAR c\n57".to_string();
|
|
|
|
|
|
|
|
assert_eq!(output, expected);
|
|
|
|
}
|
|
|
|
|
2020-04-06 12:21:59 +00:00
|
|
|
fn room_from_string(string: String) -> Room {
|
|
|
|
// todo handle room_format?
|
2020-04-11 17:18:20 +00:00
|
|
|
let mut lines: Vec<&str> = string.lines().collect();
|
2020-04-06 12:21:59 +00:00
|
|
|
let id = lines[0].replace("ROOM ", "");
|
|
|
|
let mut name = None;
|
|
|
|
let mut palette = "0".to_string();
|
2020-04-10 15:27:23 +00:00
|
|
|
let mut items: Vec<Instance> = Vec::new();
|
|
|
|
let mut exits: Vec<ExitInstance> = Vec::new();
|
|
|
|
let mut endings: Vec<Instance> = Vec::new();
|
2020-04-06 12:21:59 +00:00
|
|
|
|
|
|
|
loop {
|
|
|
|
let last_line = lines.pop().unwrap();
|
|
|
|
|
|
|
|
if last_line.starts_with("NAME") {
|
|
|
|
name = Some(last_line.replace("NAME ", "").to_string());
|
|
|
|
} else if last_line.starts_with("PAL") {
|
|
|
|
palette = last_line.replace("PAL ", "").to_string();
|
|
|
|
} else if last_line.starts_with("ITM") {
|
|
|
|
let last_line = last_line.replace("ITM ", "");
|
2020-04-06 13:23:37 +00:00
|
|
|
let item_position: Vec<&str> = last_line.split(' ').collect();
|
|
|
|
let item_id = item_position[0];
|
|
|
|
let position = item_position[1];
|
|
|
|
let position = position_from_string(position.to_string());
|
|
|
|
|
2020-04-10 15:27:23 +00:00
|
|
|
items.push(Instance { position, id: item_id.to_string() });
|
2020-04-06 12:21:59 +00:00
|
|
|
} else if last_line.starts_with("EXT") {
|
2020-04-06 13:21:58 +00:00
|
|
|
let last_line = last_line.replace("EXT ", "");
|
|
|
|
let parts: Vec<&str> = last_line.split(' ').collect();
|
|
|
|
let position = position_from_string(parts[0].to_string());
|
|
|
|
let exit = exit_from_string(format!("{} {}", parts[1], parts[2]));
|
2020-04-06 12:21:59 +00:00
|
|
|
|
2020-04-10 15:27:23 +00:00
|
|
|
exits.push(ExitInstance { position, exit });
|
2020-04-06 12:21:59 +00:00
|
|
|
} else if last_line.starts_with("END") {
|
2020-04-06 19:35:01 +00:00
|
|
|
let last_line = last_line.replace("END ", "");
|
|
|
|
let ending_position: Vec<&str> = last_line.split(' ').collect();
|
|
|
|
let ending= ending_position[0].to_string();
|
|
|
|
let position = ending_position[1].to_string();
|
|
|
|
let position = position_from_string(position);
|
2020-04-06 12:21:59 +00:00
|
|
|
|
2020-04-10 15:27:23 +00:00
|
|
|
endings.push(Instance { position, id: ending });
|
2020-04-06 12:21:59 +00:00
|
|
|
} else {
|
|
|
|
lines.push(last_line); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-06 22:24:10 +00:00
|
|
|
let lines= &lines[1..];
|
2020-04-06 19:41:33 +00:00
|
|
|
|
|
|
|
let mut tiles : Vec<String> = Vec::new();
|
|
|
|
|
|
|
|
for line in lines.into_iter() {
|
|
|
|
let line: Vec<&str> = line.split(",").collect();
|
|
|
|
|
|
|
|
for tile_id in line {
|
|
|
|
tiles.push(tile_id.to_string());
|
|
|
|
}
|
2020-04-06 12:21:59 +00:00
|
|
|
}
|
2020-04-06 19:41:33 +00:00
|
|
|
|
|
|
|
Room { id, palette, name, tiles, items, exits, endings }
|
2020-04-06 12:21:59 +00:00
|
|
|
}
|
|
|
|
|
2020-04-06 22:24:10 +00:00
|
|
|
#[test]
|
|
|
|
fn test_room_from_string() {
|
2020-04-11 22:04:56 +00:00
|
|
|
let output = room_from_string(example_room_string());
|
|
|
|
let expected = example_room();
|
2020-04-06 22:24:10 +00:00
|
|
|
|
2020-04-09 07:28:08 +00:00
|
|
|
assert_eq!(output, expected);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn room_to_string(room: Room) -> String {
|
|
|
|
let mut tiles = String::new();
|
|
|
|
let mut items = String::new();
|
|
|
|
let mut exits = String::new();
|
|
|
|
let mut endings = String::new();
|
|
|
|
|
|
|
|
let sqrt = (room.tiles.len() as f64).sqrt() as usize; // 8 for SD, 16 for HD
|
|
|
|
for line in room.tiles.chunks(sqrt) {
|
|
|
|
for tile in line {
|
|
|
|
tiles.push_str(&format!("{},", tile));
|
|
|
|
}
|
|
|
|
tiles.pop(); // remove trailing comma
|
|
|
|
tiles.push_str("\n");
|
|
|
|
}
|
|
|
|
tiles.pop(); // remove trailing newline
|
|
|
|
|
2020-04-10 15:27:23 +00:00
|
|
|
for instance in room.items {
|
|
|
|
items.push_str(
|
|
|
|
&format!("\nITM {} {}", instance.id, position_to_string(instance.position))
|
|
|
|
);
|
2020-04-09 07:28:08 +00:00
|
|
|
}
|
|
|
|
|
2020-04-10 15:27:23 +00:00
|
|
|
for instance in room.exits {
|
2020-04-09 07:28:08 +00:00
|
|
|
exits.push_str(
|
2020-04-10 15:27:23 +00:00
|
|
|
&format!(
|
|
|
|
"\nEXT {} {}",
|
|
|
|
position_to_string(instance.position),
|
|
|
|
exit_to_string(instance.exit)
|
|
|
|
)
|
2020-04-09 07:28:08 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-04-10 15:27:23 +00:00
|
|
|
for instance in room.endings {
|
|
|
|
endings.push_str(
|
|
|
|
&format!("\nEND {} {}", instance.id, position_to_string(instance.position))
|
|
|
|
);
|
2020-04-09 07:28:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
format!(
|
|
|
|
"ROOM {}\n{}{}{}{}{}\nPAL {}",
|
|
|
|
room.id,
|
|
|
|
tiles,
|
|
|
|
if room.name.is_some() {format!("\nNAME {}", room.name.unwrap())} else {"".to_string()},
|
2020-04-06 22:24:10 +00:00
|
|
|
items,
|
|
|
|
exits,
|
2020-04-09 07:28:08 +00:00
|
|
|
endings,
|
|
|
|
room.palette
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_room_to_string() {
|
2020-04-11 22:04:56 +00:00
|
|
|
assert_eq!(room_to_string(example_room()), example_room_string());
|
2020-04-06 22:24:10 +00:00
|
|
|
}
|
|
|
|
|
2020-04-11 21:40:26 +00:00
|
|
|
fn game_from_string(string: String ) -> Game {
|
|
|
|
// dialogues and endings can have 2+ line breaks inside, so deal with these separately
|
|
|
|
// otherwise, everything can be split on a double line break (\n\n)
|
|
|
|
let mut dialogues: Vec<Dialogue> = Vec::new();
|
|
|
|
let mut endings: Vec<Ending> = Vec::new();
|
|
|
|
let mut variables: Vec<Variable> = Vec::new();
|
|
|
|
let main_split: Vec<&str> = string.split("\n\nDLG").collect();
|
|
|
|
let main = main_split[0].to_string();
|
|
|
|
let mut dialogues_endings_variables: String = main_split[1..].join("\n\nDLG");
|
|
|
|
// todo handle dialogues_endings_variables
|
|
|
|
|
|
|
|
let variable_segments = dialogues_endings_variables.clone();
|
|
|
|
let variable_segments: Vec<&str> = variable_segments.split("\n\nVAR").collect();
|
|
|
|
if variable_segments.len() > 0 {
|
|
|
|
dialogues_endings_variables = variable_segments[0].to_string();
|
|
|
|
let variable_segments = variable_segments[1..].to_owned();
|
|
|
|
|
|
|
|
for segment in variable_segments {
|
|
|
|
let segment = format!("VAR{}", segment);
|
|
|
|
variables.push(variable_from_string(segment));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let ending_segments = dialogues_endings_variables.clone();
|
|
|
|
let ending_segments: Vec<&str> = ending_segments.split("\n\nEND").collect();
|
|
|
|
if ending_segments.len() > 0 {
|
|
|
|
dialogues_endings_variables = ending_segments[0].to_string();
|
|
|
|
let ending_segments = ending_segments[1..].to_owned();
|
|
|
|
|
|
|
|
for segment in ending_segments {
|
|
|
|
let segment = format!("END{}", segment);
|
|
|
|
endings.push(ending_from_string(segment));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let dialogue_segments = format!("\n\nDLG {}", dialogues_endings_variables.trim());
|
|
|
|
let dialogue_segments: Vec<&str> = dialogue_segments.split("\n\nDLG").collect();
|
|
|
|
for segment in dialogue_segments[1..].to_owned() {
|
|
|
|
let segment = format!("DLG{}", segment);
|
|
|
|
dialogues.push(dialogue_from_string(segment));
|
|
|
|
}
|
|
|
|
|
|
|
|
let segments: Vec<&str> = main.split("\n\n").collect();
|
|
|
|
|
|
|
|
let name = segments[0].to_string();
|
|
|
|
let mut version: f64 = 1.0;
|
|
|
|
let mut room_format: u8 = 1;
|
|
|
|
let mut palettes: Vec<Palette> = Vec::new();
|
|
|
|
let mut rooms: Vec<Room> = Vec::new();
|
|
|
|
let mut tiles: Vec<Tile> = Vec::new();
|
|
|
|
let mut avatar : Option<Avatar> = None; // unwrap this later
|
|
|
|
let mut sprites: Vec<Sprite> = Vec::new();
|
|
|
|
let mut items: Vec<Item> = Vec::new();
|
|
|
|
|
|
|
|
for segment in segments[1..].to_owned() {
|
|
|
|
let segment = segment.to_string();
|
|
|
|
|
|
|
|
if segment.starts_with("# BITSY VERSION") {
|
|
|
|
version = segment.replace("# BITSY VERSION ", "").parse().unwrap();
|
|
|
|
} else if segment.starts_with("! ROOM_FORMAT") {
|
|
|
|
room_format = segment.replace("! ROOM_FORMAT ", "").parse().unwrap();
|
|
|
|
} else if segment.starts_with("PAL") {
|
|
|
|
palettes.push(palette_from_string(segment));
|
|
|
|
} else if segment.starts_with("ROOM") {
|
|
|
|
rooms.push(room_from_string(segment));
|
|
|
|
} else if segment.starts_with("TIL") {
|
|
|
|
tiles.push(tile_from_string(segment));
|
|
|
|
} else if segment.starts_with("SPR A") {
|
|
|
|
avatar = Some(avatar_from_string(segment));
|
|
|
|
} else if segment.starts_with("SPR") {
|
|
|
|
sprites.push(sprite_from_string(segment));
|
|
|
|
} else if segment.starts_with("ITM") {
|
|
|
|
items.push(item_from_string(segment));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert!(avatar.is_some());
|
|
|
|
let avatar = avatar.unwrap();
|
|
|
|
|
|
|
|
Game {
|
|
|
|
name,
|
|
|
|
version,
|
|
|
|
room_format,
|
|
|
|
palettes,
|
|
|
|
rooms,
|
|
|
|
tiles,
|
|
|
|
avatar,
|
|
|
|
sprites,
|
|
|
|
items,
|
|
|
|
dialogues,
|
|
|
|
endings,
|
|
|
|
variables,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_game_from_string() {
|
|
|
|
let output = game_from_string(
|
|
|
|
include_str!["../test/resources/default.bitsy"].to_string()
|
|
|
|
);
|
|
|
|
|
|
|
|
let expected = Game {
|
|
|
|
name: "Write your game's title here".to_string(),
|
|
|
|
version: 6.5,
|
|
|
|
room_format: 1,
|
|
|
|
palettes: vec![
|
|
|
|
Palette {
|
|
|
|
id: "0".to_string(),
|
|
|
|
name: None,
|
|
|
|
colours: vec![
|
|
|
|
Colour {red: 0, green: 82, blue: 204 },
|
|
|
|
Colour {red: 128, green: 159, blue: 255 },
|
|
|
|
Colour {red: 255, green: 255, blue: 255 },
|
|
|
|
]
|
|
|
|
}
|
|
|
|
],
|
|
|
|
rooms: vec![
|
|
|
|
Room {
|
|
|
|
id: "0".to_string(),
|
|
|
|
palette: "0".to_string(),
|
|
|
|
name: None,
|
|
|
|
tiles: vec![
|
|
|
|
"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),
|
|
|
|
"0".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"0".to_string(),
|
|
|
|
"0".to_string(),"a".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"a".to_string(),"0".to_string(),
|
|
|
|
"0".to_string(),"a".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"a".to_string(),"0".to_string(),
|
|
|
|
"0".to_string(),"a".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"a".to_string(),"0".to_string(),
|
|
|
|
"0".to_string(),"a".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"a".to_string(),"0".to_string(),
|
|
|
|
"0".to_string(),"a".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"a".to_string(),"0".to_string(),
|
|
|
|
"0".to_string(),"a".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"a".to_string(),"0".to_string(),
|
|
|
|
"0".to_string(),"a".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"a".to_string(),"0".to_string(),
|
|
|
|
"0".to_string(),"a".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"a".to_string(),"0".to_string(),
|
|
|
|
"0".to_string(),"a".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"a".to_string(),"0".to_string(),
|
|
|
|
"0".to_string(),"a".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"a".to_string(),"0".to_string(),
|
|
|
|
"0".to_string(),"a".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"a".to_string(),"0".to_string(),
|
|
|
|
"0".to_string(),"a".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"a".to_string(),"0".to_string(),
|
|
|
|
"0".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"a".to_string(),"0".to_string(),
|
|
|
|
"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),"0".to_string(),
|
|
|
|
],
|
|
|
|
items: vec![],
|
|
|
|
exits: vec![],
|
|
|
|
endings: vec![]
|
|
|
|
}
|
|
|
|
],
|
|
|
|
tiles: vec![
|
|
|
|
Tile {
|
|
|
|
id: "a".to_string(),
|
|
|
|
name: None,
|
|
|
|
wall: false,
|
|
|
|
animation_frames: vec![
|
|
|
|
Image {
|
|
|
|
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,
|
|
|
|
]
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
],
|
|
|
|
avatar: Avatar {
|
|
|
|
animation_frames: vec![
|
|
|
|
Image {
|
|
|
|
pixels: vec![
|
|
|
|
0,0,0,1,1,0,0,0,
|
|
|
|
0,0,0,1,1,0,0,0,
|
|
|
|
0,0,0,1,1,0,0,0,
|
|
|
|
0,0,1,1,1,1,0,0,
|
|
|
|
0,1,1,1,1,1,1,0,
|
|
|
|
1,0,1,1,1,1,0,1,
|
|
|
|
0,0,1,0,0,1,0,0,
|
|
|
|
0,0,1,0,0,1,0,0,
|
|
|
|
]
|
|
|
|
}
|
|
|
|
],
|
|
|
|
room: "0".to_string(),
|
|
|
|
position: Position { x: 4, y: 4 }
|
|
|
|
},
|
|
|
|
sprites: vec![
|
|
|
|
Sprite {
|
|
|
|
id: "a".to_string(),
|
|
|
|
name: None,
|
|
|
|
animation_frames: vec![
|
|
|
|
Image {
|
|
|
|
pixels: vec![
|
|
|
|
0,0,0,0,0,0,0,0,
|
|
|
|
0,0,0,0,0,0,0,0,
|
|
|
|
0,1,0,1,0,0,0,1,
|
|
|
|
0,1,1,1,0,0,0,1,
|
|
|
|
0,1,1,1,0,0,1,0,
|
|
|
|
0,1,1,1,1,1,0,0,
|
|
|
|
0,0,1,1,1,1,0,0,
|
|
|
|
0,0,1,0,0,1,0,0,
|
|
|
|
]
|
|
|
|
}
|
|
|
|
],
|
|
|
|
dialogue: Some("SPR_0".to_string()),
|
|
|
|
room: "0".to_string(),
|
|
|
|
position: Position { x: 8, y: 12 }
|
|
|
|
}
|
|
|
|
],
|
|
|
|
items: vec![
|
|
|
|
Item {
|
|
|
|
id: "0".to_string(),
|
|
|
|
animation_frames: vec![
|
|
|
|
Image {
|
|
|
|
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,1,1,1,1,0,0,
|
|
|
|
0,1,1,0,0,1,0,0,
|
|
|
|
0,0,1,0,0,1,0,0,
|
|
|
|
0,0,0,1,1,0,0,0,
|
|
|
|
0,0,0,0,0,0,0,0,
|
|
|
|
]
|
|
|
|
},
|
|
|
|
],
|
|
|
|
name: Some("tea".to_string()),
|
|
|
|
dialogue: Some("ITM_0".to_string())
|
|
|
|
},
|
|
|
|
],
|
|
|
|
dialogues: vec![
|
|
|
|
Dialogue {
|
|
|
|
id: "SPR_0".to_string(),
|
|
|
|
contents: "I'm a cat".to_string(),
|
|
|
|
},
|
|
|
|
Dialogue {
|
|
|
|
id: "ITM_0".to_string(),
|
|
|
|
contents: "You found a nice warm cup of tea".to_string(),
|
|
|
|
},
|
|
|
|
],
|
|
|
|
endings: vec![],
|
|
|
|
variables: vec![
|
|
|
|
Variable { id: "a".to_string(), initial_value: "42".to_string() }
|
|
|
|
],
|
|
|
|
};
|
|
|
|
|
|
|
|
assert_eq!(output, expected);
|
|
|
|
}
|
2020-04-06 07:54:43 +00:00
|
|
|
|
2020-04-05 17:58:04 +00:00
|
|
|
// fn game_to_string(game: Game) -> String {
|
|
|
|
//
|
|
|
|
// }
|
|
|
|
|
|
|
|
fn main() {
|
2020-04-11 17:18:46 +00:00
|
|
|
println!("why does a crate need a main function?");
|
2020-04-05 17:58:04 +00:00
|
|
|
}
|