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