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