diff options
Diffstat (limited to 'src/map')
| -rw-r--r-- | src/map/data.rs | 87 | ||||
| -rw-r--r-- | src/map/icon.rs | 102 | ||||
| -rw-r--r-- | src/map/icon_renderer.rs | 91 | ||||
| -rw-r--r-- | src/map/mappable.rs | 35 | ||||
| -rw-r--r-- | src/map/mod.rs | 171 | ||||
| -rw-r--r-- | src/map/room.rs | 98 | ||||
| -rw-r--r-- | src/map/wall.rs | 125 |
7 files changed, 0 insertions, 709 deletions
diff --git a/src/map/data.rs b/src/map/data.rs deleted file mode 100644 index 20f193d..0000000 --- a/src/map/data.rs +++ /dev/null @@ -1,87 +0,0 @@ -//! Module containing the raw map data version of the map. - -use super::{IconData, Map, RoomData, WallData}; -use ron::de::from_reader; -use ron::ser::{to_string_pretty, PrettyConfig}; -use serde::{Deserialize, Serialize}; -use std::fs::File; -use std::io::{self, Write}; -use std::path::Path; - -/// The serialisable and deserialisable parts of the map. This can be created to get a version of the -/// map which is persistifiable or sendable/receivable without data overhead or data that might make -/// it easily corruptable. -#[derive(Serialize, Deserialize)] -pub struct MapData { - pub(super) rooms: Vec<RoomData>, - pub(super) walls: Vec<WallData>, - pub(super) icons: Vec<IconData>, -} - -impl MapData { - /// Create a serialisable map data type from the data elements contained in a map. - pub fn new(rooms: Vec<RoomData>, walls: Vec<WallData>, icons: Vec<IconData>) -> Self { - Self { - rooms, - walls, - icons, - } - } - - /// Creates a data struct from the Map. It is important to note, that this data element is not - /// bound to the Map in any way after this, so changing anything won't change anything in the map. - /// It is useful however to for instance serialize this map without extra rendering information - /// included. - pub fn extract_data(map: &Map) -> Self { - Self { - rooms: map - .rooms() - .iter() - .map(|p| (p as &RoomData).clone()) - .collect(), - walls: map - .walls() - .iter() - .map(|w| (w as &WallData).clone()) - .collect(), - icons: map - .icons() - .iter() - .map(|i| (i as &IconData).clone()) - .collect(), - } - } - - /// Load the map data from a file. Fails if the file does not exist or cannot be correctly parsed. - pub fn load_from_file<P: AsRef<Path>>(path: P) -> io::Result<Self> { - let file = File::open(&path)?; - let data: Self = match from_reader(file) { - Ok(data) => data, - Err(err) => { - return Err(io::Error::new(io::ErrorKind::InvalidData, err)); - } - }; - - Ok(data) - } - - /// Write the map data to the file located at `path`. If the file already exists, it will be - /// overwritten. If the write fails, an IO-Error is returned. - pub fn write_to_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> { - let mut file = File::create(&path)?; - - let pretty_conf = PrettyConfig::new() - .with_depth_limit(4) - .with_decimal_floats(true) - .with_separate_tuple_members(true) - .with_indentor("\t".to_owned()); - let string = match to_string_pretty(&self, pretty_conf) { - Ok(string) => string, - Err(err) => { - return Err(io::Error::new(io::ErrorKind::InvalidInput, err)); - } - }; - - file.write_all(&string.as_bytes()) - } -} diff --git a/src/map/icon.rs b/src/map/icon.rs deleted file mode 100644 index 2e45486..0000000 --- a/src/map/icon.rs +++ /dev/null @@ -1,102 +0,0 @@ -//! Icons are map elements that have a specific size and cannot be stretched. They are usually used -//! as markers for specific places in the world. - -use super::icon_renderer::IconRenderer; -use crate::colours::DEFAULT_COLOURS; -use crate::map::Mappable; -use crate::math::{Rect, Vec2}; -use crate::transform::Transform; -use raylib::core::drawing::{RaylibDraw, RaylibDrawHandle}; -use serde::{Deserialize, Serialize}; -use std::ops::{Deref, DerefMut}; -use std::rc::Rc; - -/// The icon data necessary to create an Icon again. -#[derive(Clone, Serialize, Deserialize)] -pub struct IconData { - /// The id of the icon is the icons position in the currently loaded icon_data vector. - pub id: usize, - /// The position of the icon on the map, given by the vector in meters. - pub position: Vec2<f64>, - /// Rotation of the icon texture in degrees. - pub rotation: f64, -} - -/// Describes an icon in the world and can be drawn. -#[derive(Clone)] -pub struct Icon { - data: IconData, - selected: bool, - renderer: Rc<IconRenderer>, -} - -impl Icon { - /// Create a new icon. Requires the icon renderer that is used to render this icon, as well as all - /// the information necessary to describe the icon itself. - pub fn new(id: usize, position: Vec2<f64>, rotation: f64, renderer: Rc<IconRenderer>) -> Self { - Self::from_data( - IconData { - id, - position, - rotation, - }, - renderer, - ) - } - - /// Like `new()`, but with the icon data bundled into the icon data type. - pub fn from_data(data: IconData, renderer: Rc<IconRenderer>) -> Self { - Self { - data, - selected: false, - renderer, - } - } -} - -impl Mappable for Icon { - fn draw(&self, rld: &mut RaylibDrawHandle, transform: &Transform) { - let (texture, info) = self.renderer.get(self.id); - // Round the position to whole pixels to fix rotation problems. - let mut position_px = - transform.point_m_to_px(&(self.position - (info.anchor / info.pixels_per_m))); - position_px.x = position_px.x.floor(); - position_px.y = position_px.y.floor(); - rld.draw_texture_ex( - texture, - position_px, - self.rotation as f32, - (transform.pixels_per_m() / info.pixels_per_m) as f32, - if self.selected() { - DEFAULT_COLOURS.icon_selected - } else { - DEFAULT_COLOURS.icon_normal - }, - ); - } - - fn set_selected(&mut self, selected: bool) { - self.selected = selected; - } - - fn selected(&self) -> bool { - self.selected - } - - fn bounding_rect(&self) -> Rect<f64> { - Rect::new(self.data.position.x, self.data.position.y, 0., 0.) - } -} - -impl Deref for Icon { - type Target = IconData; - - fn deref(&self) -> &Self::Target { - &self.data - } -} -impl DerefMut for Icon { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.data - } -} diff --git a/src/map/icon_renderer.rs b/src/map/icon_renderer.rs deleted file mode 100644 index eef053d..0000000 --- a/src/map/icon_renderer.rs +++ /dev/null @@ -1,91 +0,0 @@ -//! Since the same icon may be used very often on a map, it becomes impracticalto let every icon have -//! it's own texture data saved, which is why a texture manager type of struct is used for it instead. - -use crate::math::Vec2; -use raylib::core::texture::Texture2D; -use raylib::{RaylibHandle, RaylibThread}; -use ron::de::from_reader; -use serde::Deserialize; -use std::fs::{self, File}; - -/// The directory containing all files related to icons. -pub const ICON_DIR: &str = "assets/icons"; - -#[derive(Deserialize)] -pub(super) struct IconFileInfo { - /// The position the icon should be anchored in pixels. This is the Vector it will be moved by - /// relative to the mouse pointer (to the left and up). - pub anchor: Vec2<f64>, - /// The scale of the icon as expressed in image pixels per real meter. - pub pixels_per_m: f64, -} - -/// Manager for all icon texture or rendering data. -pub struct IconRenderer { - texture_data: Vec<(Texture2D, IconFileInfo)>, -} - -impl IconRenderer { - /// Create a new icon renderer. This loads all textures and information for icons that is needed - /// to draw them to the screen. Usually, there should be only one IconRenderer, or at least one - /// renderer per directory and program instance. - pub fn new(rl: &mut RaylibHandle, rlt: &RaylibThread) -> Self { - /* Read all available icons from the icon directory. SVGs do not need any special scale - * file, but pixel-based file formats require a RON-file declaring what the scale of the - * picture is right beside them. - */ - let mut image_files = Vec::new(); - for entry in fs::read_dir(ICON_DIR).expect("Could not open icon directory") { - let entry = entry.expect("Failed to read file from icon directory"); - - // Ignore the RON-files for now and put the image files into the vec - if entry - .path() - .extension() - .expect("Entry does not have a file extension") - != "ron" - { - image_files.push(entry); - } - } - - // Read the RON-files where it is necessary. - let mut texture_data = Vec::with_capacity(image_files.len()); - for file in image_files { - // TODO: Handle svg - - let texture = rl - .load_texture( - rlt, - file.path() - .to_str() - .expect("Unable to convert path to string."), - ) - .expect("Could not read image file"); - - let mut file = file.path(); - file.set_extension("ron"); - let ron = File::open(file).expect("Could not read ron file for icon information."); - let icon_info: IconFileInfo = - from_reader(ron).expect("Could not parse icon info from reader."); - - texture_data.push((texture, icon_info)); - } - - Self { texture_data } - } - - /// Get the data needed to render an icon of type `icon_id`. - /// - /// # Panics - /// If the `icon_id` does not describe a valid icon (is out of bounds), there is no data to be - /// accessed and the function panics. - pub(super) fn get(&self, icon_id: usize) -> &(Texture2D, IconFileInfo) { - &self.texture_data[icon_id] - } - - /// The number of icons registered in this icon-renderer. - pub fn num_icons(&self) -> usize { - self.texture_data.len() - } -} diff --git a/src/map/mappable.rs b/src/map/mappable.rs deleted file mode 100644 index 7978f50..0000000 --- a/src/map/mappable.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! Something that's mappable can be placed on the map and drawn at a specific position. It has a -//! dimension on the map and may be transformable in various ways. - -use crate::math::Rect; -use crate::transform::Transform; -use crate::transformable::NonRigidTransformable; -use raylib::drawing::RaylibDrawHandle; - -/// Anything that can be added to the map or world must implement this trait. -pub trait Mappable { - /// Draw this to `rld` with the transform. An item that cannot be drawn is not mappable, so - /// this must always be implemented. - fn draw(&self, rld: &mut RaylibDrawHandle, transform: &Transform); - - /// Set the selection status of this item. If it is selected, actions that concern all selected - /// items will be applied to this item as well. - fn set_selected(&mut self, selected: bool); - - /// Get if this item is currently selected. - fn selected(&self) -> bool; - - /// Get the rectangle that contains the mappable object in its entirety without excess. - fn bounding_rect(&self) -> Rect<f64>; - - /// If this mappable item can be transformed in a non-rigid way, a dynamic reference is returned, - /// otherwise none. - fn as_non_rigid(&self) -> Option<&dyn NonRigidTransformable> { - None - } - - /// The same as `as_non_rigid`, but mutably. - fn as_non_rigid_mut(&mut self) -> Option<&mut dyn NonRigidTransformable> { - None - } -} diff --git a/src/map/mod.rs b/src/map/mod.rs deleted file mode 100644 index e1def09..0000000 --- a/src/map/mod.rs +++ /dev/null @@ -1,171 +0,0 @@ -//! The map contains all the items that make up the world. -//! -//! There are two main structs to look out for, the first being [Map]. This is the interaction point -//! for most parts of the program. It contains the actual elements that are drawn on the screen. and -//! can be changed by the user. -//! The second is [MapData] and it contains the data that can be saved/loaded and distributed. Every -//! map item has an internal item that it can be dereferenced to and can be used to construct this -//! exact item in the same world elsewhere or at a different time. This is often different from the -//! item that is being drawn. An example would be the [PolygonRoom], which contains a triangulated -//! version of itself, so it can be drawn without always having to compute the triangles every frame. -//! It's data type however [PolygonRoomData] contains only the raw polygon data, not the triangulated -//! version, since that is enough to create the same [PolygonRoom] again. - -pub mod data; -pub mod icon; -pub mod icon_renderer; -pub mod mappable; -pub mod room; -pub mod wall; - -pub use data::MapData; -pub use icon::*; -pub use mappable::Mappable; -pub use room::*; -pub use wall::*; - -use crate::transform::Transform; -use icon_renderer::IconRenderer; -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: Vec<Room>, - walls: Vec<Wall>, - icons: Vec<Icon>, - icon_renderer: Rc<IconRenderer>, -} - -impl Map { - /// Create a new, empty map/world. - pub fn new(rl: &mut RaylibHandle, rlt: &RaylibThread) -> Self { - Self { - rooms: Vec::new(), - walls: Vec::new(), - icons: Vec::new(), - icon_renderer: Rc::new(IconRenderer::new(rl, rlt)), - } - } - - /// Add a room to the map. Currently, holes are not supported in the polygon, but this might - /// change later. - pub fn push_room(&mut self, room_data: RoomData) { - self.rooms.push(Room::from_data(room_data)); - } - - /// Add a wall to the world. - pub fn push_wall(&mut self, wall_data: WallData) { - /* 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 { - if wall.data().contains_collinear(wall_data.start) { - start_intersects = true; - } - if wall.data().contains_collinear(wall_data.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 - .push(Wall::from_data(wall_data, start_intersects, end_intersects)); - } - - /// Add an icon to the world. - pub fn push_icon(&mut self, icon: Icon) { - self.icons.push(icon); - } - - /// 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<IconRenderer> { - self.icon_renderer.clone() - } - - /// Retain all map elements that fulfill the given predicate, removing everything else. - pub fn retain<F>(&mut self, mut f: F) - where - F: FnMut(&dyn Mappable) -> bool, - { - // Call retain on all vectors containing the maps actual types. - self.rooms.retain(|p| f(p as &dyn Mappable)); - self.walls.retain(|w| f(w as &dyn Mappable)); - self.icons.retain(|i| f(i as &dyn Mappable)); - } - - /// 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 = &dyn Mappable> { - self.rooms - .iter() - .map(|p| p as &dyn Mappable) - .chain(self.walls.iter().map(|w| w as &dyn Mappable)) - .chain(self.icons.iter().map(|i| 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 = &mut dyn Mappable> { - self.rooms - .iter_mut() - .map(|p| p as &mut dyn Mappable) - .chain(self.walls.iter_mut().map(|w| w as &mut dyn Mappable)) - .chain(self.icons.iter_mut().map(|i| i as &mut dyn Mappable)) - } - - /// Get the rooms of this map. - pub fn rooms(&self) -> &Vec<Room> { - &self.rooms - } - - /// Get the walls of this map. - pub fn walls(&self) -> &Vec<Wall> { - &self.walls - } - - /// Get the icons of this map. - pub fn icons(&self) -> &Vec<Icon> { - &self.icons - } - - /// Replace the internal map data with the data provided. (Load and replace) - pub fn set_data(&mut self, data: MapData) { - // Remove all data. - self.icons.clear(); - self.rooms.clear(); - self.walls.clear(); - - // Add all data from the map data. - self.add_data(data); - } - - /// Add the data provided to the current data on the map. All elements will remain, with the - /// additional elements being pushed also. - pub fn add_data(&mut self, data: MapData) { - for i in data.icons { - self.push_icon(Icon::from_data(i, self.icon_renderer.clone())) - } - for p in data.rooms { - self.push_room(p); - } - for w in data.walls { - self.push_wall(w); - } - } -} diff --git a/src/map/room.rs b/src/map/room.rs deleted file mode 100644 index 3050763..0000000 --- a/src/map/room.rs +++ /dev/null @@ -1,98 +0,0 @@ -//! Polygon rooms are the standard rooms in graf karto. They can take the form of anything that a -//! [Polygon](crate::math::Polygon) can have. - -use super::Mappable; -use crate::colours::DEFAULT_COLOURS; -use crate::math::{self, Polygon, Rect, Triangle}; -use crate::transform::Transform; -use crate::transformable::NonRigidTransformable; -use crate::FLOAT_MARGIN; -use nalgebra::{Matrix3, Point2}; -use raylib::drawing::{RaylibDraw, RaylibDrawHandle}; -use std::ops::Deref; - -/// Data type for the Polygon room. -pub type RoomData = Polygon<f64>; - -/// A polygon room, which can be placed and modified in the world. -pub struct Room { - data: RoomData, - // The polygon shape, but in triangles, so the polygon does not have to be triangulated every frame. - triangulated: Vec<Triangle<f64>>, - selected: bool, -} - -impl Room { - /// Create a room from the given polygon data. - pub fn from_data(data: RoomData) -> Self { - Self { - data: data.clone(), - triangulated: math::triangulate(data, FLOAT_MARGIN), - selected: false, - } - } - - /* When the internal polygon changes, it must be retriangulated to be drawn on the screen - * properly, so this function must be called any time that happens. - */ - fn retriangulate(&mut self) { - self.triangulated = math::triangulate(self.data.clone(), FLOAT_MARGIN); - } -} - -impl Mappable for Room { - fn draw(&self, rld: &mut RaylibDrawHandle, transform: &Transform) { - for triangle in &self.triangulated { - rld.draw_triangle( - transform.point_m_to_px(&triangle.corners()[0]), - transform.point_m_to_px(&triangle.corners()[1]), - transform.point_m_to_px(&triangle.corners()[2]), - if self.selected() { - DEFAULT_COLOURS.room_selected - } else { - DEFAULT_COLOURS.room_normal - }, - ) - } - } - - fn set_selected(&mut self, selected: bool) { - self.selected = selected; - } - - fn selected(&self) -> bool { - self.selected - } - - fn bounding_rect(&self) -> Rect<f64> { - Rect::bounding_rect_n(&self.data.corners()) - } - - fn as_non_rigid(&self) -> Option<&dyn NonRigidTransformable> { - Some(self as &dyn NonRigidTransformable) - } - - fn as_non_rigid_mut(&mut self) -> Option<&mut dyn NonRigidTransformable> { - Some(self as &mut dyn NonRigidTransformable) - } -} - -impl NonRigidTransformable for Room { - fn apply_matrix(&mut self, matrix: &Matrix3<f64>) { - for corner in self.data.corners_mut() { - *corner = matrix - .transform_point(&Point2::new(corner.x, corner.y)) - .into(); - } - - self.retriangulate(); - } -} - -impl Deref for Room { - type Target = RoomData; - - fn deref(&self) -> &Self::Target { - &self.data - } -} diff --git a/src/map/wall.rs b/src/map/wall.rs deleted file mode 100644 index f1748bb..0000000 --- a/src/map/wall.rs +++ /dev/null @@ -1,125 +0,0 @@ -//! Walls, solid barriers that are generally unscaleable. -//! -//! This interpretation is generally up to the GM to decide, but generally speaking, a wall cannot be -//! crossed by a player. If special conditions apply (for instance, when the player wants to scale the -//! wall), a check is necessary. If a check is not necessary, then maybe you were not thinking about -//! a wall. - -use super::Mappable; -use crate::colours::DEFAULT_COLOURS; -use crate::math::{LineSegment, Rect, Vec2}; -use crate::transform::Transform; -use crate::transformable::NonRigidTransformable; -use nalgebra::Matrix3; -use raylib::drawing::{RaylibDraw, RaylibDrawHandle}; -use std::ops::{Deref, DerefMut}; - -/// The data which defines a wall segment completely for serialisation. -pub type WallData = LineSegment<f64>; - -/// A solid wall a player cannot go through, except if special conditions apply. -pub struct Wall { - data: WallData, - selected: bool, - round_start: bool, - round_end: bool, -} - -impl Wall { - /// Create a new wall from the deserialised data and information known from internal sources. - pub fn from_data(data: WallData, round_start: bool, round_end: bool) -> Self { - Self { - data, - selected: false, - round_start, - round_end, - } - } - - /// Get the internal data for serialisation - pub fn data(&self) -> &WallData { - &self.data - } -} - -fn draw_round_corner( - rld: &mut RaylibDrawHandle, - pos_px: Vec2<f64>, - transform: &Transform, - selected: bool, -) { - rld.draw_circle_v( - pos_px, - transform.length_m_to_px(0.05) as f32, - if selected { - DEFAULT_COLOURS.wall_selected - } else { - DEFAULT_COLOURS.wall_normal - }, - ); -} - -impl Mappable for Wall { - fn draw(&self, rld: &mut RaylibDrawHandle, transform: &Transform) { - let start_px = transform.point_m_to_px(&self.data.start); - let end_px = transform.point_m_to_px(&self.data.end); - rld.draw_line_ex( - start_px, - end_px, - transform.length_m_to_px(0.1) as f32, - if self.selected() { - DEFAULT_COLOURS.wall_selected - } else { - DEFAULT_COLOURS.wall_normal - }, - ); - - if self.round_start { - draw_round_corner(rld, start_px, transform, self.selected()); - } - if self.round_end { - draw_round_corner(rld, end_px, transform, self.selected()); - } - } - - fn set_selected(&mut self, selected: bool) { - self.selected = selected; - } - - fn selected(&self) -> bool { - self.selected - } - - fn bounding_rect(&self) -> Rect<f64> { - Rect::bounding_rect(self.data.start, self.data.end) - } - - fn as_non_rigid(&self) -> Option<&dyn NonRigidTransformable> { - Some(self as &dyn NonRigidTransformable) - } - - fn as_non_rigid_mut(&mut self) -> Option<&mut dyn NonRigidTransformable> { - Some(self as &mut dyn NonRigidTransformable) - } -} - -impl NonRigidTransformable for Wall { - fn apply_matrix(&mut self, matrix: &Matrix3<f64>) { - self.data.start = matrix.transform_point(&self.data.start.into()).into(); - self.data.end = matrix.transform_point(&self.data.end.into()).into(); - } -} - -impl Deref for Wall { - type Target = WallData; - - fn deref(&self) -> &Self::Target { - &self.data - } -} - -impl DerefMut for Wall { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.data - } -} |
