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 "clear_map.h"
14 #include "command_func.h"
15 #include "landscape.h"
17 #include "viewport_func.h"
19 #include "core/random_func.hpp"
20 #include "newgrf_generic.h"
22 #include "table/strings.h"
23 #include "table/sprites.h"
24 #include "table/clear_land.h"
26 #include "safeguards.h"
28 static CommandCost
ClearTile_Clear(TileIndex tile
, DoCommandFlag flags
)
30 static const Price clear_price_table
[] = {
38 CommandCost
price(EXPENSES_CONSTRUCTION
);
40 if (!IsClearGround(tile
, CLEAR_GRASS
) || GetClearDensity(tile
) != 0) {
41 price
.AddCost(_price
[clear_price_table
[GetClearGround(tile
)]]);
44 if (flags
& DC_EXEC
) DoClearSquare(tile
);
49 SpriteID
GetSpriteIDForClearLand(const Slope slope
, byte set
)
51 return SPR_FLAT_BARE_LAND
+ SlopeToSpriteOffset(slope
) + set
* 19;
54 void DrawClearLandTile(const TileInfo
*ti
, byte set
)
56 DrawGroundSprite(GetSpriteIDForClearLand(ti
->tileh
, set
), PAL_NONE
);
59 SpriteID
GetSpriteIDForHillyLand(const Slope slope
, const uint rough_index
)
61 if (slope
!= SLOPE_FLAT
) {
62 return SPR_FLAT_ROUGH_LAND
+ SlopeToSpriteOffset(slope
);
64 return _landscape_clear_sprites_rough
[rough_index
];
68 void DrawHillyLandTile(const TileInfo
*ti
)
70 DrawGroundSprite(GetSpriteIDForHillyLand(ti
->tileh
, GB(ti
->x
^ ti
->y
, 4, 3)), PAL_NONE
);
73 SpriteID
GetSpriteIDForRocks(const Slope slope
, const uint tile_hash
)
75 return (tile_hash
& 1 ? SPR_FLAT_ROCKY_LAND_2
: SPR_FLAT_ROCKY_LAND_1
) + SlopeToSpriteOffset(slope
);
78 SpriteID
GetSpriteIDForFields(const Slope slope
, const uint field_type
)
80 return _clear_land_sprites_farmland
[field_type
] + SlopeToSpriteOffset(slope
);
83 SpriteID
GetSpriteIDForSnowDesert(const Slope slope
, const uint density
)
85 return _clear_land_sprites_snow_desert
[density
] + SlopeToSpriteOffset(slope
);
88 static void DrawClearLandFence(const TileInfo
*ti
)
90 /* combine fences into one sprite object */
93 int maxz
= GetSlopeMaxPixelZ(ti
->tileh
);
95 uint fence_nw
= GetFence(ti
->tile
, DIAGDIR_NW
);
97 int z
= GetSlopePixelZInCorner(ti
->tileh
, CORNER_W
);
98 SpriteID sprite
= _clear_land_fence_sprites
[fence_nw
- 1] + _fence_mod_by_tileh_nw
[ti
->tileh
];
99 AddSortableSpriteToDraw(sprite
, PAL_NONE
, ti
->x
, ti
->y
- 15, 16, 31, maxz
- z
+ 4, ti
->z
+ z
, false, 0, 15, -z
);
102 uint fence_ne
= GetFence(ti
->tile
, DIAGDIR_NE
);
104 int z
= GetSlopePixelZInCorner(ti
->tileh
, CORNER_E
);
105 SpriteID sprite
= _clear_land_fence_sprites
[fence_ne
- 1] + _fence_mod_by_tileh_ne
[ti
->tileh
];
106 AddSortableSpriteToDraw(sprite
, PAL_NONE
, ti
->x
- 15, ti
->y
, 31, 16, maxz
- z
+ 4, ti
->z
+ z
, false, 15, 0, -z
);
109 uint fence_sw
= GetFence(ti
->tile
, DIAGDIR_SW
);
110 uint fence_se
= GetFence(ti
->tile
, DIAGDIR_SE
);
112 if (fence_sw
!= 0 || fence_se
!= 0) {
113 int z
= GetSlopePixelZInCorner(ti
->tileh
, CORNER_S
);
116 SpriteID sprite
= _clear_land_fence_sprites
[fence_sw
- 1] + _fence_mod_by_tileh_sw
[ti
->tileh
];
117 AddSortableSpriteToDraw(sprite
, PAL_NONE
, ti
->x
, ti
->y
, 16, 16, maxz
- z
+ 4, ti
->z
+ z
, false, 0, 0, -z
);
121 SpriteID sprite
= _clear_land_fence_sprites
[fence_se
- 1] + _fence_mod_by_tileh_se
[ti
->tileh
];
122 AddSortableSpriteToDraw(sprite
, PAL_NONE
, ti
->x
, ti
->y
, 16, 16, maxz
- z
+ 4, ti
->z
+ z
, false, 0, 0, -z
);
128 static void DrawTile_Clear(TileInfo
*ti
)
130 switch (GetClearGround(ti
->tile
)) {
132 DrawClearLandTile(ti
, GetClearDensity(ti
->tile
));
136 DrawHillyLandTile(ti
);
140 DrawGroundSprite(GetSpriteIDForRocks(ti
->tileh
, TileHash(ti
->x
, ti
->y
)), PAL_NONE
);
144 DrawGroundSprite(GetSpriteIDForFields(ti
->tileh
, GetFieldType(ti
->tile
)), PAL_NONE
);
145 DrawClearLandFence(ti
);
150 DrawGroundSprite(GetSpriteIDForSnowDesert(ti
->tileh
, GetClearDensity(ti
->tile
)), PAL_NONE
);
154 DrawOverlay(ti
, MP_CLEAR
);
155 DrawBridgeMiddle(ti
);
158 static int GetSlopePixelZ_Clear(TileIndex tile
, uint x
, uint y
)
161 Slope tileh
= GetTilePixelSlope(tile
, &z
);
163 return z
+ GetPartialPixelZ(x
& 0xF, y
& 0xF, tileh
);
166 static Foundation
GetFoundation_Clear(TileIndex tile
, Slope tileh
)
168 return FOUNDATION_NONE
;
171 static void UpdateFences(TileIndex tile
)
173 assert(IsTileType(tile
, MP_CLEAR
) && IsClearGround(tile
, CLEAR_FIELDS
));
176 bool neighbour
= (IsTileType(TILE_ADDXY(tile
, 1, 0), MP_CLEAR
) && IsClearGround(TILE_ADDXY(tile
, 1, 0), CLEAR_FIELDS
));
177 if (!neighbour
&& GetFence(tile
, DIAGDIR_SW
) == 0) {
178 SetFence(tile
, DIAGDIR_SW
, 3);
182 neighbour
= (IsTileType(TILE_ADDXY(tile
, 0, 1), MP_CLEAR
) && IsClearGround(TILE_ADDXY(tile
, 0, 1), CLEAR_FIELDS
));
183 if (!neighbour
&& GetFence(tile
, DIAGDIR_SE
) == 0) {
184 SetFence(tile
, DIAGDIR_SE
, 3);
188 neighbour
= (IsTileType(TILE_ADDXY(tile
, -1, 0), MP_CLEAR
) && IsClearGround(TILE_ADDXY(tile
, -1, 0), CLEAR_FIELDS
));
189 if (!neighbour
&& GetFence(tile
, DIAGDIR_NE
) == 0) {
190 SetFence(tile
, DIAGDIR_NE
, 3);
194 neighbour
= (IsTileType(TILE_ADDXY(tile
, 0, -1), MP_CLEAR
) && IsClearGround(TILE_ADDXY(tile
, 0, -1), CLEAR_FIELDS
));
195 if (!neighbour
&& GetFence(tile
, DIAGDIR_NW
) == 0) {
196 SetFence(tile
, DIAGDIR_NW
, 3);
200 if (dirty
) MarkTileDirtyByTile(tile
, ZOOM_LVL_DRAW_MAP
);
204 /** Convert to or from snowy tiles. */
205 static void TileLoopClearAlps(TileIndex tile
)
207 int k
= GetTileZ(tile
) - GetSnowLine() + 1;
210 /* Below the snow line, do nothing if no snow. */
211 if (!IsSnowTile(tile
)) return;
213 /* At or above the snow line, make snow tile if needed. */
214 if (!IsSnowTile(tile
)) {
216 MarkTileDirtyByTile(tile
);
220 /* Update snow density. */
221 uint current_density
= GetClearDensity(tile
);
222 uint req_density
= (k
< 0) ? 0u : min((uint
)k
, 3);
224 if (current_density
< req_density
) {
225 AddClearDensity(tile
, 1);
226 } else if (current_density
> req_density
) {
227 AddClearDensity(tile
, -1);
229 /* Density at the required level. */
233 MarkTileDirtyByTile(tile
);
237 * Tests if at least one surrounding tile is desert
238 * @param tile tile to check
239 * @return does this tile have at least one desert tile around?
241 static inline bool NeighbourIsDesert(TileIndex tile
)
243 return GetTropicZone(tile
+ TileDiffXY( 1, 0)) == TROPICZONE_DESERT
||
244 GetTropicZone(tile
+ TileDiffXY( -1, 0)) == TROPICZONE_DESERT
||
245 GetTropicZone(tile
+ TileDiffXY( 0, 1)) == TROPICZONE_DESERT
||
246 GetTropicZone(tile
+ TileDiffXY( 0, -1)) == TROPICZONE_DESERT
;
249 static void TileLoopClearDesert(TileIndex tile
)
251 /* Current desert level - 0 if it is not desert */
253 if (IsClearGround(tile
, CLEAR_DESERT
)) current
= GetClearDensity(tile
);
255 /* Expected desert level - 0 if it shouldn't be desert */
257 if (GetTropicZone(tile
) == TROPICZONE_DESERT
) {
259 } else if (NeighbourIsDesert(tile
)) {
263 if (current
== expected
) return;
266 SetClearGroundDensity(tile
, CLEAR_GRASS
, 3);
268 /* Transition from clear to desert is not smooth (after clearing desert tile) */
269 SetClearGroundDensity(tile
, CLEAR_DESERT
, expected
);
272 MarkTileDirtyByTile(tile
);
275 static void TileLoop_Clear(TileIndex tile
)
277 /* If the tile is at any edge flood it to prevent maps without water. */
278 if (_settings_game
.construction
.freeform_edges
&& DistanceFromEdge(tile
) == 1) {
280 if (IsTileFlat(tile
, &z
) && z
== 0) {
285 AmbientSoundEffect(tile
);
287 switch (_settings_game
.game_creation
.landscape
) {
288 case LT_TROPIC
: TileLoopClearDesert(tile
); break;
289 case LT_ARCTIC
: TileLoopClearAlps(tile
); break;
292 switch (GetClearGround(tile
)) {
294 if (GetClearDensity(tile
) == 3) return;
296 if (_game_mode
!= GM_EDITOR
) {
297 if (GetClearCounter(tile
) < 7) {
298 AddClearCounter(tile
, 1);
301 SetClearCounter(tile
, 0);
302 AddClearDensity(tile
, 1);
305 SetClearGroundDensity(tile
, GB(Random(), 0, 8) > 21 ? CLEAR_GRASS
: CLEAR_ROUGH
, 3);
312 if (_game_mode
== GM_EDITOR
) return;
314 if (GetClearCounter(tile
) < 7) {
315 AddClearCounter(tile
, 1);
318 SetClearCounter(tile
, 0);
321 if (GetIndustryIndexOfField(tile
) == INVALID_INDUSTRY
&& GetFieldType(tile
) >= 7) {
322 /* This farmfield is no longer farmfield, so make it grass again */
323 MakeClear(tile
, CLEAR_GRASS
, 2);
325 uint field_type
= GetFieldType(tile
);
326 field_type
= (field_type
< 8) ? field_type
+ 1 : 0;
327 SetFieldType(tile
, field_type
);
335 MarkTileDirtyByTile(tile
, ZOOM_LVL_DRAW_MAP
);
338 void GenerateClearTile()
343 /* add rough tiles */
344 i
= ScaleByMapSize(GB(Random(), 0, 10) + 0x400);
345 gi
= ScaleByMapSize(GB(Random(), 0, 7) + 0x80);
347 SetGeneratingWorldProgress(GWP_ROUGH_ROCKY
, gi
+ i
+ MapSize());
349 uint16 tree_line_range
= _settings_game
.construction
.trees_around_snow_line_range
;
350 uint16 tree_line_height
= _settings_game
.game_creation
.tree_line_height
;
352 for (TileIndex possible_mountain_tile
= 0; possible_mountain_tile
< MapSize(); ++possible_mountain_tile
) {
353 uint8 tile_z
= GetTileZ(possible_mountain_tile
);
354 bool is_above_tree_line
= (tile_z
>= tree_line_height
);
355 bool is_above_rough_line
= (tile_z
>= tree_line_height
- tree_line_range
);
356 bool is_clear_tile
= IsTileType(possible_mountain_tile
, MP_CLEAR
);
358 if (is_above_rough_line
&& is_clear_tile
) {
359 bool is_desert_tile
= IsClearGround(possible_mountain_tile
, CLEAR_DESERT
);
361 if (!is_desert_tile
) {
362 if (is_above_tree_line
) {
363 SetClearGroundDensity(possible_mountain_tile
, CLEAR_ROCKS
, 3);
366 SetClearGroundDensity(possible_mountain_tile
, CLEAR_ROUGH
, 3);
371 IncreaseGeneratingWorldProgress(GWP_ROUGH_ROCKY
);
375 IncreaseGeneratingWorldProgress(GWP_ROUGH_ROCKY
);
377 if (IsTileType(tile
, MP_CLEAR
) && !IsClearGround(tile
, CLEAR_DESERT
)) SetClearGroundDensity(tile
, CLEAR_ROUGH
, 3);
380 /* add rocky tiles */
384 tile
= RandomTileSeed(r
);
386 IncreaseGeneratingWorldProgress(GWP_ROUGH_ROCKY
);
387 if (IsTileType(tile
, MP_CLEAR
) && !IsClearGround(tile
, CLEAR_DESERT
)) {
388 uint j
= GB(r
, 16, 4) + 5;
392 SetClearGroundDensity(tile
, CLEAR_ROCKS
, 3);
394 if (--j
== 0) goto get_out
;
395 tile_new
= tile
+ TileOffsByDiagDir((DiagDirection
)GB(Random(), 0, 2));
396 } while (!IsTileType(tile_new
, MP_CLEAR
) || IsClearGround(tile_new
, CLEAR_DESERT
));
404 static TrackStatus
GetTileTrackStatus_Clear(TileIndex tile
, TransportType mode
, uint sub_mode
, DiagDirection side
)
409 static const StringID _clear_land_str
[] = {
410 STR_LAI_CLEAR_DESCRIPTION_GRASS
,
411 STR_LAI_CLEAR_DESCRIPTION_ROUGH_LAND
,
412 STR_LAI_CLEAR_DESCRIPTION_ROCKS
,
413 STR_LAI_CLEAR_DESCRIPTION_FIELDS
,
414 STR_LAI_CLEAR_DESCRIPTION_SNOW_COVERED_LAND
,
415 STR_LAI_CLEAR_DESCRIPTION_DESERT
418 static void GetTileDesc_Clear(TileIndex tile
, TileDesc
*td
)
420 if (IsClearGround(tile
, CLEAR_GRASS
) && GetClearDensity(tile
) == 0) {
421 td
->str
= STR_LAI_CLEAR_DESCRIPTION_BARE_LAND
;
423 td
->str
= _clear_land_str
[GetClearGround(tile
)];
425 td
->owner
[0] = GetTileOwner(tile
);
428 static void ChangeTileOwner_Clear(TileIndex tile
, Owner old_owner
, Owner new_owner
)
433 static CommandCost
TerraformTile_Clear(TileIndex tile
, DoCommandFlag flags
, int z_new
, Slope tileh_new
)
435 return DoCommand(tile
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
438 extern const TileTypeProcs _tile_type_clear_procs
= {
439 DrawTile_Clear
, ///< draw_tile_proc
440 GetSlopePixelZ_Clear
, ///< get_slope_z_proc
441 ClearTile_Clear
, ///< clear_tile_proc
442 nullptr, ///< add_accepted_cargo_proc
443 GetTileDesc_Clear
, ///< get_tile_desc_proc
444 GetTileTrackStatus_Clear
, ///< get_tile_track_status_proc
445 nullptr, ///< click_tile_proc
446 nullptr, ///< animate_tile_proc
447 TileLoop_Clear
, ///< tile_loop_proc
448 ChangeTileOwner_Clear
, ///< change_tile_owner_proc
449 nullptr, ///< add_produced_cargo_proc
450 nullptr, ///< vehicle_enter_tile_proc
451 GetFoundation_Clear
, ///< get_foundation_proc
452 TerraformTile_Clear
, ///< terraform_tile_proc