1#include <base/dbg.h>
2#include <base/logger.h>
3#include <base/mem.h>
4#include <base/os.h>
5#include <base/str.h>
6
7#include <engine/shared/datafile.h>
8#include <engine/storage.h>
9
10#include <game/mapitems.h>
11
12// global new layers data (set by ReplaceAreaTiles and ReplaceAreaQuads)
13static void *g_apNewData[1024];
14static void *g_apNewItem[1024];
15static int g_aNewDataSize[1024];
16
17class CMapObject // quad pivot or tile layer
18{
19public:
20 static constexpr float ms_aStandardScreen[2] = {1430 / 2.f, 1050 / 2.f};
21
22 float m_aLayerOffset[2];
23 bool m_UseClipping;
24 float m_aaClipArea[2][2];
25 float m_aSpeed[2];
26 float m_aaScreenOffset[2][2];
27 float m_aaBaseArea[2][2]; // adapted to offset
28 float m_aaExtendedArea[2][2]; // extended with parallax
29};
30
31static bool ReplaceArea(IStorage *, const char[3][64], const float[][2][2]);
32static bool OpenMaps(IStorage *, const char[3][64], CDataFileReader[2], CDataFileWriter &);
33static void SaveOutputMap(CDataFileReader &, CDataFileWriter &);
34static bool CompareLayers(const char[3][64], CDataFileReader[2]);
35static void CompareGroups(const char[3][64], CDataFileReader[2]);
36static const CMapItemGroup *GetLayerGroup(CDataFileReader &, int);
37
38static void ReplaceAreaTiles(CDataFileReader[2], const float[][2][2], const CMapItemGroup *[2], CMapItemLayer *[2]);
39static void RemoveDestinationTiles(CMapItemLayerTilemap *, CTile *, float[2][2]);
40static void ReplaceDestinationTiles(CMapItemLayerTilemap *[2], CTile *[2], float[2][2][2]);
41static bool AdaptVisibleAreas(const float[2][2][2], const CMapObject[2], float[2][2][2]);
42static bool AdaptReplaceableAreas(const float[2][2][2], const float[2][2][2], const CMapObject[2], float[2][2][2]);
43
44static void ReplaceAreaQuads(CDataFileReader[2], const float[][2][2], const CMapItemGroup *[2], CMapItemLayer *[2], int);
45static bool RemoveDestinationQuads(const float[2][2], const CQuad *, int, const CMapItemGroup *, CQuad *, int &);
46static bool InsertDestinationQuads(const float[2][2][2], const CQuad *, int, const CMapItemGroup *[2], CQuad *, int &);
47static bool AdaptVisiblePoint(const float[2][2][2], const float[2][2], const CMapObject[2], float[2]);
48
49static CMapObject CreateMapObject(const CMapItemGroup *, int, int, int, int);
50static void SetExtendedArea(CMapObject &);
51static bool GetVisibleArea(const float[2][2], const CMapObject &, float[2][2] = nullptr);
52static bool GetReplaceableArea(const float[2][2], const CMapObject &, float[2][2]);
53
54static void GetGameAreaDistance(const float[2][2][2], const CMapObject[2], const float[2][2][2], float[2]);
55static void GetGameAreaDistance(const float[2][2][2], const CMapObject[2], const float[2][2], float[2]);
56static void GetSignificantScreenPos(const CMapObject &, const float[2][2], const float[2][2], float[2]);
57static void ConvertToTiles(const float[2][2], int[2][2]);
58
59static bool GetLineIntersection(const float[2], const float[2], float[2]);
60static void SetInexistent(float *, int);
61static bool IsInexistent(const float *, int);
62
63int main(int argc, const char *argv[])
64{
65 CCmdlineFix CmdlineFix(&argc, &argv);
66 log_set_global_logger_default();
67
68 if(argc != 10)
69 {
70 dbg_msg(sys: "map_replace_area", fmt: "Invalid arguments");
71 dbg_msg(sys: "map_replace_area", fmt: "Usage: %s <from_map> <from_x> <from_y> <to_map> <to_x> <to_y> <width> <height> <output_map>", argv[0]);
72 dbg_msg(sys: "map_replace_area", fmt: "Note: use game layer tiles as a reference for both coordinates and sizes");
73
74 return -1;
75 }
76
77 char aaMapNames[3][64];
78 str_copy(dst&: aaMapNames[0], src: argv[1]); //from_map
79 str_copy(dst&: aaMapNames[1], src: argv[4]); //to_map
80 str_copy(dst&: aaMapNames[2], src: argv[9]); //output_map
81
82 float aaaGameAreas[2][2][2];
83
84 for(int i = 0; i < 2; i++)
85 {
86 aaaGameAreas[i][0][0] = str_tofloat(str: argv[2 + i * 3]) * 32; //x
87 aaaGameAreas[i][1][0] = str_tofloat(str: argv[3 + i * 3]) * 32; //y
88 aaaGameAreas[i][0][1] = aaaGameAreas[i][0][0] + str_tofloat(str: argv[7]) * 32; //x + width
89 aaaGameAreas[i][1][1] = aaaGameAreas[i][1][0] + str_tofloat(str: argv[8]) * 32; //y + height
90 }
91
92 cmdline_free(argc, argv);
93
94 dbg_msg(sys: "map_replace_area", fmt: "from_map='%s'; to_map='%s'; from_area='%fx,%fy'; to_area='%fx,%fy'; area_width='%fpx'; area_height='%fpx'; output_map='%s'",
95 aaMapNames[0], aaMapNames[1], aaaGameAreas[0][0][0], aaaGameAreas[0][1][0], aaaGameAreas[1][0][0], aaaGameAreas[1][1][0],
96 aaaGameAreas[0][0][1] - aaaGameAreas[0][0][0], aaaGameAreas[0][1][1] - aaaGameAreas[0][1][0], aaMapNames[2]);
97
98 std::unique_ptr<IStorage> pStorage = CreateLocalStorage();
99 if(!pStorage)
100 {
101 log_error("map_replace_area", "Error creating local storage");
102 return -1;
103 }
104
105 for(int i = 0; i < 1024; i++)
106 {
107 g_apNewData[i] = g_apNewItem[i] = nullptr;
108 g_aNewDataSize[i] = 0;
109 }
110
111 return ReplaceArea(pStorage.get(), aaMapNames, aaaGameAreas) ? 0 : 1;
112}
113
114bool ReplaceArea(IStorage *pStorage, const char aaMapNames[3][64], const float aaaGameAreas[][2][2])
115{
116 CDataFileReader aInputMaps[2];
117 CDataFileWriter OutputMap;
118
119 if(!OpenMaps(pStorage, aaMapNames, aInputMaps, OutputMap))
120 return false;
121 if(!CompareLayers(aaMapNames, aInputMaps))
122 return false;
123 CompareGroups(aaMapNames, aInputMaps);
124
125 int aLayersStart[2], LayersCount;
126 for(int i = 0; i < 2; i++)
127 aInputMaps[i].GetType(Type: MAPITEMTYPE_LAYER, pStart: &aLayersStart[i], pNum: &LayersCount);
128
129 for(int i = 0; i < LayersCount; i++)
130 {
131 const CMapItemGroup *apLayerGroups[2];
132 CMapItemLayer *apItem[2];
133 for(int j = 0; j < 2; j++)
134 {
135 apLayerGroups[j] = GetLayerGroup(aInputMaps[j], i + 1);
136 apItem[j] = (CMapItemLayer *)aInputMaps[j].GetItem(Index: aLayersStart[j] + i);
137 }
138
139 if(!apLayerGroups[0] || !apLayerGroups[1])
140 continue;
141
142 if(apItem[0]->m_Type == LAYERTYPE_TILES)
143 ReplaceAreaTiles(aInputMaps, aaaGameAreas, apLayerGroups, apItem);
144 else if(apItem[0]->m_Type == LAYERTYPE_QUADS)
145 ReplaceAreaQuads(aInputMaps, aaaGameAreas, apLayerGroups, apItem, aLayersStart[1] + i);
146 }
147
148 SaveOutputMap(aInputMaps[1], OutputMap);
149
150 return true;
151}
152
153bool OpenMaps(IStorage *pStorage, const char aaMapNames[3][64], CDataFileReader aInputMaps[2], CDataFileWriter &OutputMap)
154{
155 for(int i = 0; i < 2; i++)
156 {
157 if(!aInputMaps[i].Open(pStorage, pPath: aaMapNames[i], StorageType: IStorage::TYPE_ABSOLUTE))
158 {
159 dbg_msg(sys: "map_replace_area", fmt: "ERROR: unable to open map '%s'", aaMapNames[i]);
160 return false;
161 }
162 }
163
164 if(!OutputMap.Open(pStorage, pFilename: aaMapNames[2], StorageType: IStorage::TYPE_ABSOLUTE))
165 {
166 dbg_msg(sys: "map_replace_area", fmt: "ERROR: unable to open map '%s'", aaMapNames[2]);
167 return false;
168 }
169
170 return true;
171}
172
173void SaveOutputMap(CDataFileReader &InputMap, CDataFileWriter &OutputMap)
174{
175 for(int i = 0; i < InputMap.NumItems(); i++)
176 {
177 int Id, Type;
178 CUuid Uuid;
179 void *pItem = InputMap.GetItem(Index: i, pType: &Type, pId: &Id, pUuid: &Uuid);
180
181 // Filter ITEMTYPE_EX items, they will be automatically added again.
182 if(Type == ITEMTYPE_EX)
183 {
184 continue;
185 }
186
187 if(g_apNewItem[i])
188 pItem = g_apNewItem[i];
189
190 int Size = InputMap.GetItemSize(Index: i);
191 OutputMap.AddItem(Type, Id, Size, pData: pItem, pUuid: &Uuid);
192 }
193
194 for(int i = 0; i < InputMap.NumData(); i++)
195 {
196 void *pData = g_apNewData[i] ? g_apNewData[i] : InputMap.GetData(Index: i);
197 int Size = g_aNewDataSize[i] ? g_aNewDataSize[i] : InputMap.GetDataSize(Index: i);
198 OutputMap.AddData(Size, pData);
199 }
200
201 OutputMap.Finish();
202}
203
204bool CompareLayers(const char aaMapNames[3][64], CDataFileReader aInputMaps[2])
205{
206 int aStart[2], aNum[2];
207 for(int i = 0; i < 2; i++)
208 aInputMaps[i].GetType(Type: MAPITEMTYPE_LAYER, pStart: &aStart[i], pNum: &aNum[i]);
209
210 if(aNum[0] != aNum[1])
211 {
212 dbg_msg(sys: "map_replace_area", fmt: "ERROR: different layers quantity");
213 for(int i = 0; i < 2; i++)
214 dbg_msg(sys: "map_replace_area", fmt: " \"%s\": %d layers", aaMapNames[i], aNum[i]);
215 return false;
216 }
217
218 for(int i = 0; i < aNum[0]; i++)
219 {
220 CMapItemLayer *apItem[2];
221 for(int j = 0; j < 2; j++)
222 apItem[j] = (CMapItemLayer *)aInputMaps[j].GetItem(Index: aStart[j] + i);
223
224 if(apItem[0]->m_Type != apItem[1]->m_Type)
225 {
226 dbg_msg(sys: "map_replace_area", fmt: "ERROR: different types on layer #%d", i);
227 for(int j = 0; j < 2; j++)
228 dbg_msg(sys: "map_replace_area", fmt: " \"%s\": %s", aaMapNames[j], apItem[j]->m_Type == LAYERTYPE_TILES ? "tiles layer" : "quad layer");
229 return false;
230 }
231 }
232
233 return true;
234}
235
236void CompareGroups(const char aaMapNames[3][64], CDataFileReader aInputMaps[2])
237{
238 int aStart[2], aNum[2];
239 for(int i = 0; i < 2; i++)
240 aInputMaps[i].GetType(Type: MAPITEMTYPE_GROUP, pStart: &aStart[i], pNum: &aNum[i]);
241
242 for(int i = 0; i < std::max(a: aNum[0], b: aNum[1]); i++)
243 {
244 CMapItemGroup *apItem[2];
245 for(int j = 0; j < 2; j++)
246 apItem[j] = (CMapItemGroup *)aInputMaps[j].GetItem(Index: aStart[j] + i);
247
248 bool SameConfig = apItem[0]->m_ParallaxX == apItem[1]->m_ParallaxX && apItem[0]->m_ParallaxY == apItem[1]->m_ParallaxY && apItem[0]->m_OffsetX == apItem[1]->m_OffsetX && apItem[0]->m_OffsetY == apItem[1]->m_OffsetY && apItem[0]->m_UseClipping == apItem[1]->m_UseClipping && apItem[0]->m_ClipX == apItem[1]->m_ClipX && apItem[0]->m_ClipY == apItem[1]->m_ClipY && apItem[0]->m_ClipW == apItem[1]->m_ClipW && apItem[0]->m_ClipH == apItem[1]->m_ClipH;
249
250 if(!SameConfig)
251 dbg_msg(sys: "map_replace_area", fmt: "WARNING: different configuration on layergroup #%d, this might lead to unexpected results", i);
252 }
253}
254
255const CMapItemGroup *GetLayerGroup(CDataFileReader &InputMap, const int LayerNumber)
256{
257 int Start, Num;
258 InputMap.GetType(Type: MAPITEMTYPE_GROUP, pStart: &Start, pNum: &Num);
259
260 for(int i = 0; i < Num; i++)
261 {
262 CMapItemGroup *pItem = (CMapItemGroup *)InputMap.GetItem(Index: Start + i);
263 if(LayerNumber >= pItem->m_StartLayer && LayerNumber <= pItem->m_StartLayer + pItem->m_NumLayers)
264 return pItem;
265 }
266
267 return nullptr;
268}
269
270void ReplaceAreaTiles(CDataFileReader aInputMaps[2], const float aaaGameAreas[][2][2], const CMapItemGroup *apLayerGroups[2], CMapItemLayer *apItem[2])
271{
272 CMapItemLayerTilemap *apTilemap[2];
273 CTile *apTile[2];
274 float aaaVisibleAreas[2][2][2], aaaReplaceableAreas[2][2][2];
275 CMapObject aObs[2];
276
277 for(int i = 0; i < 2; i++)
278 {
279 apTilemap[i] = (CMapItemLayerTilemap *)apItem[i];
280 apTile[i] = (CTile *)aInputMaps[i].GetData(Index: apTilemap[i]->m_Data);
281 aObs[i] = CreateMapObject(apLayerGroups[i], 0, 0, apTilemap[i]->m_Width * 32, apTilemap[i]->m_Height * 32);
282 }
283
284 if(!GetVisibleArea(aaaGameAreas[1], aObs[1], aaaVisibleAreas[1]))
285 return;
286
287 GetReplaceableArea(aaaVisibleAreas[1], aObs[1], aaaReplaceableAreas[1]);
288 RemoveDestinationTiles(apTilemap[1], apTile[1], aaaReplaceableAreas[1]);
289
290 if(GetVisibleArea(aaaGameAreas[0], aObs[0], aaaVisibleAreas[0]) && AdaptVisibleAreas(aaaGameAreas, aObs, aaaVisibleAreas))
291 {
292 for(int i = 0; i < 2; i++)
293 GetReplaceableArea(aaaVisibleAreas[i], aObs[i], aaaReplaceableAreas[i]);
294
295 if(AdaptReplaceableAreas(aaaGameAreas, aaaVisibleAreas, aObs, aaaReplaceableAreas))
296 ReplaceDestinationTiles(apTilemap, apTile, aaaReplaceableAreas);
297 }
298
299 g_apNewData[apTilemap[1]->m_Data] = apTile[1];
300}
301
302void RemoveDestinationTiles(CMapItemLayerTilemap *pTilemap, CTile *pTile, float aaReplaceableArea[2][2])
303{
304 int aaRange[2][2];
305 ConvertToTiles(aaReplaceableArea, aaRange);
306
307 CTile EmptyTile;
308 EmptyTile.m_Index = EmptyTile.m_Flags = EmptyTile.m_Skip = EmptyTile.m_Reserved = 0;
309
310 for(int y = aaRange[1][0]; y < aaRange[1][1]; y++)
311 for(int x = aaRange[0][0]; x < aaRange[0][1]; x++)
312 pTile[x + (y * pTilemap->m_Width)] = EmptyTile;
313}
314
315void ReplaceDestinationTiles(CMapItemLayerTilemap *apTilemap[2], CTile *apTile[2], float aaaReplaceableAreas[2][2][2])
316{
317 int aaaRanges[2][2][2];
318 for(int i = 0; i < 2; i++)
319 ConvertToTiles(aaaReplaceableAreas[i], aaaRanges[i]);
320
321 for(int y0 = aaaRanges[0][1][0], y1 = aaaRanges[1][1][0]; y0 < aaaRanges[0][1][1] && y1 < aaaRanges[1][1][1]; y0++, y1++)
322 for(int x0 = aaaRanges[0][0][0], x1 = aaaRanges[1][0][0]; x0 < aaaRanges[0][0][1] && x1 < aaaRanges[1][0][1]; x0++, x1++)
323 apTile[1][x1 + (y1 * apTilemap[1]->m_Width)] = apTile[0][x0 + (y0 * apTilemap[0]->m_Width)];
324}
325
326bool AdaptVisibleAreas(const float aaaGameAreas[2][2][2], const CMapObject aObs[2], float aaaVisibleAreas[2][2][2])
327{
328 float aDistance[2];
329 GetGameAreaDistance(aaaGameAreas, aObs, aaaVisibleAreas, aDistance);
330
331 for(int i = 0; i < 2; i++)
332 {
333 if(aObs[0].m_aSpeed[i] == 1 || aObs[1].m_aSpeed[i] == 1)
334 continue;
335
336 for(int j = 0; j < 2; j++)
337 aaaVisibleAreas[1][i][j] -= aDistance[i];
338
339 if(!GetLineIntersection(aaaVisibleAreas[0][i], aaaVisibleAreas[1][i], aaaVisibleAreas[0][i]))
340 return false;
341
342 for(int j = 0; j < 2; j++)
343 aaaVisibleAreas[1][i][j] = aaaVisibleAreas[0][i][j] + aDistance[i];
344 }
345
346 return true;
347}
348
349bool AdaptReplaceableAreas(const float aaaGameAreas[2][2][2], const float aaaVisibleAreas[2][2][2], const CMapObject aObs[2], float aaaReplaceableAreas[2][2][2])
350{
351 float aDistance[2], aScreenPos[2];
352 GetGameAreaDistance(aaaGameAreas, aObs, aaaVisibleAreas, aDistance);
353 GetSignificantScreenPos(aObs[0], aaaVisibleAreas[0], aaaReplaceableAreas[0], aScreenPos);
354
355 for(int i = 0; i < 2; i++)
356 {
357 float aDestLine[2], aSourceLine[2], aVisibleLine[2];
358
359 aDestLine[0] = aObs[1].m_aaBaseArea[i][0] + (aScreenPos[i] + aDistance[i]) * aObs[1].m_aSpeed[i];
360 aDestLine[1] = aDestLine[0] + (aObs[1].m_aaBaseArea[i][1] - aObs[1].m_aaBaseArea[i][0]);
361
362 if(!GetLineIntersection(aDestLine, aaaVisibleAreas[1][i], aVisibleLine))
363 return false;
364
365 aSourceLine[0] = aaaVisibleAreas[0][i][0] + aDistance[i] - aaaReplaceableAreas[0][i][0];
366 aSourceLine[1] = aaaVisibleAreas[0][i][1] + aDistance[i] + aaaReplaceableAreas[0][i][1] - aaaReplaceableAreas[0][i][0];
367
368 if(!GetLineIntersection(aSourceLine, aVisibleLine, aVisibleLine))
369 return false;
370
371 aaaReplaceableAreas[0][i][0] = aVisibleLine[0] - aSourceLine[0];
372 aaaReplaceableAreas[1][i][0] = aVisibleLine[0] - aDestLine[0];
373 }
374
375 return true;
376}
377
378void ReplaceAreaQuads(CDataFileReader aInputMaps[2], const float aaaGameAreas[][2][2], const CMapItemGroup *apLayerGroups[2], CMapItemLayer *apItem[2], const int ItemNumber)
379{
380 CMapItemLayerQuads *apQuadLayer[2];
381 for(int i = 0; i < 2; i++)
382 apQuadLayer[i] = (CMapItemLayerQuads *)apItem[i];
383
384 CQuad *apQuads[3];
385 for(int i = 0; i < 2; i++)
386 apQuads[i] = (CQuad *)aInputMaps[i].GetDataSwapped(Index: apQuadLayer[i]->m_Data);
387
388 apQuads[2] = new CQuad[apQuadLayer[0]->m_NumQuads + apQuadLayer[1]->m_NumQuads];
389 int QuadsCounter = 0;
390
391 bool DataChanged = RemoveDestinationQuads(aaaGameAreas[1], apQuads[1], apQuadLayer[1]->m_NumQuads, apLayerGroups[1], apQuads[2], QuadsCounter);
392 DataChanged |= InsertDestinationQuads(aaaGameAreas, apQuads[0], apQuadLayer[0]->m_NumQuads, apLayerGroups, apQuads[2], QuadsCounter);
393
394 if(DataChanged)
395 {
396 g_apNewData[apQuadLayer[1]->m_Data] = apQuads[2];
397 g_aNewDataSize[apQuadLayer[1]->m_Data] = ((int)sizeof(CQuad)) * QuadsCounter;
398 apQuadLayer[1]->m_NumQuads = QuadsCounter;
399 g_apNewItem[ItemNumber] = apItem[1];
400 }
401 else
402 delete[] apQuads[2];
403}
404
405bool RemoveDestinationQuads(const float aaGameArea[2][2], const CQuad *pQuads, const int NumQuads, const CMapItemGroup *pLayerGroup, CQuad *pDestQuads, int &QuadsCounter)
406{
407 bool DataChanged = false;
408
409 for(int i = 0; i < NumQuads; i++)
410 {
411 CMapObject Ob = CreateMapObject(pLayerGroup, fx2f(v: pQuads[i].m_aPoints[4].x), fx2f(v: pQuads[i].m_aPoints[4].y), 0, 0);
412
413 if(GetVisibleArea(aaGameArea, Ob))
414 {
415 DataChanged = true;
416 continue;
417 }
418
419 pDestQuads[QuadsCounter] = pQuads[i];
420 QuadsCounter++;
421 }
422
423 return DataChanged;
424}
425
426bool InsertDestinationQuads(const float aaaGameAreas[2][2][2], const CQuad *pQuads, const int NumQuads, const CMapItemGroup *apLayerGroups[2], CQuad *pDestQuads, int &QuadsCounter)
427{
428 bool DataChanged = false;
429
430 for(int i = 0; i < NumQuads; i++)
431 {
432 CMapObject aObs[2];
433 aObs[0] = CreateMapObject(apLayerGroups[0], fx2f(v: pQuads[i].m_aPoints[4].x), fx2f(v: pQuads[i].m_aPoints[4].y), 0, 0);
434 float aaVisibleArea[2][2];
435
436 if(GetVisibleArea(aaaGameAreas[0], aObs[0], aaVisibleArea))
437 {
438 float aQuadPos[2];
439 aObs[1] = CreateMapObject(apLayerGroups[1], 0, 0, 0, 0);
440
441 if(!AdaptVisiblePoint(aaaGameAreas, aaVisibleArea, aObs, aQuadPos))
442 continue;
443
444 pDestQuads[QuadsCounter] = pQuads[i];
445 for(auto &Point : pDestQuads[QuadsCounter].m_aPoints)
446 {
447 Point.x += f2fx(v: aQuadPos[0]) - pDestQuads[QuadsCounter].m_aPoints[4].x;
448 Point.y += f2fx(v: aQuadPos[1]) - pDestQuads[QuadsCounter].m_aPoints[4].y;
449 }
450
451 QuadsCounter++;
452 DataChanged = true;
453 }
454 }
455
456 return DataChanged;
457}
458
459bool AdaptVisiblePoint(const float aaaGameAreas[2][2][2], const float aaVisibleArea[2][2], const CMapObject aObs[2], float aPos[2])
460{
461 float aDistance[2], aScreenPos[2];
462 GetGameAreaDistance(aaaGameAreas, aObs, aaVisibleArea, aDistance);
463 GetSignificantScreenPos(aObs[0], aaVisibleArea, nullptr, aScreenPos);
464
465 for(int i = 0; i < 2; i++)
466 aPos[i] = aaVisibleArea[i][0] + aDistance[i] + aObs[1].m_aLayerOffset[i] - (aScreenPos[i] + aDistance[i]) * aObs[1].m_aSpeed[i];
467
468 CMapObject FinalOb = aObs[1];
469 for(int i = 0; i < 2; i++)
470 FinalOb.m_aaBaseArea[i][0] = FinalOb.m_aaBaseArea[i][1] += aPos[i];
471 SetExtendedArea(FinalOb);
472
473 return GetVisibleArea(aaaGameAreas[1], FinalOb);
474}
475
476CMapObject CreateMapObject(const CMapItemGroup *pLayerGroup, const int PosX, const int PosY, const int Width, const int Height)
477{
478 CMapObject Ob;
479
480 Ob.m_aaBaseArea[0][0] = PosX - pLayerGroup->m_OffsetX;
481 Ob.m_aaBaseArea[1][0] = PosY - pLayerGroup->m_OffsetY;
482 Ob.m_aaBaseArea[0][1] = Ob.m_aaBaseArea[0][0] + Width;
483 Ob.m_aaBaseArea[1][1] = Ob.m_aaBaseArea[1][0] + Height;
484 Ob.m_aLayerOffset[0] = pLayerGroup->m_OffsetX;
485 Ob.m_aLayerOffset[1] = pLayerGroup->m_OffsetY;
486 Ob.m_UseClipping = pLayerGroup->m_UseClipping;
487 Ob.m_aaClipArea[0][0] = pLayerGroup->m_ClipX;
488 Ob.m_aaClipArea[1][0] = pLayerGroup->m_ClipY;
489 Ob.m_aaClipArea[0][1] = pLayerGroup->m_ClipX + pLayerGroup->m_ClipW;
490 Ob.m_aaClipArea[1][1] = pLayerGroup->m_ClipY + pLayerGroup->m_ClipH;
491 Ob.m_aSpeed[0] = 1 - (pLayerGroup->m_ParallaxX / 100.0f);
492 Ob.m_aSpeed[1] = 1 - (pLayerGroup->m_ParallaxY / 100.0f);
493
494 for(int i = 0; i < 2; i++)
495 {
496 Ob.m_aaScreenOffset[i][0] = -CMapObject::ms_aStandardScreen[i];
497 Ob.m_aaScreenOffset[i][1] = CMapObject::ms_aStandardScreen[i];
498 if(Ob.m_aSpeed[i] < 0)
499 std::swap(a&: Ob.m_aaScreenOffset[i][0], b&: Ob.m_aaScreenOffset[i][1]);
500 }
501
502 SetExtendedArea(Ob);
503 return Ob;
504}
505
506void SetExtendedArea(CMapObject &Ob)
507{
508 SetInexistent((float *)Ob.m_aaExtendedArea, 4);
509
510 for(int i = 0; i < 2; i++)
511 {
512 if(Ob.m_aSpeed[i] == 1)
513 {
514 float aInspectedArea[2];
515 if(GetLineIntersection(Ob.m_aaBaseArea[i], Ob.m_aaScreenOffset[i], aInspectedArea))
516 mem_copy(dest: Ob.m_aaExtendedArea[i], source: aInspectedArea, size: sizeof(float[2]));
517 continue;
518 }
519
520 for(int j = 0; j < 2; j++)
521 Ob.m_aaExtendedArea[i][j] = (Ob.m_aaBaseArea[i][j] + Ob.m_aaScreenOffset[i][j] * Ob.m_aSpeed[i]) / (1 - Ob.m_aSpeed[i]);
522
523 if(Ob.m_aaExtendedArea[i][0] > Ob.m_aaExtendedArea[i][1])
524 std::swap(a&: Ob.m_aaExtendedArea[i][0], b&: Ob.m_aaExtendedArea[i][1]);
525 }
526}
527
528bool GetVisibleArea(const float aaGameArea[2][2], const CMapObject &Ob, float aaVisibleArea[2][2])
529{
530 if(IsInexistent((float *)Ob.m_aaExtendedArea, 4))
531 return false;
532
533 if(aaVisibleArea)
534 SetInexistent((float *)aaVisibleArea, 4);
535
536 float aaInspectedArea[2][2];
537 mem_copy(dest: aaInspectedArea, source: aaGameArea, size: sizeof(float[2][2]));
538
539 for(int i = 0; i < 2; i++)
540 {
541 if(Ob.m_aSpeed[i] == 1)
542 {
543 mem_copy(dest: aaInspectedArea[i], source: Ob.m_aaExtendedArea[i], size: sizeof(float[2]));
544 continue;
545 }
546
547 if(Ob.m_UseClipping && !GetLineIntersection(aaInspectedArea[i], Ob.m_aaClipArea[i], aaInspectedArea[i]))
548 return false;
549
550 if(!GetLineIntersection(aaInspectedArea[i], Ob.m_aaExtendedArea[i], aaInspectedArea[i]))
551 return false;
552 }
553
554 if(aaVisibleArea)
555 mem_copy(dest: aaVisibleArea, source: aaInspectedArea, size: sizeof(float[2][2]));
556
557 return true;
558}
559
560bool GetReplaceableArea(const float aaVisibleArea[2][2], const CMapObject &Ob, float aaReplaceableArea[2][2])
561{
562 SetInexistent((float *)aaReplaceableArea, 4);
563 if(IsInexistent((float *)aaVisibleArea, 4))
564 return false;
565
566 for(int i = 0; i < 2; i++)
567 {
568 if(Ob.m_aSpeed[i] == 1)
569 {
570 aaReplaceableArea[i][0] = aaVisibleArea[i][0] - Ob.m_aaBaseArea[i][0];
571 aaReplaceableArea[i][1] = aaVisibleArea[i][1] - Ob.m_aaBaseArea[i][0];
572 continue;
573 }
574
575 for(int j = 0; j < 2; j++)
576 {
577 float aVisibleLine[2], aReplaceableLine[2];
578 int k = Ob.m_aSpeed[i] > 1 ? !j : j;
579
580 aVisibleLine[0] = Ob.m_aaBaseArea[i][0] + (aaVisibleArea[i][j] - Ob.m_aaScreenOffset[i][k]) * Ob.m_aSpeed[i];
581 aVisibleLine[1] = aVisibleLine[0] + Ob.m_aaBaseArea[i][1] - Ob.m_aaBaseArea[i][0];
582
583 if(GetLineIntersection(aaVisibleArea[i], aVisibleLine, aReplaceableLine))
584 aaReplaceableArea[i][k] = aReplaceableLine[j] - aVisibleLine[0];
585 else
586 aaReplaceableArea[i][k] = k * (Ob.m_aaBaseArea[i][1] - Ob.m_aaBaseArea[i][0]);
587 }
588 }
589
590 return true;
591}
592
593void GetGameAreaDistance(const float aaaGameAreas[2][2][2], const CMapObject aObs[2], const float aaaVisibleAreas[2][2][2], float aDistance[2])
594{
595 for(int i = 0; i < 2; i++)
596 {
597 if(aObs[0].m_aSpeed[i] == 1 && aObs[1].m_aSpeed[i] == 1)
598 aDistance[i] = 0;
599 else if(aObs[0].m_aSpeed[i] == 1 && aObs[1].m_aSpeed[i] != 1)
600 aDistance[i] = aaaGameAreas[1][i][0] - aaaVisibleAreas[0][i][0];
601 else if(aObs[0].m_aSpeed[i] != 1 && aObs[1].m_aSpeed[i] == 1)
602 aDistance[i] = aaaVisibleAreas[1][i][0] - aaaGameAreas[0][i][0];
603 else
604 aDistance[i] = aaaGameAreas[1][i][0] - aaaGameAreas[0][i][0];
605 }
606}
607
608void GetGameAreaDistance(const float aaaGameAreas[2][2][2], const CMapObject aObs[2], const float aaVisibleArea[2][2], float aDistance[2])
609{
610 float aaaVisibleAreas[2][2][2];
611 mem_copy(dest: aaaVisibleAreas[0], source: aaVisibleArea[0], size: sizeof(float[2][2]));
612 mem_copy(dest: aaaVisibleAreas[1], source: aaVisibleArea[0], size: sizeof(float[2][2]));
613 GetGameAreaDistance(aaaGameAreas, aObs, aaaVisibleAreas, aDistance);
614}
615
616void GetSignificantScreenPos(const CMapObject &Ob, const float aaVisibleArea[2][2], const float aaReplaceableArea[2][2], float aScreen[2])
617{
618 for(int i = 0; i < 2; i++)
619 {
620 if(!Ob.m_aSpeed[i])
621 {
622 aScreen[i] = aaVisibleArea[i][0] + Ob.m_aaScreenOffset[i][1];
623 continue;
624 }
625
626 float BaseOffset = aaReplaceableArea ? aaReplaceableArea[i][0] : 0;
627 aScreen[i] = (aaVisibleArea[i][0] - Ob.m_aaBaseArea[i][0] - BaseOffset) / Ob.m_aSpeed[i];
628 }
629}
630
631void ConvertToTiles(const float aaArea[2][2], int aaTiles[2][2])
632{
633 for(int i = 0; i < 2; i++)
634 {
635 aaTiles[i][0] = std::floor(x: (std::floor(x: aaArea[i][0] * 100.0f) / 100.0f) / 32.0f);
636 aaTiles[i][1] = std::ceil(x: (std::floor(x: aaArea[i][1] * 100.0f) / 100.0f) / 32.0f);
637 }
638}
639
640bool GetLineIntersection(const float aLine1[2], const float aLine2[2], float aIntersection[2])
641{
642 float aBorders[2] = {
643 std::max(a: aLine1[0], b: aLine2[0]),
644 std::min(a: aLine1[1], b: aLine2[1])};
645
646 if(aIntersection)
647 SetInexistent(aIntersection, 2);
648
649 if(aBorders[0] - aBorders[1] > 0.01f)
650 return false;
651
652 if(aIntersection)
653 mem_copy(dest: aIntersection, source: aBorders, size: sizeof(float[2]));
654
655 return true;
656}
657
658void SetInexistent(float *pArray, const int Count)
659{
660 for(int i = 0; i < Count; i++)
661 pArray[i] = std::numeric_limits<float>::max();
662}
663
664bool IsInexistent(const float *pArray, const int Count)
665{
666 for(int i = 0; i < Count; i++)
667 if(pArray[i] == std::numeric_limits<float>::max())
668 return true;
669 return false;
670}
671