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 if (GetTileZ(tile) == 0 && IsTunnelInWay(tile, 0)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_TUNNEL_FIRST);
00395
00396
00397 CommandCost ret = EnsureNoVehicleOnGround(tile);
00398 if (ret.Failed()) return ret;
00399
00400 if (GetTileOwner(tile) != OWNER_WATER && GetTileOwner(tile) != OWNER_NONE) {
00401 CommandCost ret = CheckTileOwnership(tile);
00402 if (ret.Failed()) return ret;
00403 }
00404
00405 if (flags & DC_EXEC) {
00406 DoClearSquare(tile);
00407 MarkCanalsAndRiversAroundDirty(tile);
00408 }
00409
00410 return CommandCost(EXPENSES_CONSTRUCTION, base_cost);
00411 }
00412
00413 case WATER_TILE_COAST: {
00414 Slope slope = GetTileSlope(tile, NULL);
00415
00416
00417 CommandCost ret = EnsureNoVehicleOnGround(tile);
00418 if (ret.Failed()) return ret;
00419
00420 if (flags & DC_EXEC) {
00421 DoClearSquare(tile);
00422 MarkCanalsAndRiversAroundDirty(tile);
00423 }
00424 if (IsSlopeWithOneCornerRaised(slope)) {
00425 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_WATER]);
00426 } else {
00427 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_ROUGH]);
00428 }
00429 }
00430
00431 case WATER_TILE_LOCK: {
00432 static const TileIndexDiffC _lock_tomiddle_offs[] = {
00433 { 0, 0}, {0, 0}, { 0, 0}, {0, 0},
00434 {-1, 0}, {0, 1}, { 1, 0}, {0, -1},
00435 { 1, 0}, {0, -1}, {-1, 0}, {0, 1},
00436 };
00437
00438 if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
00439 if (_current_company == OWNER_WATER) return CMD_ERROR;
00440
00441 return RemoveLock(tile + ToTileIndexDiff(_lock_tomiddle_offs[GetSection(tile)]), flags);
00442 }
00443
00444 case WATER_TILE_DEPOT:
00445 if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
00446 return RemoveShipDepot(tile, flags);
00447
00448 default:
00449 NOT_REACHED();
00450 }
00451 }
00452
00461 static bool IsWateredTile(TileIndex tile, Direction from)
00462 {
00463 switch (GetTileType(tile)) {
00464 case MP_WATER:
00465 switch (GetWaterTileType(tile)) {
00466 default: NOT_REACHED();
00467 case WATER_TILE_DEPOT: case WATER_TILE_CLEAR: return true;
00468 case WATER_TILE_LOCK: return DiagDirToAxis(GetLockDirection(tile)) == DiagDirToAxis(DirToDiagDir(from));
00469
00470 case WATER_TILE_COAST:
00471 switch (GetTileSlope(tile, NULL)) {
00472 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
00473 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
00474 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
00475 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
00476 default: return false;
00477 }
00478 }
00479
00480 case MP_RAILWAY:
00481 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
00482 assert(IsPlainRail(tile));
00483 switch (GetTileSlope(tile, NULL)) {
00484 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
00485 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
00486 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
00487 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
00488 default: return false;
00489 }
00490 }
00491 return false;
00492
00493 case MP_STATION:
00494 if (IsOilRig(tile)) {
00495
00496
00497 TileIndex src_tile = tile + TileOffsByDir(from);
00498 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
00499 (IsTileType(src_tile, MP_INDUSTRY))) return true;
00500
00501 return IsTileOnWater(tile);
00502 }
00503 return (IsDock(tile) && GetTileSlope(tile, NULL) == SLOPE_FLAT) || IsBuoy(tile);
00504
00505 case MP_INDUSTRY: {
00506
00507
00508 TileIndex src_tile = tile + TileOffsByDir(from);
00509 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
00510 (IsTileType(src_tile, MP_INDUSTRY) && GetIndustryIndex(src_tile) == GetIndustryIndex(tile))) return true;
00511
00512 return IsTileOnWater(tile);
00513 }
00514
00515 case MP_OBJECT: return IsTileOnWater(tile);
00516
00517 case MP_TUNNELBRIDGE: return GetTunnelBridgeTransportType(tile) == TRANSPORT_WATER && ReverseDiagDir(GetTunnelBridgeDirection(tile)) == DirToDiagDir(from);
00518
00519 default: return false;
00520 }
00521 }
00522
00530 static void DrawWaterSprite(SpriteID base, uint offset, CanalFeature feature, TileIndex tile)
00531 {
00532 if (base != SPR_FLAT_WATER_TILE) {
00533
00534 offset = GetCanalSpriteOffset(feature, tile, offset);
00535 }
00536 DrawGroundSprite(base + offset, PAL_NONE);
00537 }
00538
00545 static void DrawWaterEdges(bool canal, uint offset, TileIndex tile)
00546 {
00547 CanalFeature feature;
00548 SpriteID base = 0;
00549 if (canal) {
00550 feature = CF_DIKES;
00551 base = GetCanalSprite(CF_DIKES, tile);
00552 if (base == 0) base = SPR_CANAL_DIKES_BASE;
00553 } else {
00554 feature = CF_RIVER_EDGE;
00555 base = GetCanalSprite(CF_RIVER_EDGE, tile);
00556 if (base == 0) return;
00557 }
00558
00559 uint wa;
00560
00561
00562 wa = IsWateredTile(TILE_ADDXY(tile, -1, 0), DIR_SW) << 0;
00563 wa += IsWateredTile(TILE_ADDXY(tile, 0, 1), DIR_NW) << 1;
00564 wa += IsWateredTile(TILE_ADDXY(tile, 1, 0), DIR_NE) << 2;
00565 wa += IsWateredTile(TILE_ADDXY(tile, 0, -1), DIR_SE) << 3;
00566
00567 if (!(wa & 1)) DrawWaterSprite(base, offset, feature, tile);
00568 if (!(wa & 2)) DrawWaterSprite(base, offset + 1, feature, tile);
00569 if (!(wa & 4)) DrawWaterSprite(base, offset + 2, feature, tile);
00570 if (!(wa & 8)) DrawWaterSprite(base, offset + 3, feature, tile);
00571
00572
00573 switch (wa & 0x03) {
00574 case 0: DrawWaterSprite(base, offset + 4, feature, tile); break;
00575 case 3: if (!IsWateredTile(TILE_ADDXY(tile, -1, 1), DIR_W)) DrawWaterSprite(base, offset + 8, feature, tile); break;
00576 }
00577
00578
00579 switch (wa & 0x06) {
00580 case 0: DrawWaterSprite(base, offset + 5, feature, tile); break;
00581 case 6: if (!IsWateredTile(TILE_ADDXY(tile, 1, 1), DIR_N)) DrawWaterSprite(base, offset + 9, feature, tile); break;
00582 }
00583
00584
00585 switch (wa & 0x0C) {
00586 case 0: DrawWaterSprite(base, offset + 6, feature, tile); break;
00587 case 12: if (!IsWateredTile(TILE_ADDXY(tile, 1, -1), DIR_E)) DrawWaterSprite(base, offset + 10, feature, tile); break;
00588 }
00589
00590
00591 switch (wa & 0x09) {
00592 case 0: DrawWaterSprite(base, offset + 7, feature, tile); break;
00593 case 9: if (!IsWateredTile(TILE_ADDXY(tile, -1, -1), DIR_S)) DrawWaterSprite(base, offset + 11, feature, tile); break;
00594 }
00595 }
00596
00598 static void DrawSeaWater(TileIndex tile)
00599 {
00600 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
00601 }
00602
00604 static void DrawCanalWater(TileIndex tile)
00605 {
00606 SpriteID image = SPR_FLAT_WATER_TILE;
00607 if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
00608
00609 image = GetCanalSprite(CF_WATERSLOPE, tile);
00610 if (image == 0) image = SPR_FLAT_WATER_TILE;
00611 }
00612 DrawWaterSprite(image, 0, CF_WATERSLOPE, tile);
00613
00614 DrawWaterEdges(true, 0, tile);
00615 }
00616
00617 #include "table/water_land.h"
00618
00628 static void DrawWaterTileStruct(const TileInfo *ti, const DrawTileSeqStruct *dtss, SpriteID base, uint offset, PaletteID palette, CanalFeature feature)
00629 {
00630
00631 if (IsInvisibilitySet(TO_BUILDINGS)) return;
00632
00633 for (; !dtss->IsTerminator(); dtss++) {
00634 uint tile_offs = offset + dtss->image.sprite;
00635 if (feature < CF_END) tile_offs = GetCanalSpriteOffset(feature, ti->tile, tile_offs);
00636 AddSortableSpriteToDraw(base + tile_offs, palette,
00637 ti->x + dtss->delta_x, ti->y + dtss->delta_y,
00638 dtss->size_x, dtss->size_y,
00639 dtss->size_z, ti->z + dtss->delta_z,
00640 IsTransparencySet(TO_BUILDINGS));
00641 }
00642 }
00643
00645 static void DrawWaterLock(const TileInfo *ti)
00646 {
00647 int section = GetSection(ti->tile);
00648 const DrawTileSprites &dts = _lock_display_data[section];
00649
00650
00651 SpriteID image = dts.ground.sprite;
00652
00653 SpriteID water_base = GetCanalSprite(CF_WATERSLOPE, ti->tile);
00654 if (water_base == 0) {
00655
00656 water_base = SPR_CANALS_BASE;
00657 } else if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
00658
00659 if (image == SPR_FLAT_WATER_TILE) {
00660 image = water_base;
00661 } else {
00662 image++;
00663 }
00664 }
00665
00666 if (image < 5) image += water_base;
00667 DrawGroundSprite(image, PAL_NONE);
00668
00669
00670 uint zoffs = 0;
00671 SpriteID base = GetCanalSprite(CF_LOCKS, ti->tile);
00672
00673 if (base == 0) {
00674
00675 base = SPR_LOCK_BASE;
00676 uint8 z_threshold = section >= 8 ? 8 : 0;
00677 zoffs = ti->z > z_threshold ? 24 : 0;
00678 }
00679
00680 DrawWaterTileStruct(ti, dts.seq, base, zoffs, PAL_NONE, CF_LOCKS);
00681 }
00682
00684 static void DrawWaterDepot(const TileInfo *ti)
00685 {
00686 DrawWaterClassGround(ti);
00687 DrawWaterTileStruct(ti, _shipdepot_display_data[GetSection(ti->tile)].seq, 0, 0, COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile)), CF_END);
00688 }
00689
00690 static void DrawRiverWater(const TileInfo *ti)
00691 {
00692 SpriteID image = SPR_FLAT_WATER_TILE;
00693 uint offset = 0;
00694 uint edges_offset = 0;
00695
00696 if (ti->tileh != SLOPE_FLAT || HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
00697 image = GetCanalSprite(CF_RIVER_SLOPE, ti->tile);
00698 if (image == 0) {
00699 switch (ti->tileh) {
00700 case SLOPE_NW: image = SPR_WATER_SLOPE_Y_DOWN; break;
00701 case SLOPE_SW: image = SPR_WATER_SLOPE_X_UP; break;
00702 case SLOPE_SE: image = SPR_WATER_SLOPE_Y_UP; break;
00703 case SLOPE_NE: image = SPR_WATER_SLOPE_X_DOWN; break;
00704 default: image = SPR_FLAT_WATER_TILE; break;
00705 }
00706 } else {
00707
00708 offset = HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE) ? 1 : 0;
00709
00710 switch (ti->tileh) {
00711 case SLOPE_SE: edges_offset += 12; break;
00712 case SLOPE_NE: offset += 1; edges_offset += 24; break;
00713 case SLOPE_SW: offset += 2; edges_offset += 36; break;
00714 case SLOPE_NW: offset += 3; edges_offset += 48; break;
00715 default: offset = 0; break;
00716 }
00717
00718 offset = GetCanalSpriteOffset(CF_RIVER_SLOPE, ti->tile, offset);
00719 }
00720 }
00721
00722 DrawGroundSprite(image + offset, PAL_NONE);
00723
00724
00725 DrawWaterEdges(false, edges_offset, ti->tile);
00726 }
00727
00728 void DrawShoreTile(Slope tileh)
00729 {
00730
00731
00732 static const byte tileh_to_shoresprite[32] = {
00733 0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0,
00734 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 15, 0,
00735 };
00736
00737 assert(!IsHalftileSlope(tileh));
00738 assert(tileh != SLOPE_FLAT);
00739
00740 assert((tileh != SLOPE_EW) && (tileh != SLOPE_NS));
00741
00742 DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[tileh], PAL_NONE);
00743 }
00744
00745 void DrawWaterClassGround(const TileInfo *ti)
00746 {
00747 switch (GetWaterClass(ti->tile)) {
00748 case WATER_CLASS_SEA: DrawSeaWater(ti->tile); break;
00749 case WATER_CLASS_CANAL: DrawCanalWater(ti->tile); break;
00750 case WATER_CLASS_RIVER: DrawRiverWater(ti); break;
00751 default: NOT_REACHED();
00752 }
00753 }
00754
00755 static void DrawTile_Water(TileInfo *ti)
00756 {
00757 switch (GetWaterTileType(ti->tile)) {
00758 case WATER_TILE_CLEAR:
00759 DrawWaterClassGround(ti);
00760 DrawBridgeMiddle(ti);
00761 break;
00762
00763 case WATER_TILE_COAST: {
00764 DrawShoreTile(ti->tileh);
00765 DrawBridgeMiddle(ti);
00766 break;
00767 }
00768
00769 case WATER_TILE_LOCK:
00770 DrawWaterLock(ti);
00771 break;
00772
00773 case WATER_TILE_DEPOT:
00774 DrawWaterDepot(ti);
00775 break;
00776 }
00777 }
00778
00779 void DrawShipDepotSprite(int x, int y, int image)
00780 {
00781 const DrawTileSprites &dts = _shipdepot_display_data[image];
00782
00783 DrawSprite(dts.ground.sprite, dts.ground.pal, x, y);
00784 DrawOrigTileSeqInGUI(x, y, &dts, COMPANY_SPRITE_COLOUR(_local_company));
00785 }
00786
00787
00788 static uint GetSlopeZ_Water(TileIndex tile, uint x, uint y)
00789 {
00790 uint z;
00791 Slope tileh = GetTileSlope(tile, &z);
00792
00793 return z + GetPartialZ(x & 0xF, y & 0xF, tileh);
00794 }
00795
00796 static Foundation GetFoundation_Water(TileIndex tile, Slope tileh)
00797 {
00798 return FOUNDATION_NONE;
00799 }
00800
00801 static void GetTileDesc_Water(TileIndex tile, TileDesc *td)
00802 {
00803 switch (GetWaterTileType(tile)) {
00804 case WATER_TILE_CLEAR:
00805 switch (GetWaterClass(tile)) {
00806 case WATER_CLASS_SEA: td->str = STR_LAI_WATER_DESCRIPTION_WATER; break;
00807 case WATER_CLASS_CANAL: td->str = STR_LAI_WATER_DESCRIPTION_CANAL; break;
00808 case WATER_CLASS_RIVER: td->str = STR_LAI_WATER_DESCRIPTION_RIVER; break;
00809 default: NOT_REACHED(); break;
00810 }
00811 break;
00812 case WATER_TILE_COAST: td->str = STR_LAI_WATER_DESCRIPTION_COAST_OR_RIVERBANK; break;
00813 case WATER_TILE_LOCK : td->str = STR_LAI_WATER_DESCRIPTION_LOCK; break;
00814 case WATER_TILE_DEPOT:
00815 td->str = STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT;
00816 td->build_date = Depot::GetByTile(tile)->build_date;
00817 break;
00818 default: NOT_REACHED(); break;
00819 }
00820
00821 td->owner[0] = GetTileOwner(tile);
00822 }
00823
00829 static void FloodVehicle(Vehicle *v)
00830 {
00831 uint pass = v->Crash(true);
00832
00833 AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, AIEventVehicleCrashed::CRASH_FLOODED));
00834 SetDParam(0, pass);
00835 AddVehicleNewsItem(STR_NEWS_DISASTER_FLOOD_VEHICLE, NS_ACCIDENT, v->index);
00836 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
00837 SndPlayVehicleFx(SND_12_EXPLOSION, v);
00838 }
00839
00846 static Vehicle *FloodVehicleProc(Vehicle *v, void *data)
00847 {
00848 if ((v->vehstatus & VS_CRASHED) != 0) return NULL;
00849
00850 switch (v->type) {
00851 default: break;
00852
00853 case VEH_AIRCRAFT: {
00854 if (!IsAirportTile(v->tile) || GetTileMaxZ(v->tile) != 0) break;
00855 if (v->subtype == AIR_SHADOW) break;
00856
00857
00858
00859 const Station *st = Station::GetByTile(v->tile);
00860 const AirportFTAClass *airport = st->airport.GetFTA();
00861 if (v->z_pos != airport->delta_z + 1) break;
00862
00863 FloodVehicle(v);
00864 break;
00865 }
00866
00867 case VEH_TRAIN:
00868 case VEH_ROAD: {
00869 int z = *(byte*)data;
00870 if (v->z_pos > z) break;
00871 FloodVehicle(v->First());
00872 break;
00873 }
00874 }
00875
00876 return NULL;
00877 }
00878
00884 static void FloodVehicles(TileIndex tile)
00885 {
00886 int z = 0;
00887
00888 if (IsAirportTile(tile)) {
00889 const Station *st = Station::GetByTile(tile);
00890 TILE_AREA_LOOP(tile, st->airport) {
00891 if (st->TileBelongsToAirport(tile)) FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00892 }
00893
00894
00895 return;
00896 }
00897
00898 if (!IsBridgeTile(tile)) {
00899 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00900 return;
00901 }
00902
00903 TileIndex end = GetOtherBridgeEnd(tile);
00904 z = GetBridgeHeight(tile);
00905
00906 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00907 FindVehicleOnPos(end, &z, &FloodVehicleProc);
00908 }
00909
00915 FloodingBehaviour GetFloodingBehaviour(TileIndex tile)
00916 {
00917
00918
00919
00920
00921
00922 switch (GetTileType(tile)) {
00923 case MP_WATER:
00924 if (IsCoast(tile)) {
00925 Slope tileh = GetTileSlope(tile, NULL);
00926 return (IsSlopeWithOneCornerRaised(tileh) ? FLOOD_ACTIVE : FLOOD_DRYUP);
00927 }
00928
00929 case MP_STATION:
00930 case MP_INDUSTRY:
00931 case MP_OBJECT:
00932 return (GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE;
00933
00934 case MP_RAILWAY:
00935 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
00936 return (IsSlopeWithOneCornerRaised(GetTileSlope(tile, NULL)) ? FLOOD_ACTIVE : FLOOD_DRYUP);
00937 }
00938 return FLOOD_NONE;
00939
00940 case MP_TREES:
00941 return (GetTreeGround(tile) == TREE_GROUND_SHORE ? FLOOD_DRYUP : FLOOD_NONE);
00942
00943 default:
00944 return FLOOD_NONE;
00945 }
00946 }
00947
00951 void DoFloodTile(TileIndex target)
00952 {
00953 assert(!IsTileType(target, MP_WATER));
00954
00955 bool flooded = false;
00956
00957 Backup<CompanyByte> cur_company(_current_company, OWNER_WATER, FILE_LINE);
00958
00959 Slope tileh = GetTileSlope(target, NULL);
00960 if (tileh != SLOPE_FLAT) {
00961
00962 switch (GetTileType(target)) {
00963 case MP_RAILWAY: {
00964 if (!IsPlainRail(target)) break;
00965 FloodVehicles(target);
00966 flooded = FloodHalftile(target);
00967 break;
00968 }
00969
00970 case MP_TREES:
00971 if (!IsSlopeWithOneCornerRaised(tileh)) {
00972 SetTreeGroundDensity(target, TREE_GROUND_SHORE, 3);
00973 MarkTileDirtyByTile(target);
00974 flooded = true;
00975 break;
00976 }
00977
00978
00979 case MP_CLEAR:
00980 if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
00981 MakeShore(target);
00982 MarkTileDirtyByTile(target);
00983 flooded = true;
00984 }
00985 break;
00986
00987 default:
00988 break;
00989 }
00990 } else {
00991
00992 FloodVehicles(target);
00993
00994
00995 if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
00996 MakeSea(target);
00997 MarkTileDirtyByTile(target);
00998 flooded = true;
00999 }
01000 }
01001
01002 if (flooded) {
01003
01004 MarkCanalsAndRiversAroundDirty(target);
01005
01006
01007 UpdateSignalsInBuffer();
01008 }
01009
01010 cur_company.Restore();
01011 }
01012
01016 static void DoDryUp(TileIndex tile)
01017 {
01018 Backup<CompanyByte> cur_company(_current_company, OWNER_WATER, FILE_LINE);
01019
01020 switch (GetTileType(tile)) {
01021 case MP_RAILWAY:
01022 assert(IsPlainRail(tile));
01023 assert(GetRailGroundType(tile) == RAIL_GROUND_WATER);
01024
01025 RailGroundType new_ground;
01026 switch (GetTrackBits(tile)) {
01027 case TRACK_BIT_UPPER: new_ground = RAIL_GROUND_FENCE_HORIZ1; break;
01028 case TRACK_BIT_LOWER: new_ground = RAIL_GROUND_FENCE_HORIZ2; break;
01029 case TRACK_BIT_LEFT: new_ground = RAIL_GROUND_FENCE_VERT1; break;
01030 case TRACK_BIT_RIGHT: new_ground = RAIL_GROUND_FENCE_VERT2; break;
01031 default: NOT_REACHED();
01032 }
01033 SetRailGroundType(tile, new_ground);
01034 MarkTileDirtyByTile(tile);
01035 break;
01036
01037 case MP_TREES:
01038 SetTreeGroundDensity(tile, TREE_GROUND_GRASS, 3);
01039 MarkTileDirtyByTile(tile);
01040 break;
01041
01042 case MP_WATER:
01043 assert(IsCoast(tile));
01044
01045 if (DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
01046 MakeClear(tile, CLEAR_GRASS, 3);
01047 MarkTileDirtyByTile(tile);
01048 }
01049 break;
01050
01051 default: NOT_REACHED();
01052 }
01053
01054 cur_company.Restore();
01055 }
01056
01063 void TileLoop_Water(TileIndex tile)
01064 {
01065 switch (GetFloodingBehaviour(tile)) {
01066 case FLOOD_ACTIVE:
01067 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
01068 TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir(dir));
01069 if (dest == INVALID_TILE) continue;
01070
01071 if (IsTileType(dest, MP_WATER)) continue;
01072
01073 uint z_dest;
01074 Slope slope_dest = GetFoundationSlope(dest, &z_dest) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
01075 if (z_dest > 0) continue;
01076
01077 if (!HasBit(_flood_from_dirs[slope_dest], ReverseDir(dir))) continue;
01078
01079 DoFloodTile(dest);
01080 }
01081 break;
01082
01083 case FLOOD_DRYUP: {
01084 Slope slope_here = GetFoundationSlope(tile, NULL) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
01085 uint dir;
01086 FOR_EACH_SET_BIT(dir, _flood_from_dirs[slope_here]) {
01087 TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir((Direction)dir));
01088 if (dest == INVALID_TILE) continue;
01089
01090 FloodingBehaviour dest_behaviour = GetFloodingBehaviour(dest);
01091 if ((dest_behaviour == FLOOD_ACTIVE) || (dest_behaviour == FLOOD_PASSIVE)) return;
01092 }
01093 DoDryUp(tile);
01094 break;
01095 }
01096
01097 default: return;
01098 }
01099 }
01100
01101 void ConvertGroundTilesIntoWaterTiles()
01102 {
01103 uint z;
01104
01105 for (TileIndex tile = 0; tile < MapSize(); ++tile) {
01106 Slope slope = GetTileSlope(tile, &z);
01107 if (IsTileType(tile, MP_CLEAR) && z == 0) {
01108
01109
01110
01111 switch (slope) {
01112 case SLOPE_FLAT:
01113 MakeSea(tile);
01114 break;
01115
01116 case SLOPE_N:
01117 case SLOPE_E:
01118 case SLOPE_S:
01119 case SLOPE_W:
01120 MakeShore(tile);
01121 break;
01122
01123 default:
01124 uint dir;
01125 FOR_EACH_SET_BIT(dir, _flood_from_dirs[slope & ~SLOPE_STEEP]) {
01126 TileIndex dest = TILE_ADD(tile, TileOffsByDir((Direction)dir));
01127 Slope slope_dest = GetTileSlope(dest, NULL) & ~SLOPE_STEEP;
01128 if (slope_dest == SLOPE_FLAT || IsSlopeWithOneCornerRaised(slope_dest)) {
01129 MakeShore(tile);
01130 break;
01131 }
01132 }
01133 break;
01134 }
01135 }
01136 }
01137 }
01138
01139 static TrackStatus GetTileTrackStatus_Water(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
01140 {
01141 static const byte coast_tracks[] = {0, 32, 4, 0, 16, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0};
01142
01143 TrackBits ts;
01144
01145 if (mode != TRANSPORT_WATER) return 0;
01146
01147 switch (GetWaterTileType(tile)) {
01148 case WATER_TILE_CLEAR: ts = (GetTileSlope(tile, NULL) == SLOPE_FLAT) ? TRACK_BIT_ALL : TRACK_BIT_NONE; break;
01149 case WATER_TILE_COAST: ts = (TrackBits)coast_tracks[GetTileSlope(tile, NULL) & 0xF]; break;
01150 case WATER_TILE_LOCK: ts = DiagDirToDiagTrackBits(GetLockDirection(tile)); break;
01151 case WATER_TILE_DEPOT: ts = AxisToTrackBits(GetShipDepotAxis(tile)); break;
01152 default: return 0;
01153 }
01154 if (TileX(tile) == 0) {
01155
01156 ts &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
01157 }
01158 if (TileY(tile) == 0) {
01159
01160 ts &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
01161 }
01162 return CombineTrackStatus(TrackBitsToTrackdirBits(ts), TRACKDIR_BIT_NONE);
01163 }
01164
01165 static bool ClickTile_Water(TileIndex tile)
01166 {
01167 if (GetWaterTileType(tile) == WATER_TILE_DEPOT) {
01168 ShowDepotWindow(GetShipDepotNorthTile(tile), VEH_SHIP);
01169 return true;
01170 }
01171 return false;
01172 }
01173
01174 static void ChangeTileOwner_Water(TileIndex tile, Owner old_owner, Owner new_owner)
01175 {
01176 if (!IsTileOwner(tile, old_owner)) return;
01177
01178 if (new_owner != INVALID_OWNER) {
01179 SetTileOwner(tile, new_owner);
01180 return;
01181 }
01182
01183
01184 if (IsShipDepot(tile)) DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
01185
01186
01187
01188 if (IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
01189 }
01190
01191 static VehicleEnterTileStatus VehicleEnter_Water(Vehicle *v, TileIndex tile, int x, int y)
01192 {
01193 return VETSB_CONTINUE;
01194 }
01195
01196 static CommandCost TerraformTile_Water(TileIndex tile, DoCommandFlag flags, uint z_new, Slope tileh_new)
01197 {
01198
01199 if (IsWaterTile(tile) && IsCanal(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_CANAL_FIRST);
01200
01201 return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
01202 }
01203
01204
01205 extern const TileTypeProcs _tile_type_water_procs = {
01206 DrawTile_Water,
01207 GetSlopeZ_Water,
01208 ClearTile_Water,
01209 NULL,
01210 GetTileDesc_Water,
01211 GetTileTrackStatus_Water,
01212 ClickTile_Water,
01213 NULL,
01214 TileLoop_Water,
01215 ChangeTileOwner_Water,
01216 NULL,
01217 VehicleEnter_Water,
01218 GetFoundation_Water,
01219 TerraformTile_Water,
01220 };