aboutsummaryrefslogtreecommitdiff
path: root/src/map
diff options
context:
space:
mode:
Diffstat (limited to 'src/map')
-rw-r--r--src/map/data.rs6
-rw-r--r--src/map/icon.rs8
-rw-r--r--src/map/icon_renderer.rs14
-rw-r--r--src/map/mappable.rs14
-rw-r--r--src/map/mod.rs29
-rw-r--r--src/map/polygon_room.rs34
-rw-r--r--src/map/rect_room.rs23
-rw-r--r--src/map/wall.rs36
8 files changed, 120 insertions, 44 deletions
diff --git a/src/map/data.rs b/src/map/data.rs
index f978081..1031d3c 100644
--- a/src/map/data.rs
+++ b/src/map/data.rs
@@ -1,3 +1,5 @@
+//! Module containing the raw map data version of the map.
+
use super::{IconData, PolygonRoomData, RectRoomData, WallData};
use ron::de::from_reader;
use ron::ser::{to_string_pretty, PrettyConfig};
@@ -18,6 +20,7 @@ pub struct MapData {
}
impl MapData {
+ /// Create a serialisable map data type from the data elements contained in a map.
pub fn new(
rect_rooms: Vec<RectRoomData>,
polygon_rooms: Vec<PolygonRoomData>,
@@ -32,6 +35,7 @@ impl MapData {
}
}
+ /// 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>>(&mut self, path: P) -> io::Result<Self> {
let file = File::open(&path)?;
let data: Self = match from_reader(file) {
@@ -44,6 +48,8 @@ impl MapData {
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)?;
diff --git a/src/map/icon.rs b/src/map/icon.rs
index f623c98..2e45486 100644
--- a/src/map/icon.rs
+++ b/src/map/icon.rs
@@ -1,3 +1,6 @@
+//! 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;
@@ -8,6 +11,7 @@ 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.
@@ -18,6 +22,7 @@ pub struct IconData {
pub rotation: f64,
}
+/// Describes an icon in the world and can be drawn.
#[derive(Clone)]
pub struct Icon {
data: IconData,
@@ -26,6 +31,8 @@ pub struct Icon {
}
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 {
@@ -37,6 +44,7 @@ impl Icon {
)
}
+ /// 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,
diff --git a/src/map/icon_renderer.rs b/src/map/icon_renderer.rs
index fb81e24..eef053d 100644
--- a/src/map/icon_renderer.rs
+++ b/src/map/icon_renderer.rs
@@ -1,3 +1,6 @@
+//! 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};
@@ -5,6 +8,7 @@ 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)]
@@ -16,11 +20,15 @@ pub(super) struct IconFileInfo {
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
@@ -67,10 +75,16 @@ impl IconRenderer {
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
index b348c4b..7978f50 100644
--- a/src/map/mappable.rs
+++ b/src/map/mappable.rs
@@ -1,11 +1,12 @@
//! 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 scaleable
+//! dimension on the map and may be transformable in various ways.
use crate::math::Rect;
-use crate::scaleable::Scaleable;
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.
@@ -21,7 +22,14 @@ pub trait Mappable {
/// Get the rectangle that contains the mappable object in its entirety without excess.
fn bounding_rect(&self) -> Rect<f64>;
- fn as_scaleable(&self) -> Option<&dyn Scaleable> {
+ /// 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
index ff03474..88a7e6c 100644
--- a/src/map/mod.rs
+++ b/src/map/mod.rs
@@ -1,3 +1,16 @@
+//! 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;
@@ -19,6 +32,7 @@ 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 {
rect_rooms: Vec<RectRoom>,
polygon_rooms: Vec<PolygonRoom>,
@@ -28,6 +42,7 @@ pub struct Map {
}
impl Map {
+ /// Create a new, empty map/world.
pub fn new(rl: &mut RaylibHandle, rlt: &RaylibThread) -> Self {
Self {
rect_rooms: Vec::new(),
@@ -38,14 +53,20 @@ impl Map {
}
}
+ /// Add a rectangularly shaped room to the world. Since the polygon room tool was added, and
+ /// afaik rects are polygon rooms, this will be phased out in favour of only having polygon
+ /// rooms, which will then become just normal rooms.
pub fn push_rect_room(&mut self, room_data: RectRoomData) {
self.rect_rooms.push(RectRoom::from_data(room_data));
}
+ /// Add a room to the map. Currently, holes are not supported in the polygon, but this might
+ /// change later.
pub fn push_polygon_room(&mut self, room_data: PolygonRoomData) {
self.polygon_rooms.push(PolygonRoom::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.
@@ -70,21 +91,25 @@ impl Map {
.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.
+ /// 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,
@@ -107,6 +132,8 @@ impl Map {
.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.rect_rooms
.iter_mut()
diff --git a/src/map/polygon_room.rs b/src/map/polygon_room.rs
index a209a7c..fd4122e 100644
--- a/src/map/polygon_room.rs
+++ b/src/map/polygon_room.rs
@@ -1,12 +1,18 @@
+//! 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, Vec2};
-use crate::scaleable::Scaleable;
+use crate::math::{self, Polygon, Rect, Triangle};
use crate::transform::Transform;
+use crate::transformable::NonRigidTransformable;
+use nalgebra::{Matrix3, Point2};
use raylib::drawing::{RaylibDraw, RaylibDrawHandle};
+/// Data type for the Polygon room.
pub type PolygonRoomData = Polygon<f64>;
+/// A polygon room, which can be placed and modified in the world.
pub struct PolygonRoom {
data: PolygonRoomData,
// The polygon shape, but in triangles, so the polygon does not have to be triangulated every frame.
@@ -15,6 +21,7 @@ pub struct PolygonRoom {
}
impl PolygonRoom {
+ /// Create a room from the given polygon data.
pub fn from_data(data: PolygonRoomData) -> Self {
Self {
data: data.clone(),
@@ -23,6 +30,8 @@ impl PolygonRoom {
}
}
+ // 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());
}
@@ -56,20 +65,21 @@ impl Mappable for PolygonRoom {
Rect::bounding_rect_n(&self.data.corners())
}
- fn as_scaleable(&self) -> Option<&dyn Scaleable> {
- Some(self as &dyn Scaleable)
+ fn as_non_rigid(&self) -> Option<&dyn NonRigidTransformable> {
+ Some(self as &dyn NonRigidTransformable)
}
-}
-impl Scaleable for PolygonRoom {
- fn scale(&mut self, by: &Vec2<f64>) {
- if by.x < 0. || by.y < 0. {
- panic!("Cannot set dimensions with negative size");
- }
+ fn as_non_rigid_mut(&mut self) -> Option<&mut dyn NonRigidTransformable> {
+ Some(self as &mut dyn NonRigidTransformable)
+ }
+}
+impl NonRigidTransformable for PolygonRoom {
+ fn apply_matrix(&mut self, matrix: &Matrix3<f64>) {
for corner in self.data.corners_mut() {
- corner.x *= by.x;
- corner.y *= by.y;
+ *corner = matrix
+ .transform_point(&Point2::new(corner.x, corner.y))
+ .into();
}
self.retriangulate();
diff --git a/src/map/rect_room.rs b/src/map/rect_room.rs
index 5008c63..6ed3ed6 100644
--- a/src/map/rect_room.rs
+++ b/src/map/rect_room.rs
@@ -1,14 +1,17 @@
+//! Deprecated simple rectangular room type.
+
use crate::colours::DEFAULT_COLOURS;
use crate::map::Mappable;
-use crate::math::{Rect, Vec2};
-use crate::scaleable::Scaleable;
+use crate::math::Rect;
use crate::transform::Transform;
use raylib::drawing::{RaylibDraw, RaylibDrawHandle};
use serde::Serialize;
use std::ops::{Deref, DerefMut};
+#[allow(missing_docs)]
pub type RectRoomData = Rect<f64>;
+#[allow(missing_docs)]
#[derive(Serialize)]
pub struct RectRoom {
data: RectRoomData,
@@ -16,6 +19,7 @@ pub struct RectRoom {
}
impl RectRoom {
+ #[allow(missing_docs)]
pub fn from_data(data: RectRoomData) -> Self {
RectRoom {
data,
@@ -47,21 +51,6 @@ impl Mappable for RectRoom {
fn bounding_rect(&self) -> Rect<f64> {
self.data.clone()
}
-
- fn as_scaleable(&self) -> Option<&dyn Scaleable> {
- Some(self as &dyn Scaleable)
- }
-}
-
-impl Scaleable for RectRoom {
- fn scale(&mut self, by: &Vec2<f64>) {
- if by.x < 0. || by.y < 0. {
- panic!("Cannot set dimensions with negative size");
- }
-
- self.data.x *= by.x;
- self.data.y *= by.y;
- }
}
impl Deref for RectRoom {
diff --git a/src/map/wall.rs b/src/map/wall.rs
index 22393bb..f1748bb 100644
--- a/src/map/wall.rs
+++ b/src/map/wall.rs
@@ -1,13 +1,23 @@
+//! 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::scaleable::Scaleable;
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,
@@ -16,6 +26,7 @@ pub struct Wall {
}
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,
@@ -25,6 +36,7 @@ impl Wall {
}
}
+ /// Get the internal data for serialisation
pub fn data(&self) -> &WallData {
&self.data
}
@@ -81,18 +93,20 @@ impl Mappable for Wall {
fn bounding_rect(&self) -> Rect<f64> {
Rect::bounding_rect(self.data.start, self.data.end)
}
-}
-impl Scaleable for Wall {
- fn scale(&mut self, by: &Vec2<f64>) {
- if by.x <= 0. || by.y <= 0. {
- panic!("Cannot set dimensions with negative size");
- }
+ 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)
+ }
+}
- self.data.start.x *= by.x;
- self.data.start.y *= by.y;
- self.data.end.x *= by.x;
- self.data.end.y *= by.y;
+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();
}
}