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 + delta));
00283 MakeWaterKeepingClass(tile - delta, GetTileOwner(tile - delta));
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 struct LocksDrawTileStruct {
00615 int8 delta_x, delta_y, delta_z;
00616 byte width, height, depth;
00617 SpriteID image;
00618 };
00619
00620 #include "table/water_land.h"
00621
00631 static void DrawWaterTileStruct(const TileInfo *ti, const WaterDrawTileStruct *wdts, SpriteID base, uint offset, PaletteID palette, CanalFeature feature)
00632 {
00633
00634 if (IsInvisibilitySet(TO_BUILDINGS)) return;
00635
00636 for (; wdts->delta_x != 0x80; wdts++) {
00637 uint tile_offs = offset + wdts->image;
00638 if (feature < CF_END) tile_offs = GetCanalSpriteOffset(feature, ti->tile, tile_offs);
00639 AddSortableSpriteToDraw(base + tile_offs, palette,
00640 ti->x + wdts->delta_x, ti->y + wdts->delta_y,
00641 wdts->size_x, wdts->size_y,
00642 wdts->size_z, ti->z + wdts->delta_z,
00643 IsTransparencySet(TO_BUILDINGS));
00644 }
00645 }
00646
00648 static void DrawWaterLock(const TileInfo *ti)
00649 {
00650 int section = GetSection(ti->tile);
00651 const WaterDrawTileStruct *wdts = _lock_display_seq[section];
00652
00653
00654 SpriteID image = wdts++->image;
00655
00656 SpriteID water_base = GetCanalSprite(CF_WATERSLOPE, ti->tile);
00657 if (water_base == 0) {
00658
00659 water_base = SPR_CANALS_BASE;
00660 } else if (HasBit(_water_feature[CF_WATERSLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
00661
00662 if (image == SPR_FLAT_WATER_TILE) {
00663 image = water_base;
00664 } else {
00665 image++;
00666 }
00667 }
00668
00669 if (image < 5) image += water_base;
00670 DrawGroundSprite(image, PAL_NONE);
00671
00672
00673 uint zoffs = 0;
00674 SpriteID base = GetCanalSprite(CF_LOCKS, ti->tile);
00675
00676 if (base == 0) {
00677
00678 base = SPR_LOCK_BASE;
00679 uint8 z_threshold = section >= 8 ? 8 : 0;
00680 zoffs = ti->z > z_threshold ? 24 : 0;
00681 }
00682
00683 DrawWaterTileStruct(ti, wdts, base, zoffs, PAL_NONE, CF_LOCKS);
00684 }
00685
00687 static void DrawWaterDepot(const TileInfo *ti)
00688 {
00689 DrawWaterClassGround(ti);
00690
00691 DrawWaterTileStruct(ti, _shipdepot_display_seq[GetSection(ti->tile)] + 1, 0, 0, COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile)), CF_END);
00692 }
00693
00694 static void DrawRiverWater(const TileInfo *ti)
00695 {
00696 SpriteID image = SPR_FLAT_WATER_TILE;
00697 uint offset = 0;
00698 uint edges_offset = 0;
00699
00700 if (ti->tileh != SLOPE_FLAT || HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE)) {
00701 image = GetCanalSprite(CF_RIVER_SLOPE, ti->tile);
00702 if (image == 0) {
00703 switch (ti->tileh) {
00704 case SLOPE_NW: image = SPR_WATER_SLOPE_Y_DOWN; break;
00705 case SLOPE_SW: image = SPR_WATER_SLOPE_X_UP; break;
00706 case SLOPE_SE: image = SPR_WATER_SLOPE_Y_UP; break;
00707 case SLOPE_NE: image = SPR_WATER_SLOPE_X_DOWN; break;
00708 default: image = SPR_FLAT_WATER_TILE; break;
00709 }
00710 } else {
00711
00712 offset = HasBit(_water_feature[CF_RIVER_SLOPE].flags, CFF_HAS_FLAT_SPRITE) ? 1 : 0;
00713
00714 switch (ti->tileh) {
00715 case SLOPE_SE: edges_offset += 12; break;
00716 case SLOPE_NE: offset += 1; edges_offset += 24; break;
00717 case SLOPE_SW: offset += 2; edges_offset += 36; break;
00718 case SLOPE_NW: offset += 3; edges_offset += 48; break;
00719 default: offset = 0; break;
00720 }
00721
00722 offset = GetCanalSpriteOffset(CF_RIVER_SLOPE, ti->tile, offset);
00723 }
00724 }
00725
00726 DrawGroundSprite(image + offset, PAL_NONE);
00727
00728
00729 DrawWaterEdges(false, edges_offset, ti->tile);
00730 }
00731
00732 void DrawShoreTile(Slope tileh)
00733 {
00734
00735
00736 static const byte tileh_to_shoresprite[32] = {
00737 0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0,
00738 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 15, 0,
00739 };
00740
00741 assert(!IsHalftileSlope(tileh));
00742 assert(tileh != SLOPE_FLAT);
00743
00744 assert((tileh != SLOPE_EW) && (tileh != SLOPE_NS));
00745
00746 DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[tileh], PAL_NONE);
00747 }
00748
00749 void DrawWaterClassGround(const TileInfo *ti)
00750 {
00751 switch (GetWaterClass(ti->tile)) {
00752 case WATER_CLASS_SEA: DrawSeaWater(ti->tile); break;
00753 case WATER_CLASS_CANAL: DrawCanalWater(ti->tile); break;
00754 case WATER_CLASS_RIVER: DrawRiverWater(ti); break;
00755 default: NOT_REACHED();
00756 }
00757 }
00758
00759 static void DrawTile_Water(TileInfo *ti)
00760 {
00761 switch (GetWaterTileType(ti->tile)) {
00762 case WATER_TILE_CLEAR:
00763 DrawWaterClassGround(ti);
00764 DrawBridgeMiddle(ti);
00765 break;
00766
00767 case WATER_TILE_COAST: {
00768 DrawShoreTile(ti->tileh);
00769 DrawBridgeMiddle(ti);
00770 break;
00771 }
00772
00773 case WATER_TILE_LOCK:
00774 DrawWaterLock(ti);
00775 break;
00776
00777 case WATER_TILE_DEPOT:
00778 DrawWaterDepot(ti);
00779 break;
00780 }
00781 }
00782
00783 void DrawShipDepotSprite(int x, int y, int image)
00784 {
00785 const WaterDrawTileStruct *wdts = _shipdepot_display_seq[image];
00786
00787 DrawSprite(wdts++->image, PAL_NONE, x, y);
00788
00789 for (; wdts->delta_x != 0x80; wdts++) {
00790 Point pt = RemapCoords(wdts->delta_x, wdts->delta_y, wdts->delta_z);
00791 DrawSprite(wdts->image, COMPANY_SPRITE_COLOUR(_local_company), x + pt.x, y + pt.y);
00792 }
00793 }
00794
00795
00796 static uint GetSlopeZ_Water(TileIndex tile, uint x, uint y)
00797 {
00798 uint z;
00799 Slope tileh = GetTileSlope(tile, &z);
00800
00801 return z + GetPartialZ(x & 0xF, y & 0xF, tileh);
00802 }
00803
00804 static Foundation GetFoundation_Water(TileIndex tile, Slope tileh)
00805 {
00806 return FOUNDATION_NONE;
00807 }
00808
00809 static void GetTileDesc_Water(TileIndex tile, TileDesc *td)
00810 {
00811 switch (GetWaterTileType(tile)) {
00812 case WATER_TILE_CLEAR:
00813 switch (GetWaterClass(tile)) {
00814 case WATER_CLASS_SEA: td->str = STR_LAI_WATER_DESCRIPTION_WATER; break;
00815 case WATER_CLASS_CANAL: td->str = STR_LAI_WATER_DESCRIPTION_CANAL; break;
00816 case WATER_CLASS_RIVER: td->str = STR_LAI_WATER_DESCRIPTION_RIVER; break;
00817 default: NOT_REACHED(); break;
00818 }
00819 break;
00820 case WATER_TILE_COAST: td->str = STR_LAI_WATER_DESCRIPTION_COAST_OR_RIVERBANK; break;
00821 case WATER_TILE_LOCK : td->str = STR_LAI_WATER_DESCRIPTION_LOCK; break;
00822 case WATER_TILE_DEPOT:
00823 td->str = STR_LAI_WATER_DESCRIPTION_SHIP_DEPOT;
00824 td->build_date = Depot::GetByTile(tile)->build_date;
00825 break;
00826 default: NOT_REACHED(); break;
00827 }
00828
00829 td->owner[0] = GetTileOwner(tile);
00830 }
00831
00837 static void FloodVehicle(Vehicle *v)
00838 {
00839 uint pass = v->Crash(true);
00840
00841 AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, AIEventVehicleCrashed::CRASH_FLOODED));
00842 SetDParam(0, pass);
00843 AddVehicleNewsItem(STR_NEWS_DISASTER_FLOOD_VEHICLE, NS_ACCIDENT, v->index);
00844 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
00845 SndPlayVehicleFx(SND_12_EXPLOSION, v);
00846 }
00847
00854 static Vehicle *FloodVehicleProc(Vehicle *v, void *data)
00855 {
00856 if ((v->vehstatus & VS_CRASHED) != 0) return NULL;
00857
00858 switch (v->type) {
00859 default: break;
00860
00861 case VEH_AIRCRAFT: {
00862 if (!IsAirportTile(v->tile) || GetTileMaxZ(v->tile) != 0) break;
00863 if (v->subtype == AIR_SHADOW) break;
00864
00865
00866
00867 const Station *st = Station::GetByTile(v->tile);
00868 const AirportFTAClass *airport = st->airport.GetFTA();
00869 if (v->z_pos != airport->delta_z + 1) break;
00870
00871 FloodVehicle(v);
00872 break;
00873 }
00874
00875 case VEH_TRAIN:
00876 case VEH_ROAD: {
00877 byte z = *(byte*)data;
00878 if (v->z_pos > z) break;
00879 FloodVehicle(v->First());
00880 break;
00881 }
00882 }
00883
00884 return NULL;
00885 }
00886
00892 static void FloodVehicles(TileIndex tile)
00893 {
00894 byte z = 0;
00895
00896 if (IsAirportTile(tile)) {
00897 const Station *st = Station::GetByTile(tile);
00898 TILE_AREA_LOOP(tile, st->airport) {
00899 if (st->TileBelongsToAirport(tile)) FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00900 }
00901
00902
00903 return;
00904 }
00905
00906 if (!IsBridgeTile(tile)) {
00907 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00908 return;
00909 }
00910
00911 TileIndex end = GetOtherBridgeEnd(tile);
00912 z = GetBridgeHeight(tile);
00913
00914 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00915 FindVehicleOnPos(end, &z, &FloodVehicleProc);
00916 }
00917
00923 FloodingBehaviour GetFloodingBehaviour(TileIndex tile)
00924 {
00925
00926
00927
00928
00929
00930 switch (GetTileType(tile)) {
00931 case MP_WATER:
00932 if (IsCoast(tile)) {
00933 Slope tileh = GetTileSlope(tile, NULL);
00934 return (IsSlopeWithOneCornerRaised(tileh) ? FLOOD_ACTIVE : FLOOD_DRYUP);
00935 }
00936
00937 case MP_STATION:
00938 case MP_INDUSTRY:
00939 case MP_OBJECT:
00940 return (GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE;
00941
00942 case MP_RAILWAY:
00943 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
00944 return (IsSlopeWithOneCornerRaised(GetTileSlope(tile, NULL)) ? FLOOD_ACTIVE : FLOOD_DRYUP);
00945 }
00946 return FLOOD_NONE;
00947
00948 case MP_TREES:
00949 return (GetTreeGround(tile) == TREE_GROUND_SHORE ? FLOOD_DRYUP : FLOOD_NONE);
00950
00951 default:
00952 return FLOOD_NONE;
00953 }
00954 }
00955
00959 void DoFloodTile(TileIndex target)
00960 {
00961 assert(!IsTileType(target, MP_WATER));
00962
00963 bool flooded = false;
00964
00965 Backup<CompanyByte> cur_company(_current_company, OWNER_WATER, FILE_LINE);
00966
00967 Slope tileh = GetTileSlope(target, NULL);
00968 if (tileh != SLOPE_FLAT) {
00969
00970 switch (GetTileType(target)) {
00971 case MP_RAILWAY: {
00972 if (!IsPlainRail(target)) break;
00973 FloodVehicles(target);
00974 flooded = FloodHalftile(target);
00975 break;
00976 }
00977
00978 case MP_TREES:
00979 if (!IsSlopeWithOneCornerRaised(tileh)) {
00980 SetTreeGroundDensity(target, TREE_GROUND_SHORE, 3);
00981 MarkTileDirtyByTile(target);
00982 flooded = true;
00983 break;
00984 }
00985
00986
00987 case MP_CLEAR:
00988 if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
00989 MakeShore(target);
00990 MarkTileDirtyByTile(target);
00991 flooded = true;
00992 }
00993 break;
00994
00995 default:
00996 break;
00997 }
00998 } else {
00999
01000 FloodVehicles(target);
01001
01002
01003 if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
01004 MakeSea(target);
01005 MarkTileDirtyByTile(target);
01006 flooded = true;
01007 }
01008 }
01009
01010 if (flooded) {
01011
01012 MarkCanalsAndRiversAroundDirty(target);
01013
01014
01015 UpdateSignalsInBuffer();
01016 }
01017
01018 cur_company.Restore();
01019 }
01020
01024 static void DoDryUp(TileIndex tile)
01025 {
01026 Backup<CompanyByte> cur_company(_current_company, OWNER_WATER, FILE_LINE);
01027
01028 switch (GetTileType(tile)) {
01029 case MP_RAILWAY:
01030 assert(IsPlainRail(tile));
01031 assert(GetRailGroundType(tile) == RAIL_GROUND_WATER);
01032
01033 RailGroundType new_ground;
01034 switch (GetTrackBits(tile)) {
01035 case TRACK_BIT_UPPER: new_ground = RAIL_GROUND_FENCE_HORIZ1; break;
01036 case TRACK_BIT_LOWER: new_ground = RAIL_GROUND_FENCE_HORIZ2; break;
01037 case TRACK_BIT_LEFT: new_ground = RAIL_GROUND_FENCE_VERT1; break;
01038 case TRACK_BIT_RIGHT: new_ground = RAIL_GROUND_FENCE_VERT2; break;
01039 default: NOT_REACHED();
01040 }
01041 SetRailGroundType(tile, new_ground);
01042 MarkTileDirtyByTile(tile);
01043 break;
01044
01045 case MP_TREES:
01046 SetTreeGroundDensity(tile, TREE_GROUND_GRASS, 3);
01047 MarkTileDirtyByTile(tile);
01048 break;
01049
01050 case MP_WATER:
01051 assert(IsCoast(tile));
01052
01053 if (DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
01054 MakeClear(tile, CLEAR_GRASS, 3);
01055 MarkTileDirtyByTile(tile);
01056 }
01057 break;
01058
01059 default: NOT_REACHED();
01060 }
01061
01062 cur_company.Restore();
01063 }
01064
01071 void TileLoop_Water(TileIndex tile)
01072 {
01073 switch (GetFloodingBehaviour(tile)) {
01074 case FLOOD_ACTIVE:
01075 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
01076 TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir(dir));
01077 if (dest == INVALID_TILE) continue;
01078
01079 if (IsTileType(dest, MP_WATER)) continue;
01080
01081 uint z_dest;
01082 Slope slope_dest = GetFoundationSlope(dest, &z_dest) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
01083 if (z_dest > 0) continue;
01084
01085 if (!HasBit(_flood_from_dirs[slope_dest], ReverseDir(dir))) continue;
01086
01087 DoFloodTile(dest);
01088 }
01089 break;
01090
01091 case FLOOD_DRYUP: {
01092 Slope slope_here = GetFoundationSlope(tile, NULL) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
01093 uint dir;
01094 FOR_EACH_SET_BIT(dir, _flood_from_dirs[slope_here]) {
01095 TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir((Direction)dir));
01096 if (dest == INVALID_TILE) continue;
01097
01098 FloodingBehaviour dest_behaviour = GetFloodingBehaviour(dest);
01099 if ((dest_behaviour == FLOOD_ACTIVE) || (dest_behaviour == FLOOD_PASSIVE)) return;
01100 }
01101 DoDryUp(tile);
01102 break;
01103 }
01104
01105 default: return;
01106 }
01107 }
01108
01109 void ConvertGroundTilesIntoWaterTiles()
01110 {
01111 uint z;
01112
01113 for (TileIndex tile = 0; tile < MapSize(); ++tile) {
01114 Slope slope = GetTileSlope(tile, &z);
01115 if (IsTileType(tile, MP_CLEAR) && z == 0) {
01116
01117
01118
01119 switch (slope) {
01120 case SLOPE_FLAT:
01121 MakeSea(tile);
01122 break;
01123
01124 case SLOPE_N:
01125 case SLOPE_E:
01126 case SLOPE_S:
01127 case SLOPE_W:
01128 MakeShore(tile);
01129 break;
01130
01131 default:
01132 uint dir;
01133 FOR_EACH_SET_BIT(dir, _flood_from_dirs[slope & ~SLOPE_STEEP]) {
01134 TileIndex dest = TILE_ADD(tile, TileOffsByDir((Direction)dir));
01135 Slope slope_dest = GetTileSlope(dest, NULL) & ~SLOPE_STEEP;
01136 if (slope_dest == SLOPE_FLAT || IsSlopeWithOneCornerRaised(slope_dest)) {
01137 MakeShore(tile);
01138 break;
01139 }
01140 }
01141 break;
01142 }
01143 }
01144 }
01145 }
01146
01147 static TrackStatus GetTileTrackStatus_Water(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
01148 {
01149 static const byte coast_tracks[] = {0, 32, 4, 0, 16, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0};
01150
01151 TrackBits ts;
01152
01153 if (mode != TRANSPORT_WATER) return 0;
01154
01155 switch (GetWaterTileType(tile)) {
01156 case WATER_TILE_CLEAR: ts = (GetTileSlope(tile, NULL) == SLOPE_FLAT) ? TRACK_BIT_ALL : TRACK_BIT_NONE; break;
01157 case WATER_TILE_COAST: ts = (TrackBits)coast_tracks[GetTileSlope(tile, NULL) & 0xF]; break;
01158 case WATER_TILE_LOCK: ts = DiagDirToDiagTrackBits(GetLockDirection(tile)); break;
01159 case WATER_TILE_DEPOT: ts = AxisToTrackBits(GetShipDepotAxis(tile)); break;
01160 default: return 0;
01161 }
01162 if (TileX(tile) == 0) {
01163
01164 ts &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
01165 }
01166 if (TileY(tile) == 0) {
01167
01168 ts &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
01169 }
01170 return CombineTrackStatus(TrackBitsToTrackdirBits(ts), TRACKDIR_BIT_NONE);
01171 }
01172
01173 static bool ClickTile_Water(TileIndex tile)
01174 {
01175 if (GetWaterTileType(tile) == WATER_TILE_DEPOT) {
01176 ShowDepotWindow(GetShipDepotNorthTile(tile), VEH_SHIP);
01177 return true;
01178 }
01179 return false;
01180 }
01181
01182 static void ChangeTileOwner_Water(TileIndex tile, Owner old_owner, Owner new_owner)
01183 {
01184 if (!IsTileOwner(tile, old_owner)) return;
01185
01186 if (new_owner != INVALID_OWNER) {
01187 SetTileOwner(tile, new_owner);
01188 return;
01189 }
01190
01191
01192 if (IsShipDepot(tile)) DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
01193
01194
01195
01196 if (IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
01197 }
01198
01199 static VehicleEnterTileStatus VehicleEnter_Water(Vehicle *v, TileIndex tile, int x, int y)
01200 {
01201 return VETSB_CONTINUE;
01202 }
01203
01204 static CommandCost TerraformTile_Water(TileIndex tile, DoCommandFlag flags, uint z_new, Slope tileh_new)
01205 {
01206
01207 if (IsWaterTile(tile) && IsCanal(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_CANAL_FIRST);
01208
01209 return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
01210 }
01211
01212
01213 extern const TileTypeProcs _tile_type_water_procs = {
01214 DrawTile_Water,
01215 GetSlopeZ_Water,
01216 ClearTile_Water,
01217 NULL,
01218 GetTileDesc_Water,
01219 GetTileTrackStatus_Water,
01220 ClickTile_Water,
01221 NULL,
01222 TileLoop_Water,
01223 ChangeTileOwner_Water,
01224 NULL,
01225 VehicleEnter_Water,
01226 GetFoundation_Water,
01227 TerraformTile_Water,
01228 };