summaryrefslogtreecommitdiff
path: root/src/landscape.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/landscape.cpp')
-rw-r--r--src/landscape.cpp93
1 files changed, 83 insertions, 10 deletions
diff --git a/src/landscape.cpp b/src/landscape.cpp
index 98ff6fdbc..bdfcc51f1 100644
--- a/src/landscape.cpp
+++ b/src/landscape.cpp
@@ -968,11 +968,10 @@ static void GenerateTerrain(int type, uint flag)
#include "table/genland.h"
-static void CreateDesertOrRainForest()
+static void CreateDesertOrRainForest(uint desert_tropic_line)
{
TileIndex update_freq = MapSize() / 4;
const TileIndexDiffC *data;
- uint max_desert_height = CeilDiv(_settings_game.construction.max_heightlevel, 4);
for (TileIndex tile = 0; tile != MapSize(); ++tile) {
if ((tile % update_freq) == 0) IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
@@ -982,7 +981,7 @@ static void CreateDesertOrRainForest()
for (data = _make_desert_or_rainforest_data;
data != endof(_make_desert_or_rainforest_data); ++data) {
TileIndex t = AddTileIndexDiffCWrap(tile, *data);
- if (t != INVALID_TILE && (TileHeight(t) >= max_desert_height || IsTileType(t, MP_WATER))) break;
+ if (t != INVALID_TILE && (TileHeight(t) >= desert_tropic_line || IsTileType(t, MP_WATER))) break;
}
if (data == endof(_make_desert_or_rainforest_data)) {
SetTropicZone(tile, TROPICZONE_DESERT);
@@ -1296,15 +1295,50 @@ static void CreateRivers()
}
/**
- * Calculate the line from which snow begins.
+ * Calculate what height would be needed to cover N% of the landmass.
+ *
+ * The function allows both snow and desert/tropic line to be calculated. It
+ * tries to find the closests height which covers N% of the landmass; it can
+ * be below or above it.
+ *
+ * Tropic has a mechanism where water and tropic tiles in mountains grow
+ * inside the desert. To better approximate the requested coverage, this is
+ * taken into account via an edge histogram, which tells how many neighbouring
+ * tiles are lower than the tiles of that height. The multiplier indicates how
+ * severe this has to be taken into account.
+ *
+ * @param coverage A value between 0 and 100 indicating a percentage of landmass that should be covered.
+ * @param edge_multiplier How much effect neighbouring tiles that are of a lower height level have on the score.
+ * @return The estimated best height to use to cover N% of the landmass.
*/
-static void CalculateSnowLine()
+static uint CalculateCoverageLine(uint coverage, uint edge_multiplier)
{
- /* Build a histogram of the map height. */
+ const DiagDirection neighbour_dir[] = {
+ DIAGDIR_NE,
+ DIAGDIR_SE,
+ DIAGDIR_SW,
+ DIAGDIR_NW,
+ };
+
+ /* Histogram of how many tiles per height level exist. */
std::array<int, MAX_TILE_HEIGHT + 1> histogram = {};
+ /* Histogram of how many neighbour tiles are lower than the tiles of the height level. */
+ std::array<int, MAX_TILE_HEIGHT + 1> edge_histogram = {};
+
+ /* Build a histogram of the map height. */
for (TileIndex tile = 0; tile < MapSize(); tile++) {
uint h = TileHeight(tile);
histogram[h]++;
+
+ if (edge_multiplier != 0) {
+ /* Check if any of our neighbours is below us. */
+ for (auto dir : neighbour_dir) {
+ TileIndex neighbour_tile = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDiagDir(dir));
+ if (IsValidTile(neighbour_tile) && TileHeight(neighbour_tile) < h) {
+ edge_histogram[h]++;
+ }
+ }
+ }
}
/* The amount of land we have is the map size minus the first (sea) layer. */
@@ -1312,7 +1346,7 @@ static void CalculateSnowLine()
int best_score = land_tiles;
/* Our goal is the coverage amount of the land-mass. */
- int goal_tiles = land_tiles * _settings_game.game_creation.snow_coverage / 100;
+ int goal_tiles = land_tiles * coverage / 100;
/* We scan from top to bottom. */
uint h = MAX_TILE_HEIGHT;
@@ -1323,13 +1357,50 @@ static void CalculateSnowLine()
current_tiles += histogram[h];
int current_score = goal_tiles - current_tiles;
+ /* Tropic grows from water and mountains into the desert. This is a
+ * great visual, but it also means we* need to take into account how
+ * much less desert tiles are being created if we are on this
+ * height-level. We estimate this based on how many neighbouring
+ * tiles are below us for a given length, assuming that is where
+ * tropic is growing from.
+ */
+ if (edge_multiplier != 0 && h > 1) {
+ /* From water tropic tiles grow for a few tiles land inward. */
+ current_score -= edge_histogram[1] * edge_multiplier;
+ /* Tropic tiles grow into the desert for a few tiles. */
+ current_score -= edge_histogram[h] * edge_multiplier;
+ }
+
if (std::abs(current_score) < std::abs(best_score)) {
best_score = current_score;
best_h = h;
}
+
+ /* Always scan all height-levels, as h == 1 might give a better
+ * score than any before. This is true for example with 0% desert
+ * coverage. */
}
- _settings_game.game_creation.snow_line_height = std::max(best_h, 2u);
+ return best_h;
+}
+
+/**
+ * Calculate the line from which snow begins.
+ */
+static void CalculateSnowLine()
+{
+ /* We do not have snow sprites on coastal tiles, so never allow "1" as height. */
+ _settings_game.game_creation.snow_line_height = std::max(CalculateCoverageLine(_settings_game.game_creation.snow_coverage, 0), 2u);
+}
+
+/**
+ * Calculate the line (in height) between desert and tropic.
+ * @return The height of the line between desert and tropic.
+ */
+static uint8 CalculateDesertLine()
+{
+ /* CalculateCoverageLine() runs from top to bottom, so we need to invert the coverage. */
+ return _settings_game.game_creation.snow_line_height = CalculateCoverageLine(100 - _settings_game.game_creation.desert_coverage, 4);
}
void GenerateLandscape(byte mode)
@@ -1421,9 +1492,11 @@ void GenerateLandscape(byte mode)
CalculateSnowLine();
break;
- case LT_TROPIC:
- CreateDesertOrRainForest();
+ case LT_TROPIC: {
+ uint desert_tropic_line = CalculateDesertLine();
+ CreateDesertOrRainForest(desert_tropic_line);
break;
+ }
default:
break;