| 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) |
| 10 | static void *g_apNewData[1024]; |
| 11 | static void *g_apNewItem[1024]; |
| 12 | static int g_aNewDataSize[1024]; |
| 13 | |
| 14 | class CMapObject // quad pivot or tile layer |
| 15 | { |
| 16 | public: |
| 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 | |
| 28 | static bool ReplaceArea(IStorage *, const char[3][64], const float[][2][2]); |
| 29 | static bool OpenMaps(IStorage *, const char[3][64], CDataFileReader[2], CDataFileWriter &); |
| 30 | static void SaveOutputMap(CDataFileReader &, CDataFileWriter &); |
| 31 | static bool CompareLayers(const char[3][64], CDataFileReader[2]); |
| 32 | static void CompareGroups(const char[3][64], CDataFileReader[2]); |
| 33 | static const CMapItemGroup *GetLayerGroup(CDataFileReader &, int); |
| 34 | |
| 35 | static void ReplaceAreaTiles(CDataFileReader[2], const float[][2][2], const CMapItemGroup *[2], CMapItemLayer *[2]); |
| 36 | static void RemoveDestinationTiles(CMapItemLayerTilemap *, CTile *, float[2][2]); |
| 37 | static void ReplaceDestinationTiles(CMapItemLayerTilemap *[2], CTile *[2], float[2][2][2]); |
| 38 | static bool AdaptVisibleAreas(const float[2][2][2], const CMapObject[2], float[2][2][2]); |
| 39 | static bool AdaptReplaceableAreas(const float[2][2][2], const float[2][2][2], const CMapObject[2], float[2][2][2]); |
| 40 | |
| 41 | static void ReplaceAreaQuads(CDataFileReader[2], const float[][2][2], const CMapItemGroup *[2], CMapItemLayer *[2], int); |
| 42 | static bool RemoveDestinationQuads(const float[2][2], const CQuad *, int, const CMapItemGroup *, CQuad *, int &); |
| 43 | static bool InsertDestinationQuads(const float[2][2][2], const CQuad *, int, const CMapItemGroup *[2], CQuad *, int &); |
| 44 | static bool AdaptVisiblePoint(const float[2][2][2], const float[2][2], const CMapObject[2], float[2]); |
| 45 | |
| 46 | static CMapObject CreateMapObject(const CMapItemGroup *, int, int, int, int); |
| 47 | static void SetExtendedArea(CMapObject &); |
| 48 | static bool GetVisibleArea(const float[2][2], const CMapObject &, float[2][2] = nullptr); |
| 49 | static bool GetReplaceableArea(const float[2][2], const CMapObject &, float[2][2]); |
| 50 | |
| 51 | static void GetGameAreaDistance(const float[2][2][2], const CMapObject[2], const float[2][2][2], float[2]); |
| 52 | static void GetGameAreaDistance(const float[2][2][2], const CMapObject[2], const float[2][2], float[2]); |
| 53 | static void GetSignificantScreenPos(const CMapObject &, const float[2][2], const float[2][2], float[2]); |
| 54 | static void ConvertToTiles(const float[2][2], int[2][2]); |
| 55 | |
| 56 | static bool GetLineIntersection(const float[2], const float[2], float[2]); |
| 57 | static void SetInexistent(float *, int); |
| 58 | static bool IsInexistent(const float *, int); |
| 59 | |
| 60 | int 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 | |
| 111 | bool 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 [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 | |
| 150 | bool 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 | |
| 170 | void 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 | |
| 201 | bool 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 | |
| 233 | void 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 | |
| 252 | const 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 | |
| 267 | void 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 | |
| 299 | void 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 | |
| 312 | void 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 | |
| 323 | bool 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 | |
| 346 | bool 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 | |
| 375 | void 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 | |
| 402 | bool 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 | |
| 423 | bool 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 | |
| 456 | bool 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 | |
| 473 | CMapObject 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 | |
| 503 | void 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 | |
| 525 | bool 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 | |
| 557 | bool 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 | |
| 590 | void 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 | |
| 605 | void 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 | |
| 613 | void 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 | |
| 628 | void 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 | |
| 637 | bool 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 | |
| 655 | void 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 | |
| 661 | bool 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 | |