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/>.
10 /** @file clear_cmd.cpp Commands related to clear tiles. */
13 #include "map/ground.h"
14 #include "map/water.h"
15 #include "map/slope.h"
16 #include "command_func.h"
17 #include "landscape.h"
19 #include "viewport_func.h"
21 #include "company_base.h"
22 #include "company_func.h"
24 #include "sound_func.h"
25 #include "core/random_func.hpp"
26 #include "newgrf_generic.h"
29 #include "table/strings.h"
30 #include "table/sprites.h"
31 #include "table/clear_land.h"
32 #include "table/tree_land.h"
34 static CommandCost
ClearTile_Clear(TileIndex tile
, DoCommandFlag flags
)
36 static const Price clear_price_table
[] = {
46 switch (GetTileSubtype(tile
)) {
47 default: NOT_REACHED();
50 return_cmd_error(STR_ERROR_OFF_EDGE_OF_MAP
);
52 case TT_GROUND_FIELDS
:
53 cost
= _price
[PR_CLEAR_FIELDS
];
57 if (IsSnowTile(tile
)) {
58 cost
= _price
[PR_CLEAR_ROUGH
];
59 } else if (IsClearGround(tile
, GROUND_GRASS
) && GetClearDensity(tile
) == 0) {
62 cost
= _price
[clear_price_table
[GetClearGround(tile
)]];
67 if (Company::IsValidID(_current_company
)) {
68 Town
*t
= LocalAuthorityTownFromTile(tile
);
69 if (t
!= NULL
) ChangeTownRating(t
, RATING_TREE_DOWN_STEP
, RATING_TREE_MINIMUM
, flags
);
71 cost
= GetTreeCount(tile
) * _price
[PR_CLEAR_TREES
];
72 if (IsInsideMM(GetTreeType(tile
), TREE_RAINFOREST
, TREE_CACTUS
)) cost
*= 4;
76 if (flags
& DC_EXEC
) DoClearSquare(tile
);
78 return CommandCost(EXPENSES_CONSTRUCTION
, cost
);
81 void DrawVoidTile(TileInfo
*ti
)
83 DrawGroundSprite (ti
, SPR_FLAT_BARE_LAND
+ SlopeToSpriteOffset(ti
->tileh
), PALETTE_ALL_BLACK
);
86 void DrawClearLandTile(const TileInfo
*ti
, byte set
)
88 DrawGroundSprite (ti
, SPR_FLAT_BARE_LAND
+ SlopeToSpriteOffset(ti
->tileh
) + set
* 19, PAL_NONE
);
91 static void DrawClearLandFence(const TileInfo
*ti
)
93 /* combine fences into one sprite object */
94 StartSpriteCombine (ti
->vd
);
96 int maxz
= GetSlopeMaxPixelZ(ti
->tileh
);
98 uint fence_nw
= GetFence(ti
->tile
, DIAGDIR_NW
);
100 int z
= GetSlopePixelZInCorner(ti
->tileh
, CORNER_W
);
101 SpriteID sprite
= _clear_land_fence_sprites
[fence_nw
- 1] + _fence_mod_by_tileh_nw
[ti
->tileh
];
102 AddSortableSpriteToDraw (ti
->vd
, sprite
, PAL_NONE
, ti
->x
, ti
->y
- 15, 16, 31, maxz
- z
+ 4, ti
->z
+ z
, false, 0, 15, -z
);
105 uint fence_ne
= GetFence(ti
->tile
, DIAGDIR_NE
);
107 int z
= GetSlopePixelZInCorner(ti
->tileh
, CORNER_E
);
108 SpriteID sprite
= _clear_land_fence_sprites
[fence_ne
- 1] + _fence_mod_by_tileh_ne
[ti
->tileh
];
109 AddSortableSpriteToDraw (ti
->vd
, sprite
, PAL_NONE
, ti
->x
- 15, ti
->y
, 31, 16, maxz
- z
+ 4, ti
->z
+ z
, false, 15, 0, -z
);
112 uint fence_sw
= GetFence(ti
->tile
, DIAGDIR_SW
);
113 uint fence_se
= GetFence(ti
->tile
, DIAGDIR_SE
);
115 if (fence_sw
!= 0 || fence_se
!= 0) {
116 int z
= GetSlopePixelZInCorner(ti
->tileh
, CORNER_S
);
119 SpriteID sprite
= _clear_land_fence_sprites
[fence_sw
- 1] + _fence_mod_by_tileh_sw
[ti
->tileh
];
120 AddSortableSpriteToDraw (ti
->vd
, sprite
, PAL_NONE
, ti
->x
, ti
->y
, 16, 16, maxz
- z
+ 4, ti
->z
+ z
, false, 0, 0, -z
);
124 SpriteID sprite
= _clear_land_fence_sprites
[fence_se
- 1] + _fence_mod_by_tileh_se
[ti
->tileh
];
125 AddSortableSpriteToDraw (ti
->vd
, sprite
, PAL_NONE
, ti
->x
, ti
->y
, 16, 16, maxz
- z
+ 4, ti
->z
+ z
, false, 0, 0, -z
);
128 EndSpriteCombine (ti
->vd
);
131 struct TreeListEnt
: PalSpriteID
{
135 static void DrawTrees(TileInfo
*ti
)
137 uint tmp
= CountBits(ti
->tile
+ ti
->x
+ ti
->y
);
138 uint index
= GB(tmp
, 0, 2) + (GetTreeType(ti
->tile
) << 2);
140 /* different tree styles above one of the grounds */
141 if (IsSnowTile(ti
->tile
) && GetClearDensity(ti
->tile
) >= 2 &&
142 IsInsideMM(index
, TREE_SUB_ARCTIC
<< 2, TREE_RAINFOREST
<< 2)) {
143 index
+= 164 - (TREE_SUB_ARCTIC
<< 2);
146 assert(index
< lengthof(_tree_layout_sprite
));
148 const PalSpriteID
*s
= _tree_layout_sprite
[index
];
149 const TreePos
*d
= _tree_layout_xy
[GB(tmp
, 2, 2)];
151 /* combine trees into one sprite object */
152 StartSpriteCombine (ti
->vd
);
156 /* put the trees to draw in a list */
157 uint trees
= GetTreeCount(ti
->tile
);
159 for (uint i
= 0; i
< trees
; i
++) {
160 SpriteID sprite
= s
[0].sprite
+ (i
== trees
- 1 ? GetTreeGrowth(ti
->tile
) : 3);
161 PaletteID pal
= s
[0].pal
;
163 te
[i
].sprite
= sprite
;
171 /* draw them in a sorted way */
172 int z
= ti
->z
+ GetSlopeMaxPixelZ(ti
->tileh
) / 2;
174 for (; trees
> 0; trees
--) {
175 uint min
= te
[0].x
+ te
[0].y
;
178 for (uint i
= 1; i
< trees
; i
++) {
179 if ((uint
)(te
[i
].x
+ te
[i
].y
) < min
) {
180 min
= te
[i
].x
+ te
[i
].y
;
185 AddSortableSpriteToDraw (ti
->vd
, 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
);
187 /* replace the removed one with the last one */
188 te
[mi
] = te
[trees
- 1];
191 EndSpriteCombine (ti
->vd
);
194 static void DrawTile_Clear(TileInfo
*ti
)
196 switch (GetTileSubtype(ti
->tile
)) {
201 case TT_GROUND_FIELDS
:
202 DrawGroundSprite (ti
, _clear_land_sprites_farmland
[GetFieldType(ti
->tile
)] + SlopeToSpriteOffset(ti
->tileh
), PAL_NONE
);
203 DrawClearLandFence(ti
);
204 DrawBridgeMiddle(ti
);
208 switch (GetFullClearGround(ti
->tile
)) {
210 DrawClearLandTile(ti
, GetClearDensity(ti
->tile
));
218 DrawGroundSprite (ti
, ti
->tileh
!= SLOPE_FLAT
?
219 SPR_FLAT_ROUGH_LAND
+ SlopeToSpriteOffset(ti
->tileh
) :
220 _landscape_clear_sprites_rough
[GB(TileHash(ti
->x
, ti
->y
), 0, 3)],
225 DrawGroundSprite (ti
, (HasGrfMiscBit(GMB_SECOND_ROCKY_TILE_SET
) && (TileHash(ti
->x
, ti
->y
) & 1) ? SPR_FLAT_ROCKY_LAND_2
: SPR_FLAT_ROCKY_LAND_1
) + SlopeToSpriteOffset(ti
->tileh
), PAL_NONE
);
229 DrawGroundSprite (ti
, _clear_land_sprites_snow_desert
[GetClearDensity(ti
->tile
)] + SlopeToSpriteOffset(ti
->tileh
), PAL_NONE
);
233 if (!IsTileSubtype(ti
->tile
, TT_GROUND_TREES
)) {
234 DrawBridgeMiddle(ti
);
235 } else if (!IsInvisibilitySet(TO_TREES
)) {
244 static int GetSlopePixelZ_Clear(TileIndex tile
, uint x
, uint y
)
246 if (IsTileSubtype(tile
, TT_GROUND_VOID
)) return TilePixelHeight(tile
);
249 Slope tileh
= GetTilePixelSlope(tile
, &z
);
251 return z
+ GetPartialPixelZ(x
& 0xF, y
& 0xF, tileh
);
254 static Foundation
GetFoundation_Clear(TileIndex tile
, Slope tileh
)
256 return FOUNDATION_NONE
;
259 static void UpdateFences(TileIndex tile
)
261 assert(IsFieldsTile(tile
));
264 bool neighbour
= IsFieldsTile(TILE_ADDXY(tile
, 1, 0));
265 if (!neighbour
&& GetFence(tile
, DIAGDIR_SW
) == 0) {
266 SetFence(tile
, DIAGDIR_SW
, 3);
270 neighbour
= IsFieldsTile(TILE_ADDXY(tile
, 0, 1));
271 if (!neighbour
&& GetFence(tile
, DIAGDIR_SE
) == 0) {
272 SetFence(tile
, DIAGDIR_SE
, 3);
276 neighbour
= IsFieldsTile(TILE_ADDXY(tile
, -1, 0));
277 if (!neighbour
&& GetFence(tile
, DIAGDIR_NE
) == 0) {
278 SetFence(tile
, DIAGDIR_NE
, 3);
282 neighbour
= IsFieldsTile(TILE_ADDXY(tile
, 0, -1));
283 if (!neighbour
&& GetFence(tile
, DIAGDIR_NW
) == 0) {
284 SetFence(tile
, DIAGDIR_NW
, 3);
288 if (dirty
) MarkTileDirtyByTile(tile
);
292 /** Convert to or from snowy tiles. */
293 static void TileLoopClearAlps(TileIndex tile
)
295 int k
= GetTileZ(tile
) - GetSnowLine() + 1;
297 if (!IsSnowTile(tile
)) {
298 /* No snow, make it if needed, otherwise do nothing. */
302 /* Update snow density. */
303 uint cur_density
= GetClearDensity(tile
);
304 uint req_density
= (k
< 0) ? 0u : min((uint
)k
, 3);
306 if (cur_density
< req_density
) {
307 AddClearDensity(tile
, 1);
308 } else if (cur_density
> req_density
) {
309 AddClearDensity(tile
, -1);
313 /* Density at the required level. */
314 if (IsTileSubtype(tile
, TT_GROUND_TREES
) && cur_density
== 3) {
316 if (Chance16I(1, 200, r
) && _settings_client
.sound
.ambient
) {
317 SndPlayTileFx((r
& 0x80000000) ? SND_39_HEAVY_WIND
: SND_34_WIND
, tile
);
324 MarkTileDirtyByTile(tile
);
328 * Tests if at least one surrounding tile is desert
329 * @param tile tile to check
330 * @return does this tile have at least one desert tile around?
332 static inline bool NeighbourIsDesert(TileIndex tile
)
334 return GetTropicZone(tile
+ TileDiffXY( 1, 0)) == TROPICZONE_DESERT
||
335 GetTropicZone(tile
+ TileDiffXY( -1, 0)) == TROPICZONE_DESERT
||
336 GetTropicZone(tile
+ TileDiffXY( 0, 1)) == TROPICZONE_DESERT
||
337 GetTropicZone(tile
+ TileDiffXY( 0, -1)) == TROPICZONE_DESERT
;
340 static void TileLoopClearDesert(TileIndex tile
)
342 /* Expected desert level - 0 if it shouldn't be desert */
343 uint expected
= GetTropicZone(tile
) == TROPICZONE_DESERT
? 3 :
344 NeighbourIsDesert(tile
) ? 1 : 0;
346 switch (GetTileSubtype(tile
)) {
347 default: NOT_REACHED();
349 case TT_GROUND_FIELDS
:
350 if (expected
== 0) return;
351 MakeClear(tile
, GROUND_DESERT
, expected
);
354 case TT_GROUND_TREES
:
355 if (GetTropicZone(tile
) == TROPICZONE_RAINFOREST
) {
356 static const SoundFx forest_sounds
[] = {
364 if (Chance16I(1, 200, r
) && _settings_client
.sound
.ambient
) SndPlayTileFx(forest_sounds
[GB(r
, 16, 2)], tile
);
368 case TT_GROUND_CLEAR
: {
369 /* Current desert level - 0 if it is not desert */
370 uint current
= IsClearGround(tile
, GROUND_DESERT
) ? GetClearDensity(tile
) : 0;
372 if (current
== expected
) return;
375 SetClearGroundDensity(tile
, GROUND_GRASS
, 3);
377 /* Transition from clear to desert is not smooth (after clearing desert tile) */
378 SetClearGroundDensity(tile
, GROUND_DESERT
, expected
);
384 MarkTileDirtyByTile(tile
);
387 extern void AddNeighbouringTree(TileIndex tile
);
389 static void HandleTreeGrowth(TileIndex tile
)
391 switch (GetTreeGrowth(tile
)) {
392 case 3: // regular sized tree
393 if (_settings_game
.game_creation
.landscape
== LT_TROPIC
&&
394 GetTreeType(tile
) != TREE_CACTUS
&&
395 GetTropicZone(tile
) == TROPICZONE_DESERT
) {
396 AddTreeGrowth(tile
, 1);
398 switch (GB(Random(), 0, 3)) {
399 case 0: // start destructing
400 AddTreeGrowth(tile
, 1);
403 case 1: // add a tree
404 if (GetTreeCount(tile
) < 4) {
405 AddTreeCount(tile
, 1);
406 SetTreeGrowth(tile
, 0);
411 case 2: // add a neighbouring tree
412 AddNeighbouringTree(tile
);
421 case 6: // final stage of tree destruction
422 if (GetTreeCount(tile
) > 1) {
423 /* more than one tree, delete it */
424 AddTreeCount(tile
, -1);
425 SetTreeGrowth(tile
, 3);
427 /* just one tree, change type into clear */
428 Ground g
= GetClearGround(tile
);
429 if (g
== GROUND_SHORE
) {
432 MakeClear(tile
, g
, GetClearDensity(tile
));
438 AddTreeGrowth(tile
, 1);
443 static void TileLoop_Clear(TileIndex tile
)
445 if (IsTileSubtype(tile
, TT_GROUND_VOID
)) return;
447 if (!IsTileSubtype(tile
, TT_GROUND_FIELDS
) && GetClearGround(tile
) == GROUND_SHORE
) {
448 TileLoop_Water(tile
);
450 /* If the tile is at any edge flood it to prevent maps without water. */
451 if (_settings_game
.construction
.freeform_edges
&& DistanceFromEdge(tile
) == 1) {
453 if (IsTileFlat(tile
, &z
) && z
== 0) {
455 MarkTileDirtyByTile(tile
);
460 switch (_settings_game
.game_creation
.landscape
) {
461 case LT_TROPIC
: TileLoopClearDesert(tile
); break;
462 case LT_ARCTIC
: TileLoopClearAlps(tile
); break;
466 AmbientSoundEffect(tile
);
468 switch (GetTileSubtype(tile
)) {
469 default: NOT_REACHED();
471 case TT_GROUND_FIELDS
:
474 if (_game_mode
== GM_EDITOR
) return;
476 if (GetClearCounter(tile
) < 7) {
477 AddClearCounter(tile
, 1);
481 SetClearCounter(tile
, 0);
483 if (GetIndustryIndexOfField(tile
) == INVALID_INDUSTRY
&& GetFieldType(tile
) >= 7) {
484 /* This farmfield is no longer farmfield, so make it grass again */
485 MakeClear(tile
, GROUND_GRASS
, 2);
487 uint field_type
= GetFieldType(tile
);
488 field_type
= (field_type
< 8) ? field_type
+ 1 : 0;
489 SetFieldType(tile
, field_type
);
493 case TT_GROUND_CLEAR
:
494 if (GetClearGround(tile
) == GROUND_GRASS
) {
495 if (GetClearDensity(tile
) == 3) return;
497 if (_game_mode
!= GM_EDITOR
) {
498 if (GetClearCounter(tile
) < 7) {
499 AddClearCounter(tile
, 1);
502 SetClearCounter(tile
, 0);
503 AddClearDensity(tile
, 1);
505 SetClearGroundDensity(tile
, GB(Random(), 0, 8) > 21 ? GROUND_GRASS
: GROUND_ROUGH
, 3);
510 case TT_GROUND_TREES
: {
511 uint counter
= GetClearCounter(tile
);
513 /* Handle growth of grass (under trees) at every 8th processings, like it's done for grass on clear tiles. */
514 if ((counter
& 7) == 7 && GetClearGround(tile
) == GROUND_GRASS
) {
515 if (GetClearDensity(tile
) < 3) {
516 AddClearDensity(tile
, 1);
517 MarkTileDirtyByTile(tile
);
521 AddClearCounter(tile
, 1);
524 SetClearCounter(tile
, 0);
525 HandleTreeGrowth(tile
);
530 MarkTileDirtyByTile(tile
);
533 void GenerateClearTile()
538 /* add rough tiles */
539 i
= ScaleByMapSize(GB(Random(), 0, 10) + 0x400);
540 gi
= ScaleByMapSize(GB(Random(), 0, 7) + 0x80);
542 SetGeneratingWorldProgress(GWP_ROUGH_ROCKY
, gi
+ i
);
544 IncreaseGeneratingWorldProgress(GWP_ROUGH_ROCKY
);
546 if (IsClearTile(tile
) && !IsClearGround(tile
, GROUND_DESERT
)) SetClearGroundDensity(tile
, GROUND_ROUGH
, 3);
549 /* add rocky tiles */
553 tile
= RandomTileSeed(r
);
555 IncreaseGeneratingWorldProgress(GWP_ROUGH_ROCKY
);
556 if (IsClearTile(tile
) && !IsClearGround(tile
, GROUND_DESERT
)) {
557 uint j
= GB(r
, 16, 4) + 5;
561 SetClearGroundDensity(tile
, GROUND_ROCKS
, 3);
563 if (--j
== 0) goto get_out
;
564 tile_new
= tile
+ TileOffsByDiagDir((DiagDirection
)GB(Random(), 0, 2));
565 } while (!IsClearTile(tile_new
) || IsClearGround(tile_new
, GROUND_DESERT
));
573 static const StringID _clear_land_str
[] = {
574 STR_LAI_CLEAR_DESCRIPTION_GRASS
,
575 STR_LAI_CLEAR_DESCRIPTION_GRASS
,
576 STR_LAI_CLEAR_DESCRIPTION_ROUGH_LAND
,
577 STR_LAI_CLEAR_DESCRIPTION_ROCKS
,
578 STR_LAI_CLEAR_DESCRIPTION_DESERT
,
581 static void GetTileDesc_Clear(TileIndex tile
, TileDesc
*td
)
583 switch (GetTileSubtype(tile
)) {
584 default: NOT_REACHED();
588 td
->owner
[0] = OWNER_NONE
;
591 case TT_GROUND_FIELDS
:
592 td
->str
= STR_LAI_CLEAR_DESCRIPTION_FIELDS
;
595 case TT_GROUND_CLEAR
:
596 if (IsSnowTile(tile
)) {
597 td
->str
= STR_LAI_CLEAR_DESCRIPTION_SNOW_COVERED_LAND
;
598 } else if (IsClearGround(tile
, GROUND_GRASS
) && GetClearDensity(tile
) == 0) {
599 td
->str
= STR_LAI_CLEAR_DESCRIPTION_BARE_LAND
;
601 td
->str
= _clear_land_str
[GetClearGround(tile
)];
605 case TT_GROUND_TREES
: {
606 TreeType tt
= GetTreeType(tile
);
607 if (IsInsideMM(tt
, TREE_RAINFOREST
, TREE_CACTUS
)) {
608 td
->str
= STR_LAI_TREE_NAME_RAINFOREST
;
610 td
->str
= tt
== TREE_CACTUS
? STR_LAI_TREE_NAME_CACTUS_PLANTS
: STR_LAI_TREE_NAME_TREES
;
616 td
->owner
[0] = GetTileOwner(tile
);
619 static void ChangeTileOwner_Clear(TileIndex tile
, Owner old_owner
, Owner new_owner
)
624 static CommandCost
TerraformTile_Clear(TileIndex tile
, DoCommandFlag flags
, int z_new
, Slope tileh_new
)
626 if (IsTileSubtype(tile
, TT_GROUND_VOID
)) return_cmd_error(STR_ERROR_OFF_EDGE_OF_MAP
);
628 return DoCommand(tile
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
631 extern const TileTypeProcs _tile_type_clear_procs
= {
632 DrawTile_Clear
, ///< draw_tile_proc
633 GetSlopePixelZ_Clear
, ///< get_slope_z_proc
634 ClearTile_Clear
, ///< clear_tile_proc
635 NULL
, ///< add_accepted_cargo_proc
636 GetTileDesc_Clear
, ///< get_tile_desc_proc
637 NULL
, ///< get_tile_railway_status_proc
638 NULL
, ///< get_tile_road_status_proc
639 NULL
, ///< get_tile_waterway_status_proc
640 NULL
, ///< click_tile_proc
641 NULL
, ///< animate_tile_proc
642 TileLoop_Clear
, ///< tile_loop_proc
643 ChangeTileOwner_Clear
, ///< change_tile_owner_proc
644 NULL
, ///< add_produced_cargo_proc
645 GetFoundation_Clear
, ///< get_foundation_proc
646 TerraformTile_Clear
, ///< terraform_tile_proc