From b972ed86045e1c287d03b3f7cda1ff21243d00fe Mon Sep 17 00:00:00 2001 From: PeterN Date: Wed, 12 May 2021 18:17:57 +0100 Subject: Fix #9242: Tree tick handler did not scale by map size. (#9246) This means that random tree generation density is higher on small maps and lower on large maps. This difference is enough to make the Lumber Mill impractical to use on large maps. This change skips ticks on maps smaller than 256x256 and increases iterations or shortens the interval on maps larger than 256x256. --- src/tree_cmd.cpp | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/tree_cmd.cpp b/src/tree_cmd.cpp index c0865ffc6..f1a5be7f9 100644 --- a/src/tree_cmd.cpp +++ b/src/tree_cmd.cpp @@ -22,6 +22,7 @@ #include "company_base.h" #include "core/random_func.hpp" #include "newgrf_generic.h" +#include "date_func.h" #include "table/strings.h" #include "table/tree_land.h" @@ -803,6 +804,23 @@ static void TileLoop_Trees(TileIndex tile) MarkTileDirtyByTile(tile); } +/** + * Decrement the tree tick counter. + * The interval is scaled by map size to allow for the same density regardless of size. + * Adjustment for map sizes below the standard 256 * 256 are handled earlier. + * @return true if the counter was decremented past zero + */ +bool DecrementTreeCounter() +{ + /* Ensure _trees_tick_ctr can be decremented past zero only once for the largest map size. */ + static_assert(2 * (MAX_MAP_SIZE_BITS - MIN_MAP_SIZE_BITS) - 4 <= std::numeric_limits::digits); + + /* byte underflow */ + byte old_trees_tick_ctr = _trees_tick_ctr; + _trees_tick_ctr -= ScaleByMapSize(1); + return old_trees_tick_ctr <= _trees_tick_ctr; +} + void OnTick_Trees() { /* Don't spread trees if that's not allowed */ @@ -812,16 +830,24 @@ void OnTick_Trees() TileIndex tile; TreeType tree; + /* Skip some tree ticks for map sizes below 256 * 256. 64 * 64 is 16 times smaller, so + * this is the maximum number of ticks that are skipped. Number of ticks to skip is + * inversely proportional to map size, so that is handled to create a mask. */ + int skip = ScaleByMapSize(16); + if (skip < 16 && (_tick_counter & (16 / skip - 1)) != 0) return; + /* place a tree at a random rainforest spot */ - if (_settings_game.game_creation.landscape == LT_TROPIC && - (r = Random(), tile = RandomTileSeed(r), GetTropicZone(tile) == TROPICZONE_RAINFOREST) && - CanPlantTreesOnTile(tile, false) && - (tree = GetRandomTreeType(tile, GB(r, 24, 8))) != TREE_INVALID) { - PlantTreesOnTile(tile, tree, 0, 0); + if (_settings_game.game_creation.landscape == LT_TROPIC) { + for (uint c = ScaleByMapSize(1); c > 0; c--) { + if ((r = Random(), tile = RandomTileSeed(r), GetTropicZone(tile) == TROPICZONE_RAINFOREST) && + CanPlantTreesOnTile(tile, false) && + (tree = GetRandomTreeType(tile, GB(r, 24, 8))) != TREE_INVALID) { + PlantTreesOnTile(tile, tree, 0, 0); + } + } } - /* byte underflow */ - if (--_trees_tick_ctr != 0 || _settings_game.construction.extra_tree_placement == ETP_SPREAD_RAINFOREST) return; + if (!DecrementTreeCounter() || _settings_game.construction.extra_tree_placement == ETP_SPREAD_RAINFOREST) return; /* place a tree at a random spot */ r = Random(); -- cgit v1.2.3-54-g00ecf