Update: Translations from eints
[openttd-github.git] / src / water_cmd.cpp
blob3e273797949379c1b27a21af85cac51168b35ca4
1 /*
2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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/>.
6 */
8 /** @file water_cmd.cpp Handling of water tiles. */
10 #include "stdafx.h"
11 #include "landscape.h"
12 #include "viewport_func.h"
13 #include "command_func.h"
14 #include "town.h"
15 #include "news_func.h"
16 #include "depot_base.h"
17 #include "depot_func.h"
18 #include "water.h"
19 #include "industry_map.h"
20 #include "newgrf_canal.h"
21 #include "strings_func.h"
22 #include "vehicle_func.h"
23 #include "sound_func.h"
24 #include "company_func.h"
25 #include "clear_map.h"
26 #include "tree_map.h"
27 #include "aircraft.h"
28 #include "effectvehicle_func.h"
29 #include "tunnelbridge_map.h"
30 #include "station_base.h"
31 #include "ai/ai.hpp"
32 #include "game/game.hpp"
33 #include "core/random_func.hpp"
34 #include "core/backup_type.hpp"
35 #include "timer/timer_game_calendar.h"
36 #include "company_base.h"
37 #include "company_gui.h"
38 #include "newgrf_generic.h"
39 #include "industry.h"
40 #include "water_cmd.h"
41 #include "landscape_cmd.h"
42 #include "pathfinder/water_regions.h"
44 #include "table/strings.h"
46 #include "safeguards.h"
48 /**
49 * Describes from which directions a specific slope can be flooded (if the tile is floodable at all).
51 static const uint8_t _flood_from_dirs[] = {
52 (1 << DIR_NW) | (1 << DIR_SW) | (1 << DIR_SE) | (1 << DIR_NE), // SLOPE_FLAT
53 (1 << DIR_NE) | (1 << DIR_SE), // SLOPE_W
54 (1 << DIR_NW) | (1 << DIR_NE), // SLOPE_S
55 (1 << DIR_NE), // SLOPE_SW
56 (1 << DIR_NW) | (1 << DIR_SW), // SLOPE_E
57 0, // SLOPE_EW
58 (1 << DIR_NW), // SLOPE_SE
59 (1 << DIR_N ) | (1 << DIR_NW) | (1 << DIR_NE), // SLOPE_WSE, SLOPE_STEEP_S
60 (1 << DIR_SW) | (1 << DIR_SE), // SLOPE_N
61 (1 << DIR_SE), // SLOPE_NW
62 0, // SLOPE_NS
63 (1 << DIR_E ) | (1 << DIR_NE) | (1 << DIR_SE), // SLOPE_NWS, SLOPE_STEEP_W
64 (1 << DIR_SW), // SLOPE_NE
65 (1 << DIR_S ) | (1 << DIR_SW) | (1 << DIR_SE), // SLOPE_ENW, SLOPE_STEEP_N
66 (1 << DIR_W ) | (1 << DIR_SW) | (1 << DIR_NW), // SLOPE_SEN, SLOPE_STEEP_E
69 /**
70 * Marks tile dirty if it is a canal or river tile.
71 * Called to avoid glitches when flooding tiles next to canal tile.
73 * @param tile tile to check
75 static inline void MarkTileDirtyIfCanalOrRiver(TileIndex tile)
77 if (IsValidTile(tile) && IsTileType(tile, MP_WATER) && (IsCanal(tile) || IsRiver(tile))) MarkTileDirtyByTile(tile);
80 /**
81 * Marks the tiles around a tile as dirty, if they are canals or rivers.
83 * @param tile The center of the tile where all other tiles are marked as dirty
84 * @ingroup dirty
86 static void MarkCanalsAndRiversAroundDirty(TileIndex tile)
88 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
89 MarkTileDirtyIfCanalOrRiver(tile + TileOffsByDir(dir));
94 /**
95 * Build a ship depot.
96 * @param flags type of operation
97 * @param tile tile where ship depot is built
98 * @param axis depot orientation (Axis)
99 * @return the cost of this operation or an error
101 CommandCost CmdBuildShipDepot(DoCommandFlag flags, TileIndex tile, Axis axis)
103 if (!IsValidAxis(axis)) return CMD_ERROR;
104 TileIndex tile2 = tile + TileOffsByAxis(axis);
106 if (!HasTileWaterGround(tile) || !HasTileWaterGround(tile2)) {
107 return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER);
110 if (IsBridgeAbove(tile) || IsBridgeAbove(tile2)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
112 if (!IsTileFlat(tile) || !IsTileFlat(tile2)) {
113 /* Prevent depots on rapids */
114 return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
117 if (!Depot::CanAllocateItem()) return CMD_ERROR;
119 WaterClass wc1 = GetWaterClass(tile);
120 WaterClass wc2 = GetWaterClass(tile2);
121 CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_DEPOT_SHIP]);
123 bool add_cost = !IsWaterTile(tile);
124 CommandCost ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags | DC_AUTO, tile);
125 if (ret.Failed()) return ret;
126 if (add_cost) {
127 cost.AddCost(ret);
129 add_cost = !IsWaterTile(tile2);
130 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags | DC_AUTO, tile2);
131 if (ret.Failed()) return ret;
132 if (add_cost) {
133 cost.AddCost(ret);
136 if (flags & DC_EXEC) {
137 Depot *depot = new Depot(tile);
138 depot->build_date = TimerGameCalendar::date;
140 uint new_water_infra = 2 * LOCK_DEPOT_TILE_FACTOR;
141 /* Update infrastructure counts after the tile clears earlier.
142 * Clearing object tiles may result in water tiles which are already accounted for in the water infrastructure total.
143 * See: MakeWaterKeepingClass() */
144 if (wc1 == WATER_CLASS_CANAL && !(HasTileWaterClass(tile) && GetWaterClass(tile) == WATER_CLASS_CANAL && IsTileOwner(tile, _current_company))) new_water_infra++;
145 if (wc2 == WATER_CLASS_CANAL && !(HasTileWaterClass(tile2) && GetWaterClass(tile2) == WATER_CLASS_CANAL && IsTileOwner(tile2, _current_company))) new_water_infra++;
147 Company::Get(_current_company)->infrastructure.water += new_water_infra;
148 DirtyCompanyInfrastructureWindows(_current_company);
150 MakeShipDepot(tile, _current_company, depot->index, DEPOT_PART_NORTH, axis, wc1);
151 MakeShipDepot(tile2, _current_company, depot->index, DEPOT_PART_SOUTH, axis, wc2);
152 CheckForDockingTile(tile);
153 CheckForDockingTile(tile2);
154 MarkTileDirtyByTile(tile);
155 MarkTileDirtyByTile(tile2);
156 MakeDefaultName(depot);
159 return cost;
162 bool IsPossibleDockingTile(Tile t)
164 assert(IsValidTile(t));
165 switch (GetTileType(t)) {
166 case MP_WATER:
167 if (IsLock(t) && GetLockPart(t) == LOCK_PART_MIDDLE) return false;
168 [[fallthrough]];
169 case MP_RAILWAY:
170 case MP_STATION:
171 case MP_TUNNELBRIDGE:
172 return TrackStatusToTrackBits(GetTileTrackStatus(t, TRANSPORT_WATER, 0)) != TRACK_BIT_NONE;
174 default:
175 return false;
180 * Mark the supplied tile as a docking tile if it is suitable for docking.
181 * Tiles surrounding the tile are tested to be docks with correct orientation.
182 * @param t Tile to test.
184 void CheckForDockingTile(TileIndex t)
186 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
187 TileIndex tile = t + TileOffsByDiagDir(d);
188 if (!IsValidTile(tile)) continue;
190 if (IsDockTile(tile) && IsDockWaterPart(tile)) {
191 Station::GetByTile(tile)->docking_station.Add(t);
192 SetDockingTile(t, true);
194 if (IsTileType(tile, MP_INDUSTRY)) {
195 Station *st = Industry::GetByTile(tile)->neutral_station;
196 if (st != nullptr) {
197 st->docking_station.Add(t);
198 SetDockingTile(t, true);
201 if (IsTileType(tile, MP_STATION) && IsOilRig(tile)) {
202 Station::GetByTile(tile)->docking_station.Add(t);
203 SetDockingTile(t, true);
208 void MakeWaterKeepingClass(TileIndex tile, Owner o)
210 WaterClass wc = GetWaterClass(tile);
212 /* Autoslope might turn an originally canal or river tile into land */
213 auto [slope, z] = GetTileSlopeZ(tile);
215 if (slope != SLOPE_FLAT) {
216 if (wc == WATER_CLASS_CANAL) {
217 /* If we clear the canal, we have to remove it from the infrastructure count as well. */
218 Company *c = Company::GetIfValid(o);
219 if (c != nullptr) {
220 c->infrastructure.water--;
221 DirtyCompanyInfrastructureWindows(c->index);
223 /* Sloped canals are locks and no natural water remains whatever the slope direction */
224 wc = WATER_CLASS_INVALID;
227 /* Only river water should be restored on appropriate slopes. Other water would be invalid on slopes */
228 if (wc != WATER_CLASS_RIVER || GetInclinedSlopeDirection(slope) == INVALID_DIAGDIR) {
229 wc = WATER_CLASS_INVALID;
233 if (wc == WATER_CLASS_SEA && z > 0) {
234 /* Update company infrastructure count. */
235 Company *c = Company::GetIfValid(o);
236 if (c != nullptr) {
237 c->infrastructure.water++;
238 DirtyCompanyInfrastructureWindows(c->index);
241 wc = WATER_CLASS_CANAL;
244 /* Zero map array and terminate animation */
245 DoClearSquare(tile);
247 /* Maybe change to water */
248 switch (wc) {
249 case WATER_CLASS_SEA: MakeSea(tile); break;
250 case WATER_CLASS_CANAL: MakeCanal(tile, o, Random()); break;
251 case WATER_CLASS_RIVER: MakeRiver(tile, Random()); break;
252 default: break;
255 if (wc != WATER_CLASS_INVALID) CheckForDockingTile(tile);
256 MarkTileDirtyByTile(tile);
259 static CommandCost RemoveShipDepot(TileIndex tile, DoCommandFlag flags)
261 if (!IsShipDepot(tile)) return CMD_ERROR;
263 CommandCost ret = CheckTileOwnership(tile);
264 if (ret.Failed()) return ret;
266 TileIndex tile2 = GetOtherShipDepotTile(tile);
268 /* do not check for ship on tile when company goes bankrupt */
269 if (!(flags & DC_BANKRUPT)) {
270 ret = EnsureNoVehicleOnGround(tile);
271 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile2);
272 if (ret.Failed()) return ret;
275 bool do_clear = (flags & DC_FORCE_CLEAR_TILE) != 0;
277 if (flags & DC_EXEC) {
278 delete Depot::GetByTile(tile);
280 Company *c = Company::GetIfValid(GetTileOwner(tile));
281 if (c != nullptr) {
282 c->infrastructure.water -= 2 * LOCK_DEPOT_TILE_FACTOR;
283 if (do_clear && GetWaterClass(tile) == WATER_CLASS_CANAL) c->infrastructure.water--;
284 DirtyCompanyInfrastructureWindows(c->index);
287 if (!do_clear) MakeWaterKeepingClass(tile, GetTileOwner(tile));
288 MakeWaterKeepingClass(tile2, GetTileOwner(tile2));
291 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_SHIP]);
295 * Builds a lock.
296 * @param tile Central tile of the lock.
297 * @param dir Uphill direction.
298 * @param flags Operation to perform.
299 * @return The cost in case of success, or an error code if it failed.
301 static CommandCost DoBuildLock(TileIndex tile, DiagDirection dir, DoCommandFlag flags)
303 CommandCost cost(EXPENSES_CONSTRUCTION);
305 TileIndexDiff delta = TileOffsByDiagDir(dir);
306 CommandCost ret = EnsureNoVehicleOnGround(tile);
307 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta);
308 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta);
309 if (ret.Failed()) return ret;
311 /* middle tile */
312 WaterClass wc_middle = HasTileWaterGround(tile) ? GetWaterClass(tile) : WATER_CLASS_CANAL;
313 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
314 if (ret.Failed()) return ret;
315 cost.AddCost(ret);
317 /* lower tile */
318 if (!IsWaterTile(tile - delta)) {
319 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile - delta);
320 if (ret.Failed()) return ret;
321 cost.AddCost(ret);
322 cost.AddCost(_price[PR_BUILD_CANAL]);
324 if (!IsTileFlat(tile - delta)) {
325 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
327 WaterClass wc_lower = IsWaterTile(tile - delta) ? GetWaterClass(tile - delta) : WATER_CLASS_CANAL;
329 /* upper tile */
330 if (!IsWaterTile(tile + delta)) {
331 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile + delta);
332 if (ret.Failed()) return ret;
333 cost.AddCost(ret);
334 cost.AddCost(_price[PR_BUILD_CANAL]);
336 if (!IsTileFlat(tile + delta)) {
337 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
339 WaterClass wc_upper = IsWaterTile(tile + delta) ? GetWaterClass(tile + delta) : WATER_CLASS_CANAL;
341 if (IsBridgeAbove(tile) || IsBridgeAbove(tile - delta) || IsBridgeAbove(tile + delta)) {
342 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
345 if (flags & DC_EXEC) {
346 /* Update company infrastructure counts. */
347 Company *c = Company::GetIfValid(_current_company);
348 if (c != nullptr) {
349 /* Counts for the water. */
350 if (!IsWaterTile(tile - delta)) c->infrastructure.water++;
351 if (!IsWaterTile(tile + delta)) c->infrastructure.water++;
352 /* Count for the lock itself. */
353 c->infrastructure.water += 3 * LOCK_DEPOT_TILE_FACTOR; // Lock is three tiles.
354 DirtyCompanyInfrastructureWindows(_current_company);
357 MakeLock(tile, _current_company, dir, wc_lower, wc_upper, wc_middle);
358 CheckForDockingTile(tile - delta);
359 CheckForDockingTile(tile + delta);
360 MarkTileDirtyByTile(tile);
361 MarkTileDirtyByTile(tile - delta);
362 MarkTileDirtyByTile(tile + delta);
363 MarkCanalsAndRiversAroundDirty(tile - delta);
364 MarkCanalsAndRiversAroundDirty(tile + delta);
365 InvalidateWaterRegion(tile - delta);
366 InvalidateWaterRegion(tile + delta);
368 cost.AddCost(_price[PR_BUILD_LOCK]);
370 return cost;
374 * Remove a lock.
375 * @param tile Central tile of the lock.
376 * @param flags Operation to perform.
377 * @return The cost in case of success, or an error code if it failed.
379 static CommandCost RemoveLock(TileIndex tile, DoCommandFlag flags)
381 if (GetTileOwner(tile) != OWNER_NONE) {
382 CommandCost ret = CheckTileOwnership(tile);
383 if (ret.Failed()) return ret;
386 TileIndexDiff delta = TileOffsByDiagDir(GetLockDirection(tile));
388 /* make sure no vehicle is on the tile. */
389 CommandCost ret = EnsureNoVehicleOnGround(tile);
390 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta);
391 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta);
392 if (ret.Failed()) return ret;
394 if (flags & DC_EXEC) {
395 /* Remove middle part from company infrastructure count. */
396 Company *c = Company::GetIfValid(GetTileOwner(tile));
397 if (c != nullptr) {
398 c->infrastructure.water -= 3 * LOCK_DEPOT_TILE_FACTOR; // three parts of the lock.
399 DirtyCompanyInfrastructureWindows(c->index);
402 if (GetWaterClass(tile) == WATER_CLASS_RIVER) {
403 MakeRiver(tile, Random());
404 } else {
405 DoClearSquare(tile);
407 MakeWaterKeepingClass(tile + delta, GetTileOwner(tile + delta));
408 MakeWaterKeepingClass(tile - delta, GetTileOwner(tile - delta));
409 MarkCanalsAndRiversAroundDirty(tile);
410 MarkCanalsAndRiversAroundDirty(tile - delta);
411 MarkCanalsAndRiversAroundDirty(tile + delta);
414 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_LOCK]);
418 * Builds a lock.
419 * @param flags type of operation
420 * @param tile tile where to place the lock
421 * @return the cost of this operation or an error
423 CommandCost CmdBuildLock(DoCommandFlag flags, TileIndex tile)
425 DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile));
426 if (dir == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
428 return DoBuildLock(tile, dir, flags);
431 /** Callback to create non-desert around a river tile. */
432 static bool RiverModifyDesertZone(TileIndex tile, void *)
434 if (GetTropicZone(tile) == TROPICZONE_DESERT) SetTropicZone(tile, TROPICZONE_NORMAL);
435 return false;
439 * Make a river tile and remove desert directly around it.
440 * @param tile The tile to change into river and create non-desert around
442 void MakeRiverAndModifyDesertZoneAround(TileIndex tile)
444 MakeRiver(tile, Random());
445 MarkTileDirtyByTile(tile);
447 /* Remove desert directly around the river tile. */
448 CircularTileSearch(&tile, RIVER_OFFSET_DESERT_DISTANCE, RiverModifyDesertZone, nullptr);
452 * Build a piece of canal.
453 * @param flags type of operation
454 * @param tile end tile of stretch-dragging
455 * @param start_tile start tile of stretch-dragging
456 * @param wc waterclass to build. sea and river can only be built in scenario editor
457 * @param diagonal Whether to use the Orthogonal (0) or Diagonal (1) iterator.
458 * @return the cost of this operation or an error
460 CommandCost CmdBuildCanal(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, WaterClass wc, bool diagonal)
462 if (start_tile >= Map::Size() || !IsValidWaterClass(wc)) return CMD_ERROR;
464 /* Outside of the editor you can only build canals, not oceans */
465 if (wc != WATER_CLASS_CANAL && _game_mode != GM_EDITOR) return CMD_ERROR;
467 CommandCost cost(EXPENSES_CONSTRUCTION);
469 std::unique_ptr<TileIterator> iter = TileIterator::Create(tile, start_tile, diagonal);
470 for (; *iter != INVALID_TILE; ++(*iter)) {
471 TileIndex current_tile = *iter;
472 CommandCost ret;
474 Slope slope = GetTileSlope(current_tile);
475 if (slope != SLOPE_FLAT && (wc != WATER_CLASS_RIVER || !IsInclinedSlope(slope))) {
476 return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
479 bool water = IsWaterTile(current_tile);
481 /* Outside the editor, prevent building canals over your own or OWNER_NONE owned canals */
482 if (water && IsCanal(current_tile) && _game_mode != GM_EDITOR && (IsTileOwner(current_tile, _current_company) || IsTileOwner(current_tile, OWNER_NONE))) continue;
484 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, current_tile);
485 if (ret.Failed()) return ret;
487 if (!water) cost.AddCost(ret);
489 if (flags & DC_EXEC) {
490 if (IsTileType(current_tile, MP_WATER) && IsCanal(current_tile)) {
491 Owner owner = GetTileOwner(current_tile);
492 if (Company::IsValidID(owner)) {
493 Company::Get(owner)->infrastructure.water--;
494 DirtyCompanyInfrastructureWindows(owner);
498 switch (wc) {
499 case WATER_CLASS_RIVER:
500 MakeRiver(current_tile, Random());
501 if (_game_mode == GM_EDITOR) {
502 TileIndex tile2 = current_tile;
503 CircularTileSearch(&tile2, RIVER_OFFSET_DESERT_DISTANCE, RiverModifyDesertZone, nullptr);
505 break;
507 case WATER_CLASS_SEA:
508 if (TileHeight(current_tile) == 0) {
509 MakeSea(current_tile);
510 break;
512 [[fallthrough]];
514 default:
515 MakeCanal(current_tile, _current_company, Random());
516 if (Company::IsValidID(_current_company)) {
517 Company::Get(_current_company)->infrastructure.water++;
518 DirtyCompanyInfrastructureWindows(_current_company);
520 break;
522 MarkTileDirtyByTile(current_tile);
523 MarkCanalsAndRiversAroundDirty(current_tile);
524 CheckForDockingTile(current_tile);
527 cost.AddCost(_price[PR_BUILD_CANAL]);
530 if (cost.GetCost() == 0) {
531 return_cmd_error(STR_ERROR_ALREADY_BUILT);
532 } else {
533 return cost;
538 static CommandCost ClearTile_Water(TileIndex tile, DoCommandFlag flags)
540 switch (GetWaterTileType(tile)) {
541 case WATER_TILE_CLEAR: {
542 if (flags & DC_NO_WATER) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER);
544 Money base_cost = IsCanal(tile) ? _price[PR_CLEAR_CANAL] : _price[PR_CLEAR_WATER];
545 /* Make sure freeform edges are allowed or it's not an edge tile. */
546 if (!_settings_game.construction.freeform_edges && (!IsInsideMM(TileX(tile), 1, Map::MaxX() - 1) ||
547 !IsInsideMM(TileY(tile), 1, Map::MaxY() - 1))) {
548 return_cmd_error(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP);
551 /* Make sure no vehicle is on the tile */
552 CommandCost ret = EnsureNoVehicleOnGround(tile);
553 if (ret.Failed()) return ret;
555 Owner owner = GetTileOwner(tile);
556 if (owner != OWNER_WATER && owner != OWNER_NONE) {
557 ret = CheckTileOwnership(tile);
558 if (ret.Failed()) return ret;
561 if (flags & DC_EXEC) {
562 if (IsCanal(tile) && Company::IsValidID(owner)) {
563 Company::Get(owner)->infrastructure.water--;
564 DirtyCompanyInfrastructureWindows(owner);
566 DoClearSquare(tile);
567 MarkCanalsAndRiversAroundDirty(tile);
570 return CommandCost(EXPENSES_CONSTRUCTION, base_cost);
573 case WATER_TILE_COAST: {
574 Slope slope = GetTileSlope(tile);
576 /* Make sure no vehicle is on the tile */
577 CommandCost ret = EnsureNoVehicleOnGround(tile);
578 if (ret.Failed()) return ret;
580 if (flags & DC_EXEC) {
581 DoClearSquare(tile);
582 MarkCanalsAndRiversAroundDirty(tile);
584 if (IsSlopeWithOneCornerRaised(slope)) {
585 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_WATER]);
586 } else {
587 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_ROUGH]);
591 case WATER_TILE_LOCK: {
592 static const TileIndexDiffC _lock_tomiddle_offs[][DIAGDIR_END] = {
593 /* NE SE SW NW */
594 { { 0, 0}, {0, 0}, { 0, 0}, {0, 0} }, // LOCK_PART_MIDDLE
595 { {-1, 0}, {0, 1}, { 1, 0}, {0, -1} }, // LOCK_PART_LOWER
596 { { 1, 0}, {0, -1}, {-1, 0}, {0, 1} }, // LOCK_PART_UPPER
599 if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
600 if (_current_company == OWNER_WATER) return CMD_ERROR;
601 /* move to the middle tile.. */
602 return RemoveLock(tile + ToTileIndexDiff(_lock_tomiddle_offs[GetLockPart(tile)][GetLockDirection(tile)]), flags);
605 case WATER_TILE_DEPOT:
606 if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
607 return RemoveShipDepot(tile, flags);
609 default:
610 NOT_REACHED();
615 * return true if a tile is a water tile wrt. a certain direction.
617 * @param tile The tile of interest.
618 * @param from The direction of interest.
619 * @return true iff the tile is water in the view of 'from'.
622 bool IsWateredTile(TileIndex tile, Direction from)
624 switch (GetTileType(tile)) {
625 case MP_WATER:
626 switch (GetWaterTileType(tile)) {
627 default: NOT_REACHED();
628 case WATER_TILE_DEPOT: case WATER_TILE_CLEAR: return true;
629 case WATER_TILE_LOCK: return DiagDirToAxis(GetLockDirection(tile)) == DiagDirToAxis(DirToDiagDir(from));
631 case WATER_TILE_COAST:
632 switch (GetTileSlope(tile)) {
633 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
634 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
635 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
636 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
637 default: return false;
641 case MP_RAILWAY:
642 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
643 assert(IsPlainRail(tile));
644 switch (GetTileSlope(tile)) {
645 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
646 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
647 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
648 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
649 default: return false;
652 return false;
654 case MP_STATION:
655 if (IsOilRig(tile)) {
656 /* Do not draw waterborders inside of industries.
657 * Note: There is no easy way to detect the industry of an oilrig tile. */
658 TileIndex src_tile = tile + TileOffsByDir(from);
659 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
660 (IsTileType(src_tile, MP_INDUSTRY))) return true;
662 return IsTileOnWater(tile);
664 return (IsDock(tile) && IsTileFlat(tile)) || IsBuoy(tile);
666 case MP_INDUSTRY: {
667 /* Do not draw waterborders inside of industries.
668 * Note: There is no easy way to detect the industry of an oilrig tile. */
669 TileIndex src_tile = tile + TileOffsByDir(from);
670 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
671 (IsTileType(src_tile, MP_INDUSTRY) && GetIndustryIndex(src_tile) == GetIndustryIndex(tile))) return true;
673 return IsTileOnWater(tile);
676 case MP_OBJECT: return IsTileOnWater(tile);
678 case MP_TUNNELBRIDGE: return GetTunnelBridgeTransportType(tile) == TRANSPORT_WATER && ReverseDiagDir(GetTunnelBridgeDirection(tile)) == DirToDiagDir(from);
680 case MP_VOID: return true; // consider map border as water, esp. for rivers
682 default: return false;
687 * Draw a water sprite, potentially with a NewGRF-modified sprite offset.
688 * @param base Sprite base.
689 * @param offset Sprite offset.
690 * @param feature The type of sprite that is drawn.
691 * @param tile Tile index to draw.
693 static void DrawWaterSprite(SpriteID base, uint offset, CanalFeature feature, TileIndex tile)
695 if (base != SPR_FLAT_WATER_TILE) {
696 /* Only call offset callback if the sprite is NewGRF-provided. */
697 offset = GetCanalSpriteOffset(feature, tile, offset);
699 DrawGroundSprite(base + offset, PAL_NONE);
703 * Draw canal or river edges.
704 * @param canal True if canal edges should be drawn, false for river edges.
705 * @param offset Sprite offset.
706 * @param tile Tile to draw.
708 static void DrawWaterEdges(bool canal, uint offset, TileIndex tile)
710 CanalFeature feature;
711 SpriteID base = 0;
712 if (canal) {
713 feature = CF_DIKES;
714 base = GetCanalSprite(CF_DIKES, tile);
715 if (base == 0) base = SPR_CANAL_DIKES_BASE;
716 } else {
717 feature = CF_RIVER_EDGE;
718 base = GetCanalSprite(CF_RIVER_EDGE, tile);
719 if (base == 0) return; // Don't draw if no sprites provided.
722 uint wa;
724 /* determine the edges around with water. */
725 wa = IsWateredTile(TileAddXY(tile, -1, 0), DIR_SW) << 0;
726 wa += IsWateredTile(TileAddXY(tile, 0, 1), DIR_NW) << 1;
727 wa += IsWateredTile(TileAddXY(tile, 1, 0), DIR_NE) << 2;
728 wa += IsWateredTile(TileAddXY(tile, 0, -1), DIR_SE) << 3;
730 if (!(wa & 1)) DrawWaterSprite(base, offset, feature, tile);
731 if (!(wa & 2)) DrawWaterSprite(base, offset + 1, feature, tile);
732 if (!(wa & 4)) DrawWaterSprite(base, offset + 2, feature, tile);
733 if (!(wa & 8)) DrawWaterSprite(base, offset + 3, feature, tile);
735 /* right corner */
736 switch (wa & 0x03) {
737 case 0: DrawWaterSprite(base, offset + 4, feature, tile); break;
738 case 3: if (!IsWateredTile(TileAddXY(tile, -1, 1), DIR_W)) DrawWaterSprite(base, offset + 8, feature, tile); break;
741 /* bottom corner */
742 switch (wa & 0x06) {
743 case 0: DrawWaterSprite(base, offset + 5, feature, tile); break;
744 case 6: if (!IsWateredTile(TileAddXY(tile, 1, 1), DIR_N)) DrawWaterSprite(base, offset + 9, feature, tile); break;
747 /* left corner */
748 switch (wa & 0x0C) {
749 case 0: DrawWaterSprite(base, offset + 6, feature, tile); break;
750 case 12: if (!IsWateredTile(TileAddXY(tile, 1, -1), DIR_E)) DrawWaterSprite(base, offset + 10, feature, tile); break;
753 /* upper corner */
754 switch (wa & 0x09) {
755 case 0: DrawWaterSprite(base, offset + 7, feature, tile); break;
756 case 9: if (!IsWateredTile(TileAddXY(tile, -1, -1), DIR_S)) DrawWaterSprite(base, offset + 11, feature, tile); break;
760 /** Draw a plain sea water tile with no edges */
761 static void DrawSeaWater(TileIndex)
763 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
766 /** draw a canal styled water tile with dikes around */
767 static void DrawCanalWater(TileIndex tile)
769 SpriteID image = SPR_FLAT_WATER_TILE;
770 if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
771 /* First water slope sprite is flat water. */
772 image = GetCanalSprite(CF_WATERSLOPE, tile);
773 if (image == 0) image = SPR_FLAT_WATER_TILE;
775 DrawWaterSprite(image, 0, CF_WATERSLOPE, tile);
777 DrawWaterEdges(true, 0, tile);
780 #include "table/water_land.h"
783 * Draw a build sprite sequence for water tiles.
784 * If buildings are invisible, nothing will be drawn.
785 * @param ti Tile info.
786 * @param dtss Sprite sequence to draw.
787 * @param base Base sprite.
788 * @param offset Additional sprite offset.
789 * @param palette Palette to use.
791 static void DrawWaterTileStruct(const TileInfo *ti, const DrawTileSeqStruct *dtss, SpriteID base, uint offset, PaletteID palette, CanalFeature feature)
793 /* Don't draw if buildings are invisible. */
794 if (IsInvisibilitySet(TO_BUILDINGS)) return;
796 for (; !dtss->IsTerminator(); dtss++) {
797 uint tile_offs = offset + dtss->image.sprite;
798 if (feature < CF_END) tile_offs = GetCanalSpriteOffset(feature, ti->tile, tile_offs);
799 AddSortableSpriteToDraw(base + tile_offs, palette,
800 ti->x + dtss->delta_x, ti->y + dtss->delta_y,
801 dtss->size_x, dtss->size_y,
802 dtss->size_z, ti->z + dtss->delta_z,
803 IsTransparencySet(TO_BUILDINGS));
807 /** Draw a lock tile. */
808 static void DrawWaterLock(const TileInfo *ti)
810 int part = GetLockPart(ti->tile);
811 const DrawTileSprites &dts = _lock_display_data[part][GetLockDirection(ti->tile)];
813 /* Draw ground sprite. */
814 SpriteID image = dts.ground.sprite;
816 SpriteID water_base = GetCanalSprite(CF_WATERSLOPE, ti->tile);
817 if (water_base == 0) {
818 /* Use default sprites. */
819 water_base = SPR_CANALS_BASE;
820 } else if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
821 /* NewGRF supplies a flat sprite as first sprite. */
822 if (image == SPR_FLAT_WATER_TILE) {
823 image = water_base;
824 } else {
825 image++;
829 if (image < 5) image += water_base;
830 DrawGroundSprite(image, PAL_NONE);
832 /* Draw structures. */
833 uint zoffs = 0;
834 SpriteID base = GetCanalSprite(CF_LOCKS, ti->tile);
836 if (base == 0) {
837 /* If no custom graphics, use defaults. */
838 base = SPR_LOCK_BASE;
839 uint8_t z_threshold = part == LOCK_PART_UPPER ? 8 : 0;
840 zoffs = ti->z > z_threshold ? 24 : 0;
843 DrawWaterTileStruct(ti, dts.seq, base, zoffs, PAL_NONE, CF_LOCKS);
846 /** Draw a ship depot tile. */
847 static void DrawWaterDepot(const TileInfo *ti)
849 DrawWaterClassGround(ti);
850 DrawWaterTileStruct(ti, _shipdepot_display_data[GetShipDepotAxis(ti->tile)][GetShipDepotPart(ti->tile)].seq, 0, 0, COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile)), CF_END);
853 static void DrawRiverWater(const TileInfo *ti)
855 SpriteID image = SPR_FLAT_WATER_TILE;
856 uint offset = 0;
857 uint edges_offset = 0;
859 if (ti->tileh != SLOPE_FLAT || HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
860 image = GetCanalSprite(CF_RIVER_SLOPE, ti->tile);
861 if (image == 0) {
862 switch (ti->tileh) {
863 case SLOPE_NW: image = SPR_WATER_SLOPE_Y_DOWN; break;
864 case SLOPE_SW: image = SPR_WATER_SLOPE_X_UP; break;
865 case SLOPE_SE: image = SPR_WATER_SLOPE_Y_UP; break;
866 case SLOPE_NE: image = SPR_WATER_SLOPE_X_DOWN; break;
867 default: image = SPR_FLAT_WATER_TILE; break;
869 } else {
870 /* Flag bit 0 indicates that the first sprite is flat water. */
871 offset = HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE) ? 1 : 0;
873 switch (ti->tileh) {
874 case SLOPE_SE: edges_offset += 12; break;
875 case SLOPE_NE: offset += 1; edges_offset += 24; break;
876 case SLOPE_SW: offset += 2; edges_offset += 36; break;
877 case SLOPE_NW: offset += 3; edges_offset += 48; break;
878 default: offset = 0; break;
881 offset = GetCanalSpriteOffset(CF_RIVER_SLOPE, ti->tile, offset);
885 DrawGroundSprite(image + offset, PAL_NONE);
887 /* Draw river edges if available. */
888 DrawWaterEdges(false, edges_offset, ti->tile);
891 void DrawShoreTile(Slope tileh)
893 /* Converts the enum Slope into an offset based on SPR_SHORE_BASE.
894 * This allows to calculate the proper sprite to display for this Slope */
895 static const uint8_t tileh_to_shoresprite[32] = {
896 0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0,
897 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 15, 0,
900 assert(!IsHalftileSlope(tileh)); // Halftile slopes need to get handled earlier.
901 assert(tileh != SLOPE_FLAT); // Shore is never flat
903 assert((tileh != SLOPE_EW) && (tileh != SLOPE_NS)); // No suitable sprites for current flooding behaviour
905 DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[tileh], PAL_NONE);
908 void DrawWaterClassGround(const TileInfo *ti)
910 switch (GetWaterClass(ti->tile)) {
911 case WATER_CLASS_SEA: DrawSeaWater(ti->tile); break;
912 case WATER_CLASS_CANAL: DrawCanalWater(ti->tile); break;
913 case WATER_CLASS_RIVER: DrawRiverWater(ti); break;
914 default: NOT_REACHED();
918 static void DrawTile_Water(TileInfo *ti)
920 switch (GetWaterTileType(ti->tile)) {
921 case WATER_TILE_CLEAR:
922 DrawWaterClassGround(ti);
923 DrawBridgeMiddle(ti);
924 break;
926 case WATER_TILE_COAST: {
927 DrawShoreTile(ti->tileh);
928 DrawBridgeMiddle(ti);
929 break;
932 case WATER_TILE_LOCK:
933 DrawWaterLock(ti);
934 break;
936 case WATER_TILE_DEPOT:
937 DrawWaterDepot(ti);
938 break;
942 void DrawShipDepotSprite(int x, int y, Axis axis, DepotPart part)
944 const DrawTileSprites &dts = _shipdepot_display_data[axis][part];
946 DrawSprite(dts.ground.sprite, dts.ground.pal, x, y);
947 DrawOrigTileSeqInGUI(x, y, &dts, COMPANY_SPRITE_COLOUR(_local_company));
951 static int GetSlopePixelZ_Water(TileIndex tile, uint x, uint y, bool)
953 auto [tileh, z] = GetTilePixelSlope(tile);
955 return z + GetPartialPixelZ(x & 0xF, y & 0xF, tileh);
958 static Foundation GetFoundation_Water(TileIndex, Slope)
960 return FOUNDATION_NONE;
963 static void GetTileDesc_Water(TileIndex tile, TileDesc *td)
965 switch (GetWaterTileType(tile)) {
966 case WATER_TILE_CLEAR:
967 switch (GetWaterClass(tile)) {
968 case WATER_CLASS_SEA: td->str = STR_LAI_WATER_DESCRIPTION_WATER; break;
969 case WATER_CLASS_CANAL: td->str = STR_LAI_WATER_DESCRIPTION_CANAL; break;
970 case WATER_CLASS_RIVER: td->str = STR_LAI_WATER_DESCRIPTION_RIVER; break;
971 default: NOT_REACHED();
973 break;
974 case WATER_TILE_COAST: td->str = STR_LAI_WATER_DESCRIPTION_COAST_OR_RIVERBANK; break;
975 case WATER_TILE_LOCK : td->str = STR_LAI_WATER_DESCRIPTION_LOCK; break;
976 case WATER_TILE_DEPOT:
977 td->str = STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT;
978 td->build_date = Depot::GetByTile(tile)->build_date;
979 break;
980 default: NOT_REACHED();
983 td->owner[0] = GetTileOwner(tile);
987 * Handle the flooding of a vehicle. This sets the vehicle state to crashed,
988 * creates a newsitem and dirties the necessary windows.
989 * @param v The vehicle to flood.
991 static void FloodVehicle(Vehicle *v)
993 uint victims = v->Crash(true);
995 AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_FLOODED, victims));
996 Game::NewEvent(new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_FLOODED, victims));
997 SetDParam(0, victims);
998 AddTileNewsItem(STR_NEWS_DISASTER_FLOOD_VEHICLE, NT_ACCIDENT, v->tile);
999 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
1000 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v);
1004 * Flood a vehicle if we are allowed to flood it, i.e. when it is on the ground.
1005 * @param v The vehicle to test for flooding.
1006 * @param data The z of level to flood.
1007 * @return nullptr as we always want to remove everything.
1009 static Vehicle *FloodVehicleProc(Vehicle *v, void *data)
1011 if ((v->vehstatus & VS_CRASHED) != 0) return nullptr;
1013 switch (v->type) {
1014 default: break;
1016 case VEH_AIRCRAFT: {
1017 if (!IsAirportTile(v->tile) || GetTileMaxZ(v->tile) != 0) break;
1018 if (v->subtype == AIR_SHADOW) break;
1020 /* We compare v->z_pos against delta_z + 1 because the shadow
1021 * is at delta_z and the actual aircraft at delta_z + 1. */
1022 const Station *st = Station::GetByTile(v->tile);
1023 const AirportFTAClass *airport = st->airport.GetFTA();
1024 if (v->z_pos != airport->delta_z + 1) break;
1026 FloodVehicle(v);
1027 break;
1030 case VEH_TRAIN:
1031 case VEH_ROAD: {
1032 int z = *(int*)data;
1033 if (v->z_pos > z) break;
1034 FloodVehicle(v->First());
1035 break;
1039 return nullptr;
1043 * Finds a vehicle to flood.
1044 * It does not find vehicles that are already crashed on bridges, i.e. flooded.
1045 * @param tile the tile where to find a vehicle to flood
1047 static void FloodVehicles(TileIndex tile)
1049 int z = 0;
1051 if (IsAirportTile(tile)) {
1052 const Station *st = Station::GetByTile(tile);
1053 for (TileIndex airport_tile : st->airport) {
1054 if (st->TileBelongsToAirport(airport_tile)) FindVehicleOnPos(airport_tile, &z, &FloodVehicleProc);
1057 /* No vehicle could be flooded on this airport anymore */
1058 return;
1061 if (!IsBridgeTile(tile)) {
1062 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
1063 return;
1066 TileIndex end = GetOtherBridgeEnd(tile);
1067 z = GetBridgePixelHeight(tile);
1069 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
1070 FindVehicleOnPos(end, &z, &FloodVehicleProc);
1074 * Returns the behaviour of a tile during flooding.
1076 * @return Behaviour of the tile
1078 FloodingBehaviour GetFloodingBehaviour(TileIndex tile)
1080 /* FLOOD_ACTIVE: 'single-corner-raised'-coast, sea, sea-shipdepots, sea-buoys, sea-docks (water part), rail with flooded halftile, sea-water-industries, sea-oilrigs
1081 * FLOOD_DRYUP: coast with more than one corner raised, coast with rail-track, coast with trees
1082 * FLOOD_PASSIVE: (not used)
1083 * FLOOD_NONE: canals, rivers, everything else
1085 switch (GetTileType(tile)) {
1086 case MP_WATER:
1087 if (IsCoast(tile)) {
1088 Slope tileh = GetTileSlope(tile);
1089 return (IsSlopeWithOneCornerRaised(tileh) ? FLOOD_ACTIVE : FLOOD_DRYUP);
1091 [[fallthrough]];
1092 case MP_STATION:
1093 case MP_INDUSTRY:
1094 case MP_OBJECT:
1095 return (GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE;
1097 case MP_RAILWAY:
1098 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
1099 return (IsSlopeWithOneCornerRaised(GetTileSlope(tile)) ? FLOOD_ACTIVE : FLOOD_DRYUP);
1101 return FLOOD_NONE;
1103 case MP_TREES:
1104 return (GetTreeGround(tile) == TREE_GROUND_SHORE ? FLOOD_DRYUP : FLOOD_NONE);
1106 case MP_VOID:
1107 return FLOOD_ACTIVE;
1109 default:
1110 return FLOOD_NONE;
1115 * Floods a tile.
1117 static void DoFloodTile(TileIndex target)
1119 assert(!IsTileType(target, MP_WATER));
1121 bool flooded = false; // Will be set to true if something is changed.
1123 Backup<CompanyID> cur_company(_current_company, OWNER_WATER);
1125 Slope tileh = GetTileSlope(target);
1126 if (tileh != SLOPE_FLAT) {
1127 /* make coast.. */
1128 switch (GetTileType(target)) {
1129 case MP_RAILWAY: {
1130 if (!IsPlainRail(target)) break;
1131 FloodVehicles(target);
1132 flooded = FloodHalftile(target);
1133 break;
1136 case MP_TREES:
1137 if (!IsSlopeWithOneCornerRaised(tileh)) {
1138 SetTreeGroundDensity(target, TREE_GROUND_SHORE, 3);
1139 MarkTileDirtyByTile(target);
1140 flooded = true;
1141 break;
1143 [[fallthrough]];
1145 case MP_CLEAR:
1146 if (Command<CMD_LANDSCAPE_CLEAR>::Do(DC_EXEC, target).Succeeded()) {
1147 MakeShore(target);
1148 MarkTileDirtyByTile(target);
1149 flooded = true;
1151 break;
1153 default:
1154 break;
1156 } else {
1157 /* Flood vehicles */
1158 FloodVehicles(target);
1160 /* flood flat tile */
1161 if (Command<CMD_LANDSCAPE_CLEAR>::Do(DC_EXEC, target).Succeeded()) {
1162 MakeSea(target);
1163 MarkTileDirtyByTile(target);
1164 flooded = true;
1168 if (flooded) {
1169 /* Mark surrounding canal tiles dirty too to avoid glitches */
1170 MarkCanalsAndRiversAroundDirty(target);
1172 /* update signals if needed */
1173 UpdateSignalsInBuffer();
1175 if (IsPossibleDockingTile(target)) CheckForDockingTile(target);
1178 cur_company.Restore();
1182 * Drys a tile up.
1184 static void DoDryUp(TileIndex tile)
1186 Backup<CompanyID> cur_company(_current_company, OWNER_WATER);
1188 switch (GetTileType(tile)) {
1189 case MP_RAILWAY:
1190 assert(IsPlainRail(tile));
1191 assert(GetRailGroundType(tile) == RAIL_GROUND_WATER);
1193 RailGroundType new_ground;
1194 switch (GetTrackBits(tile)) {
1195 case TRACK_BIT_UPPER: new_ground = RAIL_GROUND_FENCE_HORIZ1; break;
1196 case TRACK_BIT_LOWER: new_ground = RAIL_GROUND_FENCE_HORIZ2; break;
1197 case TRACK_BIT_LEFT: new_ground = RAIL_GROUND_FENCE_VERT1; break;
1198 case TRACK_BIT_RIGHT: new_ground = RAIL_GROUND_FENCE_VERT2; break;
1199 default: NOT_REACHED();
1201 SetRailGroundType(tile, new_ground);
1202 MarkTileDirtyByTile(tile);
1203 break;
1205 case MP_TREES:
1206 SetTreeGroundDensity(tile, TREE_GROUND_GRASS, 3);
1207 MarkTileDirtyByTile(tile);
1208 break;
1210 case MP_WATER:
1211 assert(IsCoast(tile));
1213 if (Command<CMD_LANDSCAPE_CLEAR>::Do(DC_EXEC, tile).Succeeded()) {
1214 MakeClear(tile, CLEAR_GRASS, 3);
1215 MarkTileDirtyByTile(tile);
1217 break;
1219 default: NOT_REACHED();
1222 cur_company.Restore();
1226 * Let a water tile floods its diagonal adjoining tiles
1227 * called from tunnelbridge_cmd, and by TileLoop_Industry() and TileLoop_Track()
1229 * @param tile the water/shore tile that floods
1231 void TileLoop_Water(TileIndex tile)
1233 if (IsTileType(tile, MP_WATER)) AmbientSoundEffect(tile);
1235 switch (GetFloodingBehaviour(tile)) {
1236 case FLOOD_ACTIVE:
1237 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
1238 TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir(dir));
1239 /* Contrary to drying up, flooding does not consider MP_VOID tiles. */
1240 if (!IsValidTile(dest)) continue;
1241 /* do not try to flood water tiles - increases performance a lot */
1242 if (IsTileType(dest, MP_WATER)) continue;
1244 /* TREE_GROUND_SHORE is the sign of a previous flood. */
1245 if (IsTileType(dest, MP_TREES) && GetTreeGround(dest) == TREE_GROUND_SHORE) continue;
1247 auto [slope_dest, z_dest] = GetFoundationSlope(dest);
1248 if (z_dest > 0) continue;
1250 if (!HasBit(_flood_from_dirs[slope_dest & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP], ReverseDir(dir))) continue;
1252 DoFloodTile(dest);
1254 break;
1256 case FLOOD_DRYUP: {
1257 Slope slope_here = std::get<0>(GetFoundationSlope(tile)) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
1258 for (Direction dir : SetBitIterator<Direction>(_flood_from_dirs[slope_here])) {
1259 TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir(dir));
1260 /* Contrary to flooding, drying up does consider MP_VOID tiles. */
1261 if (dest == INVALID_TILE) continue;
1263 FloodingBehaviour dest_behaviour = GetFloodingBehaviour(dest);
1264 if ((dest_behaviour == FLOOD_ACTIVE) || (dest_behaviour == FLOOD_PASSIVE)) return;
1266 DoDryUp(tile);
1267 break;
1270 default: return;
1274 void ConvertGroundTilesIntoWaterTiles()
1276 for (TileIndex tile = 0; tile < Map::Size(); ++tile) {
1277 auto [slope, z] = GetTileSlopeZ(tile);
1278 if (IsTileType(tile, MP_CLEAR) && z == 0) {
1279 /* Make both water for tiles at level 0
1280 * and make shore, as that looks much better
1281 * during the generation. */
1282 switch (slope) {
1283 case SLOPE_FLAT:
1284 MakeSea(tile);
1285 break;
1287 case SLOPE_N:
1288 case SLOPE_E:
1289 case SLOPE_S:
1290 case SLOPE_W:
1291 MakeShore(tile);
1292 break;
1294 default:
1295 for (Direction dir : SetBitIterator<Direction>(_flood_from_dirs[slope & ~SLOPE_STEEP])) {
1296 TileIndex dest = TileAddByDir(tile, dir);
1297 Slope slope_dest = GetTileSlope(dest) & ~SLOPE_STEEP;
1298 if (slope_dest == SLOPE_FLAT || IsSlopeWithOneCornerRaised(slope_dest) || IsTileType(dest, MP_VOID)) {
1299 MakeShore(tile);
1300 break;
1303 break;
1309 static TrackStatus GetTileTrackStatus_Water(TileIndex tile, TransportType mode, uint, DiagDirection)
1311 static const TrackBits coast_tracks[] = {TRACK_BIT_NONE, TRACK_BIT_RIGHT, TRACK_BIT_UPPER, TRACK_BIT_NONE, TRACK_BIT_LEFT, TRACK_BIT_NONE, TRACK_BIT_NONE,
1312 TRACK_BIT_NONE, TRACK_BIT_LOWER, TRACK_BIT_NONE, TRACK_BIT_NONE, TRACK_BIT_NONE, TRACK_BIT_NONE, TRACK_BIT_NONE, TRACK_BIT_NONE, TRACK_BIT_NONE};
1314 TrackBits ts;
1316 if (mode != TRANSPORT_WATER) return 0;
1318 switch (GetWaterTileType(tile)) {
1319 case WATER_TILE_CLEAR: ts = IsTileFlat(tile) ? TRACK_BIT_ALL : TRACK_BIT_NONE; break;
1320 case WATER_TILE_COAST: ts = coast_tracks[GetTileSlope(tile) & 0xF]; break;
1321 case WATER_TILE_LOCK: ts = DiagDirToDiagTrackBits(GetLockDirection(tile)); break;
1322 case WATER_TILE_DEPOT: ts = AxisToTrackBits(GetShipDepotAxis(tile)); break;
1323 default: return 0;
1325 if (TileX(tile) == 0) {
1326 /* NE border: remove tracks that connects NE tile edge */
1327 ts &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
1329 if (TileY(tile) == 0) {
1330 /* NW border: remove tracks that connects NW tile edge */
1331 ts &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
1333 return CombineTrackStatus(TrackBitsToTrackdirBits(ts), TRACKDIR_BIT_NONE);
1336 static bool ClickTile_Water(TileIndex tile)
1338 if (GetWaterTileType(tile) == WATER_TILE_DEPOT) {
1339 ShowDepotWindow(GetShipDepotNorthTile(tile), VEH_SHIP);
1340 return true;
1342 return false;
1345 static void ChangeTileOwner_Water(TileIndex tile, Owner old_owner, Owner new_owner)
1347 if (!IsTileOwner(tile, old_owner)) return;
1349 bool is_lock_middle = IsLock(tile) && GetLockPart(tile) == LOCK_PART_MIDDLE;
1351 /* No need to dirty company windows here, we'll redraw the whole screen anyway. */
1352 if (is_lock_middle) Company::Get(old_owner)->infrastructure.water -= 3 * LOCK_DEPOT_TILE_FACTOR; // Lock has three parts.
1353 if (new_owner != INVALID_OWNER) {
1354 if (is_lock_middle) Company::Get(new_owner)->infrastructure.water += 3 * LOCK_DEPOT_TILE_FACTOR; // Lock has three parts.
1355 /* Only subtract from the old owner here if the new owner is valid,
1356 * otherwise we clear ship depots and canal water below. */
1357 if (GetWaterClass(tile) == WATER_CLASS_CANAL && !is_lock_middle) {
1358 Company::Get(old_owner)->infrastructure.water--;
1359 Company::Get(new_owner)->infrastructure.water++;
1361 if (IsShipDepot(tile)) {
1362 Company::Get(old_owner)->infrastructure.water -= LOCK_DEPOT_TILE_FACTOR;
1363 Company::Get(new_owner)->infrastructure.water += LOCK_DEPOT_TILE_FACTOR;
1366 SetTileOwner(tile, new_owner);
1367 return;
1370 /* Remove depot */
1371 if (IsShipDepot(tile)) Command<CMD_LANDSCAPE_CLEAR>::Do(DC_EXEC | DC_BANKRUPT, tile);
1373 /* Set owner of canals and locks ... and also canal under dock there was before.
1374 * Check if the new owner after removing depot isn't OWNER_WATER. */
1375 if (IsTileOwner(tile, old_owner)) {
1376 if (GetWaterClass(tile) == WATER_CLASS_CANAL && !is_lock_middle) Company::Get(old_owner)->infrastructure.water--;
1377 SetTileOwner(tile, OWNER_NONE);
1381 static VehicleEnterTileStatus VehicleEnter_Water(Vehicle *, TileIndex, int, int)
1383 return VETSB_CONTINUE;
1386 static CommandCost TerraformTile_Water(TileIndex tile, DoCommandFlag flags, int, Slope)
1388 /* Canals can't be terraformed */
1389 if (IsWaterTile(tile) && IsCanal(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_CANAL_FIRST);
1391 return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
1395 extern const TileTypeProcs _tile_type_water_procs = {
1396 DrawTile_Water, // draw_tile_proc
1397 GetSlopePixelZ_Water, // get_slope_z_proc
1398 ClearTile_Water, // clear_tile_proc
1399 nullptr, // add_accepted_cargo_proc
1400 GetTileDesc_Water, // get_tile_desc_proc
1401 GetTileTrackStatus_Water, // get_tile_track_status_proc
1402 ClickTile_Water, // click_tile_proc
1403 nullptr, // animate_tile_proc
1404 TileLoop_Water, // tile_loop_proc
1405 ChangeTileOwner_Water, // change_tile_owner_proc
1406 nullptr, // add_produced_cargo_proc
1407 VehicleEnter_Water, // vehicle_enter_tile_proc
1408 GetFoundation_Water, // get_foundation_proc
1409 TerraformTile_Water, // terraform_tile_proc