00001
00002
00003
00004
00005
00006
00007
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 "functions.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 "effectvehicle_func.h"
00033 #include "tunnelbridge_map.h"
00034 #include "station_base.h"
00035 #include "ai/ai.hpp"
00036 #include "core/random_func.hpp"
00037
00038 #include "table/sprites.h"
00039 #include "table/strings.h"
00040
00044 enum FloodingBehaviour {
00045 FLOOD_NONE,
00046 FLOOD_ACTIVE,
00047 FLOOD_PASSIVE,
00048 FLOOD_DRYUP,
00049 };
00050
00054 static const uint8 _flood_from_dirs[] = {
00055 (1 << DIR_NW) | (1 << DIR_SW) | (1 << DIR_SE) | (1 << DIR_NE),
00056 (1 << DIR_NE) | (1 << DIR_SE),
00057 (1 << DIR_NW) | (1 << DIR_NE),
00058 (1 << DIR_NE),
00059 (1 << DIR_NW) | (1 << DIR_SW),
00060 0,
00061 (1 << DIR_NW),
00062 (1 << DIR_N ) | (1 << DIR_NW) | (1 << DIR_NE),
00063 (1 << DIR_SW) | (1 << DIR_SE),
00064 (1 << DIR_SE),
00065 0,
00066 (1 << DIR_E ) | (1 << DIR_NE) | (1 << DIR_SE),
00067 (1 << DIR_SW),
00068 (1 << DIR_S ) | (1 << DIR_SW) | (1 << DIR_SE),
00069 (1 << DIR_W ) | (1 << DIR_SW) | (1 << DIR_NW),
00070 };
00071
00078 static inline void MarkTileDirtyIfCanalOrRiver(TileIndex tile)
00079 {
00080 if (IsTileType(tile, MP_WATER) && (IsCanal(tile) || IsRiver(tile))) MarkTileDirtyByTile(tile);
00081 }
00082
00089 static void MarkCanalsAndRiversAroundDirty(TileIndex tile)
00090 {
00091 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
00092 MarkTileDirtyIfCanalOrRiver(tile + TileOffsByDir(dir));
00093 }
00094 }
00095
00096
00105 CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00106 {
00107 TileIndex tile2;
00108
00109 CommandCost ret;
00110
00111 Axis axis = Extract<Axis, 0>(p1);
00112
00113 tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
00114
00115 if (!IsWaterTile(tile) || !IsWaterTile(tile2)) {
00116 return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER);
00117 }
00118
00119 if (IsBridgeAbove(tile) || IsBridgeAbove(tile2)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
00120
00121 if (GetTileSlope(tile, NULL) != SLOPE_FLAT || GetTileSlope(tile2, NULL) != SLOPE_FLAT) {
00122
00123 return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
00124 }
00125
00126 WaterClass wc1 = GetWaterClass(tile);
00127 WaterClass wc2 = GetWaterClass(tile2);
00128 ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00129 if (ret.Failed()) return CMD_ERROR;
00130 ret = DoCommand(tile2, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00131 if (ret.Failed()) return CMD_ERROR;
00132
00133 if (!Depot::CanAllocateItem()) return CMD_ERROR;
00134
00135 if (flags & DC_EXEC) {
00136 Depot *depot = new Depot(tile);
00137 depot->town_index = ClosestTownFromTile(tile, UINT_MAX)->index;
00138
00139 MakeShipDepot(tile, _current_company, depot->index, DEPOT_NORTH, axis, wc1);
00140 MakeShipDepot(tile2, _current_company, depot->index, DEPOT_SOUTH, axis, wc2);
00141 MarkTileDirtyByTile(tile);
00142 MarkTileDirtyByTile(tile2);
00143 }
00144
00145 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_DEPOT_SHIP]);
00146 }
00147
00148 void MakeWaterKeepingClass(TileIndex tile, Owner o)
00149 {
00150 assert(IsTileType(tile, MP_WATER) || (IsTileType(tile, MP_STATION) && (IsBuoy(tile) || IsDock(tile) || IsOilRig(tile))) || IsTileType(tile, MP_INDUSTRY));
00151
00152 WaterClass wc = GetWaterClass(tile);
00153
00154
00155 uint z;
00156 if (GetTileSlope(tile, &z) != SLOPE_FLAT) wc = WATER_CLASS_INVALID;
00157
00158 if (wc == WATER_CLASS_SEA && z > 0) wc = WATER_CLASS_CANAL;
00159
00160 switch (wc) {
00161 case WATER_CLASS_SEA: MakeSea(tile); break;
00162 case WATER_CLASS_CANAL: MakeCanal(tile, o, Random()); break;
00163 case WATER_CLASS_RIVER: MakeRiver(tile, Random()); break;
00164 default: DoClearSquare(tile); break;
00165 }
00166 }
00167
00168 static CommandCost RemoveShipDepot(TileIndex tile, DoCommandFlag flags)
00169 {
00170 if (!IsShipDepot(tile)) return CMD_ERROR;
00171 if (!CheckTileOwnership(tile)) return CMD_ERROR;
00172
00173 TileIndex tile2 = GetOtherShipDepotTile(tile);
00174
00175
00176 if (!(flags & DC_BANKRUPT)) {
00177 if (!EnsureNoVehicleOnGround(tile) || !EnsureNoVehicleOnGround(tile2)) return CMD_ERROR;
00178 }
00179
00180 if (flags & DC_EXEC) {
00181
00182 delete Depot::GetByTile(tile);
00183
00184 MakeWaterKeepingClass(tile, GetTileOwner(tile));
00185 MakeWaterKeepingClass(tile2, GetTileOwner(tile2));
00186 MarkTileDirtyByTile(tile);
00187 MarkTileDirtyByTile(tile2);
00188 }
00189
00190 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_SHIP]);
00191 }
00192
00194 static CommandCost DoBuildShiplift(TileIndex tile, DiagDirection dir, DoCommandFlag flags)
00195 {
00196 CommandCost ret;
00197 int delta;
00198
00199
00200 ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00201 if (ret.Failed()) return CMD_ERROR;
00202
00203 delta = TileOffsByDiagDir(dir);
00204
00205 WaterClass wc_lower = IsWaterTile(tile - delta) ? GetWaterClass(tile - delta) : WATER_CLASS_CANAL;
00206
00207 ret = DoCommand(tile - delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00208 if (ret.Failed()) return CMD_ERROR;
00209 if (GetTileSlope(tile - delta, NULL) != SLOPE_FLAT) {
00210 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
00211 }
00212
00213
00214 WaterClass wc_upper = IsWaterTile(tile + delta) ? GetWaterClass(tile + delta) : WATER_CLASS_CANAL;
00215
00216 ret = DoCommand(tile + delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00217 if (ret.Failed()) return CMD_ERROR;
00218 if (GetTileSlope(tile + delta, NULL) != SLOPE_FLAT) {
00219 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
00220 }
00221
00222 if ((MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) ||
00223 (MayHaveBridgeAbove(tile - delta) && IsBridgeAbove(tile - delta)) ||
00224 (MayHaveBridgeAbove(tile + delta) && IsBridgeAbove(tile + delta))) {
00225 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
00226 }
00227
00228 if (flags & DC_EXEC) {
00229 MakeLock(tile, _current_company, dir, wc_lower, wc_upper);
00230 MarkTileDirtyByTile(tile);
00231 MarkTileDirtyByTile(tile - delta);
00232 MarkTileDirtyByTile(tile + delta);
00233 MarkCanalsAndRiversAroundDirty(tile - delta);
00234 MarkCanalsAndRiversAroundDirty(tile + delta);
00235 }
00236
00237 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_WATER] * 22 >> 3);
00238 }
00239
00240 static CommandCost RemoveShiplift(TileIndex tile, DoCommandFlag flags)
00241 {
00242 TileIndexDiff delta = TileOffsByDiagDir(GetLockDirection(tile));
00243
00244 if (!CheckTileOwnership(tile) && GetTileOwner(tile) != OWNER_NONE) return CMD_ERROR;
00245
00246
00247 if (!EnsureNoVehicleOnGround(tile) || !EnsureNoVehicleOnGround(tile + delta) || !EnsureNoVehicleOnGround(tile - delta))
00248 return CMD_ERROR;
00249
00250 if (flags & DC_EXEC) {
00251 DoClearSquare(tile);
00252 MakeWaterKeepingClass(tile + delta, GetTileOwner(tile));
00253 MakeWaterKeepingClass(tile - delta, GetTileOwner(tile));
00254 MarkTileDirtyByTile(tile - delta);
00255 MarkTileDirtyByTile(tile + delta);
00256 MarkCanalsAndRiversAroundDirty(tile - delta);
00257 MarkCanalsAndRiversAroundDirty(tile + delta);
00258 }
00259
00260 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_WATER] * 2);
00261 }
00262
00271 CommandCost CmdBuildLock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00272 {
00273 DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile, NULL));
00274 if (dir == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
00275
00276
00277 if (IsWaterTile(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
00278
00279 return DoBuildShiplift(tile, dir, flags);
00280 }
00281
00290 CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00291 {
00292 CommandCost cost(EXPENSES_CONSTRUCTION);
00293
00294 if (p1 >= MapSize()) return CMD_ERROR;
00295
00296
00297 if (p2 != 0 && _game_mode != GM_EDITOR) return CMD_ERROR;
00298
00299 TileArea ta(tile, p1);
00300
00301
00302 if (_game_mode != GM_EDITOR && ta.w != 1 && ta.h != 1) return CMD_ERROR;
00303
00304 TILE_AREA_LOOP(tile, ta) {
00305 CommandCost ret;
00306
00307 Slope slope = GetTileSlope(tile, NULL);
00308 if (slope != SLOPE_FLAT && (p2 != 2 || !IsInclinedSlope(slope))) {
00309 return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
00310 }
00311
00312
00313 if (IsTileType(tile, MP_WATER) && (!IsTileOwner(tile, OWNER_WATER) || p2 == 1)) continue;
00314
00315 ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00316 if (ret.Failed()) return ret;
00317 cost.AddCost(ret);
00318
00319 if (flags & DC_EXEC) {
00320 if (TileHeight(tile) == 0 && p2 == 1) {
00321 MakeSea(tile);
00322 } else if (p2 == 2) {
00323 MakeRiver(tile, Random());
00324 } else {
00325 MakeCanal(tile, _current_company, Random());
00326 }
00327 MarkTileDirtyByTile(tile);
00328 MarkCanalsAndRiversAroundDirty(tile);
00329 }
00330
00331 cost.AddCost(_price[PR_CLEAR_WATER]);
00332 }
00333
00334 if (cost.GetCost() == 0) {
00335 return_cmd_error(STR_ERROR_ALREADY_BUILT);
00336 } else {
00337 return cost;
00338 }
00339 }
00340
00341 static CommandCost ClearTile_Water(TileIndex tile, DoCommandFlag flags)
00342 {
00343 switch (GetWaterTileType(tile)) {
00344 case WATER_TILE_CLEAR:
00345 if (flags & DC_NO_WATER) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER);
00346
00347
00348 if (!_settings_game.construction.freeform_edges && (!IsInsideMM(TileX(tile), 1, MapMaxX() - 1) ||
00349 !IsInsideMM(TileY(tile), 1, MapMaxY() - 1))) {
00350 return_cmd_error(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP);
00351 }
00352
00353
00354 if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
00355
00356 if (GetTileOwner(tile) != OWNER_WATER && GetTileOwner(tile) != OWNER_NONE && !CheckTileOwnership(tile)) return CMD_ERROR;
00357
00358 if (flags & DC_EXEC) {
00359 DoClearSquare(tile);
00360 MarkCanalsAndRiversAroundDirty(tile);
00361 }
00362 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_WATER]);
00363
00364 case WATER_TILE_COAST: {
00365 Slope slope = GetTileSlope(tile, NULL);
00366
00367
00368 if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
00369
00370 if (flags & DC_EXEC) {
00371 DoClearSquare(tile);
00372 MarkCanalsAndRiversAroundDirty(tile);
00373 }
00374 if (IsSlopeWithOneCornerRaised(slope)) {
00375 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_WATER]);
00376 } else {
00377 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_ROUGH]);
00378 }
00379 }
00380
00381 case WATER_TILE_LOCK: {
00382 static const TileIndexDiffC _shiplift_tomiddle_offs[] = {
00383 { 0, 0}, {0, 0}, { 0, 0}, {0, 0},
00384 {-1, 0}, {0, 1}, { 1, 0}, {0, -1},
00385 { 1, 0}, {0, -1}, {-1, 0}, {0, 1},
00386 };
00387
00388 if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
00389 if (_current_company == OWNER_WATER) return CMD_ERROR;
00390
00391 return RemoveShiplift(tile + ToTileIndexDiff(_shiplift_tomiddle_offs[GetSection(tile)]), flags);
00392 }
00393
00394 case WATER_TILE_DEPOT:
00395 if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
00396 return RemoveShipDepot(tile, flags);
00397
00398 default:
00399 NOT_REACHED();
00400 }
00401 }
00402
00411 static bool IsWateredTile(TileIndex tile, Direction from)
00412 {
00413 switch (GetTileType(tile)) {
00414 case MP_WATER:
00415 switch (GetWaterTileType(tile)) {
00416 default: NOT_REACHED();
00417 case WATER_TILE_DEPOT: case WATER_TILE_CLEAR: return true;
00418 case WATER_TILE_LOCK: return DiagDirToAxis(GetLockDirection(tile)) == DiagDirToAxis(DirToDiagDir(from));
00419
00420 case WATER_TILE_COAST:
00421 switch (GetTileSlope(tile, NULL)) {
00422 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
00423 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
00424 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
00425 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
00426 default: return false;
00427 }
00428 }
00429
00430 case MP_RAILWAY:
00431 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
00432 assert(IsPlainRail(tile));
00433 switch (GetTileSlope(tile, NULL)) {
00434 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
00435 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
00436 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
00437 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
00438 default: return false;
00439 }
00440 }
00441 return false;
00442
00443 case MP_STATION:
00444 if (IsOilRig(tile)) {
00445
00446
00447 TileIndex src_tile = tile + TileOffsByDir(from);
00448 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
00449 (IsTileType(src_tile, MP_INDUSTRY))) return true;
00450
00451 return GetWaterClass(tile) != WATER_CLASS_INVALID;
00452 }
00453 return (IsDock(tile) && GetTileSlope(tile, NULL) == SLOPE_FLAT) || IsBuoy(tile);
00454
00455 case MP_INDUSTRY: {
00456
00457
00458 TileIndex src_tile = tile + TileOffsByDir(from);
00459 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
00460 (IsTileType(src_tile, MP_INDUSTRY) && GetIndustryIndex(src_tile) == GetIndustryIndex(tile))) return true;
00461
00462 return IsIndustryTileOnWater(tile);
00463 }
00464
00465 case MP_TUNNELBRIDGE: return GetTunnelBridgeTransportType(tile) == TRANSPORT_WATER && ReverseDiagDir(GetTunnelBridgeDirection(tile)) == DirToDiagDir(from);
00466
00467 default: return false;
00468 }
00469 }
00470
00471 static void DrawWaterEdges(SpriteID base, TileIndex tile)
00472 {
00473 uint wa;
00474
00475
00476 wa = IsWateredTile(TILE_ADDXY(tile, -1, 0), DIR_SW) << 0;
00477 wa += IsWateredTile(TILE_ADDXY(tile, 0, 1), DIR_NW) << 1;
00478 wa += IsWateredTile(TILE_ADDXY(tile, 1, 0), DIR_NE) << 2;
00479 wa += IsWateredTile(TILE_ADDXY(tile, 0, -1), DIR_SE) << 3;
00480
00481 if (!(wa & 1)) DrawGroundSprite(base, PAL_NONE);
00482 if (!(wa & 2)) DrawGroundSprite(base + 1, PAL_NONE);
00483 if (!(wa & 4)) DrawGroundSprite(base + 2, PAL_NONE);
00484 if (!(wa & 8)) DrawGroundSprite(base + 3, PAL_NONE);
00485
00486
00487 switch (wa & 0x03) {
00488 case 0: DrawGroundSprite(base + 4, PAL_NONE); break;
00489 case 3: if (!IsWateredTile(TILE_ADDXY(tile, -1, 1), DIR_W)) DrawGroundSprite(base + 8, PAL_NONE); break;
00490 }
00491
00492
00493 switch (wa & 0x06) {
00494 case 0: DrawGroundSprite(base + 5, PAL_NONE); break;
00495 case 6: if (!IsWateredTile(TILE_ADDXY(tile, 1, 1), DIR_N)) DrawGroundSprite(base + 9, PAL_NONE); break;
00496 }
00497
00498
00499 switch (wa & 0x0C) {
00500 case 0: DrawGroundSprite(base + 6, PAL_NONE); break;
00501 case 12: if (!IsWateredTile(TILE_ADDXY(tile, 1, -1), DIR_E)) DrawGroundSprite(base + 10, PAL_NONE); break;
00502 }
00503
00504
00505 switch (wa & 0x09) {
00506 case 0: DrawGroundSprite(base + 7, PAL_NONE); break;
00507 case 9: if (!IsWateredTile(TILE_ADDXY(tile, -1, -1), DIR_S)) DrawGroundSprite(base + 11, PAL_NONE); break;
00508 }
00509 }
00510
00512 static void DrawSeaWater(TileIndex tile)
00513 {
00514 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
00515 }
00516
00518 static void DrawCanalWater(TileIndex tile)
00519 {
00520 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
00521
00522
00523 SpriteID dikes_base = GetCanalSprite(CF_DIKES, tile);
00524 if (dikes_base == 0) dikes_base = SPR_CANAL_DIKES_BASE;
00525
00526 DrawWaterEdges(dikes_base, tile);
00527 }
00528
00529 struct LocksDrawTileStruct {
00530 int8 delta_x, delta_y, delta_z;
00531 byte width, height, depth;
00532 SpriteID image;
00533 };
00534
00535 #include "table/water_land.h"
00536
00537 static void DrawWaterStuff(const TileInfo *ti, const WaterDrawTileStruct *wdts,
00538 PaletteID palette, uint base, bool draw_ground)
00539 {
00540 SpriteID image;
00541 SpriteID water_base = GetCanalSprite(CF_WATERSLOPE, ti->tile);
00542 SpriteID locks_base = GetCanalSprite(CF_LOCKS, ti->tile);
00543
00544
00545 if (water_base == 0) water_base = SPR_CANALS_BASE;
00546 if (locks_base == 0) {
00547 locks_base = SPR_SHIPLIFT_BASE;
00548 } else {
00549
00550 base = 0;
00551 }
00552
00553 image = wdts++->image;
00554 if (image < 4) image += water_base;
00555 if (draw_ground) DrawGroundSprite(image, PAL_NONE);
00556
00557
00558 if (IsInvisibilitySet(TO_BUILDINGS)) return;
00559
00560 for (; wdts->delta_x != 0x80; wdts++) {
00561 AddSortableSpriteToDraw(wdts->image + base + ((wdts->image < 24) ? locks_base : 0), palette,
00562 ti->x + wdts->delta_x, ti->y + wdts->delta_y,
00563 wdts->size_x, wdts->size_y,
00564 wdts->size_z, ti->z + wdts->delta_z,
00565 IsTransparencySet(TO_BUILDINGS));
00566 }
00567 }
00568
00569 static void DrawRiverWater(const TileInfo *ti)
00570 {
00571 SpriteID image = SPR_FLAT_WATER_TILE;
00572 SpriteID edges_base = GetCanalSprite(CF_RIVER_EDGE, ti->tile);
00573
00574 if (ti->tileh != SLOPE_FLAT) {
00575 image = GetCanalSprite(CF_RIVER_SLOPE, ti->tile);
00576 if (image == 0) {
00577 switch (ti->tileh) {
00578 case SLOPE_NW: image = SPR_WATER_SLOPE_Y_DOWN; break;
00579 case SLOPE_SW: image = SPR_WATER_SLOPE_X_UP; break;
00580 case SLOPE_SE: image = SPR_WATER_SLOPE_Y_UP; break;
00581 case SLOPE_NE: image = SPR_WATER_SLOPE_X_DOWN; break;
00582 default: image = SPR_FLAT_WATER_TILE; break;
00583 }
00584 } else {
00585 switch (ti->tileh) {
00586 default: NOT_REACHED();
00587 case SLOPE_SE: edges_base += 12; break;
00588 case SLOPE_NE: image += 1; edges_base += 24; break;
00589 case SLOPE_SW: image += 2; edges_base += 36; break;
00590 case SLOPE_NW: image += 3; edges_base += 48; break;
00591 }
00592 }
00593 }
00594
00595 DrawGroundSprite(image, PAL_NONE);
00596
00597
00598 if (edges_base > 48) DrawWaterEdges(edges_base, ti->tile);
00599 }
00600
00601 void DrawShoreTile(Slope tileh)
00602 {
00603
00604
00605 static const byte tileh_to_shoresprite[32] = {
00606 0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0,
00607 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 15, 0,
00608 };
00609
00610 assert(!IsHalftileSlope(tileh));
00611 assert(tileh != SLOPE_FLAT);
00612
00613 assert((tileh != SLOPE_EW) && (tileh != SLOPE_NS));
00614
00615 DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[tileh], PAL_NONE);
00616 }
00617
00618 void DrawWaterClassGround(const TileInfo *ti)
00619 {
00620 switch (GetWaterClass(ti->tile)) {
00621 case WATER_CLASS_SEA: DrawSeaWater(ti->tile); break;
00622 case WATER_CLASS_CANAL: DrawCanalWater(ti->tile); break;
00623 case WATER_CLASS_RIVER: DrawRiverWater(ti); break;
00624 default: NOT_REACHED();
00625 }
00626 }
00627
00628 static void DrawTile_Water(TileInfo *ti)
00629 {
00630 switch (GetWaterTileType(ti->tile)) {
00631 case WATER_TILE_CLEAR:
00632 DrawWaterClassGround(ti);
00633 DrawBridgeMiddle(ti);
00634 break;
00635
00636 case WATER_TILE_COAST: {
00637 DrawShoreTile(ti->tileh);
00638 DrawBridgeMiddle(ti);
00639 } break;
00640
00641 case WATER_TILE_LOCK: {
00642 const WaterDrawTileStruct *t = _shiplift_display_seq[GetSection(ti->tile)];
00643 DrawWaterStuff(ti, t, 0, ti->z > t[3].delta_y ? 24 : 0, true);
00644 } break;
00645
00646 case WATER_TILE_DEPOT:
00647 DrawWaterClassGround(ti);
00648 DrawWaterStuff(ti, _shipdepot_display_seq[GetSection(ti->tile)], COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile)), 0, false);
00649 break;
00650 }
00651 }
00652
00653 void DrawShipDepotSprite(int x, int y, int image)
00654 {
00655 const WaterDrawTileStruct *wdts = _shipdepot_display_seq[image];
00656
00657 DrawSprite(wdts++->image, PAL_NONE, x, y);
00658
00659 for (; wdts->delta_x != 0x80; wdts++) {
00660 Point pt = RemapCoords(wdts->delta_x, wdts->delta_y, wdts->delta_z);
00661 DrawSprite(wdts->image, COMPANY_SPRITE_COLOUR(_local_company), x + pt.x, y + pt.y);
00662 }
00663 }
00664
00665
00666 static uint GetSlopeZ_Water(TileIndex tile, uint x, uint y)
00667 {
00668 uint z;
00669 Slope tileh = GetTileSlope(tile, &z);
00670
00671 return z + GetPartialZ(x & 0xF, y & 0xF, tileh);
00672 }
00673
00674 static Foundation GetFoundation_Water(TileIndex tile, Slope tileh)
00675 {
00676 return FOUNDATION_NONE;
00677 }
00678
00679 static void GetTileDesc_Water(TileIndex tile, TileDesc *td)
00680 {
00681 switch (GetWaterTileType(tile)) {
00682 case WATER_TILE_CLEAR:
00683 switch (GetWaterClass(tile)) {
00684 case WATER_CLASS_SEA: td->str = STR_LAI_WATER_DESCRIPTION_WATER; break;
00685 case WATER_CLASS_CANAL: td->str = STR_LAI_WATER_DESCRIPTION_CANAL; break;
00686 case WATER_CLASS_RIVER: td->str = STR_LAI_WATER_DESCRIPTION_RIVER; break;
00687 default: NOT_REACHED(); break;
00688 }
00689 break;
00690 case WATER_TILE_COAST: td->str = STR_LAI_WATER_DESCRIPTION_COAST_OR_RIVERBANK; break;
00691 case WATER_TILE_LOCK : td->str = STR_LAI_WATER_DESCRIPTION_LOCK; break;
00692 case WATER_TILE_DEPOT: td->str = STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT; break;
00693 default: NOT_REACHED(); break;
00694 }
00695
00696 td->owner[0] = GetTileOwner(tile);
00697 }
00698
00699 static void FloodVehicle(Vehicle *v);
00700
00707 static Vehicle *FloodVehicleProc(Vehicle *v, void *data)
00708 {
00709 byte z = *(byte*)data;
00710
00711 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00712 if (v->z_pos > z || (v->vehstatus & VS_CRASHED) != 0) return NULL;
00713
00714 FloodVehicle(v);
00715 return NULL;
00716 }
00717
00723 static void FloodVehicles(TileIndex tile)
00724 {
00725 byte z = 0;
00726
00727 if (IsAirportTile(tile)) {
00728 const Station *st = Station::GetByTile(tile);
00729 const AirportSpec *as = st->GetAirportSpec();
00730 z = 1 + st->Airport()->delta_z;
00731 for (uint x = 0; x < as->size_x; x++) {
00732 for (uint y = 0; y < as->size_y; y++) {
00733 tile = TILE_ADDXY(st->airport_tile, x, y);
00734 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00735 }
00736 }
00737
00738
00739 return;
00740 }
00741
00742
00743 if (!_settings_game.station.nonuniform_stations && IsTileType(tile, MP_STATION) && GetStationType(tile) == STATION_RAIL) {
00744 const Station *st = Station::GetByTile(tile);
00745
00746 TILE_AREA_LOOP(t, st->train_station) {
00747 if (st->TileBelongsToRailStation(t)) {
00748 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00749 }
00750 }
00751
00752 return;
00753 }
00754
00755 if (!IsBridgeTile(tile)) {
00756 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00757 return;
00758 }
00759
00760 TileIndex end = GetOtherBridgeEnd(tile);
00761 z = GetBridgeHeight(tile);
00762
00763 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00764 FindVehicleOnPos(end, &z, &FloodVehicleProc);
00765 }
00766
00767 static void FloodVehicle(Vehicle *v)
00768 {
00769 if ((v->vehstatus & VS_CRASHED) != 0) return;
00770 if (v->type != VEH_TRAIN && v->type != VEH_ROAD && v->type != VEH_AIRCRAFT) return;
00771
00772 if (v->type == VEH_AIRCRAFT) {
00773
00774
00775
00776 if (!IsAirportTile(v->tile) || GetTileMaxZ(v->tile) != 0) return;
00777 const Station *st = Station::GetByTile(v->tile);
00778 const AirportFTAClass *airport = st->Airport();
00779
00780 if (v->z_pos != airport->delta_z + 1) return;
00781 } else {
00782 v = v->First();
00783 }
00784
00785 uint pass = v->Crash(true);
00786
00787 AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, AIEventVehicleCrashed::CRASH_FLOODED));
00788 SetDParam(0, pass);
00789 AddVehicleNewsItem(STR_NEWS_DISASTER_FLOOD_VEHICLE,
00790 NS_ACCIDENT,
00791 v->index);
00792 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
00793 SndPlayVehicleFx(SND_12_EXPLOSION, v);
00794 }
00795
00801 static FloodingBehaviour GetFloodingBehaviour(TileIndex tile)
00802 {
00803
00804
00805
00806
00807
00808 switch (GetTileType(tile)) {
00809 case MP_WATER:
00810 if (IsCoast(tile)) {
00811 Slope tileh = GetTileSlope(tile, NULL);
00812 return (IsSlopeWithOneCornerRaised(tileh) ? FLOOD_ACTIVE : FLOOD_DRYUP);
00813 } else {
00814 return (GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE;
00815 }
00816
00817 case MP_RAILWAY:
00818 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
00819 return (IsSlopeWithOneCornerRaised(GetTileSlope(tile, NULL)) ? FLOOD_ACTIVE : FLOOD_DRYUP);
00820 }
00821 return FLOOD_NONE;
00822
00823 case MP_TREES:
00824 return (GetTreeGround(tile) == TREE_GROUND_SHORE ? FLOOD_DRYUP : FLOOD_NONE);
00825
00826 case MP_STATION:
00827 if (IsBuoy(tile) || (IsDock(tile) && GetTileSlope(tile, NULL) == SLOPE_FLAT) || IsOilRig(tile)) {
00828 return (GetWaterClass(tile) == WATER_CLASS_SEA ? FLOOD_ACTIVE : FLOOD_NONE);
00829 }
00830 return FLOOD_NONE;
00831
00832 case MP_INDUSTRY:
00833 return ((IsIndustryTileOnWater(tile) && GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE);
00834
00835 default:
00836 return FLOOD_NONE;
00837 }
00838 }
00839
00843 void DoFloodTile(TileIndex target)
00844 {
00845 assert(!IsTileType(target, MP_WATER));
00846
00847 bool flooded = false;
00848
00849 _current_company = OWNER_WATER;
00850
00851 Slope tileh = GetTileSlope(target, NULL);
00852 if (tileh != SLOPE_FLAT) {
00853
00854 switch (GetTileType(target)) {
00855 case MP_RAILWAY: {
00856 if (!IsPlainRail(target)) break;
00857 FloodVehicles(target);
00858 flooded = FloodHalftile(target);
00859 break;
00860 }
00861
00862 case MP_TREES:
00863 if (!IsSlopeWithOneCornerRaised(tileh)) {
00864 SetTreeGroundDensity(target, TREE_GROUND_SHORE, 3);
00865 MarkTileDirtyByTile(target);
00866 flooded = true;
00867 break;
00868 }
00869
00870 case MP_CLEAR:
00871 if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
00872 MakeShore(target);
00873 MarkTileDirtyByTile(target);
00874 flooded = true;
00875 }
00876 break;
00877
00878 default:
00879 break;
00880 }
00881 } else {
00882
00883 FloodVehicles(target);
00884
00885
00886 if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
00887 MakeSea(target);
00888 MarkTileDirtyByTile(target);
00889 flooded = true;
00890 }
00891 }
00892
00893 if (flooded) {
00894
00895 MarkCanalsAndRiversAroundDirty(target);
00896
00897
00898 UpdateSignalsInBuffer();
00899 }
00900
00901 _current_company = OWNER_NONE;
00902 }
00903
00907 static void DoDryUp(TileIndex tile)
00908 {
00909 _current_company = OWNER_WATER;
00910
00911 switch (GetTileType(tile)) {
00912 case MP_RAILWAY:
00913 assert(IsPlainRail(tile));
00914 assert(GetRailGroundType(tile) == RAIL_GROUND_WATER);
00915
00916 RailGroundType new_ground;
00917 switch (GetTrackBits(tile)) {
00918 case TRACK_BIT_UPPER: new_ground = RAIL_GROUND_FENCE_HORIZ1; break;
00919 case TRACK_BIT_LOWER: new_ground = RAIL_GROUND_FENCE_HORIZ2; break;
00920 case TRACK_BIT_LEFT: new_ground = RAIL_GROUND_FENCE_VERT1; break;
00921 case TRACK_BIT_RIGHT: new_ground = RAIL_GROUND_FENCE_VERT2; break;
00922 default: NOT_REACHED();
00923 }
00924 SetRailGroundType(tile, new_ground);
00925 MarkTileDirtyByTile(tile);
00926 break;
00927
00928 case MP_TREES:
00929 SetTreeGroundDensity(tile, TREE_GROUND_GRASS, 3);
00930 MarkTileDirtyByTile(tile);
00931 break;
00932
00933 case MP_WATER:
00934 assert(IsCoast(tile));
00935
00936 if (DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
00937 MakeClear(tile, CLEAR_GRASS, 3);
00938 MarkTileDirtyByTile(tile);
00939 }
00940 break;
00941
00942 default: NOT_REACHED();
00943 }
00944
00945 _current_company = OWNER_NONE;
00946 }
00947
00954 void TileLoop_Water(TileIndex tile)
00955 {
00956 switch (GetFloodingBehaviour(tile)) {
00957 case FLOOD_ACTIVE:
00958 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
00959 TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir(dir));
00960 if (dest == INVALID_TILE) continue;
00961
00962 if (IsTileType(dest, MP_WATER)) continue;
00963
00964 uint z_dest;
00965 Slope slope_dest = GetFoundationSlope(dest, &z_dest) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
00966 if (z_dest > 0) continue;
00967
00968 if (!HasBit(_flood_from_dirs[slope_dest], ReverseDir(dir))) continue;
00969
00970 DoFloodTile(dest);
00971 }
00972 break;
00973
00974 case FLOOD_DRYUP: {
00975 Slope slope_here = GetFoundationSlope(tile, NULL) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
00976 uint check_dirs = _flood_from_dirs[slope_here];
00977 uint dir;
00978 FOR_EACH_SET_BIT(dir, check_dirs) {
00979 TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir((Direction)dir));
00980 if (dest == INVALID_TILE) continue;
00981
00982 FloodingBehaviour dest_behaviour = GetFloodingBehaviour(dest);
00983 if ((dest_behaviour == FLOOD_ACTIVE) || (dest_behaviour == FLOOD_PASSIVE)) return;
00984 }
00985 DoDryUp(tile);
00986 break;
00987 }
00988
00989 default: return;
00990 }
00991 }
00992
00993 void ConvertGroundTilesIntoWaterTiles()
00994 {
00995 TileIndex tile;
00996 uint z;
00997 Slope slope;
00998
00999 for (tile = 0; tile < MapSize(); ++tile) {
01000 slope = GetTileSlope(tile, &z);
01001 if (IsTileType(tile, MP_CLEAR) && z == 0) {
01002
01003
01004
01005 switch (slope) {
01006 case SLOPE_FLAT:
01007 MakeSea(tile);
01008 break;
01009
01010 case SLOPE_N:
01011 case SLOPE_E:
01012 case SLOPE_S:
01013 case SLOPE_W:
01014 MakeShore(tile);
01015 break;
01016
01017 default:
01018 uint check_dirs = _flood_from_dirs[slope & ~SLOPE_STEEP];
01019 uint dir;
01020 FOR_EACH_SET_BIT(dir, check_dirs) {
01021 TileIndex dest = TILE_ADD(tile, TileOffsByDir((Direction)dir));
01022 Slope slope_dest = GetTileSlope(dest, NULL) & ~SLOPE_STEEP;
01023 if (slope_dest == SLOPE_FLAT || IsSlopeWithOneCornerRaised(slope_dest)) {
01024 MakeShore(tile);
01025 break;
01026 }
01027 }
01028 break;
01029 }
01030 }
01031 }
01032 }
01033
01034 static TrackStatus GetTileTrackStatus_Water(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
01035 {
01036 static const byte coast_tracks[] = {0, 32, 4, 0, 16, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0};
01037
01038 TrackBits ts;
01039
01040 if (mode != TRANSPORT_WATER) return 0;
01041
01042 switch (GetWaterTileType(tile)) {
01043 case WATER_TILE_CLEAR: ts = (GetTileSlope(tile, NULL) == SLOPE_FLAT) ? TRACK_BIT_ALL : TRACK_BIT_NONE; break;
01044 case WATER_TILE_COAST: ts = (TrackBits)coast_tracks[GetTileSlope(tile, NULL) & 0xF]; break;
01045 case WATER_TILE_LOCK: ts = DiagDirToDiagTrackBits(GetLockDirection(tile)); break;
01046 case WATER_TILE_DEPOT: ts = AxisToTrackBits(GetShipDepotAxis(tile)); break;
01047 default: return 0;
01048 }
01049 if (TileX(tile) == 0) {
01050
01051 ts &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
01052 }
01053 if (TileY(tile) == 0) {
01054
01055 ts &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
01056 }
01057 return CombineTrackStatus(TrackBitsToTrackdirBits(ts), TRACKDIR_BIT_NONE);
01058 }
01059
01060 static bool ClickTile_Water(TileIndex tile)
01061 {
01062 if (GetWaterTileType(tile) == WATER_TILE_DEPOT) {
01063 TileIndex tile2 = GetOtherShipDepotTile(tile);
01064
01065 ShowDepotWindow(tile < tile2 ? tile : tile2, VEH_SHIP);
01066 return true;
01067 }
01068 return false;
01069 }
01070
01071 static void ChangeTileOwner_Water(TileIndex tile, Owner old_owner, Owner new_owner)
01072 {
01073 if (!IsTileOwner(tile, old_owner)) return;
01074
01075 if (new_owner != INVALID_OWNER) {
01076 SetTileOwner(tile, new_owner);
01077 return;
01078 }
01079
01080
01081 if (IsShipDepot(tile)) DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
01082
01083
01084
01085 if (IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
01086 }
01087
01088 static VehicleEnterTileStatus VehicleEnter_Water(Vehicle *v, TileIndex tile, int x, int y)
01089 {
01090 return VETSB_CONTINUE;
01091 }
01092
01093 static CommandCost TerraformTile_Water(TileIndex tile, DoCommandFlag flags, uint z_new, Slope tileh_new)
01094 {
01095
01096 if (IsWaterTile(tile) && IsCanal(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_CANAL_FIRST);
01097
01098 return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
01099 }
01100
01101
01102 extern const TileTypeProcs _tile_type_water_procs = {
01103 DrawTile_Water,
01104 GetSlopeZ_Water,
01105 ClearTile_Water,
01106 NULL,
01107 GetTileDesc_Water,
01108 GetTileTrackStatus_Water,
01109 ClickTile_Water,
01110 NULL,
01111 TileLoop_Water,
01112 ChangeTileOwner_Water,
01113 NULL,
01114 VehicleEnter_Water,
01115 GetFoundation_Water,
01116 TerraformTile_Water,
01117 };