From 8f9836793f24bd8452b546e5ecff6f461e2cf0aa Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Sat, 19 Oct 2024 19:09:53 +0100 Subject: [PATCH] Change: Store water tile flooding state in the map. This allows water tiles which cannot flood any further to not even try to flood. On a large map with lots of water tiles this can noticeably reduce game loop processing time. Mostly ported from JGRPP. --- docs/landscape.html | 6 ++++++ docs/landscape_grid.html | 2 +- src/landscape.cpp | 2 ++ src/object_cmd.cpp | 3 ++- src/saveload/afterload.cpp | 7 +++++++ src/saveload/saveload.h | 1 + src/tree_cmd.cpp | 1 + src/water.h | 1 + src/water_cmd.cpp | 27 +++++++++++++++++++++++++-- src/water_map.h | 20 ++++++++++++++++++++ src/waypoint_cmd.cpp | 1 + 11 files changed, 67 insertions(+), 4 deletions(-) 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:
    +
  • 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);