Improve tree placement

Prior to this change, trees tended to either cover the entire map like an ancient forest, or alternatively you turn off their growth which breaks industry. Furthermore there are these ugly random tree clumps at the beginning of the game which look like squares on the map someone placed there.

This change adds a new tree placing setting which removes the ugly random clumps and only slightly modifies the initial placement. The actual growth causes trees to bunch up in higher levels as usual but on the lower 4 levels their growth works differently. The number of trees per tile is limited and the trees spread out over a wider area instead of only to the neighboring tile. That spreads them out more and makes for a nicer look.

This also allows cacti to spread, since they can now use that same algorithm and avoid bunching up, but spread as they should.
pull/278/head
Andreas Schmitt 3 years ago committed by Jonathan G Rennison
parent ac9749d015
commit 3b4cbd3323

@ -1543,6 +1543,7 @@ STR_CONFIG_SETTING_TREE_PLACER_HELPTEXT :Choose the dist
STR_CONFIG_SETTING_TREE_PLACER_NONE :None
STR_CONFIG_SETTING_TREE_PLACER_ORIGINAL :Original
STR_CONFIG_SETTING_TREE_PLACER_IMPROVED :Improved
STR_CONFIG_SETTING_TREE_PLACER_PERFECT :Perfect
STR_CONFIG_SETTING_ROAD_SIDE :Road vehicles: {STRING2}
STR_CONFIG_SETTING_ROAD_SIDE_HELPTEXT :Choose the driving side
STR_CONFIG_SETTING_HEIGHTMAP_ROTATION :Heightmap rotation: {STRING2}

@ -3803,9 +3803,9 @@ var = game_creation.tree_placer
type = SLE_UINT8
from = SLV_30
guiflags = SGF_MULTISTRING | SGF_NEWGAME_ONLY | SGF_SCENEDIT_TOO
def = 2
def = 3
min = 0
max = 2
max = 3
str = STR_CONFIG_SETTING_TREE_PLACER
strhelp = STR_CONFIG_SETTING_TREE_PLACER_HELPTEXT
strval = STR_CONFIG_SETTING_TREE_PLACER_NONE

