summaryrefslogtreecommitdiff
path: root/src/water_cmd.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/water_cmd.cpp')
-rw-r--r--src/water_cmd.cpp355
1 files changed, 234 insertions, 121 deletions
diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp
index eeed4a696..681ccb331 100644
--- a/src/water_cmd.cpp
+++ b/src/water_cmd.cpp
@@ -33,12 +33,46 @@
#include "variables.h"
#include "player_func.h"
#include "settings_type.h"
+#include "clear_map.h"
#include "table/sprites.h"
#include "table/strings.h"
-static Vehicle *FindFloodableVehicleOnTile(TileIndex tile);
-static void FloodVehicle(Vehicle *v);
+/**
+ * Describes the behaviour of a tile during flooding.
+ */
+enum FloodingBehaviour {
+ FLOOD_NONE, ///< The tile does not flood neighboured tiles.
+ FLOOD_ACTIVE, ///< The tile floods neighboured tiles.
+ FLOOD_PASSIVE, ///< The tile does not actively flood neighboured tiles, but it prevents them from drying up.
+ FLOOD_DRYUP, ///< The tile drys up if it is not constantly flooded from neighboured tiles.
+};
+
+/**
+ * Describes from which directions a specific slope can be flooded (if the tile is floodable at all).
+ */
+static const uint8 _flood_from_dirs[] = {
+ (1 << DIR_NW) | (1 << DIR_SW) | (1 << DIR_SE) | (1 << DIR_NE), // SLOPE_FLAT
+ (1 << DIR_NE) | (1 << DIR_SE), // SLOPE_W
+ (1 << DIR_NW) | (1 << DIR_NE), // SLOPE_S
+ (1 << DIR_NE), // SLOPE_SW
+ (1 << DIR_NW) | (1 << DIR_SW), // SLOPE_E
+ 0, // SLOPE_EW
+ (1 << DIR_NW), // SLOPE_SE
+ (1 << DIR_N ) | (1 << DIR_NW) | (1 << DIR_NE), // SLOPE_WSE, SLOPE_STEEP_S
+ (1 << DIR_SW) | (1 << DIR_SE), // SLOPE_N
+ (1 << DIR_SE), // SLOPE_NW
+ 0, // SLOPE_NS
+ (1 << DIR_E ) | (1 << DIR_NE) | (1 << DIR_SE), // SLOPE_NWS, SLOPE_STEEP_W
+ (1 << DIR_SW), // SLOPE_NE
+ (1 << DIR_S ) | (1 << DIR_SW) | (1 << DIR_SE), // SLOPE_ENW, SLOPE_STEEP_N
+ (1 << DIR_W ) | (1 << DIR_SW) | (1 << DIR_NW), // SLOPE_SEN, SLOPE_STEEP_E
+};
+
+/**
+ * Slopes that contain flat water and not only shore.
+ */
+static const uint32 _active_water_slopes = (1 << SLOPE_FLAT) | (1 << SLOPE_W) | (1 << SLOPE_S) | (1 << SLOPE_E) | (1 << SLOPE_N);
/**
* Makes a tile canal or water depending on the surroundings.
@@ -520,6 +554,23 @@ static void DrawRiverWater(const TileInfo *ti)
DrawWaterEdges(edges_base, ti->tile);
}
+void DrawShoreTile(Slope tileh)
+{
+ /* Converts the enum Slope into an offset based on SPR_SHORE_BASE.
+ * This allows to calculate the proper sprite to display for this Slope */
+ static const byte tileh_to_shoresprite[32] = {
+ 0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 15, 0,
+ };
+
+ assert(!IsHalftileSlope(tileh)); // Halftile slopes need to get handled earlier.
+ assert(tileh != SLOPE_FLAT); // Shore is never flat
+
+ assert((tileh != SLOPE_EW) && (tileh != SLOPE_NS)); // No suitable sprites for current flooding behaviour
+
+ DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[tileh], PAL_NONE);
+}
+
static void DrawTile_Water(TileInfo *ti)
{
switch (GetWaterTileType(ti->tile)) {
@@ -530,15 +581,7 @@ static void DrawTile_Water(TileInfo *ti)
break;
case WATER_TILE_COAST: {
- /* Converts the enum Slope into an offset based on SPR_SHORE_BASE.
- * This allows to calculate the proper sprite to display for this Slope */
- static const byte tileh_to_shoresprite[32] = {
- 0, 1, 2, 3, 4, 16, 6, 7, 8, 9, 17, 11, 12, 13, 14, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 15, 0,
- };
-
- assert(!IsSteepSlope(ti->tileh));
- DrawGroundSprite(SPR_SHORE_BASE + tileh_to_shoresprite[ti->tileh], PAL_NONE);
+ DrawShoreTile(ti->tileh);
DrawBridgeMiddle(ti);
} break;
@@ -625,84 +668,6 @@ static inline void MarkTileDirtyIfCanal(TileIndex tile)
if (IsTileType(tile, MP_WATER) && IsCanal(tile)) MarkTileDirtyByTile(tile);
}
-/**
- * Floods neighboured floodable tiles
- *
- * @param tile The water source tile that causes the flooding.
- * @param offs[0] Destination tile to flood.
- * @param offs[1] First corner of edge between source and dest tile.
- * @param offs[2] Second corder of edge between source and dest tile.
- * @param offs[3] Third corner of dest tile.
- * @param offs[4] Fourth corner of dest tile.
- */
-static void TileLoopWaterHelper(TileIndex tile, const TileIndexDiffC *offs)
-{
- TileIndex target = TILE_ADD(tile, ToTileIndexDiff(offs[0]));
-
- /* type of this tile mustn't be water already. */
- if (IsTileType(target, MP_WATER)) return;
-
- /* Are both corners of the edge between source and dest on height 0 ? */
- if (TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[1]))) != 0 ||
- TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[2]))) != 0) {
- return;
- }
-
- bool flooded = false; // Will be set to true, when something is flooded
-
- /* Is any corner of the dest tile raised? (First two corners already checked above. */
- if (TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[3]))) != 0 ||
- TileHeight(TILE_ADD(tile, ToTileIndexDiff(offs[4]))) != 0) {
- /* make coast.. */
- switch (GetTileType(target)) {
- case MP_RAILWAY: {
- if (!IsPlainRailTile(target)) break;
-
- flooded = FloodHalftile(target);
-
- Vehicle *v = FindFloodableVehicleOnTile(target);
- if (v != NULL) FloodVehicle(v);
-
- break;
- }
-
- case MP_CLEAR:
- case MP_TREES:
- _current_player = OWNER_WATER;
- if (CmdSucceeded(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) {
- flooded = true;
- MakeShore(target);
- MarkTileDirtyByTile(target);
- }
- break;
-
- default:
- break;
- }
- } else {
- /* Flood vehicles */
- _current_player = OWNER_WATER;
-
- Vehicle *v = FindFloodableVehicleOnTile(target);
- if (v != NULL) FloodVehicle(v);
-
- /* flood flat tile */
- if (CmdSucceeded(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) {
- flooded = true;
- MakeWater(target);
- MarkTileDirtyByTile(target);
- }
- }
-
- if (flooded) {
- /* Mark surrounding canal tiles dirty too to avoid glitches */
- for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
- MarkTileDirtyIfCanal(target + TileOffsByDir(dir));
- }
- /* update signals if needed */
- UpdateSignalsInBuffer();
- }
-}
/**
* Finds a vehicle to flood.
@@ -827,54 +792,202 @@ static void FloodVehicle(Vehicle *v)
}
/**
- * Let a water tile floods its diagonal adjoining tiles
- * called from tunnelbridge_cmd, and by TileLoop_Industry() and TileLoop_Track()
+ * Returns the behaviour of a tile during flooding.
*
- * @param tile the water/shore tile that floods
+ * @return Behaviour of the tile
*/
-void TileLoop_Water(TileIndex tile)
+static FloodingBehaviour GetFloodingBehaviour(TileIndex tile)
{
- static const TileIndexDiffC _tile_loop_offs_array[][5] = {
- // tile to mod shore? shore?
- {{-1, 0}, {0, 0}, {0, 1}, {-1, 0}, {-1, 1}},
- {{ 0, 1}, {0, 1}, {1, 1}, { 0, 2}, { 1, 2}},
- {{ 1, 0}, {1, 0}, {1, 1}, { 2, 0}, { 2, 1}},
- {{ 0, -1}, {0, 0}, {1, 0}, { 0, -1}, { 1, -1}}
- };
+ /* FLOOD_ACTIVE: 'single-corner-raised'-coast, sea, sea-shipdepots, sea-buoys, rail with flooded halftile
+ * FLOOD_DRYUP: coast with more than one corner raised
+ * FLOOD_PASSIVE: oilrig, dock, water-industries
+ * FLOOD_NONE: canals, rivers, everything else
+ */
+ switch (GetTileType(tile)) {
+ case MP_WATER:
+ if (IsCoast(tile)) {
+ Slope tileh = GetTileSlope(tile, NULL);
+ return (HasBit(_active_water_slopes, tileh) ? FLOOD_ACTIVE : FLOOD_DRYUP);
+ } else {
+ return ((IsSea(tile) || (IsShipDepot(tile) && (GetShipDepotWaterOwner(tile) == OWNER_WATER))) ? FLOOD_ACTIVE : FLOOD_NONE);
+ }
+
+ case MP_RAILWAY:
+ return ((GetRailGroundType(tile) == RAIL_GROUND_WATER) ? FLOOD_ACTIVE : FLOOD_NONE);
- /* Ensure buoys on canal borders do not flood */
- if (IsCanalBuoyTile(tile)) return;
- /* Ensure only sea and coast floods, not canals or rivers */
- if (IsTileType(tile, MP_WATER) && !(IsSea(tile) || IsCoast(tile))) return;
+ case MP_STATION:
+ if (IsSeaBuoyTile(tile)) return FLOOD_ACTIVE;
+ if (IsOilRig(tile) || IsDock(tile)) return FLOOD_PASSIVE;
+ return FLOOD_NONE;
- /* floods in all four diagonal directions with the exception of the edges */
- if (IsInsideMM(TileX(tile), 1, MapSizeX() - 3 + 1) &&
- IsInsideMM(TileY(tile), 1, MapSizeY() - 3 + 1)) {
- uint i;
+ case MP_INDUSTRY:
+ return ((GetIndustrySpec(GetIndustryType(tile))->behaviour & INDUSTRYBEH_BUILT_ONWATER) != 0 ? FLOOD_PASSIVE : FLOOD_NONE);
- for (i = 0; i != lengthof(_tile_loop_offs_array); i++) {
- TileLoopWaterHelper(tile, _tile_loop_offs_array[i]);
+ default:
+ return FLOOD_NONE;
+ }
+}
+
+/**
+ * Floods a tile.
+ */
+static void DoFloodTile(TileIndex target)
+{
+ if (IsTileType(target, MP_WATER)) return;
+
+ bool flooded = false; // Will be set to true if something is changed.
+
+ _current_player = OWNER_WATER;
+
+ if (GetTileSlope(target, NULL) != SLOPE_FLAT) {
+ /* make coast.. */
+ switch (GetTileType(target)) {
+ case MP_RAILWAY: {
+ if (!IsPlainRailTile(target)) break;
+
+ flooded = FloodHalftile(target);
+
+ Vehicle *v = FindFloodableVehicleOnTile(target);
+ if (v != NULL) FloodVehicle(v);
+
+ break;
+ }
+
+ case MP_CLEAR:
+ case MP_TREES:
+ if (CmdSucceeded(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) {
+ MakeShore(target);
+ MarkTileDirtyByTile(target);
+ flooded = true;
+ }
+ break;
+
+ default:
+ break;
+ }
+ } else {
+ /* Flood vehicles */
+ Vehicle *v = FindFloodableVehicleOnTile(target);
+ if (v != NULL) FloodVehicle(v);
+
+ /* flood flat tile */
+ if (CmdSucceeded(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) {
+ MakeWater(target);
+ MarkTileDirtyByTile(target);
+ flooded = true;
}
}
- /* _current_player can be changed by TileLoopWaterHelper.. reset it back here */
- _current_player = OWNER_NONE;
+ if (flooded) {
+ /* Mark surrounding canal tiles dirty too to avoid glitches */
+ for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
+ MarkTileDirtyIfCanal(target + TileOffsByDir(dir));
+ }
- /* edges */
- if (TileX(tile) == 0 && IsInsideMM(TileY(tile), 1, MapSizeY() - 3 + 1)) { //NE
- TileLoopWaterHelper(tile, _tile_loop_offs_array[2]);
+ /* update signals if needed */
+ UpdateSignalsInBuffer();
}
- if (TileX(tile) == MapSizeX() - 2 && IsInsideMM(TileY(tile), 1, MapSizeY() - 3 + 1)) { //SW
- TileLoopWaterHelper(tile, _tile_loop_offs_array[0]);
+ _current_player = OWNER_NONE;
+}
+
+/**
+ * Drys a tile up.
+ */
+static void DoDryUp(TileIndex tile)
+{
+ assert(IsTileType(tile, MP_WATER) && IsCoast(tile));
+ _current_player = OWNER_WATER;
+
+ if (CmdSucceeded(DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) {
+ MakeClear(tile, CLEAR_GRASS, 3);
+ MarkTileDirtyByTile(tile);
}
- if (TileY(tile) == 0 && IsInsideMM(TileX(tile), 1, MapSizeX() - 3 + 1)) { //NW
- TileLoopWaterHelper(tile, _tile_loop_offs_array[1]);
+ _current_player = OWNER_NONE;
+}
+
+/**
+ * Let a water tile floods its diagonal adjoining tiles
+ * called from tunnelbridge_cmd, and by TileLoop_Industry() and TileLoop_Track()
+ *
+ * @param tile the water/shore tile that floods
+ */
+void TileLoop_Water(TileIndex tile)
+{
+ switch (GetFloodingBehaviour(tile)) {
+ case FLOOD_ACTIVE:
+ for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
+ TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir(dir));
+ if (dest == INVALID_TILE) continue;
+
+ uint z_dest;
+ Slope slope_dest = (Slope)(GetFoundationSlope(dest, &z_dest) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP);
+ if (z_dest > 0) continue;
+
+ if (!HasBit(_flood_from_dirs[slope_dest], ReverseDir(dir))) continue;
+
+ DoFloodTile(dest);
+ }
+ break;
+
+ case FLOOD_DRYUP: {
+ Slope slope_here = (Slope)(GetFoundationSlope(tile, NULL) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP);
+ uint check_dirs = _flood_from_dirs[slope_here];
+ uint dir;
+ FOR_EACH_SET_BIT(dir, check_dirs) {
+ TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir((Direction)dir));
+ if (dest == INVALID_TILE) continue;
+
+ FloodingBehaviour dest_behaviour = GetFloodingBehaviour(dest);
+ if ((dest_behaviour == FLOOD_ACTIVE) || (dest_behaviour == FLOOD_PASSIVE)) return;
+ }
+ DoDryUp(tile);
+ break;
+ }
+
+ default: return;
}
+}
- if (TileY(tile) == MapSizeY() - 2 && IsInsideMM(TileX(tile), 1, MapSizeX() - 3 + 1)) { //SE
- TileLoopWaterHelper(tile, _tile_loop_offs_array[3]);
+void ConvertGroundTilesIntoWaterTiles()
+{
+ TileIndex tile;
+ uint z;
+ Slope slope;
+
+ for (tile = 0; tile < MapSize(); ++tile) {
+ slope = GetTileSlope(tile, &z);
+ if (IsTileType(tile, MP_CLEAR) && z == 0) {
+ /* Make both water for tiles at level 0
+ * and make shore, as that looks much better
+ * during the generation. */
+ switch (slope) {
+ case SLOPE_FLAT:
+ MakeWater(tile);
+ break;
+
+ case SLOPE_N:
+ case SLOPE_E:
+ case SLOPE_S:
+ case SLOPE_W:
+ MakeShore(tile);
+ break;
+
+ default:
+ uint check_dirs = _flood_from_dirs[slope & ~SLOPE_STEEP];
+ uint dir;
+ FOR_EACH_SET_BIT(dir, check_dirs) {
+ TileIndex dest = TILE_ADD(tile, TileOffsByDir((Direction)dir));
+ Slope slope_dest = (Slope)(GetTileSlope(dest, NULL) & ~SLOPE_STEEP);
+ if (HasBit(_active_water_slopes, slope_dest)) {
+ MakeShore(tile);
+ break;
+ }
+ }
+ break;
+ }
+ }
}
}