summaryrefslogtreecommitdiff
path: root/src/landscape.cpp
diff options
context:
space:
mode:
authorPatric Stout <truebrain@openttd.org>2021-03-24 16:38:36 +0100
committerPatric Stout <github@truebrain.nl>2021-03-26 12:22:32 +0100
commit70bc55cfd6e14f710427012b03aa211a0d85ca0f (patch)
treeb61c6c7e26f799afe542000d77f7cd3811d252d4 /src/landscape.cpp
parentcafe4eed6e482149eefe75393ad3a13e0f6e7ffe (diff)
downloadopenttd-70bc55cfd6e14f710427012b03aa211a0d85ca0f.tar.xz
Feature: setting to indicate desert coverage for tropic climate
This is an indication value; the game tries to get as close as it can, but due to the complex tropic rules, that is unlikely to be exact. In the end, it picks a height-level to base the desert/tropic line on. This is strictly seen not needed, as we can convert any tile to either. But it is the simplest way to get started with this without redoing all related functions.
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;