diff options
Diffstat (limited to 'src/landscape.cpp')
-rw-r--r-- | src/landscape.cpp | 93 |
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; |