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 int delta = TileOffsByDiagDir(dir);
00209 CommandCost ret = EnsureNoVehicleOnGround(tile);
00210 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta);
00211 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta);
00212 if (ret.Failed()) return ret;
00213
00214
00215 ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00216 if (ret.Failed()) return ret;
00217 cost.AddCost(ret);
00218
00219
00220 WaterClass wc_lower = IsWaterTile(tile - delta) ? GetWaterClass(tile - delta) : WATER_CLASS_CANAL;
00221
00222 if (!IsWaterTile(tile - delta)) {
00223 ret = DoCommand(tile - delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00224 if (ret.Failed()) return ret;
00225 cost.AddCost(ret);
00226 cost.AddCost(_price[PR_BUILD_CANAL]);
00227 }
00228 if (GetTileSlope(tile - delta, NULL) != SLOPE_FLAT) {
00229 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
00230 }
00231
00232
00233 WaterClass wc_upper = IsWaterTile(tile + delta) ? GetWaterClass(tile + delta) : WATER_CLASS_CANAL;
00234
00235 if (!IsWaterTile(tile + delta)) {
00236 ret = DoCommand(tile + delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00237 if (ret.Failed()) return ret;
00238 cost.AddCost(ret);
00239 cost.AddCost(_price[PR_BUILD_CANAL]);
00240 }
00241 if (GetTileSlope(tile + delta, NULL) != SLOPE_FLAT) {
00242 return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
00243 }
00244
00245 if ((MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) ||
00246 (MayHaveBridgeAbove(tile - delta) && IsBridgeAbove(tile - delta)) ||
00247 (MayHaveBridgeAbove(tile + delta) && IsBridgeAbove(tile + delta))) {
00248 return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
00249 }
00250
00251 if (flags & DC_EXEC) {
00252 MakeLock(tile, _current_company, dir, wc_lower, wc_upper);
00253 MarkTileDirtyByTile(tile);
00254 MarkTileDirtyByTile(tile - delta);
00255 MarkTileDirtyByTile(tile + delta);
00256 MarkCanalsAndRiversAroundDirty(tile - delta);
00257 MarkCanalsAndRiversAroundDirty(tile + delta);
00258 }
00259 cost.AddCost(_price[PR_BUILD_LOCK]);
00260
00261 return cost;
00262 }
00263
00270 static CommandCost RemoveLock(TileIndex tile, DoCommandFlag flags)
00271 {
00272 if (GetTileOwner(tile) != OWNER_NONE) {
00273 CommandCost ret = CheckTileOwnership(tile);
00274 if (ret.Failed()) return ret;
00275 }
00276
00277 TileIndexDiff delta = TileOffsByDiagDir(GetLockDirection(tile));
00278
00279
00280 CommandCost ret = EnsureNoVehicleOnGround(tile);
00281 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile + delta);
00282 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile - delta);
00283 if (ret.Failed()) return ret;
00284
00285 if (flags & DC_EXEC) {
00286 DoClearSquare(tile);
00287 MakeWaterKeepingClass(tile + delta, GetTileOwner(tile + delta));
00288 MakeWaterKeepingClass(tile - delta, GetTileOwner(tile - delta));
00289 MarkCanalsAndRiversAroundDirty(tile - delta);
00290 MarkCanalsAndRiversAroundDirty(tile + delta);
00291 }
00292
00293 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_LOCK]);
00294 }
00295
00305 CommandCost CmdBuildLock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00306 {
00307 DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile, NULL));
00308 if (dir == INVALID_DIAGDIR) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
00309
00310
00311 if (IsWaterTile(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
00312
00313 return DoBuildLock(tile, dir, flags);
00314 }
00315
00325 CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00326 {
00327 WaterClass wc = Extract<WaterClass, 0, 2>(p2);
00328 if (p1 >= MapSize() || wc == WATER_CLASS_INVALID) return CMD_ERROR;
00329
00330
00331 if (wc != WATER_CLASS_CANAL && _game_mode != GM_EDITOR) return CMD_ERROR;
00332
00333 TileArea ta(tile, p1);
00334
00335
00336 if (_game_mode != GM_EDITOR && ta.w != 1 && ta.h != 1) return CMD_ERROR;
00337
00338 CommandCost cost(EXPENSES_CONSTRUCTION);
00339 TILE_AREA_LOOP(tile, ta) {
00340 CommandCost ret;
00341
00342 Slope slope = GetTileSlope(tile, NULL);
00343 if (slope != SLOPE_FLAT && (wc != WATER_CLASS_RIVER || !IsInclinedSlope(slope))) {
00344 return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
00345 }
00346
00347
00348 if (IsTileType(tile, MP_WATER) && (!IsTileOwner(tile, OWNER_WATER) || wc == WATER_CLASS_SEA)) continue;
00349
00350 ret = DoCommand(tile, 0, 0, flags | DC_FORCE_CLEAR_TILE, CMD_LANDSCAPE_CLEAR);
00351 if (ret.Failed()) return ret;
00352 cost.AddCost(ret);
00353
00354 if (flags & DC_EXEC) {
00355 switch (wc) {
00356 case WATER_CLASS_RIVER:
00357 MakeRiver(tile, Random());
00358 break;
00359
00360 case WATER_CLASS_SEA:
00361 if (TileHeight(tile) == 0) {
00362 MakeSea(tile);
00363 break;
00364 }
00365
00366
00367 default:
00368 MakeCanal(tile, _current_company, Random());
00369 break;
00370 }
00371 MarkTileDirtyByTile(tile);
00372 MarkCanalsAndRiversAroundDirty(tile);
00373 }
00374
00375 cost.AddCost(_price[PR_BUILD_CANAL]);
00376 }
00377
00378 if (cost.GetCost() == 0) {
00379 return_cmd_error(STR_ERROR_ALREADY_BUILT);
00380 } else {
00381 return cost;
00382 }
00383 }
00384
00385 static CommandCost ClearTile_Water(TileIndex tile, DoCommandFlag flags)
00386 {
00387 switch (GetWaterTileType(tile)) {
00388 case WATER_TILE_CLEAR: {
00389 if (flags & DC_NO_WATER) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER);
00390
00391 Money base_cost = IsCanal(tile) ? _price[PR_CLEAR_CANAL] : _price[PR_CLEAR_WATER];
00392
00393 if (!_settings_game.construction.freeform_edges && (!IsInsideMM(TileX(tile), 1, MapMaxX() - 1) ||
00394 !IsInsideMM(TileY(tile), 1, MapMaxY() - 1))) {
00395 return_cmd_error(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP);
00396 }
00397
00398
00399 CommandCost ret = EnsureNoVehicleOnGround(tile);
00400 if (ret.Failed()) return ret;
00401
00402 if (GetTileOwner(tile) != OWNER_WATER && GetTileOwner(tile) != OWNER_NONE) {
00403 CommandCost ret = CheckTileOwnership(tile);
00404 if (ret.Failed()) return ret;
00405 }
00406
00407 if (flags & DC_EXEC) {
00408 DoClearSquare(tile);
00409 MarkCanalsAndRiversAroundDirty(tile);
00410 }
00411
00412 return CommandCost(EXPENSES_CONSTRUCTION, base_cost);
00413 }
00414
00415 case WATER_TILE_COAST: {
00416 Slope slope = GetTileSlope(tile, NULL);
00417
00418
00419 CommandCost ret = EnsureNoVehicleOnGround(tile);
00420 if (ret.Failed()) return ret;
00421
00422 if (flags & DC_EXEC) {
00423 DoClearSquare(tile);
00424 MarkCanalsAndRiversAroundDirty(tile);
00425 }
00426 if (IsSlopeWithOneCornerRaised(slope)) {
00427 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_WATER]);
00428 } else {
00429 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_ROUGH]);
00430 }
00431 }
00432
00433 case WATER_TILE_LOCK: {
00434 static const TileIndexDiffC _lock_tomiddle_offs[] = {
00435 { 0, 0}, {0, 0}, { 0, 0}, {0, 0},
00436 {-1, 0}, {0, 1}, { 1, 0}, {0, -1},
00437 { 1, 0}, {0, -1}, {-1, 0}, {0, 1},
00438 };
00439
00440 if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
00441 if (_current_company == OWNER_WATER) return CMD_ERROR;
00442
00443 return RemoveLock(tile + ToTileIndexDiff(_lock_tomiddle_offs[GetSection(tile)]), flags);
00444 }
00445
00446 case WATER_TILE_DEPOT:
00447 if (flags & DC_AUTO) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
00448 return RemoveShipDepot(tile, flags);
00449
00450 default:
00451 NOT_REACHED();
00452 }
00453 }
00454
00463 static bool IsWateredTile(TileIndex tile, Direction from)
00464 {
00465 switch (GetTileType(tile)) {
00466 case MP_WATER:
00467 switch (GetWaterTileType(tile)) {
00468 default: NOT_REACHED();
00469 case WATER_TILE_DEPOT: case WATER_TILE_CLEAR: return true;
00470 case WATER_TILE_LOCK: return DiagDirToAxis(GetLockDirection(tile)) == DiagDirToAxis(DirToDiagDir(from));
00471
00472 case WATER_TILE_COAST:
00473 switch (GetTileSlope(tile, NULL)) {
00474 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
00475 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
00476 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
00477 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
00478 default: return false;
00479 }
00480 }
00481
00482 case MP_RAILWAY:
00483 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
00484 assert(IsPlainRail(tile));
00485 switch (GetTileSlope(tile, NULL)) {
00486 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
00487 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
00488 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
00489 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
00490 default: return false;
00491 }
00492 }
00493 return false;
00494
00495 case MP_STATION:
00496 if (IsOilRig(tile)) {
00497
00498
00499 TileIndex src_tile = tile + TileOffsByDir(from);
00500 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
00501 (IsTileType(src_tile, MP_INDUSTRY))) return true;
00502
00503 return IsTileOnWater(tile);
00504 }
00505 return (IsDock(tile) && GetTileSlope(tile, NULL) == SLOPE_FLAT) || IsBuoy(tile);
00506
00507 case MP_INDUSTRY: {
00508
00509
00510 TileIndex src_tile = tile + TileOffsByDir(from);
00511 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
00512 (IsTileType(src_tile, MP_INDUSTRY) && GetIndustryIndex(src_tile) == GetIndustryIndex(tile))) return true;
00513
00514 return IsTileOnWater(tile);
00515 }
00516
00517 case MP_OBJECT: return IsTileOnWater(tile);
00518
00519 case MP_TUNNELBRIDGE: return GetTunnelBridgeTransportType(tile) == TRANSPORT_WATER && ReverseDiagDir(GetTunnelBridgeDirection(tile)) == DirToDiagDir(from);
00520
00521 default: return false;
00522 }
00523 }
00524
00532 static void DrawWaterSprite(SpriteID base, uint offset, CanalFeature feature, TileIndex tile)
00533 {
00534 if (base != SPR_FLAT_WATER_TILE) {
00535
00536 offset = GetCanalSpriteOffset(feature, tile, offset);
00537 }
00538 DrawGroundSprite(base + offset, PAL_NONE);
00539 }
00540
00547 static void DrawWaterEdges(bool canal, uint offset, TileIndex tile)
00548 {
00549 CanalFeature feature;
00550 SpriteID base = 0;
00551 if (canal) {
00552 feature = CF_DIKES;
00553 base = GetCanalSprite(CF_DIKES, tile);
00554 if (base == 0) base = SPR_CANAL_DIKES_BASE;
00555 } else {
00556 feature = CF_RIVER_EDGE;
00557 base = GetCanalSprite(CF_RIVER_EDGE, tile);
00558 if (base == 0) return;
00559 }
00560
00561 uint wa;
00562
00563
00564 wa = IsWateredTile(TILE_ADDXY(tile, -1, 0), DIR_SW) << 0;
00565 wa += IsWateredTile(TILE_ADDXY(tile, 0, 1), DIR_NW) << 1;
00566 wa += IsWateredTile(TILE_ADDXY(tile, 1, 0), DIR_NE) << 2;
00567 wa += IsWateredTile(TILE_ADDXY(tile, 0, -1), DIR_SE) << 3;
00568
00569 if (!(wa & 1)) DrawWaterSprite(base, offset, feature, tile);
00570 if (!(wa & 2)) DrawWaterSprite(base, offset + 1, feature, tile);
00571 if (!(wa & 4)) DrawWaterSprite(base, offset + 2, feature, tile);
00572 if (!(wa & 8)) DrawWaterSprite(base, offset + 3, feature, tile);
00573
00574
00575 switch (wa & 0x03) {
00576 case 0: DrawWaterSprite(base, offset + 4, feature, tile); break;
00577 case 3: if (!IsWateredTile(TILE_ADDXY(tile, -1, 1), DIR_W)) DrawWaterSprite(base, offset + 8, feature, tile); break;
00578 }
00579
00580
00581 switch (wa & 0x06) {
00582 case 0: DrawWaterSprite(base, offset + 5, feature, tile); break;
00583 case 6: if (!IsWateredTile(TILE_ADDXY(tile, 1, 1), DIR_N)) DrawWaterSprite(base, offset + 9, feature, tile); break;
00584 }
00585
00586
00587 switch (wa & 0x0C) {
00588 case 0: DrawWaterSprite(base, offset + 6, feature, tile); break;
00589 case 12: if (!IsWateredTile(TILE_ADDXY(tile, 1, -1), DIR_E)) DrawWaterSprite(base, offset + 10, feature, tile); break;
00590 }
00591
00592
00593 switch (wa & 0x09) {
00594 case 0: DrawWaterSprite(base, offset + 7, feature, tile); break;
00595 case 9: if (!IsWateredTile(TILE_ADDXY(tile, -1, -1), DIR_S)) DrawWaterSprite(base, offset + 11, feature, tile); break;
00596 }
00597 }
00598
00600 static void DrawSeaWater(TileIndex tile)
00601 {
00602 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
00603 }
00604
00606 static void DrawCanalWater(TileIndex tile)
00607 {
00608 SpriteID image = SPR_FLAT_WATER_TILE;
00609 if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
00610
00611 image = GetCanalSprite(CF_WATERSLOPE, tile);
00612 if (image == 0) image = SPR_FLAT_WATER_TILE;
00613 }
00614 DrawWaterSprite(image, 0, CF_WATERSLOPE, tile);
00615
00616 DrawWaterEdges(true, 0, tile);
00617 }
00618
00619 struct LocksDrawTileStruct {
00620 int8 delta_x, delta_y, delta_z;
00621 byte width, height, depth;
00622 SpriteID image;
00623 };
00624
00625 #include "table/water_land.h"
00626
00636 static void DrawWaterTileStruct(const TileInfo *ti, const WaterDrawTileStruct *wdts, SpriteID base, uint offset, PaletteID palette, CanalFeature feature)
00637 {
00638
00639 if (IsInvisibilitySet(TO_BUILDINGS)) return;
00640
00641 for (; wdts->delta_x != 0x80; wdts++) {
00642 uint tile_offs = offset + wdts->image;
00643 if (feature < CF_END) tile_offs = GetCanalSpriteOffset(feature, ti->tile, tile_offs);
00644 AddSortableSpriteToDraw(base + tile_offs, palette,
00645 ti->x + wdts->delta_x, ti->y + wdts->delta_y,
00646 wdts->size_x, wdts->size_y,
00647 wdts->size_z, ti->z + wdts->delta_z,
00648 IsTransparencySet(TO_BUILDINGS));
00649 }
00650 }
00651
00653 static void DrawWaterLock(const TileInfo *ti)
00654 {
00655 int section = GetSection(ti->tile);
00656 const WaterDrawTileStruct *wdts = _lock_display_seq[section];
00657
00658
00659 SpriteID image = wdts++->image;
00660
00661 SpriteID water_base = GetCanalSprite(CF_WATERSLOPE, ti->tile);
00662 if (water_base == 0) {
00663
00664 water_base = SPR_CANALS_BASE;
00665 } else if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
00666
00667 if (image == SPR_FLAT_WATER_TILE) {
00668 image = water_base;
00669 } else {
00670 image++;
00671 }
00672 }
00673
00674 if (image < 5) image += water_base;
00675 DrawGroundSprite(image, PAL_NONE);
00676
00677
00678 uint zoffs = 0;
00679 SpriteID base = GetCanalSprite(CF_LOCKS, ti->tile);
00680
00681 if (base == 0) {
00682
00683 base = SPR_LOCK_BASE;
00684 uint8 z_threshold = section >= 8 ? 8 : 0;
00685 zoffs = ti->z > z_threshold ? 24 : 0;
00686 }
00687
00688 DrawWaterTileStruct(ti, wdts, base, zoffs, PAL_NONE, CF_LOCKS);
00689 }
00690
00692 static void DrawWaterDepot(const TileInfo *ti)
00693 {
00694 DrawWaterClassGround(ti);
00695
00696 DrawWaterTileStruct(ti, _shipdepot_display_seq[GetSection(ti->tile)] + 1, 0, 0, COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile)), CF_END);
00697 }
00698
00699 static void DrawRiverWater(const TileInfo *ti)
00700 {
00701 SpriteID image = SPR_FLAT_WATER_TILE;
00702 uint offset = 0;
00703 uint edges_offset = 0;
00704
00705 if (ti->tileh != SLOPE_FLAT || HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
00706 image = GetCanalSprite(CF_RIVER_SLOPE, ti->tile);
00707 if (image == 0) {
00708 switch (ti->tileh) {
00709 case SLOPE_NW: image = SPR_WATER_SLOPE_Y_DOWN; break;
00710 case SLOPE_SW: image = SPR_WATER_SLOPE_X_UP; break;
00711 case SLOPE_SE: image = SPR_WATER_SLOPE_Y_UP; break;
00712 case SLOPE_NE: image = SPR_WATER_SLOPE_X_DOWN; break;
00713 default: image = SPR_FLAT_WATER_TILE; break;
00714 }
00715 } else {
00716
00717 offset = HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE) ? 1 : 0;
00718
00719 switch (ti->tileh) {
00720 case SLOPE_SE: edges_offset += 12; break;
00721 case SLOPE_NE: offset += 1; edges_offset += 24; break;
00722 case SLOPE_SW: offset += 2; edges_offset += 36; break;
00723 case SLOPE_NW: offset += 3; edges_offset += 48; break;
00724 default: offset = 0; break;
00725 }
00726
00727 offset = GetCanalSpriteOffset(CF_RIVER_SLOPE, ti->tile, offset);
00728 }
00729 }
00730
00731 DrawGroundSprite(image + offset, PAL_NONE);
00732
00733
00734 DrawWaterEdges(false, edges_offset, ti->tile);
00735 }
00736
00737 void DrawShoreTile(Slope tileh)
00738 {
00739
00740
00741 static const byte tileh_to_shoresprite[32] = {
00742 0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0,
00743 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 15, 0,
00744 };
00745
00746 assert(!IsHalftileSlope(tileh));
00747 assert(tileh != SLOPE_FLAT);
00748
00749 assert((tileh != SLOPE_EW) && (tileh != SLOPE_NS));
00750
00751 DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[tileh], PAL_NONE);
00752 }
00753
00754 void DrawWaterClassGround(const TileInfo *ti)
00755 {
00756 switch (GetWaterClass(ti->tile)) {
00757 case WATER_CLASS_SEA: DrawSeaWater(ti->tile); break;
00758 case WATER_CLASS_CANAL: DrawCanalWater(ti->tile); break;
00759 case WATER_CLASS_RIVER: DrawRiverWater(ti); break;
00760 default: NOT_REACHED();
00761 }
00762 }
00763
00764 static void DrawTile_Water(TileInfo *ti)
00765 {
00766 switch (GetWaterTileType(ti->tile)) {
00767 case WATER_TILE_CLEAR:
00768 DrawWaterClassGround(ti);
00769 DrawBridgeMiddle(ti);
00770 break;
00771
00772 case WATER_TILE_COAST: {
00773 DrawShoreTile(ti->tileh);
00774 DrawBridgeMiddle(ti);
00775 break;
00776 }
00777
00778 case WATER_TILE_LOCK:
00779 DrawWaterLock(ti);
00780 break;
00781
00782 case WATER_TILE_DEPOT:
00783 DrawWaterDepot(ti);
00784 break;
00785 }
00786 }
00787
00788 void DrawShipDepotSprite(int x, int y, int image)
00789 {
00790 const WaterDrawTileStruct *wdts = _shipdepot_display_seq[image];
00791
00792 DrawSprite(wdts++->image, PAL_NONE, x, y);
00793
00794 for (; wdts->delta_x != 0x80; wdts++) {
00795 Point pt = RemapCoords(wdts->delta_x, wdts->delta_y, wdts->delta_z);
00796 DrawSprite(wdts->image, COMPANY_SPRITE_COLOUR(_local_company), x + pt.x, y + pt.y);
00797 }
00798 }
00799
00800
00801 static uint GetSlopeZ_Water(TileIndex tile, uint x, uint y)
00802 {
00803 uint z;
00804 Slope tileh = GetTileSlope(tile, &z);
00805
00806 return z + GetPartialZ(x & 0xF, y & 0xF, tileh);
00807 }
00808
00809 static Foundation GetFoundation_Water(TileIndex tile, Slope tileh)
00810 {
00811 return FOUNDATION_NONE;
00812 }
00813
00814 static void GetTileDesc_Water(TileIndex tile, TileDesc *td)
00815 {
00816 switch (GetWaterTileType(tile)) {
00817 case WATER_TILE_CLEAR:
00818 switch (GetWaterClass(tile)) {
00819 case WATER_CLASS_SEA: td->str = STR_LAI_WATER_DESCRIPTION_WATER; break;
00820 case WATER_CLASS_CANAL: td->str = STR_LAI_WATER_DESCRIPTION_CANAL; break;
00821 case WATER_CLASS_RIVER: td->str = STR_LAI_WATER_DESCRIPTION_RIVER; break;
00822 default: NOT_REACHED(); break;
00823 }
00824 break;
00825 case WATER_TILE_COAST: td->str = STR_LAI_WATER_DESCRIPTION_COAST_OR_RIVERBANK; break;
00826 case WATER_TILE_LOCK : td->str = STR_LAI_WATER_DESCRIPTION_LOCK; break;
00827 case WATER_TILE_DEPOT:
00828 td->str = STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT;
00829 td->build_date = Depot::GetByTile(tile)->build_date;
00830 break;
00831 default: NOT_REACHED(); break;
00832 }
00833
00834 td->owner[0] = GetTileOwner(tile);
00835 }
00836
00842 static void FloodVehicle(Vehicle *v)
00843 {
00844 uint pass = v->Crash(true);
00845
00846 AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, AIEventVehicleCrashed::CRASH_FLOODED));
00847 SetDParam(0, pass);
00848 AddVehicleNewsItem(STR_NEWS_DISASTER_FLOOD_VEHICLE, NS_ACCIDENT, v->index);
00849 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
00850 SndPlayVehicleFx(SND_12_EXPLOSION, v);
00851 }
00852
00859 static Vehicle *FloodVehicleProc(Vehicle *v, void *data)
00860 {
00861 if ((v->vehstatus & VS_CRASHED) != 0) return NULL;
00862
00863 switch (v->type) {
00864 default: break;
00865
00866 case VEH_AIRCRAFT: {
00867 if (!IsAirportTile(v->tile) || GetTileMaxZ(v->tile) != 0) break;
00868 if (v->subtype == AIR_SHADOW) break;
00869
00870
00871
00872 const Station *st = Station::GetByTile(v->tile);
00873 const AirportFTAClass *airport = st->airport.GetFTA();
00874 if (v->z_pos != airport->delta_z + 1) break;
00875
00876 FloodVehicle(v);
00877 break;
00878 }
00879
00880 case VEH_TRAIN:
00881 case VEH_ROAD: {
00882 byte z = *(byte*)data;
00883 if (v->z_pos > z) break;
00884 FloodVehicle(v->First());
00885 break;
00886 }
00887 }
00888
00889 return NULL;
00890 }
00891
00897 static void FloodVehicles(TileIndex tile)
00898 {
00899 byte z = 0;
00900
00901 if (IsAirportTile(tile)) {
00902 const Station *st = Station::GetByTile(tile);
00903 TILE_AREA_LOOP(tile, st->airport) {
00904 if (st->TileBelongsToAirport(tile)) FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00905 }
00906
00907
00908 return;
00909 }
00910
00911 if (!IsBridgeTile(tile)) {
00912 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00913 return;
00914 }
00915
00916 TileIndex end = GetOtherBridgeEnd(tile);
00917 z = GetBridgeHeight(tile);
00918
00919 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00920 FindVehicleOnPos(end, &z, &FloodVehicleProc);
00921 }
00922
00928 FloodingBehaviour GetFloodingBehaviour(TileIndex tile)
00929 {
00930
00931
00932
00933
00934
00935 switch (GetTileType(tile)) {
00936 case MP_WATER:
00937 if (IsCoast(tile)) {
00938 Slope tileh = GetTileSlope(tile, NULL);
00939 return (IsSlopeWithOneCornerRaised(tileh) ? FLOOD_ACTIVE : FLOOD_DRYUP);
00940 }
00941
00942 case MP_STATION:
00943 case MP_INDUSTRY:
00944 case MP_OBJECT:
00945 return (GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE;
00946
00947 case MP_RAILWAY:
00948 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
00949 return (IsSlopeWithOneCornerRaised(GetTileSlope(tile, NULL)) ? FLOOD_ACTIVE : FLOOD_DRYUP);
00950 }
00951 return FLOOD_NONE;
00952
00953 case MP_TREES:
00954 return (GetTreeGround(tile) == TREE_GROUND_SHORE ? FLOOD_DRYUP : FLOOD_NONE);
00955
00956 default:
00957 return FLOOD_NONE;
00958 }
00959 }
00960
00964 void DoFloodTile(TileIndex target)
00965 {
00966 assert(!IsTileType(target, MP_WATER));
00967
00968 bool flooded = false;
00969
00970 Backup<CompanyByte> cur_company(_current_company, OWNER_WATER, FILE_LINE);
00971
00972 Slope tileh = GetTileSlope(target, NULL);
00973 if (tileh != SLOPE_FLAT) {
00974
00975 switch (GetTileType(target)) {
00976 case MP_RAILWAY: {
00977 if (!IsPlainRail(target)) break;
00978 FloodVehicles(target);
00979 flooded = FloodHalftile(target);
00980 break;
00981 }
00982
00983 case MP_TREES:
00984 if (!IsSlopeWithOneCornerRaised(tileh)) {
00985 SetTreeGroundDensity(target, TREE_GROUND_SHORE, 3);
00986 MarkTileDirtyByTile(target);
00987 flooded = true;
00988 break;
00989 }
00990
00991
00992 case MP_CLEAR:
00993 if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
00994 MakeShore(target);
00995 MarkTileDirtyByTile(target);
00996 flooded = true;
00997 }
00998 break;
00999
01000 default:
01001 break;
01002 }
01003 } else {
01004
01005 FloodVehicles(target);
01006
01007
01008 if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
01009 MakeSea(target);
01010 MarkTileDirtyByTile(target);
01011 flooded = true;
01012 }
01013 }
01014
01015 if (flooded) {
01016
01017 MarkCanalsAndRiversAroundDirty(target);
01018
01019
01020 UpdateSignalsInBuffer();
01021 }
01022
01023 cur_company.Restore();
01024 }
01025
01029 static void DoDryUp(TileIndex tile)
01030 {
01031 Backup<CompanyByte> cur_company(_current_company, OWNER_WATER, FILE_LINE);
01032
01033 switch (GetTileType(tile)) {
01034 case MP_RAILWAY:
01035 assert(IsPlainRail(tile));
01036 assert(GetRailGroundType(tile) == RAIL_GROUND_WATER);
01037
01038 RailGroundType new_ground;
01039 switch (GetTrackBits(tile)) {
01040 case TRACK_BIT_UPPER: new_ground = RAIL_GROUND_FENCE_HORIZ1; break;
01041 case TRACK_BIT_LOWER: new_ground = RAIL_GROUND_FENCE_HORIZ2; break;
01042 case TRACK_BIT_LEFT: new_ground = RAIL_GROUND_FENCE_VERT1; break;
01043 case TRACK_BIT_RIGHT: new_ground = RAIL_GROUND_FENCE_VERT2; break;
01044 default: NOT_REACHED();
01045 }
01046 SetRailGroundType(tile, new_ground);
01047 MarkTileDirtyByTile(tile);
01048 break;
01049
01050 case MP_TREES:
01051 SetTreeGroundDensity(tile, TREE_GROUND_GRASS, 3);
01052 MarkTileDirtyByTile(tile);
01053 break;
01054
01055 case MP_WATER:
01056 assert(IsCoast(tile));
01057
01058 if (DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
01059 MakeClear(tile, CLEAR_GRASS, 3);
01060 MarkTileDirtyByTile(tile);
01061 }
01062 break;
01063
01064 default: NOT_REACHED();
01065 }
01066
01067 cur_company.Restore();
01068 }
01069
01076 void TileLoop_Water(TileIndex tile)
01077 {
01078 switch (GetFloodingBehaviour(tile)) {
01079 case FLOOD_ACTIVE:
01080 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
01081 TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir(dir));
01082 if (dest == INVALID_TILE) continue;
01083
01084 if (IsTileType(dest, MP_WATER)) continue;
01085
01086 uint z_dest;
01087 Slope slope_dest = GetFoundationSlope(dest, &z_dest) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
01088 if (z_dest > 0) continue;
01089
01090 if (!HasBit(_flood_from_dirs[slope_dest], ReverseDir(dir))) continue;
01091
01092 DoFloodTile(dest);
01093 }
01094 break;
01095
01096 case FLOOD_DRYUP: {
01097 Slope slope_here = GetFoundationSlope(tile, NULL) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
01098 uint dir;
01099 FOR_EACH_SET_BIT(dir, _flood_from_dirs[slope_here]) {
01100 TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir((Direction)dir));
01101 if (dest == INVALID_TILE) continue;
01102
01103 FloodingBehaviour dest_behaviour = GetFloodingBehaviour(dest);
01104 if ((dest_behaviour == FLOOD_ACTIVE) || (dest_behaviour == FLOOD_PASSIVE)) return;
01105 }
01106 DoDryUp(tile);
01107 break;
01108 }
01109
01110 default: return;
01111 }
01112 }
01113
01114 void ConvertGroundTilesIntoWaterTiles()
01115 {
01116 uint z;
01117
01118 for (TileIndex tile = 0; tile < MapSize(); ++tile) {
01119 Slope slope = GetTileSlope(tile, &z);
01120 if (IsTileType(tile, MP_CLEAR) && z == 0) {
01121
01122
01123
01124 switch (slope) {
01125 case SLOPE_FLAT:
01126 MakeSea(tile);
01127 break;
01128
01129 case SLOPE_N:
01130 case SLOPE_E:
01131 case SLOPE_S:
01132 case SLOPE_W:
01133 MakeShore(tile);
01134 break;
01135
01136 default:
01137 uint dir;
01138 FOR_EACH_SET_BIT(dir, _flood_from_dirs[slope & ~SLOPE_STEEP]) {
01139 TileIndex dest = TILE_ADD(tile, TileOffsByDir((Direction)dir));
01140 Slope slope_dest = GetTileSlope(dest, NULL) & ~SLOPE_STEEP;
01141 if (slope_dest == SLOPE_FLAT || IsSlopeWithOneCornerRaised(slope_dest)) {
01142 MakeShore(tile);
01143 break;
01144 }
01145 }
01146 break;
01147 }
01148 }
01149 }
01150 }
01151
01152 static TrackStatus GetTileTrackStatus_Water(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
01153 {
01154 static const byte coast_tracks[] = {0, 32, 4, 0, 16, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0};
01155
01156 TrackBits ts;
01157
01158 if (mode != TRANSPORT_WATER) return 0;
01159
01160 switch (GetWaterTileType(tile)) {
01161 case WATER_TILE_CLEAR: ts = (GetTileSlope(tile, NULL) == SLOPE_FLAT) ? TRACK_BIT_ALL : TRACK_BIT_NONE; break;
01162 case WATER_TILE_COAST: ts = (TrackBits)coast_tracks[GetTileSlope(tile, NULL) & 0xF]; break;
01163 case WATER_TILE_LOCK: ts = DiagDirToDiagTrackBits(GetLockDirection(tile)); break;
01164 case WATER_TILE_DEPOT: ts = AxisToTrackBits(GetShipDepotAxis(tile)); break;
01165 default: return 0;
01166 }
01167 if (TileX(tile) == 0) {
01168
01169 ts &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
01170 }
01171 if (TileY(tile) == 0) {
01172
01173 ts &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
01174 }
01175 return CombineTrackStatus(TrackBitsToTrackdirBits(ts), TRACKDIR_BIT_NONE);
01176 }
01177
01178 static bool ClickTile_Water(TileIndex tile)
01179 {
01180 if (GetWaterTileType(tile) == WATER_TILE_DEPOT) {
01181 ShowDepotWindow(GetShipDepotNorthTile(tile), VEH_SHIP);
01182 return true;
01183 }
01184 return false;
01185 }
01186
01187 static void ChangeTileOwner_Water(TileIndex tile, Owner old_owner, Owner new_owner)
01188 {
01189 if (!IsTileOwner(tile, old_owner)) return;
01190
01191 if (new_owner != INVALID_OWNER) {
01192 SetTileOwner(tile, new_owner);
01193 return;
01194 }
01195
01196
01197 if (IsShipDepot(tile)) DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
01198
01199
01200
01201 if (IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
01202 }
01203
01204 static VehicleEnterTileStatus VehicleEnter_Water(Vehicle *v, TileIndex tile, int x, int y)
01205 {
01206 return VETSB_CONTINUE;
01207 }
01208
01209 static CommandCost TerraformTile_Water(TileIndex tile, DoCommandFlag flags, uint z_new, Slope tileh_new)
01210 {
01211
01212 if (IsWaterTile(tile) && IsCanal(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_CANAL_FIRST);
01213
01214 return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
01215 }
01216
01217
01218 extern const TileTypeProcs _tile_type_water_procs = {
01219 DrawTile_Water,
01220 GetSlopeZ_Water,
01221 ClearTile_Water,
01222 NULL,
01223 GetTileDesc_Water,
01224 GetTileTrackStatus_Water,
01225 ClickTile_Water,
01226 NULL,
01227 TileLoop_Water,
01228 ChangeTileOwner_Water,
01229 NULL,
01230 VehicleEnter_Water,
01231 GetFoundation_Water,
01232 TerraformTile_Water,
01233 };