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 "vehicle_func.h"
00026 #include "sound_func.h"
00027 #include "company_func.h"
00028 #include "clear_map.h"
00029 #include "tree_map.h"
00030 #include "aircraft.h"
00031 #include "effectvehicle_func.h"
00032 #include "tunnelbridge_map.h"
00033 #include "station_base.h"
00034 #include "ai/ai.hpp"
00035 #include "core/random_func.hpp"
00036 #include "core/backup_type.hpp"
00037 #include "date_func.h"
00038 #include "company_base.h"
00039 #include "company_gui.h"
00040
00041 #include "table/strings.h"
00042
00046 static const uint8 _flood_from_dirs[] = {
00047 (1 << DIR_NW) | (1 << DIR_SW) | (1 << DIR_SE) | (1 << DIR_NE),
00048 (1 << DIR_NE) | (1 << DIR_SE),
00049 (1 << DIR_NW) | (1 << DIR_NE),
00050 (1 << DIR_NE),
00051 (1 << DIR_NW) | (1 << DIR_SW),
00052 0,
00053 (1 << DIR_NW),
00054 (1 << DIR_N ) | (1 << DIR_NW) | (1 << DIR_NE),
00055 (1 << DIR_SW) | (1 << DIR_SE),
00056 (1 << DIR_SE),
00057 0,
00058 (1 << DIR_E ) | (1 << DIR_NE) | (1 << DIR_SE),
00059 (1 << DIR_SW),
00060 (1 << DIR_S ) | (1 << DIR_SW) | (1 << DIR_SE),
00061 (1 << DIR_W ) | (1 << DIR_SW) | (1 << DIR_NW),
00062 };
00063
00070 static inline void MarkTileDirtyIfCanalOrRiver(TileIndex tile)
00071 {
00072 if (IsTileType(tile, MP_WATER) && (IsCanal(tile) || IsRiver(tile))) MarkTileDirtyByTile(tile);
00073 }
00074
00081 static void MarkCanalsAndRiversAroundDirty(TileIndex tile)
00082 {
00083 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
00084 MarkTileDirtyIfCanalOrRiver(tile + TileOffsByDir(dir));
00085 }
00086 }
00087
00088
00098 CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00099 {
00100 Axis axis = Extract<Axis, 0, 1>(p1);
00101
00102 TileIndex tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
00103
00104 if (!HasTileWaterGround(tile) || !HasTileWaterGround(tile2)) {
00105 return_cmd_error(STR_ERROR_MUST_BE_BUILT_ON_WATER);
00106 }
00107
00108 if ((MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) ||
00109 (MayHaveBridgeAbove(tile2) && IsBridgeAbove(tile2))) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
00110
00111 if (GetTileSlope(tile, NULL) != SLOPE_FLAT || GetTileSlope(tile2, NULL) != SLOPE_FLAT) {
00112
00113 return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
00114 }
00115
00116 if (!Depot::CanAllocateItem()) return CMD_ERROR;
00117
00118 WaterClass wc1 = GetWaterClass(tile);
00119 WaterClass wc2 = GetWaterClass(tile2);
00120 CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_DEPOT_SHIP]);
00121
00122 bool add_cost = !IsWaterTile(tile);
00123 CommandCost ret = DoCommand(tile, 0, 0, flags | DC_AUTO, CMD_LANDSCAPE_CLEAR);
00124 if (ret.Failed()) return ret;
00125 if (add_cost) {
00126 cost.AddCost(ret);
00127 }
00128 add_cost = !IsWaterTile(tile2);
00129 ret = DoCommand(tile2, 0, 0, flags | DC_AUTO, CMD_LANDSCAPE_CLEAR);
00130 if (ret.Failed()) return ret;
00131 if (add_cost) {
00132 cost.AddCost(ret);
00133 }
00134
00135 if (flags & DC_EXEC) {
00136 Depot *depot = new Depot(tile);
00137 depot->build_date = _date;
00138
00139 if (wc1 == WATER_CLASS_CANAL || wc2 == WATER_CLASS_CANAL) {
00140
00141 Company::Get(_current_company)->water_infrastructure += wc1 == WATER_CLASS_CANAL && wc2 == WATER_CLASS_CANAL ? 2 : 1;
00142 DirtyCompanyInfrastructureWindows(_current_company);
00143 }
00144
00145 MakeShipDepot(tile, _current_company, depot->index, DEPOT_NORTH, axis, wc1);
00146 MakeShipDepot(tile2, _current_company, depot->index, DEPOT_SOUTH, axis, wc2);
00147 MarkTileDirtyByTile(tile);
00148 MarkTileDirtyByTile(tile2);
00149 MakeDefaultName(depot);
00150 }
00151
00152 return cost;
00153 }
00154
00155 void MakeWaterKeepingClass(TileIndex tile, Owner o)
00156 {
00157 WaterClass wc = GetWaterClass(tile);
00158
00159
00160 uint z;
00161 if (GetTileSlope(tile, &z) != SLOPE_FLAT) {
00162 if (wc == WATER_CLASS_CANAL) {
00163
00164 Company *c = Company::GetIfValid(o);
00165 if (c != NULL) {
00166 c->water_infrastructure--;
00167 DirtyCompanyInfrastructureWindows(c->index);
00168 }
00169 }
00170 wc = WATER_CLASS_INVALID;
00171 }
00172
00173 if (wc == WATER_CLASS_SEA && z > 0) {
00174
00175 Company *c = Company::GetIfValid(o);
00176 if (c != NULL) {
00177 c->water_infrastructure++;
00178 DirtyCompanyInfrastructureWindows(c->index);
00179 }
00180
00181 wc = WATER_CLASS_CANAL;
00182 }
00183
00184
00185 DoClearSquare(tile);
00186
00187
00188 switch (wc) {
00189 case WATER_CLASS_SEA: MakeSea(tile); break;
00190 case WATER_CLASS_CANAL: MakeCanal(tile, o, Random()); break;
00191 case WATER_CLASS_RIVER: MakeRiver(tile, Random()); break;
00192 default: break;
00193 }
00194
00195 MarkTileDirtyByTile(tile);
00196 }
00197
00198 static CommandCost RemoveShipDepot(TileIndex tile, DoCommandFlag flags)
00199 {
00200 if (!IsShipDepot(tile)) return CMD_ERROR;
00201
00202 CommandCost ret = CheckTileOwnership(tile);
00203 if (ret.Failed()) return ret;
00204
00205 TileIndex tile2 = GetOtherShipDepotTile(tile);
00206
00207
00208 if (!(flags & DC_BANKRUPT)) {
00209 CommandCost ret = EnsureNoVehicleOnGround(tile);
00210 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile2);
00211 if (ret.Failed()) return ret;
00212 }
00213
00214 if (flags & DC_EXEC) {
00215 delete Depot::GetByTile(tile);
00216
00217 MakeWaterKeepingClass(tile, GetTileOwner(tile));
00218 MakeWaterKeepingClass(tile2, GetTileOwner(tile2));
00219 }
00220
00221 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_SHIP]);
00222 }
00223
00231 static CommandCost DoBuildLock(TileIndex tile, DiagDirection dir, DoCommandFlag flags)
00232 {
00233 CommandCost cost(EXPENSES_CONSTRUCTION);
00234
00235
00236 CommandCost ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00237 if (ret.Failed()) return ret;
00238 cost.AddCost(ret);
00239
00240 int delta = TileOffsByDiagDir(dir);
00241
00242 WaterClass wc_lower = IsWaterTile(tile - delta) ? GetWaterClass(tile - delta) : WATER_CLASS_CANAL;
00243
00244 if (!IsWaterTile(tile - delta)) {
00245 ret = DoCommand(tile - delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00246 if (ret.Failed()) return ret;
00247 cost.AddCost(ret);
00248 cost.AddCost(_price[PR_BUILD_CANAL]);
00249 }
00250 if (GetTileSlope(tile - delta, NULL) != SLOPE_FLAT) {
00251 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
00252 }
00253
00254
00255 WaterClass wc_upper = IsWaterTile(tile + delta) ? GetWaterClass(tile + delta) : WATER_CLASS_CANAL;
00256
00257 if (!IsWaterTile(tile + delta)) {
00258 ret = DoCommand(tile + delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00259 if (ret.Failed()) return ret;
00260 cost.AddCost(ret);
00261 cost.AddCost(_price[PR_BUILD_CANAL]);
00262 }
00263 if (GetTileSlope(tile + delta, NULL) != SLOPE_FLAT) {
00264 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
00265 }
00266
00267 if ((MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) ||
00268 (MayHaveBridgeAbove(tile - delta) && IsBridgeAbove(tile - delta)) ||
00269 (MayHaveBridgeAbove(tile + delta) && IsBridgeAbove(tile + delta))) {
00270 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
00271 }
00272
00273 if (flags & DC_EXEC) {
00274
00275 Company *c = Company::Get(_current_company);
00276 c->water_infrastructure++;
00277 if (!IsWaterTile(tile - delta)) c->water_infrastructure++;
00278 if (!IsWaterTile(tile + delta)) c->water_infrastructure++;
00279 DirtyCompanyInfrastructureWindows(_current_company);
00280
00281 MakeLock(tile, _current_company, dir, wc_lower, wc_upper);
00282 MarkTileDirtyByTile(tile);
00283 MarkTileDirtyByTile(tile - delta);
00284 MarkTileDirtyByTile(tile + delta);
00285 MarkCanalsAndRiversAroundDirty(tile - delta);
00286 MarkCanalsAndRiversAroundDirty(tile + delta);
00287 }
00288 cost.AddCost(_price[PR_BUILD_LOCK]);
00289
00290 return cost;
00291 }
00292
00299 static CommandCost RemoveLock(TileIndex tile, DoCommandFlag flags)
00300 {
00301 if (GetTileOwner(tile) != OWNER_NONE) {
00302 CommandCost ret = CheckTileOwnership(tile);
00303 if (ret.Failed()) return ret;
00304 }
00305
00306 TileIndexDiff delta = TileOffsByDiagDir(GetLockDirection(tile));
00307
00308
00309 CommandCost ret = EnsureNoVehicleOnGround(tile);
00310 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta);
00311 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta);
00312 if (ret.Failed()) return ret;
00313
00314 if (flags & DC_EXEC) {
00315
00316 Company *c = Company::GetIfValid(GetTileOwner(tile));
00317 if (c != NULL) {
00318 c->water_infrastructure--;
00319 DirtyCompanyInfrastructureWindows(c->index);
00320 }
00321
00322 DoClearSquare(tile);
00323 MakeWaterKeepingClass(tile + delta, GetTileOwner(tile));
00324 MakeWaterKeepingClass(tile - delta, GetTileOwner(tile));
00325 MarkCanalsAndRiversAroundDirty(tile - delta);
00326 MarkCanalsAndRiversAroundDirty(tile + delta);
00327 }
00328
00329 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_LOCK]);
00330 }
00331
00341 CommandCost CmdBuildLock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00342 {
00343 DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile, NULL));
00344 if (dir == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
00345
00346
00347 if (IsWaterTile(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
00348
00349 return DoBuildLock(tile, dir, flags);
00350 }
00351
00361 CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00362 {
00363 WaterClass wc = Extract<WaterClass, 0, 2>(p2);
00364 if (p1 >= MapSize() || wc == WATER_CLASS_INVALID) return CMD_ERROR;
00365
00366
00367 if (wc != WATER_CLASS_CANAL && _game_mode != GM_EDITOR) return CMD_ERROR;
00368
00369 TileArea ta(tile, p1);
00370
00371
00372 if (_game_mode != GM_EDITOR && ta.w != 1 && ta.h != 1) return CMD_ERROR;
00373
00374 CommandCost cost(EXPENSES_CONSTRUCTION);
00375 TILE_AREA_LOOP(tile, ta) {
00376 CommandCost ret;
00377
00378 Slope slope = GetTileSlope(tile, NULL);
00379 if (slope != SLOPE_FLAT && (wc != WATER_CLASS_RIVER || !IsInclinedSlope(slope))) {
00380 return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
00381 }
00382
00383
00384 if (IsTileType(tile, MP_WATER) && (!IsTileOwner(tile, OWNER_WATER) || wc == WATER_CLASS_SEA)) continue;
00385
00386 ret = DoCommand(tile, 0, 0, flags | DC_FORCE_CLEAR_TILE, CMD_LANDSCAPE_CLEAR);
00387 if (ret.Failed()) return ret;
00388 cost.AddCost(ret);
00389
00390 if (flags & DC_EXEC) {
00391 switch (wc) {
00392 case WATER_CLASS_RIVER:
00393 MakeRiver(tile, Random());
00394 break;
00395
00396 case WATER_CLASS_SEA:
00397 if (TileHeight(tile) == 0) {
00398 MakeSea(tile);
00399 break;
00400 }
00401
00402
00403 default:
00404 MakeCanal(tile, _current_company, Random());
00405 if (Company::IsValidID(_current_company)) {
00406 Company::Get(_current_company)->water_infrastructure++;
00407 DirtyCompanyInfrastructureWindows(_current_company);
00408 }
00409 break;
00410 }
00411 MarkTileDirtyByTile(tile);
00412 MarkCanalsAndRiversAroundDirty(tile);
00413 }
00414
00415 cost.AddCost(_price[PR_BUILD_CANAL]);
00416 }
00417
00418 if (cost.GetCost() == 0) {
00419 return_cmd_error(STR_ERROR_ALREADY_BUILT);
00420 } else {
00421 return cost;
00422 }
00423 }
00424
00425 static CommandCost ClearTile_Water(TileIndex tile, DoCommandFlag flags)
00426 {
00427 switch (GetWaterTileType(tile)) {
00428 case WATER_TILE_CLEAR: {
00429 if (flags & DC_NO_WATER) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER);
00430
00431 Money base_cost = IsCanal(tile) ? _price[PR_CLEAR_CANAL] : _price[PR_CLEAR_WATER];
00432
00433 if (!_settings_game.construction.freeform_edges && (!IsInsideMM(TileX(tile), 1, MapMaxX() - 1) ||
00434 !IsInsideMM(TileY(tile), 1, MapMaxY() - 1))) {
00435 return_cmd_error(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP);
00436 }
00437
00438
00439 if (GetTileZ(tile) == 0 && IsTunnelInWay(tile, 0)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_TUNNEL_FIRST);
00440
00441
00442 CommandCost ret = EnsureNoVehicleOnGround(tile);
00443 if (ret.Failed()) return ret;
00444
00445 Owner owner = GetTileOwner(tile);
00446 if (owner != OWNER_WATER && owner != OWNER_NONE) {
00447 CommandCost ret = CheckTileOwnership(tile);
00448 if (ret.Failed()) return ret;
00449 }
00450
00451 if (flags & DC_EXEC) {
00452 if (IsCanal(tile) && Company::IsValidID(owner)) {
00453 Company::Get(owner)->water_infrastructure--;
00454 DirtyCompanyInfrastructureWindows(owner);
00455 }
00456 DoClearSquare(tile);
00457 MarkCanalsAndRiversAroundDirty(tile);
00458 }
00459
00460 return CommandCost(EXPENSES_CONSTRUCTION, base_cost);
00461 }
00462
00463 case WATER_TILE_COAST: {
00464 Slope slope = GetTileSlope(tile, NULL);
00465
00466
00467 CommandCost ret = EnsureNoVehicleOnGround(tile);
00468 if (ret.Failed()) return ret;
00469
00470 if (flags & DC_EXEC) {
00471 DoClearSquare(tile);
00472 MarkCanalsAndRiversAroundDirty(tile);
00473 }
00474 if (IsSlopeWithOneCornerRaised(slope)) {
00475 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_WATER]);
00476 } else {
00477 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_ROUGH]);
00478 }
00479 }
00480
00481 case WATER_TILE_LOCK: {
00482 static const TileIndexDiffC _lock_tomiddle_offs[] = {
00483 { 0, 0}, {0, 0}, { 0, 0}, {0, 0},
00484 {-1, 0}, {0, 1}, { 1, 0}, {0, -1},
00485 { 1, 0}, {0, -1}, {-1, 0}, {0, 1},
00486 };
00487
00488 if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
00489 if (_current_company == OWNER_WATER) return CMD_ERROR;
00490
00491 return RemoveLock(tile + ToTileIndexDiff(_lock_tomiddle_offs[GetSection(tile)]), flags);
00492 }
00493
00494 case WATER_TILE_DEPOT:
00495 if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
00496 return RemoveShipDepot(tile, flags);
00497
00498 default:
00499 NOT_REACHED();
00500 }
00501 }
00502
00511 static bool IsWateredTile(TileIndex tile, Direction from)
00512 {
00513 switch (GetTileType(tile)) {
00514 case MP_WATER:
00515 switch (GetWaterTileType(tile)) {
00516 default: NOT_REACHED();
00517 case WATER_TILE_DEPOT: case WATER_TILE_CLEAR: return true;
00518 case WATER_TILE_LOCK: return DiagDirToAxis(GetLockDirection(tile)) == DiagDirToAxis(DirToDiagDir(from));
00519
00520 case WATER_TILE_COAST:
00521 switch (GetTileSlope(tile, NULL)) {
00522 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
00523 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
00524 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
00525 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
00526 default: return false;
00527 }
00528 }
00529
00530 case MP_RAILWAY:
00531 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
00532 assert(IsPlainRail(tile));
00533 switch (GetTileSlope(tile, NULL)) {
00534 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
00535 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
00536 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
00537 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
00538 default: return false;
00539 }
00540 }
00541 return false;
00542
00543 case MP_STATION:
00544 if (IsOilRig(tile)) {
00545
00546
00547 TileIndex src_tile = tile + TileOffsByDir(from);
00548 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
00549 (IsTileType(src_tile, MP_INDUSTRY))) return true;
00550
00551 return IsTileOnWater(tile);
00552 }
00553 return (IsDock(tile) && GetTileSlope(tile, NULL) == SLOPE_FLAT) || IsBuoy(tile);
00554
00555 case MP_INDUSTRY: {
00556
00557
00558 TileIndex src_tile = tile + TileOffsByDir(from);
00559 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
00560 (IsTileType(src_tile, MP_INDUSTRY) && GetIndustryIndex(src_tile) == GetIndustryIndex(tile))) return true;
00561
00562 return IsTileOnWater(tile);
00563 }
00564
00565 case MP_OBJECT: return IsTileOnWater(tile);
00566
00567 case MP_TUNNELBRIDGE: return GetTunnelBridgeTransportType(tile) == TRANSPORT_WATER && ReverseDiagDir(GetTunnelBridgeDirection(tile)) == DirToDiagDir(from);
00568
00569 default: return false;
00570 }
00571 }
00572
00580 static void DrawWaterSprite(SpriteID base, uint offset, CanalFeature feature, TileIndex tile)
00581 {
00582 if (base != SPR_FLAT_WATER_TILE) {
00583
00584 offset = GetCanalSpriteOffset(feature, tile, offset);
00585 }
00586 DrawGroundSprite(base + offset, PAL_NONE);
00587 }
00588
00595 static void DrawWaterEdges(bool canal, uint offset, TileIndex tile)
00596 {
00597 CanalFeature feature;
00598 SpriteID base = 0;
00599 if (canal) {
00600 feature = CF_DIKES;
00601 base = GetCanalSprite(CF_DIKES, tile);
00602 if (base == 0) base = SPR_CANAL_DIKES_BASE;
00603 } else {
00604 feature = CF_RIVER_EDGE;
00605 base = GetCanalSprite(CF_RIVER_EDGE, tile);
00606 if (base == 0) return;
00607 }
00608
00609 uint wa;
00610
00611
00612 wa = IsWateredTile(TILE_ADDXY(tile, -1, 0), DIR_SW) << 0;
00613 wa += IsWateredTile(TILE_ADDXY(tile, 0, 1), DIR_NW) << 1;
00614 wa += IsWateredTile(TILE_ADDXY(tile, 1, 0), DIR_NE) << 2;
00615 wa += IsWateredTile(TILE_ADDXY(tile, 0, -1), DIR_SE) << 3;
00616
00617 if (!(wa & 1)) DrawWaterSprite(base, offset, feature, tile);
00618 if (!(wa & 2)) DrawWaterSprite(base, offset + 1, feature, tile);
00619 if (!(wa & 4)) DrawWaterSprite(base, offset + 2, feature, tile);
00620 if (!(wa & 8)) DrawWaterSprite(base, offset + 3, feature, tile);
00621
00622
00623 switch (wa & 0x03) {
00624 case 0: DrawWaterSprite(base, offset + 4, feature, tile); break;
00625 case 3: if (!IsWateredTile(TILE_ADDXY(tile, -1, 1), DIR_W)) DrawWaterSprite(base, offset + 8, feature, tile); break;
00626 }
00627
00628
00629 switch (wa & 0x06) {
00630 case 0: DrawWaterSprite(base, offset + 5, feature, tile); break;
00631 case 6: if (!IsWateredTile(TILE_ADDXY(tile, 1, 1), DIR_N)) DrawWaterSprite(base, offset + 9, feature, tile); break;
00632 }
00633
00634
00635 switch (wa & 0x0C) {
00636 case 0: DrawWaterSprite(base, offset + 6, feature, tile); break;
00637 case 12: if (!IsWateredTile(TILE_ADDXY(tile, 1, -1), DIR_E)) DrawWaterSprite(base, offset + 10, feature, tile); break;
00638 }
00639
00640
00641 switch (wa & 0x09) {
00642 case 0: DrawWaterSprite(base, offset + 7, feature, tile); break;
00643 case 9: if (!IsWateredTile(TILE_ADDXY(tile, -1, -1), DIR_S)) DrawWaterSprite(base, offset + 11, feature, tile); break;
00644 }
00645 }
00646
00648 static void DrawSeaWater(TileIndex tile)
00649 {
00650 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
00651 }
00652
00654 static void DrawCanalWater(TileIndex tile)
00655 {
00656 SpriteID image = SPR_FLAT_WATER_TILE;
00657 if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
00658
00659 image = GetCanalSprite(CF_WATERSLOPE, tile);
00660 if (image == 0) image = SPR_FLAT_WATER_TILE;
00661 }
00662 DrawWaterSprite(image, 0, CF_WATERSLOPE, tile);
00663
00664 DrawWaterEdges(true, 0, tile);
00665 }
00666
00667 #include "table/water_land.h"
00668
00678 static void DrawWaterTileStruct(const TileInfo *ti, const DrawTileSeqStruct *dtss, SpriteID base, uint offset, PaletteID palette, CanalFeature feature)
00679 {
00680
00681 if (IsInvisibilitySet(TO_BUILDINGS)) return;
00682
00683 for (; !dtss->IsTerminator(); dtss++) {
00684 uint tile_offs = offset + dtss->image.sprite;
00685 if (feature < CF_END) tile_offs = GetCanalSpriteOffset(feature, ti->tile, tile_offs);
00686 AddSortableSpriteToDraw(base + tile_offs, palette,
00687 ti->x + dtss->delta_x, ti->y + dtss->delta_y,
00688 dtss->size_x, dtss->size_y,
00689 dtss->size_z, ti->z + dtss->delta_z,
00690 IsTransparencySet(TO_BUILDINGS));
00691 }
00692 }
00693
00695 static void DrawWaterLock(const TileInfo *ti)
00696 {
00697 int section = GetSection(ti->tile);
00698 const DrawTileSprites &dts = _lock_display_data[section];
00699
00700
00701 SpriteID image = dts.ground.sprite;
00702
00703 SpriteID water_base = GetCanalSprite(CF_WATERSLOPE, ti->tile);
00704 if (water_base == 0) {
00705
00706 water_base = SPR_CANALS_BASE;
00707 } else if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
00708
00709 if (image == SPR_FLAT_WATER_TILE) {
00710 image = water_base;
00711 } else {
00712 image++;
00713 }
00714 }
00715
00716 if (image < 5) image += water_base;
00717 DrawGroundSprite(image, PAL_NONE);
00718
00719
00720 uint zoffs = 0;
00721 SpriteID base = GetCanalSprite(CF_LOCKS, ti->tile);
00722
00723 if (base == 0) {
00724
00725 base = SPR_LOCK_BASE;
00726 uint8 z_threshold = section >= 8 ? 8 : 0;
00727 zoffs = ti->z > z_threshold ? 24 : 0;
00728 }
00729
00730 DrawWaterTileStruct(ti, dts.seq, base, zoffs, PAL_NONE, CF_LOCKS);
00731 }
00732
00734 static void DrawWaterDepot(const TileInfo *ti)
00735 {
00736 DrawWaterClassGround(ti);
00737 DrawWaterTileStruct(ti, _shipdepot_display_data[GetSection(ti->tile)].seq, 0, 0, COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile)), CF_END);
00738 }
00739
00740 static void DrawRiverWater(const TileInfo *ti)
00741 {
00742 SpriteID image = SPR_FLAT_WATER_TILE;
00743 uint offset = 0;
00744 uint edges_offset = 0;
00745
00746 if (ti->tileh != SLOPE_FLAT || HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
00747 image = GetCanalSprite(CF_RIVER_SLOPE, ti->tile);
00748 if (image == 0) {
00749 switch (ti->tileh) {
00750 case SLOPE_NW: image = SPR_WATER_SLOPE_Y_DOWN; break;
00751 case SLOPE_SW: image = SPR_WATER_SLOPE_X_UP; break;
00752 case SLOPE_SE: image = SPR_WATER_SLOPE_Y_UP; break;
00753 case SLOPE_NE: image = SPR_WATER_SLOPE_X_DOWN; break;
00754 default: image = SPR_FLAT_WATER_TILE; break;
00755 }
00756 } else {
00757
00758 offset = HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE) ? 1 : 0;
00759
00760 switch (ti->tileh) {
00761 case SLOPE_SE: edges_offset += 12; break;
00762 case SLOPE_NE: offset += 1; edges_offset += 24; break;
00763 case SLOPE_SW: offset += 2; edges_offset += 36; break;
00764 case SLOPE_NW: offset += 3; edges_offset += 48; break;
00765 default: offset = 0; break;
00766 }
00767
00768 offset = GetCanalSpriteOffset(CF_RIVER_SLOPE, ti->tile, offset);
00769 }
00770 }
00771
00772 DrawGroundSprite(image + offset, PAL_NONE);
00773
00774
00775 DrawWaterEdges(false, edges_offset, ti->tile);
00776 }
00777
00778 void DrawShoreTile(Slope tileh)
00779 {
00780
00781
00782 static const byte tileh_to_shoresprite[32] = {
00783 0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0,
00784 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 15, 0,
00785 };
00786
00787 assert(!IsHalftileSlope(tileh));
00788 assert(tileh != SLOPE_FLAT);
00789
00790 assert((tileh != SLOPE_EW) && (tileh != SLOPE_NS));
00791
00792 DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[tileh], PAL_NONE);
00793 }
00794
00795 void DrawWaterClassGround(const TileInfo *ti)
00796 {
00797 switch (GetWaterClass(ti->tile)) {
00798 case WATER_CLASS_SEA: DrawSeaWater(ti->tile); break;
00799 case WATER_CLASS_CANAL: DrawCanalWater(ti->tile); break;
00800 case WATER_CLASS_RIVER: DrawRiverWater(ti); break;
00801 default: NOT_REACHED();
00802 }
00803 }
00804
00805 static void DrawTile_Water(TileInfo *ti)
00806 {
00807 switch (GetWaterTileType(ti->tile)) {
00808 case WATER_TILE_CLEAR:
00809 DrawWaterClassGround(ti);
00810 DrawBridgeMiddle(ti);
00811 break;
00812
00813 case WATER_TILE_COAST: {
00814 DrawShoreTile(ti->tileh);
00815 DrawBridgeMiddle(ti);
00816 break;
00817 }
00818
00819 case WATER_TILE_LOCK:
00820 DrawWaterLock(ti);
00821 break;
00822
00823 case WATER_TILE_DEPOT:
00824 DrawWaterDepot(ti);
00825 break;
00826 }
00827 }
00828
00829 void DrawShipDepotSprite(int x, int y, int image)
00830 {
00831 const DrawTileSprites &dts = _shipdepot_display_data[image];
00832
00833 DrawSprite(dts.ground.sprite, dts.ground.pal, x, y);
00834 DrawOrigTileSeqInGUI(x, y, &dts, COMPANY_SPRITE_COLOUR(_local_company));
00835 }
00836
00837
00838 static uint GetSlopeZ_Water(TileIndex tile, uint x, uint y)
00839 {
00840 uint z;
00841 Slope tileh = GetTileSlope(tile, &z);
00842
00843 return z + GetPartialZ(x & 0xF, y & 0xF, tileh);
00844 }
00845
00846 static Foundation GetFoundation_Water(TileIndex tile, Slope tileh)
00847 {
00848 return FOUNDATION_NONE;
00849 }
00850
00851 static void GetTileDesc_Water(TileIndex tile, TileDesc *td)
00852 {
00853 switch (GetWaterTileType(tile)) {
00854 case WATER_TILE_CLEAR:
00855 switch (GetWaterClass(tile)) {
00856 case WATER_CLASS_SEA: td->str = STR_LAI_WATER_DESCRIPTION_WATER; break;
00857 case WATER_CLASS_CANAL: td->str = STR_LAI_WATER_DESCRIPTION_CANAL; break;
00858 case WATER_CLASS_RIVER: td->str = STR_LAI_WATER_DESCRIPTION_RIVER; break;
00859 default: NOT_REACHED(); break;
00860 }
00861 break;
00862 case WATER_TILE_COAST: td->str = STR_LAI_WATER_DESCRIPTION_COAST_OR_RIVERBANK; break;
00863 case WATER_TILE_LOCK : td->str = STR_LAI_WATER_DESCRIPTION_LOCK; break;
00864 case WATER_TILE_DEPOT:
00865 td->str = STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT;
00866 td->build_date = Depot::GetByTile(tile)->build_date;
00867 break;
00868 default: NOT_REACHED(); break;
00869 }
00870
00871 td->owner[0] = GetTileOwner(tile);
00872 }
00873
00879 static void FloodVehicle(Vehicle *v)
00880 {
00881 uint pass = v->Crash(true);
00882
00883 AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, AIEventVehicleCrashed::CRASH_FLOODED));
00884 SetDParam(0, pass);
00885 AddVehicleNewsItem(STR_NEWS_DISASTER_FLOOD_VEHICLE, NS_ACCIDENT, v->index);
00886 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
00887 SndPlayVehicleFx(SND_12_EXPLOSION, v);
00888 }
00889
00896 static Vehicle *FloodVehicleProc(Vehicle *v, void *data)
00897 {
00898 if ((v->vehstatus & VS_CRASHED) != 0) return NULL;
00899
00900 switch (v->type) {
00901 default: break;
00902
00903 case VEH_AIRCRAFT: {
00904 if (!IsAirportTile(v->tile) || GetTileMaxZ(v->tile) != 0) break;
00905 if (v->subtype == AIR_SHADOW) break;
00906
00907
00908
00909 const Station *st = Station::GetByTile(v->tile);
00910 const AirportFTAClass *airport = st->airport.GetFTA();
00911 if (v->z_pos != airport->delta_z + 1) break;
00912
00913 FloodVehicle(v);
00914 break;
00915 }
00916
00917 case VEH_TRAIN:
00918 case VEH_ROAD: {
00919 int z = *(byte*)data;
00920 if (v->z_pos > z) break;
00921 FloodVehicle(v->First());
00922 break;
00923 }
00924 }
00925
00926 return NULL;
00927 }
00928
00934 static void FloodVehicles(TileIndex tile)
00935 {
00936 int z = 0;
00937
00938 if (IsAirportTile(tile)) {
00939 const Station *st = Station::GetByTile(tile);
00940 TILE_AREA_LOOP(tile, st->airport) {
00941 if (st->TileBelongsToAirport(tile)) FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00942 }
00943
00944
00945 return;
00946 }
00947
00948 if (!IsBridgeTile(tile)) {
00949 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00950 return;
00951 }
00952
00953 TileIndex end = GetOtherBridgeEnd(tile);
00954 z = GetBridgeHeight(tile);
00955
00956 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00957 FindVehicleOnPos(end, &z, &FloodVehicleProc);
00958 }
00959
00965 FloodingBehaviour GetFloodingBehaviour(TileIndex tile)
00966 {
00967
00968
00969
00970
00971
00972 switch (GetTileType(tile)) {
00973 case MP_WATER:
00974 if (IsCoast(tile)) {
00975 Slope tileh = GetTileSlope(tile, NULL);
00976 return (IsSlopeWithOneCornerRaised(tileh) ? FLOOD_ACTIVE : FLOOD_DRYUP);
00977 }
00978
00979 case MP_STATION:
00980 case MP_INDUSTRY:
00981 case MP_OBJECT:
00982 return (GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE;
00983
00984 case MP_RAILWAY:
00985 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
00986 return (IsSlopeWithOneCornerRaised(GetTileSlope(tile, NULL)) ? FLOOD_ACTIVE : FLOOD_DRYUP);
00987 }
00988 return FLOOD_NONE;
00989
00990 case MP_TREES:
00991 return (GetTreeGround(tile) == TREE_GROUND_SHORE ? FLOOD_DRYUP : FLOOD_NONE);
00992
00993 default:
00994 return FLOOD_NONE;
00995 }
00996 }
00997
01001 void DoFloodTile(TileIndex target)
01002 {
01003 assert(!IsTileType(target, MP_WATER));
01004
01005 bool flooded = false;
01006
01007 Backup<CompanyByte> cur_company(_current_company, OWNER_WATER, FILE_LINE);
01008
01009 Slope tileh = GetTileSlope(target, NULL);
01010 if (tileh != SLOPE_FLAT) {
01011
01012 switch (GetTileType(target)) {
01013 case MP_RAILWAY: {
01014 if (!IsPlainRail(target)) break;
01015 FloodVehicles(target);
01016 flooded = FloodHalftile(target);
01017 break;
01018 }
01019
01020 case MP_TREES:
01021 if (!IsSlopeWithOneCornerRaised(tileh)) {
01022 SetTreeGroundDensity(target, TREE_GROUND_SHORE, 3);
01023 MarkTileDirtyByTile(target);
01024 flooded = true;
01025 break;
01026 }
01027
01028
01029 case MP_CLEAR:
01030 if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
01031 MakeShore(target);
01032 MarkTileDirtyByTile(target);
01033 flooded = true;
01034 }
01035 break;
01036
01037 default:
01038 break;
01039 }
01040 } else {
01041
01042 FloodVehicles(target);
01043
01044
01045 if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
01046 MakeSea(target);
01047 MarkTileDirtyByTile(target);
01048 flooded = true;
01049 }
01050 }
01051
01052 if (flooded) {
01053
01054 MarkCanalsAndRiversAroundDirty(target);
01055
01056
01057 UpdateSignalsInBuffer();
01058 }
01059
01060 cur_company.Restore();
01061 }
01062
01066 static void DoDryUp(TileIndex tile)
01067 {
01068 Backup<CompanyByte> cur_company(_current_company, OWNER_WATER, FILE_LINE);
01069
01070 switch (GetTileType(tile)) {
01071 case MP_RAILWAY:
01072 assert(IsPlainRail(tile));
01073 assert(GetRailGroundType(tile) == RAIL_GROUND_WATER);
01074
01075 RailGroundType new_ground;
01076 switch (GetTrackBits(tile)) {
01077 case TRACK_BIT_UPPER: new_ground = RAIL_GROUND_FENCE_HORIZ1; break;
01078 case TRACK_BIT_LOWER: new_ground = RAIL_GROUND_FENCE_HORIZ2; break;
01079 case TRACK_BIT_LEFT: new_ground = RAIL_GROUND_FENCE_VERT1; break;
01080 case TRACK_BIT_RIGHT: new_ground = RAIL_GROUND_FENCE_VERT2; break;
01081 default: NOT_REACHED();
01082 }
01083 SetRailGroundType(tile, new_ground);
01084 MarkTileDirtyByTile(tile);
01085 break;
01086
01087 case MP_TREES:
01088 SetTreeGroundDensity(tile, TREE_GROUND_GRASS, 3);
01089 MarkTileDirtyByTile(tile);
01090 break;
01091
01092 case MP_WATER:
01093 assert(IsCoast(tile));
01094
01095 if (DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
01096 MakeClear(tile, CLEAR_GRASS, 3);
01097 MarkTileDirtyByTile(tile);
01098 }
01099 break;
01100
01101 default: NOT_REACHED();
01102 }
01103
01104 cur_company.Restore();
01105 }
01106
01113 void TileLoop_Water(TileIndex tile)
01114 {
01115 switch (GetFloodingBehaviour(tile)) {
01116 case FLOOD_ACTIVE:
01117 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
01118 TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir(dir));
01119 if (dest == INVALID_TILE) continue;
01120
01121 if (IsTileType(dest, MP_WATER)) continue;
01122
01123 uint z_dest;
01124 Slope slope_dest = GetFoundationSlope(dest, &z_dest) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
01125 if (z_dest > 0) continue;
01126
01127 if (!HasBit(_flood_from_dirs[slope_dest], ReverseDir(dir))) continue;
01128
01129 DoFloodTile(dest);
01130 }
01131 break;
01132
01133 case FLOOD_DRYUP: {
01134 Slope slope_here = GetFoundationSlope(tile, NULL) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
01135 uint dir;
01136 FOR_EACH_SET_BIT(dir, _flood_from_dirs[slope_here]) {
01137 TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir((Direction)dir));
01138 if (dest == INVALID_TILE) continue;
01139
01140 FloodingBehaviour dest_behaviour = GetFloodingBehaviour(dest);
01141 if ((dest_behaviour == FLOOD_ACTIVE) || (dest_behaviour == FLOOD_PASSIVE)) return;
01142 }
01143 DoDryUp(tile);
01144 break;
01145 }
01146
01147 default: return;
01148 }
01149 }
01150
01151 void ConvertGroundTilesIntoWaterTiles()
01152 {
01153 uint z;
01154
01155 for (TileIndex tile = 0; tile < MapSize(); ++tile) {
01156 Slope slope = GetTileSlope(tile, &z);
01157 if (IsTileType(tile, MP_CLEAR) && z == 0) {
01158
01159
01160
01161 switch (slope) {
01162 case SLOPE_FLAT:
01163 MakeSea(tile);
01164 break;
01165
01166 case SLOPE_N:
01167 case SLOPE_E:
01168 case SLOPE_S:
01169 case SLOPE_W:
01170 MakeShore(tile);
01171 break;
01172
01173 default:
01174 uint dir;
01175 FOR_EACH_SET_BIT(dir, _flood_from_dirs[slope & ~SLOPE_STEEP]) {
01176 TileIndex dest = TILE_ADD(tile, TileOffsByDir((Direction)dir));
01177 Slope slope_dest = GetTileSlope(dest, NULL) & ~SLOPE_STEEP;
01178 if (slope_dest == SLOPE_FLAT || IsSlopeWithOneCornerRaised(slope_dest)) {
01179 MakeShore(tile);
01180 break;
01181 }
01182 }
01183 break;
01184 }
01185 }
01186 }
01187 }
01188
01189 static TrackStatus GetTileTrackStatus_Water(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
01190 {
01191 static const byte coast_tracks[] = {0, 32, 4, 0, 16, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0};
01192
01193 TrackBits ts;
01194
01195 if (mode != TRANSPORT_WATER) return 0;
01196
01197 switch (GetWaterTileType(tile)) {
01198 case WATER_TILE_CLEAR: ts = (GetTileSlope(tile, NULL) == SLOPE_FLAT) ? TRACK_BIT_ALL : TRACK_BIT_NONE; break;
01199 case WATER_TILE_COAST: ts = (TrackBits)coast_tracks[GetTileSlope(tile, NULL) & 0xF]; break;
01200 case WATER_TILE_LOCK: ts = DiagDirToDiagTrackBits(GetLockDirection(tile)); break;
01201 case WATER_TILE_DEPOT: ts = AxisToTrackBits(GetShipDepotAxis(tile)); break;
01202 default: return 0;
01203 }
01204 if (TileX(tile) == 0) {
01205
01206 ts &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
01207 }
01208 if (TileY(tile) == 0) {
01209
01210 ts &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
01211 }
01212 return CombineTrackStatus(TrackBitsToTrackdirBits(ts), TRACKDIR_BIT_NONE);
01213 }
01214
01215 static bool ClickTile_Water(TileIndex tile)
01216 {
01217 if (GetWaterTileType(tile) == WATER_TILE_DEPOT) {
01218 ShowDepotWindow(GetShipDepotNorthTile(tile), VEH_SHIP);
01219 return true;
01220 }
01221 return false;
01222 }
01223
01224 static void ChangeTileOwner_Water(TileIndex tile, Owner old_owner, Owner new_owner)
01225 {
01226 if (!IsTileOwner(tile, old_owner)) return;
01227
01228
01229 if (IsCanal(tile)) Company::Get(old_owner)->water_infrastructure--;
01230 if (new_owner != INVALID_OWNER) {
01231 if (IsCanal(tile)) Company::Get(new_owner)->water_infrastructure++;
01232 SetTileOwner(tile, new_owner);
01233 return;
01234 }
01235
01236
01237 if (IsShipDepot(tile)) DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
01238
01239
01240
01241 if (IsTileOwner(tile, old_owner)) {
01242 if (IsCanal(tile)) Company::Get(old_owner)->water_infrastructure--;
01243 SetTileOwner(tile, OWNER_NONE);
01244 }
01245 }
01246
01247 static VehicleEnterTileStatus VehicleEnter_Water(Vehicle *v, TileIndex tile, int x, int y)
01248 {
01249 return VETSB_CONTINUE;
01250 }
01251
01252 static CommandCost TerraformTile_Water(TileIndex tile, DoCommandFlag flags, uint z_new, Slope tileh_new)
01253 {
01254
01255 if (IsWaterTile(tile) && IsCanal(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_CANAL_FIRST);
01256
01257 return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
01258 }
01259
01260
01261 extern const TileTypeProcs _tile_type_water_procs = {
01262 DrawTile_Water,
01263 GetSlopeZ_Water,
01264 ClearTile_Water,
01265 NULL,
01266 GetTileDesc_Water,
01267 GetTileTrackStatus_Water,
01268 ClickTile_Water,
01269 NULL,
01270 TileLoop_Water,
01271 ChangeTileOwner_Water,
01272 NULL,
01273 VehicleEnter_Water,
01274 GetFoundation_Water,
01275 TerraformTile_Water,
01276 };