/**
* Make a connected lake; fill all tiles in the circular tile search that are connected.
* @param tile The tile to consider for lake making.
* @param user_data The height of the lake.
* @return Always false, so it continues searching.
*/
static bool MakeLake(TileIndex tile, void *user_data)
{
uint height = *(uint*)user_data;
if (!IsValidTile(tile) || TileHeight(tile) != height || !IsTileFlat(tile)) return false;
if (_settings_game.game_creation.landscape == LT_TROPIC && GetTropicZone(tile) == TROPICZONE_DESERT) return false;
for (DiagDirection d = DIAGDIR_BEGIN; d < DIAGDIR_END; d++) {
TileIndex t2 = tile + TileOffsByDiagDir(d);
if (IsWaterTile(t2)) {
MakeRiver(tile, Random());
return false;
}
}
return false;
}
/**
* Check whether a river could (logically) flow into a lock.
* @param tile the middle tile of a lock.
* @recursive whether the function is being called recursively.
* @return true iff the water can be flowing into a lock.
*/
bool FlowsLock(TileIndex tile, bool recursive = false)
{
DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile));
if (dir == INVALID_DIAGDIR) return false;
int delta_mid = TileOffsByDiagDir(dir);
if (!IsTileFlat(tile + delta_mid)) return false;
if (!IsTileFlat(tile - delta_mid)) return false;
if (!recursive) return true;
DiagDirection dir_rot = ChangeDiagDir(dir, DIAGDIRDIFF_90RIGHT);
int delta = TileOffsByDiagDir(dir_rot);
for (int m = -1; m <= 1; m += 2) {
TileIndex t_dm = tile + m * delta_mid;
if (IsValidTile(t_dm)) {
if (DistanceFromEdgeDir(t_dm, dir) == 0 || DistanceFromEdgeDir(t_dm, ReverseDiagDir(dir)) == 0) {
return false;
}
for (int d = -1; d <= 1; d += 2) {
if (IsValidTile(t_dm + d * delta)) {
if (!IsTileFlat(t_dm + d * delta) &&
(GetInclinedSlopeDirection(GetTileSlope(t_dm + d * delta)) == dir_rot ||
GetInclinedSlopeDirection(GetTileSlope(t_dm + d * delta)) == ReverseDiagDir(dir_rot)) &&
FlowsLock(t_dm + d * delta)) {
return false;
}
if (IsValidTile(t_dm + d * 2 * delta)) {
if (!IsTileFlat(t_dm + d * 2 * delta) &&
(GetInclinedSlopeDirection(GetTileSlope(t_dm + d * 2 * delta)) == dir_rot ||
GetInclinedSlopeDirection(GetTileSlope(t_dm + d * 2 * delta)) == ReverseDiagDir(dir_rot)) &&
FlowsLock(t_dm + d * 2 * delta)) {
return false;
}
}
}
}
TileIndex t_2dm = t_dm + m * delta_mid;
if (IsValidTile(t_2dm)) {
if (!IsTileFlat(t_2dm)) {
return false;
}
for (int d = -1; d <= 1; d += 2) {
if (IsValidTile(t_2dm + d * delta)) {
if (IsTileFlat(t_dm + d * delta) && !IsTileFlat(t_2dm + d * delta)) {
return false;
}
if (!IsTileFlat(t_2dm + d * delta) &&
(GetInclinedSlopeDirection(GetTileSlope(t_2dm + d * delta)) == dir_rot ||
GetInclinedSlopeDirection(GetTileSlope(t_2dm + d * delta)) == ReverseDiagDir(dir_rot)) &&
FlowsLock(t_2dm + d * delta)) {
return false;
}
}
}
TileIndex t_3dm = t_2dm + m * delta_mid;
if (IsValidTile(t_3dm)) {
if ((GetInclinedSlopeDirection(GetTileSlope(t_3dm)) == dir ||
GetInclinedSlopeDirection(GetTileSlope(t_3dm)) == ReverseDiagDir(dir)) &&
FlowsLock(t_3dm)) {
return false;
}
}
for (int d = -1; d <= 1; d += 2) {
if (IsValidTile(t_3dm + d * delta)) {
if ((GetInclinedSlopeDirection(GetTileSlope(t_3dm + d * delta)) == dir ||
GetInclinedSlopeDirection(GetTileSlope(t_3dm + d * delta)) == ReverseDiagDir(dir)) &&
FlowsLock(t_3dm + d * delta)) {
return false;
}
}
}
}
}
}
return true;
}
/**
* Check whether a river at begin could (logically) flow down to end.
* @param begin The origin of the flow.
* @param end The destination of the flow.
* @return True iff the water can be flowing down.
*/
static bool FlowsDown(TileIndex begin, TileIndex end)
{
assert(DistanceManhattan(begin, end) == 1);
int heightBegin;
int heightEnd;
Slope slopeBegin = GetTileSlope(begin, &heightBegin);
Slope slopeEnd = GetTileSlope(end, &heightEnd);
return heightEnd <= heightBegin &&
/* Slope either is inclined or flat; rivers don't support other slopes. */
(slopeEnd == SLOPE_FLAT || (IsInclinedSlope(slopeEnd) && FlowsLock(end, true))) &&
/* Slope continues, then it must be lower... or either end must be flat. */
((slopeEnd == slopeBegin && heightEnd < heightBegin) || slopeEnd == SLOPE_FLAT || (slopeBegin == SLOPE_FLAT && GetTileMaxZ(end) == heightBegin));
}