OpenTTD: src/water_cmd.cpp Source File

OpenTTD

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

- File List
- Globals

water_cmd.cpp
Go to the documentation of this file.
1 /* $Id: water_cmd.cpp 26879 2014-09-21 11:24:51Z rubidium $ */
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 "cmd_helper.h"
14 #include "landscape.h"
15 #include "viewport_func.h"
16 #include "command_func.h"
17 #include "town.h"
18 #include "news_func.h"
19 #include "depot_base.h"
20 #include "depot_func.h"
21 #include "water.h"
22 #include "industry_map.h"
23 #include "newgrf_canal.h"
24 #include "strings_func.h"
25 #include "vehicle_func.h"
26 #include "sound_func.h"
27 #include "company_func.h"
28 #include "clear_map.h"
29 #include "tree_map.h"
30 #include "aircraft.h"
31 #include "effectvehicle_func.h"
32 #include "tunnelbridge_map.h"
33 #include "station_base.h"
34 #include "ai/ai.hpp"
35 #include "game/game.hpp"
36 #include "core/random_func.hpp"
37 #include "core/backup_type.hpp"
38 #include "date_func.h"
39 #include "company_base.h"
40 #include "company_gui.h"
41 #include "newgrf_generic.h"
42
43 #include "table/strings.h"
44
45 #include "safeguards.h"
46
50 static const uint8 _flood_from_dirs[] = {
51 (1 << DIR_NW) | (1 << DIR_SW) | (1 << DIR_SE) | (1 << DIR_NE), // SLOPE_FLAT
52 (1 << DIR_NE) | (1 << DIR_SE), // SLOPE_W
53 (1 << DIR_NW) | (1 << DIR_NE), // SLOPE_S
54 (1 << DIR_NE), // SLOPE_SW
55 (1 << DIR_NW) | (1 << DIR_SW), // SLOPE_E
56 0, // SLOPE_EW
57 (1 << DIR_NW), // SLOPE_SE
58 (1 << DIR_N ) | (1 << DIR_NW) | (1 << DIR_NE), // SLOPE_WSE, SLOPE_STEEP_S
59 (1 << DIR_SW) | (1 << DIR_SE), // SLOPE_N
60 (1 << DIR_SE), // SLOPE_NW
61 0, // SLOPE_NS
62 (1 << DIR_E ) | (1 << DIR_NE) | (1 << DIR_SE), // SLOPE_NWS, SLOPE_STEEP_W
63 (1 << DIR_SW), // SLOPE_NE
64 (1 << DIR_S ) | (1 << DIR_SW) | (1 << DIR_SE), // SLOPE_ENW, SLOPE_STEEP_N
65 (1 << DIR_W ) | (1 << DIR_SW) | (1 << DIR_NW), // SLOPE_SEN, SLOPE_STEEP_E
66 };
67
74 static inline void MarkTileDirtyIfCanalOrRiver(TileIndex tile)
75 {
76 if (IsTileType(tile, MP_WATER) && (IsCanal(tile) || IsRiver(tile))) MarkTileDirtyByTile(tile);
77 }
78
85 static void MarkCanalsAndRiversAroundDirty(TileIndex tile)
86 {
87 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
88 MarkTileDirtyIfCanalOrRiver(tile + TileOffsByDir(dir));
89 }
90 }
91
92
102 CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
103 {
104 Axis axis = Extract<Axis, 0, 1>(p1);
105
106 TileIndex tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
107
108 if (!HasTileWaterGround(tile) || !HasTileWaterGround(tile2)) {
109 return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER);
110 }
111
112 if (IsBridgeAbove(tile) || IsBridgeAbove(tile2)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
113
114 if (!IsTileFlat(tile) || !IsTileFlat(tile2)) {
115 /* Prevent depots on rapids */
116 return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
117 }
118
119 if (!Depot::CanAllocateItem()) return CMD_ERROR;
120
121 WaterClass wc1 = GetWaterClass(tile);
122 WaterClass wc2 = GetWaterClass(tile2);
123 CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_DEPOT_SHIP]);
124
125 bool add_cost = !IsWaterTile(tile);
126 CommandCost ret = DoCommand(tile, 0, 0, flags | DC_AUTO, CMD_LANDSCAPE_CLEAR);
127 if (ret.Failed()) return ret;
128 if (add_cost) {
129 cost.AddCost(ret);
130 }
131 add_cost = !IsWaterTile(tile2);
132 ret = DoCommand(tile2, 0, 0, flags | DC_AUTO, CMD_LANDSCAPE_CLEAR);
133 if (ret.Failed()) return ret;
134 if (add_cost) {
135 cost.AddCost(ret);
136 }
137
138 if (flags & DC_EXEC) {
139 Depot *depot = new Depot(tile);
140 depot->build_date = _date;
141
142 if (wc1 == WATER_CLASS_CANAL || wc2 == WATER_CLASS_CANAL) {
143 /* Update infrastructure counts after the unconditional clear earlier. */
144 Company::Get(_current_company)->infrastructure.water += wc1 == WATER_CLASS_CANAL && wc2 == WATER_CLASS_CANAL ? 2 : 1;
145 }
146 Company::Get(_current_company)->infrastructure.water += 2 * LOCK_DEPOT_TILE_FACTOR;
147 DirtyCompanyInfrastructureWindows(_current_company);
148
149 MakeShipDepot(tile, _current_company, depot->index, DEPOT_PART_NORTH, axis, wc1);
150 MakeShipDepot(tile2, _current_company, depot->index, DEPOT_PART_SOUTH, axis, wc2);
151 MarkTileDirtyByTile(tile);
152 MarkTileDirtyByTile(tile2);
153 MakeDefaultName(depot);
154 }
155
156 return cost;
157 }
158
159 void MakeWaterKeepingClass(TileIndex tile, Owner o)
160 {
161 WaterClass wc = GetWaterClass(tile);
162
163 /* Autoslope might turn an originally canal or river tile into land */
164 int z;
165 Slope slope = GetTileSlope(tile, &z);
166
167 if (slope != SLOPE_FLAT) {
168 if (wc == WATER_CLASS_CANAL) {
169 /* If we clear the canal, we have to remove it from the infrastructure count as well. */
170 Company *c = Company::GetIfValid(o);
171 if (c != NULL) {
172 c->infrastructure.water--;
173 DirtyCompanyInfrastructureWindows(c->index);
174 }
175 /* Sloped canals are locks and no natural water remains whatever the slope direction */
176 wc = WATER_CLASS_INVALID;
177 }
178
179 /* Only river water should be restored on appropriate slopes. Other water would be invalid on slopes */
180 if (wc != WATER_CLASS_RIVER || GetInclinedSlopeDirection(slope) == INVALID_DIAGDIR) {
181 wc = WATER_CLASS_INVALID;
182 }
183 }
184
185 if (wc == WATER_CLASS_SEA && z > 0) {
186 /* Update company infrastructure count. */
187 Company *c = Company::GetIfValid(o);
188 if (c != NULL) {
189 c->infrastructure.water++;
190 DirtyCompanyInfrastructureWindows(c->index);
191 }
192
193 wc = WATER_CLASS_CANAL;
194 }
195
196 /* Zero map array and terminate animation */
197 DoClearSquare(tile);
198
199 /* Maybe change to water */
200 switch (wc) {
201 case WATER_CLASS_SEA: MakeSea(tile); break;
202 case WATER_CLASS_CANAL: MakeCanal(tile, o, Random()); break;
203 case WATER_CLASS_RIVER: MakeRiver(tile, Random()); break;
204 default: break;
205 }
206
207 MarkTileDirtyByTile(tile);
208 }
209
210 static CommandCost RemoveShipDepot(TileIndex tile, DoCommandFlag flags)
211 {
212 if (!IsShipDepot(tile)) return CMD_ERROR;
213
214 CommandCost ret = CheckTileOwnership(tile);
215 if (ret.Failed()) return ret;
216
217 TileIndex tile2 = GetOtherShipDepotTile(tile);
218
219 /* do not check for ship on tile when company goes bankrupt */
220 if (!(flags & DC_BANKRUPT)) {
221 CommandCost ret = EnsureNoVehicleOnGround(tile);
222 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile2);
223 if (ret.Failed()) return ret;
224 }
225
226 if (flags & DC_EXEC) {
227 delete Depot::GetByTile(tile);
228
229 Company *c = Company::GetIfValid(GetTileOwner(tile));
230 if (c != NULL) {
231 c->infrastructure.water -= 2 * LOCK_DEPOT_TILE_FACTOR;
232 DirtyCompanyInfrastructureWindows(c->index);
233 }
234
235 MakeWaterKeepingClass(tile, GetTileOwner(tile));
236 MakeWaterKeepingClass(tile2, GetTileOwner(tile2));
237 }
238
239 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_SHIP]);
240 }
241
249 static CommandCost DoBuildLock(TileIndex tile, DiagDirection dir, DoCommandFlag flags)
250 {
251 CommandCost cost(EXPENSES_CONSTRUCTION);
252
253 int delta = TileOffsByDiagDir(dir);
254 CommandCost ret = EnsureNoVehicleOnGround(tile);
255 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta);
256 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta);
257 if (ret.Failed()) return ret;
258
259 /* middle tile */
260 WaterClass wc_middle = IsWaterTile(tile) ? GetWaterClass(tile) : WATER_CLASS_CANAL;
261 ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
262 if (ret.Failed()) return ret;
263 cost.AddCost(ret);
264
265 /* lower tile */
266 WaterClass wc_lower = IsWaterTile(tile - delta) ? GetWaterClass(tile - delta) : WATER_CLASS_CANAL;
267
268 if (!IsWaterTile(tile - delta)) {
269 ret = DoCommand(tile - delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
270 if (ret.Failed()) return ret;
271 cost.AddCost(ret);
272 cost.AddCost(_price[PR_BUILD_CANAL]);
273 }
274 if (!IsTileFlat(tile - delta)) {
275 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
276 }
277
278 /* upper tile */
279 WaterClass wc_upper = IsWaterTile(tile + delta) ? GetWaterClass(tile + delta) : WATER_CLASS_CANAL;
280
281 if (!IsWaterTile(tile + delta)) {
282 ret = DoCommand(tile + delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
283 if (ret.Failed()) return ret;
284 cost.AddCost(ret);
285 cost.AddCost(_price[PR_BUILD_CANAL]);
286 }
287 if (!IsTileFlat(tile + delta)) {
288 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
289 }
290
291 if (IsBridgeAbove(tile) || IsBridgeAbove(tile - delta) || IsBridgeAbove(tile + delta)) {
292 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
293 }
294
295 if (flags & DC_EXEC) {
296 /* Update company infrastructure counts. */
297 Company *c = Company::GetIfValid(_current_company);
298 if (c != NULL) {
299 /* Counts for the water. */
300 if (!IsWaterTile(tile - delta)) c->infrastructure.water++;
301 if (!IsWaterTile(tile + delta)) c->infrastructure.water++;
302 /* Count for the lock itself. */
303 c->infrastructure.water += 3 * LOCK_DEPOT_TILE_FACTOR; // Lock is three tiles.
304 DirtyCompanyInfrastructureWindows(_current_company);
305 }
306
307 MakeLock(tile, _current_company, dir, wc_lower, wc_upper, wc_middle);
308 MarkTileDirtyByTile(tile);
309 MarkTileDirtyByTile(tile - delta);
310 MarkTileDirtyByTile(tile + delta);
311 MarkCanalsAndRiversAroundDirty(tile - delta);
312 MarkCanalsAndRiversAroundDirty(tile + delta);
313 }
314 cost.AddCost(_price[PR_BUILD_LOCK]);
315
316 return cost;
317 }
318
325 static CommandCost RemoveLock(TileIndex tile, DoCommandFlag flags)
326 {
327 if (GetTileOwner(tile) != OWNER_NONE) {
328 CommandCost ret = CheckTileOwnership(tile);
329 if (ret.Failed()) return ret;
330 }
331
332 TileIndexDiff delta = TileOffsByDiagDir(GetLockDirection(tile));
333
334 /* make sure no vehicle is on the tile. */
335 CommandCost ret = EnsureNoVehicleOnGround(tile);
336 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta);
337 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta);
338 if (ret.Failed()) return ret;
339
340 if (flags & DC_EXEC) {
341 /* Remove middle part from company infrastructure count. */
342 Company *c = Company::GetIfValid(GetTileOwner(tile));
343 if (c != NULL) {
344 c->infrastructure.water -= 3 * LOCK_DEPOT_TILE_FACTOR; // three parts of the lock.
345 DirtyCompanyInfrastructureWindows(c->index);
346 }
347
348 if (GetWaterClass(tile) == WATER_CLASS_RIVER) {
349 MakeRiver(tile, Random());
350 } else {
351 DoClearSquare(tile);
352 }
353 MakeWaterKeepingClass(tile + delta, GetTileOwner(tile + delta));
354 MakeWaterKeepingClass(tile - delta, GetTileOwner(tile - delta));
355 MarkCanalsAndRiversAroundDirty(tile);
356 MarkCanalsAndRiversAroundDirty(tile - delta);
357 MarkCanalsAndRiversAroundDirty(tile + delta);
358 }
359
360 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_LOCK]);
361 }
362
372 CommandCost CmdBuildLock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
373 {
374 DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile));
375 if (dir == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
376
377 return DoBuildLock(tile, dir, flags);
378 }
379
381 bool RiverModifyDesertZone(TileIndex tile, void *)
382 {
383 if (GetTropicZone(tile) == TROPICZONE_DESERT) SetTropicZone(tile, TROPICZONE_NORMAL);
384 return false;
385 }
386
396 CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
397 {
398 WaterClass wc = Extract<WaterClass, 0, 2>(p2);
399 if (p1 >= MapSize() || wc == WATER_CLASS_INVALID) return CMD_ERROR;
400
401 /* Outside of the editor you can only build canals, not oceans */
402 if (wc != WATER_CLASS_CANAL && _game_mode != GM_EDITOR) return CMD_ERROR;
403
404 TileArea ta(tile, p1);
405
406 /* Outside the editor you can only drag canals, and not areas */
407 if (_game_mode != GM_EDITOR && ta.w != 1 && ta.h != 1) return CMD_ERROR;
408
409 CommandCost cost(EXPENSES_CONSTRUCTION);
410 TILE_AREA_LOOP(tile, ta) {
411 CommandCost ret;
412
413 Slope slope = GetTileSlope(tile);
414 if (slope != SLOPE_FLAT && (wc != WATER_CLASS_RIVER || !IsInclinedSlope(slope))) {
415 return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
416 }
417
418 /* can't make water of water! */
419 if (IsTileType(tile, MP_WATER) && (!IsTileOwner(tile, OWNER_WATER) || wc == WATER_CLASS_SEA)) continue;
420
421 bool water = IsWaterTile(tile);
422 ret = DoCommand(tile, 0, 0, flags | DC_FORCE_CLEAR_TILE, CMD_LANDSCAPE_CLEAR);
423 if (ret.Failed()) return ret;
424
425 if (!water) cost.AddCost(ret);
426
427 if (flags & DC_EXEC) {
428 switch (wc) {
429 case WATER_CLASS_RIVER:
430 MakeRiver(tile, Random());
431 if (_game_mode == GM_EDITOR) {
432 TileIndex tile2 = tile;
433 CircularTileSearch(&tile2, 5, RiverModifyDesertZone, NULL);
434 }
435 break;
436
437 case WATER_CLASS_SEA:
438 if (TileHeight(tile) == 0) {
439 MakeSea(tile);
440 break;
441 }
442 /* FALL THROUGH */
443
444 default:
445 MakeCanal(tile, _current_company, Random());
446 if (Company::IsValidID(_current_company)) {
447 Company::Get(_current_company)->infrastructure.water++;
448 DirtyCompanyInfrastructureWindows(_current_company);
449 }
450 break;
451 }
452 MarkTileDirtyByTile(tile);
453 MarkCanalsAndRiversAroundDirty(tile);
454 }
455
456 cost.AddCost(_price[PR_BUILD_CANAL]);
457 }
458
459 if (cost.GetCost() == 0) {
460 return_cmd_error(STR_ERROR_ALREADY_BUILT);
461 } else {
462 return cost;
463 }
464 }
465
466 static CommandCost ClearTile_Water(TileIndex tile, DoCommandFlag flags)
467 {
468 switch (GetWaterTileType(tile)) {
469 case WATER_TILE_CLEAR: {
470 if (flags & DC_NO_WATER) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER);
471
472 Money base_cost = IsCanal(tile) ? _price[PR_CLEAR_CANAL] : _price[PR_CLEAR_WATER];
473 /* Make sure freeform edges are allowed or it's not an edge tile. */
474 if (!_settings_game.construction.freeform_edges && (!IsInsideMM(TileX(tile), 1, MapMaxX() - 1) ||
475 !IsInsideMM(TileY(tile), 1, MapMaxY() - 1))) {
476 return_cmd_error(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP);
477 }
478
479 /* Make sure no vehicle is on the tile */
480 CommandCost ret = EnsureNoVehicleOnGround(tile);
481 if (ret.Failed()) return ret;
482
483 Owner owner = GetTileOwner(tile);
484 if (owner != OWNER_WATER && owner != OWNER_NONE) {
485 CommandCost ret = CheckTileOwnership(tile);
486 if (ret.Failed()) return ret;
487 }
488
489 if (flags & DC_EXEC) {
490 if (IsCanal(tile) && Company::IsValidID(owner)) {
491 Company::Get(owner)->infrastructure.water--;
492 DirtyCompanyInfrastructureWindows(owner);
493 }
494 DoClearSquare(tile);
495 MarkCanalsAndRiversAroundDirty(tile);
496 }
497
498 return CommandCost(EXPENSES_CONSTRUCTION, base_cost);
499 }
500
501 case WATER_TILE_COAST: {
502 Slope slope = GetTileSlope(tile);
503
504 /* Make sure no vehicle is on the tile */
505 CommandCost ret = EnsureNoVehicleOnGround(tile);
506 if (ret.Failed()) return ret;
507
508 if (flags & DC_EXEC) {
509 DoClearSquare(tile);
510 MarkCanalsAndRiversAroundDirty(tile);
511 }
512 if (IsSlopeWithOneCornerRaised(slope)) {
513 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_WATER]);
514 } else {
515 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_ROUGH]);
516 }
517 }
518
519 case WATER_TILE_LOCK: {
520 static const TileIndexDiffC _lock_tomiddle_offs[][DIAGDIR_END] = {
521 /* NE SE SW NW */
522 { { 0, 0}, {0, 0}, { 0, 0}, {0, 0} }, // LOCK_PART_MIDDLE
523 { {-1, 0}, {0, 1}, { 1, 0}, {0, -1} }, // LOCK_PART_LOWER
524 { { 1, 0}, {0, -1}, {-1, 0}, {0, 1} }, // LOCK_PART_UPPER
525 };
526
527 if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
528 if (_current_company == OWNER_WATER) return CMD_ERROR;
529 /* move to the middle tile.. */
530 return RemoveLock(tile + ToTileIndexDiff(_lock_tomiddle_offs[GetLockPart(tile)][GetLockDirection(tile)]), flags);
531 }
532
533 case WATER_TILE_DEPOT:
534 if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
535 return RemoveShipDepot(tile, flags);
536
537 default:
538 NOT_REACHED();
539 }
540 }
541
550 bool IsWateredTile(TileIndex tile, Direction from)
551 {
552 switch (GetTileType(tile)) {
553 case MP_WATER:
554 switch (GetWaterTileType(tile)) {
555 default: NOT_REACHED();
556 case WATER_TILE_DEPOT: case WATER_TILE_CLEAR: return true;
557 case WATER_TILE_LOCK: return DiagDirToAxis(GetLockDirection(tile)) == DiagDirToAxis(DirToDiagDir(from));
558
559 case WATER_TILE_COAST:
560 switch (GetTileSlope(tile)) {
561 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
562 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
563 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
564 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
565 default: return false;
566 }
567 }
568
569 case MP_RAILWAY:
570 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
571 assert(IsPlainRail(tile));
572 switch (GetTileSlope(tile)) {
573 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
574 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
575 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
576 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
577 default: return false;
578 }
579 }
580 return false;
581
582 case MP_STATION:
583 if (IsOilRig(tile)) {
584 /* Do not draw waterborders inside of industries.
585 * Note: There is no easy way to detect the industry of an oilrig tile. */
586 TileIndex src_tile = tile + TileOffsByDir(from);
587 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
588 (IsTileType(src_tile, MP_INDUSTRY))) return true;
589
590 return IsTileOnWater(tile);
591 }
592 return (IsDock(tile) && IsTileFlat(tile)) || IsBuoy(tile);
593
594 case MP_INDUSTRY: {
595 /* Do not draw waterborders inside of industries.
596 * Note: There is no easy way to detect the industry of an oilrig tile. */
597 TileIndex src_tile = tile + TileOffsByDir(from);
598 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
599 (IsTileType(src_tile, MP_INDUSTRY) && GetIndustryIndex(src_tile) == GetIndustryIndex(tile))) return true;
600
601 return IsTileOnWater(tile);
602 }
603
604 case MP_OBJECT: return IsTileOnWater(tile);
605
606 case MP_TUNNELBRIDGE: return GetTunnelBridgeTransportType(tile) == TRANSPORT_WATER && ReverseDiagDir(GetTunnelBridgeDirection(tile)) == DirToDiagDir(from);
607
608 case MP_VOID: return true; // consider map border as water, esp. for rivers
609
610 default: return false;
611 }
612 }
613
621 static void DrawWaterSprite(SpriteID base, uint offset, CanalFeature feature, TileIndex tile)
622 {
623 if (base != SPR_FLAT_WATER_TILE) {
624 /* Only call offset callback if the sprite is NewGRF-provided. */
625 offset = GetCanalSpriteOffset(feature, tile, offset);
626 }
627 DrawGroundSprite(base + offset, PAL_NONE);
628 }
629
636 static void DrawWaterEdges(bool canal, uint offset, TileIndex tile)
637 {
638 CanalFeature feature;
639 SpriteID base = 0;
640 if (canal) {
641 feature = CF_DIKES;
642 base = GetCanalSprite(CF_DIKES, tile);
643 if (base == 0) base = SPR_CANAL_DIKES_BASE;
644 } else {
645 feature = CF_RIVER_EDGE;
646 base = GetCanalSprite(CF_RIVER_EDGE, tile);
647 if (base == 0) return; // Don't draw if no sprites provided.
648 }
649
650 uint wa;
651
652 /* determine the edges around with water. */
653 wa = IsWateredTile(TILE_ADDXY(tile, -1, 0), DIR_SW) << 0;
654 wa += IsWateredTile(TILE_ADDXY(tile, 0, 1), DIR_NW) << 1;
655 wa += IsWateredTile(TILE_ADDXY(tile, 1, 0), DIR_NE) << 2;
656 wa += IsWateredTile(TILE_ADDXY(tile, 0, -1), DIR_SE) << 3;
657
658 if (!(wa & 1)) DrawWaterSprite(base, offset, feature, tile);
659 if (!(wa & 2)) DrawWaterSprite(base, offset + 1, feature, tile);
660 if (!(wa & 4)) DrawWaterSprite(base, offset + 2, feature, tile);
661 if (!(wa & 8)) DrawWaterSprite(base, offset + 3, feature, tile);
662
663 /* right corner */
664 switch (wa & 0x03) {
665 case 0: DrawWaterSprite(base, offset + 4, feature, tile); break;
666 case 3: if (!IsWateredTile(TILE_ADDXY(tile, -1, 1), DIR_W)) DrawWaterSprite(base, offset + 8, feature, tile); break;
667 }
668
669 /* bottom corner */
670 switch (wa & 0x06) {
671 case 0: DrawWaterSprite(base, offset + 5, feature, tile); break;
672 case 6: if (!IsWateredTile(TILE_ADDXY(tile, 1, 1), DIR_N)) DrawWaterSprite(base, offset + 9, feature, tile); break;
673 }
674
675 /* left corner */
676 switch (wa & 0x0C) {
677 case 0: DrawWaterSprite(base, offset + 6, feature, tile); break;
678 case 12: if (!IsWateredTile(TILE_ADDXY(tile, 1, -1), DIR_E)) DrawWaterSprite(base, offset + 10, feature, tile); break;
679 }
680
681 /* upper corner */
682 switch (wa & 0x09) {
683 case 0: DrawWaterSprite(base, offset + 7, feature, tile); break;
684 case 9: if (!IsWateredTile(TILE_ADDXY(tile, -1, -1), DIR_S)) DrawWaterSprite(base, offset + 11, feature, tile); break;
685 }
686 }
687
689 static void DrawSeaWater(TileIndex tile)
690 {
691 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
692 }
693
695 static void DrawCanalWater(TileIndex tile)
696 {
697 SpriteID image = SPR_FLAT_WATER_TILE;
698 if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
699 /* First water slope sprite is flat water. */
700 image = GetCanalSprite(CF_WATERSLOPE, tile);
701 if (image == 0) image = SPR_FLAT_WATER_TILE;
702 }
703 DrawWaterSprite(image, 0, CF_WATERSLOPE, tile);
704
705 DrawWaterEdges(true, 0, tile);
706 }
707
708 #include "table/water_land.h"
709
719 static void DrawWaterTileStruct(const TileInfo *ti, const DrawTileSeqStruct *dtss, SpriteID base, uint offset, PaletteID palette, CanalFeature feature)
720 {
721 /* Don't draw if buildings are invisible. */
722 if (IsInvisibilitySet(TO_BUILDINGS)) return;
723
724 for (; !dtss->IsTerminator(); dtss++) {
725 uint tile_offs = offset + dtss->image.sprite;
726 if (feature < CF_END) tile_offs = GetCanalSpriteOffset(feature, ti->tile, tile_offs);
727 AddSortableSpriteToDraw(base + tile_offs, palette,
728 ti->x + dtss->delta_x, ti->y + dtss->delta_y,
729 dtss->size_x, dtss->size_y,
730 dtss->size_z, ti->z + dtss->delta_z,
731 IsTransparencySet(TO_BUILDINGS));
732 }
733 }
734
736 static void DrawWaterLock(const TileInfo *ti)
737 {
738 int part = GetLockPart(ti->tile);
739 const DrawTileSprites &dts = _lock_display_data[part][GetLockDirection(ti->tile)];
740
741 /* Draw ground sprite. */
742 SpriteID image = dts.ground.sprite;
743
744 SpriteID water_base = GetCanalSprite(CF_WATERSLOPE, ti->tile);
745 if (water_base == 0) {
746 /* Use default sprites. */
747 water_base = SPR_CANALS_BASE;
748 } else if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
749 /* NewGRF supplies a flat sprite as first sprite. */
750 if (image == SPR_FLAT_WATER_TILE) {
751 image = water_base;
752 } else {
753 image++;
754 }
755 }
756
757 if (image < 5) image += water_base;
758 DrawGroundSprite(image, PAL_NONE);
759
760 /* Draw structures. */
761 uint zoffs = 0;
762 SpriteID base = GetCanalSprite(CF_LOCKS, ti->tile);
763
764 if (base == 0) {
765 /* If no custom graphics, use defaults. */
766 base = SPR_LOCK_BASE;
767 uint8 z_threshold = part == LOCK_PART_UPPER ? 8 : 0;
768 zoffs = ti->z > z_threshold ? 24 : 0;
769 }
770
771 DrawWaterTileStruct(ti, dts.seq, base, zoffs, PAL_NONE, CF_LOCKS);
772 }
773
775 static void DrawWaterDepot(const TileInfo *ti)
776 {
777 DrawWaterClassGround(ti);
778 DrawWaterTileStruct(ti, _shipdepot_display_data[GetShipDepotAxis(ti->tile)][GetShipDepotPart(ti->tile)].seq, 0, 0, COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile)), CF_END);
779 }
780
781 static void DrawRiverWater(const TileInfo *ti)
782 {
783 SpriteID image = SPR_FLAT_WATER_TILE;
784 uint offset = 0;
785 uint edges_offset = 0;
786
787 if (ti->tileh != SLOPE_FLAT || HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
788 image = GetCanalSprite(CF_RIVER_SLOPE, ti->tile);
789 if (image == 0) {
790 switch (ti->tileh) {
791 case SLOPE_NW: image = SPR_WATER_SLOPE_Y_DOWN; break;
792 case SLOPE_SW: image = SPR_WATER_SLOPE_X_UP; break;
793 case SLOPE_SE: image = SPR_WATER_SLOPE_Y_UP; break;
794 case SLOPE_NE: image = SPR_WATER_SLOPE_X_DOWN; break;
795 default: image = SPR_FLAT_WATER_TILE; break;
796 }
797 } else {
798 /* Flag bit 0 indicates that the first sprite is flat water. */
799 offset = HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE) ? 1 : 0;
800
801 switch (ti->tileh) {
802 case SLOPE_SE: edges_offset += 12; break;
803 case SLOPE_NE: offset += 1; edges_offset += 24; break;
804 case SLOPE_SW: offset += 2; edges_offset += 36; break;
805 case SLOPE_NW: offset += 3; edges_offset += 48; break;
806 default: offset = 0; break;
807 }
808
809 offset = GetCanalSpriteOffset(CF_RIVER_SLOPE, ti->tile, offset);
810 }
811 }
812
813 DrawGroundSprite(image + offset, PAL_NONE);
814
815 /* Draw river edges if available. */
816 DrawWaterEdges(false, edges_offset, ti->tile);
817 }
818
819 void DrawShoreTile(Slope tileh)
820 {
821 /* Converts the enum Slope into an offset based on SPR_SHORE_BASE.
822 * This allows to calculate the proper sprite to display for this Slope */
823 static const byte tileh_to_shoresprite[32] = {
824 0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0,
825 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 15, 0,
826 };
827
828 assert(!IsHalftileSlope(tileh)); // Halftile slopes need to get handled earlier.
829 assert(tileh != SLOPE_FLAT); // Shore is never flat
830
831 assert((tileh != SLOPE_EW) && (tileh != SLOPE_NS)); // No suitable sprites for current flooding behaviour
832
833 DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[tileh], PAL_NONE);
834 }
835
836 void DrawWaterClassGround(const TileInfo *ti)
837 {
838 switch (GetWaterClass(ti->tile)) {
839 case WATER_CLASS_SEA: DrawSeaWater(ti->tile); break;
840 case WATER_CLASS_CANAL: DrawCanalWater(ti->tile); break;
841 case WATER_CLASS_RIVER: DrawRiverWater(ti); break;
842 default: NOT_REACHED();
843 }
844 }
845
846 static void DrawTile_Water(TileInfo *ti)
847 {
848 switch (GetWaterTileType(ti->tile)) {
849 case WATER_TILE_CLEAR:
850 DrawWaterClassGround(ti);
851 DrawBridgeMiddle(ti);
852 break;
853
854 case WATER_TILE_COAST: {
855 DrawShoreTile(ti->tileh);
856 DrawBridgeMiddle(ti);
857 break;
858 }
859
860 case WATER_TILE_LOCK:
861 DrawWaterLock(ti);
862 break;
863
864 case WATER_TILE_DEPOT:
865 DrawWaterDepot(ti);
866 break;
867 }
868 }
869
870 void DrawShipDepotSprite(int x, int y, Axis axis, DepotPart part)
871 {
872 const DrawTileSprites &dts = _shipdepot_display_data[axis][part];
873
874 DrawSprite(dts.ground.sprite, dts.ground.pal, x, y);
875 DrawOrigTileSeqInGUI(x, y, &dts, COMPANY_SPRITE_COLOUR(_local_company));
876 }
877
878
879 static int GetSlopePixelZ_Water(TileIndex tile, uint x, uint y)
880 {
881 int z;
882 Slope tileh = GetTilePixelSlope(tile, &z);
883
884 return z + GetPartialPixelZ(x & 0xF, y & 0xF, tileh);
885 }
886
887 static Foundation GetFoundation_Water(TileIndex tile, Slope tileh)
888 {
889 return FOUNDATION_NONE;
890 }
891
892 static void GetTileDesc_Water(TileIndex tile, TileDesc *td)
893 {
894 switch (GetWaterTileType(tile)) {
895 case WATER_TILE_CLEAR:
896 switch (GetWaterClass(tile)) {
897 case WATER_CLASS_SEA: td->str = STR_LAI_WATER_DESCRIPTION_WATER; break;
898 case WATER_CLASS_CANAL: td->str = STR_LAI_WATER_DESCRIPTION_CANAL; break;
899 case WATER_CLASS_RIVER: td->str = STR_LAI_WATER_DESCRIPTION_RIVER; break;
900 default: NOT_REACHED(); break;
901 }
902 break;
903 case WATER_TILE_COAST: td->str = STR_LAI_WATER_DESCRIPTION_COAST_OR_RIVERBANK; break;
904 case WATER_TILE_LOCK : td->str = STR_LAI_WATER_DESCRIPTION_LOCK; break;
905 case WATER_TILE_DEPOT:
906 td->str = STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT;
907 td->build_date = Depot::GetByTile(tile)->build_date;
908 break;
909 default: NOT_REACHED(); break;
910 }
911
912 td->owner[0] = GetTileOwner(tile);
913 }
914
920 static void FloodVehicle(Vehicle *v)
921 {
922 uint pass = v->Crash(true);
923
924 AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_FLOODED));
925 Game::NewEvent(new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_FLOODED));
926 SetDParam(0, pass);
927 AddVehicleNewsItem(STR_NEWS_DISASTER_FLOOD_VEHICLE, NT_ACCIDENT, v->index);
928 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
929 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v);
930 }
931
938 static Vehicle *FloodVehicleProc(Vehicle *v, void *data)
939 {
940 if ((v->vehstatus & VS_CRASHED) != 0) return NULL;
941
942 switch (v->type) {
943 default: break;
944
945 case VEH_AIRCRAFT: {
946 if (!IsAirportTile(v->tile) || GetTileMaxZ(v->tile) != 0) break;
947 if (v->subtype == AIR_SHADOW) break;
948
949 /* We compare v->z_pos against delta_z + 1 because the shadow
950 * is at delta_z and the actual aircraft at delta_z + 1. */
951 const Station *st = Station::GetByTile(v->tile);
952 const AirportFTAClass *airport = st->airport.GetFTA();
953 if (v->z_pos != airport->delta_z + 1) break;
954
955 FloodVehicle(v);
956 break;
957 }
958
959 case VEH_TRAIN:
960 case VEH_ROAD: {
961 int z = *(int*)data;
962 if (v->z_pos > z) break;
963 FloodVehicle(v->First());
964 break;
965 }
966 }
967
968 return NULL;
969 }
970
976 static void FloodVehicles(TileIndex tile)
977 {
978 int z = 0;
979
980 if (IsAirportTile(tile)) {
981 const Station *st = Station::GetByTile(tile);
982 TILE_AREA_LOOP(tile, st->airport) {
983 if (st->TileBelongsToAirport(tile)) FindVehicleOnPos(tile, &z, &FloodVehicleProc);
984 }
985
986 /* No vehicle could be flooded on this airport anymore */
987 return;
988 }
989
990 if (!IsBridgeTile(tile)) {
991 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
992 return;
993 }
994
995 TileIndex end = GetOtherBridgeEnd(tile);
996 z = GetBridgePixelHeight(tile);
997
998 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
999 FindVehicleOnPos(end, &z, &FloodVehicleProc);
1000 }
1001
1007 FloodingBehaviour GetFloodingBehaviour(TileIndex tile)
1008 {
1009 /* FLOOD_ACTIVE: 'single-corner-raised'-coast, sea, sea-shipdepots, sea-buoys, sea-docks (water part), rail with flooded halftile, sea-water-industries, sea-oilrigs
1010 * FLOOD_DRYUP: coast with more than one corner raised, coast with rail-track, coast with trees
1011 * FLOOD_PASSIVE: (not used)
1012 * FLOOD_NONE: canals, rivers, everything else
1013 */
1014 switch (GetTileType(tile)) {
1015 case MP_WATER:
1016 if (IsCoast(tile)) {
1017 Slope tileh = GetTileSlope(tile);
1018 return (IsSlopeWithOneCornerRaised(tileh) ? FLOOD_ACTIVE : FLOOD_DRYUP);
1019 }
1020 /* FALL THROUGH */
1021 case MP_STATION:
1022 case MP_INDUSTRY:
1023 case MP_OBJECT:
1024 return (GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE;
1025
1026 case MP_RAILWAY:
1027 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
1028 return (IsSlopeWithOneCornerRaised(GetTileSlope(tile)) ? FLOOD_ACTIVE : FLOOD_DRYUP);
1029 }
1030 return FLOOD_NONE;
1031
1032 case MP_TREES:
1033 return (GetTreeGround(tile) == TREE_GROUND_SHORE ? FLOOD_DRYUP : FLOOD_NONE);
1034
1035 default:
1036 return FLOOD_NONE;
1037 }
1038 }
1039
1043 void DoFloodTile(TileIndex target)
1044 {
1045 assert(!IsTileType(target, MP_WATER));
1046
1047 bool flooded = false; // Will be set to true if something is changed.
1048
1049 Backup<CompanyByte> cur_company(_current_company, OWNER_WATER, FILE_LINE);
1050
1051 Slope tileh = GetTileSlope(target);
1052 if (tileh != SLOPE_FLAT) {
1053 /* make coast.. */
1054 switch (GetTileType(target)) {
1055 case MP_RAILWAY: {
1056 if (!IsPlainRail(target)) break;
1057 FloodVehicles(target);
1058 flooded = FloodHalftile(target);
1059 break;
1060 }
1061
1062 case MP_TREES:
1063 if (!IsSlopeWithOneCornerRaised(tileh)) {
1064 SetTreeGroundDensity(target, TREE_GROUND_SHORE, 3);
1065 MarkTileDirtyByTile(target);
1066 flooded = true;
1067 break;
1068 }
1069 /* FALL THROUGH */
1070
1071 case MP_CLEAR:
1072 if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
1073 MakeShore(target);
1074 MarkTileDirtyByTile(target);
1075 flooded = true;
1076 }
1077 break;
1078
1079 default:
1080 break;
1081 }
1082 } else {
1083 /* Flood vehicles */
1084 FloodVehicles(target);
1085
1086 /* flood flat tile */
1087 if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
1088 MakeSea(target);
1089 MarkTileDirtyByTile(target);
1090 flooded = true;
1091 }
1092 }
1093
1094 if (flooded) {
1095 /* Mark surrounding canal tiles dirty too to avoid glitches */
1096 MarkCanalsAndRiversAroundDirty(target);
1097
1098 /* update signals if needed */
1099 UpdateSignalsInBuffer();
1100 }
1101
1102 cur_company.Restore();
1103 }
1104
1108 static void DoDryUp(TileIndex tile)
1109 {
1110 Backup<CompanyByte> cur_company(_current_company, OWNER_WATER, FILE_LINE);
1111
1112 switch (GetTileType(tile)) {
1113 case MP_RAILWAY:
1114 assert(IsPlainRail(tile));
1115 assert(GetRailGroundType(tile) == RAIL_GROUND_WATER);
1116
1117 RailGroundType new_ground;
1118 switch (GetTrackBits(tile)) {
1119 case TRACK_BIT_UPPER: new_ground = RAIL_GROUND_FENCE_HORIZ1; break;
1120 case TRACK_BIT_LOWER: new_ground = RAIL_GROUND_FENCE_HORIZ2; break;
1121 case TRACK_BIT_LEFT: new_ground = RAIL_GROUND_FENCE_VERT1; break;
1122 case TRACK_BIT_RIGHT: new_ground = RAIL_GROUND_FENCE_VERT2; break;
1123 default: NOT_REACHED();
1124 }
1125 SetRailGroundType(tile, new_ground);
1126 MarkTileDirtyByTile(tile);
1127 break;
1128
1129 case MP_TREES:
1130 SetTreeGroundDensity(tile, TREE_GROUND_GRASS, 3);
1131 MarkTileDirtyByTile(tile);
1132 break;
1133
1134 case MP_WATER:
1135 assert(IsCoast(tile));
1136
1137 if (DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
1138 MakeClear(tile, CLEAR_GRASS, 3);
1139 MarkTileDirtyByTile(tile);
1140 }
1141 break;
1142
1143 default: NOT_REACHED();
1144 }
1145
1146 cur_company.Restore();
1147 }
1148
1155 void TileLoop_Water(TileIndex tile)
1156 {
1157 if (IsTileType(tile, MP_WATER)) AmbientSoundEffect(tile);
1158
1159 switch (GetFloodingBehaviour(tile)) {
1160 case FLOOD_ACTIVE:
1161 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
1162 TileIndex dest = tile + TileOffsByDir(dir);
1163 if (!IsValidTile(dest)) continue;
1164 /* do not try to flood water tiles - increases performance a lot */
1165 if (IsTileType(dest, MP_WATER)) continue;
1166
1167 /* TREE_GROUND_SHORE is the sign of a previous flood. */
1168 if (IsTileType(dest, MP_TREES) && GetTreeGround(dest) == TREE_GROUND_SHORE) continue;
1169
1170 int z_dest;
1171 Slope slope_dest = GetFoundationSlope(dest, &z_dest) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
1172 if (z_dest > 0) continue;
1173
1174 if (!HasBit(_flood_from_dirs[slope_dest], ReverseDir(dir))) continue;
1175
1176 DoFloodTile(dest);
1177 }
1178 break;
1179
1180 case FLOOD_DRYUP: {
1181 Slope slope_here = GetFoundationSlope(tile) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
1182 uint dir;
1183 FOR_EACH_SET_BIT(dir, _flood_from_dirs[slope_here]) {
1184 TileIndex dest = tile + TileOffsByDir((Direction)dir);
1185 if (!IsValidTile(dest)) continue;
1186
1187 FloodingBehaviour dest_behaviour = GetFloodingBehaviour(dest);
1188 if ((dest_behaviour == FLOOD_ACTIVE) || (dest_behaviour == FLOOD_PASSIVE)) return;
1189 }
1190 DoDryUp(tile);
1191 break;
1192 }
1193
1194 default: return;
1195 }
1196 }
1197
1198 void ConvertGroundTilesIntoWaterTiles()
1199 {
1200 int z;
1201
1202 for (TileIndex tile = 0; tile < MapSize(); ++tile) {
1203 Slope slope = GetTileSlope(tile, &z);
1204 if (IsTileType(tile, MP_CLEAR) && z == 0) {
1205 /* Make both water for tiles at level 0
1206 * and make shore, as that looks much better
1207 * during the generation. */
1208 switch (slope) {
1209 case SLOPE_FLAT:
1210 MakeSea(tile);
1211 break;
1212
1213 case SLOPE_N:
1214 case SLOPE_E:
1215 case SLOPE_S:
1216 case SLOPE_W:
1217 MakeShore(tile);
1218 break;
1219
1220 default:
1221 uint dir;
1222 FOR_EACH_SET_BIT(dir, _flood_from_dirs[slope & ~SLOPE_STEEP]) {
1223 TileIndex dest = TILE_ADD(tile, TileOffsByDir((Direction)dir));
1224 Slope slope_dest = GetTileSlope(dest) & ~SLOPE_STEEP;
1225 if (slope_dest == SLOPE_FLAT || IsSlopeWithOneCornerRaised(slope_dest)) {
1226 MakeShore(tile);
1227 break;
1228 }
1229 }
1230 break;
1231 }
1232 }
1233 }
1234 }
1235
1236 static TrackStatus GetTileTrackStatus_Water(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
1237 {
1238 static const byte coast_tracks[] = {0, 32, 4, 0, 16, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0};
1239
1240 TrackBits ts;
1241
1242 if (mode != TRANSPORT_WATER) return 0;
1243
1244 switch (GetWaterTileType(tile)) {
1245 case WATER_TILE_CLEAR: ts = IsTileFlat(tile) ? TRACK_BIT_ALL : TRACK_BIT_NONE; break;
1246 case WATER_TILE_COAST: ts = (TrackBits)coast_tracks[GetTileSlope(tile) & 0xF]; break;
1247 case WATER_TILE_LOCK: ts = DiagDirToDiagTrackBits(GetLockDirection(tile)); break;
1248 case WATER_TILE_DEPOT: ts = AxisToTrackBits(GetShipDepotAxis(tile)); break;
1249 default: return 0;
1250 }
1251 if (TileX(tile) == 0) {
1252 /* NE border: remove tracks that connects NE tile edge */
1253 ts &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
1254 }
1255 if (TileY(tile) == 0) {
1256 /* NW border: remove tracks that connects NW tile edge */
1257 ts &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
1258 }
1259 return CombineTrackStatus(TrackBitsToTrackdirBits(ts), TRACKDIR_BIT_NONE);
1260 }
1261
1262 static bool ClickTile_Water(TileIndex tile)
1263 {
1264 if (GetWaterTileType(tile) == WATER_TILE_DEPOT) {
1265 ShowDepotWindow(GetShipDepotNorthTile(tile), VEH_SHIP);
1266 return true;
1267 }
1268 return false;
1269 }
1270
1271 static void ChangeTileOwner_Water(TileIndex tile, Owner old_owner, Owner new_owner)
1272 {
1273 if (!IsTileOwner(tile, old_owner)) return;
1274
1275 bool is_lock_middle = IsLock(tile) && GetLockPart(tile) == LOCK_PART_MIDDLE;
1276
1277 /* No need to dirty company windows here, we'll redraw the whole screen anyway. */
1278 if (is_lock_middle) Company::Get(old_owner)->infrastructure.water -= 3 * LOCK_DEPOT_TILE_FACTOR; // Lock has three parts.
1279 if (new_owner != INVALID_OWNER) {
1280 if (is_lock_middle) Company::Get(new_owner)->infrastructure.water += 3 * LOCK_DEPOT_TILE_FACTOR; // Lock has three parts.
1281 /* Only subtract from the old owner here if the new owner is valid,
1282 * otherwise we clear ship depots and canal water below. */
1283 if (GetWaterClass(tile) == WATER_CLASS_CANAL && !is_lock_middle) {
1284 Company::Get(old_owner)->infrastructure.water--;
1285 Company::Get(new_owner)->infrastructure.water++;
1286 }
1287 if (IsShipDepot(tile)) {
1288 Company::Get(old_owner)->infrastructure.water -= LOCK_DEPOT_TILE_FACTOR;
1289 Company::Get(new_owner)->infrastructure.water += LOCK_DEPOT_TILE_FACTOR;
1290 }
1291
1292 SetTileOwner(tile, new_owner);
1293 return;
1294 }
1295
1296 /* Remove depot */
1297 if (IsShipDepot(tile)) DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
1298
1299 /* Set owner of canals and locks ... and also canal under dock there was before.
1300 * Check if the new owner after removing depot isn't OWNER_WATER. */
1301 if (IsTileOwner(tile, old_owner)) {
1302 if (GetWaterClass(tile) == WATER_CLASS_CANAL && !is_lock_middle) Company::Get(old_owner)->infrastructure.water--;
1303 SetTileOwner(tile, OWNER_NONE);
1304 }
1305 }
1306
1307 static VehicleEnterTileStatus VehicleEnter_Water(Vehicle *v, TileIndex tile, int x, int y)
1308 {
1309 return VETSB_CONTINUE;
1310 }
1311
1312 static CommandCost TerraformTile_Water(TileIndex tile, DoCommandFlag flags, int z_new, Slope tileh_new)
1313 {
1314 /* Canals can't be terraformed */
1315 if (IsWaterTile(tile) && IsCanal(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_CANAL_FIRST);
1316
1317 return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
1318 }
1319
1320
1321 extern const TileTypeProcs _tile_type_water_procs = {
1322 DrawTile_Water, // draw_tile_proc
1323 GetSlopePixelZ_Water, // get_slope_z_proc
1324 ClearTile_Water, // clear_tile_proc
1325 NULL, // add_accepted_cargo_proc
1326 GetTileDesc_Water, // get_tile_desc_proc
1327 GetTileTrackStatus_Water, // get_tile_track_status_proc
1328 ClickTile_Water, // click_tile_proc
1329 NULL, // animate_tile_proc
1330 TileLoop_Water, // tile_loop_proc
1331 ChangeTileOwner_Water, // change_tile_owner_proc
1332 NULL, // add_produced_cargo_proc
1333 VehicleEnter_Water, // vehicle_enter_tile_proc
1334 GetFoundation_Water, // get_foundation_proc
1335 TerraformTile_Water, // terraform_tile_proc
1336 };