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
199
200
201
202
203
204
205
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());
}
}
}
|