reimplement avatar as a plain sprite; handle sprite items as these are mostly needed for avatar

This commit is contained in:
Max Bradbury 2020-04-26 13:31:20 +01:00
parent ed6552f39c
commit cf1ca6f1b5
6 changed files with 40 additions and 135 deletions

View File

@ -66,8 +66,7 @@ some more practical uses would be things like:
* "unquote" function for dialogues, game title, anything else that supports """ * "unquote" function for dialogues, game title, anything else that supports """
* add Bitsy HD and Bitsy 7.0 test data * add Bitsy HD and Bitsy 7.0 test data
* fix variables/endings becoming "DLG DLG"? * fix variables/endings becoming "DLG DLG"?
* ITM in avatars? are these starting items? * dedupe "animation frames from string" functionality
* make the avatar a sprite - they're not fundamentally different according to Bitsy
### tidy up ### tidy up

View File

@ -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<Image>,
pub name: Option<String>,
pub room_id: u64,
pub position: Position,
pub colour_id: Option<u64>,
}
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<Avatar, &'static dyn Error> {
let string = string.replace("SPR A\n", "");
let mut lines: Vec<&str> = string.lines().collect();
let mut name: Option<String> = None;
let mut room_id: Option<u64> = None;
let mut position: Option<Position> = None;
let mut colour_id: Option<u64> = 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<Image> = 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"));
}
}

View File

