Skip to content

Commit

Permalink
Feature: Generate lock ready rivers upon world generation
Browse files Browse the repository at this point in the history
  • Loading branch information
SamuXarick committed Feb 8, 2019
1 parent 5e4f76f commit 3af1f0d
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 8 deletions.
121 changes: 113 additions & 8 deletions src/landscape.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1041,11 +1041,18 @@ static bool FindSpring(TileIndex tile, void *user_data)

if (num < 4) return false;

/* Are we near the top of a hill? */
for (int dx = -16; dx <= 16; dx++) {
for (int dy = -16; dy <= 16; dy++) {
TileIndex t = TileAddWrap(tile, dx, dy);
if (t != INVALID_TILE && GetTileMaxZ(t) > referenceHeight + 2) return false;
if (t != INVALID_TILE) {
/* Are we near the top of a hill? */
if (GetTileMaxZ(t) > referenceHeight + 2) return false;

/* Are we too close to another river? */
if (dx >= -8 && dx <= 8 && dy >= -8 && dy <= 8) {
if (IsWaterTile(t) && IsRiver(t)) return false;
}
}
}
}

Expand Down Expand Up @@ -1075,6 +1082,70 @@ static bool MakeLake(TileIndex tile, void *user_data)
return false;
}

/**
* Check whether a river could (logically) flow into a lock.
* @param tile the middle tile of a lock.
* @return true iff the water can be flowing into a lock.
*/
static bool IsPossibleLockLocationRecursively(TileIndex tile)
{
if (!IsPossibleLockLocation(tile)) return false;

DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile));
TileIndexDiff delta_mid = TileOffsByDiagDir(dir);

DiagDirection dir_rot = ChangeDiagDir(dir, DIAGDIRDIFF_90RIGHT);
TileIndexDiff delta_side = 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) {
TileIndex t_dm_ds = t_dm + d * delta_side;
if (IsValidTile(t_dm_ds)) {
if (!IsTileFlat(t_dm_ds) && IsPossibleLockLocationOnDiagDir(t_dm_ds, dir_rot)) return false;

TileIndex t_dm_2ds = t_dm_ds + d * delta_side;
if (IsValidTile(t_dm_2ds)) {
if (!IsTileFlat(t_dm_2ds) && IsPossibleLockLocationOnDiagDir(t_dm_2ds, dir_rot)) 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) {
TileIndex t_2dm_ds = t_2dm + d * delta_side;
if (IsValidTile(t_2dm_ds)) {
if (!IsTileFlat(t_2dm_ds)) {
if (IsTileFlat(t_dm + d * delta_side)) return false;
if (IsPossibleLockLocationOnDiagDir(t_2dm_ds, dir_rot)) return false;
}
}
}

TileIndex t_3dm = t_2dm + m * delta_mid;
if (IsValidTile(t_3dm)) {
if (IsPossibleLockLocationOnDiagDir(t_3dm, dir)) return false;
}

for (int d = -1; d <= 1; d += 2) {
TileIndex t_3dm_ds = t_3dm + d * delta_side;
if (IsValidTile(t_3dm_ds)) {
if (IsPossibleLockLocationOnDiagDir(t_3dm_ds, dir)) return false;
}
}
}
}
}

return true;
}

/**
* Check whether a river at begin could (logically) flow down to end.
* @param begin The origin of the flow.
Expand All @@ -1092,9 +1163,9 @@ static bool FlowsDown(TileIndex begin, TileIndex end)

return heightEnd <= heightBegin &&
/* Slope either is inclined or flat; rivers don't support other slopes. */
(slopeEnd == SLOPE_FLAT || IsInclinedSlope(slopeEnd)) &&
(slopeEnd == SLOPE_FLAT || (IsInclinedSlope(slopeEnd) && IsPossibleLockLocationRecursively(end))) &&
/* Slope continues, then it must be lower... or either end must be flat. */
((slopeEnd == slopeBegin && heightEnd < heightBegin) || slopeEnd == SLOPE_FLAT || slopeBegin == SLOPE_FLAT);
((slopeEnd == slopeBegin && heightEnd < heightBegin) || slopeEnd == SLOPE_FLAT || (slopeBegin == SLOPE_FLAT && GetTileMaxZ(end) == heightBegin));
}

