# water_cmd.cpp

Go to the documentation of this file.

    00001 /* $Id: water_cmd.cpp 16744 2009-07-04 21:06:17Z rubidium $ */
    00002
    00005 #include "stdafx.h"
    00006 #include "openttd.h"
    00007 #include "cmd_helper.h"
    00008 #include "landscape.h"
    00009 #include "viewport_func.h"
    00010 #include "command_func.h"
    00011 #include "town.h"
    00012 #include "news_func.h"
    00013 #include "depot_base.h"
    00014 #include "depot_func.h"
    00015 #include "vehicle_gui.h"
    00016 #include "train.h"
    00017 #include "roadveh.h"
    00018 #include "water.h"
    00019 #include "industry_map.h"
    00020 #include "cargotype.h"
    00021 #include "newgrf_canal.h"
    00022 #include "transparency.h"
    00023 #include "strings_func.h"
    00024 #include "functions.h"
    00025 #include "window_func.h"
    00026 #include "vehicle_func.h"
    00027 #include "sound_func.h"
    00028 #include "company_func.h"
    00029 #include "clear_map.h"
    00030 #include "tree_map.h"
    00031 #include "aircraft.h"
    00032 #include "newgrf_cargo.h"
    00033 #include "effectvehicle_func.h"
    00034 #include "tunnelbridge_map.h"
    00035 #include "ai/ai.hpp"
    00036
    00037 #include "table/sprites.h"
    00038 #include "table/strings.h"
    00039
    00043 enum FloodingBehaviour {
    00044   FLOOD_NONE,
    00045   FLOOD_ACTIVE,
    00046   FLOOD_PASSIVE,
    00047   FLOOD_DRYUP,
    00048 };
    00049
    00053 static const uint8 _flood_from_dirs[] = {
    00054   (1 << DIR_NW) | (1 << DIR_SW) | (1 << DIR_SE) | (1 << DIR_NE), // SLOPE_FLAT
    00055   (1 << DIR_NE) | (1 << DIR_SE),                                 // SLOPE_W
    00056   (1 << DIR_NW) | (1 << DIR_NE),                                 // SLOPE_S
    00057   (1 << DIR_NE),                                                 // SLOPE_SW
    00058   (1 << DIR_NW) | (1 << DIR_SW),                                 // SLOPE_E
    00059   0,                                                             // SLOPE_EW
    00060   (1 << DIR_NW),                                                 // SLOPE_SE
    00061   (1 << DIR_N ) | (1 << DIR_NW) | (1 << DIR_NE),                 // SLOPE_WSE, SLOPE_STEEP_S
    00062   (1 << DIR_SW) | (1 << DIR_SE),                                 // SLOPE_N
    00063   (1 << DIR_SE),                                                 // SLOPE_NW
    00064   0,                                                             // SLOPE_NS
    00065   (1 << DIR_E ) | (1 << DIR_NE) | (1 << DIR_SE),                 // SLOPE_NWS, SLOPE_STEEP_W
    00066   (1 << DIR_SW),                                                 // SLOPE_NE
    00067   (1 << DIR_S ) | (1 << DIR_SW) | (1 << DIR_SE),                 // SLOPE_ENW, SLOPE_STEEP_N
    00068   (1 << DIR_W ) | (1 << DIR_SW) | (1 << DIR_NW),                 // SLOPE_SEN, SLOPE_STEEP_E
    00069 };
    00070
    00077 static inline void MarkTileDirtyIfCanalOrRiver(TileIndex tile)
    00078 {
    00079   if (IsTileType(tile, MP_WATER) && (IsCanal(tile) || IsRiver(tile))) MarkTileDirtyByTile(tile);
    00080 }
    00081
    00088 static void MarkCanalsAndRiversAroundDirty(TileIndex tile)
    00089 {
    00090   for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
    00091     MarkTileDirtyIfCanalOrRiver(tile + TileOffsByDir(dir));
    00092   }
    00093 }
    00094
    00095
    00102 CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00103 {
    00104   TileIndex tile2;
    00105
    00106   CommandCost ret;
    00107
    00108   Axis axis = Extract<Axis, 0>(p1);
    00109
    00110   tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
    00111
    00112   if (!IsWaterTile(tile) || !IsWaterTile(tile2)) {
    00113     return_cmd_error(STR_3801_MUST_BE_BUILT_ON_WATER);
    00114   }
    00115
    00116   if (IsBridgeAbove(tile) || IsBridgeAbove(tile2)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
    00117
    00118   if (GetTileSlope(tile, NULL) != SLOPE_FLAT || GetTileSlope(tile2, NULL) != SLOPE_FLAT) {
    00119     /* Prevent depots on rapids */
    00120     return_cmd_error(STR_0239_SITE_UNSUITABLE);
    00121   }
    00122
    00123   WaterClass wc1 = GetWaterClass(tile);
    00124   WaterClass wc2 = GetWaterClass(tile2);
    00125   ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
    00126   if (CmdFailed(ret)) return CMD_ERROR;
    00127   ret = DoCommand(tile2, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
    00128   if (CmdFailed(ret)) return CMD_ERROR;
    00129
    00130   if (!Depot::CanAllocateItem()) return CMD_ERROR;
    00131
    00132   if (flags & DC_EXEC) {
    00133     Depot *depot = new Depot(tile);
    00134     depot->town_index = ClosestTownFromTile(tile, UINT_MAX)->index;
    00135
    00136     MakeShipDepot(tile,  _current_company, DEPOT_NORTH, axis, wc1);
    00137     MakeShipDepot(tile2, _current_company, DEPOT_SOUTH, axis, wc2);
    00138     MarkTileDirtyByTile(tile);
    00139     MarkTileDirtyByTile(tile2);
    00140   }
    00141
    00142   return CommandCost(EXPENSES_CONSTRUCTION, _price.build_ship_depot);
    00143 }
    00144
    00145 void MakeWaterKeepingClass(TileIndex tile, Owner o)
    00146 {
    00147   assert(IsTileType(tile, MP_WATER) || (IsTileType(tile, MP_STATION) && (IsBuoy(tile) || IsDock(tile) || IsOilRig(tile))) || IsTileType(tile, MP_INDUSTRY));
    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   switch (wc) {
    00158     case WATER_CLASS_SEA:   MakeWater(tile);              break;
    00159     case WATER_CLASS_CANAL: MakeCanal(tile, o, Random()); break;
    00160     case WATER_CLASS_RIVER: MakeRiver(tile, Random());    break;
    00161     default:                DoClearSquare(tile);          break;
    00162   }
    00163 }
    00164
    00165 static CommandCost RemoveShipDepot(TileIndex tile, DoCommandFlag flags)
    00166 {
    00167   if (!IsShipDepot(tile)) return CMD_ERROR;
    00168   if (!CheckTileOwnership(tile)) return CMD_ERROR;
    00169
    00170   TileIndex tile2 = GetOtherShipDepotTile(tile);
    00171
    00172   /* do not check for ship on tile when company goes bankrupt */
    00173   if (!(flags & DC_BANKRUPT)) {
    00174     if (!EnsureNoVehicleOnGround(tile) || !EnsureNoVehicleOnGround(tile2)) return CMD_ERROR;
    00175   }
    00176
    00177   if (flags & DC_EXEC) {
    00178     /* Kill the depot, which is registered at the northernmost tile. Use that one */
    00179     delete GetDepotByTile(tile2 < tile ? tile2 : tile);
    00180
    00181     MakeWaterKeepingClass(tile,  GetTileOwner(tile));
    00182     MakeWaterKeepingClass(tile2, GetTileOwner(tile2));
    00183     MarkTileDirtyByTile(tile);
    00184     MarkTileDirtyByTile(tile2);
    00185   }
    00186
    00187   return CommandCost(EXPENSES_CONSTRUCTION, _price.remove_ship_depot);
    00188 }
    00189
    00191 static CommandCost DoBuildShiplift(TileIndex tile, DiagDirection dir, DoCommandFlag flags)
    00192 {
    00193   CommandCost ret;
    00194   int delta;
    00195
    00196   /* middle tile */
    00197   ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
    00198   if (CmdFailed(ret)) return CMD_ERROR;
    00199
    00200   delta = TileOffsByDiagDir(dir);
    00201   /* lower tile */
    00202   WaterClass wc_lower = IsWaterTile(tile - delta) ? GetWaterClass(tile - delta) : WATER_CLASS_CANAL;
    00203
    00204   ret = DoCommand(tile - delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
    00205   if (CmdFailed(ret)) return CMD_ERROR;
    00206   if (GetTileSlope(tile - delta, NULL) != SLOPE_FLAT) {
    00207     return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
    00208   }
    00209
    00210   /* upper tile */
    00211   WaterClass wc_upper = IsWaterTile(tile + delta) ? GetWaterClass(tile + delta) : WATER_CLASS_CANAL;
    00212
    00213   ret = DoCommand(tile + delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
    00214   if (CmdFailed(ret)) return CMD_ERROR;
    00215   if (GetTileSlope(tile + delta, NULL) != SLOPE_FLAT) {
    00216     return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
    00217   }
    00218
    00219   if ((MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) ||
    00220       (MayHaveBridgeAbove(tile - delta) && IsBridgeAbove(tile - delta)) ||
    00221       (MayHaveBridgeAbove(tile + delta) && IsBridgeAbove(tile + delta))) {
    00222     return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
    00223   }
    00224
    00225   if (flags & DC_EXEC) {
    00226     MakeLock(tile, _current_company, dir, wc_lower, wc_upper);
    00227     MarkTileDirtyByTile(tile);
    00228     MarkTileDirtyByTile(tile - delta);
    00229     MarkTileDirtyByTile(tile + delta);
    00230     MarkCanalsAndRiversAroundDirty(tile - delta);
    00231     MarkCanalsAndRiversAroundDirty(tile + delta);
    00232   }
    00233
    00234   return CommandCost(EXPENSES_CONSTRUCTION, _price.clear_water * 22 >> 3);
    00235 }
    00236
    00237 static CommandCost RemoveShiplift(TileIndex tile, DoCommandFlag flags)
    00238 {
    00239   TileIndexDiff delta = TileOffsByDiagDir(GetLockDirection(tile));
    00240
    00241   if (!CheckTileOwnership(tile) && GetTileOwner(tile) != OWNER_NONE) return CMD_ERROR;
    00242
    00243   /* make sure no vehicle is on the tile. */
    00244   if (!EnsureNoVehicleOnGround(tile) || !EnsureNoVehicleOnGround(tile + delta) || !EnsureNoVehicleOnGround(tile - delta))
    00245     return CMD_ERROR;
    00246
    00247   if (flags & DC_EXEC) {
    00248     DoClearSquare(tile);
    00249     MakeWaterKeepingClass(tile + delta, GetTileOwner(tile));
    00250     MakeWaterKeepingClass(tile - delta, GetTileOwner(tile));
    00251     MarkTileDirtyByTile(tile - delta);
    00252     MarkTileDirtyByTile(tile + delta);
    00253     MarkCanalsAndRiversAroundDirty(tile - delta);
    00254     MarkCanalsAndRiversAroundDirty(tile + delta);
    00255   }
    00256
    00257   return CommandCost(EXPENSES_CONSTRUCTION, _price.clear_water * 2);
    00258 }
    00259
    00266 CommandCost CmdBuildLock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00267 {
    00268   DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile, NULL));
    00269   if (dir == INVALID_DIAGDIR) return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
    00270
    00271   /* Disallow building of locks on river rapids */
    00272   if (IsWaterTile(tile)) return_cmd_error(STR_0239_SITE_UNSUITABLE);
    00273
    00274   return DoBuildShiplift(tile, dir, flags);
    00275 }
    00276
    00283 CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00284 {
    00285   CommandCost cost(EXPENSES_CONSTRUCTION);
    00286   int size_x, size_y;
    00287   int x;
    00288   int y;
    00289   int sx, sy;
    00290
    00291   if (p1 >= MapSize()) return CMD_ERROR;
    00292
    00293   /* Outside of the editor you can only build canals, not oceans */
    00294   if (p2 != 0 && _game_mode != GM_EDITOR) return CMD_ERROR;
    00295
    00296   x = TileX(tile);
    00297   y = TileY(tile);
    00298   sx = TileX(p1);
    00299   sy = TileY(p1);
    00300
    00301   if (x < sx) Swap(x, sx);
    00302   if (y < sy) Swap(y, sy);
    00303   size_x = (x - sx) + 1;
    00304   size_y = (y - sy) + 1;
    00305
    00306   /* Outside the editor you can only drag canals, and not areas */
    00307   if (_game_mode != GM_EDITOR && (sx != x && sy != y)) return CMD_ERROR;
    00308
    00309   BEGIN_TILE_LOOP(tile, size_x, size_y, TileXY(sx, sy)) {
    00310     CommandCost ret;
    00311
    00312     Slope slope = GetTileSlope(tile, NULL);
    00313     if (slope != SLOPE_FLAT && (p2 != 2 || !IsInclinedSlope(slope))) {
    00314       return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
    00315     }
    00316
    00317     /* can't make water of water! */
    00318     if (IsTileType(tile, MP_WATER) && (!IsTileOwner(tile, OWNER_WATER) || p2 == 1)) continue;
    00319
    00320     ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
    00321     if (CmdFailed(ret)) return ret;
    00322     cost.AddCost(ret);
    00323
    00324     if (flags & DC_EXEC) {
    00325       if (TileHeight(tile) == 0 && p2 == 1) {
    00326         MakeWater(tile);
    00327       } else if (p2 == 2) {
    00328         MakeRiver(tile, Random());
    00329       } else {
    00330         MakeCanal(tile, _current_company, Random());
    00331       }
    00332       MarkTileDirtyByTile(tile);
    00333       MarkCanalsAndRiversAroundDirty(tile);
    00334     }
    00335
    00336     cost.AddCost(_price.clear_water);
    00337   } END_TILE_LOOP(tile, size_x, size_y, 0);
    00338
    00339   if (cost.GetCost() == 0) {
    00340     return_cmd_error(STR_1007_ALREADY_BUILT);
    00341   } else {
    00342     return cost;
    00343   }
    00344 }
    00345
    00346 static CommandCost ClearTile_Water(TileIndex tile, DoCommandFlag flags)
    00347 {
    00348   switch (GetWaterTileType(tile)) {
    00349     case WATER_TILE_CLEAR:
    00350       if (flags & DC_NO_WATER) return_cmd_error(STR_3807_CAN_T_BUILD_ON_WATER);
    00351
    00352       /* Make sure freeform edges are allowed or it's not an edge tile. */
    00353       if (!_settings_game.construction.freeform_edges && (!IsInsideMM(TileX(tile), 1, MapMaxX() - 1) ||
    00354           !IsInsideMM(TileY(tile), 1, MapMaxY() - 1))) {
    00355         return_cmd_error(STR_0002_TOO_CLOSE_TO_EDGE_OF_MAP);
    00356       }
    00357
    00358       /* Make sure no vehicle is on the tile */
    00359       if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
    00360
    00361       if (GetTileOwner(tile) != OWNER_WATER && GetTileOwner(tile) != OWNER_NONE && !CheckTileOwnership(tile)) return CMD_ERROR;
    00362
    00363       if (flags & DC_EXEC) {
    00364         DoClearSquare(tile);
    00365         MarkCanalsAndRiversAroundDirty(tile);
    00366       }
    00367       return CommandCost(EXPENSES_CONSTRUCTION, _price.clear_water);
    00368
    00369     case WATER_TILE_COAST: {
    00370       Slope slope = GetTileSlope(tile, NULL);
    00371
    00372       /* Make sure no vehicle is on the tile */
    00373       if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
    00374
    00375       if (flags & DC_EXEC) {
    00376         DoClearSquare(tile);
    00377         MarkCanalsAndRiversAroundDirty(tile);
    00378       }
    00379       if (IsSlopeWithOneCornerRaised(slope)) {
    00380         return CommandCost(EXPENSES_CONSTRUCTION, _price.clear_water);
    00381       } else {
    00382         return CommandCost(EXPENSES_CONSTRUCTION, _price.clear_roughland);
    00383       }
    00384     }
    00385
    00386     case WATER_TILE_LOCK: {
    00387       static const TileIndexDiffC _shiplift_tomiddle_offs[] = {
    00388         { 0,  0}, {0,  0}, { 0, 0}, {0,  0}, // middle
    00389         {-1,  0}, {0,  1}, { 1, 0}, {0, -1}, // lower
    00390         { 1,  0}, {0, -1}, {-1, 0}, {0,  1}, // upper
    00391       };
    00392
    00393       if (flags & DC_AUTO) return_cmd_error(STR_2004_BUILDING_MUST_BE_DEMOLISHED);
    00394       if (_current_company == OWNER_WATER) return CMD_ERROR;
    00395       /* move to the middle tile.. */
    00396       return RemoveShiplift(tile + ToTileIndexDiff(_shiplift_tomiddle_offs[GetSection(tile)]), flags);
    00397     }
    00398
    00399     case WATER_TILE_DEPOT:
    00400       if (flags & DC_AUTO) return_cmd_error(STR_2004_BUILDING_MUST_BE_DEMOLISHED);
    00401       return RemoveShipDepot(tile, flags);
    00402
    00403     default:
    00404       NOT_REACHED();
    00405   }
    00406 }
    00407
    00416 static bool IsWateredTile(TileIndex tile, Direction from)
    00417 {
    00418   switch (GetTileType(tile)) {
    00419     case MP_WATER:
    00420       switch (GetWaterTileType(tile)) {
    00421         default: NOT_REACHED();
    00422         case WATER_TILE_DEPOT: case WATER_TILE_CLEAR: return true;
    00423         case WATER_TILE_LOCK: return DiagDirToAxis(GetLockDirection(tile)) == DiagDirToAxis(DirToDiagDir(from));
    00424
    00425         case WATER_TILE_COAST:
    00426           switch (GetTileSlope(tile, NULL)) {
    00427             case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
    00428             case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
    00429             case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
    00430             case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
    00431             default: return false;
    00432           }
    00433       }
    00434
    00435     case MP_RAILWAY:
    00436       if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
    00437         assert(IsPlainRailTile(tile));
    00438         switch (GetTileSlope(tile, NULL)) {
    00439           case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
    00440           case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
    00441           case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
    00442           case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
    00443           default: return false;
    00444         }
    00445       }
    00446       return false;
    00447
    00448     case MP_STATION:
    00449       if (IsOilRig(tile)) {
    00450         /* Do not draw waterborders inside of industries.
    00451          * Note: There is no easy way to detect the industry of an oilrig tile. */
    00452         TileIndex src_tile = tile + TileOffsByDir(from);
    00453         if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
    00454             (IsTileType(src_tile, MP_INDUSTRY))) return true;
    00455
    00456         return GetWaterClass(tile) != WATER_CLASS_INVALID;
    00457       }
    00458       return (IsDock(tile) && GetTileSlope(tile, NULL) == SLOPE_FLAT) || IsBuoy(tile);
    00459
    00460     case MP_INDUSTRY: {
    00461       /* Do not draw waterborders inside of industries.
    00462        * Note: There is no easy way to detect the industry of an oilrig tile. */
    00463       TileIndex src_tile = tile + TileOffsByDir(from);
    00464       if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
    00465           (IsTileType(src_tile, MP_INDUSTRY) && GetIndustryIndex(src_tile) == GetIndustryIndex(tile))) return true;
    00466
    00467       return IsIndustryTileOnWater(tile);
    00468     }
    00469
    00470     case MP_TUNNELBRIDGE: return GetTunnelBridgeTransportType(tile) == TRANSPORT_WATER && ReverseDiagDir(GetTunnelBridgeDirection(tile)) == DirToDiagDir(from);
    00471
    00472     default:          return false;
    00473   }
    00474 }
    00475
    00476 static void DrawWaterEdges(SpriteID base, TileIndex tile)
    00477 {
    00478   uint wa;
    00479
    00480   /* determine the edges around with water. */
    00481   wa  = IsWateredTile(TILE_ADDXY(tile, -1,  0), DIR_SW) << 0;
    00482   wa += IsWateredTile(TILE_ADDXY(tile,  0,  1), DIR_NW) << 1;
    00483   wa += IsWateredTile(TILE_ADDXY(tile,  1,  0), DIR_NE) << 2;
    00484   wa += IsWateredTile(TILE_ADDXY(tile,  0, -1), DIR_SE) << 3;
    00485
    00486   if (!(wa & 1)) DrawGroundSprite(base,     PAL_NONE);
    00487   if (!(wa & 2)) DrawGroundSprite(base + 1, PAL_NONE);
    00488   if (!(wa & 4)) DrawGroundSprite(base + 2, PAL_NONE);
    00489   if (!(wa & 8)) DrawGroundSprite(base + 3, PAL_NONE);
    00490
    00491   /* right corner */
    00492   switch (wa & 0x03) {
    00493     case 0: DrawGroundSprite(base + 4, PAL_NONE); break;
    00494     case 3: if (!IsWateredTile(TILE_ADDXY(tile, -1, 1), DIR_W)) DrawGroundSprite(base + 8, PAL_NONE); break;
    00495   }
    00496
    00497   /* bottom corner */
    00498   switch (wa & 0x06) {
    00499     case 0: DrawGroundSprite(base + 5, PAL_NONE); break;
    00500     case 6: if (!IsWateredTile(TILE_ADDXY(tile, 1, 1), DIR_N)) DrawGroundSprite(base + 9, PAL_NONE); break;
    00501   }
    00502
    00503   /* left corner */
    00504   switch (wa & 0x0C) {
    00505     case  0: DrawGroundSprite(base + 6, PAL_NONE); break;
    00506     case 12: if (!IsWateredTile(TILE_ADDXY(tile, 1, -1), DIR_E)) DrawGroundSprite(base + 10, PAL_NONE); break;
    00507   }
    00508
    00509   /* upper corner */
    00510   switch (wa & 0x09) {
    00511     case 0: DrawGroundSprite(base + 7, PAL_NONE); break;
    00512     case 9: if (!IsWateredTile(TILE_ADDXY(tile, -1, -1), DIR_S)) DrawGroundSprite(base + 11, PAL_NONE); break;
    00513   }
    00514 }
    00515
    00517 static void DrawSeaWater(TileIndex tile)
    00518 {
    00519   DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
    00520 }
    00521
    00523 static void DrawCanalWater(TileIndex tile)
    00524 {
    00525   DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
    00526
    00527   /* Test for custom graphics, else use the default */
    00528   SpriteID dikes_base = GetCanalSprite(CF_DIKES, tile);
    00529   if (dikes_base == 0) dikes_base = SPR_CANAL_DIKES_BASE;
    00530
    00531   DrawWaterEdges(dikes_base, tile);
    00532 }
    00533
    00534 struct LocksDrawTileStruct {
    00535   int8 delta_x, delta_y, delta_z;
    00536   byte width, height, depth;
    00537   SpriteID image;
    00538 };
    00539
    00540 #include "table/water_land.h"
    00541
    00542 static void DrawWaterStuff(const TileInfo *ti, const WaterDrawTileStruct *wdts,
    00543   SpriteID palette, uint base, bool draw_ground
    00544 )
    00545 {
    00546   SpriteID image;
    00547   SpriteID water_base = GetCanalSprite(CF_WATERSLOPE, ti->tile);
    00548   SpriteID locks_base = GetCanalSprite(CF_LOCKS, ti->tile);
    00549
    00550   /* If no custom graphics, use defaults */
    00551   if (water_base == 0) water_base = SPR_CANALS_BASE;
    00552   if (locks_base == 0) {
    00553     locks_base = SPR_SHIPLIFT_BASE;
    00554   } else {
    00555     /* If using custom graphics, ignore the variation on height */
    00556     base = 0;
    00557   }
    00558
    00559   image = wdts++->image;
    00560   if (image < 4) image += water_base;
    00561   if (draw_ground) DrawGroundSprite(image, PAL_NONE);
    00562
    00563   /* End now if buildings are invisible */
    00564   if (IsInvisibilitySet(TO_BUILDINGS)) return;
    00565
    00566   for (; wdts->delta_x != 0x80; wdts++) {
    00567     AddSortableSpriteToDraw(wdts->image + base + ((wdts->image < 24) ? locks_base : 0), palette,
    00568       ti->x + wdts->delta_x, ti->y + wdts->delta_y,
    00569       wdts->size_x, wdts->size_y,
    00570       wdts->size_z, ti->z + wdts->delta_z,
    00571       IsTransparencySet(TO_BUILDINGS));
    00572   }
    00573 }
    00574
    00575 static void DrawRiverWater(const TileInfo *ti)
    00576 {
    00577   SpriteID image = SPR_FLAT_WATER_TILE;
    00578   SpriteID edges_base = GetCanalSprite(CF_RIVER_EDGE, ti->tile);
    00579
    00580   if (ti->tileh != SLOPE_FLAT) {
    00581     image = GetCanalSprite(CF_RIVER_SLOPE, ti->tile);
    00582     if (image == 0) {
    00583       switch (ti->tileh) {
    00584         case SLOPE_NW: image = SPR_WATER_SLOPE_Y_DOWN; break;
    00585         case SLOPE_SW: image = SPR_WATER_SLOPE_X_UP;   break;
    00586         case SLOPE_SE: image = SPR_WATER_SLOPE_Y_UP;   break;
    00587         case SLOPE_NE: image = SPR_WATER_SLOPE_X_DOWN; break;
    00588         default:       image = SPR_FLAT_WATER_TILE;    break;
    00589       }
    00590     } else {
    00591       switch (ti->tileh) {
    00592         default: NOT_REACHED();
    00593         case SLOPE_SE:             edges_base += 12; break;
    00594         case SLOPE_NE: image += 1; edges_base += 24; break;
    00595         case SLOPE_SW: image += 2; edges_base += 36; break;
    00596         case SLOPE_NW: image += 3; edges_base += 48; break;
    00597       }
    00598     }
    00599   }
    00600
    00601   DrawGroundSprite(image, PAL_NONE);
    00602
    00603   /* Draw river edges if available. */
    00604   if (edges_base > 48) DrawWaterEdges(edges_base, ti->tile);
    00605 }
    00606
    00607 void DrawShoreTile(Slope tileh)
    00608 {
    00609   /* Converts the enum Slope into an offset based on SPR_SHORE_BASE.
    00610    * This allows to calculate the proper sprite to display for this Slope */
    00611   static const byte tileh_to_shoresprite[32] = {
    00612     0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0,
    00613     0, 0, 0, 0, 0,  0, 0, 0, 0, 0,  0,  5,  0, 10, 15, 0,
    00614   };
    00615
    00616   assert(!IsHalftileSlope(tileh)); // Halftile slopes need to get handled earlier.
    00617   assert(tileh != SLOPE_FLAT);     // Shore is never flat
    00618
    00619   assert((tileh != SLOPE_EW) && (tileh != SLOPE_NS)); // No suitable sprites for current flooding behaviour
    00620
    00621   DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[tileh], PAL_NONE);
    00622 }
    00623
    00624 void DrawWaterClassGround(const TileInfo *ti) {
    00625   switch (GetWaterClass(ti->tile)) {
    00626     case WATER_CLASS_SEA:   DrawSeaWater(ti->tile); break;
    00627     case WATER_CLASS_CANAL: DrawCanalWater(ti->tile); break;
    00628     case WATER_CLASS_RIVER: DrawRiverWater(ti); break;
    00629     default: NOT_REACHED();
    00630   }
    00631 }
    00632
    00633 static void DrawTile_Water(TileInfo *ti)
    00634 {
    00635   switch (GetWaterTileType(ti->tile)) {
    00636     case WATER_TILE_CLEAR:
    00637       DrawWaterClassGround(ti);
    00638       DrawBridgeMiddle(ti);
    00639       break;
    00640
    00641     case WATER_TILE_COAST: {
    00642       DrawShoreTile(ti->tileh);
    00643       DrawBridgeMiddle(ti);
    00644     } break;
    00645
    00646     case WATER_TILE_LOCK: {
    00647       const WaterDrawTileStruct *t = _shiplift_display_seq[GetSection(ti->tile)];
    00648       DrawWaterStuff(ti, t, 0, ti->z > t[3].delta_y ? 24 : 0, true);
    00649     } break;
    00650
    00651     case WATER_TILE_DEPOT:
    00652       DrawWaterClassGround(ti);
    00653       DrawWaterStuff(ti, _shipdepot_display_seq[GetSection(ti->tile)], COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile)), 0, false);
    00654       break;
    00655   }
    00656 }
    00657
    00658 void DrawShipDepotSprite(int x, int y, int image)
    00659 {
    00660   const WaterDrawTileStruct *wdts = _shipdepot_display_seq[image];
    00661
    00662   DrawSprite(wdts++->image, PAL_NONE, x, y);
    00663
    00664   for (; wdts->delta_x != 0x80; wdts++) {
    00665     Point pt = RemapCoords(wdts->delta_x, wdts->delta_y, wdts->delta_z);
    00666     DrawSprite(wdts->image, COMPANY_SPRITE_COLOUR(_local_company), x + pt.x, y + pt.y);
    00667   }
    00668 }
    00669
    00670
    00671 static uint GetSlopeZ_Water(TileIndex tile, uint x, uint y)
    00672 {
    00673   uint z;
    00674   Slope tileh = GetTileSlope(tile, &z);
    00675
    00676   return z + GetPartialZ(x & 0xF, y & 0xF, tileh);
    00677 }
    00678
    00679 static Foundation GetFoundation_Water(TileIndex tile, Slope tileh)
    00680 {
    00681   return FOUNDATION_NONE;
    00682 }
    00683
    00684 static void GetAcceptedCargo_Water(TileIndex tile, AcceptedCargo ac)
    00685 {
    00686   /* not used */
    00687 }
    00688
    00689 static void GetTileDesc_Water(TileIndex tile, TileDesc *td)
    00690 {
    00691   switch (GetWaterTileType(tile)) {
    00692     case WATER_TILE_CLEAR:
    00693       switch (GetWaterClass(tile)) {
    00694         case WATER_CLASS_SEA:   td->str = STR_3804_WATER;     break;
    00695         case WATER_CLASS_CANAL: td->str = STR_LANDINFO_CANAL; break;
    00696         case WATER_CLASS_RIVER: td->str = STR_LANDINFO_RIVER; break;
    00697         default: assert(0); break;
    00698       }
    00699       break;
    00700     case WATER_TILE_COAST: td->str = STR_3805_COAST_OR_RIVERBANK; break;
    00701     case WATER_TILE_LOCK : td->str = STR_LANDINFO_LOCK;           break;
    00702     case WATER_TILE_DEPOT: td->str = STR_3806_SHIP_DEPOT;         break;
    00703     default: assert(0); break;
    00704   }
    00705
    00706   td->owner[0] = GetTileOwner(tile);
    00707 }
    00708
    00709 static void FloodVehicle(Vehicle *v);
    00710
    00717 static Vehicle *FloodVehicleProc(Vehicle *v, void *data)
    00718 {
    00719   byte z = *(byte*)data;
    00720
    00721   if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
    00722   if (v->z_pos > z || (v->vehstatus & VS_CRASHED) != 0) return NULL;
    00723
    00724   FloodVehicle(v);
    00725   return NULL;
    00726 }
    00727
    00733 static void FloodVehicles(TileIndex tile)
    00734 {
    00735   byte z = 0;
    00736
    00737   if (IsTileType(tile, MP_STATION) && IsAirport(tile)) {
    00738     const Station *st = GetStationByTile(tile);
    00739     const AirportFTAClass *airport = st->Airport();
    00740     z = 1 + airport->delta_z;
    00741     for (uint x = 0; x < airport->size_x; x++) {
    00742       for (uint y = 0; y < airport->size_y; y++) {
    00743         tile = TILE_ADDXY(st->airport_tile, x, y);
    00744         FindVehicleOnPos(tile, &z, &FloodVehicleProc);
    00745       }
    00746     }
    00747
    00748     /* No vehicle could be flooded on this airport anymore */
    00749     return;
    00750   }
    00751
    00752   /* if non-uniform stations are disabled, flood some train in this train station (if there is any) */
    00753   if (!_settings_game.station.nonuniform_stations && IsTileType(tile, MP_STATION) && GetStationType(tile) == STATION_RAIL) {
    00754     const Station *st = GetStationByTile(tile);
    00755
    00756     BEGIN_TILE_LOOP(t, st->trainst_w, st->trainst_h, st->train_tile)
    00757       if (st->TileBelongsToRailStation(t)) {
    00758         FindVehicleOnPos(tile, &z, &FloodVehicleProc);
    00759       }
    00760     END_TILE_LOOP(t, st->trainst_w, st->trainst_h, st->train_tile)
    00761
    00762     return;
    00763   }
    00764
    00765   if (!IsBridgeTile(tile)) {
    00766     FindVehicleOnPos(tile, &z, &FloodVehicleProc);
    00767     return;
    00768   }
    00769
    00770   TileIndex end = GetOtherBridgeEnd(tile);
    00771   z = GetBridgeHeight(tile);
    00772
    00773   FindVehicleOnPos(tile, &z, &FloodVehicleProc);
    00774   FindVehicleOnPos(end, &z, &FloodVehicleProc);
    00775 }
    00776
    00777 static void FloodVehicle(Vehicle *v)
    00778 {
    00779   if (!(v->vehstatus & VS_CRASHED)) {
    00780     uint16 pass = 0;
    00781
    00782     if (v->type == VEH_TRAIN || v->type == VEH_ROAD || v->type == VEH_AIRCRAFT) {
    00783       if (v->type == VEH_AIRCRAFT) {
    00784         /* Crashing aircraft are always at z_pos == 1, never on z_pos == 0,
    00785          * because that's always the shadow. Except for the heliport, because
    00786          * that station has a big z_offset for the aircraft. */
    00787         if (!IsTileType(v->tile, MP_STATION) || !IsAirport(v->tile) || GetTileMaxZ(v->tile) != 0) return;
    00788         const Station *st = GetStationByTile(v->tile);
    00789         const AirportFTAClass *airport = st->Airport();
    00790
    00791         if (v->z_pos != airport->delta_z + 1) return;
    00792       }
    00793
    00794       if (v->type != VEH_AIRCRAFT) v = v->First();
    00795
    00796       /* crash all wagons, and count passengers */
    00797       for (Vehicle *u = v; u != NULL; u = u->Next()) {
    00798         if (IsCargoInClass(u->cargo_type, CC_PASSENGERS)) pass += u->cargo.Count();
    00799         u->vehstatus |= VS_CRASHED;
    00800         MarkSingleVehicleDirty(u);
    00801       }
    00802
    00803       switch (v->type) {
    00804         default: NOT_REACHED();
    00805         case VEH_TRAIN:
    00806           if (IsFrontEngine(v)) {
    00807             pass += 4; // driver
    00808             /* FreeTrainTrackReservation() calls GetVehicleTrackdir() that doesn't like crashed vehicles.
    00809              * In this case, v->direction matches v->u.rail.track, so we can do this (it wasn't crashed before) */
    00810             v->vehstatus &= ~VS_CRASHED;
    00811             FreeTrainTrackReservation(v);
    00812             v->vehstatus |= VS_CRASHED;
    00813           }
    00814           v->u.rail.crash_anim_pos = 4000; // max 4440, disappear pretty fast
    00815           InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
    00816           break;
    00817
    00818         case VEH_ROAD:
    00819           if (IsRoadVehFront(v)) pass += 1; // driver
    00820           v->u.road.crashed_ctr = 2000; // max 2220, disappear pretty fast
    00821           InvalidateWindowClassesData(WC_ROADVEH_LIST, 0);
    00822           break;
    00823
    00824         case VEH_AIRCRAFT:
    00825           pass += 2; // driver
    00826           v->u.air.crashed_counter = 9000; // max 10000, disappear pretty fast
    00827           InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0);
    00828           break;
    00829       }
    00830     } else {
    00831       return;
    00832     }
    00833
    00834     InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
    00835     InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
    00836
    00837     AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, AIEventVehicleCrashed::CRASH_FLOODED));
    00838     SetDParam(0, pass);
    00839     AddNewsItem(STR_B006_FLOOD_VEHICLE_DESTROYED,
    00840       NS_ACCIDENT_VEHICLE,
    00841       v->index,
    00842       0);
    00843     CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
    00844     SndPlayVehicleFx(SND_12_EXPLOSION, v);
    00845   }
    00846 }
    00847
    00853 static FloodingBehaviour GetFloodingBehaviour(TileIndex tile)
    00854 {
    00855   /* FLOOD_ACTIVE:  'single-corner-raised'-coast, sea, sea-shipdepots, sea-buoys, sea-docks (water part), rail with flooded halftile, sea-water-industries, sea-oilrigs
    00856    * FLOOD_DRYUP:   coast with more than one corner raised, coast with rail-track, coast with trees
    00857    * FLOOD_PASSIVE: (not used)
    00858    * FLOOD_NONE:    canals, rivers, everything else
    00859    */
    00860   switch (GetTileType(tile)) {
    00861     case MP_WATER:
    00862       if (IsCoast(tile)) {
    00863         Slope tileh = GetTileSlope(tile, NULL);
    00864         return (IsSlopeWithOneCornerRaised(tileh) ? FLOOD_ACTIVE : FLOOD_DRYUP);
    00865       } else {
    00866         return (GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE;
    00867       }
    00868
    00869     case MP_RAILWAY:
    00870       if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
    00871         return (IsSlopeWithOneCornerRaised(GetTileSlope(tile, NULL)) ? FLOOD_ACTIVE : FLOOD_DRYUP);
    00872       }
    00873       return FLOOD_NONE;
    00874
    00875     case MP_TREES:
    00876       return (GetTreeGround(tile) == TREE_GROUND_SHORE ? FLOOD_DRYUP : FLOOD_NONE);
    00877
    00878     case MP_STATION:
    00879       if (IsBuoy(tile) || (IsDock(tile) && GetTileSlope(tile, NULL) == SLOPE_FLAT) || IsOilRig(tile)) {
    00880         return (GetWaterClass(tile) == WATER_CLASS_SEA ? FLOOD_ACTIVE : FLOOD_NONE);
    00881       }
    00882       return FLOOD_NONE;
    00883
    00884     case MP_INDUSTRY:
    00885       return ((IsIndustryTileOnWater(tile) && GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE);
    00886
    00887     default:
    00888       return FLOOD_NONE;
    00889   }
    00890 }
    00891
    00895 void DoFloodTile(TileIndex target)
    00896 {
    00897   assert(!IsTileType(target, MP_WATER));
    00898
    00899   bool flooded = false; // Will be set to true if something is changed.
    00900
    00901   _current_company = OWNER_WATER;
    00902
    00903   Slope tileh = GetTileSlope(target, NULL);
    00904   if (tileh != SLOPE_FLAT) {
    00905     /* make coast.. */
    00906     switch (GetTileType(target)) {
    00907       case MP_RAILWAY: {
    00908         if (!IsPlainRailTile(target)) break;
    00909         FloodVehicles(target);
    00910         flooded = FloodHalftile(target);
    00911         break;
    00912       }
    00913
    00914       case MP_TREES:
    00915         if (!IsSlopeWithOneCornerRaised(tileh)) {
    00916           SetTreeGroundDensity(target, TREE_GROUND_SHORE, 3);
    00917           MarkTileDirtyByTile(target);
    00918           flooded = true;
    00919           break;
    00920         }
    00921       /* FALL THROUGH */
    00922       case MP_CLEAR:
    00923         if (CmdSucceeded(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) {
    00924           MakeShore(target);
    00925           MarkTileDirtyByTile(target);
    00926           flooded = true;
    00927         }
    00928         break;
    00929
    00930       default:
    00931         break;
    00932     }
    00933   } else {
    00934     /* Flood vehicles */
    00935     FloodVehicles(target);
    00936
    00937     /* flood flat tile */
    00938     if (CmdSucceeded(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) {
    00939       MakeWater(target);
    00940       MarkTileDirtyByTile(target);
    00941       flooded = true;
    00942     }
    00943   }
    00944
    00945   if (flooded) {
    00946     /* Mark surrounding canal tiles dirty too to avoid glitches */
    00947     MarkCanalsAndRiversAroundDirty(target);
    00948
    00949     /* update signals if needed */
    00950     UpdateSignalsInBuffer();
    00951   }
    00952
    00953   _current_company = OWNER_NONE;
    00954 }
    00955
    00959 static void DoDryUp(TileIndex tile)
    00960 {
    00961   _current_company = OWNER_WATER;
    00962
    00963   switch (GetTileType(tile)) {
    00964     case MP_RAILWAY:
    00965       assert(IsPlainRailTile(tile));
    00966       assert(GetRailGroundType(tile) == RAIL_GROUND_WATER);
    00967
    00968       RailGroundType new_ground;
    00969       switch (GetTrackBits(tile)) {
    00970         case TRACK_BIT_UPPER: new_ground = RAIL_GROUND_FENCE_HORIZ1; break;
    00971         case TRACK_BIT_LOWER: new_ground = RAIL_GROUND_FENCE_HORIZ2; break;
    00972         case TRACK_BIT_LEFT:  new_ground = RAIL_GROUND_FENCE_VERT1;  break;
    00973         case TRACK_BIT_RIGHT: new_ground = RAIL_GROUND_FENCE_VERT2;  break;
    00974         default: NOT_REACHED();
    00975       }
    00976       SetRailGroundType(tile, new_ground);
    00977       MarkTileDirtyByTile(tile);
    00978       break;
    00979
    00980     case MP_TREES:
    00981       SetTreeGroundDensity(tile, TREE_GROUND_GRASS, 3);
    00982       MarkTileDirtyByTile(tile);
    00983       break;
    00984
    00985     case MP_WATER:
    00986       assert(IsCoast(tile));
    00987
    00988       if (CmdSucceeded(DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) {
    00989         MakeClear(tile, CLEAR_GRASS, 3);
    00990         MarkTileDirtyByTile(tile);
    00991       }
    00992       break;
    00993
    00994     default: NOT_REACHED();
    00995   }
    00996
    00997   _current_company = OWNER_NONE;
    00998 }
    00999
    01006 void TileLoop_Water(TileIndex tile)
    01007 {
    01008   switch (GetFloodingBehaviour(tile)) {
    01009     case FLOOD_ACTIVE:
    01010       for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
    01011         TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir(dir));
    01012         if (dest == INVALID_TILE) continue;
    01013         /* do not try to flood water tiles - increases performance a lot */
    01014         if (IsTileType(dest, MP_WATER)) continue;
    01015
    01016         uint z_dest;
    01017         Slope slope_dest = GetFoundationSlope(dest, &z_dest) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
    01018         if (z_dest > 0) continue;
    01019
    01020         if (!HasBit(_flood_from_dirs[slope_dest], ReverseDir(dir))) continue;
    01021
    01022         DoFloodTile(dest);
    01023       }
    01024       break;
    01025
    01026     case FLOOD_DRYUP: {
    01027       Slope slope_here = GetFoundationSlope(tile, NULL) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
    01028       uint check_dirs = _flood_from_dirs[slope_here];
    01029       uint dir;
    01030       FOR_EACH_SET_BIT(dir, check_dirs) {
    01031         TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir((Direction)dir));
    01032         if (dest == INVALID_TILE) continue;
    01033
    01034         FloodingBehaviour dest_behaviour = GetFloodingBehaviour(dest);
    01035         if ((dest_behaviour == FLOOD_ACTIVE) || (dest_behaviour == FLOOD_PASSIVE)) return;
    01036       }
    01037       DoDryUp(tile);
    01038       break;
    01039     }
    01040
    01041     default: return;
    01042   }
    01043 }
    01044
    01045 void ConvertGroundTilesIntoWaterTiles()
    01046 {
    01047   TileIndex tile;
    01048   uint z;
    01049   Slope slope;
    01050
    01051   for (tile = 0; tile < MapSize(); ++tile) {
    01052     slope = GetTileSlope(tile, &z);
    01053     if (IsTileType(tile, MP_CLEAR) && z == 0) {
    01054       /* Make both water for tiles at level 0
    01055        * and make shore, as that looks much better
    01056        * during the generation. */
    01057       switch (slope) {
    01058         case SLOPE_FLAT:
    01059           MakeWater(tile);
    01060           break;
    01061
    01062         case SLOPE_N:
    01063         case SLOPE_E:
    01064         case SLOPE_S:
    01065         case SLOPE_W:
    01066           MakeShore(tile);
    01067           break;
    01068
    01069         default:
    01070           uint check_dirs = _flood_from_dirs[slope & ~SLOPE_STEEP];
    01071           uint dir;
    01072           FOR_EACH_SET_BIT(dir, check_dirs) {
    01073             TileIndex dest = TILE_ADD(tile, TileOffsByDir((Direction)dir));
    01074             Slope slope_dest = GetTileSlope(dest, NULL) & ~SLOPE_STEEP;
    01075             if (slope_dest == SLOPE_FLAT || IsSlopeWithOneCornerRaised(slope_dest)) {
    01076               MakeShore(tile);
    01077               break;
    01078             }
    01079           }
    01080           break;
    01081       }
    01082     }
    01083   }
    01084 }
    01085
    01086 static TrackStatus GetTileTrackStatus_Water(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
    01087 {
    01088   static const byte coast_tracks[] = {0, 32, 4, 0, 16, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0};
    01089
    01090   TrackBits ts;
    01091
    01092   if (mode != TRANSPORT_WATER) return 0;
    01093
    01094   switch (GetWaterTileType(tile)) {
    01095     case WATER_TILE_CLEAR: ts = (GetTileSlope(tile, NULL) == SLOPE_FLAT) ? TRACK_BIT_ALL : TRACK_BIT_NONE; break;
    01096     case WATER_TILE_COAST: ts = (TrackBits)coast_tracks[GetTileSlope(tile, NULL) & 0xF]; break;
    01097     case WATER_TILE_LOCK:  ts = DiagDirToDiagTrackBits(GetLockDirection(tile)); break;
    01098     case WATER_TILE_DEPOT: ts = AxisToTrackBits(GetShipDepotAxis(tile)); break;
    01099     default: return 0;
    01100   }
    01101   if (TileX(tile) == 0) {
    01102     /* NE border: remove tracks that connects NE tile edge */
    01103     ts &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
    01104   }
    01105   if (TileY(tile) == 0) {
    01106     /* NW border: remove tracks that connects NW tile edge */
    01107     ts &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
    01108   }
    01109   return CombineTrackStatus(TrackBitsToTrackdirBits(ts), TRACKDIR_BIT_NONE);
    01110 }
    01111
    01112 static bool ClickTile_Water(TileIndex tile)
    01113 {
    01114   if (GetWaterTileType(tile) == WATER_TILE_DEPOT) {
    01115     TileIndex tile2 = GetOtherShipDepotTile(tile);
    01116
    01117     ShowDepotWindow(tile < tile2 ? tile : tile2, VEH_SHIP);
    01118     return true;
    01119   }
    01120   return false;
    01121 }
    01122
    01123 static void ChangeTileOwner_Water(TileIndex tile, Owner old_owner, Owner new_owner)
    01124 {
    01125   if (!IsTileOwner(tile, old_owner)) return;
    01126
    01127   if (new_owner != INVALID_OWNER) {
    01128     SetTileOwner(tile, new_owner);
    01129     return;
    01130   }
    01131
    01132   /* Remove depot */
    01133   if (IsShipDepot(tile)) DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
    01134
    01135   /* Set owner of canals and locks ... and also canal under dock there was before.
    01136    * Check if the new owner after removing depot isn't OWNER_WATER. */
    01137   if (IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
    01138 }
    01139
    01140 static VehicleEnterTileStatus VehicleEnter_Water(Vehicle *v, TileIndex tile, int x, int y)
    01141 {
    01142   return VETSB_CONTINUE;
    01143 }
    01144
    01145 static CommandCost TerraformTile_Water(TileIndex tile, DoCommandFlag flags, uint z_new, Slope tileh_new)
    01146 {
    01147   /* Canals can't be terraformed */
    01148   if (IsWaterTile(tile) && IsCanal(tile)) return_cmd_error(STR_MUST_DEMOLISH_CANAL_FIRST);
    01149
    01150   return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
    01151 }
    01152
    01153
    01154 extern const TileTypeProcs _tile_type_water_procs = {
    01155   DrawTile_Water,           // draw_tile_proc
    01156   GetSlopeZ_Water,          // get_slope_z_proc
    01157   ClearTile_Water,          // clear_tile_proc
    01158   GetAcceptedCargo_Water,   // get_accepted_cargo_proc
    01159   GetTileDesc_Water,        // get_tile_desc_proc
    01160   GetTileTrackStatus_Water, // get_tile_track_status_proc
    01161   ClickTile_Water,          // click_tile_proc
    01162   NULL,                     // animate_tile_proc
    01163   TileLoop_Water,           // tile_loop_clear
    01164   ChangeTileOwner_Water,    // change_tile_owner_clear
    01165   NULL,                     // get_produced_cargo_proc
    01166   VehicleEnter_Water,       // vehicle_enter_tile_proc
    01167   GetFoundation_Water,      // get_foundation_proc
    01168   TerraformTile_Water,      // terraform_tile_proc
    01169 };