@ -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 std::error::Error;
use loe::TransformMode; use loe::TransformMode;
@ -76,7 +76,7 @@ pub struct Game {
pub palettes: Vec<Palette>, pub palettes: Vec<Palette>,
pub rooms: Vec<Room>, pub rooms: Vec<Room>,
pub tiles: Vec<Tile>, pub tiles: Vec<Tile>,
pub avatar: Avatar, pub avatar: Sprite,
pub sprites: Vec<Sprite>, pub sprites: Vec<Sprite>,
pub items: Vec<Item>, pub items: Vec<Item>,
pub dialogues: Vec<Dialogue>, pub dialogues: Vec<Dialogue>,
@ -127,7 +127,7 @@ impl Game {
let mut palettes: Vec<Palette> = Vec::new(); let mut palettes: Vec<Palette> = Vec::new();
let mut rooms: Vec<Room> = Vec::new(); let mut rooms: Vec<Room> = Vec::new();
let mut tiles: Vec<Tile> = Vec::new(); let mut tiles: Vec<Tile> = Vec::new();
let mut avatar: Option<Avatar> = None; // unwrap this later let mut avatar: Option<Sprite> = None; // unwrap this later
let mut sprites: Vec<Sprite> = Vec::new(); let mut sprites: Vec<Sprite> = Vec::new();
let mut items: Vec<Item> = Vec::new(); let mut items: Vec<Item> = Vec::new();
@ -158,8 +158,8 @@ impl Game {
} else if segment.starts_with("TIL") { } else if segment.starts_with("TIL") {
tiles.push(Tile::from(segment)); tiles.push(Tile::from(segment));
} else if segment.starts_with("SPR A") { } else if segment.starts_with("SPR A") {
avatar = Some(Avatar::from(segment).unwrap()); avatar = Some(Sprite::from(segment));
} else if segment.starts_with("SPR") { } else if segment.starts_with("SPR ") {
sprites.push(Sprite::from(segment)); sprites.push(Sprite::from(segment));
} else if segment.starts_with("ITM") { } else if segment.starts_with("ITM") {
items.push(Item::from(segment)); 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 { for sprite in &self.sprites {
if !is_string_numeric(sprite.id.to_base36()) { if !is_string_numeric(sprite.id.to_base36()) {

View File

@ -4,7 +4,6 @@ use std::io::Cursor;
use radix_fmt::radix_36; use radix_fmt::radix_36;
use loe::{process, Config, TransformMode}; use loe::{process, Config, TransformMode};
pub mod avatar;
pub mod colour; pub mod colour;
pub mod dialogue; pub mod dialogue;
pub mod ending; pub mod ending;

View File

@ -25,8 +25,9 @@ pub mod image {
} }
} }
pub fn avatar() -> Avatar { pub fn avatar() -> Sprite {
Avatar { Sprite {
id: 0,
animation_frames: vec![ animation_frames: vec![
Image { Image {
pixels: vec![ pixels: vec![
@ -44,9 +45,11 @@ pub fn avatar() -> Avatar {
}, },
], ],
name: None, name: None,
room_id: 0, room_id: Some(0),
position: Position { x: 2, y: 5 }, position: Some(Position { x: 2, y: 5 }),
colour_id: None, colour_id: None,
dialogue_id: None,
items: vec![]
} }
} }
@ -81,6 +84,7 @@ pub fn sprite() -> Sprite {
room_id: Some(4), room_id: Some(4),
position: Some(Position { x: 9, y: 7 }), position: Some(Position { x: 9, y: 7 }),
colour_id: None, colour_id: None,
items: vec![]
} }
} }
@ -699,7 +703,8 @@ pub fn game_default() -> Game {
walls: vec![], walls: vec![],
}], }],
tiles: vec![self::tile_default()], tiles: vec![self::tile_default()],
avatar: Avatar { avatar: Sprite {
id: 0,
animation_frames: vec![Image { animation_frames: vec![Image {
pixels: vec![ 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, 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, name: None,
room_id: 0, room_id: Some(0),
position: Position { x: 4, y: 4 }, position: Option::from(Position { x: 4, y: 4 }),
colour_id: None, colour_id: None,
dialogue_id: None,
items: vec![]
}, },
sprites: vec![Sprite { sprites: vec![Sprite {
id: 10, id: 10,
@ -726,6 +733,7 @@ pub fn game_default() -> Game {
room_id: Some(0), room_id: Some(0),
position: Some(Position { x: 8, y: 12 }), position: Some(Position { x: 8, y: 12 }),
colour_id: None, colour_id: None,
items: vec![]
}], }],
items: vec![Item { items: vec![Item {
id: 0, id: 0,

View File

@ -9,6 +9,7 @@ pub struct Sprite {
pub room_id: Option<u64>, pub room_id: Option<u64>,
pub position: Option<Position>, pub position: Option<Position>,
pub colour_id: Option<u64>, pub colour_id: Option<u64>,
pub items: Vec<String>,
} }
impl Sprite { impl Sprite {
@ -35,6 +36,15 @@ impl Sprite {
fn colour_line(&self) -> String { fn colour_line(&self) -> String {
optional_data_line("COL", self.colour_id.as_ref()) 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<String> = self.items.iter().map(|item| format!("ITM {}", item)).collect();
format!("\n{}", lines.join("\n"))
}
}
} }
impl From<String> for Sprite { impl From<String> for Sprite {
@ -47,6 +57,7 @@ impl From<String> for Sprite {
let mut room_id: Option<u64> = None; let mut room_id: Option<u64> = None;
let mut position: Option<Position> = None; let mut position: Option<Position> = None;
let mut colour_id: Option<u64> = None; let mut colour_id: Option<u64> = None;
let mut items: Vec<String> = Vec::new();
loop { loop {
let last_line = lines.pop().unwrap(); let last_line = lines.pop().unwrap();
@ -67,12 +78,16 @@ impl From<String> for Sprite {
position = Some(Position::from(room_position[1].to_string()).unwrap()); position = Some(Position::from(room_position[1].to_string()).unwrap());
} else if last_line.starts_with("COL") { } else if last_line.starts_with("COL") {
colour_id = Some(last_line.replace("COL ", "").parse().unwrap()); colour_id = Some(last_line.replace("COL ", "").parse().unwrap());
} else if last_line.starts_with("ITM") {
items.push(last_line.replace("ITM ", ""));
} else { } else {
lines.push(last_line); lines.push(last_line);
break; break;
} }
} }
items.reverse();
// todo dedupe // todo dedupe
let animation_frames = lines[1..].join(""); let animation_frames = lines[1..].join("");
let animation_frames: Vec<&str> = animation_frames.split(">").collect(); let animation_frames: Vec<&str> = animation_frames.split(">").collect();
@ -89,6 +104,7 @@ impl From<String> for Sprite {
room_id, room_id,
position, position,
colour_id, colour_id,
items
} }
} }
} }
@ -97,13 +113,14 @@ impl ToString for Sprite {
#[inline] #[inline]
fn to_string(&self) -> String { fn to_string(&self) -> String {
format!( format!(
"SPR {}\n{}{}{}{}{}", "SPR {}\n{}{}{}{}{}{}",
self.id.to_base36(), self.id.to_base36(),
self.animation_frames.to_string(), self.animation_frames.to_string(),
self.name_line(), self.name_line(),
self.dialogue_line(), self.dialogue_line(),
self.room_position_line(), self.room_position_line(),
self.colour_line(), self.colour_line(),
self.item_lines(),
) )
} }
} }