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
00906 if (!_settings_game.station.nonuniform_stations && IsTileType(tile, MP_STATION) && GetStationType(tile) == STATION_RAIL) {
00907 const Station *st = Station::GetByTile(tile);
00908
00909 TILE_AREA_LOOP(t, st->train_station) {
00910 if (st->TileBelongsToRailStation(t)) {
00911 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00912 }
00913 }
00914
00915 return;
00916 }
00917
00918 if (!IsBridgeTile(tile)) {
00919 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00920 return;
00921 }
00922
00923 TileIndex end = GetOtherBridgeEnd(tile);
00924 z = GetBridgeHeight(tile);
00925
00926 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00927 FindVehicleOnPos(end, &z, &FloodVehicleProc);
00928 }
00929
00935 FloodingBehaviour GetFloodingBehaviour(TileIndex tile)
00936 {
00937
00938
00939
00940
00941
00942 switch (GetTileType(tile)) {
00943 case MP_WATER:
00944 if (IsCoast(tile)) {
00945 Slope tileh = GetTileSlope(tile, NULL);
00946 return (IsSlopeWithOneCornerRaised(tileh) ? FLOOD_ACTIVE : FLOOD_DRYUP);
00947 }
00948
00949 case MP_STATION:
00950 case MP_INDUSTRY:
00951 case MP_OBJECT:
00952 return (GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE;
00953
00954 case MP_RAILWAY:
00955 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
00956 return (IsSlopeWithOneCornerRaised(GetTileSlope(tile, NULL)) ? FLOOD_ACTIVE : FLOOD_DRYUP);
00957 }
00958 return FLOOD_NONE;
00959
00960 case MP_TREES:
00961 return (GetTreeGround(tile) == TREE_GROUND_SHORE ? FLOOD_DRYUP : FLOOD_NONE);
00962
00963 default:
00964 return FLOOD_NONE;
00965 }
00966 }
00967
00971 void DoFloodTile(TileIndex target)
00972 {
00973 assert(!IsTileType(target, MP_WATER));
00974
00975 bool flooded = false;
00976
00977 Backup<CompanyByte> cur_company(_current_company, OWNER_WATER, FILE_LINE);
00978
00979 Slope tileh = GetTileSlope(target, NULL);
00980 if (tileh != SLOPE_FLAT) {
00981
00982 switch (GetTileType(target)) {
00983 case MP_RAILWAY: {
00984 if (!IsPlainRail(target)) break;
00985 FloodVehicles(target);
00986 flooded = FloodHalftile(target);
00987 break;
00988 }
00989
00990 case MP_TREES:
00991 if (!IsSlopeWithOneCornerRaised(tileh)) {
00992 SetTreeGroundDensity(target, TREE_GROUND_SHORE, 3);
00993 MarkTileDirtyByTile(target);
00994 flooded = true;
00995 break;
00996 }
00997
00998
00999 case MP_CLEAR:
01000 if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
01001 MakeShore(target);
01002 MarkTileDirtyByTile(target);
01003 flooded = true;
01004 }
01005 break;
01006
01007 default:
01008 break;
01009 }
01010 } else {
01011
01012 FloodVehicles(target);
01013
01014
01015 if (DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
01016 MakeSea(target);
01017 MarkTileDirtyByTile(target);
01018 flooded = true;
01019 }
01020 }
01021
01022 if (flooded) {
01023
01024 MarkCanalsAndRiversAroundDirty(target);
01025
01026
01027 UpdateSignalsInBuffer();
01028 }
01029
01030 cur_company.Restore();
01031 }
01032
01036 static void DoDryUp(TileIndex tile)
01037 {
01038 Backup<CompanyByte> cur_company(_current_company, OWNER_WATER, FILE_LINE);
01039
01040 switch (GetTileType(tile)) {
01041 case MP_RAILWAY:
01042 assert(IsPlainRail(tile));
01043 assert(GetRailGroundType(tile) == RAIL_GROUND_WATER);
01044
01045 RailGroundType new_ground;
01046 switch (GetTrackBits(tile)) {
01047 case TRACK_BIT_UPPER: new_ground = RAIL_GROUND_FENCE_HORIZ1; break;
01048 case TRACK_BIT_LOWER: new_ground = RAIL_GROUND_FENCE_HORIZ2; break;
01049 case TRACK_BIT_LEFT: new_ground = RAIL_GROUND_FENCE_VERT1; break;
01050 case TRACK_BIT_RIGHT: new_ground = RAIL_GROUND_FENCE_VERT2; break;
01051 default: NOT_REACHED();
01052 }
01053 SetRailGroundType(tile, new_ground);
01054 MarkTileDirtyByTile(tile);
01055 break;
01056
01057 case MP_TREES:
01058 SetTreeGroundDensity(tile, TREE_GROUND_GRASS, 3);
01059 MarkTileDirtyByTile(tile);
01060 break;
01061
01062 case MP_WATER:
01063 assert(IsCoast(tile));
01064
01065 if (DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR).Succeeded()) {
01066 MakeClear(tile, CLEAR_GRASS, 3);
01067 MarkTileDirtyByTile(tile);
01068 }
01069 break;
01070
01071 default: NOT_REACHED();
01072 }
01073
01074 cur_company.Restore();
01075 }
01076
01083 void TileLoop_Water(TileIndex tile)
01084 {
01085 switch (GetFloodingBehaviour(tile)) {
01086 case FLOOD_ACTIVE:
01087 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
01088 TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir(dir));
01089 if (dest == INVALID_TILE) continue;
01090
01091 if (IsTileType(dest, MP_WATER)) continue;
01092
01093 uint z_dest;
01094 Slope slope_dest = GetFoundationSlope(dest, &z_dest) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
01095 if (z_dest > 0) continue;
01096
01097 if (!HasBit(_flood_from_dirs[slope_dest], ReverseDir(dir))) continue;
01098
01099 DoFloodTile(dest);
01100 }
01101 break;
01102
01103 case FLOOD_DRYUP: {
01104 Slope slope_here = GetFoundationSlope(tile, NULL) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
01105 uint dir;
01106 FOR_EACH_SET_BIT(dir, _flood_from_dirs[slope_here]) {
01107 TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir((Direction)dir));
01108 if (dest == INVALID_TILE) continue;
01109
01110 FloodingBehaviour dest_behaviour = GetFloodingBehaviour(dest);
01111 if ((dest_behaviour == FLOOD_ACTIVE) || (dest_behaviour == FLOOD_PASSIVE)) return;
01112 }
01113 DoDryUp(tile);
01114 break;
01115 }
01116
01117 default: return;
01118 }
01119 }
01120
01121 void ConvertGroundTilesIntoWaterTiles()
01122 {
01123 uint z;
01124
01125 for (TileIndex tile = 0; tile < MapSize(); ++tile) {
01126 Slope slope = GetTileSlope(tile, &z);
01127 if (IsTileType(tile, MP_CLEAR) && z == 0) {
01128
01129
01130
01131 switch (slope) {
01132 case SLOPE_FLAT:
01133 MakeSea(tile);
01134 break;
01135
01136 case SLOPE_N:
01137 case SLOPE_E:
01138 case SLOPE_S:
01139 case SLOPE_W:
01140 MakeShore(tile);
01141 break;
01142
01143 default:
01144 uint dir;
01145 FOR_EACH_SET_BIT(dir, _flood_from_dirs[slope & ~SLOPE_STEEP]) {
01146 TileIndex dest = TILE_ADD(tile, TileOffsByDir((Direction)dir));
01147 Slope slope_dest = GetTileSlope(dest, NULL) & ~SLOPE_STEEP;
01148 if (slope_dest == SLOPE_FLAT || IsSlopeWithOneCornerRaised(slope_dest)) {
01149 MakeShore(tile);
01150 break;
01151 }
01152 }
01153 break;
01154 }
01155 }
01156 }
01157 }
01158
01159 static TrackStatus GetTileTrackStatus_Water(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
01160 {
01161 static const byte coast_tracks[] = {0, 32, 4, 0, 16, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0};
01162
01163 TrackBits ts;
01164
01165 if (mode != TRANSPORT_WATER) return 0;
01166
01167 switch (GetWaterTileType(tile)) {
01168 case WATER_TILE_CLEAR: ts = (GetTileSlope(tile, NULL) == SLOPE_FLAT) ? TRACK_BIT_ALL : TRACK_BIT_NONE; break;
01169 case WATER_TILE_COAST: ts = (TrackBits)coast_tracks[GetTileSlope(tile, NULL) & 0xF]; break;
01170 case WATER_TILE_LOCK: ts = DiagDirToDiagTrackBits(GetLockDirection(tile)); break;
01171 case WATER_TILE_DEPOT: ts = AxisToTrackBits(GetShipDepotAxis(tile)); break;
01172 default: return 0;
01173 }
01174 if (TileX(tile) == 0) {
01175
01176 ts &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
01177 }
01178 if (TileY(tile) == 0) {
01179
01180 ts &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
01181 }
01182 return CombineTrackStatus(TrackBitsToTrackdirBits(ts), TRACKDIR_BIT_NONE);
01183 }
01184
01185 static bool ClickTile_Water(TileIndex tile)
01186 {
01187 if (GetWaterTileType(tile) == WATER_TILE_DEPOT) {
01188 ShowDepotWindow(GetShipDepotNorthTile(tile), VEH_SHIP);
01189 return true;
01190 }
01191 return false;
01192 }
01193
01194 static void ChangeTileOwner_Water(TileIndex tile, Owner old_owner, Owner new_owner)
01195 {
01196 if (!IsTileOwner(tile, old_owner)) return;
01197
01198 if (new_owner != INVALID_OWNER) {
01199 SetTileOwner(tile, new_owner);
01200 return;
01201 }
01202
01203
01204 if (IsShipDepot(tile)) DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
01205
01206
01207
01208 if (IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
01209 }
01210
01211 static VehicleEnterTileStatus VehicleEnter_Water(Vehicle *v, TileIndex tile, int x, int y)
01212 {
01213 return VETSB_CONTINUE;
01214 }
01215
01216 static CommandCost TerraformTile_Water(TileIndex tile, DoCommandFlag flags, uint z_new, Slope tileh_new)
01217 {
01218
01219 if (IsWaterTile(tile) && IsCanal(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_CANAL_FIRST);
01220
01221 return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
01222 }
01223
01224
01225 extern const TileTypeProcs _tile_type_water_procs = {
01226 DrawTile_Water,
01227 GetSlopeZ_Water,
01228 ClearTile_Water,
01229 NULL,
01230 GetTileDesc_Water,
01231 GetTileTrackStatus_Water,
01232 ClickTile_Water,
01233 NULL,
01234 TileLoop_Water,
01235 ChangeTileOwner_Water,
01236 NULL,
01237 VehicleEnter_Water,
01238 GetFoundation_Water,
01239 TerraformTile_Water,
01240 };