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