tron@2186: /* $Id$ */ tron@2186: belugas@6918: /** @file tree_cmd.cpp */ belugas@6918: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" celestar@5573: #include "bridge_map.h" tron@3144: #include "clear_map.h" tron@507: #include "table/strings.h" celestar@2148: #include "table/sprites.h" Darkvater@1784: #include "table/tree_land.h" tron@2163: #include "functions.h" tron@679: #include "map.h" maedhros@6669: #include "landscape.h" tron@1209: #include "tile.h" tron@3144: #include "tree_map.h" truelight@0: #include "viewport.h" truelight@0: #include "command.h" truelight@0: #include "town.h" tron@337: #include "sound.h" tron@2153: #include "variables.h" truelight@4300: #include "genworld.h" belugas@8345: #include "transparency.h" truelight@4300: rubidium@8093: /** rubidium@8093: * List of tree placer algorithm. rubidium@8093: * rubidium@8093: * This enumeration defines all possible tree placer algorithm in the game. rubidium@8093: */ truelight@4300: enum TreePlacer { rubidium@8093: TP_NONE, ///< No tree placer algorithm rubidium@8093: TP_ORIGINAL, ///< The original algorithm rubidium@8093: TP_IMPROVED, ///< A 'improved' algorithm truelight@4300: }; truelight@0: rubidium@8093: /** rubidium@8093: * Get a random TreeType for the given tile based on a given seed rubidium@8093: * rubidium@8093: * This function returns a random TreeType which can be placed on the given tile. rubidium@8093: * The seed for randomness must be less or equal 256, use #GB on the value of Random() rubidium@8093: * to get such a value. rubidium@8093: * rubidium@8093: * @param tile The tile to get a random TreeType from rubidium@8093: * @param seed The seed for randomness, must be less or equal 256 rubidium@8093: * @return The random tree type rubidium@8093: */ tron@2981: static TreeType GetRandomTreeType(TileIndex tile, uint seed) truelight@0: { tron@1286: switch (_opt.landscape) { belugas@6683: case LT_TEMPERATE: rubidium@5838: return (TreeType)(seed * TREE_COUNT_TEMPERATE / 256 + TREE_TEMPERATE); truelight@0: belugas@6683: case LT_ARCTIC: rubidium@5838: return (TreeType)(seed * TREE_COUNT_SUB_ARCTIC / 256 + TREE_SUB_ARCTIC); tron@1286: belugas@6683: case LT_TROPIC: belugas@3379: switch (GetTropicZone(tile)) { rubidium@5838: case TROPICZONE_INVALID: 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@8093: /** rubidium@8093: * Make a random tree tile of the given tile rubidium@8093: * rubidium@8093: * Create a new tree-tile for the given tile. The second parameter is used for rubidium@8093: * randomness like type and number of trees. rubidium@8093: * rubidium@8093: * @param tile The tile to make a tree-tile from rubidium@8093: * @param r The randomness value from a Random() value rubidium@8093: */ 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) { tron@3441: MakeTree(tile, tree, GB(r, 22, 2), min(GB(r, 16, 3), 6), TREE_GROUND_GRASS, 0); truelight@0: belugas@6918: /* above snowline? */ belugas@6683: if (_opt.landscape == LT_ARCTIC && GetTileZ(tile) > GetSnowLine()) { tron@3441: SetTreeGroundDensity(tile, TREE_GROUND_SNOW_DESERT, 3); rubidium@5838: SetTreeCounter(tile, (TreeGround)GB(r, 24, 3)); tron@2088: } else { truelight@8206: SetTreeGroundDensity(tile, (TreeGround)GB(r, 28, 1), 3); rubidium@5838: SetTreeCounter(tile, (TreeGround)GB(r, 24, 4)); truelight@0: } truelight@0: } truelight@0: } truelight@0: rubidium@8093: /** rubidium@8093: * Place some amount of trees around a given tile. rubidium@8093: * rubidium@8093: * This function adds some trees around a given tile. As this function use rubidium@8093: * the Random() call it depends on the random how many trees are actually placed rubidium@8093: * around the given tile. rubidium@8093: * rubidium@8093: * @param tile The center of the trees to add rubidium@8093: */ 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@8419: uint dist = abs(x) + abs(y); tron@2088: TileIndex cur_tile = TILE_MASK(tile + TileDiffXY(x, y)); truelight@0: tron@2955: if (dist <= 13 && tron@2955: IsTileType(cur_tile, MP_CLEAR) && celestar@5573: !IsBridgeAbove(cur_tile) && tron@3447: !IsClearGround(cur_tile, CLEAR_FIELDS) && tron@3447: !IsClearGround(cur_tile, CLEAR_ROCKS)) { tron@2958: PlaceTree(cur_tile, r); truelight@0: } tron@2088: } truelight@0: } truelight@0: rubidium@8093: /** rubidium@8093: * Place more trees on the map. rubidium@8093: * rubidium@8093: * This function add more trees to the map. rubidium@8093: */ 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@8093: * rubidium@8093: * Add a new tree around the given tile which is at the same rubidium@8093: * height or at some offset (2 units) of it. rubidium@8093: * rubidium@8093: * @param tile The base tile to add a new tree somewhere around rubidium@8093: * @param height The height (like the one from the tile) truelight@4300: */ truelight@4300: 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@8419: if (abs(x) + abs(y) > 16) continue; truelight@4300: truelight@4300: /* Clear tile, no farm-tiles or rocks */ truelight@4300: if (!IsTileType(cur_tile, MP_CLEAR) || truelight@4300: IsClearGround(cur_tile, CLEAR_FIELDS) || truelight@4300: IsClearGround(cur_tile, CLEAR_ROCKS)) truelight@4300: continue; truelight@4300: truelight@4300: /* Not too much height difference */ rubidium@5838: 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@8093: /** rubidium@8093: * Place some trees randomly rubidium@8093: * rubidium@8093: * This function just place some trees randomly on the map. rubidium@8093: */ 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: tron@2955: if (IsTileType(tile, MP_CLEAR) && celestar@5573: !IsBridgeAbove(tile) && tron@3447: !IsClearGround(tile, CLEAR_FIELDS) && tron@3447: !IsClearGround(tile, CLEAR_ROCKS)) { tron@2958: PlaceTree(tile, r); truelight@4300: if (_patches.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! */ belugas@6683: if (_opt.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 */ belugas@6683: if (_opt.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: celestar@5573: if (IsTileType(tile, MP_CLEAR) && celestar@5573: !IsBridgeAbove(tile) && celestar@5573: !IsClearGround(tile, CLEAR_FIELDS) && celestar@5573: GetTropicZone(tile) == TROPICZONE_RAINFOREST) { tron@2958: PlaceTree(tile, r); truelight@0: } truelight@0: } while (--i); truelight@0: } truelight@0: } truelight@0: rubidium@8093: /** rubidium@8093: * Place new trees. rubidium@8093: * rubidium@8093: * This function takes care of the selected tree placer algorithm and rubidium@8093: * place randomly the trees for a new game. rubidium@8093: */ rubidium@6573: void GenerateTrees() truelight@0: { truelight@4300: uint i, total; truelight@4300: truelight@4300: if (_patches.tree_placer == TP_NONE) return; truelight@0: belugas@6683: if (_opt.landscape != LT_TOYLAND) PlaceMoreTrees(); tron@2088: truelight@4300: switch (_patches.tree_placer) { belugas@6683: case TP_ORIGINAL: i = _opt.landscape == LT_ARCTIC ? 15 : 6; break; belugas@6683: case TP_IMPROVED: i = _opt.landscape == LT_ARCTIC ? 4 : 2; break; truelight@4300: default: NOT_REACHED(); return; truelight@4300: } truelight@4300: truelight@4300: total = ScaleByMapSize(1000); belugas@6683: if (_opt.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@6918: * @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@7439: CommandCost CmdPlantTree(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { tron@3183: StringID msg = INVALID_STRING_ID; rubidium@7439: CommandCost cost; 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 */ Darkvater@1784: if (p1 != (uint)-1 && p1 - _tree_base_by_landscape[_opt.landscape] >= _tree_count_by_landscape[_opt.landscape]) return CMD_ERROR; truelight@193: truelight@0: SET_EXPENSES_TYPE(EXPENSES_OTHER); truelight@0: 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: belugas@6918: /* 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: } belugas@6918: /* 2x as expensive to add more trees to an existing tile */ rubidium@7446: cost.AddCost(_price.build_trees * 2); tron@1286: break; tron@1286: rubidium@8005: case MP_WATER: rubidium@8005: msg = STR_3807_CAN_T_BUILD_ON_WATER; rubidium@8005: continue; rubidium@8005: break; rubidium@8005: tron@1286: case MP_CLEAR: celestar@5573: if (!IsTileOwner(tile, OWNER_NONE) || celestar@5573: IsBridgeAbove(tile)) { tron@3183: msg = STR_2804_SITE_UNSUITABLE; tron@1286: continue; truelight@0: } truelight@193: tron@2955: switch (GetClearGround(tile)) { rubidium@7446: case CLEAR_FIELDS: cost.AddCost(_price.clear_3); break; rubidium@7446: case CLEAR_ROCKS: cost.AddCost(_price.clear_2); break; tron@2955: default: break; tron@2955: } truelight@0: tron@1286: if (flags & DC_EXEC) { tron@2981: TreeType treetype; tron@3079: uint growth; tron@1286: Darkvater@4850: if (_game_mode != GM_EDITOR && IsValidPlayer(_current_player)) { tron@2088: Town *t = ClosestTownFromTile(tile, _patches.dist_local_authority); tron@1286: if (t != NULL) tron@1286: ChangeTownRating(t, RATING_TREE_UP_STEP, RATING_TREE_MAXIMUM); tron@1286: } 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: tron@3079: growth = _game_mode == GM_EDITOR ? 3 : 0; tron@2981: switch (GetClearGround(tile)) { truelight@8206: case CLEAR_ROUGH: MakeTree(tile, treetype, 0, growth, TREE_GROUND_ROUGH, 3); break; truelight@8206: case CLEAR_SNOW: truelight@8206: case CLEAR_DESERT: MakeTree(tile, treetype, 0, growth, TREE_GROUND_SNOW_DESERT, GetClearDensity(tile)); break; truelight@8206: default: MakeTree(tile, treetype, 0, growth, TREE_GROUND_GRASS, GetClearDensity(tile)); break; tron@1286: } tron@3003: MarkTileDirtyByTile(tile); tron@1286: skidd13@8450: if (_game_mode == GM_EDITOR && IsInsideMM(treetype, TREE_RAINFOREST, TREE_CACTUS)) belugas@3379: SetTropicZone(tile, TROPICZONE_RAINFOREST); truelight@0: } rubidium@7446: 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@7446: 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@6987: byte x, y; rubidium@6574: }; truelight@0: truelight@0: static void DrawTile_Trees(TileInfo *ti) truelight@0: { peter1138@5919: const PalSpriteID *s; tron@2654: const TreePos* d; truelight@0: byte z; truelight@0: tron@2981: switch (GetTreeGround(ti->tile)) { truelight@8206: 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: truelight@0: z = ti->z; tron@3636: if (ti->tileh != SLOPE_FLAT) { truelight@0: z += 4; tron@3636: if (IsSteepSlope(ti->tileh)) z += 4; truelight@0: } truelight@0: truelight@0: { truelight@0: uint16 tmp = ti->x; tron@959: uint index; truelight@0: tron@2088: tmp = ROR(tmp, 2); truelight@0: tmp -= ti->y; tron@2088: tmp = ROR(tmp, 3); truelight@0: tmp -= ti->x; tron@2088: tmp = ROR(tmp, 1); truelight@0: tmp += ti->y; truelight@0: tron@2088: d = _tree_layout_xy[GB(tmp, 4, 2)]; truelight@0: tron@2981: index = GB(tmp, 6, 2) + (GetTreeType(ti->tile) << 2); truelight@193: truelight@0: /* different tree styles above one of the grounds */ tron@3441: if (GetTreeGround(ti->tile) == TREE_GROUND_SNOW_DESERT && tron@2981: GetTreeDensity(ti->tile) >= 2 && skidd13@8450: IsInsideMM(index, TREE_SUB_ARCTIC << 2, TREE_RAINFOREST << 2)) { tron@3441: index += 164 - (TREE_SUB_ARCTIC << 2); tron@2981: } truelight@193: truelight@193: assert(index < lengthof(_tree_layout_sprite)); truelight@0: s = _tree_layout_sprite[index]; truelight@0: } truelight@0: truelight@0: StartSpriteCombine(); truelight@0: belugas@8372: if (!(IsTransparencySet(TO_TREES) && _patches.invisible_trees)) { tron@2088: TreeListEnt te[4]; tron@2088: uint i; truelight@0: truelight@0: /* put the trees to draw in a list */ tron@3271: i = GetTreeCount(ti->tile) + 1; truelight@0: do { peter1138@5919: SpriteID image = s[0].sprite + (--i == 0 ? GetTreeGrowth(ti->tile) : 3); rubidium@7829: SpriteID pal = s[0].pal; rubidium@7829: truelight@0: te[i].image = image; peter1138@5919: te[i].pal = pal; tron@2654: te[i].x = d->x; tron@2654: te[i].y = d->y; truelight@0: s++; tron@2654: d++; truelight@0: } while (i); truelight@0: truelight@0: /* draw them in a sorted way */ tron@2952: for (;;) { truelight@0: byte min = 0xFF; truelight@0: TreeListEnt *tep = NULL; truelight@0: tron@3271: i = GetTreeCount(ti->tile) + 1; truelight@0: do { tron@2644: if (te[--i].image != 0 && te[i].x + te[i].y < min) { truelight@0: min = te[i].x + te[i].y; truelight@0: tep = &te[i]; truelight@0: } truelight@0: } while (i); truelight@0: tron@2088: if (tep == NULL) break; truelight@0: belugas@8345: AddSortableSpriteToDraw(tep->image, tep->pal, ti->x + tep->x, ti->y + tep->y, 16 - tep->x, 16 - tep->y, 0x30, z, IsTransparencySet(TO_TREES), -tep->x, -tep->y); truelight@0: tep->image = 0; truelight@0: } 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@7831: static Foundation GetFoundation_Trees(TileIndex tile, Slope tileh) tron@2548: { rubidium@7831: return FOUNDATION_NONE; dominik@39: } dominik@39: rubidium@7439: static CommandCost ClearTile_Trees(TileIndex tile, byte flags) tron@1977: { tron@2243: uint num; truelight@0: Darkvater@4850: if ((flags & DC_EXEC) && IsValidPlayer(_current_player)) { truelight@0: Town *t = ClosestTownFromTile(tile, _patches.dist_local_authority); truelight@193: if (t != NULL) celestar@1005: ChangeTownRating(t, RATING_TREE_DOWN_STEP, RATING_TREE_MINIMUM); truelight@0: } truelight@0: tron@2981: num = GetTreeCount(tile) + 1; skidd13@8450: if (IsInsideMM(GetTreeType(tile), TREE_RAINFOREST, TREE_CACTUS)) num *= 4; truelight@0: tron@2243: if (flags & DC_EXEC) DoClearSquare(tile); truelight@0: rubidium@7446: return CommandCost(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@8450: if (IsInsideMM(tt, TREE_RAINFOREST, TREE_CACTUS)) { tron@2981: td->str = STR_280F_RAINFOREST; tron@2981: } else { belugas@8208: td->str = tt == TREE_CACTUS ? STR_2810_CACTUS_PLANTS : STR_280E_TREES; tron@2981: } truelight@0: tron@1901: td->owner = 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@8463: 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@6669: 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@8206: 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@8463: 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: { tron@2981: switch (_opt.landscape) { belugas@6683: case LT_TROPIC: TileLoopTreesDesert(tile); break; belugas@6683: case LT_ARCTIC: TileLoopTreesAlps(tile); break; truelight@0: } truelight@0: truelight@0: TileLoopClearHelper(tile); truelight@0: truelight@8206: uint treeCounter = GetTreeCounter(tile); truelight@8206: truelight@8206: /* Handle growth of grass at every 8th processings, like it's done for grass */ truelight@8206: if ((treeCounter & 7) == 7 && GetTreeGround(tile) == TREE_GROUND_GRASS) { truelight@8206: uint density = GetTreeDensity(tile); truelight@8206: if (density < 3) { truelight@8206: SetTreeGroundDensity(tile, TREE_GROUND_GRASS, density + 1); truelight@8206: MarkTileDirtyByTile(tile); truelight@8206: } truelight@8206: } 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 */ belugas@6683: if (_opt.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@7813: tile += TileOffsByDir((Direction)(Random() & 7)); truelight@0: celestar@5573: if (!IsTileType(tile, MP_CLEAR) || IsBridgeAbove(tile)) return; tron@2981: tron@2981: switch (GetClearGround(tile)) { tron@3447: case CLEAR_GRASS: tron@2981: if (GetClearDensity(tile) != 3) return; truelight@8206: MakeTree(tile, treetype, 0, 0, TREE_GROUND_GRASS, 3); tron@2981: break; tron@2981: truelight@8206: case CLEAR_ROUGH: MakeTree(tile, treetype, 0, 0, TREE_GROUND_ROUGH, 3); break; truelight@8206: case CLEAR_DESERT: return; // Cacti don't spread truelight@8206: case CLEAR_SNOW: MakeTree(tile, treetype, 0, 0, TREE_GROUND_SNOW_DESERT, GetClearDensity(tile)); break; tron@2981: default: return; tron@2981: } 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)) { truelight@8206: case TREE_GROUND_GRASS: MakeClear(tile, CLEAR_GRASS, GetTreeDensity(tile)); break; tron@3447: case TREE_GROUND_ROUGH: MakeClear(tile, CLEAR_ROUGH, 3); break; truelight@8206: default: // snow or desert belugas@8208: MakeClear(tile, _opt.landscape == LT_TROPIC ? CLEAR_DESERT : CLEAR_SNOW, GetTreeDensity(tile)); truelight@8206: 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@2955: ClearGround ct; tron@3017: TreeType tree; truelight@0: truelight@0: /* place a tree at a random rainforest spot */ belugas@6683: if (_opt.landscape == LT_TROPIC && belugas@3379: (r = Random(), tile = RandomTileSeed(r), GetTropicZone(tile) == TROPICZONE_RAINFOREST) && tron@1035: IsTileType(tile, MP_CLEAR) && celestar@5573: !IsBridgeAbove(tile) && tron@3447: (ct = GetClearGround(tile), ct == CLEAR_GRASS || ct == CLEAR_ROUGH) && tron@3441: (tree = GetRandomTreeType(tile, GB(r, 24, 8))) != TREE_INVALID) { belugas@8208: MakeTree(tile, tree, 0, 0, ct == CLEAR_ROUGH ? TREE_GROUND_ROUGH : TREE_GROUND_GRASS, GetClearDensity(tile)); truelight@0: } truelight@0: belugas@6918: /* 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); tron@1035: if (IsTileType(tile, MP_CLEAR) && celestar@5573: !IsBridgeAbove(tile) && tron@3447: (ct = GetClearGround(tile), ct == CLEAR_GRASS || ct == CLEAR_ROUGH || ct == CLEAR_SNOW) && tron@3441: (tree = GetRandomTreeType(tile, GB(r, 24, 8))) != TREE_INVALID) { tron@2955: switch (ct) { truelight@8206: case CLEAR_GRASS: MakeTree(tile, tree, 0, 0, TREE_GROUND_GRASS, GetClearDensity(tile)); break; truelight@8206: case CLEAR_ROUGH: MakeTree(tile, tree, 0, 0, TREE_GROUND_ROUGH, 3); break; tron@3441: default: MakeTree(tile, tree, 0, 0, TREE_GROUND_SNOW_DESERT, GetClearDensity(tile)); break; truelight@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: rubidium@7179: static uint32 GetTileTrackStatus_Trees(TileIndex tile, TransportType mode, uint sub_mode) 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@7990: static CommandCost TerraformTile_Trees(TileIndex tile, uint32 flags, uint z_new, Slope tileh_new) rubidium@7990: { rubidium@7990: return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); rubidium@7990: } rubidium@7990: 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@7831: GetFoundation_Trees, /* get_foundation_proc */ rubidium@7990: TerraformTile_Trees, /* terraform_tile_proc */ truelight@0: };