Line data Source code
1 : #include "Map.hpp"
2 :
3 : #include "core/map/Map.hpp"
4 : #include "core/map/PMSConstants.hpp"
5 : #include "core/map/PMSEnums.hpp"
6 : #include "core/map/PMSStructs.hpp"
7 : #include "core/math/Calc.hpp"
8 :
9 : #include <algorithm>
10 : #include <array>
11 : #include <cmath>
12 : #include <spdlog/spdlog.h>
13 : #include <utility>
14 : #include <sstream>
15 :
16 : namespace Soldank
17 : {
18 0 : void Map::CreateEmptyMap()
19 : {
20 0 : map_data_.boundaries_xy[TopBoundary] = -MAP_BOUNDARY;
21 0 : map_data_.boundaries_xy[BottomBoundary] = MAP_BOUNDARY;
22 0 : map_data_.boundaries_xy[LeftBoundary] = -MAP_BOUNDARY;
23 0 : map_data_.boundaries_xy[RightBoundary] = MAP_BOUNDARY;
24 :
25 0 : map_data_.name = std::nullopt;
26 0 : map_data_.description = "New Soldank++ map";
27 0 : map_data_.texture_name = "banana.png";
28 :
29 0 : map_data_.polygons_min_x = 0.0F;
30 0 : map_data_.polygons_max_x = 0.0F;
31 0 : map_data_.polygons_min_y = 0.0F;
32 0 : map_data_.polygons_max_y = 0.0F;
33 :
34 0 : int n = 2 * map_data_.sectors_count + 1;
35 0 : map_data_.sectors_poly = std::vector<std::vector<PMSSector>>(n, std::vector<PMSSector>(n));
36 :
37 0 : map_data_.background_top_color = PMSColor(100, 200, 100, 255);
38 0 : map_data_.background_bottom_color = PMSColor(50, 50, 50, 255);
39 :
40 0 : UpdateBoundaries();
41 0 : GenerateSectors();
42 0 : }
43 :
44 2 : void Map::LoadMap(const std::filesystem::path& map_path, const IFileReader& file_reader)
45 : {
46 : // TODO: Add map validation, whether the map was loaded correctly. Check the sizes of arrays
47 2 : auto file_data = file_reader.Read(map_path.string(), std::ios::in | std::ios::binary);
48 2 : if (!file_data.has_value()) {
49 1 : spdlog::critical("Map not found {}", map_path.string());
50 : // TODO: should return an error
51 1 : return;
52 : }
53 1 : std::stringstream data_buffer{ *file_data };
54 :
55 1 : map_data_.name = map_path.filename().string();
56 :
57 1 : map_data_.boundaries_xy[TopBoundary] = -MAP_BOUNDARY;
58 1 : map_data_.boundaries_xy[BottomBoundary] = MAP_BOUNDARY;
59 1 : map_data_.boundaries_xy[LeftBoundary] = -MAP_BOUNDARY;
60 1 : map_data_.boundaries_xy[RightBoundary] = MAP_BOUNDARY;
61 :
62 1 : map_data_.polygons_min_x = 0.0F;
63 1 : map_data_.polygons_max_x = 0.0F;
64 1 : map_data_.polygons_min_y = 0.0F;
65 1 : map_data_.polygons_max_y = 0.0F;
66 :
67 1 : ReadFromBuffer(data_buffer, map_data_.version);
68 1 : ReadStringFromBuffer(data_buffer, map_data_.description, DESCRIPTION_MAX_LENGTH);
69 1 : ReadStringFromBuffer(data_buffer, map_data_.texture_name, TEXTURE_NAME_MAX_LENGTH);
70 :
71 1 : ReadFromBuffer(data_buffer, map_data_.background_top_color);
72 1 : ReadFromBuffer(data_buffer, map_data_.background_bottom_color);
73 1 : ReadFromBuffer(data_buffer, map_data_.jet_count);
74 1 : ReadFromBuffer(data_buffer, map_data_.grenades_count);
75 1 : ReadFromBuffer(data_buffer, map_data_.medikits_count);
76 1 : ReadFromBuffer(data_buffer, map_data_.weather_type);
77 1 : ReadFromBuffer(data_buffer, map_data_.step_type);
78 1 : ReadFromBuffer(data_buffer, map_data_.random_id);
79 :
80 1 : ReadPolygonsFromBuffer(data_buffer);
81 1 : ReadSectorsFromBuffer(data_buffer);
82 :
83 1 : ReadSceneryInstancesFromBuffer(data_buffer);
84 1 : ReadSceneryTypesFromBuffer(data_buffer);
85 :
86 1 : ReadCollidersFromBuffer(data_buffer);
87 1 : ReadSpawnPointsFromBuffer(data_buffer);
88 1 : ReadWayPointsFromBuffer(data_buffer);
89 :
90 1 : UpdateBoundaries();
91 2 : }
92 :
93 0 : void Map::SaveMap(const std::filesystem::path& map_path, std::shared_ptr<IFileWriter> file_writer)
94 : {
95 0 : UpdateBoundaries();
96 0 : GenerateSectors();
97 :
98 0 : AppendToFileWriter(file_writer, map_data_.version);
99 0 : AppendStringToFileWriter(file_writer, map_data_.description, DESCRIPTION_MAX_LENGTH);
100 0 : AppendStringToFileWriter(file_writer, map_data_.texture_name, TEXTURE_NAME_MAX_LENGTH);
101 :
102 0 : AppendToFileWriter(file_writer, map_data_.background_top_color);
103 0 : AppendToFileWriter(file_writer, map_data_.background_bottom_color);
104 0 : AppendToFileWriter(file_writer, map_data_.jet_count);
105 0 : AppendToFileWriter(file_writer, map_data_.grenades_count);
106 0 : AppendToFileWriter(file_writer, map_data_.medikits_count);
107 0 : AppendToFileWriter(file_writer, map_data_.weather_type);
108 0 : AppendToFileWriter(file_writer, map_data_.step_type);
109 0 : AppendToFileWriter(file_writer, map_data_.random_id);
110 :
111 0 : AppendPolygonsToFileWriter(file_writer);
112 0 : AppendSectorsToFileWriter(file_writer);
113 0 : AppendSceneryInstancesToFileWriter(file_writer);
114 0 : AppendSceneryTypesToFileWriter(file_writer);
115 0 : AppendCollidersToFileWriter(file_writer);
116 0 : AppendSpawnPointsToFileWriter(file_writer);
117 0 : AppendWayPointsToFileWriter(file_writer);
118 :
119 0 : int zero = 0;
120 0 : AppendToFileWriter(file_writer, zero);
121 0 : AppendToFileWriter(file_writer, zero);
122 :
123 0 : auto error = file_writer->Write(map_path, std::ios::out | std::ios::binary | std::ios::trunc);
124 0 : if (error != FileWriterError::NoError) {
125 0 : spdlog::critical("Could not save map: {}", map_path.string());
126 0 : return;
127 : }
128 : }
129 :
130 1 : void Map::ReadPolygonsFromBuffer(std::stringstream& buffer)
131 : {
132 1 : int polygons_count = 0;
133 1 : ReadFromBuffer(buffer, polygons_count);
134 1 : map_data_.polygons.clear();
135 3 : for (int i = 0; i < polygons_count; i++) {
136 2 : PMSPolygon new_polygon;
137 2 : new_polygon.id = i;
138 :
139 8 : for (unsigned int j = 0; j < 3; j++) {
140 6 : ReadFromBuffer(buffer, new_polygon.vertices.at(j));
141 :
142 6 : if (new_polygon.vertices.at(j).x < map_data_.polygons_min_x) {
143 0 : map_data_.polygons_min_x = new_polygon.vertices.at(j).x;
144 : }
145 6 : if (new_polygon.vertices.at(j).x > map_data_.polygons_max_x) {
146 2 : map_data_.polygons_max_x = new_polygon.vertices.at(j).x;
147 : }
148 :
149 6 : if (new_polygon.vertices.at(j).y < map_data_.polygons_min_y) {
150 0 : map_data_.polygons_min_y = new_polygon.vertices.at(j).y;
151 : }
152 6 : if (new_polygon.vertices.at(j).y > map_data_.polygons_max_y) {
153 2 : map_data_.polygons_max_y = new_polygon.vertices.at(j).y;
154 : }
155 : }
156 8 : for (unsigned int j = 0; j < 3; j++) {
157 6 : ReadFromBuffer(buffer, new_polygon.perpendiculars.at(j));
158 : }
159 2 : new_polygon.bounciness =
160 2 : glm::length(glm::vec2(new_polygon.perpendiculars[2].x, new_polygon.perpendiculars[2].y));
161 :
162 8 : for (unsigned int j = 0; j < 3; j++) {
163 6 : glm::vec2 normalized_perpendiculars = Calc::Vec2Normalize(
164 6 : glm::vec2(new_polygon.perpendiculars.at(j).x, new_polygon.perpendiculars.at(j).y));
165 6 : new_polygon.perpendiculars.at(j).x = normalized_perpendiculars.x;
166 6 : new_polygon.perpendiculars.at(j).y = normalized_perpendiculars.y;
167 : }
168 :
169 2 : ReadFromBuffer(buffer, new_polygon.polygon_type);
170 :
171 2 : map_data_.polygons.push_back(new_polygon);
172 : }
173 1 : }
174 :
175 1 : void Map::ReadSectorsFromBuffer(std::stringstream& buffer)
176 : {
177 :
178 1 : ReadFromBuffer(buffer, map_data_.sectors_size);
179 1 : ReadFromBuffer(buffer, map_data_.sectors_count);
180 :
181 1 : int n = 2 * map_data_.sectors_count + 1;
182 2 : map_data_.sectors_poly = std::vector<std::vector<PMSSector>>(n, std::vector<PMSSector>(n));
183 :
184 4 : for (auto& sec_i : map_data_.sectors_poly) {
185 12 : for (auto& sec_ij : sec_i) {
186 9 : unsigned short sector_polygons_count = 0;
187 9 : ReadFromBuffer(buffer, sector_polygons_count);
188 :
189 15 : for (int j = 0; j < sector_polygons_count; j++) {
190 6 : sec_ij.polygons.push_back(0);
191 6 : ReadFromBuffer(buffer, sec_ij.polygons.back());
192 : }
193 : }
194 : }
195 1 : }
196 :
197 1 : void Map::ReadSceneryInstancesFromBuffer(std::stringstream& buffer)
198 : {
199 1 : int scenery_instances_count = 0;
200 1 : ReadFromBuffer(buffer, scenery_instances_count);
201 1 : map_data_.scenery_instances.clear();
202 2 : for (int i = 0; i < scenery_instances_count; i++) {
203 1 : map_data_.scenery_instances.push_back({});
204 1 : ReadFromBuffer(buffer, map_data_.scenery_instances.back());
205 : }
206 1 : }
207 1 : void Map::ReadSceneryTypesFromBuffer(std::stringstream& buffer)
208 : {
209 1 : int scenery_types_count = 0;
210 1 : ReadFromBuffer(buffer, scenery_types_count);
211 1 : map_data_.scenery_types.clear();
212 2 : for (int i = 0; i < scenery_types_count; i++) {
213 1 : map_data_.scenery_types.push_back({});
214 1 : ReadStringFromBuffer(buffer, map_data_.scenery_types.back().name, SCENERY_NAME_MAX_LENGTH);
215 1 : ReadFromBuffer(buffer, map_data_.scenery_types.back().timestamp);
216 : }
217 2 : }
218 1 : void Map::ReadCollidersFromBuffer(std::stringstream& buffer)
219 : {
220 1 : int colliders_count = 0;
221 1 : ReadFromBuffer(buffer, colliders_count);
222 1 : map_data_.colliders.clear();
223 2 : for (int i = 0; i < colliders_count; i++) {
224 1 : map_data_.colliders.push_back({});
225 1 : ReadFromBuffer(buffer, map_data_.colliders.back());
226 : }
227 1 : }
228 1 : void Map::ReadSpawnPointsFromBuffer(std::stringstream& buffer)
229 : {
230 1 : int spawn_points_count = 0;
231 1 : ReadFromBuffer(buffer, spawn_points_count);
232 1 : map_data_.spawn_points.clear();
233 2 : for (int i = 0; i < spawn_points_count; i++) {
234 1 : map_data_.spawn_points.push_back({});
235 1 : ReadFromBuffer(buffer, map_data_.spawn_points.back());
236 : }
237 1 : }
238 :
239 1 : void Map::ReadWayPointsFromBuffer(std::stringstream& buffer)
240 : {
241 1 : int way_points_count = 0;
242 1 : ReadFromBuffer(buffer, way_points_count);
243 2 : for (int i = 0; i < way_points_count; i++) {
244 1 : map_data_.way_points.push_back({});
245 1 : ReadFromBuffer(buffer, map_data_.way_points.back());
246 : }
247 1 : }
248 :
249 0 : void Map::AppendPolygonsToFileWriter(std::shared_ptr<IFileWriter>& file_writer)
250 : {
251 0 : unsigned int polygons_count = map_data_.polygons.size();
252 0 : AppendToFileWriter(file_writer, polygons_count);
253 :
254 0 : for (auto& polygon : map_data_.polygons) {
255 : // Polygons' vertices have to be arranged in clock-wise order.
256 0 : if (!polygon.AreVerticesClockwise()) {
257 0 : PMSVertex tmp = polygon.vertices[1];
258 0 : polygon.vertices[1] = polygon.vertices[2];
259 0 : polygon.vertices[2] = tmp;
260 : }
261 :
262 0 : for (const auto& vertex : polygon.vertices) {
263 0 : auto vertex_copy = vertex;
264 0 : vertex_copy.x -= map_data_.center_x;
265 0 : vertex_copy.y -= map_data_.center_y;
266 0 : vertex_copy.z = 1.0F;
267 :
268 0 : AppendToFileWriter(file_writer, vertex_copy);
269 : }
270 :
271 0 : for (int i = 0; i < 3; ++i) {
272 0 : unsigned int j = i + 1;
273 0 : if (j > 2) {
274 0 : j = 0;
275 : }
276 :
277 0 : float diff_x = polygon.vertices.at(j).x - polygon.vertices.at(i).x;
278 0 : float diff_y = polygon.vertices.at(i).y - polygon.vertices.at(j).y;
279 0 : float length = NAN;
280 0 : if (fabs(diff_x) < 0.00001F && fabs(diff_y) < 0.00001F) {
281 0 : length = 1.0F;
282 : } else {
283 0 : length = hypotf(diff_x, diff_y);
284 : }
285 :
286 0 : float bounciness = 1.0F;
287 :
288 0 : if (polygon.polygon_type == PMSPolygonType::Bouncy) {
289 0 : if (bounciness < 1.0F) {
290 0 : bounciness = 1.0F;
291 : } else {
292 0 : bounciness = polygon.bounciness;
293 : }
294 : } else {
295 0 : bounciness = 1.0F;
296 : }
297 :
298 0 : polygon.perpendiculars.at(i).x = (diff_y / length) * bounciness;
299 0 : polygon.perpendiculars.at(i).y = (diff_x / length) * bounciness;
300 0 : polygon.perpendiculars.at(i).z = 1.0F;
301 :
302 0 : AppendToFileWriter(file_writer, polygon.perpendiculars.at(i));
303 : }
304 0 : AppendToFileWriter(file_writer, polygon.polygon_type);
305 : }
306 0 : }
307 :
308 0 : void Map::AppendSectorsToFileWriter(std::shared_ptr<IFileWriter>& file_writer)
309 : {
310 0 : AppendToFileWriter(file_writer, map_data_.sectors_size);
311 : /**
312 : * In VB6/Pascal, an array can have negative indexes. Basically, Soldat creates an
313 : * array like this: [-sectorsCount...sectorsCount, -sectorsCount...sectorsCount].
314 : * This is a 2-dimensional array equal to C++'s [sectorsCount * 2 + 1][sectorsCount * 2 + 1].
315 : */
316 0 : AppendToFileWriter(file_writer, map_data_.sectors_count);
317 0 : for (int x = 0; x <= map_data_.sectors_count * 2; ++x) {
318 0 : for (int y = 0; y <= map_data_.sectors_count * 2; ++y) {
319 0 : unsigned short sector_polygons_count = map_data_.sectors_poly[x][y].polygons.size();
320 0 : AppendToFileWriter(file_writer, sector_polygons_count);
321 0 : for (const auto& polygon_id : map_data_.sectors_poly[x][y].polygons) {
322 0 : AppendToFileWriter(file_writer, polygon_id);
323 : }
324 : }
325 : }
326 0 : }
327 :
328 0 : void Map::AppendSceneryInstancesToFileWriter(std::shared_ptr<IFileWriter>& file_writer)
329 : {
330 0 : unsigned int scenery_instances_count = map_data_.scenery_instances.size();
331 0 : AppendToFileWriter(file_writer, scenery_instances_count);
332 0 : for (const auto& scenery : map_data_.scenery_instances) {
333 0 : auto scenery_copy = scenery;
334 0 : scenery_copy.x -= map_data_.center_x;
335 0 : scenery_copy.y -= map_data_.center_y;
336 0 : AppendToFileWriter(file_writer, scenery_copy);
337 : }
338 0 : }
339 :
340 0 : void Map::AppendSceneryTypesToFileWriter(std::shared_ptr<IFileWriter>& file_writer)
341 : {
342 0 : unsigned int scenery_types_count = map_data_.scenery_types.size();
343 0 : AppendToFileWriter(file_writer, scenery_types_count);
344 0 : for (const auto& scenery_type : map_data_.scenery_types) {
345 0 : AppendStringToFileWriter(file_writer, scenery_type.name, SCENERY_NAME_MAX_LENGTH);
346 0 : AppendToFileWriter(file_writer, scenery_type.timestamp);
347 : }
348 0 : }
349 :
350 0 : void Map::AppendCollidersToFileWriter(std::shared_ptr<IFileWriter>& file_writer)
351 : {
352 0 : unsigned int colliders_count = map_data_.colliders.size();
353 0 : AppendToFileWriter(file_writer, colliders_count);
354 0 : for (const auto& collider : map_data_.colliders) {
355 0 : auto collider_copy = collider;
356 0 : collider_copy.x -= map_data_.center_x;
357 0 : collider_copy.y -= map_data_.center_y;
358 0 : AppendToFileWriter(file_writer, collider_copy);
359 : }
360 0 : }
361 :
362 0 : void Map::AppendSpawnPointsToFileWriter(std::shared_ptr<IFileWriter>& file_writer)
363 : {
364 0 : unsigned int spawn_points_count = map_data_.spawn_points.size();
365 0 : AppendToFileWriter(file_writer, spawn_points_count);
366 0 : for (const auto& spawn_point : map_data_.spawn_points) {
367 0 : auto spawn_point_copy = spawn_point;
368 0 : spawn_point_copy.x -= (int)map_data_.center_x;
369 0 : spawn_point_copy.y -= (int)map_data_.center_y;
370 0 : AppendToFileWriter(file_writer, spawn_point_copy);
371 : }
372 0 : }
373 :
374 0 : void Map::AppendWayPointsToFileWriter(std::shared_ptr<IFileWriter>& file_writer)
375 : {
376 0 : unsigned int way_points_count = map_data_.way_points.size();
377 0 : AppendToFileWriter(file_writer, way_points_count);
378 0 : for (const auto& way_point : map_data_.way_points) {
379 0 : auto way_point_copy = way_point;
380 0 : way_point_copy.x -= (int)map_data_.center_x;
381 0 : way_point_copy.y -= (int)map_data_.center_y;
382 0 : AppendToFileWriter(file_writer, way_point_copy);
383 : }
384 0 : }
385 :
386 1 : void Map::UpdateBoundaries()
387 : {
388 1 : map_data_.width = fabs(map_data_.polygons_max_x - map_data_.polygons_min_x);
389 1 : map_data_.height = fabs(map_data_.polygons_max_y - map_data_.polygons_min_y);
390 :
391 1 : map_data_.center_x = floor((map_data_.polygons_min_x + map_data_.polygons_max_x) / 2.0F);
392 1 : map_data_.center_y = floor((map_data_.polygons_min_y + map_data_.polygons_max_y) / 2.0F);
393 :
394 1 : map_data_.boundaries_xy[TopBoundary] = map_data_.polygons_min_y;
395 1 : map_data_.boundaries_xy[BottomBoundary] = map_data_.polygons_max_y;
396 1 : map_data_.boundaries_xy[LeftBoundary] = map_data_.polygons_min_x;
397 1 : map_data_.boundaries_xy[RightBoundary] = map_data_.polygons_max_x;
398 :
399 1 : if (map_data_.height > map_data_.width) {
400 0 : map_data_.boundaries_xy[LeftBoundary] -= (map_data_.height - map_data_.width) / 2.0F;
401 0 : map_data_.boundaries_xy[RightBoundary] += (map_data_.height - map_data_.width) / 2.0F;
402 : } else {
403 1 : map_data_.boundaries_xy[TopBoundary] -= (map_data_.width - map_data_.height) / 2.0F;
404 1 : map_data_.boundaries_xy[BottomBoundary] += (map_data_.width - map_data_.height) / 2.0F;
405 : }
406 :
407 1 : map_data_.boundaries_xy[TopBoundary] -= MAP_BOUNDARY;
408 1 : map_data_.boundaries_xy[BottomBoundary] += MAP_BOUNDARY;
409 1 : map_data_.boundaries_xy[LeftBoundary] -= MAP_BOUNDARY;
410 1 : map_data_.boundaries_xy[RightBoundary] += MAP_BOUNDARY;
411 :
412 1 : map_change_events_.changed_background_color.Notify(
413 1 : map_data_.background_top_color, map_data_.background_bottom_color, GetBoundaries());
414 1 : }
415 :
416 1525 : bool Map::PointInPoly(glm::vec2 p, PMSPolygon poly)
417 : {
418 1525 : auto a = poly.vertices[0];
419 1525 : auto b = poly.vertices[1];
420 1525 : auto c = poly.vertices[2];
421 :
422 1525 : auto ap_x = p.x - a.x;
423 1525 : auto ap_y = p.y - a.y;
424 1525 : auto p_ab = (b.x - a.x) * ap_y - (b.y - a.y) * ap_x > 0.0F;
425 1525 : auto p_ac = (c.x - a.x) * ap_y - (c.y - a.y) * ap_x > 0.0F;
426 :
427 1525 : if (p_ac == p_ab) {
428 1208 : return false;
429 : }
430 :
431 317 : if (((c.x - b.x) * (p.y - b.y) - (c.y - b.y) * (p.x - b.x) > 0.0F) != p_ab) {
432 100 : return false;
433 : }
434 :
435 217 : return true;
436 : }
437 :
438 0 : bool Map::PointInPolyEdges(float x, float y, int i) const
439 : {
440 0 : auto u_x = x - map_data_.polygons[i].vertices[0].x;
441 0 : auto u_y = y - map_data_.polygons[i].vertices[0].y;
442 0 : auto d = map_data_.polygons[i].perpendiculars[0].x * u_x +
443 0 : map_data_.polygons[i].perpendiculars[0].y * u_y;
444 0 : if (d < 0.0F) {
445 0 : return false;
446 : }
447 :
448 0 : u_x = x - map_data_.polygons[i].vertices[1].x;
449 0 : u_y = y - map_data_.polygons[i].vertices[1].y;
450 0 : d = map_data_.polygons[i].perpendiculars[1].x * u_x +
451 0 : map_data_.polygons[i].perpendiculars[1].y * u_y;
452 0 : if (d < 0.0F) {
453 0 : return false;
454 : }
455 :
456 0 : u_x = x - map_data_.polygons[i].vertices[2].x;
457 0 : u_y = y - map_data_.polygons[i].vertices[2].y;
458 0 : d = map_data_.polygons[i].perpendiculars[2].x * u_x +
459 0 : map_data_.polygons[i].perpendiculars[2].y * u_y;
460 0 : return d >= 0.0F;
461 : }
462 :
463 0 : bool Map::PointInScenery(glm::vec2 p, const PMSScenery& scenery)
464 : {
465 0 : auto scenery_vertex_positions = GetSceneryVertexPositions(scenery);
466 :
467 0 : glm::vec2 a = scenery_vertex_positions.at(0);
468 0 : glm::vec2 b = scenery_vertex_positions.at(1);
469 0 : if (Calc::Det(a, b, p) > 0) {
470 0 : return false;
471 : }
472 :
473 0 : a = scenery_vertex_positions.at(1);
474 0 : b = scenery_vertex_positions.at(2);
475 0 : if (Calc::Det(a, b, p) > 0) {
476 0 : return false;
477 : }
478 :
479 0 : a = scenery_vertex_positions.at(2);
480 0 : b = scenery_vertex_positions.at(3);
481 0 : if (Calc::Det(a, b, p) > 0) {
482 0 : return false;
483 : }
484 :
485 0 : a = scenery_vertex_positions.at(3);
486 0 : b = scenery_vertex_positions.at(0);
487 0 : return Calc::Det(a, b, p) <= 0;
488 : }
489 :
490 0 : glm::vec2 Map::ClosestPerpendicular(int j, glm::vec2 pos, float* d, int* n) const
491 : {
492 : auto px = std::array{
493 0 : map_data_.polygons[j].vertices[0].x,
494 0 : map_data_.polygons[j].vertices[1].x,
495 0 : map_data_.polygons[j].vertices[2].x,
496 0 : };
497 :
498 : auto py = std::array{
499 0 : map_data_.polygons[j].vertices[0].y,
500 0 : map_data_.polygons[j].vertices[1].y,
501 0 : map_data_.polygons[j].vertices[2].y,
502 0 : };
503 :
504 0 : auto p1 = glm::vec2(px[0], py[0]);
505 0 : auto p2 = glm::vec2(px[1], py[1]);
506 :
507 0 : auto d1 = Soldank::Calc::PointLineDistance(p1, p2, pos);
508 :
509 0 : *d = d1;
510 :
511 0 : auto edge_v1 = 1;
512 0 : auto edge_v2 = 2;
513 :
514 0 : p1.x = px[1];
515 0 : p1.y = py[1];
516 :
517 0 : p2.x = px[2];
518 0 : p2.y = py[2];
519 :
520 0 : auto d2 = Soldank::Calc::PointLineDistance(p1, p2, pos);
521 :
522 0 : if (d2 < d1) {
523 0 : edge_v1 = 2;
524 0 : edge_v2 = 3;
525 0 : *d = d2;
526 : }
527 :
528 0 : p1.x = px[2];
529 0 : p1.y = py[2];
530 :
531 0 : p2.x = px[0];
532 0 : p2.y = py[0];
533 :
534 0 : auto d3 = Soldank::Calc::PointLineDistance(p1, p2, pos);
535 :
536 0 : if ((d3 < d2) && (d3 < d1)) {
537 0 : edge_v1 = 3;
538 0 : edge_v2 = 1;
539 0 : *d = d3;
540 : }
541 :
542 0 : if (edge_v1 == 1 && edge_v2 == 2) {
543 0 : *n = 1;
544 0 : return { map_data_.polygons[j].perpendiculars[0].x,
545 0 : map_data_.polygons[j].perpendiculars[0].y };
546 : }
547 :
548 0 : if (edge_v1 == 2 && edge_v2 == 3) {
549 0 : *n = 2;
550 0 : return { map_data_.polygons[j].perpendiculars[1].x,
551 0 : map_data_.polygons[j].perpendiculars[1].y };
552 : }
553 :
554 0 : if (edge_v1 == 3 && edge_v2 == 1) {
555 0 : *n = 3;
556 0 : return { map_data_.polygons[j].perpendiculars[2].x,
557 0 : map_data_.polygons[j].perpendiculars[2].y };
558 : }
559 :
560 0 : return { 0.0F, 0.0F };
561 : }
562 :
563 0 : bool Map::CollisionTest(glm::vec2 pos, glm::vec2& perp_vec, bool is_flag) const
564 : {
565 0 : constexpr const std::array EXCLUDED1 = {
566 : PMSPolygonType::OnlyBulletsCollide, PMSPolygonType::OnlyPlayersCollide,
567 : PMSPolygonType::NoCollide, PMSPolygonType::AlphaPlayers,
568 : PMSPolygonType::BravoPlayers, PMSPolygonType::CharliePlayers,
569 : PMSPolygonType::DeltaPlayers, /* TODO: add those PMSPolygonType::BACKGROUND,
570 : PMSPolygonType::BACKGROUND_TRANSITION*/
571 : };
572 :
573 0 : constexpr const std::array EXCLUDED2 = {
574 : PMSPolygonType::FlaggerCollides,
575 : PMSPolygonType::NonFlaggerCollides, /* TODO: what's that: PMSPolygonType::NOT_FLAGGERS???*/
576 : };
577 :
578 0 : auto rx = ((int)std::round((pos.x / (float)GetSectorsSize()))) + 25;
579 0 : auto ry = ((int)std::round((pos.y / (float)GetSectorsSize()))) + 25;
580 0 : if ((rx > 0) && (rx < GetSectorsCount() + 25) && (ry > 0) && (ry < GetSectorsCount() + 25)) {
581 0 : for (unsigned short polygon_id : GetSector(rx, ry).polygons) {
582 0 : auto poly = GetPolygons().at(polygon_id - 1);
583 :
584 0 : if (!std::ranges::contains(EXCLUDED1, poly.polygon_type) &&
585 0 : (is_flag || !std::ranges::contains(EXCLUDED2, poly.polygon_type))) {
586 0 : if (PointInPoly(pos, poly)) {
587 0 : float d = NAN;
588 0 : int b = 0;
589 0 : perp_vec = ClosestPerpendicular(polygon_id - 1, pos, &d, &b);
590 0 : perp_vec = Calc::Vec2Scale(perp_vec, 1.5F * d);
591 0 : return true;
592 : }
593 : }
594 : }
595 : }
596 :
597 0 : return false;
598 : }
599 :
600 0 : bool Map::RayCast(glm::vec2 a,
601 : glm::vec2 b,
602 : float& distance,
603 : float max_dist,
604 : bool player,
605 : bool flag,
606 : bool bullet,
607 : bool check_collider,
608 : std::uint8_t team_id) const
609 : {
610 0 : distance = Calc::Vec2Length(a - b);
611 0 : if (distance > max_dist) {
612 0 : distance = 9999999.0F;
613 0 : return true;
614 : }
615 :
616 0 : int ax = ((int)std::round(std::min(a.x, b.x) / (float)map_data_.sectors_size)) + 25;
617 0 : int ay = ((int)std::round(std::min(a.y, b.y) / (float)map_data_.sectors_size)) + 25;
618 0 : int bx = ((int)std::round(std::max(a.x, b.x) / (float)map_data_.sectors_size)) + 25;
619 0 : int by = ((int)std::round(std::max(a.y, b.y) / (float)map_data_.sectors_size)) + 25;
620 :
621 0 : if (ax > GetSectorsCount() + 25 || bx < 0 || ay > GetSectorsCount() + 25 || by < 0) {
622 0 : return false;
623 : }
624 :
625 0 : ax = std::max(0, ax);
626 0 : ay = std::max(0, ay);
627 0 : bx = std::min(GetSectorsCount() + 25, bx);
628 0 : by = std::min(GetSectorsCount() + 25, by);
629 :
630 0 : bool npCol = !player;
631 0 : bool nbCol = !bullet;
632 :
633 0 : for (unsigned int i = ax; i < bx; ++i) {
634 0 : for (unsigned int j = ay; j < by; ++j) {
635 0 : for (const auto& polygon_id : GetSector(i, j).polygons) {
636 0 : const PMSPolygon& polygon = map_data_.polygons.at(polygon_id - 1);
637 :
638 0 : bool testcol = true;
639 : // TODO: replace team_id with some enum
640 0 : if ((polygon.polygon_type == PMSPolygonType::AlphaBullets &&
641 0 : (team_id != 1 || nbCol)) ||
642 0 : (polygon.polygon_type == PMSPolygonType::AlphaPlayers &&
643 0 : (team_id != 1 || npCol))) {
644 :
645 0 : testcol = false;
646 : }
647 :
648 0 : if ((polygon.polygon_type == PMSPolygonType::BravoBullets &&
649 0 : (team_id != 2 || nbCol)) ||
650 0 : (polygon.polygon_type == PMSPolygonType::BravoPlayers &&
651 0 : (team_id != 2 || npCol))) {
652 :
653 0 : testcol = false;
654 : }
655 :
656 0 : if ((polygon.polygon_type == PMSPolygonType::CharlieBullets &&
657 0 : (team_id != 3 || nbCol)) ||
658 0 : (polygon.polygon_type == PMSPolygonType::CharliePlayers &&
659 0 : (team_id != 3 || npCol))) {
660 :
661 0 : testcol = false;
662 : }
663 :
664 0 : if ((polygon.polygon_type == PMSPolygonType::DeltaBullets &&
665 0 : (team_id != 4 || nbCol)) ||
666 0 : (polygon.polygon_type == PMSPolygonType::DeltaPlayers &&
667 0 : (team_id != 4 || npCol))) {
668 :
669 0 : testcol = false;
670 : }
671 :
672 0 : if (((!flag || npCol) &&
673 0 : (polygon.polygon_type == PMSPolygonType::FlaggerCollides)) ||
674 0 : ((flag || npCol) &&
675 0 : polygon.polygon_type == PMSPolygonType::NonFlaggerCollides)) {
676 :
677 0 : testcol = false;
678 : }
679 :
680 0 : if ((!flag || npCol || nbCol) &&
681 0 : polygon.polygon_type == PMSPolygonType::NonFlaggerCollides) {
682 :
683 0 : testcol = false;
684 : }
685 :
686 0 : if (
687 0 : (polygon.polygon_type == PMSPolygonType::OnlyBulletsCollide && nbCol) ||
688 0 : (polygon.polygon_type == PMSPolygonType::OnlyPlayersCollide && npCol) || (polygon.polygon_type == PMSPolygonType::NoCollide
689 : // TODO: add this when those are implemented
690 : /*|| polygon.polygon_type == PMSPolygonType::POLY_TYPE_BACKGROUND || polygon.polygon_type == PMSPolygonType::POLY_TYPE_BACKGROUND_TRANSITION*/)) {
691 :
692 0 : testcol = false;
693 : }
694 :
695 0 : if (testcol) {
696 0 : if (PointInPoly(a, polygon)) {
697 0 : distance = 0;
698 0 : return true;
699 : }
700 : glm::vec2 d;
701 0 : if (LineInPoly(a, b, polygon, d)) {
702 0 : glm::vec2 c = d - a;
703 0 : distance = Calc::Vec2Length(c);
704 0 : return true;
705 : }
706 : }
707 : }
708 : }
709 : }
710 :
711 : // TODO: Dead code, decide whether it's needed and refactor or delete
712 0 : if (check_collider) {
713 : // check if vector crosses any colliders
714 : // |A*x + B*y + C| / Sqrt(A^2 + B^2) < r
715 :
716 0 : float e = a.y - b.y;
717 0 : float f = b.x - a.x;
718 0 : float g = a.x * b.y - a.y * b.x;
719 0 : float h = std::sqrt(e * e + f * f);
720 0 : for (const auto& collider : map_data_.colliders) {
721 0 : if (collider.active != 0) {
722 0 : if (std::abs(e * collider.x + f * collider.y + g) / h <= collider.radius) {
723 0 : float r = Calc::SquareDistance(a, b) + collider.radius * collider.radius;
724 0 : if (Calc::SquareDistance(a, { collider.x, collider.y }) <= r) {
725 0 : if (Calc::SquareDistance(b, { collider.x, collider.y }) <= r) {
726 : // TODO: it looks like check_collider never returns true. Check
727 : // what's up with that
728 0 : return false;
729 : }
730 : }
731 : }
732 : }
733 : }
734 : }
735 :
736 0 : return false;
737 : }
738 :
739 0 : bool Map::LineInPoly(const glm::vec2& a,
740 : const glm::vec2& b,
741 : const PMSPolygon& polygon,
742 : glm::vec2& v)
743 : {
744 0 : for (unsigned int i = 0; i < 3; ++i) {
745 0 : unsigned int j = i + 1;
746 0 : if (j >= 3) {
747 0 : j = 0;
748 : }
749 :
750 0 : const auto& p = polygon.vertices.at(i);
751 0 : const auto& q = polygon.vertices.at(j);
752 :
753 0 : if ((std::abs(b.x - a.x) > 0.00001F) || (std::abs(q.x - p.x) > 0.00001F)) {
754 0 : if (b.x == a.x) {
755 0 : float bk = (q.y - p.y) / (q.x - p.x);
756 0 : float bm = p.y - bk * p.x;
757 0 : v.x = a.x;
758 0 : v.y = bk * v.x + bm;
759 :
760 0 : if ((v.x > std::min(p.x, q.x)) && (v.x < std::max(p.x, q.x)) &&
761 0 : (v.y > std::min(a.y, b.y)) && (v.y < std::max(a.y, b.y))) {
762 0 : return true;
763 : }
764 0 : } else if (std::abs(q.x - p.x) <= 0.000001F) {
765 0 : float ak = (b.y - a.y) / (b.x - a.x);
766 0 : float am = a.y - ak * a.x;
767 0 : v.x = p.x;
768 0 : v.y = ak * v.x + am;
769 :
770 0 : if ((v.y > std::min(p.y, q.y)) && (v.y < std::max(p.y, q.y)) &&
771 0 : (v.x > std::min(a.x, b.x)) && (v.x < std::max(a.x, b.x))) {
772 0 : return true;
773 : }
774 : } else {
775 0 : float ak = (b.y - a.y) / (b.x - a.x);
776 0 : float bk = (q.y - p.y) / (q.x - p.x);
777 :
778 0 : if (std::abs(ak - bk) > 0.000001F) {
779 0 : float am = a.y - ak * a.x;
780 0 : float bm = p.y - bk * p.x;
781 0 : v.x = (bm - am) / (ak - bk);
782 0 : v.y = ak * v.x + am;
783 :
784 0 : if ((v.x > std::min(p.x, q.x)) && (v.x < std::max(p.x, q.x)) &&
785 0 : (v.x > std::min(a.x, b.x)) && (v.x < std::max(a.x, b.x))) {
786 0 : return true;
787 : }
788 : }
789 : }
790 : }
791 : }
792 :
793 0 : return false;
794 : }
795 :
796 0 : std::optional<PMSSpawnPoint> Map::FindFirstSpawnPoint(PMSSpawnPointType spawn_point_type) const
797 : {
798 0 : for (const auto& spawn_point : GetSpawnPoints()) {
799 0 : if (spawn_point.type == spawn_point_type) {
800 0 : return spawn_point;
801 : }
802 : }
803 :
804 0 : return std::nullopt;
805 : }
806 :
807 0 : PMSPolygon Map::AddNewPolygon(const PMSPolygon& polygon)
808 : {
809 :
810 0 : PMSPolygon new_polygon = polygon;
811 :
812 0 : new_polygon.id = map_data_.polygons.size();
813 :
814 0 : SetPolygonVerticesAndPerpendiculars(new_polygon);
815 :
816 0 : map_data_.polygons.push_back(new_polygon);
817 :
818 0 : UpdateMinMaxPolygonPositions();
819 0 : FixPolygonIds();
820 0 : UpdateBoundaries();
821 0 : are_sectors_generated_ = false;
822 :
823 0 : map_change_events_.added_new_polygon.Notify(new_polygon);
824 :
825 0 : return new_polygon;
826 : }
827 :
828 0 : void Map::AddPolygons(const std::vector<PMSPolygon>& polygons)
829 : {
830 0 : std::vector<PMSPolygon> polygons_to_add = polygons;
831 0 : std::sort(polygons_to_add.begin(),
832 : polygons_to_add.end(),
833 0 : [](const PMSPolygon& a, const PMSPolygon& b) { return a.id < b.id; });
834 :
835 0 : for (auto& polygon : polygons_to_add) {
836 0 : SetPolygonVerticesAndPerpendiculars(polygon);
837 : }
838 0 : std::vector<PMSPolygon> old_polygons = map_data_.polygons;
839 0 : map_data_.polygons.clear();
840 0 : unsigned int old_polygons_id = 0;
841 0 : unsigned int polygons_to_add_id = 0;
842 0 : while (old_polygons_id < old_polygons.size() || polygons_to_add_id < polygons_to_add.size()) {
843 0 : if (polygons_to_add_id < polygons_to_add.size() &&
844 0 : polygons_to_add.at(polygons_to_add_id).id == map_data_.polygons.size()) {
845 :
846 0 : map_data_.polygons.push_back(polygons_to_add.at(polygons_to_add_id));
847 0 : ++polygons_to_add_id;
848 : } else {
849 0 : map_data_.polygons.push_back(old_polygons.at(old_polygons_id));
850 0 : ++old_polygons_id;
851 : }
852 : }
853 :
854 0 : UpdateMinMaxPolygonPositions();
855 0 : FixPolygonIds();
856 0 : UpdateBoundaries();
857 0 : are_sectors_generated_ = false;
858 :
859 0 : map_change_events_.added_new_polygons.Notify(polygons_to_add, map_data_.polygons);
860 0 : }
861 :
862 0 : PMSPolygon Map::RemovePolygonById(unsigned int id)
863 : {
864 0 : PMSPolygon removed_polygon = map_data_.polygons.at(id);
865 0 : map_data_.polygons.erase(map_data_.polygons.begin() + id);
866 0 : UpdateMinMaxPolygonPositions();
867 0 : FixPolygonIds();
868 0 : UpdateBoundaries();
869 0 : are_sectors_generated_ = false;
870 :
871 0 : map_change_events_.removed_polygon.Notify(removed_polygon, map_data_.polygons);
872 :
873 0 : return removed_polygon;
874 : }
875 :
876 0 : void Map::RemovePolygonsById(const std::vector<unsigned int>& polygon_ids)
877 : {
878 0 : std::vector<unsigned int> sorted_polygon_ids = polygon_ids;
879 0 : std::sort(sorted_polygon_ids.begin(), sorted_polygon_ids.end());
880 0 : std::vector<PMSPolygon> removed_polygons;
881 0 : removed_polygons.reserve(polygon_ids.size());
882 0 : for (const auto& polygon_id : sorted_polygon_ids) {
883 0 : removed_polygons.push_back(map_data_.polygons.at(polygon_id));
884 : }
885 :
886 0 : unsigned int polygon_id = 0;
887 0 : unsigned int removal_id = 0;
888 0 : while (polygon_id + removal_id < map_data_.polygons.size()) {
889 0 : while (removal_id < sorted_polygon_ids.size() &&
890 0 : polygon_id + removal_id == sorted_polygon_ids.at(removal_id)) {
891 0 : ++removal_id;
892 : }
893 :
894 0 : if (polygon_id + removal_id >= map_data_.polygons.size()) {
895 0 : break;
896 : }
897 :
898 0 : if (removal_id > 0) {
899 0 : std::swap(map_data_.polygons[polygon_id], map_data_.polygons[polygon_id + removal_id]);
900 : }
901 :
902 0 : ++polygon_id;
903 : }
904 :
905 0 : for (unsigned i = 0; i < sorted_polygon_ids.size(); ++i) {
906 0 : map_data_.polygons.pop_back();
907 : }
908 :
909 0 : UpdateMinMaxPolygonPositions();
910 0 : FixPolygonIds();
911 0 : UpdateBoundaries();
912 0 : are_sectors_generated_ = false;
913 :
914 0 : map_change_events_.removed_polygons.Notify(removed_polygons, map_data_.polygons);
915 0 : }
916 :
917 0 : void Map::SetPolygonVerticesColorById(
918 : const std::vector<std::pair<std::pair<unsigned int, unsigned int>, PMSColor>>&
919 : polygon_vertices_with_new_color)
920 : {
921 0 : for (const auto& [polygon_vertex_id, new_color] : polygon_vertices_with_new_color) {
922 0 : map_data_.polygons.at(polygon_vertex_id.first).vertices.at(polygon_vertex_id.second).color =
923 : new_color;
924 : }
925 :
926 0 : map_change_events_.modified_polygons.Notify(map_data_.polygons);
927 0 : }
928 :
929 0 : void Map::MovePolygonVerticesById(
930 : const std::vector<std::pair<std::pair<unsigned int, unsigned int>, glm::vec2>>&
931 : polygon_vertices_with_new_position)
932 : {
933 0 : for (const auto& [polygon_vertex, new_position] : polygon_vertices_with_new_position) {
934 0 : map_data_.polygons.at(polygon_vertex.first).vertices.at(polygon_vertex.second).x =
935 0 : new_position.x;
936 0 : map_data_.polygons.at(polygon_vertex.first).vertices.at(polygon_vertex.second).y =
937 0 : new_position.y;
938 : }
939 :
940 0 : UpdateMinMaxPolygonPositions();
941 0 : FixPolygonIds();
942 0 : UpdateBoundaries();
943 0 : are_sectors_generated_ = false;
944 :
945 0 : map_change_events_.modified_polygons.Notify(map_data_.polygons);
946 0 : }
947 :
948 0 : void Map::SetPolygonsById(const std::vector<std::pair<unsigned int, PMSPolygon>>& polygons)
949 : {
950 0 : for (const auto& [polygon_id, polygon] : polygons) {
951 0 : map_data_.polygons.at(polygon_id) = polygon;
952 : }
953 :
954 0 : UpdateMinMaxPolygonPositions();
955 0 : FixPolygonIds();
956 0 : UpdateBoundaries();
957 0 : are_sectors_generated_ = false;
958 :
959 0 : map_change_events_.modified_polygons.Notify(map_data_.polygons);
960 0 : }
961 :
962 0 : unsigned int Map::AddNewSpawnPoint(const PMSSpawnPoint& spawn_point)
963 : {
964 0 : map_data_.spawn_points.push_back(spawn_point);
965 0 : unsigned int new_spawn_point_id = map_data_.spawn_points.size() - 1;
966 0 : map_change_events_.added_new_spawn_point.Notify(map_data_.spawn_points.back(),
967 : new_spawn_point_id);
968 0 : return new_spawn_point_id;
969 : }
970 :
971 0 : PMSSpawnPoint Map::RemoveSpawnPointById(unsigned int id)
972 : {
973 0 : PMSSpawnPoint removed_spawn_point = map_data_.spawn_points.at(id);
974 0 : map_data_.spawn_points.erase(map_data_.spawn_points.begin() + id);
975 0 : map_change_events_.removed_spawn_point.Notify(removed_spawn_point, id);
976 0 : return removed_spawn_point;
977 : }
978 :
979 0 : void Map::MoveSpawnPointsById(
980 : const std::vector<std::pair<unsigned int, glm::ivec2>>& spawn_point_ids_with_new_position)
981 : {
982 0 : for (const auto& [spawn_point_id, new_position] : spawn_point_ids_with_new_position) {
983 0 : map_data_.spawn_points.at(spawn_point_id).x = new_position.x;
984 0 : map_data_.spawn_points.at(spawn_point_id).y = new_position.y;
985 : }
986 :
987 0 : map_change_events_.modified_spawn_points.Notify(map_data_.spawn_points);
988 0 : }
989 :
990 0 : void Map::SetSpawnPointsById(
991 : const std::vector<std::pair<unsigned int, PMSSpawnPoint>>& spawn_points)
992 : {
993 0 : for (const auto& [spawn_point_id, spawn_point] : spawn_points) {
994 0 : map_data_.spawn_points.at(spawn_point_id) = spawn_point;
995 : }
996 :
997 0 : map_change_events_.modified_spawn_points.Notify(map_data_.spawn_points);
998 0 : }
999 :
1000 0 : void Map::AddSpawnPoints(const std::vector<std::pair<unsigned int, PMSSpawnPoint>>& spawn_points)
1001 : {
1002 0 : std::vector<std::pair<unsigned int, PMSSpawnPoint>> spawn_points_to_add = spawn_points;
1003 0 : std::sort(spawn_points_to_add.begin(),
1004 : spawn_points_to_add.end(),
1005 0 : [](const std::pair<unsigned int, PMSSpawnPoint>& a,
1006 0 : const std::pair<unsigned int, PMSSpawnPoint>& b) { return a.first < b.first; });
1007 :
1008 0 : std::vector<PMSSpawnPoint> old_spawn_points = map_data_.spawn_points;
1009 0 : map_data_.spawn_points.clear();
1010 0 : unsigned int old_spawn_point_id = 0;
1011 0 : unsigned int spawn_points_to_add_id = 0;
1012 :
1013 0 : while (old_spawn_point_id < old_spawn_points.size() ||
1014 0 : spawn_points_to_add_id < spawn_points_to_add.size()) {
1015 :
1016 0 : if (spawn_points_to_add_id < spawn_points_to_add.size() &&
1017 0 : spawn_points_to_add.at(spawn_points_to_add_id).first == map_data_.spawn_points.size()) {
1018 :
1019 0 : map_data_.spawn_points.push_back(spawn_points_to_add.at(spawn_points_to_add_id).second);
1020 0 : ++spawn_points_to_add_id;
1021 : } else {
1022 0 : map_data_.spawn_points.push_back(old_spawn_points.at(old_spawn_point_id));
1023 0 : ++old_spawn_point_id;
1024 : }
1025 : }
1026 :
1027 0 : map_change_events_.added_spawn_points.Notify(map_data_.spawn_points);
1028 0 : }
1029 :
1030 0 : void Map::RemoveSpawnPointsById(const std::vector<unsigned int>& spawn_point_ids)
1031 : {
1032 0 : std::vector<PMSSpawnPoint> old_spawn_points = map_data_.spawn_points;
1033 0 : std::vector<unsigned int> spawn_point_ids_to_remove = spawn_point_ids;
1034 0 : std::sort(spawn_point_ids_to_remove.begin(), spawn_point_ids_to_remove.end());
1035 0 : map_data_.spawn_points.clear();
1036 0 : unsigned int removal_id = 0;
1037 :
1038 0 : for (unsigned int i = 0; i < old_spawn_points.size(); ++i) {
1039 0 : if (removal_id < spawn_point_ids_to_remove.size() &&
1040 0 : i == spawn_point_ids_to_remove.at(removal_id)) {
1041 :
1042 0 : ++removal_id;
1043 0 : continue;
1044 : }
1045 :
1046 0 : map_data_.spawn_points.push_back(old_spawn_points.at(i));
1047 : }
1048 :
1049 0 : map_change_events_.removed_spawn_points.Notify(map_data_.spawn_points);
1050 0 : }
1051 :
1052 0 : unsigned int Map::AddNewScenery(const PMSScenery& scenery, const std::string& file_name)
1053 : {
1054 0 : bool scenery_type_exists = false;
1055 0 : unsigned short scenery_style = 0;
1056 :
1057 0 : for (const auto& scenery_type : GetSceneryTypes()) {
1058 0 : if (scenery_type.name == file_name) {
1059 0 : scenery_type_exists = true;
1060 0 : break;
1061 : }
1062 :
1063 0 : ++scenery_style;
1064 : }
1065 :
1066 0 : if (!scenery_type_exists) {
1067 0 : map_data_.scenery_types.push_back({ .name = file_name, .timestamp = {} });
1068 0 : scenery_style = map_data_.scenery_types.size() - 1;
1069 0 : map_change_events_.added_new_scenery_type.Notify(map_data_.scenery_types.back());
1070 : }
1071 :
1072 0 : ++scenery_style;
1073 :
1074 0 : map_data_.scenery_instances.push_back(scenery);
1075 0 : map_data_.scenery_instances.back().style = scenery_style;
1076 0 : unsigned int new_scenery_id = map_data_.scenery_instances.size() - 1;
1077 :
1078 0 : map_change_events_.added_new_scenery.Notify(map_data_.scenery_instances.back(), new_scenery_id);
1079 :
1080 0 : return new_scenery_id;
1081 0 : }
1082 :
1083 0 : PMSScenery Map::RemoveSceneryById(unsigned int id)
1084 : {
1085 0 : PMSScenery removed_scenery = map_data_.scenery_instances.at(id);
1086 0 : unsigned short removed_scenery_style = removed_scenery.style;
1087 :
1088 0 : bool other_scenery_with_same_style_exists = false;
1089 0 : for (unsigned int i = 0; i < GetSceneryInstances().size(); ++i) {
1090 0 : if (i == id) {
1091 0 : continue;
1092 : }
1093 :
1094 0 : if (GetSceneryInstances().at(i).style == removed_scenery_style) {
1095 0 : other_scenery_with_same_style_exists = true;
1096 0 : break;
1097 : }
1098 : }
1099 :
1100 0 : map_data_.scenery_instances.erase(map_data_.scenery_instances.begin() + id);
1101 :
1102 0 : if (!other_scenery_with_same_style_exists) {
1103 0 : PMSSceneryType removed_scenery_type = map_data_.scenery_types.at(removed_scenery_style - 1);
1104 0 : map_data_.scenery_types.erase(map_data_.scenery_types.begin() +
1105 0 : (removed_scenery_style - 1));
1106 0 : for (auto& scenery : map_data_.scenery_instances) {
1107 0 : if (scenery.style > removed_scenery_style) {
1108 0 : --scenery.style;
1109 : }
1110 : }
1111 :
1112 0 : map_change_events_.removed_scenery.Notify(removed_scenery, id, map_data_.scenery_instances);
1113 0 : map_change_events_.removed_scenery_type.Notify(
1114 0 : removed_scenery_type, removed_scenery_style, map_data_.scenery_types);
1115 0 : } else {
1116 0 : map_change_events_.removed_scenery.Notify(removed_scenery, id, map_data_.scenery_instances);
1117 : }
1118 :
1119 0 : return removed_scenery;
1120 : }
1121 :
1122 0 : void Map::AddSceneries(
1123 : const std::vector<std::pair<unsigned int, std::pair<PMSScenery, std::string>>>& sceneries)
1124 : {
1125 0 : auto sceneries_to_add = sceneries;
1126 0 : std::sort(sceneries_to_add.begin(), sceneries_to_add.end(), [](const auto& a, const auto& b) {
1127 0 : return a.first < b.first;
1128 : });
1129 :
1130 0 : for (auto& scenery_to_add : sceneries_to_add) {
1131 0 : std::string file_name = scenery_to_add.second.second;
1132 0 : bool found = false;
1133 0 : for (unsigned int i = 0; i < map_data_.scenery_types.size(); ++i) {
1134 0 : if (map_data_.scenery_types.at(i).name == file_name) {
1135 0 : scenery_to_add.second.first.style = i + 1;
1136 0 : found = true;
1137 0 : break;
1138 : }
1139 : }
1140 :
1141 0 : if (!found) {
1142 0 : map_data_.scenery_types.push_back({ .name = file_name });
1143 0 : scenery_to_add.second.first.style = map_data_.scenery_types.size();
1144 0 : map_change_events_.added_new_scenery_type.Notify(map_data_.scenery_types.back());
1145 : }
1146 0 : }
1147 :
1148 0 : std::vector<PMSScenery> old_sceneries = map_data_.scenery_instances;
1149 0 : map_data_.scenery_instances.clear();
1150 0 : unsigned int old_scenery_id = 0;
1151 0 : unsigned int sceneries_to_add_id = 0;
1152 :
1153 0 : while (old_scenery_id < old_sceneries.size() || sceneries_to_add_id < sceneries_to_add.size()) {
1154 :
1155 0 : if (sceneries_to_add_id < sceneries_to_add.size() &&
1156 0 : sceneries_to_add.at(sceneries_to_add_id).first == map_data_.scenery_instances.size()) {
1157 :
1158 0 : map_data_.scenery_instances.push_back(
1159 0 : sceneries_to_add.at(sceneries_to_add_id).second.first);
1160 0 : ++sceneries_to_add_id;
1161 : } else {
1162 0 : map_data_.scenery_instances.push_back(old_sceneries.at(old_scenery_id));
1163 0 : ++old_scenery_id;
1164 : }
1165 : }
1166 :
1167 0 : map_change_events_.added_sceneries.Notify(map_data_.scenery_instances);
1168 0 : }
1169 :
1170 0 : void Map::RemoveSceneriesById(const std::vector<unsigned int>& scenery_ids)
1171 : {
1172 0 : std::vector<PMSScenery> old_sceneries = map_data_.scenery_instances;
1173 0 : std::vector<unsigned int> scenery_ids_to_remove = scenery_ids;
1174 0 : std::sort(scenery_ids_to_remove.begin(), scenery_ids_to_remove.end());
1175 0 : map_data_.scenery_instances.clear();
1176 0 : unsigned int removal_id = 0;
1177 0 : std::array<unsigned int, MAX_SCENERIES_COUNT + 10> scenery_type_usage_count{};
1178 0 : scenery_type_usage_count.fill(0);
1179 :
1180 0 : for (unsigned int i = 0; i < old_sceneries.size(); ++i) {
1181 0 : if (removal_id < scenery_ids_to_remove.size() &&
1182 0 : i == scenery_ids_to_remove.at(removal_id)) {
1183 :
1184 0 : ++removal_id;
1185 0 : continue;
1186 : }
1187 :
1188 0 : map_data_.scenery_instances.push_back(old_sceneries.at(i));
1189 0 : ++scenery_type_usage_count.at(map_data_.scenery_instances.back().style);
1190 : }
1191 :
1192 0 : std::array<unsigned int, MAX_SCENERIES_COUNT + 10> scenery_type_new_style{};
1193 0 : for (unsigned int i = 0; i < scenery_type_new_style.size(); ++i) {
1194 0 : scenery_type_new_style.at(i) = i;
1195 : }
1196 :
1197 0 : unsigned int acc = 0;
1198 0 : for (unsigned int i = 1; i < scenery_type_new_style.size(); ++i) {
1199 0 : if (scenery_type_usage_count.at(i) == 0) {
1200 0 : scenery_type_new_style.at(i) = 0;
1201 0 : acc++;
1202 : }
1203 :
1204 0 : scenery_type_new_style.at(i) -= acc;
1205 : }
1206 :
1207 0 : std::vector<PMSSceneryType> old_scenery_types = map_data_.scenery_types;
1208 0 : std::vector<std::pair<unsigned short, PMSSceneryType>> removed_scenery_types;
1209 0 : map_data_.scenery_types.clear();
1210 :
1211 0 : for (unsigned int i = 0; i < old_scenery_types.size(); ++i) {
1212 0 : if (scenery_type_usage_count.at(i + 1) != 0) {
1213 0 : map_data_.scenery_types.push_back(old_scenery_types.at(i));
1214 : } else {
1215 0 : removed_scenery_types.emplace_back(i + 1, old_scenery_types.at(i));
1216 : }
1217 : }
1218 :
1219 0 : for (auto& scenery_instance : map_data_.scenery_instances) {
1220 0 : scenery_instance.style = scenery_type_new_style.at(scenery_instance.style);
1221 : }
1222 :
1223 0 : map_change_events_.removed_scenery_types.Notify(removed_scenery_types);
1224 0 : map_change_events_.removed_sceneries.Notify(map_data_.scenery_instances);
1225 0 : }
1226 :
1227 0 : void Map::SetSceneriesColorById(
1228 : const std::vector<std::pair<unsigned int, PMSColor>>& scenery_ids_with_new_color)
1229 : {
1230 0 : for (const auto& [scenery_id, new_color] : scenery_ids_with_new_color) {
1231 0 : map_data_.scenery_instances.at(scenery_id).color = new_color;
1232 0 : map_data_.scenery_instances.at(scenery_id).alpha = new_color.alpha;
1233 : }
1234 :
1235 0 : map_change_events_.modified_sceneries.Notify(map_data_.scenery_instances);
1236 0 : }
1237 :
1238 0 : void Map::MoveSceneriesById(
1239 : const std::vector<std::pair<unsigned int, glm::vec2>>& scenery_ids_with_new_position)
1240 : {
1241 0 : for (const auto& [scenery_id, new_position] : scenery_ids_with_new_position) {
1242 0 : map_data_.scenery_instances.at(scenery_id).x = new_position.x;
1243 0 : map_data_.scenery_instances.at(scenery_id).y = new_position.y;
1244 : }
1245 :
1246 0 : map_change_events_.modified_sceneries.Notify(map_data_.scenery_instances);
1247 0 : }
1248 :
1249 0 : void Map::SetSceneriesById(const std::vector<std::pair<unsigned int, PMSScenery>>& sceneries)
1250 : {
1251 0 : for (const auto& [scenery_id, scenery] : sceneries) {
1252 0 : map_data_.scenery_instances.at(scenery_id) = scenery;
1253 : }
1254 :
1255 0 : map_change_events_.modified_sceneries.Notify(map_data_.scenery_instances);
1256 0 : }
1257 :
1258 0 : std::array<glm::vec2, 4> Map::GetSceneryVertexPositions(const PMSScenery& scenery)
1259 : {
1260 0 : glm::mat4 transform_matrix(1.0F);
1261 0 : transform_matrix = glm::rotate(transform_matrix, -scenery.rotation, glm::vec3(0.0, 0.0, 1.0));
1262 : transform_matrix =
1263 0 : glm::scale(transform_matrix, glm::vec3(scenery.scale_x, scenery.scale_y, 0.0));
1264 :
1265 0 : std::array<glm::vec2, 4> vertex_positions{
1266 0 : glm::vec2{ 0.0F, scenery.height },
1267 0 : glm::vec2{ scenery.width, scenery.height },
1268 0 : glm::vec2{ scenery.width, 0.0F },
1269 : glm::vec2{ 0.0F, 0.0F },
1270 0 : };
1271 :
1272 0 : for (auto& vertex_position : vertex_positions) {
1273 : glm::vec4 v =
1274 0 : transform_matrix * glm::vec4{ vertex_position.x, vertex_position.y, 1.0F, 1.0F };
1275 0 : vertex_position.x = v.x;
1276 0 : vertex_position.y = v.y;
1277 0 : vertex_position.x += scenery.x;
1278 0 : vertex_position.y += scenery.y;
1279 : }
1280 :
1281 0 : return vertex_positions;
1282 : }
1283 :
1284 0 : void Map::GenerateSectors()
1285 : {
1286 0 : if (are_sectors_generated_) {
1287 0 : return;
1288 : }
1289 0 : are_sectors_generated_ = true;
1290 :
1291 0 : FixPolygonIds();
1292 0 : UpdateMinMaxPolygonPositions();
1293 0 : UpdateBoundaries();
1294 0 : for (auto& polygon : map_data_.polygons) {
1295 : // Polygons' vertices have to be arranged in clock-wise order.
1296 0 : if (!polygon.AreVerticesClockwise()) {
1297 0 : PMSVertex tmp = polygon.vertices[1];
1298 0 : polygon.vertices[1] = polygon.vertices[2];
1299 0 : polygon.vertices[2] = tmp;
1300 : }
1301 :
1302 0 : for (auto& vertex : polygon.vertices) {
1303 0 : vertex.x -= map_data_.center_x;
1304 0 : vertex.y -= map_data_.center_y;
1305 : }
1306 : }
1307 0 : for (auto& scenery : map_data_.scenery_instances) {
1308 0 : scenery.x -= map_data_.center_x;
1309 0 : scenery.y -= map_data_.center_y;
1310 : }
1311 0 : for (auto& spawn_point : map_data_.spawn_points) {
1312 0 : spawn_point.x -= map_data_.center_x;
1313 0 : spawn_point.y -= map_data_.center_y;
1314 : }
1315 0 : UpdateMinMaxPolygonPositions();
1316 0 : UpdateBoundaries();
1317 0 : map_change_events_.modified_polygons.Notify(map_data_.polygons);
1318 0 : map_change_events_.modified_sceneries.Notify(map_data_.scenery_instances);
1319 0 : map_change_events_.modified_spawn_points.Notify(map_data_.spawn_points);
1320 :
1321 0 : map_data_.sectors_count = 25;
1322 0 : int n = 2 * map_data_.sectors_count + 1;
1323 0 : map_data_.sectors_poly = std::vector<std::vector<PMSSector>>(n, std::vector<PMSSector>(n));
1324 :
1325 0 : if (map_data_.width > map_data_.height) {
1326 0 : map_data_.sectors_size = floor((map_data_.width + 2.0 * 100.0F) / (float)(n - 1));
1327 : } else {
1328 0 : map_data_.sectors_size = floor((map_data_.height + 2.0 * 100.0F) / (float)(n - 1));
1329 : }
1330 :
1331 0 : for (int x = 0; x < n; ++x) {
1332 0 : for (int y = 0; y < n; ++y) {
1333 0 : map_data_.sectors_poly[x][y].boundaries[LeftBoundary] = std::floor(
1334 0 : (float)map_data_.sectors_size * ((float)x - (float)map_data_.sectors_count - 0.5F) -
1335 0 : 1.0F + map_data_.center_x);
1336 0 : map_data_.sectors_poly[x][y].boundaries[TopBoundary] = std::floor(
1337 0 : (float)map_data_.sectors_size * ((float)y - (float)map_data_.sectors_count - 0.5F) -
1338 0 : 1.0F + map_data_.center_y);
1339 0 : map_data_.sectors_poly[x][y].boundaries[RightBoundary] =
1340 0 : map_data_.sectors_poly[x][y].boundaries[LeftBoundary] + (float)map_data_.sectors_size;
1341 0 : map_data_.sectors_poly[x][y].boundaries[BottomBoundary] =
1342 0 : map_data_.sectors_poly[x][y].boundaries[TopBoundary] + (float)map_data_.sectors_size;
1343 :
1344 0 : for (unsigned int i = 0; i < map_data_.polygons.size(); ++i) {
1345 0 : if (IsPolygonInSector(i,
1346 0 : floor((float)map_data_.sectors_size *
1347 0 : ((float)x - (float)map_data_.sectors_count - 0.5F) -
1348 0 : 1.0F + map_data_.center_x),
1349 0 : floor((float)map_data_.sectors_size *
1350 0 : ((float)y - (float)map_data_.sectors_count - 0.5F) -
1351 0 : 1.0F + map_data_.center_y),
1352 0 : (float)map_data_.sectors_size + 2)) {
1353 :
1354 0 : map_data_.sectors_poly[x][y].polygons.push_back(i + 1);
1355 : }
1356 : }
1357 : }
1358 : }
1359 : }
1360 :
1361 0 : bool Map::IsPolygonInSector(unsigned short polygon_index,
1362 : float sector_x,
1363 : float sector_y,
1364 : float sector_size)
1365 : {
1366 0 : if (map_data_.polygons[polygon_index].polygon_type == PMSPolygonType::NoCollide) {
1367 0 : return false;
1368 : }
1369 :
1370 0 : if ((map_data_.polygons[polygon_index].vertices[0].x < sector_x &&
1371 0 : map_data_.polygons[polygon_index].vertices[1].x < sector_x &&
1372 0 : map_data_.polygons[polygon_index].vertices[2].x < sector_x) ||
1373 0 : (map_data_.polygons[polygon_index].vertices[0].x > sector_x + sector_size &&
1374 0 : map_data_.polygons[polygon_index].vertices[1].x > sector_x + sector_size &&
1375 0 : map_data_.polygons[polygon_index].vertices[2].x > sector_x + sector_size) ||
1376 0 : (map_data_.polygons[polygon_index].vertices[0].y < sector_y &&
1377 0 : map_data_.polygons[polygon_index].vertices[1].y < sector_y &&
1378 0 : map_data_.polygons[polygon_index].vertices[2].y < sector_y) ||
1379 0 : (map_data_.polygons[polygon_index].vertices[0].y > sector_y + sector_size &&
1380 0 : map_data_.polygons[polygon_index].vertices[1].y > sector_y + sector_size &&
1381 0 : map_data_.polygons[polygon_index].vertices[2].y > sector_y + sector_size)) {
1382 0 : return false;
1383 : }
1384 :
1385 : // Check if any of the polygon's vertices is inside the sector.
1386 0 : unsigned int i = 0;
1387 0 : for (i = 0; i < 3; ++i) {
1388 0 : if (map_data_.polygons[polygon_index].vertices.at(i).x >= sector_x &&
1389 0 : map_data_.polygons[polygon_index].vertices.at(i).x <= sector_x + sector_size &&
1390 0 : map_data_.polygons[polygon_index].vertices.at(i).y >= sector_y &&
1391 0 : map_data_.polygons[polygon_index].vertices.at(i).y <= sector_y + sector_size) {
1392 0 : return true;
1393 : }
1394 : }
1395 :
1396 : // Check if any of the 4 sector's corners is inside the polygon.
1397 0 : if (PointInPoly({ sector_x, sector_y }, map_data_.polygons[polygon_index]) ||
1398 0 : PointInPoly({ sector_x + sector_size, sector_y }, map_data_.polygons[polygon_index]) ||
1399 0 : PointInPoly({ sector_x + sector_size, sector_y + sector_size },
1400 0 : map_data_.polygons[polygon_index]) ||
1401 0 : PointInPoly({ sector_x, sector_y + sector_size }, map_data_.polygons[polygon_index])) {
1402 0 : return true;
1403 : }
1404 :
1405 : /**
1406 : * Check intersections between polygon's sides and sector's sides.
1407 : * AB is polygon's side, CD is sector's side.
1408 : */
1409 0 : unsigned int j = 0;
1410 : glm::vec2 a;
1411 : glm::vec2 b;
1412 : glm::vec2 c;
1413 : glm::vec2 d;
1414 0 : for (i = 0; i < 3; ++i) {
1415 0 : j = i + 1;
1416 0 : if (j > 2) {
1417 0 : j = 0;
1418 : }
1419 :
1420 0 : a = { map_data_.polygons[polygon_index].vertices.at(i).x,
1421 0 : map_data_.polygons[polygon_index].vertices.at(i).y };
1422 0 : b = { map_data_.polygons[polygon_index].vertices.at(j).x,
1423 0 : map_data_.polygons[polygon_index].vertices.at(j).y };
1424 :
1425 : // Top side of sector.
1426 0 : c = { sector_x, sector_y };
1427 0 : d = { sector_x + sector_size, sector_y };
1428 0 : if (Calc::SegmentsIntersect(a, b, c, d)) {
1429 0 : return true;
1430 : }
1431 :
1432 : // Right side of sector.
1433 0 : c = { sector_x + sector_size, sector_y };
1434 0 : d = { sector_x + sector_size, sector_y + sector_size };
1435 0 : if (Calc::SegmentsIntersect(a, b, c, d)) {
1436 0 : return true;
1437 : }
1438 :
1439 : // Bottom side of sector.
1440 0 : c = { sector_x, sector_y + sector_size };
1441 0 : d = { sector_x + sector_size, sector_y + sector_size };
1442 0 : if (Calc::SegmentsIntersect(a, b, c, d)) {
1443 0 : return true;
1444 : }
1445 :
1446 : // Left side of sector.
1447 0 : c = { sector_x, sector_y };
1448 0 : d = { sector_x, sector_y + sector_size };
1449 0 : if (Calc::SegmentsIntersect(a, b, c, d)) {
1450 0 : return true;
1451 : }
1452 : }
1453 :
1454 0 : return false;
1455 : }
1456 :
1457 0 : void Map::SetPolygonVerticesAndPerpendiculars(PMSPolygon& polygon)
1458 : {
1459 : // Polygons' vertices have to be arranged in clock-wise order.
1460 0 : if (!polygon.AreVerticesClockwise()) {
1461 0 : PMSVertex tmp = polygon.vertices[1];
1462 0 : polygon.vertices[1] = polygon.vertices[2];
1463 0 : polygon.vertices[2] = tmp;
1464 : }
1465 :
1466 0 : for (int i = 0; i < 3; ++i) {
1467 0 : unsigned int j = i + 1;
1468 0 : if (j > 2) {
1469 0 : j = 0;
1470 : }
1471 :
1472 0 : float diff_x = polygon.vertices.at(j).x - polygon.vertices.at(i).x;
1473 0 : float diff_y = polygon.vertices.at(i).y - polygon.vertices.at(j).y;
1474 0 : float length = NAN;
1475 0 : if (fabs(diff_x) < 0.00001F && fabs(diff_y) < 0.00001F) {
1476 0 : length = 1.0F;
1477 : } else {
1478 0 : length = hypotf(diff_x, diff_y);
1479 : }
1480 :
1481 0 : float bounciness = 1.0F;
1482 :
1483 0 : if (polygon.polygon_type == PMSPolygonType::Bouncy) {
1484 0 : if (bounciness < 1.0F) {
1485 0 : bounciness = 1.0F;
1486 : } else {
1487 0 : bounciness = polygon.bounciness;
1488 : }
1489 : } else {
1490 0 : bounciness = 1.0F;
1491 : }
1492 :
1493 0 : polygon.perpendiculars.at(i).x = (diff_y / length);
1494 0 : polygon.perpendiculars.at(i).y = (diff_x / length);
1495 0 : polygon.perpendiculars.at(i).z = 1.0F;
1496 : }
1497 0 : }
1498 :
1499 0 : void Map::FixPolygonIds()
1500 : {
1501 0 : unsigned int next_id = 0;
1502 0 : for (auto& polygon : map_data_.polygons) {
1503 0 : polygon.id = next_id;
1504 0 : ++next_id;
1505 : }
1506 0 : }
1507 :
1508 0 : void Map::UpdateMinMaxPolygonPositions(const PMSPolygon& polygon, bool should_notify)
1509 : {
1510 0 : for (unsigned int i = 0; i < 3; ++i) {
1511 0 : const auto& vertex = polygon.vertices.at(i);
1512 :
1513 0 : if (vertex.x < map_data_.polygons_min_x) {
1514 0 : map_data_.polygons_min_x = vertex.x;
1515 : }
1516 0 : if (vertex.x > map_data_.polygons_max_x) {
1517 0 : map_data_.polygons_max_x = vertex.x;
1518 : }
1519 :
1520 0 : if (vertex.y < map_data_.polygons_min_y) {
1521 0 : map_data_.polygons_min_y = vertex.y;
1522 : }
1523 0 : if (vertex.y > map_data_.polygons_max_y) {
1524 0 : map_data_.polygons_max_y = vertex.y;
1525 : }
1526 : }
1527 :
1528 0 : if (should_notify) {
1529 0 : map_change_events_.changed_background_color.Notify(
1530 0 : map_data_.background_top_color, map_data_.background_bottom_color, GetBoundaries());
1531 : }
1532 0 : }
1533 :
1534 0 : void Map::UpdateMinMaxPolygonPositions()
1535 : {
1536 0 : map_data_.polygons_min_x = 0.0F;
1537 0 : map_data_.polygons_max_x = 0.0F;
1538 0 : map_data_.polygons_min_y = 0.0F;
1539 0 : map_data_.polygons_max_y = 0.0F;
1540 :
1541 0 : for (const auto& polygon : map_data_.polygons) {
1542 0 : UpdateMinMaxPolygonPositions(polygon, false);
1543 : }
1544 :
1545 0 : map_change_events_.changed_background_color.Notify(
1546 0 : map_data_.background_top_color, map_data_.background_bottom_color, GetBoundaries());
1547 0 : }
1548 : } // namespace Soldank
|