OpenTTD
tree_cmd.cpp
Go to the documentation of this file.
1 /* $Id: tree_cmd.cpp 27893 2017-08-13 18:38:42Z 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  FALLTHROUGH;
379 
380  case MP_CLEAR: {
381  if (IsBridgeAbove(tile)) {
382  msg = STR_ERROR_SITE_UNSUITABLE;
383  continue;
384  }
385 
386  TreeType treetype = (TreeType)tree_to_plant;
387  /* Be a bit picky about which trees go where. */
388  if (_settings_game.game_creation.landscape == LT_TROPIC && treetype != TREE_INVALID && (
389  /* No cacti outside the desert */
390  (treetype == TREE_CACTUS && GetTropicZone(tile) != TROPICZONE_DESERT) ||
391  /* No rain forest trees outside the rain forest, except in the editor mode where it makes those tiles rain forest tile */
392  (IsInsideMM(treetype, TREE_RAINFOREST, TREE_CACTUS) && GetTropicZone(tile) != TROPICZONE_RAINFOREST && _game_mode != GM_EDITOR) ||
393  /* And no subtropical trees in the desert/rain forest */
395  msg = STR_ERROR_TREE_WRONG_TERRAIN_FOR_TREE_TYPE;
396  continue;
397  }
398 
399  /* Test tree limit. */
400  if (--limit < 1) {
401  msg = STR_ERROR_TREE_PLANT_LIMIT_REACHED;
402  break;
403  }
404 
405  if (IsTileType(tile, MP_CLEAR)) {
406  /* Remove fields or rocks. Note that the ground will get barrened */
407  switch (GetRawClearGround(tile)) {
408  case CLEAR_FIELDS:
409  case CLEAR_ROCKS: {
410  CommandCost ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
411  if (ret.Failed()) return ret;
412  cost.AddCost(ret);
413  break;
414  }
415 
416  default: break;
417  }
418  }
419 
420  if (_game_mode != GM_EDITOR && Company::IsValidID(_current_company)) {
422  if (t != NULL) ChangeTownRating(t, RATING_TREE_UP_STEP, RATING_TREE_MAXIMUM, flags);
423  }
424 
425  if (flags & DC_EXEC) {
426  if (treetype == TREE_INVALID) {
427  treetype = GetRandomTreeType(tile, GB(Random(), 24, 8));
428  if (treetype == TREE_INVALID) treetype = TREE_CACTUS;
429  }
430 
431  /* Plant full grown trees in scenario editor */
432  PlantTreesOnTile(tile, treetype, 0, _game_mode == GM_EDITOR ? 3 : 0);
433  MarkTileDirtyByTile(tile);
434  if (c != NULL) c->tree_limit -= 1 << 16;
435 
436  /* When planting rainforest-trees, set tropiczone to rainforest in editor. */
437  if (_game_mode == GM_EDITOR && IsInsideMM(treetype, TREE_RAINFOREST, TREE_CACTUS)) {
439  }
440  }
441  cost.AddCost(_price[PR_BUILD_TREES]);
442  break;
443  }
444 
445  default:
446  msg = STR_ERROR_SITE_UNSUITABLE;
447  break;
448  }
449 
450  /* Tree limit used up? No need to check more. */
451  if (limit < 0) break;
452  }
453 
454  if (cost.GetCost() == 0) {
455  return_cmd_error(msg);
456  } else {
457  return cost;
458  }
459 }
460 
462  byte x, y;
463 };
464 
465 static void DrawTile_Trees(TileInfo *ti)
466 {
467  switch (GetTreeGround(ti->tile)) {
468  case TREE_GROUND_SHORE: DrawShoreTile(ti->tileh); break;
469  case TREE_GROUND_GRASS: DrawClearLandTile(ti, GetTreeDensity(ti->tile)); break;
470  case TREE_GROUND_ROUGH: DrawHillyLandTile(ti); break;
471  default: DrawGroundSprite(_clear_land_sprites_snow_desert[GetTreeDensity(ti->tile)] + SlopeToSpriteOffset(ti->tileh), PAL_NONE); break;
472  }
473 
474  /* Do not draw trees when the invisible trees setting is set */
475  if (IsInvisibilitySet(TO_TREES)) return;
476 
477  uint tmp = CountBits(ti->tile + ti->x + ti->y);
478  uint index = GB(tmp, 0, 2) + (GetTreeType(ti->tile) << 2);
479 
480  /* different tree styles above one of the grounds */
482  GetTreeDensity(ti->tile) >= 2 &&
483  IsInsideMM(index, TREE_SUB_ARCTIC << 2, TREE_RAINFOREST << 2)) {
484  index += 164 - (TREE_SUB_ARCTIC << 2);
485  }
486 
487  assert(index < lengthof(_tree_layout_sprite));
488 
489  const PalSpriteID *s = _tree_layout_sprite[index];
490  const TreePos *d = _tree_layout_xy[GB(tmp, 2, 2)];
491 
492  /* combine trees into one sprite object */
494 
495  TreeListEnt te[4];
496 
497  /* put the trees to draw in a list */
498  uint trees = GetTreeCount(ti->tile);
499 
500  for (uint i = 0; i < trees; i++) {
501  SpriteID sprite = s[0].sprite + (i == trees - 1 ? GetTreeGrowth(ti->tile) : 3);
502  PaletteID pal = s[0].pal;
503 
504  te[i].sprite = sprite;
505  te[i].pal = pal;
506  te[i].x = d->x;
507  te[i].y = d->y;
508  s++;
509  d++;
510  }
511 
512  /* draw them in a sorted way */
513  int z = ti->z + GetSlopeMaxPixelZ(ti->tileh) / 2;
514 
515  for (; trees > 0; trees--) {
516  uint min = te[0].x + te[0].y;
517  uint mi = 0;
518 
519  for (uint i = 1; i < trees; i++) {
520  if ((uint)(te[i].x + te[i].y) < min) {
521  min = te[i].x + te[i].y;
522  mi = i;
523  }
524  }
525 
526  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);
527 
528  /* replace the removed one with the last one */
529  te[mi] = te[trees - 1];
530  }
531 
533 }
534 
535 
536 static int GetSlopePixelZ_Trees(TileIndex tile, uint x, uint y)
537 {
538  int z;
539  Slope tileh = GetTilePixelSlope(tile, &z);
540 
541  return z + GetPartialPixelZ(x & 0xF, y & 0xF, tileh);
542 }
543 
544 static Foundation GetFoundation_Trees(TileIndex tile, Slope tileh)
545 {
546  return FOUNDATION_NONE;
547 }
548 
549 static CommandCost ClearTile_Trees(TileIndex tile, DoCommandFlag flags)
550 {
551  uint num;
552 
555  if (t != NULL) ChangeTownRating(t, RATING_TREE_DOWN_STEP, RATING_TREE_MINIMUM, flags);
556  }
557 
558  num = GetTreeCount(tile);
559  if (IsInsideMM(GetTreeType(tile), TREE_RAINFOREST, TREE_CACTUS)) num *= 4;
560 
561  if (flags & DC_EXEC) DoClearSquare(tile);
562 
563  return CommandCost(EXPENSES_CONSTRUCTION, num * _price[PR_CLEAR_TREES]);
564 }
565 
566 static void GetTileDesc_Trees(TileIndex tile, TileDesc *td)
567 {
568  TreeType tt = GetTreeType(tile);
569 
571  td->str = STR_LAI_TREE_NAME_RAINFOREST;
572  } else {
573  td->str = tt == TREE_CACTUS ? STR_LAI_TREE_NAME_CACTUS_PLANTS : STR_LAI_TREE_NAME_TREES;
574  }
575 
576  td->owner[0] = GetTileOwner(tile);
577 }
578 
579 static void TileLoopTreesDesert(TileIndex tile)
580 {
581  switch (GetTropicZone(tile)) {
582  case TROPICZONE_DESERT:
583  if (GetTreeGround(tile) != TREE_GROUND_SNOW_DESERT) {
585  MarkTileDirtyByTile(tile);
586  }
587  break;
588 
589  case TROPICZONE_RAINFOREST: {
590  static const SoundFx forest_sounds[] = {
591  SND_42_LOON_BIRD,
592  SND_43_LION,
593  SND_44_MONKEYS,
594  SND_48_DISTANT_BIRD
595  };
596  uint32 r = Random();
597 
598  if (Chance16I(1, 200, r) && _settings_client.sound.ambient) SndPlayTileFx(forest_sounds[GB(r, 16, 2)], tile);
599  break;
600  }
601 
602  default: break;
603  }
604 }
605 
606 static void TileLoopTreesAlps(TileIndex tile)
607 {
608  int k = GetTileZ(tile) - GetSnowLine() + 1;
609 
610  if (k < 0) {
611  switch (GetTreeGround(tile)) {
614  default: return;
615  }
616  } else {
617  uint density = min<uint>(k, 3);
618 
621  SetTreeGroundDensity(tile, tg, density);
622  } else if (GetTreeDensity(tile) != density) {
623  SetTreeGroundDensity(tile, GetTreeGround(tile), density);
624  } else {
625  if (GetTreeDensity(tile) == 3) {
626  uint32 r = Random();
627  if (Chance16I(1, 200, r) && _settings_client.sound.ambient) {
628  SndPlayTileFx((r & 0x80000000) ? SND_39_HEAVY_WIND : SND_34_WIND, tile);
629  }
630  }
631  return;
632  }
633  }
634  MarkTileDirtyByTile(tile);
635 }
636 
637 static void TileLoop_Trees(TileIndex tile)
638 {
639  if (GetTreeGround(tile) == TREE_GROUND_SHORE) {
640  TileLoop_Water(tile);
641  } else {
643  case LT_TROPIC: TileLoopTreesDesert(tile); break;
644  case LT_ARCTIC: TileLoopTreesAlps(tile); break;
645  }
646  }
647 
648  AmbientSoundEffect(tile);
649 
650  uint treeCounter = GetTreeCounter(tile);
651 
652  /* Handle growth of grass (under trees/on MP_TREES tiles) at every 8th processings, like it's done for grass on MP_CLEAR tiles. */
653  if ((treeCounter & 7) == 7 && GetTreeGround(tile) == TREE_GROUND_GRASS) {
654  uint density = GetTreeDensity(tile);
655  if (density < 3) {
656  SetTreeGroundDensity(tile, TREE_GROUND_GRASS, density + 1);
657  MarkTileDirtyByTile(tile);
658  }
659  }
660  if (GetTreeCounter(tile) < 15) {
661  AddTreeCounter(tile, 1);
662  return;
663  }
664  SetTreeCounter(tile, 0);
665 
666  switch (GetTreeGrowth(tile)) {
667  case 3: // regular sized tree
668  if (_settings_game.game_creation.landscape == LT_TROPIC &&
669  GetTreeType(tile) != TREE_CACTUS &&
670  GetTropicZone(tile) == TROPICZONE_DESERT) {
671  AddTreeGrowth(tile, 1);
672  } else {
673  switch (GB(Random(), 0, 3)) {
674  case 0: // start destructing
675  AddTreeGrowth(tile, 1);
676  break;
677 
678  case 1: // add a tree
679  if (GetTreeCount(tile) < 4) {
680  AddTreeCount(tile, 1);
681  SetTreeGrowth(tile, 0);
682  break;
683  }
684  FALLTHROUGH;
685 
686  case 2: { // add a neighbouring tree
687  /* Don't plant extra trees if that's not allowed. */
691  break;
692  }
693 
694  TreeType treetype = GetTreeType(tile);
695 
696  tile += TileOffsByDir((Direction)(Random() & 7));
697 
698  /* Cacti don't spread */
699  if (!CanPlantTreesOnTile(tile, false)) return;
700 
701  /* Don't plant trees, if ground was freshly cleared */
702  if (IsTileType(tile, MP_CLEAR) && GetClearGround(tile) == CLEAR_GRASS && GetClearDensity(tile) != 3) return;
703 
704  PlantTreesOnTile(tile, treetype, 0, 0);
705 
706  break;
707  }
708 
709  default:
710  return;
711  }
712  }
713  break;
714 
715  case 6: // final stage of tree destruction
716  if (GetTreeCount(tile) > 1) {
717  /* more than one tree, delete it */
718  AddTreeCount(tile, -1);
719  SetTreeGrowth(tile, 3);
720  } else {
721  /* just one tree, change type into MP_CLEAR */
722  switch (GetTreeGround(tile)) {
723  case TREE_GROUND_SHORE: MakeShore(tile); break;
724  case TREE_GROUND_GRASS: MakeClear(tile, CLEAR_GRASS, GetTreeDensity(tile)); break;
725  case TREE_GROUND_ROUGH: MakeClear(tile, CLEAR_ROUGH, 3); break;
726  case TREE_GROUND_ROUGH_SNOW: {
727  uint density = GetTreeDensity(tile);
728  MakeClear(tile, CLEAR_ROUGH, 3);
729  MakeSnow(tile, density);
730  break;
731  }
732  default: // snow or desert
733  if (_settings_game.game_creation.landscape == LT_TROPIC) {
734  MakeClear(tile, CLEAR_DESERT, GetTreeDensity(tile));
735  } else {
736  uint density = GetTreeDensity(tile);
737  MakeClear(tile, CLEAR_GRASS, 3);
738  MakeSnow(tile, density);
739  }
740  break;
741  }
742  }
743  break;
744 
745  default:
746  AddTreeGrowth(tile, 1);
747  break;
748  }
749 
750  MarkTileDirtyByTile(tile);
751 }
752 
753 void OnTick_Trees()
754 {
755  /* Don't place trees if that's not allowed */
757 
758  uint32 r;
759  TileIndex tile;
760  TreeType tree;
761 
762  /* place a tree at a random rainforest spot */
763  if (_settings_game.game_creation.landscape == LT_TROPIC &&
764  (r = Random(), tile = RandomTileSeed(r), GetTropicZone(tile) == TROPICZONE_RAINFOREST) &&
765  CanPlantTreesOnTile(tile, false) &&
766  (tree = GetRandomTreeType(tile, GB(r, 24, 8))) != TREE_INVALID) {
767  PlantTreesOnTile(tile, tree, 0, 0);
768  }
769 
770  /* byte underflow */
772 
773  /* place a tree at a random spot */
774  r = Random();
775  tile = RandomTileSeed(r);
776  if (CanPlantTreesOnTile(tile, false) && (tree = GetRandomTreeType(tile, GB(r, 24, 8))) != TREE_INVALID) {
777  PlantTreesOnTile(tile, tree, 0, 0);
778  }
779 }
780 
781 static TrackStatus GetTileTrackStatus_Trees(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
782 {
783  return 0;
784 }
785 
786 static void ChangeTileOwner_Trees(TileIndex tile, Owner old_owner, Owner new_owner)
787 {
788  /* not used */
789 }
790 
791 void InitializeTrees()
792 {
793  _trees_tick_ctr = 0;
794 }
795 
796 static CommandCost TerraformTile_Trees(TileIndex tile, DoCommandFlag flags, int z_new, Slope tileh_new)
797 {
798  return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
799 }
800 
801 
802 extern const TileTypeProcs _tile_type_trees_procs = {
803  DrawTile_Trees, // draw_tile_proc
804  GetSlopePixelZ_Trees, // get_slope_z_proc
805  ClearTile_Trees, // clear_tile_proc
806  NULL, // add_accepted_cargo_proc
807  GetTileDesc_Trees, // get_tile_desc_proc
808  GetTileTrackStatus_Trees, // get_tile_track_status_proc
809  NULL, // click_tile_proc
810  NULL, // animate_tile_proc
811  TileLoop_Trees, // tile_loop_proc
812  ChangeTileOwner_Trees, // change_tile_owner_proc
813  NULL, // add_produced_cargo_proc
814  NULL, // vehicle_enter_tile_proc
815  GetFoundation_Trees, // get_foundation_proc
816  TerraformTile_Trees, // terraform_tile_proc
817 };