diff --git a/README.md b/README.md index a231dcc..1e54195 100644 --- a/README.md +++ b/README.md @@ -66,8 +66,7 @@ some more practical uses would be things like: * "unquote" function for dialogues, game title, anything else that supports """ * add Bitsy HD and Bitsy 7.0 test data * fix variables/endings becoming "DLG DLG"? -* ITM in avatars? are these starting items? -* make the avatar a sprite - they're not fundamentally different according to Bitsy +* dedupe "animation frames from string" functionality ### tidy up diff --git a/src/avatar.rs b/src/avatar.rs deleted file mode 100644 index a4c3939..0000000 --- a/src/avatar.rs +++ /dev/null @@ -1,118 +0,0 @@ -use crate::{from_base36, optional_data_line, AnimationFrames, Image, Position, ToBase36, is_string_numeric}; -use std::error::Error; - -/// avatar is a "sprite" in the game data but with a specific id -#[derive(Debug, Eq, PartialEq)] -pub struct Avatar { - pub animation_frames: Vec, - pub name: Option, - pub room_id: u64, - pub position: Position, - pub colour_id: Option, -} - -impl Avatar { - fn room_position_line(&self) -> String { - format!( - "\nPOS {} {}", - self.room_id.to_base36(), - self.position.to_string() - ) - } - - fn colour_line(&self) -> String { - optional_data_line("COL", self.colour_id.as_ref()) - } -} - -impl Avatar { - pub(crate) fn from(string: String) -> Result { - let string = string.replace("SPR A\n", ""); - let mut lines: Vec<&str> = string.lines().collect(); - - let mut name: Option = None; - let mut room_id: Option = None; - let mut position: Option = None; - let mut colour_id: Option = None; - - loop { - let last_line = lines.pop().unwrap(); - - if last_line.starts_with("POS") { - let room_pos = last_line.replace("POS ", ""); - let room_pos: Vec<&str> = room_pos.split_whitespace().collect(); - room_id = Some(from_base36(room_pos[0])); - - if room_pos.len() < 2 { - panic!("Bad room/position for avatar: {}", string); - } - - position = Some(Position::from(room_pos[1].to_string()).unwrap()); - } else if last_line.starts_with("NAME") { - name = Some(last_line.replace("NAME ", "")); - } else if last_line.starts_with("COL") { - let last_line = last_line.replace("COL ", ""); - let result = last_line.parse(); - if result.is_ok() { - colour_id = Some(result.unwrap()); - } else { - println!("Bad colour id: {}", last_line) - } - } else if is_string_numeric(last_line.to_string()) { - lines.push(last_line); - break; - } - } - - let room_id = room_id.unwrap(); - let position = position.unwrap(); - - let animation_frames: String = lines.join("\n"); - let animation_frames: Vec<&str> = animation_frames.split(">").collect(); - let animation_frames: Vec = animation_frames - .iter() - .map(|&frame| Image::from(frame.to_string())) - .collect(); - - Ok(Avatar { - animation_frames, - name, - room_id, - position, - colour_id, - }) - } -} - -impl ToString for Avatar { - #[inline] - fn to_string(&self) -> String { - format!( - "SPR A\n{}{}{}{}", - self.animation_frames.to_string(), - optional_data_line("NAME", self.name.as_ref()), - self.room_position_line(), - self.colour_line(), - ) - } -} - -#[cfg(test)] -mod test { - use crate::avatar::Avatar; - use crate::mock; - - #[test] - fn avatar_from_string() { - let output = Avatar::from( - include_str!("test-resources/avatar").to_string() - ).unwrap(); - - assert_eq!(output, mock::avatar()); - } - - #[test] - fn avatar_to_string() { - assert_eq!(mock::avatar().to_string(), include_str!("test-resources/avatar")); - } -} diff --git a/src/game.rs b/src/game.rs index f2be14a..bcd7d00 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,4 +1,4 @@ -use crate::{Avatar, Dialogue, Ending, Font, Item, Palette, Room, Sprite, TextDirection, Tile, ToBase36, Variable, transform_line_endings, segments_from_string, is_string_numeric}; +use crate::{Dialogue, Ending, Font, Item, Palette, Room, Sprite, TextDirection, Tile, ToBase36, Variable, transform_line_endings, segments_from_string, is_string_numeric}; use std::error::Error; use loe::TransformMode; @@ -76,7 +76,7 @@ pub struct Game { pub palettes: Vec, pub rooms: Vec, pub tiles: Vec, - pub avatar: Avatar, + pub avatar: Sprite, pub sprites: Vec, pub items: Vec, pub dialogues: Vec, @@ -127,7 +127,7 @@ impl Game { let mut palettes: Vec = Vec::new(); let mut rooms: Vec = Vec::new(); let mut tiles: Vec = Vec::new(); - let mut avatar: Option = None; // unwrap this later + let mut avatar: Option = None; // unwrap this later let mut sprites: Vec = Vec::new(); let mut items: Vec = Vec::new(); @@ -158,8 +158,8 @@ impl Game { } else if segment.starts_with("TIL") { tiles.push(Tile::from(segment)); } else if segment.starts_with("SPR A") { - avatar = Some(Avatar::from(segment).unwrap()); - } else if segment.starts_with("SPR") { + avatar = Some(Sprite::from(segment)); + } else if segment.starts_with("SPR ") { sprites.push(Sprite::from(segment)); } else if segment.starts_with("ITM") { items.push(Item::from(segment)); @@ -227,7 +227,7 @@ impl ToString for Game { } } - segments.push(self.avatar.to_string()); + segments.push(self.avatar.to_string().replace("SPR a", "SPR A")); for sprite in &self.sprites { if !is_string_numeric(sprite.id.to_base36()) { diff --git a/src/lib.rs b/src/lib.rs index 3b970c7..a751251 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,6 @@ use std::io::Cursor; use radix_fmt::radix_36; use loe::{process, Config, TransformMode}; -pub mod avatar; pub mod colour; pub mod dialogue; pub mod ending; diff --git a/src/mock.rs b/src/mock.rs index c284d7b..def051e 100644 --- a/src/mock.rs +++ b/src/mock.rs @@ -25,8 +25,9 @@ pub mod image { } } -pub fn avatar() -> Avatar { - Avatar { +pub fn avatar() -> Sprite { + Sprite { + id: 0, animation_frames: vec![ Image { pixels: vec![ @@ -44,9 +45,11 @@ pub fn avatar() -> Avatar { }, ], name: None, - room_id: 0, - position: Position { x: 2, y: 5 }, + room_id: Some(0), + position: Some(Position { x: 2, y: 5 }), colour_id: None, + dialogue_id: None, + items: vec![] } } @@ -81,6 +84,7 @@ pub fn sprite() -> Sprite { room_id: Some(4), position: Some(Position { x: 9, y: 7 }), colour_id: None, + items: vec![] } } @@ -699,7 +703,8 @@ pub fn game_default() -> Game { walls: vec![], }], tiles: vec![self::tile_default()], - avatar: Avatar { + avatar: Sprite { + id: 0, 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, @@ -708,9 +713,11 @@ pub fn game_default() -> Game { ], }], name: None, - room_id: 0, - position: Position { x: 4, y: 4 }, + room_id: Some(0), + position: Option::from(Position { x: 4, y: 4 }), colour_id: None, + dialogue_id: None, + items: vec![] }, sprites: vec![Sprite { id: 10, @@ -726,6 +733,7 @@ pub fn game_default() -> Game { room_id: Some(0), position: Some(Position { x: 8, y: 12 }), colour_id: None, + items: vec![] }], items: vec![Item { id: 0, diff --git a/src/sprite.rs b/src/sprite.rs index 66a2e94..7660446 100644 --- a/src/sprite.rs +++ b/src/sprite.rs @@ -9,6 +9,7 @@ pub struct Sprite { pub room_id: Option, pub position: Option, pub colour_id: Option, + pub items: Vec, } impl Sprite { @@ -35,6 +36,15 @@ impl Sprite { fn colour_line(&self) -> String { optional_data_line("COL", self.colour_id.as_ref()) } + + fn item_lines(&self) -> String { + if self.items.len() == 0 { + "".to_string() + } else { + let lines: Vec = self.items.iter().map(|item| format!("ITM {}", item)).collect(); + format!("\n{}", lines.join("\n")) + } + } } impl From for Sprite { @@ -47,6 +57,7 @@ impl From for Sprite { let mut room_id: Option = None; let mut position: Option = None; let mut colour_id: Option = None; + let mut items: Vec = Vec::new(); loop { let last_line = lines.pop().unwrap(); @@ -67,12 +78,16 @@ impl From for Sprite { position = Some(Position::from(room_position[1].to_string()).unwrap()); } else if last_line.starts_with("COL") { colour_id = Some(last_line.replace("COL ", "").parse().unwrap()); + } else if last_line.starts_with("ITM") { + items.push(last_line.replace("ITM ", "")); } else { lines.push(last_line); break; } } + items.reverse(); + // todo dedupe let animation_frames = lines[1..].join(""); let animation_frames: Vec<&str> = animation_frames.split(">").collect(); @@ -89,6 +104,7 @@ impl From for Sprite { room_id, position, colour_id, + items } } } @@ -97,13 +113,14 @@ impl ToString for Sprite { #[inline] fn to_string(&self) -> String { format!( - "SPR {}\n{}{}{}{}{}", + "SPR {}\n{}{}{}{}{}{}", self.id.to_base36(), self.animation_frames.to_string(), self.name_line(), self.dialogue_line(), self.room_position_line(), self.colour_line(), + self.item_lines(), ) } }