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/>.
8 /** @file clear_cmd.cpp Commands related to clear tiles. */
11 #include "clear_map.h"
12 #include "command_func.h"
13 #include "landscape.h"
15 #include "viewport_func.h"
16 #include "core/random_func.hpp"
17 #include "newgrf_generic.h"
18 #include "landscape_cmd.h"
20 #include "table/strings.h"
21 #include "table/sprites.h"
22 #include "table/clear_land.h"
24 #include "safeguards.h"
26 static CommandCost
ClearTile_Clear(TileIndex tile
, DoCommandFlag flags
)
28 static const Price clear_price_table
[] = {
36 CommandCost
price(EXPENSES_CONSTRUCTION
);
38 if (!IsClearGround(tile
, CLEAR_GRASS
) || GetClearDensity(tile
) != 0) {
39 price
.AddCost(_price
[clear_price_table
[GetClearGround(tile
)]]);
42 if (flags
& DC_EXEC
) DoClearSquare(tile
);
47 void DrawClearLandTile(const TileInfo
*ti
, uint8_t set
)
49 DrawGroundSprite(SPR_FLAT_BARE_LAND
+ SlopeToSpriteOffset(ti
->tileh
) + set
* 19, PAL_NONE
);
52 void DrawHillyLandTile(const TileInfo
*ti
)
54 if (ti
->tileh
!= SLOPE_FLAT
) {
55 DrawGroundSprite(SPR_FLAT_ROUGH_LAND
+ SlopeToSpriteOffset(ti
->tileh
), PAL_NONE
);
57 DrawGroundSprite(_landscape_clear_sprites_rough
[GB(TileHash(ti
->x
, ti
->y
), 0, 3)], PAL_NONE
);
61 static void DrawClearLandFence(const TileInfo
*ti
)
63 /* combine fences into one sprite object */
66 int maxz
= GetSlopeMaxPixelZ(ti
->tileh
);
68 uint fence_nw
= GetFence(ti
->tile
, DIAGDIR_NW
);
70 int z
= GetSlopePixelZInCorner(ti
->tileh
, CORNER_W
);
71 SpriteID sprite
= _clear_land_fence_sprites
[fence_nw
- 1] + _fence_mod_by_tileh_nw
[ti
->tileh
];
72 AddSortableSpriteToDraw(sprite
, PAL_NONE
, ti
->x
, ti
->y
- 16, 16, 32, maxz
- z
+ 4, ti
->z
+ z
, false, 0, 16, -z
);
75 uint fence_ne
= GetFence(ti
->tile
, DIAGDIR_NE
);
77 int z
= GetSlopePixelZInCorner(ti
->tileh
, CORNER_E
);
78 SpriteID sprite
= _clear_land_fence_sprites
[fence_ne
- 1] + _fence_mod_by_tileh_ne
[ti
->tileh
];
79 AddSortableSpriteToDraw(sprite
, PAL_NONE
, ti
->x
- 16, ti
->y
, 32, 16, maxz
- z
+ 4, ti
->z
+ z
, false, 16, 0, -z
);
82 uint fence_sw
= GetFence(ti
->tile
, DIAGDIR_SW
);
83 uint fence_se
= GetFence(ti
->tile
, DIAGDIR_SE
);
85 if (fence_sw
!= 0 || fence_se
!= 0) {
86 int z
= GetSlopePixelZInCorner(ti
->tileh
, CORNER_S
);
89 SpriteID sprite
= _clear_land_fence_sprites
[fence_sw
- 1] + _fence_mod_by_tileh_sw
[ti
->tileh
];
90 AddSortableSpriteToDraw(sprite
, PAL_NONE
, ti
->x
, ti
->y
, 16, 16, maxz
- z
+ 4, ti
->z
+ z
, false, 0, 0, -z
);
94 SpriteID sprite
= _clear_land_fence_sprites
[fence_se
- 1] + _fence_mod_by_tileh_se
[ti
->tileh
];
95 AddSortableSpriteToDraw(sprite
, PAL_NONE
, ti
->x
, ti
->y
, 16, 16, maxz
- z
+ 4, ti
->z
+ z
, false, 0, 0, -z
);
101 static void DrawTile_Clear(TileInfo
*ti
)
103 switch (GetClearGround(ti
->tile
)) {
105 DrawClearLandTile(ti
, GetClearDensity(ti
->tile
));
109 DrawHillyLandTile(ti
);
113 DrawGroundSprite((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
);
117 DrawGroundSprite(_clear_land_sprites_farmland
[GetFieldType(ti
->tile
)] + SlopeToSpriteOffset(ti
->tileh
), PAL_NONE
);
118 DrawClearLandFence(ti
);
123 DrawGroundSprite(_clear_land_sprites_snow_desert
[GetClearDensity(ti
->tile
)] + SlopeToSpriteOffset(ti
->tileh
), PAL_NONE
);
127 DrawBridgeMiddle(ti
);
130 static int GetSlopePixelZ_Clear(TileIndex tile
, uint x
, uint y
, bool)
132 auto [tileh
, z
] = GetTilePixelSlope(tile
);
134 return z
+ GetPartialPixelZ(x
& 0xF, y
& 0xF, tileh
);
137 static Foundation
GetFoundation_Clear(TileIndex
, Slope
)
139 return FOUNDATION_NONE
;
142 static void UpdateFences(TileIndex tile
)
144 assert(IsTileType(tile
, MP_CLEAR
) && IsClearGround(tile
, CLEAR_FIELDS
));
147 for (DiagDirection dir
= DIAGDIR_BEGIN
; dir
< DIAGDIR_END
; dir
++) {
148 if (GetFence(tile
, dir
) != 0) continue;
149 TileIndex neighbour
= tile
+ TileOffsByDiagDir(dir
);
150 if (IsTileType(neighbour
, MP_CLEAR
) && IsClearGround(neighbour
, CLEAR_FIELDS
)) continue;
151 SetFence(tile
, dir
, 3);
155 if (dirty
) MarkTileDirtyByTile(tile
);
159 /** Convert to or from snowy tiles. */
160 static void TileLoopClearAlps(TileIndex tile
)
162 int k
= GetTileZ(tile
) - GetSnowLine() + 1;
164 if (!IsSnowTile(tile
)) {
165 /* Below the snow line, do nothing if no snow. */
166 /* At or above the snow line, make snow tile if needed. */
169 MarkTileDirtyByTile(tile
);
174 /* Update snow density. */
175 uint current_density
= GetClearDensity(tile
);
176 uint req_density
= (k
< 0) ? 0u : std::min
<uint
>(k
, 3u);
178 if (current_density
== req_density
) {
179 /* Density at the required level. */
183 AddClearDensity(tile
, current_density
< req_density
? 1 : -1);
186 MarkTileDirtyByTile(tile
);
190 * Tests if at least one surrounding tile is non-desert
191 * @param tile tile to check
192 * @return does this tile have at least one non-desert tile around?
194 static inline bool NeighbourIsNormal(TileIndex tile
)
196 for (DiagDirection dir
= DIAGDIR_BEGIN
; dir
< DIAGDIR_END
; dir
++) {
197 TileIndex t
= tile
+ TileOffsByDiagDir(dir
);
198 if (!IsValidTile(t
)) continue;
199 if (GetTropicZone(t
) != TROPICZONE_DESERT
) return true;
200 if (HasTileWaterClass(t
) && GetWaterClass(t
) == WATER_CLASS_SEA
) return true;
205 static void TileLoopClearDesert(TileIndex tile
)
207 /* Current desert level - 0 if it is not desert */
209 if (IsClearGround(tile
, CLEAR_DESERT
)) current
= GetClearDensity(tile
);
211 /* Expected desert level - 0 if it shouldn't be desert */
213 if (GetTropicZone(tile
) == TROPICZONE_DESERT
) {
214 expected
= NeighbourIsNormal(tile
) ? 1 : 3;
217 if (current
== expected
) return;
220 SetClearGroundDensity(tile
, CLEAR_GRASS
, 3);
222 /* Transition from clear to desert is not smooth (after clearing desert tile) */
223 SetClearGroundDensity(tile
, CLEAR_DESERT
, expected
);
226 MarkTileDirtyByTile(tile
);
229 static void TileLoop_Clear(TileIndex tile
)
231 AmbientSoundEffect(tile
);
233 switch (_settings_game
.game_creation
.landscape
) {
234 case LT_TROPIC
: TileLoopClearDesert(tile
); break;
235 case LT_ARCTIC
: TileLoopClearAlps(tile
); break;
238 switch (GetClearGround(tile
)) {
240 if (GetClearDensity(tile
) == 3) return;
242 if (_game_mode
!= GM_EDITOR
) {
243 if (GetClearCounter(tile
) < 7) {
244 AddClearCounter(tile
, 1);
247 SetClearCounter(tile
, 0);
248 AddClearDensity(tile
, 1);
251 SetClearGroundDensity(tile
, GB(Random(), 0, 8) > 21 ? CLEAR_GRASS
: CLEAR_ROUGH
, 3);
258 if (_game_mode
== GM_EDITOR
) return;
260 if (GetClearCounter(tile
) < 7) {
261 AddClearCounter(tile
, 1);
264 SetClearCounter(tile
, 0);
267 if (GetIndustryIndexOfField(tile
) == INVALID_INDUSTRY
&& GetFieldType(tile
) >= 7) {
268 /* This farmfield is no longer farmfield, so make it grass again */
269 MakeClear(tile
, CLEAR_GRASS
, 2);
271 uint field_type
= GetFieldType(tile
);
272 field_type
= (field_type
< 8) ? field_type
+ 1 : 0;
273 SetFieldType(tile
, field_type
);
281 MarkTileDirtyByTile(tile
);
284 void GenerateClearTile()
289 /* add rough tiles */
290 i
= Map::ScaleBySize(GB(Random(), 0, 10) + 0x400);
291 gi
= Map::ScaleBySize(GB(Random(), 0, 7) + 0x80);
293 SetGeneratingWorldProgress(GWP_ROUGH_ROCKY
, gi
+ i
);
295 IncreaseGeneratingWorldProgress(GWP_ROUGH_ROCKY
);
297 if (IsTileType(tile
, MP_CLEAR
) && !IsClearGround(tile
, CLEAR_DESERT
)) SetClearGroundDensity(tile
, CLEAR_ROUGH
, 3);
300 /* add rocky tiles */
303 uint32_t r
= Random();
304 tile
= RandomTileSeed(r
);
306 IncreaseGeneratingWorldProgress(GWP_ROUGH_ROCKY
);
307 if (IsTileType(tile
, MP_CLEAR
) && !IsClearGround(tile
, CLEAR_DESERT
)) {
308 uint j
= GB(r
, 16, 4) + 5;
312 SetClearGroundDensity(tile
, CLEAR_ROCKS
, 3);
313 MarkTileDirtyByTile(tile
);
315 if (--j
== 0) goto get_out
;
316 tile_new
= tile
+ TileOffsByDiagDir((DiagDirection
)GB(Random(), 0, 2));
317 } while (!IsTileType(tile_new
, MP_CLEAR
) || IsClearGround(tile_new
, CLEAR_DESERT
));
325 static TrackStatus
GetTileTrackStatus_Clear(TileIndex
, TransportType
, uint
, DiagDirection
)
330 static const StringID _clear_land_str
[] = {
331 STR_LAI_CLEAR_DESCRIPTION_GRASS
,
332 STR_LAI_CLEAR_DESCRIPTION_ROUGH_LAND
,
333 STR_LAI_CLEAR_DESCRIPTION_ROCKS
,
334 STR_LAI_CLEAR_DESCRIPTION_FIELDS
,
335 STR_LAI_CLEAR_DESCRIPTION_SNOW_COVERED_LAND
,
336 STR_LAI_CLEAR_DESCRIPTION_DESERT
339 static void GetTileDesc_Clear(TileIndex tile
, TileDesc
*td
)
341 if (IsClearGround(tile
, CLEAR_GRASS
) && GetClearDensity(tile
) == 0) {
342 td
->str
= STR_LAI_CLEAR_DESCRIPTION_BARE_LAND
;
344 td
->str
= _clear_land_str
[GetClearGround(tile
)];
346 td
->owner
[0] = GetTileOwner(tile
);
349 static void ChangeTileOwner_Clear(TileIndex
, Owner
, Owner
)
354 static CommandCost
TerraformTile_Clear(TileIndex tile
, DoCommandFlag flags
, int, Slope
)
356 return Command
<CMD_LANDSCAPE_CLEAR
>::Do(flags
, tile
);
359 extern const TileTypeProcs _tile_type_clear_procs
= {
360 DrawTile_Clear
, ///< draw_tile_proc
361 GetSlopePixelZ_Clear
, ///< get_slope_z_proc
362 ClearTile_Clear
, ///< clear_tile_proc
363 nullptr, ///< add_accepted_cargo_proc
364 GetTileDesc_Clear
, ///< get_tile_desc_proc
365 GetTileTrackStatus_Clear
, ///< get_tile_track_status_proc
366 nullptr, ///< click_tile_proc
367 nullptr, ///< animate_tile_proc
368 TileLoop_Clear
, ///< tile_loop_proc
369 ChangeTileOwner_Clear
, ///< change_tile_owner_proc
370 nullptr, ///< add_produced_cargo_proc
371 nullptr, ///< vehicle_enter_tile_proc
372 GetFoundation_Clear
, ///< get_foundation_proc
373 TerraformTile_Clear
, ///< terraform_tile_proc