extern crate loe; use std::io::Cursor; use radix_fmt::radix_36; use loe::{process, Config, TransformMode}; pub mod colour; pub mod dialogue; pub mod ending; pub mod exit; pub mod game; pub mod image; pub mod item; pub mod mock; pub mod palette; pub mod position; pub mod room; pub mod sprite; pub mod text; pub mod tile; pub mod variable; use colour::Colour; use dialogue::Dialogue; use ending::Ending; use exit::{Exit, Transition}; use game::{Game, Version}; use image::Image; use item::Item; use palette::Palette; use position::Position; use room::Room; use sprite::Sprite; use std::fmt::Display; use text::{Font, TextDirection}; use tile::Tile; use variable::Variable; #[derive(Debug, Eq, PartialEq)] pub struct Instance { position: Position, id: String, // item / ending.rs id } #[derive(Debug, Eq, PartialEq)] pub struct ExitInstance { position: Position, exit: Exit, transition: Option, dialogue_id: Option, } pub trait AnimationFrames { fn to_string(&self) -> String; } impl AnimationFrames for Vec { #[inline] fn to_string(&self) -> String { let mut string = String::new(); let last_frame = self.len() - 1; for (i, frame) in self.into_iter().enumerate() { string.push_str(&frame.to_string()); if i < last_frame { string.push_str(&"\n>\n".to_string()); } } string } } #[inline] fn from_base36(str: &str) -> u64 { u64::from_str_radix(str, 36).expect(&format!("Invalid base36 string: {}", str)) } /// this doesn't work inside ToBase36 for some reason #[inline] fn to_base36(int: u64) -> String { format!("{}", radix_36(int)) } pub trait ToBase36 { fn to_base36(&self) -> String; } impl ToBase36 for u64 { #[inline] fn to_base36(&self) -> String { to_base36(*self) } } /// e.g. `\nNAME DLG_0` #[inline] fn optional_data_line(label: &str, item: Option) -> String { if item.is_some() { format!("\n{} {}", label, item.unwrap()) } else { "".to_string() } } #[inline] fn transform_line_endings(input: String, mode: TransformMode) -> String { let mut input = Cursor::new(input); let mut output = Cursor::new(Vec::new()); process(&mut input, &mut output, Config::default().transform(mode)).unwrap(); String::from_utf8(output.into_inner()).unwrap() } #[inline] fn segments_from_string(string: String) -> Vec { let mut output:Vec = Vec::new(); // are we inside `"""\n...\n"""`? if so, ignore empty lines let mut inside_escaped_block = false; let mut current_segment : Vec = Vec::new(); let lines: Vec<&str> = string.lines().collect(); for line in lines { if line == "\"\"\"" { inside_escaped_block = ! inside_escaped_block; } if line == "" && !inside_escaped_block { output.push(current_segment.join("\n")); current_segment = Vec::new(); } else { current_segment.push(line.to_string()); } } output.push(current_segment.join("\n")); output } /// for some reason the sprites with numeric IDs go first, /// then SPR A (avatar), then all the non-numeric IDs #[inline] fn is_string_numeric(str: String) -> bool { for c in str.chars() { if !c.is_numeric() { return false; } } return true; } #[cfg(test)] mod test { use crate::{from_base36, ToBase36, optional_data_line, mock, segments_from_string}; #[test] fn test_from_base36() { assert_eq!(from_base36("0"), 0); assert_eq!(from_base36("0z"), 35); assert_eq!(from_base36("11"), 37); } #[test] fn test_to_base36() { assert_eq!((37 as u64).to_base36(), "11"); } #[test] fn test_optional_data_line() { let output = optional_data_line("NAME", mock::item().name); assert_eq!(output, "\nNAME door".to_string()); } #[test] fn test_string_to_segments() { let output = segments_from_string( include_str!("./test-resources/segments").to_string() ); let expected = vec![ "\"\"\"\nthe first segment is a long bit of text\n\n\nit contains empty lines\n\n\"\"\"".to_string(), "this is a new segment\nthis is still the second segment\nblah\nblah".to_string(), "DLG SEGMENT_3\n\"\"\"\nthis is a short \"long\" bit of text\n\"\"\"".to_string(), "this is the last segment".to_string(), ]; assert_eq!(output, expected); } }