Codechange: Use functor for Kdtree's XYFunc. (#13074)
[openttd-github.git] / src / water_cmd.cpp
blob88a8fc9360b472dacbe7bc1304c11d2eaddac3cc
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));
93 /**
94 * Clear non-flooding state of the tiles around a tile.
95 * @param tile The centre of the tile where other tiles' non-flooding state is cleared.
97 void ClearNeighbourNonFloodingStates(TileIndex tile)
99 for (Direction dir = DIR_BEGIN; dir != DIR_END; dir++) {
100 TileIndex dest = tile + TileOffsByDir(dir);
101 if (IsValidTile(dest) && IsTileType(dest, MP_WATER)) SetNonFloodingWaterTile(dest, false);
106 * Build a ship depot.
107 * @param flags type of operation
108 * @param tile tile where ship depot is built
109 * @param axis depot orientation (Axis)
110 * @return the cost of this operation or an error
112 CommandCost CmdBuildShipDepot(DoCommandFlag flags, TileIndex tile, Axis axis)
114 if (!IsValidAxis(axis)) return CMD_ERROR;
115 TileIndex tile2 = tile + TileOffsByAxis(axis);
117 if (!HasTileWaterGround(tile) || !HasTileWaterGround(tile2)) {
118 return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER);
121 if (IsBridgeAbove(tile) || IsBridgeAbove(tile2)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
123 if (!IsTileFlat(tile) || !IsTileFlat(tile2)) {
124 /* Prevent depots on rapids */
125 return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
128 if (!Depot::CanAllocateItem()) return CMD_ERROR;
130 WaterClass wc1 = GetWaterClass(tile);
131 WaterClass wc2 = GetWaterClass(tile2);
132 CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_DEPOT_SHIP]);
134 bool add_cost = !IsWaterTile(tile);
135 CommandCost ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags | DC_AUTO, tile);
136 if (ret.Failed()) return ret;
137 if (add_cost) {
138 cost.AddCost(ret);
140 add_cost = !IsWaterTile(tile2);
141 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags | DC_AUTO, tile2);
142 if (ret.Failed()) return ret;
143 if (add_cost) {
144 cost.AddCost(ret);
147 if (flags & DC_EXEC) {
148 Depot *depot = new Depot(tile);
149 depot->build_date = TimerGameCalendar::date;
151 uint new_water_infra = 2 * LOCK_DEPOT_TILE_FACTOR;
152 /* Update infrastructure counts after the tile clears earlier.
153 * Clearing object tiles may result in water tiles which are already accounted for in the water infrastructure total.
154 * See: MakeWaterKeepingClass() */
155 if (wc1 == WATER_CLASS_CANAL && !(HasTileWaterClass(tile) && GetWaterClass(tile) == WATER_CLASS_CANAL && IsTileOwner(tile, _current_company))) new_water_infra++;
156 if (wc2 == WATER_CLASS_CANAL && !(HasTileWaterClass(tile2) && GetWaterClass(tile2) == WATER_CLASS_CANAL && IsTileOwner(tile2, _current_company))) new_water_infra++;
158 Company::Get(_current_company)->infrastructure.water += new_water_infra;
159 DirtyCompanyInfrastructureWindows(_current_company);
161 MakeShipDepot(tile, _current_company, depot->index, DEPOT_PART_NORTH, axis, wc1);
162 MakeShipDepot(tile2, _current_company, depot->index, DEPOT_PART_SOUTH, axis, wc2);
163 CheckForDockingTile(tile);
164 CheckForDockingTile(tile2);
165 MarkTileDirtyByTile(tile);
166 MarkTileDirtyByTile(tile2);
167 MakeDefaultName(depot);
170 return cost;
173 bool IsPossibleDockingTile(Tile t)
175 assert(IsValidTile(t));
176 switch (GetTileType(t)) {
177 case MP_WATER:
178 if (IsLock(t) && GetLockPart(t) == LOCK_PART_MIDDLE) return false;
179 [[fallthrough]];
180 case MP_RAILWAY:
181 case MP_STATION:
182 case MP_TUNNELBRIDGE:
183 return TrackStatusToTrackBits(GetTileTrackStatus(t, TRANSPORT_WATER, 0)) != TRACK_BIT_NONE;
185 default:
186 return false;
191 * Mark the supplied tile as a docking tile if it is suitable for docking.
192 * Tiles surrounding the tile are tested to be docks with correct orientation.
193 * @param t Tile to test.
195 void CheckForDockingTile(TileIndex t)
197 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
198 TileIndex tile = t + TileOffsByDiagDir(d);
199 if (!IsValidTile(tile)) continue;
201 if (IsDockTile(tile) && IsDockWaterPart(tile)) {
202 Station::GetByTile(tile)->docking_station.Add(t);
203 SetDockingTile(t, true);
205 if (IsTileType(tile, MP_INDUSTRY)) {
206 Station *st = Industry::GetByTile(tile)->neutral_station;
207 if (st != nullptr) {
208 st->docking_station.Add(t);
209 SetDockingTile(t, true);
212 if (IsTileType(tile, MP_STATION) && IsOilRig(tile)) {
213 Station::GetByTile(tile)->docking_station.Add(t);
214 SetDockingTile(t, true);
219 void MakeWaterKeepingClass(TileIndex tile, Owner o)
221 WaterClass wc = GetWaterClass(tile);
223 /* Autoslope might turn an originally canal or river tile into land */
224 auto [slope, z] = GetTileSlopeZ(tile);
226 if (slope != SLOPE_FLAT) {
227 if (wc == WATER_CLASS_CANAL) {
228 /* If we clear the canal, we have to remove it from the infrastructure count as well. */
229 Company *c = Company::GetIfValid(o);
230 if (c != nullptr) {
231 c->infrastructure.water--;
232 DirtyCompanyInfrastructureWindows(c->index);
234 /* Sloped canals are locks and no natural water remains whatever the slope direction */
235 wc = WATER_CLASS_INVALID;
238 /* Only river water should be restored on appropriate slopes. Other water would be invalid on slopes */
239 if (wc != WATER_CLASS_RIVER || GetInclinedSlopeDirection(slope) == INVALID_DIAGDIR) {
240 wc = WATER_CLASS_INVALID;
244 if (wc == WATER_CLASS_SEA && z > 0) {
245 /* Update company infrastructure count. */
246 Company *c = Company::GetIfValid(o);
247 if (c != nullptr) {
248 c->infrastructure.water++;
249 DirtyCompanyInfrastructureWindows(c->index);
252 wc = WATER_CLASS_CANAL;
255 /* Zero map array and terminate animation */
256 DoClearSquare(tile);
258 /* Maybe change to water */
259 switch (wc) {
260 case WATER_CLASS_SEA: MakeSea(tile); break;
261 case WATER_CLASS_CANAL: MakeCanal(tile, o, Random()); break;
262 case WATER_CLASS_RIVER: MakeRiver(tile, Random()); break;
263 default: break;
266 if (wc != WATER_CLASS_INVALID) CheckForDockingTile(tile);
267 MarkTileDirtyByTile(tile);
270 static CommandCost RemoveShipDepot(TileIndex tile, DoCommandFlag flags)
272 if (!IsShipDepot(tile)) return CMD_ERROR;
274 CommandCost ret = CheckTileOwnership(tile);
275 if (ret.Failed()) return ret;
277 TileIndex tile2 = GetOtherShipDepotTile(tile);
279 /* do not check for ship on tile when company goes bankrupt */
280 if (!(flags & DC_BANKRUPT)) {
281 ret = EnsureNoVehicleOnGround(tile);
282 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile2);
283 if (ret.Failed()) return ret;
286 bool do_clear = (flags & DC_FORCE_CLEAR_TILE) != 0;
288 if (flags & DC_EXEC) {
289 delete Depot::GetByTile(tile);
291 Company *c = Company::GetIfValid(GetTileOwner(tile));
292 if (c != nullptr) {
293 c->infrastructure.water -= 2 * LOCK_DEPOT_TILE_FACTOR;
294 if (do_clear && GetWaterClass(tile) == WATER_CLASS_CANAL) c->infrastructure.water--;
295 DirtyCompanyInfrastructureWindows(c->index);
298 if (!do_clear) MakeWaterKeepingClass(tile, GetTileOwner(tile));
299 MakeWaterKeepingClass(tile2, GetTileOwner(tile2));
302 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_SHIP]);
306 * Builds a lock.
307 * @param tile Central tile of the lock.
308 * @param dir Uphill direction.
309 * @param flags Operation to perform.
310 * @return The cost in case of success, or an error code if it failed.
312 static CommandCost DoBuildLock(TileIndex tile, DiagDirection dir, DoCommandFlag flags)
314 CommandCost cost(EXPENSES_CONSTRUCTION);
316 TileIndexDiff delta = TileOffsByDiagDir(dir);
317 CommandCost ret = EnsureNoVehicleOnGround(tile);
318 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta);
319 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta);
320 if (ret.Failed()) return ret;
322 /* middle tile */
323 WaterClass wc_middle = HasTileWaterGround(tile) ? GetWaterClass(tile) : WATER_CLASS_CANAL;
324 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
325 if (ret.Failed()) return ret;
326 cost.AddCost(ret);
328 /* lower tile */
329 if (!IsWaterTile(tile - delta)) {
330 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile - delta);
331 if (ret.Failed()) return ret;
332 cost.AddCost(ret);
333 cost.AddCost(_price[PR_BUILD_CANAL]);
335 if (!IsTileFlat(tile - delta)) {
336 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
338 WaterClass wc_lower = IsWaterTile(tile - delta) ? GetWaterClass(tile - delta) : WATER_CLASS_CANAL;
340 /* upper tile */
341 if (!IsWaterTile(tile + delta)) {
342 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile + delta);
343 if (ret.Failed()) return ret;
344 cost.AddCost(ret);
345 cost.AddCost(_price[PR_BUILD_CANAL]);
347 if (!IsTileFlat(tile + delta)) {
348 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
350 WaterClass wc_upper = IsWaterTile(tile + delta) ? GetWaterClass(tile + delta) : WATER_CLASS_CANAL;
352 if (IsBridgeAbove(tile) || IsBridgeAbove(tile - delta) || IsBridgeAbove(tile + delta)) {
353 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
356 if (flags & DC_EXEC) {
357 /* Update company infrastructure counts. */
358 Company *c = Company::GetIfValid(_current_company);
359 if (c != nullptr) {
360 /* Counts for the water. */
361 if (!IsWaterTile(tile - delta)) c->infrastructure.water++;
362 if (!IsWaterTile(tile + delta)) c->infrastructure.water++;
363 /* Count for the lock itself. */
364 c->infrastructure.water += 3 * LOCK_DEPOT_TILE_FACTOR; // Lock is three tiles.
365 DirtyCompanyInfrastructureWindows(_current_company);
368 MakeLock(tile, _current_company, dir, wc_lower, wc_upper, wc_middle);
369 CheckForDockingTile(tile - delta);
370 CheckForDockingTile(tile + delta);
371 MarkTileDirtyByTile(tile);
372 MarkTileDirtyByTile(tile - delta);
373 MarkTileDirtyByTile(tile + delta);
374 MarkCanalsAndRiversAroundDirty(tile - delta);
375 MarkCanalsAndRiversAroundDirty(tile + delta);
376 InvalidateWaterRegion(tile - delta);
377 InvalidateWaterRegion(tile + delta);
379 cost.AddCost(_price[PR_BUILD_LOCK]);
381 return cost;
385 * Remove a lock.
386 * @param tile Central tile of the lock.
387 * @param flags Operation to perform.
388 * @return The cost in case of success, or an error code if it failed.
390 static CommandCost RemoveLock(TileIndex tile, DoCommandFlag flags)
392 if (GetTileOwner(tile) != OWNER_NONE) {
393 CommandCost ret = CheckTileOwnership(tile);
394 if (ret.Failed()) return ret;
397 TileIndexDiff delta = TileOffsByDiagDir(GetLockDirection(tile));
399 /* make sure no vehicle is on the tile. */
400 CommandCost ret = EnsureNoVehicleOnGround(tile);
401 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta);
402 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta);
403 if (ret.Failed()) return ret;
405 if (flags & DC_EXEC) {
406 /* Remove middle part from company infrastructure count. */
407 Company *c = Company::GetIfValid(GetTileOwner(tile));
408 if (c != nullptr) {
409 c->infrastructure.water -= 3 * LOCK_DEPOT_TILE_FACTOR; // three parts of the lock.
410 DirtyCompanyInfrastructureWindows(c->index);
413 if (GetWaterClass(tile) == WATER_CLASS_RIVER) {
414 MakeRiver(tile, Random());
415 } else {
416 DoClearSquare(tile);
417 ClearNeighbourNonFloodingStates(tile);
419 MakeWaterKeepingClass(tile + delta, GetTileOwner(tile + delta));
420 MakeWaterKeepingClass(tile - delta, GetTileOwner(tile - delta));
421 MarkCanalsAndRiversAroundDirty(tile);
422 MarkCanalsAndRiversAroundDirty(tile - delta);
423 MarkCanalsAndRiversAroundDirty(tile + delta);
426 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_LOCK]);
430 * Builds a lock.
431 * @param flags type of operation
432 * @param tile tile where to place the lock
433 * @return the cost of this operation or an error
435 CommandCost CmdBuildLock(DoCommandFlag flags, TileIndex tile)
437 DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile));
438 if (dir == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
440 return DoBuildLock(tile, dir, flags);
443 /** Callback to create non-desert around a river tile. */
444 static bool RiverModifyDesertZone(TileIndex tile, void *)
446 if (GetTropicZone(tile) == TROPICZONE_DESERT) SetTropicZone(tile, TROPICZONE_NORMAL);
447 return false;
451 * Make a river tile and remove desert directly around it.
452 * @param tile The tile to change into river and create non-desert around
454 void MakeRiverAndModifyDesertZoneAround(TileIndex tile)
456 MakeRiver(tile, Random());
457 MarkTileDirtyByTile(tile);
459 /* Remove desert directly around the river tile. */
460 CircularTileSearch(&tile, RIVER_OFFSET_DESERT_DISTANCE, RiverModifyDesertZone, nullptr);
464 * Build a piece of canal.
465 * @param flags type of operation
466 * @param tile end tile of stretch-dragging
467 * @param start_tile start tile of stretch-dragging
468 * @param wc waterclass to build. sea and river can only be built in scenario editor
469 * @param diagonal Whether to use the Orthogonal (0) or Diagonal (1) iterator.
470 * @return the cost of this operation or an error
472 CommandCost CmdBuildCanal(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, WaterClass wc, bool diagonal)
474 if (start_tile >= Map::Size() || !IsValidWaterClass(wc)) return CMD_ERROR;
476 /* Outside of the editor you can only build canals, not oceans */
477 if (wc != WATER_CLASS_CANAL && _game_mode != GM_EDITOR) return CMD_ERROR;
479 CommandCost cost(EXPENSES_CONSTRUCTION);
481 std::unique_ptr<TileIterator> iter = TileIterator::Create(tile, start_tile, diagonal);
482 for (; *iter != INVALID_TILE; ++(*iter)) {
483 TileIndex current_tile = *iter;
484 CommandCost ret;
486 Slope slope = GetTileSlope(current_tile);
487 if (slope != SLOPE_FLAT && (wc != WATER_CLASS_RIVER || !IsInclinedSlope(slope))) {
488 return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
491 bool water = IsWaterTile(current_tile);
493 /* Outside the editor, prevent building canals over your own or OWNER_NONE owned canals */
494 if (water && IsCanal(current_tile) && _game_mode != GM_EDITOR && (IsTileOwner(current_tile, _current_company) || IsTileOwner(current_tile, OWNER_NONE))) continue;
496 ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, current_tile);
497 if (ret.Failed()) return ret;
499 if (!water) cost.AddCost(ret);
501 if (flags & DC_EXEC) {
502 if (IsTileType(current_tile, MP_WATER) && IsCanal(current_tile)) {
503 Owner owner = GetTileOwner(current_tile);
504 if (Company::IsValidID(owner)) {
505 Company::Get(owner)->infrastructure.water--;
506 DirtyCompanyInfrastructureWindows(owner);
510 switch (wc) {
511 case WATER_CLASS_RIVER:
512 MakeRiver(current_tile, Random());
513 if (_game_mode == GM_EDITOR) {
514 TileIndex tile2 = current_tile;
515 CircularTileSearch(&tile2, RIVER_OFFSET_DESERT_DISTANCE, RiverModifyDesertZone, nullptr);
517 break;
519 case WATER_CLASS_SEA:
520 if (TileHeight(current_tile) == 0) {
521 MakeSea(current_tile);
522 break;
524 [[fallthrough]];
526 default:
527 MakeCanal(current_tile, _current_company, Random());
528 if (Company::IsValidID(_current_company)) {
529 Company::Get(_current_company)->infrastructure.water++;
530 DirtyCompanyInfrastructureWindows(_current_company);
532 break;
534 MarkTileDirtyByTile(current_tile);
535 MarkCanalsAndRiversAroundDirty(current_tile);
536 CheckForDockingTile(current_tile);
539 cost.AddCost(_price[PR_BUILD_CANAL]);
542 if (cost.GetCost() == 0) {
543 return_cmd_error(STR_ERROR_ALREADY_BUILT);
544 } else {
545 return cost;
550 static CommandCost ClearTile_Water(TileIndex tile, DoCommandFlag flags)
552 switch (GetWaterTileType(tile)) {
553 case WATER_TILE_CLEAR: {
554 if (flags & DC_NO_WATER) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER);
556 Money base_cost = IsCanal(tile) ? _price[PR_CLEAR_CANAL] : _price[PR_CLEAR_WATER];
557 /* Make sure freeform edges are allowed or it's not an edge tile. */
558 if (!_settings_game.construction.freeform_edges && (!IsInsideMM(TileX(tile), 1, Map::MaxX() - 1) ||
559 !IsInsideMM(TileY(tile), 1, Map::MaxY() - 1))) {
560 return_cmd_error(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP);
563 /* Make sure no vehicle is on the tile */
564 CommandCost ret = EnsureNoVehicleOnGround(tile);
565 if (ret.Failed()) return ret;
567 Owner owner = GetTileOwner(tile);
568 if (owner != OWNER_WATER && owner != OWNER_NONE) {
569 ret = CheckTileOwnership(tile);
570 if (ret.Failed()) return ret;
573 if (flags & DC_EXEC) {
574 if (IsCanal(tile) && Company::IsValidID(owner)) {
575 Company::Get(owner)->infrastructure.water--;
576 DirtyCompanyInfrastructureWindows(owner);
578 DoClearSquare(tile);
579 MarkCanalsAndRiversAroundDirty(tile);
580 ClearNeighbourNonFloodingStates(tile);
583 return CommandCost(EXPENSES_CONSTRUCTION, base_cost);
586 case WATER_TILE_COAST: {
587 Slope slope = GetTileSlope(tile);
589 /* Make sure no vehicle is on the tile */
590 CommandCost ret = EnsureNoVehicleOnGround(tile);
591 if (ret.Failed()) return ret;
593 if (flags & DC_EXEC) {
594 DoClearSquare(tile);
595 MarkCanalsAndRiversAroundDirty(tile);
596 ClearNeighbourNonFloodingStates(tile);
598 if (IsSlopeWithOneCornerRaised(slope)) {
599 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_WATER]);
600 } else {
601 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_ROUGH]);
605 case WATER_TILE_LOCK: {
606 static const TileIndexDiffC _lock_tomiddle_offs[][DIAGDIR_END] = {
607 /* NE SE SW NW */
608 { { 0, 0}, {0, 0}, { 0, 0}, {0, 0} }, // LOCK_PART_MIDDLE
609 { {-1, 0}, {0, 1}, { 1, 0}, {0, -1} }, // LOCK_PART_LOWER
610 { { 1, 0}, {0, -1}, {-1, 0}, {0, 1} }, // LOCK_PART_UPPER
613 if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
614 if (_current_company == OWNER_WATER) return CMD_ERROR;
615 /* move to the middle tile.. */
616 return RemoveLock(tile + ToTileIndexDiff(_lock_tomiddle_offs[GetLockPart(tile)][GetLockDirection(tile)]), flags);
619 case WATER_TILE_DEPOT:
620 if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
621 return RemoveShipDepot(tile, flags);
623 default:
624 NOT_REACHED();
629 * return true if a tile is a water tile wrt. a certain direction.
631 * @param tile The tile of interest.
632 * @param from The direction of interest.
633 * @return true iff the tile is water in the view of 'from'.
636 bool IsWateredTile(TileIndex tile, Direction from)
638 switch (GetTileType(tile)) {
639 case MP_WATER:
640 switch (GetWaterTileType(tile)) {
641 default: NOT_REACHED();
642 case WATER_TILE_DEPOT: case WATER_TILE_CLEAR: return true;
643 case WATER_TILE_LOCK: return DiagDirToAxis(GetLockDirection(tile)) == DiagDirToAxis(DirToDiagDir(from));
645 case WATER_TILE_COAST:
646 switch (GetTileSlope(tile)) {
647 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
648 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
649 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
650 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
651 default: return false;
655 case MP_RAILWAY:
656 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
657 assert(IsPlainRail(tile));
658 switch (GetTileSlope(tile)) {
659 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
660 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
661 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
662 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
663 default: return false;
666 return false;
668 case MP_STATION:
669 if (IsOilRig(tile)) {
670 /* Do not draw waterborders inside of industries.
671 * Note: There is no easy way to detect the industry of an oilrig tile. */
672 TileIndex src_tile = tile + TileOffsByDir(from);
673 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
674 (IsTileType(src_tile, MP_INDUSTRY))) return true;
676 return IsTileOnWater(tile);
678 return (IsDock(tile) && IsTileFlat(tile)) || IsBuoy(tile);
680 case MP_INDUSTRY: {
681 /* Do not draw waterborders inside of industries.
682 * Note: There is no easy way to detect the industry of an oilrig tile. */
683 TileIndex src_tile = tile + TileOffsByDir(from);
684 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
685 (IsTileType(src_tile, MP_INDUSTRY) && GetIndustryIndex(src_tile) == GetIndustryIndex(tile))) return true;
687 return IsTileOnWater(tile);
690 case MP_OBJECT: return IsTileOnWater(tile);
692 case MP_TUNNELBRIDGE: return GetTunnelBridgeTransportType(tile) == TRANSPORT_WATER && ReverseDiagDir(GetTunnelBridgeDirection(tile)) == DirToDiagDir(from);
694 case MP_VOID: return true; // consider map border as water, esp. for rivers
696 default: return false;
701 * Draw a water sprite, potentially with a NewGRF-modified sprite offset.
702 * @param base Sprite base.
703 * @param offset Sprite offset.
704 * @param feature The type of sprite that is drawn.
705 * @param tile Tile index to draw.
707 static void DrawWaterSprite(SpriteID base, uint offset, CanalFeature feature, TileIndex tile)
709 if (base != SPR_FLAT_WATER_TILE) {
710 /* Only call offset callback if the sprite is NewGRF-provided. */
711 offset = GetCanalSpriteOffset(feature, tile, offset);
713 DrawGroundSprite(base + offset, PAL_NONE);
717 * Draw canal or river edges.
718 * @param canal True if canal edges should be drawn, false for river edges.
719 * @param offset Sprite offset.
720 * @param tile Tile to draw.
722 static void DrawWaterEdges(bool canal, uint offset, TileIndex tile)
724 CanalFeature feature;
725 SpriteID base = 0;
726 if (canal) {
727 feature = CF_DIKES;
728 base = GetCanalSprite(CF_DIKES, tile);
729 if (base == 0) base = SPR_CANAL_DIKES_BASE;
730 } else {
731 feature = CF_RIVER_EDGE;
732 base = GetCanalSprite(CF_RIVER_EDGE, tile);
733 if (base == 0) return; // Don't draw if no sprites provided.
736 uint wa;
738 /* determine the edges around with water. */
739 wa = IsWateredTile(TileAddXY(tile, -1, 0), DIR_SW) << 0;
740 wa += IsWateredTile(TileAddXY(tile, 0, 1), DIR_NW) << 1;
741 wa += IsWateredTile(TileAddXY(tile, 1, 0), DIR_NE) << 2;
742 wa += IsWateredTile(TileAddXY(tile, 0, -1), DIR_SE) << 3;
744 if (!(wa & 1)) DrawWaterSprite(base, offset, feature, tile);
745 if (!(wa & 2)) DrawWaterSprite(base, offset + 1, feature, tile);
746 if (!(wa & 4)) DrawWaterSprite(base, offset + 2, feature, tile);
747 if (!(wa & 8)) DrawWaterSprite(base, offset + 3, feature, tile);
749 /* right corner */
750 switch (wa & 0x03) {
751 case 0: DrawWaterSprite(base, offset + 4, feature, tile); break;
752 case 3: if (!IsWateredTile(TileAddXY(tile, -1, 1), DIR_W)) DrawWaterSprite(base, offset + 8, feature, tile); break;
755 /* bottom corner */
756 switch (wa & 0x06) {
757 case 0: DrawWaterSprite(base, offset + 5, feature, tile); break;
758 case 6: if (!IsWateredTile(TileAddXY(tile, 1, 1), DIR_N)) DrawWaterSprite(base, offset + 9, feature, tile); break;
761 /* left corner */
762 switch (wa & 0x0C) {
763 case 0: DrawWaterSprite(base, offset + 6, feature, tile); break;
764 case 12: if (!IsWateredTile(TileAddXY(tile, 1, -1), DIR_E)) DrawWaterSprite(base, offset + 10, feature, tile); break;
767 /* upper corner */
768 switch (wa & 0x09) {
769 case 0: DrawWaterSprite(base, offset + 7, feature, tile); break;
770 case 9: if (!IsWateredTile(TileAddXY(tile, -1, -1), DIR_S)) DrawWaterSprite(base, offset + 11, feature, tile); break;
774 /** Draw a plain sea water tile with no edges */
775 static void DrawSeaWater(TileIndex)
777 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
780 /** draw a canal styled water tile with dikes around */
781 static void DrawCanalWater(TileIndex tile)
783 SpriteID image = SPR_FLAT_WATER_TILE;
784 if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
785 /* First water slope sprite is flat water. */
786 image = GetCanalSprite(CF_WATERSLOPE, tile);
787 if (image == 0) image = SPR_FLAT_WATER_TILE;
789 DrawWaterSprite(image, 0, CF_WATERSLOPE, tile);
791 DrawWaterEdges(true, 0, tile);
794 #include "table/water_land.h"
797 * Draw a build sprite sequence for water tiles.
798 * If buildings are invisible, nothing will be drawn.
799 * @param ti Tile info.
800 * @param dtss Sprite sequence to draw.
801 * @param base Base sprite.
802 * @param offset Additional sprite offset.
803 * @param palette Palette to use.
805 static void DrawWaterTileStruct(const TileInfo *ti, const DrawTileSeqStruct *dtss, SpriteID base, uint offset, PaletteID palette, CanalFeature feature)
807 /* Don't draw if buildings are invisible. */
808 if (IsInvisibilitySet(TO_BUILDINGS)) return;
810 for (; !dtss->IsTerminator(); dtss++) {
811 uint tile_offs = offset + dtss->image.sprite;
812 if (feature < CF_END) tile_offs = GetCanalSpriteOffset(feature, ti->tile, tile_offs);
813 AddSortableSpriteToDraw(base + tile_offs, palette,
814 ti->x + dtss->delta_x, ti->y + dtss->delta_y,
815 dtss->size_x, dtss->size_y,
816 dtss->size_z, ti->z + dtss->delta_z,
817 IsTransparencySet(TO_BUILDINGS));
821 /** Draw a lock tile. */
822 static void DrawWaterLock(const TileInfo *ti)
824 int part = GetLockPart(ti->tile);
825 const DrawTileSprites &dts = _lock_display_data[part][GetLockDirection(ti->tile)];
827 /* Draw ground sprite. */
828 SpriteID image = dts.ground.sprite;
830 SpriteID water_base = GetCanalSprite(CF_WATERSLOPE, ti->tile);
831 if (water_base == 0) {
832 /* Use default sprites. */
833 water_base = SPR_CANALS_BASE;
834 } else if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
835 /* NewGRF supplies a flat sprite as first sprite. */
836 if (image == SPR_FLAT_WATER_TILE) {
837 image = water_base;
838 } else {
839 image++;
843 if (image < 5) image += water_base;
844 DrawGroundSprite(image, PAL_NONE);
846 /* Draw structures. */
847 uint zoffs = 0;
848 SpriteID base = GetCanalSprite(CF_LOCKS, ti->tile);
850 if (base == 0) {
851 /* If no custom graphics, use defaults. */
852 base = SPR_LOCK_BASE;
853 uint8_t z_threshold = part == LOCK_PART_UPPER ? 8 : 0;
854 zoffs = ti->z > z_threshold ? 24 : 0;
857 DrawWaterTileStruct(ti, dts.seq, base, zoffs, PAL_NONE, CF_LOCKS);
860 /** Draw a ship depot tile. */
861 static void DrawWaterDepot(const TileInfo *ti)
863 DrawWaterClassGround(ti);
864 DrawWaterTileStruct(ti, _shipdepot_display_data[GetShipDepotAxis(ti->tile)][GetShipDepotPart(ti->tile)].seq, 0, 0, COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile)), CF_END);
867 static void DrawRiverWater(const TileInfo *ti)
869 SpriteID image = SPR_FLAT_WATER_TILE;
870 uint offset = 0;
871 uint edges_offset = 0;
873 if (ti->tileh != SLOPE_FLAT || HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
874 image = GetCanalSprite(CF_RIVER_SLOPE, ti->tile);
875 if (image == 0) {
876 switch (ti->tileh) {
877 case SLOPE_NW: image = SPR_WATER_SLOPE_Y_DOWN; break;
878 case SLOPE_SW: image = SPR_WATER_SLOPE_X_UP; break;
879 case SLOPE_SE: image = SPR_WATER_SLOPE_Y_UP; break;
880 case SLOPE_NE: image = SPR_WATER_SLOPE_X_DOWN; break;
881 default: image = SPR_FLAT_WATER_TILE; break;
883 } else {
884 /* Flag bit 0 indicates that the first sprite is flat water. */
885 offset = HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE) ? 1 : 0;
887 switch (ti->tileh) {
888 case SLOPE_SE: edges_offset += 12; break;
889 case SLOPE_NE: offset += 1; edges_offset += 24; break;
890 case SLOPE_SW: offset += 2; edges_offset += 36; break;
891 case SLOPE_NW: offset += 3; edges_offset += 48; break;
892 default: offset = 0; break;
895 offset = GetCanalSpriteOffset(CF_RIVER_SLOPE, ti->tile, offset);
899 DrawGroundSprite(image + offset, PAL_NONE);
901 /* Draw river edges if available. */
902 DrawWaterEdges(false, edges_offset, ti->tile);
905 void DrawShoreTile(Slope tileh)
907 /* Converts the enum Slope into an offset based on SPR_SHORE_BASE.
908 * This allows to calculate the proper sprite to display for this Slope */
909 static const uint8_t tileh_to_shoresprite[32] = {
910 0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0,
911 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 15, 0,
914 assert(!IsHalftileSlope(tileh)); // Halftile slopes need to get handled earlier.
915 assert(tileh != SLOPE_FLAT); // Shore is never flat
917 assert((tileh != SLOPE_EW) && (tileh != SLOPE_NS)); // No suitable sprites for current flooding behaviour
919 DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[tileh], PAL_NONE);
922 void DrawWaterClassGround(const TileInfo *ti)
924 switch (GetWaterClass(ti->tile)) {
925 case WATER_CLASS_SEA: DrawSeaWater(ti->tile); break;
926 case WATER_CLASS_CANAL: DrawCanalWater(ti->tile); break;
927 case WATER_CLASS_RIVER: DrawRiverWater(ti); break;
928 default: NOT_REACHED();
932 static void DrawTile_Water(TileInfo *ti)
934 switch (GetWaterTileType(ti->tile)) {
935 case WATER_TILE_CLEAR:
936 DrawWaterClassGround(ti);
937 DrawBridgeMiddle(ti);
938 break;
940 case WATER_TILE_COAST: {
941 DrawShoreTile(ti->tileh);
942 DrawBridgeMiddle(ti);
943 break;
946 case WATER_TILE_LOCK:
947 DrawWaterLock(ti);
948 break;
950 case WATER_TILE_DEPOT:
951 DrawWaterDepot(ti);
952 break;
956 void DrawShipDepotSprite(int x, int y, Axis axis, DepotPart part)
958 const DrawTileSprites &dts = _shipdepot_display_data[axis][part];
960 DrawSprite(dts.ground.sprite, dts.ground.pal, x, y);
961 DrawOrigTileSeqInGUI(x, y, &dts, COMPANY_SPRITE_COLOUR(_local_company));
965 static int GetSlopePixelZ_Water(TileIndex tile, uint x, uint y, bool)
967 auto [tileh, z] = GetTilePixelSlope(tile);
969 return z + GetPartialPixelZ(x & 0xF, y & 0xF, tileh);
972 static Foundation GetFoundation_Water(TileIndex, Slope)
974 return FOUNDATION_NONE;
977 static void GetTileDesc_Water(TileIndex tile, TileDesc *td)
979 switch (GetWaterTileType(tile)) {
980 case WATER_TILE_CLEAR:
981 switch (GetWaterClass(tile)) {
982 case WATER_CLASS_SEA: td->str = STR_LAI_WATER_DESCRIPTION_WATER; break;
983 case WATER_CLASS_CANAL: td->str = STR_LAI_WATER_DESCRIPTION_CANAL; break;
984 case WATER_CLASS_RIVER: td->str = STR_LAI_WATER_DESCRIPTION_RIVER; break;
985 default: NOT_REACHED();
987 break;
988 case WATER_TILE_COAST: td->str = STR_LAI_WATER_DESCRIPTION_COAST_OR_RIVERBANK; break;
989 case WATER_TILE_LOCK : td->str = STR_LAI_WATER_DESCRIPTION_LOCK; break;
990 case WATER_TILE_DEPOT:
991 td->str = STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT;
992 td->build_date = Depot::GetByTile(tile)->build_date;
993 break;
994 default: NOT_REACHED();
997 td->owner[0] = GetTileOwner(tile);
1001 * Handle the flooding of a vehicle. This sets the vehicle state to crashed,
1002 * creates a newsitem and dirties the necessary windows.
1003 * @param v The vehicle to flood.
1005 static void FloodVehicle(Vehicle *v)
1007 uint victims = v->Crash(true);
1009 AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_FLOODED, victims));
1010 Game::NewEvent(new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_FLOODED, victims));
1011 SetDParam(0, victims);
1012 AddTileNewsItem(STR_NEWS_DISASTER_FLOOD_VEHICLE, NT_ACCIDENT, v->tile);
1013 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
1014 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v);
1018 * Flood a vehicle if we are allowed to flood it, i.e. when it is on the ground.
1019 * @param v The vehicle to test for flooding.
1020 * @param data The z of level to flood.
1021 * @return nullptr as we always want to remove everything.
1023 static Vehicle *FloodVehicleProc(Vehicle *v, void *data)
1025 if ((v->vehstatus & VS_CRASHED) != 0) return nullptr;
1027 switch (v->type) {
1028 default: break;
1030 case VEH_AIRCRAFT: {
1031 if (!IsAirportTile(v->tile) || GetTileMaxZ(v->tile) != 0) break;
1032 if (v->subtype == AIR_SHADOW) break;
1034 /* We compare v->z_pos against delta_z + 1 because the shadow
1035 * is at delta_z and the actual aircraft at delta_z + 1. */
1036 const Station *st = Station::GetByTile(v->tile);
1037 const AirportFTAClass *airport = st->airport.GetFTA();
1038 if (v->z_pos != airport->delta_z + 1) break;
1040 FloodVehicle(v);
1041 break;
1044 case VEH_TRAIN:
1045 case VEH_ROAD: {
1046 int z = *(int*)data;
1047 if (v->z_pos > z) break;
1048 FloodVehicle(v->First());
1049 break;
1053 return nullptr;
1057 * Finds a vehicle to flood.
1058 * It does not find vehicles that are already crashed on bridges, i.e. flooded.
1059 * @param tile the tile where to find a vehicle to flood
1061 static void FloodVehicles(TileIndex tile)
1063 int z = 0;
1065 if (IsAirportTile(tile)) {
1066 const Station *st = Station::GetByTile(tile);
1067 for (TileIndex airport_tile : st->airport) {
1068 if (st->TileBelongsToAirport(airport_tile)) FindVehicleOnPos(airport_tile, &z, &FloodVehicleProc);
1071 /* No vehicle could be flooded on this airport anymore */
1072 return;
1075 if (!IsBridgeTile(tile)) {
1076 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
1077 return;
1080 TileIndex end = GetOtherBridgeEnd(tile);
1081 z = GetBridgePixelHeight(tile);
1083 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
1084 FindVehicleOnPos(end, &z, &FloodVehicleProc);
1088 * Returns the behaviour of a tile during flooding.
1090 * @return Behaviour of the tile
1092 FloodingBehaviour GetFloodingBehaviour(TileIndex tile)
1094 /* FLOOD_ACTIVE: 'single-corner-raised'-coast, sea, sea-shipdepots, sea-buoys, sea-docks (water part), rail with flooded halftile, sea-water-industries, sea-oilrigs
1095 * FLOOD_DRYUP: coast with more than one corner raised, coast with rail-track, coast with trees
1096 * FLOOD_PASSIVE: (not used)
1097 * FLOOD_NONE: canals, rivers, everything else
1099 switch (GetTileType(tile)) {
1100 case MP_WATER:
1101 if (IsCoast(tile)) {
1102 Slope tileh = GetTileSlope(tile);
1103 return (IsSlopeWithOneCornerRaised(tileh) ? FLOOD_ACTIVE : FLOOD_DRYUP);
1105 [[fallthrough]];
1106 case MP_STATION:
1107 case MP_INDUSTRY:
1108 case MP_OBJECT:
1109 return (GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE;
1111 case MP_RAILWAY:
1112 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
1113 return (IsSlopeWithOneCornerRaised(GetTileSlope(tile)) ? FLOOD_ACTIVE : FLOOD_DRYUP);
1115 return FLOOD_NONE;
1117 case MP_TREES:
1118 return (GetTreeGround(tile) == TREE_GROUND_SHORE ? FLOOD_DRYUP : FLOOD_NONE);
1120 case MP_VOID:
1121 return FLOOD_ACTIVE;
1123 default:
1124 return FLOOD_NONE;
1129 * Floods a tile.
1131 static void DoFloodTile(TileIndex target)
1133 assert(!IsTileType(target, MP_WATER));
1135 bool flooded = false; // Will be set to true if something is changed.
1137 Backup<CompanyID> cur_company(_current_company, OWNER_WATER);
1139 Slope tileh = GetTileSlope(target);
1140 if (tileh != SLOPE_FLAT) {
1141 /* make coast.. */
1142 switch (GetTileType(target)) {
1143 case MP_RAILWAY: {
1144 if (!IsPlainRail(target)) break;
1145 FloodVehicles(target);
1146 flooded = FloodHalftile(target);
1147 break;
1150 case MP_TREES:
1151 if (!IsSlopeWithOneCornerRaised(tileh)) {
1152 SetTreeGroundDensity(target, TREE_GROUND_SHORE, 3);
1153 MarkTileDirtyByTile(target);
1154 flooded = true;
1155 break;
1157 [[fallthrough]];
1159 case MP_CLEAR:
1160 if (Command<CMD_LANDSCAPE_CLEAR>::Do(DC_EXEC, target).Succeeded()) {
1161 MakeShore(target);
1162 MarkTileDirtyByTile(target);
1163 flooded = true;
1165 break;
1167 default:
1168 break;
1170 } else {
1171 /* Flood vehicles */
1172 FloodVehicles(target);
1174 /* flood flat tile */
1175 if (Command<CMD_LANDSCAPE_CLEAR>::Do(DC_EXEC, target).Succeeded()) {
1176 MakeSea(target);
1177 MarkTileDirtyByTile(target);
1178 flooded = true;
1182 if (flooded) {
1183 /* Mark surrounding canal tiles dirty too to avoid glitches */
1184 MarkCanalsAndRiversAroundDirty(target);
1186 /* update signals if needed */
1187 UpdateSignalsInBuffer();
1189 if (IsPossibleDockingTile(target)) CheckForDockingTile(target);
1192 cur_company.Restore();
1196 * Drys a tile up.
1198 static void DoDryUp(TileIndex tile)
1200 Backup<CompanyID> cur_company(_current_company, OWNER_WATER);
1202 switch (GetTileType(tile)) {
1203 case MP_RAILWAY:
1204 assert(IsPlainRail(tile));
1205 assert(GetRailGroundType(tile) == RAIL_GROUND_WATER);
1207 RailGroundType new_ground;
1208 switch (GetTrackBits(tile)) {
1209 case TRACK_BIT_UPPER: new_ground = RAIL_GROUND_FENCE_HORIZ1; break;
1210 case TRACK_BIT_LOWER: new_ground = RAIL_GROUND_FENCE_HORIZ2; break;
1211 case TRACK_BIT_LEFT: new_ground = RAIL_GROUND_FENCE_VERT1; break;
1212 case TRACK_BIT_RIGHT: new_ground = RAIL_GROUND_FENCE_VERT2; break;
1213 default: NOT_REACHED();
1215 SetRailGroundType(tile, new_ground);
1216 MarkTileDirtyByTile(tile);
1217 break;
1219 case MP_TREES:
1220 SetTreeGroundDensity(tile, TREE_GROUND_GRASS, 3);
1221 MarkTileDirtyByTile(tile);
1222 break;
1224 case MP_WATER:
1225 assert(IsCoast(tile));
1227 if (Command<CMD_LANDSCAPE_CLEAR>::Do(DC_EXEC, tile).Succeeded()) {
1228 MakeClear(tile, CLEAR_GRASS, 3);
1229 MarkTileDirtyByTile(tile);
1231 break;
1233 default: NOT_REACHED();
1236 cur_company.Restore();
1240 * Let a water tile floods its diagonal adjoining tiles
1241 * called from tunnelbridge_cmd, and by TileLoop_Industry() and TileLoop_Track()
1243 * @param tile the water/shore tile that floods
1245 void TileLoop_Water(TileIndex tile)
1247 if (IsTileType(tile, MP_WATER)) {
1248 AmbientSoundEffect(tile);
1249 if (IsNonFloodingWaterTile(tile)) return;
1252 switch (GetFloodingBehaviour(tile)) {
1253 case FLOOD_ACTIVE: {
1254 bool continue_flooding = false;
1255 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
1256 TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir(dir));
1257 /* Contrary to drying up, flooding does not consider MP_VOID tiles. */
1258 if (!IsValidTile(dest)) continue;
1259 /* do not try to flood water tiles - increases performance a lot */
1260 if (IsTileType(dest, MP_WATER)) continue;
1262 /* Buoys and docks cannot be flooded, and when removed turn into flooding water. */
1263 if (IsTileType(dest, MP_STATION) && (IsBuoy(dest) || IsDock(dest))) continue;
1265 /* This neighbour tile might be floodable later if the tile is cleared, so allow flooding to continue. */
1266 continue_flooding = true;
1268 /* TREE_GROUND_SHORE is the sign of a previous flood. */
1269 if (IsTileType(dest, MP_TREES) && GetTreeGround(dest) == TREE_GROUND_SHORE) continue;
1271 auto [slope_dest, z_dest] = GetFoundationSlope(dest);
1272 if (z_dest > 0) continue;
1274 if (!HasBit(_flood_from_dirs[slope_dest & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP], ReverseDir(dir))) continue;
1276 DoFloodTile(dest);
1278 if (!continue_flooding && IsTileType(tile, MP_WATER)) SetNonFloodingWaterTile(tile, true);
1279 break;
1282 case FLOOD_DRYUP: {
1283 Slope slope_here = std::get<0>(GetFoundationSlope(tile)) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
1284 for (Direction dir : SetBitIterator<Direction>(_flood_from_dirs[slope_here])) {
1285 TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir(dir));
1286 /* Contrary to flooding, drying up does consider MP_VOID tiles. */
1287 if (dest == INVALID_TILE) continue;
1289 FloodingBehaviour dest_behaviour = GetFloodingBehaviour(dest);
1290 if ((dest_behaviour == FLOOD_ACTIVE) || (dest_behaviour == FLOOD_PASSIVE)) return;
1292 DoDryUp(tile);
1293 break;
1296 default: return;
1300 void ConvertGroundTilesIntoWaterTiles()
1302 for (TileIndex tile = 0; tile < Map::Size(); ++tile) {
1303 auto [slope, z] = GetTileSlopeZ(tile);
1304 if (IsTileType(tile, MP_CLEAR) && z == 0) {
1305 /* Make both water for tiles at level 0
1306 * and make shore, as that looks much better
1307 * during the generation. */
1308 switch (slope) {
1309 case SLOPE_FLAT:
1310 MakeSea(tile);
1311 break;
1313 case SLOPE_N:
1314 case SLOPE_E:
1315 case SLOPE_S:
1316 case SLOPE_W:
1317 MakeShore(tile);
1318 break;
1320 default:
1321 for (Direction dir : SetBitIterator<Direction>(_flood_from_dirs[slope & ~SLOPE_STEEP])) {
1322 TileIndex dest = TileAddByDir(tile, dir);
1323 Slope slope_dest = GetTileSlope(dest) & ~SLOPE_STEEP;
1324 if (slope_dest == SLOPE_FLAT || IsSlopeWithOneCornerRaised(slope_dest) || IsTileType(dest, MP_VOID)) {
1325 MakeShore(tile);
1326 break;
1329 break;
1335 static TrackStatus GetTileTrackStatus_Water(TileIndex tile, TransportType mode, uint, DiagDirection)
1337 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,
1338 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};
1340 TrackBits ts;
1342 if (mode != TRANSPORT_WATER) return 0;
1344 switch (GetWaterTileType(tile)) {
1345 case WATER_TILE_CLEAR: ts = IsTileFlat(tile) ? TRACK_BIT_ALL : TRACK_BIT_NONE; break;
1346 case WATER_TILE_COAST: ts = coast_tracks[GetTileSlope(tile) & 0xF]; break;
1347 case WATER_TILE_LOCK: ts = DiagDirToDiagTrackBits(GetLockDirection(tile)); break;
1348 case WATER_TILE_DEPOT: ts = AxisToTrackBits(GetShipDepotAxis(tile)); break;
1349 default: return 0;
1351 if (TileX(tile) == 0) {
1352 /* NE border: remove tracks that connects NE tile edge */
1353 ts &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
1355 if (TileY(tile) == 0) {
1356 /* NW border: remove tracks that connects NW tile edge */
1357 ts &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
1359 return CombineTrackStatus(TrackBitsToTrackdirBits(ts), TRACKDIR_BIT_NONE);
1362 static bool ClickTile_Water(TileIndex tile)
1364 if (GetWaterTileType(tile) == WATER_TILE_DEPOT) {
1365 ShowDepotWindow(GetShipDepotNorthTile(tile), VEH_SHIP);
1366 return true;
1368 return false;
1371 static void ChangeTileOwner_Water(TileIndex tile, Owner old_owner, Owner new_owner)
1373 if (!IsTileOwner(tile, old_owner)) return;
1375 bool is_lock_middle = IsLock(tile) && GetLockPart(tile) == LOCK_PART_MIDDLE;
1377 /* No need to dirty company windows here, we'll redraw the whole screen anyway. */
1378 if (is_lock_middle) Company::Get(old_owner)->infrastructure.water -= 3 * LOCK_DEPOT_TILE_FACTOR; // Lock has three parts.
1379 if (new_owner != INVALID_OWNER) {
1380 if (is_lock_middle) Company::Get(new_owner)->infrastructure.water += 3 * LOCK_DEPOT_TILE_FACTOR; // Lock has three parts.
1381 /* Only subtract from the old owner here if the new owner is valid,
1382 * otherwise we clear ship depots and canal water below. */
1383 if (GetWaterClass(tile) == WATER_CLASS_CANAL && !is_lock_middle) {
1384 Company::Get(old_owner)->infrastructure.water--;
1385 Company::Get(new_owner)->infrastructure.water++;
1387 if (IsShipDepot(tile)) {
1388 Company::Get(old_owner)->infrastructure.water -= LOCK_DEPOT_TILE_FACTOR;
1389 Company::Get(new_owner)->infrastructure.water += LOCK_DEPOT_TILE_FACTOR;
1392 SetTileOwner(tile, new_owner);
1393 return;
1396 /* Remove depot */
1397 if (IsShipDepot(tile)) Command<CMD_LANDSCAPE_CLEAR>::Do(DC_EXEC | DC_BANKRUPT, tile);
1399 /* Set owner of canals and locks ... and also canal under dock there was before.
1400 * Check if the new owner after removing depot isn't OWNER_WATER. */
1401 if (IsTileOwner(tile, old_owner)) {
1402 if (GetWaterClass(tile) == WATER_CLASS_CANAL && !is_lock_middle) Company::Get(old_owner)->infrastructure.water--;
1403 SetTileOwner(tile, OWNER_NONE);
1407 static VehicleEnterTileStatus VehicleEnter_Water(Vehicle *, TileIndex, int, int)
1409 return VETSB_CONTINUE;
1412 static CommandCost TerraformTile_Water(TileIndex tile, DoCommandFlag flags, int, Slope)
1414 /* Canals can't be terraformed */
1415 if (IsWaterTile(tile) && IsCanal(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_CANAL_FIRST);
1417 return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
1421 extern const TileTypeProcs _tile_type_water_procs = {
1422 DrawTile_Water, // draw_tile_proc
1423 GetSlopePixelZ_Water, // get_slope_z_proc
1424 ClearTile_Water, // clear_tile_proc
1425 nullptr, // add_accepted_cargo_proc
1426 GetTileDesc_Water, // get_tile_desc_proc
1427 GetTileTrackStatus_Water, // get_tile_track_status_proc
1428 ClickTile_Water, // click_tile_proc
1429 nullptr, // animate_tile_proc
1430 TileLoop_Water, // tile_loop_proc
1431 ChangeTileOwner_Water, // change_tile_owner_proc
1432 nullptr, // add_produced_cargo_proc
1433 VehicleEnter_Water, // vehicle_enter_tile_proc
1434 GetFoundation_Water, // get_foundation_proc
1435 TerraformTile_Water, // terraform_tile_proc