Line data Source code
1 : #ifndef __MAP_HPP__
2 : #define __MAP_HPP__
3 :
4 : #include "core/map/PMSConstants.hpp"
5 : #include "core/map/PMSEnums.hpp"
6 : #include "core/map/PMSStructs.hpp"
7 :
8 : #include "core/math/Glm.hpp"
9 : #include "core/data/IFileReader.hpp"
10 : #include "core/data/FileReader.hpp"
11 : #include "core/data/IFileWriter.hpp"
12 : #include "core/data/FileWriter.hpp"
13 :
14 : #include "core/utility/Observable.hpp"
15 :
16 : #include <bitset>
17 : #include <optional>
18 : #include <utility>
19 : #include <vector>
20 : #include <sstream>
21 : #include <cstring>
22 : #include <span>
23 : #include <array>
24 : #include <memory>
25 : #include <filesystem>
26 :
27 : namespace Soldank
28 : {
29 : struct MapData
30 : {
31 : std::array<float, 4> boundaries_xy;
32 : float polygons_min_x;
33 : float polygons_max_x;
34 : float polygons_min_y;
35 : float polygons_max_y;
36 : float width;
37 : float height;
38 : float center_x;
39 : float center_y;
40 :
41 : int version;
42 :
43 : std::optional<std::string> name;
44 : std::string description;
45 : std::string texture_name;
46 :
47 : PMSColor background_top_color;
48 : PMSColor background_bottom_color;
49 :
50 : int jet_count;
51 : unsigned char grenades_count;
52 : unsigned char medikits_count;
53 : PMSWeatherType weather_type;
54 : PMSStepType step_type;
55 : int random_id;
56 :
57 : std::vector<PMSPolygon> polygons;
58 :
59 : int sectors_size;
60 : int sectors_count;
61 :
62 : std::vector<std::vector<PMSSector>> sectors_poly;
63 : std::vector<PMSScenery> scenery_instances;
64 : std::vector<PMSSceneryType> scenery_types;
65 : std::vector<PMSCollider> colliders;
66 : std::vector<PMSSpawnPoint> spawn_points;
67 : std::vector<PMSWayPoint> way_points;
68 : };
69 :
70 : struct MapChangeEvents
71 : {
72 : Observable<const PMSPolygon&> added_new_polygon;
73 : Observable<const PMSPolygon&, const std::vector<PMSPolygon>&> removed_polygon;
74 : Observable<const std::vector<PMSPolygon>, const std::vector<PMSPolygon>&> added_new_polygons;
75 : Observable<const std::vector<PMSPolygon>, const std::vector<PMSPolygon>&> removed_polygons;
76 : Observable<const PMSSpawnPoint&, unsigned int> added_new_spawn_point;
77 : Observable<const PMSSpawnPoint&, unsigned int> removed_spawn_point;
78 : Observable<const std::vector<PMSSpawnPoint>&> removed_spawn_points;
79 : Observable<const std::vector<PMSSpawnPoint>&> added_spawn_points;
80 : Observable<const PMSColor&, const PMSColor&, std::span<const float, 4>>
81 : changed_background_color;
82 : Observable<const std::string&> changed_texture_name;
83 : Observable<const PMSScenery&, unsigned int> added_new_scenery;
84 : Observable<const PMSScenery&, unsigned int, const std::vector<PMSScenery>&> removed_scenery;
85 : Observable<const PMSSceneryType&> added_new_scenery_type;
86 : Observable<const PMSSceneryType&, unsigned short, const std::vector<PMSSceneryType>&>
87 : removed_scenery_type;
88 : Observable<const std::vector<PMSScenery>&> added_sceneries;
89 : Observable<const std::vector<PMSScenery>&> removed_sceneries;
90 : Observable<const std::vector<std::pair<unsigned short, PMSSceneryType>>&> removed_scenery_types;
91 : Observable<const std::vector<PMSScenery>&> modified_sceneries;
92 : Observable<const std::vector<PMSPolygon>&> modified_polygons;
93 : Observable<const std::vector<PMSSpawnPoint>&> modified_spawn_points;
94 : };
95 :
96 : class Map
97 : {
98 : public:
99 2 : Map() = default;
100 1 : Map(MapData map_data)
101 1 : : map_data_(std::move(map_data)){};
102 :
103 : void CreateEmptyMap();
104 : void LoadMap(const std::filesystem::path& map_path,
105 : const IFileReader& file_reader = FileReader());
106 : void SaveMap(const std::filesystem::path& map_path,
107 : std::shared_ptr<IFileWriter> file_writer = std::make_shared<FileWriter>());
108 :
109 : static bool PointInPoly(glm::vec2 p, PMSPolygon poly);
110 : bool PointInPolyEdges(float x, float y, int i) const;
111 : static bool PointInScenery(glm::vec2 p, const PMSScenery& scenery);
112 : glm::vec2 ClosestPerpendicular(int j, glm::vec2 pos, float* d, int* n) const;
113 : bool CollisionTest(glm::vec2 pos, glm::vec2& perp_vec, bool is_flag = false) const;
114 : bool RayCast(glm::vec2 a,
115 : glm::vec2 b,
116 : float& distance,
117 : float max_dist,
118 : bool player = false,
119 : bool flag = false,
120 : bool bullet = true,
121 : bool check_collider = false,
122 : std::uint8_t team_id = 0) const;
123 : static bool LineInPoly(const glm::vec2& a,
124 : const glm::vec2& b,
125 : const PMSPolygon& polygon,
126 : glm::vec2& v);
127 :
128 : MapChangeEvents& GetMapChangeEvents() { return map_change_events_; }
129 :
130 : PMSPolygon AddNewPolygon(const PMSPolygon& polygon);
131 : void AddPolygons(const std::vector<PMSPolygon>& polygons);
132 : PMSPolygon RemovePolygonById(unsigned int id);
133 : void RemovePolygonsById(const std::vector<unsigned int>& polygon_ids);
134 : void SetPolygonVerticesColorById(
135 : const std::vector<std::pair<std::pair<unsigned int, unsigned int>, PMSColor>>&
136 : polygon_vertices_with_new_color);
137 : void MovePolygonVerticesById(
138 : const std::vector<std::pair<std::pair<unsigned int, unsigned int>, glm::vec2>>&
139 : polygon_vertices_with_new_position);
140 : void SetPolygonsById(const std::vector<std::pair<unsigned int, PMSPolygon>>& polygons);
141 :
142 : unsigned int AddNewSpawnPoint(const PMSSpawnPoint& spawn_point);
143 : PMSSpawnPoint RemoveSpawnPointById(unsigned int id);
144 : void AddSpawnPoints(const std::vector<std::pair<unsigned int, PMSSpawnPoint>>& spawn_points);
145 : void RemoveSpawnPointsById(const std::vector<unsigned int>& spawn_point_ids);
146 : void MoveSpawnPointsById(
147 : const std::vector<std::pair<unsigned int, glm::ivec2>>& spawn_point_ids_with_new_position);
148 : void SetSpawnPointsById(
149 : const std::vector<std::pair<unsigned int, PMSSpawnPoint>>& spawn_points);
150 :
151 : unsigned int AddNewScenery(const PMSScenery& scenery, const std::string& file_name);
152 : PMSScenery RemoveSceneryById(unsigned int id);
153 : void AddSceneries(
154 : const std::vector<std::pair<unsigned int, std::pair<PMSScenery, std::string>>>& sceneries);
155 : void RemoveSceneriesById(const std::vector<unsigned int>& scenery_ids);
156 : void SetSceneriesColorById(
157 : const std::vector<std::pair<unsigned int, PMSColor>>& scenery_ids_with_new_color);
158 : void MoveSceneriesById(
159 : const std::vector<std::pair<unsigned int, glm::vec2>>& scenery_ids_with_new_position);
160 : void SetSceneriesById(const std::vector<std::pair<unsigned int, PMSScenery>>& sceneries);
161 :
162 : static std::array<glm::vec2, 4> GetSceneryVertexPositions(const PMSScenery& scenery);
163 :
164 1 : int GetVersion() const { return map_data_.version; }
165 :
166 : std::optional<std::string> GetName() const { return map_data_.name; }
167 : void SetName(const std::string& new_name) { map_data_.name = new_name; }
168 :
169 : void SetDescription(const std::string& new_description)
170 : {
171 : map_data_.description = new_description;
172 : }
173 :
174 2 : std::string GetDescription() const { return map_data_.description; }
175 :
176 : void SetBackgroundTopColor(const PMSColor& color)
177 : {
178 : map_data_.background_top_color = color;
179 : map_change_events_.changed_background_color.Notify(
180 : map_data_.background_top_color, map_data_.background_bottom_color, GetBoundaries());
181 : }
182 1 : PMSColor GetBackgroundTopColor() const { return map_data_.background_top_color; }
183 :
184 : void SetBackgroundBottomColor(const PMSColor& color)
185 : {
186 : map_data_.background_bottom_color = color;
187 : map_change_events_.changed_background_color.Notify(
188 : map_data_.background_top_color, map_data_.background_bottom_color, GetBoundaries());
189 : }
190 1 : PMSColor GetBackgroundBottomColor() const { return map_data_.background_bottom_color; }
191 :
192 1 : std::span<const float, 4> GetBoundaries() const { return map_data_.boundaries_xy; }
193 :
194 12 : const std::vector<PMSPolygon>& GetPolygons() const { return map_data_.polygons; }
195 :
196 4 : unsigned int GetPolygonsCount() const { return map_data_.polygons.size(); }
197 :
198 4 : const std::vector<PMSScenery>& GetSceneryInstances() const
199 : {
200 4 : return map_data_.scenery_instances;
201 : }
202 :
203 4 : const std::vector<PMSSceneryType>& GetSceneryTypes() const { return map_data_.scenery_types; }
204 :
205 4 : const std::vector<PMSSpawnPoint>& GetSpawnPoints() const { return map_data_.spawn_points; }
206 :
207 4 : const std::vector<PMSCollider>& GetColliders() const { return map_data_.colliders; }
208 :
209 4 : const std::vector<PMSWayPoint>& GetWayPoints() const { return map_data_.way_points; }
210 :
211 : void SetTextureName(const std::string& texture_name)
212 : {
213 : map_data_.texture_name = texture_name;
214 : map_change_events_.changed_texture_name.Notify(map_data_.texture_name);
215 : }
216 1 : const std::string& GetTextureName() const { return map_data_.texture_name; }
217 :
218 : void SetJetCount(int jet_count) { map_data_.jet_count = jet_count; }
219 1 : int GetJetCount() const { return map_data_.jet_count; }
220 :
221 : void SetGrenadesCount(unsigned char grenades_count)
222 : {
223 : map_data_.grenades_count = grenades_count;
224 : }
225 1 : unsigned char GetGrenadesCount() const { return map_data_.grenades_count; }
226 :
227 : void SetMedikitsCount(unsigned char medikits_count)
228 : {
229 : map_data_.medikits_count = medikits_count;
230 : }
231 1 : unsigned char GetMedikitsCount() const { return map_data_.medikits_count; }
232 :
233 : void SetWeatherType(PMSWeatherType weather_type) { map_data_.weather_type = weather_type; }
234 1 : PMSWeatherType GetWeatherType() const { return map_data_.weather_type; }
235 : std::string GetWeatherTypeText() const
236 : {
237 : switch (map_data_.weather_type) {
238 : case PMSWeatherType::None:
239 : return "None";
240 : case PMSWeatherType::Rain:
241 : return "Rain";
242 : case PMSWeatherType::Sandstorm:
243 : return "Sandstorm";
244 : case PMSWeatherType::Snow:
245 : return "Snow";
246 : }
247 : }
248 :
249 : void SetStepType(PMSStepType step_type) { map_data_.step_type = step_type; }
250 1 : PMSStepType GetStepType() const { return map_data_.step_type; }
251 : std::string GetStepTypeText() const
252 : {
253 : switch (map_data_.step_type) {
254 : case PMSStepType::HardGround:
255 : return "Hard";
256 : case PMSStepType::SoftGround:
257 : return "Soft";
258 : case PMSStepType::None:
259 : return "None";
260 : }
261 : }
262 :
263 1 : int GetSectorsSize() const { return map_data_.sectors_size; }
264 :
265 1 : int GetSectorsCount() const { return map_data_.sectors_count; }
266 :
267 2610 : const PMSSector& GetSector(unsigned int x, unsigned int y) const
268 : {
269 2610 : return map_data_.sectors_poly[x][y];
270 : }
271 :
272 : std::optional<PMSSpawnPoint> FindFirstSpawnPoint(PMSSpawnPointType spawn_point_type) const;
273 :
274 : void GenerateSectors();
275 : glm::vec2 GetCenter() const { return { map_data_.center_x, map_data_.center_y }; }
276 :
277 : enum MapBoundary
278 : {
279 : TopBoundary = 0,
280 : BottomBoundary,
281 : LeftBoundary,
282 : RightBoundary
283 : };
284 :
285 : private:
286 : MapData map_data_;
287 : MapChangeEvents map_change_events_;
288 :
289 : template<typename DataType>
290 54 : static void ReadFromBuffer(std::stringstream& buffer, DataType& variable_to_save_data)
291 : {
292 54 : buffer.read((char*)&variable_to_save_data, sizeof(DataType));
293 54 : }
294 :
295 3 : static void ReadStringFromBuffer(std::stringstream& buffer,
296 : std::string& string_to_save_data,
297 : unsigned int max_string_size)
298 : {
299 3 : unsigned char string_size = 0;
300 3 : ReadFromBuffer(buffer, string_size);
301 : // We need an array with dynamic size here
302 : // NOLINTNEXTLINE(modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays)
303 3 : auto bytes = std::make_unique<char[]>(string_size);
304 3 : buffer.read(bytes.get(), string_size);
305 3 : string_to_save_data.assign(bytes.get(), string_size);
306 : // We need an array with dynamic size here
307 : // NOLINTNEXTLINE(modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays)
308 3 : auto filler = std::make_unique<char[]>(max_string_size - string_size);
309 3 : buffer.read(filler.get(), max_string_size - string_size);
310 3 : }
311 :
312 : template<typename DataType>
313 0 : static void AppendToFileWriter(std::shared_ptr<IFileWriter>& file_writer, const DataType& data)
314 : {
315 : // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
316 0 : file_writer->AppendData(reinterpret_cast<const char*>(&data), sizeof(data));
317 0 : }
318 :
319 0 : static void AppendStringToFileWriter(std::shared_ptr<IFileWriter>& file_writer,
320 : const std::string& data,
321 : unsigned int max_string_size)
322 : {
323 0 : unsigned char string_size = data.length();
324 : // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
325 0 : file_writer->AppendData(reinterpret_cast<char*>(&string_size), sizeof(string_size));
326 0 : unsigned long long data_size = data.length() * sizeof(char);
327 0 : file_writer->AppendData(data.c_str(), (std::streamsize)data_size);
328 0 : std::array<char, 64> filler{};
329 0 : filler.fill(0);
330 0 : data_size = (max_string_size - data.length()) * sizeof(char);
331 0 : file_writer->AppendData(filler.data(), (std::streamsize)data_size);
332 0 : }
333 :
334 : void ReadPolygonsFromBuffer(std::stringstream& buffer);
335 : void ReadSectorsFromBuffer(std::stringstream& buffer);
336 : void ReadSceneryInstancesFromBuffer(std::stringstream& buffer);
337 : void ReadSceneryTypesFromBuffer(std::stringstream& buffer);
338 : void ReadCollidersFromBuffer(std::stringstream& buffer);
339 : void ReadSpawnPointsFromBuffer(std::stringstream& buffer);
340 : void ReadWayPointsFromBuffer(std::stringstream& buffer);
341 :
342 : void AppendPolygonsToFileWriter(std::shared_ptr<IFileWriter>& file_writer);
343 : void AppendSectorsToFileWriter(std::shared_ptr<IFileWriter>& file_writer);
344 : void AppendSceneryInstancesToFileWriter(std::shared_ptr<IFileWriter>& file_writer);
345 : void AppendSceneryTypesToFileWriter(std::shared_ptr<IFileWriter>& file_writer);
346 : void AppendCollidersToFileWriter(std::shared_ptr<IFileWriter>& file_writer);
347 : void AppendSpawnPointsToFileWriter(std::shared_ptr<IFileWriter>& file_writer);
348 : void AppendWayPointsToFileWriter(std::shared_ptr<IFileWriter>& file_writer);
349 :
350 : void UpdateBoundaries();
351 : bool IsPolygonInSector(unsigned short polygon_index,
352 : float sector_x,
353 : float sector_y,
354 : float sector_size);
355 :
356 : static void SetPolygonVerticesAndPerpendiculars(PMSPolygon& polygon);
357 :
358 : void FixPolygonIds();
359 : void UpdateMinMaxPolygonPositions(const PMSPolygon& polygon, bool should_notify = true);
360 : void UpdateMinMaxPolygonPositions();
361 :
362 : bool are_sectors_generated_{};
363 : };
364 : } // namespace Soldank
365 :
366 : #endif
|