1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
use crate::{AnimationFrames, Image, Position, mock, from_base36, ToBase36, optional_data_line};

#[derive(Debug, Eq, PartialEq)]
pub struct Sprite {
    pub id: u64,
    pub name: Option<String>,
    pub animation_frames: Vec<Image>,
    pub dialogue_id: Option<String>,
    pub room_id: Option<u64>,
    pub position: Option<Position>,
    pub colour_id: Option<u64>,
}

impl Sprite {
    fn name_line(&self) -> String {
        optional_data_line("NAME", self.name.as_ref())
    }

    fn dialogue_line(&self) -> String {
        optional_data_line("DLG", self.dialogue_id.as_ref())
    }

    fn room_position_line(&self) -> String {
        if self.room_id.is_some() && self.position.is_some() {
            format!("\nPOS {} {}", self.room_id.unwrap().to_base36(), self.position.as_ref().unwrap().to_string())
        } else {
            "".to_string()
        }
    }

    fn colour_line(&self) -> String {
        optional_data_line("COL", self.colour_id.as_ref())
    }
}

impl From<String> for Sprite {
    fn from(string: String) -> Sprite {
        let mut lines: Vec<&str> = string.lines().collect();

        let id = from_base36(&lines[0].replace("SPR ", ""));
        let mut name = None;
        let mut dialogue_id: 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("NAME") {
                name = Some(last_line.replace("NAME ", "").to_string());
            } else if last_line.starts_with("DLG") {
                dialogue_id = Some(last_line.replace("DLG ", "").to_string());
            } else if last_line.starts_with("POS") {
                let last_line = last_line.replace("POS ", "");
                let room_position: Vec<&str> = last_line.split(' ').collect();
                room_id = Some(from_base36(&room_position[0]));

                if room_position.len() < 2 {
                    panic!("Bad room/position for sprite: {}", string);
                }

                position = Some(Position::from(room_position[1].to_string()));
            } else if last_line.starts_with("COL") {
                colour_id = Some(last_line.replace("COL ", "").parse().unwrap());
            } else {
                lines.push(last_line);
                break;
            }
        }

        // todo dedupe
        let animation_frames = lines[1..].join("");
        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();

        Sprite { id, name, animation_frames, dialogue_id, room_id, position, colour_id }
    }
}

impl ToString for Sprite {
    #[inline]
    fn to_string(&self) -> String {
        format!(
            "SPR {}\n{}{}{}{}{}",
            self.id.to_base36(),
            self.animation_frames.to_string(),
            self.name_line(),
            self.dialogue_line(),
            self.room_position_line(),
            self.colour_line(),
        )
    }
}

#[test]
fn test_sprite_from_string() {
    let output = Sprite::from(
        include_str!("test-resources/sprite").to_string()
    );

    let expected = mock::sprite();

    assert_eq!(output, expected);
}

#[test]
fn test_sprite_to_string() {
    assert_eq!(mock::sprite().to_string(), include_str!("test-resources/sprite").to_string());
}