tron@2186: /* $Id$ */ tron@2186: rubidium@9111: /** @file tree_cmd.cpp Handling of tree tiles. */ belugas@6422: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" celestar@5385: #include "bridge_map.h" tron@3144: #include "clear_map.h" rubidium@8119: #include "tile_cmd.h" maedhros@6343: #include "landscape.h" tron@3144: #include "tree_map.h" rubidium@8225: #include "viewport_func.h" rubidium@8116: #include "command_func.h" rubidium@8116: #include "economy_func.h" truelight@0: #include "town.h" tron@2153: #include "variables.h" truelight@4300: #include "genworld.h" belugas@7849: #include "transparency.h" rubidium@8131: #include "functions.h" rubidium@10208: #include "company_func.h" rubidium@8157: #include "sound_func.h" rubidium@8270: #include "settings_type.h" frosch@8459: #include "water_map.h" frosch@8459: #include "water.h" rubidium@9126: #include "landscape_type.h" rubidium@10208: #include "company_base.h" truelight@4300: rubidium@8264: #include "table/strings.h" rubidium@8264: #include "table/sprites.h" rubidium@8264: #include "table/tree_land.h" rubidium@8264: rubidium@7597: /** rubidium@7597: * List of tree placer algorithm. rubidium@7597: * rubidium@7597: * This enumeration defines all possible tree placer algorithm in the game. rubidium@7597: */ truelight@4300: enum TreePlacer { rubidium@7597: TP_NONE, ///< No tree placer algorithm rubidium@7597: TP_ORIGINAL, ///< The original algorithm rubidium@7597: TP_IMPROVED, ///< A 'improved' algorithm truelight@4300: }; truelight@0: rubidium@7597: /** frosch@8454: * Tests if a tile can be converted to MP_TREES frosch@8454: * This is true for clear ground without farms or rocks. frosch@8454: * frosch@8454: * @param tile the tile of interest frosch@8454: * @param allow_desert Allow planting trees on CLEAR_DESERT? frosch@8454: * @return true if trees can be built. frosch@8454: */ frosch@8459: static bool CanPlantTreesOnTile(TileIndex tile, bool allow_desert) frosch@8454: { frosch@8459: switch (GetTileType(tile)) { frosch@8459: case MP_WATER: frosch@8459: return !IsBridgeAbove(tile) && IsCoast(tile) && !IsSlopeWithOneCornerRaised(GetTileSlope(tile, NULL)); frosch@8459: frosch@8459: case MP_CLEAR: frosch@8459: return !IsBridgeAbove(tile) && !IsClearGround(tile, CLEAR_FIELDS) && !IsClearGround(tile, CLEAR_ROCKS) && frosch@8459: (allow_desert || !IsClearGround(tile, CLEAR_DESERT)); frosch@8459: frosch@8459: default: return false; frosch@8459: } frosch@8454: } frosch@8454: frosch@8454: /** frosch@8454: * Creates a tree tile frosch@8454: * Ground type and density is preserved. frosch@8454: * frosch@8454: * @pre the tile must be suitable for trees. frosch@8454: * frosch@8454: * @param tile where to plant the trees. frosch@8454: * @param type The type of the tree frosch@8454: * @param count the number of trees (minus 1) frosch@8454: * @param growth the growth status frosch@8454: */ frosch@8454: static void PlantTreesOnTile(TileIndex tile, TreeType treetype, uint count, uint growth) frosch@8454: { frosch@8454: assert(treetype != TREE_INVALID); frosch@8454: assert(CanPlantTreesOnTile(tile, true)); frosch@8454: frosch@8454: TreeGround ground; frosch@8454: uint density = 3; frosch@8459: frosch@8459: switch (GetTileType(tile)) { frosch@8459: case MP_WATER: frosch@8459: ground = TREE_GROUND_SHORE; frosch@8459: break; frosch@8459: frosch@8459: case MP_CLEAR: frosch@8459: switch (GetClearGround(tile)) { frosch@8459: case CLEAR_GRASS: ground = TREE_GROUND_GRASS; density = GetClearDensity(tile); break; frosch@8459: case CLEAR_ROUGH: ground = TREE_GROUND_ROUGH; break; frosch@8459: default: ground = TREE_GROUND_SNOW_DESERT; density = GetClearDensity(tile); break; frosch@8459: } frosch@8459: break; frosch@8459: frosch@8459: default: NOT_REACHED(); frosch@8454: } frosch@8454: frosch@8454: MakeTree(tile, treetype, count, growth, ground, density); frosch@8454: } frosch@8454: frosch@8454: /** rubidium@7597: * Get a random TreeType for the given tile based on a given seed rubidium@7597: * rubidium@7597: * This function returns a random TreeType which can be placed on the given tile. rubidium@7597: * The seed for randomness must be less or equal 256, use #GB on the value of Random() rubidium@7597: * to get such a value. rubidium@7597: * rubidium@7597: * @param tile The tile to get a random TreeType from rubidium@7597: * @param seed The seed for randomness, must be less or equal 256 rubidium@7597: * @return The random tree type rubidium@7597: */ tron@2981: static TreeType GetRandomTreeType(TileIndex tile, uint seed) truelight@0: { rubidium@9413: switch (_settings_game.game_creation.landscape) { belugas@6357: case LT_TEMPERATE: rubidium@5587: return (TreeType)(seed * TREE_COUNT_TEMPERATE / 256 + TREE_TEMPERATE); truelight@0: belugas@6357: case LT_ARCTIC: rubidium@5587: return (TreeType)(seed * TREE_COUNT_SUB_ARCTIC / 256 + TREE_SUB_ARCTIC); tron@1286: belugas@6357: case LT_TROPIC: belugas@3379: switch (GetTropicZone(tile)) { frosch@8450: case TROPICZONE_NORMAL: return (TreeType)(seed * TREE_COUNT_SUB_TROPICAL / 256 + TREE_SUB_TROPICAL); rubidium@5587: case TROPICZONE_DESERT: return (TreeType)((seed > 12) ? TREE_INVALID : TREE_CACTUS); rubidium@5587: default: return (TreeType)(seed * TREE_COUNT_RAINFOREST / 256 + TREE_RAINFOREST); tron@1286: } tron@1286: tron@1286: default: rubidium@5587: return (TreeType)(seed * TREE_COUNT_TOYLAND / 256 + TREE_TOYLAND); truelight@0: } truelight@0: } truelight@0: rubidium@7597: /** rubidium@7597: * Make a random tree tile of the given tile rubidium@7597: * rubidium@7597: * Create a new tree-tile for the given tile. The second parameter is used for rubidium@7597: * randomness like type and number of trees. rubidium@7597: * rubidium@7597: * @param tile The tile to make a tree-tile from rubidium@7597: * @param r The randomness value from a Random() value rubidium@7597: */ tron@2958: static void PlaceTree(TileIndex tile, uint32 r) truelight@0: { tron@2981: TreeType tree = GetRandomTreeType(tile, GB(r, 24, 8)); tron@2088: tron@3441: if (tree != TREE_INVALID) { frosch@8454: PlantTreesOnTile(tile, tree, GB(r, 22, 2), min(GB(r, 16, 3), 6)); truelight@0: frosch@8459: /* Rerandomize ground, if neither snow nor shore */ frosch@8459: TreeGround ground = GetTreeGround(tile); frosch@8459: if (ground != TREE_GROUND_SNOW_DESERT && ground != TREE_GROUND_SHORE) { truelight@7710: SetTreeGroundDensity(tile, (TreeGround)GB(r, 28, 1), 3); truelight@0: } frosch@8454: frosch@8454: /* Set the counter to a random start value */ frosch@8454: SetTreeCounter(tile, (TreeGround)GB(r, 24, 4)); truelight@0: } truelight@0: } truelight@0: rubidium@7597: /** rubidium@7597: * Place some amount of trees around a given tile. rubidium@7597: * rubidium@7597: * This function adds some trees around a given tile. As this function use rubidium@7597: * the Random() call it depends on the random how many trees are actually placed rubidium@7597: * around the given tile. rubidium@7597: * rubidium@7597: * @param tile The center of the trees to add rubidium@7597: */ tron@1977: static void DoPlaceMoreTrees(TileIndex tile) truelight@0: { tron@2088: uint i; truelight@0: tron@2088: for (i = 0; i < 1000; i++) { truelight@0: uint32 r = Random(); tron@2088: int x = GB(r, 0, 5) - 16; tron@2088: int y = GB(r, 8, 5) - 16; skidd13@7923: uint dist = abs(x) + abs(y); tron@2088: TileIndex cur_tile = TILE_MASK(tile + TileDiffXY(x, y)); truelight@0: frosch@8454: if (dist <= 13 && CanPlantTreesOnTile(cur_tile, true)) { tron@2958: PlaceTree(cur_tile, r); truelight@0: } tron@2088: } truelight@0: } truelight@0: rubidium@7597: /** rubidium@7597: * Place more trees on the map. rubidium@7597: * rubidium@7597: * This function add more trees to the map. rubidium@7597: */ rubidium@6247: static void PlaceMoreTrees() truelight@0: { tron@2243: uint i = ScaleByMapSize(GB(Random(), 0, 5) + 25); terom@10438: truelight@0: do { ludde@2051: DoPlaceMoreTrees(RandomTile()); terom@10438: } while (i && --i); truelight@0: } truelight@0: truelight@4300: /** truelight@4300: * Place a tree at the same height as an existing tree. rubidium@7597: * rubidium@7597: * Add a new tree around the given tile which is at the same rubidium@7597: * height or at some offset (2 units) of it. rubidium@7597: * rubidium@7597: * @param tile The base tile to add a new tree somewhere around rubidium@7597: * @param height The height (like the one from the tile) truelight@4300: */ frosch@8454: static void PlaceTreeAtSameHeight(TileIndex tile, uint height) truelight@4300: { truelight@4300: uint i; truelight@4300: truelight@4300: for (i = 0; i < 1000; i++) { truelight@4300: uint32 r = Random(); truelight@4300: int x = GB(r, 0, 5) - 16; truelight@4300: int y = GB(r, 8, 5) - 16; truelight@4300: TileIndex cur_tile = TILE_MASK(tile + TileDiffXY(x, y)); truelight@4300: truelight@4300: /* Keep in range of the existing tree */ skidd13@7923: if (abs(x) + abs(y) > 16) continue; truelight@4300: truelight@4300: /* Clear tile, no farm-tiles or rocks */ frosch@8454: if (!CanPlantTreesOnTile(cur_tile, true)) continue; truelight@4300: truelight@4300: /* Not too much height difference */ skidd13@7970: if (Delta(GetTileZ(cur_tile), height) > 2) continue; truelight@4300: truelight@4300: /* Place one tree and quit */ truelight@4300: PlaceTree(cur_tile, r); truelight@4300: break; truelight@4300: } truelight@4300: } truelight@4300: rubidium@7597: /** rubidium@7597: * Place some trees randomly rubidium@7597: * rubidium@7597: * This function just place some trees randomly on the map. rubidium@7597: */ rubidium@6247: void PlaceTreesRandomly() truelight@0: { truelight@4300: uint i, j, ht; truelight@0: tron@1202: i = ScaleByMapSize(1000); truelight@0: do { tron@2088: uint32 r = Random(); tron@2088: TileIndex tile = RandomTileSeed(r); truelight@4300: truelight@4300: IncreaseGeneratingWorldProgress(GWP_TREE); truelight@4300: frosch@8454: if (CanPlantTreesOnTile(tile, true)) { tron@2958: PlaceTree(tile, r); rubidium@9413: if (_settings_game.game_creation.tree_placer != TP_IMPROVED) continue; truelight@4300: truelight@4300: /* Place a number of trees based on the tile height. truelight@4300: * This gives a cool effect of multiple trees close together. truelight@4300: * It is almost real life ;) */ truelight@4300: ht = GetTileZ(tile); truelight@4300: /* The higher we get, the more trees we plant */ truelight@4300: j = GetTileZ(tile) / TILE_HEIGHT * 2; truelight@4300: while (j--) { truelight@4300: /* Above snowline more trees! */ rubidium@9413: if (_settings_game.game_creation.landscape == LT_ARCTIC && ht > GetSnowLine()) { truelight@4300: PlaceTreeAtSameHeight(tile, ht); truelight@4300: PlaceTreeAtSameHeight(tile, ht); truelight@4300: }; truelight@4300: truelight@4300: PlaceTreeAtSameHeight(tile, ht); truelight@4300: } truelight@0: } terom@10438: } while (i && --i); truelight@0: truelight@0: /* place extra trees at rainforest area */ rubidium@9413: if (_settings_game.game_creation.landscape == LT_TROPIC) { tron@1202: i = ScaleByMapSize(15000); truelight@0: truelight@0: do { tron@2088: uint32 r = Random(); tron@2088: TileIndex tile = RandomTileSeed(r); truelight@4300: truelight@4300: IncreaseGeneratingWorldProgress(GWP_TREE); truelight@4300: frosch@8454: if (GetTropicZone(tile) == TROPICZONE_RAINFOREST && CanPlantTreesOnTile(tile, false)) { tron@2958: PlaceTree(tile, r); truelight@0: } terom@10438: } while (i && --i); truelight@0: } truelight@0: } truelight@0: rubidium@7597: /** rubidium@7597: * Place new trees. rubidium@7597: * rubidium@7597: * This function takes care of the selected tree placer algorithm and rubidium@7597: * place randomly the trees for a new game. rubidium@7597: */ rubidium@6247: void GenerateTrees() truelight@0: { truelight@4300: uint i, total; truelight@4300: rubidium@9413: if (_settings_game.game_creation.tree_placer == TP_NONE) return; truelight@0: rubidium@9413: if (_settings_game.game_creation.landscape != LT_TOYLAND) PlaceMoreTrees(); tron@2088: rubidium@9413: switch (_settings_game.game_creation.tree_placer) { rubidium@9413: case TP_ORIGINAL: i = _settings_game.game_creation.landscape == LT_ARCTIC ? 15 : 6; break; rubidium@9413: case TP_IMPROVED: i = _settings_game.game_creation.landscape == LT_ARCTIC ? 4 : 2; break; truelight@4300: default: NOT_REACHED(); return; truelight@4300: } truelight@4300: truelight@4300: total = ScaleByMapSize(1000); rubidium@9413: if (_settings_game.game_creation.landscape == LT_TROPIC) total += ScaleByMapSize(15000); truelight@4300: total *= i; truelight@4300: SetGeneratingWorldProgress(GWP_TREE, total); truelight@4300: truelight@4300: for (; i != 0; i--) { tron@2088: PlaceTreesRandomly(); truelight@0: } truelight@0: } truelight@0: Darkvater@1784: /** Plant a tree. tron@3491: * @param tile start tile of area-drag of tree plantation belugas@6422: * @param flags type of operation Darkvater@1784: * @param p1 tree type, -1 means random. Darkvater@1784: * @param p2 end tile of area-drag truelight@0: */ rubidium@6943: CommandCost CmdPlantTree(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { tron@3183: StringID msg = INVALID_STRING_ID; rubidium@8230: CommandCost cost(EXPENSES_OTHER); tron@3491: int ex; tron@3491: int ey; Darkvater@2118: int sx, sy, x, y; tron@1286: Darkvater@2892: if (p2 >= MapSize()) return CMD_ERROR; Darkvater@1784: /* Check the tree type. It can be random or some valid value within the current climate */ rubidium@10236: if (p1 != UINT_MAX && p1 - _tree_base_by_landscape[_settings_game.game_creation.landscape] >= _tree_count_by_landscape[_settings_game.game_creation.landscape]) return CMD_ERROR; truelight@193: truelight@0: // make sure sx,sy are smaller than ex,ey tron@3491: ex = TileX(tile); tron@3491: ey = TileY(tile); Darkvater@2118: sx = TileX(p2); Darkvater@2118: sy = TileY(p2); tron@6106: if (ex < sx) Swap(ex, sx); tron@6106: if (ey < sy) Swap(ey, sy); truelight@193: Darkvater@2118: for (x = sx; x <= ex; x++) { Darkvater@2118: for (y = sy; y <= ey; y++) { tron@2088: TileIndex tile = TileXY(x, y); tron@1286: tron@2088: switch (GetTileType(tile)) { tron@1286: case MP_TREES: belugas@6422: /* no more space for trees? */ smatz@10363: if (_game_mode != GM_EDITOR && GetTreeCount(tile) == 4) { tron@3183: msg = STR_2803_TREE_ALREADY_HERE; tron@1286: continue; truelight@0: } truelight@0: tron@1286: if (flags & DC_EXEC) { tron@2981: AddTreeCount(tile, 1); tron@2088: MarkTileDirtyByTile(tile); tron@1286: } belugas@6422: /* 2x as expensive to add more trees to an existing tile */ rubidium@6950: cost.AddCost(_price.build_trees * 2); tron@1286: break; tron@1286: rubidium@7509: case MP_WATER: frosch@8459: if (!IsCoast(tile) || IsSlopeWithOneCornerRaised(GetTileSlope(tile, NULL))) { frosch@8459: msg = STR_3807_CAN_T_BUILD_ON_WATER; frosch@8459: continue; frosch@8459: } frosch@8459: /* FALL THROUGH */ tron@1286: case MP_CLEAR: frosch@8454: if (IsBridgeAbove(tile)) { tron@3183: msg = STR_2804_SITE_UNSUITABLE; tron@1286: continue; truelight@0: } truelight@193: frosch@8459: if (IsTileType(tile, MP_CLEAR)) { frosch@8459: /* Remove fields or rocks. Note that the ground will get barrened */ frosch@8459: switch (GetClearGround(tile)) { frosch@8459: case CLEAR_FIELDS: frosch@8459: case CLEAR_ROCKS: { frosch@8459: CommandCost ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); frosch@8459: if (CmdFailed(ret)) return ret; frosch@8459: cost.AddCost(ret); frosch@8459: break; frosch@8459: } frosch@8459: frosch@8459: default: break; frosch@8454: } tron@2955: } truelight@0: rubidium@10207: if (_game_mode != GM_EDITOR && IsValidCompanyID(_current_company)) { rubidium@9413: Town *t = ClosestTownFromTile(tile, _settings_game.economy.dist_local_authority); glx@8232: if (t != NULL) ChangeTownRating(t, RATING_TREE_UP_STEP, RATING_TREE_MAXIMUM); glx@8232: } glx@8232: tron@1286: if (flags & DC_EXEC) { tron@2981: TreeType treetype; tron@1286: rubidium@5587: treetype = (TreeType)p1; tron@3441: if (treetype == TREE_INVALID) { tron@2981: treetype = GetRandomTreeType(tile, GB(Random(), 24, 8)); tron@3441: if (treetype == TREE_INVALID) treetype = TREE_CACTUS; tron@1286: } tron@1286: frosch@8454: /* Plant full grown trees in scenario editor */ frosch@8454: PlantTreesOnTile(tile, treetype, 0, _game_mode == GM_EDITOR ? 3 : 0); tron@3003: MarkTileDirtyByTile(tile); tron@1286: frosch@8454: /* When planting rainforest-trees, set tropiczone to rainforest in editor. */ skidd13@7954: if (_game_mode == GM_EDITOR && IsInsideMM(treetype, TREE_RAINFOREST, TREE_CACTUS)) belugas@3379: SetTropicZone(tile, TROPICZONE_RAINFOREST); truelight@0: } rubidium@6950: cost.AddCost(_price.build_trees); tron@1286: break; tron@1286: tron@1286: default: tron@3183: msg = STR_2804_SITE_UNSUITABLE; tron@1286: break; truelight@0: } truelight@0: } truelight@0: } truelight@193: rubidium@6950: if (cost.GetCost() == 0) { tron@3183: return_cmd_error(msg); tron@3183: } else { tron@3183: return cost; tron@3183: } truelight@0: } truelight@0: rubidium@6248: struct TreeListEnt { peter1138@5668: SpriteID image; peter1138@5668: SpriteID pal; rubidium@6491: byte x, y; rubidium@6248: }; truelight@0: truelight@0: static void DrawTile_Trees(TileInfo *ti) truelight@0: { tron@2981: switch (GetTreeGround(ti->tile)) { frosch@8459: case TREE_GROUND_SHORE: DrawShoreTile(ti->tileh); break; truelight@7710: case TREE_GROUND_GRASS: DrawClearLandTile(ti, GetTreeDensity(ti->tile)); break; tron@3441: case TREE_GROUND_ROUGH: DrawHillyLandTile(ti); break; peter1138@5668: default: DrawGroundSprite(_tree_sprites_1[GetTreeDensity(ti->tile)] + _tileh_to_sprite[ti->tileh], PAL_NONE); break; truelight@0: } truelight@0: tron@2220: DrawClearLandFence(ti); truelight@0: smatz@8498: /* Do not draw trees when the invisible trees patch and transparency tree are set */ smatz@8806: if (IsInvisibilitySet(TO_TREES)) return; smatz@8498: smatz@8499: uint16 tmp = ti->x; smatz@8499: smatz@8499: tmp = ROR(tmp, 2); smatz@8499: tmp -= ti->y; smatz@8499: tmp = ROR(tmp, 3); smatz@8499: tmp -= ti->x; smatz@8499: tmp = ROR(tmp, 1); smatz@8499: tmp += ti->y; smatz@8499: smatz@8499: uint index = GB(tmp, 6, 2) + (GetTreeType(ti->tile) << 2); smatz@8499: smatz@8499: /* different tree styles above one of the grounds */ smatz@8499: if (GetTreeGround(ti->tile) == TREE_GROUND_SNOW_DESERT && smatz@8499: GetTreeDensity(ti->tile) >= 2 && smatz@8499: IsInsideMM(index, TREE_SUB_ARCTIC << 2, TREE_RAINFOREST << 2)) { smatz@8499: index += 164 - (TREE_SUB_ARCTIC << 2); truelight@0: } truelight@0: smatz@8499: assert(index < lengthof(_tree_layout_sprite)); truelight@0: smatz@8499: const PalSpriteID *s = _tree_layout_sprite[index]; smatz@8499: const TreePos *d = _tree_layout_xy[GB(tmp, 4, 2)]; truelight@193: smatz@8499: /* combine trees into one sprite object */ truelight@0: StartSpriteCombine(); truelight@0: smatz@8498: TreeListEnt te[4]; truelight@0: smatz@8498: /* put the trees to draw in a list */ smatz@10363: uint trees = GetTreeCount(ti->tile); smatz@8499: smatz@8499: for (uint i = 0; i < trees; i++) { smatz@8499: SpriteID image = s[0].sprite + (i == trees - 1 ? GetTreeGrowth(ti->tile) : 3); smatz@8498: SpriteID pal = s[0].pal; smatz@8498: smatz@8498: te[i].image = image; smatz@8498: te[i].pal = pal; smatz@8498: te[i].x = d->x; smatz@8498: te[i].y = d->y; smatz@8498: s++; smatz@8498: d++; smatz@8499: } smatz@8498: smatz@8498: /* draw them in a sorted way */ smatz@8499: byte z = ti->z + GetSlopeMaxZ(ti->tileh) / 2; smatz@8498: smatz@8499: for (; trees > 0; trees--) { smatz@8499: uint min = te[0].x + te[0].y; smatz@8499: uint mi = 0; smatz@8499: smatz@8499: for (uint i = 1; i < trees; i++) { glx@8512: if ((uint)(te[i].x + te[i].y) < min) { smatz@8498: min = te[i].x + te[i].y; smatz@8499: mi = i; smatz@8498: } smatz@8499: } truelight@0: smatz@8499: AddSortableSpriteToDraw(te[mi].image, te[mi].pal, ti->x + te[mi].x, ti->y + te[mi].y, 16 - te[mi].x, 16 - te[mi].y, 0x30, z, IsTransparencySet(TO_TREES), -te[mi].x, -te[mi].y); truelight@0: smatz@8499: /* replace the removed one with the last one */ smatz@8499: te[mi] = te[trees - 1]; truelight@0: } truelight@0: truelight@0: EndSpriteCombine(); truelight@0: } truelight@0: truelight@0: tron@4231: static uint GetSlopeZ_Trees(TileIndex tile, uint x, uint y) tron@2537: { tron@4231: uint z; rubidium@5587: Slope tileh = GetTileSlope(tile, &z); tron@4231: tron@4231: return z + GetPartialZ(x & 0xF, y & 0xF, tileh); truelight@0: } truelight@0: rubidium@7335: static Foundation GetFoundation_Trees(TileIndex tile, Slope tileh) tron@2548: { rubidium@7335: return FOUNDATION_NONE; dominik@39: } dominik@39: rubidium@6943: static CommandCost ClearTile_Trees(TileIndex tile, byte flags) tron@1977: { tron@2243: uint num; truelight@0: rubidium@10207: if (IsValidCompanyID(_current_company)) { rubidium@9413: Town *t = ClosestTownFromTile(tile, _settings_game.economy.dist_local_authority); glx@8232: if (t != NULL) ChangeTownRating(t, RATING_TREE_DOWN_STEP, RATING_TREE_MINIMUM); truelight@0: } truelight@0: smatz@10363: num = GetTreeCount(tile); skidd13@7954: if (IsInsideMM(GetTreeType(tile), TREE_RAINFOREST, TREE_CACTUS)) num *= 4; truelight@0: tron@2243: if (flags & DC_EXEC) DoClearSquare(tile); truelight@0: rubidium@8230: return CommandCost(EXPENSES_CONSTRUCTION, num * _price.remove_trees); truelight@0: } truelight@0: tron@1977: static void GetAcceptedCargo_Trees(TileIndex tile, AcceptedCargo ac) truelight@0: { truelight@0: /* not used */ truelight@0: } truelight@0: tron@1977: static void GetTileDesc_Trees(TileIndex tile, TileDesc *td) truelight@0: { tron@2981: TreeType tt = GetTreeType(tile); tron@2981: skidd13@7954: if (IsInsideMM(tt, TREE_RAINFOREST, TREE_CACTUS)) { tron@2981: td->str = STR_280F_RAINFOREST; tron@2981: } else { belugas@7712: td->str = tt == TREE_CACTUS ? STR_2810_CACTUS_PLANTS : STR_280E_TREES; tron@2981: } truelight@0: frosch@9322: td->owner[0] = GetTileOwner(tile); truelight@0: } truelight@0: tron@1977: static void AnimateTile_Trees(TileIndex tile) truelight@0: { truelight@0: /* not used */ truelight@0: } truelight@0: tron@1977: static void TileLoopTreesDesert(TileIndex tile) truelight@0: { belugas@3379: switch (GetTropicZone(tile)) { belugas@3379: case TROPICZONE_DESERT: tron@3441: if (GetTreeGround(tile) != TREE_GROUND_SNOW_DESERT) { tron@3441: SetTreeGroundDensity(tile, TREE_GROUND_SNOW_DESERT, 3); tron@2981: MarkTileDirtyByTile(tile); tron@2981: } tron@2981: break; tron@2243: belugas@3379: case TROPICZONE_RAINFOREST: { tron@2981: static const SoundFx forest_sounds[] = { tron@2981: SND_42_LOON_BIRD, tron@2981: SND_43_LION, tron@2981: SND_44_MONKEYS, tron@2981: SND_48_DISTANT_BIRD tron@2981: }; tron@2981: uint32 r = Random(); truelight@0: skidd13@7967: if (Chance16I(1, 200, r)) SndPlayTileFx(forest_sounds[GB(r, 16, 2)], tile); tron@2981: break; truelight@0: } belugas@3379: belugas@3379: default: break; truelight@0: } truelight@0: } truelight@0: tron@1977: static void TileLoopTreesAlps(TileIndex tile) truelight@0: { maedhros@6343: int k = GetTileZ(tile) - GetSnowLine() + TILE_HEIGHT; truelight@0: tron@4157: if (k < 0) { tron@3441: if (GetTreeGround(tile) != TREE_GROUND_SNOW_DESERT) return; truelight@7710: SetTreeGroundDensity(tile, TREE_GROUND_GRASS, 3); truelight@0: } else { tron@4157: uint density = min((uint)k / TILE_HEIGHT, 3); tron@2981: tron@3441: if (GetTreeGround(tile) != TREE_GROUND_SNOW_DESERT || tron@3441: GetTreeDensity(tile) != density) { tron@3441: SetTreeGroundDensity(tile, TREE_GROUND_SNOW_DESERT, density); tron@2981: } else { tron@2981: if (GetTreeDensity(tile) == 3) { tron@2981: uint32 r = Random(); skidd13@7967: if (Chance16I(1, 200, r)) { tron@2981: SndPlayTileFx((r & 0x80000000) ? SND_39_HEAVY_WIND : SND_34_WIND, tile); tron@2981: } truelight@0: } truelight@0: return; truelight@0: } truelight@0: } truelight@0: MarkTileDirtyByTile(tile); truelight@0: } truelight@0: tron@1977: static void TileLoop_Trees(TileIndex tile) truelight@0: { frosch@8459: if (GetTreeGround(tile) == TREE_GROUND_SHORE) { frosch@8459: TileLoop_Water(tile); frosch@8459: } else { rubidium@9413: switch (_settings_game.game_creation.landscape) { frosch@8459: case LT_TROPIC: TileLoopTreesDesert(tile); break; frosch@8459: case LT_ARCTIC: TileLoopTreesAlps(tile); break; frosch@8459: } truelight@0: } truelight@0: truelight@0: TileLoopClearHelper(tile); truelight@0: truelight@7710: uint treeCounter = GetTreeCounter(tile); truelight@7710: truelight@7710: /* Handle growth of grass at every 8th processings, like it's done for grass */ truelight@7710: if ((treeCounter & 7) == 7 && GetTreeGround(tile) == TREE_GROUND_GRASS) { truelight@7710: uint density = GetTreeDensity(tile); truelight@7710: if (density < 3) { truelight@7710: SetTreeGroundDensity(tile, TREE_GROUND_GRASS, density + 1); truelight@7710: MarkTileDirtyByTile(tile); truelight@7710: } truelight@7710: } tron@2981: if (GetTreeCounter(tile) < 15) { tron@2981: AddTreeCounter(tile, 1); tron@2981: return; tron@2981: } tron@2981: SetTreeCounter(tile, 0); truelight@193: tron@2981: switch (GetTreeGrowth(tile)) { tron@2981: case 3: /* regular sized tree */ rubidium@9413: if (_settings_game.game_creation.landscape == LT_TROPIC && tron@3441: GetTreeType(tile) != TREE_CACTUS && belugas@3379: GetTropicZone(tile) == TROPICZONE_DESERT) { tron@2981: AddTreeGrowth(tile, 1); tron@2981: } else { tron@2981: switch (GB(Random(), 0, 3)) { tron@2981: case 0: /* start destructing */ tron@2981: AddTreeGrowth(tile, 1); tron@2955: break; tron@2955: tron@2981: case 1: /* add a tree */ smatz@10363: if (GetTreeCount(tile) < 4) { tron@2981: AddTreeCount(tile, 1); tron@2981: SetTreeGrowth(tile, 0); tron@2981: break; tron@2981: } tron@2981: /* FALL THROUGH */ truelight@0: tron@2981: case 2: { /* add a neighbouring tree */ tron@2981: TreeType treetype = GetTreeType(tile); terom@10438: terom@10438: TileIndexDiff dt = TileOffsByDir((Direction)(Random() & 7)); terom@10438: terom@10438: /* Don't plant trees in the void */ terom@10438: if (!IsValidTile(tile + dt)) return; truelight@0: terom@10438: tile += dt; truelight@0: frosch@8454: /* Cacti don't spread */ frosch@8454: if (!CanPlantTreesOnTile(tile, false)) return; tron@2981: frosch@8454: /* Don't plant trees, if ground was freshly cleared */ frosch@8459: if (IsTileType(tile, MP_CLEAR) && GetClearGround(tile) == CLEAR_GRASS && GetClearDensity(tile) != 3) return; tron@2981: frosch@8454: PlantTreesOnTile(tile, treetype, 0, 0); frosch@8454: tron@2981: break; tron@2981: } tron@2981: tron@2981: default: tron@2981: return; tron@2981: } truelight@0: } tron@2981: break; tron@2981: tron@2981: case 6: /* final stage of tree destruction */ smatz@10363: if (GetTreeCount(tile) > 1) { tron@2981: /* more than one tree, delete it */ tron@2981: AddTreeCount(tile, -1); tron@2981: SetTreeGrowth(tile, 3); tron@2981: } else { tron@2981: /* just one tree, change type into MP_CLEAR */ tron@2981: switch (GetTreeGround(tile)) { frosch@8459: case TREE_GROUND_SHORE: MakeShore(tile); break; truelight@7710: case TREE_GROUND_GRASS: MakeClear(tile, CLEAR_GRASS, GetTreeDensity(tile)); break; tron@3447: case TREE_GROUND_ROUGH: MakeClear(tile, CLEAR_ROUGH, 3); break; truelight@7710: default: // snow or desert rubidium@9413: MakeClear(tile, _settings_game.game_creation.landscape == LT_TROPIC ? CLEAR_DESERT : CLEAR_SNOW, GetTreeDensity(tile)); truelight@7710: break; tron@2981: } truelight@0: } tron@2981: break; tron@2981: tron@2981: default: tron@2981: AddTreeGrowth(tile, 1); tron@2981: break; truelight@0: } truelight@0: truelight@0: MarkTileDirtyByTile(tile); truelight@0: } truelight@0: rubidium@6247: void OnTick_Trees() truelight@0: { truelight@0: uint32 r; tron@1977: TileIndex tile; tron@3017: TreeType tree; truelight@0: truelight@0: /* place a tree at a random rainforest spot */ rubidium@9413: if (_settings_game.game_creation.landscape == LT_TROPIC && belugas@3379: (r = Random(), tile = RandomTileSeed(r), GetTropicZone(tile) == TROPICZONE_RAINFOREST) && frosch@8454: CanPlantTreesOnTile(tile, false) && tron@3441: (tree = GetRandomTreeType(tile, GB(r, 24, 8))) != TREE_INVALID) { frosch@8454: PlantTreesOnTile(tile, tree, 0, 0); truelight@0: } truelight@0: belugas@6422: /* byte underflow */ tron@2088: if (--_trees_tick_ctr != 0) return; truelight@193: truelight@0: /* place a tree at a random spot */ truelight@0: r = Random(); truelight@0: tile = TILE_MASK(r); frosch@8454: if (CanPlantTreesOnTile(tile, false) && (tree = GetRandomTreeType(tile, GB(r, 24, 8))) != TREE_INVALID) { frosch@8454: PlantTreesOnTile(tile, tree, 0, 0); truelight@0: } truelight@0: } truelight@0: tron@1977: static void ClickTile_Trees(TileIndex tile) truelight@0: { truelight@0: /* not used */ truelight@0: } truelight@0: frosch@8616: static TrackStatus GetTileTrackStatus_Trees(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side) truelight@0: { truelight@0: return 0; truelight@0: } truelight@0: rubidium@10207: static void ChangeTileOwner_Trees(TileIndex tile, Owner old_owner, Owner new_owner) truelight@0: { truelight@0: /* not used */ truelight@0: } truelight@0: rubidium@6247: void InitializeTrees() truelight@0: { truelight@0: _trees_tick_ctr = 0; truelight@0: } truelight@0: rubidium@7494: static CommandCost TerraformTile_Trees(TileIndex tile, uint32 flags, uint z_new, Slope tileh_new) rubidium@7494: { rubidium@7494: return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); rubidium@7494: } rubidium@7494: truelight@0: rubidium@5587: extern const TileTypeProcs _tile_type_trees_procs = { rubidium@4344: DrawTile_Trees, /* draw_tile_proc */ rubidium@4344: GetSlopeZ_Trees, /* get_slope_z_proc */ rubidium@4344: ClearTile_Trees, /* clear_tile_proc */ rubidium@4344: GetAcceptedCargo_Trees, /* get_accepted_cargo_proc */ rubidium@4344: GetTileDesc_Trees, /* get_tile_desc_proc */ rubidium@4344: GetTileTrackStatus_Trees, /* get_tile_track_status_proc */ rubidium@4344: ClickTile_Trees, /* click_tile_proc */ rubidium@4344: AnimateTile_Trees, /* animate_tile_proc */ rubidium@4344: TileLoop_Trees, /* tile_loop_clear */ rubidium@4344: ChangeTileOwner_Trees, /* change_tile_owner_clear */ rubidium@4344: NULL, /* get_produced_cargo_proc */ rubidium@4344: NULL, /* vehicle_enter_tile_proc */ rubidium@7335: GetFoundation_Trees, /* get_foundation_proc */ rubidium@7494: TerraformTile_Trees, /* terraform_tile_proc */ truelight@0: };