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