OpenTTD Source 20241224-master-gf74b0cf984
tree_cmd.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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/>.
6 */
7
10#include "stdafx.h"
11#include "clear_map.h"
12#include "landscape.h"
13#include "tree_map.h"
14#include "viewport_func.h"
15#include "command_func.h"
16#include "town.h"
17#include "genworld.h"
18#include "clear_func.h"
19#include "company_func.h"
20#include "sound_func.h"
21#include "water.h"
22#include "company_base.h"
23#include "core/random_func.hpp"
24#include "newgrf_generic.h"
26#include "tree_cmd.h"
27#include "landscape_cmd.h"
28
29#include "table/strings.h"
30#include "table/tree_land.h"
31#include "table/clear_land.h"
32
33#include "safeguards.h"
34
45
53
56
57static const uint16_t DEFAULT_TREE_STEPS = 1000;
58static const uint16_t DEFAULT_RAINFOREST_TREE_STEPS = 15000;
59static const uint16_t EDITOR_TREE_DIV = 5;
60
69static bool CanPlantTreesOnTile(TileIndex tile, bool allow_desert)
70{
71 switch (GetTileType(tile)) {
72 case MP_WATER:
73 return !IsBridgeAbove(tile) && IsCoast(tile) && !IsSlopeWithOneCornerRaised(GetTileSlope(tile));
74
75 case MP_CLEAR:
76 return !IsBridgeAbove(tile) && !IsClearGround(tile, CLEAR_FIELDS) && GetRawClearGround(tile) != CLEAR_ROCKS &&
77 (allow_desert || !IsClearGround(tile, CLEAR_DESERT));
78
79 default: return false;
80 }
81}
82
94static void PlantTreesOnTile(TileIndex tile, TreeType treetype, uint count, TreeGrowthStage growth)
95{
96 assert(treetype != TREE_INVALID);
97 assert(CanPlantTreesOnTile(tile, true));
98
99 TreeGround ground;
100 uint density = 3;
101
102 switch (GetTileType(tile)) {
103 case MP_WATER:
104 ground = TREE_GROUND_SHORE;
106 break;
107
108 case MP_CLEAR:
109 switch (GetClearGround(tile)) {
110 case CLEAR_GRASS: ground = TREE_GROUND_GRASS; break;
111 case CLEAR_ROUGH: ground = TREE_GROUND_ROUGH; break;
113 default: ground = TREE_GROUND_SNOW_DESERT; break;
114 }
115 if (GetClearGround(tile) != CLEAR_ROUGH) density = GetClearDensity(tile);
116 break;
117
118 default: NOT_REACHED();
119 }
120
121 MakeTree(tile, treetype, count, growth, ground, density);
122}
123
135static TreeType GetRandomTreeType(TileIndex tile, uint seed)
136{
138 case LT_TEMPERATE:
139 return static_cast<TreeType>(seed * TREE_COUNT_TEMPERATE / 256 + TREE_TEMPERATE);
140
141 case LT_ARCTIC:
142 return static_cast<TreeType>(seed * TREE_COUNT_SUB_ARCTIC / 256 + TREE_SUB_ARCTIC);
143
144 case LT_TROPIC:
145 switch (GetTropicZone(tile)) {
146 case TROPICZONE_NORMAL: return static_cast<TreeType>(seed * TREE_COUNT_SUB_TROPICAL / 256 + TREE_SUB_TROPICAL);
147 case TROPICZONE_DESERT: return static_cast<TreeType>((seed > 12) ? TREE_INVALID : TREE_CACTUS);
148 default: return static_cast<TreeType>(seed * TREE_COUNT_RAINFOREST / 256 + TREE_RAINFOREST);
149 }
150
151 default:
152 return static_cast<TreeType>(seed * TREE_COUNT_TOYLAND / 256 + TREE_TOYLAND);
153 }
154}
155
165static void PlaceTree(TileIndex tile, uint32_t r)
166{
167 TreeType tree = GetRandomTreeType(tile, GB(r, 24, 8));
168
169 if (tree != TREE_INVALID) {
170 PlantTreesOnTile(tile, tree, GB(r, 22, 2), static_cast<TreeGrowthStage>(std::min<uint8_t>(GB(r, 16, 3), 6)));
172
173 /* Rerandomize ground, if neither snow nor shore */
174 TreeGround ground = GetTreeGround(tile);
175 if (ground != TREE_GROUND_SNOW_DESERT && ground != TREE_GROUND_ROUGH_SNOW && ground != TREE_GROUND_SHORE) {
176 SetTreeGroundDensity(tile, (TreeGround)GB(r, 28, 1), 3);
177 }
178 }
179}
180
187static void PlaceTreeGroups(uint num_groups)
188{
189 do {
190 TileIndex center_tile = RandomTile();
191
192 for (uint i = 0; i < DEFAULT_TREE_STEPS; i++) {
193 uint32_t r = Random();
194 int x = GB(r, 0, 5) - 16;
195 int y = GB(r, 8, 5) - 16;
196 uint dist = abs(x) + abs(y);
197 TileIndex cur_tile = TileAddWrap(center_tile, x, y);
198
200
201 if (cur_tile != INVALID_TILE && dist <= 13 && CanPlantTreesOnTile(cur_tile, true)) {
202 PlaceTree(cur_tile, r);
203 }
204 }
205
206 } while (--num_groups);
207}
208
218static void PlaceTreeAtSameHeight(TileIndex tile, int height)
219{
220 for (uint i = 0; i < DEFAULT_TREE_STEPS; i++) {
221 uint32_t r = Random();
222 int x = GB(r, 0, 5) - 16;
223 int y = GB(r, 8, 5) - 16;
224 TileIndex cur_tile = TileAddWrap(tile, x, y);
225 if (cur_tile == INVALID_TILE) continue;
226
227 /* Keep in range of the existing tree */
228 if (abs(x) + abs(y) > 16) continue;
229
230 /* Clear tile, no farm-tiles or rocks */
231 if (!CanPlantTreesOnTile(cur_tile, true)) continue;
232
233 /* Not too much height difference */
234 if (Delta(GetTileZ(cur_tile), height) > 2) continue;
235
236 /* Place one tree and quit */
237 PlaceTree(cur_tile, r);
238 break;
239 }
240}
241
248{
249 int i, j, ht;
250
252 if (_game_mode == GM_EDITOR) i /= EDITOR_TREE_DIV;
253 do {
254 uint32_t r = Random();
255 TileIndex tile = RandomTileSeed(r);
256
258
259 if (CanPlantTreesOnTile(tile, true)) {
260 PlaceTree(tile, r);
262
263 /* Place a number of trees based on the tile height.
264 * This gives a cool effect of multiple trees close together.
265 * It is almost real life ;) */
266 ht = GetTileZ(tile);
267 /* The higher we get, the more trees we plant */
268 j = GetTileZ(tile) * 2;
269 /* Above snowline more trees! */
270 if (_settings_game.game_creation.landscape == LT_ARCTIC && ht > GetSnowLine()) j *= 3;
271 while (j--) {
272 PlaceTreeAtSameHeight(tile, ht);
273 }
274 }
275 } while (--i);
276
277 /* place extra trees at rainforest area */
278 if (_settings_game.game_creation.landscape == LT_TROPIC) {
280 if (_game_mode == GM_EDITOR) i /= EDITOR_TREE_DIV;
281
282 do {
283 uint32_t r = Random();
284 TileIndex tile = RandomTileSeed(r);
285
287
288 if (GetTropicZone(tile) == TROPICZONE_RAINFOREST && CanPlantTreesOnTile(tile, false)) {
289 PlaceTree(tile, r);
290 }
291 } while (--i);
292 }
293}
294
307uint PlaceTreeGroupAroundTile(TileIndex tile, TreeType treetype, uint radius, uint count, bool set_zone)
308{
309 assert(_game_mode == GM_EDITOR); // Due to InteractiveRandom being used in this function
310 assert(treetype < TREE_TOYLAND + TREE_COUNT_TOYLAND);
311 const bool allow_desert = treetype == TREE_CACTUS;
312 uint planted = 0;
313
314 for (; count > 0; count--) {
315 /* Simple quasi-normal distribution with range [-radius; radius) */
316 auto mkcoord = [&]() -> int32_t {
317 const uint32_t rand = InteractiveRandom();
318 const int32_t dist = GB<int32_t>(rand, 0, 8) + GB<int32_t>(rand, 8, 8) + GB<int32_t>(rand, 16, 8) + GB<int32_t>(rand, 24, 8);
319 const int32_t scu = dist * radius / 512;
320 return scu - radius;
321 };
322 const int32_t xofs = mkcoord();
323 const int32_t yofs = mkcoord();
324 const TileIndex tile_to_plant = TileAddWrap(tile, xofs, yofs);
325 if (tile_to_plant != INVALID_TILE) {
326 if (IsTileType(tile_to_plant, MP_TREES) && GetTreeCount(tile_to_plant) < 4) {
327 AddTreeCount(tile_to_plant, 1);
329 MarkTileDirtyByTile(tile_to_plant, 0);
330 planted++;
331 } else if (CanPlantTreesOnTile(tile_to_plant, allow_desert)) {
332 PlantTreesOnTile(tile_to_plant, treetype, 0, TreeGrowthStage::Grown);
333 MarkTileDirtyByTile(tile_to_plant, 0);
334 planted++;
335 }
336 }
337 }
338
339 if (set_zone && IsInsideMM(treetype, TREE_RAINFOREST, TREE_CACTUS)) {
340 for (TileIndex t : TileArea(tile).Expand(radius)) {
341 if (GetTileType(t) != MP_VOID && DistanceSquare(tile, t) < radius * radius) SetTropicZone(t, TROPICZONE_RAINFOREST);
342 }
343 }
344
345 return planted;
346}
347
355{
356 uint i, total;
357
359
361 case TP_ORIGINAL: i = _settings_game.game_creation.landscape == LT_ARCTIC ? 15 : 6; break;
362 case TP_IMPROVED: i = _settings_game.game_creation.landscape == LT_ARCTIC ? 4 : 2; break;
363 default: NOT_REACHED();
364 }
365
368 total *= i;
369 uint num_groups = (_settings_game.game_creation.landscape != LT_TOYLAND) ? Map::ScaleBySize(GB(Random(), 0, 5) + 25) : 0;
370 total += num_groups * DEFAULT_TREE_STEPS;
372
373 if (num_groups != 0) PlaceTreeGroups(num_groups);
374
375 for (; i != 0; i--) {
377 }
378}
379
389CommandCost CmdPlantTree(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, uint8_t tree_to_plant, bool diagonal)
390{
393
394 if (start_tile >= Map::Size()) return CMD_ERROR;
395 /* Check the tree type within the current climate */
396 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;
397
398 Company *c = (_game_mode != GM_EDITOR) ? Company::GetIfValid(_current_company) : nullptr;
399 int limit = (c == nullptr ? INT32_MAX : GB(c->tree_limit, 16, 16));
400
401 std::unique_ptr<TileIterator> iter = TileIterator::Create(tile, start_tile, diagonal);
402 for (; *iter != INVALID_TILE; ++(*iter)) {
403 TileIndex current_tile = *iter;
404 switch (GetTileType(current_tile)) {
405 case MP_TREES:
406 /* no more space for trees? */
407 if (GetTreeCount(current_tile) == 4) {
408 msg = STR_ERROR_TREE_ALREADY_HERE;
409 continue;
410 }
411
412 /* Test tree limit. */
413 if (--limit < 1) {
414 msg = STR_ERROR_TREE_PLANT_LIMIT_REACHED;
415 break;
416 }
417
418 if (flags & DC_EXEC) {
419 AddTreeCount(current_tile, 1);
420 MarkTileDirtyByTile(current_tile);
421 if (c != nullptr) c->tree_limit -= 1 << 16;
422 }
423 /* 2x as expensive to add more trees to an existing tile */
424 cost.AddCost(_price[PR_BUILD_TREES] * 2);
425 break;
426
427 case MP_WATER:
428 if (!IsCoast(current_tile) || IsSlopeWithOneCornerRaised(GetTileSlope(current_tile))) {
429 msg = STR_ERROR_CAN_T_BUILD_ON_WATER;
430 continue;
431 }
432 [[fallthrough]];
433
434 case MP_CLEAR: {
435 if (IsBridgeAbove(current_tile)) {
436 msg = STR_ERROR_SITE_UNSUITABLE;
437 continue;
438 }
439
440 TreeType treetype = (TreeType)tree_to_plant;
441 /* Be a bit picky about which trees go where. */
442 if (_settings_game.game_creation.landscape == LT_TROPIC && treetype != TREE_INVALID && (
443 /* No cacti outside the desert */
444 (treetype == TREE_CACTUS && GetTropicZone(current_tile) != TROPICZONE_DESERT) ||
445 /* No rain forest trees outside the rain forest, except in the editor mode where it makes those tiles rain forest tile */
446 (IsInsideMM(treetype, TREE_RAINFOREST, TREE_CACTUS) && GetTropicZone(current_tile) != TROPICZONE_RAINFOREST && _game_mode != GM_EDITOR) ||
447 /* And no subtropical trees in the desert/rain forest */
448 (IsInsideMM(treetype, TREE_SUB_TROPICAL, TREE_TOYLAND) && GetTropicZone(current_tile) != TROPICZONE_NORMAL))) {
449 msg = STR_ERROR_TREE_WRONG_TERRAIN_FOR_TREE_TYPE;
450 continue;
451 }
452
453 /* Test tree limit. */
454 if (--limit < 1) {
455 msg = STR_ERROR_TREE_PLANT_LIMIT_REACHED;
456 break;
457 }
458
459 if (IsTileType(current_tile, MP_CLEAR)) {
460 /* Remove fields or rocks. Note that the ground will get barrened */
461 switch (GetRawClearGround(current_tile)) {
462 case CLEAR_FIELDS:
463 case CLEAR_ROCKS: {
464 CommandCost ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, current_tile);
465 if (ret.Failed()) return ret;
466 cost.AddCost(ret);
467 break;
468 }
469
470 default: break;
471 }
472 }
473
474 if (_game_mode != GM_EDITOR && Company::IsValidID(_current_company)) {
476 if (t != nullptr) ChangeTownRating(t, RATING_TREE_UP_STEP, RATING_TREE_MAXIMUM, flags);
477 }
478
479 if (flags & DC_EXEC) {
480 if (treetype == TREE_INVALID) {
481 treetype = GetRandomTreeType(current_tile, GB(Random(), 24, 8));
482 if (treetype == TREE_INVALID) treetype = TREE_CACTUS;
483 }
484
485 /* Plant full grown trees in scenario editor */
486 PlantTreesOnTile(current_tile, treetype, 0, _game_mode == GM_EDITOR ? TreeGrowthStage::Grown : TreeGrowthStage::Growing1);
487 MarkTileDirtyByTile(current_tile);
488 if (c != nullptr) c->tree_limit -= 1 << 16;
489
490 /* When planting rainforest-trees, set tropiczone to rainforest in editor. */
491 if (_game_mode == GM_EDITOR && IsInsideMM(treetype, TREE_RAINFOREST, TREE_CACTUS)) {
493 }
494 }
495 cost.AddCost(_price[PR_BUILD_TREES]);
496 break;
497 }
498
499 default:
500 msg = STR_ERROR_SITE_UNSUITABLE;
501 break;
502 }
503
504 /* Tree limit used up? No need to check more. */
505 if (limit < 0) break;
506 }
507
508 if (cost.GetCost() == 0) {
509 return CommandCost(msg);
510 } else {
511 return cost;
512 }
513}
514
516 uint8_t x, y;
517};
518
519static void DrawTile_Trees(TileInfo *ti)
520{
521 switch (GetTreeGround(ti->tile)) {
522 case TREE_GROUND_SHORE: DrawShoreTile(ti->tileh); break;
523 case TREE_GROUND_GRASS: DrawClearLandTile(ti, GetTreeDensity(ti->tile)); break;
524 case TREE_GROUND_ROUGH: DrawHillyLandTile(ti); break;
525 default: DrawGroundSprite(_clear_land_sprites_snow_desert[GetTreeDensity(ti->tile)] + SlopeToSpriteOffset(ti->tileh), PAL_NONE); break;
526 }
527
528 /* Do not draw trees when the invisible trees setting is set */
529 if (IsInvisibilitySet(TO_TREES)) return;
530
531 uint tmp = CountBits(ti->tile.base() + ti->x + ti->y);
532 uint index = GB(tmp, 0, 2) + (GetTreeType(ti->tile) << 2);
533
534 /* different tree styles above one of the grounds */
536 GetTreeDensity(ti->tile) >= 2 &&
537 IsInsideMM(index, TREE_SUB_ARCTIC << 2, TREE_RAINFOREST << 2)) {
538 index += 164 - (TREE_SUB_ARCTIC << 2);
539 }
540
541 assert(index < lengthof(_tree_layout_sprite));
542
543 const PalSpriteID *s = _tree_layout_sprite[index];
544 const TreePos *d = _tree_layout_xy[GB(tmp, 2, 2)];
545
546 /* combine trees into one sprite object */
548
549 TreeListEnt te[4];
550
551 /* put the trees to draw in a list */
552 uint trees = GetTreeCount(ti->tile);
553
554 for (uint i = 0; i < trees; i++) {
555 SpriteID sprite = s[0].sprite + (i == trees - 1 ? static_cast<uint>(GetTreeGrowth(ti->tile)) : 3);
556 PaletteID pal = s[0].pal;
557
558 te[i].sprite = sprite;
559 te[i].pal = pal;
560 te[i].x = d->x;
561 te[i].y = d->y;
562 s++;
563 d++;
564 }
565
566 /* draw them in a sorted way */
567 int z = ti->z + GetSlopeMaxPixelZ(ti->tileh) / 2;
568
569 for (; trees > 0; trees--) {
570 uint min = te[0].x + te[0].y;
571 uint mi = 0;
572
573 for (uint i = 1; i < trees; i++) {
574 if ((uint)(te[i].x + te[i].y) < min) {
575 min = te[i].x + te[i].y;
576 mi = i;
577 }
578 }
579
580 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);
581
582 /* replace the removed one with the last one */
583 te[mi] = te[trees - 1];
584 }
585
587}
588
589
590static int GetSlopePixelZ_Trees(TileIndex tile, uint x, uint y, bool)
591{
592 auto [tileh, z] = GetTilePixelSlope(tile);
593
594 return z + GetPartialPixelZ(x & 0xF, y & 0xF, tileh);
595}
596
597static Foundation GetFoundation_Trees(TileIndex, Slope)
598{
599 return FOUNDATION_NONE;
600}
601
602static CommandCost ClearTile_Trees(TileIndex tile, DoCommandFlag flags)
603{
604 uint num;
605
608 if (t != nullptr) ChangeTownRating(t, RATING_TREE_DOWN_STEP, RATING_TREE_MINIMUM, flags);
609 }
610
611 num = GetTreeCount(tile);
612 if (IsInsideMM(GetTreeType(tile), TREE_RAINFOREST, TREE_CACTUS)) num *= 4;
613
614 if (flags & DC_EXEC) DoClearSquare(tile);
615
616 return CommandCost(EXPENSES_CONSTRUCTION, num * _price[PR_CLEAR_TREES]);
617}
618
619static void GetTileDesc_Trees(TileIndex tile, TileDesc *td)
620{
621 TreeType tt = GetTreeType(tile);
622
624 td->str = STR_LAI_TREE_NAME_RAINFOREST;
625 } else {
626 td->str = tt == TREE_CACTUS ? STR_LAI_TREE_NAME_CACTUS_PLANTS : STR_LAI_TREE_NAME_TREES;
627 }
628
629 td->owner[0] = GetTileOwner(tile);
630}
631
632static void TileLoopTreesDesert(TileIndex tile)
633{
634 switch (GetTropicZone(tile)) {
639 }
640 break;
641
643 static const SoundFx forest_sounds[] = {
648 };
649 uint32_t r = Random();
650
651 if (Chance16I(1, 200, r) && _settings_client.sound.ambient) SndPlayTileFx(forest_sounds[GB(r, 16, 2)], tile);
652 break;
653 }
654
655 default: break;
656 }
657}
658
659static void TileLoopTreesAlps(TileIndex tile)
660{
661 int k = GetTileZ(tile) - GetSnowLine() + 1;
662
663 if (k < 0) {
664 switch (GetTreeGround(tile)) {
667 default: return;
668 }
669 } else {
670 uint density = std::min<uint>(k, 3);
671
674 SetTreeGroundDensity(tile, tg, density);
675 } else if (GetTreeDensity(tile) != density) {
676 SetTreeGroundDensity(tile, GetTreeGround(tile), density);
677 } else {
678 if (GetTreeDensity(tile) == 3) {
679 uint32_t r = Random();
680 if (Chance16I(1, 200, r) && _settings_client.sound.ambient) {
681 SndPlayTileFx((r & 0x80000000) ? SND_39_ARCTIC_SNOW_2 : SND_34_ARCTIC_SNOW_1, tile);
682 }
683 }
684 return;
685 }
686 }
688}
689
690static bool CanPlantExtraTrees(TileIndex tile)
691{
694 _settings_game.construction.extra_tree_placement == ETP_SPREAD_ALL);
695}
696
697static void TileLoop_Trees(TileIndex tile)
698{
699 if (GetTreeGround(tile) == TREE_GROUND_SHORE) {
700 TileLoop_Water(tile);
701 } else {
703 case LT_TROPIC: TileLoopTreesDesert(tile); break;
704 case LT_ARCTIC: TileLoopTreesAlps(tile); break;
705 }
706 }
707
708 AmbientSoundEffect(tile);
709
710 /* TimerGameTick::counter is incremented by 256 between each call, so ignore lower 8 bits.
711 * Also, we use a simple hash to spread the updates evenly over the map.
712 * 11 and 9 are just some co-prime numbers for better spread.
713 */
714 uint32_t cycle = 11 * TileX(tile) + 9 * TileY(tile) + (TimerGameTick::counter >> 8);
715
716 /* Handle growth of grass (under trees/on MP_TREES tiles) at every 8th processings, like it's done for grass on MP_CLEAR tiles. */
717 if ((cycle & 7) == 7 && GetTreeGround(tile) == TREE_GROUND_GRASS) {
718 uint density = GetTreeDensity(tile);
719 if (density < 3) {
720 SetTreeGroundDensity(tile, TREE_GROUND_GRASS, density + 1);
722 }
723 }
724
726
727 static const uint32_t TREE_UPDATE_FREQUENCY = 16; // How many tile updates happen for one tree update
728 if (cycle % TREE_UPDATE_FREQUENCY != TREE_UPDATE_FREQUENCY - 1) return;
729
730 switch (GetTreeGrowth(tile)) {
731 case TreeGrowthStage::Grown: // regular sized tree
732 if (_settings_game.game_creation.landscape == LT_TROPIC &&
733 GetTreeType(tile) != TREE_CACTUS &&
735 AddTreeGrowth(tile, 1);
736 } else {
737 switch (GB(Random(), 0, 3)) {
738 case 0: // start destructing
739 AddTreeGrowth(tile, 1);
740 break;
741
742 case 1: // add a tree
743 if (GetTreeCount(tile) < 4 && CanPlantExtraTrees(tile)) {
744 AddTreeCount(tile, 1);
746 break;
747 }
748 [[fallthrough]];
749
750 case 2: { // add a neighbouring tree
751 if (!CanPlantExtraTrees(tile)) break;
752
753 TreeType treetype = GetTreeType(tile);
754
755 tile += TileOffsByDir(static_cast<Direction>(Random() % DIR_END));
756
757 /* Cacti don't spread */
758 if (!CanPlantTreesOnTile(tile, false)) return;
759
760 /* Don't plant trees, if ground was freshly cleared */
761 if (IsTileType(tile, MP_CLEAR) && GetClearGround(tile) == CLEAR_GRASS && GetClearDensity(tile) != 3) return;
762
764
765 break;
766 }
767
768 default:
769 return;
770 }
771 }
772 break;
773
774 case TreeGrowthStage::Dead: // final stage of tree destruction
775 if (!CanPlantExtraTrees(tile)) {
776 /* if trees can't spread just plant a new one to prevent deforestation */
778 } else if (GetTreeCount(tile) > 1) {
779 /* more than one tree, delete it */
780 AddTreeCount(tile, -1);
782 } else {
783 /* just one tree, change type into MP_CLEAR */
784 switch (GetTreeGround(tile)) {
785 case TREE_GROUND_SHORE: MakeShore(tile); break;
786 case TREE_GROUND_GRASS: MakeClear(tile, CLEAR_GRASS, GetTreeDensity(tile)); break;
787 case TREE_GROUND_ROUGH: MakeClear(tile, CLEAR_ROUGH, 3); break;
789 uint density = GetTreeDensity(tile);
790 MakeClear(tile, CLEAR_ROUGH, 3);
791 MakeSnow(tile, density);
792 break;
793 }
794 default: // snow or desert
795 if (_settings_game.game_creation.landscape == LT_TROPIC) {
797 } else {
798 uint density = GetTreeDensity(tile);
799 MakeClear(tile, CLEAR_GRASS, 3);
800 MakeSnow(tile, density);
801 }
802 break;
803 }
804 }
805 break;
806
807 default:
808 AddTreeGrowth(tile, 1);
809 break;
810 }
811
813}
814
822{
823 /* Ensure _trees_tick_ctr can be decremented past zero only once for the largest map size. */
824 static_assert(2 * (MAX_MAP_SIZE_BITS - MIN_MAP_SIZE_BITS) - 4 <= std::numeric_limits<uint8_t>::digits);
825
826 /* byte underflow */
827 uint8_t old_trees_tick_ctr = _trees_tick_ctr;
829 return old_trees_tick_ctr <= _trees_tick_ctr;
830}
831
832void OnTick_Trees()
833{
834 /* Don't spread trees if that's not allowed */
836
837 uint32_t r;
838 TileIndex tile;
839 TreeType tree;
840
841 /* Skip some tree ticks for map sizes below 256 * 256. 64 * 64 is 16 times smaller, so
842 * this is the maximum number of ticks that are skipped. Number of ticks to skip is
843 * inversely proportional to map size, so that is handled to create a mask. */
844 int skip = Map::ScaleBySize(16);
845 if (skip < 16 && (TimerGameTick::counter & (16 / skip - 1)) != 0) return;
846
847 /* place a tree at a random rainforest spot */
848 if (_settings_game.game_creation.landscape == LT_TROPIC) {
849 for (uint c = Map::ScaleBySize(1); c > 0; c--) {
850 if ((r = Random(), tile = RandomTileSeed(r), GetTropicZone(tile) == TROPICZONE_RAINFOREST) &&
851 CanPlantTreesOnTile(tile, false) &&
852 (tree = GetRandomTreeType(tile, GB(r, 24, 8))) != TREE_INVALID) {
854 }
855 }
856 }
857
859
860 /* place a tree at a random spot */
861 r = Random();
862 tile = RandomTileSeed(r);
863 if (CanPlantTreesOnTile(tile, false) && (tree = GetRandomTreeType(tile, GB(r, 24, 8))) != TREE_INVALID) {
865 }
866}
867
868static TrackStatus GetTileTrackStatus_Trees(TileIndex, TransportType, uint, DiagDirection)
869{
870 return 0;
871}
872
873static void ChangeTileOwner_Trees(TileIndex, Owner, Owner)
874{
875 /* not used */
876}
877
878void InitializeTrees()
879{
880 _trees_tick_ctr = 0;
881}
882
883static CommandCost TerraformTile_Trees(TileIndex tile, DoCommandFlag flags, int, Slope)
884{
885 return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
886}
887
888
889extern const TileTypeProcs _tile_type_trees_procs = {
890 DrawTile_Trees, // draw_tile_proc
891 GetSlopePixelZ_Trees, // get_slope_z_proc
892 ClearTile_Trees, // clear_tile_proc
893 nullptr, // add_accepted_cargo_proc
894 GetTileDesc_Trees, // get_tile_desc_proc
895 GetTileTrackStatus_Trees, // get_tile_track_status_proc
896 nullptr, // click_tile_proc
897 nullptr, // animate_tile_proc
898 TileLoop_Trees, // tile_loop_proc
899 ChangeTileOwner_Trees, // change_tile_owner_proc
900 nullptr, // add_produced_cargo_proc
901 nullptr, // vehicle_enter_tile_proc
902 GetFoundation_Trees, // get_foundation_proc
903 TerraformTile_Trees, // terraform_tile_proc
904};
constexpr uint CountBits(T value)
Counts the number of set bits in a variable.
debug_inline static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
bool IsBridgeAbove(Tile t)
checks if a bridge is set above the ground of this tile
Definition bridge_map.h:45
Common return value for all commands.
void AddCost(const Money &cost)
Adds the given cost to the cost of the command.
Money GetCost() const
The costs as made up to this moment.
bool Failed() const
Did this command fail?
static std::unique_ptr< TileIterator > Create(TileIndex corner1, TileIndex corner2, bool diagonal)
Create either an OrthogonalTileIterator or DiagonalTileIterator given the diagonal parameter.
Definition tilearea.cpp:291
static TickCounter counter
Monotonic counter, in ticks, since start of game.
Functions related to clear (MP_CLEAR) land.
Tables with sprites for clear land and fences.
Map accessors for 'clear' tiles.
ClearGround GetRawClearGround(Tile t)
Get the type of clear tile but never return CLEAR_SNOW.
Definition clear_map.h:47
bool IsClearGround(Tile t, ClearGround ct)
Set the type of clear tile.
Definition clear_map.h:71
@ CLEAR_GRASS
0-3
Definition clear_map.h:20
@ CLEAR_FIELDS
3
Definition clear_map.h:23
@ CLEAR_DESERT
1,3
Definition clear_map.h:25
@ CLEAR_SNOW
0-3
Definition clear_map.h:24
@ CLEAR_ROUGH
3
Definition clear_map.h:21
@ CLEAR_ROCKS
3
Definition clear_map.h:22
void MakeSnow(Tile t, uint density=0)
Make a snow tile.
Definition clear_map.h:300
void MakeClear(Tile t, ClearGround g, uint density)
Make a clear tile.
Definition clear_map.h:259
ClearGround GetClearGround(Tile t)
Get the type of clear tile.
Definition clear_map.h:59
uint GetClearDensity(Tile t)
Get the density of a non-field clear tile.
Definition clear_map.h:83
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
DoCommandFlag
List of flags for a command.
@ DC_EXEC
execute the given command
Definition of stuff that is very close to a company, like the company struct itself.
CompanyID _current_company
Company currently doing an action.
Functions related to companies.
Owner
Enum for all companies/owners.
Direction
Defines the 8 directions on the map.
@ DIR_END
Used to iterate.
DiagDirection
Enumeration for diagonal directions.
@ EXPENSES_CONSTRUCTION
Construction costs.
@ EXPENSES_OTHER
Other expenses.
Functions related to world/map generation.
void IncreaseGeneratingWorldProgress(GenWorldProgress cls)
Increases the current stage of the world generation with one.
@ GWP_TREE
Generate trees.
Definition genworld.h:77
void SetGeneratingWorldProgress(GenWorldProgress cls, uint total)
Set the total of a stage of the world generation.
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:18
uint32_t PaletteID
The number of the palette.
Definition gfx_type.h:19
uint8_t GetSnowLine()
Get the current snow line, either variable or static.
void MarkTileDirtyByTile(TileIndex tile, int bridge_level_offset, int tile_height_override)
Mark a tile given by its index dirty for repaint.
uint GetPartialPixelZ(int x, int y, Slope corners)
Determines height at given coordinate of a slope.
Functions related to OTTD's landscape.
Command definitions related to landscape (slopes etc.).
uint DistanceSquare(TileIndex t0, TileIndex t1)
Gets the 'Square' distance between the two given tiles.
Definition map.cpp:163
TileIndex TileAddWrap(TileIndex tile, int addx, int addy)
This function checks if we add addx/addy to tile, if we do wrap around the edges.
Definition map.cpp:97
TileIndex RandomTileSeed(uint32_t r)
Get a random tile out of a given seed.
Definition map_func.h:650
#define RandomTile()
Get a valid random tile.
Definition map_func.h:661
TileIndexDiff TileOffsByDir(Direction dir)
Convert a Direction to a TileIndexDiff.
Definition map_func.h:581
static debug_inline uint TileY(TileIndex tile)
Get the Y component of a tile.
Definition map_func.h:425
static debug_inline uint TileX(TileIndex tile)
Get the X component of a tile.
Definition map_func.h:415
static const uint MIN_MAP_SIZE_BITS
Minimal and maximal map width and height.
Definition map_type.h:37
static const uint MAX_MAP_SIZE_BITS
Maximal size of map is equal to 2 ^ MAX_MAP_SIZE_BITS.
Definition map_type.h:38
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
Checks if a value is between a window started at some base point.
constexpr T abs(const T a)
Returns the absolute value of (scalar) variable.
Definition math_func.hpp:23
constexpr T Delta(const T a, const T b)
Returns the (absolute) difference between two (scalar) variables.
constexpr bool IsInsideMM(const T x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
Functions related to generic callbacks.
void AmbientSoundEffect(TileIndex tile)
Play an ambient sound effect for an empty tile.
Pseudo random number generator.
bool Chance16I(const uint32_t a, const uint32_t b, const uint32_t r)
Checks if a given randomize-number is below a given probability.
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:57
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:56
bool IsSlopeWithOneCornerRaised(Slope s)
Tests if a specific slope has exactly one corner raised.
Definition slope_func.h:88
uint SlopeToSpriteOffset(Slope s)
Returns the Sprite offset for a given Slope.
Definition slope_func.h:415
static constexpr int GetSlopeMaxPixelZ(Slope s)
Returns the height of the highest corner of a slope relative to TileZ (= minimal height)
Definition slope_func.h:173
Slope
Enumeration for the slope-type.
Definition slope_type.h:48
Foundation
Enumeration for Foundations.
Definition slope_type.h:93
@ FOUNDATION_NONE
The tile has no foundation, the slope remains unchanged.
Definition slope_type.h:94
Functions related to sound.
SoundFx
Sound effects from baseset.
Definition sound_type.h:45
@ SND_39_ARCTIC_SNOW_2
57 == 0x39 Tree ambient: arctic snow (2): heavy wind
Definition sound_type.h:104
@ SND_44_RAINFOREST_3
68 == 0x44 Tree ambient: rainforest ambient (3): monkeys
Definition sound_type.h:115
@ SND_34_ARCTIC_SNOW_1
52 == 0x34 Tree ambient: arctic snow (1): wind
Definition sound_type.h:99
@ SND_43_RAINFOREST_2
67 == 0x43 Tree ambient: rainforest ambient (2): lion
Definition sound_type.h:114
@ SND_42_RAINFOREST_1
66 == 0x42 Tree ambient: rainforest ambient (1): bird (1)
Definition sound_type.h:113
@ SND_48_RAINFOREST_4
72 == 0x48 Tree ambient: rainforest ambient (4): bird (2)
Definition sound_type.h:119
Definition of base types and functions in a cross-platform compatible way.
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:280
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
static const StringID INVALID_STRING_ID
Constant representing an invalid string (16bit in case it is used in savegames)
SoundSettings sound
sound effect settings
uint32_t tree_limit
Amount of trees we can (still) plant (times 65536).
uint8_t extra_tree_placement
(dis)allow building extra trees in-game
uint8_t dist_local_authority
distance for town local authority, default 20
uint8_t tree_placer
the tree placer algorithm
uint8_t landscape
the landscape we're currently in
EconomySettings economy
settings to change the economy
ConstructionSettings construction
construction of things in-game
GameCreationSettings game_creation
settings used during the creation of a game (map)
static uint ScaleBySize(uint n)
Scales the given value by the map size, where the given value is for a 256 by 256 map.
Definition map_func.h:328
static debug_inline uint Size()
Get the size of the map.
Definition map_func.h:288
OrthogonalTileArea & Expand(int rad)
Expand a tile area by rad tiles in each direction, keeping within map bounds.
Definition tilearea.cpp:123
Combination of a palette sprite and a 'real' sprite.
Definition gfx_type.h:23
SpriteID sprite
The 'real' sprite.
Definition gfx_type.h:24
PaletteID pal
The palette (use PAL_NONE) if not needed)
Definition gfx_type.h:25
static bool IsValidID(size_t index)
Tests whether given index can be used to get valid (non-nullptr) Titem.
static Titem * GetIfValid(size_t index)
Returns Titem with given index.
bool ambient
Play ambient, industry and town sounds.
Tile description for the 'land area information' tool.
Definition tile_cmd.h:52
StringID str
Description of the tile.
Definition tile_cmd.h:53
Owner owner[4]
Name of the owner(s)
Definition tile_cmd.h:55
Tile information, used while rendering the tile.
Definition tile_cmd.h:43
int z
Height.
Definition tile_cmd.h:48
int x
X position of the tile in unit coordinates.
Definition tile_cmd.h:44
Slope tileh
Slope of the tile.
Definition tile_cmd.h:46
TileIndex tile
Tile index.
Definition tile_cmd.h:47
int y
Y position of the tile in unit coordinates.
Definition tile_cmd.h:45
Set of callback functions for performing tile operations of a given tile type.
Definition tile_cmd.h:158
Town data structure.
Definition town.h:54
int GetTileZ(TileIndex tile)
Get bottom height of the tile.
Definition tile_map.cpp:116
Owner GetTileOwner(Tile tile)
Returns the owner of a tile.
Definition tile_map.h:178
static debug_inline TileType GetTileType(Tile tile)
Get the tiletype of a given tile.
Definition tile_map.h:96
std::tuple< Slope, int > GetTilePixelSlope(TileIndex tile)
Return the slope of a given tile.
Definition tile_map.h:289
TropicZone GetTropicZone(Tile tile)
Get the tropic zone.
Definition tile_map.h:238
static debug_inline bool IsTileType(Tile tile, TileType type)
Checks if a tile is a given tiletype.
Definition tile_map.h:150
Slope GetTileSlope(TileIndex tile)
Return the slope of a given tile inside the map.
Definition tile_map.h:279
void SetTropicZone(Tile tile, TropicZone type)
Set the tropic zone.
Definition tile_map.h:225
@ TROPICZONE_RAINFOREST
Rainforest tile.
Definition tile_type.h:79
@ TROPICZONE_DESERT
Tile is desert.
Definition tile_type.h:78
@ TROPICZONE_NORMAL
Normal tropiczone.
Definition tile_type.h:77
constexpr TileIndex INVALID_TILE
The very nice invalid tile marker.
Definition tile_type.h:95
@ MP_TREES
Tile got trees.
Definition tile_type.h:52
@ MP_CLEAR
A tile without any structures, i.e. grass, rocks, farm fields etc.
Definition tile_type.h:48
@ MP_WATER
Water tile.
Definition tile_type.h:54
@ MP_VOID
Invisible tiles at the SW and SE border.
Definition tile_type.h:55
OrthogonalTileArea TileArea
Shorthand for the much more common orthogonal tile area.
Definition of the tick-based game-timer.
Base of the town class.
Town * ClosestTownFromTile(TileIndex tile, uint threshold)
Return the town closest (in distance or ownership) to a given tile, within a given threshold.
void ChangeTownRating(Town *t, int add, int max, DoCommandFlag flags)
Changes town rating of the current company.
@ TO_TREES
trees
bool IsTransparencySet(TransparencyOption to)
Check if the transparency option bit is set and if we aren't in the game menu (there's never transpar...
bool IsInvisibilitySet(TransparencyOption to)
Check if the invisibility option bit is set and if we aren't in the game menu (there's never transpar...
TransportType
Available types of transport.
uint PlaceTreeGroupAroundTile(TileIndex tile, TreeType treetype, uint radius, uint count, bool set_zone)
Place some trees in a radius around a tile.
Definition tree_cmd.cpp:307
static void PlantTreesOnTile(TileIndex tile, TreeType treetype, uint count, TreeGrowthStage growth)
Creates a tree tile Ground type and density is preserved.
Definition tree_cmd.cpp:94
static TreeType GetRandomTreeType(TileIndex tile, uint seed)
Get a random TreeType for the given tile based on a given seed.
Definition tree_cmd.cpp:135
ExtraTreePlacement
Where to place trees while in-game?
Definition tree_cmd.cpp:47
@ ETP_SPREAD_RAINFOREST
Grow trees on tiles that have them, only spread to new ones in rainforests.
Definition tree_cmd.cpp:49
@ ETP_NO_SPREAD
Grow trees on tiles that have them but don't spread to new ones.
Definition tree_cmd.cpp:48
@ ETP_NO_GROWTH_NO_SPREAD
Don't grow trees and don't spread them at all.
Definition tree_cmd.cpp:51
@ ETP_SPREAD_ALL
Grow trees and spread them without restrictions.
Definition tree_cmd.cpp:50
void GenerateTrees()
Place new trees.
Definition tree_cmd.cpp:354
static const uint16_t EDITOR_TREE_DIV
Game editor tree generation divisor factor.
Definition tree_cmd.cpp:59
static bool CanPlantTreesOnTile(TileIndex tile, bool allow_desert)
Tests if a tile can be converted to MP_TREES This is true for clear ground without farms or rocks.
Definition tree_cmd.cpp:69
TreePlacer
List of tree placer algorithm.
Definition tree_cmd.cpp:40
@ TP_IMPROVED
A 'improved' algorithm.
Definition tree_cmd.cpp:43
@ TP_NONE
No tree placer algorithm.
Definition tree_cmd.cpp:41
@ TP_ORIGINAL
The original algorithm.
Definition tree_cmd.cpp:42
void PlaceTreesRandomly()
Place some trees randomly.
Definition tree_cmd.cpp:247
static const uint16_t DEFAULT_TREE_STEPS
Default number of attempts for placing trees.
Definition tree_cmd.cpp:57
static const uint16_t DEFAULT_RAINFOREST_TREE_STEPS
Default number of attempts for placing extra trees at rainforest in tropic.
Definition tree_cmd.cpp:58
static void PlaceTreeAtSameHeight(TileIndex tile, int height)
Place a tree at the same height as an existing tree.
Definition tree_cmd.cpp:218
CommandCost CmdPlantTree(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, uint8_t tree_to_plant, bool diagonal)
Plant a tree.
Definition tree_cmd.cpp:389
uint8_t _trees_tick_ctr
Determines when to consider building more trees.
Definition tree_cmd.cpp:55
static void PlaceTree(TileIndex tile, uint32_t r)
Make a random tree tile of the given tile.
Definition tree_cmd.cpp:165
static void PlaceTreeGroups(uint num_groups)
Creates a number of tree groups.
Definition tree_cmd.cpp:187
bool DecrementTreeCounter()
Decrement the tree tick counter.
Definition tree_cmd.cpp:821
Command definitions related to tree tiles.
Sprites to use and how to display them for tree tiles.
Map accessors for tree tiles.
static const uint TREE_COUNT_RAINFOREST
number of tree types for the 'rainforest part' of a sub-tropic map.
Definition tree_map.h:43
uint GetTreeCount(Tile t)
Returns the number of trees on a tile.
Definition tree_map.h:163
TreeGrowthStage GetTreeGrowth(Tile t)
Returns the tree growth stage.
Definition tree_map.h:195
static const uint TREE_COUNT_SUB_TROPICAL
number of tree types for the 'sub-tropic part' of a sub-tropic map.
Definition tree_map.h:44
static const uint TREE_COUNT_TOYLAND
number of tree types on a toyland map.
Definition tree_map.h:45
void MakeTree(Tile t, TreeType type, uint count, TreeGrowthStage growth, TreeGround ground, uint density)
Make a tree-tile.
Definition tree_map.h:244
void SetTreeGrowth(Tile t, TreeGrowthStage g)
Sets the tree growth stage of a tile.
Definition tree_map.h:226
TreeGround GetTreeGround(Tile t)
Returns the groundtype for tree tiles.
Definition tree_map.h:102
TreeGrowthStage
Enumeration for tree growth stages.
Definition tree_map.h:65
@ Grown
Fully grown tree.
@ Dead
Dead tree.
@ Growing1
First stage of growth.
void AddTreeCount(Tile t, int c)
Add a amount to the tree-count value of a tile with trees.
Definition tree_map.h:180
static const uint TREE_COUNT_SUB_ARCTIC
number of tree types on a sub arctic map.
Definition tree_map.h:42
TreeType GetTreeType(Tile t)
Returns the treetype of a tile.
Definition tree_map.h:87
TreeGround
Enumeration for ground types of tiles with trees.
Definition tree_map.h:52
@ TREE_GROUND_GRASS
normal grass
Definition tree_map.h:53
@ TREE_GROUND_SHORE
shore
Definition tree_map.h:56
@ TREE_GROUND_ROUGH_SNOW
A snow tile that is rough underneath.
Definition tree_map.h:57
@ TREE_GROUND_SNOW_DESERT
a desert or snow tile, depend on landscape
Definition tree_map.h:55
@ TREE_GROUND_ROUGH
some rough tile
Definition tree_map.h:54
TreeType
List of tree types along all landscape types.
Definition tree_map.h:25
@ TREE_RAINFOREST
tree on the 'green part' on a sub-tropical map
Definition tree_map.h:28
@ TREE_TOYLAND
tree on a toyland map
Definition tree_map.h:31
@ TREE_SUB_ARCTIC
tree on a sub_arctic landscape
Definition tree_map.h:27
@ TREE_SUB_TROPICAL
tree on a sub-tropical map, non-rainforest, non-desert
Definition tree_map.h:30
@ TREE_TEMPERATE
temperate tree
Definition tree_map.h:26
@ TREE_CACTUS
a cactus for the 'desert part' on a sub-tropical map
Definition tree_map.h:29
@ TREE_INVALID
An invalid tree.
Definition tree_map.h:32
void SetTreeGroundDensity(Tile t, TreeGround g, uint d)
Set the density and ground type of a tile with trees.
Definition tree_map.h:144
void AddTreeGrowth(Tile t, int a)
Add a value to the tree growth stage.
Definition tree_map.h:210
uint GetTreeDensity(Tile t)
Returns the 'density' of a tile with trees.
Definition tree_map.h:127
static const uint TREE_COUNT_TEMPERATE
number of tree types on a temperate map.
Definition tree_map.h:41
void StartSpriteCombine()
Starts a block of sprites, which are "combined" into a single bounding box.
Definition viewport.cpp:767
void AddSortableSpriteToDraw(SpriteID image, PaletteID pal, int x, int y, int w, int h, int dz, int z, bool transparent, int bb_offset_x, int bb_offset_y, int bb_offset_z, const SubSprite *sub)
Draw a (transparent) sprite at given coordinates with a given bounding box.
Definition viewport.cpp:671
void EndSpriteCombine()
Terminates a block of sprites started by StartSpriteCombine.
Definition viewport.cpp:777
void DrawGroundSprite(SpriteID image, PaletteID pal, const SubSprite *sub, int extra_offs_x, int extra_offs_y)
Draws a ground sprite for the current tile.
Definition viewport.cpp:587
Functions related to (drawing on) viewports.
Functions related to water (management)
void TileLoop_Water(TileIndex tile)
Let a water tile floods its diagonal adjoining tiles called from tunnelbridge_cmd,...
void ClearNeighbourNonFloodingStates(TileIndex tile)
Clear non-flooding state of the tiles around a tile.
Definition water_cmd.cpp:97
void MakeShore(Tile t)
Helper function to make a coast tile.
Definition water_map.h:381
bool IsCoast(Tile t)
Is it a coast tile?
Definition water_map.h:201