OpenTTD: src/water_cmd.cpp Source File

OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

water_cmd.cpp
Go to the documentation of this file.
1 /* $Id: water_cmd.cpp 27254 2015-04-26 10:50:36Z frosch $ */
2
3 /*
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/>.
8 */
9
12 #include "stdafx.h"
13 #include "cmd_helper.h"
14 #include "landscape.h"
15 #include "viewport_func.h"
16 #include "command_func.h"
17 #include "town.h"
18 #include "news_func.h"
19 #include "depot_base.h"
20 #include "depot_func.h"
21 #include "water.h"
22 #include "industry_map.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 "clear_map.h"
29 #include "tree_map.h"
30 #include "aircraft.h"
31 #include "effectvehicle_func.h"
32 #include "tunnelbridge_map.h"
33 #include "station_base.h"
34 #include "ai/ai.hpp"
35 #include "game/game.hpp"
36 #include "core/random_func.hpp"
37 #include "core/backup_type.hpp"
38 #include "date_func.h"
39 #include "company_base.h"
40 #include "company_gui.h"
41 #include "newgrf_generic.h"
42
43 #include "table/strings.h"
44
45 #include "safeguards.h"
46
50 static const uint8 _flood_from_dirs[] = {
51 (1 << DIR_NW) | (1 << DIR_SW) | (1 << DIR_SE) | (1 << DIR_NE), // SLOPE_FLAT
52 (1 << DIR_NE) | (1 << DIR_SE), // SLOPE_W
53 (1 << DIR_NW) | (1 << DIR_NE), // SLOPE_S
54 (1 << DIR_NE), // SLOPE_SW
55 (1 << DIR_NW) | (1 << DIR_SW), // SLOPE_E
56 0, // SLOPE_EW
57 (1 << DIR_NW), // SLOPE_SE
58 (1 << DIR_N ) | (1 << DIR_NW) | (1 << DIR_NE), // SLOPE_WSE, SLOPE_STEEP_S
59 (1 << DIR_SW) | (1 << DIR_SE), // SLOPE_N
60 (1 << DIR_SE), // SLOPE_NW
61 0, // SLOPE_NS
62 (1 << DIR_E ) | (1 << DIR_NE) | (1 << DIR_SE), // SLOPE_NWS, SLOPE_STEEP_W
63 (1 << DIR_SW), // SLOPE_NE
64 (1 << DIR_S ) | (1 << DIR_SW) | (1 << DIR_SE), // SLOPE_ENW, SLOPE_STEEP_N
65 (1 << DIR_W ) | (1 << DIR_SW) | (1 << DIR_NW), // SLOPE_SEN, SLOPE_STEEP_E
66 };
67
74 static inline void MarkTileDirtyIfCanalOrRiver(TileIndex tile)
75 {
76 if (IsTileType(tile, MP_WATER) && (IsCanal(tile) || IsRiver(tile))) MarkTileDirtyByTile(tile);
77 }
78
85 static void MarkCanalsAndRiversAroundDirty(TileIndex tile)
86 {
87 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
88 MarkTileDirtyIfCanalOrRiver(tile + TileOffsByDir(dir));
89 }
90 }
91
92
102 CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
103 {
104 Axis axis = Extract<Axis, 0, 1>(p1);
105
106 TileIndex tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
107
108 if (!HasTileWaterGround(tile) || !HasTileWaterGround(tile2)) {
109 return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER);
110 }
111
112 if (IsBridgeAbove(tile) || IsBridgeAbove(tile2)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
113
114 if (!IsTileFlat(tile) || !IsTileFlat(tile2)) {
115 /* Prevent depots on rapids */
116 return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
117 }
118
119 if (!Depot::CanAllocateItem()) return CMD_ERROR;
120
121 WaterClass wc1 = GetWaterClass(tile);
122 WaterClass wc2 = GetWaterClass(tile2);
123 CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_DEPOT_SHIP]);
124
125 bool add_cost = !IsWaterTile(tile);
126 CommandCost ret = DoCommand(tile, 0, 0, flags | DC_AUTO, CMD_LANDSCAPE_CLEAR);
127 if (ret.Failed()) return ret;
128 if (add_cost) {
129 cost.AddCost(ret);
130 }
131 add_cost = !IsWaterTile(tile2);
132 ret = DoCommand(tile2, 0, 0, flags | DC_AUTO, CMD_LANDSCAPE_CLEAR);
133 if (ret.Failed()) return ret;
134 if (add_cost) {
135 cost.AddCost(ret);
136 }
137
138 if (flags & DC_EXEC) {
139 Depot *depot = new Depot(tile);
140 depot->build_date = _date;
141
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;
145 }
146 Company::Get(_current_company)->infrastructure.water += 2 * LOCK_DEPOT_TILE_FACTOR;
147 DirtyCompanyInfrastructureWindows(_current_company);
148
149 MakeShipDepot(tile, _current_company, depot->index, DEPOT_PART_NORTH, axis, wc1);
150 MakeShipDepot(tile2, _current_company, depot->index, DEPOT_PART_SOUTH, axis, wc2);
151 MarkTileDirtyByTile(tile);
152 MarkTileDirtyByTile(tile2);
153 MakeDefaultName(depot);
154 }
155
156 return cost;
157 }
158
159 void MakeWaterKeepingClass(TileIndex tile, Owner o)
160 {
161 WaterClass wc = GetWaterClass(tile);
162
163 /* Autoslope might turn an originally canal or river tile into land */
164 int z;
165 Slope slope = GetTileSlope(tile, &z);
166
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);
171 if (c != NULL) {
172 c->infrastructure.water--;
173 DirtyCompanyInfrastructureWindows(c->index);
174 }
175 /* Sloped canals are locks and no natural water remains whatever the slope direction */
176 wc = WATER_CLASS_INVALID;
177 }
178
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;
182 }
183 }
184
185 if (wc == WATER_CLASS_SEA && z > 0) {
186 /* Update company infrastructure count. */
187 Company *c = Company::GetIfValid(o);
188 if (c != NULL) {
189 c->infrastructure.water++;
190 DirtyCompanyInfrastructureWindows(c->index);
191 }
192
193 wc = WATER_CLASS_CANAL;
194 }
195
196 /* Zero map array and terminate animation */
197 DoClearSquare(tile);
198
199 /* Maybe change to water */
200 switch (wc) {
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;
204 default: break;
205 }
206
207 MarkTileDirtyByTile(tile);
208 }
209
210 static CommandCost RemoveShipDepot(TileIndex tile, DoCommandFlag flags)
211 {
212 if (!IsShipDepot(tile)) return CMD_ERROR;
213
214 CommandCost ret = CheckTileOwnership(tile);
215 if (ret.Failed()) return ret;
216
217 TileIndex tile2 = GetOtherShipDepotTile(tile);
218
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;
224 }
225
226 if (flags & DC_EXEC) {
227 delete Depot::GetByTile(tile);
228
229 Company *c = Company::GetIfValid(GetTileOwner(tile));
230 if (c != NULL) {
231 c->infrastructure.water -= 2 * LOCK_DEPOT_TILE_FACTOR;
232 DirtyCompanyInfrastructureWindows(c->index);
233 }
234
235 MakeWaterKeepingClass(tile, GetTileOwner(tile));
236 MakeWaterKeepingClass(tile2, GetTileOwner(tile2));
237 }
238
239 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_SHIP]);
240 }
241
249 static CommandCost DoBuildLock(TileIndex tile, DiagDirection dir, DoCommandFlag flags)
250 {
251 CommandCost cost(EXPENSES_CONSTRUCTION);
252
253 int delta = TileOffsByDiagDir(dir);
254 CommandCost ret = EnsureNoVehicleOnGround(tile);
255 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta);
256 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta);
257 if (ret.Failed()) return ret;
258
259 /* middle tile */
260 WaterClass wc_middle = IsWaterTile(tile) ? GetWaterClass(tile) : WATER_CLASS_CANAL;
261 ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
262 if (ret.Failed()) return ret;
263 cost.AddCost(ret);
264
265 /* lower tile */
266 if (!IsWaterTile(tile - delta)) {
267 ret = DoCommand(tile - delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
268 if (ret.Failed()) return ret;
269 cost.AddCost(ret);
270 cost.AddCost(_price[PR_BUILD_CANAL]);
271 }
272 if (!IsTileFlat(tile - delta)) {
273 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
274 }
275 WaterClass wc_lower = IsWaterTile(tile - delta) ? GetWaterClass(tile - delta) : WATER_CLASS_CANAL;
276
277 /* upper tile */
278 if (!IsWaterTile(tile + delta)) {
279 ret = DoCommand(tile + delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
280 if (ret.Failed()) return ret;
281 cost.AddCost(ret);
282 cost.AddCost(_price[PR_BUILD_CANAL]);
283 }
284 if (!IsTileFlat(tile + delta)) {
285 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
286 }
287 WaterClass wc_upper = IsWaterTile(tile + delta) ? GetWaterClass(tile + delta) : WATER_CLASS_CANAL;
288
289 if (IsBridgeAbove(tile) || IsBridgeAbove(tile - delta) || IsBridgeAbove(tile + delta)) {
290 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
291 }
292
293 if (flags & DC_EXEC) {
294 /* Update company infrastructure counts. */
295 Company *c = Company::GetIfValid(_current_company);
296 if (c != NULL) {
297 /* Counts for the water. */
298 if (!IsWaterTile(tile - delta)) c->infrastructure.water++;
299 if (!IsWaterTile(tile + delta)) c->infrastructure.water++;
300 /* Count for the lock itself. */
301 c->infrastructure.water += 3 * LOCK_DEPOT_TILE_FACTOR; // Lock is three tiles.
302 DirtyCompanyInfrastructureWindows(_current_company);
303 }
304
305 MakeLock(tile, _current_company, dir, wc_lower, wc_upper, wc_middle);
306 MarkTileDirtyByTile(tile);
307 MarkTileDirtyByTile(tile - delta);
308 MarkTileDirtyByTile(tile + delta);
309 MarkCanalsAndRiversAroundDirty(tile - delta);
310 MarkCanalsAndRiversAroundDirty(tile + delta);
311 }
312 cost.AddCost(_price[PR_BUILD_LOCK]);
313
314 return cost;
315 }
316
323 static CommandCost RemoveLock(TileIndex tile, DoCommandFlag flags)
324 {
325 if (GetTileOwner(tile) != OWNER_NONE) {
326 CommandCost ret = CheckTileOwnership(tile);
327 if (ret.Failed()) return ret;
328 }
329
330 TileIndexDiff delta = TileOffsByDiagDir(GetLockDirection(tile));
331
332 /* make sure no vehicle is on the tile. */
333 CommandCost ret = EnsureNoVehicleOnGround(tile);
334 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta);
335 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta);
336 if (ret.Failed()) return ret;
337
338 if (flags & DC_EXEC) {
339 /* Remove middle part from company infrastructure count. */
340 Company *c = Company::GetIfValid(GetTileOwner(tile));
341 if (c != NULL) {
342 c->infrastructure.water -= 3 * LOCK_DEPOT_TILE_FACTOR; // three parts of the lock.
343 DirtyCompanyInfrastructureWindows(c->index);
344 }
345
346 if (GetWaterClass(tile) == WATER_CLASS_RIVER) {
347 MakeRiver(tile, Random());
348 } else {
349 DoClearSquare(tile);
350 }
351 MakeWaterKeepingClass(tile + delta, GetTileOwner(tile + delta));
352 MakeWaterKeepingClass(tile - delta, GetTileOwner(tile - delta));
353 MarkCanalsAndRiversAroundDirty(tile);
354 MarkCanalsAndRiversAroundDirty(tile - delta);
355 MarkCanalsAndRiversAroundDirty(tile + delta);
356 }
357
358 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_LOCK]);
359 }
360
370 CommandCost CmdBuildLock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
371 {
372 DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile));
373 if (dir == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
374
375 return DoBuildLock(tile, dir, flags);
376 }
377
379 bool RiverModifyDesertZone(TileIndex tile, void *)
380 {
381 if (GetTropicZone(tile) == TROPICZONE_DESERT) SetTropicZone(tile, TROPICZONE_NORMAL);
382 return false;
383 }
384
394 CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
395 {
396 WaterClass wc = Extract<WaterClass, 0, 2>(p2);
397 if (p1 >= MapSize() || wc == WATER_CLASS_INVALID) return CMD_ERROR;
398
399 /* Outside of the editor you can only build canals, not oceans */
400 if (wc != WATER_CLASS_CANAL && _game_mode != GM_EDITOR) return CMD_ERROR;
401
402 TileArea ta(tile, p1);
403
404 /* Outside the editor you can only drag canals, and not areas */
405 if (_game_mode != GM_EDITOR && ta.w != 1 && ta.h != 1) return CMD_ERROR;
406
407 CommandCost cost(EXPENSES_CONSTRUCTION);
408 TILE_AREA_LOOP(tile, ta) {
409 CommandCost ret;
410
411 Slope slope = GetTileSlope(tile);
412 if (slope != SLOPE_FLAT && (wc != WATER_CLASS_RIVER || !IsInclinedSlope(slope))) {
413 return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
414 }
415
416 /* can't make water of water! */
417 if (IsTileType(tile, MP_WATER) && (!IsTileOwner(tile, OWNER_WATER) || wc == WATER_CLASS_SEA)) continue;
418
419 bool water = IsWaterTile(tile);
420 ret = DoCommand(tile, 0, 0, flags | DC_FORCE_CLEAR_TILE, CMD_LANDSCAPE_CLEAR);
421 if (ret.Failed()) return ret;
422
423 if (!water) cost.AddCost(ret);
424
425 if (flags & DC_EXEC) {
426 switch (wc) {
427 case WATER_CLASS_RIVER:
428 MakeRiver(tile, Random());
429 if (_game_mode == GM_EDITOR) {
430 TileIndex tile2 = tile;
431 CircularTileSearch(&tile2, 5, RiverModifyDesertZone, NULL);
432 }
433 break;
434
435 case WATER_CLASS_SEA:
436 if (TileHeight(tile) == 0) {
437 MakeSea(tile);
438 break;
439 }
440 /* FALL THROUGH */
441
442 default:
443 MakeCanal(tile, _current_company, Random());
444 if (Company::IsValidID(_current_company)) {
445 Company::Get(_current_company)->infrastructure.water++;
446 DirtyCompanyInfrastructureWindows(_current_company);
447 }
448 break;
449 }
450 MarkTileDirtyByTile(tile);
451 MarkCanalsAndRiversAroundDirty(tile);
452 }
453
454 cost.AddCost(_price[PR_BUILD_CANAL]);
455 }
456
457 if (cost.GetCost() == 0) {
458 return_cmd_error(STR_ERROR_ALREADY_BUILT);
459 } else {
460 return cost;
461 }
462 }
463
464 static CommandCost ClearTile_Water(TileIndex tile, DoCommandFlag flags)
465 {
466 switch (GetWaterTileType(tile)) {
467 case WATER_TILE_CLEAR: {
468 if (flags & DC_NO_WATER) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER);
469
470 Money base_cost = IsCanal(tile) ? _price[PR_CLEAR_CANAL] : _price[PR_CLEAR_WATER];
471 /* Make sure freeform edges are allowed or it's not an edge tile. */
472 if (!_settings_game.construction.freeform_edges && (!IsInsideMM(TileX(tile), 1, MapMaxX() - 1) ||
473 !IsInsideMM(TileY(tile), 1, MapMaxY() - 1))) {
474 return_cmd_error(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP);
475 }
476
477 /* Make sure no vehicle is on the tile */
478 CommandCost ret = EnsureNoVehicleOnGround(tile);
479 if (ret.Failed()) return ret;
480
481 Owner owner = GetTileOwner(tile);
482 if (owner != OWNER_WATER && owner != OWNER_NONE) {
483 CommandCost ret = CheckTileOwnership(tile);
484 if (ret.Failed()) return ret;
485 }
486
487 if (flags & DC_EXEC) {
488 if (IsCanal(tile) && Company::IsValidID(owner)) {
489 Company::Get(owner)->infrastructure.water--;
490 DirtyCompanyInfrastructureWindows(owner);
491 }
492 DoClearSquare(tile);
493 MarkCanalsAndRiversAroundDirty(tile);
494 }
495
496 return CommandCost(EXPENSES_CONSTRUCTION, base_cost);
497 }
498
499 case WATER_TILE_COAST: {
500 Slope slope = GetTileSlope(tile);
501
502 /* Make sure no vehicle is on the tile */
503 CommandCost ret = EnsureNoVehicleOnGround(tile);
504 if (ret.Failed()) return ret;
505
506 if (flags & DC_EXEC) {
507 DoClearSquare(tile);
508 MarkCanalsAndRiversAroundDirty(tile);
509 }
510 if (IsSlopeWithOneCornerRaised(slope)) {
511 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_WATER]);
512 } else {
513 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_ROUGH]);
514 }
515 }
516
517 case WATER_TILE_LOCK: {
518 static const TileIndexDiffC _lock_tomiddle_offs[][DIAGDIR_END] = {
519 /* NE SE SW NW */
520 { { 0, 0}, {0, 0}, { 0, 0}, {0, 0} }, // LOCK_PART_MIDDLE
521 { {-1, 0}, {0, 1}, { 1, 0}, {0, -1} }, // LOCK_PART_LOWER
522 { { 1, 0}, {0, -1}, {-1, 0}, {0, 1} }, // LOCK_PART_UPPER
523 };
524
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 return RemoveLock(tile + ToTileIndexDiff(_lock_tomiddle_offs[GetLockPart(tile)][GetLockDirection(tile)]), flags);
529 }
530
531 case WATER_TILE_DEPOT:
532 if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
533 return RemoveShipDepot(tile, flags);
534
535 default:
536 NOT_REACHED();
537 }
538 }
539
548 bool IsWateredTile(TileIndex tile, Direction from)
549 {
550 switch (GetTileType(tile)) {
551 case MP_WATER:
552 switch (GetWaterTileType(tile)) {
553 default: NOT_REACHED();
554 case WATER_TILE_DEPOT: case WATER_TILE_CLEAR: return true;
555 case WATER_TILE_LOCK: return DiagDirToAxis(GetLockDirection(tile)) == DiagDirToAxis(DirToDiagDir(from));
556
557 case WATER_TILE_COAST:
558 switch (GetTileSlope(tile)) {
559 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
560 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
561 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
562 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
563 default: return false;
564 }
565 }
566
567 case MP_RAILWAY:
568 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
569 assert(IsPlainRail(tile));
570 switch (GetTileSlope(tile)) {
571 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
572 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
573 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
574 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
575 default: return false;
576 }
577 }
578 return false;
579
580 case MP_STATION:
581 if (IsOilRig(tile)) {
582 /* Do not draw waterborders inside of industries.
583 * Note: There is no easy way to detect the industry of an oilrig tile. */
584 TileIndex src_tile = tile + TileOffsByDir(from);
585 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
586 (IsTileType(src_tile, MP_INDUSTRY))) return true;
587
588 return IsTileOnWater(tile);
589 }
590 return (IsDock(tile) && IsTileFlat(tile)) || IsBuoy(tile);
591
592 case MP_INDUSTRY: {
593 /* Do not draw waterborders inside of industries.
594 * Note: There is no easy way to detect the industry of an oilrig tile. */
595 TileIndex src_tile = tile + TileOffsByDir(from);
596 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
597 (IsTileType(src_tile, MP_INDUSTRY) && GetIndustryIndex(src_tile) == GetIndustryIndex(tile))) return true;
598
599 return IsTileOnWater(tile);
600 }
601
602 case MP_OBJECT: return IsTileOnWater(tile);
603
604 case MP_TUNNELBRIDGE: return GetTunnelBridgeTransportType(tile) == TRANSPORT_WATER && ReverseDiagDir(GetTunnelBridgeDirection(tile)) == DirToDiagDir(from);
605
606 case MP_VOID: return true; // consider map border as water, esp. for rivers
607
608 default: return false;
609 }
610 }
611
619 static void DrawWaterSprite(SpriteID base, uint offset, CanalFeature feature, TileIndex tile)
620 {
621 if (base != SPR_FLAT_WATER_TILE) {
622 /* Only call offset callback if the sprite is NewGRF-provided. */
623 offset = GetCanalSpriteOffset(feature, tile, offset);
624 }
625 DrawGroundSprite(base + offset, PAL_NONE);
626 }
627
634 static void DrawWaterEdges(bool canal, uint offset, TileIndex tile)
635 {
636 CanalFeature feature;
637 SpriteID base = 0;
638 if (canal) {
639 feature = CF_DIKES;
640 base = GetCanalSprite(CF_DIKES, tile);
641 if (base == 0) base = SPR_CANAL_DIKES_BASE;
642 } else {
643 feature = CF_RIVER_EDGE;
644 base = GetCanalSprite(CF_RIVER_EDGE, tile);
645 if (base == 0) return; // Don't draw if no sprites provided.
646 }
647
648 uint wa;
649
650 /* determine the edges around with water. */
651 wa = IsWateredTile(TILE_ADDXY(tile, -1, 0), DIR_SW) << 0;
652 wa += IsWateredTile(TILE_ADDXY(tile, 0, 1), DIR_NW) << 1;
653 wa += IsWateredTile(TILE_ADDXY(tile, 1, 0), DIR_NE) << 2;
654 wa += IsWateredTile(TILE_ADDXY(tile, 0, -1), DIR_SE) << 3;
655
656 if (!(wa & 1)) DrawWaterSprite(base, offset, feature, tile);
657 if (!(wa & 2)) DrawWaterSprite(base, offset + 1, feature, tile);
658 if (!(wa & 4)) DrawWaterSprite(base, offset + 2, feature, tile);
659 if (!(wa & 8)) DrawWaterSprite(base, offset + 3, feature, tile);
660
661 /* right corner */
662 switch (wa & 0x03) {
663 case 0: DrawWaterSprite(base, offset + 4, feature, tile); break;
664 case 3: if (!IsWateredTile(TILE_ADDXY(tile, -1, 1), DIR_W)) DrawWaterSprite(base, offset + 8, feature, tile); break;
665 }
666
667 /* bottom corner */
668 switch (wa & 0x06) {
669 case 0: DrawWaterSprite(base, offset + 5, feature, tile); break;
670 case 6: if (!IsWateredTile(TILE_ADDXY(tile, 1, 1), DIR_N)) DrawWaterSprite(base, offset + 9, feature, tile); break;
671 }
672
673 /* left corner */
674 switch (wa & 0x0C) {
675 case 0: DrawWaterSprite(base, offset + 6, feature, tile); break;
676 case 12: if (!IsWateredTile(TILE_ADDXY(tile, 1, -1), DIR_E)) DrawWaterSprite(base, offset + 10, feature, tile); break;
677 }
678
679 /* upper corner */
680 switch (wa & 0x09) {
681 case 0: DrawWaterSprite(base, offset + 7, feature, tile); break;
682 case 9: if (!IsWateredTile(TILE_ADDXY(tile, -1, -1), DIR_S)) DrawWaterSprite(base, offset + 11, feature, tile); break;
683 }
684 }
685
687 static void DrawSeaWater(TileIndex tile)
688 {
689 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
690 }
691
693 static void DrawCanalWater(TileIndex tile)
694 {
695 SpriteID image = SPR_FLAT_WATER_TILE;
696 if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
697 /* First water slope sprite is flat water. */
698 image = GetCanalSprite(CF_WATERSLOPE, tile);
699 if (image == 0) image = SPR_FLAT_WATER_TILE;
700 }
701 DrawWaterSprite(image, 0, CF_WATERSLOPE, tile);
702
703 DrawWaterEdges(true, 0, tile);
704 }
705
706 #include "table/water_land.h"
707
717 static void DrawWaterTileStruct(const TileInfo *ti, const DrawTileSeqStruct *dtss, SpriteID base, uint offset, PaletteID palette, CanalFeature feature)
718 {
719 /* Don't draw if buildings are invisible. */
720 if (IsInvisibilitySet(TO_BUILDINGS)) return;
721
722 for (; !dtss->IsTerminator(); dtss++) {
723 uint tile_offs = offset + dtss->image.sprite;
724 if (feature < CF_END) tile_offs = GetCanalSpriteOffset(feature, ti->tile, tile_offs);
725 AddSortableSpriteToDraw(base + tile_offs, palette,
726 ti->x + dtss->delta_x, ti->y + dtss->delta_y,
727 dtss->size_x, dtss->size_y,
728 dtss->size_z, ti->z + dtss->delta_z,
729 IsTransparencySet(TO_BUILDINGS));
730 }
731 }
732
734 static void DrawWaterLock(const TileInfo *ti)
735 {
736 int part = GetLockPart(ti->tile);
737 const DrawTileSprites &dts = _lock_display_data[part][GetLockDirection(ti->tile)];
738
739 /* Draw ground sprite. */
740 SpriteID image = dts.ground.sprite;
741
742 SpriteID water_base = GetCanalSprite(CF_WATERSLOPE, ti->tile);
743 if (water_base == 0) {
744 /* Use default sprites. */
745 water_base = SPR_CANALS_BASE;
746 } else if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
747 /* NewGRF supplies a flat sprite as first sprite. */
748 if (image == SPR_FLAT_WATER_TILE) {
749 image = water_base;
750 } else {
751 image++;
752 }
753 }
754
755 if (image < 5) image += water_base;
756 DrawGroundSprite(image, PAL_NONE);
757
758 /* Draw structures. */
759 uint zoffs = 0;
760 SpriteID base = GetCanalSprite(CF_LOCKS, ti->tile);
761
762 if (base == 0) {
763 /* If no custom graphics, use defaults. */
764 base = SPR_LOCK_BASE;
765 uint8 z_threshold = part == LOCK_PART_UPPER ? 8 : 0;
766 zoffs = ti->z > z_threshold ? 24 : 0;
767 }
768
769 DrawWaterTileStruct(ti, dts.seq, base, zoffs, PAL_NONE, CF_LOCKS);
770 }
771
773 static void DrawWaterDepot(const TileInfo *ti)
774 {
775 DrawWaterClassGround(ti);
776 DrawWaterTileStruct(ti, _shipdepot_display_data[GetShipDepotAxis(ti->tile)][GetShipDepotPart(ti->tile)].seq, 0, 0, COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile)), CF_END);
777 }
778
779 static void DrawRiverWater(const TileInfo *ti)
780 {
781 SpriteID image = SPR_FLAT_WATER_TILE;
782 uint offset = 0;
783 uint edges_offset = 0;
784
785 if (ti->tileh != SLOPE_FLAT || HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
786 image = GetCanalSprite(CF_RIVER_SLOPE, ti->tile);
787 if (image == 0) {
788 switch (ti->tileh) {
789 case SLOPE_NW: image = SPR_WATER_SLOPE_Y_DOWN; break;
790 case SLOPE_SW: image = SPR_WATER_SLOPE_X_UP; break;
791 case SLOPE_SE: image = SPR_WATER_SLOPE_Y_UP; break;
792 case SLOPE_NE: image = SPR_WATER_SLOPE_X_DOWN; break;
793 default: image = SPR_FLAT_WATER_TILE; break;
794 }
795 } else {
796 /* Flag bit 0 indicates that the first sprite is flat water. */
797 offset = HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE) ? 1 : 0;
798
799 switch (ti->tileh) {
800 case SLOPE_SE: edges_offset += 12; break;
801 case SLOPE_NE: offset += 1; edges_offset += 24; break;
802 case SLOPE_SW: offset += 2; edges_offset += 36; break;
803 case SLOPE_NW: offset += 3; edges_offset += 48; break;
804 default: offset = 0; break;
805 }
806
807 offset = GetCanalSpriteOffset(CF_RIVER_SLOPE, ti->tile, offset);
808 }
809 }
810
811 DrawGroundSprite(image + offset, PAL_NONE);
812
813 /* Draw river edges if available. */
814 DrawWaterEdges(false, edges_offset, ti->tile);
815 }
816
817 void DrawShoreTile(Slope tileh)
818 {
819 /* Converts the enum Slope into an offset based on SPR_SHORE_BASE.
820 * This allows to calculate the proper sprite to display for this Slope */
821 static const byte tileh_to_shoresprite[32] = {
822 0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0,
823 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 15, 0,
824 };
825
826 assert(!IsHalftileSlope(tileh)); // Halftile slopes need to get handled earlier.
827 assert(tileh != SLOPE_FLAT); // Shore is never flat
828
829 assert((tileh != SLOPE_EW) && (tileh != SLOPE_NS)); // No suitable sprites for current flooding behaviour
830
831 DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[tileh], PAL_NONE);
832 }
833
834 void DrawWaterClassGround(const TileInfo *ti)
835 {
836 switch (GetWaterClass(ti->tile)) {
837 case WATER_CLASS_SEA: DrawSeaWater(ti->tile); break;
838 case WATER_CLASS_CANAL: DrawCanalWater(ti->tile); break;
839 case WATER_CLASS_RIVER: DrawRiverWater(ti); break;
840 default: NOT_REACHED();
841 }
842 }
843
844 static void DrawTile_Water(TileInfo *ti)
845 {
846 switch (GetWaterTileType(ti->tile)) {
847 case WATER_TILE_CLEAR:
848 DrawWaterClassGround(ti);
849 DrawBridgeMiddle(ti);
850 break;
851
852 case WATER_TILE_COAST: {
853 DrawShoreTile(ti->tileh);
854 DrawBridgeMiddle(ti);
855 break;
856 }
857
858 case WATER_TILE_LOCK:
859 DrawWaterLock(ti);
860 break;
861
862 case WATER_TILE_DEPOT:
863 DrawWaterDepot(ti);
864 break;
865 }
866 }
867
868 void DrawShipDepotSprite(int x, int y, Axis axis, DepotPart part)
869 {
870 const DrawTileSprites &dts = _shipdepot_display_data[axis][part];
871
872 DrawSprite(dts.ground.sprite, dts.ground.pal, x, y);
873 DrawOrigTileSeqInGUI(x, y, &dts, COMPANY_SPRITE_COLOUR(_local_company));
874 }
875
876
877 static int GetSlopePixelZ_Water(TileIndex tile, uint x, uint y)
878 {
879 int z;
880 Slope tileh = GetTilePixelSlope(tile, &z);
881
882 return z + GetPartialPixelZ(x & 0xF, y & 0xF, tileh);
883 }
884
885 static Foundation GetFoundation_Water(TileIndex tile, Slope tileh)
886 {
887 return FOUNDATION_NONE;
888 }
889
890 static void GetTileDesc_Water(TileIndex tile, TileDesc *td)
891 {
892 switch (GetWaterTileType(tile)) {
893 case WATER_TILE_CLEAR:
894 switch (GetWaterClass(tile)) {
895 case WATER_CLASS_SEA: td->str = STR_LAI_WATER_DESCRIPTION_WATER; break;
896 case WATER_CLASS_CANAL: td->str = STR_LAI_WATER_DESCRIPTION_CANAL; break;
897 case WATER_CLASS_RIVER: td->str = STR_LAI_WATER_DESCRIPTION_RIVER; break;
898 default: NOT_REACHED(); break;
899 }
900 break;
901 case WATER_TILE_COAST: td->str = STR_LAI_WATER_DESCRIPTION_COAST_OR_RIVERBANK; break;
902 case WATER_TILE_LOCK : td->str = STR_LAI_WATER_DESCRIPTION_LOCK; break;
903 case WATER_TILE_DEPOT:
904 td->str = STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT;
905 td->build_date = Depot::GetByTile(tile)->build_date;
906 break;
907 default: NOT_REACHED(); break;
908 }
909
910 td->owner[0] = GetTileOwner(tile);
911 }
912
918 static void FloodVehicle(Vehicle *v)
919 {
920 uint pass = v->Crash(true);
921
922 AI::NewEvent(v->owner, new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_FLOODED));
923 Game::NewEvent(new ScriptEventVehicleCrashed(v->index, v->tile, ScriptEventVehicleCrashed::CRASH_FLOODED));
924 SetDParam(0, pass);
925 AddVehicleNewsItem(STR_NEWS_DISASTER_FLOOD_VEHICLE, NT_ACCIDENT, v->index);
926 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
927 if (_settings_client.sound.disaster) SndPlayVehicleFx(SND_12_EXPLOSION, v);
928 }
929
936 static Vehicle *FloodVehicleProc(Vehicle *v, void *data)
937 {
938 if ((v->vehstatus & VS_CRASHED) != 0) return NULL;
939
940 switch (v->type) {
941 default: break;
942
943 case VEH_AIRCRAFT: {
944 if (!IsAirportTile(v->tile) || GetTileMaxZ(v->tile) != 0) break;
945 if (v->subtype == AIR_SHADOW) break;
946
947 /* We compare v->z_pos against delta_z + 1 because the shadow
948 * is at delta_z and the actual aircraft at delta_z + 1. */
949 const Station *st = Station::GetByTile(v->tile);
950 const AirportFTAClass *airport = st->airport.GetFTA();
951 if (v->z_pos != airport->delta_z + 1) break;
952
953 FloodVehicle(v);
954 break;
955 }
956
957 case VEH_TRAIN:
958 case VEH_ROAD: {
959 int z = *(int*)data;
960 if (v->z_pos > z) break;
961 FloodVehicle(v->First());
962 break;
963 }
964 }
965
966 return NULL;
967 }
968
974 static void FloodVehicles(TileIndex tile)
975 {
976 int z = 0;
977
978 if (IsAirportTile(tile)) {
979 const Station *st = Station::GetByTile(tile);
980 TILE_AREA_LOOP(tile, st->airport) {
981 if (st->TileBelongsToAirport(tile)) FindVehicleOnPos(tile, &z, &FloodVehicleProc);
982 }
983
984 /* No vehicle could be flooded on this airport anymore */
985 return;
986 }
987
988 if (!IsBridgeTile(tile)) {
989 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
990 return;
991 }
992
993 TileIndex end = GetOtherBridgeEnd(tile);
994 z = GetBridgePixelHeight(tile);
995
996 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
997 FindVehicleOnPos(end, &z, &FloodVehicleProc);
998 }
999
1005 FloodingBehaviour GetFloodingBehaviour(TileIndex tile)
1006 {
1007 /* FLOOD_ACTIVE: 'single-corner-raised'-coast, sea, sea-shipdepots, sea-buoys, sea-docks (water part), rail with flooded halftile, sea-water-industries, sea-oilrigs
1008 * FLOOD_DRYUP: coast with more than one corner raised, coast with rail-track, coast with trees
1009 * FLOOD_PASSIVE: (not used)
1010 * FLOOD_NONE: canals, rivers, everything else
1011 */
1012 switch (GetTileType(tile)) {
1013 case MP_WATER:
1014 if (IsCoast(tile)) {
1015 Slope tileh = GetTileSlope(tile);
1016 return (IsSlopeWithOneCornerRaised(tileh) ? FLOOD_ACTIVE : FLOOD_DRYUP);
1017 }
1018 /* FALL THROUGH */
1019 case MP_STATION:
1020 case MP_INDUSTRY:
1021 case MP_OBJECT:
1022 return (GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE;
1023
1024 case MP_RAILWAY:
1025 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
1026 return (IsSlopeWithOneCornerRaised(GetTileSlope(tile)) ? FLOOD_ACTIVE : FLOOD_DRYUP);
1027 }
1028 return FLOOD_NONE;
1029
1030 case MP_TREES:
1031 return (GetTreeGround(tile) == TREE_GROUND_SHORE ? FLOOD_DRYUP : FLOOD_NONE);
1032
1033 default:
1034 return FLOOD_NONE;
1035 }
1036 }
1037
1041 void DoFloodTile(TileIndex target)
1042 {
1043 assert(!IsTileType(target, MP_WATER));
1044
1045 bool flooded = false; // Will be set to true if something is changed.
1046
1047 Backup<CompanyByte> cur_company(_current_company, OWNER_WATER, FILE_LINE);
1048
1049 Slope tileh = GetTileSlope(target);
1050 if (tileh != SLOPE_FLAT) {
1051 /* make coast.. */
1052 switch (GetTileType(target)) {
1053 case MP_RAILWAY: {
1054 if (!IsPlainRail(target)) break;
1055 FloodVehicles(target);
1056 flooded = FloodHalftile(target);
1057 break;
1058 }
1059
1060 case MP_TREES:
1061 if (!IsSlopeWithOneCornerRaised(tileh)) {
1062 SetTreeGroundDensity(target, TREE_GROUND_SHORE, 3);
1063 MarkTileDirtyByTile(target);
1064 flooded = true;
1065 break;
1066 }
1067 /* FALL THROUGH */
1068
1069 case MP_CLEAR:
1070 if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
1071 MakeShore(target);
1072 MarkTileDirtyByTile(target);
1073 flooded = true;
1074 }
1075 break;
1076
1077 default:
1078 break;
1079 }
1080 } else {
1081 /* Flood vehicles */
1082 FloodVehicles(target);
1083
1084 /* flood flat tile */
1085 if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
1086 MakeSea(target);
1087 MarkTileDirtyByTile(target);
1088 flooded = true;
1089 }
1090 }
1091
1092 if (flooded) {
1093 /* Mark surrounding canal tiles dirty too to avoid glitches */
1094 MarkCanalsAndRiversAroundDirty(target);
1095
1096 /* update signals if needed */
1097 UpdateSignalsInBuffer();
1098 }
1099
1100 cur_company.Restore();
1101 }
1102
1106 static void DoDryUp(TileIndex tile)
1107 {
1108 Backup<CompanyByte> cur_company(_current_company, OWNER_WATER, FILE_LINE);
1109
1110 switch (GetTileType(tile)) {
1111 case MP_RAILWAY:
1112 assert(IsPlainRail(tile));
1113 assert(GetRailGroundType(tile) == RAIL_GROUND_WATER);
1114
1115 RailGroundType new_ground;
1116 switch (GetTrackBits(tile)) {
1117 case TRACK_BIT_UPPER: new_ground = RAIL_GROUND_FENCE_HORIZ1; break;
1118 case TRACK_BIT_LOWER: new_ground = RAIL_GROUND_FENCE_HORIZ2; break;
1119 case TRACK_BIT_LEFT: new_ground = RAIL_GROUND_FENCE_VERT1; break;
1120 case TRACK_BIT_RIGHT: new_ground = RAIL_GROUND_FENCE_VERT2; break;
1121 default: NOT_REACHED();
1122 }
1123 SetRailGroundType(tile, new_ground);
1124 MarkTileDirtyByTile(tile);
1125 break;
1126
1127 case MP_TREES:
1128 SetTreeGroundDensity(tile, TREE_GROUND_GRASS, 3);
1129 MarkTileDirtyByTile(tile);
1130 break;
1131
1132 case MP_WATER:
1133 assert(IsCoast(tile));
1134
1135 if (DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
1136 MakeClear(tile, CLEAR_GRASS, 3);
1137 MarkTileDirtyByTile(tile);
1138 }
1139 break;
1140
1141 default: NOT_REACHED();
1142 }
1143
1144 cur_company.Restore();
1145 }
1146
1153 void TileLoop_Water(TileIndex tile)
1154 {
1155 if (IsTileType(tile, MP_WATER)) AmbientSoundEffect(tile);
1156
1157 switch (GetFloodingBehaviour(tile)) {
1158 case FLOOD_ACTIVE:
1159 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
1160 TileIndex dest = tile + TileOffsByDir(dir);
1161 if (!IsValidTile(dest)) continue;
1162 /* do not try to flood water tiles - increases performance a lot */
1163 if (IsTileType(dest, MP_WATER)) continue;
1164
1165 /* TREE_GROUND_SHORE is the sign of a previous flood. */
1166 if (IsTileType(dest, MP_TREES) && GetTreeGround(dest) == TREE_GROUND_SHORE) continue;
1167
1168 int z_dest;
1169 Slope slope_dest = GetFoundationSlope(dest, &z_dest) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
1170 if (z_dest > 0) continue;
1171
1172 if (!HasBit(_flood_from_dirs[slope_dest], ReverseDir(dir))) continue;
1173
1174 DoFloodTile(dest);
1175 }
1176 break;
1177
1178 case FLOOD_DRYUP: {
1179 Slope slope_here = GetFoundationSlope(tile) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
1180 uint dir;
1181 FOR_EACH_SET_BIT(dir, _flood_from_dirs[slope_here]) {
1182 TileIndex dest = tile + TileOffsByDir((Direction)dir);
1183 if (!IsValidTile(dest)) continue;
1184
1185 FloodingBehaviour dest_behaviour = GetFloodingBehaviour(dest);
1186 if ((dest_behaviour == FLOOD_ACTIVE) || (dest_behaviour == FLOOD_PASSIVE)) return;
1187 }
1188 DoDryUp(tile);
1189 break;
1190 }
1191
1192 default: return;
1193 }
1194 }
1195
1196 void ConvertGroundTilesIntoWaterTiles()
1197 {
1198 int z;
1199
1200 for (TileIndex tile = 0; tile < MapSize(); ++tile) {
1201 Slope slope = GetTileSlope(tile, &z);
1202 if (IsTileType(tile, MP_CLEAR) && z == 0) {
1203 /* Make both water for tiles at level 0
1204 * and make shore, as that looks much better
1205 * during the generation. */
1206 switch (slope) {
1207 case SLOPE_FLAT:
1208 MakeSea(tile);
1209 break;
1210
1211 case SLOPE_N:
1212 case SLOPE_E:
1213 case SLOPE_S:
1214 case SLOPE_W:
1215 MakeShore(tile);
1216 break;
1217
1218 default:
1219 uint dir;
1220 FOR_EACH_SET_BIT(dir, _flood_from_dirs[slope & ~SLOPE_STEEP]) {
1221 TileIndex dest = TILE_ADD(tile, TileOffsByDir((Direction)dir));
1222 Slope slope_dest = GetTileSlope(dest) & ~SLOPE_STEEP;
1223 if (slope_dest == SLOPE_FLAT || IsSlopeWithOneCornerRaised(slope_dest)) {
1224 MakeShore(tile);
1225 break;
1226 }
1227 }
1228 break;
1229 }
1230 }
1231 }
1232 }
1233
1234 static TrackStatus GetTileTrackStatus_Water(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
1235 {
1236 static const byte coast_tracks[] = {0, 32, 4, 0, 16, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0};
1237
1238 TrackBits ts;
1239
1240 if (mode != TRANSPORT_WATER) return 0;
1241
1242 switch (GetWaterTileType(tile)) {
1243 case WATER_TILE_CLEAR: ts = IsTileFlat(tile) ? TRACK_BIT_ALL : TRACK_BIT_NONE; break;
1244 case WATER_TILE_COAST: ts = (TrackBits)coast_tracks[GetTileSlope(tile) & 0xF]; break;
1245 case WATER_TILE_LOCK: ts = DiagDirToDiagTrackBits(GetLockDirection(tile)); break;
1246 case WATER_TILE_DEPOT: ts = AxisToTrackBits(GetShipDepotAxis(tile)); break;
1247 default: return 0;
1248 }
1249 if (TileX(tile) == 0) {
1250 /* NE border: remove tracks that connects NE tile edge */
1251 ts &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
1252 }
1253 if (TileY(tile) == 0) {
1254 /* NW border: remove tracks that connects NW tile edge */
1255 ts &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
1256 }
1257 return CombineTrackStatus(TrackBitsToTrackdirBits(ts), TRACKDIR_BIT_NONE);
1258 }
1259
1260 static bool ClickTile_Water(TileIndex tile)
1261 {
1262 if (GetWaterTileType(tile) == WATER_TILE_DEPOT) {
1263 ShowDepotWindow(GetShipDepotNorthTile(tile), VEH_SHIP);
1264 return true;
1265 }
1266 return false;
1267 }
1268
1269 static void ChangeTileOwner_Water(TileIndex tile, Owner old_owner, Owner new_owner)
1270 {
1271 if (!IsTileOwner(tile, old_owner)) return;
1272
1273 bool is_lock_middle = IsLock(tile) && GetLockPart(tile) == LOCK_PART_MIDDLE;
1274
1275 /* No need to dirty company windows here, we'll redraw the whole screen anyway. */
1276 if (is_lock_middle) Company::Get(old_owner)->infrastructure.water -= 3 * LOCK_DEPOT_TILE_FACTOR; // Lock has three parts.
1277 if (new_owner != INVALID_OWNER) {
1278 if (is_lock_middle) Company::Get(new_owner)->infrastructure.water += 3 * LOCK_DEPOT_TILE_FACTOR; // Lock has three parts.
1279 /* Only subtract from the old owner here if the new owner is valid,
1280 * otherwise we clear ship depots and canal water below. */
1281 if (GetWaterClass(tile) == WATER_CLASS_CANAL && !is_lock_middle) {
1282 Company::Get(old_owner)->infrastructure.water--;
1283 Company::Get(new_owner)->infrastructure.water++;
1284 }
1285 if (IsShipDepot(tile)) {
1286 Company::Get(old_owner)->infrastructure.water -= LOCK_DEPOT_TILE_FACTOR;
1287 Company::Get(new_owner)->infrastructure.water += LOCK_DEPOT_TILE_FACTOR;
1288 }
1289
1290 SetTileOwner(tile, new_owner);
1291 return;
1292 }
1293
1294 /* Remove depot */
1295 if (IsShipDepot(tile)) DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
1296
1297 /* Set owner of canals and locks ... and also canal under dock there was before.
1298 * Check if the new owner after removing depot isn't OWNER_WATER. */
1299 if (IsTileOwner(tile, old_owner)) {
1300 if (GetWaterClass(tile) == WATER_CLASS_CANAL && !is_lock_middle) Company::Get(old_owner)->infrastructure.water--;
1301 SetTileOwner(tile, OWNER_NONE);
1302 }
1303 }
1304
1305 static VehicleEnterTileStatus VehicleEnter_Water(Vehicle *v, TileIndex tile, int x, int y)
1306 {
1307 return VETSB_CONTINUE;
1308 }
1309
1310 static CommandCost TerraformTile_Water(TileIndex tile, DoCommandFlag flags, int z_new, Slope tileh_new)
1311 {
1312 /* Canals can't be terraformed */
1313 if (IsWaterTile(tile) && IsCanal(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_CANAL_FIRST);
1314
1315 return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
1316 }
1317
1318
1319 extern const TileTypeProcs _tile_type_water_procs = {
1320 DrawTile_Water, // draw_tile_proc
1321 GetSlopePixelZ_Water, // get_slope_z_proc
1322 ClearTile_Water, // clear_tile_proc
1323 NULL, // add_accepted_cargo_proc
1324 GetTileDesc_Water, // get_tile_desc_proc
1325 GetTileTrackStatus_Water, // get_tile_track_status_proc
1326 ClickTile_Water, // click_tile_proc
1327 NULL, // animate_tile_proc
1328 TileLoop_Water, // tile_loop_proc
1329 ChangeTileOwner_Water, // change_tile_owner_proc
1330 NULL, // add_produced_cargo_proc
1331 VehicleEnter_Water, // vehicle_enter_tile_proc
1332 GetFoundation_Water, // get_foundation_proc
1333 TerraformTile_Water, // terraform_tile_proc
1334 };