@ -39,6 +39,7 @@ enum TreePlacer {
TP_NONE, ///< No tree placer algorithm
TP_ORIGINAL, ///< The original algorithm
TP_IMPROVED, ///< A 'improved' algorithm
TP_PERFECT, ///< A 'best' algorithm
};
/** Where to place trees while in-game? */
@ -305,6 +306,13 @@ static void PlaceTreeAtSameHeight(TileIndex tile, int height)
}
}
int MaxTreeCount(const TileIndex tile)
{
const auto tile_z = GetTileZ(tile);
return (tile_z == 1) ? 1 : tile_z + (_settings_game.game_creation.landscape != LT_TROPIC ? 0 : 1);
}
/**
* Place some trees randomly
*
@ -324,14 +332,19 @@ void PlaceTreesRandomly()
if (CanPlantTreesOnTile(tile, true)) {
PlaceTree(tile, r);
if (_settings_game.game_creation.tree_placer != TP_IMPROVED) continue;
if (_settings_game.game_creation.tree_placer != TP_IMPROVED &&
_settings_game.game_creation.tree_placer != TP_PERFECT) continue;
/* Place a number of trees based on the tile height.
* This gives a cool effect of multiple trees close together.
* It is almost real life ;) */
ht = GetTileZ(tile);
/* The higher we get, the more trees we plant */
j = GetTileZ(tile) * 2;
if (_settings_game.game_creation.tree_placer == TP_PERFECT) {
j = MaxTreeCount(tile);
} else {
j = GetTileZ(tile) * 2;
}
/* Above snowline more trees! */
if (_settings_game.game_creation.landscape == LT_ARCTIC && ht > GetSnowLine()) j *= 3;
while (j--) {
@ -436,7 +449,8 @@ void GenerateTrees()
switch (_settings_game.game_creation.tree_placer) {
case TP_ORIGINAL: i = _settings_game.game_creation.landscape == LT_ARCTIC ? 15 : 6; break;
case TP_IMPROVED: i = _settings_game.game_creation.landscape == LT_ARCTIC ? 4 : 2; break;
case TP_IMPROVED:
case TP_PERFECT: i = _settings_game.game_creation.landscape == LT_ARCTIC ? 4 : 2; break;
default: NOT_REACHED();
}
@ -444,10 +458,16 @@ void GenerateTrees()
if (_settings_game.game_creation.landscape == LT_TROPIC) total += ScaleByMapSize(DEFAULT_RAINFOREST_TREE_STEPS);
total *= i;
uint num_groups = (_settings_game.game_creation.landscape != LT_TOYLAND) ? ScaleByMapSize(GB(Random(), 0, 5) + 25) : 0;
total += num_groups * DEFAULT_TREE_STEPS;
if (_settings_game.game_creation.tree_placer != TP_PERFECT) {
total += num_groups * DEFAULT_TREE_STEPS;
}
SetGeneratingWorldProgress(GWP_TREE, total);
if (num_groups != 0) PlaceTreeGroups(num_groups);
if (_settings_game.game_creation.tree_placer != TP_PERFECT) {
if (num_groups != 0) PlaceTreeGroups(num_groups);
}
for (; i != 0; i--) {
PlaceTreesRandomly();
@ -479,11 +499,24 @@ CommandCost CmdPlantTree(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
TileArea ta(tile, p2);
TILE_AREA_LOOP(tile, ta) {
switch (GetTileType(tile)) {
case MP_TREES:
case MP_TREES: {
bool grow_existing_tree_instead = false;
/* no more space for trees? */
if (GetTreeCount(tile) == 4) {
msg = STR_ERROR_TREE_ALREADY_HERE;
continue;
if (_settings_game.game_creation.tree_placer == TP_PERFECT) {
if (GetTreeCount(tile) >= 4 || ((int)GetTreeCount(tile) >= MaxTreeCount(tile))) {
if (GetTreeGrowth(tile) < 3) {
grow_existing_tree_instead = true;
} else {
msg = STR_ERROR_TREE_ALREADY_HERE;
continue;
}
}
} else {
if (GetTreeCount(tile) == 4) {
msg = STR_ERROR_TREE_ALREADY_HERE;
continue;
}
}
/* Test tree limit. */
@ -493,23 +526,28 @@ CommandCost CmdPlantTree(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
}
if (flags & DC_EXEC) {
AddTreeCount(tile, 1);
if (grow_existing_tree_instead) {
SetTreeGrowth(tile, 3);
} else {
AddTreeCount(tile, 1);
}
MarkTileDirtyByTile(tile, VMDF_NOT_MAP_MODE);
if (c != nullptr) c->tree_limit -= 1 << 16;
}
/* 2x as expensive to add more trees to an existing tile */
cost.AddCost(_price[PR_BUILD_TREES] * 2);
break;
}
case MP_WATER:
if (!IsCoast(tile) || IsSlopeWithOneCornerRaised(GetTileSlope(tile))) {
if (!CanPlantTreesOnTile(tile, false) || !IsCoast(tile) || IsSlopeWithOneCornerRaised(GetTileSlope(tile))) {
msg = STR_ERROR_CAN_T_BUILD_ON_WATER;
continue;
}
FALLTHROUGH;
case MP_CLEAR: {
if (IsBridgeAbove(tile)) {
if (!CanPlantTreesOnTile(tile, false) || IsBridgeAbove(tile)) {
msg = STR_ERROR_SITE_UNSUITABLE;
continue;
}
@ -853,29 +891,73 @@ static void TileLoop_Trees(TileIndex tile)
AddTreeGrowth(tile, 1);
break;
case 1: // add a tree
if (GetTreeCount(tile) < 4 && CanPlantExtraTrees(tile)) {
case 1: { // add a tree
if (_settings_game.game_creation.tree_placer == TP_PERFECT) {
if ((GetTreeCount(tile) < 4) && (GetTreeType(tile) != TREE_CACTUS) && ((int)GetTreeCount(tile) < MaxTreeCount(tile))) {
AddTreeCount(tile, 1);
SetTreeGrowth(tile, 0);
break;
}
} else if (GetTreeCount(tile) < 4 && CanPlantExtraTrees(tile)) {
AddTreeCount(tile, 1);
SetTreeGrowth(tile, 0);
break;
}
FALLTHROUGH;
}
FALLTHROUGH;
case 2: { // add a neighbouring tree
if (!CanPlantExtraTrees(tile)) break;
TreeType treetype = GetTreeType(tile);
if (_settings_game.game_creation.tree_placer == TP_PERFECT &&
((_settings_game.game_creation.landscape != LT_TROPIC && GetTileZ(tile) < 3) || (GetTreeType(tile) == TREE_CACTUS))) {
// On lower levels we spread more randomly to not bunch up.
const uint32 r = Random();
int x = 0;
int y = 0;
TileIndex cur_tile = INVALID_TILE;
// Give cacti more chance to spread since they are sparse to begin with.
const int max_tries = (GetTreeType(tile) == TREE_CACTUS) ? 8 : 4;
// Try multiple times to spread
for (int i = 0; i < max_tries; ++i) {
x = GB(r, 0, 5) - 16;
y = GB(r, 8, 5) - 16;
cur_tile = TileAddWrap(tile, x, y);
if ((cur_tile != INVALID_TILE) &&
(abs(x) + abs(y) <= 16) &&
(CanPlantTreesOnTile(cur_tile, true)) &&
(Delta(GetTileZ(cur_tile), GetTileZ(tile)) <= 2)) {
break;
}
}
tile += TileOffsByDir((Direction)(Random() & 7));
if (cur_tile == INVALID_TILE) return;
/* Cacti don't spread */
if (!CanPlantTreesOnTile(tile, false)) return;
// Keep in range of the existing tree
if (abs(x) + abs(y) > 16) return;
/* Don't plant trees, if ground was freshly cleared */
if (IsTileType(tile, MP_CLEAR) && GetClearGround(tile) == CLEAR_GRASS && GetClearDensity(tile) != 3) return;
// Clear tile, no farm-tiles or rocks
if (!CanPlantTreesOnTile(cur_tile, true)) return;
PlantTreesOnTile(tile, treetype, 0, 0);
// Not too much height difference
if (Delta(GetTileZ(cur_tile), GetTileZ(tile)) > 2) return;
// Place one tree and quit
PlantTreesOnTile(cur_tile, GetTreeType(tile), 0, 0);
} else {
const TreeType tree_type = GetTreeType(tile);
tile += TileOffsByDir((Direction)(Random() & 7));
if (!CanPlantTreesOnTile(tile, false)) return;
// Don't plant trees, if ground was freshly cleared
if (IsTileType(tile, MP_CLEAR) && GetClearGround(tile) == CLEAR_GRASS && GetClearDensity(tile) != 3) return;
PlantTreesOnTile(tile, tree_type, 0, 0);
}
break;
}

Loading…
Cancel
Save