* Try to flow the river down from a given begin. * @param spring The springing point of the river. * @param begin The begin point we are looking from; somewhere down hill from the spring. * @param min_river_length The minimum length for the river. * @return First element: True iff a river could/has been built, otherwise false; second element: River ends at sea. */
| 1371 | * @return First element: True iff a river could/has been built, otherwise false; second element: River ends at sea. |
| 1372 | */ |
| 1373 | static std::tuple<bool, bool> FlowRiver(TileIndex spring, TileIndex begin, uint min_river_length) |
| 1374 | { |
| 1375 | if (IsWaterTile(begin)) { |
| 1376 | return { DistanceManhattan(spring, begin) > min_river_length, GetTileZ(begin) == 0 }; |
| 1377 | } |
| 1378 | |
| 1379 | int height_begin = TileHeight(begin); |
| 1380 | |
| 1381 | std::unordered_set<TileIndex> marks; |
| 1382 | marks.insert(begin); |
| 1383 | |
| 1384 | std::vector<TileIndex> queue; |
| 1385 | queue.push_back(begin); |
| 1386 | |
| 1387 | /* Breadth first search for the closest tile we can flow down to. |
| 1388 | * We keep the queue to find the Nth tile for lake guessing. The tiles |
| 1389 | * in the queue are neatly ordered by insertion. |
| 1390 | */ |
| 1391 | bool found = false; |
| 1392 | TileIndex end; |
| 1393 | for (size_t i = 0; i != queue.size(); i++) { |
| 1394 | end = queue[i]; |
| 1395 | |
| 1396 | int height_end; |
| 1397 | if (IsTileFlat(end, &height_end) && (height_end < height_begin || (height_end == height_begin && IsWaterTile(end)))) { |
| 1398 | if (IsWaterTile(end) && GetWaterClass(end) == WaterClass::Sea) { |
| 1399 | /* If we've found the sea, make sure it's large enough. Scale by the map size but set a cap to avoid performance issues on large maps. */ |
| 1400 | const uint MAX_SEA_SIZE_THRESHOLD = 1024; |
| 1401 | const uint SEA_SIZE_THRESHOLD = std::min(static_cast<uint>(2 * std::sqrt(Map::SizeX() * Map::SizeY())), MAX_SEA_SIZE_THRESHOLD); |
| 1402 | std::unordered_set<TileIndex> sea; |
| 1403 | /* Count the connected tiles, if the sea is large we can end the river here. */ |
| 1404 | bool found_edge = CountConnectedSeaTiles(end, sea, SEA_SIZE_THRESHOLD); |
| 1405 | if (found_edge || sea.size() > SEA_SIZE_THRESHOLD) { |
| 1406 | found = true; |
| 1407 | break; |
| 1408 | } else { |
| 1409 | /* Sea is too small, flatten it so the river keeps looking or forms a lake / wetland. */ |
| 1410 | for (TileIndex sea_tile : sea) { |
| 1411 | Command<CMD_TERRAFORM_LAND>::Do(DoCommandFlag::Execute, sea_tile, SLOPE_ELEVATED, false); |
| 1412 | Slope slope = ComplementSlope(GetTileSlope(sea_tile)); |
| 1413 | Command<CMD_TERRAFORM_LAND>::Do(DoCommandFlag::Execute, sea_tile, slope, true); |
| 1414 | } |
| 1415 | } |
| 1416 | } else { |
| 1417 | /* We've found a river. */ |
| 1418 | found = true; |
| 1419 | break; |
| 1420 | } |
| 1421 | } |
| 1422 | |
| 1423 | for (DiagDirection d = DIAGDIR_BEGIN; d < DIAGDIR_END; d++) { |
| 1424 | TileIndex t = end + TileOffsByDiagDir(d); |
| 1425 | if (IsValidTile(t) && !marks.contains(t) && RiverFlowsDown(end, t)) { |
| 1426 | marks.insert(t); |
| 1427 | queue.push_back(t); |
| 1428 | } |
| 1429 | } |
| 1430 | } |
no test coverage detected