Translations update
[openttd/fttd.git] / src / clear_cmd.cpp
blob2f0fda4eb326f80c3998ace3dfec20838ebc7372
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 "map/ground.h"
14 #include "map/water.h"
15 #include "map/slope.h"
16 #include "command_func.h"
17 #include "landscape.h"
18 #include "genworld.h"
19 #include "viewport_func.h"
20 #include "water.h"
21 #include "company_base.h"
22 #include "company_func.h"
23 #include "town.h"
24 #include "sound_func.h"
25 #include "core/random_func.hpp"
26 #include "newgrf_generic.h"
27 #include "bridge.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[] = {
37 PR_CLEAR_GRASS,
38 PR_CLEAR_ROUGH,
39 PR_CLEAR_ROUGH,
40 PR_CLEAR_ROCKS,
41 PR_CLEAR_ROUGH,
44 Money cost;
46 switch (GetTileSubtype(tile)) {
47 default: NOT_REACHED();
49 case TT_GROUND_VOID:
50 return_cmd_error(STR_ERROR_OFF_EDGE_OF_MAP);
52 case TT_GROUND_FIELDS:
53 cost = _price[PR_CLEAR_FIELDS];
54 break;
56 case TT_GROUND_CLEAR:
57 if (IsSnowTile(tile)) {
58 cost = _price[PR_CLEAR_ROUGH];
59 } else if (IsClearGround(tile, GROUND_GRASS) && GetClearDensity(tile) == 0) {
60 cost = 0;
61 } else {
62 cost = _price[clear_price_table[GetClearGround(tile)]];
64 break;
66 case TT_GROUND_TREES:
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;
73 break;
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);
99 if (fence_nw != 0) {
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);
106 if (fence_ne != 0) {
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);
118 if (fence_sw != 0) {
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);
123 if (fence_se != 0) {
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 {
132 byte x, y;
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);
154 TreeListEnt te[4];
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;
164 te[i].pal = pal;
165 te[i].x = d->x;
166 te[i].y = d->y;
167 s++;
168 d++;
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;
176 uint mi = 0;
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;
181 mi = i;
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)) {
197 case TT_GROUND_VOID:
198 DrawVoidTile(ti);
199 return;
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);
205 break;
207 default:
208 switch (GetFullClearGround(ti->tile)) {
209 case GROUND_GRASS:
210 DrawClearLandTile(ti, GetClearDensity(ti->tile));
211 break;
213 case GROUND_SHORE:
214 DrawShoreTile (ti);
215 break;
217 case GROUND_ROUGH:
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)],
221 PAL_NONE);
222 break;
224 case GROUND_ROCKS:
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);
226 break;
228 default:
229 DrawGroundSprite (ti, _clear_land_sprites_snow_desert[GetClearDensity(ti->tile)] + SlopeToSpriteOffset(ti->tileh), PAL_NONE);
230 break;
233 if (!IsTileSubtype(ti->tile, TT_GROUND_TREES)) {
234 DrawBridgeMiddle(ti);
235 } else if (!IsInvisibilitySet(TO_TREES)) {
236 DrawTrees(ti);
239 break;
244 static int GetSlopePixelZ_Clear(TileIndex tile, uint x, uint y)
246 if (IsTileSubtype(tile, TT_GROUND_VOID)) return TilePixelHeight(tile);
248 int z;
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));
262 bool dirty = false;
264 bool neighbour = IsFieldsTile(TILE_ADDXY(tile, 1, 0));
265 if (!neighbour && GetFence(tile, DIAGDIR_SW) == 0) {
266 SetFence(tile, DIAGDIR_SW, 3);
267 dirty = true;
270 neighbour = IsFieldsTile(TILE_ADDXY(tile, 0, 1));
271 if (!neighbour && GetFence(tile, DIAGDIR_SE) == 0) {
272 SetFence(tile, DIAGDIR_SE, 3);
273 dirty = true;
276 neighbour = IsFieldsTile(TILE_ADDXY(tile, -1, 0));
277 if (!neighbour && GetFence(tile, DIAGDIR_NE) == 0) {
278 SetFence(tile, DIAGDIR_NE, 3);
279 dirty = true;
282 neighbour = IsFieldsTile(TILE_ADDXY(tile, 0, -1));
283 if (!neighbour && GetFence(tile, DIAGDIR_NW) == 0) {
284 SetFence(tile, DIAGDIR_NW, 3);
285 dirty = true;
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. */
299 if (k < 0) return;
300 MakeSnow(tile);
301 } else {
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);
310 } else if (k < 0) {
311 ClearSnow(tile);
312 } else {
313 /* Density at the required level. */
314 if (IsTileSubtype(tile, TT_GROUND_TREES) && cur_density == 3) {
315 uint32 r = Random();
316 if (Chance16I(1, 200, r) && _settings_client.sound.ambient) {
317 SndPlayTileFx((r & 0x80000000) ? SND_39_HEAVY_WIND : SND_34_WIND, tile);
320 return;
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);
352 break;
354 case TT_GROUND_TREES:
355 if (GetTropicZone(tile) == TROPICZONE_RAINFOREST) {
356 static const SoundFx forest_sounds[] = {
357 SND_42_LOON_BIRD,
358 SND_43_LION,
359 SND_44_MONKEYS,
360 SND_48_DISTANT_BIRD
362 uint32 r = Random();
364 if (Chance16I(1, 200, r) && _settings_client.sound.ambient) SndPlayTileFx(forest_sounds[GB(r, 16, 2)], tile);
365 return;
367 /* fall through */
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;
374 if (expected == 0) {
375 SetClearGroundDensity(tile, GROUND_GRASS, 3);
376 } else {
377 /* Transition from clear to desert is not smooth (after clearing desert tile) */
378 SetClearGroundDensity(tile, GROUND_DESERT, expected);
380 break;
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);
397 } else {
398 switch (GB(Random(), 0, 3)) {
399 case 0: // start destructing
400 AddTreeGrowth(tile, 1);
401 break;
403 case 1: // add a tree
404 if (GetTreeCount(tile) < 4) {
405 AddTreeCount(tile, 1);
406 SetTreeGrowth(tile, 0);
407 break;
409 /* FALL THROUGH */
411 case 2: // add a neighbouring tree
412 AddNeighbouringTree(tile);
413 break;
415 default:
416 return;
419 break;
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);
426 } else {
427 /* just one tree, change type into clear */
428 Ground g = GetClearGround(tile);
429 if (g == GROUND_SHORE) {
430 MakeShore(tile);
431 } else {
432 MakeClear(tile, g, GetClearDensity(tile));
435 break;
437 default:
438 AddTreeGrowth(tile, 1);
439 break;
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);
449 } else {
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) {
452 int z;
453 if (IsTileFlat(tile, &z) && z == 0) {
454 DoFloodTile(tile);
455 MarkTileDirtyByTile(tile);
456 return;
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:
472 UpdateFences(tile);
474 if (_game_mode == GM_EDITOR) return;
476 if (GetClearCounter(tile) < 7) {
477 AddClearCounter(tile, 1);
478 return;
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);
486 } else {
487 uint field_type = GetFieldType(tile);
488 field_type = (field_type < 8) ? field_type + 1 : 0;
489 SetFieldType(tile, field_type);
491 break;
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);
500 return;
502 SetClearCounter(tile, 0);
503 AddClearDensity(tile, 1);
504 } else {
505 SetClearGroundDensity(tile, GB(Random(), 0, 8) > 21 ? GROUND_GRASS : GROUND_ROUGH, 3);
508 break;
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);
520 if (counter < 15) {
521 AddClearCounter(tile, 1);
522 return;
524 SetClearCounter(tile, 0);
525 HandleTreeGrowth(tile);
526 break;
530 MarkTileDirtyByTile(tile);
533 void GenerateClearTile()
535 uint i, gi;
536 TileIndex tile;
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);
543 do {
544 IncreaseGeneratingWorldProgress(GWP_ROUGH_ROCKY);
545 tile = RandomTile();
546 if (IsClearTile(tile) && !IsClearGround(tile, GROUND_DESERT)) SetClearGroundDensity(tile, GROUND_ROUGH, 3);
547 } while (--i);
549 /* add rocky tiles */
550 i = gi;
551 do {
552 uint32 r = Random();
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;
558 for (;;) {
559 TileIndex tile_new;
561 SetClearGroundDensity(tile, GROUND_ROCKS, 3);
562 do {
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));
566 tile = tile_new;
568 get_out:;
570 } while (--i);
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();
586 case TT_GROUND_VOID:
587 td->str = STR_EMPTY;
588 td->owner[0] = OWNER_NONE;
589 return;
591 case TT_GROUND_FIELDS:
592 td->str = STR_LAI_CLEAR_DESCRIPTION_FIELDS;
593 break;
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;
600 } else {
601 td->str = _clear_land_str[GetClearGround(tile)];
603 break;
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;
609 } else {
610 td->str = tt == TREE_CACTUS ? STR_LAI_TREE_NAME_CACTUS_PLANTS : STR_LAI_TREE_NAME_TREES;
612 break;
616 td->owner[0] = GetTileOwner(tile);
619 static void ChangeTileOwner_Clear(TileIndex tile, Owner old_owner, Owner new_owner)
621 return;
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