Fix: Data races on cursor state in OpenGL backends
[openttd-github.git] / src / water_cmd.cpp
blob731954c2ebe4cd7d9a4cdbb2ee666ccab0b0f5e5
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"
42 #include "table/strings.h"
44 #include "safeguards.h"
46 /**
47 * Describes from which directions a specific slope can be flooded (if the tile is floodable at all).
49 static const uint8 _flood_from_dirs[] = {
50 (1 << DIR_NW) | (1 << DIR_SW) | (1 << DIR_SE) | (1 << DIR_NE), // SLOPE_FLAT
51 (1 << DIR_NE) | (1 << DIR_SE), // SLOPE_W
52 (1 << DIR_NW) | (1 << DIR_NE), // SLOPE_S
53 (1 << DIR_NE), // SLOPE_SW
54 (1 << DIR_NW) | (1 << DIR_SW), // SLOPE_E
55 0, // SLOPE_EW
56 (1 << DIR_NW), // SLOPE_SE
57 (1 << DIR_N ) | (1 << DIR_NW) | (1 << DIR_NE), // SLOPE_WSE, SLOPE_STEEP_S
58 (1 << DIR_SW) | (1 << DIR_SE), // SLOPE_N
59 (1 << DIR_SE), // SLOPE_NW
60 0, // SLOPE_NS
61 (1 << DIR_E ) | (1 << DIR_NE) | (1 << DIR_SE), // SLOPE_NWS, SLOPE_STEEP_W
62 (1 << DIR_SW), // SLOPE_NE
63 (1 << DIR_S ) | (1 << DIR_SW) | (1 << DIR_SE), // SLOPE_ENW, SLOPE_STEEP_N
64 (1 << DIR_W ) | (1 << DIR_SW) | (1 << DIR_NW), // SLOPE_SEN, SLOPE_STEEP_E
67 /**
68 * Marks tile dirty if it is a canal or river tile.
69 * Called to avoid glitches when flooding tiles next to canal tile.
71 * @param tile tile to check
73 static inline void MarkTileDirtyIfCanalOrRiver(TileIndex tile)
75 if (IsValidTile(tile) && IsTileType(tile, MP_WATER) && (IsCanal(tile) || IsRiver(tile))) MarkTileDirtyByTile(tile);
78 /**
79 * Marks the tiles around a tile as dirty, if they are canals or rivers.
81 * @param tile The center of the tile where all other tiles are marked as dirty
82 * @ingroup dirty
84 static void MarkCanalsAndRiversAroundDirty(TileIndex tile)
86 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
87 MarkTileDirtyIfCanalOrRiver(tile + TileOffsByDir(dir));
92 /**
93 * Build a ship depot.
94 * @param tile tile where ship depot is built
95 * @param flags type of operation
96 * @param p1 bit 0 depot orientation (Axis)
97 * @param p2 unused
98 * @param text unused
99 * @return the cost of this operation or an error
101 CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
103 Axis axis = Extract<Axis, 0, 1>(p1);
105 TileIndex tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
107 if (!HasTileWaterGround(tile) || !HasTileWaterGround(tile2)) {
108 return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER);
111 if (IsBridgeAbove(tile) || IsBridgeAbove(tile2)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
113 if (!IsTileFlat(tile) || !IsTileFlat(tile2)) {
114 /* Prevent depots on rapids */
115 return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
118 if (!Depot::CanAllocateItem()) return CMD_ERROR;
120 WaterClass wc1 = GetWaterClass(tile);
121 WaterClass wc2 = GetWaterClass(tile2);
122 CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_DEPOT_SHIP]);
124 bool add_cost = !IsWaterTile(tile);
125 CommandCost ret = DoCommand(tile, 0, 0, flags | DC_AUTO, CMD_LANDSCAPE_CLEAR);
126 if (ret.Failed()) return ret;
127 if (add_cost) {
128 cost.AddCost(ret);
130 add_cost = !IsWaterTile(tile2);
131 ret = DoCommand(tile2, 0, 0, flags | DC_AUTO, CMD_LANDSCAPE_CLEAR);
132 if (ret.Failed()) return ret;
133 if (add_cost) {
134 cost.AddCost(ret);
137 if (flags & DC_EXEC) {
138 Depot *depot = new Depot(tile);
139 depot->build_date = _date;
141 if (wc1 == WATER_CLASS_CANAL || wc2 == WATER_CLASS_CANAL) {
142 /* Update infrastructure counts after the unconditional clear earlier. */
143 Company::Get(_current_company)->infrastructure.water += wc1 == WATER_CLASS_CANAL && wc2 == WATER_CLASS_CANAL ? 2 : 1;
145 Company::Get(_current_company)->infrastructure.water += 2 * LOCK_DEPOT_TILE_FACTOR;
146 DirtyCompanyInfrastructureWindows(_current_company);
148 MakeShipDepot(tile, _current_company, depot->index, DEPOT_PART_NORTH, axis, wc1);
149 MakeShipDepot(tile2, _current_company, depot->index, DEPOT_PART_SOUTH, axis, wc2);
150 CheckForDockingTile(tile);
151 CheckForDockingTile(tile2);
152 MarkTileDirtyByTile(tile);
153 MarkTileDirtyByTile(tile2);
154 MakeDefaultName(depot);
157 return cost;
160 bool IsPossibleDockingTile(TileIndex t)
162 assert(IsValidTile(t));
163 switch (GetTileType(t)) {
164 case MP_WATER:
165 if (IsLock(t) && GetLockPart(t) == LOCK_PART_MIDDLE) return false;
166 FALLTHROUGH;
167 case MP_RAILWAY:
168 case MP_STATION:
169 case MP_TUNNELBRIDGE:
170 return TrackStatusToTrackBits(GetTileTrackStatus(t, TRANSPORT_WATER, 0)) != TRACK_BIT_NONE;
172 default:
173 return false;
178 * Mark the supplied tile as a docking tile if it is suitable for docking.
179 * Tiles surrounding the tile are tested to be docks with correct orientation.
180 * @param t Tile to test.
182 void CheckForDockingTile(TileIndex t)
184 for (DiagDirection d = DIAGDIR_BEGIN; d != DIAGDIR_END; d++) {
185 TileIndex tile = t + TileOffsByDiagDir(d);
186 if (!IsValidTile(tile)) continue;
188 if (IsDockTile(tile) && IsValidDockingDirectionForDock(tile, d)) {
189 Station::GetByTile(tile)->docking_station.Add(t);
190 SetDockingTile(t, true);
192 if (IsTileType(tile, MP_INDUSTRY)) {
193 Station *st = Industry::GetByTile(tile)->neutral_station;
194 if (st != nullptr) {
195 st->docking_station.Add(t);
196 SetDockingTile(t, true);
199 if (IsTileType(tile, MP_STATION) && IsOilRig(tile)) {
200 Station::GetByTile(tile)->docking_station.Add(t);
201 SetDockingTile(t, true);
206 void MakeWaterKeepingClass(TileIndex tile, Owner o)
208 WaterClass wc = GetWaterClass(tile);
210 /* Autoslope might turn an originally canal or river tile into land */
211 int z;
212 Slope slope = GetTileSlope(tile, &z);
214 if (slope != SLOPE_FLAT) {
215 if (wc == WATER_CLASS_CANAL) {
216 /* If we clear the canal, we have to remove it from the infrastructure count as well. */
217 Company *c = Company::GetIfValid(o);
218 if (c != nullptr) {
219 c->infrastructure.water--;
220 DirtyCompanyInfrastructureWindows(c->index);
222 /* Sloped canals are locks and no natural water remains whatever the slope direction */
223 wc = WATER_CLASS_INVALID;
226 /* Only river water should be restored on appropriate slopes. Other water would be invalid on slopes */
227 if (wc != WATER_CLASS_RIVER || GetInclinedSlopeDirection(slope) == INVALID_DIAGDIR) {
228 wc = WATER_CLASS_INVALID;
232 if (wc == WATER_CLASS_SEA && z > 0) {
233 /* Update company infrastructure count. */
234 Company *c = Company::GetIfValid(o);
235 if (c != nullptr) {
236 c->infrastructure.water++;
237 DirtyCompanyInfrastructureWindows(c->index);
240 wc = WATER_CLASS_CANAL;
243 /* Zero map array and terminate animation */
244 DoClearSquare(tile);
246 /* Maybe change to water */
247 switch (wc) {
248 case WATER_CLASS_SEA: MakeSea(tile); break;
249 case WATER_CLASS_CANAL: MakeCanal(tile, o, Random()); break;
250 case WATER_CLASS_RIVER: MakeRiver(tile, Random()); break;
251 default: break;
254 if (wc != WATER_CLASS_INVALID) CheckForDockingTile(tile);
255 MarkTileDirtyByTile(tile);
258 static CommandCost RemoveShipDepot(TileIndex tile, DoCommandFlag flags)
260 if (!IsShipDepot(tile)) return CMD_ERROR;
262 CommandCost ret = CheckTileOwnership(tile);
263 if (ret.Failed()) return ret;
265 TileIndex tile2 = GetOtherShipDepotTile(tile);
267 /* do not check for ship on tile when company goes bankrupt */
268 if (!(flags & DC_BANKRUPT)) {
269 CommandCost ret = EnsureNoVehicleOnGround(tile);
270 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile2);
271 if (ret.Failed()) return ret;
274 if (flags & DC_EXEC) {
275 delete Depot::GetByTile(tile);
277 Company *c = Company::GetIfValid(GetTileOwner(tile));
278 if (c != nullptr) {
279 c->infrastructure.water -= 2 * LOCK_DEPOT_TILE_FACTOR;
280 DirtyCompanyInfrastructureWindows(c->index);
283 MakeWaterKeepingClass(tile, GetTileOwner(tile));
284 MakeWaterKeepingClass(tile2, GetTileOwner(tile2));
287 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_SHIP]);
291 * Builds a lock.
292 * @param tile Central tile of the lock.
293 * @param dir Uphill direction.
294 * @param flags Operation to perform.
295 * @return The cost in case of success, or an error code if it failed.
297 static CommandCost DoBuildLock(TileIndex tile, DiagDirection dir, DoCommandFlag flags)
299 CommandCost cost(EXPENSES_CONSTRUCTION);
301 int delta = TileOffsByDiagDir(dir);
302 CommandCost ret = EnsureNoVehicleOnGround(tile);
303 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta);
304 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta);
305 if (ret.Failed()) return ret;
307 /* middle tile */
308 WaterClass wc_middle = HasTileWaterGround(tile) ? GetWaterClass(tile) : WATER_CLASS_CANAL;
309 ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
310 if (ret.Failed()) return ret;
311 cost.AddCost(ret);
313 /* lower tile */
314 if (!IsWaterTile(tile - delta)) {
315 ret = DoCommand(tile - delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
316 if (ret.Failed()) return ret;
317 cost.AddCost(ret);
318 cost.AddCost(_price[PR_BUILD_CANAL]);
320 if (!IsTileFlat(tile - delta)) {
321 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
323 WaterClass wc_lower = IsWaterTile(tile - delta) ? GetWaterClass(tile - delta) : WATER_CLASS_CANAL;
325 /* upper tile */
326 if (!IsWaterTile(tile + delta)) {
327 ret = DoCommand(tile + delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
328 if (ret.Failed()) return ret;
329 cost.AddCost(ret);
330 cost.AddCost(_price[PR_BUILD_CANAL]);
332 if (!IsTileFlat(tile + delta)) {
333 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
335 WaterClass wc_upper = IsWaterTile(tile + delta) ? GetWaterClass(tile + delta) : WATER_CLASS_CANAL;
337 if (IsBridgeAbove(tile) || IsBridgeAbove(tile - delta) || IsBridgeAbove(tile + delta)) {
338 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
341 if (flags & DC_EXEC) {
342 /* Update company infrastructure counts. */
343 Company *c = Company::GetIfValid(_current_company);
344 if (c != nullptr) {
345 /* Counts for the water. */
346 if (!IsWaterTile(tile - delta)) c->infrastructure.water++;
347 if (!IsWaterTile(tile + delta)) c->infrastructure.water++;
348 /* Count for the lock itself. */
349 c->infrastructure.water += 3 * LOCK_DEPOT_TILE_FACTOR; // Lock is three tiles.
350 DirtyCompanyInfrastructureWindows(_current_company);
353 MakeLock(tile, _current_company, dir, wc_lower, wc_upper, wc_middle);
354 CheckForDockingTile(tile - delta);
355 CheckForDockingTile(tile + delta);
356 MarkTileDirtyByTile(tile);
357 MarkTileDirtyByTile(tile - delta);
358 MarkTileDirtyByTile(tile + delta);
359 MarkCanalsAndRiversAroundDirty(tile - delta);
360 MarkCanalsAndRiversAroundDirty(tile + delta);
362 cost.AddCost(_price[PR_BUILD_LOCK]);
364 return cost;
368 * Remove a lock.
369 * @param tile Central tile of the lock.
370 * @param flags Operation to perform.
371 * @return The cost in case of success, or an error code if it failed.
373 static CommandCost RemoveLock(TileIndex tile, DoCommandFlag flags)
375 if (GetTileOwner(tile) != OWNER_NONE) {
376 CommandCost ret = CheckTileOwnership(tile);
377 if (ret.Failed()) return ret;
380 TileIndexDiff delta = TileOffsByDiagDir(GetLockDirection(tile));
382 /* make sure no vehicle is on the tile. */
383 CommandCost ret = EnsureNoVehicleOnGround(tile);
384 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta);
385 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta);
386 if (ret.Failed()) return ret;
388 if (flags & DC_EXEC) {
389 /* Remove middle part from company infrastructure count. */
390 Company *c = Company::GetIfValid(GetTileOwner(tile));
391 if (c != nullptr) {
392 c->infrastructure.water -= 3 * LOCK_DEPOT_TILE_FACTOR; // three parts of the lock.
393 DirtyCompanyInfrastructureWindows(c->index);
396 if (GetWaterClass(tile) == WATER_CLASS_RIVER) {
397 MakeRiver(tile, Random());
398 } else {
399 DoClearSquare(tile);
401 MakeWaterKeepingClass(tile + delta, GetTileOwner(tile + delta));
402 MakeWaterKeepingClass(tile - delta, GetTileOwner(tile - delta));
403 MarkCanalsAndRiversAroundDirty(tile);
404 MarkCanalsAndRiversAroundDirty(tile - delta);
405 MarkCanalsAndRiversAroundDirty(tile + delta);
408 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_LOCK]);
412 * Builds a lock.
413 * @param tile tile where to place the lock
414 * @param flags type of operation
415 * @param p1 unused
416 * @param p2 unused
417 * @param text unused
418 * @return the cost of this operation or an error
420 CommandCost CmdBuildLock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
422 DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile));
423 if (dir == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
425 return DoBuildLock(tile, dir, flags);
428 /** Callback to create non-desert around a river tile. */
429 bool RiverModifyDesertZone(TileIndex tile, void *)
431 if (GetTropicZone(tile) == TROPICZONE_DESERT) SetTropicZone(tile, TROPICZONE_NORMAL);
432 return false;
436 * Build a piece of canal.
437 * @param tile end tile of stretch-dragging
438 * @param flags type of operation
439 * @param p1 start tile of stretch-dragging
440 * @param p2 various bitstuffed data
441 * bits 0-1: waterclass to build. sea and river can only be built in scenario editor
442 * bit 2: Whether to use the Orthogonal (0) or Diagonal (1) iterator.
443 * @param text unused
444 * @return the cost of this operation or an error
446 CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
448 WaterClass wc = Extract<WaterClass, 0, 2>(p2);
449 if (p1 >= MapSize() || wc == WATER_CLASS_INVALID) return CMD_ERROR;
451 /* Outside of the editor you can only build canals, not oceans */
452 if (wc != WATER_CLASS_CANAL && _game_mode != GM_EDITOR) return CMD_ERROR;
454 /* Outside the editor you can only drag canals, and not areas */
455 if (_game_mode != GM_EDITOR) {
456 TileArea ta(tile, p1);
457 if (ta.w != 1 && ta.h != 1) return CMD_ERROR;
460 CommandCost cost(EXPENSES_CONSTRUCTION);
462 std::unique_ptr<TileIterator> iter;
463 if (HasBit(p2, 2)) {
464 iter = std::make_unique<DiagonalTileIterator>(tile, p1);
465 } else {
466 iter = std::make_unique<OrthogonalTileIterator>(tile, p1);
469 for (; *iter != INVALID_TILE; ++(*iter)) {
470 TileIndex tile = *iter;
471 CommandCost ret;
473 Slope slope = GetTileSlope(tile);
474 if (slope != SLOPE_FLAT && (wc != WATER_CLASS_RIVER || !IsInclinedSlope(slope))) {
475 return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
478 /* can't make water of water! */
479 if (IsTileType(tile, MP_WATER) && (!IsTileOwner(tile, OWNER_WATER) || wc == WATER_CLASS_SEA)) continue;
481 bool water = IsWaterTile(tile);
482 ret = DoCommand(tile, 0, 0, flags | DC_FORCE_CLEAR_TILE, CMD_LANDSCAPE_CLEAR);
483 if (ret.Failed()) return ret;
485 if (!water) cost.AddCost(ret);
487 if (flags & DC_EXEC) {
488 switch (wc) {
489 case WATER_CLASS_RIVER:
490 MakeRiver(tile, Random());
491 if (_game_mode == GM_EDITOR) {
492 TileIndex tile2 = tile;
493 CircularTileSearch(&tile2, RIVER_OFFSET_DESERT_DISTANCE, RiverModifyDesertZone, nullptr);
495 break;
497 case WATER_CLASS_SEA:
498 if (TileHeight(tile) == 0) {
499 MakeSea(tile);
500 break;
502 FALLTHROUGH;
504 default:
505 MakeCanal(tile, _current_company, Random());
506 if (Company::IsValidID(_current_company)) {
507 Company::Get(_current_company)->infrastructure.water++;
508 DirtyCompanyInfrastructureWindows(_current_company);
510 break;
512 MarkTileDirtyByTile(tile);
513 MarkCanalsAndRiversAroundDirty(tile);
514 CheckForDockingTile(tile);
517 cost.AddCost(_price[PR_BUILD_CANAL]);
520 if (cost.GetCost() == 0) {
521 return_cmd_error(STR_ERROR_ALREADY_BUILT);
522 } else {
523 return cost;
527 static CommandCost ClearTile_Water(TileIndex tile, DoCommandFlag flags)
529 switch (GetWaterTileType(tile)) {
530 case WATER_TILE_CLEAR: {
531 if (flags & DC_NO_WATER) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER);
533 Money base_cost = IsCanal(tile) ? _price[PR_CLEAR_CANAL] : _price[PR_CLEAR_WATER];
534 /* Make sure freeform edges are allowed or it's not an edge tile. */
535 if (!_settings_game.construction.freeform_edges && (!IsInsideMM(TileX(tile), 1, MapMaxX() - 1) ||
536 !IsInsideMM(TileY(tile), 1, MapMaxY() - 1))) {
537 return_cmd_error(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP);
540 /* Make sure no vehicle is on the tile */
541 CommandCost ret = EnsureNoVehicleOnGround(tile);
542 if (ret.Failed()) return ret;
544 Owner owner = GetTileOwner(tile);
545 if (owner != OWNER_WATER && owner != OWNER_NONE) {
546 CommandCost ret = CheckTileOwnership(tile);
547 if (ret.Failed()) return ret;
550 if (flags & DC_EXEC) {
551 if (IsCanal(tile) && Company::IsValidID(owner)) {
552 Company::Get(owner)->infrastructure.water--;
553 DirtyCompanyInfrastructureWindows(owner);
555 bool remove = IsDockingTile(tile);
556 DoClearSquare(tile);
557 MarkCanalsAndRiversAroundDirty(tile);
558 if (remove) RemoveDockingTile(tile);
561 return CommandCost(EXPENSES_CONSTRUCTION, base_cost);
564 case WATER_TILE_COAST: {
565 Slope slope = GetTileSlope(tile);
567 /* Make sure no vehicle is on the tile */
568 CommandCost ret = EnsureNoVehicleOnGround(tile);
569 if (ret.Failed()) return ret;
571 if (flags & DC_EXEC) {
572 bool remove = IsDockingTile(tile);
573 DoClearSquare(tile);
574 MarkCanalsAndRiversAroundDirty(tile);
575 if (remove) RemoveDockingTile(tile);
577 if (IsSlopeWithOneCornerRaised(slope)) {
578 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_WATER]);
579 } else {
580 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_ROUGH]);
584 case WATER_TILE_LOCK: {
585 static const TileIndexDiffC _lock_tomiddle_offs[][DIAGDIR_END] = {
586 /* NE SE SW NW */
587 { { 0, 0}, {0, 0}, { 0, 0}, {0, 0} }, // LOCK_PART_MIDDLE
588 { {-1, 0}, {0, 1}, { 1, 0}, {0, -1} }, // LOCK_PART_LOWER
589 { { 1, 0}, {0, -1}, {-1, 0}, {0, 1} }, // LOCK_PART_UPPER
592 if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
593 if (_current_company == OWNER_WATER) return CMD_ERROR;
594 /* move to the middle tile.. */
595 return RemoveLock(tile + ToTileIndexDiff(_lock_tomiddle_offs[GetLockPart(tile)][GetLockDirection(tile)]), flags);
598 case WATER_TILE_DEPOT:
599 if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
600 return RemoveShipDepot(tile, flags);
602 default:
603 NOT_REACHED();
608 * return true if a tile is a water tile wrt. a certain direction.
610 * @param tile The tile of interest.
611 * @param from The direction of interest.
612 * @return true iff the tile is water in the view of 'from'.
615 bool IsWateredTile(TileIndex tile, Direction from)
617 switch (GetTileType(tile)) {
618 case MP_WATER:
619 switch (GetWaterTileType(tile)) {
620 default: NOT_REACHED();
621 case WATER_TILE_DEPOT: case WATER_TILE_CLEAR: return true;
622 case WATER_TILE_LOCK: return DiagDirToAxis(GetLockDirection(tile)) == DiagDirToAxis(DirToDiagDir(from));
624 case WATER_TILE_COAST:
625 switch (GetTileSlope(tile)) {
626 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
627 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
628 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
629 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
630 default: return false;
634 case MP_RAILWAY:
635 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
636 assert(IsPlainRail(tile));
637 switch (GetTileSlope(tile)) {
638 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
639 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
640 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
641 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
642 default: return false;
645 return false;
647 case MP_STATION:
648 if (IsOilRig(tile)) {
649 /* Do not draw waterborders inside of industries.
650 * Note: There is no easy way to detect the industry of an oilrig tile. */
651 TileIndex src_tile = tile + TileOffsByDir(from);
652 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
653 (IsTileType(src_tile, MP_INDUSTRY))) return true;
655 return IsTileOnWater(tile);
657 return (IsDock(tile) && IsTileFlat(tile)) || IsBuoy(tile);
659 case MP_INDUSTRY: {
660 /* Do not draw waterborders inside of industries.
661 * Note: There is no easy way to detect the industry of an oilrig tile. */
662 TileIndex src_tile = tile + TileOffsByDir(from);
663 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
664 (IsTileType(src_tile, MP_INDUSTRY) && GetIndustryIndex(src_tile) == GetIndustryIndex(tile))) return true;
666 return IsTileOnWater(tile);
669 case MP_OBJECT: return IsTileOnWater(tile);
671 case MP_TUNNELBRIDGE: return GetTunnelBridgeTransportType(tile) == TRANSPORT_WATER && ReverseDiagDir(GetTunnelBridgeDirection(tile)) == DirToDiagDir(from);
673 case MP_VOID: return true; // consider map border as water, esp. for rivers
675 default: return false;
680 * Draw a water sprite, potentially with a NewGRF-modified sprite offset.
681 * @param base Sprite base.
682 * @param offset Sprite offset.
683 * @param feature The type of sprite that is drawn.
684 * @param tile Tile index to draw.
686 static void DrawWaterSprite(SpriteID base, uint offset, CanalFeature feature, TileIndex tile)
688 if (base != SPR_FLAT_WATER_TILE) {
689 /* Only call offset callback if the sprite is NewGRF-provided. */
690 offset = GetCanalSpriteOffset(feature, tile, offset);
692 DrawGroundSprite(base + offset, PAL_NONE);
696 * Draw canal or river edges.
697 * @param canal True if canal edges should be drawn, false for river edges.
698 * @param offset Sprite offset.
699 * @param tile Tile to draw.
701 static void DrawWaterEdges(bool canal, uint offset, TileIndex tile)
703 CanalFeature feature;
704 SpriteID base = 0;
705 if (canal) {
706 feature = CF_DIKES;
707 base = GetCanalSprite(CF_DIKES, tile);
708 if (base == 0) base = SPR_CANAL_DIKES_BASE;
709 } else {
710 feature = CF_RIVER_EDGE;
711 base = GetCanalSprite(CF_RIVER_EDGE, tile);
712 if (base == 0) return; // Don't draw if no sprites provided.
715 uint wa;
717 /* determine the edges around with water. */
718 wa = IsWateredTile(TILE_ADDXY(tile, -1, 0), DIR_SW) << 0;
719 wa += IsWateredTile(TILE_ADDXY(tile, 0, 1), DIR_NW) << 1;
720 wa += IsWateredTile(TILE_ADDXY(tile, 1, 0), DIR_NE) << 2;
721 wa += IsWateredTile(TILE_ADDXY(tile, 0, -1), DIR_SE) << 3;
723 if (!(wa & 1)) DrawWaterSprite(base, offset, feature, tile);
724 if (!(wa & 2)) DrawWaterSprite(base, offset + 1, feature, tile);
725 if (!(wa & 4)) DrawWaterSprite(base, offset + 2, feature, tile);
726 if (!(wa & 8)) DrawWaterSprite(base, offset + 3, feature, tile);
728 /* right corner */
729 switch (wa & 0x03) {
730 case 0: DrawWaterSprite(base, offset + 4, feature, tile); break;
731 case 3: if (!IsWateredTile(TILE_ADDXY(tile, -1, 1), DIR_W)) DrawWaterSprite(base, offset + 8, feature, tile); break;
734 /* bottom corner */
735 switch (wa & 0x06) {
736 case 0: DrawWaterSprite(base, offset + 5, feature, tile); break;
737 case 6: if (!IsWateredTile(TILE_ADDXY(tile, 1, 1), DIR_N)) DrawWaterSprite(base, offset + 9, feature, tile); break;
740 /* left corner */
741 switch (wa & 0x0C) {
742 case 0: DrawWaterSprite(base, offset + 6, feature, tile); break;
743 case 12: if (!IsWateredTile(TILE_ADDXY(tile, 1, -1), DIR_E)) DrawWaterSprite(base, offset + 10, feature, tile); break;
746 /* upper corner */
747 switch (wa & 0x09) {
748 case 0: DrawWaterSprite(base, offset + 7, feature, tile); break;
749 case 9: if (!IsWateredTile(TILE_ADDXY(tile, -1, -1), DIR_S)) DrawWaterSprite(base, offset + 11, feature, tile); break;
753 /** Draw a plain sea water tile with no edges */
754 static void DrawSeaWater(TileIndex tile)
756 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
759 /** draw a canal styled water tile with dikes around */
760 static void DrawCanalWater(TileIndex tile)
762 SpriteID image = SPR_FLAT_WATER_TILE;
763 if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
764 /* First water slope sprite is flat water. */
765 image = GetCanalSprite(CF_WATERSLOPE, tile);
766 if (image == 0) image = SPR_FLAT_WATER_TILE;
768 DrawWaterSprite(image, 0, CF_WATERSLOPE, tile);
770 DrawWaterEdges(true, 0, tile);
773 #include "table/water_land.h"
776 * Draw a build sprite sequence for water tiles.
777 * If buildings are invisible, nothing will be drawn.
778 * @param ti Tile info.
779 * @param dtss Sprite sequence to draw.
780 * @param base Base sprite.
781 * @param offset Additional sprite offset.
782 * @param palette Palette to use.
784 static void DrawWaterTileStruct(const TileInfo *ti, const DrawTileSeqStruct *dtss, SpriteID base, uint offset, PaletteID palette, CanalFeature feature)
786 /* Don't draw if buildings are invisible. */
787 if (IsInvisibilitySet(TO_BUILDINGS)) return;
789 for (; !dtss->IsTerminator(); dtss++) {
790 uint tile_offs = offset + dtss->image.sprite;
791 if (feature < CF_END) tile_offs = GetCanalSpriteOffset(feature, ti->tile, tile_offs);
792 AddSortableSpriteToDraw(base + tile_offs, palette,
793 ti->x + dtss->delta_x, ti->y + dtss->delta_y,
794 dtss->size_x, dtss->size_y,
795 dtss->size_z, ti->z + dtss->delta_z,
796 IsTransparencySet(TO_BUILDINGS));
800 /** Draw a lock tile. */
801 static void DrawWaterLock(const TileInfo *ti)
803 int part = GetLockPart(ti->tile);
804 const DrawTileSprites &dts = _lock_display_data[part][GetLockDirection(ti->tile)];
806 /* Draw ground sprite. */
807 SpriteID image = dts.ground.sprite;
809 SpriteID water_base = GetCanalSprite(CF_WATERSLOPE, ti->tile);
810 if (water_base == 0) {
811 /* Use default sprites. */
812 water_base = SPR_CANALS_BASE;
813 } else if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
814 /* NewGRF supplies a flat sprite as first sprite. */
815 if (image == SPR_FLAT_WATER_TILE) {
816 image = water_base;
817 } else {
818 image++;
822 if (image < 5) image += water_base;
823 DrawGroundSprite(image, PAL_NONE);
825 /* Draw structures. */
826 uint zoffs = 0;
827 SpriteID base = GetCanalSprite(CF_LOCKS, ti->tile);
829 if (base == 0) {
830 /* If no custom graphics, use defaults. */
831 base = SPR_LOCK_BASE;
832 uint8 z_threshold = part == LOCK_PART_UPPER ? 8 : 0;
833 zoffs = ti->z > z_threshold ? 24 : 0;
836 DrawWaterTileStruct(ti, dts.seq, base, zoffs, PAL_NONE, CF_LOCKS);
839 /** Draw a ship depot tile. */
840 static void DrawWaterDepot(const TileInfo *ti)
842 DrawWaterClassGround(ti);
843 DrawWaterTileStruct(ti, _shipdepot_display_data[GetShipDepotAxis(ti->tile)][GetShipDepotPart(ti->tile)].seq, 0, 0, COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile)), CF_END);
846 static void DrawRiverWater(const TileInfo *ti)
848 SpriteID image = SPR_FLAT_WATER_TILE;
849 uint offset = 0;
850 uint edges_offset = 0;
852 if (ti->tileh != SLOPE_FLAT || HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
853 image = GetCanalSprite(CF_RIVER_SLOPE, ti->tile);
854 if (image == 0) {
855 switch (ti->tileh) {
856 case SLOPE_NW: image = SPR_WATER_SLOPE_Y_DOWN; break;
857 case SLOPE_SW: image = SPR_WATER_SLOPE_X_UP; break;
858 case SLOPE_SE: image = SPR_WATER_SLOPE_Y_UP; break;
859 case SLOPE_NE: image = SPR_WATER_SLOPE_X_DOWN; break;
860 default: image = SPR_FLAT_WATER_TILE; break;
862 } else {
863 /* Flag bit 0 indicates that the first sprite is flat water. */
864 offset = HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE) ? 1 : 0;
866 switch (ti->tileh) {
867 case SLOPE_SE: edges_offset += 12; break;
868 case SLOPE_NE: offset += 1; edges_offset += 24; break;
869 case SLOPE_SW: offset += 2; edges_offset += 36; break;
870 case SLOPE_NW: offset += 3; edges_offset += 48; break;
871 default: offset = 0; break;
874 offset = GetCanalSpriteOffset(CF_RIVER_SLOPE, ti->tile, offset);
878 DrawGroundSprite(image + offset, PAL_NONE);
880 /* Draw river edges if available. */
881 DrawWaterEdges(false, edges_offset, ti->tile);
884 void DrawShoreTile(Slope tileh)
886 /* Converts the enum Slope into an offset based on SPR_SHORE_BASE.
887 * This allows to calculate the proper sprite to display for this Slope */
888 static const byte tileh_to_shoresprite[32] = {
889 0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0,
890 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 15, 0,
893 assert(!IsHalftileSlope(tileh)); // Halftile slopes need to get handled earlier.
894 assert(tileh != SLOPE_FLAT); // Shore is never flat
896 assert((tileh != SLOPE_EW) && (tileh != SLOPE_NS)); // No suitable sprites for current flooding behaviour
898 DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[tileh], PAL_NONE);
901 void DrawWaterClassGround(const TileInfo *ti)
903 switch (GetWaterClass(ti->tile)) {
904 case WATER_CLASS_SEA: DrawSeaWater(ti->tile); break;
905 case WATER_CLASS_CANAL: DrawCanalWater(ti->tile); break;
906 case WATER_CLASS_RIVER: DrawRiverWater(ti); break;
907 default: NOT_REACHED();
911 static void DrawTile_Water(TileInfo *ti)
913 switch (GetWaterTileType(ti->tile)) {
914 case WATER_TILE_CLEAR:
915 DrawWaterClassGround(ti);
916 DrawBridgeMiddle(ti);
917 break;
919 case WATER_TILE_COAST: {
920 DrawShoreTile(ti->tileh);
921 DrawBridgeMiddle(ti);
922 break;
925 case WATER_TILE_LOCK:
926 DrawWaterLock(ti);
927 break;
929 case WATER_TILE_DEPOT:
930 DrawWaterDepot(ti);
931 break;
935 void DrawShipDepotSprite(int x, int y, Axis axis, DepotPart part)
937 const DrawTileSprites &dts = _shipdepot_display_data[axis][part];
939 DrawSprite(dts.ground.sprite, dts.ground.pal, x, y);
940 DrawOrigTileSeqInGUI(x, y, &dts, COMPANY_SPRITE_COLOUR(_local_company));
944 static int GetSlopePixelZ_Water(TileIndex tile, uint x, uint y)
946 int z;
947 Slope tileh = GetTilePixelSlope(tile, &z);
949 return z + GetPartialPixelZ(x & 0xF, y & 0xF, tileh);
952 static Foundation GetFoundation_Water(TileIndex tile, Slope tileh)
954 return FOUNDATION_NONE;
957 static void GetTileDesc_Water(TileIndex tile, TileDesc *td)
959 switch (GetWaterTileType(tile)) {
960 case WATER_TILE_CLEAR:
961 switch (GetWaterClass(tile)) {
962 case WATER_CLASS_SEA: td->str = STR_LAI_WATER_DESCRIPTION_WATER; break;
963 case WATER_CLASS_CANAL: td->str = STR_LAI_WATER_DESCRIPTION_CANAL; break;
964 case WATER_CLASS_RIVER: td->str = STR_LAI_WATER_DESCRIPTION_RIVER; break;
965 default: NOT_REACHED();
967 break;
968 case WATER_TILE_COAST: td->str = STR_LAI_WATER_DESCRIPTION_COAST_OR_RIVERBANK; break;
969 case WATER_TILE_LOCK : td->str = STR_LAI_WATER_DESCRIPTION_LOCK; break;
970 case WATER_TILE_DEPOT:
971 td->str = STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT;
972 td->build_date = Depot::GetByTile(tile)->build_date;
973 break;
974 default: NOT_REACHED();
977 td->owner[0] = GetTileOwner(tile);
981 * Handle the flooding of a vehicle. This sets the vehicle state to crashed,
982 * creates a newsitem and dirties the necessary windows.
983 * @param v The vehicle to flood.
985 static void FloodVehicle(Vehicle *v)
987 uint pass = v->Crash(true);
989 AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_FLOODED));
990 Game::NewEvent(new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_FLOODED));
991 SetDParam(0, pass);
992 AddTileNewsItem(STR_NEWS_DISASTER_FLOOD_VEHICLE, NT_ACCIDENT, v->tile);
993 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
994 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v);
998 * Flood a vehicle if we are allowed to flood it, i.e. when it is on the ground.
999 * @param v The vehicle to test for flooding.
1000 * @param data The z of level to flood.
1001 * @return nullptr as we always want to remove everything.
1003 static Vehicle *FloodVehicleProc(Vehicle *v, void *data)
1005 if ((v->vehstatus & VS_CRASHED) != 0) return nullptr;
1007 switch (v->type) {
1008 default: break;
1010 case VEH_AIRCRAFT: {
1011 if (!IsAirportTile(v->tile) || GetTileMaxZ(v->tile) != 0) break;
1012 if (v->subtype == AIR_SHADOW) break;
1014 /* We compare v->z_pos against delta_z + 1 because the shadow
1015 * is at delta_z and the actual aircraft at delta_z + 1. */
1016 const Station *st = Station::GetByTile(v->tile);
1017 const AirportFTAClass *airport = st->airport.GetFTA();
1018 if (v->z_pos != airport->delta_z + 1) break;
1020 FloodVehicle(v);
1021 break;
1024 case VEH_TRAIN:
1025 case VEH_ROAD: {
1026 int z = *(int*)data;
1027 if (v->z_pos > z) break;
1028 FloodVehicle(v->First());
1029 break;
1033 return nullptr;
1037 * Finds a vehicle to flood.
1038 * It does not find vehicles that are already crashed on bridges, i.e. flooded.
1039 * @param tile the tile where to find a vehicle to flood
1041 static void FloodVehicles(TileIndex tile)
1043 int z = 0;
1045 if (IsAirportTile(tile)) {
1046 const Station *st = Station::GetByTile(tile);
1047 TILE_AREA_LOOP(tile, st->airport) {
1048 if (st->TileBelongsToAirport(tile)) FindVehicleOnPos(tile, &z, &FloodVehicleProc);
1051 /* No vehicle could be flooded on this airport anymore */
1052 return;
1055 if (!IsBridgeTile(tile)) {
1056 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
1057 return;
1060 TileIndex end = GetOtherBridgeEnd(tile);
1061 z = GetBridgePixelHeight(tile);
1063 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
1064 FindVehicleOnPos(end, &z, &FloodVehicleProc);
1068 * Returns the behaviour of a tile during flooding.
1070 * @return Behaviour of the tile
1072 FloodingBehaviour GetFloodingBehaviour(TileIndex tile)
1074 /* FLOOD_ACTIVE: 'single-corner-raised'-coast, sea, sea-shipdepots, sea-buoys, sea-docks (water part), rail with flooded halftile, sea-water-industries, sea-oilrigs
1075 * FLOOD_DRYUP: coast with more than one corner raised, coast with rail-track, coast with trees
1076 * FLOOD_PASSIVE: (not used)
1077 * FLOOD_NONE: canals, rivers, everything else
1079 switch (GetTileType(tile)) {
1080 case MP_WATER:
1081 if (IsCoast(tile)) {
1082 Slope tileh = GetTileSlope(tile);
1083 return (IsSlopeWithOneCornerRaised(tileh) ? FLOOD_ACTIVE : FLOOD_DRYUP);
1085 FALLTHROUGH;
1086 case MP_STATION:
1087 case MP_INDUSTRY:
1088 case MP_OBJECT:
1089 return (GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE;
1091 case MP_RAILWAY:
1092 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
1093 return (IsSlopeWithOneCornerRaised(GetTileSlope(tile)) ? FLOOD_ACTIVE : FLOOD_DRYUP);
1095 return FLOOD_NONE;
1097 case MP_TREES:
1098 return (GetTreeGround(tile) == TREE_GROUND_SHORE ? FLOOD_DRYUP : FLOOD_NONE);
1100 default:
1101 return FLOOD_NONE;
1106 * Floods a tile.
1108 void DoFloodTile(TileIndex target)
1110 assert(!IsTileType(target, MP_WATER));
1112 bool flooded = false; // Will be set to true if something is changed.
1114 Backup<CompanyID> cur_company(_current_company, OWNER_WATER, FILE_LINE);
1116 Slope tileh = GetTileSlope(target);
1117 if (tileh != SLOPE_FLAT) {
1118 /* make coast.. */
1119 switch (GetTileType(target)) {
1120 case MP_RAILWAY: {
1121 if (!IsPlainRail(target)) break;
1122 FloodVehicles(target);
1123 flooded = FloodHalftile(target);
1124 break;
1127 case MP_TREES:
1128 if (!IsSlopeWithOneCornerRaised(tileh)) {
1129 SetTreeGroundDensity(target, TREE_GROUND_SHORE, 3);
1130 MarkTileDirtyByTile(target);
1131 flooded = true;
1132 break;
1134 FALLTHROUGH;
1136 case MP_CLEAR:
1137 if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
1138 MakeShore(target);
1139 MarkTileDirtyByTile(target);
1140 flooded = true;
1142 break;
1144 default:
1145 break;
1147 } else {
1148 /* Flood vehicles */
1149 FloodVehicles(target);
1151 /* flood flat tile */
1152 if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
1153 MakeSea(target);
1154 MarkTileDirtyByTile(target);
1155 flooded = true;
1159 if (flooded) {
1160 /* Mark surrounding canal tiles dirty too to avoid glitches */
1161 MarkCanalsAndRiversAroundDirty(target);
1163 /* update signals if needed */
1164 UpdateSignalsInBuffer();
1166 if (IsPossibleDockingTile(target)) CheckForDockingTile(target);
1169 cur_company.Restore();
1173 * Drys a tile up.
1175 static void DoDryUp(TileIndex tile)
1177 Backup<CompanyID> cur_company(_current_company, OWNER_WATER, FILE_LINE);
1179 switch (GetTileType(tile)) {
1180 case MP_RAILWAY:
1181 assert(IsPlainRail(tile));
1182 assert(GetRailGroundType(tile) == RAIL_GROUND_WATER);
1184 RailGroundType new_ground;
1185 switch (GetTrackBits(tile)) {
1186 case TRACK_BIT_UPPER: new_ground = RAIL_GROUND_FENCE_HORIZ1; break;
1187 case TRACK_BIT_LOWER: new_ground = RAIL_GROUND_FENCE_HORIZ2; break;
1188 case TRACK_BIT_LEFT: new_ground = RAIL_GROUND_FENCE_VERT1; break;
1189 case TRACK_BIT_RIGHT: new_ground = RAIL_GROUND_FENCE_VERT2; break;
1190 default: NOT_REACHED();
1192 SetRailGroundType(tile, new_ground);
1193 MarkTileDirtyByTile(tile);
1194 break;
1196 case MP_TREES:
1197 SetTreeGroundDensity(tile, TREE_GROUND_GRASS, 3);
1198 MarkTileDirtyByTile(tile);
1199 break;
1201 case MP_WATER:
1202 assert(IsCoast(tile));
1204 if (DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
1205 MakeClear(tile, CLEAR_GRASS, 3);
1206 MarkTileDirtyByTile(tile);
1208 break;
1210 default: NOT_REACHED();
1213 cur_company.Restore();
1217 * Let a water tile floods its diagonal adjoining tiles
1218 * called from tunnelbridge_cmd, and by TileLoop_Industry() and TileLoop_Track()
1220 * @param tile the water/shore tile that floods
1222 void TileLoop_Water(TileIndex tile)
1224 if (IsTileType(tile, MP_WATER)) AmbientSoundEffect(tile);
1226 switch (GetFloodingBehaviour(tile)) {
1227 case FLOOD_ACTIVE:
1228 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
1229 TileIndex dest = tile + TileOffsByDir(dir);
1230 if (!IsValidTile(dest)) continue;
1231 /* do not try to flood water tiles - increases performance a lot */
1232 if (IsTileType(dest, MP_WATER)) continue;
1234 /* TREE_GROUND_SHORE is the sign of a previous flood. */
1235 if (IsTileType(dest, MP_TREES) && GetTreeGround(dest) == TREE_GROUND_SHORE) continue;
1237 int z_dest;
1238 Slope slope_dest = GetFoundationSlope(dest, &z_dest) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
1239 if (z_dest > 0) continue;
1241 if (!HasBit(_flood_from_dirs[slope_dest], ReverseDir(dir))) continue;
1243 DoFloodTile(dest);
1245 break;
1247 case FLOOD_DRYUP: {
1248 Slope slope_here = GetFoundationSlope(tile) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
1249 uint dir;
1250 FOR_EACH_SET_BIT(dir, _flood_from_dirs[slope_here]) {
1251 TileIndex dest = tile + TileOffsByDir((Direction)dir);
1252 if (!IsValidTile(dest)) continue;
1254 FloodingBehaviour dest_behaviour = GetFloodingBehaviour(dest);
1255 if ((dest_behaviour == FLOOD_ACTIVE) || (dest_behaviour == FLOOD_PASSIVE)) return;
1257 DoDryUp(tile);
1258 break;
1261 default: return;
1265 void ConvertGroundTilesIntoWaterTiles()
1267 int z;
1269 for (TileIndex tile = 0; tile < MapSize(); ++tile) {
1270 Slope slope = GetTileSlope(tile, &z);
1271 if (IsTileType(tile, MP_CLEAR) && z == 0) {
1272 /* Make both water for tiles at level 0
1273 * and make shore, as that looks much better
1274 * during the generation. */
1275 switch (slope) {
1276 case SLOPE_FLAT:
1277 MakeSea(tile);
1278 break;
1280 case SLOPE_N:
1281 case SLOPE_E:
1282 case SLOPE_S:
1283 case SLOPE_W:
1284 MakeShore(tile);
1285 break;
1287 default:
1288 uint dir;
1289 FOR_EACH_SET_BIT(dir, _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(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
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(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
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