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