Feature: Add worldgen setting for average height (#14989)

This commit is contained in:
Tyler Trahan
2026-01-10 16:53:27 +00:00
committed by GitHub
parent 666073fc7b
commit 7ccfd0a658
9 changed files with 175 additions and 93 deletions
+92 -56
View File
@@ -215,7 +215,7 @@ static const int64_t _water_percent[4] = {70, 170, 270, 420};
*/
static Height TGPGetMaxHeight()
{
if (_settings_game.difficulty.terrain_type == TT_CUSTOM) {
if (_settings_game.difficulty.terrain_type == GenworldMaxHeight::Custom) {
/* TGP never reaches this height; this means that if a user inputs "2",
* it would create a flat map without the "+ 1". But that would
* overflow on "255". So we reduce it by 1 to get back in range. */
@@ -242,7 +242,7 @@ static Height TGPGetMaxHeight()
};
int map_size_bucket = std::min(Map::LogX(), Map::LogY()) - MIN_MAP_SIZE_BITS;
int max_height_from_table = max_height[_settings_game.difficulty.terrain_type][map_size_bucket];
int max_height_from_table = max_height[to_underlying(_settings_game.difficulty.terrain_type)][map_size_bucket];
/* If there is a manual map height limit, clamp to it. */
if (_settings_game.construction.map_height_limit != 0) {
@@ -455,6 +455,81 @@ static int *HeightMapMakeHistogram(Height h_min, [[maybe_unused]] Height h_max,
return hist;
}
/**
* Adjust the landscape to create lowlands on average (tropic landscape).
* @param fheight The height to adjust.
* @return The adjusted height.
*/
static double SineTransformLowlands(double fheight)
{
double height = fheight;
/* Half of tiles should be at lowest (0..25%) heights */
double sine_lower_limit = 0.5;
double linear_compression = 2;
if (height <= sine_lower_limit) {
/* Under the limit we do linear compression down */
height = height / linear_compression;
} else {
double m = sine_lower_limit / linear_compression;
/* Get sine_lower_limit..1 into -1..1 */
height = 2.0 * ((height - sine_lower_limit) / (1.0 - sine_lower_limit)) - 1.0;
/* Sine wave transform */
height = sin(height * M_PI_2);
/* Get -1..1 back to (sine_lower_limit / linear_compression)..1.0 */
height = 0.5 * ((1.0 - m) * height + (1.0 + m));
}
return height;
}
/**
* Adjust the landscape to create normal average height (temperate and toyland landscapes).
* @param fheight The height to adjust.
* @return The adjusted height.
*/
static double SineTransformNormal(double &fheight)
{
double height = fheight;
/* Move and scale 0..1 into -1..+1 */
height = 2 * height - 1;
/* Sine transform */
height = sin(height * M_PI_2);
/* Transform it back from -1..1 into 0..1 space */
height = 0.5 * (height + 1);
return height;
}
/**
* Adjust the landscape to create plateaus on average (arctic landscape).
* @param fheight The height to adjust.
* @return The adjusted height.
*/
static double SineTransformPlateaus(double &fheight)
{
double height = fheight;
/* Redistribute heights to have more tiles at highest (75%..100%) range */
double sine_upper_limit = 0.75;
double linear_compression = 2;
if (height >= sine_upper_limit) {
/* Over the limit we do linear compression up */
height = 1.0 - (1.0 - height) / linear_compression;
} else {
double m = 1.0 - (1.0 - sine_upper_limit) / linear_compression;
/* Get 0..sine_upper_limit into -1..1 */
height = 2.0 * height / sine_upper_limit - 1.0;
/* Sine wave transform */
height = sin(height * M_PI_2);
/* Get -1..1 back to 0..(1 - (1 - sine_upper_limit) / linear_compression) == 0.0..m */
height = 0.5 * (height + 1.0) * m;
}
return height;
}
/** Applies sine wave redistribution onto height map */
static void HeightMapSineTransform(Height h_min, Height h_max)
{
@@ -465,66 +540,27 @@ static void HeightMapSineTransform(Height h_min, Height h_max)
/* Transform height into 0..1 space */
fheight = (double)(h - h_min) / (double)(h_max - h_min);
/* Apply sine transform depending on landscape type */
switch (_settings_game.game_creation.landscape) {
case LandscapeType::Toyland:
case LandscapeType::Temperate:
/* Move and scale 0..1 into -1..+1 */
fheight = 2 * fheight - 1;
/* Sine transform */
fheight = sin(fheight * M_PI_2);
/* Transform it back from -1..1 into 0..1 space */
fheight = 0.5 * (fheight + 1);
break;
case LandscapeType::Arctic:
{
/* Arctic terrain needs special height distribution.
* Redistribute heights to have more tiles at highest (75%..100%) range */
double sine_upper_limit = 0.75;
double linear_compression = 2;
if (fheight >= sine_upper_limit) {
/* Over the limit we do linear compression up */
fheight = 1.0 - (1.0 - fheight) / linear_compression;
} else {
double m = 1.0 - (1.0 - sine_upper_limit) / linear_compression;
/* Get 0..sine_upper_limit into -1..1 */
fheight = 2.0 * fheight / sine_upper_limit - 1.0;
/* Sine wave transform */
fheight = sin(fheight * M_PI_2);
/* Get -1..1 back to 0..(1 - (1 - sine_upper_limit) / linear_compression) == 0.0..m */
fheight = 0.5 * (fheight + 1.0) * m;
}
switch (_settings_game.game_creation.average_height) {
case GenworldAverageHeight::Auto:
/* Apply sine transform depending on landscape type */
switch (_settings_game.game_creation.landscape) {
case LandscapeType::Temperate: fheight = SineTransformNormal(fheight); break;
case LandscapeType::Tropic: fheight = SineTransformLowlands(fheight); break;
case LandscapeType::Arctic: fheight = SineTransformPlateaus(fheight); break;
case LandscapeType::Toyland: fheight = SineTransformNormal(fheight); break;
default: NOT_REACHED();
}
break;
case LandscapeType::Tropic:
{
/* Desert terrain needs special height distribution.
* Half of tiles should be at lowest (0..25%) heights */
double sine_lower_limit = 0.5;
double linear_compression = 2;
if (fheight <= sine_lower_limit) {
/* Under the limit we do linear compression down */
fheight = fheight / linear_compression;
} else {
double m = sine_lower_limit / linear_compression;
/* Get sine_lower_limit..1 into -1..1 */
fheight = 2.0 * ((fheight - sine_lower_limit) / (1.0 - sine_lower_limit)) - 1.0;
/* Sine wave transform */
fheight = sin(fheight * M_PI_2);
/* Get -1..1 back to (sine_lower_limit / linear_compression)..1.0 */
fheight = 0.5 * ((1.0 - m) * fheight + (1.0 + m));
}
}
break;
default:
NOT_REACHED();
break;
case GenworldAverageHeight::Lowlands: fheight = SineTransformLowlands(fheight); break;
case GenworldAverageHeight::Normal: fheight = SineTransformNormal(fheight); break;
case GenworldAverageHeight::Plateaus: fheight = SineTransformPlateaus(fheight); break;
default: NOT_REACHED();
}
/* Transform it back into h_min..h_max space */
h = (Height)(fheight * (h_max - h_min) + h_min);
h = static_cast<Height>(fheight * (h_max - h_min) + h_min);
if (h < 0) h = I2H(0);
if (h >= h_max) h = h_max - 1;
}