diff options
Diffstat (limited to 'src/client/map/mod.rs')
| -rw-r--r-- | src/client/map/mod.rs | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/src/client/map/mod.rs b/src/client/map/mod.rs new file mode 100644 index 0000000..eaab72f --- /dev/null +++ b/src/client/map/mod.rs @@ -0,0 +1,206 @@ +//! 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::rc::Rc; + +/// The map containing all map elements that are seen on the screen. +pub struct Map { + rooms: StableVec<RoomMark>, + walls: StableVec<WallMark>, + icons: StableVec<IconMark>, + used_ids: StableVec<()>, + icon_renderer: Rc<IconTextureManager>, +} + +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<IconTextureManager> { + 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<Item = (usize, &dyn Mappable)> { + 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<Item = (usize, &mut dyn Mappable)> { + 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<RoomMark> { + &self.rooms + } + + /// Get the walls of this map. + pub fn walls(&self) -> &StableVec<WallMark> { + &self.walls + } + + /// Get the icons of this map. + pub fn icons(&self) -> &StableVec<IconMark> { + &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, 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()); + } + } +} |
