mirror of https://github.com/OpenTTD/OpenTTD
Add: Procedural tree growth
parent
f98b90ac2e
commit
17114369c0
|
@ -365,4 +365,22 @@ constexpr uint64_t PowerOfTen(int power)
|
|||
|
||||
uint32_t IntSqrt(uint32_t num);
|
||||
|
||||
|
||||
/**
|
||||
* Simple 32 bit to 32 bit hash.
|
||||
* From MurmurHash3.
|
||||
* @param h The value to take the hash of.
|
||||
* @return The hash of the value.
|
||||
*/
|
||||
inline uint32_t SimpleHash32(uint32_t h)
|
||||
{
|
||||
h ^= h >> 16;
|
||||
h *= 0x85ebca6b;
|
||||
h ^= h >> 13;
|
||||
h *= 0xc2b2ae35;
|
||||
h ^= h >> 16;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
#endif /* MATH_FUNC_HPP */
|
||||
|
|
|
@ -383,6 +383,7 @@ enum SaveLoadVersion : uint16_t {
|
|||
SLV_GROUP_NUMBERS, ///< 336 PR#12297 Add per-company group numbers.
|
||||
SLV_INCREASE_STATION_TYPE_FIELD_SIZE, ///< 337 PR#12572 Increase size of StationType field in map array
|
||||
SLV_ROAD_WAYPOINTS, ///< 338 PR#12572 Road waypoints
|
||||
SLV_PROCEDURAL_TREE_GROWTH, ///< 339 PR#11955 Procedural tree growth.
|
||||
|
||||
SL_MAX_VERSION, ///< Highest possible saveload version
|
||||
};
|
||||
|
|
|
@ -579,4 +579,5 @@ max = 3
|
|||
str = STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT
|
||||
strhelp = STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_HELPTEXT
|
||||
strval = STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_NO_SPREAD
|
||||
post_cb = [](auto) { MarkWholeScreenDirty(); }
|
||||
cat = SC_BASIC
|
||||
|
|
208
src/tree_cmd.cpp
208
src/tree_cmd.cpp
|
@ -58,6 +58,10 @@ static const uint16_t DEFAULT_TREE_STEPS = 1000; ///< Default number
|
|||
static const uint16_t DEFAULT_RAINFOREST_TREE_STEPS = 15000; ///< Default number of attempts for placing extra trees at rainforest in tropic.
|
||||
static const uint16_t EDITOR_TREE_DIV = 5; ///< Game editor tree generation divisor factor.
|
||||
|
||||
/* Pre-randomized tree growth cycles (for procedural growth). */
|
||||
static const uint8_t RANDOM_GROWTH_SPREAD[] = {141, 66, 6, 10, 14, 46, 46, 78, 78, 78, 78, 67, 7, 11, 15, 47, 79, 19, 23, 27, 14, 78, 18, 22, 26, 13, 66, 6, 10, 14, 46, 46, 67, 7, 11, 15, 19, 23, 27, 14, 46, 46, 67, 7, 11, 15, 79, 19, 23, 27, 14, 78, 78, 46, 46, 46, 78, 46, 46, 46, 46, 46, 67, 7, 11, 15, 19, 23, 27, 14, 46, 18, 22, 26, 13, 77, 66, 6, 10, 14, 46, 18, 22, 26, 13, 45, 45, 45, 45, 45, 66, 6, 10, 14, 67, 7, 11, 15, 19, 23, 27, 14, 18, 22, 26, 13, 45, 45, 45, 66, 6, 10, 14, 18, 22, 26, 13, 45, 45, 45, 45, 17, 21, 25, 12, 44, 44, 44, 76, 44, 65, 5, 9, 13, 66, 6, 10, 14, 46, 46, 67, 7, 11, 15, 47, 47, 47, 79, 47, 47, 47, 47, 47, 79, 79, 79, 47, 47, 47, 47, 47, 47, 47, 47, 47, 79, 79, 47, 47, 19, 23, 27, 14, 67, 7, 11, 15, 47, 79, 47, 47, 79, 47, 47, 19, 23, 27, 14, 46, 46, 67, 7, 11, 15, 47, 47, 47, 47, 79, 47, 47, 47, 47, 47, 19, 23, 27, 14, 78, 46, 46, 46, 46, 46, 78, 46, 46, 46, 78, 46, 67, 7, 11, 15, 79, 79, 47, 47, 79, 47, 79, 47, 47, 47, 79, 79, 79, 79, 47, 47, 47, 19, 23, 27, 14, 46, 46, 46, 18, 22, 26, 13, 45, 66, 6, 10, 14, 46, 18, 22, 26, 13, 45, 45, 45, 17, 21, 25, 12, 16, 20, 24, 141, 77, 45, 45, 45, 77, 45, 77, 45, 17, 21, 25, 12, 76, 44, 44, 44, 44, 16, 20, 24, 141, 45, 17, 21, 25, 12, 44, 76, 76, 44, 44, 44, 44, 44, 44, 76, 44, 44, 65, 5, 9, 13, 45, 77, 17, 21, 25, 12, 44, 44, 44, 44, 44, 44, 44, 44, 76, 16, 20, 24, 141, 17, 21, 25, 12, 44, 44, 76, 16, 20, 24, 141, 45, 17, 21, 25, 12, 44, 16, 20, 24, 141, 66, 6, 10, 14, 46, 46, 67, 7, 11, 15, 47, 47, 47, 47, 79, 47, 47, 47, 47, 47, 47, 47, 79, 47, 47, 19, 23, 27, 14, 46, 46, 46, 67, 7, 11, 15, 47, 79, 47, 47, 79, 47, 47, 47, 79, 47, 19, 23, 27, 14, 46, 46, 46, 18, 22, 26, 13, 45, 45, 17, 21, 25, 12, 65, 5, 9, 13, 45, 45, 45, 45, 45, 17, 21, 25, 12, 44, 65, 5, 9, 13, 77, 45, 45, 45, 45, 77, 45, 45, 45, 77, 45, 77, 17, 21, 25, 12, 44, 44, 76, 16, 20, 24, 141, 45, 45, 45, 45, 45, 45, 77, 45, 77, 66, 6, 10, 14, 46, 46, 46, 67, 7, 11, 15, 79, 79, 79, 79, 79, 47, 19, 23, 27, 14, 46, 78, 18, 22, 26, 13, 45, 45, 45, 45, 45, 45, 66, 6, 10, 14, 46, 78, 78, 46, 46, 46, 46, 46, 67, 7, 11, 15, 79, 47, 47, 47, 47, 47, 47, 79, 47, 47, 19, 23, 27, 14, 46, 46, 67, 7, 11, 15, 47, 47, 79, 47, 47, 47, 47, 19, 23, 27, 14, 78, 46, 67, 7, 11, 15, 19, 23, 27, 14, 46, 67, 7, 11, 15, 47, 19, 23, 27, 14, 46, 78, 46, 67, 7, 11, 15, 79, 79, 47, 47, 47, 47, 47, 47, 47, 79, 19, 23, 27, 14, 46, 18, 22, 26, 13, 17, 21, 25, 12, 44, 44, 44, 44, 16, 20, 24, 141, 66, 6, 10, 14, 46, 46, 46, 78, 46, 46, 18, 22, 26, 13, 45, 45, 45, 45, 45, 45, 45, 66, 6, 10, 14, 67, 7, 11, 15, 79, 47, 19, 23, 27, 14, 46, 46, 46, 78, 18, 22, 26, 13, 17, 21, 25, 12, 44, 44, 16, 20, 24, 141, 45, 45, 66, 6, 10, 14, 46, 18, 22, 26, 13, 17, 21, 25, 12, 44, 44, 44, 76, 44, 44, 65, 5, 9, 13, 17, 21, 25, 12, 44, 44, 76, 44, 16, 20, 24, 141, 77, 45, 66, 6, 10, 14, 67, 7, 11, 15, 19, 23, 27, 14, 46, 67, 7, 11, 15, 79, 47, 47, 79, 79, 79, 47, 79, 47, 47, 47, 19, 23, 27, 14, 46, 46, 46, 46, 46, 78, 46, 67, 7, 11, 15, 47, 79, 79, 79, 19, 23, 27, 14, 46, 78, 46, 67, 7, 11, 15, 47, 47, 19, 23, 27, 14, 46, 67, 7, 11, 15, 19, 23, 27, 14, 46, 46, 67, 7, 11, 15, 79, 47, 47, 47, 47, 19, 23, 27, 14, 46, 46, 18, 22, 26, 13, 45, 66, 6, 10, 14, 46, 46, 78, 46, 18, 22, 26, 13, 17, 21, 25, 12, 44, 44, 44, 76, 44, 16, 20, 24, 141, 45, 66, 6, 10, 14, 46, 46, 78, 46, 46, 46, 46, 46, 46, 18, 22, 26, 13, 66, 6, 10, 14, 67, 7, 11, 15, 19, 23, 27, 14, 78, 67, 7, 11, 15, 47, 47, 47, 47, 47, 47, 47, 47, 47, 79, 79, 47, 47, 19, 23, 27, 14, 46, 78, 46, 18, 22, 26, 13, 66, 6, 10, 14, 46, 67, 7, 11, 15, 47, 19, 23, 27, 14, 46, 46, 46, 46, 67, 7, 11, 15, 47, 47, 47, 19, 23, 27, 14, 46, 46, 46, 46, 67, 7, 11, 15, 79, 47, 79, 47, 47, 47, 47, 79, 47, 47, 47, 79, 79, 47, 47, 19, 23, 27, 14, 46, 18, 22, 26, 13, 45, 77, 45, 45, 45, 45, 77, 77, 45, 17, 21, 25, 12, 16, 20, 24, 141, 45, 45, 45, 45, 66, 6, 10, 14, 18, 22, 26, 13, 45, 45, 45, 45, 66, 6, 10, 14, 46, 78, 46, 78, 46, 78, 67, 7, 11, 15, 47, 19, 23, 27, 14, 46, 67, 7, 11, 15, 19, 23, 27, 14, 18, 22, 26, 13, 45, 45, 17, 21, 25, 12, 16, 20, 24};
|
||||
static const uint8_t RANDOM_GROWTH_NO_SPREAD[] = {44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 16, 20, 24, 0, 4, 8, 12, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 16, 20, 24, 0, 4, 8, 12, 16, 20, 24, 0, 4, 8, 12, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 16, 20, 24, 0, 4, 8, 12, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 16, 20, 24, 0, 4, 8, 12, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 16, 20, 24, 0, 4, 8, 12, 44};
|
||||
|
||||
/**
|
||||
* Tests if a tile can be converted to MP_TREES
|
||||
* This is true for clear ground without farms or rocks.
|
||||
|
@ -134,21 +138,44 @@ static void PlantTreesOnTile(TileIndex tile, TreeType treetype, uint count, Tree
|
|||
static TreeType GetRandomTreeType(TileIndex tile, uint seed)
|
||||
{
|
||||
switch (_settings_game.game_creation.landscape) {
|
||||
case LT_TEMPERATE:
|
||||
return static_cast<TreeType>(seed * TREE_COUNT_TEMPERATE / 256 + TREE_TEMPERATE);
|
||||
|
||||
case LT_ARCTIC:
|
||||
return static_cast<TreeType>(seed * TREE_COUNT_SUB_ARCTIC / 256 + TREE_SUB_ARCTIC);
|
||||
|
||||
case LT_TEMPERATE: return TREE_RANDOM_TEMPERATE;
|
||||
case LT_ARCTIC: return TREE_RANDOM_ARCTIC;
|
||||
case LT_TROPIC:
|
||||
switch (GetTropicZone(tile)) {
|
||||
case TROPICZONE_NORMAL: return static_cast<TreeType>(seed * TREE_COUNT_SUB_TROPICAL / 256 + TREE_SUB_TROPICAL);
|
||||
case TROPICZONE_DESERT: return static_cast<TreeType>((seed > 12) ? TREE_INVALID : TREE_CACTUS);
|
||||
default: return static_cast<TreeType>(seed * TREE_COUNT_RAINFOREST / 256 + TREE_RAINFOREST);
|
||||
case TROPICZONE_NORMAL: return TREE_RANDOM_TROPIC_NORMAL;
|
||||
case TROPICZONE_DESERT: return ((seed > 12) ? TREE_INVALID : TREE_CACTUS);
|
||||
case TROPICZONE_RAINFOREST: return TREE_RANDOM_TROPIC_RAINFOREST;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
case LT_TOYLAND: return TREE_RANDOM_TOYLAND;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a TreeType for the given tile. Random procedural tree types are coverted to
|
||||
* specific ones based on the tile hash.
|
||||
*
|
||||
* @param tile The tile to get a TreeType from
|
||||
* @param tile_hash The hash of this tile, used as source of randomness
|
||||
* @return The tree type
|
||||
*/
|
||||
static TreeType GetProceduralTreeType(TileIndex tile, uint32_t tile_hash)
|
||||
{
|
||||
TreeType type = GetTreeType(tile);
|
||||
switch (type) {
|
||||
case TREE_RANDOM_TEMPERATE:
|
||||
return static_cast<TreeType>(tile_hash % TREE_COUNT_TEMPERATE + TREE_TEMPERATE);
|
||||
case TREE_RANDOM_ARCTIC:
|
||||
return static_cast<TreeType>(tile_hash % TREE_COUNT_SUB_ARCTIC + TREE_SUB_ARCTIC);
|
||||
case TREE_RANDOM_TROPIC_NORMAL:
|
||||
return static_cast<TreeType>(tile_hash % TREE_COUNT_SUB_TROPICAL + TREE_SUB_TROPICAL);
|
||||
case TREE_RANDOM_TROPIC_RAINFOREST:
|
||||
return static_cast<TreeType>(tile_hash % TREE_COUNT_RAINFOREST + TREE_RAINFOREST);
|
||||
case TREE_RANDOM_TOYLAND:
|
||||
return static_cast<TreeType>(tile_hash % TREE_COUNT_TOYLAND + TREE_TOYLAND);
|
||||
default:
|
||||
return static_cast<TreeType>(seed * TREE_COUNT_TOYLAND / 256 + TREE_TOYLAND);
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,7 +193,7 @@ static void PlaceTree(TileIndex tile, uint32_t r)
|
|||
TreeType tree = GetRandomTreeType(tile, GB(r, 24, 8));
|
||||
|
||||
if (tree != TREE_INVALID) {
|
||||
PlantTreesOnTile(tile, tree, GB(r, 22, 2), static_cast<TreeGrowthStage>(std::min<uint8_t>(GB(r, 16, 3), 6)));
|
||||
PlantTreesOnTile(tile, tree, 0, TreeGrowthStage::Procedural);
|
||||
MarkTileDirtyByTile(tile);
|
||||
|
||||
/* Rerandomize ground, if neither snow nor shore */
|
||||
|
@ -515,6 +542,34 @@ struct TreeListEnt : PalSpriteID {
|
|||
uint8_t x, y;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines whether new trees can be planted on the tile or around it according to game settings.
|
||||
* @param tile The tile to check.
|
||||
* @return True iff new trees can be planted.
|
||||
*/
|
||||
static bool CanPlantExtraTrees(TileIndex tile)
|
||||
{
|
||||
if (_settings_game.game_creation.landscape == LT_TROPIC && GetTropicZone(tile) == TROPICZONE_RAINFOREST) {
|
||||
return (_settings_game.construction.extra_tree_placement == ETP_SPREAD_ALL || _settings_game.construction.extra_tree_placement == ETP_SPREAD_RAINFOREST);
|
||||
}
|
||||
return _settings_game.construction.extra_tree_placement == ETP_SPREAD_ALL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Picks predefined tree growth state based on input parameters.
|
||||
* @param tile_hash The hash of the tile.
|
||||
* @param can_plant_extra Whether new trees can be planted (result of CanPlantExtraTrees call).
|
||||
* @param counter Time counter.
|
||||
* @return tree growth state (bits 0..1 - tree count, 2..4 - growth stage).
|
||||
*/
|
||||
static uint8_t PickRandomGrowth(uint32_t tile_hash, bool can_plant_extra, uint32_t counter)
|
||||
{
|
||||
if (can_plant_extra) return RANDOM_GROWTH_SPREAD[(tile_hash + counter) % lengthof(RANDOM_GROWTH_SPREAD)];
|
||||
uint8_t value = RANDOM_GROWTH_NO_SPREAD[(tile_hash + counter) % lengthof(RANDOM_GROWTH_NO_SPREAD)];
|
||||
/* When trees don't spread their count doesn't change so just use a random one. */
|
||||
return value | (tile_hash % 4);
|
||||
}
|
||||
|
||||
static void DrawTile_Trees(TileInfo *ti)
|
||||
{
|
||||
switch (GetTreeGround(ti->tile)) {
|
||||
|
@ -528,11 +583,14 @@ static void DrawTile_Trees(TileInfo *ti)
|
|||
if (IsInvisibilitySet(TO_TREES)) return;
|
||||
|
||||
uint tmp = CountBits(ti->tile.base() + ti->x + ti->y);
|
||||
uint index = GB(tmp, 0, 2) + (GetTreeType(ti->tile) << 2);
|
||||
uint32_t tile_hash = SimpleHash32(ti->tile.base());
|
||||
auto tt = GetProceduralTreeType(ti->tile, tile_hash);
|
||||
uint index = GB(tmp, 0, 2) + (tt << 2);
|
||||
uint density = GetTreeDensity(ti->tile);
|
||||
|
||||
/* different tree styles above one of the grounds */
|
||||
if ((GetTreeGround(ti->tile) == TREE_GROUND_SNOW_DESERT || GetTreeGround(ti->tile) == TREE_GROUND_ROUGH_SNOW) &&
|
||||
GetTreeDensity(ti->tile) >= 2 &&
|
||||
density >= 2 &&
|
||||
IsInsideMM(index, TREE_SUB_ARCTIC << 2, TREE_RAINFOREST << 2)) {
|
||||
index += 164 - (TREE_SUB_ARCTIC << 2);
|
||||
}
|
||||
|
@ -549,9 +607,25 @@ static void DrawTile_Trees(TileInfo *ti)
|
|||
|
||||
/* put the trees to draw in a list */
|
||||
uint trees = GetTreeCount(ti->tile);
|
||||
TreeGrowthStage growth = GetTreeGrowth(ti->tile);
|
||||
if (growth == TreeGrowthStage::Procedural) {
|
||||
uint64_t counter = 0;
|
||||
|
||||
/* Use a fixed value for the counter (0) when trees aren't growing. */
|
||||
if (_settings_game.construction.extra_tree_placement != ETP_NO_GROWTH_NO_SPREAD) {
|
||||
counter = TimerGameTick::counter >> 12;
|
||||
|
||||
/* For procedural growth counter parity is stored instead of tree count. */
|
||||
if (counter % 2 != trees - 1) counter++;
|
||||
}
|
||||
|
||||
auto proc_value = PickRandomGrowth(tile_hash, CanPlantExtraTrees(ti->tile), counter);
|
||||
growth = static_cast<TreeGrowthStage>(GB(proc_value, 2, 3));
|
||||
trees = GB(proc_value, 0, 2) + 1;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < trees; i++) {
|
||||
SpriteID sprite = s[0].sprite + (i == trees - 1 ? static_cast<uint>(GetTreeGrowth(ti->tile)) : 3);
|
||||
SpriteID sprite = s[0].sprite + (i == trees - 1 ? static_cast<uint>(growth) : 3);
|
||||
PaletteID pal = s[0].pal;
|
||||
|
||||
te[i].sprite = sprite;
|
||||
|
@ -608,7 +682,8 @@ static CommandCost ClearTile_Trees(TileIndex tile, DoCommandFlag flags)
|
|||
}
|
||||
|
||||
num = GetTreeCount(tile);
|
||||
if (IsInsideMM(GetTreeType(tile), TREE_RAINFOREST, TREE_CACTUS)) num *= 4;
|
||||
auto tt = GetProceduralTreeType(tile, SimpleHash32(tile.base()));
|
||||
if (IsInsideMM(tt, TREE_RAINFOREST, TREE_CACTUS)) num *= 4;
|
||||
|
||||
if (flags & DC_EXEC) DoClearSquare(tile);
|
||||
|
||||
|
@ -617,7 +692,7 @@ static CommandCost ClearTile_Trees(TileIndex tile, DoCommandFlag flags)
|
|||
|
||||
static void GetTileDesc_Trees(TileIndex tile, TileDesc *td)
|
||||
{
|
||||
TreeType tt = GetTreeType(tile);
|
||||
TreeType tt = GetProceduralTreeType(tile, SimpleHash32(tile.base()));
|
||||
|
||||
if (IsInsideMM(tt, TREE_RAINFOREST, TREE_CACTUS)) {
|
||||
td->str = STR_LAI_TREE_NAME_RAINFOREST;
|
||||
|
@ -686,13 +761,6 @@ static void TileLoopTreesAlps(TileIndex tile)
|
|||
MarkTileDirtyByTile(tile);
|
||||
}
|
||||
|
||||
static bool CanPlantExtraTrees(TileIndex tile)
|
||||
{
|
||||
return ((_settings_game.game_creation.landscape == LT_TROPIC && GetTropicZone(tile) == TROPICZONE_RAINFOREST) ?
|
||||
(_settings_game.construction.extra_tree_placement == ETP_SPREAD_ALL || _settings_game.construction.extra_tree_placement == ETP_SPREAD_RAINFOREST) :
|
||||
_settings_game.construction.extra_tree_placement == ETP_SPREAD_ALL);
|
||||
}
|
||||
|
||||
static void TileLoop_Trees(TileIndex tile)
|
||||
{
|
||||
if (GetTreeGround(tile) == TREE_GROUND_SHORE) {
|
||||
|
@ -708,9 +776,9 @@ static void TileLoop_Trees(TileIndex tile)
|
|||
|
||||
/* TimerGameTick::counter is incremented by 256 between each call, so ignore lower 8 bits.
|
||||
* Also, we use a simple hash to spread the updates evenly over the map.
|
||||
* 11 and 9 are just some co-prime numbers for better spread.
|
||||
*/
|
||||
uint32_t cycle = 11 * TileX(tile) + 9 * TileY(tile) + (TimerGameTick::counter >> 8);
|
||||
uint32_t tile_hash = SimpleHash32(tile.base());
|
||||
uint32_t cycle = tile_hash + (TimerGameTick::counter >> 8);
|
||||
|
||||
/* Handle growth of grass (under trees/on MP_TREES tiles) at every 8th processings, like it's done for grass on MP_CLEAR tiles. */
|
||||
if ((cycle & 7) == 7 && GetTreeGround(tile) == TREE_GROUND_GRASS) {
|
||||
|
@ -726,7 +794,38 @@ static void TileLoop_Trees(TileIndex tile)
|
|||
static const uint32_t TREE_UPDATE_FREQUENCY = 16; // How many tile updates happen for one tree update
|
||||
if (cycle % TREE_UPDATE_FREQUENCY != TREE_UPDATE_FREQUENCY - 1) return;
|
||||
|
||||
switch (GetTreeGrowth(tile)) {
|
||||
uint64_t counter = (TimerGameTick::counter >> 12) + 1;
|
||||
bool can_plant_extra = CanPlantExtraTrees(tile);
|
||||
uint8_t value = PickRandomGrowth(tile_hash, can_plant_extra, counter);
|
||||
|
||||
TreeGrowthStage growth = GetTreeGrowth(tile);
|
||||
uint count = GetTreeCount(tile);
|
||||
TreeGrowthStage proc_growth = static_cast<TreeGrowthStage>(GB(value, 2, 3));
|
||||
uint proc_count = GB(value, 0, 2) + 1;
|
||||
bool spread = false;
|
||||
bool clear = false;
|
||||
|
||||
/* Check if the growth state matches procedural growth so we can switch to it. */
|
||||
if (growth == proc_growth && count == proc_count) {
|
||||
growth = TreeGrowthStage::Procedural;
|
||||
SetTreeGrowth(tile, growth);
|
||||
SetTreeCycle(tile, 0); // Tree cycle overwrites tree count
|
||||
}
|
||||
|
||||
if (growth == TreeGrowthStage::Procedural) {
|
||||
/* Store the parity of this cycle so that drawing routine can detect
|
||||
* whether the tile was alredy looped this cycle asd so chose appropriate state.
|
||||
*/
|
||||
SetTreeCycle(tile, counter % 2);
|
||||
|
||||
/* Don't mark dirty if trees didn't change. */
|
||||
if (HasBit(value, 5)) return;
|
||||
|
||||
spread = HasBit(value, 6);
|
||||
clear = HasBit(value, 7);
|
||||
} else {
|
||||
/* Custom growth */
|
||||
switch (growth) {
|
||||
case TreeGrowthStage::Grown: // regular sized tree
|
||||
if (_settings_game.game_creation.landscape == LT_TROPIC &&
|
||||
GetTreeType(tile) != TREE_CACTUS &&
|
||||
|
@ -739,28 +838,16 @@ static void TileLoop_Trees(TileIndex tile)
|
|||
break;
|
||||
|
||||
case 1: // add a tree
|
||||
if (GetTreeCount(tile) < 4 && CanPlantExtraTrees(tile)) {
|
||||
spread = can_plant_extra;
|
||||
if (count < 4 && spread) {
|
||||
AddTreeCount(tile, 1);
|
||||
SetTreeGrowth(tile, TreeGrowthStage::Growing1);
|
||||
break;
|
||||
spread = false;
|
||||
}
|
||||
[[fallthrough]];
|
||||
break;
|
||||
|
||||
case 2: { // add a neighbouring tree
|
||||
if (!CanPlantExtraTrees(tile)) break;
|
||||
|
||||
TreeType treetype = GetTreeType(tile);
|
||||
|
||||
tile += TileOffsByDir(static_cast<Direction>(Random() % DIR_END));
|
||||
|
||||
/* Cacti don't spread */
|
||||
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, treetype, 0, TreeGrowthStage::Growing1);
|
||||
|
||||
spread = can_plant_extra;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -771,15 +858,27 @@ static void TileLoop_Trees(TileIndex tile)
|
|||
break;
|
||||
|
||||
case TreeGrowthStage::Dead: // final stage of tree destruction
|
||||
if (!CanPlantExtraTrees(tile)) {
|
||||
if (!can_plant_extra) {
|
||||
/* if trees can't spread just plant a new one to prevent deforestation */
|
||||
SetTreeGrowth(tile, TreeGrowthStage::Growing1);
|
||||
} else if (GetTreeCount(tile) > 1) {
|
||||
} else if (count > 1) {
|
||||
/* more than one tree, delete it */
|
||||
AddTreeCount(tile, -1);
|
||||
SetTreeGrowth(tile, TreeGrowthStage::Grown);
|
||||
} else {
|
||||
/* just one tree, change type into MP_CLEAR */
|
||||
/* no more trees, mark the tile for clearing */
|
||||
clear = true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
AddTreeGrowth(tile, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (clear) {
|
||||
/* All trees died, change type into MP_CLEAR */
|
||||
switch (GetTreeGround(tile)) {
|
||||
case TREE_GROUND_SHORE: MakeShore(tile); break;
|
||||
case TREE_GROUND_GRASS: MakeClear(tile, CLEAR_GRASS, GetTreeDensity(tile)); break;
|
||||
|
@ -801,11 +900,20 @@ static void TileLoop_Trees(TileIndex tile)
|
|||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
AddTreeGrowth(tile, 1);
|
||||
break;
|
||||
if (spread) {
|
||||
TreeType treetype = GetProceduralTreeType(tile, tile_hash);
|
||||
|
||||
/* We never spread while changing the current tile so it's ok to overwrite and only mark the new tile dirty later. */
|
||||
tile += TileOffsByDir((Direction)(Random() % DIR_END));
|
||||
|
||||
/* Cacti don't spread */
|
||||
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, treetype, 0, TreeGrowthStage::Growing1);
|
||||
}
|
||||
|
||||
MarkTileDirtyByTile(tile);
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
* offsets from the grfs files. These points to the start of
|
||||
* the tree list for a landscape. See the TREE_COUNT_* enumerations
|
||||
* for the amount of different trees for a specific landscape.
|
||||
* TREE_RANDOM_* are special values used in the map array that signify that
|
||||
* exact tree type is not stored and should be determined procedurally.
|
||||
*/
|
||||
enum TreeType {
|
||||
TREE_TEMPERATE = 0x00, ///< temperate tree
|
||||
|
@ -29,7 +31,14 @@ enum TreeType {
|
|||
TREE_CACTUS = 0x1B, ///< a cactus for the 'desert part' on a sub-tropical map
|
||||
TREE_SUB_TROPICAL = 0x1C, ///< tree on a sub-tropical map, non-rainforest, non-desert
|
||||
TREE_TOYLAND = 0x20, ///< tree on a toyland map
|
||||
TREE_RANDOM_TEMPERATE = 0xFA, ///< procedural TREE_TEMPERATE
|
||||
TREE_RANDOM_BEGIN = TREE_RANDOM_TEMPERATE,
|
||||
TREE_RANDOM_ARCTIC = 0xFB, ///< procedural TREE_SUB_ARCTIC
|
||||
TREE_RANDOM_TROPIC_NORMAL = 0xFC, ///< procedural tropic tree
|
||||
TREE_RANDOM_TROPIC_RAINFOREST = 0xFD, ///< procedural TREE_RAINFOREST
|
||||
TREE_RANDOM_TOYLAND = 0xFE, ///< procedural TREE_TOYLAND
|
||||
TREE_INVALID = 0xFF, ///< An invalid tree
|
||||
TREE_RANDOM_END = TREE_INVALID,
|
||||
};
|
||||
|
||||
/* Counts the number of tree types for each landscape.
|
||||
|
@ -70,6 +79,7 @@ enum class TreeGrowthStage : uint {
|
|||
Dying1 = 4, ///< First stage of dying
|
||||
Dying2 = 5, ///< Second stage of dying
|
||||
Dead = 6, ///< Dead tree
|
||||
Procedural = 7, ///< Magic value that signifies that tree growth stage is determined procedurally, not stored in the map array.
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -183,6 +193,20 @@ inline void AddTreeCount(Tile t, int c)
|
|||
t.m5() += c << 6;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tree growth stage.
|
||||
* Sets the tree cycle parity (for procedural growth). Uses the same bits as tree counter.
|
||||
*
|
||||
* @param t The tile to set the value on
|
||||
* @param c The value to set
|
||||
* @pre Tile must be of type MP_TREES
|
||||
*/
|
||||
static inline void SetTreeCycle(Tile t, int c)
|
||||
{
|
||||
assert(IsTileType(t, MP_TREES));
|
||||
SB(t.m5(), 6, 2, c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tree growth stage.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue