tron@2186: /* $Id$ */ tron@2186: rubidium@10455: /** @file tree_cmd.cpp Handling of tree tiles. */ glx@9574: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" celestar@5573: #include "bridge_map.h" tron@3144: #include "clear_map.h" rubidium@9723: #include "tile_cmd.h" glx@9505: #include "landscape.h" tron@3144: #include "tree_map.h" rubidium@9723: #include "viewport_func.h" rubidium@9723: #include "command_func.h" rubidium@9723: #include "economy_func.h" truelight@0: #include "town.h" tron@2153: #include "variables.h" truelight@4300: #include "genworld.h" rubidium@9722: #include "transparency.h" rubidium@9723: #include "functions.h" rubidium@9724: #include "player_func.h" rubidium@9723: #include "sound_func.h" rubidium@9724: #include "settings_type.h" rubidium@9724: #include "water_map.h" rubidium@9724: #include "water.h" rubidium@10455: #include "landscape_type.h" rubidium@9724: rubidium@9724: #include "table/strings.h" rubidium@9724: #include "table/sprites.h" rubidium@9724: #include "table/tree_land.h" truelight@4300: rubidium@9703: /** rubidium@9703: * List of tree placer algorithm. rubidium@9703: * rubidium@9703: * This enumeration defines all possible tree placer algorithm in the game. rubidium@9703: */ truelight@4300: enum TreePlacer { rubidium@9703: TP_NONE, ///< No tree placer algorithm rubidium@9703: TP_ORIGINAL, ///< The original algorithm rubidium@9703: TP_IMPROVED, ///< A 'improved' algorithm truelight@4300: }; truelight@0: rubidium@9703: /** rubidium@9724: * Tests if a tile can be converted to MP_TREES rubidium@9724: * This is true for clear ground without farms or rocks. rubidium@9724: * rubidium@9724: * @param tile the tile of interest rubidium@9724: * @param allow_desert Allow planting trees on CLEAR_DESERT? rubidium@9724: * @return true if trees can be built. rubidium@9724: */ rubidium@9724: static bool CanPlantTreesOnTile(TileIndex tile, bool allow_desert) rubidium@9724: { rubidium@9724: switch (GetTileType(tile)) { rubidium@9724: case MP_WATER: rubidium@9724: return !IsBridgeAbove(tile) && IsCoast(tile) && !IsSlopeWithOneCornerRaised(GetTileSlope(tile, NULL)); rubidium@9724: rubidium@9724: case MP_CLEAR: rubidium@9724: return !IsBridgeAbove(tile) && !IsClearGround(tile, CLEAR_FIELDS) && !IsClearGround(tile, CLEAR_ROCKS) && rubidium@9724: (allow_desert || !IsClearGround(tile, CLEAR_DESERT)); rubidium@9724: rubidium@9724: default: return false; rubidium@9724: } rubidium@9724: } rubidium@9724: rubidium@9724: /** rubidium@9724: * Creates a tree tile rubidium@9724: * Ground type and density is preserved. rubidium@9724: * rubidium@9724: * @pre the tile must be suitable for trees. rubidium@9724: * rubidium@9724: * @param tile where to plant the trees. rubidium@9724: * @param type The type of the tree rubidium@9724: * @param count the number of trees (minus 1) rubidium@9724: * @param growth the growth status rubidium@9724: */ rubidium@9724: static void PlantTreesOnTile(TileIndex tile, TreeType treetype, uint count, uint growth) rubidium@9724: { rubidium@9724: assert(treetype != TREE_INVALID); rubidium@9724: assert(CanPlantTreesOnTile(tile, true)); rubidium@9724: rubidium@9724: TreeGround ground; rubidium@9724: uint density = 3; rubidium@9724: rubidium@9724: switch (GetTileType(tile)) { rubidium@9724: case MP_WATER: rubidium@9724: ground = TREE_GROUND_SHORE; rubidium@9724: break; rubidium@9724: rubidium@9724: case MP_CLEAR: rubidium@9724: switch (GetClearGround(tile)) { rubidium@9724: case CLEAR_GRASS: ground = TREE_GROUND_GRASS; density = GetClearDensity(tile); break; rubidium@9724: case CLEAR_ROUGH: ground = TREE_GROUND_ROUGH; break; rubidium@9724: default: ground = TREE_GROUND_SNOW_DESERT; density = GetClearDensity(tile); break; rubidium@9724: } rubidium@9724: break; rubidium@9724: rubidium@9724: default: NOT_REACHED(); rubidium@9724: } rubidium@9724: rubidium@9724: MakeTree(tile, treetype, count, growth, ground, density); rubidium@9724: } rubidium@9724: rubidium@9724: /** rubidium@9703: * Get a random TreeType for the given tile based on a given seed rubidium@9703: * rubidium@9703: * This function returns a random TreeType which can be placed on the given tile. rubidium@9703: * The seed for randomness must be less or equal 256, use #GB on the value of Random() rubidium@9703: * to get such a value. rubidium@9703: * rubidium@9703: * @param tile The tile to get a random TreeType from rubidium@9703: * @param seed The seed for randomness, must be less or equal 256 rubidium@9703: * @return The random tree type rubidium@9703: */ tron@2981: static TreeType GetRandomTreeType(TileIndex tile, uint seed) truelight@0: { rubidium@10715: switch (_settings.game_creation.landscape) { truelight@9517: case LT_TEMPERATE: rubidium@5838: return (TreeType)(seed * TREE_COUNT_TEMPERATE / 256 + TREE_TEMPERATE); truelight@0: truelight@9517: case LT_ARCTIC: rubidium@5838: return (TreeType)(seed * TREE_COUNT_SUB_ARCTIC / 256 + TREE_SUB_ARCTIC); tron@1286: truelight@9517: case LT_TROPIC: belugas@3379: switch (GetTropicZone(tile)) { rubidium@9724: case TROPICZONE_NORMAL: return (TreeType)(seed * TREE_COUNT_SUB_TROPICAL / 256 + TREE_SUB_TROPICAL); rubidium@5838: case TROPICZONE_DESERT: return (TreeType)((seed > 12) ? TREE_INVALID : TREE_CACTUS); rubidium@5838: default: return (TreeType)(seed * TREE_COUNT_RAINFOREST / 256 + TREE_RAINFOREST); tron@1286: } tron@1286: tron@1286: default: rubidium@5838: return (TreeType)(seed * TREE_COUNT_TOYLAND / 256 + TREE_TOYLAND); truelight@0: } truelight@0: } truelight@0: rubidium@9703: /** rubidium@9703: * Make a random tree tile of the given tile rubidium@9703: * rubidium@9703: * Create a new tree-tile for the given tile. The second parameter is used for rubidium@9703: * randomness like type and number of trees. rubidium@9703: * rubidium@9703: * @param tile The tile to make a tree-tile from rubidium@9703: * @param r The randomness value from a Random() value rubidium@9703: */ 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) { rubidium@9724: PlantTreesOnTile(tile, tree, GB(r, 22, 2), min(GB(r, 16, 3), 6)); truelight@0: rubidium@9724: /* Rerandomize ground, if neither snow nor shore */ rubidium@9724: TreeGround ground = GetTreeGround(tile); rubidium@9724: if (ground != TREE_GROUND_SNOW_DESERT && ground != TREE_GROUND_SHORE) { truelight@9718: SetTreeGroundDensity(tile, (TreeGround)GB(r, 28, 1), 3); truelight@0: } rubidium@9724: rubidium@9724: /* Set the counter to a random start value */ rubidium@9724: SetTreeCounter(tile, (TreeGround)GB(r, 24, 4)); truelight@0: } truelight@0: } truelight@0: rubidium@9703: /** rubidium@9703: * Place some amount of trees around a given tile. rubidium@9703: * rubidium@9703: * This function adds some trees around a given tile. As this function use rubidium@9703: * the Random() call it depends on the random how many trees are actually placed rubidium@9703: * around the given tile. rubidium@9703: * rubidium@9703: * @param tile The center of the trees to add rubidium@9703: */ 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; rubidium@9722: uint dist = abs(x) + abs(y); tron@2088: TileIndex cur_tile = TILE_MASK(tile + TileDiffXY(x, y)); truelight@0: rubidium@9724: if (dist <= 13 && CanPlantTreesOnTile(cur_tile, true)) { tron@2958: PlaceTree(cur_tile, r); truelight@0: } tron@2088: } truelight@0: } truelight@0: rubidium@9703: /** rubidium@9703: * Place more trees on the map. rubidium@9703: * rubidium@9703: * This function add more trees to the map. rubidium@9703: */ rubidium@6573: static void PlaceMoreTrees() truelight@0: { tron@2243: uint i = ScaleByMapSize(GB(Random(), 0, 5) + 25); truelight@0: do { ludde@2051: DoPlaceMoreTrees(RandomTile()); truelight@0: } while (--i); truelight@0: } truelight@0: truelight@4300: /** truelight@4300: * Place a tree at the same height as an existing tree. rubidium@9703: * rubidium@9703: * Add a new tree around the given tile which is at the same rubidium@9703: * height or at some offset (2 units) of it. rubidium@9703: * rubidium@9703: * @param tile The base tile to add a new tree somewhere around rubidium@9703: * @param height The height (like the one from the tile) truelight@4300: */ rubidium@9724: 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 */ rubidium@9722: if (abs(x) + abs(y) > 16) continue; truelight@4300: truelight@4300: /* Clear tile, no farm-tiles or rocks */ rubidium@9724: if (!CanPlantTreesOnTile(cur_tile, true)) continue; truelight@4300: truelight@4300: /* Not too much height difference */ rubidium@9723: 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@9703: /** rubidium@9703: * Place some trees randomly rubidium@9703: * rubidium@9703: * This function just place some trees randomly on the map. rubidium@9703: */ rubidium@6573: 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: rubidium@9724: if (CanPlantTreesOnTile(tile, true)) { tron@2958: PlaceTree(tile, r); rubidium@10715: if (_settings.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@10715: if (_settings.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: } truelight@0: } while (--i); truelight@0: truelight@0: /* place extra trees at rainforest area */ rubidium@10715: if (_settings.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: rubidium@9724: if (GetTropicZone(tile) == TROPICZONE_RAINFOREST && CanPlantTreesOnTile(tile, false)) { tron@2958: PlaceTree(tile, r); truelight@0: } truelight@0: } while (--i); truelight@0: } truelight@0: } truelight@0: rubidium@9703: /** rubidium@9703: * Place new trees. rubidium@9703: * rubidium@9703: * This function takes care of the selected tree placer algorithm and rubidium@9703: * place randomly the trees for a new game. rubidium@9703: */ rubidium@6573: void GenerateTrees() truelight@0: { truelight@4300: uint i, total; truelight@4300: rubidium@10715: if (_settings.game_creation.tree_placer == TP_NONE) return; truelight@0: rubidium@10715: if (_settings.game_creation.landscape != LT_TOYLAND) PlaceMoreTrees(); tron@2088: rubidium@10715: switch (_settings.game_creation.tree_placer) { rubidium@10715: case TP_ORIGINAL: i = _settings.game_creation.landscape == LT_ARCTIC ? 15 : 6; break; rubidium@10715: case TP_IMPROVED: i = _settings.game_creation.landscape == LT_ARCTIC ? 4 : 2; break; truelight@4300: default: NOT_REACHED(); return; truelight@4300: } truelight@4300: truelight@4300: total = ScaleByMapSize(1000); rubidium@10715: if (_settings.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 glx@9574: * @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: */ glx@9629: CommandCost CmdPlantTree(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { tron@3183: StringID msg = INVALID_STRING_ID; rubidium@9723: 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@10715: if (p1 != (uint)-1 && p1 - _tree_base_by_landscape[_settings.game_creation.landscape] >= _tree_count_by_landscape[_settings.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@6432: if (ex < sx) Swap(ex, sx); tron@6432: 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: glx@9574: /* no more space for trees? */ tron@3003: if (_game_mode != GM_EDITOR && GetTreeCount(tile) == 3) { 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: } glx@9574: /* 2x as expensive to add more trees to an existing tile */ glx@9629: cost.AddCost(_price.build_trees * 2); tron@1286: break; tron@1286: rubidium@9701: case MP_WATER: rubidium@9724: if (!IsCoast(tile) || IsSlopeWithOneCornerRaised(GetTileSlope(tile, NULL))) { rubidium@9724: msg = STR_3807_CAN_T_BUILD_ON_WATER; rubidium@9724: continue; rubidium@9724: } rubidium@9724: /* FALL THROUGH */ tron@1286: case MP_CLEAR: rubidium@9724: if (IsBridgeAbove(tile)) { tron@3183: msg = STR_2804_SITE_UNSUITABLE; tron@1286: continue; truelight@0: } truelight@193: rubidium@9724: if (IsTileType(tile, MP_CLEAR)) { rubidium@9724: /* Remove fields or rocks. Note that the ground will get barrened */ rubidium@9724: switch (GetClearGround(tile)) { rubidium@9724: case CLEAR_FIELDS: rubidium@9724: case CLEAR_ROCKS: { rubidium@9724: CommandCost ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); rubidium@9724: if (CmdFailed(ret)) return ret; rubidium@9724: cost.AddCost(ret); rubidium@9724: break; rubidium@9724: } rubidium@9724: rubidium@9724: default: break; rubidium@9724: } tron@2955: } truelight@0: rubidium@9723: if (_game_mode != GM_EDITOR && IsValidPlayer(_current_player)) { rubidium@10715: Town *t = ClosestTownFromTile(tile, _settings.economy.dist_local_authority); rubidium@9723: if (t != NULL) ChangeTownRating(t, RATING_TREE_UP_STEP, RATING_TREE_MAXIMUM); rubidium@9723: } rubidium@9723: tron@1286: if (flags & DC_EXEC) { tron@2981: TreeType treetype; tron@1286: rubidium@5838: 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: rubidium@9724: /* Plant full grown trees in scenario editor */ rubidium@9724: PlantTreesOnTile(tile, treetype, 0, _game_mode == GM_EDITOR ? 3 : 0); tron@3003: MarkTileDirtyByTile(tile); tron@1286: rubidium@9724: /* When planting rainforest-trees, set tropiczone to rainforest in editor. */ rubidium@9723: if (_game_mode == GM_EDITOR && IsInsideMM(treetype, TREE_RAINFOREST, TREE_CACTUS)) belugas@3379: SetTropicZone(tile, TROPICZONE_RAINFOREST); truelight@0: } glx@9629: 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: glx@9629: if (cost.GetCost() == 0) { tron@3183: return_cmd_error(msg); tron@3183: } else { tron@3183: return cost; tron@3183: } truelight@0: } truelight@0: rubidium@6574: struct TreeListEnt { peter1138@5919: SpriteID image; peter1138@5919: SpriteID pal; rubidium@9601: byte x, y; rubidium@6574: }; truelight@0: truelight@0: static void DrawTile_Trees(TileInfo *ti) truelight@0: { tron@2981: switch (GetTreeGround(ti->tile)) { rubidium@9724: case TREE_GROUND_SHORE: DrawShoreTile(ti->tileh); break; truelight@9718: case TREE_GROUND_GRASS: DrawClearLandTile(ti, GetTreeDensity(ti->tile)); break; tron@3441: case TREE_GROUND_ROUGH: DrawHillyLandTile(ti); break; peter1138@5919: 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: glx@9732: /* Do not draw trees when the invisible trees patch and transparency tree are set */ rubidium@9869: if (IsInvisibilitySet(TO_TREES)) return; glx@9732: glx@9732: uint16 tmp = ti->x; glx@9732: glx@9732: tmp = ROR(tmp, 2); glx@9732: tmp -= ti->y; glx@9732: tmp = ROR(tmp, 3); glx@9732: tmp -= ti->x; glx@9732: tmp = ROR(tmp, 1); glx@9732: tmp += ti->y; glx@9732: glx@9732: uint index = GB(tmp, 6, 2) + (GetTreeType(ti->tile) << 2); glx@9732: glx@9732: /* different tree styles above one of the grounds */ glx@9732: if (GetTreeGround(ti->tile) == TREE_GROUND_SNOW_DESERT && glx@9732: GetTreeDensity(ti->tile) >= 2 && glx@9732: IsInsideMM(index, TREE_SUB_ARCTIC << 2, TREE_RAINFOREST << 2)) { glx@9732: index += 164 - (TREE_SUB_ARCTIC << 2); truelight@0: } truelight@0: glx@9732: assert(index < lengthof(_tree_layout_sprite)); truelight@0: glx@9732: const PalSpriteID *s = _tree_layout_sprite[index]; glx@9732: const TreePos *d = _tree_layout_xy[GB(tmp, 4, 2)]; truelight@193: glx@9732: /* combine trees into one sprite object */ truelight@0: StartSpriteCombine(); truelight@0: glx@9732: TreeListEnt te[4]; rubidium@9694: glx@9732: /* put the trees to draw in a list */ glx@9732: uint trees = GetTreeCount(ti->tile) + 1; truelight@0: glx@9732: for (uint i = 0; i < trees; i++) { glx@9732: SpriteID image = s[0].sprite + (i == trees - 1 ? GetTreeGrowth(ti->tile) : 3); glx@9732: SpriteID pal = s[0].pal; truelight@0: glx@9732: te[i].image = image; glx@9732: te[i].pal = pal; glx@9732: te[i].x = d->x; glx@9732: te[i].y = d->y; glx@9732: s++; glx@9732: d++; glx@9732: } truelight@0: glx@9732: /* draw them in a sorted way */ glx@9732: byte z = ti->z + GetSlopeMaxZ(ti->tileh) / 2; glx@9732: glx@9732: for (; trees > 0; trees--) { glx@9732: uint min = te[0].x + te[0].y; glx@9732: uint mi = 0; glx@9732: glx@9732: for (uint i = 1; i < trees; i++) { glx@9732: if ((uint)(te[i].x + te[i].y) < min) { glx@9732: min = te[i].x + te[i].y; glx@9732: mi = i; glx@9732: } truelight@0: } glx@9732: glx@9732: 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); glx@9732: glx@9732: /* replace the removed one with the last one */ glx@9732: 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@5838: Slope tileh = GetTileSlope(tile, &z); tron@4231: tron@4231: return z + GetPartialZ(x & 0xF, y & 0xF, tileh); truelight@0: } truelight@0: rubidium@9694: static Foundation GetFoundation_Trees(TileIndex tile, Slope tileh) tron@2548: { rubidium@9694: return FOUNDATION_NONE; dominik@39: } dominik@39: glx@9629: static CommandCost ClearTile_Trees(TileIndex tile, byte flags) tron@1977: { tron@2243: uint num; truelight@0: rubidium@9723: if (IsValidPlayer(_current_player)) { rubidium@10715: Town *t = ClosestTownFromTile(tile, _settings.economy.dist_local_authority); rubidium@9723: if (t != NULL) ChangeTownRating(t, RATING_TREE_DOWN_STEP, RATING_TREE_MINIMUM); truelight@0: } truelight@0: tron@2981: num = GetTreeCount(tile) + 1; rubidium@9723: if (IsInsideMM(GetTreeType(tile), TREE_RAINFOREST, TREE_CACTUS)) num *= 4; truelight@0: tron@2243: if (flags & DC_EXEC) DoClearSquare(tile); truelight@0: rubidium@9723: 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: rubidium@9723: if (IsInsideMM(tt, TREE_RAINFOREST, TREE_CACTUS)) { tron@2981: td->str = STR_280F_RAINFOREST; tron@2981: } else { truelight@9718: td->str = tt == TREE_CACTUS ? STR_2810_CACTUS_PLANTS : STR_280E_TREES; tron@2981: } truelight@0: rubidium@10715: 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: rubidium@9723: 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: { glx@9505: 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@9718: 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(); rubidium@9723: 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: { rubidium@9724: if (GetTreeGround(tile) == TREE_GROUND_SHORE) { rubidium@9724: TileLoop_Water(tile); rubidium@9724: } else { rubidium@10715: switch (_settings.game_creation.landscape) { rubidium@9724: case LT_TROPIC: TileLoopTreesDesert(tile); break; rubidium@9724: case LT_ARCTIC: TileLoopTreesAlps(tile); break; rubidium@9724: } truelight@0: } truelight@0: truelight@0: TileLoopClearHelper(tile); truelight@0: truelight@9718: uint treeCounter = GetTreeCounter(tile); truelight@9718: truelight@9718: /* Handle growth of grass at every 8th processings, like it's done for grass */ truelight@9718: if ((treeCounter & 7) == 7 && GetTreeGround(tile) == TREE_GROUND_GRASS) { truelight@9718: uint density = GetTreeDensity(tile); truelight@9718: if (density < 3) { truelight@9718: SetTreeGroundDensity(tile, TREE_GROUND_GRASS, density + 1); truelight@9718: MarkTileDirtyByTile(tile); truelight@9718: } truelight@9718: } 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@10715: if (_settings.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 */ tron@2981: if (GetTreeCount(tile) < 3) { 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); truelight@0: rubidium@9694: tile += TileOffsByDir((Direction)(Random() & 7)); truelight@0: rubidium@9724: /* Cacti don't spread */ rubidium@9724: if (!CanPlantTreesOnTile(tile, false)) return; tron@2981: rubidium@9724: /* Don't plant trees, if ground was freshly cleared */ rubidium@9724: if (IsTileType(tile, MP_CLEAR) && GetClearGround(tile) == CLEAR_GRASS && GetClearDensity(tile) != 3) return; tron@2981: rubidium@9724: PlantTreesOnTile(tile, treetype, 0, 0); rubidium@9724: 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 */ tron@2981: if (GetTreeCount(tile) > 0) { 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)) { rubidium@9724: case TREE_GROUND_SHORE: MakeShore(tile); break; truelight@9718: case TREE_GROUND_GRASS: MakeClear(tile, CLEAR_GRASS, GetTreeDensity(tile)); break; tron@3447: case TREE_GROUND_ROUGH: MakeClear(tile, CLEAR_ROUGH, 3); break; truelight@9718: default: // snow or desert rubidium@10715: MakeClear(tile, _settings.game_creation.landscape == LT_TROPIC ? CLEAR_DESERT : CLEAR_SNOW, GetTreeDensity(tile)); truelight@9718: 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@6573: 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@10715: if (_settings.game_creation.landscape == LT_TROPIC && belugas@3379: (r = Random(), tile = RandomTileSeed(r), GetTropicZone(tile) == TROPICZONE_RAINFOREST) && rubidium@9724: CanPlantTreesOnTile(tile, false) && tron@3441: (tree = GetRandomTreeType(tile, GB(r, 24, 8))) != TREE_INVALID) { rubidium@9724: PlantTreesOnTile(tile, tree, 0, 0); truelight@0: } truelight@0: glx@9574: /* 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); rubidium@9724: if (CanPlantTreesOnTile(tile, false) && (tree = GetRandomTreeType(tile, GB(r, 24, 8))) != TREE_INVALID) { rubidium@9724: 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: glx@9732: static TrackStatus GetTileTrackStatus_Trees(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side) truelight@0: { truelight@0: return 0; truelight@0: } truelight@0: Darkvater@2436: static void ChangeTileOwner_Trees(TileIndex tile, PlayerID old_player, PlayerID new_player) truelight@0: { truelight@0: /* not used */ truelight@0: } truelight@0: rubidium@6573: void InitializeTrees() truelight@0: { truelight@0: _trees_tick_ctr = 0; truelight@0: } truelight@0: rubidium@9701: static CommandCost TerraformTile_Trees(TileIndex tile, uint32 flags, uint z_new, Slope tileh_new) rubidium@9701: { rubidium@9701: return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); rubidium@9701: } rubidium@9701: truelight@0: rubidium@5838: 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@9694: GetFoundation_Trees, /* get_foundation_proc */ rubidium@9701: TerraformTile_Trees, /* terraform_tile_proc */ truelight@0: };