@@ -630,8 +630,8 @@ void SetSnowLine(byte table[SNOW_LINE_MONTHS][SNOW_LINE_DAYS])
for (uint i = 0; i < SNOW_LINE_MONTHS; i++) {
for (uint j = 0; j < SNOW_LINE_DAYS; j++) {
_snow_line->highest_value = max (_snow_line->highest_value , table[i][j]);
_snow_line->lowest_value = min (_snow_line->lowest_value , table[i][j]);
_snow_line->highest_value = max(_snow_line->highest_value, min((byte)MAX_SNOWLINE_HEIGHT, table[i][j]) );
_snow_line->lowest_value = min(_snow_line->lowest_value, max((byte)MIN_SNOWLINE_HEIGHT, table[i][j]) );
}
}
}
@@ -643,11 +643,11 @@ void SetSnowLine(byte table[SNOW_LINE_MONTHS][SNOW_LINE_DAYS])
*/
byte GetSnowLine()
{
if (_snow_line == nullptr ) return _settings_game.game_creation .snow_line_height ;
if (_snow_line == nullptr) return Clamp( _settings_game.game_creation.snow_line_height, MIN_SNOWLINE_HEIGHT, MAX_SNOWLINE_HEIGHT) ;
YearMonthDay ymd;
ConvertDateToYMD(_date, &ymd);
return _snow_line->table [ymd.month ][ymd.day ];
return Clamp( _snow_line->table[ymd.month][ymd.day], MIN_SNOWLINE_HEIGHT, MAX_SNOWLINE_HEIGHT) ;
}
/**
@@ -657,7 +657,7 @@ byte GetSnowLine()
*/
byte HighestSnowLine()
{
return _snow_line == nullptr ? _settings_game.game_creation .snow_line_height : _snow_line->highest_value ;
return min((byte)MAX_SNOWLINE_HEIGHT, _snow_line == nullptr ? _settings_game.game_creation.snow_line_height : _snow_line->highest_value) ;
}
/**
@@ -667,7 +667,7 @@ byte HighestSnowLine()
*/
byte LowestSnowLine()
{
return _snow_line == nullptr ? _settings_game.game_creation .snow_line_height : _snow_line->lowest_value ;
return max((byte)MIN_SNOWLINE_HEIGHT, _snow_line == nullptr ? _settings_game.game_creation.snow_line_height : _snow_line->lowest_value) ;
}
/**
@@ -865,7 +865,8 @@ static void GenerateTerrain(int type, uint flag)
uint x = r & MapMaxX();
uint y = (r >> MapLogX()) & MapMaxY();
if (x < 2 || y < 2 ) return ;
if (x < (uint)2 + (_settings_game.construction.freeform_edges ? 1 : 0)) return;
if (y < (uint)2 + (_settings_game.construction.freeform_edges ? 1 : 0)) return;
DiagDirection direction = (DiagDirection)GB(r, 22, 2);
uint w = templ->width;
@@ -882,26 +883,26 @@ static void GenerateTerrain(int type, uint flag)
switch (flag & 3) {
default: NOT_REACHED();
case 0 :
case 0: // Northern side
if (xw + yw > MapSize() - bias) return;
break;
case 1 :
case 1: // Eastern side
if (yw < xw + bias) return;
break;
case 2 :
case 2: // Southern side
if (xw + yw < MapSize() + bias) return;
break;
case 3 :
case 3: // Western side
if (xw < yw + bias) return;
break;
}
}
if (x + w >= MapMaxX () - 1 ) return ;
if (y + h >= MapMaxY () - 1 ) return ;
if (x + w >= MapMaxX()) return;
if (y + h >= MapMaxY()) return;
TileIndex tile = TileXY(x, y);
@@ -963,6 +964,55 @@ static void GenerateTerrain(int type, uint flag)
}
}
/**
* Automatically determine the value for snow line height and set it
* @param flat_world_height value >= 0: land height a flat world gets
* value == -1: not generating a flat world
*/
void DetermineSnowLineHeight(int flat_world_height)
{
/* Determine snow line height only when snow_line_height is lower or higher than the minimum or maximum values for the setting */
if (_settings_game.game_creation.snow_line_height != MIN_SNOWLINE_HEIGHT - 1 && _settings_game.game_creation.snow_line_height != MAX_SNOWLINE_HEIGHT + 1) return;
if (flat_world_height >= 0) { // generating a flat world
/* This doesn't require the extensive computations below */
int max_value = min(MAX_SNOWLINE_HEIGHT, max((int)MIN_SNOWLINE_HEIGHT, flat_world_height - 2));
uint half_max_value = max_value / 2 + max_value % 2;
_settings_game.game_creation.snow_line_height = max(MIN_SNOWLINE_HEIGHT, half_max_value);
}
if (flat_world_height == -1) { // generating landscape
int h0_tile_count = 0; // count tiles at sea level
int highest_height = 0;
for (uint y = 0; y < MapSizeY(); y++) {
for (uint x = 0; x < MapSizeX(); x++) {
int height = TileHeight(TileXY(x, y));
if (height == 0) h0_tile_count++;
if (height > highest_height) highest_height = height;
}
}
/* Determine the snow line height and make it so that it's at most 50% of the land mass */
int land_mass_size = MapSizeX() * MapSizeY() - h0_tile_count; // available tiles above sea
int tile_count = 0, snow_line_height = 1;
while (tile_count < land_mass_size / 2 && snow_line_height <= highest_height) {
for (uint y = 0; y < MapSizeY(); y++) {
for (uint x = 0; x < MapSizeX(); x++) {
if (TileHeight(TileXY(x, y)) == snow_line_height) tile_count++;
}
}
snow_line_height++;
}
/* Farms can only generate below 'snow_line_height - 1' and above sea level,
* which limits minimum snow_line_height to 'snow_line_height - 1 > 1', thus '3'.
* Forests can only generate at a minimum of 'snow_line_height + 2'
* which limits maximum snow_line_height to 'highest_height - 2'.
* @see CheckNewIndustry_Farm and CheckNewIndustry_Forest. */
_settings_game.game_creation.snow_line_height = Clamp(snow_line_height, 3, max(3, min(MAX_SNOWLINE_HEIGHT, highest_height - 2)));
}
}
#include "table/genland.h"
@@ -1034,11 +1084,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;
}
}
}
}
@@ -1071,6 +1128,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 tile_offset = tile + m * delta_mid;
if (IsValidTile(tile_offset)) {
if (DistanceFromEdgeDir(tile_offset, dir) == 0 || DistanceFromEdgeDir(tile_offset, ReverseDiagDir(dir)) == 0) return false;
for (int s = -1; s <= 1; s += 2) {
tile_offset = tile + m * delta_mid + s * delta_side;
if (IsValidTile(tile_offset)) {
if (!IsTileFlat(tile_offset) && IsPossibleLockLocationOnDiagDir(tile_offset, dir_rot)) return false;
tile_offset = tile + m * delta_mid + 2 * s * delta_side;
if (IsValidTile(tile_offset)) {
if (!IsTileFlat(tile_offset) && IsPossibleLockLocationOnDiagDir(tile_offset, dir_rot)) return false;
}
}
}
tile_offset = tile + 2 * m * delta_mid;
if (IsValidTile(tile_offset)) {
if (!IsTileFlat(tile_offset)) return false;
for (int s = -1; s <= 1; s += 2) {
tile_offset = tile + 2 * m * delta_mid + s * delta_side;
if (IsValidTile(tile_offset)) {
if (!IsTileFlat(tile_offset)) {
if (IsTileFlat(tile + m * delta_mid + s * delta_side)) return false;
if (IsPossibleLockLocationOnDiagDir(tile_offset, dir_rot)) return false;
}
}
}
tile_offset = tile + 3 * m * delta_mid;
if (IsValidTile(tile_offset)) {
if (IsPossibleLockLocationOnDiagDir(tile_offset, dir)) return false;
}
for (int s = -1; s <= 1; s += 2) {
tile_offset = tile + 3 * m * delta_mid + s * delta_side;
if (IsValidTile(tile_offset)) {
if (IsPossibleLockLocationOnDiagDir(tile_offset, dir)) return false;
}
}
}
}
}
return true;
}
/**
* Check whether a river at begin could (logically) flow down to end.
* @param begin The origin of the flow.
@@ -1088,9 +1209,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. */
@@ -1182,9 +1303,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())
@@ -1207,7 +1329,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;
}
@@ -1224,8 +1346,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;
@@ -1263,6 +1385,43 @@ static bool FlowRiver(TileIndex spring, TileIndex begin)
return found;
}
/* Create additional river tiles around possible lock locations to connect them. */
static void ConnectPossibleLocksWithRivers()
{
for (TileIndex tile = 0; tile != MapSize(); tile++) {
if (IsValidTile(tile) && IsTileType(tile, MP_WATER) && IsRiver(tile)) {
Slope slope = GetTileSlope(tile);
if (!IsInclinedSlope(slope)) continue;
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 tile_offset = tile + m * mid_counts[i] * delta_mid + side_counts[i] * delta_side;
TileIndex t = INVALID_TILE;
if (side_counts[i] != 0) {
if (IsValidTile(tile_offset) && IsWaterTile(tile_offset)) {
tile_offset = tile + m * mid_counts[i] * delta_mid + side_counts[i] * delta_side + m * delta_mid;
if (IsValidTile(tile_offset) && !IsWaterTile(tile_offset) && IsTileFlat(tile_offset)) t = tile_offset;
}
} else if (IsValidTile(tile_offset) && !IsWaterTile(tile_offset)) t = tile_offset;
if (t != INVALID_TILE && IsValidTile(t)) {
MakeRiver(t, Random());
/* Remove desert directly around the river tile. */
CircularTileSearch(&t, 5, RiverModifyDesertZone, NULL);
}
}
}
}
}
}
/**
* Actually (try to) create some rivers.
*/
@@ -1282,6 +1441,7 @@ static void CreateRivers()
if (FlowRiver(t, t)) break;
}
}
ConnectPossibleLocksWithRivers();
/* Run tile loop to update the ground density. */
for (uint i = 0; i != 256; i++) {
@@ -1294,18 +1454,32 @@ void GenerateLandscape(byte mode)
{
/** Number of steps of landscape generation */
enum GenLandscapeSteps {
GLS_HEIGHTMAP = 3 , // /< Loading a heightmap
GLS_TERRAGENESIS = 5 , // /< Terragenesis generator
GLS_HEIGHTMAP = 2 , ///< Loading a heightmap
GLS_TERRAGENESIS = 4 , ///< Terragenesis generator
GLS_ORIGINAL = 2, ///< Original generator
GLS_ARCTIC = 1, ///< Extra step needed for arctic landscape
GLS_TROPIC = 12, ///< Extra steps needed for tropic landscape
GLS_OTHER = 0, ///< Extra steps for other landscapes
};
uint steps = (_settings_game.game_creation .landscape == LT_TROPIC) ? GLS_TROPIC : GLS_OTHER;
uint steps;
switch (_settings_game.game_creation.landscape) {
case LT_TEMPERATE:
case LT_TOYLAND:
steps = GLS_OTHER;
break;
case LT_ARCTIC:
steps = GLS_ARCTIC;
break;
case LT_TROPIC:
steps = GLS_TROPIC;
break;
}
if (mode == GWM_HEIGHTMAP) {
SetGeneratingWorldProgress(GWP_LANDSCAPE, steps + GLS_HEIGHTMAP);
LoadHeightmap(_file_to_saveload.detail_ftype, _file_to_saveload.name);
IncreaseGeneratingWorldProgress (GWP_LANDSCAPE);
} else if (_settings_game.game_creation.land_generator == LG_TERRAGENESIS) {
SetGeneratingWorldProgress(GWP_LANDSCAPE, steps + GLS_TERRAGENESIS);
GenerateTerrainPerlin();
@@ -1364,13 +1538,19 @@ void GenerateLandscape(byte mode)
}
}
/* Do not call IncreaseGeneratingWorldProgress() before FixSlopes(),
* it allows screen redraw. Drawing of broken slopes crashes the game */
FixSlopes ();
IncreaseGeneratingWorldProgress (GWP_LANDSCAPE);
if (mode == GWM_HEIGHTMAP || _settings_game.game_creation.land_generator == LG_ORIGINAL) {
/* Do not call IncreaseGeneratingWorldProgress() before FixSlopes(),
* it allows screen redraw. Drawing of broken slopes crashes the game */
FixSlopes();
IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
}
ConvertGroundTilesIntoWaterTiles();
IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
if (_settings_game.game_creation.landscape == LT_ARCTIC) {
DetermineSnowLineHeight();
IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
}
if (_settings_game.game_creation.landscape == LT_TROPIC) CreateDesertOrRainForest();
CreateRivers();