00001
00002
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "cmd_helper.h"
00008 #include "landscape.h"
00009 #include "viewport_func.h"
00010 #include "command_func.h"
00011 #include "town.h"
00012 #include "news_func.h"
00013 #include "depot_base.h"
00014 #include "depot_func.h"
00015 #include "vehicle_gui.h"
00016 #include "train.h"
00017 #include "roadveh.h"
00018 #include "water.h"
00019 #include "industry_map.h"
00020 #include "cargotype.h"
00021 #include "newgrf_canal.h"
00022 #include "transparency.h"
00023 #include "strings_func.h"
00024 #include "functions.h"
00025 #include "window_func.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 "newgrf_cargo.h"
00033 #include "effectvehicle_func.h"
00034 #include "tunnelbridge_map.h"
00035 #include "ai/ai.hpp"
00036
00037 #include "table/sprites.h"
00038 #include "table/strings.h"
00039
00043 enum FloodingBehaviour {
00044 FLOOD_NONE,
00045 FLOOD_ACTIVE,
00046 FLOOD_PASSIVE,
00047 FLOOD_DRYUP,
00048 };
00049
00053 static const uint8 _flood_from_dirs[] = {
00054 (1 << DIR_NW) | (1 << DIR_SW) | (1 << DIR_SE) | (1 << DIR_NE),
00055 (1 << DIR_NE) | (1 << DIR_SE),
00056 (1 << DIR_NW) | (1 << DIR_NE),
00057 (1 << DIR_NE),
00058 (1 << DIR_NW) | (1 << DIR_SW),
00059 0,
00060 (1 << DIR_NW),
00061 (1 << DIR_N ) | (1 << DIR_NW) | (1 << DIR_NE),
00062 (1 << DIR_SW) | (1 << DIR_SE),
00063 (1 << DIR_SE),
00064 0,
00065 (1 << DIR_E ) | (1 << DIR_NE) | (1 << DIR_SE),
00066 (1 << DIR_SW),
00067 (1 << DIR_S ) | (1 << DIR_SW) | (1 << DIR_SE),
00068 (1 << DIR_W ) | (1 << DIR_SW) | (1 << DIR_NW),
00069 };
00070
00077 static inline void MarkTileDirtyIfCanalOrRiver(TileIndex tile)
00078 {
00079 if (IsTileType(tile, MP_WATER) && (IsCanal(tile) || IsRiver(tile))) MarkTileDirtyByTile(tile);
00080 }
00081
00088 static void MarkCanalsAndRiversAroundDirty(TileIndex tile)
00089 {
00090 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
00091 MarkTileDirtyIfCanalOrRiver(tile + TileOffsByDir(dir));
00092 }
00093 }
00094
00095
00102 CommandCost CmdBuildShipDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00103 {
00104 TileIndex tile2;
00105
00106 CommandCost ret;
00107
00108 Axis axis = Extract<Axis, 0>(p1);
00109
00110 tile2 = tile + (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
00111
00112 if (!IsWaterTile(tile) || !IsWaterTile(tile2)) {
00113 return_cmd_error(STR_3801_MUST_BE_BUILT_ON_WATER);
00114 }
00115
00116 if (IsBridgeAbove(tile) || IsBridgeAbove(tile2)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
00117
00118 if (GetTileSlope(tile, NULL) != SLOPE_FLAT || GetTileSlope(tile2, NULL) != SLOPE_FLAT) {
00119
00120 return_cmd_error(STR_0239_SITE_UNSUITABLE);
00121 }
00122
00123 WaterClass wc1 = GetWaterClass(tile);
00124 WaterClass wc2 = GetWaterClass(tile2);
00125 ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00126 if (CmdFailed(ret)) return CMD_ERROR;
00127 ret = DoCommand(tile2, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00128 if (CmdFailed(ret)) return CMD_ERROR;
00129
00130 if (!Depot::CanAllocateItem()) return CMD_ERROR;
00131
00132 if (flags & DC_EXEC) {
00133 Depot *depot = new Depot(tile);
00134 depot->town_index = ClosestTownFromTile(tile, UINT_MAX)->index;
00135
00136 MakeShipDepot(tile, _current_company, DEPOT_NORTH, axis, wc1);
00137 MakeShipDepot(tile2, _current_company, DEPOT_SOUTH, axis, wc2);
00138 MarkTileDirtyByTile(tile);
00139 MarkTileDirtyByTile(tile2);
00140 }
00141
00142 return CommandCost(EXPENSES_CONSTRUCTION, _price.build_ship_depot);
00143 }
00144
00145 void MakeWaterKeepingClass(TileIndex tile, Owner o)
00146 {
00147 assert(IsTileType(tile, MP_WATER) || (IsTileType(tile, MP_STATION) && (IsBuoy(tile) || IsDock(tile) || IsOilRig(tile))) || IsTileType(tile, MP_INDUSTRY));
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 switch (wc) {
00158 case WATER_CLASS_SEA: MakeWater(tile); break;
00159 case WATER_CLASS_CANAL: MakeCanal(tile, o, Random()); break;
00160 case WATER_CLASS_RIVER: MakeRiver(tile, Random()); break;
00161 default: DoClearSquare(tile); break;
00162 }
00163 }
00164
00165 static CommandCost RemoveShipDepot(TileIndex tile, DoCommandFlag flags)
00166 {
00167 if (!IsShipDepot(tile)) return CMD_ERROR;
00168 if (!CheckTileOwnership(tile)) return CMD_ERROR;
00169
00170 TileIndex tile2 = GetOtherShipDepotTile(tile);
00171
00172
00173 if (!(flags & DC_BANKRUPT)) {
00174 if (!EnsureNoVehicleOnGround(tile) || !EnsureNoVehicleOnGround(tile2)) return CMD_ERROR;
00175 }
00176
00177 if (flags & DC_EXEC) {
00178
00179 delete GetDepotByTile(tile2 < tile ? tile2 : tile);
00180
00181 MakeWaterKeepingClass(tile, GetTileOwner(tile));
00182 MakeWaterKeepingClass(tile2, GetTileOwner(tile2));
00183 MarkTileDirtyByTile(tile);
00184 MarkTileDirtyByTile(tile2);
00185 }
00186
00187 return CommandCost(EXPENSES_CONSTRUCTION, _price.remove_ship_depot);
00188 }
00189
00191 static CommandCost DoBuildShiplift(TileIndex tile, DiagDirection dir, DoCommandFlag flags)
00192 {
00193 CommandCost ret;
00194 int delta;
00195
00196
00197 ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00198 if (CmdFailed(ret)) return CMD_ERROR;
00199
00200 delta = TileOffsByDiagDir(dir);
00201
00202 WaterClass wc_lower = IsWaterTile(tile - delta) ? GetWaterClass(tile - delta) : WATER_CLASS_CANAL;
00203
00204 ret = DoCommand(tile - delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00205 if (CmdFailed(ret)) return CMD_ERROR;
00206 if (GetTileSlope(tile - delta, NULL) != SLOPE_FLAT) {
00207 return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
00208 }
00209
00210
00211 WaterClass wc_upper = IsWaterTile(tile + delta) ? GetWaterClass(tile + delta) : WATER_CLASS_CANAL;
00212
00213 ret = DoCommand(tile + delta, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00214 if (CmdFailed(ret)) return CMD_ERROR;
00215 if (GetTileSlope(tile + delta, NULL) != SLOPE_FLAT) {
00216 return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
00217 }
00218
00219 if ((MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) ||
00220 (MayHaveBridgeAbove(tile - delta) && IsBridgeAbove(tile - delta)) ||
00221 (MayHaveBridgeAbove(tile + delta) && IsBridgeAbove(tile + delta))) {
00222 return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
00223 }
00224
00225 if (flags & DC_EXEC) {
00226 MakeLock(tile, _current_company, dir, wc_lower, wc_upper);
00227 MarkTileDirtyByTile(tile);
00228 MarkTileDirtyByTile(tile - delta);
00229 MarkTileDirtyByTile(tile + delta);
00230 MarkCanalsAndRiversAroundDirty(tile - delta);
00231 MarkCanalsAndRiversAroundDirty(tile + delta);
00232 }
00233
00234 return CommandCost(EXPENSES_CONSTRUCTION, _price.clear_water * 22 >> 3);
00235 }
00236
00237 static CommandCost RemoveShiplift(TileIndex tile, DoCommandFlag flags)
00238 {
00239 TileIndexDiff delta = TileOffsByDiagDir(GetLockDirection(tile));
00240
00241 if (!CheckTileOwnership(tile) && GetTileOwner(tile) != OWNER_NONE) return CMD_ERROR;
00242
00243
00244 if (!EnsureNoVehicleOnGround(tile) || !EnsureNoVehicleOnGround(tile + delta) || !EnsureNoVehicleOnGround(tile - delta))
00245 return CMD_ERROR;
00246
00247 if (flags & DC_EXEC) {
00248 DoClearSquare(tile);
00249 MakeWaterKeepingClass(tile + delta, GetTileOwner(tile));
00250 MakeWaterKeepingClass(tile - delta, GetTileOwner(tile));
00251 MarkTileDirtyByTile(tile - delta);
00252 MarkTileDirtyByTile(tile + delta);
00253 MarkCanalsAndRiversAroundDirty(tile - delta);
00254 MarkCanalsAndRiversAroundDirty(tile + delta);
00255 }
00256
00257 return CommandCost(EXPENSES_CONSTRUCTION, _price.clear_water * 2);
00258 }
00259
00266 CommandCost CmdBuildLock(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00267 {
00268 DiagDirection dir = GetInclinedSlopeDirection(GetTileSlope(tile, NULL));
00269 if (dir == INVALID_DIAGDIR) return_cmd_error(STR_1000_LAND_SLOPED_IN_WRONG_DIRECTION);
00270
00271
00272 if (IsWaterTile(tile)) return_cmd_error(STR_0239_SITE_UNSUITABLE);
00273
00274 return DoBuildShiplift(tile, dir, flags);
00275 }
00276
00283 CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00284 {
00285 CommandCost cost(EXPENSES_CONSTRUCTION);
00286 int size_x, size_y;
00287 int x;
00288 int y;
00289 int sx, sy;
00290
00291 if (p1 >= MapSize()) return CMD_ERROR;
00292
00293
00294 if (p2 != 0 && _game_mode != GM_EDITOR) return CMD_ERROR;
00295
00296 x = TileX(tile);
00297 y = TileY(tile);
00298 sx = TileX(p1);
00299 sy = TileY(p1);
00300
00301 if (x < sx) Swap(x, sx);
00302 if (y < sy) Swap(y, sy);
00303 size_x = (x - sx) + 1;
00304 size_y = (y - sy) + 1;
00305
00306
00307 if (_game_mode != GM_EDITOR && (sx != x && sy != y)) return CMD_ERROR;
00308
00309 BEGIN_TILE_LOOP(tile, size_x, size_y, TileXY(sx, sy)) {
00310 CommandCost ret;
00311
00312 Slope slope = GetTileSlope(tile, NULL);
00313 if (slope != SLOPE_FLAT && (p2 != 2 || !IsInclinedSlope(slope))) {
00314 return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
00315 }
00316
00317
00318 if (IsTileType(tile, MP_WATER) && (!IsTileOwner(tile, OWNER_WATER) || p2 == 1)) continue;
00319
00320 ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
00321 if (CmdFailed(ret)) return ret;
00322 cost.AddCost(ret);
00323
00324 if (flags & DC_EXEC) {
00325 if (TileHeight(tile) == 0 && p2 == 1) {
00326 MakeWater(tile);
00327 } else if (p2 == 2) {
00328 MakeRiver(tile, Random());
00329 } else {
00330 MakeCanal(tile, _current_company, Random());
00331 }
00332 MarkTileDirtyByTile(tile);
00333 MarkCanalsAndRiversAroundDirty(tile);
00334 }
00335
00336 cost.AddCost(_price.clear_water);
00337 } END_TILE_LOOP(tile, size_x, size_y, 0);
00338
00339 if (cost.GetCost() == 0) {
00340 return_cmd_error(STR_1007_ALREADY_BUILT);
00341 } else {
00342 return cost;
00343 }
00344 }
00345
00346 static CommandCost ClearTile_Water(TileIndex tile, DoCommandFlag flags)
00347 {
00348 switch (GetWaterTileType(tile)) {
00349 case WATER_TILE_CLEAR:
00350 if (flags & DC_NO_WATER) return_cmd_error(STR_3807_CAN_T_BUILD_ON_WATER);
00351
00352
00353 if (!_settings_game.construction.freeform_edges && (!IsInsideMM(TileX(tile), 1, MapMaxX() - 1) ||
00354 !IsInsideMM(TileY(tile), 1, MapMaxY() - 1))) {
00355 return_cmd_error(STR_0002_TOO_CLOSE_TO_EDGE_OF_MAP);
00356 }
00357
00358
00359 if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
00360
00361 if (GetTileOwner(tile) != OWNER_WATER && GetTileOwner(tile) != OWNER_NONE && !CheckTileOwnership(tile)) return CMD_ERROR;
00362
00363 if (flags & DC_EXEC) {
00364 DoClearSquare(tile);
00365 MarkCanalsAndRiversAroundDirty(tile);
00366 }
00367 return CommandCost(EXPENSES_CONSTRUCTION, _price.clear_water);
00368
00369 case WATER_TILE_COAST: {
00370 Slope slope = GetTileSlope(tile, NULL);
00371
00372
00373 if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
00374
00375 if (flags & DC_EXEC) {
00376 DoClearSquare(tile);
00377 MarkCanalsAndRiversAroundDirty(tile);
00378 }
00379 if (IsSlopeWithOneCornerRaised(slope)) {
00380 return CommandCost(EXPENSES_CONSTRUCTION, _price.clear_water);
00381 } else {
00382 return CommandCost(EXPENSES_CONSTRUCTION, _price.clear_roughland);
00383 }
00384 }
00385
00386 case WATER_TILE_LOCK: {
00387 static const TileIndexDiffC _shiplift_tomiddle_offs[] = {
00388 { 0, 0}, {0, 0}, { 0, 0}, {0, 0},
00389 {-1, 0}, {0, 1}, { 1, 0}, {0, -1},
00390 { 1, 0}, {0, -1}, {-1, 0}, {0, 1},
00391 };
00392
00393 if (flags & DC_AUTO) return_cmd_error(STR_2004_BUILDING_MUST_BE_DEMOLISHED);
00394 if (_current_company == OWNER_WATER) return CMD_ERROR;
00395
00396 return RemoveShiplift(tile + ToTileIndexDiff(_shiplift_tomiddle_offs[GetSection(tile)]), flags);
00397 }
00398
00399 case WATER_TILE_DEPOT:
00400 if (flags & DC_AUTO) return_cmd_error(STR_2004_BUILDING_MUST_BE_DEMOLISHED);
00401 return RemoveShipDepot(tile, flags);
00402
00403 default:
00404 NOT_REACHED();
00405 }
00406 }
00407
00416 static bool IsWateredTile(TileIndex tile, Direction from)
00417 {
00418 switch (GetTileType(tile)) {
00419 case MP_WATER:
00420 switch (GetWaterTileType(tile)) {
00421 default: NOT_REACHED();
00422 case WATER_TILE_DEPOT: case WATER_TILE_CLEAR: return true;
00423 case WATER_TILE_LOCK: return DiagDirToAxis(GetLockDirection(tile)) == DiagDirToAxis(DirToDiagDir(from));
00424
00425 case WATER_TILE_COAST:
00426 switch (GetTileSlope(tile, NULL)) {
00427 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
00428 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
00429 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
00430 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
00431 default: return false;
00432 }
00433 }
00434
00435 case MP_RAILWAY:
00436 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
00437 assert(IsPlainRailTile(tile));
00438 switch (GetTileSlope(tile, NULL)) {
00439 case SLOPE_W: return (from == DIR_SE) || (from == DIR_E) || (from == DIR_NE);
00440 case SLOPE_S: return (from == DIR_NE) || (from == DIR_N) || (from == DIR_NW);
00441 case SLOPE_E: return (from == DIR_NW) || (from == DIR_W) || (from == DIR_SW);
00442 case SLOPE_N: return (from == DIR_SW) || (from == DIR_S) || (from == DIR_SE);
00443 default: return false;
00444 }
00445 }
00446 return false;
00447
00448 case MP_STATION:
00449 if (IsOilRig(tile)) {
00450
00451
00452 TileIndex src_tile = tile + TileOffsByDir(from);
00453 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
00454 (IsTileType(src_tile, MP_INDUSTRY))) return true;
00455
00456 return GetWaterClass(tile) != WATER_CLASS_INVALID;
00457 }
00458 return (IsDock(tile) && GetTileSlope(tile, NULL) == SLOPE_FLAT) || IsBuoy(tile);
00459
00460 case MP_INDUSTRY: {
00461
00462
00463 TileIndex src_tile = tile + TileOffsByDir(from);
00464 if ((IsTileType(src_tile, MP_STATION) && IsOilRig(src_tile)) ||
00465 (IsTileType(src_tile, MP_INDUSTRY) && GetIndustryIndex(src_tile) == GetIndustryIndex(tile))) return true;
00466
00467 return IsIndustryTileOnWater(tile);
00468 }
00469
00470 case MP_TUNNELBRIDGE: return GetTunnelBridgeTransportType(tile) == TRANSPORT_WATER && ReverseDiagDir(GetTunnelBridgeDirection(tile)) == DirToDiagDir(from);
00471
00472 default: return false;
00473 }
00474 }
00475
00476 static void DrawWaterEdges(SpriteID base, TileIndex tile)
00477 {
00478 uint wa;
00479
00480
00481 wa = IsWateredTile(TILE_ADDXY(tile, -1, 0), DIR_SW) << 0;
00482 wa += IsWateredTile(TILE_ADDXY(tile, 0, 1), DIR_NW) << 1;
00483 wa += IsWateredTile(TILE_ADDXY(tile, 1, 0), DIR_NE) << 2;
00484 wa += IsWateredTile(TILE_ADDXY(tile, 0, -1), DIR_SE) << 3;
00485
00486 if (!(wa & 1)) DrawGroundSprite(base, PAL_NONE);
00487 if (!(wa & 2)) DrawGroundSprite(base + 1, PAL_NONE);
00488 if (!(wa & 4)) DrawGroundSprite(base + 2, PAL_NONE);
00489 if (!(wa & 8)) DrawGroundSprite(base + 3, PAL_NONE);
00490
00491
00492 switch (wa & 0x03) {
00493 case 0: DrawGroundSprite(base + 4, PAL_NONE); break;
00494 case 3: if (!IsWateredTile(TILE_ADDXY(tile, -1, 1), DIR_W)) DrawGroundSprite(base + 8, PAL_NONE); break;
00495 }
00496
00497
00498 switch (wa & 0x06) {
00499 case 0: DrawGroundSprite(base + 5, PAL_NONE); break;
00500 case 6: if (!IsWateredTile(TILE_ADDXY(tile, 1, 1), DIR_N)) DrawGroundSprite(base + 9, PAL_NONE); break;
00501 }
00502
00503
00504 switch (wa & 0x0C) {
00505 case 0: DrawGroundSprite(base + 6, PAL_NONE); break;
00506 case 12: if (!IsWateredTile(TILE_ADDXY(tile, 1, -1), DIR_E)) DrawGroundSprite(base + 10, PAL_NONE); break;
00507 }
00508
00509
00510 switch (wa & 0x09) {
00511 case 0: DrawGroundSprite(base + 7, PAL_NONE); break;
00512 case 9: if (!IsWateredTile(TILE_ADDXY(tile, -1, -1), DIR_S)) DrawGroundSprite(base + 11, PAL_NONE); break;
00513 }
00514 }
00515
00517 static void DrawSeaWater(TileIndex tile)
00518 {
00519 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
00520 }
00521
00523 static void DrawCanalWater(TileIndex tile)
00524 {
00525 DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
00526
00527
00528 SpriteID dikes_base = GetCanalSprite(CF_DIKES, tile);
00529 if (dikes_base == 0) dikes_base = SPR_CANAL_DIKES_BASE;
00530
00531 DrawWaterEdges(dikes_base, tile);
00532 }
00533
00534 struct LocksDrawTileStruct {
00535 int8 delta_x, delta_y, delta_z;
00536 byte width, height, depth;
00537 SpriteID image;
00538 };
00539
00540 #include "table/water_land.h"
00541
00542 static void DrawWaterStuff(const TileInfo *ti, const WaterDrawTileStruct *wdts,
00543 SpriteID palette, uint base, bool draw_ground
00544 )
00545 {
00546 SpriteID image;
00547 SpriteID water_base = GetCanalSprite(CF_WATERSLOPE, ti->tile);
00548 SpriteID locks_base = GetCanalSprite(CF_LOCKS, ti->tile);
00549
00550
00551 if (water_base == 0) water_base = SPR_CANALS_BASE;
00552 if (locks_base == 0) {
00553 locks_base = SPR_SHIPLIFT_BASE;
00554 } else {
00555
00556 base = 0;
00557 }
00558
00559 image = wdts++->image;
00560 if (image < 4) image += water_base;
00561 if (draw_ground) DrawGroundSprite(image, PAL_NONE);
00562
00563
00564 if (IsInvisibilitySet(TO_BUILDINGS)) return;
00565
00566 for (; wdts->delta_x != 0x80; wdts++) {
00567 AddSortableSpriteToDraw(wdts->image + base + ((wdts->image < 24) ? locks_base : 0), palette,
00568 ti->x + wdts->delta_x, ti->y + wdts->delta_y,
00569 wdts->size_x, wdts->size_y,
00570 wdts->size_z, ti->z + wdts->delta_z,
00571 IsTransparencySet(TO_BUILDINGS));
00572 }
00573 }
00574
00575 static void DrawRiverWater(const TileInfo *ti)
00576 {
00577 SpriteID image = SPR_FLAT_WATER_TILE;
00578 SpriteID edges_base = GetCanalSprite(CF_RIVER_EDGE, ti->tile);
00579
00580 if (ti->tileh != SLOPE_FLAT) {
00581 image = GetCanalSprite(CF_RIVER_SLOPE, ti->tile);
00582 if (image == 0) {
00583 switch (ti->tileh) {
00584 case SLOPE_NW: image = SPR_WATER_SLOPE_Y_DOWN; break;
00585 case SLOPE_SW: image = SPR_WATER_SLOPE_X_UP; break;
00586 case SLOPE_SE: image = SPR_WATER_SLOPE_Y_UP; break;
00587 case SLOPE_NE: image = SPR_WATER_SLOPE_X_DOWN; break;
00588 default: image = SPR_FLAT_WATER_TILE; break;
00589 }
00590 } else {
00591 switch (ti->tileh) {
00592 default: NOT_REACHED();
00593 case SLOPE_SE: edges_base += 12; break;
00594 case SLOPE_NE: image += 1; edges_base += 24; break;
00595 case SLOPE_SW: image += 2; edges_base += 36; break;
00596 case SLOPE_NW: image += 3; edges_base += 48; break;
00597 }
00598 }
00599 }
00600
00601 DrawGroundSprite(image, PAL_NONE);
00602
00603
00604 if (edges_base > 48) DrawWaterEdges(edges_base, ti->tile);
00605 }
00606
00607 void DrawShoreTile(Slope tileh)
00608 {
00609
00610
00611 static const byte tileh_to_shoresprite[32] = {
00612 0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0,
00613 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 15, 0,
00614 };
00615
00616 assert(!IsHalftileSlope(tileh));
00617 assert(tileh != SLOPE_FLAT);
00618
00619 assert((tileh != SLOPE_EW) && (tileh != SLOPE_NS));
00620
00621 DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[tileh], PAL_NONE);
00622 }
00623
00624 void DrawWaterClassGround(const TileInfo *ti) {
00625 switch (GetWaterClass(ti->tile)) {
00626 case WATER_CLASS_SEA: DrawSeaWater(ti->tile); break;
00627 case WATER_CLASS_CANAL: DrawCanalWater(ti->tile); break;
00628 case WATER_CLASS_RIVER: DrawRiverWater(ti); break;
00629 default: NOT_REACHED();
00630 }
00631 }
00632
00633 static void DrawTile_Water(TileInfo *ti)
00634 {
00635 switch (GetWaterTileType(ti->tile)) {
00636 case WATER_TILE_CLEAR:
00637 DrawWaterClassGround(ti);
00638 DrawBridgeMiddle(ti);
00639 break;
00640
00641 case WATER_TILE_COAST: {
00642 DrawShoreTile(ti->tileh);
00643 DrawBridgeMiddle(ti);
00644 } break;
00645
00646 case WATER_TILE_LOCK: {
00647 const WaterDrawTileStruct *t = _shiplift_display_seq[GetSection(ti->tile)];
00648 DrawWaterStuff(ti, t, 0, ti->z > t[3].delta_y ? 24 : 0, true);
00649 } break;
00650
00651 case WATER_TILE_DEPOT:
00652 DrawWaterClassGround(ti);
00653 DrawWaterStuff(ti, _shipdepot_display_seq[GetSection(ti->tile)], COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile)), 0, false);
00654 break;
00655 }
00656 }
00657
00658 void DrawShipDepotSprite(int x, int y, int image)
00659 {
00660 const WaterDrawTileStruct *wdts = _shipdepot_display_seq[image];
00661
00662 DrawSprite(wdts++->image, PAL_NONE, x, y);
00663
00664 for (; wdts->delta_x != 0x80; wdts++) {
00665 Point pt = RemapCoords(wdts->delta_x, wdts->delta_y, wdts->delta_z);
00666 DrawSprite(wdts->image, COMPANY_SPRITE_COLOUR(_local_company), x + pt.x, y + pt.y);
00667 }
00668 }
00669
00670
00671 static uint GetSlopeZ_Water(TileIndex tile, uint x, uint y)
00672 {
00673 uint z;
00674 Slope tileh = GetTileSlope(tile, &z);
00675
00676 return z + GetPartialZ(x & 0xF, y & 0xF, tileh);
00677 }
00678
00679 static Foundation GetFoundation_Water(TileIndex tile, Slope tileh)
00680 {
00681 return FOUNDATION_NONE;
00682 }
00683
00684 static void GetAcceptedCargo_Water(TileIndex tile, AcceptedCargo ac)
00685 {
00686
00687 }
00688
00689 static void GetTileDesc_Water(TileIndex tile, TileDesc *td)
00690 {
00691 switch (GetWaterTileType(tile)) {
00692 case WATER_TILE_CLEAR:
00693 switch (GetWaterClass(tile)) {
00694 case WATER_CLASS_SEA: td->str = STR_3804_WATER; break;
00695 case WATER_CLASS_CANAL: td->str = STR_LANDINFO_CANAL; break;
00696 case WATER_CLASS_RIVER: td->str = STR_LANDINFO_RIVER; break;
00697 default: assert(0); break;
00698 }
00699 break;
00700 case WATER_TILE_COAST: td->str = STR_3805_COAST_OR_RIVERBANK; break;
00701 case WATER_TILE_LOCK : td->str = STR_LANDINFO_LOCK; break;
00702 case WATER_TILE_DEPOT: td->str = STR_3806_SHIP_DEPOT; break;
00703 default: assert(0); break;
00704 }
00705
00706 td->owner[0] = GetTileOwner(tile);
00707 }
00708
00709 static void AnimateTile_Water(TileIndex tile)
00710 {
00711
00712 }
00713
00714 static void FloodVehicle(Vehicle *v);
00715
00722 static Vehicle *FloodVehicleProc(Vehicle *v, void *data)
00723 {
00724 byte z = *(byte*)data;
00725
00726 if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL;
00727 if (v->z_pos > z || (v->vehstatus & VS_CRASHED) != 0) return NULL;
00728
00729 FloodVehicle(v);
00730 return NULL;
00731 }
00732
00738 static void FloodVehicles(TileIndex tile)
00739 {
00740 byte z = 0;
00741
00742 if (IsTileType(tile, MP_STATION) && IsAirport(tile)) {
00743 const Station *st = GetStationByTile(tile);
00744 const AirportFTAClass *airport = st->Airport();
00745 z = 1 + airport->delta_z;
00746 for (uint x = 0; x < airport->size_x; x++) {
00747 for (uint y = 0; y < airport->size_y; y++) {
00748 tile = TILE_ADDXY(st->airport_tile, x, y);
00749 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00750 }
00751 }
00752
00753
00754 return;
00755 }
00756
00757
00758 if (!_settings_game.station.nonuniform_stations && IsTileType(tile, MP_STATION) && GetStationType(tile) == STATION_RAIL) {
00759 const Station *st = GetStationByTile(tile);
00760
00761 BEGIN_TILE_LOOP(t, st->trainst_w, st->trainst_h, st->train_tile)
00762 if (st->TileBelongsToRailStation(t)) {
00763 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00764 }
00765 END_TILE_LOOP(t, st->trainst_w, st->trainst_h, st->train_tile)
00766
00767 return;
00768 }
00769
00770 if (!IsBridgeTile(tile)) {
00771 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00772 return;
00773 }
00774
00775 TileIndex end = GetOtherBridgeEnd(tile);
00776 z = GetBridgeHeight(tile);
00777
00778 FindVehicleOnPos(tile, &z, &FloodVehicleProc);
00779 FindVehicleOnPos(end, &z, &FloodVehicleProc);
00780 }
00781
00782 static void FloodVehicle(Vehicle *v)
00783 {
00784 if (!(v->vehstatus & VS_CRASHED)) {
00785 uint16 pass = 0;
00786
00787 if (v->type == VEH_TRAIN || v->type == VEH_ROAD || v->type == VEH_AIRCRAFT) {
00788 if (v->type == VEH_AIRCRAFT) {
00789
00790
00791
00792 if (!IsTileType(v->tile, MP_STATION) || !IsAirport(v->tile) || GetTileMaxZ(v->tile) != 0) return;
00793 const Station *st = GetStationByTile(v->tile);
00794 const AirportFTAClass *airport = st->Airport();
00795
00796 if (v->z_pos != airport->delta_z + 1) return;
00797 }
00798
00799 if (v->type != VEH_AIRCRAFT) v = v->First();
00800
00801
00802 for (Vehicle *u = v; u != NULL; u = u->Next()) {
00803 if (IsCargoInClass(u->cargo_type, CC_PASSENGERS)) pass += u->cargo.Count();
00804 u->vehstatus |= VS_CRASHED;
00805 MarkSingleVehicleDirty(u);
00806 }
00807
00808 switch (v->type) {
00809 default: NOT_REACHED();
00810 case VEH_TRAIN:
00811 if (IsFrontEngine(v)) {
00812 pass += 4;
00813
00814
00815 v->vehstatus &= ~VS_CRASHED;
00816 FreeTrainTrackReservation(v);
00817 v->vehstatus |= VS_CRASHED;
00818 }
00819 v->u.rail.crash_anim_pos = 4000;
00820 InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
00821 break;
00822
00823 case VEH_ROAD:
00824 if (IsRoadVehFront(v)) pass += 1;
00825 v->u.road.crashed_ctr = 2000;
00826 InvalidateWindowClassesData(WC_ROADVEH_LIST, 0);
00827 break;
00828
00829 case VEH_AIRCRAFT:
00830 pass += 2;
00831 v->u.air.crashed_counter = 9000;
00832 InvalidateWindowClassesData(WC_AIRCRAFT_LIST, 0);
00833 break;
00834 }
00835 } else {
00836 return;
00837 }
00838
00839 InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
00840 InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
00841
00842 AI::NewEvent(v->owner, new AIEventVehicleCrashed(v->index, v->tile, AIEventVehicleCrashed::CRASH_FLOODED));
00843 SetDParam(0, pass);
00844 AddNewsItem(STR_B006_FLOOD_VEHICLE_DESTROYED,
00845 NS_ACCIDENT_VEHICLE,
00846 v->index,
00847 0);
00848 CreateEffectVehicleRel(v, 4, 4, 8, EV_EXPLOSION_LARGE);
00849 SndPlayVehicleFx(SND_12_EXPLOSION, v);
00850 }
00851 }
00852
00858 static FloodingBehaviour GetFloodingBehaviour(TileIndex tile)
00859 {
00860
00861
00862
00863
00864
00865 switch (GetTileType(tile)) {
00866 case MP_WATER:
00867 if (IsCoast(tile)) {
00868 Slope tileh = GetTileSlope(tile, NULL);
00869 return (IsSlopeWithOneCornerRaised(tileh) ? FLOOD_ACTIVE : FLOOD_DRYUP);
00870 } else {
00871 return (GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE;
00872 }
00873
00874 case MP_RAILWAY:
00875 if (GetRailGroundType(tile) == RAIL_GROUND_WATER) {
00876 return (IsSlopeWithOneCornerRaised(GetTileSlope(tile, NULL)) ? FLOOD_ACTIVE : FLOOD_DRYUP);
00877 }
00878 return FLOOD_NONE;
00879
00880 case MP_TREES:
00881 return (GetTreeGround(tile) == TREE_GROUND_SHORE ? FLOOD_DRYUP : FLOOD_NONE);
00882
00883 case MP_STATION:
00884 if (IsBuoy(tile) || (IsDock(tile) && GetTileSlope(tile, NULL) == SLOPE_FLAT) || IsOilRig(tile)) {
00885 return (GetWaterClass(tile) == WATER_CLASS_SEA ? FLOOD_ACTIVE : FLOOD_NONE);
00886 }
00887 return FLOOD_NONE;
00888
00889 case MP_INDUSTRY:
00890 return ((IsIndustryTileOnWater(tile) && GetWaterClass(tile) == WATER_CLASS_SEA) ? FLOOD_ACTIVE : FLOOD_NONE);
00891
00892 default:
00893 return FLOOD_NONE;
00894 }
00895 }
00896
00900 void DoFloodTile(TileIndex target)
00901 {
00902 assert(!IsTileType(target, MP_WATER));
00903
00904 bool flooded = false;
00905
00906 _current_company = OWNER_WATER;
00907
00908 Slope tileh = GetTileSlope(target, NULL);
00909 if (tileh != SLOPE_FLAT) {
00910
00911 switch (GetTileType(target)) {
00912 case MP_RAILWAY: {
00913 if (!IsPlainRailTile(target)) break;
00914 FloodVehicles(target);
00915 flooded = FloodHalftile(target);
00916 break;
00917 }
00918
00919 case MP_TREES:
00920 if (!IsSlopeWithOneCornerRaised(tileh)) {
00921 SetTreeGroundDensity(target, TREE_GROUND_SHORE, 3);
00922 MarkTileDirtyByTile(target);
00923 flooded = true;
00924 break;
00925 }
00926
00927 case MP_CLEAR:
00928 if (CmdSucceeded(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) {
00929 MakeShore(target);
00930 MarkTileDirtyByTile(target);
00931 flooded = true;
00932 }
00933 break;
00934
00935 default:
00936 break;
00937 }
00938 } else {
00939
00940 FloodVehicles(target);
00941
00942
00943 if (CmdSucceeded(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) {
00944 MakeWater(target);
00945 MarkTileDirtyByTile(target);
00946 flooded = true;
00947 }
00948 }
00949
00950 if (flooded) {
00951
00952 MarkCanalsAndRiversAroundDirty(target);
00953
00954
00955 UpdateSignalsInBuffer();
00956 }
00957
00958 _current_company = OWNER_NONE;
00959 }
00960
00964 static void DoDryUp(TileIndex tile)
00965 {
00966 _current_company = OWNER_WATER;
00967
00968 switch (GetTileType(tile)) {
00969 case MP_RAILWAY:
00970 assert(IsPlainRailTile(tile));
00971 assert(GetRailGroundType(tile) == RAIL_GROUND_WATER);
00972
00973 RailGroundType new_ground;
00974 switch (GetTrackBits(tile)) {
00975 case TRACK_BIT_UPPER: new_ground = RAIL_GROUND_FENCE_HORIZ1; break;
00976 case TRACK_BIT_LOWER: new_ground = RAIL_GROUND_FENCE_HORIZ2; break;
00977 case TRACK_BIT_LEFT: new_ground = RAIL_GROUND_FENCE_VERT1; break;
00978 case TRACK_BIT_RIGHT: new_ground = RAIL_GROUND_FENCE_VERT2; break;
00979 default: NOT_REACHED();
00980 }
00981 SetRailGroundType(tile, new_ground);
00982 MarkTileDirtyByTile(tile);
00983 break;
00984
00985 case MP_TREES:
00986 SetTreeGroundDensity(tile, TREE_GROUND_GRASS, 3);
00987 MarkTileDirtyByTile(tile);
00988 break;
00989
00990 case MP_WATER:
00991 assert(IsCoast(tile));
00992
00993 if (CmdSucceeded(DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) {
00994 MakeClear(tile, CLEAR_GRASS, 3);
00995 MarkTileDirtyByTile(tile);
00996 }
00997 break;
00998
00999 default: NOT_REACHED();
01000 }
01001
01002 _current_company = OWNER_NONE;
01003 }
01004
01011 void TileLoop_Water(TileIndex tile)
01012 {
01013 switch (GetFloodingBehaviour(tile)) {
01014 case FLOOD_ACTIVE:
01015 for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
01016 TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir(dir));
01017 if (dest == INVALID_TILE) continue;
01018
01019 if (IsTileType(dest, MP_WATER)) continue;
01020
01021 uint z_dest;
01022 Slope slope_dest = GetFoundationSlope(dest, &z_dest) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
01023 if (z_dest > 0) continue;
01024
01025 if (!HasBit(_flood_from_dirs[slope_dest], ReverseDir(dir))) continue;
01026
01027 DoFloodTile(dest);
01028 }
01029 break;
01030
01031 case FLOOD_DRYUP: {
01032 Slope slope_here = GetFoundationSlope(tile, NULL) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
01033 uint check_dirs = _flood_from_dirs[slope_here];
01034 uint dir;
01035 FOR_EACH_SET_BIT(dir, check_dirs) {
01036 TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir((Direction)dir));
01037 if (dest == INVALID_TILE) continue;
01038
01039 FloodingBehaviour dest_behaviour = GetFloodingBehaviour(dest);
01040 if ((dest_behaviour == FLOOD_ACTIVE) || (dest_behaviour == FLOOD_PASSIVE)) return;
01041 }
01042 DoDryUp(tile);
01043 break;
01044 }
01045
01046 default: return;
01047 }
01048 }
01049
01050 void ConvertGroundTilesIntoWaterTiles()
01051 {
01052 TileIndex tile;
01053 uint z;
01054 Slope slope;
01055
01056 for (tile = 0; tile < MapSize(); ++tile) {
01057 slope = GetTileSlope(tile, &z);
01058 if (IsTileType(tile, MP_CLEAR) && z == 0) {
01059
01060
01061
01062 switch (slope) {
01063 case SLOPE_FLAT:
01064 MakeWater(tile);
01065 break;
01066
01067 case SLOPE_N:
01068 case SLOPE_E:
01069 case SLOPE_S:
01070 case SLOPE_W:
01071 MakeShore(tile);
01072 break;
01073
01074 default:
01075 uint check_dirs = _flood_from_dirs[slope & ~SLOPE_STEEP];
01076 uint dir;
01077 FOR_EACH_SET_BIT(dir, check_dirs) {
01078 TileIndex dest = TILE_ADD(tile, TileOffsByDir((Direction)dir));
01079 Slope slope_dest = GetTileSlope(dest, NULL) & ~SLOPE_STEEP;
01080 if (slope_dest == SLOPE_FLAT || IsSlopeWithOneCornerRaised(slope_dest)) {
01081 MakeShore(tile);
01082 break;
01083 }
01084 }
01085 break;
01086 }
01087 }
01088 }
01089 }
01090
01091 static TrackStatus GetTileTrackStatus_Water(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
01092 {
01093 static const byte coast_tracks[] = {0, 32, 4, 0, 16, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0};
01094
01095 TrackBits ts;
01096
01097 if (mode != TRANSPORT_WATER) return 0;
01098
01099 switch (GetWaterTileType(tile)) {
01100 case WATER_TILE_CLEAR: ts = (GetTileSlope(tile, NULL) == SLOPE_FLAT) ? TRACK_BIT_ALL : TRACK_BIT_NONE; break;
01101 case WATER_TILE_COAST: ts = (TrackBits)coast_tracks[GetTileSlope(tile, NULL) & 0xF]; break;
01102 case WATER_TILE_LOCK: ts = DiagDirToDiagTrackBits(GetLockDirection(tile)); break;
01103 case WATER_TILE_DEPOT: ts = AxisToTrackBits(GetShipDepotAxis(tile)); break;
01104 default: return 0;
01105 }
01106 if (TileX(tile) == 0) {
01107
01108 ts &= ~(TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_RIGHT);
01109 }
01110 if (TileY(tile) == 0) {
01111
01112 ts &= ~(TRACK_BIT_Y | TRACK_BIT_LEFT | TRACK_BIT_UPPER);
01113 }
01114 return CombineTrackStatus(TrackBitsToTrackdirBits(ts), TRACKDIR_BIT_NONE);
01115 }
01116
01117 static bool ClickTile_Water(TileIndex tile)
01118 {
01119 if (GetWaterTileType(tile) == WATER_TILE_DEPOT) {
01120 TileIndex tile2 = GetOtherShipDepotTile(tile);
01121
01122 ShowDepotWindow(tile < tile2 ? tile : tile2, VEH_SHIP);
01123 return true;
01124 }
01125 return false;
01126 }
01127
01128 static void ChangeTileOwner_Water(TileIndex tile, Owner old_owner, Owner new_owner)
01129 {
01130 if (!IsTileOwner(tile, old_owner)) return;
01131
01132 if (new_owner != INVALID_OWNER) {
01133 SetTileOwner(tile, new_owner);
01134 return;
01135 }
01136
01137
01138 if (IsShipDepot(tile)) DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
01139
01140
01141
01142 if (IsTileOwner(tile, old_owner)) SetTileOwner(tile, OWNER_NONE);
01143 }
01144
01145 static VehicleEnterTileStatus VehicleEnter_Water(Vehicle *v, TileIndex tile, int x, int y)
01146 {
01147 return VETSB_CONTINUE;
01148 }
01149
01150 static CommandCost TerraformTile_Water(TileIndex tile, DoCommandFlag flags, uint z_new, Slope tileh_new)
01151 {
01152
01153 if (IsWaterTile(tile) && IsCanal(tile)) return_cmd_error(STR_MUST_DEMOLISH_CANAL_FIRST);
01154
01155 return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
01156 }
01157
01158
01159 extern const TileTypeProcs _tile_type_water_procs = {
01160 DrawTile_Water,
01161 GetSlopeZ_Water,
01162 ClearTile_Water,
01163 GetAcceptedCargo_Water,
01164 GetTileDesc_Water,
01165 GetTileTrackStatus_Water,
01166 ClickTile_Water,
01167 AnimateTile_Water,
01168 TileLoop_Water,
01169 ChangeTileOwner_Water,
01170 NULL,
01171 VehicleEnter_Water,
01172 GetFoundation_Water,
01173 TerraformTile_Water,
01174 };