Codechange: Move flags in CommandProc in front of the command arguments.
[openttd-github.git] / src / water_cmd.cpp
blob145b8084dc36abbe49f08315a64f612322626559
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 "cmd_helper.h"
12 #include "landscape.h"
13 #include "viewport_func.h"
14 #include "command_func.h"
15 #include "town.h"
16 #include "news_func.h"
17 #include "depot_base.h"
18 #include "depot_func.h"
19 #include "water.h"
20 #include "industry_map.h"
21 #include "newgrf_canal.h"
22 #include "strings_func.h"
23 #include "vehicle_func.h"
24 #include "sound_func.h"
25 #include "company_func.h"
26 #include "clear_map.h"
27 #include "tree_map.h"
28 #include "aircraft.h"
29 #include "effectvehicle_func.h"
30 #include "tunnelbridge_map.h"
31 #include "station_base.h"
32 #include "ai/ai.hpp"
33 #include "game/game.hpp"
34 #include "core/random_func.hpp"
35 #include "core/backup_type.hpp"
36 #include "date_func.h"
37 #include "company_base.h"
38 #include "company_gui.h"
39 #include "newgrf_generic.h"
40 #include "industry.h"
41 #include "water_cmd.h"
43 #include "table/strings.h"
45 #include "safeguards.h"
47 /**
48 * Describes from which directions a specific slope can be flooded (if the tile is floodable at all).
50 static const uint8 _flood_from_dirs[] = {
51 (1 << DIR_NW) | (1 << DIR_SW) | (1 << DIR_SE) | (1 << DIR_NE), // SLOPE_FLAT
52 (1 << DIR_NE) | (1 << DIR_SE), // SLOPE_W
53 (1 << DIR_NW) | (1 << DIR_NE), // SLOPE_S
54 (1 << DIR_NE), // SLOPE_SW
55 (1 << DIR_NW) | (1 << DIR_SW), // SLOPE_E
56 0, // SLOPE_EW
57 (1 << DIR_NW), // SLOPE_SE
58 (1 << DIR_N ) | (1 << DIR_NW) | (1 << DIR_NE), // SLOPE_WSE, SLOPE_STEEP_S
59 (1 << DIR_SW) | (1 << DIR_SE), // SLOPE_N
60 (1 << DIR_SE), // SLOPE_NW
61 0, // SLOPE_NS
62 (1 << DIR_E ) | (1 << DIR_NE) | (1 << DIR_SE), // SLOPE_NWS, SLOPE_STEEP_W
63 (1 << DIR_SW), // SLOPE_NE
64 (1 << DIR_S ) | (1 << DIR_SW) | (1 << DIR_SE), // SLOPE_ENW, SLOPE_STEEP_N
65 (1 << DIR_W ) | (1 << DIR_SW) | (1 << DIR_NW), // SLOPE_SEN, SLOPE_STEEP_E
68 /**
69 * Marks tile dirty if it is a canal or river tile.
70 * Called to avoid glitches when flooding tiles next to canal tile.
72 * @param tile tile to check
74 static inline void MarkTileDirtyIfCanalOrRiver(TileIndex tile)
76 if (IsValidTile(tile) && IsTileType(tile, MP_WATER) && (IsCanal(tile) || IsRiver(tile))) MarkTileDirtyByTile(tile);
79 /**
80 * Marks the tiles around a tile as dirty, if they are canals or rivers.
82 * @param tile The center of the tile where all other tiles are marked as dirty
83 * @ingroup dirty
85 static void MarkCanalsAndRiversAroundDirty(TileIndex tile)
87 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
88 MarkTileDirtyIfCanalOrRiver(tile + TileOffsByDir(dir));
93 /**
94 * Build a ship depot.
95 * @param flags type of operation
96 * @param tile tile where ship depot is built
97 * @param p1 bit 0 depot orientation (Axis)
98 * @param p2 unused
99 * @param text unused
100 * @return the cost of this operation or an error
102 CommandCost CmdBuildShipDepot(DoCommandFlag flags, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
104 Axis axis = Extract<Axis, 0, 1>(p1);
106 TileIndex tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
108 if (!HasTileWaterGround(tile) || !HasTileWaterGround(tile2)) {
109 return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER);
112 if (IsBridgeAbove(tile) || IsBridgeAbove(tile2)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
114 if (!IsTileFlat(tile) || !IsTileFlat(tile2)) {
115 /* Prevent depots on rapids */
116 return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
119 if (!Depot::CanAllocateItem()) return CMD_ERROR;
121 WaterClass wc1 = GetWaterClass(tile);
122 WaterClass wc2 = GetWaterClass(tile2);
123 CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_DEPOT_SHIP]);
125 bool add_cost = !IsWaterTile(tile);
126 CommandCost ret = DoCommand(flags | DC_AUTO, CMD_LANDSCAPE_CLEAR, tile, 0, 0);
127 if (ret.Failed()) return ret;
128 if (add_cost) {
129 cost.AddCost(ret);
131 add_cost = !IsWaterTile(tile2);
132 ret = DoCommand(flags | DC_AUTO, CMD_LANDSCAPE_CLEAR, tile2, 0, 0);
133 if (ret.Failed()) return ret;
134 if (add_cost) {
135 cost.AddCost(ret);
138 if (flags & DC_EXEC) {
139 Depot *depot = new Depot(tile);
140 depot->build_date = _date;
142 if (wc1 == WATER_CLASS_CANAL || wc2 == WATER_CLASS_CANAL) {
143 /* Update infrastructure counts after the unconditional clear earlier. */
144 Company::Get(_current_company)->infrastructure.water += wc1 == WATER_CLASS_CANAL && wc2 == WATER_CLASS_CANAL ? 2 : 1;
146 Company::Get(_current_company)->infrastructure.water += 2 * LOCK_DEPOT_TILE_FACTOR;
147 DirtyCompanyInfrastructureWindows(_current_company);
149 MakeShipDepot(tile, _current_company, depot->index, DEPOT_PART_NORTH, axis, wc1);
150 MakeShipDepot(tile2, _current_company, depot->index, DEPOT_PART_SOUTH, axis, wc2);
151 CheckForDockingTile(tile);
152 CheckForDockingTile(tile2);
153 MarkTileDirtyByTile(tile);
154 MarkTileDirtyByTile(tile2);
155 MakeDefaultName(depot);
158 return cost;
161 bool IsPossibleDockingTile(TileIndex t)
163 assert(IsValidTile(t));
164 switch (GetTileType(t)) {
165 case MP_WATER:
166 if (IsLock(t) && GetLockPart(t) == LOCK_PART_MIDDLE) return false;
167 FALLTHROUGH;
168 case MP_RAILWAY:
169 case MP_STATION:
170 case MP_TUNNELBRIDGE:
171 return TrackStatusToTrackBits(GetTileTrackStatus(t, TRANSPORT_WATER, 0)) != TRACK_BIT_NONE;
173 default:
174 return false;
179 * Mark the supplied tile as a docking tile if it is suitable for docking.
180 * Tiles surrounding the tile are tested to be docks with correct orientation.
181 * @param t Tile to test.
183 void CheckForDockingTile(TileIndex t)
185 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
186 TileIndex tile = t + TileOffsByDiagDir(d);
187 if (!IsValidTile(tile)) continue;
189 if (IsDockTile(tile) && IsValidDockingDirectionForDock(tile, d)) {
190 Station::GetByTile(tile)->docking_station.Add(t);
191 SetDockingTile(t, true);
193 if (IsTileType(tile, MP_INDUSTRY)) {
194 Station *st = Industry::GetByTile(tile)->neutral_station;
195 if (st != nullptr) {
196 st->docking_station.Add(t);
197 SetDockingTile(t, true);
200 if (IsTileType(tile, MP_STATION) && IsOilRig(tile)) {
201 Station::GetByTile(tile)->docking_station.Add(t);
202 SetDockingTile(t, true);
207 void MakeWaterKeepingClass(TileIndex tile, Owner o)
209 WaterClass wc = GetWaterClass(tile);
211 /* Autoslope might turn an originally canal or river tile into land */
212 int z;
213 Slope slope = GetTileSlope(tile, &z);
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 CommandCost ret = EnsureNoVehicleOnGround(tile);
271 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile2);
272 if (ret.Failed()) return ret;
275 if (flags & DC_EXEC) {
276 delete Depot::GetByTile(tile);
278 Company *c = Company::GetIfValid(GetTileOwner(tile));
279 if (c != nullptr) {
280 c->infrastructure.water -= 2 * LOCK_DEPOT_TILE_FACTOR;
281 DirtyCompanyInfrastructureWindows(c->index);
284 MakeWaterKeepingClass(tile, GetTileOwner(tile));
285 MakeWaterKeepingClass(tile2, GetTileOwner(tile2));
288 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_SHIP]);
292 * Builds a lock.
293 * @param tile Central tile of the lock.
294 * @param dir Uphill direction.
295 * @param flags Operation to perform.
296 * @return The cost in case of success, or an error code if it failed.
298 static CommandCost DoBuildLock(TileIndex tile, DiagDirection dir, DoCommandFlag flags)
300 CommandCost cost(EXPENSES_CONSTRUCTION);
302 int delta = TileOffsByDiagDir(dir);
303 CommandCost ret = EnsureNoVehicleOnGround(tile);
304 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta);
305 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta);
306 if (ret.Failed()) return ret;
308 /* middle tile */
309 WaterClass wc_middle = HasTileWaterGround(tile) ? GetWaterClass(tile) : WATER_CLASS_CANAL;
310 ret = DoCommand(flags, CMD_LANDSCAPE_CLEAR, tile, 0, 0);
311 if (ret.Failed()) return ret;
312 cost.AddCost(ret);
314 /* lower tile */
315 if (!IsWaterTile(tile - delta)) {
316 ret = DoCommand(flags, CMD_LANDSCAPE_CLEAR, tile - delta, 0, 0);
317 if (ret.Failed()) return ret;
318 cost.AddCost(ret);
319 cost.AddCost(_price[PR_BUILD_CANAL]);
321 if (!IsTileFlat(tile - delta)) {
322 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
324 WaterClass wc_lower = IsWaterTile(tile - delta) ? GetWaterClass(tile - delta) : WATER_CLASS_CANAL;
326 /* upper tile */
327 if (!IsWaterTile(tile + delta)) {
328 ret = DoCommand(flags, CMD_LANDSCAPE_CLEAR, tile + delta, 0, 0);
329 if (ret.Failed()) return ret;
330 cost.AddCost(ret);
331 cost.AddCost(_price[PR_BUILD_CANAL]);
333 if (!IsTileFlat(tile + delta)) {
334 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
336 WaterClass wc_upper = IsWaterTile(tile + delta) ? GetWaterClass(tile + delta) : WATER_CLASS_CANAL;
338 if (IsBridgeAbove(tile) || IsBridgeAbove(tile - delta) || IsBridgeAbove(tile + delta)) {
339 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
342 if (flags & DC_EXEC) {
343 /* Update company infrastructure counts. */
344 Company *c = Company::GetIfValid(_current_company);
345 if (c != nullptr) {
346 /* Counts for the water. */
347 if (!IsWaterTile(tile - delta)) c->infrastructure.water++;
348 if (!IsWaterTile(tile + delta)) c->infrastructure.water++;
349 /* Count for the lock itself. */
350 c->infrastructure.water += 3 * LOCK_DEPOT_TILE_FACTOR; // Lock is three tiles.
351 DirtyCompanyInfrastructureWindows(_current_company);
354 MakeLock(tile, _current_company, dir, wc_lower, wc_upper, wc_middle);
355 CheckForDockingTile(tile - delta);
356 CheckForDockingTile(tile + delta);
357 MarkTileDirtyByTile(tile);
358 MarkTileDirtyByTile(tile - delta);
359 MarkTileDirtyByTile(tile + delta);
360 MarkCanalsAndRiversAroundDirty(tile - delta);
361 MarkCanalsAndRiversAroundDirty(tile + delta);
363 cost.AddCost(_price[PR_BUILD_LOCK]);
365 return cost;
369 * Remove a lock.
370 * @param tile Central tile of the lock.
371 * @param flags Operation to perform.
372 * @return The cost in case of success, or an error code if it failed.
374 static CommandCost RemoveLock(TileIndex tile, DoCommandFlag flags)
376 if (GetTileOwner(tile) != OWNER_NONE) {
377 CommandCost ret = CheckTileOwnership(tile);
378 if (ret.Failed()) return ret;
381 TileIndexDiff delta = TileOffsByDiagDir(GetLockDirection(tile));
383 /* make sure no vehicle is on the tile. */
384 CommandCost ret = EnsureNoVehicleOnGround(tile);
385 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta);
386 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta);
387 if (ret.Failed()) return ret;
389 if (flags & DC_EXEC) {
390 /* Remove middle part from company infrastructure count. */
391 Company *c = Company::GetIfValid(GetTileOwner(tile));
392 if (c != nullptr) {
393 c->infrastructure.water -= 3 * LOCK_DEPOT_TILE_FACTOR; // three parts of the lock.
394 DirtyCompanyInfrastructureWindows(c->index);
397 if (GetWaterClass(tile) == WATER_CLASS_RIVER) {
398 MakeRiver(tile, Random());
399 } else {
400 DoClearSquare(tile);
402 MakeWaterKeepingClass(tile + delta, GetTileOwner(tile + delta));
403 MakeWaterKeepingClass(tile - delta, GetTileOwner(tile - delta));
404 MarkCanalsAndRiversAroundDirty(tile);
405 MarkCanalsAndRiversAroundDirty(tile - delta);
406 MarkCanalsAndRiversAroundDirty(tile + delta);
409 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_LOCK]);
413 * Builds a lock.
414 * @param flags type of operation
415 * @param tile tile where to place the lock
416 * @param p1 unused
417 * @param p2 unused
418 * @param text unused
419 * @return the cost of this operation or an error
421 CommandCost CmdBuildLock(DoCommandFlag flags, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
423 DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile));
424 if (dir == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
426 return DoBuildLock(tile, dir, flags);
429 /** Callback to create non-desert around a river tile. */
430 bool RiverModifyDesertZone(TileIndex tile, void *)
432 if (GetTropicZone(tile) == TROPICZONE_DESERT) SetTropicZone(tile, TROPICZONE_NORMAL);
433 return false;
437 * Build a piece of canal.
438 * @param flags type of operation
439 * @param tile end tile of stretch-dragging
440 * @param p1 start tile of stretch-dragging
441 * @param p2 various bitstuffed data
442 * bits 0-1: waterclass to build. sea and river can only be built in scenario editor
443 * bit 2: Whether to use the Orthogonal (0) or Diagonal (1) iterator.
444 * @param text unused
445 * @return the cost of this operation or an error
447 CommandCost CmdBuildCanal(DoCommandFlag flags, TileIndex tile, uint32 p1, uint32 p2, const std::string &text)
449 WaterClass wc = Extract<WaterClass, 0, 2>(p2);
450 if (p1 >= MapSize() || wc == WATER_CLASS_INVALID) return CMD_ERROR;
452 /* Outside of the editor you can only build canals, not oceans */
453 if (wc != WATER_CLASS_CANAL && _game_mode != GM_EDITOR) return CMD_ERROR;
455 /* Outside the editor you can only drag canals, and not areas */
456 if (_game_mode != GM_EDITOR) {
457 TileArea ta(tile, p1);
458 if (ta.w != 1 && ta.h != 1) return CMD_ERROR;
461 CommandCost cost(EXPENSES_CONSTRUCTION);
463 std::unique_ptr<TileIterator> iter;
464 if (HasBit(p2, 2)) {
465 iter = std::make_unique<DiagonalTileIterator>(tile, p1);
466 } else {
467 iter = std::make_unique<OrthogonalTileIterator>(tile, p1);
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 = DoCommand(flags, CMD_LANDSCAPE_CLEAR, current_tile, 0, 0);
485 if (ret.Failed()) return ret;
487 if (!water) cost.AddCost(ret);
489 if (flags & DC_EXEC) {
490 switch (wc) {
491 case WATER_CLASS_RIVER:
492 MakeRiver(current_tile, Random());
493 if (_game_mode == GM_EDITOR) {
494 TileIndex tile2 = current_tile;
495 CircularTileSearch(&tile2, RIVER_OFFSET_DESERT_DISTANCE, RiverModifyDesertZone, nullptr);
497 break;
499 case WATER_CLASS_SEA:
500 if (TileHeight(current_tile) == 0) {
501 MakeSea(current_tile);
502 break;
504 FALLTHROUGH;
506 default:
507 MakeCanal(current_tile, _current_company, Random());
508 if (Company::IsValidID(_current_company)) {
509 Company::Get(_current_company)->infrastructure.water++;
510 DirtyCompanyInfrastructureWindows(_current_company);
512 break;
514 MarkTileDirtyByTile(current_tile);
515 MarkCanalsAndRiversAroundDirty(current_tile);
516 CheckForDockingTile(current_tile);
519 cost.AddCost(_price[PR_BUILD_CANAL]);
522 if (cost.GetCost() == 0) {
523 return_cmd_error(STR_ERROR_ALREADY_BUILT);
524 } else {
525 return cost;
529 static CommandCost ClearTile_Water(TileIndex tile, DoCommandFlag flags)
531 switch (GetWaterTileType(tile)) {
532 case WATER_TILE_CLEAR: {
533 if (flags & DC_NO_WATER) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER);
535 Money base_cost = IsCanal(tile) ? _price[PR_CLEAR_CANAL] : _price[PR_CLEAR_WATER];
536 /* Make sure freeform edges are allowed or it's not an edge tile. */
537 if (!_settings_game.construction.freeform_edges && (!IsInsideMM(TileX(tile), 1, MapMaxX() - 1) ||
538 !IsInsideMM(TileY(tile), 1, MapMaxY() - 1))) {
539 return_cmd_error(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP);
542 /* Make sure no vehicle is on the tile */
543 CommandCost ret = EnsureNoVehicleOnGround(tile);
544 if (ret.Failed()) return ret;
546 Owner owner = GetTileOwner(tile);
547 if (owner != OWNER_WATER && owner != OWNER_NONE) {
548 CommandCost ret = CheckTileOwnership(tile);
549 if (ret.Failed()) return ret;
552 if (flags & DC_EXEC) {
553 if (IsCanal(tile) && Company::IsValidID(owner)) {
554 Company::Get(owner)->infrastructure.water--;
555 DirtyCompanyInfrastructureWindows(owner);
557 bool remove = IsDockingTile(tile);
558 DoClearSquare(tile);
559 MarkCanalsAndRiversAroundDirty(tile);
560 if (remove) RemoveDockingTile(tile);
563 return CommandCost(EXPENSES_CONSTRUCTION, base_cost);
566 case WATER_TILE_COAST: {
567 Slope slope = GetTileSlope(tile);
569 /* Make sure no vehicle is on the tile */
570 CommandCost ret = EnsureNoVehicleOnGround(tile);
571 if (ret.Failed()) return ret;
573 if (flags & DC_EXEC) {
574 bool remove = IsDockingTile(tile);
575 DoClearSquare(tile);
576 MarkCanalsAndRiversAroundDirty(tile);
577 if (remove) RemoveDockingTile(tile);
579 if (IsSlopeWithOneCornerRaised(slope)) {
580 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_WATER]);
581 } else {
582 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_ROUGH]);
586 case WATER_TILE_LOCK: {
587 static const TileIndexDiffC _lock_tomiddle_offs[][DIAGDIR_END] = {
588 /* NE SE SW NW */
589 { { 0, 0}, {0, 0}, { 0, 0}, {0, 0} }, // LOCK_PART_MIDDLE
590 { {-1, 0}, {0, 1}, { 1, 0}, {0, -1} }, // LOCK_PART_LOWER
591 { { 1, 0}, {0, -1}, {-1, 0}, {0, 1} }, // LOCK_PART_UPPER
594 if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
595 if (_current_company == OWNER_WATER) return CMD_ERROR;
596 /* move to the middle tile.. */
597 return RemoveLock(tile + ToTileIndexDiff(_lock_tomiddle_offs[GetLockPart(tile)][GetLockDirection(tile)]), flags);
600 case WATER_TILE_DEPOT:
601 if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
602 return RemoveShipDepot(tile, flags);
604 default:
605 NOT_REACHED();
610 * return true if a tile is a water tile wrt. a certain direction.
612 * @param tile The tile of interest.
613 * @param from The direction of interest.
614 * @return true iff the tile is water in the view of 'from'.
617 bool IsWateredTile(TileIndex tile, Direction from)
619 switch (GetTileType(tile)) {
620 case MP_WATER:
621 switch (GetWaterTileType(tile)) {
622 default: NOT_REACHED();
623 case WATER_TILE_DEPOT: case WATER_TILE_CLEAR: return true;
624 case WATER_TILE_LOCK: return DiagDirToAxis(GetLockDirection(tile)) == DiagDirToAxis(DirToDiagDir(from));
626 case WATER_TILE_COAST:
627 switch (GetTileSlope(tile)) {
628 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
629 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
630 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
631 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
632 default: return false;
636 case MP_RAILWAY:
637 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
638 assert(IsPlainRail(tile));
639 switch (GetTileSlope(tile)) {
640 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
641 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
642 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
643 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
644 default: return false;
647 return false;
649 case MP_STATION:
650 if (IsOilRig(tile)) {
651 /* Do not draw waterborders inside of industries.
652 * Note: There is no easy way to detect the industry of an oilrig tile. */
653 TileIndex src_tile = tile + TileOffsByDir(from);
654 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
655 (IsTileType(src_tile, MP_INDUSTRY))) return true;
657 return IsTileOnWater(tile);
659 return (IsDock(tile) && IsTileFlat(tile)) || IsBuoy(tile);
661 case MP_INDUSTRY: {
662 /* Do not draw waterborders inside of industries.
663 * Note: There is no easy way to detect the industry of an oilrig tile. */
664 TileIndex src_tile = tile + TileOffsByDir(from);
665 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
666 (IsTileType(src_tile, MP_INDUSTRY) && GetIndustryIndex(src_tile) == GetIndustryIndex(tile))) return true;
668 return IsTileOnWater(tile);
671 case MP_OBJECT: return IsTileOnWater(tile);
673 case MP_TUNNELBRIDGE: return GetTunnelBridgeTransportType(tile) == TRANSPORT_WATER && ReverseDiagDir(GetTunnelBridgeDirection(tile)) == DirToDiagDir(from);
675 case MP_VOID: return true; // consider map border as water, esp. for rivers
677 default: return false;
682 * Draw a water sprite, potentially with a NewGRF-modified sprite offset.
683 * @param base Sprite base.
684 * @param offset Sprite offset.
685 * @param feature The type of sprite that is drawn.
686 * @param tile Tile index to draw.
688 static void DrawWaterSprite(SpriteID base, uint offset, CanalFeature feature, TileIndex tile)
690 if (base != SPR_FLAT_WATER_TILE) {
691 /* Only call offset callback if the sprite is NewGRF-provided. */
692 offset = GetCanalSpriteOffset(feature, tile, offset);
694 DrawGroundSprite(base + offset, PAL_NONE);
698 * Draw canal or river edges.
699 * @param canal True if canal edges should be drawn, false for river edges.
700 * @param offset Sprite offset.
701 * @param tile Tile to draw.
703 static void DrawWaterEdges(bool canal, uint offset, TileIndex tile)
705 CanalFeature feature;
706 SpriteID base = 0;
707 if (canal) {
708 feature = CF_DIKES;
709 base = GetCanalSprite(CF_DIKES, tile);
710 if (base == 0) base = SPR_CANAL_DIKES_BASE;
711 } else {
712 feature = CF_RIVER_EDGE;
713 base = GetCanalSprite(CF_RIVER_EDGE, tile);
714 if (base == 0) return; // Don't draw if no sprites provided.
717 uint wa;
719 /* determine the edges around with water. */
720 wa = IsWateredTile(TILE_ADDXY(tile, -1, 0), DIR_SW) << 0;
721 wa += IsWateredTile(TILE_ADDXY(tile, 0, 1), DIR_NW) << 1;
722 wa += IsWateredTile(TILE_ADDXY(tile, 1, 0), DIR_NE) << 2;
723 wa += IsWateredTile(TILE_ADDXY(tile, 0, -1), DIR_SE) << 3;
725 if (!(wa & 1)) DrawWaterSprite(base, offset, feature, tile);
726 if (!(wa & 2)) DrawWaterSprite(base, offset + 1, feature, tile);
727 if (!(wa & 4)) DrawWaterSprite(base, offset + 2, feature, tile);
728 if (!(wa & 8)) DrawWaterSprite(base, offset + 3, feature, tile);
730 /* right corner */
731 switch (wa & 0x03) {
732 case 0: DrawWaterSprite(base, offset + 4, feature, tile); break;
733 case 3: if (!IsWateredTile(TILE_ADDXY(tile, -1, 1), DIR_W)) DrawWaterSprite(base, offset + 8, feature, tile); break;
736 /* bottom corner */
737 switch (wa & 0x06) {
738 case 0: DrawWaterSprite(base, offset + 5, feature, tile); break;
739 case 6: if (!IsWateredTile(TILE_ADDXY(tile, 1, 1), DIR_N)) DrawWaterSprite(base, offset + 9, feature, tile); break;
742 /* left corner */
743 switch (wa & 0x0C) {
744 case 0: DrawWaterSprite(base, offset + 6, feature, tile); break;
745 case 12: if (!IsWateredTile(TILE_ADDXY(tile, 1, -1), DIR_E)) DrawWaterSprite(base, offset + 10, feature, tile); break;
748 /* upper corner */
749 switch (wa & 0x09) {
750 case 0: DrawWaterSprite(base, offset + 7, feature, tile); break;
751 case 9: if (!IsWateredTile(TILE_ADDXY(tile, -1, -1), DIR_S)) DrawWaterSprite(base, offset + 11, feature, tile); break;
755 /** Draw a plain sea water tile with no edges */
756 static void DrawSeaWater(TileIndex tile)
758 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
761 /** draw a canal styled water tile with dikes around */
762 static void DrawCanalWater(TileIndex tile)
764 SpriteID image = SPR_FLAT_WATER_TILE;
765 if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
766 /* First water slope sprite is flat water. */
767 image = GetCanalSprite(CF_WATERSLOPE, tile);
768 if (image == 0) image = SPR_FLAT_WATER_TILE;
770 DrawWaterSprite(image, 0, CF_WATERSLOPE, tile);
772 DrawWaterEdges(true, 0, tile);
775 #include "table/water_land.h"
778 * Draw a build sprite sequence for water tiles.
779 * If buildings are invisible, nothing will be drawn.
780 * @param ti Tile info.
781 * @param dtss Sprite sequence to draw.
782 * @param base Base sprite.
783 * @param offset Additional sprite offset.
784 * @param palette Palette to use.
786 static void DrawWaterTileStruct(const TileInfo *ti, const DrawTileSeqStruct *dtss, SpriteID base, uint offset, PaletteID palette, CanalFeature feature)
788 /* Don't draw if buildings are invisible. */
789 if (IsInvisibilitySet(TO_BUILDINGS)) return;
791 for (; !dtss->IsTerminator(); dtss++) {
792 uint tile_offs = offset + dtss->image.sprite;
793 if (feature < CF_END) tile_offs = GetCanalSpriteOffset(feature, ti->tile, tile_offs);
794 AddSortableSpriteToDraw(base + tile_offs, palette,
795 ti->x + dtss->delta_x, ti->y + dtss->delta_y,
796 dtss->size_x, dtss->size_y,
797 dtss->size_z, ti->z + dtss->delta_z,
798 IsTransparencySet(TO_BUILDINGS));
802 /** Draw a lock tile. */
803 static void DrawWaterLock(const TileInfo *ti)
805 int part = GetLockPart(ti->tile);
806 const DrawTileSprites &dts = _lock_display_data[part][GetLockDirection(ti->tile)];
808 /* Draw ground sprite. */
809 SpriteID image = dts.ground.sprite;
811 SpriteID water_base = GetCanalSprite(CF_WATERSLOPE, ti->tile);
812 if (water_base == 0) {
813 /* Use default sprites. */
814 water_base = SPR_CANALS_BASE;
815 } else if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
816 /* NewGRF supplies a flat sprite as first sprite. */
817 if (image == SPR_FLAT_WATER_TILE) {
818 image = water_base;
819 } else {
820 image++;
824 if (image < 5) image += water_base;
825 DrawGroundSprite(image, PAL_NONE);
827 /* Draw structures. */
828 uint zoffs = 0;
829 SpriteID base = GetCanalSprite(CF_LOCKS, ti->tile);
831 if (base == 0) {
832 /* If no custom graphics, use defaults. */
833 base = SPR_LOCK_BASE;
834 uint8 z_threshold = part == LOCK_PART_UPPER ? 8 : 0;
835 zoffs = ti->z > z_threshold ? 24 : 0;
838 DrawWaterTileStruct(ti, dts.seq, base, zoffs, PAL_NONE, CF_LOCKS);
841 /** Draw a ship depot tile. */
842 static void DrawWaterDepot(const TileInfo *ti)
844 DrawWaterClassGround(ti);
845 DrawWaterTileStruct(ti, _shipdepot_display_data[GetShipDepotAxis(ti->tile)][GetShipDepotPart(ti->tile)].seq, 0, 0, COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile)), CF_END);
848 static void DrawRiverWater(const TileInfo *ti)
850 SpriteID image = SPR_FLAT_WATER_TILE;
851 uint offset = 0;
852 uint edges_offset = 0;
854 if (ti->tileh != SLOPE_FLAT || HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
855 image = GetCanalSprite(CF_RIVER_SLOPE, ti->tile);
856 if (image == 0) {
857 switch (ti->tileh) {
858 case SLOPE_NW: image = SPR_WATER_SLOPE_Y_DOWN; break;
859 case SLOPE_SW: image = SPR_WATER_SLOPE_X_UP; break;
860 case SLOPE_SE: image = SPR_WATER_SLOPE_Y_UP; break;
861 case SLOPE_NE: image = SPR_WATER_SLOPE_X_DOWN; break;
862 default: image = SPR_FLAT_WATER_TILE; break;
864 } else {
865 /* Flag bit 0 indicates that the first sprite is flat water. */
866 offset = HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE) ? 1 : 0;
868 switch (ti->tileh) {
869 case SLOPE_SE: edges_offset += 12; break;
870 case SLOPE_NE: offset += 1; edges_offset += 24; break;
871 case SLOPE_SW: offset += 2; edges_offset += 36; break;
872 case SLOPE_NW: offset += 3; edges_offset += 48; break;
873 default: offset = 0; break;
876 offset = GetCanalSpriteOffset(CF_RIVER_SLOPE, ti->tile, offset);
880 DrawGroundSprite(image + offset, PAL_NONE);
882 /* Draw river edges if available. */
883 DrawWaterEdges(false, edges_offset, ti->tile);
886 void DrawShoreTile(Slope tileh)
888 /* Converts the enum Slope into an offset based on SPR_SHORE_BASE.
889 * This allows to calculate the proper sprite to display for this Slope */
890 static const byte tileh_to_shoresprite[32] = {
891 0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0,
892 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 15, 0,
895 assert(!IsHalftileSlope(tileh)); // Halftile slopes need to get handled earlier.
896 assert(tileh != SLOPE_FLAT); // Shore is never flat
898 assert((tileh != SLOPE_EW) && (tileh != SLOPE_NS)); // No suitable sprites for current flooding behaviour
900 DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[tileh], PAL_NONE);
903 void DrawWaterClassGround(const TileInfo *ti)
905 switch (GetWaterClass(ti->tile)) {
906 case WATER_CLASS_SEA: DrawSeaWater(ti->tile); break;
907 case WATER_CLASS_CANAL: DrawCanalWater(ti->tile); break;
908 case WATER_CLASS_RIVER: DrawRiverWater(ti); break;
909 default: NOT_REACHED();
913 static void DrawTile_Water(TileInfo *ti)
915 switch (GetWaterTileType(ti->tile)) {
916 case WATER_TILE_CLEAR:
917 DrawWaterClassGround(ti);
918 DrawBridgeMiddle(ti);
919 break;
921 case WATER_TILE_COAST: {
922 DrawShoreTile(ti->tileh);
923 DrawBridgeMiddle(ti);
924 break;
927 case WATER_TILE_LOCK:
928 DrawWaterLock(ti);
929 break;
931 case WATER_TILE_DEPOT:
932 DrawWaterDepot(ti);
933 break;
937 void DrawShipDepotSprite(int x, int y, Axis axis, DepotPart part)
939 const DrawTileSprites &dts = _shipdepot_display_data[axis][part];
941 DrawSprite(dts.ground.sprite, dts.ground.pal, x, y);
942 DrawOrigTileSeqInGUI(x, y, &dts, COMPANY_SPRITE_COLOUR(_local_company));
946 static int GetSlopePixelZ_Water(TileIndex tile, uint x, uint y)
948 int z;
949 Slope tileh = GetTilePixelSlope(tile, &z);
951 return z + GetPartialPixelZ(x & 0xF, y & 0xF, tileh);
954 static Foundation GetFoundation_Water(TileIndex tile, Slope tileh)
956 return FOUNDATION_NONE;
959 static void GetTileDesc_Water(TileIndex tile, TileDesc *td)
961 switch (GetWaterTileType(tile)) {
962 case WATER_TILE_CLEAR:
963 switch (GetWaterClass(tile)) {
964 case WATER_CLASS_SEA: td->str = STR_LAI_WATER_DESCRIPTION_WATER; break;
965 case WATER_CLASS_CANAL: td->str = STR_LAI_WATER_DESCRIPTION_CANAL; break;
966 case WATER_CLASS_RIVER: td->str = STR_LAI_WATER_DESCRIPTION_RIVER; break;
967 default: NOT_REACHED();
969 break;
970 case WATER_TILE_COAST: td->str = STR_LAI_WATER_DESCRIPTION_COAST_OR_RIVERBANK; break;
971 case WATER_TILE_LOCK : td->str = STR_LAI_WATER_DESCRIPTION_LOCK; break;
972 case WATER_TILE_DEPOT:
973 td->str = STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT;
974 td->build_date = Depot::GetByTile(tile)->build_date;
975 break;
976 default: NOT_REACHED();
979 td->owner[0] = GetTileOwner(tile);
983 * Handle the flooding of a vehicle. This sets the vehicle state to crashed,
984 * creates a newsitem and dirties the necessary windows.
985 * @param v The vehicle to flood.
987 static void FloodVehicle(Vehicle *v)
989 uint pass = v->Crash(true);
991 AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_FLOODED));
992 Game::NewEvent(new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_FLOODED));
993 SetDParam(0, pass);
994 AddTileNewsItem(STR_NEWS_DISASTER_FLOOD_VEHICLE, NT_ACCIDENT, v->tile);
995 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
996 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v);
1000 * Flood a vehicle if we are allowed to flood it, i.e. when it is on the ground.
1001 * @param v The vehicle to test for flooding.
1002 * @param data The z of level to flood.
1003 * @return nullptr as we always want to remove everything.
1005 static Vehicle *FloodVehicleProc(Vehicle *v, void *data)
1007 if ((v->vehstatus & VS_CRASHED) != 0) return nullptr;
1009 switch (v->type) {
1010 default: break;
1012 case VEH_AIRCRAFT: {
1013 if (!IsAirportTile(v->tile) || GetTileMaxZ(v->tile) != 0) break;
1014 if (v->subtype == AIR_SHADOW) break;
1016 /* We compare v->z_pos against delta_z + 1 because the shadow
1017 * is at delta_z and the actual aircraft at delta_z + 1. */
1018 const Station *st = Station::GetByTile(v->tile);
1019 const AirportFTAClass *airport = st->airport.GetFTA();
1020 if (v->z_pos != airport->delta_z + 1) break;
1022 FloodVehicle(v);
1023 break;
1026 case VEH_TRAIN:
1027 case VEH_ROAD: {
1028 int z = *(int*)data;
1029 if (v->z_pos > z) break;
1030 FloodVehicle(v->First());
1031 break;
1035 return nullptr;
1039 * Finds a vehicle to flood.
1040 * It does not find vehicles that are already crashed on bridges, i.e. flooded.
1041 * @param tile the tile where to find a vehicle to flood
1043 static void FloodVehicles(TileIndex tile)
1045 int z = 0;
1047 if (IsAirportTile(tile)) {
1048 const Station *st = Station::GetByTile(tile);
1049 for (TileIndex airport_tile : st->airport) {
1050 if (st->TileBelongsToAirport(airport_tile)) FindVehicleOnPos(airport_tile, &z, &FloodVehicleProc);
1053 /* No vehicle could be flooded on this airport anymore */
1054 return;
1057 if (!IsBridgeTile(tile)) {
1058 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
1059 return;
1062 TileIndex end = GetOtherBridgeEnd(tile);
1063 z = GetBridgePixelHeight(tile);
1065 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
1066 FindVehicleOnPos(end, &z, &FloodVehicleProc);
1070 * Returns the behaviour of a tile during flooding.
1072 * @return Behaviour of the tile
1074 FloodingBehaviour GetFloodingBehaviour(TileIndex tile)
1076 /* FLOOD_ACTIVE: 'single-corner-raised'-coast, sea, sea-shipdepots, sea-buoys, sea-docks (water part), rail with flooded halftile, sea-water-industries, sea-oilrigs
1077 * FLOOD_DRYUP: coast with more than one corner raised, coast with rail-track, coast with trees
1078 * FLOOD_PASSIVE: (not used)
1079 * FLOOD_NONE: canals, rivers, everything else
1081 switch (GetTileType(tile)) {
1082 case MP_WATER:
1083 if (IsCoast(tile)) {
1084 Slope tileh = GetTileSlope(tile);
1085 return (IsSlopeWithOneCornerRaised(tileh) ? FLOOD_ACTIVE : FLOOD_DRYUP);
1087 FALLTHROUGH;
1088 case MP_STATION:
1089 case MP_INDUSTRY:
1090 case MP_OBJECT:
1091 return (GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE;
1093 case MP_RAILWAY:
1094 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
1095 return (IsSlopeWithOneCornerRaised(GetTileSlope(tile)) ? FLOOD_ACTIVE : FLOOD_DRYUP);
1097 return FLOOD_NONE;
1099 case MP_TREES:
1100 return (GetTreeGround(tile) == TREE_GROUND_SHORE ? FLOOD_DRYUP : FLOOD_NONE);
1102 default:
1103 return FLOOD_NONE;
1108 * Floods a tile.
1110 void DoFloodTile(TileIndex target)
1112 assert(!IsTileType(target, MP_WATER));
1114 bool flooded = false; // Will be set to true if something is changed.
1116 Backup<CompanyID> cur_company(_current_company, OWNER_WATER, FILE_LINE);
1118 Slope tileh = GetTileSlope(target);
1119 if (tileh != SLOPE_FLAT) {
1120 /* make coast.. */
1121 switch (GetTileType(target)) {
1122 case MP_RAILWAY: {
1123 if (!IsPlainRail(target)) break;
1124 FloodVehicles(target);
1125 flooded = FloodHalftile(target);
1126 break;
1129 case MP_TREES:
1130 if (!IsSlopeWithOneCornerRaised(tileh)) {
1131 SetTreeGroundDensity(target, TREE_GROUND_SHORE, 3);
1132 MarkTileDirtyByTile(target);
1133 flooded = true;
1134 break;
1136 FALLTHROUGH;
1138 case MP_CLEAR:
1139 if (DoCommand(DC_EXEC, CMD_LANDSCAPE_CLEAR, target, 0, 0).Succeeded()) {
1140 MakeShore(target);
1141 MarkTileDirtyByTile(target);
1142 flooded = true;
1144 break;
1146 default:
1147 break;
1149 } else {
1150 /* Flood vehicles */
1151 FloodVehicles(target);
1153 /* flood flat tile */
1154 if (DoCommand(DC_EXEC, CMD_LANDSCAPE_CLEAR, target, 0, 0).Succeeded()) {
1155 MakeSea(target);
1156 MarkTileDirtyByTile(target);
1157 flooded = true;
1161 if (flooded) {
1162 /* Mark surrounding canal tiles dirty too to avoid glitches */
1163 MarkCanalsAndRiversAroundDirty(target);
1165 /* update signals if needed */
1166 UpdateSignalsInBuffer();
1168 if (IsPossibleDockingTile(target)) CheckForDockingTile(target);
1171 cur_company.Restore();
1175 * Drys a tile up.
1177 static void DoDryUp(TileIndex tile)
1179 Backup<CompanyID> cur_company(_current_company, OWNER_WATER, FILE_LINE);
1181 switch (GetTileType(tile)) {
1182 case MP_RAILWAY:
1183 assert(IsPlainRail(tile));
1184 assert(GetRailGroundType(tile) == RAIL_GROUND_WATER);
1186 RailGroundType new_ground;
1187 switch (GetTrackBits(tile)) {
1188 case TRACK_BIT_UPPER: new_ground = RAIL_GROUND_FENCE_HORIZ1; break;
1189 case TRACK_BIT_LOWER: new_ground = RAIL_GROUND_FENCE_HORIZ2; break;
1190 case TRACK_BIT_LEFT: new_ground = RAIL_GROUND_FENCE_VERT1; break;
1191 case TRACK_BIT_RIGHT: new_ground = RAIL_GROUND_FENCE_VERT2; break;
1192 default: NOT_REACHED();
1194 SetRailGroundType(tile, new_ground);
1195 MarkTileDirtyByTile(tile);
1196 break;
1198 case MP_TREES:
1199 SetTreeGroundDensity(tile, TREE_GROUND_GRASS, 3);
1200 MarkTileDirtyByTile(tile);
1201 break;
1203 case MP_WATER:
1204 assert(IsCoast(tile));
1206 if (DoCommand(DC_EXEC, CMD_LANDSCAPE_CLEAR, tile, 0, 0).Succeeded()) {
1207 MakeClear(tile, CLEAR_GRASS, 3);
1208 MarkTileDirtyByTile(tile);
1210 break;
1212 default: NOT_REACHED();
1215 cur_company.Restore();
1219 * Let a water tile floods its diagonal adjoining tiles
1220 * called from tunnelbridge_cmd, and by TileLoop_Industry() and TileLoop_Track()
1222 * @param tile the water/shore tile that floods
1224 void TileLoop_Water(TileIndex tile)
1226 if (IsTileType(tile, MP_WATER)) AmbientSoundEffect(tile);
1228 switch (GetFloodingBehaviour(tile)) {
1229 case FLOOD_ACTIVE:
1230 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
1231 TileIndex dest = tile + TileOffsByDir(dir);
1232 if (!IsValidTile(dest)) continue;
1233 /* do not try to flood water tiles - increases performance a lot */
1234 if (IsTileType(dest, MP_WATER)) continue;
1236 /* TREE_GROUND_SHORE is the sign of a previous flood. */
1237 if (IsTileType(dest, MP_TREES) && GetTreeGround(dest) == TREE_GROUND_SHORE) continue;
1239 int z_dest;
1240 Slope slope_dest = GetFoundationSlope(dest, &z_dest) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
1241 if (z_dest > 0) continue;
1243 if (!HasBit(_flood_from_dirs[slope_dest], ReverseDir(dir))) continue;
1245 DoFloodTile(dest);
1247 break;
1249 case FLOOD_DRYUP: {
1250 Slope slope_here = GetFoundationSlope(tile) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
1251 for (uint dir : SetBitIterator(_flood_from_dirs[slope_here])) {
1252 TileIndex dest = tile + TileOffsByDir((Direction)dir);
1253 if (!IsValidTile(dest)) continue;
1255 FloodingBehaviour dest_behaviour = GetFloodingBehaviour(dest);
1256 if ((dest_behaviour == FLOOD_ACTIVE) || (dest_behaviour == FLOOD_PASSIVE)) return;
1258 DoDryUp(tile);
1259 break;
1262 default: return;
1266 void ConvertGroundTilesIntoWaterTiles()
1268 int z;
1270 for (TileIndex tile = 0; tile < MapSize(); ++tile) {
1271 Slope slope = GetTileSlope(tile, &z);
1272 if (IsTileType(tile, MP_CLEAR) && z == 0) {
1273 /* Make both water for tiles at level 0
1274 * and make shore, as that looks much better
1275 * during the generation. */
1276 switch (slope) {
1277 case SLOPE_FLAT:
1278 MakeSea(tile);
1279 break;
1281 case SLOPE_N:
1282 case SLOPE_E:
1283 case SLOPE_S:
1284 case SLOPE_W:
1285 MakeShore(tile);
1286 break;
1288 default:
1289 for (uint dir : SetBitIterator(_flood_from_dirs[slope & ~SLOPE_STEEP])) {
1290 TileIndex dest = TileAddByDir(tile, (Direction)dir);
1291 Slope slope_dest = GetTileSlope(dest) & ~SLOPE_STEEP;
1292 if (slope_dest == SLOPE_FLAT || IsSlopeWithOneCornerRaised(slope_dest)) {
1293 MakeShore(tile);
1294 break;
1297 break;
1303 static TrackStatus GetTileTrackStatus_Water(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
1305 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,
1306 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};
1308 TrackBits ts;
1310 if (mode != TRANSPORT_WATER) return 0;
1312 switch (GetWaterTileType(tile)) {
1313 case WATER_TILE_CLEAR: ts = IsTileFlat(tile) ? TRACK_BIT_ALL : TRACK_BIT_NONE; break;
1314 case WATER_TILE_COAST: ts = coast_tracks[GetTileSlope(tile) & 0xF]; break;
1315 case WATER_TILE_LOCK: ts = DiagDirToDiagTrackBits(GetLockDirection(tile)); break;
1316 case WATER_TILE_DEPOT: ts = AxisToTrackBits(GetShipDepotAxis(tile)); break;
1317 default: return 0;
1319 if (TileX(tile) == 0) {
1320 /* NE border: remove tracks that connects NE tile edge */
1321 ts &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
1323 if (TileY(tile) == 0) {
1324 /* NW border: remove tracks that connects NW tile edge */
1325 ts &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
1327 return CombineTrackStatus(TrackBitsToTrackdirBits(ts), TRACKDIR_BIT_NONE);
1330 static bool ClickTile_Water(TileIndex tile)
1332 if (GetWaterTileType(tile) == WATER_TILE_DEPOT) {
1333 ShowDepotWindow(GetShipDepotNorthTile(tile), VEH_SHIP);
1334 return true;
1336 return false;
1339 static void ChangeTileOwner_Water(TileIndex tile, Owner old_owner, Owner new_owner)
1341 if (!IsTileOwner(tile, old_owner)) return;
1343 bool is_lock_middle = IsLock(tile) && GetLockPart(tile) == LOCK_PART_MIDDLE;
1345 /* No need to dirty company windows here, we'll redraw the whole screen anyway. */
1346 if (is_lock_middle) Company::Get(old_owner)->infrastructure.water -= 3 * LOCK_DEPOT_TILE_FACTOR; // Lock has three parts.
1347 if (new_owner != INVALID_OWNER) {
1348 if (is_lock_middle) Company::Get(new_owner)->infrastructure.water += 3 * LOCK_DEPOT_TILE_FACTOR; // Lock has three parts.
1349 /* Only subtract from the old owner here if the new owner is valid,
1350 * otherwise we clear ship depots and canal water below. */
1351 if (GetWaterClass(tile) == WATER_CLASS_CANAL && !is_lock_middle) {
1352 Company::Get(old_owner)->infrastructure.water--;
1353 Company::Get(new_owner)->infrastructure.water++;
1355 if (IsShipDepot(tile)) {
1356 Company::Get(old_owner)->infrastructure.water -= LOCK_DEPOT_TILE_FACTOR;
1357 Company::Get(new_owner)->infrastructure.water += LOCK_DEPOT_TILE_FACTOR;
1360 SetTileOwner(tile, new_owner);
1361 return;
1364 /* Remove depot */
1365 if (IsShipDepot(tile)) DoCommand(DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR, tile, 0, 0);
1367 /* Set owner of canals and locks ... and also canal under dock there was before.
1368 * Check if the new owner after removing depot isn't OWNER_WATER. */
1369 if (IsTileOwner(tile, old_owner)) {
1370 if (GetWaterClass(tile) == WATER_CLASS_CANAL && !is_lock_middle) Company::Get(old_owner)->infrastructure.water--;
1371 SetTileOwner(tile, OWNER_NONE);
1375 static VehicleEnterTileStatus VehicleEnter_Water(Vehicle *v, TileIndex tile, int x, int y)
1377 return VETSB_CONTINUE;
1380 static CommandCost TerraformTile_Water(TileIndex tile, DoCommandFlag flags, int z_new, Slope tileh_new)
1382 /* Canals can't be terraformed */
1383 if (IsWaterTile(tile) && IsCanal(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_CANAL_FIRST);
1385 return DoCommand(flags, CMD_LANDSCAPE_CLEAR, tile, 0, 0);
1389 extern const TileTypeProcs _tile_type_water_procs = {
1390 DrawTile_Water, // draw_tile_proc
1391 GetSlopePixelZ_Water, // get_slope_z_proc
1392 ClearTile_Water, // clear_tile_proc
1393 nullptr, // add_accepted_cargo_proc
1394 GetTileDesc_Water, // get_tile_desc_proc
1395 GetTileTrackStatus_Water, // get_tile_track_status_proc
1396 ClickTile_Water, // click_tile_proc
1397 nullptr, // animate_tile_proc
1398 TileLoop_Water, // tile_loop_proc
1399 ChangeTileOwner_Water, // change_tile_owner_proc
1400 nullptr, // add_produced_cargo_proc
1401 VehicleEnter_Water, // vehicle_enter_tile_proc
1402 GetFoundation_Water, // get_foundation_proc
1403 TerraformTile_Water, // terraform_tile_proc