diff --git a/docs/landscape.html b/docs/landscape.html
index 24739af619..c72b070e26 100644
--- a/docs/landscape.html
+++ b/docs/landscape.html
@@ -98,6 +98,12 @@
+
m3:
+
+ -
+ Bit 0: Non-flooding state.
+
+
m4:
Road roadtype. Used for all tiles with road (road, station, tunnelbridge).
diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html
index 841e43b6bc..93e6415aa2 100644
--- a/docs/landscape_grid.html
+++ b/docs/landscape_grid.html
@@ -244,7 +244,7 @@ the array so you can quickly see what is used and what is not.
sea |
X XX XXXXX |
OOOO OOOO OOOO OOOO |
- OOOO OOOO |
+ OOOO OOOX |
OOOO OOOO |
0000 OOO0 |
OOOO OOOO |
diff --git a/src/landscape.cpp b/src/landscape.cpp
index 8cdfb7227b..411eb514d8 100644
--- a/src/landscape.cpp
+++ b/src/landscape.cpp
@@ -537,6 +537,7 @@ void DoClearSquare(TileIndex tile)
MarkTileDirtyByTile(tile);
if (remove) RemoveDockingTile(tile);
+ ClearNeighbourNonFloodingStates(tile);
InvalidateWaterRegion(tile);
}
@@ -693,6 +694,7 @@ CommandCost CmdLandscapeClear(DoCommandFlag flags, TileIndex tile)
}
}
DoClearSquare(tile);
+ ClearNeighbourNonFloodingStates(tile);
}
}
return cost;
diff --git a/src/object_cmd.cpp b/src/object_cmd.cpp
index a1c5e0e9b2..3c7bfa1432 100644
--- a/src/object_cmd.cpp
+++ b/src/object_cmd.cpp
@@ -120,6 +120,8 @@ void BuildObject(ObjectType type, TileIndex tile, CompanyID owner, Town *town, u
assert(o->town != nullptr);
for (TileIndex t : ta) {
+ if (IsWaterTile(t)) ClearNeighbourNonFloodingStates(t);
+ if (HasTileWaterGround(t)) InvalidateWaterRegion(t);
WaterClass wc = (IsWaterTile(t) ? GetWaterClass(t) : WATER_CLASS_INVALID);
/* Update company infrastructure counts for objects build on canals owned by nobody. */
if (wc == WATER_CLASS_CANAL && owner != OWNER_NONE && (IsTileOwner(t, OWNER_NONE) || IsTileOwner(t, OWNER_WATER))) {
@@ -364,7 +366,6 @@ CommandCost CmdBuildObject(DoCommandFlag flags, TileIndex tile, ObjectType type,
if (flags & DC_EXEC) {
BuildObject(type, tile, _current_company == OWNER_DEITY ? OWNER_NONE : _current_company, nullptr, view);
- for (TileIndex t : ta) InvalidateWaterRegion(t);
/* Make sure the HQ starts at the right size. */
if (type == OBJECT_HQ) UpdateCompanyHQ(tile, hq_score);
diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp
index a8fb1b98ac..5df9556bba 100644
--- a/src/saveload/afterload.cpp
+++ b/src/saveload/afterload.cpp
@@ -2218,6 +2218,13 @@ bool AfterLoadGame()
}
}
+ if (IsSavegameVersionBefore(SLV_NONFLOODING_WATER_TILES)) {
+ for (auto t : Map::Iterate()) {
+ if (!IsTileType(t, MP_WATER)) continue;
+ SetNonFloodingWaterTile(t, false);
+ }
+ }
+
if (IsSavegameVersionBefore(SLV_124) && !IsSavegameVersionBefore(SLV_1)) {
/* The train station tile area was added, but for really old (TTDPatch) it's already valid. */
for (Waypoint *wp : Waypoint::Iterate()) {
diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h
index 81847ca521..79f533d3f8 100644
--- a/src/saveload/saveload.h
+++ b/src/saveload/saveload.h
@@ -390,6 +390,7 @@ enum SaveLoadVersion : uint16_t {
SLV_WATER_TILE_TYPE, ///< 342 PR#13030 Simplify water tile type.
SLV_PRODUCTION_HISTORY, ///< 343 PR#10541 Industry production history.
SLV_ROAD_TYPE_LABEL_MAP, ///< 344 PR#13021 Add road type label map to allow upgrade/conversion of road types.
+ SLV_NONFLOODING_WATER_TILES, ///< 345 PR#13013 Store water tile non-flooding state.
SL_MAX_VERSION, ///< Highest possible saveload version
};
diff --git a/src/tree_cmd.cpp b/src/tree_cmd.cpp
index 1cf11de25d..87d5685f4e 100644
--- a/src/tree_cmd.cpp
+++ b/src/tree_cmd.cpp
@@ -102,6 +102,7 @@ static void PlantTreesOnTile(TileIndex tile, TreeType treetype, uint count, Tree
switch (GetTileType(tile)) {
case MP_WATER:
ground = TREE_GROUND_SHORE;
+ ClearNeighbourNonFloodingStates(tile);
break;
case MP_CLEAR:
diff --git a/src/water.h b/src/water.h
index 94a7db342b..323ccd5c53 100644
--- a/src/water.h
+++ b/src/water.h
@@ -24,6 +24,7 @@ enum FloodingBehaviour {
};
FloodingBehaviour GetFloodingBehaviour(TileIndex tile);
+void ClearNeighbourNonFloodingStates(TileIndex tile);
void TileLoop_Water(TileIndex tile);
bool FloodHalftile(TileIndex t);
diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp
index 3e27379794..2691ee3b2f 100644
--- a/src/water_cmd.cpp
+++ b/src/water_cmd.cpp
@@ -90,6 +90,17 @@ static void MarkCanalsAndRiversAroundDirty(TileIndex tile)
}
}
+/**
+ * Clear non-flooding state of the tiles around a tile.
+ * @param tile The centre of the tile where other tiles' non-flooding state is cleared.
+ */
+void ClearNeighbourNonFloodingStates(TileIndex tile)
+{
+ for (Direction dir = DIR_BEGIN; dir != DIR_END; dir++) {
+ TileIndex dest = tile + TileOffsByDir(dir);
+ if (IsValidTile(dest) && IsTileType(dest, MP_WATER)) SetNonFloodingWaterTile(dest, false);
+ }
+}
/**
* Build a ship depot.
@@ -403,6 +414,7 @@ static CommandCost RemoveLock(TileIndex tile, DoCommandFlag flags)
MakeRiver(tile, Random());
} else {
DoClearSquare(tile);
+ ClearNeighbourNonFloodingStates(tile);
}
MakeWaterKeepingClass(tile + delta, GetTileOwner(tile + delta));
MakeWaterKeepingClass(tile - delta, GetTileOwner(tile - delta));
@@ -565,6 +577,7 @@ static CommandCost ClearTile_Water(TileIndex tile, DoCommandFlag flags)
}
DoClearSquare(tile);
MarkCanalsAndRiversAroundDirty(tile);
+ ClearNeighbourNonFloodingStates(tile);
}
return CommandCost(EXPENSES_CONSTRUCTION, base_cost);
@@ -580,6 +593,7 @@ static CommandCost ClearTile_Water(TileIndex tile, DoCommandFlag flags)
if (flags & DC_EXEC) {
DoClearSquare(tile);
MarkCanalsAndRiversAroundDirty(tile);
+ ClearNeighbourNonFloodingStates(tile);
}
if (IsSlopeWithOneCornerRaised(slope)) {
return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_WATER]);
@@ -1230,10 +1244,14 @@ static void DoDryUp(TileIndex tile)
*/
void TileLoop_Water(TileIndex tile)
{
- if (IsTileType(tile, MP_WATER)) AmbientSoundEffect(tile);
+ if (IsTileType(tile, MP_WATER)) {
+ AmbientSoundEffect(tile);
+ if (IsNonFloodingWaterTile(tile)) return;
+ }
switch (GetFloodingBehaviour(tile)) {
- case FLOOD_ACTIVE:
+ case FLOOD_ACTIVE: {
+ bool continue_flooding = false;
for (Direction dir = DIR_BEGIN; dir < DIR_END; dir++) {
TileIndex dest = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir(dir));
/* Contrary to drying up, flooding does not consider MP_VOID tiles. */
@@ -1241,6 +1259,9 @@ void TileLoop_Water(TileIndex tile)
/* do not try to flood water tiles - increases performance a lot */
if (IsTileType(dest, MP_WATER)) continue;
+ /* This neighbour tile might be floodable later if the tile is cleared, so allow flooding to continue. */
+ continue_flooding = true;
+
/* TREE_GROUND_SHORE is the sign of a previous flood. */
if (IsTileType(dest, MP_TREES) && GetTreeGround(dest) == TREE_GROUND_SHORE) continue;
@@ -1251,7 +1272,9 @@ void TileLoop_Water(TileIndex tile)
DoFloodTile(dest);
}
+ if (!continue_flooding && IsTileType(tile, MP_WATER)) SetNonFloodingWaterTile(tile, true);
break;
+ }
case FLOOD_DRYUP: {
Slope slope_here = std::get<0>(GetFoundationSlope(tile)) & ~SLOPE_HALFTILE_MASK & ~SLOPE_STEEP;
diff --git a/src/water_map.h b/src/water_map.h
index f42622f5ec..c21029b670 100644
--- a/src/water_map.h
+++ b/src/water_map.h
@@ -516,4 +516,24 @@ inline void MakeLock(Tile t, Owner o, DiagDirection d, WaterClass wc_lower, Wate
MakeLockTile(upper_tile, IsWaterTile(upper_tile) ? GetTileOwner(upper_tile) : o, LOCK_PART_UPPER, d, wc_upper);
}
+/**
+ * Set the non-flooding water tile state of a tile.
+ * @param t the tile
+ * @param b the non-flooding water tile state
+ */
+inline void SetNonFloodingWaterTile(Tile t, bool b)
+{
+ assert(IsTileType(t, MP_WATER));
+ AssignBit(t.m3(), 0, b);
+}
+/**
+ * Checks whether the tile is marked as a non-flooding water tile.
+ * @return true iff the tile is marked as a non-flooding water tile.
+ */
+inline bool IsNonFloodingWaterTile(Tile t)
+{
+ assert(IsTileType(t, MP_WATER));
+ return HasBit(t.m3(), 0);
+}
+
#endif /* WATER_MAP_H */
diff --git a/src/waypoint_cmd.cpp b/src/waypoint_cmd.cpp
index 40b6f32ee6..b6796b25a3 100644
--- a/src/waypoint_cmd.cpp
+++ b/src/waypoint_cmd.cpp
@@ -505,6 +505,7 @@ CommandCost CmdBuildBuoy(DoCommandFlag flags, TileIndex tile)
MakeBuoy(tile, wp->index, GetWaterClass(tile));
CheckForDockingTile(tile);
MarkTileDirtyByTile(tile);
+ ClearNeighbourNonFloodingStates(tile);
wp->UpdateVirtCoord();
InvalidateWindowData(WC_WAYPOINT_VIEW, wp->index);