/* AyStar callback for checking whether we reached our destination. */
Expand Down Expand Up @@ -1186,9 +1257,10 @@ static void BuildRiver(TileIndex begin, TileIndex end)
* 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 flowdown_count The number of times the river has flowed down
* @return True iff a river could/has been built, otherwise false.
*/
static bool FlowRiver(TileIndex spring, TileIndex begin)
static bool FlowRiver(TileIndex spring, TileIndex begin, uint flowdown_count = 0)
{
#define SET_MARK(x) marks.insert(x)
#define IS_MARKED(x) (marks.find(x) != marks.end())
Expand All @@ -1211,7 +1283,7 @@ static bool FlowRiver(TileIndex spring, TileIndex begin)
queue.pop_front();

uint height2 = TileHeight(end);
if (IsTileFlat(end) && (height2 < height || (height2 == height && IsWaterTile(end)))) {
if (IsTileFlat(end) && ((height2 < height && ++flowdown_count) || (height2 == height && IsWaterTile(end)))) {
found = true;
break;
}
Expand All @@ -1228,8 +1300,8 @@ static bool FlowRiver(TileIndex spring, TileIndex begin)

if (found) {
/* Flow further down hill. */
found = FlowRiver(spring, end);
} else if (count > 32) {
found = FlowRiver(spring, end, flowdown_count);
} else if (count > 32 && flowdown_count > 1) {
/* Maybe we can make a lake. Find the Nth of the considered tiles. */
TileIndex lakeCenter = 0;
int i = RandomRange(count - 1) + 1;
Expand Down Expand Up @@ -1284,6 +1356,39 @@ static void CreateRivers()
}
}

/* Create additional river tiles around possible lock locations to connect them. */
for (TileIndex tile = 0; tile != MapSize(); tile++) {
if (IsValidTile(tile) && IsTileType(tile, MP_WATER) && IsRiver(tile) && IsInclinedSlope(GetTileSlope(tile))) {

Slope slope = GetTileSlope(tile);
DiagDirection dir = GetInclinedSlopeDirection(slope);
TileIndexDiff delta_side = TileOffsByDiagDir(ChangeDiagDir(dir, DIAGDIRDIFF_90RIGHT));
TileIndexDiff delta_mid = TileOffsByDiagDir(dir);
int mid_counts[] = {2, 1, 1};
int side_counts[] = {0, 1, -1};

for (int m = -1; m <= 1; m += 2) {
for (int i = 0; i < 3; i++) {
TileIndex tc = tile + m * mid_counts[i] * delta_mid + side_counts[i] * delta_side;

TileIndex t = INVALID_TILE;
if (side_counts[i] != 0) {
if (IsValidTile(tc) && IsWaterTile(tc)) {
TileIndex tr = tc + m * delta_mid;
if (IsValidTile(tr) && !IsWaterTile(tr) && IsTileFlat(tr)) t = tr;
}
} else if (IsValidTile(tc) && !IsWaterTile(tc)) t = tc;

if (t != INVALID_TILE && IsValidTile(t)) {
MakeRiver(t, Random());
/* Remove desert directly around the river tile. */
CircularTileSearch(&t, 5, RiverModifyDesertZone, NULL);
}
}
}
}
}

/* Run tile loop to update the ground density. */
for (uint i = 0; i != 256; i++) {
if (i % 64 == 0) IncreaseGeneratingWorldProgress(GWP_RIVER);
Expand Down
3 changes: 3 additions & 0 deletions src/water.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ void MakeWaterKeepingClass(TileIndex tile, Owner o);

bool RiverModifyDesertZone(TileIndex tile, void *data);

bool IsPossibleLockLocation(TileIndex tile);
bool IsPossibleLockLocationOnDiagDir(TileIndex tile, DiagDirection dir);

bool IsWateredTile(TileIndex tile, Direction from);

/**
Expand Down
24 changes: 24 additions & 0 deletions src/water_cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,30 @@ static CommandCost RemoveShipDepot(TileIndex tile, DoCommandFlag flags)
return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_SHIP]);
}

bool IsPossibleLockLocation(TileIndex tile)
{
DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile));
if (!IsValidDiagDirection(dir)) return false;

TileIndexDiff delta_mid = TileOffsByDiagDir(dir);
if (!IsTileFlat(tile + delta_mid)) return false;
if (!IsTileFlat(tile - delta_mid)) return false;

return true;
}

/**
* Tests whether a tile is suitable for a lock for the provided diagonal direction (axis).
* @param tile The middle tile of a lock.
* @param dir Any diagonal direction belonging to the same axis.
* @return true if a lock can be placed in the given direction.
*/
bool IsPossibleLockLocationOnDiagDir(TileIndex tile, DiagDirection dir)
{
DiagDirection tdir = GetInclinedSlopeDirection(GetTileSlope(tile));
return (tdir == dir || tdir == ReverseDiagDir(dir)) && IsPossibleLockLocation(tile);
}

/**
* Builds a lock.
* @param tile Central tile of the lock.
Expand Down

0 comments on commit 3af1f0d

Please sign in to comment.