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