LCOV - code coverage report
Current view: top level - core/map - Map.cpp (source / functions) Coverage Total Hit
Test: coverage-src.info Lines: 15.4 % 957 147
Test Date: 2025-05-27 23:26:07 Functions: 17.9 % 56 10

            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
        

Generated by: LCOV version 2.0-1