water_cmd.cpp

Go to the documentation of this file.
00001 /* $Id: water_cmd.cpp 23403 2011-12-03 21:31:44Z rubidium $ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * 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.
00006  * 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.
00007  * 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/>.
00008  */
00009 
00012 #include "stdafx.h"
00013 #include "cmd_helper.h"
00014 #include "landscape.h"
00015 #include "viewport_func.h"
00016 #include "command_func.h"
00017 #include "town.h"
00018 #include "news_func.h"
00019 #include "depot_base.h"
00020 #include "depot_func.h"
00021 #include "water.h"
00022 #include "industry_map.h"
00023 #include "newgrf_canal.h"
00024 #include "strings_func.h"
00025 #include "vehicle_func.h"
00026 #include "sound_func.h"
00027 #include "company_func.h"
00028 #include "clear_map.h"
00029 #include "tree_map.h"
00030 #include "aircraft.h"
00031 #include "effectvehicle_func.h"
00032 #include "tunnelbridge_map.h"
00033 #include "station_base.h"
00034 #include "ai/ai.hpp"
00035 #include "core/random_func.hpp"
00036 #include "core/backup_type.hpp"
00037 #include "date_func.h"
00038 
00039 #include "table/strings.h"
00040 
00044 static const uint8 _flood_from_dirs[] = {
00045   (1 << DIR_NW) | (1 << DIR_SW) | (1 << DIR_SE) | (1 << DIR_NE), // SLOPE_FLAT
00046   (1 << DIR_NE) | (1 << DIR_SE),                                 // SLOPE_W
00047   (1 << DIR_NW) | (1 << DIR_NE),                                 // SLOPE_S
00048   (1 << DIR_NE),                                                 // SLOPE_SW
00049   (1 << DIR_NW) | (1 << DIR_SW),                                 // SLOPE_E
00050   0,                                                             // SLOPE_EW
00051   (1 << DIR_NW),                                                 // SLOPE_SE
00052   (1 << DIR_N ) | (1 << DIR_NW) | (1 << DIR_NE),                 // SLOPE_WSE, SLOPE_STEEP_S
00053   (1 << DIR_SW) | (1 << DIR_SE),                                 // SLOPE_N
00054   (1 << DIR_SE),                                                 // SLOPE_NW
00055   0,                                                             // SLOPE_NS
00056   (1 << DIR_E ) | (1 << DIR_NE) | (1 << DIR_SE),                 // SLOPE_NWS, SLOPE_STEEP_W
00057   (1 << DIR_SW),                                                 // SLOPE_NE
00058   (1 << DIR_S ) | (1 << DIR_SW) | (1 << DIR_SE),                 // SLOPE_ENW, SLOPE_STEEP_N
00059   (1 << DIR_W ) | (1 << DIR_SW) | (1 << DIR_NW),                 // SLOPE_SEN, SLOPE_STEEP_E
00060 };
00061 
00068 static inline void MarkTileDirtyIfCanalOrRiver(TileIndex tile)
00069 {
00070   if (IsTileType(tile, MP_WATER) && (IsCanal(tile) || IsRiver(tile))) MarkTileDirtyByTile(tile);
00071 }
00072 
00079 static void MarkCanalsAndRiversAroundDirty(TileIndex tile)
00080 {
00081   for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
00082     MarkTileDirtyIfCanalOrRiver(tile + TileOffsByDir(dir));
00083   }
00084 }
00085 
00086 
00096 CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00097 {
00098   Axis axis = Extract<Axis, 0, 1>(p1);
00099 
00100   TileIndex tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
00101 
00102   if (!HasTileWaterGround(tile) || !HasTileWaterGround(tile2)) {
00103     return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER);
00104   }
00105 
00106   if ((MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) ||
00107     (MayHaveBridgeAbove(tile2) && IsBridgeAbove(tile2))) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
00108 
00109   if (GetTileSlope(tile, NULL) != SLOPE_FLAT || GetTileSlope(tile2, NULL) != SLOPE_FLAT) {
00110     /* Prevent depots on rapids */
00111     return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
00112   }
00113 
00114   if (!Depot::CanAllocateItem()) return CMD_ERROR;
00115 
00116   WaterClass wc1 = GetWaterClass(tile);
00117   WaterClass wc2 = GetWaterClass(tile2);
00118   CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_DEPOT_SHIP]);
00119 
00120   bool add_cost = !IsWaterTile(tile);
00121   CommandCost ret = DoCommand(tile, 0, 0, flags | DC_AUTO, CMD_LANDSCAPE_CLEAR);
00122   if (ret.Failed()) return ret;
00123   if (add_cost) {
00124     cost.AddCost(ret);
00125   }
00126   add_cost = !IsWaterTile(tile2);
00127   ret = DoCommand(tile2, 0, 0, flags | DC_AUTO, CMD_LANDSCAPE_CLEAR);
00128   if (ret.Failed()) return ret;
00129   if (add_cost) {
00130     cost.AddCost(ret);
00131   }
00132 
00133   if (flags & DC_EXEC) {
00134     Depot *depot = new Depot(tile);
00135     depot->build_date = _date;
00136 
00137     MakeShipDepot(tile,  _current_company, depot->index, DEPOT_NORTH, axis, wc1);
00138     MakeShipDepot(tile2, _current_company, depot->index, DEPOT_SOUTH, axis, wc2);
00139     MarkTileDirtyByTile(tile);
00140     MarkTileDirtyByTile(tile2);
00141     MakeDefaultName(depot);
00142   }
00143 
00144   return cost;
00145 }
00146 
00147 void MakeWaterKeepingClass(TileIndex tile, Owner o)
00148 {
00149   WaterClass wc = GetWaterClass(tile);
00150 
00151   /* Autoslope might turn an originally canal or river tile into land */
00152   uint z;
00153   if (GetTileSlope(tile, &z) != SLOPE_FLAT) wc = WATER_CLASS_INVALID;
00154 
00155   if (wc == WATER_CLASS_SEA && z > 0) wc = WATER_CLASS_CANAL;
00156 
00157   /* Zero map array and terminate animation */
00158   DoClearSquare(tile);
00159 
00160   /* Maybe change to water */
00161   switch (wc) {
00162     case WATER_CLASS_SEA:   MakeSea(tile);                break;
00163     case WATER_CLASS_CANAL: MakeCanal(tile, o, Random()); break;
00164     case WATER_CLASS_RIVER: MakeRiver(tile, Random());    break;
00165     default: break;
00166   }
00167 
00168   MarkTileDirtyByTile(tile);
00169 }
00170 
00171 static CommandCost RemoveShipDepot(TileIndex tile, DoCommandFlag flags)
00172 {
00173   if (!IsShipDepot(tile)) return CMD_ERROR;
00174 
00175   CommandCost ret = CheckTileOwnership(tile);
00176   if (ret.Failed()) return ret;
00177 
00178   TileIndex tile2 = GetOtherShipDepotTile(tile);
00179 
00180   /* do not check for ship on tile when company goes bankrupt */
00181   if (!(flags & DC_BANKRUPT)) {
00182     CommandCost ret = EnsureNoVehicleOnGround(tile);
00183     if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile2);
00184     if (ret.Failed()) return ret;
00185   }
00186 
00187   if (flags & DC_EXEC) {
00188     delete Depot::GetByTile(tile);
00189 
00190     MakeWaterKeepingClass(tile,  GetTileOwner(tile));
00191     MakeWaterKeepingClass(tile2, GetTileOwner(tile2));
00192   }
00193 
00194   return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_SHIP]);
00195 }
00196 
00204 static CommandCost DoBuildLock(TileIndex tile, DiagDirection dir, DoCommandFlag flags)
00205 {
00206   CommandCost cost(EXPENSES_CONSTRUCTION);
00207 
00208   int delta = TileOffsByDiagDir(dir);
00209   CommandCost ret = EnsureNoVehicleOnGround(tile);
00210   if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta);
00211   if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta);
00212   if (ret.Failed()) return ret;
00213 
00214   /* middle tile */
00215   ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00216   if (ret.Failed()) return ret;
00217   cost.AddCost(ret);
00218 
00219   /* lower tile */
00220   WaterClass wc_lower = IsWaterTile(tile - delta) ? GetWaterClass(tile - delta) : WATER_CLASS_CANAL;
00221 
00222   if (!IsWaterTile(tile - delta)) {
00223     ret = DoCommand(tile - delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00224     if (ret.Failed()) return ret;
00225     cost.AddCost(ret);
00226     cost.AddCost(_price[PR_BUILD_CANAL]);
00227   }
00228   if (GetTileSlope(tile - delta, NULL) != SLOPE_FLAT) {
00229     return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
00230   }
00231 
00232   /* upper tile */
00233   WaterClass wc_upper = IsWaterTile(tile + delta) ? GetWaterClass(tile + delta) : WATER_CLASS_CANAL;
00234 
00235   if (!IsWaterTile(tile + delta)) {
00236     ret = DoCommand(tile + delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00237     if (ret.Failed()) return ret;
00238     cost.AddCost(ret);
00239     cost.AddCost(_price[PR_BUILD_CANAL]);
00240   }
00241   if (GetTileSlope(tile + delta, NULL) != SLOPE_FLAT) {
00242     return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
00243   }
00244 
00245   if ((MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) ||
00246       (MayHaveBridgeAbove(tile - delta) && IsBridgeAbove(tile - delta)) ||
00247       (MayHaveBridgeAbove(tile + delta) && IsBridgeAbove(tile + delta))) {
00248     return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
00249   }
00250 
00251   if (flags & DC_EXEC) {
00252     MakeLock(tile, _current_company, dir, wc_lower, wc_upper);
00253     MarkTileDirtyByTile(tile);
00254     MarkTileDirtyByTile(tile - delta);
00255     MarkTileDirtyByTile(tile + delta);
00256     MarkCanalsAndRiversAroundDirty(tile - delta);
00257     MarkCanalsAndRiversAroundDirty(tile + delta);
00258   }
00259   cost.AddCost(_price[PR_BUILD_LOCK]);
00260 
00261   return cost;
00262 }
00263 
00270 static CommandCost RemoveLock(TileIndex tile, DoCommandFlag flags)
00271 {
00272   if (GetTileOwner(tile) != OWNER_NONE) {
00273     CommandCost ret = CheckTileOwnership(tile);
00274     if (ret.Failed()) return ret;
00275   }
00276 
00277   TileIndexDiff delta = TileOffsByDiagDir(GetLockDirection(tile));
00278 
00279   /* make sure no vehicle is on the tile. */
00280   CommandCost ret = EnsureNoVehicleOnGround(tile);
00281   if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta);
00282   if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta);
00283   if (ret.Failed()) return ret;
00284 
00285   if (flags & DC_EXEC) {
00286     DoClearSquare(tile);
00287     MakeWaterKeepingClass(tile + delta, GetTileOwner(tile + delta));
00288     MakeWaterKeepingClass(tile - delta, GetTileOwner(tile - delta));
00289     MarkCanalsAndRiversAroundDirty(tile - delta);
00290     MarkCanalsAndRiversAroundDirty(tile + delta);
00291   }
00292 
00293   return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_LOCK]);
00294 }
00295 
00305 CommandCost CmdBuildLock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00306 {
00307   DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile, NULL));
00308   if (dir == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
00309 
00310   /* Disallow building of locks on river rapids */
00311   if (IsWaterTile(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
00312 
00313   return DoBuildLock(tile, dir, flags);
00314 }
00315 
00325 CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00326 {
00327   WaterClass wc = Extract<WaterClass, 0, 2>(p2);
00328   if (p1 >= MapSize() || wc == WATER_CLASS_INVALID) return CMD_ERROR;
00329 
00330   /* Outside of the editor you can only build canals, not oceans */
00331   if (wc != WATER_CLASS_CANAL && _game_mode != GM_EDITOR) return CMD_ERROR;
00332 
00333   TileArea ta(tile, p1);
00334 
00335   /* Outside the editor you can only drag canals, and not areas */
00336   if (_game_mode != GM_EDITOR && ta.w != 1 && ta.h != 1) return CMD_ERROR;
00337 
00338   CommandCost cost(EXPENSES_CONSTRUCTION);
00339   TILE_AREA_LOOP(tile, ta) {
00340     CommandCost ret;
00341 
00342     Slope slope = GetTileSlope(tile, NULL);
00343     if (slope != SLOPE_FLAT && (wc != WATER_CLASS_RIVER || !IsInclinedSlope(slope))) {
00344       return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
00345     }
00346 
00347     /* can't make water of water! */
00348     if (IsTileType(tile, MP_WATER) && (!IsTileOwner(tile, OWNER_WATER) || wc == WATER_CLASS_SEA)) continue;
00349 
00350     ret = DoCommand(tile, 0, 0, flags | DC_FORCE_CLEAR_TILE, CMD_LANDSCAPE_CLEAR);
00351     if (ret.Failed()) return ret;
00352     cost.AddCost(ret);
00353 
00354     if (flags & DC_EXEC) {
00355       switch (wc) {
00356         case WATER_CLASS_RIVER:
00357           MakeRiver(tile, Random());
00358           break;
00359 
00360         case WATER_CLASS_SEA:
00361           if (TileHeight(tile) == 0) {
00362             MakeSea(tile);
00363             break;
00364           }
00365           /* FALL THROUGH */
00366 
00367         default:
00368           MakeCanal(tile, _current_company, Random());
00369           break;
00370       }
00371       MarkTileDirtyByTile(tile);
00372       MarkCanalsAndRiversAroundDirty(tile);
00373     }
00374 
00375     cost.AddCost(_price[PR_BUILD_CANAL]);
00376   }
00377 
00378   if (cost.GetCost() == 0) {
00379     return_cmd_error(STR_ERROR_ALREADY_BUILT);
00380   } else {
00381     return cost;
00382   }
00383 }
00384 
00385 static CommandCost ClearTile_Water(TileIndex tile, DoCommandFlag flags)
00386 {
00387   switch (GetWaterTileType(tile)) {
00388     case WATER_TILE_CLEAR: {
00389       if (flags & DC_NO_WATER) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER);
00390 
00391       Money base_cost = IsCanal(tile) ? _price[PR_CLEAR_CANAL] : _price[PR_CLEAR_WATER];
00392       /* Make sure freeform edges are allowed or it's not an edge tile. */
00393       if (!_settings_game.construction.freeform_edges && (!IsInsideMM(TileX(tile), 1, MapMaxX() - 1) ||
00394           !IsInsideMM(TileY(tile), 1, MapMaxY() - 1))) {
00395         return_cmd_error(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP);
00396       }
00397 
00398       /* Make sure no vehicle is on the tile */
00399       CommandCost ret = EnsureNoVehicleOnGround(tile);
00400       if (ret.Failed()) return ret;
00401 
00402       if (GetTileOwner(tile) != OWNER_WATER && GetTileOwner(tile) != OWNER_NONE) {
00403         CommandCost ret = CheckTileOwnership(tile);
00404         if (ret.Failed()) return ret;
00405       }
00406 
00407       if (flags & DC_EXEC) {
00408         DoClearSquare(tile);
00409         MarkCanalsAndRiversAroundDirty(tile);
00410       }
00411 
00412       return CommandCost(EXPENSES_CONSTRUCTION, base_cost);
00413     }
00414 
00415     case WATER_TILE_COAST: {
00416       Slope slope = GetTileSlope(tile, NULL);
00417 
00418       /* Make sure no vehicle is on the tile */
00419       CommandCost ret = EnsureNoVehicleOnGround(tile);
00420       if (ret.Failed()) return ret;
00421 
00422       if (flags & DC_EXEC) {
00423         DoClearSquare(tile);
00424         MarkCanalsAndRiversAroundDirty(tile);
00425       }
00426       if (IsSlopeWithOneCornerRaised(slope)) {
00427         return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_WATER]);
00428       } else {
00429         return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_ROUGH]);
00430       }
00431     }
00432 
00433     case WATER_TILE_LOCK: {
00434       static const TileIndexDiffC _lock_tomiddle_offs[] = {
00435         { 0,  0}, {0,  0}, { 0, 0}, {0,  0}, // middle
00436         {-1,  0}, {0,  1}, { 1, 0}, {0, -1}, // lower
00437         { 1,  0}, {0, -1}, {-1, 0}, {0,  1}, // upper
00438       };
00439 
00440       if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
00441       if (_current_company == OWNER_WATER) return CMD_ERROR;
00442       /* move to the middle tile.. */
00443       return RemoveLock(tile + ToTileIndexDiff(_lock_tomiddle_offs[GetSection(tile)]), flags);
00444     }
00445 
00446     case WATER_TILE_DEPOT:
00447       if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
00448       return RemoveShipDepot(tile, flags);
00449 
00450     default:
00451       NOT_REACHED();
00452   }
00453 }
00454 
00463 static bool IsWateredTile(TileIndex tile, Direction from)
00464 {
00465   switch (GetTileType(tile)) {
00466     case MP_WATER:
00467       switch (GetWaterTileType(tile)) {
00468         default: NOT_REACHED();
00469         case WATER_TILE_DEPOT: case WATER_TILE_CLEAR: return true;
00470         case WATER_TILE_LOCK: return DiagDirToAxis(GetLockDirection(tile)) == DiagDirToAxis(DirToDiagDir(from));
00471 
00472         case WATER_TILE_COAST:
00473           switch (GetTileSlope(tile, NULL)) {
00474             case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
00475             case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
00476             case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
00477             case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
00478             default: return false;
00479           }
00480       }
00481 
00482     case MP_RAILWAY:
00483       if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
00484         assert(IsPlainRail(tile));
00485         switch (GetTileSlope(tile, NULL)) {
00486           case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
00487           case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
00488           case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
00489           case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
00490           default: return false;
00491         }
00492       }
00493       return false;
00494 
00495     case MP_STATION:
00496       if (IsOilRig(tile)) {
00497         /* Do not draw waterborders inside of industries.
00498          * Note: There is no easy way to detect the industry of an oilrig tile. */
00499         TileIndex src_tile = tile + TileOffsByDir(from);
00500         if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
00501             (IsTileType(src_tile, MP_INDUSTRY))) return true;
00502 
00503         return IsTileOnWater(tile);
00504       }
00505       return (IsDock(tile) && GetTileSlope(tile, NULL) == SLOPE_FLAT) || IsBuoy(tile);
00506 
00507     case MP_INDUSTRY: {
00508       /* Do not draw waterborders inside of industries.
00509        * Note: There is no easy way to detect the industry of an oilrig tile. */
00510       TileIndex src_tile = tile + TileOffsByDir(from);
00511       if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
00512           (IsTileType(src_tile, MP_INDUSTRY) && GetIndustryIndex(src_tile) == GetIndustryIndex(tile))) return true;
00513 
00514       return IsTileOnWater(tile);
00515     }
00516 
00517     case MP_OBJECT: return IsTileOnWater(tile);
00518 
00519     case MP_TUNNELBRIDGE: return GetTunnelBridgeTransportType(tile) == TRANSPORT_WATER && ReverseDiagDir(GetTunnelBridgeDirection(tile)) == DirToDiagDir(from);
00520 
00521     default:          return false;
00522   }
00523 }
00524 
00532 static void DrawWaterSprite(SpriteID base, uint offset, CanalFeature feature, TileIndex tile)
00533 {
00534   if (base != SPR_FLAT_WATER_TILE) {
00535     /* Only call offset callback if the sprite is NewGRF-provided. */
00536     offset = GetCanalSpriteOffset(feature, tile, offset);
00537   }
00538   DrawGroundSprite(base + offset, PAL_NONE);
00539 }
00540 
00547 static void DrawWaterEdges(bool canal, uint offset, TileIndex tile)
00548 {
00549   CanalFeature feature;
00550   SpriteID base = 0;
00551   if (canal) {
00552     feature = CF_DIKES;
00553     base = GetCanalSprite(CF_DIKES, tile);
00554     if (base == 0) base = SPR_CANAL_DIKES_BASE;
00555   } else {
00556     feature = CF_RIVER_EDGE;
00557     base = GetCanalSprite(CF_RIVER_EDGE, tile);
00558     if (base == 0) return; // Don't draw if no sprites provided.
00559   }
00560 
00561   uint wa;
00562 
00563   /* determine the edges around with water. */
00564   wa  = IsWateredTile(TILE_ADDXY(tile, -1,  0), DIR_SW) << 0;
00565   wa += IsWateredTile(TILE_ADDXY(tile,  0,  1), DIR_NW) << 1;
00566   wa += IsWateredTile(TILE_ADDXY(tile,  1,  0), DIR_NE) << 2;
00567   wa += IsWateredTile(TILE_ADDXY(tile,  0, -1), DIR_SE) << 3;
00568 
00569   if (!(wa & 1)) DrawWaterSprite(base, offset,     feature, tile);
00570   if (!(wa & 2)) DrawWaterSprite(base, offset + 1, feature, tile);
00571   if (!(wa & 4)) DrawWaterSprite(base, offset + 2, feature, tile);
00572   if (!(wa & 8)) DrawWaterSprite(base, offset + 3, feature, tile);
00573 
00574   /* right corner */
00575   switch (wa & 0x03) {
00576     case 0: DrawWaterSprite(base, offset + 4, feature, tile); break;
00577     case 3: if (!IsWateredTile(TILE_ADDXY(tile, -1, 1), DIR_W)) DrawWaterSprite(base, offset + 8, feature, tile); break;
00578   }
00579 
00580   /* bottom corner */
00581   switch (wa & 0x06) {
00582     case 0: DrawWaterSprite(base, offset + 5, feature, tile); break;
00583     case 6: if (!IsWateredTile(TILE_ADDXY(tile, 1, 1), DIR_N)) DrawWaterSprite(base, offset + 9, feature, tile); break;
00584   }
00585 
00586   /* left corner */
00587   switch (wa & 0x0C) {
00588     case  0: DrawWaterSprite(base, offset + 6, feature, tile); break;
00589     case 12: if (!IsWateredTile(TILE_ADDXY(tile, 1, -1), DIR_E)) DrawWaterSprite(base, offset + 10, feature, tile); break;
00590   }
00591 
00592   /* upper corner */
00593   switch (wa & 0x09) {
00594     case 0: DrawWaterSprite(base, offset + 7, feature, tile); break;
00595     case 9: if (!IsWateredTile(TILE_ADDXY(tile, -1, -1), DIR_S)) DrawWaterSprite(base, offset + 11, feature, tile); break;
00596   }
00597 }
00598 
00600 static void DrawSeaWater(TileIndex tile)
00601 {
00602   DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
00603 }
00604 
00606 static void DrawCanalWater(TileIndex tile)
00607 {
00608   SpriteID image = SPR_FLAT_WATER_TILE;
00609   if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
00610     /* First water slope sprite is flat water. */
00611     image = GetCanalSprite(CF_WATERSLOPE, tile);
00612     if (image == 0) image = SPR_FLAT_WATER_TILE;
00613   }
00614   DrawWaterSprite(image, 0, CF_WATERSLOPE, tile);
00615 
00616   DrawWaterEdges(true, 0, tile);
00617 }
00618 
00619 struct LocksDrawTileStruct {
00620   int8 delta_x, delta_y, delta_z;
00621   byte width, height, depth;
00622   SpriteID image;
00623 };
00624 
00625 #include "table/water_land.h"
00626 
00636 static void DrawWaterTileStruct(const TileInfo *ti, const WaterDrawTileStruct *wdts, SpriteID base, uint offset, PaletteID palette, CanalFeature feature)
00637 {
00638   /* Don't draw if buildings are invisible. */
00639   if (IsInvisibilitySet(TO_BUILDINGS)) return;
00640 
00641   for (; wdts->delta_x != 0x80; wdts++) {
00642     uint tile_offs = offset + wdts->image;
00643     if (feature < CF_END) tile_offs = GetCanalSpriteOffset(feature, ti->tile, tile_offs);
00644     AddSortableSpriteToDraw(base + tile_offs, palette,
00645       ti->x + wdts->delta_x, ti->y + wdts->delta_y,
00646       wdts->size_x, wdts->size_y,
00647       wdts->size_z, ti->z + wdts->delta_z,
00648       IsTransparencySet(TO_BUILDINGS));
00649   }
00650 }
00651 
00653 static void DrawWaterLock(const TileInfo *ti)
00654 {
00655   int section = GetSection(ti->tile);
00656   const WaterDrawTileStruct *wdts = _lock_display_seq[section];
00657 
00658   /* Draw ground sprite. */
00659   SpriteID image = wdts++->image;
00660 
00661   SpriteID water_base = GetCanalSprite(CF_WATERSLOPE, ti->tile);
00662   if (water_base == 0) {
00663     /* Use default sprites. */
00664     water_base = SPR_CANALS_BASE;
00665   } else if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
00666     /* NewGRF supplies a flat sprite as first sprite. */
00667     if (image == SPR_FLAT_WATER_TILE) {
00668       image = water_base;
00669     } else {
00670       image++;
00671     }
00672   }
00673 
00674   if (image < 5) image += water_base;
00675   DrawGroundSprite(image, PAL_NONE);
00676 
00677   /* Draw structures. */
00678   uint     zoffs = 0;
00679   SpriteID base  = GetCanalSprite(CF_LOCKS, ti->tile);
00680 
00681   if (base == 0) {
00682     /* If no custom graphics, use defaults. */
00683     base = SPR_LOCK_BASE;
00684     uint8 z_threshold = section >= 8 ? 8 : 0;
00685     zoffs = ti->z > z_threshold ? 24 : 0;
00686   }
00687 
00688   DrawWaterTileStruct(ti, wdts, base, zoffs, PAL_NONE, CF_LOCKS);
00689 }
00690 
00692 static void DrawWaterDepot(const TileInfo *ti)
00693 {
00694   DrawWaterClassGround(ti);
00695   /* Skip first entry in _shipdepot_display_seq as this is the ground sprite. */
00696   DrawWaterTileStruct(ti, _shipdepot_display_seq[GetSection(ti->tile)] + 1, 0, 0, COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile)), CF_END);
00697 }
00698 
00699 static void DrawRiverWater(const TileInfo *ti)
00700 {
00701   SpriteID image = SPR_FLAT_WATER_TILE;
00702   uint     offset = 0;
00703   uint     edges_offset = 0;
00704 
00705   if (ti->tileh != SLOPE_FLAT || HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
00706     image = GetCanalSprite(CF_RIVER_SLOPE, ti->tile);
00707     if (image == 0) {
00708       switch (ti->tileh) {
00709         case SLOPE_NW: image = SPR_WATER_SLOPE_Y_DOWN; break;
00710         case SLOPE_SW: image = SPR_WATER_SLOPE_X_UP;   break;
00711         case SLOPE_SE: image = SPR_WATER_SLOPE_Y_UP;   break;
00712         case SLOPE_NE: image = SPR_WATER_SLOPE_X_DOWN; break;
00713         default:       image = SPR_FLAT_WATER_TILE;    break;
00714       }
00715     } else {
00716       /* Flag bit 0 indicates that the first sprite is flat water. */
00717       offset = HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE) ? 1 : 0;
00718 
00719       switch (ti->tileh) {
00720         case SLOPE_SE:              edges_offset += 12; break;
00721         case SLOPE_NE: offset += 1; edges_offset += 24; break;
00722         case SLOPE_SW: offset += 2; edges_offset += 36; break;
00723         case SLOPE_NW: offset += 3; edges_offset += 48; break;
00724         default:       offset  = 0; break;
00725       }
00726 
00727       offset = GetCanalSpriteOffset(CF_RIVER_SLOPE, ti->tile, offset);
00728     }
00729   }
00730 
00731   DrawGroundSprite(image + offset, PAL_NONE);
00732 
00733   /* Draw river edges if available. */
00734   DrawWaterEdges(false, edges_offset, ti->tile);
00735 }
00736 
00737 void DrawShoreTile(Slope tileh)
00738 {
00739   /* Converts the enum Slope into an offset based on SPR_SHORE_BASE.
00740    * This allows to calculate the proper sprite to display for this Slope */
00741   static const byte tileh_to_shoresprite[32] = {
00742     0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0,
00743     0, 0, 0, 0, 0,  0, 0, 0, 0, 0,  0,  5,  0, 10, 15, 0,
00744   };
00745 
00746   assert(!IsHalftileSlope(tileh)); // Halftile slopes need to get handled earlier.
00747   assert(tileh != SLOPE_FLAT);     // Shore is never flat
00748 
00749   assert((tileh != SLOPE_EW) && (tileh != SLOPE_NS)); // No suitable sprites for current flooding behaviour
00750 
00751   DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[tileh], PAL_NONE);
00752 }
00753 
00754 void DrawWaterClassGround(const TileInfo *ti)
00755 {
00756   switch (GetWaterClass(ti->tile)) {
00757     case WATER_CLASS_SEA:   DrawSeaWater(ti->tile); break;
00758     case WATER_CLASS_CANAL: DrawCanalWater(ti->tile); break;
00759     case WATER_CLASS_RIVER: DrawRiverWater(ti); break;
00760     default: NOT_REACHED();
00761   }
00762 }
00763 
00764 static void DrawTile_Water(TileInfo *ti)
00765 {
00766   switch (GetWaterTileType(ti->tile)) {
00767     case WATER_TILE_CLEAR:
00768       DrawWaterClassGround(ti);
00769       DrawBridgeMiddle(ti);
00770       break;
00771 
00772     case WATER_TILE_COAST: {
00773       DrawShoreTile(ti->tileh);
00774       DrawBridgeMiddle(ti);
00775       break;
00776     }
00777 
00778     case WATER_TILE_LOCK:
00779       DrawWaterLock(ti);
00780       break;
00781 
00782     case WATER_TILE_DEPOT:
00783       DrawWaterDepot(ti);
00784       break;
00785   }
00786 }
00787 
00788 void DrawShipDepotSprite(int x, int y, int image)
00789 {
00790   const WaterDrawTileStruct *wdts = _shipdepot_display_seq[image];
00791 
00792   DrawSprite(wdts++->image, PAL_NONE, x, y);
00793 
00794   for (; wdts->delta_x != 0x80; wdts++) {
00795     Point pt = RemapCoords(wdts->delta_x, wdts->delta_y, wdts->delta_z);
00796     DrawSprite(wdts->image, COMPANY_SPRITE_COLOUR(_local_company), x + pt.x, y + pt.y);
00797   }
00798 }
00799 
00800 
00801 static uint GetSlopeZ_Water(TileIndex tile, uint x, uint y)
00802 {
00803   uint z;
00804   Slope tileh = GetTileSlope(tile, &z);
00805 
00806   return z + GetPartialZ(x & 0xF, y & 0xF, tileh);
00807 }
00808 
00809 static Foundation GetFoundation_Water(TileIndex tile, Slope tileh)
00810 {
00811   return FOUNDATION_NONE;
00812 }
00813 
00814 static void GetTileDesc_Water(TileIndex tile, TileDesc *td)
00815 {
00816   switch (GetWaterTileType(tile)) {
00817     case WATER_TILE_CLEAR:
00818       switch (GetWaterClass(tile)) {
00819         case WATER_CLASS_SEA:   td->str = STR_LAI_WATER_DESCRIPTION_WATER; break;
00820         case WATER_CLASS_CANAL: td->str = STR_LAI_WATER_DESCRIPTION_CANAL; break;
00821         case WATER_CLASS_RIVER: td->str = STR_LAI_WATER_DESCRIPTION_RIVER; break;
00822         default: NOT_REACHED(); break;
00823       }
00824       break;
00825     case WATER_TILE_COAST: td->str = STR_LAI_WATER_DESCRIPTION_COAST_OR_RIVERBANK; break;
00826     case WATER_TILE_LOCK : td->str = STR_LAI_WATER_DESCRIPTION_LOCK;               break;
00827     case WATER_TILE_DEPOT:
00828       td->str = STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT;
00829       td->build_date = Depot::GetByTile(tile)->build_date;
00830       break;
00831     default: NOT_REACHED(); break;
00832   }
00833 
00834   td->owner[0] = GetTileOwner(tile);
00835 }
00836 
00842 static void FloodVehicle(Vehicle *v)
00843 {
00844   uint pass = v->Crash(true);
00845 
00846   AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, AIEventVehicleCrashed::CRASH_FLOODED));
00847   SetDParam(0, pass);
00848   AddVehicleNewsItem(STR_NEWS_DISASTER_FLOOD_VEHICLE, NS_ACCIDENT, v->index);
00849   CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
00850   SndPlayVehicleFx(SND_12_EXPLOSION, v);
00851 }
00852 
00859 static Vehicle *FloodVehicleProc(Vehicle *v, void *data)
00860 {
00861   if ((v->vehstatus & VS_CRASHED) != 0) return NULL;
00862 
00863   switch (v->type) {
00864     default: break;
00865 
00866     case VEH_AIRCRAFT: {
00867       if (!IsAirportTile(v->tile) || GetTileMaxZ(v->tile) != 0) break;
00868       if (v->subtype == AIR_SHADOW) break;
00869 
00870       /* We compare v->z_pos against delta_z + 1 because the shadow
00871        * is at delta_z and the actual aircraft at delta_z + 1. */
00872       const Station *st = Station::GetByTile(v->tile);
00873       const AirportFTAClass *airport = st->airport.GetFTA();
00874       if (v->z_pos != airport->delta_z + 1) break;
00875 
00876       FloodVehicle(v);
00877       break;
00878     }
00879 
00880     case VEH_TRAIN:
00881     case VEH_ROAD: {
00882       byte z = *(byte*)data;
00883       if (v->z_pos > z) break;
00884       FloodVehicle(v->First());
00885       break;
00886     }
00887   }
00888 
00889   return NULL;
00890 }
00891 
00897 static void FloodVehicles(TileIndex tile)
00898 {
00899   byte z = 0;
00900 
00901   if (IsAirportTile(tile)) {
00902     const Station *st = Station::GetByTile(tile);
00903     TILE_AREA_LOOP(tile, st->airport) {
00904       if (st->TileBelongsToAirport(tile)) FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00905     }
00906 
00907     /* No vehicle could be flooded on this airport anymore */
00908     return;
00909   }
00910 
00911   if (!IsBridgeTile(tile)) {
00912     FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00913     return;
00914   }
00915 
00916   TileIndex end = GetOtherBridgeEnd(tile);
00917   z = GetBridgeHeight(tile);
00918 
00919   FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00920   FindVehicleOnPos(end, &z, &FloodVehicleProc);
00921 }
00922 
00928 FloodingBehaviour GetFloodingBehaviour(TileIndex tile)
00929 {
00930   /* FLOOD_ACTIVE:  'single-corner-raised'-coast, sea, sea-shipdepots, sea-buoys, sea-docks (water part), rail with flooded halftile, sea-water-industries, sea-oilrigs
00931    * FLOOD_DRYUP:   coast with more than one corner raised, coast with rail-track, coast with trees
00932    * FLOOD_PASSIVE: (not used)
00933    * FLOOD_NONE:    canals, rivers, everything else
00934    */
00935   switch (GetTileType(tile)) {
00936     case MP_WATER:
00937       if (IsCoast(tile)) {
00938         Slope tileh = GetTileSlope(tile, NULL);
00939         return (IsSlopeWithOneCornerRaised(tileh) ? FLOOD_ACTIVE : FLOOD_DRYUP);
00940       }
00941       /* FALL THROUGH */
00942     case MP_STATION:
00943     case MP_INDUSTRY:
00944     case MP_OBJECT:
00945       return (GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE;
00946 
00947     case MP_RAILWAY:
00948       if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
00949         return (IsSlopeWithOneCornerRaised(GetTileSlope(tile, NULL)) ? FLOOD_ACTIVE : FLOOD_DRYUP);
00950       }
00951       return FLOOD_NONE;
00952 
00953     case MP_TREES:
00954       return (GetTreeGround(tile) == TREE_GROUND_SHORE ? FLOOD_DRYUP : FLOOD_NONE);
00955 
00956     default:
00957       return FLOOD_NONE;
00958   }
00959 }
00960 
00964 void DoFloodTile(TileIndex target)
00965 {
00966   assert(!IsTileType(target, MP_WATER));
00967 
00968   bool flooded = false; // Will be set to true if something is changed.
00969 
00970   Backup<CompanyByte> cur_company(_current_company, OWNER_WATER, FILE_LINE);
00971 
00972   Slope tileh = GetTileSlope(target, NULL);
00973   if (tileh != SLOPE_FLAT) {
00974     /* make coast.. */
00975     switch (GetTileType(target)) {
00976       case MP_RAILWAY: {
00977         if (!IsPlainRail(target)) break;
00978         FloodVehicles(target);
00979         flooded = FloodHalftile(target);
00980         break;
00981       }
00982 
00983       case MP_TREES:
00984         if (!IsSlopeWithOneCornerRaised(tileh)) {
00985           SetTreeGroundDensity(target, TREE_GROUND_SHORE, 3);
00986           MarkTileDirtyByTile(target);
00987           flooded = true;
00988           break;
00989         }
00990         /* FALL THROUGH */
00991 
00992       case MP_CLEAR:
00993         if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
00994           MakeShore(target);
00995           MarkTileDirtyByTile(target);
00996           flooded = true;
00997         }
00998         break;
00999 
01000       default:
01001         break;
01002     }
01003   } else {
01004     /* Flood vehicles */
01005     FloodVehicles(target);
01006 
01007     /* flood flat tile */
01008     if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
01009       MakeSea(target);
01010       MarkTileDirtyByTile(target);
01011       flooded = true;
01012     }
01013   }
01014 
01015   if (flooded) {
01016     /* Mark surrounding canal tiles dirty too to avoid glitches */
01017     MarkCanalsAndRiversAroundDirty(target);
01018 
01019     /* update signals if needed */
01020     UpdateSignalsInBuffer();
01021   }
01022 
01023   cur_company.Restore();
01024 }
01025 
01029 static void DoDryUp(TileIndex tile)
01030 {
01031   Backup<CompanyByte> cur_company(_current_company, OWNER_WATER, FILE_LINE);
01032 
01033   switch (GetTileType(tile)) {
01034     case MP_RAILWAY:
01035       assert(IsPlainRail(tile));
01036       assert(GetRailGroundType(tile) == RAIL_GROUND_WATER);
01037 
01038       RailGroundType new_ground;
01039       switch (GetTrackBits(tile)) {
01040         case TRACK_BIT_UPPER: new_ground = RAIL_GROUND_FENCE_HORIZ1; break;
01041         case TRACK_BIT_LOWER: new_ground = RAIL_GROUND_FENCE_HORIZ2; break;
01042         case TRACK_BIT_LEFT:  new_ground = RAIL_GROUND_FENCE_VERT1;  break;
01043         case TRACK_BIT_RIGHT: new_ground = RAIL_GROUND_FENCE_VERT2;  break;
01044         default: NOT_REACHED();
01045       }
01046       SetRailGroundType(tile, new_ground);
01047       MarkTileDirtyByTile(tile);
01048       break;
01049 
01050     case MP_TREES:
01051       SetTreeGroundDensity(tile, TREE_GROUND_GRASS, 3);
01052       MarkTileDirtyByTile(tile);
01053       break;
01054 
01055     case MP_WATER:
01056       assert(IsCoast(tile));
01057 
01058       if (DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
01059         MakeClear(tile, CLEAR_GRASS, 3);
01060         MarkTileDirtyByTile(tile);
01061       }
01062       break;
01063 
01064     default: NOT_REACHED();
01065   }
01066 
01067   cur_company.Restore();
01068 }
01069 
01076 void TileLoop_Water(TileIndex tile)
01077 {
01078   switch (GetFloodingBehaviour(tile)) {
01079     case FLOOD_ACTIVE:
01080       for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
01081         TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir(dir));
01082         if (dest == INVALID_TILE) continue;
01083         /* do not try to flood water tiles - increases performance a lot */
01084         if (IsTileType(dest, MP_WATER)) continue;
01085 
01086         uint z_dest;
01087         Slope slope_dest = GetFoundationSlope(dest, &z_dest) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
01088         if (z_dest > 0) continue;
01089 
01090         if (!HasBit(_flood_from_dirs[slope_dest], ReverseDir(dir))) continue;
01091 
01092         DoFloodTile(dest);
01093       }
01094       break;
01095 
01096     case FLOOD_DRYUP: {
01097       Slope slope_here = GetFoundationSlope(tile, NULL) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
01098       uint dir;
01099       FOR_EACH_SET_BIT(dir, _flood_from_dirs[slope_here]) {
01100         TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir((Direction)dir));
01101         if (dest == INVALID_TILE) continue;
01102 
01103         FloodingBehaviour dest_behaviour = GetFloodingBehaviour(dest);
01104         if ((dest_behaviour == FLOOD_ACTIVE) || (dest_behaviour == FLOOD_PASSIVE)) return;
01105       }
01106       DoDryUp(tile);
01107       break;
01108     }
01109 
01110     default: return;
01111   }
01112 }
01113 
01114 void ConvertGroundTilesIntoWaterTiles()
01115 {
01116   uint z;
01117 
01118   for (TileIndex tile = 0; tile < MapSize(); ++tile) {
01119     Slope slope = GetTileSlope(tile, &z);
01120     if (IsTileType(tile, MP_CLEAR) && z == 0) {
01121       /* Make both water for tiles at level 0
01122        * and make shore, as that looks much better
01123        * during the generation. */
01124       switch (slope) {
01125         case SLOPE_FLAT:
01126           MakeSea(tile);
01127           break;
01128 
01129         case SLOPE_N:
01130         case SLOPE_E:
01131         case SLOPE_S:
01132         case SLOPE_W:
01133           MakeShore(tile);
01134           break;
01135 
01136         default:
01137           uint dir;
01138           FOR_EACH_SET_BIT(dir, _flood_from_dirs[slope & ~SLOPE_STEEP]) {
01139             TileIndex dest = TILE_ADD(tile, TileOffsByDir((Direction)dir));
01140             Slope slope_dest = GetTileSlope(dest, NULL) & ~SLOPE_STEEP;
01141             if (slope_dest == SLOPE_FLAT || IsSlopeWithOneCornerRaised(slope_dest)) {
01142               MakeShore(tile);
01143               break;
01144             }
01145           }
01146           break;
01147       }
01148     }
01149   }
01150 }
01151 
01152 static TrackStatus GetTileTrackStatus_Water(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
01153 {
01154   static const byte coast_tracks[] = {0, 32, 4, 0, 16, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0};
01155 
01156   TrackBits ts;
01157 
01158   if (mode != TRANSPORT_WATER) return 0;
01159 
01160   switch (GetWaterTileType(tile)) {
01161     case WATER_TILE_CLEAR: ts = (GetTileSlope(tile, NULL) == SLOPE_FLAT) ? TRACK_BIT_ALL : TRACK_BIT_NONE; break;
01162     case WATER_TILE_COAST: ts = (TrackBits)coast_tracks[GetTileSlope(tile, NULL) & 0xF]; break;
01163     case WATER_TILE_LOCK:  ts = DiagDirToDiagTrackBits(GetLockDirection(tile)); break;
01164     case WATER_TILE_DEPOT: ts = AxisToTrackBits(GetShipDepotAxis(tile)); break;
01165     default: return 0;
01166   }
01167   if (TileX(tile) == 0) {
01168     /* NE border: remove tracks that connects NE tile edge */
01169     ts &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
01170   }
01171   if (TileY(tile) == 0) {
01172     /* NW border: remove tracks that connects NW tile edge */
01173     ts &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
01174   }
01175   return CombineTrackStatus(TrackBitsToTrackdirBits(ts), TRACKDIR_BIT_NONE);
01176 }
01177 
01178 static bool ClickTile_Water(TileIndex tile)
01179 {
01180   if (GetWaterTileType(tile) == WATER_TILE_DEPOT) {
01181     ShowDepotWindow(GetShipDepotNorthTile(tile), VEH_SHIP);
01182     return true;
01183   }
01184   return false;
01185 }
01186 
01187 static void ChangeTileOwner_Water(TileIndex tile, Owner old_owner, Owner new_owner)
01188 {
01189   if (!IsTileOwner(tile, old_owner)) return;
01190 
01191   if (new_owner != INVALID_OWNER) {
01192     SetTileOwner(tile, new_owner);
01193     return;
01194   }
01195 
01196   /* Remove depot */
01197   if (IsShipDepot(tile)) DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
01198 
01199   /* Set owner of canals and locks ... and also canal under dock there was before.
01200    * Check if the new owner after removing depot isn't OWNER_WATER. */
01201   if (IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
01202 }
01203 
01204 static VehicleEnterTileStatus VehicleEnter_Water(Vehicle *v, TileIndex tile, int x, int y)
01205 {
01206   return VETSB_CONTINUE;
01207 }
01208 
01209 static CommandCost TerraformTile_Water(TileIndex tile, DoCommandFlag flags, uint z_new, Slope tileh_new)
01210 {
01211   /* Canals can't be terraformed */
01212   if (IsWaterTile(tile) && IsCanal(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_CANAL_FIRST);
01213 
01214   return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
01215 }
01216 
01217 
01218 extern const TileTypeProcs _tile_type_water_procs = {
01219   DrawTile_Water,           // draw_tile_proc
01220   GetSlopeZ_Water,          // get_slope_z_proc
01221   ClearTile_Water,          // clear_tile_proc
01222   NULL,                     // add_accepted_cargo_proc
01223   GetTileDesc_Water,        // get_tile_desc_proc
01224   GetTileTrackStatus_Water, // get_tile_track_status_proc
01225   ClickTile_Water,          // click_tile_proc
01226   NULL,                     // animate_tile_proc
01227   TileLoop_Water,           // tile_loop_clear
01228   ChangeTileOwner_Water,    // change_tile_owner_clear
01229   NULL,                     // add_produced_cargo_proc
01230   VehicleEnter_Water,       // vehicle_enter_tile_proc
01231   GetFoundation_Water,      // get_foundation_proc
01232   TerraformTile_Water,      // terraform_tile_proc
01233 };