Make it possible to stop road vehicles from slowing down in curves so "diagonal"...
[openttd-joker.git] / src / water_cmd.cpp
blob97d7ced6d9818549f07629226d5de4250fc13dbc
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 "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"
43 #include "table/strings.h"
45 #include "safeguards.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 (IsValidTile(tile) && IsTileType(tile, MP_WATER) && (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 CommandError(STR_ERROR_MUST_BE_BUILT_ON_WATER);
112 if (IsBridgeAbove(tile) || IsBridgeAbove(tile2)) return CommandError(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
114 if (!IsTileFlat(tile) || !IsTileFlat(tile2)) {
115 /* Prevent depots on rapids */
116 return CommandError(STR_ERROR_SITE_UNSUITABLE);
119 if (!Depot::CanAllocateItem()) return CommandError();
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 = !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);
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);
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, 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);
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 != nullptr) {
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 != nullptr) {
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 CommandError();
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 != nullptr) {
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 Central tile of the lock.
245 * @param dir Uphill direction.
246 * @param flags Operation to perform.
247 * @return The cost in case of success, or an error code if it failed.
249 static CommandCost DoBuildLock(TileIndex tile, DiagDirection dir, DoCommandFlag flags)
251 CommandCost cost(EXPENSES_CONSTRUCTION);
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;
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);
265 /* lower tile */
266 if (!IsWaterTile(tile - delta)) {
267 ret = DoCommand(tile - delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
268 if (ret.Failed()) return ret;
269 cost.AddCost(ret);
270 cost.AddCost(_price[PR_BUILD_CANAL]);
272 if (!IsTileFlat(tile - delta)) {
273 return CommandError(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
275 WaterClass wc_lower = IsWaterTile(tile - delta) ? GetWaterClass(tile - delta) : WATER_CLASS_CANAL;
277 /* upper tile */
278 if (!IsWaterTile(tile + delta)) {
279 ret = DoCommand(tile + delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
280 if (ret.Failed()) return ret;
281 cost.AddCost(ret);
282 cost.AddCost(_price[PR_BUILD_CANAL]);
284 if (!IsTileFlat(tile + delta)) {
285 return CommandError(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
287 WaterClass wc_upper = IsWaterTile(tile + delta) ? GetWaterClass(tile + delta) : WATER_CLASS_CANAL;
289 if (IsBridgeAbove(tile) || IsBridgeAbove(tile - delta) || IsBridgeAbove(tile + delta)) {
290 return CommandError(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
293 if (flags & DC_EXEC) {
294 /* Update company infrastructure counts. */
295 Company *c = Company::GetIfValid(_current_company);
296 if (c != nullptr) {
297 /* Counts for the water. */
298 if (!IsWaterTile(tile - delta)) c->infrastructure.water++;
299 if (!IsWaterTile(tile + delta)) c->infrastructure.water++;
300 /* Count for the lock itself. */
301 c->infrastructure.water += 3 * LOCK_DEPOT_TILE_FACTOR; // Lock is three tiles.
302 DirtyCompanyInfrastructureWindows(_current_company);
305 MakeLock(tile, _current_company, dir, wc_lower, wc_upper, wc_middle);
306 MarkTileDirtyByTile(tile);
307 MarkTileDirtyByTile(tile - delta);
308 MarkTileDirtyByTile(tile + delta);
309 MarkCanalsAndRiversAroundDirty(tile - delta);
310 MarkCanalsAndRiversAroundDirty(tile + delta);
312 cost.AddCost(_price[PR_BUILD_LOCK]);
314 return cost;
318 * Remove a lock.
319 * @param tile Central tile of the lock.
320 * @param flags Operation to perform.
321 * @return The cost in case of success, or an error code if it failed.
323 static CommandCost RemoveLock(TileIndex tile, DoCommandFlag flags)
325 if (GetTileOwner(tile) != OWNER_NONE) {
326 CommandCost ret = CheckTileOwnership(tile);
327 if (ret.Failed()) return ret;
330 TileIndexDiff delta = TileOffsByDiagDir(GetLockDirection(tile));
332 /* make sure no vehicle is on the tile. */
333 CommandCost ret = EnsureNoVehicleOnGround(tile);
334 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta);
335 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta);
336 if (ret.Failed()) return ret;
338 if (flags & DC_EXEC) {
339 /* Remove middle part from company infrastructure count. */
340 Company *c = Company::GetIfValid(GetTileOwner(tile));
341 if (c != nullptr) {
342 c->infrastructure.water -= 3 * LOCK_DEPOT_TILE_FACTOR; // three parts of the lock.
343 DirtyCompanyInfrastructureWindows(c->index);
346 if (GetWaterClass(tile) == WATER_CLASS_RIVER) {
347 MakeRiver(tile, Random());
348 } else {
349 DoClearSquare(tile);
351 MakeWaterKeepingClass(tile + delta, GetTileOwner(tile + delta));
352 MakeWaterKeepingClass(tile - delta, GetTileOwner(tile - delta));
353 MarkCanalsAndRiversAroundDirty(tile);
354 MarkCanalsAndRiversAroundDirty(tile - delta);
355 MarkCanalsAndRiversAroundDirty(tile + delta);
358 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_LOCK]);
362 * Builds a lock.
363 * @param tile tile where to place the lock
364 * @param flags type of operation
365 * @param p1 unused
366 * @param p2 unused
367 * @param text unused
368 * @return the cost of this operation or an error
370 CommandCost CmdBuildLock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
372 DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile));
373 if (dir == INVALID_DIAGDIR) return CommandError(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
375 return DoBuildLock(tile, dir, flags);
378 /** Callback to create non-desert around a river tile. */
379 bool RiverModifyDesertZone(TileIndex tile, void *)
381 if (GetTropicZone(tile) == TROPICZONE_DESERT) SetTropicZone(tile, TROPICZONE_NORMAL);
382 return false;
386 * Build a piece of canal.
387 * @param tile end tile of stretch-dragging
388 * @param flags type of operation
389 * @param p1 start tile of stretch-dragging
390 * @param p2 waterclass to build. sea can only be built in scenario editor
391 * @param text unused
392 * @return the cost of this operation or an error
394 CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
396 WaterClass wc = Extract<WaterClass, 0, 2>(p2);
397 if (p1 >= MapSize() || wc == WATER_CLASS_INVALID) return CommandError();
399 /* Outside of the editor you may not build oceans */
400 if (wc == WATER_CLASS_SEA && _game_mode != GM_EDITOR) return CommandError();
402 TileArea ta(tile, p1);
404 /* Outside the editor you can only drag canals, and not areas */
405 if (_game_mode != GM_EDITOR && ta.w != 1 && ta.h != 1) return CommandError();
407 CommandCost cost(EXPENSES_CONSTRUCTION);
408 TILE_AREA_LOOP(tile, ta) {
409 CommandCost ret;
411 Slope slope = GetTileSlope(tile);
412 if (slope != SLOPE_FLAT && (wc != WATER_CLASS_RIVER || !IsInclinedSlope(slope))) {
413 return CommandError(STR_ERROR_FLAT_LAND_REQUIRED);
416 /* skip tile if it already has the right type */
417 if (IsTileType(tile, MP_WATER) && (wc == WATER_CLASS_SEA && IsSea(tile) || (wc == WATER_CLASS_CANAL && IsCanal(tile)) || (wc == WATER_CLASS_RIVER && IsRiver(tile)))) continue;
419 bool water = IsWaterTile(tile);
420 ret = DoCommand(tile, 0, 0, flags | DC_FORCE_CLEAR_TILE, CMD_LANDSCAPE_CLEAR);
421 if (ret.Failed()) return ret;
423 if (!water) cost.AddCost(ret);
425 if (flags & DC_EXEC) {
426 switch (wc) {
427 case WATER_CLASS_RIVER:
428 MakeRiver(tile, Random());
429 if (_game_mode == GM_EDITOR) {
430 TileIndex tile2 = tile;
431 CircularTileSearch(&tile2, 5, RiverModifyDesertZone, nullptr);
433 break;
435 case WATER_CLASS_SEA:
436 if (TileHeight(tile) == 0) {
437 MakeSea(tile);
438 break;
440 FALLTHROUGH;
442 default:
443 MakeCanal(tile, _current_company, Random());
444 if (Company::IsValidID(_current_company)) {
445 Company::Get(_current_company)->infrastructure.water++;
446 DirtyCompanyInfrastructureWindows(_current_company);
448 break;
450 MarkTileDirtyByTile(tile);
451 MarkCanalsAndRiversAroundDirty(tile);
454 cost.AddCost(_price[PR_BUILD_CANAL]);
457 if (cost.GetCost() == 0) {
458 return CommandError(STR_ERROR_ALREADY_BUILT);
459 } else {
460 return cost;
464 static CommandCost ClearTile_Water(TileIndex tile, DoCommandFlag flags)
466 switch (GetWaterTileType(tile)) {
467 case WATER_TILE_CLEAR: {
468 if (flags & DC_NO_WATER) return CommandError(STR_ERROR_CAN_T_BUILD_ON_WATER);
470 Money base_cost = IsCanal(tile) ? _price[PR_CLEAR_CANAL] : _price[PR_CLEAR_WATER];
471 /* Make sure freeform edges are allowed or it's not an edge tile. */
472 if (!_settings_game.construction.freeform_edges && (!IsInsideMM(TileX(tile), 1, MapMaxX() - 1) ||
473 !IsInsideMM(TileY(tile), 1, MapMaxY() - 1))) {
474 return CommandError(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP);
477 /* Make sure no vehicle is on the tile */
478 CommandCost ret = EnsureNoVehicleOnGround(tile);
479 if (ret.Failed()) return ret;
481 Owner owner = GetTileOwner(tile);
482 if (owner != OWNER_WATER && owner != OWNER_NONE) {
483 CommandCost ret = CheckTileOwnership(tile);
484 if (ret.Failed()) return ret;
487 if (flags & DC_EXEC) {
488 if (IsCanal(tile) && Company::IsValidID(owner)) {
489 Company::Get(owner)->infrastructure.water--;
490 DirtyCompanyInfrastructureWindows(owner);
492 DoClearSquare(tile);
493 MarkCanalsAndRiversAroundDirty(tile);
496 return CommandCost(EXPENSES_CONSTRUCTION, base_cost);
499 case WATER_TILE_COAST: {
500 Slope slope = GetTileSlope(tile);
502 /* Make sure no vehicle is on the tile */
503 CommandCost ret = EnsureNoVehicleOnGround(tile);
504 if (ret.Failed()) return ret;
506 if (flags & DC_EXEC) {
507 DoClearSquare(tile);
508 MarkCanalsAndRiversAroundDirty(tile);
510 if (IsSlopeWithOneCornerRaised(slope)) {
511 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_WATER]);
512 } else {
513 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_ROUGH]);
517 case WATER_TILE_LOCK: {
518 static const TileIndexDiffC _lock_tomiddle_offs[][DIAGDIR_END] = {
519 /* NE SE SW NW */
520 { { 0, 0}, {0, 0}, { 0, 0}, {0, 0} }, // LOCK_PART_MIDDLE
521 { {-1, 0}, {0, 1}, { 1, 0}, {0, -1} }, // LOCK_PART_LOWER
522 { { 1, 0}, {0, -1}, {-1, 0}, {0, 1} }, // LOCK_PART_UPPER
525 if (flags & DC_AUTO) return CommandError(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
526 if (_current_company == OWNER_WATER) return CommandError();
527 /* move to the middle tile.. */
528 return RemoveLock(tile + ToTileIndexDiff(_lock_tomiddle_offs[GetLockPart(tile)][GetLockDirection(tile)]), flags);
531 case WATER_TILE_DEPOT:
532 if (flags & DC_AUTO) return CommandError(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
533 return RemoveShipDepot(tile, flags);
535 default:
536 NOT_REACHED();
541 * return true if a tile is a water tile wrt. a certain direction.
543 * @param tile The tile of interest.
544 * @param from The direction of interest.
545 * @return true iff the tile is water in the view of 'from'.
548 bool IsWateredTile(TileIndex tile, Direction from)
550 switch (GetTileType(tile)) {
551 case MP_WATER:
552 switch (GetWaterTileType(tile)) {
553 default: NOT_REACHED();
554 case WATER_TILE_DEPOT: case WATER_TILE_CLEAR: return true;
555 case WATER_TILE_LOCK: return DiagDirToAxis(GetLockDirection(tile)) == DiagDirToAxis(DirToDiagDir(from));
557 case WATER_TILE_COAST:
558 switch (GetTileSlope(tile)) {
559 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
560 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
561 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
562 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
563 default: return false;
567 case MP_RAILWAY:
568 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
569 assert(IsPlainRail(tile));
570 switch (GetTileSlope(tile)) {
571 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
572 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
573 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
574 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
575 default: return false;
578 return false;
580 case MP_STATION:
581 if (IsOilRig(tile)) {
582 /* Do not draw waterborders inside of industries.
583 * Note: There is no easy way to detect the industry of an oilrig tile. */
584 TileIndex src_tile = tile + TileOffsByDir(from);
585 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
586 (IsTileType(src_tile, MP_INDUSTRY))) return true;
588 return IsTileOnWater(tile);
590 return (IsDock(tile) && IsTileFlat(tile)) || IsBuoy(tile) || IsSeaplanePort(tile);
592 case MP_INDUSTRY: {
593 /* Do not draw waterborders inside of industries.
594 * Note: There is no easy way to detect the industry of an oilrig tile. */
595 TileIndex src_tile = tile + TileOffsByDir(from);
596 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
597 (IsTileType(src_tile, MP_INDUSTRY) && GetIndustryIndex(src_tile) == GetIndustryIndex(tile))) return true;
599 return IsTileOnWater(tile);
602 case MP_OBJECT: return IsTileOnWater(tile);
604 case MP_TUNNELBRIDGE: return GetTunnelBridgeTransportType(tile) == TRANSPORT_WATER && ReverseDiagDir(GetTunnelBridgeDirection(tile)) == DirToDiagDir(from);
606 case MP_VOID: return true; // consider map border as water, esp. for rivers
608 default: return false;
613 * Draw a water sprite, potentially with a NewGRF-modified sprite offset.
614 * @param base Sprite base.
615 * @param offset Sprite offset.
616 * @param feature The type of sprite that is drawn.
617 * @param tile Tile index to draw.
619 static void DrawWaterSprite(SpriteID base, uint offset, CanalFeature feature, TileIndex tile)
621 if (base != SPR_FLAT_WATER_TILE) {
622 /* Only call offset callback if the sprite is NewGRF-provided. */
623 offset = GetCanalSpriteOffset(feature, tile, offset);
625 DrawGroundSprite(base + offset, PAL_NONE);
629 * Draw canal or river edges.
630 * @param canal True if canal edges should be drawn, false for river edges.
631 * @param offset Sprite offset.
632 * @param tile Tile to draw.
634 static void DrawWaterEdges(bool canal, uint offset, TileIndex tile)
636 CanalFeature feature;
637 SpriteID base = 0;
638 if (canal) {
639 feature = CF_DIKES;
640 base = GetCanalSprite(CF_DIKES, tile);
641 if (base == 0) base = SPR_CANAL_DIKES_BASE;
642 } else {
643 feature = CF_RIVER_EDGE;
644 base = GetCanalSprite(CF_RIVER_EDGE, tile);
645 if (base == 0) return; // Don't draw if no sprites provided.
648 uint wa;
650 /* determine the edges around with water. */
651 wa = IsWateredTile(TILE_ADDXY(tile, -1, 0), DIR_SW) << 0;
652 wa += IsWateredTile(TILE_ADDXY(tile, 0, 1), DIR_NW) << 1;
653 wa += IsWateredTile(TILE_ADDXY(tile, 1, 0), DIR_NE) << 2;
654 wa += IsWateredTile(TILE_ADDXY(tile, 0, -1), DIR_SE) << 3;
656 if (!(wa & 1)) DrawWaterSprite(base, offset, feature, tile);
657 if (!(wa & 2)) DrawWaterSprite(base, offset + 1, feature, tile);
658 if (!(wa & 4)) DrawWaterSprite(base, offset + 2, feature, tile);
659 if (!(wa & 8)) DrawWaterSprite(base, offset + 3, feature, tile);
661 /* right corner */
662 switch (wa & 0x03) {
663 case 0: DrawWaterSprite(base, offset + 4, feature, tile); break;
664 case 3: if (!IsWateredTile(TILE_ADDXY(tile, -1, 1), DIR_W)) DrawWaterSprite(base, offset + 8, feature, tile); break;
667 /* bottom corner */
668 switch (wa & 0x06) {
669 case 0: DrawWaterSprite(base, offset + 5, feature, tile); break;
670 case 6: if (!IsWateredTile(TILE_ADDXY(tile, 1, 1), DIR_N)) DrawWaterSprite(base, offset + 9, feature, tile); break;
673 /* left corner */
674 switch (wa & 0x0C) {
675 case 0: DrawWaterSprite(base, offset + 6, feature, tile); break;
676 case 12: if (!IsWateredTile(TILE_ADDXY(tile, 1, -1), DIR_E)) DrawWaterSprite(base, offset + 10, feature, tile); break;
679 /* upper corner */
680 switch (wa & 0x09) {
681 case 0: DrawWaterSprite(base, offset + 7, feature, tile); break;
682 case 9: if (!IsWateredTile(TILE_ADDXY(tile, -1, -1), DIR_S)) DrawWaterSprite(base, offset + 11, feature, tile); break;
686 /** Draw a plain sea water tile with no edges */
687 static void DrawSeaWater(TileIndex tile)
689 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
692 /** draw a canal styled water tile with dikes around */
693 static void DrawCanalWater(TileIndex tile)
695 SpriteID image = SPR_FLAT_WATER_TILE;
696 if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
697 /* First water slope sprite is flat water. */
698 image = GetCanalSprite(CF_WATERSLOPE, tile);
699 if (image == 0) image = SPR_FLAT_WATER_TILE;
701 DrawWaterSprite(image, 0, CF_WATERSLOPE, tile);
703 DrawWaterEdges(true, 0, tile);
706 #include "table/water_land.h"
709 * Draw a build sprite sequence for water tiles.
710 * If buildings are invisible, nothing will be drawn.
711 * @param ti Tile info.
712 * @param dtss Sprite sequence to draw.
713 * @param base Base sprite.
714 * @param offset Additional sprite offset.
715 * @param palette Palette to use.
717 static void DrawWaterTileStruct(const TileInfo *ti, const DrawTileSeqStruct *dtss, SpriteID base, uint offset, PaletteID palette, CanalFeature feature)
719 /* Don't draw if buildings are invisible. */
720 if (IsInvisibilitySet(TO_BUILDINGS)) return;
722 for (; !dtss->IsTerminator(); dtss++) {
723 uint tile_offs = offset + dtss->image.sprite;
724 if (feature < CF_END) tile_offs = GetCanalSpriteOffset(feature, ti->tile, tile_offs);
725 AddSortableSpriteToDraw(base + tile_offs, palette,
726 ti->x + dtss->delta_x, ti->y + dtss->delta_y,
727 dtss->size_x, dtss->size_y,
728 dtss->size_z, ti->z + dtss->delta_z,
729 IsTransparencySet(TO_BUILDINGS));
733 /** Draw a lock tile. */
734 static void DrawWaterLock(const TileInfo *ti)
736 int part = GetLockPart(ti->tile);
737 const DrawTileSprites &dts = _lock_display_data[part][GetLockDirection(ti->tile)];
739 /* Draw ground sprite. */
740 SpriteID image = dts.ground.sprite;
742 SpriteID water_base = GetCanalSprite(CF_WATERSLOPE, ti->tile);
743 if (water_base == 0) {
744 /* Use default sprites. */
745 water_base = SPR_CANALS_BASE;
746 } else if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
747 /* NewGRF supplies a flat sprite as first sprite. */
748 if (image == SPR_FLAT_WATER_TILE) {
749 image = water_base;
750 } else {
751 image++;
755 if (image < 5) image += water_base;
756 DrawGroundSprite(image, PAL_NONE);
758 /* Draw structures. */
759 uint zoffs = 0;
760 SpriteID base = GetCanalSprite(CF_LOCKS, ti->tile);
762 if (base == 0) {
763 /* If no custom graphics, use defaults. */
764 base = SPR_LOCK_BASE;
765 uint8 z_threshold = part == LOCK_PART_UPPER ? 8 : 0;
766 zoffs = ti->z > z_threshold ? 24 : 0;
769 DrawWaterTileStruct(ti, dts.seq, base, zoffs, PAL_NONE, CF_LOCKS);
772 /** Draw a ship depot tile. */
773 static void DrawWaterDepot(const TileInfo *ti)
775 DrawWaterClassGround(ti);
776 DrawWaterTileStruct(ti, _shipdepot_display_data[GetShipDepotAxis(ti->tile)][GetShipDepotPart(ti->tile)].seq, 0, 0, COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile)), CF_END);
779 static void DrawRiverWater(const TileInfo *ti)
781 SpriteID image = SPR_FLAT_WATER_TILE;
782 uint offset = 0;
783 uint edges_offset = 0;
785 if (ti->tileh != SLOPE_FLAT || HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
786 image = GetCanalSprite(CF_RIVER_SLOPE, ti->tile);
787 if (image == 0) {
788 switch (ti->tileh) {
789 case SLOPE_NW: image = SPR_WATER_SLOPE_Y_DOWN; break;
790 case SLOPE_SW: image = SPR_WATER_SLOPE_X_UP; break;
791 case SLOPE_SE: image = SPR_WATER_SLOPE_Y_UP; break;
792 case SLOPE_NE: image = SPR_WATER_SLOPE_X_DOWN; break;
793 default: image = SPR_FLAT_WATER_TILE; break;
795 } else {
796 /* Flag bit 0 indicates that the first sprite is flat water. */
797 offset = HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE) ? 1 : 0;
799 switch (ti->tileh) {
800 case SLOPE_SE: edges_offset += 12; break;
801 case SLOPE_NE: offset += 1; edges_offset += 24; break;
802 case SLOPE_SW: offset += 2; edges_offset += 36; break;
803 case SLOPE_NW: offset += 3; edges_offset += 48; break;
804 default: offset = 0; break;
807 offset = GetCanalSpriteOffset(CF_RIVER_SLOPE, ti->tile, offset);
811 DrawGroundSprite(image + offset, PAL_NONE);
813 /* Draw river edges if available. */
814 DrawWaterEdges(false, edges_offset, ti->tile);
817 void DrawShoreTile(Slope tileh)
819 /* Converts the enum Slope into an offset based on SPR_SHORE_BASE.
820 * This allows to calculate the proper sprite to display for this Slope */
821 static const byte tileh_to_shoresprite[32] = {
822 0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0,
823 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 15, 0,
826 assert(!IsHalftileSlope(tileh)); // Halftile slopes need to get handled earlier.
827 assert(tileh != SLOPE_FLAT); // Shore is never flat
829 assert((tileh != SLOPE_EW) && (tileh != SLOPE_NS)); // No suitable sprites for current flooding behaviour
831 DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[tileh], PAL_NONE);
834 void DrawWaterClassGround(const TileInfo *ti)
836 switch (GetWaterClass(ti->tile)) {
837 case WATER_CLASS_SEA: DrawSeaWater(ti->tile); break;
838 case WATER_CLASS_CANAL: DrawCanalWater(ti->tile); break;
839 case WATER_CLASS_RIVER: DrawRiverWater(ti); break;
840 default: NOT_REACHED();
844 static void DrawTile_Water(TileInfo *ti)
846 switch (GetWaterTileType(ti->tile)) {
847 case WATER_TILE_CLEAR:
848 DrawWaterClassGround(ti);
849 DrawBridgeMiddle(ti);
850 break;
852 case WATER_TILE_COAST: {
853 DrawShoreTile(ti->tileh);
854 DrawBridgeMiddle(ti);
855 break;
858 case WATER_TILE_LOCK:
859 DrawWaterLock(ti);
860 break;
862 case WATER_TILE_DEPOT:
863 DrawWaterDepot(ti);
864 break;
867 DrawOverlay(ti, MP_WATER);
870 void DrawShipDepotSprite(int x, int y, Axis axis, DepotPart part)
872 const DrawTileSprites &dts = _shipdepot_display_data[axis][part];
874 DrawSprite(dts.ground.sprite, dts.ground.pal, x, y);
875 DrawOrigTileSeqInGUI(x, y, &dts, COMPANY_SPRITE_COLOUR(_local_company));
879 static int GetSlopePixelZ_Water(TileIndex tile, uint x, uint y)
881 int z;
882 Slope tileh = GetTilePixelSlope(tile, &z);
884 return z + GetPartialPixelZ(x & 0xF, y & 0xF, tileh);
887 static Foundation GetFoundation_Water(TileIndex tile, Slope tileh)
889 return FOUNDATION_NONE;
892 static void GetTileDesc_Water(TileIndex tile, TileDesc *td)
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;
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;
912 td->owner[0] = GetTileOwner(tile);
916 * Handle the flooding of a vehicle. This sets the vehicle state to crashed,
917 * creates a newsitem and dirties the necessary windows.
918 * @param v The vehicle to flood.
920 static void FloodVehicle(Vehicle *v)
922 uint pass = v->Crash(true);
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);
933 * Flood a vehicle if we are allowed to flood it, i.e. when it is on the ground.
934 * @param v The vehicle to test for flooding.
935 * @param data The z of level to flood.
936 * @return nullptr as we always want to remove everything.
938 static Vehicle *FloodVehicleProc(Vehicle *v, void *data)
940 if ((v->vehstatus & VS_CRASHED) != 0) return nullptr;
942 switch (v->type) {
943 default: break;
945 case VEH_AIRCRAFT: {
946 if (!IsAirportTile(v->tile) || GetTileMaxZ(v->tile) != 0) break;
947 if (v->subtype == AIR_SHADOW) break;
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;
955 FloodVehicle(v);
956 break;
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;
968 return nullptr;
972 * Finds a vehicle to flood.
973 * It does not find vehicles that are already crashed on bridges, i.e. flooded.
974 * @param tile the tile where to find a vehicle to flood
976 static void FloodVehicles(TileIndex tile)
978 int z = 0;
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);
986 /* No vehicle could be flooded on this airport anymore */
987 return;
990 if (!IsBridgeTile(tile)) {
991 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
992 return;
995 TileIndex end = GetOtherBridgeEnd(tile);
996 z = GetBridgePixelHeight(tile);
998 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
999 FindVehicleOnPos(end, &z, &FloodVehicleProc);
1003 * Returns the behaviour of a tile during flooding.
1005 * @return Behaviour of the tile
1007 FloodingBehaviour GetFloodingBehaviour(TileIndex tile)
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
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);
1020 FALLTHROUGH;
1021 case MP_STATION:
1022 case MP_INDUSTRY:
1023 case MP_OBJECT:
1024 return (GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE;
1026 case MP_RAILWAY:
1027 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
1028 return (IsSlopeWithOneCornerRaised(GetTileSlope(tile)) ? FLOOD_ACTIVE : FLOOD_DRYUP);
1030 return FLOOD_NONE;
1032 case MP_TREES:
1033 return (GetTreeGround(tile) == TREE_GROUND_SHORE ? FLOOD_DRYUP : FLOOD_NONE);
1035 default:
1036 return FLOOD_NONE;
1041 * Floods a tile.
1043 void DoFloodTile(TileIndex target)
1045 assert(!IsTileType(target, MP_WATER));
1047 bool flooded = false; // Will be set to true if something is changed.
1049 Backup<CompanyByte> cur_company(_current_company, OWNER_WATER, FILE_LINE);
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;
1062 case MP_TREES:
1063 if (!IsSlopeWithOneCornerRaised(tileh)) {
1064 SetTreeGroundDensity(target, TREE_GROUND_SHORE, 3);
1065 MarkTileDirtyByTile(target);
1066 flooded = true;
1067 break;
1069 FALLTHROUGH;
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;
1077 break;
1079 default:
1080 break;
1082 } else {
1083 /* Flood vehicles */
1084 FloodVehicles(target);
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;
1094 if (flooded) {
1095 /* Mark surrounding canal tiles dirty too to avoid glitches */
1096 MarkCanalsAndRiversAroundDirty(target);
1098 /* update signals if needed */
1099 UpdateSignalsInBuffer();
1102 cur_company.Restore();
1106 * Drys a tile up.
1108 static void DoDryUp(TileIndex tile)
1110 Backup<CompanyByte> cur_company(_current_company, OWNER_WATER, FILE_LINE);
1112 switch (GetTileType(tile)) {
1113 case MP_RAILWAY:
1114 assert(IsPlainRail(tile));
1115 assert(GetRailGroundType(tile) == RAIL_GROUND_WATER);
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();
1125 SetRailGroundType(tile, new_ground);
1126 MarkTileDirtyByTile(tile);
1127 break;
1129 case MP_TREES:
1130 SetTreeGroundDensity(tile, TREE_GROUND_GRASS, 3);
1131 MarkTileDirtyByTile(tile, ZOOM_LVL_DRAW_MAP);
1132 break;
1134 case MP_WATER:
1135 assert(IsCoast(tile));
1137 if (DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
1138 MakeClear(tile, CLEAR_GRASS, 3);
1139 MarkTileDirtyByTile(tile);
1141 break;
1143 default: NOT_REACHED();
1146 cur_company.Restore();
1150 * Let a water tile floods its diagonal adjoining tiles
1151 * called from tunnelbridge_cmd, and by TileLoop_Industry() and TileLoop_Track()
1153 * @param tile the water/shore tile that floods
1155 void TileLoop_Water(TileIndex tile)
1157 if (IsTileType(tile, MP_WATER)) AmbientSoundEffect(tile);
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;
1167 /* TREE_GROUND_SHORE is the sign of a previous flood. */
1168 if (IsTileType(dest, MP_TREES) && GetTreeGround(dest) == TREE_GROUND_SHORE) continue;
1170 /* Prevent station on water flooding */
1171 if (IsTileType(dest, MP_STATION) && GetWaterClass(dest) != WATER_CLASS_INVALID) continue;
1173 int z_dest;
1174 Slope slope_dest = GetFoundationSlope(dest, &z_dest) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
1175 if (z_dest > 0) continue;
1177 if (!HasBit(_flood_from_dirs[slope_dest], ReverseDir(dir))) continue;
1179 DoFloodTile(dest);
1181 break;
1183 case FLOOD_DRYUP: {
1184 Slope slope_here = GetFoundationSlope(tile) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
1185 uint dir;
1186 FOR_EACH_SET_BIT(dir, _flood_from_dirs[slope_here]) {
1187 TileIndex dest = tile + TileOffsByDir((Direction)dir);
1188 if (!IsValidTile(dest)) continue;
1190 FloodingBehaviour dest_behaviour = GetFloodingBehaviour(dest);
1191 if ((dest_behaviour == FLOOD_ACTIVE) || (dest_behaviour == FLOOD_PASSIVE)) return;
1193 DoDryUp(tile);
1194 break;
1197 default: return;
1201 void ConvertGroundTilesIntoWaterTiles()
1203 int z;
1205 for (TileIndex tile = 0; tile < MapSize(); ++tile) {
1206 Slope slope = GetTileSlope(tile, &z);
1207 if (IsTileType(tile, MP_CLEAR) && z == 0) {
1208 /* Make both water for tiles at level 0
1209 * and make shore, as that looks much better
1210 * during the generation. */
1211 switch (slope) {
1212 case SLOPE_FLAT:
1213 MakeSea(tile);
1214 break;
1216 case SLOPE_N:
1217 case SLOPE_E:
1218 case SLOPE_S:
1219 case SLOPE_W:
1220 MakeShore(tile);
1221 break;
1223 default:
1224 uint dir;
1225 FOR_EACH_SET_BIT(dir, _flood_from_dirs[slope & ~SLOPE_STEEP]) {
1226 TileIndex dest = TILE_ADD(tile, TileOffsByDir((Direction)dir));
1227 Slope slope_dest = GetTileSlope(dest) & ~SLOPE_STEEP;
1228 if (slope_dest == SLOPE_FLAT || IsSlopeWithOneCornerRaised(slope_dest)) {
1229 MakeShore(tile);
1230 break;
1233 break;
1239 static TrackStatus GetTileTrackStatus_Water(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
1241 static const byte coast_tracks[] = {0, 32, 4, 0, 16, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0};
1243 TrackBits ts;
1245 if (mode != TRANSPORT_WATER) return 0;
1247 switch (GetWaterTileType(tile)) {
1248 case WATER_TILE_CLEAR: ts = IsTileFlat(tile) ? TRACK_BIT_ALL : TRACK_BIT_NONE; break;
1249 case WATER_TILE_COAST: ts = (TrackBits)coast_tracks[GetTileSlope(tile) & 0xF]; break;
1250 case WATER_TILE_LOCK: ts = DiagDirToDiagTrackBits(GetLockDirection(tile)); break;
1251 case WATER_TILE_DEPOT: ts = AxisToTrackBits(GetShipDepotAxis(tile)); break;
1252 default: return 0;
1254 if (TileX(tile) == 0) {
1255 /* NE border: remove tracks that connects NE tile edge */
1256 ts &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
1258 if (TileY(tile) == 0) {
1259 /* NW border: remove tracks that connects NW tile edge */
1260 ts &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
1262 return CombineTrackStatus(TrackBitsToTrackdirBits(ts), TRACKDIR_BIT_NONE);
1265 static bool ClickTile_Water(TileIndex tile)
1267 if (GetWaterTileType(tile) == WATER_TILE_DEPOT) {
1268 ShowDepotWindow(GetShipDepotNorthTile(tile), VEH_SHIP);
1269 return true;
1271 return false;
1274 static void ChangeTileOwner_Water(TileIndex tile, Owner old_owner, Owner new_owner)
1276 if (!IsTileOwner(tile, old_owner)) return;
1278 bool is_lock_middle = IsLock(tile) && GetLockPart(tile) == LOCK_PART_MIDDLE;
1280 /* No need to dirty company windows here, we'll redraw the whole screen anyway. */
1281 if (is_lock_middle) Company::Get(old_owner)->infrastructure.water -= 3 * LOCK_DEPOT_TILE_FACTOR; // Lock has three parts.
1282 if (new_owner != INVALID_OWNER) {
1283 if (is_lock_middle) Company::Get(new_owner)->infrastructure.water += 3 * LOCK_DEPOT_TILE_FACTOR; // Lock has three parts.
1284 /* Only subtract from the old owner here if the new owner is valid,
1285 * otherwise we clear ship depots and canal water below. */
1286 if (GetWaterClass(tile) == WATER_CLASS_CANAL && !is_lock_middle) {
1287 Company::Get(old_owner)->infrastructure.water--;
1288 Company::Get(new_owner)->infrastructure.water++;
1290 if (IsShipDepot(tile)) {
1291 Company::Get(old_owner)->infrastructure.water -= LOCK_DEPOT_TILE_FACTOR;
1292 Company::Get(new_owner)->infrastructure.water += LOCK_DEPOT_TILE_FACTOR;
1295 SetTileOwner(tile, new_owner);
1296 return;
1299 /* Remove depot */
1300 if (IsShipDepot(tile)) DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
1302 /* Set owner of canals and locks ... and also canal under dock there was before.
1303 * Check if the new owner after removing depot isn't OWNER_WATER. */
1304 if (IsTileOwner(tile, old_owner)) {
1305 if (GetWaterClass(tile) == WATER_CLASS_CANAL && !is_lock_middle) Company::Get(old_owner)->infrastructure.water--;
1306 SetTileOwner(tile, OWNER_NONE);
1310 static VehicleEnterTileStatus VehicleEnter_Water(Vehicle *v, TileIndex tile, int x, int y)
1312 return VETSB_CONTINUE;
1315 static CommandCost TerraformTile_Water(TileIndex tile, DoCommandFlag flags, int z_new, Slope tileh_new)
1317 /* Canals can't be terraformed */
1318 if (IsWaterTile(tile) && IsCanal(tile)) return CommandError(STR_ERROR_MUST_DEMOLISH_CANAL_FIRST);
1320 return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
1324 extern const TileTypeProcs _tile_type_water_procs = {
1325 DrawTile_Water, // draw_tile_proc
1326 GetSlopePixelZ_Water, // get_slope_z_proc
1327 ClearTile_Water, // clear_tile_proc
1328 nullptr, // add_accepted_cargo_proc
1329 GetTileDesc_Water, // get_tile_desc_proc
1330 GetTileTrackStatus_Water, // get_tile_track_status_proc
1331 ClickTile_Water, // click_tile_proc
1332 nullptr, // animate_tile_proc
1333 TileLoop_Water, // tile_loop_proc
1334 ChangeTileOwner_Water, // change_tile_owner_proc
1335 nullptr, // add_produced_cargo_proc
1336 VehicleEnter_Water, // vehicle_enter_tile_proc
1337 GetFoundation_Water, // get_foundation_proc
1338 TerraformTile_Water, // terraform_tile_proc