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/>.
8 /** @file water_cmd.cpp Handling of water tiles. */
11 #include "landscape.h"
12 #include "viewport_func.h"
13 #include "command_func.h"
15 #include "news_func.h"
16 #include "depot_base.h"
17 #include "depot_func.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"
28 #include "effectvehicle_func.h"
29 #include "tunnelbridge_map.h"
30 #include "station_base.h"
32 #include "game/game.hpp"
33 #include "core/random_func.hpp"
34 #include "core/backup_type.hpp"
35 #include "date_func.h"
36 #include "company_base.h"
37 #include "company_gui.h"
38 #include "newgrf_generic.h"
40 #include "water_cmd.h"
41 #include "landscape_cmd.h"
43 #include "table/strings.h"
45 #include "safeguards.h"
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
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
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
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
);
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
85 static void MarkCanalsAndRiversAroundDirty(TileIndex tile
)
87 for (Direction dir
= DIR_BEGIN
; dir
< DIR_END
; dir
++) {
88 MarkTileDirtyIfCanalOrRiver(tile
+ TileOffsByDir(dir
));
95 * @param flags type of operation
96 * @param tile tile where ship depot is built
97 * @param axis depot orientation (Axis)
98 * @return the cost of this operation or an error
100 CommandCost
CmdBuildShipDepot(DoCommandFlag flags
, TileIndex tile
, Axis axis
)
102 if (!IsValidAxis(axis
)) return CMD_ERROR
;
103 TileIndex tile2
= tile
+ (axis
== AXIS_X
? TileDiffXY(1, 0) : TileDiffXY(0, 1));
105 if (!HasTileWaterGround(tile
) || !HasTileWaterGround(tile2
)) {
106 return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER
);
109 if (IsBridgeAbove(tile
) || IsBridgeAbove(tile2
)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
);
111 if (!IsTileFlat(tile
) || !IsTileFlat(tile2
)) {
112 /* Prevent depots on rapids */
113 return_cmd_error(STR_ERROR_SITE_UNSUITABLE
);
116 if (!Depot::CanAllocateItem()) return CMD_ERROR
;
118 WaterClass wc1
= GetWaterClass(tile
);
119 WaterClass wc2
= GetWaterClass(tile2
);
120 CommandCost cost
= CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_BUILD_DEPOT_SHIP
]);
122 bool add_cost
= !IsWaterTile(tile
);
123 CommandCost ret
= Command
<CMD_LANDSCAPE_CLEAR
>::Do(flags
| DC_AUTO
, tile
);
124 if (ret
.Failed()) return ret
;
128 add_cost
= !IsWaterTile(tile2
);
129 ret
= Command
<CMD_LANDSCAPE_CLEAR
>::Do(flags
| DC_AUTO
, tile2
);
130 if (ret
.Failed()) return ret
;
135 if (flags
& DC_EXEC
) {
136 Depot
*depot
= new Depot(tile
);
137 depot
->build_date
= _date
;
139 if (wc1
== WATER_CLASS_CANAL
|| wc2
== WATER_CLASS_CANAL
) {
140 /* Update infrastructure counts after the unconditional clear earlier. */
141 Company::Get(_current_company
)->infrastructure
.water
+= wc1
== WATER_CLASS_CANAL
&& wc2
== WATER_CLASS_CANAL
? 2 : 1;
143 Company::Get(_current_company
)->infrastructure
.water
+= 2 * LOCK_DEPOT_TILE_FACTOR
;
144 DirtyCompanyInfrastructureWindows(_current_company
);
146 MakeShipDepot(tile
, _current_company
, depot
->index
, DEPOT_PART_NORTH
, axis
, wc1
);
147 MakeShipDepot(tile2
, _current_company
, depot
->index
, DEPOT_PART_SOUTH
, axis
, wc2
);
148 CheckForDockingTile(tile
);
149 CheckForDockingTile(tile2
);
150 MarkTileDirtyByTile(tile
);
151 MarkTileDirtyByTile(tile2
);
152 MakeDefaultName(depot
);
158 bool IsPossibleDockingTile(TileIndex t
)
160 assert(IsValidTile(t
));
161 switch (GetTileType(t
)) {
163 if (IsLock(t
) && GetLockPart(t
) == LOCK_PART_MIDDLE
) return false;
167 case MP_TUNNELBRIDGE
:
168 return TrackStatusToTrackBits(GetTileTrackStatus(t
, TRANSPORT_WATER
, 0)) != TRACK_BIT_NONE
;
176 * Mark the supplied tile as a docking tile if it is suitable for docking.
177 * Tiles surrounding the tile are tested to be docks with correct orientation.
178 * @param t Tile to test.
180 void CheckForDockingTile(TileIndex t
)
182 for (DiagDirection d
= DIAGDIR_BEGIN
; d
!= DIAGDIR_END
; d
++) {
183 TileIndex tile
= t
+ TileOffsByDiagDir(d
);
184 if (!IsValidTile(tile
)) continue;
186 if (IsDockTile(tile
) && IsValidDockingDirectionForDock(tile
, d
)) {
187 Station::GetByTile(tile
)->docking_station
.Add(t
);
188 SetDockingTile(t
, true);
190 if (IsTileType(tile
, MP_INDUSTRY
)) {
191 Station
*st
= Industry::GetByTile(tile
)->neutral_station
;
193 st
->docking_station
.Add(t
);
194 SetDockingTile(t
, true);
197 if (IsTileType(tile
, MP_STATION
) && IsOilRig(tile
)) {
198 Station::GetByTile(tile
)->docking_station
.Add(t
);
199 SetDockingTile(t
, true);
204 void MakeWaterKeepingClass(TileIndex tile
, Owner o
)
206 WaterClass wc
= GetWaterClass(tile
);
208 /* Autoslope might turn an originally canal or river tile into land */
210 Slope slope
= GetTileSlope(tile
, &z
);
212 if (slope
!= SLOPE_FLAT
) {
213 if (wc
== WATER_CLASS_CANAL
) {
214 /* If we clear the canal, we have to remove it from the infrastructure count as well. */
215 Company
*c
= Company::GetIfValid(o
);
217 c
->infrastructure
.water
--;
218 DirtyCompanyInfrastructureWindows(c
->index
);
220 /* Sloped canals are locks and no natural water remains whatever the slope direction */
221 wc
= WATER_CLASS_INVALID
;
224 /* Only river water should be restored on appropriate slopes. Other water would be invalid on slopes */
225 if (wc
!= WATER_CLASS_RIVER
|| GetInclinedSlopeDirection(slope
) == INVALID_DIAGDIR
) {
226 wc
= WATER_CLASS_INVALID
;
230 if (wc
== WATER_CLASS_SEA
&& z
> 0) {
231 /* Update company infrastructure count. */
232 Company
*c
= Company::GetIfValid(o
);
234 c
->infrastructure
.water
++;
235 DirtyCompanyInfrastructureWindows(c
->index
);
238 wc
= WATER_CLASS_CANAL
;
241 /* Zero map array and terminate animation */
244 /* Maybe change to water */
246 case WATER_CLASS_SEA
: MakeSea(tile
); break;
247 case WATER_CLASS_CANAL
: MakeCanal(tile
, o
, Random()); break;
248 case WATER_CLASS_RIVER
: MakeRiver(tile
, Random()); break;
252 if (wc
!= WATER_CLASS_INVALID
) CheckForDockingTile(tile
);
253 MarkTileDirtyByTile(tile
);
256 static CommandCost
RemoveShipDepot(TileIndex tile
, DoCommandFlag flags
)
258 if (!IsShipDepot(tile
)) return CMD_ERROR
;
260 CommandCost ret
= CheckTileOwnership(tile
);
261 if (ret
.Failed()) return ret
;
263 TileIndex tile2
= GetOtherShipDepotTile(tile
);
265 /* do not check for ship on tile when company goes bankrupt */
266 if (!(flags
& DC_BANKRUPT
)) {
267 CommandCost ret
= EnsureNoVehicleOnGround(tile
);
268 if (ret
.Succeeded()) ret
= EnsureNoVehicleOnGround(tile2
);
269 if (ret
.Failed()) return ret
;
272 if (flags
& DC_EXEC
) {
273 delete Depot::GetByTile(tile
);
275 Company
*c
= Company::GetIfValid(GetTileOwner(tile
));
277 c
->infrastructure
.water
-= 2 * LOCK_DEPOT_TILE_FACTOR
;
278 DirtyCompanyInfrastructureWindows(c
->index
);
281 MakeWaterKeepingClass(tile
, GetTileOwner(tile
));
282 MakeWaterKeepingClass(tile2
, GetTileOwner(tile2
));
285 return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_CLEAR_DEPOT_SHIP
]);
290 * @param tile Central tile of the lock.
291 * @param dir Uphill direction.
292 * @param flags Operation to perform.
293 * @return The cost in case of success, or an error code if it failed.
295 static CommandCost
DoBuildLock(TileIndex tile
, DiagDirection dir
, DoCommandFlag flags
)
297 CommandCost
cost(EXPENSES_CONSTRUCTION
);
299 int delta
= TileOffsByDiagDir(dir
);
300 CommandCost ret
= EnsureNoVehicleOnGround(tile
);
301 if (ret
.Succeeded()) ret
= EnsureNoVehicleOnGround(tile
+ delta
);
302 if (ret
.Succeeded()) ret
= EnsureNoVehicleOnGround(tile
- delta
);
303 if (ret
.Failed()) return ret
;
306 WaterClass wc_middle
= HasTileWaterGround(tile
) ? GetWaterClass(tile
) : WATER_CLASS_CANAL
;
307 ret
= Command
<CMD_LANDSCAPE_CLEAR
>::Do(flags
, tile
);
308 if (ret
.Failed()) return ret
;
312 if (!IsWaterTile(tile
- delta
)) {
313 ret
= Command
<CMD_LANDSCAPE_CLEAR
>::Do(flags
, tile
- delta
);
314 if (ret
.Failed()) return ret
;
316 cost
.AddCost(_price
[PR_BUILD_CANAL
]);
318 if (!IsTileFlat(tile
- delta
)) {
319 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION
);
321 WaterClass wc_lower
= IsWaterTile(tile
- delta
) ? GetWaterClass(tile
- delta
) : WATER_CLASS_CANAL
;
324 if (!IsWaterTile(tile
+ delta
)) {
325 ret
= Command
<CMD_LANDSCAPE_CLEAR
>::Do(flags
, tile
+ delta
);
326 if (ret
.Failed()) return ret
;
328 cost
.AddCost(_price
[PR_BUILD_CANAL
]);
330 if (!IsTileFlat(tile
+ delta
)) {
331 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION
);
333 WaterClass wc_upper
= IsWaterTile(tile
+ delta
) ? GetWaterClass(tile
+ delta
) : WATER_CLASS_CANAL
;
335 if (IsBridgeAbove(tile
) || IsBridgeAbove(tile
- delta
) || IsBridgeAbove(tile
+ delta
)) {
336 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
);
339 if (flags
& DC_EXEC
) {
340 /* Update company infrastructure counts. */
341 Company
*c
= Company::GetIfValid(_current_company
);
343 /* Counts for the water. */
344 if (!IsWaterTile(tile
- delta
)) c
->infrastructure
.water
++;
345 if (!IsWaterTile(tile
+ delta
)) c
->infrastructure
.water
++;
346 /* Count for the lock itself. */
347 c
->infrastructure
.water
+= 3 * LOCK_DEPOT_TILE_FACTOR
; // Lock is three tiles.
348 DirtyCompanyInfrastructureWindows(_current_company
);
351 MakeLock(tile
, _current_company
, dir
, wc_lower
, wc_upper
, wc_middle
);
352 CheckForDockingTile(tile
- delta
);
353 CheckForDockingTile(tile
+ delta
);
354 MarkTileDirtyByTile(tile
);
355 MarkTileDirtyByTile(tile
- delta
);
356 MarkTileDirtyByTile(tile
+ delta
);
357 MarkCanalsAndRiversAroundDirty(tile
- delta
);
358 MarkCanalsAndRiversAroundDirty(tile
+ delta
);
360 cost
.AddCost(_price
[PR_BUILD_LOCK
]);
367 * @param tile Central tile of the lock.
368 * @param flags Operation to perform.
369 * @return The cost in case of success, or an error code if it failed.
371 static CommandCost
RemoveLock(TileIndex tile
, DoCommandFlag flags
)
373 if (GetTileOwner(tile
) != OWNER_NONE
) {
374 CommandCost ret
= CheckTileOwnership(tile
);
375 if (ret
.Failed()) return ret
;
378 TileIndexDiff delta
= TileOffsByDiagDir(GetLockDirection(tile
));
380 /* make sure no vehicle is on the tile. */
381 CommandCost ret
= EnsureNoVehicleOnGround(tile
);
382 if (ret
.Succeeded()) ret
= EnsureNoVehicleOnGround(tile
+ delta
);
383 if (ret
.Succeeded()) ret
= EnsureNoVehicleOnGround(tile
- delta
);
384 if (ret
.Failed()) return ret
;
386 if (flags
& DC_EXEC
) {
387 /* Remove middle part from company infrastructure count. */
388 Company
*c
= Company::GetIfValid(GetTileOwner(tile
));
390 c
->infrastructure
.water
-= 3 * LOCK_DEPOT_TILE_FACTOR
; // three parts of the lock.
391 DirtyCompanyInfrastructureWindows(c
->index
);
394 if (GetWaterClass(tile
) == WATER_CLASS_RIVER
) {
395 MakeRiver(tile
, Random());
399 MakeWaterKeepingClass(tile
+ delta
, GetTileOwner(tile
+ delta
));
400 MakeWaterKeepingClass(tile
- delta
, GetTileOwner(tile
- delta
));
401 MarkCanalsAndRiversAroundDirty(tile
);
402 MarkCanalsAndRiversAroundDirty(tile
- delta
);
403 MarkCanalsAndRiversAroundDirty(tile
+ delta
);
406 return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_CLEAR_LOCK
]);
411 * @param flags type of operation
412 * @param tile tile where to place the lock
413 * @return the cost of this operation or an error
415 CommandCost
CmdBuildLock(DoCommandFlag flags
, TileIndex tile
)
417 DiagDirection dir
= GetInclinedSlopeDirection(GetTileSlope(tile
));
418 if (dir
== INVALID_DIAGDIR
) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION
);
420 return DoBuildLock(tile
, dir
, flags
);
423 /** Callback to create non-desert around a river tile. */
424 bool RiverModifyDesertZone(TileIndex tile
, void *)
426 if (GetTropicZone(tile
) == TROPICZONE_DESERT
) SetTropicZone(tile
, TROPICZONE_NORMAL
);
431 * Make a river tile and remove desert directly around it.
432 * @param tile The tile to change into river and create non-desert around
434 void MakeRiverAndModifyDesertZoneAround(TileIndex tile
) {
435 MakeRiver(tile
, Random());
436 MarkTileDirtyByTile(tile
);
438 /* Remove desert directly around the river tile. */
439 CircularTileSearch(&tile
, RIVER_OFFSET_DESERT_DISTANCE
, RiverModifyDesertZone
, nullptr);
443 * Build a piece of canal.
444 * @param flags type of operation
445 * @param tile end tile of stretch-dragging
446 * @param start_tile start tile of stretch-dragging
447 * @param wc waterclass to build. sea and river can only be built in scenario editor
448 * @param diagonal Whether to use the Orthogonal (0) or Diagonal (1) iterator.
449 * @return the cost of this operation or an error
451 CommandCost
CmdBuildCanal(DoCommandFlag flags
, TileIndex tile
, TileIndex start_tile
, WaterClass wc
, bool diagonal
)
453 if (start_tile
>= Map::Size() || !IsValidWaterClass(wc
)) return CMD_ERROR
;
455 /* Outside of the editor you can only build canals, not oceans */
456 if (wc
!= WATER_CLASS_CANAL
&& _game_mode
!= GM_EDITOR
) return CMD_ERROR
;
458 CommandCost
cost(EXPENSES_CONSTRUCTION
);
460 std::unique_ptr
<TileIterator
> iter
= TileIterator::Create(tile
, start_tile
, diagonal
);
461 for (; *iter
!= INVALID_TILE
; ++(*iter
)) {
462 TileIndex current_tile
= *iter
;
465 Slope slope
= GetTileSlope(current_tile
);
466 if (slope
!= SLOPE_FLAT
&& (wc
!= WATER_CLASS_RIVER
|| !IsInclinedSlope(slope
))) {
467 return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED
);
470 bool water
= IsWaterTile(current_tile
);
472 /* Outside the editor, prevent building canals over your own or OWNER_NONE owned canals */
473 if (water
&& IsCanal(current_tile
) && _game_mode
!= GM_EDITOR
&& (IsTileOwner(current_tile
, _current_company
) || IsTileOwner(current_tile
, OWNER_NONE
))) continue;
475 ret
= Command
<CMD_LANDSCAPE_CLEAR
>::Do(flags
, current_tile
);
476 if (ret
.Failed()) return ret
;
478 if (!water
) cost
.AddCost(ret
);
480 if (flags
& DC_EXEC
) {
482 case WATER_CLASS_RIVER
:
483 MakeRiver(current_tile
, Random());
484 if (_game_mode
== GM_EDITOR
) {
485 TileIndex tile2
= current_tile
;
486 CircularTileSearch(&tile2
, RIVER_OFFSET_DESERT_DISTANCE
, RiverModifyDesertZone
, nullptr);
490 case WATER_CLASS_SEA
:
491 if (TileHeight(current_tile
) == 0) {
492 MakeSea(current_tile
);
498 /* If we overbuild a water object with a canal, don't update the infrastructure total. */
499 bool is_existing_canal
= IsTileType(current_tile
, MP_WATER
) && IsCanal(current_tile
);
500 if (Company::IsValidID(_current_company
) && !is_existing_canal
) {
501 Company::Get(_current_company
)->infrastructure
.water
++;
502 DirtyCompanyInfrastructureWindows(_current_company
);
505 MakeCanal(current_tile
, _current_company
, Random());
508 MarkTileDirtyByTile(current_tile
);
509 MarkCanalsAndRiversAroundDirty(current_tile
);
510 CheckForDockingTile(current_tile
);
513 cost
.AddCost(_price
[PR_BUILD_CANAL
]);
516 if (cost
.GetCost() == 0) {
517 return_cmd_error(STR_ERROR_ALREADY_BUILT
);
523 static CommandCost
ClearTile_Water(TileIndex tile
, DoCommandFlag flags
)
525 switch (GetWaterTileType(tile
)) {
526 case WATER_TILE_CLEAR
: {
527 if (flags
& DC_NO_WATER
) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER
);
529 Money base_cost
= IsCanal(tile
) ? _price
[PR_CLEAR_CANAL
] : _price
[PR_CLEAR_WATER
];
530 /* Make sure freeform edges are allowed or it's not an edge tile. */
531 if (!_settings_game
.construction
.freeform_edges
&& (!IsInsideMM(TileX(tile
), 1, Map::MaxX() - 1) ||
532 !IsInsideMM(TileY(tile
), 1, Map::MaxY() - 1))) {
533 return_cmd_error(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP
);
536 /* Make sure no vehicle is on the tile */
537 CommandCost ret
= EnsureNoVehicleOnGround(tile
);
538 if (ret
.Failed()) return ret
;
540 Owner owner
= GetTileOwner(tile
);
541 if (owner
!= OWNER_WATER
&& owner
!= OWNER_NONE
) {
542 CommandCost ret
= CheckTileOwnership(tile
);
543 if (ret
.Failed()) return ret
;
546 if (flags
& DC_EXEC
) {
547 if (IsCanal(tile
) && Company::IsValidID(owner
)) {
548 Company::Get(owner
)->infrastructure
.water
--;
549 DirtyCompanyInfrastructureWindows(owner
);
552 MarkCanalsAndRiversAroundDirty(tile
);
555 return CommandCost(EXPENSES_CONSTRUCTION
, base_cost
);
558 case WATER_TILE_COAST
: {
559 Slope slope
= GetTileSlope(tile
);
561 /* Make sure no vehicle is on the tile */
562 CommandCost ret
= EnsureNoVehicleOnGround(tile
);
563 if (ret
.Failed()) return ret
;
565 if (flags
& DC_EXEC
) {
567 MarkCanalsAndRiversAroundDirty(tile
);
569 if (IsSlopeWithOneCornerRaised(slope
)) {
570 return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_CLEAR_WATER
]);
572 return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_CLEAR_ROUGH
]);
576 case WATER_TILE_LOCK
: {
577 static const TileIndexDiffC _lock_tomiddle_offs
[][DIAGDIR_END
] = {
579 { { 0, 0}, {0, 0}, { 0, 0}, {0, 0} }, // LOCK_PART_MIDDLE
580 { {-1, 0}, {0, 1}, { 1, 0}, {0, -1} }, // LOCK_PART_LOWER
581 { { 1, 0}, {0, -1}, {-1, 0}, {0, 1} }, // LOCK_PART_UPPER
584 if (flags
& DC_AUTO
) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED
);
585 if (_current_company
== OWNER_WATER
) return CMD_ERROR
;
586 /* move to the middle tile.. */
587 return RemoveLock(tile
+ ToTileIndexDiff(_lock_tomiddle_offs
[GetLockPart(tile
)][GetLockDirection(tile
)]), flags
);
590 case WATER_TILE_DEPOT
:
591 if (flags
& DC_AUTO
) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED
);
592 return RemoveShipDepot(tile
, flags
);
600 * return true if a tile is a water tile wrt. a certain direction.
602 * @param tile The tile of interest.
603 * @param from The direction of interest.
604 * @return true iff the tile is water in the view of 'from'.
607 bool IsWateredTile(TileIndex tile
, Direction from
)
609 switch (GetTileType(tile
)) {
611 switch (GetWaterTileType(tile
)) {
612 default: NOT_REACHED();
613 case WATER_TILE_DEPOT
: case WATER_TILE_CLEAR
: return true;
614 case WATER_TILE_LOCK
: return DiagDirToAxis(GetLockDirection(tile
)) == DiagDirToAxis(DirToDiagDir(from
));
616 case WATER_TILE_COAST
:
617 switch (GetTileSlope(tile
)) {
618 case SLOPE_W
: return (from
== DIR_SE
) || (from
== DIR_E
) || (from
== DIR_NE
);
619 case SLOPE_S
: return (from
== DIR_NE
) || (from
== DIR_N
) || (from
== DIR_NW
);
620 case SLOPE_E
: return (from
== DIR_NW
) || (from
== DIR_W
) || (from
== DIR_SW
);
621 case SLOPE_N
: return (from
== DIR_SW
) || (from
== DIR_S
) || (from
== DIR_SE
);
622 default: return false;
627 if (GetRailGroundType(tile
) == RAIL_GROUND_WATER
) {
628 assert(IsPlainRail(tile
));
629 switch (GetTileSlope(tile
)) {
630 case SLOPE_W
: return (from
== DIR_SE
) || (from
== DIR_E
) || (from
== DIR_NE
);
631 case SLOPE_S
: return (from
== DIR_NE
) || (from
== DIR_N
) || (from
== DIR_NW
);
632 case SLOPE_E
: return (from
== DIR_NW
) || (from
== DIR_W
) || (from
== DIR_SW
);
633 case SLOPE_N
: return (from
== DIR_SW
) || (from
== DIR_S
) || (from
== DIR_SE
);
634 default: return false;
640 if (IsOilRig(tile
)) {
641 /* Do not draw waterborders inside of industries.
642 * Note: There is no easy way to detect the industry of an oilrig tile. */
643 TileIndex src_tile
= tile
+ TileOffsByDir(from
);
644 if ((IsTileType(src_tile
, MP_STATION
) && IsOilRig(src_tile
)) ||
645 (IsTileType(src_tile
, MP_INDUSTRY
))) return true;
647 return IsTileOnWater(tile
);
649 return (IsDock(tile
) && IsTileFlat(tile
)) || IsBuoy(tile
);
652 /* Do not draw waterborders inside of industries.
653 * Note: There is no easy way to detect the industry of an oilrig tile. */
654 TileIndex src_tile
= tile
+ TileOffsByDir(from
);
655 if ((IsTileType(src_tile
, MP_STATION
) && IsOilRig(src_tile
)) ||
656 (IsTileType(src_tile
, MP_INDUSTRY
) && GetIndustryIndex(src_tile
) == GetIndustryIndex(tile
))) return true;
658 return IsTileOnWater(tile
);
661 case MP_OBJECT
: return IsTileOnWater(tile
);
663 case MP_TUNNELBRIDGE
: return GetTunnelBridgeTransportType(tile
) == TRANSPORT_WATER
&& ReverseDiagDir(GetTunnelBridgeDirection(tile
)) == DirToDiagDir(from
);
665 case MP_VOID
: return true; // consider map border as water, esp. for rivers
667 default: return false;
672 * Draw a water sprite, potentially with a NewGRF-modified sprite offset.
673 * @param base Sprite base.
674 * @param offset Sprite offset.
675 * @param feature The type of sprite that is drawn.
676 * @param tile Tile index to draw.
678 static void DrawWaterSprite(SpriteID base
, uint offset
, CanalFeature feature
, TileIndex tile
)
680 if (base
!= SPR_FLAT_WATER_TILE
) {
681 /* Only call offset callback if the sprite is NewGRF-provided. */
682 offset
= GetCanalSpriteOffset(feature
, tile
, offset
);
684 DrawGroundSprite(base
+ offset
, PAL_NONE
);
688 * Draw canal or river edges.
689 * @param canal True if canal edges should be drawn, false for river edges.
690 * @param offset Sprite offset.
691 * @param tile Tile to draw.
693 static void DrawWaterEdges(bool canal
, uint offset
, TileIndex tile
)
695 CanalFeature feature
;
699 base
= GetCanalSprite(CF_DIKES
, tile
);
700 if (base
== 0) base
= SPR_CANAL_DIKES_BASE
;
702 feature
= CF_RIVER_EDGE
;
703 base
= GetCanalSprite(CF_RIVER_EDGE
, tile
);
704 if (base
== 0) return; // Don't draw if no sprites provided.
709 /* determine the edges around with water. */
710 wa
= IsWateredTile(TILE_ADDXY(tile
, -1, 0), DIR_SW
) << 0;
711 wa
+= IsWateredTile(TILE_ADDXY(tile
, 0, 1), DIR_NW
) << 1;
712 wa
+= IsWateredTile(TILE_ADDXY(tile
, 1, 0), DIR_NE
) << 2;
713 wa
+= IsWateredTile(TILE_ADDXY(tile
, 0, -1), DIR_SE
) << 3;
715 if (!(wa
& 1)) DrawWaterSprite(base
, offset
, feature
, tile
);
716 if (!(wa
& 2)) DrawWaterSprite(base
, offset
+ 1, feature
, tile
);
717 if (!(wa
& 4)) DrawWaterSprite(base
, offset
+ 2, feature
, tile
);
718 if (!(wa
& 8)) DrawWaterSprite(base
, offset
+ 3, feature
, tile
);
722 case 0: DrawWaterSprite(base
, offset
+ 4, feature
, tile
); break;
723 case 3: if (!IsWateredTile(TILE_ADDXY(tile
, -1, 1), DIR_W
)) DrawWaterSprite(base
, offset
+ 8, feature
, tile
); break;
728 case 0: DrawWaterSprite(base
, offset
+ 5, feature
, tile
); break;
729 case 6: if (!IsWateredTile(TILE_ADDXY(tile
, 1, 1), DIR_N
)) DrawWaterSprite(base
, offset
+ 9, feature
, tile
); break;
734 case 0: DrawWaterSprite(base
, offset
+ 6, feature
, tile
); break;
735 case 12: if (!IsWateredTile(TILE_ADDXY(tile
, 1, -1), DIR_E
)) DrawWaterSprite(base
, offset
+ 10, feature
, tile
); break;
740 case 0: DrawWaterSprite(base
, offset
+ 7, feature
, tile
); break;
741 case 9: if (!IsWateredTile(TILE_ADDXY(tile
, -1, -1), DIR_S
)) DrawWaterSprite(base
, offset
+ 11, feature
, tile
); break;
745 /** Draw a plain sea water tile with no edges */
746 static void DrawSeaWater(TileIndex tile
)
748 DrawGroundSprite(SPR_FLAT_WATER_TILE
, PAL_NONE
);
751 /** draw a canal styled water tile with dikes around */
752 static void DrawCanalWater(TileIndex tile
)
754 SpriteID image
= SPR_FLAT_WATER_TILE
;
755 if (HasBit(_water_feature
[CF_WATERSLOPE
].flags
, CFF_HAS_FLAT_SPRITE
)) {
756 /* First water slope sprite is flat water. */
757 image
= GetCanalSprite(CF_WATERSLOPE
, tile
);
758 if (image
== 0) image
= SPR_FLAT_WATER_TILE
;
760 DrawWaterSprite(image
, 0, CF_WATERSLOPE
, tile
);
762 DrawWaterEdges(true, 0, tile
);
765 #include "table/water_land.h"
768 * Draw a build sprite sequence for water tiles.
769 * If buildings are invisible, nothing will be drawn.
770 * @param ti Tile info.
771 * @param dtss Sprite sequence to draw.
772 * @param base Base sprite.
773 * @param offset Additional sprite offset.
774 * @param palette Palette to use.
776 static void DrawWaterTileStruct(const TileInfo
*ti
, const DrawTileSeqStruct
*dtss
, SpriteID base
, uint offset
, PaletteID palette
, CanalFeature feature
)
778 /* Don't draw if buildings are invisible. */
779 if (IsInvisibilitySet(TO_BUILDINGS
)) return;
781 for (; !dtss
->IsTerminator(); dtss
++) {
782 uint tile_offs
= offset
+ dtss
->image
.sprite
;
783 if (feature
< CF_END
) tile_offs
= GetCanalSpriteOffset(feature
, ti
->tile
, tile_offs
);
784 AddSortableSpriteToDraw(base
+ tile_offs
, palette
,
785 ti
->x
+ dtss
->delta_x
, ti
->y
+ dtss
->delta_y
,
786 dtss
->size_x
, dtss
->size_y
,
787 dtss
->size_z
, ti
->z
+ dtss
->delta_z
,
788 IsTransparencySet(TO_BUILDINGS
));
792 /** Draw a lock tile. */
793 static void DrawWaterLock(const TileInfo
*ti
)
795 int part
= GetLockPart(ti
->tile
);
796 const DrawTileSprites
&dts
= _lock_display_data
[part
][GetLockDirection(ti
->tile
)];
798 /* Draw ground sprite. */
799 SpriteID image
= dts
.ground
.sprite
;
801 SpriteID water_base
= GetCanalSprite(CF_WATERSLOPE
, ti
->tile
);
802 if (water_base
== 0) {
803 /* Use default sprites. */
804 water_base
= SPR_CANALS_BASE
;
805 } else if (HasBit(_water_feature
[CF_WATERSLOPE
].flags
, CFF_HAS_FLAT_SPRITE
)) {
806 /* NewGRF supplies a flat sprite as first sprite. */
807 if (image
== SPR_FLAT_WATER_TILE
) {
814 if (image
< 5) image
+= water_base
;
815 DrawGroundSprite(image
, PAL_NONE
);
817 /* Draw structures. */
819 SpriteID base
= GetCanalSprite(CF_LOCKS
, ti
->tile
);
822 /* If no custom graphics, use defaults. */
823 base
= SPR_LOCK_BASE
;
824 uint8 z_threshold
= part
== LOCK_PART_UPPER
? 8 : 0;
825 zoffs
= ti
->z
> z_threshold
? 24 : 0;
828 DrawWaterTileStruct(ti
, dts
.seq
, base
, zoffs
, PAL_NONE
, CF_LOCKS
);
831 /** Draw a ship depot tile. */
832 static void DrawWaterDepot(const TileInfo
*ti
)
834 DrawWaterClassGround(ti
);
835 DrawWaterTileStruct(ti
, _shipdepot_display_data
[GetShipDepotAxis(ti
->tile
)][GetShipDepotPart(ti
->tile
)].seq
, 0, 0, COMPANY_SPRITE_COLOUR(GetTileOwner(ti
->tile
)), CF_END
);
838 static void DrawRiverWater(const TileInfo
*ti
)
840 SpriteID image
= SPR_FLAT_WATER_TILE
;
842 uint edges_offset
= 0;
844 if (ti
->tileh
!= SLOPE_FLAT
|| HasBit(_water_feature
[CF_RIVER_SLOPE
].flags
, CFF_HAS_FLAT_SPRITE
)) {
845 image
= GetCanalSprite(CF_RIVER_SLOPE
, ti
->tile
);
848 case SLOPE_NW
: image
= SPR_WATER_SLOPE_Y_DOWN
; break;
849 case SLOPE_SW
: image
= SPR_WATER_SLOPE_X_UP
; break;
850 case SLOPE_SE
: image
= SPR_WATER_SLOPE_Y_UP
; break;
851 case SLOPE_NE
: image
= SPR_WATER_SLOPE_X_DOWN
; break;
852 default: image
= SPR_FLAT_WATER_TILE
; break;
855 /* Flag bit 0 indicates that the first sprite is flat water. */
856 offset
= HasBit(_water_feature
[CF_RIVER_SLOPE
].flags
, CFF_HAS_FLAT_SPRITE
) ? 1 : 0;
859 case SLOPE_SE
: edges_offset
+= 12; break;
860 case SLOPE_NE
: offset
+= 1; edges_offset
+= 24; break;
861 case SLOPE_SW
: offset
+= 2; edges_offset
+= 36; break;
862 case SLOPE_NW
: offset
+= 3; edges_offset
+= 48; break;
863 default: offset
= 0; break;
866 offset
= GetCanalSpriteOffset(CF_RIVER_SLOPE
, ti
->tile
, offset
);
870 DrawGroundSprite(image
+ offset
, PAL_NONE
);
872 /* Draw river edges if available. */
873 DrawWaterEdges(false, edges_offset
, ti
->tile
);
876 void DrawShoreTile(Slope tileh
)
878 /* Converts the enum Slope into an offset based on SPR_SHORE_BASE.
879 * This allows to calculate the proper sprite to display for this Slope */
880 static const byte tileh_to_shoresprite
[32] = {
881 0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0,
882 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 15, 0,
885 assert(!IsHalftileSlope(tileh
)); // Halftile slopes need to get handled earlier.
886 assert(tileh
!= SLOPE_FLAT
); // Shore is never flat
888 assert((tileh
!= SLOPE_EW
) && (tileh
!= SLOPE_NS
)); // No suitable sprites for current flooding behaviour
890 DrawGroundSprite(SPR_SHORE_BASE
+ tileh_to_shoresprite
[tileh
], PAL_NONE
);
893 void DrawWaterClassGround(const TileInfo
*ti
)
895 switch (GetWaterClass(ti
->tile
)) {
896 case WATER_CLASS_SEA
: DrawSeaWater(ti
->tile
); break;
897 case WATER_CLASS_CANAL
: DrawCanalWater(ti
->tile
); break;
898 case WATER_CLASS_RIVER
: DrawRiverWater(ti
); break;
899 default: NOT_REACHED();
903 static void DrawTile_Water(TileInfo
*ti
)
905 switch (GetWaterTileType(ti
->tile
)) {
906 case WATER_TILE_CLEAR
:
907 DrawWaterClassGround(ti
);
908 DrawBridgeMiddle(ti
);
911 case WATER_TILE_COAST
: {
912 DrawShoreTile(ti
->tileh
);
913 DrawBridgeMiddle(ti
);
917 case WATER_TILE_LOCK
:
921 case WATER_TILE_DEPOT
:
927 void DrawShipDepotSprite(int x
, int y
, Axis axis
, DepotPart part
)
929 const DrawTileSprites
&dts
= _shipdepot_display_data
[axis
][part
];
931 DrawSprite(dts
.ground
.sprite
, dts
.ground
.pal
, x
, y
);
932 DrawOrigTileSeqInGUI(x
, y
, &dts
, COMPANY_SPRITE_COLOUR(_local_company
));
936 static int GetSlopePixelZ_Water(TileIndex tile
, uint x
, uint y
)
939 Slope tileh
= GetTilePixelSlope(tile
, &z
);
941 return z
+ GetPartialPixelZ(x
& 0xF, y
& 0xF, tileh
);
944 static Foundation
GetFoundation_Water(TileIndex tile
, Slope tileh
)
946 return FOUNDATION_NONE
;
949 static void GetTileDesc_Water(TileIndex tile
, TileDesc
*td
)
951 switch (GetWaterTileType(tile
)) {
952 case WATER_TILE_CLEAR
:
953 switch (GetWaterClass(tile
)) {
954 case WATER_CLASS_SEA
: td
->str
= STR_LAI_WATER_DESCRIPTION_WATER
; break;
955 case WATER_CLASS_CANAL
: td
->str
= STR_LAI_WATER_DESCRIPTION_CANAL
; break;
956 case WATER_CLASS_RIVER
: td
->str
= STR_LAI_WATER_DESCRIPTION_RIVER
; break;
957 default: NOT_REACHED();
960 case WATER_TILE_COAST
: td
->str
= STR_LAI_WATER_DESCRIPTION_COAST_OR_RIVERBANK
; break;
961 case WATER_TILE_LOCK
: td
->str
= STR_LAI_WATER_DESCRIPTION_LOCK
; break;
962 case WATER_TILE_DEPOT
:
963 td
->str
= STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT
;
964 td
->build_date
= Depot::GetByTile(tile
)->build_date
;
966 default: NOT_REACHED();
969 td
->owner
[0] = GetTileOwner(tile
);
973 * Handle the flooding of a vehicle. This sets the vehicle state to crashed,
974 * creates a newsitem and dirties the necessary windows.
975 * @param v The vehicle to flood.
977 static void FloodVehicle(Vehicle
*v
)
979 uint pass
= v
->Crash(true);
981 AI::NewEvent(v
->owner
, new ScriptEventVehicleCrashed(v
->index
, v
->tile
, ScriptEventVehicleCrashed::CRASH_FLOODED
));
982 Game::NewEvent(new ScriptEventVehicleCrashed(v
->index
, v
->tile
, ScriptEventVehicleCrashed::CRASH_FLOODED
));
984 AddTileNewsItem(STR_NEWS_DISASTER_FLOOD_VEHICLE
, NT_ACCIDENT
, v
->tile
);
985 CreateEffectVehicleRel(v
, 4, 4, 8, EV_EXPLOSION_LARGE
);
986 if (_settings_client
.sound
.disaster
) SndPlayVehicleFx(SND_12_EXPLOSION
, v
);
990 * Flood a vehicle if we are allowed to flood it, i.e. when it is on the ground.
991 * @param v The vehicle to test for flooding.
992 * @param data The z of level to flood.
993 * @return nullptr as we always want to remove everything.
995 static Vehicle
*FloodVehicleProc(Vehicle
*v
, void *data
)
997 if ((v
->vehstatus
& VS_CRASHED
) != 0) return nullptr;
1002 case VEH_AIRCRAFT
: {
1003 if (!IsAirportTile(v
->tile
) || GetTileMaxZ(v
->tile
) != 0) break;
1004 if (v
->subtype
== AIR_SHADOW
) break;
1006 /* We compare v->z_pos against delta_z + 1 because the shadow
1007 * is at delta_z and the actual aircraft at delta_z + 1. */
1008 const Station
*st
= Station::GetByTile(v
->tile
);
1009 const AirportFTAClass
*airport
= st
->airport
.GetFTA();
1010 if (v
->z_pos
!= airport
->delta_z
+ 1) break;
1018 int z
= *(int*)data
;
1019 if (v
->z_pos
> z
) break;
1020 FloodVehicle(v
->First());
1029 * Finds a vehicle to flood.
1030 * It does not find vehicles that are already crashed on bridges, i.e. flooded.
1031 * @param tile the tile where to find a vehicle to flood
1033 static void FloodVehicles(TileIndex tile
)
1037 if (IsAirportTile(tile
)) {
1038 const Station
*st
= Station::GetByTile(tile
);
1039 for (TileIndex airport_tile
: st
->airport
) {
1040 if (st
->TileBelongsToAirport(airport_tile
)) FindVehicleOnPos(airport_tile
, &z
, &FloodVehicleProc
);
1043 /* No vehicle could be flooded on this airport anymore */
1047 if (!IsBridgeTile(tile
)) {
1048 FindVehicleOnPos(tile
, &z
, &FloodVehicleProc
);
1052 TileIndex end
= GetOtherBridgeEnd(tile
);
1053 z
= GetBridgePixelHeight(tile
);
1055 FindVehicleOnPos(tile
, &z
, &FloodVehicleProc
);
1056 FindVehicleOnPos(end
, &z
, &FloodVehicleProc
);
1060 * Returns the behaviour of a tile during flooding.
1062 * @return Behaviour of the tile
1064 FloodingBehaviour
GetFloodingBehaviour(TileIndex tile
)
1066 /* FLOOD_ACTIVE: 'single-corner-raised'-coast, sea, sea-shipdepots, sea-buoys, sea-docks (water part), rail with flooded halftile, sea-water-industries, sea-oilrigs
1067 * FLOOD_DRYUP: coast with more than one corner raised, coast with rail-track, coast with trees
1068 * FLOOD_PASSIVE: (not used)
1069 * FLOOD_NONE: canals, rivers, everything else
1071 switch (GetTileType(tile
)) {
1073 if (IsCoast(tile
)) {
1074 Slope tileh
= GetTileSlope(tile
);
1075 return (IsSlopeWithOneCornerRaised(tileh
) ? FLOOD_ACTIVE
: FLOOD_DRYUP
);
1081 return (GetWaterClass(tile
) == WATER_CLASS_SEA
) ? FLOOD_ACTIVE
: FLOOD_NONE
;
1084 if (GetRailGroundType(tile
) == RAIL_GROUND_WATER
) {
1085 return (IsSlopeWithOneCornerRaised(GetTileSlope(tile
)) ? FLOOD_ACTIVE
: FLOOD_DRYUP
);
1090 return (GetTreeGround(tile
) == TREE_GROUND_SHORE
? FLOOD_DRYUP
: FLOOD_NONE
);
1093 return FLOOD_ACTIVE
;
1103 void DoFloodTile(TileIndex target
)
1105 assert(!IsTileType(target
, MP_WATER
));
1107 bool flooded
= false; // Will be set to true if something is changed.
1109 Backup
<CompanyID
> cur_company(_current_company
, OWNER_WATER
, FILE_LINE
);
1111 Slope tileh
= GetTileSlope(target
);
1112 if (tileh
!= SLOPE_FLAT
) {
1114 switch (GetTileType(target
)) {
1116 if (!IsPlainRail(target
)) break;
1117 FloodVehicles(target
);
1118 flooded
= FloodHalftile(target
);
1123 if (!IsSlopeWithOneCornerRaised(tileh
)) {
1124 SetTreeGroundDensity(target
, TREE_GROUND_SHORE
, 3);
1125 MarkTileDirtyByTile(target
);
1132 if (Command
<CMD_LANDSCAPE_CLEAR
>::Do(DC_EXEC
, target
).Succeeded()) {
1134 MarkTileDirtyByTile(target
);
1143 /* Flood vehicles */
1144 FloodVehicles(target
);
1146 /* flood flat tile */
1147 if (Command
<CMD_LANDSCAPE_CLEAR
>::Do(DC_EXEC
, target
).Succeeded()) {
1149 MarkTileDirtyByTile(target
);
1155 /* Mark surrounding canal tiles dirty too to avoid glitches */
1156 MarkCanalsAndRiversAroundDirty(target
);
1158 /* update signals if needed */
1159 UpdateSignalsInBuffer();
1161 if (IsPossibleDockingTile(target
)) CheckForDockingTile(target
);
1164 cur_company
.Restore();
1170 static void DoDryUp(TileIndex tile
)
1172 Backup
<CompanyID
> cur_company(_current_company
, OWNER_WATER
, FILE_LINE
);
1174 switch (GetTileType(tile
)) {
1176 assert(IsPlainRail(tile
));
1177 assert(GetRailGroundType(tile
) == RAIL_GROUND_WATER
);
1179 RailGroundType new_ground
;
1180 switch (GetTrackBits(tile
)) {
1181 case TRACK_BIT_UPPER
: new_ground
= RAIL_GROUND_FENCE_HORIZ1
; break;
1182 case TRACK_BIT_LOWER
: new_ground
= RAIL_GROUND_FENCE_HORIZ2
; break;
1183 case TRACK_BIT_LEFT
: new_ground
= RAIL_GROUND_FENCE_VERT1
; break;
1184 case TRACK_BIT_RIGHT
: new_ground
= RAIL_GROUND_FENCE_VERT2
; break;
1185 default: NOT_REACHED();
1187 SetRailGroundType(tile
, new_ground
);
1188 MarkTileDirtyByTile(tile
);
1192 SetTreeGroundDensity(tile
, TREE_GROUND_GRASS
, 3);
1193 MarkTileDirtyByTile(tile
);
1197 assert(IsCoast(tile
));
1199 if (Command
<CMD_LANDSCAPE_CLEAR
>::Do(DC_EXEC
, tile
).Succeeded()) {
1200 MakeClear(tile
, CLEAR_GRASS
, 3);
1201 MarkTileDirtyByTile(tile
);
1205 default: NOT_REACHED();
1208 cur_company
.Restore();
1212 * Let a water tile floods its diagonal adjoining tiles
1213 * called from tunnelbridge_cmd, and by TileLoop_Industry() and TileLoop_Track()
1215 * @param tile the water/shore tile that floods
1217 void TileLoop_Water(TileIndex tile
)
1219 if (IsTileType(tile
, MP_WATER
)) AmbientSoundEffect(tile
);
1221 switch (GetFloodingBehaviour(tile
)) {
1223 for (Direction dir
= DIR_BEGIN
; dir
< DIR_END
; dir
++) {
1224 TileIndex dest
= tile
+ TileOffsByDir(dir
);
1225 if (!IsValidTile(dest
)) continue;
1226 /* do not try to flood water tiles - increases performance a lot */
1227 if (IsTileType(dest
, MP_WATER
)) continue;
1229 /* TREE_GROUND_SHORE is the sign of a previous flood. */
1230 if (IsTileType(dest
, MP_TREES
) && GetTreeGround(dest
) == TREE_GROUND_SHORE
) continue;
1233 Slope slope_dest
= GetFoundationSlope(dest
, &z_dest
) & ~SLOPE_HALFTILE_MASK
& ~SLOPE_STEEP
;
1234 if (z_dest
> 0) continue;
1236 if (!HasBit(_flood_from_dirs
[slope_dest
], ReverseDir(dir
))) continue;
1243 Slope slope_here
= GetFoundationSlope(tile
) & ~SLOPE_HALFTILE_MASK
& ~SLOPE_STEEP
;
1244 for (uint dir
: SetBitIterator(_flood_from_dirs
[slope_here
])) {
1245 TileIndex dest
= tile
+ TileOffsByDir((Direction
)dir
);
1246 if (dest
>= Map::Size()) continue;
1248 FloodingBehaviour dest_behaviour
= GetFloodingBehaviour(dest
);
1249 if ((dest_behaviour
== FLOOD_ACTIVE
) || (dest_behaviour
== FLOOD_PASSIVE
)) return;
1259 void ConvertGroundTilesIntoWaterTiles()
1263 for (TileIndex tile
= 0; tile
< Map::Size(); ++tile
) {
1264 Slope slope
= GetTileSlope(tile
, &z
);
1265 if (IsTileType(tile
, MP_CLEAR
) && z
== 0) {
1266 /* Make both water for tiles at level 0
1267 * and make shore, as that looks much better
1268 * during the generation. */
1282 for (uint dir
: SetBitIterator(_flood_from_dirs
[slope
& ~SLOPE_STEEP
])) {
1283 TileIndex dest
= TileAddByDir(tile
, (Direction
)dir
);
1284 Slope slope_dest
= GetTileSlope(dest
) & ~SLOPE_STEEP
;
1285 if (slope_dest
== SLOPE_FLAT
|| IsSlopeWithOneCornerRaised(slope_dest
) || IsTileType(dest
, MP_VOID
)) {
1296 static TrackStatus
GetTileTrackStatus_Water(TileIndex tile
, TransportType mode
, uint sub_mode
, DiagDirection side
)
1298 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
,
1299 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
};
1303 if (mode
!= TRANSPORT_WATER
) return 0;
1305 switch (GetWaterTileType(tile
)) {
1306 case WATER_TILE_CLEAR
: ts
= IsTileFlat(tile
) ? TRACK_BIT_ALL
: TRACK_BIT_NONE
; break;
1307 case WATER_TILE_COAST
: ts
= coast_tracks
[GetTileSlope(tile
) & 0xF]; break;
1308 case WATER_TILE_LOCK
: ts
= DiagDirToDiagTrackBits(GetLockDirection(tile
)); break;
1309 case WATER_TILE_DEPOT
: ts
= AxisToTrackBits(GetShipDepotAxis(tile
)); break;
1312 if (TileX(tile
) == 0) {
1313 /* NE border: remove tracks that connects NE tile edge */
1314 ts
&= ~(TRACK_BIT_X
| TRACK_BIT_UPPER
| TRACK_BIT_RIGHT
);
1316 if (TileY(tile
) == 0) {
1317 /* NW border: remove tracks that connects NW tile edge */
1318 ts
&= ~(TRACK_BIT_Y
| TRACK_BIT_LEFT
| TRACK_BIT_UPPER
);
1320 return CombineTrackStatus(TrackBitsToTrackdirBits(ts
), TRACKDIR_BIT_NONE
);
1323 static bool ClickTile_Water(TileIndex tile
)
1325 if (GetWaterTileType(tile
) == WATER_TILE_DEPOT
) {
1326 ShowDepotWindow(GetShipDepotNorthTile(tile
), VEH_SHIP
);
1332 static void ChangeTileOwner_Water(TileIndex tile
, Owner old_owner
, Owner new_owner
)
1334 if (!IsTileOwner(tile
, old_owner
)) return;
1336 bool is_lock_middle
= IsLock(tile
) && GetLockPart(tile
) == LOCK_PART_MIDDLE
;
1338 /* No need to dirty company windows here, we'll redraw the whole screen anyway. */
1339 if (is_lock_middle
) Company::Get(old_owner
)->infrastructure
.water
-= 3 * LOCK_DEPOT_TILE_FACTOR
; // Lock has three parts.
1340 if (new_owner
!= INVALID_OWNER
) {
1341 if (is_lock_middle
) Company::Get(new_owner
)->infrastructure
.water
+= 3 * LOCK_DEPOT_TILE_FACTOR
; // Lock has three parts.
1342 /* Only subtract from the old owner here if the new owner is valid,
1343 * otherwise we clear ship depots and canal water below. */
1344 if (GetWaterClass(tile
) == WATER_CLASS_CANAL
&& !is_lock_middle
) {
1345 Company::Get(old_owner
)->infrastructure
.water
--;
1346 Company::Get(new_owner
)->infrastructure
.water
++;
1348 if (IsShipDepot(tile
)) {
1349 Company::Get(old_owner
)->infrastructure
.water
-= LOCK_DEPOT_TILE_FACTOR
;
1350 Company::Get(new_owner
)->infrastructure
.water
+= LOCK_DEPOT_TILE_FACTOR
;
1353 SetTileOwner(tile
, new_owner
);
1358 if (IsShipDepot(tile
)) Command
<CMD_LANDSCAPE_CLEAR
>::Do(DC_EXEC
| DC_BANKRUPT
, tile
);
1360 /* Set owner of canals and locks ... and also canal under dock there was before.
1361 * Check if the new owner after removing depot isn't OWNER_WATER. */
1362 if (IsTileOwner(tile
, old_owner
)) {
1363 if (GetWaterClass(tile
) == WATER_CLASS_CANAL
&& !is_lock_middle
) Company::Get(old_owner
)->infrastructure
.water
--;
1364 SetTileOwner(tile
, OWNER_NONE
);
1368 static VehicleEnterTileStatus
VehicleEnter_Water(Vehicle
*v
, TileIndex tile
, int x
, int y
)
1370 return VETSB_CONTINUE
;
1373 static CommandCost
TerraformTile_Water(TileIndex tile
, DoCommandFlag flags
, int z_new
, Slope tileh_new
)
1375 /* Canals can't be terraformed */
1376 if (IsWaterTile(tile
) && IsCanal(tile
)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_CANAL_FIRST
);
1378 return Command
<CMD_LANDSCAPE_CLEAR
>::Do(flags
, tile
);
1382 extern const TileTypeProcs _tile_type_water_procs
= {
1383 DrawTile_Water
, // draw_tile_proc
1384 GetSlopePixelZ_Water
, // get_slope_z_proc
1385 ClearTile_Water
, // clear_tile_proc
1386 nullptr, // add_accepted_cargo_proc
1387 GetTileDesc_Water
, // get_tile_desc_proc
1388 GetTileTrackStatus_Water
, // get_tile_track_status_proc
1389 ClickTile_Water
, // click_tile_proc
1390 nullptr, // animate_tile_proc
1391 TileLoop_Water
, // tile_loop_proc
1392 ChangeTileOwner_Water
, // change_tile_owner_proc
1393 nullptr, // add_produced_cargo_proc
1394 VehicleEnter_Water
, // vehicle_enter_tile_proc
1395 GetFoundation_Water
, // get_foundation_proc
1396 TerraformTile_Water
, // terraform_tile_proc