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