OpenTTD
tree_cmd.cpp
Go to the documentation of this file.
1 /* $Id: tree_cmd.cpp 27406 2015-09-30 17:28:58Z frosch $ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8  */
9 
12 #include "stdafx.h"
13 #include "clear_map.h"
14 #include "landscape.h"
15 #include "tree_map.h"
16 #include "viewport_func.h"
17 #include "command_func.h"
18 #include "town.h"
19 #include "genworld.h"
20 #include "clear_func.h"
21 #include "company_func.h"
22 #include "sound_func.h"
23 #include "water.h"
24 #include "company_base.h"
25 #include "core/random_func.hpp"
26 #include "newgrf_generic.h"
27 
28 #include "table/strings.h"
29 #include "table/tree_land.h"
30 #include "table/clear_land.h"
31 
32 #include "safeguards.h"
33 
39 enum TreePlacer {
43 };
44 
50 };
51 
54 
55 static const uint16 DEFAULT_TREE_STEPS = 1000;
56 static const uint16 DEFAULT_RAINFOREST_TREE_STEPS = 15000;
57 static const uint16 EDITOR_TREE_DIV = 5;
58 
67 static bool CanPlantTreesOnTile(TileIndex tile, bool allow_desert)
68 {
69  switch (GetTileType(tile)) {
70  case MP_WATER:
71  return !IsBridgeAbove(tile) && IsCoast(tile) && !IsSlopeWithOneCornerRaised(GetTileSlope(tile));
72 
73  case MP_CLEAR:
74  return !IsBridgeAbove(tile) && !IsClearGround(tile, CLEAR_FIELDS) && GetRawClearGround(tile) != CLEAR_ROCKS &&
75  (allow_desert || !IsClearGround(tile, CLEAR_DESERT));
76 
77  default: return false;
78  }
79 }
80 
92 static void PlantTreesOnTile(TileIndex tile, TreeType treetype, uint count, uint growth)
93 {
94  assert(treetype != TREE_INVALID);
95  assert(CanPlantTreesOnTile(tile, true));
96 
97  TreeGround ground;
98  uint density = 3;
99 
100  switch (GetTileType(tile)) {
101  case MP_WATER:
102  ground = TREE_GROUND_SHORE;
103  break;
104 
105  case MP_CLEAR:
106  switch (GetClearGround(tile)) {
107  case CLEAR_GRASS: ground = TREE_GROUND_GRASS; break;
108  case CLEAR_ROUGH: ground = TREE_GROUND_ROUGH; break;
110  default: ground = TREE_GROUND_SNOW_DESERT; break;
111  }
112  if (GetClearGround(tile) != CLEAR_ROUGH) density = GetClearDensity(tile);
113  break;
114 
115  default: NOT_REACHED();
116  }
117 
118  MakeTree(tile, treetype, count, growth, ground, density);
119 }
120 
132 static TreeType GetRandomTreeType(TileIndex tile, uint seed)
133 {
135  case LT_TEMPERATE:
136  return (TreeType)(seed * TREE_COUNT_TEMPERATE / 256 + TREE_TEMPERATE);
137 
138  case LT_ARCTIC:
139  return (TreeType)(seed * TREE_COUNT_SUB_ARCTIC / 256 + TREE_SUB_ARCTIC);
140 
141  case LT_TROPIC:
142  switch (GetTropicZone(tile)) {
144  case TROPICZONE_DESERT: return (TreeType)((seed > 12) ? TREE_INVALID : TREE_CACTUS);
145  default: return (TreeType)(seed * TREE_COUNT_RAINFOREST / 256 + TREE_RAINFOREST);
146  }
147 
148  default:
149  return (TreeType)(seed * TREE_COUNT_TOYLAND / 256 + TREE_TOYLAND);
150  }
151 }
152 
162 static void PlaceTree(TileIndex tile, uint32 r)
163 {
164  TreeType tree = GetRandomTreeType(tile, GB(r, 24, 8));
165 
166  if (tree != TREE_INVALID) {
167  PlantTreesOnTile(tile, tree, GB(r, 22, 2), min(GB(r, 16, 3), 6));
168 
169  /* Rerandomize ground, if neither snow nor shore */
170  TreeGround ground = GetTreeGround(tile);
171  if (ground != TREE_GROUND_SNOW_DESERT && ground != TREE_GROUND_ROUGH_SNOW && ground != TREE_GROUND_SHORE) {
172  SetTreeGroundDensity(tile, (TreeGround)GB(r, 28, 1), 3);
173  }
174 
175  /* Set the counter to a random start value */
176  SetTreeCounter(tile, (TreeGround)GB(r, 24, 4));
177  }
178 }
179 
186 static void PlaceTreeGroups(uint num_groups)
187 {
188  do {
189  TileIndex center_tile = RandomTile();
190 
191  for (uint i = 0; i < DEFAULT_TREE_STEPS; i++) {
192  uint32 r = Random();
193  int x = GB(r, 0, 5) - 16;
194  int y = GB(r, 8, 5) - 16;
195  uint dist = abs(x) + abs(y);
196  TileIndex cur_tile = TileAddWrap(center_tile, x, y);
197 
199 
200  if (cur_tile != INVALID_TILE && dist <= 13 && CanPlantTreesOnTile(cur_tile, true)) {
201  PlaceTree(cur_tile, r);
202  }
203  }
204 
205  } while (--num_groups);
206 }
207 
217 static void PlaceTreeAtSameHeight(TileIndex tile, int height)
218 {
219  for (uint i = 0; i < DEFAULT_TREE_STEPS; i++) {
220  uint32 r = Random();
221  int x = GB(r, 0, 5) - 16;
222  int y = GB(r, 8, 5) - 16;
223  TileIndex cur_tile = TileAddWrap(tile, x, y);
224  if (cur_tile == INVALID_TILE) continue;
225 
226  /* Keep in range of the existing tree */
227  if (abs(x) + abs(y) > 16) continue;
228 
229  /* Clear tile, no farm-tiles or rocks */
230  if (!CanPlantTreesOnTile(cur_tile, true)) continue;
231 
232  /* Not too much height difference */
233  if (Delta(GetTileZ(cur_tile), height) > 2) continue;
234 
235  /* Place one tree and quit */
236  PlaceTree(cur_tile, r);
237  break;
238  }
239 }
240 
247 {
248  int i, j, ht;
249 
251  if (_game_mode == GM_EDITOR) i /= EDITOR_TREE_DIV;
252  do {
253  uint32 r = Random();
254  TileIndex tile = RandomTileSeed(r);
255 
257 
258  if (CanPlantTreesOnTile(tile, true)) {
259  PlaceTree(tile, r);
261 
262  /* Place a number of trees based on the tile height.
263  * This gives a cool effect of multiple trees close together.
264  * It is almost real life ;) */
265  ht = GetTileZ(tile);
266  /* The higher we get, the more trees we plant */
267  j = GetTileZ(tile) * 2;
268  /* Above snowline more trees! */
269  if (_settings_game.game_creation.landscape == LT_ARCTIC && ht > GetSnowLine()) j *= 3;
270  while (j--) {
271  PlaceTreeAtSameHeight(tile, ht);
272  }
273  }
274  } while (--i);
275 
276  /* place extra trees at rainforest area */
277  if (_settings_game.game_creation.landscape == LT_TROPIC) {
279  if (_game_mode == GM_EDITOR) i /= EDITOR_TREE_DIV;
280 
281  do {
282  uint32 r = Random();
283  TileIndex tile = RandomTileSeed(r);
284 
286 
287  if (GetTropicZone(tile) == TROPICZONE_RAINFOREST && CanPlantTreesOnTile(tile, false)) {
288  PlaceTree(tile, r);
289  }
290  } while (--i);
291  }
292 }
293 
301 {
302  uint i, total;
303 
305 
307  case TP_ORIGINAL: i = _settings_game.game_creation.landscape == LT_ARCTIC ? 15 : 6; break;
308  case TP_IMPROVED: i = _settings_game.game_creation.landscape == LT_ARCTIC ? 4 : 2; break;
309  default: NOT_REACHED();
310  }
311 
314  total *= i;
315  uint num_groups = (_settings_game.game_creation.landscape != LT_TOYLAND) ? ScaleByMapSize(GB(Random(), 0, 5) + 25) : 0;
316  total += num_groups * DEFAULT_TREE_STEPS;
318 
319  if (num_groups != 0) PlaceTreeGroups(num_groups);
320 
321  for (; i != 0; i--) {
323  }
324 }
325 
335 CommandCost CmdPlantTree(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
336 {
339  const byte tree_to_plant = GB(p1, 0, 8); // We cannot use Extract as min and max are climate specific.
340 
341  if (p2 >= MapSize()) return CMD_ERROR;
342  /* Check the tree type within the current climate */
343  if (tree_to_plant != TREE_INVALID && !IsInsideBS(tree_to_plant, _tree_base_by_landscape[_settings_game.game_creation.landscape], _tree_count_by_landscape[_settings_game.game_creation.landscape])) return CMD_ERROR;
344 
345  Company *c = (_game_mode != GM_EDITOR) ? Company::GetIfValid(_current_company) : NULL;
346  int limit = (c == NULL ? INT32_MAX : GB(c->tree_limit, 16, 16));
347 
348  TileArea ta(tile, p2);
349  TILE_AREA_LOOP(tile, ta) {
350  switch (GetTileType(tile)) {
351  case MP_TREES:
352  /* no more space for trees? */
353  if (_game_mode != GM_EDITOR && GetTreeCount(tile) == 4) {
354  msg = STR_ERROR_TREE_ALREADY_HERE;
355  continue;
356  }
357 
358  /* Test tree limit. */
359  if (--limit < 1) {
360  msg = STR_ERROR_TREE_PLANT_LIMIT_REACHED;
361  break;
362  }
363 
364  if (flags & DC_EXEC) {
365  AddTreeCount(tile, 1);
366  MarkTileDirtyByTile(tile);
367  if (c != NULL) c->tree_limit -= 1 << 16;
368  }
369  /* 2x as expensive to add more trees to an existing tile */
370  cost.AddCost(_price[PR_BUILD_TREES] * 2);
371  break;
372 
373  case MP_WATER:
374  if (!IsCoast(tile) || IsSlopeWithOneCornerRaised(GetTileSlope(tile))) {
375  msg = STR_ERROR_CAN_T_BUILD_ON_WATER;
376  continue;
377  }
378  /* FALL THROUGH */
379  case MP_CLEAR: {
380  if (IsBridgeAbove(tile)) {
381  msg = STR_ERROR_SITE_UNSUITABLE;
382  continue;
383  }
384 
385  TreeType treetype = (TreeType)tree_to_plant;
386  /* Be a bit picky about which trees go where. */
387  if (_settings_game.game_creation.landscape == LT_TROPIC && treetype != TREE_INVALID && (
388  /* No cacti outside the desert */
389  (treetype == TREE_CACTUS && GetTropicZone(tile) != TROPICZONE_DESERT) ||
390  /* No rain forest trees outside the rain forest, except in the editor mode where it makes those tiles rain forest tile */
391  (IsInsideMM(treetype, TREE_RAINFOREST, TREE_CACTUS) && GetTropicZone(tile) != TROPICZONE_RAINFOREST && _game_mode != GM_EDITOR) ||
392  /* And no subtropical trees in the desert/rain forest */
394  msg = STR_ERROR_TREE_WRONG_TERRAIN_FOR_TREE_TYPE;
395  continue;
396  }
397 
398  /* Test tree limit. */
399  if (--limit < 1) {
400  msg = STR_ERROR_TREE_PLANT_LIMIT_REACHED;
401  break;
402  }
403 
404  if (IsTileType(tile, MP_CLEAR)) {
405  /* Remove fields or rocks. Note that the ground will get barrened */
406  switch (GetRawClearGround(tile)) {
407  case CLEAR_FIELDS:
408  case CLEAR_ROCKS: {
409  CommandCost ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
410  if (ret.Failed()) return ret;
411  cost.AddCost(ret);
412  break;
413  }
414 
415  default: break;
416  }
417  }
418 
419  if (_game_mode != GM_EDITOR && Company::IsValidID(_current_company)) {
421  if (t != NULL) ChangeTownRating(t, RATING_TREE_UP_STEP, RATING_TREE_MAXIMUM, flags);
422  }
423 
424  if (flags & DC_EXEC) {
425  if (treetype == TREE_INVALID) {
426  treetype = GetRandomTreeType(tile, GB(Random(), 24, 8));
427  if (treetype == TREE_INVALID) treetype = TREE_CACTUS;
428  }
429 
430  /* Plant full grown trees in scenario editor */
431  PlantTreesOnTile(tile, treetype, 0, _game_mode == GM_EDITOR ? 3 : 0);
432  MarkTileDirtyByTile(tile);
433  if (c != NULL) c->tree_limit -= 1 << 16;
434 
435  /* When planting rainforest-trees, set tropiczone to rainforest in editor. */
436  if (_game_mode == GM_EDITOR && IsInsideMM(treetype, TREE_RAINFOREST, TREE_CACTUS)) {
438  }
439  }
440  cost.AddCost(_price[PR_BUILD_TREES]);
441  break;
442  }
443 
444  default:
445  msg = STR_ERROR_SITE_UNSUITABLE;
446  break;
447  }
448 
449  /* Tree limit used up? No need to check more. */
450  if (limit < 0) break;
451  }
452 
453  if (cost.GetCost() == 0) {
454  return_cmd_error(msg);
455  } else {
456  return cost;
457  }
458 }
459 
461  byte x, y;
462 };
463 
464 static void DrawTile_Trees(TileInfo *ti)
465 {
466  switch (GetTreeGround(ti->tile)) {
467  case TREE_GROUND_SHORE: DrawShoreTile(ti->tileh); break;
468  case TREE_GROUND_GRASS: DrawClearLandTile(ti, GetTreeDensity(ti->tile)); break;
469  case TREE_GROUND_ROUGH: DrawHillyLandTile(ti); break;
470  default: DrawGroundSprite(_clear_land_sprites_snow_desert[GetTreeDensity(ti->tile)] + SlopeToSpriteOffset(ti->tileh), PAL_NONE); break;
471  }
472 
473  /* Do not draw trees when the invisible trees setting is set */
474  if (IsInvisibilitySet(TO_TREES)) return;
475 
476  uint tmp = CountBits(ti->tile + ti->x + ti->y);
477  uint index = GB(tmp, 0, 2) + (GetTreeType(ti->tile) << 2);
478 
479  /* different tree styles above one of the grounds */
481  GetTreeDensity(ti->tile) >= 2 &&
482  IsInsideMM(index, TREE_SUB_ARCTIC << 2, TREE_RAINFOREST << 2)) {
483  index += 164 - (TREE_SUB_ARCTIC << 2);
484  }
485 
486  assert(index < lengthof(_tree_layout_sprite));
487 
488  const PalSpriteID *s = _tree_layout_sprite[index];
489  const TreePos *d = _tree_layout_xy[GB(tmp, 2, 2)];
490 
491  /* combine trees into one sprite object */
493 
494  TreeListEnt te[4];
495 
496  /* put the trees to draw in a list */
497  uint trees = GetTreeCount(ti->tile);
498 
499  for (uint i = 0; i < trees; i++) {
500  SpriteID sprite = s[0].sprite + (i == trees - 1 ? GetTreeGrowth(ti->tile) : 3);
501  PaletteID pal = s[0].pal;
502 
503  te[i].sprite = sprite;
504  te[i].pal = pal;
505  te[i].x = d->x;
506  te[i].y = d->y;
507  s++;
508  d++;
509  }
510 
511  /* draw them in a sorted way */
512  int z = ti->z + GetSlopeMaxPixelZ(ti->tileh) / 2;
513 
514  for (; trees > 0; trees--) {
515  uint min = te[0].x + te[0].y;
516  uint mi = 0;
517 
518  for (uint i = 1; i < trees; i++) {
519  if ((uint)(te[i].x + te[i].y) < min) {
520  min = te[i].x + te[i].y;
521  mi = i;
522  }
523  }
524 
525  AddSortableSpriteToDraw(te[mi].sprite, 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);
526 
527  /* replace the removed one with the last one */
528  te[mi] = te[trees - 1];
529  }
530 
532 }
533 
534 
535 static int GetSlopePixelZ_Trees(TileIndex tile, uint x, uint y)
536 {
537  int z;
538  Slope tileh = GetTilePixelSlope(tile, &z);
539 
540  return z + GetPartialPixelZ(x & 0xF, y & 0xF, tileh);
541 }
542 
543 static Foundation GetFoundation_Trees(TileIndex tile, Slope tileh)
544 {
545  return FOUNDATION_NONE;
546 }
547 
548 static CommandCost ClearTile_Trees(TileIndex tile, DoCommandFlag flags)
549 {
550  uint num;
551 
554  if (t != NULL) ChangeTownRating(t, RATING_TREE_DOWN_STEP, RATING_TREE_MINIMUM, flags);
555  }
556 
557  num = GetTreeCount(tile);
558  if (IsInsideMM(GetTreeType(tile), TREE_RAINFOREST, TREE_CACTUS)) num *= 4;
559 
560  if (flags & DC_EXEC) DoClearSquare(tile);
561 
562  return CommandCost(EXPENSES_CONSTRUCTION, num * _price[PR_CLEAR_TREES]);
563 }
564 
565 static void GetTileDesc_Trees(TileIndex tile, TileDesc *td)
566 {
567  TreeType tt = GetTreeType(tile);
568 
570  td->str = STR_LAI_TREE_NAME_RAINFOREST;
571  } else {
572  td->str = tt == TREE_CACTUS ? STR_LAI_TREE_NAME_CACTUS_PLANTS : STR_LAI_TREE_NAME_TREES;
573  }
574 
575  td->owner[0] = GetTileOwner(tile);
576 }
577 
578 static void TileLoopTreesDesert(TileIndex tile)
579 {
580  switch (GetTropicZone(tile)) {
581  case TROPICZONE_DESERT:
582  if (GetTreeGround(tile) != TREE_GROUND_SNOW_DESERT) {
584  MarkTileDirtyByTile(tile);
585  }
586  break;
587 
588  case TROPICZONE_RAINFOREST: {
589  static const SoundFx forest_sounds[] = {
590  SND_42_LOON_BIRD,
591  SND_43_LION,
592  SND_44_MONKEYS,
593  SND_48_DISTANT_BIRD
594  };
595  uint32 r = Random();
596 
597  if (Chance16I(1, 200, r) && _settings_client.sound.ambient) SndPlayTileFx(forest_sounds[GB(r, 16, 2)], tile);
598  break;
599  }
600 
601  default: break;
602  }
603 }
604 
605 static void TileLoopTreesAlps(TileIndex tile)
606 {
607  int k = GetTileZ(tile) - GetSnowLine() + 1;
608 
609  if (k < 0) {
610  switch (GetTreeGround(tile)) {
613  default: return;
614  }
615  } else {
616  uint density = min<uint>(k, 3);
617 
620  SetTreeGroundDensity(tile, tg, density);
621  } else if (GetTreeDensity(tile) != density) {
622  SetTreeGroundDensity(tile, GetTreeGround(tile), density);
623  } else {
624  if (GetTreeDensity(tile) == 3) {
625  uint32 r = Random();
626  if (Chance16I(1, 200, r) && _settings_client.sound.ambient) {
627  SndPlayTileFx((r & 0x80000000) ? SND_39_HEAVY_WIND : SND_34_WIND, tile);
628  }
629  }
630  return;
631  }
632  }
633  MarkTileDirtyByTile(tile);
634 }
635 
636 static void TileLoop_Trees(TileIndex tile)
637 {
638  if (GetTreeGround(tile) == TREE_GROUND_SHORE) {
639  TileLoop_Water(tile);
640  } else {
642  case LT_TROPIC: TileLoopTreesDesert(tile); break;
643  case LT_ARCTIC: TileLoopTreesAlps(tile); break;
644  }
645  }
646 
647  AmbientSoundEffect(tile);
648 
649  uint treeCounter = GetTreeCounter(tile);
650 
651  /* Handle growth of grass (under trees/on MP_TREES tiles) at every 8th processings, like it's done for grass on MP_CLEAR tiles. */
652  if ((treeCounter & 7) == 7 && GetTreeGround(tile) == TREE_GROUND_GRASS) {
653  uint density = GetTreeDensity(tile);
654  if (density < 3) {
655  SetTreeGroundDensity(tile, TREE_GROUND_GRASS, density + 1);
656  MarkTileDirtyByTile(tile);
657  }
658  }
659  if (GetTreeCounter(tile) < 15) {
660  AddTreeCounter(tile, 1);
661  return;
662  }
663  SetTreeCounter(tile, 0);
664 
665  switch (GetTreeGrowth(tile)) {
666  case 3: // regular sized tree
667  if (_settings_game.game_creation.landscape == LT_TROPIC &&
668  GetTreeType(tile) != TREE_CACTUS &&
669  GetTropicZone(tile) == TROPICZONE_DESERT) {
670  AddTreeGrowth(tile, 1);
671  } else {
672  switch (GB(Random(), 0, 3)) {
673  case 0: // start destructing
674  AddTreeGrowth(tile, 1);
675  break;
676 
677  case 1: // add a tree
678  if (GetTreeCount(tile) < 4) {
679  AddTreeCount(tile, 1);
680  SetTreeGrowth(tile, 0);
681  break;
682  }
683  /* FALL THROUGH */
684 
685  case 2: { // add a neighbouring tree
686  /* Don't plant extra trees if that's not allowed. */
690  break;
691  }
692 
693  TreeType treetype = GetTreeType(tile);
694 
695  tile += TileOffsByDir((Direction)(Random() & 7));
696 
697  /* Cacti don't spread */
698  if (!CanPlantTreesOnTile(tile, false)) return;
699 
700  /* Don't plant trees, if ground was freshly cleared */
701  if (IsTileType(tile, MP_CLEAR) && GetClearGround(tile) == CLEAR_GRASS && GetClearDensity(tile) != 3) return;
702 
703  PlantTreesOnTile(tile, treetype, 0, 0);
704 
705  break;
706  }
707 
708  default:
709  return;
710  }
711  }
712  break;
713 
714  case 6: // final stage of tree destruction
715  if (GetTreeCount(tile) > 1) {
716  /* more than one tree, delete it */
717  AddTreeCount(tile, -1);
718  SetTreeGrowth(tile, 3);
719  } else {
720  /* just one tree, change type into MP_CLEAR */
721  switch (GetTreeGround(tile)) {
722  case TREE_GROUND_SHORE: MakeShore(tile); break;
723  case TREE_GROUND_GRASS: MakeClear(tile, CLEAR_GRASS, GetTreeDensity(tile)); break;
724  case TREE_GROUND_ROUGH: MakeClear(tile, CLEAR_ROUGH, 3); break;
725  case TREE_GROUND_ROUGH_SNOW: {
726  uint density = GetTreeDensity(tile);
727  MakeClear(tile, CLEAR_ROUGH, 3);
728  MakeSnow(tile, density);
729  break;
730  }
731  default: // snow or desert
732  if (_settings_game.game_creation.landscape == LT_TROPIC) {
733  MakeClear(tile, CLEAR_DESERT, GetTreeDensity(tile));
734  } else {
735  uint density = GetTreeDensity(tile);
736  MakeClear(tile, CLEAR_GRASS, 3);
737  MakeSnow(tile, density);
738  }
739  break;
740  }
741  }
742  break;
743 
744  default:
745  AddTreeGrowth(tile, 1);
746  break;
747  }
748 
749  MarkTileDirtyByTile(tile);
750 }
751 
752 void OnTick_Trees()
753 {
754  /* Don't place trees if that's not allowed */
756 
757  uint32 r;
758  TileIndex tile;
759  TreeType tree;
760 
761  /* place a tree at a random rainforest spot */
762  if (_settings_game.game_creation.landscape == LT_TROPIC &&
763  (r = Random(), tile = RandomTileSeed(r), GetTropicZone(tile) == TROPICZONE_RAINFOREST) &&
764  CanPlantTreesOnTile(tile, false) &&
765  (tree = GetRandomTreeType(tile, GB(r, 24, 8))) != TREE_INVALID) {
766  PlantTreesOnTile(tile, tree, 0, 0);
767  }
768 
769  /* byte underflow */
771 
772  /* place a tree at a random spot */
773  r = Random();
774  tile = RandomTileSeed(r);
775  if (CanPlantTreesOnTile(tile, false) && (tree = GetRandomTreeType(tile, GB(r, 24, 8))) != TREE_INVALID) {
776  PlantTreesOnTile(tile, tree, 0, 0);
777  }
778 }
779 
780 static TrackStatus GetTileTrackStatus_Trees(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
781 {
782  return 0;
783 }
784 
785 static void ChangeTileOwner_Trees(TileIndex tile, Owner old_owner, Owner new_owner)
786 {
787  /* not used */
788 }
789 
790 void InitializeTrees()
791 {
792  _trees_tick_ctr = 0;
793 }
794 
795 static CommandCost TerraformTile_Trees(TileIndex tile, DoCommandFlag flags, int z_new, Slope tileh_new)
796 {
797  return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
798 }
799 
800 
801 extern const TileTypeProcs _tile_type_trees_procs = {
802  DrawTile_Trees, // draw_tile_proc
803  GetSlopePixelZ_Trees, // get_slope_z_proc
804  ClearTile_Trees, // clear_tile_proc
805  NULL, // add_accepted_cargo_proc
806  GetTileDesc_Trees, // get_tile_desc_proc
807  GetTileTrackStatus_Trees, // get_tile_track_status_proc
808  NULL, // click_tile_proc
809  NULL, // animate_tile_proc
810  TileLoop_Trees, // tile_loop_proc
811  ChangeTileOwner_Trees, // change_tile_owner_proc
812  NULL, // add_produced_cargo_proc
813  NULL, // vehicle_enter_tile_proc
814  GetFoundation_Trees, // get_foundation_proc
815  TerraformTile_Trees, // terraform_tile_proc
816 };