//! The map is a visual interpretation of all the items that make up the world. //! //! It's the main structure that the client uses to interact with the world, since //! the world contains all the pub mod icon_mark; pub mod icon_texture_manager; pub mod mappable; pub mod room_mark; pub mod wall_mark; pub use icon_mark::*; pub use mappable::Mappable; pub use room_mark::*; pub use wall_mark::*; use crate::client::Transform; use crate::stable_vec::StableVec; use crate::world::{Room, Wall, World}; use icon_texture_manager::IconTextureManager; use raylib::drawing::RaylibDrawHandle; use raylib::{RaylibHandle, RaylibThread}; use std::ops::Deref; use std::rc::Rc; /// The map containing all map elements that are seen on the screen. pub struct Map { rooms: StableVec, walls: StableVec, icons: StableVec, used_ids: StableVec<()>, icon_renderer: Rc, } impl Map { /// Create a new, empty map/world. pub fn new(rl: &mut RaylibHandle, rlt: &RaylibThread) -> Self { Self { rooms: StableVec::new(), walls: StableVec::new(), icons: StableVec::new(), used_ids: StableVec::new(), icon_renderer: Rc::new(IconTextureManager::new(rl, rlt)), } } pub fn add_room(&mut self, id: usize, room: Room) -> bool { if self.used_ids.try_insert(id, ()).is_ok() { self.rooms .try_insert(id, RoomMark::from_room(room)) .unwrap(); true } else { error!("Unable to add room. Id already in use."); false } } /// Add a wall with a specific id. May fail if there already is an entity with that id. pub fn add_wall(&mut self, id: usize, wall: Wall) -> bool { if self.used_ids.try_insert(id, ()).is_ok() { /* Check for intersections with any wall that was arleady created so the wall ends can be * rendered properly. */ let mut start_intersects = false; let mut end_intersects = false; for (_, wall) in self.walls.iter() { if wall.shape().contains_collinear(wall.shape().start) { start_intersects = true; } if wall.shape().contains_collinear(wall.shape().end) { end_intersects = true; } // Currently, additional intersections can be ignored, since it is just a yes-no-question if start_intersects && end_intersects { break; } } self.walls .try_insert( id, WallMark::from_wall(wall, start_intersects, end_intersects), ) .unwrap(); true } else { error!("Unable to add wall. Id already in use."); false } } /// Add an icon with a specific id. May fail if there already is an entity with that id. pub fn add_icon(&mut self, id: usize, icon: IconMark) -> bool { if self.used_ids.try_insert(id, ()).is_ok() { self.icons.try_insert(id, icon).unwrap(); true } else { error!("Unable to add icon. Id already in use."); false } } /// Draw all elements of the map to the screen. This should be called after the background of the /// map has already been drawn. pub fn draw(&self, rld: &mut RaylibDrawHandle, transform: &Transform) { for (_, element) in self.elements() { element.draw(rld, transform); } } /// Get the icon-renderer that is currently used to render the icons. pub fn icon_renderer(&self) -> Rc { self.icon_renderer.clone() } /// Remove item with the id, if it exists. Returns `true` if an item existed, `false` otherwise. pub fn remove(&mut self, id: usize) -> bool { if self.used_ids.remove(id).is_none() { return false; } if self.rooms.remove(id).is_some() || self.walls.remove(id).is_some() || self.icons.remove(id).is_some() { true } else { panic!( "Id {} was still registered, eventhough there was no such entity.", id ); } } /// Iterator over all elements as objects when an operation needs to go over all elements of the /// map. pub fn elements(&self) -> impl Iterator { self.rooms .iter() .map(|(id, p)| (*id, p as &dyn Mappable)) .chain(self.walls.iter().map(|(id, w)| (*id, w as &dyn Mappable))) .chain(self.icons.iter().map(|(id, i)| (*id, i as &dyn Mappable))) } /// Iterator over all elements, but the individual elements can be mutated. It is however /// impossible to add or remove elements in this way. For that, use the dedicated functions. pub fn elements_mut(&mut self) -> impl Iterator { self.rooms .id_iter_mut() .map(|(id, p)| (id, p as &mut dyn Mappable)) .chain( self.walls .id_iter_mut() .map(|(id, w)| (id, w as &mut dyn Mappable)), ) .chain( self.icons .id_iter_mut() .map(|(id, i)| (id, i as &mut dyn Mappable)), ) } /// Get the rooms of this map. pub fn rooms(&self) -> &StableVec { &self.rooms } /// Get the walls of this map. pub fn walls(&self) -> &StableVec { &self.walls } /// Get the icons of this map. pub fn icons(&self) -> &StableVec { &self.icons } /// Replace the internal map data with the data of the world provided. /// (Load and replace) pub fn set_data(&mut self, world: World) { // Remove all data. self.icons.clear(); self.rooms.clear(); self.walls.clear(); // Add all data from the map data. self.add_data(world); } /// Add the data provided to the current data on the map. All elements will /// remain, with the additional elements being pushed also. This must be /// used with caution, since the ids of the items will remain unchanged, and /// items with the same id will therefore be ignored and not added. pub fn add_data(&mut self, world: World) { for (id, i) in world.icons().iter() { self.add_icon( *id, IconMark::from_icon(i.clone(), self.icon_renderer.clone()), ); } for (id, r) in world.rooms().iter() { self.add_room(*id, r.clone()); } for (id, w) in world.walls().iter() { self.add_wall(*id, w.clone()); } } /// Creates a world from pub fn clone_as_world(&self) -> World { let rooms = self .rooms .iter() .map(|(id, mark)| (*id, mark.deref().clone())) .collect(); let rooms = StableVec::from_raw_unchecked(rooms); let icons = self .icons .iter() .map(|(id, mark)| (*id, mark.deref().clone())) .collect(); let icons = StableVec::from_raw_unchecked(icons); let walls = self .walls .iter() .map(|(id, mark)| (*id, mark.deref().clone())) .collect(); let walls = StableVec::from_raw_unchecked(walls); World::from_raw_unchecked(rooms, walls, icons, self.used_ids.clone()) } }