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