4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
10 /** @file water_cmd.cpp Handling of water tiles. */
13 #include "cmd_helper.h"
14 #include "landscape.h"
15 #include "viewport_func.h"
16 #include "command_func.h"
18 #include "news_func.h"
19 #include "depot_base.h"
20 #include "depot_func.h"
22 #include "map/industry.h"
23 #include "newgrf_canal.h"
24 #include "strings_func.h"
25 #include "vehicle_func.h"
26 #include "sound_func.h"
27 #include "company_func.h"
28 #include "map/ground.h"
29 #include "map/slope.h"
31 #include "effectvehicle_func.h"
32 #include "map/bridge.h"
34 #include "station_base.h"
36 #include "game/game.hpp"
37 #include "core/random_func.hpp"
38 #include "core/backup_type.hpp"
39 #include "date_func.h"
40 #include "company_base.h"
41 #include "company_gui.h"
42 #include "newgrf_generic.h"
43 #include "signalbuffer.h"
45 #include "table/strings.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 (IsWaterTile(tile
) && (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 tile tile where ship depot is built
96 * @param flags type of operation
97 * @param p1 bit 0 depot orientation (Axis)
100 * @return the cost of this operation or an error
102 CommandCost
CmdBuildShipDepot(TileIndex tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
104 Axis axis
= Extract
<Axis
, 0, 1>(p1
);
106 TileIndex tile2
= tile
+ (axis
== AXIS_X
? TileDiffXY(1, 0) : TileDiffXY(0, 1));
108 if (!HasTileWaterGround(tile
) || !HasTileWaterGround(tile2
)) {
109 return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER
);
112 if (HasBridgeAbove(tile
) || HasBridgeAbove(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
= !IsPlainWaterTile(tile
);
126 CommandCost ret
= DoCommand(tile
, 0, 0, flags
| DC_AUTO
, CMD_LANDSCAPE_CLEAR
);
127 if (ret
.Failed()) return ret
;
131 add_cost
= !IsPlainWaterTile(tile2
);
132 ret
= DoCommand(tile2
, 0, 0, flags
| DC_AUTO
, CMD_LANDSCAPE_CLEAR
);
133 if (ret
.Failed()) return 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
, ReverseDiagDir(AxisToDiagDir(axis
)), wc1
);
150 MakeShipDepot(tile2
, _current_company
, depot
->index
, AxisToDiagDir(axis
), wc2
);
151 MarkTileDirtyByTile(tile
);
152 MarkTileDirtyByTile(tile2
);
153 MakeDefaultName(depot
);
159 void MakeWaterKeepingClass(TileIndex tile
, Owner o
)
161 WaterClass wc
= GetWaterClass(tile
);
163 /* Autoslope might turn an originally canal or river tile into land */
165 Slope slope
= GetTileSlope(tile
, &z
);
167 if (slope
!= SLOPE_FLAT
) {
168 if (wc
== WATER_CLASS_CANAL
) {
169 /* If we clear the canal, we have to remove it from the infrastructure count as well. */
170 Company
*c
= Company::GetIfValid(o
);
172 c
->infrastructure
.water
--;
173 DirtyCompanyInfrastructureWindows(c
->index
);
175 /* Sloped canals are locks and no natural water remains whatever the slope direction */
176 wc
= WATER_CLASS_INVALID
;
179 /* Only river water should be restored on appropriate slopes. Other water would be invalid on slopes */
180 if (wc
!= WATER_CLASS_RIVER
|| GetInclinedSlopeDirection(slope
) == INVALID_DIAGDIR
) {
181 wc
= WATER_CLASS_INVALID
;
185 if (wc
== WATER_CLASS_SEA
&& z
> 0) {
186 /* Update company infrastructure count. */
187 Company
*c
= Company::GetIfValid(o
);
189 c
->infrastructure
.water
++;
190 DirtyCompanyInfrastructureWindows(c
->index
);
193 wc
= WATER_CLASS_CANAL
;
196 /* Zero map array and terminate animation */
199 /* Maybe change to water */
201 case WATER_CLASS_SEA
: MakeSea(tile
); break;
202 case WATER_CLASS_CANAL
: MakeCanal(tile
, o
, Random()); break;
203 case WATER_CLASS_RIVER
: MakeRiver(tile
, Random()); break;
207 MarkTileDirtyByTile(tile
);
210 static CommandCost
RemoveShipDepot(TileIndex tile
, DoCommandFlag flags
)
212 if (!IsShipDepot(tile
)) return CMD_ERROR
;
214 CommandCost ret
= CheckTileOwnership(tile
);
215 if (ret
.Failed()) return ret
;
217 TileIndex tile2
= GetOtherShipDepotTile(tile
);
219 /* do not check for ship on tile when company goes bankrupt */
220 if (!(flags
& DC_BANKRUPT
)) {
221 CommandCost ret
= EnsureNoVehicleOnGround(tile
);
222 if (ret
.Succeeded()) ret
= EnsureNoVehicleOnGround(tile2
);
223 if (ret
.Failed()) return ret
;
226 if (flags
& DC_EXEC
) {
227 delete Depot::GetByTile(tile
);
229 Company
*c
= Company::GetIfValid(GetTileOwner(tile
));
231 c
->infrastructure
.water
-= 2 * LOCK_DEPOT_TILE_FACTOR
;
232 DirtyCompanyInfrastructureWindows(c
->index
);
235 MakeWaterKeepingClass(tile
, GetTileOwner(tile
));
236 MakeWaterKeepingClass(tile2
, GetTileOwner(tile2
));
239 return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_CLEAR_DEPOT_SHIP
]);
244 * @param tile tile where to place the lock
245 * @param flags type of operation
249 * @return the cost of this operation or an error
251 CommandCost
CmdBuildLock(TileIndex tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
253 DiagDirection dir
= GetInclinedSlopeDirection(GetTileSlope(tile
));
254 if (dir
== INVALID_DIAGDIR
) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION
);
256 CommandCost
cost(EXPENSES_CONSTRUCTION
);
258 int delta
= TileOffsByDiagDir(dir
);
259 CommandCost ret
= EnsureNoVehicleOnGround(tile
);
260 if (ret
.Succeeded()) ret
= EnsureNoVehicleOnGround(tile
+ delta
);
261 if (ret
.Succeeded()) ret
= EnsureNoVehicleOnGround(tile
- delta
);
262 if (ret
.Failed()) return ret
;
265 WaterClass wc_middle
= IsPlainWaterTile(tile
) ? GetWaterClass(tile
) : WATER_CLASS_CANAL
;
266 ret
= DoCommand(tile
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
267 if (ret
.Failed()) return ret
;
271 if (!IsPlainWaterTile(tile
- delta
)) {
272 ret
= DoCommand(tile
- delta
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
273 if (ret
.Failed()) return ret
;
275 cost
.AddCost(_price
[PR_BUILD_CANAL
]);
277 if (!IsTileFlat(tile
- delta
)) {
278 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION
);
280 WaterClass wc_lower
= IsPlainWaterTile(tile
- delta
) ? GetWaterClass(tile
- delta
) : WATER_CLASS_CANAL
;
283 if (!IsPlainWaterTile(tile
+ delta
)) {
284 ret
= DoCommand(tile
+ delta
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
285 if (ret
.Failed()) return ret
;
287 cost
.AddCost(_price
[PR_BUILD_CANAL
]);
289 if (!IsTileFlat(tile
+ delta
)) {
290 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION
);
292 WaterClass wc_upper
= IsPlainWaterTile(tile
+ delta
) ? GetWaterClass(tile
+ delta
) : WATER_CLASS_CANAL
;
294 if (HasBridgeAbove(tile
) || HasBridgeAbove(tile
- delta
) || HasBridgeAbove(tile
+ delta
)) {
295 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST
);
298 if (flags
& DC_EXEC
) {
299 /* Update company infrastructure counts. */
300 Company
*c
= Company::GetIfValid(_current_company
);
302 /* Counts for the water. */
303 if (!IsPlainWaterTile(tile
- delta
)) c
->infrastructure
.water
++;
304 if (!IsPlainWaterTile(tile
+ delta
)) c
->infrastructure
.water
++;
305 /* Count for the lock itself. */
306 c
->infrastructure
.water
+= 3 * LOCK_DEPOT_TILE_FACTOR
; // Lock is three tiles.
307 DirtyCompanyInfrastructureWindows(_current_company
);
310 MakeLock(tile
, _current_company
, dir
, wc_lower
, wc_upper
, wc_middle
);
311 MarkTileDirtyByTile(tile
);
312 MarkTileDirtyByTile(tile
- delta
);
313 MarkTileDirtyByTile(tile
+ delta
);
314 MarkCanalsAndRiversAroundDirty(tile
- delta
);
315 MarkCanalsAndRiversAroundDirty(tile
+ delta
);
317 cost
.AddCost(_price
[PR_BUILD_LOCK
]);
324 * @param tile Central tile of the lock.
325 * @param flags Operation to perform.
326 * @return The cost in case of success, or an error code if it failed.
328 static CommandCost
RemoveLock(TileIndex tile
, DoCommandFlag flags
)
330 if (GetTileOwner(tile
) != OWNER_NONE
) {
331 CommandCost ret
= CheckTileOwnership(tile
);
332 if (ret
.Failed()) return ret
;
335 TileIndexDiff delta
= TileOffsByDiagDir(GetLockDirection(tile
));
337 /* make sure no vehicle is on the tile. */
338 CommandCost ret
= EnsureNoVehicleOnGround(tile
);
339 if (ret
.Succeeded()) ret
= EnsureNoVehicleOnGround(tile
+ delta
);
340 if (ret
.Succeeded()) ret
= EnsureNoVehicleOnGround(tile
- delta
);
341 if (ret
.Failed()) return ret
;
343 if (flags
& DC_EXEC
) {
344 /* Remove middle part from company infrastructure count. */
345 Company
*c
= Company::GetIfValid(GetTileOwner(tile
));
347 c
->infrastructure
.water
-= 3 * LOCK_DEPOT_TILE_FACTOR
; // three parts of the lock.
348 DirtyCompanyInfrastructureWindows(c
->index
);
351 if (GetWaterClass(tile
) == WATER_CLASS_RIVER
) {
352 MakeRiver(tile
, Random());
356 MakeWaterKeepingClass(tile
+ delta
, GetTileOwner(tile
+ delta
));
357 MakeWaterKeepingClass(tile
- delta
, GetTileOwner(tile
- delta
));
358 MarkCanalsAndRiversAroundDirty(tile
);
359 MarkCanalsAndRiversAroundDirty(tile
- delta
);
360 MarkCanalsAndRiversAroundDirty(tile
+ delta
);
363 return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_CLEAR_LOCK
]);
366 /** Create non-desert around a river tile. */
367 void RiverModifyDesertZone (TileIndex tile
)
372 TILE_AREA_LOOP(t
, ta
) {
373 if (GetTropicZone(t
) == TROPICZONE_DESERT
) SetTropicZone (t
, TROPICZONE_NORMAL
);
378 * Build a piece of canal.
379 * @param tile end tile of stretch-dragging
380 * @param flags type of operation
381 * @param p1 start tile of stretch-dragging
382 * @param p2 waterclass to build. sea and river can only be built in scenario editor
384 * @return the cost of this operation or an error
386 CommandCost
CmdBuildCanal(TileIndex tile
, DoCommandFlag flags
, uint32 p1
, uint32 p2
, const char *text
)
388 WaterClass wc
= Extract
<WaterClass
, 0, 2>(p2
);
389 if (p1
>= MapSize() || wc
== WATER_CLASS_INVALID
) return CMD_ERROR
;
391 /* Outside of the editor you can only build canals, not oceans */
392 if (wc
!= WATER_CLASS_CANAL
&& _game_mode
!= GM_EDITOR
) return CMD_ERROR
;
394 TileArea
ta(tile
, p1
);
396 /* Outside the editor you can only drag canals, and not areas */
397 if (_game_mode
!= GM_EDITOR
&& ta
.w
!= 1 && ta
.h
!= 1) return CMD_ERROR
;
399 CommandCost
cost(EXPENSES_CONSTRUCTION
);
400 TILE_AREA_LOOP(tile
, ta
) {
403 Slope slope
= GetTileSlope(tile
);
404 if (slope
!= SLOPE_FLAT
&& (wc
!= WATER_CLASS_RIVER
|| !IsInclinedSlope(slope
))) {
405 return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED
);
408 /* can't make water of water! */
409 if (IsWaterTile(tile
) && (!IsTileOwner(tile
, OWNER_WATER
) || wc
== WATER_CLASS_SEA
)) continue;
411 bool water
= IsPlainWaterTile(tile
);
412 ret
= DoCommand(tile
, 0, 0, flags
| DC_FORCE_CLEAR_TILE
, CMD_LANDSCAPE_CLEAR
);
413 if (ret
.Failed()) return ret
;
415 if (!water
) cost
.AddCost(ret
);
417 if (flags
& DC_EXEC
) {
419 case WATER_CLASS_RIVER
:
420 MakeRiver(tile
, Random());
421 if (_game_mode
== GM_EDITOR
) {
422 RiverModifyDesertZone (tile
);
426 case WATER_CLASS_SEA
:
427 if (TileHeight(tile
) == 0) {
434 MakeCanal(tile
, _current_company
, Random());
435 if (Company::IsValidID(_current_company
)) {
436 Company::Get(_current_company
)->infrastructure
.water
++;
437 DirtyCompanyInfrastructureWindows(_current_company
);
441 MarkTileDirtyByTile(tile
);
442 MarkCanalsAndRiversAroundDirty(tile
);
445 cost
.AddCost(_price
[PR_BUILD_CANAL
]);
448 if (cost
.GetCost() == 0) {
449 return_cmd_error(STR_ERROR_ALREADY_BUILT
);
455 static CommandCost
ClearTile_Water(TileIndex tile
, DoCommandFlag flags
)
457 switch (GetWaterTileType(tile
)) {
458 case WATER_TILE_CLEAR
: {
459 if (flags
& DC_NO_WATER
) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER
);
461 Money base_cost
= IsCanal(tile
) ? _price
[PR_CLEAR_CANAL
] : _price
[PR_CLEAR_WATER
];
462 /* Make sure freeform edges are allowed or it's not an edge tile. */
463 if (!_settings_game
.construction
.freeform_edges
&& (!IsInsideMM(TileX(tile
), 1, MapMaxX() - 1) ||
464 !IsInsideMM(TileY(tile
), 1, MapMaxY() - 1))) {
465 return_cmd_error(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP
);
468 /* Make sure no vehicle is on the tile */
469 CommandCost ret
= EnsureNoVehicleOnGround(tile
);
470 if (ret
.Failed()) return ret
;
472 Owner owner
= GetTileOwner(tile
);
473 if (owner
!= OWNER_WATER
&& owner
!= OWNER_NONE
) {
474 CommandCost ret
= CheckTileOwnership(tile
);
475 if (ret
.Failed()) return ret
;
478 if (flags
& DC_EXEC
) {
479 if (IsCanal(tile
) && Company::IsValidID(owner
)) {
480 Company::Get(owner
)->infrastructure
.water
--;
481 DirtyCompanyInfrastructureWindows(owner
);
484 MarkCanalsAndRiversAroundDirty(tile
);
487 return CommandCost(EXPENSES_CONSTRUCTION
, base_cost
);
490 case WATER_TILE_COAST
: {
491 Slope slope
= GetTileSlope(tile
);
493 /* Make sure no vehicle is on the tile */
494 CommandCost ret
= EnsureNoVehicleOnGround(tile
);
495 if (ret
.Failed()) return ret
;
497 if (flags
& DC_EXEC
) {
499 MarkCanalsAndRiversAroundDirty(tile
);
501 if (IsSlopeWithOneCornerRaised(slope
)) {
502 return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_CLEAR_WATER
]);
504 return CommandCost(EXPENSES_CONSTRUCTION
, _price
[PR_CLEAR_ROUGH
]);
508 case WATER_TILE_DEPOT
:
509 if (flags
& DC_AUTO
) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED
);
510 return RemoveShipDepot(tile
, flags
);
512 case WATER_TILE_LOCK_MIDDLE
:
513 if (flags
& DC_AUTO
) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED
);
514 if (_current_company
== OWNER_WATER
) return CMD_ERROR
;
515 return RemoveLock(tile
, flags
);
517 case WATER_TILE_LOCK_LOWER
:
518 if (flags
& DC_AUTO
) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED
);
519 if (_current_company
== OWNER_WATER
) return CMD_ERROR
;
520 /* move to the middle tile.. */
521 tile
+= TileOffsByDiagDir(GetLockDirection(tile
));
522 return RemoveLock(tile
, flags
);
524 case WATER_TILE_LOCK_UPPER
:
525 if (flags
& DC_AUTO
) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED
);
526 if (_current_company
== OWNER_WATER
) return CMD_ERROR
;
527 /* move to the middle tile.. */
528 tile
-= TileOffsByDiagDir(GetLockDirection(tile
));
529 return RemoveLock(tile
, flags
);
537 * return true if a tile is a water tile wrt. a certain direction.
539 * @param tile The tile of interest.
540 * @param from The direction of interest.
541 * @return true iff the tile is water in the view of 'from'.
544 bool IsWateredTile(TileIndex tile
, Direction from
)
546 if (IsIndustryTile(tile
)) {
547 /* Do not draw waterborders inside of industries.
548 * Note: There is no easy way to detect the industry of an oilrig tile. */
549 TileIndex src_tile
= tile
+ TileOffsByDir(from
);
550 if ((IsStationTile(src_tile
) && IsOilRig(src_tile
)) ||
551 (IsIndustryTile(src_tile
) && GetIndustryIndex(src_tile
) == GetIndustryIndex(tile
))) return true;
553 return IsTileOnWater(tile
);
556 switch (GetTileType(tile
)) {
557 case TT_GROUND
: return IsTileSubtype(tile
, TT_GROUND_VOID
); // consider map border as water, esp. for rivers
560 switch (GetWaterTileType(tile
)) {
561 case WATER_TILE_CLEAR
:
562 case WATER_TILE_DEPOT
:
565 case WATER_TILE_COAST
:
566 switch (GetTileSlope(tile
)) {
567 case SLOPE_W
: return (from
== DIR_SE
) || (from
== DIR_E
) || (from
== DIR_NE
);
568 case SLOPE_S
: return (from
== DIR_NE
) || (from
== DIR_N
) || (from
== DIR_NW
);
569 case SLOPE_E
: return (from
== DIR_NW
) || (from
== DIR_W
) || (from
== DIR_SW
);
570 case SLOPE_N
: return (from
== DIR_SW
) || (from
== DIR_S
) || (from
== DIR_SE
);
571 default: return false;
575 return DiagDirToAxis(GetLockDirection(tile
)) == DiagDirToAxis(DirToDiagDir(from
));
579 if (IsTileSubtype(tile
, TT_TRACK
) && GetRailGroundType(tile
) == RAIL_GROUND_WATER
) {
580 switch (GetTileSlope(tile
)) {
581 case SLOPE_W
: return (from
== DIR_SE
) || (from
== DIR_E
) || (from
== DIR_NE
);
582 case SLOPE_S
: return (from
== DIR_NE
) || (from
== DIR_N
) || (from
== DIR_NW
);
583 case SLOPE_E
: return (from
== DIR_NW
) || (from
== DIR_W
) || (from
== DIR_SW
);
584 case SLOPE_N
: return (from
== DIR_SW
) || (from
== DIR_S
) || (from
== DIR_SE
);
585 default: return false;
591 return IsTileSubtype(tile
, TT_MISC_AQUEDUCT
) && ReverseDiagDir(GetTunnelBridgeDirection(tile
)) == DirToDiagDir(from
);
594 if (IsOilRig(tile
)) {
595 /* Do not draw waterborders inside of industries.
596 * Note: There is no easy way to detect the industry of an oilrig tile. */
597 TileIndex src_tile
= tile
+ TileOffsByDir(from
);
598 if ((IsStationTile(src_tile
) && IsOilRig(src_tile
)) ||
599 (IsIndustryTile(src_tile
))) return true;
601 return IsTileOnWater(tile
);
603 return (IsDock(tile
) && IsTileFlat(tile
)) || IsBuoy(tile
);
605 case TT_OBJECT
: return IsTileOnWater(tile
);
607 default: return false;
612 * Draw a water sprite, potentially with a NewGRF-modified sprite offset.
613 * @param ti TileInfo of the tile to draw
614 * @param base Sprite base.
615 * @param offset Sprite offset.
616 * @param feature The type of sprite that is drawn.
618 static void DrawWaterSprite (const TileInfo
*ti
, SpriteID base
, uint offset
, CanalFeature feature
)
620 if (base
!= SPR_FLAT_WATER_TILE
) {
621 /* Only call offset callback if the sprite is NewGRF-provided. */
622 offset
= GetCanalSpriteOffset (feature
, ti
->tile
, offset
);
624 DrawGroundSprite (ti
, base
+ offset
, PAL_NONE
);
628 * Draw canal or river edges.
629 * @param ti TileInfo of the tile to draw
630 * @param canal True if canal edges should be drawn, false for river edges.
631 * @param offset Sprite offset.
633 static void DrawWaterEdges (const TileInfo
*ti
, bool canal
, uint offset
)
635 TileIndex tile
= ti
->tile
;
637 CanalFeature feature
;
641 base
= GetCanalSprite(CF_DIKES
, tile
);
642 if (base
== 0) base
= SPR_CANAL_DIKES_BASE
;
644 feature
= CF_RIVER_EDGE
;
645 base
= GetCanalSprite(CF_RIVER_EDGE
, tile
);
646 if (base
== 0) return; // Don't draw if no sprites provided.
651 /* determine the edges around with water. */
652 wa
= IsWateredTile(TILE_ADDXY(tile
, -1, 0), DIR_SW
) << 0;
653 wa
+= IsWateredTile(TILE_ADDXY(tile
, 0, 1), DIR_NW
) << 1;
654 wa
+= IsWateredTile(TILE_ADDXY(tile
, 1, 0), DIR_NE
) << 2;
655 wa
+= IsWateredTile(TILE_ADDXY(tile
, 0, -1), DIR_SE
) << 3;
657 if (!(wa
& 1)) DrawWaterSprite (ti
, base
, offset
, feature
);
658 if (!(wa
& 2)) DrawWaterSprite (ti
, base
, offset
+ 1, feature
);
659 if (!(wa
& 4)) DrawWaterSprite (ti
, base
, offset
+ 2, feature
);
660 if (!(wa
& 8)) DrawWaterSprite (ti
, base
, offset
+ 3, feature
);
664 case 0: DrawWaterSprite (ti
, base
, offset
+ 4, feature
); break;
665 case 3: if (!IsWateredTile(TILE_ADDXY(tile
, -1, 1), DIR_W
)) DrawWaterSprite (ti
, base
, offset
+ 8, feature
); break;
670 case 0: DrawWaterSprite (ti
, base
, offset
+ 5, feature
); break;
671 case 6: if (!IsWateredTile(TILE_ADDXY(tile
, 1, 1), DIR_N
)) DrawWaterSprite (ti
, base
, offset
+ 9, feature
); break;
676 case 0: DrawWaterSprite (ti
, base
, offset
+ 6, feature
); break;
677 case 12: if (!IsWateredTile(TILE_ADDXY(tile
, 1, -1), DIR_E
)) DrawWaterSprite (ti
, base
, offset
+ 10, feature
); break;
682 case 0: DrawWaterSprite (ti
, base
, offset
+ 7, feature
); break;
683 case 9: if (!IsWateredTile(TILE_ADDXY(tile
, -1, -1), DIR_S
)) DrawWaterSprite (ti
, base
, offset
+ 11, feature
); break;
687 #include "table/water_land.h"
690 * Draw a build sprite sequence for water tiles.
691 * If buildings are invisible, nothing will be drawn.
692 * @param ti Tile info.
693 * @param dtss Sprite sequence to draw.
694 * @param base Base sprite.
695 * @param offset Additional sprite offset.
696 * @param palette Palette to use.
698 static void DrawWaterTileStruct(const TileInfo
*ti
, const DrawTileSeqStruct
*dtss
, SpriteID base
, uint offset
, PaletteID palette
, CanalFeature feature
)
700 /* Don't draw if buildings are invisible. */
701 if (IsInvisibilitySet(TO_BUILDINGS
)) return;
703 for (; !dtss
->IsTerminator(); dtss
++) {
704 uint tile_offs
= offset
+ dtss
->image
.sprite
;
705 if (feature
< CF_END
) tile_offs
= GetCanalSpriteOffset(feature
, ti
->tile
, tile_offs
);
706 AddSortableSpriteToDraw (ti
->vd
, base
+ tile_offs
, palette
,
707 ti
->x
+ dtss
->delta_x
, ti
->y
+ dtss
->delta_y
,
708 dtss
->size_x
, dtss
->size_y
,
709 dtss
->size_z
, ti
->z
+ dtss
->delta_z
,
710 IsTransparencySet(TO_BUILDINGS
));
714 /** Draw a lock tile. */
715 static void DrawWaterLock(const TileInfo
*ti
)
717 uint part
= GetWaterTileType (ti
->tile
) - WATER_TILE_LOCK_MIDDLE
;
718 DiagDirection dir
= GetLockDirection (ti
->tile
);
720 /* Draw ground sprite. */
721 bool has_flat_water
= HasBit(_water_feature
[CF_WATERSLOPE
].flags
, CFF_HAS_FLAT_SPRITE
);
722 bool use_default
= true;
724 if (has_flat_water
|| (part
== 0)) {
725 image
= GetCanalSprite (CF_WATERSLOPE
, ti
->tile
);
728 /* NewGRF supplies a flat sprite as first sprite? */
729 if (part
== 0) image
+= has_flat_water
;
734 /* Use default sprites. */
735 image
= (part
!= 0) ? SPR_FLAT_WATER_TILE
: SPR_CANALS_BASE
;
738 static uint8 lock_middle_offset
[DIAGDIR_END
] = { 1, 0, 2, 3 };
739 if (part
== 0) image
+= lock_middle_offset
[dir
];
740 DrawGroundSprite (ti
, image
, PAL_NONE
);
742 const DrawTileSeqStruct
*dts
= _lock_display_data
[part
][dir
];
744 /* Draw structures. */
746 SpriteID base
= GetCanalSprite(CF_LOCKS
, ti
->tile
);
749 /* If no custom graphics, use defaults. */
750 base
= SPR_LOCK_BASE
;
751 bool upper
= part
== (WATER_TILE_LOCK_UPPER
- WATER_TILE_LOCK_MIDDLE
);
752 uint8 z_threshold
= upper
? 8 : 0;
753 zoffs
= ti
->z
> z_threshold
? 24 : 0;
756 DrawWaterTileStruct (ti
, dts
, base
, zoffs
, PAL_NONE
, CF_LOCKS
);
759 /** Draw a ship depot tile. */
760 static void DrawWaterDepot(const TileInfo
*ti
)
762 DrawWaterClassGround(ti
);
763 DrawWaterTileStruct (ti
, _shipdepot_display_data
[GetShipDepotDirection(ti
->tile
)],
764 0, 0, COMPANY_SPRITE_COLOUR(GetTileOwner(ti
->tile
)), CF_END
);
767 static uint
DrawRiverWater (const TileInfo
*ti
)
769 SpriteID image
= SPR_FLAT_WATER_TILE
;
771 uint edges_offset
= 0;
773 if (ti
->tileh
!= SLOPE_FLAT
|| HasBit(_water_feature
[CF_RIVER_SLOPE
].flags
, CFF_HAS_FLAT_SPRITE
)) {
774 image
= GetCanalSprite(CF_RIVER_SLOPE
, ti
->tile
);
777 case SLOPE_NW
: image
= SPR_WATER_SLOPE_Y_DOWN
; break;
778 case SLOPE_SW
: image
= SPR_WATER_SLOPE_X_UP
; break;
779 case SLOPE_SE
: image
= SPR_WATER_SLOPE_Y_UP
; break;
780 case SLOPE_NE
: image
= SPR_WATER_SLOPE_X_DOWN
; break;
781 default: image
= SPR_FLAT_WATER_TILE
; break;
784 /* Flag bit 0 indicates that the first sprite is flat water. */
785 offset
= HasBit(_water_feature
[CF_RIVER_SLOPE
].flags
, CFF_HAS_FLAT_SPRITE
) ? 1 : 0;
788 case SLOPE_SE
: edges_offset
+= 12; break;
789 case SLOPE_NE
: offset
+= 1; edges_offset
+= 24; break;
790 case SLOPE_SW
: offset
+= 2; edges_offset
+= 36; break;
791 case SLOPE_NW
: offset
+= 3; edges_offset
+= 48; break;
792 default: offset
= 0; break;
795 offset
= GetCanalSpriteOffset(CF_RIVER_SLOPE
, ti
->tile
, offset
);
799 DrawGroundSprite (ti
, image
+ offset
, PAL_NONE
);
804 void DrawShoreTile (const TileInfo
*ti
)
806 /* Converts the enum Slope into an offset based on SPR_SHORE_BASE.
807 * This allows to calculate the proper sprite to display for this Slope */
808 static const byte tileh_to_shoresprite
[32] = {
809 0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0,
810 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 15, 0,
813 Slope tileh
= ti
->tileh
;
815 assert(!IsHalftileSlope(tileh
)); // Halftile slopes need to get handled earlier.
816 assert(tileh
!= SLOPE_FLAT
); // Shore is never flat
818 assert((tileh
!= SLOPE_EW
) && (tileh
!= SLOPE_NS
)); // No suitable sprites for current flooding behaviour
820 DrawGroundSprite (ti
, SPR_SHORE_BASE
+ tileh_to_shoresprite
[tileh
], PAL_NONE
);
823 void DrawWaterClassGround(const TileInfo
*ti
)
828 switch (GetWaterClass(ti
->tile
)) {
829 case WATER_CLASS_SEA
:
830 DrawGroundSprite (ti
, SPR_FLAT_WATER_TILE
, PAL_NONE
);
831 /* No edges drawn for sea tiles. */
834 case WATER_CLASS_CANAL
: {
835 SpriteID image
= SPR_FLAT_WATER_TILE
;
836 if (HasBit(_water_feature
[CF_WATERSLOPE
].flags
, CFF_HAS_FLAT_SPRITE
)) {
837 /* First water slope sprite is flat water. */
838 image
= GetCanalSprite (CF_WATERSLOPE
, ti
->tile
);
839 if (image
== 0) image
= SPR_FLAT_WATER_TILE
;
841 DrawWaterSprite (ti
, image
, 0, CF_WATERSLOPE
);
847 case WATER_CLASS_RIVER
:
848 edges_offset
= DrawRiverWater (ti
);
852 default: NOT_REACHED();
855 /* Draw river edges if available. */
856 DrawWaterEdges (ti
, canal
, edges_offset
);
859 static void DrawTile_Water(TileInfo
*ti
)
861 switch (GetWaterTileType(ti
->tile
)) {
862 case WATER_TILE_CLEAR
:
863 DrawWaterClassGround(ti
);
864 DrawBridgeMiddle(ti
);
867 case WATER_TILE_COAST
: {
869 DrawBridgeMiddle(ti
);
873 case WATER_TILE_DEPOT
:
883 void DrawShipDepotSprite (BlitArea
*dpi
, int x
, int y
, DiagDirection dir
)
885 DrawSprite (dpi
, SPR_FLAT_WATER_TILE
, PAL_NONE
, x
, y
);
886 DrawOrigTileSeqInGUI (dpi
, x
, y
, _shipdepot_display_data
[dir
],
887 COMPANY_SPRITE_COLOUR(_local_company
));
891 static int GetSlopePixelZ_Water(TileIndex tile
, uint x
, uint y
)
894 Slope tileh
= GetTilePixelSlope(tile
, &z
);
896 return z
+ GetPartialPixelZ(x
& 0xF, y
& 0xF, tileh
);
899 static Foundation
GetFoundation_Water(TileIndex tile
, Slope tileh
)
901 return FOUNDATION_NONE
;
904 static void GetTileDesc_Water(TileIndex tile
, TileDesc
*td
)
906 switch (GetWaterTileType(tile
)) {
907 case WATER_TILE_CLEAR
:
908 switch (GetWaterClass(tile
)) {
909 case WATER_CLASS_SEA
: td
->str
= STR_LAI_WATER_DESCRIPTION_WATER
; break;
910 case WATER_CLASS_CANAL
: td
->str
= STR_LAI_WATER_DESCRIPTION_CANAL
; break;
911 case WATER_CLASS_RIVER
: td
->str
= STR_LAI_WATER_DESCRIPTION_RIVER
; break;
912 default: NOT_REACHED(); break;
915 case WATER_TILE_COAST
: td
->str
= STR_LAI_WATER_DESCRIPTION_COAST_OR_RIVERBANK
; break;
916 case WATER_TILE_DEPOT
:
917 td
->str
= STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT
;
918 td
->build_date
= Depot::GetByTile(tile
)->build_date
;
920 default: td
->str
= STR_LAI_WATER_DESCRIPTION_LOCK
; break;
923 td
->owner
[0] = GetTileOwner(tile
);
927 * Handle the flooding of a vehicle. This sets the vehicle state to crashed,
928 * creates a newsitem and dirties the necessary windows.
929 * @param v The vehicle to flood.
931 static void FloodVehicle(Vehicle
*v
)
933 uint pass
= v
->Crash(true);
935 AI::NewEvent(v
->owner
, new ScriptEventVehicleCrashed(v
->index
, v
->tile
, ScriptEventVehicleCrashed::CRASH_FLOODED
));
936 Game::NewEvent(new ScriptEventVehicleCrashed(v
->index
, v
->tile
, ScriptEventVehicleCrashed::CRASH_FLOODED
));
937 AddNewsItem
<VehicleNewsItem
> (STR_NEWS_DISASTER_FLOOD_VEHICLE
,
938 NT_ACCIDENT
, v
->index
, pass
);
939 CreateEffectVehicleRel(v
, 4, 4, 8, EV_EXPLOSION_LARGE
);
940 if (_settings_client
.sound
.disaster
) SndPlayVehicleFx(SND_12_EXPLOSION
, v
);
944 * Flood vehicles on a tile if we are allowed to flood them, i.e. when they are on the ground.
945 * @param tile The tile to flood.
946 * @param z The z of level to flood.
948 static void FloodTileVehicles(TileIndex tile
, int z
)
950 VehicleTileIterator
iter (tile
);
951 while (!iter
.finished()) {
952 Vehicle
*v
= iter
.next();
954 if ((v
->vehstatus
& VS_CRASHED
) != 0) continue;
961 if (v
->z_pos
<= z
) FloodVehicle(v
->First());
967 * Finds a vehicle to flood.
968 * It does not find vehicles that are already crashed on bridges, i.e. flooded.
969 * @param tile the tile where to find a vehicle to flood
971 static void FloodVehicles(TileIndex tile
)
973 if (IsAirportTile(tile
)) {
974 if (GetTileMaxZ(tile
) != 0) return;
976 const Station
*st
= Station::GetByTile(tile
);
977 TILE_AREA_LOOP(tile
, st
->airport
) {
978 if (st
->TileBelongsToAirport(tile
)) {
979 VehicleTileIterator
iter (tile
);
980 while (!iter
.finished()) {
981 Vehicle
*v
= iter
.next();
983 if (v
->type
!= VEH_AIRCRAFT
) continue;
984 if (v
->subtype
== AIR_SHADOW
) continue;
985 if ((v
->vehstatus
& VS_CRASHED
) != 0) continue;
987 /* We compare v->z_pos against delta_z + 1 because the shadow
988 * is at delta_z and the actual aircraft at delta_z + 1. */
989 const AirportFTAClass
*airport
= st
->airport
.GetFTA();
990 if (v
->z_pos
== airport
->delta_z
+ 1) FloodVehicle(v
);
995 /* No vehicle could be flooded on this airport anymore */
999 if (!IsBridgeHeadTile(tile
)) {
1000 FloodTileVehicles (tile
, 0);
1004 int z
= GetBridgePixelHeight(tile
);
1005 FloodTileVehicles (tile
, z
);
1006 FloodTileVehicles (GetOtherBridgeEnd(tile
), z
);
1010 * Returns the behaviour of a tile during flooding.
1012 * @return Behaviour of the tile
1014 FloodingBehaviour
GetFloodingBehaviour(TileIndex tile
)
1016 /* FLOOD_ACTIVE: 'single-corner-raised'-coast, sea, sea-shipdepots, sea-buoys, sea-docks (water part), rail with flooded halftile, sea-water-industries, sea-oilrigs
1017 * FLOOD_DRYUP: coast with more than one corner raised, coast with rail-track, coast with trees
1018 * FLOOD_PASSIVE: (not used)
1019 * FLOOD_NONE: canals, rivers, everything else
1021 if (IsIndustryTile(tile
)) {
1022 return (GetWaterClass(tile
) == WATER_CLASS_SEA
) ? FLOOD_ACTIVE
: FLOOD_NONE
;
1025 switch (GetTileType(tile
)) {
1027 if (IsCoast(tile
)) {
1028 Slope tileh
= GetTileSlope(tile
);
1029 return (IsSlopeWithOneCornerRaised(tileh
) ? FLOOD_ACTIVE
: FLOOD_DRYUP
);
1034 return (GetWaterClass(tile
) == WATER_CLASS_SEA
) ? FLOOD_ACTIVE
: FLOOD_NONE
;
1037 if (IsTileSubtype(tile
, TT_TRACK
) && GetRailGroundType(tile
) == RAIL_GROUND_WATER
) {
1038 return (IsSlopeWithOneCornerRaised(GetTileSlope(tile
)) ? FLOOD_ACTIVE
: FLOOD_DRYUP
);
1043 return (IsTreeTile(tile
) && GetClearGround(tile
) == GROUND_SHORE
? FLOOD_DRYUP
: FLOOD_NONE
);
1053 void DoFloodTile(TileIndex target
)
1055 assert(!IsWaterTile(target
));
1057 bool flooded
= false; // Will be set to true if something is changed.
1059 Backup
<CompanyByte
> cur_company(_current_company
, OWNER_WATER
, FILE_LINE
);
1061 Slope tileh
= GetTileSlope(target
);
1062 if (tileh
!= SLOPE_FLAT
) {
1064 switch (GetTileType(target
)) {
1066 if (IsTileSubtype(target
, TT_TRACK
)) {
1067 FloodVehicles(target
);
1068 flooded
= FloodHalftile(target
);
1073 if (IsTreeTile(target
) && !IsSlopeWithOneCornerRaised(tileh
)) {
1074 SetClearGroundDensity(target
, GROUND_SHORE
, 3, true);
1075 MarkTileDirtyByTile(target
);
1079 if (DoCommand(target
, 0, 0, DC_EXEC
, CMD_LANDSCAPE_CLEAR
).Succeeded()) {
1081 MarkTileDirtyByTile(target
);
1090 /* Flood vehicles */
1091 FloodVehicles(target
);
1093 /* flood flat tile */
1094 if (DoCommand(target
, 0, 0, DC_EXEC
, CMD_LANDSCAPE_CLEAR
).Succeeded()) {
1096 MarkTileDirtyByTile(target
);
1102 /* Mark surrounding canal tiles dirty too to avoid glitches */
1103 MarkCanalsAndRiversAroundDirty(target
);
1105 /* update signals if needed */
1106 UpdateSignalsInBuffer();
1109 cur_company
.Restore();
1115 static void DoDryUp(TileIndex tile
)
1117 Backup
<CompanyByte
> cur_company(_current_company
, OWNER_WATER
, FILE_LINE
);
1119 switch (GetTileType(tile
)) {
1121 assert(IsTileSubtype(tile
, TT_TRACK
));
1122 assert(GetRailGroundType(tile
) == RAIL_GROUND_WATER
);
1124 RailGroundType new_ground
;
1125 switch (GetTrackBits(tile
)) {
1126 case TRACK_BIT_UPPER
: new_ground
= RAIL_GROUND_FENCE_HORIZ1
; break;
1127 case TRACK_BIT_LOWER
: new_ground
= RAIL_GROUND_FENCE_HORIZ2
; break;
1128 case TRACK_BIT_LEFT
: new_ground
= RAIL_GROUND_FENCE_VERT1
; break;
1129 case TRACK_BIT_RIGHT
: new_ground
= RAIL_GROUND_FENCE_VERT2
; break;
1130 default: NOT_REACHED();
1132 SetRailGroundType(tile
, new_ground
);
1133 MarkTileDirtyByTile(tile
);
1137 assert(IsTreeTile(tile
));
1138 SetClearGroundDensity(tile
, GROUND_GRASS
, 3, true);
1139 MarkTileDirtyByTile(tile
);
1143 assert(IsCoast(tile
));
1145 if (DoCommand(tile
, 0, 0, DC_EXEC
, CMD_LANDSCAPE_CLEAR
).Succeeded()) {
1146 MakeClear(tile
, GROUND_GRASS
, 3);
1147 MarkTileDirtyByTile(tile
);
1151 default: NOT_REACHED();
1154 cur_company
.Restore();
1158 * Let a water tile floods its diagonal adjoining tiles
1159 * called from tunnelbridge_cmd, and by TileLoop_Industry() and TileLoop_Track()
1161 * @param tile the water/shore tile that floods
1163 void TileLoop_Water(TileIndex tile
)
1165 if (IsWaterTile(tile
)) AmbientSoundEffect(tile
);
1167 switch (GetFloodingBehaviour(tile
)) {
1169 for (Direction dir
= DIR_BEGIN
; dir
< DIR_END
; dir
++) {
1170 TileIndex dest
= tile
+ TileOffsByDir(dir
);
1171 if (!IsValidTile(dest
)) continue;
1172 /* do not try to flood water tiles - increases performance a lot */
1173 if (IsWaterTile(dest
)) continue;
1175 /* GROUND_SHORE is the sign of a previous flood. */
1176 if ((IsClearTile(dest
) || IsTreeTile(dest
)) && IsClearGround (dest
, GROUND_SHORE
)) continue;
1179 Slope slope_dest
= GetFoundationSlope(dest
, &z_dest
) & ~SLOPE_HALFTILE_MASK
& ~SLOPE_STEEP
;
1180 if (z_dest
> 0) continue;
1182 if (!HasBit(_flood_from_dirs
[slope_dest
], ReverseDir(dir
))) continue;
1189 Slope slope_here
= GetFoundationSlope(tile
) & ~SLOPE_HALFTILE_MASK
& ~SLOPE_STEEP
;
1191 FOR_EACH_SET_BIT(dir
, _flood_from_dirs
[slope_here
]) {
1192 TileIndex dest
= tile
+ TileOffsByDir((Direction
)dir
);
1193 if (!IsValidTile(dest
)) continue;
1195 FloodingBehaviour dest_behaviour
= GetFloodingBehaviour(dest
);
1196 if ((dest_behaviour
== FLOOD_ACTIVE
) || (dest_behaviour
== FLOOD_PASSIVE
)) return;
1206 void ConvertGroundTilesIntoWaterTiles()
1210 for (TileIndex tile
= 0; tile
< MapSize(); ++tile
) {
1211 Slope slope
= GetTileSlope(tile
, &z
);
1212 if (IsGroundTile(tile
) && z
== 0) {
1213 /* Make both water for tiles at level 0
1214 * and make shore, as that looks much better
1215 * during the generation. */
1230 FOR_EACH_SET_BIT(dir
, _flood_from_dirs
[slope
& ~SLOPE_STEEP
]) {
1231 TileIndex dest
= TILE_ADD(tile
, TileOffsByDir((Direction
)dir
));
1232 Slope slope_dest
= GetTileSlope(dest
) & ~SLOPE_STEEP
;
1233 if (slope_dest
== SLOPE_FLAT
|| IsSlopeWithOneCornerRaised(slope_dest
)) {
1244 static TrackdirBits
GetTileWaterwayStatus_Water(TileIndex tile
, DiagDirection side
)
1246 static const byte coast_tracks
[] = {0, 32, 4, 0, 16, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0};
1250 switch (GetWaterTileType(tile
)) {
1251 case WATER_TILE_CLEAR
: ts
= IsTileFlat(tile
) ? TRACK_BIT_ALL
: TRACK_BIT_NONE
; break;
1252 case WATER_TILE_COAST
: ts
= (TrackBits
)coast_tracks
[GetTileSlope(tile
) & 0xF]; break;
1253 case WATER_TILE_DEPOT
: ts
= DiagDirToDiagTrackBits(GetShipDepotDirection(tile
)); break;
1254 default: ts
= DiagDirToDiagTrackBits(GetLockDirection(tile
)); break;
1256 if (TileX(tile
) == 0) {
1257 /* NE border: remove tracks that connects NE tile edge */
1258 ts
&= ~(TRACK_BIT_X
| TRACK_BIT_UPPER
| TRACK_BIT_RIGHT
);
1260 if (TileY(tile
) == 0) {
1261 /* NW border: remove tracks that connects NW tile edge */
1262 ts
&= ~(TRACK_BIT_Y
| TRACK_BIT_LEFT
| TRACK_BIT_UPPER
);
1264 return TrackBitsToTrackdirBits(ts
);
1267 static bool ClickTile_Water(TileIndex tile
)
1269 if (IsShipDepot(tile
)) {
1270 ShowDepotWindow(GetShipDepotNorthTile(tile
), VEH_SHIP
);
1276 static void ChangeTileOwner_Water(TileIndex tile
, Owner old_owner
, Owner new_owner
)
1278 if (!IsTileOwner(tile
, old_owner
)) return;
1280 bool is_lock_middle
= GetWaterTileType(tile
) == WATER_TILE_LOCK_MIDDLE
;
1282 /* No need to dirty company windows here, we'll redraw the whole screen anyway. */
1283 if (is_lock_middle
) Company::Get(old_owner
)->infrastructure
.water
-= 3 * LOCK_DEPOT_TILE_FACTOR
; // Lock has three parts.
1284 if (new_owner
!= INVALID_OWNER
) {
1285 if (is_lock_middle
) Company::Get(new_owner
)->infrastructure
.water
+= 3 * LOCK_DEPOT_TILE_FACTOR
; // Lock has three parts.
1286 /* Only subtract from the old owner here if the new owner is valid,
1287 * otherwise we clear ship depots and canal water below. */
1288 if (GetWaterClass(tile
) == WATER_CLASS_CANAL
&& !is_lock_middle
) {
1289 Company::Get(old_owner
)->infrastructure
.water
--;
1290 Company::Get(new_owner
)->infrastructure
.water
++;
1292 if (IsShipDepot(tile
)) {
1293 Company::Get(old_owner
)->infrastructure
.water
-= LOCK_DEPOT_TILE_FACTOR
;
1294 Company::Get(new_owner
)->infrastructure
.water
+= LOCK_DEPOT_TILE_FACTOR
;
1297 SetTileOwner(tile
, new_owner
);
1302 if (IsShipDepot(tile
)) DoCommand(tile
, 0, 0, DC_EXEC
| DC_BANKRUPT
, CMD_LANDSCAPE_CLEAR
);
1304 /* Set owner of canals and locks ... and also canal under dock there was before.
1305 * Check if the new owner after removing depot isn't OWNER_WATER. */
1306 if (IsTileOwner(tile
, old_owner
)) {
1307 if (GetWaterClass(tile
) == WATER_CLASS_CANAL
&& !is_lock_middle
) Company::Get(old_owner
)->infrastructure
.water
--;
1308 SetTileOwner(tile
, OWNER_NONE
);
1312 static CommandCost
TerraformTile_Water(TileIndex tile
, DoCommandFlag flags
, int z_new
, Slope tileh_new
)
1314 /* Canals can't be terraformed */
1315 if (IsPlainWaterTile(tile
) && IsCanal(tile
)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_CANAL_FIRST
);
1317 return DoCommand(tile
, 0, 0, flags
, CMD_LANDSCAPE_CLEAR
);
1321 extern const TileTypeProcs _tile_type_water_procs
= {
1322 DrawTile_Water
, // draw_tile_proc
1323 GetSlopePixelZ_Water
, // get_slope_z_proc
1324 ClearTile_Water
, // clear_tile_proc
1325 NULL
, // add_accepted_cargo_proc
1326 GetTileDesc_Water
, // get_tile_desc_proc
1327 NULL
, // get_tile_railway_status_proc
1328 NULL
, // get_tile_road_status_proc
1329 GetTileWaterwayStatus_Water
, // get_tile_waterway_status_proc
1330 ClickTile_Water
, // click_tile_proc
1331 NULL
, // animate_tile_proc
1332 TileLoop_Water
, // tile_loop_proc
1333 ChangeTileOwner_Water
, // change_tile_owner_proc
1334 NULL
, // add_produced_cargo_proc
1335 GetFoundation_Water
, // get_foundation_proc
1336 TerraformTile_Water
, // terraform_tile_proc