Codefix: Documentation comment in IndustryDirectoryWindow (#13059)
[openttd-github.git] / src / clear_cmd.cpp
blobc40904e0fd0d56fd1131bd81c66a075be9d88767
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 */
8 /** @file clear_cmd.cpp Commands related to clear tiles. */
10 #include "stdafx.h"
11 #include "clear_map.h"
12 #include "command_func.h"
13 #include "landscape.h"
14 #include "genworld.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[] = {
29 PR_CLEAR_GRASS,
30 PR_CLEAR_ROUGH,
31 PR_CLEAR_ROCKS,
32 PR_CLEAR_FIELDS,
33 PR_CLEAR_ROUGH,
34 PR_CLEAR_ROUGH,
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);
44 return price;
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);
56 } else {
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 */
64 StartSpriteCombine();
66 int maxz = GetSlopeMaxPixelZ(ti->tileh);
68 uint fence_nw = GetFence(ti->tile, DIAGDIR_NW);
69 if (fence_nw != 0) {
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);
76 if (fence_ne != 0) {
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);
88 if (fence_sw != 0) {
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);
93 if (fence_se != 0) {
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);
98 EndSpriteCombine();
101 static void DrawTile_Clear(TileInfo *ti)
103 switch (GetClearGround(ti->tile)) {
104 case CLEAR_GRASS:
105 DrawClearLandTile(ti, GetClearDensity(ti->tile));
106 break;
108 case CLEAR_ROUGH:
109 DrawHillyLandTile(ti);
110 break;
112 case CLEAR_ROCKS:
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);
114 break;
116 case CLEAR_FIELDS:
117 DrawGroundSprite(_clear_land_sprites_farmland[GetFieldType(ti->tile)] + SlopeToSpriteOffset(ti->tileh), PAL_NONE);
118 DrawClearLandFence(ti);
119 break;
121 case CLEAR_SNOW:
122 case CLEAR_DESERT:
123 DrawGroundSprite(_clear_land_sprites_snow_desert[GetClearDensity(ti->tile)] + SlopeToSpriteOffset(ti->tileh), PAL_NONE);
124 break;
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));
145 bool dirty = false;
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);
152 dirty = true;
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. */
167 if (k >= 0) {
168 MakeSnow(tile);
169 MarkTileDirtyByTile(tile);
171 return;
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. */
180 if (k >= 0) return;
181 ClearSnow(tile);
182 } else {
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;
202 return false;
205 static void TileLoopClearDesert(TileIndex tile)
207 /* Current desert level - 0 if it is not desert */
208 uint current = 0;
209 if (IsClearGround(tile, CLEAR_DESERT)) current = GetClearDensity(tile);
211 /* Expected desert level - 0 if it shouldn't be desert */
212 uint expected = 0;
213 if (GetTropicZone(tile) == TROPICZONE_DESERT) {
214 expected = NeighbourIsNormal(tile) ? 1 : 3;
217 if (current == expected) return;
219 if (expected == 0) {
220 SetClearGroundDensity(tile, CLEAR_GRASS, 3);
221 } else {
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)) {
239 case CLEAR_GRASS:
240 if (GetClearDensity(tile) == 3) return;
242 if (_game_mode != GM_EDITOR) {
243 if (GetClearCounter(tile) < 7) {
244 AddClearCounter(tile, 1);
245 return;
246 } else {
247 SetClearCounter(tile, 0);
248 AddClearDensity(tile, 1);
250 } else {
251 SetClearGroundDensity(tile, GB(Random(), 0, 8) > 21 ? CLEAR_GRASS : CLEAR_ROUGH, 3);
253 break;
255 case CLEAR_FIELDS:
256 UpdateFences(tile);
258 if (_game_mode == GM_EDITOR) return;
260 if (GetClearCounter(tile) < 7) {
261 AddClearCounter(tile, 1);
262 return;
263 } else {
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);
270 } else {
271 uint field_type = GetFieldType(tile);
272 field_type = (field_type < 8) ? field_type + 1 : 0;
273 SetFieldType(tile, field_type);
275 break;
277 default:
278 return;
281 MarkTileDirtyByTile(tile);
284 void GenerateClearTile()
286 uint i, gi;
287 TileIndex tile;
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);
294 do {
295 IncreaseGeneratingWorldProgress(GWP_ROUGH_ROCKY);
296 tile = RandomTile();
297 if (IsTileType(tile, MP_CLEAR) && !IsClearGround(tile, CLEAR_DESERT)) SetClearGroundDensity(tile, CLEAR_ROUGH, 3);
298 } while (--i);
300 /* add rocky tiles */
301 i = gi;
302 do {
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;
309 for (;;) {
310 TileIndex tile_new;
312 SetClearGroundDensity(tile, CLEAR_ROCKS, 3);
313 MarkTileDirtyByTile(tile);
314 do {
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));
318 tile = tile_new;
320 get_out:;
322 } while (--i);
325 static TrackStatus GetTileTrackStatus_Clear(TileIndex, TransportType, uint, DiagDirection)
327 return 0;
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;
343 } else {
344 td->str = _clear_land_str[GetClearGround(tile)];
346 td->owner[0] = GetTileOwner(tile);
349 static void ChangeTileOwner_Clear(TileIndex, Owner, Owner)
351 return;
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