// // AniMapUtils.cpp // SteveMaggieCpp // // Created by Katarzyna Kalinowska-Górska on 05.06.2017. // // #include #include "AniMapUtils.h" #include "AniMathUtils.h" #include "AniScalingUtils.h" #include "AniJSONParseUtils.h" /// TODO: make it not a singleton // WARNING!!! CACHED GRAPH. BAD DESIGN. REMEMBER TO CLEAR/RECALCULATE THE MAP GRAPH WHEN THE MAP CHANGES. // TODO PARAM allowStartTileInaccessible is just a workaround to potantial steve stuck bugs, when steve ends up on a tile that's inaccessible (happens sometimes when doing a U-turn on tile 29,16 - still haven't discovered the source of the bug. // this method will find a path either to the tile, or to an accessible tile closest to that tile (within the row/col range set up by setMaxTileRangeOnPath method) std::vector AniMapUtils::calculatePathOnTmxMap(AniMapUtils::TileData fromTile, AniMapUtils::TileData toTile, cocos2d::TMXTiledMap* tmxMap, cocos2d::TMXLayer* tmxTileLayer, bool allowStartTileInaccessible, bool useLastMapGraph) { std::vector path; if(allowStartTileInaccessible || AniMapUtils::isTileAccessible(tmxMap, tmxTileLayer, fromTile.col, fromTile.row)){ if(!useLastMapGraph || _cachedMapGraph.size() == 0){ _tempImpassableTiles.clear(); recalculateMapGraph(tmxMap, tmxTileLayer); } path = AniMapUtils::calculatePath(fromTile, toTile, _cachedMapGraph); } return path; } void AniMapUtils::clearMap(){ _tempImpassableTiles.clear(); for(auto col : _cachedMapGraph){ for(auto value : col){ delete value; } } _cachedMapGraph.clear(); } // TODO HANDLE CASE WHEN THERE IS NO PATH!!!!! std::vector AniMapUtils::calculatePathToClosestFromTileSet(AniMapUtils::TileData fromTile, std::vector toTiles, cocos2d::TMXTiledMap* tmxMap, cocos2d::TMXLayer* tmxTileLayer, bool allowStartTileInaccessible, bool useLastMapGraph){ std::vector shortestPath; if(toTiles.size() > 0){ if(std::find(toTiles.begin(), toTiles.end(), fromTile) != toTiles.end()){ // shortestPath.push_back(fromTile); return shortestPath; // return an empty path, we're already there } else { for(int i = 0; i < toTiles.size(); ++i){ auto newShortestPath = calculatePathOnTmxMap(fromTile, toTiles[i], tmxMap, tmxTileLayer, allowStartTileInaccessible, useLastMapGraph); if(shortestPath.size() == 0 || (newShortestPath.size() < shortestPath.size() && newShortestPath.size() != 0)){ //for now, path=0 means probably no access shortestPath = newShortestPath; } //// //TODO. add a method that calculated the path, but interrupts if the path is longer than given. // //TODO . someteimes there will be no path... } } } // cocos2d::log("shortest path from tile %d %d to tile %d %d: %d\n", fromTile.col, fromTile.row, toTiles[0].col, toTiles[0].row, shortestPath.size() ); return shortestPath; } bool AniMapUtils::recalculateMapGraphAsync(cocos2d::TMXTiledMap* tmxMap, cocos2d::TMXLayer* tmxTileLayer, std::function callback){ if(!m_threadIsRunning){ m_threadIsRunning = true; m_threadTempMap = tmxMap; m_threadTempLayer = tmxTileLayer; m_threadTempCallback = callback; std::thread bgThread(&AniMapUtils::asyncGraphRecalcThread,this); bgThread.detach(); return true; } return false; } // only one should be running at a time void AniMapUtils::asyncGraphRecalcThread(){ recalculateMapGraph(m_threadTempMap, m_threadTempLayer); cocos2d::Director::getInstance()->getScheduler()->performFunctionInCocosThread([&,this]{ m_threadIsRunning = false; m_threadTempCallback(); }); } void AniMapUtils::recalculateMapGraph(cocos2d::TMXTiledMap* tmxMap, cocos2d::TMXLayer* tmxTileLayer) { auto mapCols = static_cast(tmxMap->getMapSize().width); auto mapRows = static_cast(tmxMap->getMapSize().height); for(auto col : _cachedMapGraph){ for(auto value : col){ delete value; } } _cachedMapGraph.clear(); _cachedMapGraph.reserve(mapCols); for(int j = 0; j < mapCols; ++j){ std::vector col; col.reserve(mapRows); _cachedMapGraph.push_back(col); for(int i = 0; i < mapRows; ++i){ std::vector neighbours; if(AniMapUtils::isTileAccessible(tmxMap, tmxTileLayer, j, i)){ neighbours = AniMapUtils::listTileNeighbours(TileData(j,i), tmxMap, tmxTileLayer); } PathTileData* tileData = new PathTileData(TileData(j,i), neighbours); _cachedMapGraph[j].push_back(tileData); } } } void AniMapUtils::recalculateMapGraph(cocos2d::TMXTiledMap* tmxMap, cocos2d::TMXLayer* tmxTileLayer, std::vector justForTiles) { if(_cachedMapGraph.size() == 0){ recalculateMapGraph(tmxMap, tmxTileLayer); } else { std::vector tilesToUpdate; for(auto it = justForTiles.begin(); it != justForTiles.end(); ++it){ // tilesToUpdate.push_back(*it); auto tileNeighbours = listTileNeighbours(*it, tmxMap, tmxTileLayer, true); //all neighbours, not paying attention whether accessible tileNeighbours.push_back(*it); for(auto it2 = tileNeighbours.begin(); it2 != tileNeighbours.end(); ++it2){ if(std::find(tilesToUpdate.begin(), tilesToUpdate.end(), *it2) == tilesToUpdate.end()){ tilesToUpdate.push_back(*it2); } // tilesToUpdate.insert(tilesToUpdate.end(), tileNeighbours.begin(), tileNeighbours.end()); } } // auto mapRows = static_cast(tmxMap->getMapSize().height); for(auto it = tilesToUpdate.begin(); it != tilesToUpdate.end(); ++it){ delete _cachedMapGraph[it->col][it->row]; std::vector neighbours; if(AniMapUtils::isTileAccessible(tmxMap, tmxTileLayer, it->col, it->row)){ neighbours = AniMapUtils::listTileNeighbours(TileData(*it), tmxMap, tmxTileLayer); } _cachedMapGraph[it->col][it->row] = new PathTileData(*it, neighbours); } } } // find a tile that is accessible and is closest to the toTile //TODO: this function can be extended by checking all accessible tiles with the same distance from the toTile, and selecting this tile that is closest to the fromTile AniMapUtils::TileData AniMapUtils::findClosestAccessibleTile(AniMapUtils::TileData toTile, cocos2d::TMXTiledMap* tmxMap, cocos2d::TMXLayer* tmxLayer) { std::vector checkedTiles; std::deque tilesToCheck = {toTile}; TileData accessibleTile; while(tilesToCheck.size() > 0) { auto checkedTile = tilesToCheck[0]; tilesToCheck.pop_front(); if(this->isTileAccessible(tmxMap, tmxLayer, checkedTile.col, checkedTile.row)){ // we also need to check whether a path exists to the tile from our tile // if(calculatePath(startTile, toTile, _cachedMapGraph).size() > 0){ accessibleTile = checkedTile; break; // } } else { auto tileNeighbours = this->listTileNeighbours(checkedTile, tmxMap, tmxLayer, true); for(int i = 0; i < tileNeighbours.size(); ++i){ bool addTile = true; std::vector tilesNotToAdd; tilesNotToAdd.insert(tilesNotToAdd.end(), tilesToCheck.begin(), tilesToCheck.end()); tilesNotToAdd.insert(tilesNotToAdd.end(), checkedTiles.begin(), checkedTiles.end()); for(int j = 0; j < tilesNotToAdd.size(); ++j){ if(tilesNotToAdd[j] == tileNeighbours[i]){ addTile = false; break; } } if(addTile == true){ tilesToCheck.push_back(tileNeighbours[i]); } } checkedTiles.push_back(checkedTile); } } return accessibleTile; } std::vector AniMapUtils::listTileNeighbours(AniMapUtils::TileData tile, cocos2d::TMXTiledMap* tmxMap, cocos2d::TMXLayer* tmxLayer, bool ignoreAllChecks) { std::vector tileNeighbours; auto mapCols = tmxMap->getMapSize().width; auto mapRows = tmxMap->getMapSize().height; std::vector ksTocheck = {tile.col}; if(tile.col > 0){ ksTocheck.push_back(tile.col - 1); } if(tile.col < mapCols - 1){ ksTocheck.push_back(tile.col + 1); } std::vector rsToCheck = {tile.row}; if(tile.row > 0){ rsToCheck.push_back(tile.row - 1); } if(tile.row < mapRows - 1){ rsToCheck.push_back(tile.row + 1); } for(int k = 0; k < ksTocheck.size(); ++k){ for(int r = 0; r < rsToCheck.size(); ++r){ auto tileDatatoCheck = TileData(ksTocheck[k], rsToCheck[r]); if(!(ksTocheck[k] == tile.col && rsToCheck[r] == tile.row) && areTilesNeighbours(tile, tileDatatoCheck, tmxMap, tmxLayer, 1, ignoreAllChecks)){ //(this->isTileAccessible(tmxMap, tmxLayer, ksTocheck[k],rsToCheck[r]))){ tileNeighbours.push_back(tileDatatoCheck); } } } return tileNeighbours; } //std::vector AniMapUtils::listTileColNeighbours(TileData tile, cocos2d::TMXTiledMap* tmxMap, cocos2d::TMXLayer* tmxLayer, int dir, int range){ // //} //std::vector AniMapUtils::listTileRowNeighbours(TileData tile, cocos2d::TMXTiledMap* tmxMap, cocos2d::TMXLayer* tmxLayer, int dir, int range){ // //} bool AniMapUtils::isTileAccessible(cocos2d::TMXTiledMap* tmxMap, cocos2d::TMXLayer* tmxLayer, TileData p_tileData){ return isTileAccessible(tmxMap, tmxLayer, p_tileData.col, p_tileData.row); } bool AniMapUtils::isTileAccessible(cocos2d::TMXTiledMap* tmxMap, cocos2d::TMXLayer* tmxLayer, int col, int row) { if(col < 0 || row < 0 || col > tmxMap->getMapSize().width-1 || row > tmxMap->getMapSize().height-1){ return false; } // return true;//TEMP if(_tempImpassableTiles.find(TileData(col, row)) != _tempImpassableTiles.end()){ return false; } bool isAccessible = true; auto tileGID = tmxLayer->getTileGIDAt(cocos2d::Point(col, row)); auto tileProperties = tmxMap->getPropertiesForGID(tileGID); if(tileProperties.getType() == cocos2d::Value::Type::MAP){ auto map = tileProperties.asValueMap(); if(map.find("obstacle") != map.end() && map["obstacle"].asInt() == 1){ isAccessible = false; } } return isAccessible; } int AniMapUtils::getTileLevel(cocos2d::TMXTiledMap* tmxMap, cocos2d::TMXLayer* tmxLayer, TileData p_tileData){ int level = 0; auto tileProperties = tmxMap->getPropertiesForGID(tmxLayer->getTileGIDAt(p_tileData.convertToVec2())); if(tileProperties.getType() == cocos2d::Value::Type::MAP){ auto map = tileProperties.asValueMap(); if(map.find("level") != map.end()){ level = map["level"].asInt(); } } return level; } bool AniMapUtils::areTilesNeighbours(TileData tile1, TileData tile2, cocos2d::TMXTiledMap* tmxMap, cocos2d::TMXLayer* tmxLayer, int maxDeltaLevel, bool ignoreAllChecks) { if(!ignoreAllChecks){ if(!isTileAccessible(tmxMap, tmxLayer, tile1) || !isTileAccessible(tmxMap, tmxLayer, tile2)){ return false; } } if(abs(tile1.col - tile2.col) > 1 || abs(tile1.row - tile2.row) > 1){ return false; } return ignoreAllChecks || abs(getTileLevel(tmxMap, tmxLayer, tile1) - getTileLevel(tmxMap, tmxLayer, tile2)) <= maxDeltaLevel; } std::vector AniMapUtils::closestTiles(TileData tile, cocos2d::TMXTiledMap* tmxMap, cocos2d::TMXLayer* tmxLayer, int dirX, int dirY, int range, int maxDeltaLevel){ auto farthestCol = tile.col + dirX*range; farthestCol = MAX(0, farthestCol); farthestCol = MIN(tmxMap->getMapSize().width -1, farthestCol); auto farthestRow = tile.row - dirY*range; //minus, because cols are in the opposite direction than y farthestRow = MAX(0, farthestRow); farthestRow = MIN(tmxMap->getMapSize().height -1, farthestRow); std::vector targetNeighbours; int c = tile.col; do { c += dirX; int r = tile.row; do { r -= dirY; // for(int r = tile.row + dirY; r != farthestRow - dirY; r -= dirY){ auto candidateTile = TileData(c, r); if((candidateTile != tile) && isTileAccessible(tmxMap, tmxLayer, c, r) && abs(getTileLevel(tmxMap, tmxLayer, candidateTile)-getTileLevel(tmxMap, tmxLayer, tile)) <= maxDeltaLevel){ // disable full-range diagonal tiles; they are too far if((candidateTile - tile).convertToVec2().length() <= (2*range-1)){ targetNeighbours.push_back(candidateTile); } } } while(r != farthestRow - dirY); } while(c != farthestCol + dirX); return targetNeighbours; } std::vector AniMapUtils::closestXTiles(TileData tile, cocos2d::TMXTiledMap* tmxMap, cocos2d::TMXLayer* tmxLayer, int dir, int range, int maxLevelDelta){ assert(dir == 1 || dir == -1 || dir == 0); auto farthestCol = tile.col + dir*range; farthestCol = MAX(0, farthestCol); farthestCol = MIN(tmxMap->getMapSize().width -1, farthestCol); auto minRow = MAX(0, tile.row - range); auto maxRow = MIN(tmxMap->getMapSize().height - 1, tile.row + range); std::vector targetNeighbours; // for(int i = tile.col + dir; i != farthestCol + dir; i += dir){ auto c = tile.col; do { c += dir; for(int r = minRow; r <= maxRow; ++r){ auto candidateTile = TileData(c, r); if(isTileAccessible(tmxMap, tmxLayer, c, r) && abs(getTileLevel(tmxMap, tmxLayer, candidateTile)-getTileLevel(tmxMap, tmxLayer, tile)) <= maxLevelDelta){ targetNeighbours.push_back(candidateTile); } } } while(c != farthestCol + dir); return targetNeighbours; } std::vector AniMapUtils::closestYTiles(TileData tile, cocos2d::TMXTiledMap* tmxMap, cocos2d::TMXLayer* tmxLayer, int dir, int range, int maxLevelDelta){ assert(dir == 1 || dir == -1 || dir == 0); auto farthestRow = tile.row - dir*range; //minus, because cols are in the opposite direction than y farthestRow = MAX(0, farthestRow); farthestRow = MIN(tmxMap->getMapSize().height -1, farthestRow); auto minCol = MAX(0, tile.col - range); auto maxCol = MIN(tmxMap->getMapSize().width - 1, tile.col + range); std::vector targetNeighbours; auto r = tile.row; do { r -= dir; for(int c = minCol; c <= maxCol; ++c){ auto candidateTile = TileData(c, r); if(isTileAccessible(tmxMap, tmxLayer, c, r) && abs(getTileLevel(tmxMap, tmxLayer, candidateTile)-getTileLevel(tmxMap, tmxLayer, tile)) <= maxLevelDelta){ targetNeighbours.push_back(candidateTile); } } } while(r != farthestRow - dir); return targetNeighbours; } AniMapUtils::TileData AniMapUtils::getClosestTileInDirection(cocos2d::TMXTiledMap* tmxMap, cocos2d::TMXLayer* tmxLayer, TileData origin, int colDir, int rowDir, int maxFlatDelta, int maxLevelDelta){ auto consideredTiles = closestTiles(origin, tmxMap, tmxLayer, colDir, rowDir, maxFlatDelta, maxLevelDelta); if(consideredTiles.size() > 0){ std::sort(consideredTiles.begin(), consideredTiles.end(), [&](AniMapUtils::TileData first, AniMapUtils::TileData second){ return (first-origin).convertToVec2().length() < (second-origin).convertToVec2().length(); }); return consideredTiles[0]; } else { return origin; } } AniMapUtils::TileData AniMapUtils::getClosestTileInXDirection(cocos2d::TMXTiledMap* tmxMap, cocos2d::TMXLayer* tmxLayer, TileData origin, int colDir, int maxFlatDelta, int maxLevelDelta){ auto consideredTiles = closestXTiles(origin, tmxMap, tmxLayer, colDir, maxFlatDelta, maxLevelDelta); if(consideredTiles.size() > 0){ std::sort(consideredTiles.begin(), consideredTiles.end(), [&](AniMapUtils::TileData first, AniMapUtils::TileData second){ return (first-origin).convertToVec2().length() < (second-origin).convertToVec2().length(); }); return consideredTiles[0]; } else { return origin; } } AniMapUtils::TileData AniMapUtils::getClosestTileInYDirection(cocos2d::TMXTiledMap* tmxMap, cocos2d::TMXLayer* tmxLayer, TileData origin, int rowDir, int maxFlatDelta, int maxLevelDelta){ auto consideredTiles = closestYTiles(origin, tmxMap, tmxLayer, rowDir, maxFlatDelta, maxLevelDelta); if(consideredTiles.size() > 0){ std::sort(consideredTiles.begin(), consideredTiles.end(), [&](AniMapUtils::TileData first, AniMapUtils::TileData second){ return (first-origin).convertToVec2().length() < (second-origin).convertToVec2().length(); }); return consideredTiles[0]; } else { return origin; } } /* mapGraph: 2dim array of nodeData corresponding to the tiles on the map (row/col) tiles, with additional data: arrays of neighbours, temp value nodeData: { neighbours : [tile1, tile2, ...] tempDistance : number } */ std::vector AniMapUtils::calculatePath(AniMapUtils::TileData fromTile, AniMapUtils::TileData toTile, std::vector> mapGraph) { //TODO: !!!!!!!!!! CLEAR NODE PATH DATA // log("looking for path from "+JSON.stringify(fromTile)+ " to: "+JSON.stringify(toTile)); std::vector path; if(fromTile == toTile){ return path; } for(auto col : mapGraph){ for(auto value : col){ value->tempDistance = -1; value->previousTile = TileData(-1,-1); value->visited = false; } } auto currentTileCol = fromTile.col; auto currentTileRow = fromTile.row; auto currentTile = mapGraph[currentTileCol][currentTileRow]; currentTile->tempDistance = 0; auto finished = false, pathFound = false; std::vector visitedNodes; while(!finished){ // finished when the currentTile is the final tile or we've visited all possible nodes (there are no more tiles we could visit) // log("visiting tile "+currentTileCol + " " + currentTileRow); for(int i = 0; i < currentTile->neighbours.size(); ++i){ auto neighbourTile = mapGraph[currentTile->neighbours[i].col][currentTile->neighbours[i].row]; if(!neighbourTile->visited){ auto newDistance = currentTile->tempDistance + 1; if(neighbourTile->tempDistance == -1 || neighbourTile->tempDistance > newDistance){ neighbourTile->tempDistance = newDistance; neighbourTile->previousTile = TileData(currentTileCol, currentTileRow); } } } currentTile->visited = true; visitedNodes.push_back(currentTile); if(currentTileCol == toTile.col && currentTileRow == toTile.row){ // finished = true; pathFound = true; break; } else { if(abs(currentTileCol - toTile.col) > m_maxTileRangeOnPath || abs(currentTileRow - toTile.row) > m_maxTileRangeOnPath){ pathFound = false; break; } int candidateTileCol = -1; int candidateTileRow = -1; PathTileData* candidateTile = NULL; for(int i = 0; i < visitedNodes.size(); ++i){ for(int j = 0; j < visitedNodes[i]->neighbours.size(); ++j){ auto tileCoordinates = visitedNodes[i]->neighbours[j]; auto tileData = mapGraph[tileCoordinates.col][tileCoordinates.row]; if(!tileData->visited && tileData->tempDistance != -1 && (candidateTile == NULL || tileData->tempDistance < candidateTile->tempDistance)){ candidateTile = tileData; candidateTileCol = tileCoordinates.col; candidateTileRow = tileCoordinates.row; } } } if(candidateTile != NULL){ currentTile = candidateTile; currentTileRow = candidateTileRow; currentTileCol = candidateTileCol; } else { // finished = true; pathFound = false; break; } } } auto tempTile = toTile; if(!pathFound){ auto newToTile = std::min_element(visitedNodes.begin(), visitedNodes.end(), [&](const AniMapUtils::PathTileData* first, const AniMapUtils::PathTileData* second){ return first->tile.distanceColRowToTile(toTile) < second->tile.distanceColRowToTile(toTile); }); tempTile = (*newToTile)->tile; } if(tempTile == fromTile){ return path; } path.push_back(tempTile); while(!(tempTile.row == fromTile.row && tempTile.col == fromTile.col)){ //todo write a comparing func here inside this func tempTile = mapGraph[tempTile.col][tempTile.row]->previousTile; path.push_back(tempTile); } std::reverse(path.begin(), path.end()); // cocos2d::log("path found, size: %lu\n", path.size()); // cocos2d::log("path tiles:\n"); // for(auto it = path.begin(); it != path.end(); ++it){ // cocos2d::log("col %d row %d ", (*it).col, (*it).row); // } // cocos2d::log("path found: "+JSON.stringify(path)); return path; } AniMapUtils::TileData AniMapUtils::translateXYPointToColRow (cocos2d::Point xypoint, float tileWidth, float tileHeight, int totalMapCols, int totalMapRows) { auto col = AniMapUtils::translateXToCol(xypoint.x, tileWidth, totalMapCols); auto row = AniMapUtils::translateYToRow(xypoint.y, tileHeight, totalMapRows); return AniMapUtils::TileData(col, row); } int AniMapUtils::translateXToCol(float x, float tileWidth, int totalMapCols) { return fmin(fmax(ceil(x / tileWidth) - 1, 0), totalMapCols-1); } int AniMapUtils::translateYToRow(float y, float tileHeight, int totalMapRows) { return totalMapRows - fmin(fmax(ceil(y / tileHeight) - 1, 0), totalMapRows-1) - 1; } cocos2d::Point AniMapUtils::getTileMiddlePosition(const cocos2d::TMXTiledMap* map, int col, int row) { auto x = (col + 0.5) * map->getTileSize().width; auto translatedRow = map->getMapSize().height - row - 1; auto y = (translatedRow + 0.5) * map->getTileSize().height; return cocos2d::Point(x,y); } cocos2d::Rect AniMapUtils::getTileRect(const cocos2d::TMXTiledMap* map, int col, int row){ auto tileMiddlePos = getTileMiddlePosition(map, col, row); auto tileW = map->getTileSize().width; auto tileH = map->getTileSize().height; return cocos2d::Rect{tileMiddlePos.x - tileW/2, tileMiddlePos.y - tileH/2, tileW, tileH}; } cocos2d::Point AniMapUtils::translateScreenPositionToMapPosition(cocos2d::Point pointOnScreen, cocos2d::Point mapPosition) { return cocos2d::Point(pointOnScreen.x - mapPosition.x, pointOnScreen.y - mapPosition.y); } cocos2d::Point AniMapUtils::translateMapPositionToScreenPosition(cocos2d::Point pointOnMap, cocos2d::Point mapPosition) { return cocos2d::Point(pointOnMap.x + mapPosition.x, pointOnMap.y + mapPosition.y); } // watch out for tile coords outside of the maps std::vector AniMapUtils::getTilesIntersectingRect(cocos2d::Rect rect, float tileWidth, float tileHeight, int totalMapCols, int totalMapRows){ std::vector tiles; auto upperLeftTile = translateXYPointToColRow(cocos2d::Point(rect.getMinX(), rect.getMinY()), tileWidth, tileHeight, totalMapCols, totalMapRows); auto lowerRightTile = translateXYPointToColRow(cocos2d::Point(rect.getMaxX(), rect.getMaxY()), tileWidth, tileHeight, totalMapCols, totalMapRows); for(int i = upperLeftTile.col; i <= lowerRightTile.col; ++i){ for(int j = upperLeftTile.row; j >= lowerRightTile.row; --j){ tiles.push_back(AniMapUtils::TileData{i,j}); } } return tiles; } std::vector AniMapUtils::getTilesIntersectingRect(cocos2d::Rect rect, float tileWidth, float tileHeight, int totalMapCols, int totalMapRows, std::vector& borderTiles){ borderTiles.clear(); std::vector tiles; auto upperLeftTile = translateXYPointToColRow(cocos2d::Point(rect.getMinX(), rect.getMinY()), tileWidth, tileHeight, totalMapCols, totalMapRows); auto lowerRightTile = translateXYPointToColRow(cocos2d::Point(rect.getMaxX(), rect.getMaxY()), tileWidth, tileHeight, totalMapCols, totalMapRows); for(int i = upperLeftTile.col; i <= lowerRightTile.col; ++i){ for(int j = upperLeftTile.row; j >= lowerRightTile.row; --j){ auto tile = AniMapUtils::TileData{i,j}; tiles.push_back(tile); if(i == upperLeftTile.col || j == upperLeftTile.row || i == lowerRightTile.col || j == lowerRightTile.row){ borderTiles.push_back(tile); } } } return tiles; } cocos2d::Point AniMapUtils::getRandomFreeTile(TileData& randomTile, cocos2d::TMXTiledMap* map, cocos2d::TMXLayer* layer, int minCol, int maxCol, int minRow, int maxRow) { minCol = MAX(minCol, 0); minRow = MAX(minRow, 0); maxCol = MIN(maxCol, map->getMapSize().width-1); maxRow = MIN(maxRow, map->getMapSize().height-1); cocos2d::Point tileMiddlePoint(-1,-1); do { auto randomCol = AniMathUtils::getRandomInt(minCol, maxCol); auto randomRow = AniMathUtils::getRandomInt(minRow, maxRow); if(AniMapUtils::isTileAccessible(map, layer, randomCol, randomRow)){ randomTile.row = randomRow; randomTile.col = randomCol; tileMiddlePoint = AniMapUtils::getTileMiddlePosition(map, randomCol, randomRow); } } while(tileMiddlePoint.x == -1); return tileMiddlePoint; } void AniMapUtils::setTilePassable(TileData tile, bool passable) { if(!passable){ _tempImpassableTiles[tile] = passable; } else { _tempImpassableTiles.erase(tile); } } bool operator==(const AniMapUtils::TileData& lhs, const AniMapUtils::TileData& rhs) { return lhs.col == rhs.col && lhs.row == rhs.row; } bool operator!=(const AniMapUtils::TileData& lhs, const AniMapUtils::TileData& rhs) { return lhs.col != rhs.col || lhs.row != rhs.row; } // for the map bool operator<(const AniMapUtils::TileData& lhs, const AniMapUtils::TileData& rhs) { return lhs.row < rhs.row || (lhs.row == rhs.row && lhs.col < rhs.col); }