OpenTTD: src/clear_cmd.cpp Source File

OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

clear_cmd.cpp
Go to the documentation of this file.
1 /* $Id: clear_cmd.cpp 27200 2015-03-20 19:27:15Z 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 };
38 CommandCost price(EXPENSES_CONSTRUCTION);
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(ti->x ^ ti->y, 4, 3)], PAL_NONE);
60 }
61 }
62
63 static void DrawClearLandFence(const TileInfo *ti)
64 {
65 /* combine fences into one sprite object */
66 StartSpriteCombine();
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 }
100 EndSpriteCombine();
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) {
240 SetClearGroundDensity(tile, CLEAR_GRASS, 3);
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. */
252 if (_settings_game.construction.freeform_edges && DistanceFromEdge(tile) == 1) {
253 int z;
254 if (IsTileFlat(tile, &z) && z == 0) {
255 DoFloodTile(tile);
256 MarkTileDirtyByTile(tile);
257 return;
258 }
259 }
260 AmbientSoundEffect(tile);
261
262 switch (_settings_game.game_creation.landscape) {
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
322 SetGeneratingWorldProgress(GWP_ROUGH_ROCKY, gi + i);
323 do {
324 IncreaseGeneratingWorldProgress(GWP_ROUGH_ROCKY);
325 tile = RandomTile();
326 if (IsTileType(tile, MP_CLEAR) && !IsClearGround(tile, CLEAR_DESERT)) SetClearGroundDensity(tile, CLEAR_ROUGH, 3);
327 } while (--i);
328
329 /* add rocky tiles */
330 i = gi;
331 do {
332 uint32 r = Random();
333 tile = RandomTileSeed(r);
334
335 IncreaseGeneratingWorldProgress(GWP_ROUGH_ROCKY);
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
341 SetClearGroundDensity(tile, CLEAR_ROCKS, 3);
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 };