Update readme and changelog for v1.26.0
[openttd-joker.git] / src / clear_cmd.cpp
blob8ba8ba8fca43ace0e15459afa5a4810a8a69a0c5
1 /* $Id$ */
3 /*
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/>.
8 */
10 /** @file clear_cmd.cpp Commands related to clear tiles. */
12 #include "stdafx.h"
13 #include "clear_map.h"
14 #include "command_func.h"
15 #include "landscape.h"
16 #include "genworld.h"
17 #include "viewport_func.h"
18 #include "water.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[] = {
31 PR_CLEAR_GRASS,
32 PR_CLEAR_ROUGH,
33 PR_CLEAR_ROCKS,
34 PR_CLEAR_FIELDS,
35 PR_CLEAR_ROUGH,
36 PR_CLEAR_ROUGH,
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);
46 return price;
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);
63 } else {
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 */
91 StartSpriteCombine();
93 int maxz = GetSlopeMaxPixelZ(ti->tileh);
95 uint fence_nw = GetFence(ti->tile, DIAGDIR_NW);
96 if (fence_nw != 0) {
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);
103 if (fence_ne != 0) {
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);
115 if (fence_sw != 0) {
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);
120 if (fence_se != 0) {
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);
125 EndSpriteCombine();
128 static void DrawTile_Clear(TileInfo *ti)
130 switch (GetClearGround(ti->tile)) {
131 case CLEAR_GRASS:
132 DrawClearLandTile(ti, GetClearDensity(ti->tile));
133 break;
135 case CLEAR_ROUGH:
136 DrawHillyLandTile(ti);
137 break;
139 case CLEAR_ROCKS:
140 DrawGroundSprite(GetSpriteIDForRocks(ti->tileh, TileHash(ti->x, ti->y)), PAL_NONE);
141 break;
143 case CLEAR_FIELDS:
144 DrawGroundSprite(GetSpriteIDForFields(ti->tileh, GetFieldType(ti->tile)), PAL_NONE);
145 DrawClearLandFence(ti);
146 break;
148 case CLEAR_SNOW:
149 case CLEAR_DESERT:
150 DrawGroundSprite(GetSpriteIDForSnowDesert(ti->tileh, GetClearDensity(ti->tile)), PAL_NONE);
151 break;
154 DrawOverlay(ti, MP_CLEAR);
155 DrawBridgeMiddle(ti);
158 static int GetSlopePixelZ_Clear(TileIndex tile, uint x, uint y)
160 int z;
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));
174 bool dirty = false;
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);
179 dirty = true;
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);
185 dirty = true;
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);
191 dirty = true;
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);
197 dirty = true;
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;
209 if (k < 0) {
210 /* Below the snow line, do nothing if no snow. */
211 if (!IsSnowTile(tile)) return;
212 } else {
213 /* At or above the snow line, make snow tile if needed. */
214 if (!IsSnowTile(tile)) {
215 MakeSnow(tile);
216 MarkTileDirtyByTile(tile);
217 return;
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);
228 } else {
229 /* Density at the required level. */
230 if (k >= 0) return;
231 ClearSnow(tile);
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 */
252 uint current = 0;
253 if (IsClearGround(tile, CLEAR_DESERT)) current = GetClearDensity(tile);
255 /* Expected desert level - 0 if it shouldn't be desert */
256 uint expected = 0;
257 if (GetTropicZone(tile) == TROPICZONE_DESERT) {
258 expected = 3;
259 } else if (NeighbourIsDesert(tile)) {
260 expected = 1;
263 if (current == expected) return;
265 if (expected == 0) {
266 SetClearGroundDensity(tile, CLEAR_GRASS, 3);
267 } else {
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) {
279 int z;
280 if (IsTileFlat(tile, &z) && z == 0) {
281 DoFloodTile(tile);
282 return;
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)) {
293 case CLEAR_GRASS:
294 if (GetClearDensity(tile) == 3) return;
296 if (_game_mode != GM_EDITOR) {
297 if (GetClearCounter(tile) < 7) {
298 AddClearCounter(tile, 1);
299 return;
300 } else {
301 SetClearCounter(tile, 0);
302 AddClearDensity(tile, 1);
304 } else {
305 SetClearGroundDensity(tile, GB(Random(), 0, 8) > 21 ? CLEAR_GRASS : CLEAR_ROUGH, 3);
307 break;
309 case CLEAR_FIELDS:
310 UpdateFences(tile);
312 if (_game_mode == GM_EDITOR) return;
314 if (GetClearCounter(tile) < 7) {
315 AddClearCounter(tile, 1);
316 return;
317 } else {
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);
324 } else {
325 uint field_type = GetFieldType(tile);
326 field_type = (field_type < 8) ? field_type + 1 : 0;
327 SetFieldType(tile, field_type);
329 break;
331 default:
332 return;
335 MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP);
338 void GenerateClearTile()
340 uint i, gi;
341 TileIndex tile;
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);
365 else {
366 SetClearGroundDensity(possible_mountain_tile, CLEAR_ROUGH, 3);
371 IncreaseGeneratingWorldProgress(GWP_ROUGH_ROCKY);
374 do {
375 IncreaseGeneratingWorldProgress(GWP_ROUGH_ROCKY);
376 tile = RandomTile();
377 if (IsTileType(tile, MP_CLEAR) && !IsClearGround(tile, CLEAR_DESERT)) SetClearGroundDensity(tile, CLEAR_ROUGH, 3);
378 } while (--i);
380 /* add rocky tiles */
381 i = gi;
382 do {
383 uint32 r = Random();
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;
389 for (;;) {
390 TileIndex tile_new;
392 SetClearGroundDensity(tile, CLEAR_ROCKS, 3);
393 do {
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));
397 tile = tile_new;
399 get_out:;
401 } while (--i);
404 static TrackStatus GetTileTrackStatus_Clear(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
406 return 0;
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;
422 } else {
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)
430 return;
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