use crate::{Exit, ExitInstance, Instance, Position, from_base36, ToBase36, optional_data_line}; #[derive(Debug, Eq, PartialEq)] pub struct Room { pub id: u64, pub palette_id: Option, // optional in very early versions pub name: Option, pub tiles: Vec, // tile ids pub items: Vec, pub exits: Vec, pub endings: Vec, pub walls: Vec, // old way of handling walls... } impl Room { fn name_line(&self) -> String { optional_data_line("NAME", self.name.as_ref()) } fn wall_line(&self) -> String { if self.walls.len() > 0 { let ids: Vec = self.walls.iter().map(|&id| id.to_base36()).collect(); optional_data_line("WAL", Some(ids.join(","))) } else { "".to_string() } } fn palette_line(&self) -> String { if self.palette_id.is_some() { optional_data_line("PAL", Some(self.palette_id.unwrap().to_base36())) } else { "".to_string() } } } impl From for Room { fn from(string: String) -> Room { let string = string.replace("ROOM ", ""); let string = string.replace("SET ", ""); let mut lines: Vec<&str> = string.lines().collect(); let id = from_base36(&lines[0]); let mut name = None; let mut palette_id = None; let mut items: Vec = Vec::new(); let mut exits: Vec = Vec::new(); let mut endings: Vec = Vec::new(); let mut walls: Vec = Vec::new(); loop { let last_line = lines.pop().unwrap(); if last_line.starts_with("WAL") { let last_line = last_line.replace("WAL ", ""); let ids: Vec<&str> = last_line.split(",").collect(); walls = ids.iter().map(|&id| from_base36(id)).collect(); } else if last_line.starts_with("NAME") { name = Some(last_line.replace("NAME ", "").to_string()); } else if last_line.starts_with("PAL") { palette_id = Some(from_base36(&last_line.replace("PAL ", ""))); } else if last_line.starts_with("ITM") { let last_line = last_line.replace("ITM ", ""); let item_position: Vec<&str> = last_line.split(' ').collect(); let item_id = item_position[0]; let position = item_position[1]; let position = Position::from(position.to_string()); items.push(Instance { position, id: item_id.to_string() }); } else if last_line.starts_with("EXT") { let last_line = last_line.replace("EXT ", ""); let parts: Vec<&str> = last_line.split(' ').collect(); let position = Position::from(parts[0].to_string()); let exit = Exit::from(format!("{} {}", parts[1], parts[2])); exits.push(ExitInstance { position, exit }); } else if last_line.starts_with("END") { let last_line = last_line.replace("END ", ""); let ending_position: Vec<&str> = last_line.split(' ').collect(); let ending = ending_position[0].to_string(); let position = ending_position[1].to_string(); let position = Position::from(position); endings.push(Instance { position, id: ending }); } else { lines.push(last_line); break; } } let lines = &lines[1..]; let mut tiles: Vec = Vec::new(); for line in lines.into_iter() { let line: Vec<&str> = if line.contains(",") { line.split(",").collect() } else { // old room format line.split("").collect() }; for tile_id in line { tiles.push(tile_id.to_string()); } } items.reverse(); exits.reverse(); endings.reverse(); Room { id, palette_id, name, tiles, items, exits, endings, walls } } } #[test] fn test_room_from_string() { assert_eq!( Room::from(include_str!("test-resources/room").to_string()), crate::mock::room() ); } impl ToString for Room { fn to_string(&self) -> String { let mut tiles = String::new(); let mut items = String::new(); let mut exits = String::new(); let mut endings = String::new(); let sqrt = (self.tiles.len() as f64).sqrt() as usize; // 8 for SD, 16 for HD for line in self.tiles.chunks(sqrt) { for tile in line { tiles.push_str(&format!("{},", tile)); } tiles.pop(); // remove trailing comma tiles.push_str("\n"); } tiles.pop(); // remove trailing newline for instance in &self.items { items.push_str( &format!("\nITM {} {}", instance.id, instance.position.to_string()) ); } for instance in &self.exits { exits.push_str( &format!( "\nEXT {} {}", instance.position.to_string(), instance.exit.to_string(), ) ); } for instance in &self.endings { endings.push_str( &format!("\nEND {} {}", instance.id, instance.position.to_string()) ); } format!( "ROOM {}\n{}{}{}{}{}{}{}", self.id.to_base36(), tiles, self.name_line(), self.wall_line(), items, exits, endings, self.palette_line() ) } } #[test] fn test_room_to_string() { assert_eq!(crate::mock::room().to_string(), include_str!("test-resources/room").to_string()); } #[test] fn test_room_walls_array() { let output = Room::from( include_str!("test-resources/room-with-walls").to_string() ); assert_eq!(output.walls, vec![10, 15]); }