bitsy_parser/
tile.rs

1use crate::{optional_data_line, AnimationFrames, Image};
2use crate::image::animation_frames_from_str;
3
4#[derive(Clone, Debug, Eq)]
5pub struct Tile {
6    pub id: String,
7    pub name: Option<String>,
8    /// this is "optional" in that a tile can have `WAL true`, `WAL false` or neither
9    /// obviously Some(false) is the same as None but we want to preserve the original formatting
10    pub wall: Option<bool>,
11    pub animation_frames: Vec<Image>,
12    pub colour_id: Option<u64>,
13}
14
15impl PartialEq for Tile {
16    fn eq(&self, other: &Self) -> bool {
17        self.wall == other.wall
18        &&
19        self.animation_frames == other.animation_frames
20        &&
21        self.colour_id == other.colour_id
22    }
23}
24
25impl Tile {
26    fn name_line(&self) -> String {
27        optional_data_line("NAME", self.name.as_ref())
28    }
29
30    fn wall_line(&self) -> String {
31        if self.wall.is_some() {
32            format!("\nWAL {}", self.wall.unwrap())
33        } else {
34            "".to_string()
35        }
36    }
37
38    fn colour_line(&self) -> String {
39        if self.colour_id.is_some() {
40            format!("\nCOL {}", self.colour_id.unwrap())
41        } else {
42            "".to_string()
43        }
44    }
45
46    // todo refactor
47
48    pub fn invert(&mut self) {
49        self.animation_frames = self.animation_frames.iter().map(|frame: &Image| {
50            let mut image = frame.clone();
51            image.invert();
52            image
53        }).collect()
54    }
55
56    pub fn flip(&mut self) {
57        self.animation_frames = self.animation_frames.iter().map(|frame: &Image| {
58            let mut image = frame.clone();
59            image.flip();
60            image
61        }).collect()
62    }
63
64    pub fn mirror(&mut self) {
65        self.animation_frames = self.animation_frames.iter().map(|frame: &Image| {
66            let mut image = frame.clone();
67            image.mirror();
68            image
69        }).collect()
70    }
71
72    pub fn rotate(&mut self) {
73        self.animation_frames = self.animation_frames.iter().map(|frame: &Image| {
74            let mut image = frame.clone();
75            image.rotate();
76            image
77        }).collect()
78    }
79}
80
81impl From<String> for Tile {
82    fn from(string: String) -> Tile {
83        let mut lines: Vec<&str> = string.lines().collect();
84
85        let id = lines[0].replace("TIL ", "");
86
87        let mut wall = None;
88        let mut name = None;
89        let mut colour_id = None;
90
91        loop {
92            let last_line = lines.pop().unwrap();
93
94            if last_line.starts_with("WAL") {
95                wall = Some(last_line.ends_with("true"));
96            } else if last_line.starts_with("NAME") {
97                name = Some(last_line.replace("NAME ", "").to_string());
98            } else if last_line.starts_with("COL") {
99                colour_id = Some(last_line.replace("COL ", "").parse().unwrap());
100            } else {
101                lines.push(last_line);
102                break;
103            }
104        }
105
106        let animation_frames = animation_frames_from_str(
107            &lines[1..].join("\n")
108        );
109
110        Tile {
111            id,
112            name,
113            wall,
114            animation_frames,
115            colour_id,
116        }
117    }
118}
119
120impl ToString for Tile {
121    fn to_string(&self) -> String {
122        format!(
123            "TIL {}\n{}{}{}{}",
124            self.id,
125            self.animation_frames.to_string(),
126            self.name_line(),
127            self.wall_line(),
128            self.colour_line(),
129        )
130    }
131}
132
133#[cfg(test)]
134mod test {
135    use crate::{Image, Tile, mock};
136
137    #[test]
138    fn tile_from_string() {
139        let output = Tile::from(include_str!("test-resources/tile").to_string());
140
141        let expected = Tile {
142            id: "z".to_string(),
143            name: Some("concrete 1".to_string()),
144            wall: Some(true),
145            animation_frames: vec![Image {
146                pixels: vec![1; 64],
147            }],
148            colour_id: None,
149        };
150
151        assert_eq!(output, expected);
152    }
153
154    #[test]
155    fn tile_to_string() {
156        let output = Tile {
157            id: "7a".to_string(),
158            name: Some("chequers".to_string()),
159            wall: None,
160            animation_frames: vec![
161                mock::image::chequers_1(),
162                mock::image::chequers_2(),
163            ],
164            colour_id: None,
165        }
166            .to_string();
167
168        let expected = include_str!("test-resources/tile-chequers").to_string();
169
170        assert_eq!(output, expected);
171    }
172
173    #[test]
174    fn partial_eq() {
175        let     tile_a = crate::mock::tile_default();
176        let mut tile_b = crate::mock::tile_default();
177        tile_b.id = "0".to_string();
178        assert_eq!(tile_a, tile_b);
179        tile_b.name = None;
180        assert_eq!(tile_a, tile_b);
181    }
182}