Bin no_trees_on_this_level, use exp. decay away from snow line.

Probability of placing an arctic tree is now an exponential decay
function of height distance from the snow line, instead of the previous
50% blocks.
This results in a more gradual thinning out of arctic trees in
each direction.
The algorithm is: p = exp(-3 * distance / range_setting),
using a rather crude approximation of the exponential function.

The no_trees_on_this_level setting is not really useful, and its
dual behaviour to disallow discrete height levels and turn on the
snow line behaviour is unintuitive.
Replace it with a simple on/off setting.
This commit is contained in:
Jonathan G Rennison
2015-08-21 21:11:43 +01:00
parent 3b120c6cbd
commit 381d11096f
3 changed files with 76 additions and 33 deletions

View File

@@ -311,7 +311,7 @@ struct ConstructionSettings {
bool freeform_edges; ///< allow terraforming the tiles at the map edges bool freeform_edges; ///< allow terraforming the tiles at the map edges
uint8 extra_tree_placement; ///< (dis)allow building extra trees in-game uint8 extra_tree_placement; ///< (dis)allow building extra trees in-game
uint8 trees_around_snow_line_range; ///< range around snowline for mixed and arctic forest. uint8 trees_around_snow_line_range; ///< range around snowline for mixed and arctic forest.
uint32 no_trees_on_this_level; ///< option to avoid growth of trees on certain tile level. bool trees_around_snow_line_enabled; ///< enable mixed and arctic forest around snowline, and no trees above snowline
uint8 command_pause_level; ///< level/amount of commands that can't be executed while paused uint8 command_pause_level; ///< level/amount of commands that can't be executed while paused
uint32 terraform_per_64k_frames; ///< how many tile heights may, over a long period, be terraformed per 65536 frames? uint32 terraform_per_64k_frames; ///< how many tile heights may, over a long period, be terraformed per 65536 frames?

View File

@@ -2310,23 +2310,22 @@ strhelp = STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_HELPTEXT
strval = STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_NONE strval = STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_NONE
cat = SC_BASIC cat = SC_BASIC
[SDT_BOOL]
base = GameSettings
var = construction.trees_around_snow_line_enabled
def = true
cat = SC_BASIC
patxname = ""everest_treeline.construction.trees_around_snow_line_enabled""
[SDT_VAR] [SDT_VAR]
base = GameSettings base = GameSettings
var = construction.trees_around_snow_line_range var = construction.trees_around_snow_line_range
type = SLE_UINT8 type = SLE_UINT8
def = 0 def = 8
min = 0 min = 1
max = 15 max = 20
cat = SC_BASIC
[SDT_VAR]
base = GameSettings
var = construction.no_trees_on_this_level
type = SLE_UINT32
def = 0
min = 0
max = UINT32_MAX
cat = SC_BASIC cat = SC_BASIC
patxname = ""everest_treeline.construction.trees_around_snow_line_range""
[SDT_VAR] [SDT_VAR]
base = GameSettings base = GameSettings

View File

@@ -118,6 +118,50 @@ static void PlantTreesOnTile(TileIndex tile, TreeType treetype, uint count, uint
MakeTree(tile, treetype, count, growth, ground, density); MakeTree(tile, treetype, count, growth, ground, density);
} }
/**
* Previous value of _settings_game.construction.trees_around_snow_line_range
* used to calculate _arctic_tree_occurance
*/
static uint8 _previous_trees_around_snow_line_range = 255;
/**
* Array of probabilities for artic trees to appear,
* by normalised distance from snow line
*/
static uint8 _arctic_tree_occurance[24];
/** Recalculate _arctic_tree_occurance */
static void RecalculateArcticTreeOccuranceArray()
{
/*
* Approximate: 256 * exp(-3 * distance / range)
* By using:
* 256 * ((1 + (-3 * distance / range) / 6) ** 6)
* ((256 - (128 * distance / range)) ** 6) >> (5 * 8);
*/
uint8 range = _settings_game.construction.trees_around_snow_line_range;
_previous_trees_around_snow_line_range = range;
_arctic_tree_occurance[0] = 255;
uint i = 1;
for (; i < lengthof(_arctic_tree_occurance); i++) {
if (range == 0) break;
uint x = 256 - ((128 * i) / range);
uint32 output = x;
output *= x;
output *= x;
output *= x;
output >>= 16;
output *= x;
output *= x;
output >>= 24;
if (output == 0) break;
_arctic_tree_occurance[i] = output;
}
for (; i < lengthof(_arctic_tree_occurance); i++) {
_arctic_tree_occurance[i] = 0;
}
}
/** /**
* Get a random TreeType for the given tile based on a given seed * Get a random TreeType for the given tile based on a given seed
* *
@@ -131,33 +175,33 @@ static void PlantTreesOnTile(TileIndex tile, TreeType treetype, uint count, uint
*/ */
static TreeType GetRandomTreeType(TileIndex tile, uint seed) static TreeType GetRandomTreeType(TileIndex tile, uint seed)
{ {
/* no_trees_on_this_level example: 0xDC96521 is no trees on z levels 13,12,9,6,5,2,1. Set to 0 gives you original gameplay. (See openttd.cfg) */
uint32 no_tree = _settings_game.construction.no_trees_on_this_level;
byte range = _settings_game.construction.trees_around_snow_line_range;
switch (_settings_game.game_creation.landscape) { switch (_settings_game.game_creation.landscape) {
case LT_TEMPERATE: case LT_TEMPERATE:
if (no_tree == 0) return (TreeType)(seed * TREE_COUNT_TEMPERATE / 256 + TREE_TEMPERATE); return (TreeType)(seed * TREE_COUNT_TEMPERATE / 256 + TREE_TEMPERATE);
case LT_ARCTIC: { case LT_ARCTIC: {
if (no_tree == 0) return (TreeType)(seed * TREE_COUNT_SUB_ARCTIC / 256 + TREE_SUB_ARCTIC); if (!_settings_game.construction.trees_around_snow_line_enabled) {
return (TreeType)(seed * TREE_COUNT_SUB_ARCTIC / 256 + TREE_SUB_ARCTIC);
}
uint8 range = _settings_game.construction.trees_around_snow_line_range;
if (range != _previous_trees_around_snow_line_range) RecalculateArcticTreeOccuranceArray();
int z = GetTileZ(tile); int z = GetTileZ(tile);
if (z > _settings_game.game_creation.snow_line_height + (2 * range)) return TREE_INVALID; // Above tree line. int height_above_snow_line = z - _settings_game.game_creation.snow_line_height;
uint normalised_distance = (height_above_snow_line < 0) ? -height_above_snow_line : height_above_snow_line + 1;
/* no_trees_on_this_level */ bool arctic_tree = false;
for (; no_tree != 0;) { if (normalised_distance < lengthof(_arctic_tree_occurance)) {
if ((int)(no_tree & 0xF) == z) return TREE_INVALID; uint adjusted_seed = (seed ^ tile) & 0xFF;
no_tree = no_tree >> 4; arctic_tree = adjusted_seed < _arctic_tree_occurance[normalised_distance];
} }
if (z > (int)_settings_game.game_creation.snow_line_height - range) { if (height_above_snow_line < 0) {
/* Below snow level mixed forest. Above is Arctic trees and thinning out. */ /* Below snow level mixed forest. */
if (z < _settings_game.game_creation.snow_line_height) { return (arctic_tree) ? (TreeType)(seed * TREE_COUNT_SUB_ARCTIC / 256 + TREE_SUB_ARCTIC) : (TreeType)(seed * TREE_COUNT_TEMPERATE / 256 + TREE_TEMPERATE);
return (seed & 1) ? (TreeType)(seed * TREE_COUNT_SUB_ARCTIC / 256 + TREE_SUB_ARCTIC) : (TreeType)(seed * TREE_COUNT_TEMPERATE / 256 + TREE_TEMPERATE); } else {
} else { /* Above is Arctic trees and thinning out. */
return (seed & 1) ? TREE_INVALID : (TreeType)(seed * TREE_COUNT_SUB_ARCTIC / 256 + TREE_SUB_ARCTIC); return (arctic_tree) ? (TreeType)(seed * TREE_COUNT_SUB_ARCTIC / 256 + TREE_SUB_ARCTIC) : TREE_INVALID;
}
} }
return (TreeType)(seed * TREE_COUNT_TEMPERATE / 256 + TREE_TEMPERATE);
} }
case LT_TROPIC: case LT_TROPIC:
switch (GetTropicZone(tile)) { switch (GetTropicZone(tile)) {