diff --git a/src/economy.cpp b/src/economy.cpp index 177dadf47d..d8f6e1927a 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -597,6 +597,8 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner) YapfNotifyTrackLayoutChange(INVALID_TILE, INVALID_TRACK); + NotifyRoadLayoutChanged(); + cur_company.Restore(); RegisterGameEvents(new_owner != INVALID_OWNER ? GEF_COMPANY_MERGE : GEF_COMPANY_DELETE); diff --git a/src/misc.cpp b/src/misc.cpp index 8fc15a5d15..b28b1b0e63 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -80,6 +80,7 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin _tick_skip_counter = 0; _cur_tileloop_tile = 1; _thd.redsq = INVALID_TILE; + _road_layout_change_counter = 0; _game_events_since_load = (GameEventFlags) 0; _game_events_overall = (GameEventFlags) 0; _loadgame_DBGL_data.clear(); diff --git a/src/pathfinder/yapf/yapf_road.cpp b/src/pathfinder/yapf/yapf_road.cpp index 252ad0b9cd..71194dfa42 100644 --- a/src/pathfinder/yapf/yapf_road.cpp +++ b/src/pathfinder/yapf/yapf_road.cpp @@ -420,6 +420,7 @@ public: path_cache.td.pop_back(); path_cache.tile.pop_back(); } + path_cache.layout_ctr = _road_layout_change_counter; } return next_trackdir; } diff --git a/src/road.cpp b/src/road.cpp index 8cf4e353c7..1b5f3c2d75 100644 --- a/src/road.cpp +++ b/src/road.cpp @@ -22,6 +22,8 @@ #include "safeguards.h" +uint32 _road_layout_change_counter = 0; + /** * Return if the tile is a valid tile for a crossing. * diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index e600d8c1ac..b7f422c77a 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -103,6 +103,26 @@ extern const RoadBits _invalid_tileh_slopes_road[2][15] = { static Foundation GetRoadFoundation(Slope tileh, RoadBits bits); +void NotifyRoadLayoutChangedIfTileNonLeaf(TileIndex tile, RoadType rt, RoadBits present_bits) +{ + uint connections = 0; + if ((present_bits & ROAD_NE) && (GetAnyRoadBits(TILE_ADDXY(tile, -1, 0), rt) & ROAD_SW)) connections++; + if ((present_bits & ROAD_SE) && (GetAnyRoadBits(TILE_ADDXY(tile, 0, 1), rt) & ROAD_NW)) connections++; + if ((present_bits & ROAD_SW) && (GetAnyRoadBits(TILE_ADDXY(tile, 1, 0), rt) & ROAD_NE)) connections++; + if ((present_bits & ROAD_NW) && (GetAnyRoadBits(TILE_ADDXY(tile, 0, -1), rt) & ROAD_SE)) connections++; + if (connections >= 2) { + NotifyRoadLayoutChanged(); + } +} + +void NotifyRoadLayoutChangedIfSimpleTunnelBridgeNonLeaf(TileIndex start, TileIndex end, DiagDirection start_dir, RoadType rt) +{ + if (!(GetAnyRoadBits(TileAddByDiagDir(start, ReverseDiagDir(start_dir)), rt) & DiagDirToRoadBits(start_dir))) return; + if (!(GetAnyRoadBits(TileAddByDiagDir(end, start_dir), rt) & DiagDirToRoadBits(ReverseDiagDir(start_dir)))) return; + + NotifyRoadLayoutChanged(); +} + /** * Is it allowed to remove the given road bits from the given tile? * @param tile the tile to remove the road from @@ -294,6 +314,9 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec AddRoadTunnelBridgeInfrastructure(tile, other_end); DirtyAllCompanyInfrastructureWindows(); + + /* Todo: Change this to be more fine-grained if necessary */ + NotifyRoadLayoutChanged(); } } else { assert_tile(IsDriveThroughStopTile(tile), tile); @@ -307,6 +330,7 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec } SetRoadTypes(tile, GetRoadTypes(tile) & ~RoadTypeToRoadTypes(rt)); MarkTileDirtyByTile(tile); + NotifyRoadLayoutChanged(); } } return cost; @@ -360,6 +384,8 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec } } + NotifyRoadLayoutChangedIfTileNonLeaf(tile, rt, present | pieces); + Company *c = Company::GetIfValid(GetRoadOwner(tile, rt)); if (c != nullptr) { c->infrastructure.road[rt] -= CountBits(pieces); @@ -417,6 +443,7 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec Track railtrack = GetCrossingRailTrack(tile); RoadTypes rts = GetRoadTypes(tile) & ComplementRoadTypes(RoadTypeToRoadTypes(rt)); + NotifyRoadLayoutChangedIfTileNonLeaf(tile, rt, GetCrossingRoadBits(tile)); if (rts == ROADTYPES_NONE) { TrackBits tracks = GetCrossingRailBits(tile); bool reserved = HasCrossingReservation(tile); @@ -696,6 +723,7 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 MakeRoadCrossing(tile, company, company, GetTileOwner(tile), roaddir, GetRailType(tile), RoadTypeToRoadTypes(rt) | ROADTYPES_ROAD, p2); SetCrossingReservation(tile, reserved); UpdateLevelCrossing(tile, false); + NotifyRoadLayoutChangedIfTileNonLeaf(tile, rt, GetCrossingRoadBits(tile)); MarkTileDirtyByTile(tile); } return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_ROAD] * (rt == ROADTYPE_ROAD ? 2 : 4)); @@ -796,6 +824,7 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 MarkBridgeDirty(tile); AddRoadTunnelBridgeInfrastructure(tile, other_end); + NotifyRoadLayoutChanged(); DirtyAllCompanyInfrastructureWindows(); } @@ -881,6 +910,7 @@ do_clear:; if (rt == ROADTYPE_ROAD) SetTownIndex(tile, p2); } if (rtt != ROAD_TILE_CROSSING) SetRoadBits(tile, existing | pieces, rt); + NotifyRoadLayoutChangedIfTileNonLeaf(tile, rt, existing | pieces); break; } @@ -899,6 +929,7 @@ do_clear:; MarkTileDirtyByTile(other_end); MarkTileDirtyByTile(tile); } + NotifyRoadLayoutChanged(); break; } @@ -906,10 +937,12 @@ do_clear:; assert_tile(IsDriveThroughStopTile(tile), tile); SetRoadTypes(tile, GetRoadTypes(tile) | RoadTypeToRoadTypes(rt)); SetRoadOwner(tile, rt, company); + NotifyRoadLayoutChanged(); break; default: MakeRoadNormal(tile, pieces, RoadTypeToRoadTypes(rt), p2, company, company); + NotifyRoadLayoutChangedIfTileNonLeaf(tile, rt, pieces); break; } @@ -1171,6 +1204,8 @@ CommandCost CmdBuildRoadDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, ui MakeRoadDepot(tile, _current_company, dep->index, dir, rt); MarkTileDirtyByTile(tile); MakeDefaultName(dep); + + NotifyRoadLayoutChanged(); } cost.AddCost(_price[PR_BUILD_DEPOT_ROAD]); return cost; @@ -1196,6 +1231,8 @@ static CommandCost RemoveRoadDepot(TileIndex tile, DoCommandFlag flags) delete Depot::GetByTile(tile); DoClearSquare(tile); + + NotifyRoadLayoutChanged(); } return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_ROAD]); diff --git a/src/road_func.h b/src/road_func.h index 2236fdad96..261cfcf3cf 100644 --- a/src/road_func.h +++ b/src/road_func.h @@ -182,4 +182,12 @@ RoadTypes GetCompanyRoadtypes(const CompanyID company); void UpdateLevelCrossing(TileIndex tile, bool sound = true, bool force_close = false); bool IsCrossingOccupiedByRoadVehicle(TileIndex t); +inline void NotifyRoadLayoutChanged() +{ + _road_layout_change_counter++; +} + +void NotifyRoadLayoutChangedIfTileNonLeaf(TileIndex tile, RoadType rt, RoadBits present_bits); +void NotifyRoadLayoutChangedIfSimpleTunnelBridgeNonLeaf(TileIndex start, TileIndex end, DiagDirection start_dir, RoadType rt); + #endif /* ROAD_FUNC_H */ diff --git a/src/road_type.h b/src/road_type.h index 5251a53923..947b18cad1 100644 --- a/src/road_type.h +++ b/src/road_type.h @@ -14,6 +14,8 @@ #include "core/enum_type.hpp" +extern uint32 _road_layout_change_counter; + /** * The different roadtypes we support * diff --git a/src/roadveh.h b/src/roadveh.h index d8a2df47e8..95a1b8f577 100644 --- a/src/roadveh.h +++ b/src/roadveh.h @@ -86,6 +86,7 @@ void GetRoadVehSpriteSize(EngineID engine, uint &width, uint &height, int &xoffs struct RoadVehPathCache { std::deque td; std::deque tile; + uint32 layout_ctr; inline bool empty() const { return this->td.empty(); } diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index eaff185a22..e91332810a 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -1031,6 +1031,11 @@ static Trackdir RoadFindPathToDest(RoadVehicle *v, TileIndex tile, DiagDirection return_track(FindFirstBit2x64(trackdirs)); } + /* Path cache is out of date, clear it */ + if (!v->path.empty() && v->path.layout_ctr != _road_layout_change_counter) { + v->path.clear(); + } + /* Attempt to follow cached path. */ if (!v->path.empty()) { if (v->path.tile.front() != tile) { diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 8a9d8b5b76..7a88d6e9b0 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -106,6 +106,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { { XSLFI_RV_OVERTAKING, XSCF_NULL, 1, 1, "roadveh_overtaking", nullptr, nullptr, nullptr }, { XSLFI_LINKGRAPH_MODES, XSCF_NULL, 1, 1, "linkgraph_modes", nullptr, nullptr, nullptr }, { XSLFI_GAME_EVENTS, XSCF_NULL, 1, 1, "game_events", nullptr, nullptr, nullptr }, + { XSLFI_ROAD_LAYOUT_CHANGE_CTR, XSCF_NULL, 1, 1, "road_layout_change_ctr", nullptr, nullptr, nullptr }, { XSLFI_DEBUG, XSCF_IGNORABLE_ALL, 1, 1, "debug", nullptr, nullptr, "DBGL" }, { XSLFI_NULL, XSCF_NULL, 0, 0, nullptr, nullptr, nullptr, nullptr },// This is the end marker }; diff --git a/src/saveload/extended_ver_sl.h b/src/saveload/extended_ver_sl.h index 41fc298555..6d6fa9ad82 100644 --- a/src/saveload/extended_ver_sl.h +++ b/src/saveload/extended_ver_sl.h @@ -73,6 +73,7 @@ enum SlXvFeatureIndex { XSLFI_RV_OVERTAKING, ///< Roadvehicle overtaking XSLFI_LINKGRAPH_MODES, ///< Linkgraph additional distribution modes XSLFI_GAME_EVENTS, ///< Game event flags + XSLFI_ROAD_LAYOUT_CHANGE_CTR, ///< Road layout change counter XSLFI_DEBUG, ///< Debugging info XSLFI_RIFF_HEADER_60_BIT, ///< Size field in RIFF chunk header is 60 bit diff --git a/src/saveload/misc_sl.cpp b/src/saveload/misc_sl.cpp index 1a5e565c24..9e7e7145fe 100644 --- a/src/saveload/misc_sl.cpp +++ b/src/saveload/misc_sl.cpp @@ -18,6 +18,7 @@ #include "../gfx_func.h" #include "../core/random_func.hpp" #include "../fios.h" +#include "../road_type.h" #include "saveload.h" @@ -93,6 +94,7 @@ static const SaveLoadGlobVarList _date_desc[] = { SLEG_VAR(_trees_tick_ctr, SLE_UINT8), SLEG_CONDVAR(_pause_mode, SLE_UINT8, SLV_4, SL_MAX_VERSION), SLEG_CONDVAR_X(_game_events_overall, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GAME_EVENTS)), + SLEG_CONDVAR_X(_road_layout_change_counter, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_LAYOUT_CHANGE_CTR)), SLE_CONDNULL(4, SLV_11, SLV_120), SLEG_END() }; @@ -120,6 +122,7 @@ static const SaveLoadGlobVarList _date_check_desc[] = { SLE_NULL(1), // _trees_tick_ctr SLE_CONDNULL(1, SLV_4, SL_MAX_VERSION), // _pause_mode SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_GAME_EVENTS)), // _game_events_overall + SLE_CONDNULL_X(4, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_LAYOUT_CHANGE_CTR)), // _road_layout_change_counter SLE_CONDNULL(4, SLV_11, SLV_120), SLEG_END() }; diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp index 304c8309ed..e414701cc4 100644 --- a/src/saveload/vehicle_sl.cpp +++ b/src/saveload/vehicle_sl.cpp @@ -799,6 +799,7 @@ const SaveLoad *GetVehicleDescription(VehicleType vt) SLE_VAR(RoadVehicle, reverse_ctr, SLE_UINT8), SLE_CONDDEQUE(RoadVehicle, path.td, SLE_UINT8, SLV_ROADVEH_PATH_CACHE, SL_MAX_VERSION), SLE_CONDDEQUE(RoadVehicle, path.tile, SLE_UINT32, SLV_ROADVEH_PATH_CACHE, SL_MAX_VERSION), + SLE_CONDVAR_X(RoadVehicle, path.layout_ctr, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ROAD_LAYOUT_CHANGE_CTR)), SLE_CONDNULL(2, SLV_6, SLV_69), SLE_CONDVAR(RoadVehicle, gv_flags, SLE_UINT16, SLV_139, SL_MAX_VERSION), diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 2b116ed3e0..8d3b2d4044 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -2027,6 +2027,7 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin MarkTileDirtyByTile(cur_tile); } ZoningMarkDirtyStationCoverageArea(st); + NotifyRoadLayoutChanged(); } if (st != nullptr) { @@ -2149,6 +2150,8 @@ static CommandCost RemoveRoadStop(TileIndex tile, DoCommandFlag flags) st->bus_station.Clear(); for (const RoadStop *rs = st->bus_stops; rs != nullptr; rs = rs->next) st->bus_station.Add(rs->xy); } + + NotifyRoadLayoutChanged(); } return CommandCost(EXPENSES_CONSTRUCTION, _price[is_truck ? PR_CLEAR_STATION_TRUCK : PR_CLEAR_STATION_BUS]); diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index ccddef2e42..a36a54ad27 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -600,6 +600,12 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u make_bridge_ramp(tile_start, dir); make_bridge_ramp(tile_end, ReverseDiagDir(dir)); AddRoadTunnelBridgeInfrastructure(tile_start, tile_end); + if (IsRoadCustomBridgeHead(tile_start) || IsRoadCustomBridgeHead(tile_end)) { + NotifyRoadLayoutChanged(); + } else { + if (HasBit(roadtypes, ROADTYPE_ROAD)) NotifyRoadLayoutChangedIfSimpleTunnelBridgeNonLeaf(tile_start, tile_end, dir, ROADTYPE_ROAD); + if (HasBit(roadtypes, ROADTYPE_TRAM)) NotifyRoadLayoutChangedIfSimpleTunnelBridgeNonLeaf(tile_start, tile_end, dir, ROADTYPE_TRAM); + } break; } @@ -951,6 +957,7 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, RoadType rt; FOR_EACH_SET_ROADTYPE(rt, rts ^ (IsTunnelTile(start_tile) ? GetRoadTypes(start_tile) : ROADTYPES_NONE)) { c->infrastructure.road[rt] += num_pieces * 2; // A full diagonal road has two road bits. + NotifyRoadLayoutChangedIfSimpleTunnelBridgeNonLeaf(start_tile, end_tile, direction, rt); } } MakeRoadTunnel(start_tile, company, t->index, direction, rts); @@ -1090,6 +1097,7 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags) c->infrastructure.road[rt] -= len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR; DirtyCompanyInfrastructureWindows(c->index); } + NotifyRoadLayoutChangedIfSimpleTunnelBridgeNonLeaf(tile, endtile, GetTunnelBridgeDirection(tile), rt); } delete Tunnel::GetByTile(tile); @@ -1185,6 +1193,13 @@ static CommandCost DoClearBridge(TileIndex tile, DoCommandFlag flags) SubtractRailTunnelBridgeInfrastructure(tile, endtile); } else if (GetTunnelBridgeTransportType(tile) == TRANSPORT_ROAD) { SubtractRoadTunnelBridgeInfrastructure(tile, endtile); + if (IsRoadCustomBridgeHead(tile) || IsRoadCustomBridgeHead(endtile)) { + NotifyRoadLayoutChanged(); + } else { + RoadTypes roadtypes = GetRoadTypes(tile); + if (HasBit(roadtypes, ROADTYPE_ROAD)) NotifyRoadLayoutChangedIfSimpleTunnelBridgeNonLeaf(tile, endtile, direction, ROADTYPE_ROAD); + if (HasBit(roadtypes, ROADTYPE_TRAM)) NotifyRoadLayoutChangedIfSimpleTunnelBridgeNonLeaf(tile, endtile, direction, ROADTYPE_TRAM); + } } else { // Aqueduct if (Company::IsValidID(owner)) Company::Get(owner)->infrastructure.water -= len * TUNNELBRIDGE_TRACKBIT_FACTOR; }