Fix ICU iterators on leading/trailing whitespace
[openttd/fttd.git] / src / water_cmd.cpp
blobf5b6e348f43f453f28548cfc9465e67c8f057d25
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 water_cmd.cpp Handling of water tiles. */
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 "map/industry.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 "map/ground.h"
29 #include "map/slope.h"
30 #include "aircraft.h"
31 #include "effectvehicle_func.h"
32 #include "map/bridge.h"
33 #include "bridge.h"
34 #include "station_base.h"
35 #include "ai/ai.hpp"
36 #include "game/game.hpp"
37 #include "core/random_func.hpp"
38 #include "core/backup_type.hpp"
39 #include "date_func.h"
40 #include "company_base.h"
41 #include "company_gui.h"
42 #include "newgrf_generic.h"
43 #include "signal_func.h"
45 #include "table/strings.h"
47 /**
48 * Describes from which directions a specific slope can be flooded (if the tile is floodable at all).
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
68 /**
69 * Marks tile dirty if it is a canal or river tile.
70 * Called to avoid glitches when flooding tiles next to canal tile.
72 * @param tile tile to check
74 static inline void MarkTileDirtyIfCanalOrRiver(TileIndex tile)
76 if (IsWaterTile(tile) && (IsCanal(tile) || IsRiver(tile))) MarkTileDirtyByTile(tile);
79 /**
80 * Marks the tiles around a tile as dirty, if they are canals or rivers.
82 * @param tile The center of the tile where all other tiles are marked as dirty
83 * @ingroup dirty
85 static void MarkCanalsAndRiversAroundDirty(TileIndex tile)
87 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
88 MarkTileDirtyIfCanalOrRiver(tile + TileOffsByDir(dir));
93 /**
94 * Build a ship depot.
95 * @param tile tile where ship depot is built
96 * @param flags type of operation
97 * @param p1 bit 0 depot orientation (Axis)
98 * @param p2 unused
99 * @param text unused
100 * @return the cost of this operation or an error
102 CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
104 Axis axis = Extract<Axis, 0, 1>(p1);
106 TileIndex tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
108 if (!HasTileWaterGround(tile) || !HasTileWaterGround(tile2)) {
109 return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER);
112 if (HasBridgeAbove(tile) || HasBridgeAbove(tile2)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
114 if (!IsTileFlat(tile) || !IsTileFlat(tile2)) {
115 /* Prevent depots on rapids */
116 return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
119 if (!Depot::CanAllocateItem()) return CMD_ERROR;
121 WaterClass wc1 = GetWaterClass(tile);
122 WaterClass wc2 = GetWaterClass(tile2);
123 CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_DEPOT_SHIP]);
125 bool add_cost = !IsPlainWaterTile(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);
131 add_cost = !IsPlainWaterTile(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);
138 if (flags & DC_EXEC) {
139 Depot *depot = new Depot(tile);
140 depot->build_date = _date;
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;
146 Company::Get(_current_company)->infrastructure.water += 2 * LOCK_DEPOT_TILE_FACTOR;
147 DirtyCompanyInfrastructureWindows(_current_company);
149 MakeShipDepot(tile, _current_company, depot->index, ReverseDiagDir(AxisToDiagDir(axis)), wc1);
150 MakeShipDepot(tile2, _current_company, depot->index, AxisToDiagDir(axis), wc2);
151 MarkTileDirtyByTile(tile);
152 MarkTileDirtyByTile(tile2);
153 MakeDefaultName(depot);
156 return cost;
159 void MakeWaterKeepingClass(TileIndex tile, Owner o)
161 WaterClass wc = GetWaterClass(tile);
163 /* Autoslope might turn an originally canal or river tile into land */
164 int z;
165 Slope slope = GetTileSlope(tile, &z);
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);
175 /* Sloped canals are locks and no natural water remains whatever the slope direction */
176 wc = WATER_CLASS_INVALID;
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;
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);
193 wc = WATER_CLASS_CANAL;
196 /* Zero map array and terminate animation */
197 DoClearSquare(tile);
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;
207 MarkTileDirtyByTile(tile);
210 static CommandCost RemoveShipDepot(TileIndex tile, DoCommandFlag flags)
212 if (!IsShipDepot(tile)) return CMD_ERROR;
214 CommandCost ret = CheckTileOwnership(tile);
215 if (ret.Failed()) return ret;
217 TileIndex tile2 = GetOtherShipDepotTile(tile);
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;
226 if (flags & DC_EXEC) {
227 delete Depot::GetByTile(tile);
229 Company *c = Company::GetIfValid(GetTileOwner(tile));
230 if (c != NULL) {
231 c->infrastructure.water -= 2 * LOCK_DEPOT_TILE_FACTOR;
232 DirtyCompanyInfrastructureWindows(c->index);
235 MakeWaterKeepingClass(tile, GetTileOwner(tile));
236 MakeWaterKeepingClass(tile2, GetTileOwner(tile2));
239 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_SHIP]);
243 * Builds a lock.
244 * @param tile tile where to place the lock
245 * @param flags type of operation
246 * @param p1 unused
247 * @param p2 unused
248 * @param text unused
249 * @return the cost of this operation or an error
251 CommandCost CmdBuildLock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
253 DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile));
254 if (dir == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
256 CommandCost cost(EXPENSES_CONSTRUCTION);
258 int delta = TileOffsByDiagDir(dir);
259 CommandCost ret = EnsureNoVehicleOnGround(tile);
260 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta);
261 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta);
262 if (ret.Failed()) return ret;
264 /* middle tile */
265 WaterClass wc_middle = IsPlainWaterTile(tile) ? GetWaterClass(tile) : WATER_CLASS_CANAL;
266 ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
267 if (ret.Failed()) return ret;
268 cost.AddCost(ret);
270 /* lower tile */
271 WaterClass wc_lower = IsPlainWaterTile(tile - delta) ? GetWaterClass(tile - delta) : WATER_CLASS_CANAL;
273 if (!IsPlainWaterTile(tile - delta)) {
274 ret = DoCommand(tile - delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
275 if (ret.Failed()) return ret;
276 cost.AddCost(ret);
277 cost.AddCost(_price[PR_BUILD_CANAL]);
279 if (!IsTileFlat(tile - delta)) {
280 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
283 /* upper tile */
284 WaterClass wc_upper = IsPlainWaterTile(tile + delta) ? GetWaterClass(tile + delta) : WATER_CLASS_CANAL;
286 if (!IsPlainWaterTile(tile + delta)) {
287 ret = DoCommand(tile + delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
288 if (ret.Failed()) return ret;
289 cost.AddCost(ret);
290 cost.AddCost(_price[PR_BUILD_CANAL]);
292 if (!IsTileFlat(tile + delta)) {
293 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
296 if (HasBridgeAbove(tile) || HasBridgeAbove(tile - delta) || HasBridgeAbove(tile + delta)) {
297 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
300 if (flags & DC_EXEC) {
301 /* Update company infrastructure counts. */
302 Company *c = Company::GetIfValid(_current_company);
303 if (c != NULL) {
304 /* Counts for the water. */
305 if (!IsPlainWaterTile(tile - delta)) c->infrastructure.water++;
306 if (!IsPlainWaterTile(tile + delta)) c->infrastructure.water++;
307 /* Count for the lock itself. */
308 c->infrastructure.water += 3 * LOCK_DEPOT_TILE_FACTOR; // Lock is three tiles.
309 DirtyCompanyInfrastructureWindows(_current_company);
312 MakeLock(tile, _current_company, dir, wc_lower, wc_upper, wc_middle);
313 MarkTileDirtyByTile(tile);
314 MarkTileDirtyByTile(tile - delta);
315 MarkTileDirtyByTile(tile + delta);
316 MarkCanalsAndRiversAroundDirty(tile - delta);
317 MarkCanalsAndRiversAroundDirty(tile + delta);
319 cost.AddCost(_price[PR_BUILD_LOCK]);
321 return cost;
325 * Remove a lock.
326 * @param tile Central tile of the lock.
327 * @param flags Operation to perform.
328 * @return The cost in case of success, or an error code if it failed.
330 static CommandCost RemoveLock(TileIndex tile, DoCommandFlag flags)
332 if (GetTileOwner(tile) != OWNER_NONE) {
333 CommandCost ret = CheckTileOwnership(tile);
334 if (ret.Failed()) return ret;
337 TileIndexDiff delta = TileOffsByDiagDir(GetLockDirection(tile));
339 /* make sure no vehicle is on the tile. */
340 CommandCost ret = EnsureNoVehicleOnGround(tile);
341 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta);
342 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta);
343 if (ret.Failed()) return ret;
345 if (flags & DC_EXEC) {
346 /* Remove middle part from company infrastructure count. */
347 Company *c = Company::GetIfValid(GetTileOwner(tile));
348 if (c != NULL) {
349 c->infrastructure.water -= 3 * LOCK_DEPOT_TILE_FACTOR; // three parts of the lock.
350 DirtyCompanyInfrastructureWindows(c->index);
353 if (GetWaterClass(tile) == WATER_CLASS_RIVER) {
354 MakeRiver(tile, Random());
355 } else {
356 DoClearSquare(tile);
358 MakeWaterKeepingClass(tile + delta, GetTileOwner(tile + delta));
359 MakeWaterKeepingClass(tile - delta, GetTileOwner(tile - delta));
360 MarkCanalsAndRiversAroundDirty(tile);
361 MarkCanalsAndRiversAroundDirty(tile - delta);
362 MarkCanalsAndRiversAroundDirty(tile + delta);
365 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_LOCK]);
368 /** Callback to create non-desert around a river tile. */
369 bool RiverModifyDesertZone(TileIndex tile, void *)
371 if (GetTropicZone(tile) == TROPICZONE_DESERT) SetTropicZone(tile, TROPICZONE_NORMAL);
372 return false;
376 * Build a piece of canal.
377 * @param tile end tile of stretch-dragging
378 * @param flags type of operation
379 * @param p1 start tile of stretch-dragging
380 * @param p2 waterclass to build. sea and river can only be built in scenario editor
381 * @param text unused
382 * @return the cost of this operation or an error
384 CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
386 WaterClass wc = Extract<WaterClass, 0, 2>(p2);
387 if (p1 >= MapSize() || wc == WATER_CLASS_INVALID) return CMD_ERROR;
389 /* Outside of the editor you can only build canals, not oceans */
390 if (wc != WATER_CLASS_CANAL && _game_mode != GM_EDITOR) return CMD_ERROR;
392 TileArea ta(tile, p1);
394 /* Outside the editor you can only drag canals, and not areas */
395 if (_game_mode != GM_EDITOR && ta.w != 1 && ta.h != 1) return CMD_ERROR;
397 CommandCost cost(EXPENSES_CONSTRUCTION);
398 TILE_AREA_LOOP(tile, ta) {
399 CommandCost ret;
401 Slope slope = GetTileSlope(tile);
402 if (slope != SLOPE_FLAT && (wc != WATER_CLASS_RIVER || !IsInclinedSlope(slope))) {
403 return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
406 /* can't make water of water! */
407 if (IsWaterTile(tile) && (!IsTileOwner(tile, OWNER_WATER) || wc == WATER_CLASS_SEA)) continue;
409 bool water = IsPlainWaterTile(tile);
410 ret = DoCommand(tile, 0, 0, flags | DC_FORCE_CLEAR_TILE, CMD_LANDSCAPE_CLEAR);
411 if (ret.Failed()) return ret;
413 if (!water) cost.AddCost(ret);
415 if (flags & DC_EXEC) {
416 switch (wc) {
417 case WATER_CLASS_RIVER:
418 MakeRiver(tile, Random());
419 if (_game_mode == GM_EDITOR) {
420 TileIndex tile2 = tile;
421 CircularTileSearch(&tile2, 5, RiverModifyDesertZone, NULL);
423 break;
425 case WATER_CLASS_SEA:
426 if (TileHeight(tile) == 0) {
427 MakeSea(tile);
428 break;
430 /* FALL THROUGH */
432 default:
433 MakeCanal(tile, _current_company, Random());
434 if (Company::IsValidID(_current_company)) {
435 Company::Get(_current_company)->infrastructure.water++;
436 DirtyCompanyInfrastructureWindows(_current_company);
438 break;
440 MarkTileDirtyByTile(tile);
441 MarkCanalsAndRiversAroundDirty(tile);
444 cost.AddCost(_price[PR_BUILD_CANAL]);
447 if (cost.GetCost() == 0) {
448 return_cmd_error(STR_ERROR_ALREADY_BUILT);
449 } else {
450 return cost;
454 static CommandCost ClearTile_Water(TileIndex tile, DoCommandFlag flags)
456 switch (GetWaterTileType(tile)) {
457 case WATER_TILE_CLEAR: {
458 if (flags & DC_NO_WATER) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER);
460 Money base_cost = IsCanal(tile) ? _price[PR_CLEAR_CANAL] : _price[PR_CLEAR_WATER];
461 /* Make sure freeform edges are allowed or it's not an edge tile. */
462 if (!_settings_game.construction.freeform_edges && (!IsInsideMM(TileX(tile), 1, MapMaxX() - 1) ||
463 !IsInsideMM(TileY(tile), 1, MapMaxY() - 1))) {
464 return_cmd_error(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP);
467 /* Make sure no vehicle is on the tile */
468 CommandCost ret = EnsureNoVehicleOnGround(tile);
469 if (ret.Failed()) return ret;
471 Owner owner = GetTileOwner(tile);
472 if (owner != OWNER_WATER && owner != OWNER_NONE) {
473 CommandCost ret = CheckTileOwnership(tile);
474 if (ret.Failed()) return ret;
477 if (flags & DC_EXEC) {
478 if (IsCanal(tile) && Company::IsValidID(owner)) {
479 Company::Get(owner)->infrastructure.water--;
480 DirtyCompanyInfrastructureWindows(owner);
482 DoClearSquare(tile);
483 MarkCanalsAndRiversAroundDirty(tile);
486 return CommandCost(EXPENSES_CONSTRUCTION, base_cost);
489 case WATER_TILE_COAST: {
490 Slope slope = GetTileSlope(tile);
492 /* Make sure no vehicle is on the tile */
493 CommandCost ret = EnsureNoVehicleOnGround(tile);
494 if (ret.Failed()) return ret;
496 if (flags & DC_EXEC) {
497 DoClearSquare(tile);
498 MarkCanalsAndRiversAroundDirty(tile);
500 if (IsSlopeWithOneCornerRaised(slope)) {
501 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_WATER]);
502 } else {
503 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_ROUGH]);
507 case WATER_TILE_LOCK:
508 if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
509 if (_current_company == OWNER_WATER) return CMD_ERROR;
510 /* move to the middle tile.. */
511 switch (GetLockPart(tile)) {
512 case LOCK_PART_LOWER: tile += TileOffsByDiagDir(GetLockDirection(tile)); break;
513 case LOCK_PART_UPPER: tile -= TileOffsByDiagDir(GetLockDirection(tile)); break;
514 default: break;
516 return RemoveLock(tile, flags);
518 case WATER_TILE_DEPOT:
519 if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
520 return RemoveShipDepot(tile, flags);
522 default:
523 NOT_REACHED();
528 * return true if a tile is a water tile wrt. a certain direction.
530 * @param tile The tile of interest.
531 * @param from The direction of interest.
532 * @return true iff the tile is water in the view of 'from'.
535 bool IsWateredTile(TileIndex tile, Direction from)
537 if (IsIndustryTile(tile)) {
538 /* Do not draw waterborders inside of industries.
539 * Note: There is no easy way to detect the industry of an oilrig tile. */
540 TileIndex src_tile = tile + TileOffsByDir(from);
541 if ((IsStationTile(src_tile) && IsOilRig(src_tile)) ||
542 (IsIndustryTile(src_tile) && GetIndustryIndex(src_tile) == GetIndustryIndex(tile))) return true;
544 return IsTileOnWater(tile);
547 switch (GetTileType(tile)) {
548 case TT_GROUND: return IsTileSubtype(tile, TT_GROUND_VOID); // consider map border as water, esp. for rivers
550 case TT_WATER:
551 switch (GetWaterTileType(tile)) {
552 default: NOT_REACHED();
553 case WATER_TILE_DEPOT: case WATER_TILE_CLEAR: return true;
554 case WATER_TILE_LOCK: return DiagDirToAxis(GetLockDirection(tile)) == DiagDirToAxis(DirToDiagDir(from));
556 case WATER_TILE_COAST:
557 switch (GetTileSlope(tile)) {
558 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
559 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
560 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
561 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
562 default: return false;
566 case TT_RAILWAY:
567 if (IsTileSubtype(tile, TT_TRACK) && GetRailGroundType(tile) == RAIL_GROUND_WATER) {
568 switch (GetTileSlope(tile)) {
569 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
570 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
571 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
572 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
573 default: return false;
576 return false;
578 case TT_MISC:
579 return IsTileSubtype(tile, TT_MISC_AQUEDUCT) && ReverseDiagDir(GetTunnelBridgeDirection(tile)) == DirToDiagDir(from);
581 case TT_STATION:
582 if (IsOilRig(tile)) {
583 /* Do not draw waterborders inside of industries.
584 * Note: There is no easy way to detect the industry of an oilrig tile. */
585 TileIndex src_tile = tile + TileOffsByDir(from);
586 if ((IsStationTile(src_tile) && IsOilRig(src_tile)) ||
587 (IsIndustryTile(src_tile))) return true;
589 return IsTileOnWater(tile);
591 return (IsDock(tile) && IsTileFlat(tile)) || IsBuoy(tile);
593 case TT_OBJECT: return IsTileOnWater(tile);
595 default: return false;
600 * Draw a water sprite, potentially with a NewGRF-modified sprite offset.
601 * @param base Sprite base.
602 * @param offset Sprite offset.
603 * @param feature The type of sprite that is drawn.
604 * @param tile Tile index to draw.
606 static void DrawWaterSprite(SpriteID base, uint offset, CanalFeature feature, TileIndex tile)
608 if (base != SPR_FLAT_WATER_TILE) {
609 /* Only call offset callback if the sprite is NewGRF-provided. */
610 offset = GetCanalSpriteOffset(feature, tile, offset);
612 DrawGroundSprite(base + offset, PAL_NONE);
616 * Draw canal or river edges.
617 * @param canal True if canal edges should be drawn, false for river edges.
618 * @param offset Sprite offset.
619 * @param tile Tile to draw.
621 static void DrawWaterEdges(bool canal, uint offset, TileIndex tile)
623 CanalFeature feature;
624 SpriteID base = 0;
625 if (canal) {
626 feature = CF_DIKES;
627 base = GetCanalSprite(CF_DIKES, tile);
628 if (base == 0) base = SPR_CANAL_DIKES_BASE;
629 } else {
630 feature = CF_RIVER_EDGE;
631 base = GetCanalSprite(CF_RIVER_EDGE, tile);
632 if (base == 0) return; // Don't draw if no sprites provided.
635 uint wa;
637 /* determine the edges around with water. */
638 wa = IsWateredTile(TILE_ADDXY(tile, -1, 0), DIR_SW) << 0;
639 wa += IsWateredTile(TILE_ADDXY(tile, 0, 1), DIR_NW) << 1;
640 wa += IsWateredTile(TILE_ADDXY(tile, 1, 0), DIR_NE) << 2;
641 wa += IsWateredTile(TILE_ADDXY(tile, 0, -1), DIR_SE) << 3;
643 if (!(wa & 1)) DrawWaterSprite(base, offset, feature, tile);
644 if (!(wa & 2)) DrawWaterSprite(base, offset + 1, feature, tile);
645 if (!(wa & 4)) DrawWaterSprite(base, offset + 2, feature, tile);
646 if (!(wa & 8)) DrawWaterSprite(base, offset + 3, feature, tile);
648 /* right corner */
649 switch (wa & 0x03) {
650 case 0: DrawWaterSprite(base, offset + 4, feature, tile); break;
651 case 3: if (!IsWateredTile(TILE_ADDXY(tile, -1, 1), DIR_W)) DrawWaterSprite(base, offset + 8, feature, tile); break;
654 /* bottom corner */
655 switch (wa & 0x06) {
656 case 0: DrawWaterSprite(base, offset + 5, feature, tile); break;
657 case 6: if (!IsWateredTile(TILE_ADDXY(tile, 1, 1), DIR_N)) DrawWaterSprite(base, offset + 9, feature, tile); break;
660 /* left corner */
661 switch (wa & 0x0C) {
662 case 0: DrawWaterSprite(base, offset + 6, feature, tile); break;
663 case 12: if (!IsWateredTile(TILE_ADDXY(tile, 1, -1), DIR_E)) DrawWaterSprite(base, offset + 10, feature, tile); break;
666 /* upper corner */
667 switch (wa & 0x09) {
668 case 0: DrawWaterSprite(base, offset + 7, feature, tile); break;
669 case 9: if (!IsWateredTile(TILE_ADDXY(tile, -1, -1), DIR_S)) DrawWaterSprite(base, offset + 11, feature, tile); break;
673 /** Draw a plain sea water tile with no edges */
674 static void DrawSeaWater(TileIndex tile)
676 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
679 /** draw a canal styled water tile with dikes around */
680 static void DrawCanalWater(TileIndex tile)
682 SpriteID image = SPR_FLAT_WATER_TILE;
683 if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
684 /* First water slope sprite is flat water. */
685 image = GetCanalSprite(CF_WATERSLOPE, tile);
686 if (image == 0) image = SPR_FLAT_WATER_TILE;
688 DrawWaterSprite(image, 0, CF_WATERSLOPE, tile);
690 DrawWaterEdges(true, 0, tile);
693 #include "table/water_land.h"
696 * Draw a build sprite sequence for water tiles.
697 * If buildings are invisible, nothing will be drawn.
698 * @param ti Tile info.
699 * @param dtss Sprite sequence to draw.
700 * @param base Base sprite.
701 * @param offset Additional sprite offset.
702 * @param palette Palette to use.
704 static void DrawWaterTileStruct(const TileInfo *ti, const DrawTileSeqStruct *dtss, SpriteID base, uint offset, PaletteID palette, CanalFeature feature)
706 /* Don't draw if buildings are invisible. */
707 if (IsInvisibilitySet(TO_BUILDINGS)) return;
709 for (; !dtss->IsTerminator(); dtss++) {
710 uint tile_offs = offset + dtss->image.sprite;
711 if (feature < CF_END) tile_offs = GetCanalSpriteOffset(feature, ti->tile, tile_offs);
712 AddSortableSpriteToDraw(base + tile_offs, palette,
713 ti->x + dtss->delta_x, ti->y + dtss->delta_y,
714 dtss->size_x, dtss->size_y,
715 dtss->size_z, ti->z + dtss->delta_z,
716 IsTransparencySet(TO_BUILDINGS));
720 /** Draw a lock tile. */
721 static void DrawWaterLock(const TileInfo *ti)
723 int part = GetLockPart(ti->tile);
724 const DrawTileSprites &dts = _lock_display_data[part][GetLockDirection(ti->tile)];
726 /* Draw ground sprite. */
727 SpriteID image = dts.ground.sprite;
729 SpriteID water_base = GetCanalSprite(CF_WATERSLOPE, ti->tile);
730 if (water_base == 0) {
731 /* Use default sprites. */
732 water_base = SPR_CANALS_BASE;
733 } else if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
734 /* NewGRF supplies a flat sprite as first sprite. */
735 if (image == SPR_FLAT_WATER_TILE) {
736 image = water_base;
737 } else {
738 image++;
742 if (image < 5) image += water_base;
743 DrawGroundSprite(image, PAL_NONE);
745 /* Draw structures. */
746 uint zoffs = 0;
747 SpriteID base = GetCanalSprite(CF_LOCKS, ti->tile);
749 if (base == 0) {
750 /* If no custom graphics, use defaults. */
751 base = SPR_LOCK_BASE;
752 uint8 z_threshold = part == LOCK_PART_UPPER ? 8 : 0;
753 zoffs = ti->z > z_threshold ? 24 : 0;
756 DrawWaterTileStruct(ti, dts.seq, base, zoffs, PAL_NONE, CF_LOCKS);
759 /** Draw a ship depot tile. */
760 static void DrawWaterDepot(const TileInfo *ti)
762 DrawWaterClassGround(ti);
763 DrawWaterTileStruct(ti, _shipdepot_display_data[GetShipDepotDirection(ti->tile)].seq, 0, 0, COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile)), CF_END);
766 static void DrawRiverWater(const TileInfo *ti)
768 SpriteID image = SPR_FLAT_WATER_TILE;
769 uint offset = 0;
770 uint edges_offset = 0;
772 if (ti->tileh != SLOPE_FLAT || HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
773 image = GetCanalSprite(CF_RIVER_SLOPE, ti->tile);
774 if (image == 0) {
775 switch (ti->tileh) {
776 case SLOPE_NW: image = SPR_WATER_SLOPE_Y_DOWN; break;
777 case SLOPE_SW: image = SPR_WATER_SLOPE_X_UP; break;
778 case SLOPE_SE: image = SPR_WATER_SLOPE_Y_UP; break;
779 case SLOPE_NE: image = SPR_WATER_SLOPE_X_DOWN; break;
780 default: image = SPR_FLAT_WATER_TILE; break;
782 } else {
783 /* Flag bit 0 indicates that the first sprite is flat water. */
784 offset = HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE) ? 1 : 0;
786 switch (ti->tileh) {
787 case SLOPE_SE: edges_offset += 12; break;
788 case SLOPE_NE: offset += 1; edges_offset += 24; break;
789 case SLOPE_SW: offset += 2; edges_offset += 36; break;
790 case SLOPE_NW: offset += 3; edges_offset += 48; break;
791 default: offset = 0; break;
794 offset = GetCanalSpriteOffset(CF_RIVER_SLOPE, ti->tile, offset);
798 DrawGroundSprite(image + offset, PAL_NONE);
800 /* Draw river edges if available. */
801 DrawWaterEdges(false, edges_offset, ti->tile);
804 void DrawShoreTile(Slope tileh)
806 /* Converts the enum Slope into an offset based on SPR_SHORE_BASE.
807 * This allows to calculate the proper sprite to display for this Slope */
808 static const byte tileh_to_shoresprite[32] = {
809 0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0,
810 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 15, 0,
813 assert(!IsHalftileSlope(tileh)); // Halftile slopes need to get handled earlier.
814 assert(tileh != SLOPE_FLAT); // Shore is never flat
816 assert((tileh != SLOPE_EW) && (tileh != SLOPE_NS)); // No suitable sprites for current flooding behaviour
818 DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[tileh], PAL_NONE);
821 void DrawWaterClassGround(const TileInfo *ti)
823 switch (GetWaterClass(ti->tile)) {
824 case WATER_CLASS_SEA: DrawSeaWater(ti->tile); break;
825 case WATER_CLASS_CANAL: DrawCanalWater(ti->tile); break;
826 case WATER_CLASS_RIVER: DrawRiverWater(ti); break;
827 default: NOT_REACHED();
831 static void DrawTile_Water(TileInfo *ti)
833 switch (GetWaterTileType(ti->tile)) {
834 case WATER_TILE_CLEAR:
835 DrawWaterClassGround(ti);
836 DrawBridgeMiddle(ti);
837 break;
839 case WATER_TILE_COAST: {
840 DrawShoreTile(ti->tileh);
841 DrawBridgeMiddle(ti);
842 break;
845 case WATER_TILE_LOCK:
846 DrawWaterLock(ti);
847 break;
849 case WATER_TILE_DEPOT:
850 DrawWaterDepot(ti);
851 break;
855 void DrawShipDepotSprite(int x, int y, DiagDirection dir)
857 const DrawTileSprites &dts = _shipdepot_display_data[dir];
859 DrawSprite(dts.ground.sprite, dts.ground.pal, x, y);
860 DrawOrigTileSeqInGUI(x, y, &dts, COMPANY_SPRITE_COLOUR(_local_company));
864 static int GetSlopePixelZ_Water(TileIndex tile, uint x, uint y)
866 int z;
867 Slope tileh = GetTilePixelSlope(tile, &z);
869 return z + GetPartialPixelZ(x & 0xF, y & 0xF, tileh);
872 static Foundation GetFoundation_Water(TileIndex tile, Slope tileh)
874 return FOUNDATION_NONE;
877 static void GetTileDesc_Water(TileIndex tile, TileDesc *td)
879 switch (GetWaterTileType(tile)) {
880 case WATER_TILE_CLEAR:
881 switch (GetWaterClass(tile)) {
882 case WATER_CLASS_SEA: td->str = STR_LAI_WATER_DESCRIPTION_WATER; break;
883 case WATER_CLASS_CANAL: td->str = STR_LAI_WATER_DESCRIPTION_CANAL; break;
884 case WATER_CLASS_RIVER: td->str = STR_LAI_WATER_DESCRIPTION_RIVER; break;
885 default: NOT_REACHED(); break;
887 break;
888 case WATER_TILE_COAST: td->str = STR_LAI_WATER_DESCRIPTION_COAST_OR_RIVERBANK; break;
889 case WATER_TILE_LOCK : td->str = STR_LAI_WATER_DESCRIPTION_LOCK; break;
890 case WATER_TILE_DEPOT:
891 td->str = STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT;
892 td->build_date = Depot::GetByTile(tile)->build_date;
893 break;
894 default: NOT_REACHED(); break;
897 td->owner[0] = GetTileOwner(tile);
901 * Handle the flooding of a vehicle. This sets the vehicle state to crashed,
902 * creates a newsitem and dirties the necessary windows.
903 * @param v The vehicle to flood.
905 static void FloodVehicle(Vehicle *v)
907 uint pass = v->Crash(true);
909 AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_FLOODED));
910 Game::NewEvent(new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_FLOODED));
911 SetDParam(0, pass);
912 AddVehicleNewsItem(STR_NEWS_DISASTER_FLOOD_VEHICLE, NT_ACCIDENT, v->index);
913 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
914 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v);
918 * Flood vehicles on a tile if we are allowed to flood them, i.e. when they are on the ground.
919 * @param tile The tile to flood.
920 * @param z The z of level to flood.
922 static void FloodTileVehicles(TileIndex tile, int z)
924 VehicleTileIterator iter (tile);
925 while (!iter.finished()) {
926 Vehicle *v = iter.next();
928 if ((v->vehstatus & VS_CRASHED) != 0) continue;
930 switch (v->type) {
931 default: break;
933 case VEH_TRAIN:
934 case VEH_ROAD:
935 if (v->z_pos <= z) FloodVehicle(v->First());
941 * Finds a vehicle to flood.
942 * It does not find vehicles that are already crashed on bridges, i.e. flooded.
943 * @param tile the tile where to find a vehicle to flood
945 static void FloodVehicles(TileIndex tile)
947 if (IsAirportTile(tile)) {
948 if (GetTileMaxZ(tile) != 0) return;
950 const Station *st = Station::GetByTile(tile);
951 TILE_AREA_LOOP(tile, st->airport) {
952 if (st->TileBelongsToAirport(tile)) {
953 VehicleTileIterator iter (tile);
954 while (!iter.finished()) {
955 Vehicle *v = iter.next();
957 if (v->type != VEH_AIRCRAFT) continue;
958 if (v->subtype == AIR_SHADOW) continue;
959 if ((v->vehstatus & VS_CRASHED) != 0) continue;
961 /* We compare v->z_pos against delta_z + 1 because the shadow
962 * is at delta_z and the actual aircraft at delta_z + 1. */
963 const AirportFTAClass *airport = st->airport.GetFTA();
964 if (v->z_pos == airport->delta_z + 1) FloodVehicle(v);
969 /* No vehicle could be flooded on this airport anymore */
970 return;
973 if (!IsBridgeHeadTile(tile)) {
974 FloodTileVehicles (tile, 0);
975 return;
978 int z = GetBridgePixelHeight(tile);
979 FloodTileVehicles (tile, z);
980 FloodTileVehicles (GetOtherBridgeEnd(tile), z);
984 * Returns the behaviour of a tile during flooding.
986 * @return Behaviour of the tile
988 FloodingBehaviour GetFloodingBehaviour(TileIndex tile)
990 /* FLOOD_ACTIVE: 'single-corner-raised'-coast, sea, sea-shipdepots, sea-buoys, sea-docks (water part), rail with flooded halftile, sea-water-industries, sea-oilrigs
991 * FLOOD_DRYUP: coast with more than one corner raised, coast with rail-track, coast with trees
992 * FLOOD_PASSIVE: (not used)
993 * FLOOD_NONE: canals, rivers, everything else
995 if (IsIndustryTile(tile)) {
996 return (GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE;
999 switch (GetTileType(tile)) {
1000 case TT_WATER:
1001 if (IsCoast(tile)) {
1002 Slope tileh = GetTileSlope(tile);
1003 return (IsSlopeWithOneCornerRaised(tileh) ? FLOOD_ACTIVE : FLOOD_DRYUP);
1005 /* FALL THROUGH */
1006 case TT_STATION:
1007 case TT_OBJECT:
1008 return (GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE;
1010 case TT_RAILWAY:
1011 if (IsTileSubtype(tile, TT_TRACK) && GetRailGroundType(tile) == RAIL_GROUND_WATER) {
1012 return (IsSlopeWithOneCornerRaised(GetTileSlope(tile)) ? FLOOD_ACTIVE : FLOOD_DRYUP);
1014 return FLOOD_NONE;
1016 case TT_GROUND:
1017 return (IsTreeTile(tile) && GetClearGround(tile) == GROUND_SHORE ? FLOOD_DRYUP : FLOOD_NONE);
1019 default:
1020 return FLOOD_NONE;
1025 * Floods a tile.
1027 void DoFloodTile(TileIndex target)
1029 assert(!IsWaterTile(target));
1031 bool flooded = false; // Will be set to true if something is changed.
1033 Backup<CompanyByte> cur_company(_current_company, OWNER_WATER, FILE_LINE);
1035 Slope tileh = GetTileSlope(target);
1036 if (tileh != SLOPE_FLAT) {
1037 /* make coast.. */
1038 switch (GetTileType(target)) {
1039 case TT_RAILWAY:
1040 if (IsTileSubtype(target, TT_TRACK)) {
1041 FloodVehicles(target);
1042 flooded = FloodHalftile(target);
1044 break;
1046 case TT_GROUND:
1047 if (IsTreeTile(target) && !IsSlopeWithOneCornerRaised(tileh)) {
1048 SetClearGroundDensity(target, GROUND_SHORE, 3, true);
1049 MarkTileDirtyByTile(target);
1050 flooded = true;
1051 break;
1053 if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
1054 MakeShore(target);
1055 MarkTileDirtyByTile(target);
1056 flooded = true;
1058 break;
1060 default:
1061 break;
1063 } else {
1064 /* Flood vehicles */
1065 FloodVehicles(target);
1067 /* flood flat tile */
1068 if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
1069 MakeSea(target);
1070 MarkTileDirtyByTile(target);
1071 flooded = true;
1075 if (flooded) {
1076 /* Mark surrounding canal tiles dirty too to avoid glitches */
1077 MarkCanalsAndRiversAroundDirty(target);
1079 /* update signals if needed */
1080 UpdateSignalsInBuffer();
1083 cur_company.Restore();
1087 * Drys a tile up.
1089 static void DoDryUp(TileIndex tile)
1091 Backup<CompanyByte> cur_company(_current_company, OWNER_WATER, FILE_LINE);
1093 switch (GetTileType(tile)) {
1094 case TT_RAILWAY:
1095 assert(IsTileSubtype(tile, TT_TRACK));
1096 assert(GetRailGroundType(tile) == RAIL_GROUND_WATER);
1098 RailGroundType new_ground;
1099 switch (GetTrackBits(tile)) {
1100 case TRACK_BIT_UPPER: new_ground = RAIL_GROUND_FENCE_HORIZ1; break;
1101 case TRACK_BIT_LOWER: new_ground = RAIL_GROUND_FENCE_HORIZ2; break;
1102 case TRACK_BIT_LEFT: new_ground = RAIL_GROUND_FENCE_VERT1; break;
1103 case TRACK_BIT_RIGHT: new_ground = RAIL_GROUND_FENCE_VERT2; break;
1104 default: NOT_REACHED();
1106 SetRailGroundType(tile, new_ground);
1107 MarkTileDirtyByTile(tile);
1108 break;
1110 case TT_GROUND:
1111 assert(IsTreeTile(tile));
1112 SetClearGroundDensity(tile, GROUND_GRASS, 3, true);
1113 MarkTileDirtyByTile(tile);
1114 break;
1116 case TT_WATER:
1117 assert(IsCoast(tile));
1119 if (DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
1120 MakeClear(tile, GROUND_GRASS, 3);
1121 MarkTileDirtyByTile(tile);
1123 break;
1125 default: NOT_REACHED();
1128 cur_company.Restore();
1132 * Let a water tile floods its diagonal adjoining tiles
1133 * called from tunnelbridge_cmd, and by TileLoop_Industry() and TileLoop_Track()
1135 * @param tile the water/shore tile that floods
1137 void TileLoop_Water(TileIndex tile)
1139 if (IsWaterTile(tile)) AmbientSoundEffect(tile);
1141 switch (GetFloodingBehaviour(tile)) {
1142 case FLOOD_ACTIVE:
1143 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
1144 TileIndex dest = tile + TileOffsByDir(dir);
1145 if (!IsValidTile(dest)) continue;
1146 /* do not try to flood water tiles - increases performance a lot */
1147 if (IsWaterTile(dest)) continue;
1149 /* GROUND_SHORE is the sign of a previous flood. */
1150 if ((IsClearTile(dest) || IsTreeTile(dest)) && IsClearGround (dest, GROUND_SHORE)) continue;
1152 int z_dest;
1153 Slope slope_dest = GetFoundationSlope(dest, &z_dest) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
1154 if (z_dest > 0) continue;
1156 if (!HasBit(_flood_from_dirs[slope_dest], ReverseDir(dir))) continue;
1158 DoFloodTile(dest);
1160 break;
1162 case FLOOD_DRYUP: {
1163 Slope slope_here = GetFoundationSlope(tile) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
1164 uint dir;
1165 FOR_EACH_SET_BIT(dir, _flood_from_dirs[slope_here]) {
1166 TileIndex dest = tile + TileOffsByDir((Direction)dir);
1167 if (!IsValidTile(dest)) continue;
1169 FloodingBehaviour dest_behaviour = GetFloodingBehaviour(dest);
1170 if ((dest_behaviour == FLOOD_ACTIVE) || (dest_behaviour == FLOOD_PASSIVE)) return;
1172 DoDryUp(tile);
1173 break;
1176 default: return;
1180 void ConvertGroundTilesIntoWaterTiles()
1182 int z;
1184 for (TileIndex tile = 0; tile < MapSize(); ++tile) {
1185 Slope slope = GetTileSlope(tile, &z);
1186 if (IsGroundTile(tile) && z == 0) {
1187 /* Make both water for tiles at level 0
1188 * and make shore, as that looks much better
1189 * during the generation. */
1190 switch (slope) {
1191 case SLOPE_FLAT:
1192 MakeSea(tile);
1193 break;
1195 case SLOPE_N:
1196 case SLOPE_E:
1197 case SLOPE_S:
1198 case SLOPE_W:
1199 MakeShore(tile);
1200 break;
1202 default:
1203 uint dir;
1204 FOR_EACH_SET_BIT(dir, _flood_from_dirs[slope & ~SLOPE_STEEP]) {
1205 TileIndex dest = TILE_ADD(tile, TileOffsByDir((Direction)dir));
1206 Slope slope_dest = GetTileSlope(dest) & ~SLOPE_STEEP;
1207 if (slope_dest == SLOPE_FLAT || IsSlopeWithOneCornerRaised(slope_dest)) {
1208 MakeShore(tile);
1209 break;
1212 break;
1218 static TrackdirBits GetTileWaterwayStatus_Water(TileIndex tile, DiagDirection side)
1220 static const byte coast_tracks[] = {0, 32, 4, 0, 16, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0};
1222 TrackBits ts;
1224 switch (GetWaterTileType(tile)) {
1225 case WATER_TILE_CLEAR: ts = IsTileFlat(tile) ? TRACK_BIT_ALL : TRACK_BIT_NONE; break;
1226 case WATER_TILE_COAST: ts = (TrackBits)coast_tracks[GetTileSlope(tile) & 0xF]; break;
1227 case WATER_TILE_LOCK: ts = DiagDirToDiagTrackBits(GetLockDirection(tile)); break;
1228 case WATER_TILE_DEPOT: ts = DiagDirToDiagTrackBits(GetShipDepotDirection(tile)); break;
1229 default: return TRACKDIR_BIT_NONE;
1231 if (TileX(tile) == 0) {
1232 /* NE border: remove tracks that connects NE tile edge */
1233 ts &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
1235 if (TileY(tile) == 0) {
1236 /* NW border: remove tracks that connects NW tile edge */
1237 ts &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
1239 return TrackBitsToTrackdirBits(ts);
1242 static bool ClickTile_Water(TileIndex tile)
1244 if (GetWaterTileType(tile) == WATER_TILE_DEPOT) {
1245 ShowDepotWindow(GetShipDepotNorthTile(tile), VEH_SHIP);
1246 return true;
1248 return false;
1251 static void ChangeTileOwner_Water(TileIndex tile, Owner old_owner, Owner new_owner)
1253 if (!IsTileOwner(tile, old_owner)) return;
1255 bool is_lock_middle = IsLock(tile) && GetLockPart(tile) == LOCK_PART_MIDDLE;
1257 /* No need to dirty company windows here, we'll redraw the whole screen anyway. */
1258 if (is_lock_middle) Company::Get(old_owner)->infrastructure.water -= 3 * LOCK_DEPOT_TILE_FACTOR; // Lock has three parts.
1259 if (new_owner != INVALID_OWNER) {
1260 if (is_lock_middle) Company::Get(new_owner)->infrastructure.water += 3 * LOCK_DEPOT_TILE_FACTOR; // Lock has three parts.
1261 /* Only subtract from the old owner here if the new owner is valid,
1262 * otherwise we clear ship depots and canal water below. */
1263 if (GetWaterClass(tile) == WATER_CLASS_CANAL && !is_lock_middle) {
1264 Company::Get(old_owner)->infrastructure.water--;
1265 Company::Get(new_owner)->infrastructure.water++;
1267 if (IsShipDepot(tile)) {
1268 Company::Get(old_owner)->infrastructure.water -= LOCK_DEPOT_TILE_FACTOR;
1269 Company::Get(new_owner)->infrastructure.water += LOCK_DEPOT_TILE_FACTOR;
1272 SetTileOwner(tile, new_owner);
1273 return;
1276 /* Remove depot */
1277 if (IsShipDepot(tile)) DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
1279 /* Set owner of canals and locks ... and also canal under dock there was before.
1280 * Check if the new owner after removing depot isn't OWNER_WATER. */
1281 if (IsTileOwner(tile, old_owner)) {
1282 if (GetWaterClass(tile) == WATER_CLASS_CANAL && !is_lock_middle) Company::Get(old_owner)->infrastructure.water--;
1283 SetTileOwner(tile, OWNER_NONE);
1287 static CommandCost TerraformTile_Water(TileIndex tile, DoCommandFlag flags, int z_new, Slope tileh_new)
1289 /* Canals can't be terraformed */
1290 if (IsPlainWaterTile(tile) && IsCanal(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_CANAL_FIRST);
1292 return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
1296 extern const TileTypeProcs _tile_type_water_procs = {
1297 DrawTile_Water, // draw_tile_proc
1298 GetSlopePixelZ_Water, // get_slope_z_proc
1299 ClearTile_Water, // clear_tile_proc
1300 NULL, // add_accepted_cargo_proc
1301 GetTileDesc_Water, // get_tile_desc_proc
1302 NULL, // get_tile_railway_status_proc
1303 NULL, // get_tile_road_status_proc
1304 GetTileWaterwayStatus_Water, // get_tile_waterway_status_proc
1305 ClickTile_Water, // click_tile_proc
1306 NULL, // animate_tile_proc
1307 TileLoop_Water, // tile_loop_proc
1308 ChangeTileOwner_Water, // change_tile_owner_proc
1309 NULL, // add_produced_cargo_proc
1310 GetFoundation_Water, // get_foundation_proc
1311 TerraformTile_Water, // terraform_tile_proc