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.
This commit is contained in:

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
|
||||
|
106
src/tree_cmd.cpp
106
src/tree_cmd.cpp
@@ -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 */
|
||||
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;
|
||||
|
||||
if (_settings_game.game_creation.tree_placer != TP_PERFECT) {
|
||||
total += num_groups * DEFAULT_TREE_STEPS;
|
||||
}
|
||||
|
||||
SetGeneratingWorldProgress(GWP_TREE, total);
|
||||
|
||||
if (_settings_game.game_creation.tree_placer != TP_PERFECT) {
|
||||
if (num_groups != 0) PlaceTreeGroups(num_groups);
|
||||
}
|
||||
|
||||
for (; i != 0; i--) {
|
||||
PlaceTreesRandomly();
|
||||
@@ -479,12 +499,25 @@ 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 (_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. */
|
||||
if (--limit < 1) {
|
||||
@@ -493,23 +526,28 @@ CommandCost CmdPlantTree(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
|
||||
}
|
||||
|
||||
if (flags & DC_EXEC) {
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (cur_tile == INVALID_TILE) return;
|
||||
|
||||
// Keep in range of the existing tree
|
||||
if (abs(x) + abs(y) > 16) return;
|
||||
|
||||
// Clear tile, no farm-tiles or rocks
|
||||
if (!CanPlantTreesOnTile(cur_tile, true)) return;
|
||||
|
||||
// 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));
|
||||
|
||||
/* Cacti don't spread */
|
||||
if (!CanPlantTreesOnTile(tile, false)) return;
|
||||
|
||||
/* Don't plant trees, if ground was freshly cleared */
|
||||
// Don't plant trees, if ground was freshly cleared
|
||||
if (IsTileType(tile, MP_CLEAR) && GetClearGround(tile) == CLEAR_GRASS && GetClearDensity(tile) != 3) return;
|
||||
|
||||
PlantTreesOnTile(tile, treetype, 0, 0);
|
||||
|
||||
PlantTreesOnTile(tile, tree_type, 0, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user