OpenTTD
clear_cmd.cpp
Go to the documentation of this file.
1 /* $Id: clear_cmd.cpp 27657 2016-10-02 13:41:56Z frosch $ */
2 
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  */
9 
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"
21 
22 #include "table/strings.h"
23 #include "table/sprites.h"
24 #include "table/clear_land.h"
25 
26 #include "safeguards.h"
27 
28 static CommandCost ClearTile_Clear(TileIndex tile, DoCommandFlag flags)
29 {
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,
37  };
39 
40  if (!IsClearGround(tile, CLEAR_GRASS) || GetClearDensity(tile) != 0) {
41  price.AddCost(_price[clear_price_table[GetClearGround(tile)]]);
42  }
43 
44  if (flags & DC_EXEC) DoClearSquare(tile);
45 
46  return price;
47 }
48 
49 void DrawClearLandTile(const TileInfo *ti, byte set)
50 {
51  DrawGroundSprite(SPR_FLAT_BARE_LAND + SlopeToSpriteOffset(ti->tileh) + set * 19, PAL_NONE);
52 }
53 
54 void DrawHillyLandTile(const TileInfo *ti)
55 {
56  if (ti->tileh != SLOPE_FLAT) {
57  DrawGroundSprite(SPR_FLAT_ROUGH_LAND + SlopeToSpriteOffset(ti->tileh), PAL_NONE);
58  } else {
59  DrawGroundSprite(_landscape_clear_sprites_rough[GB(TileHash(ti->x, ti->y), 0, 3)], PAL_NONE);
60  }
61 }
62 
63 static void DrawClearLandFence(const TileInfo *ti)
64 {
65  /* combine fences into one sprite object */
67 
68  int maxz = GetSlopeMaxPixelZ(ti->tileh);
69 
70  uint fence_nw = GetFence(ti->tile, DIAGDIR_NW);
71  if (fence_nw != 0) {
72  int z = GetSlopePixelZInCorner(ti->tileh, CORNER_W);
73  SpriteID sprite = _clear_land_fence_sprites[fence_nw - 1] + _fence_mod_by_tileh_nw[ti->tileh];
74  AddSortableSpriteToDraw(sprite, PAL_NONE, ti->x, ti->y - 15, 16, 31, maxz - z + 4, ti->z + z, false, 0, 15, -z);
75  }
76 
77  uint fence_ne = GetFence(ti->tile, DIAGDIR_NE);
78  if (fence_ne != 0) {
79  int z = GetSlopePixelZInCorner(ti->tileh, CORNER_E);
80  SpriteID sprite = _clear_land_fence_sprites[fence_ne - 1] + _fence_mod_by_tileh_ne[ti->tileh];
81  AddSortableSpriteToDraw(sprite, PAL_NONE, ti->x - 15, ti->y, 31, 16, maxz - z + 4, ti->z + z, false, 15, 0, -z);
82  }
83 
84  uint fence_sw = GetFence(ti->tile, DIAGDIR_SW);
85  uint fence_se = GetFence(ti->tile, DIAGDIR_SE);
86 
87  if (fence_sw != 0 || fence_se != 0) {
88  int z = GetSlopePixelZInCorner(ti->tileh, CORNER_S);
89 
90  if (fence_sw != 0) {
91  SpriteID sprite = _clear_land_fence_sprites[fence_sw - 1] + _fence_mod_by_tileh_sw[ti->tileh];
92  AddSortableSpriteToDraw(sprite, PAL_NONE, ti->x, ti->y, 16, 16, maxz - z + 4, ti->z + z, false, 0, 0, -z);
93  }
94 
95  if (fence_se != 0) {
96  SpriteID sprite = _clear_land_fence_sprites[fence_se - 1] + _fence_mod_by_tileh_se[ti->tileh];
97  AddSortableSpriteToDraw(sprite, PAL_NONE, ti->x, ti->y, 16, 16, maxz - z + 4, ti->z + z, false, 0, 0, -z);
98  }
99  }
101 }
102 
103 static void DrawTile_Clear(TileInfo *ti)
104 {
105  switch (GetClearGround(ti->tile)) {
106  case CLEAR_GRASS:
107  DrawClearLandTile(ti, GetClearDensity(ti->tile));
108  break;
109 
110  case CLEAR_ROUGH:
111  DrawHillyLandTile(ti);
112  break;
113 
114  case CLEAR_ROCKS:
115  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);
116  break;
117 
118  case CLEAR_FIELDS:
119  DrawGroundSprite(_clear_land_sprites_farmland[GetFieldType(ti->tile)] + SlopeToSpriteOffset(ti->tileh), PAL_NONE);
120  DrawClearLandFence(ti);
121  break;
122 
123  case CLEAR_SNOW:
124  case CLEAR_DESERT:
125  DrawGroundSprite(_clear_land_sprites_snow_desert[GetClearDensity(ti->tile)] + SlopeToSpriteOffset(ti->tileh), PAL_NONE);
126  break;
127  }
128 
129  DrawBridgeMiddle(ti);
130 }
131 
132 static int GetSlopePixelZ_Clear(TileIndex tile, uint x, uint y)
133 {
134  int z;
135  Slope tileh = GetTilePixelSlope(tile, &z);
136 
137  return z + GetPartialPixelZ(x & 0xF, y & 0xF, tileh);
138 }
139 
140 static Foundation GetFoundation_Clear(TileIndex tile, Slope tileh)
141 {
142  return FOUNDATION_NONE;
143 }
144 
145 static void UpdateFences(TileIndex tile)
146 {
147  assert(IsTileType(tile, MP_CLEAR) && IsClearGround(tile, CLEAR_FIELDS));
148  bool dirty = false;
149 
150  bool neighbour = (IsTileType(TILE_ADDXY(tile, 1, 0), MP_CLEAR) && IsClearGround(TILE_ADDXY(tile, 1, 0), CLEAR_FIELDS));
151  if (!neighbour && GetFence(tile, DIAGDIR_SW) == 0) {
152  SetFence(tile, DIAGDIR_SW, 3);
153  dirty = true;
154  }
155 
156  neighbour = (IsTileType(TILE_ADDXY(tile, 0, 1), MP_CLEAR) && IsClearGround(TILE_ADDXY(tile, 0, 1), CLEAR_FIELDS));
157  if (!neighbour && GetFence(tile, DIAGDIR_SE) == 0) {
158  SetFence(tile, DIAGDIR_SE, 3);
159  dirty = true;
160  }
161 
162  neighbour = (IsTileType(TILE_ADDXY(tile, -1, 0), MP_CLEAR) && IsClearGround(TILE_ADDXY(tile, -1, 0), CLEAR_FIELDS));
163  if (!neighbour && GetFence(tile, DIAGDIR_NE) == 0) {
164  SetFence(tile, DIAGDIR_NE, 3);
165  dirty = true;
166  }
167 
168  neighbour = (IsTileType(TILE_ADDXY(tile, 0, -1), MP_CLEAR) && IsClearGround(TILE_ADDXY(tile, 0, -1), CLEAR_FIELDS));
169  if (!neighbour && GetFence(tile, DIAGDIR_NW) == 0) {
170  SetFence(tile, DIAGDIR_NW, 3);
171  dirty = true;
172  }
173 
174  if (dirty) MarkTileDirtyByTile(tile);
175 }
176 
177 
179 static void TileLoopClearAlps(TileIndex tile)
180 {
181  int k = GetTileZ(tile) - GetSnowLine() + 1;
182 
183  if (k < 0) {
184  /* Below the snow line, do nothing if no snow. */
185  if (!IsSnowTile(tile)) return;
186  } else {
187  /* At or above the snow line, make snow tile if needed. */
188  if (!IsSnowTile(tile)) {
189  MakeSnow(tile);
190  MarkTileDirtyByTile(tile);
191  return;
192  }
193  }
194  /* Update snow density. */
195  uint current_density = GetClearDensity(tile);
196  uint req_density = (k < 0) ? 0u : min((uint)k, 3);
197 
198  if (current_density < req_density) {
199  AddClearDensity(tile, 1);
200  } else if (current_density > req_density) {
201  AddClearDensity(tile, -1);
202  } else {
203  /* Density at the required level. */
204  if (k >= 0) return;
205  ClearSnow(tile);
206  }
207  MarkTileDirtyByTile(tile);
208 }
209 
215 static inline bool NeighbourIsDesert(TileIndex tile)
216 {
217  return GetTropicZone(tile + TileDiffXY( 1, 0)) == TROPICZONE_DESERT ||
218  GetTropicZone(tile + TileDiffXY( -1, 0)) == TROPICZONE_DESERT ||
219  GetTropicZone(tile + TileDiffXY( 0, 1)) == TROPICZONE_DESERT ||
220  GetTropicZone(tile + TileDiffXY( 0, -1)) == TROPICZONE_DESERT;
221 }
222 
223 static void TileLoopClearDesert(TileIndex tile)
224 {
225  /* Current desert level - 0 if it is not desert */
226  uint current = 0;
227  if (IsClearGround(tile, CLEAR_DESERT)) current = GetClearDensity(tile);
228 
229  /* Expected desert level - 0 if it shouldn't be desert */
230  uint expected = 0;
231  if (GetTropicZone(tile) == TROPICZONE_DESERT) {
232  expected = 3;
233  } else if (NeighbourIsDesert(tile)) {
234  expected = 1;
235  }
236 
237  if (current == expected) return;
238 
239  if (expected == 0) {
241  } else {
242  /* Transition from clear to desert is not smooth (after clearing desert tile) */
243  SetClearGroundDensity(tile, CLEAR_DESERT, expected);
244  }
245 
246  MarkTileDirtyByTile(tile);
247 }
248 
249 static void TileLoop_Clear(TileIndex tile)
250 {
251  /* If the tile is at any edge flood it to prevent maps without water. */
253  int z;
254  if (IsTileFlat(tile, &z) && z == 0) {
255  DoFloodTile(tile);
256  MarkTileDirtyByTile(tile);
257  return;
258  }
259  }
260  AmbientSoundEffect(tile);
261 
263  case LT_TROPIC: TileLoopClearDesert(tile); break;
264  case LT_ARCTIC: TileLoopClearAlps(tile); break;
265  }
266 
267  switch (GetClearGround(tile)) {
268  case CLEAR_GRASS:
269  if (GetClearDensity(tile) == 3) return;
270 
271  if (_game_mode != GM_EDITOR) {
272  if (GetClearCounter(tile) < 7) {
273  AddClearCounter(tile, 1);
274  return;
275  } else {
276  SetClearCounter(tile, 0);
277  AddClearDensity(tile, 1);
278  }
279  } else {
280  SetClearGroundDensity(tile, GB(Random(), 0, 8) > 21 ? CLEAR_GRASS : CLEAR_ROUGH, 3);
281  }
282  break;
283 
284  case CLEAR_FIELDS:
285  UpdateFences(tile);
286 
287  if (_game_mode == GM_EDITOR) return;
288 
289  if (GetClearCounter(tile) < 7) {
290  AddClearCounter(tile, 1);
291  return;
292  } else {
293  SetClearCounter(tile, 0);
294  }
295 
296  if (GetIndustryIndexOfField(tile) == INVALID_INDUSTRY && GetFieldType(tile) >= 7) {
297  /* This farmfield is no longer farmfield, so make it grass again */
298  MakeClear(tile, CLEAR_GRASS, 2);
299  } else {
300  uint field_type = GetFieldType(tile);
301  field_type = (field_type < 8) ? field_type + 1 : 0;
302  SetFieldType(tile, field_type);
303  }
304  break;
305 
306  default:
307  return;
308  }
309 
310  MarkTileDirtyByTile(tile);
311 }
312 
313 void GenerateClearTile()
314 {
315  uint i, gi;
316  TileIndex tile;
317 
318  /* add rough tiles */
319  i = ScaleByMapSize(GB(Random(), 0, 10) + 0x400);
320  gi = ScaleByMapSize(GB(Random(), 0, 7) + 0x80);
321 
323  do {
325  tile = RandomTile();
327  } while (--i);
328 
329  /* add rocky tiles */
330  i = gi;
331  do {
332  uint32 r = Random();
333  tile = RandomTileSeed(r);
334 
336  if (IsTileType(tile, MP_CLEAR) && !IsClearGround(tile, CLEAR_DESERT)) {
337  uint j = GB(r, 16, 4) + 5;
338  for (;;) {
339  TileIndex tile_new;
340 
342  do {
343  if (--j == 0) goto get_out;
344  tile_new = tile + TileOffsByDiagDir((DiagDirection)GB(Random(), 0, 2));
345  } while (!IsTileType(tile_new, MP_CLEAR) || IsClearGround(tile_new, CLEAR_DESERT));
346  tile = tile_new;
347  }
348 get_out:;
349  }
350  } while (--i);
351 }
352 
353 static TrackStatus GetTileTrackStatus_Clear(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
354 {
355  return 0;
356 }
357 
358 static const StringID _clear_land_str[] = {
359  STR_LAI_CLEAR_DESCRIPTION_GRASS,
360  STR_LAI_CLEAR_DESCRIPTION_ROUGH_LAND,
361  STR_LAI_CLEAR_DESCRIPTION_ROCKS,
362  STR_LAI_CLEAR_DESCRIPTION_FIELDS,
363  STR_LAI_CLEAR_DESCRIPTION_SNOW_COVERED_LAND,
364  STR_LAI_CLEAR_DESCRIPTION_DESERT
365 };
366 
367 static void GetTileDesc_Clear(TileIndex tile, TileDesc *td)
368 {
369  if (IsClearGround(tile, CLEAR_GRASS) && GetClearDensity(tile) == 0) {
370  td->str = STR_LAI_CLEAR_DESCRIPTION_BARE_LAND;
371  } else {
372  td->str = _clear_land_str[GetClearGround(tile)];
373  }
374  td->owner[0] = GetTileOwner(tile);
375 }
376 
377 static void ChangeTileOwner_Clear(TileIndex tile, Owner old_owner, Owner new_owner)
378 {
379  return;
380 }
381 
382 static CommandCost TerraformTile_Clear(TileIndex tile, DoCommandFlag flags, int z_new, Slope tileh_new)
383 {
384  return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
385 }
386 
387 extern const TileTypeProcs _tile_type_clear_procs = {
388  DrawTile_Clear,
389  GetSlopePixelZ_Clear,
390  ClearTile_Clear,
391  NULL,
392  GetTileDesc_Clear,
393  GetTileTrackStatus_Clear,
394  NULL,
395  NULL,
396  TileLoop_Clear,
397  ChangeTileOwner_Clear,
398  NULL,
399  NULL,
400  GetFoundation_Clear,
401  TerraformTile_Clear,
402 };