aboutsummaryrefslogtreecommitdiff
path: root/src/map/mod.rs
blob: 70f65b3d24fc28d970b4b86aa42c658d1c8e7482 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
//! 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 polygon_room;
pub mod rect_room;
pub mod wall;

pub use data::MapData;
pub use icon::*;
pub use mappable::Mappable;
pub use polygon_room::*;
pub use rect_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 {
    rect_rooms: Vec<RectRoom>,
    polygon_rooms: Vec<PolygonRoom>,
    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 {
            rect_rooms: Vec::new(),
            polygon_rooms: Vec::new(),
            walls: Vec::new(),
            icons: Vec::new(),
            icon_renderer: Rc::new(IconRenderer::new(rl, rlt)),
        }
    }

    /// 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.
         */
        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.rect_rooms.retain(|r| f(r as &dyn Mappable));
        self.polygon_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.rect_rooms
            .iter()
            .map(|r| r as &dyn Mappable)
            .chain(self.polygon_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.rect_rooms
            .iter_mut()
            .map(|r| r as &mut dyn Mappable)
            .chain(
                self.polygon_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 rectangular rooms of this map.
    pub fn rect_rooms(&self) -> &Vec<RectRoom> {
        &self.rect_rooms
    }

    /// Get the polygon rooms of this map.
    pub fn polygon_rooms(&self) -> &Vec<PolygonRoom> {
        &self.polygon_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.polygon_rooms.clear();
        self.rect_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.polygon_rooms {
            self.push_polygon_room(p);
        }
        for r in data.rect_rooms {
            self.push_rect_room(r);
        }
        for w in data.walls {
            self.push_wall(w);
        }
    }
}