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) |
8 | void *g_apNewData[1024]; |
9 | void *g_apNewItem[1024]; |
10 | int g_aNewDataSize[1024]; |
11 | |
12 | struct 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 | |
25 | bool ReplaceArea(IStorage *, const char[3][64], const float[][2][2]); |
26 | bool OpenMaps(IStorage *, const char[3][64], CDataFileReader[2], CDataFileWriter &); |
27 | void SaveOutputMap(CDataFileReader &, CDataFileWriter &); |
28 | bool CompareLayers(const char[3][64], CDataFileReader[2]); |
29 | void CompareGroups(const char[3][64], CDataFileReader[2]); |
30 | const CMapItemGroup *GetLayerGroup(CDataFileReader &, int); |
31 | |
32 | void ReplaceAreaTiles(CDataFileReader[2], const float[][2][2], const CMapItemGroup *[2], CMapItemLayer *[2]); |
33 | void RemoveDestinationTiles(CMapItemLayerTilemap *, CTile *, float[2][2]); |
34 | void ReplaceDestinationTiles(CMapItemLayerTilemap *[2], CTile *[2], float[2][2][2]); |
35 | bool AdaptVisibleAreas(const float[2][2][2], const MapObject[2], float[2][2][2]); |
36 | bool AdaptReplaceableAreas(const float[2][2][2], const float[2][2][2], const MapObject[2], float[2][2][2]); |
37 | |
38 | void ReplaceAreaQuads(CDataFileReader[2], const float[][2][2], const CMapItemGroup *[2], CMapItemLayer *[2], int); |
39 | bool RemoveDestinationQuads(const float[2][2], const CQuad *, int, const CMapItemGroup *, CQuad *, int &); |
40 | bool InsertDestinationQuads(const float[2][2][2], const CQuad *, int, const CMapItemGroup *[2], CQuad *, int &); |
41 | bool AdaptVisiblePoint(const float[2][2][2], const float[2][2], const MapObject[2], float[2]); |
42 | |
43 | MapObject CreateMapObject(const CMapItemGroup *, int, int, int, int); |
44 | void SetExtendedArea(MapObject &); |
45 | bool GetVisibleArea(const float[2][2], const MapObject &, float[2][2] = 0x0); |
46 | bool GetReplaceableArea(const float[2][2], const MapObject &, float[2][2]); |
47 | |
48 | void GetGameAreaDistance(const float[2][2][2], const MapObject[2], const float[2][2][2], float[2]); |
49 | void GetGameAreaDistance(const float[2][2][2], const MapObject[2], const float[2][2], float[2]); |
50 | void GetSignificantScreenPos(const MapObject &, const float[2][2], const float[2][2], float[2]); |
51 | void ConvertToTiles(const float[2][2], int[2][2]); |
52 | |
53 | bool GetLineIntersection(const float[2], const float[2], float[2] = 0x0); |
54 | bool GetLineIntersection(const float[2], float); |
55 | void SetInexistent(float *, int); |
56 | bool IsInexistent(const float *, int); |
57 | bool IsInexistent(float); |
58 | |
59 | int 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 | |
104 | bool 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 [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 | |
143 | bool 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 | |
163 | void 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 | |
194 | bool 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 | |
226 | void 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 | |
245 | const 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 | |
260 | void 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 | |
292 | void 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 | |
305 | void 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 | |
316 | bool 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 | |
339 | bool 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 | |
368 | void 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 | |
395 | bool 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 | |
416 | bool 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 | |
449 | bool 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 | |
466 | MapObject 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 | |
496 | void 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 | |
518 | bool 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 | |
550 | bool 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 | |
583 | void 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 | |
598 | void 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 | |
606 | void 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 | |
621 | void 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 | |
630 | bool 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 | |
648 | bool GetLineIntersection(const float aLine[2], const float Point) |
649 | { |
650 | return aLine[0] - Point <= 0.01f && aLine[1] - Point >= 0.01f; |
651 | } |
652 | |
653 | void 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 | |
659 | bool 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 | |
667 | bool IsInexistent(const float Value) |
668 | { |
669 | return Value == std::numeric_limits<float>::max(); |
670 | } |
671 | |