From 1f727e9029d0261780c4e2922fe729c27e90cf71 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 5 Feb 2017 12:47:34 +0000 Subject: [PATCH 1/4] Custom bridge heads: Add savegame version and setting. --- src/lang/english.txt | 3 +++ src/saveload/afterload.cpp | 8 ++++++++ src/saveload/extended_ver_sl.cpp | 1 + src/saveload/extended_ver_sl.h | 1 + src/settings_gui.cpp | 1 + src/settings_type.h | 1 + src/table/settings.ini | 9 +++++++++ 7 files changed, 24 insertions(+) diff --git a/src/lang/english.txt b/src/lang/english.txt index 83b6591f6e..55ea34bae0 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1701,6 +1701,9 @@ STR_CONFIG_SETTING_PATHFINDER_FOR_SHIPS_HELPTEXT :Path finder to STR_CONFIG_SETTING_REVERSE_AT_SIGNALS :Automatic reversing at signals: {STRING2} STR_CONFIG_SETTING_REVERSE_AT_SIGNALS_HELPTEXT :Allow trains to reverse on a signal, if they waited there a long time +STR_CONFIG_SETTING_ENABLE_ROAD_CUSTOM_BRIDGE_HEADS :Enable road custom bridge heads: {STRING2} +STR_CONFIG_SETTING_ENABLE_ROAD_CUSTOM_BRIDGE_HEADS_HELPTEXT :Allow road bridges to have custom, non-straight flat entry/exit tiles + STR_CONFIG_SETTING_QUERY_CAPTION :{WHITE}Change setting value # Config errors diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index de3f7ccff9..7c70a2ac53 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -2968,6 +2968,14 @@ bool AfterLoadGame() #endif } + if (SlXvIsFeatureMissing(XSLFI_CUSTOM_BRIDGE_HEADS)) { + /* ensure that previously unused custom bridge-head bits are cleared */ + for (TileIndex t = 0; t < map_size; t++) { + if (IsBridgeTile(t) && GetTunnelBridgeTransportType(t) == TRANSPORT_ROAD) { + SB(_m[t].m2, 0, 8, 0); + } + } + } /* Station acceptance is some kind of cache */ if (IsSavegameVersionBefore(127)) { diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 7b083a7f21..f96977dd71 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -45,6 +45,7 @@ std::vector _sl_xv_discardable_chunk_ids; ///< list of chunks static const uint32 _sl_xv_slxi_chunk_version = 0; ///< current version os SLXI chunk const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { + { XSLFI_CUSTOM_BRIDGE_HEADS, XSCF_NULL, 1, 1, "custom_bridge_heads", NULL, NULL, NULL }, { XSLFI_NULL, XSCF_NULL, 0, 0, NULL, NULL, NULL, NULL },// This is the end marker }; diff --git a/src/saveload/extended_ver_sl.h b/src/saveload/extended_ver_sl.h index b495c60c4e..896e0509b6 100644 --- a/src/saveload/extended_ver_sl.h +++ b/src/saveload/extended_ver_sl.h @@ -21,6 +21,7 @@ */ enum SlXvFeatureIndex { XSLFI_NULL = 0, ///< Unused value, to indicate that no extended feature test is in use + XSLFI_CUSTOM_BRIDGE_HEADS, ///< Custom bridge heads XSLFI_SIZE, ///< Total count of features, including null feature }; diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 0652d1bd81..0fcab66738 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -1649,6 +1649,7 @@ static SettingsContainer &GetSettingsTree() limitations->Add(new SettingEntry("construction.road_stop_on_town_road")); limitations->Add(new SettingEntry("construction.road_stop_on_competitor_road")); limitations->Add(new SettingEntry("vehicle.disable_elrails")); + limitations->Add(new SettingEntry("construction.road_custom_bridge_heads")); } SettingsPage *disasters = main->Add(new SettingsPage(STR_CONFIG_SETTING_ACCIDENTS)); diff --git a/src/settings_type.h b/src/settings_type.h index 41366a7719..f968c0034e 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -311,6 +311,7 @@ struct ConstructionSettings { bool freeform_edges; ///< allow terraforming the tiles at the map edges uint8 extra_tree_placement; ///< (dis)allow building extra trees in-game uint8 command_pause_level; ///< level/amount of commands that can't be executed while paused + uint8 road_custom_bridge_heads; ///< allow construction of road custom bridge heads uint32 terraform_per_64k_frames; ///< how many tile heights may, over a long period, be terraformed per 65536 frames? uint16 terraform_frame_burst; ///< how many tile heights may, over a short period, be terraformed? diff --git a/src/table/settings.ini b/src/table/settings.ini index 3a951f9b62..77842003b7 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -1229,6 +1229,15 @@ str = STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD strhelp = STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD_HELPTEXT cat = SC_BASIC +[SDT_BOOL] +base = GameSettings +var = construction.road_custom_bridge_heads +def = true +cat = SC_BASIC +str = STR_CONFIG_SETTING_ENABLE_ROAD_CUSTOM_BRIDGE_HEADS +strhelp = STR_CONFIG_SETTING_ENABLE_ROAD_CUSTOM_BRIDGE_HEADS_HELPTEXT +patxname = ""custom_bridge_heads.construction.road_custom_bridge_heads"" + [SDT_BOOL] base = GameSettings var = station.adjacent_stations From da177d063f9b00545e21bbcf8ec232214549fa0b Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 5 Feb 2017 18:07:10 +0000 Subject: [PATCH 2/4] Custom bridge heads: Initial implementation --- src/bridge_map.h | 68 +++++++++ src/company_gui.cpp | 9 ++ src/company_gui.h | 1 + src/landscape.cpp | 5 + src/pathfinder/follow_track.hpp | 14 +- src/pathfinder/yapf/yapf_road.cpp | 14 +- src/road_cmd.cpp | 241 +++++++++++++++++++++++------- src/road_map.cpp | 1 + src/roadveh_cmd.cpp | 46 ++++-- src/saveload/company_sl.cpp | 10 +- src/tunnelbridge_cmd.cpp | 127 +++++++++++++--- src/tunnelbridge_map.h | 3 + 12 files changed, 433 insertions(+), 106 deletions(-) diff --git a/src/bridge_map.h b/src/bridge_map.h index 74c6974db2..7d8af3a319 100644 --- a/src/bridge_map.h +++ b/src/bridge_map.h @@ -180,4 +180,72 @@ static inline void MakeAqueductBridgeRamp(TileIndex t, Owner o, DiagDirection d) MakeBridgeRamp(t, o, 0, d, TRANSPORT_WATER, 0); } +/** + * Checks if this road bridge head is a custom bridge head + * @param t The tile to analyze + * @pre IsBridgeTile(t) && GetTunnelBridgeTransportType(t) == TRANSPORT_ROAD + * @return true if it is a custom bridge head + */ +static inline bool IsRoadCustomBridgeHead(TileIndex t) +{ + assert(IsBridgeTile(t) && (TransportType)GB(_m[t].m5, 2, 2) == TRANSPORT_ROAD); + return GB(_m[t].m2, 0, 8) != 0; +} + +/** + * Checks if this tile is a road bridge head with a custom bridge head + * @param t The tile to analyze + * @return true if it is a road bridge head with a custom bridge head + */ +static inline bool IsRoadCustomBridgeHeadTile(TileIndex t) +{ + return IsBridgeTile(t) && (TransportType)GB(_m[t].m5, 2, 2) == TRANSPORT_ROAD && IsRoadCustomBridgeHead(t); +} + +/** + * Returns the road bits for a (possibly custom) road bridge head + * @param t The tile to analyze + * @param rt Road type. + * @pre IsBridgeTile(t) && GetTunnelBridgeTransportType(t) == TRANSPORT_ROAD + * @return road bits for the bridge head + */ +static inline RoadBits GetCustomBridgeHeadRoadBits(TileIndex t, RoadType rt) +{ + assert(IsBridgeTile(t)); + if (!HasTileRoadType(t, rt)) return (RoadBits) 0; + RoadBits bits = (GB(_m[t].m5, 0, 1) ? ROAD_Y : ROAD_X) ^ (RoadBits) GB(_m[t].m2, rt == ROADTYPE_TRAM ? 4 : 0, 4); + return bits; +} + +/** + * Returns the road bits for a (possibly custom) road bridge head, for all road types + * @param t The tile to analyze + * @pre IsBridgeTile(t) && GetTunnelBridgeTransportType(t) == TRANSPORT_ROAD + * @return road bits for the bridge head + */ +static inline RoadBits GetCustomBridgeHeadAllRoadBits(TileIndex t) +{ + return GetCustomBridgeHeadRoadBits(t, ROADTYPE_ROAD) | GetCustomBridgeHeadRoadBits(t, ROADTYPE_TRAM); +} + +/** + * Sets the road bits for a (possibly custom) road bridge head + * @param t The tile to modify + * @param rt Road type. + * @param bits The road bits. + * @pre IsBridgeTile(t) && GetTunnelBridgeTransportType(t) == TRANSPORT_ROAD + * @pre HasTileRoadType() must be set correctly before calling this + */ +static inline void SetCustomBridgeHeadRoadBits(TileIndex t, RoadType rt, RoadBits bits) +{ + assert(IsBridgeTile(t)); + if (HasTileRoadType(t, rt)) { + assert(bits != ROAD_NONE); + SB(_m[t].m2, rt == ROADTYPE_TRAM ? 4 : 0, 4, bits ^ (GB(_m[t].m5, 0, 1) ? ROAD_Y : ROAD_X)); + } else { + assert(bits == ROAD_NONE); + SB(_m[t].m2, rt == ROADTYPE_TRAM ? 4 : 0, 4, 0); + } +} + #endif /* BRIDGE_MAP_H */ diff --git a/src/company_gui.cpp b/src/company_gui.cpp index 0be6679583..424ee85df6 100644 --- a/src/company_gui.cpp +++ b/src/company_gui.cpp @@ -2474,6 +2474,15 @@ void DirtyCompanyInfrastructureWindows(CompanyID company) SetWindowDirty(WC_COMPANY_INFRASTRUCTURE, company); } +/** + * Redraw all windows with all company infrastructure counts. + */ +void DirtyAllCompanyInfrastructureWindows() +{ + SetWindowClassesDirty(WC_COMPANY); + SetWindowClassesDirty(WC_COMPANY_INFRASTRUCTURE); +} + struct BuyCompanyWindow : Window { BuyCompanyWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc) { diff --git a/src/company_gui.h b/src/company_gui.h index f4964f3dc7..ed6326dfa4 100644 --- a/src/company_gui.h +++ b/src/company_gui.h @@ -25,5 +25,6 @@ void ShowCompany(CompanyID company); void InvalidateCompanyWindows(const Company *c); void DeleteCompanyWindows(CompanyID company); void DirtyCompanyInfrastructureWindows(CompanyID company); +void DirtyAllCompanyInfrastructureWindows(); #endif /* COMPANY_GUI_H */ diff --git a/src/landscape.cpp b/src/landscape.cpp index 185e84a80b..e71e69b659 100644 --- a/src/landscape.cpp +++ b/src/landscape.cpp @@ -30,6 +30,7 @@ #include "core/random_func.hpp" #include "object_base.h" #include "company_func.h" +#include "tunnelbridge_map.h" #include "pathfinder/npf/aystar.h" #include "saveload/saveload.h" #include @@ -351,6 +352,8 @@ Slope GetFoundationSlope(TileIndex tile, int *z) bool HasFoundationNW(TileIndex tile, Slope slope_here, uint z_here) { + if (IsRoadCustomBridgeHeadTile(tile) && GetTunnelBridgeDirection(tile) == DIAGDIR_NW) return false; + int z; int z_W_here = z_here; @@ -368,6 +371,8 @@ bool HasFoundationNW(TileIndex tile, Slope slope_here, uint z_here) bool HasFoundationNE(TileIndex tile, Slope slope_here, uint z_here) { + if (IsRoadCustomBridgeHeadTile(tile) && GetTunnelBridgeDirection(tile) == DIAGDIR_NE) return false; + int z; int z_E_here = z_here; diff --git a/src/pathfinder/follow_track.hpp b/src/pathfinder/follow_track.hpp index 9f19b029c0..b1c7bfaec4 100644 --- a/src/pathfinder/follow_track.hpp +++ b/src/pathfinder/follow_track.hpp @@ -101,8 +101,9 @@ struct CFollowTrackT { assert(IsTram()); // this function shouldn't be called in other cases - if (IsNormalRoadTile(tile)) { - RoadBits rb = GetRoadBits(tile, ROADTYPE_TRAM); + const bool is_bridge = IsRoadCustomBridgeHeadTile(tile); + if (is_bridge || IsNormalRoadTile(tile)) { + RoadBits rb = is_bridge ? GetCustomBridgeHeadRoadBits(tile, ROADTYPE_TRAM) : GetRoadBits(tile, ROADTYPE_TRAM); switch (rb) { case ROAD_NW: return DIAGDIR_NW; case ROAD_SW: return DIAGDIR_SW; @@ -215,7 +216,7 @@ protected: m_tiles_skipped = GetTunnelBridgeLength(m_new_tile, m_old_tile); return; } - assert(ReverseDiagDir(enterdir) == m_exitdir); + if (!IsRoadCustomBridgeHeadTile(m_old_tile)) assert(ReverseDiagDir(enterdir) == m_exitdir); } /* normal or station tile, do one step */ @@ -364,7 +365,12 @@ protected: } } } else { // IsBridge(m_new_tile) - if (!m_is_bridge) { + if (!m_is_bridge && IsRoadTT() && IsRoadCustomBridgeHeadTile(m_new_tile)) { + if (!(DiagDirToRoadBits(ReverseDiagDir(m_exitdir)) & GetCustomBridgeHeadRoadBits(m_new_tile, IsTram() ? ROADTYPE_TRAM : ROADTYPE_ROAD))) { + m_err = EC_NO_WAY; + return false; + } + } else if (!m_is_bridge) { DiagDirection ramp_enderdir = GetTunnelBridgeDirection(m_new_tile); if (ramp_enderdir != m_exitdir) { m_err = EC_NO_WAY; diff --git a/src/pathfinder/yapf/yapf_road.cpp b/src/pathfinder/yapf/yapf_road.cpp index 380b641da7..fa100700d1 100644 --- a/src/pathfinder/yapf/yapf_road.cpp +++ b/src/pathfinder/yapf/yapf_road.cpp @@ -104,7 +104,10 @@ public: */ inline bool PfCalcCost(Node &n, const TrackFollower *tf) { - int segment_cost = 0; + /* this is to handle the case where the starting tile is a junction custom bridge head, + * and we have advanced across the bridge in the initial step */ + int segment_cost = tf->m_tiles_skipped * YAPF_TILE_LENGTH; + uint tiles = 0; /* start at n.m_key.m_tile / n.m_key.m_td and walk to the end of segment */ TileIndex tile = n.m_key.m_tile; @@ -127,6 +130,11 @@ public: TrackFollower F(Yapf().GetVehicle()); if (!F.Follow(tile, trackdir)) break; + /* if we skipped some tunnel tiles, add their cost */ + /* with custom bridge heads, this cost must be added before checking if the segment has ended */ + segment_cost += F.m_tiles_skipped * YAPF_TILE_LENGTH; + tiles += F.m_tiles_skipped + 1; + /* if there are more trackdirs available & reachable, we are at the end of segment */ if (KillFirstBit(F.m_new_td_bits) != TRACKDIR_BIT_NONE) break; @@ -135,10 +143,6 @@ public: /* stop if RV is on simple loop with no junctions */ if (F.m_new_tile == n.m_key.m_tile && new_td == n.m_key.m_td) return false; - /* if we skipped some tunnel tiles, add their cost */ - segment_cost += F.m_tiles_skipped * YAPF_TILE_LENGTH; - tiles += F.m_tiles_skipped + 1; - /* add hilly terrain penalty */ segment_cost += Yapf().SlopeCost(tile, F.m_new_tile, trackdir); diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index 6482a14b19..f6ba3381f7 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -53,7 +53,7 @@ bool RoadVehiclesAreBuilt() } /** Invalid RoadBits on slopes. */ -static const RoadBits _invalid_tileh_slopes_road[2][15] = { +extern const RoadBits _invalid_tileh_slopes_road[2][15] = { /* The inverse of the mixable RoadBits on a leveled slope */ { ROAD_NONE, // SLOPE_FLAT @@ -213,36 +213,75 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec if (ret.Failed()) return ret; if (!IsTileType(tile, MP_ROAD)) { + const bool custom_bridge_head = IsBridgeTile(tile) && + HasBridgeFlatRamp(GetTileSlope(tile), DiagDirToAxis(GetTunnelBridgeDirection(tile))) && + (_settings_game.construction.road_custom_bridge_heads || IsRoadCustomBridgeHead(tile)); + /* If it's the last roadtype, just clear the whole tile */ - if (rts == RoadTypeToRoadTypes(rt)) return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); + if (!custom_bridge_head && rts == RoadTypeToRoadTypes(rt)) return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); CommandCost cost(EXPENSES_CONSTRUCTION); if (IsTileType(tile, MP_TUNNELBRIDGE)) { - /* Removing any roadbit in the bridge axis removes the roadtype (that's the behaviour remove-long-roads needs) */ - if ((AxisToRoadBits(DiagDirToAxis(GetTunnelBridgeDirection(tile))) & pieces) == ROAD_NONE) return_cmd_error(rt == ROADTYPE_TRAM ? STR_ERROR_THERE_IS_NO_TRAMWAY : STR_ERROR_THERE_IS_NO_ROAD); + const RoadBits entrance_piece = DiagDirToRoadBits(GetTunnelBridgeDirection(tile)); + const RoadBits axial_pieces = AxisToRoadBits(DiagDirToAxis(GetTunnelBridgeDirection(tile))); + const RoadBits existing = IsBridge(tile) ? GetCustomBridgeHeadRoadBits(tile, rt) : axial_pieces; + const RoadType other_rt = (rt == ROADTYPE_ROAD) ? ROADTYPE_TRAM : ROADTYPE_ROAD; - TileIndex other_end = GetOtherTunnelBridgeEnd(tile); - /* Pay for *every* tile of the bridge or tunnel */ - uint len = GetTunnelBridgeLength(other_end, tile) + 2; - cost.AddCost(len * _price[PR_CLEAR_ROAD]); + /* handle case where we would otherwise leave a single bridge entrance piece */ + if ((existing & ~pieces) == entrance_piece) { + pieces |= entrance_piece; + } + + /* Removing any roadbit in the bridge axis removes the roadtype (that's the behaviour remove-long-roads needs) */ + if ((existing & pieces) == ROAD_NONE) return_cmd_error(rt == ROADTYPE_TRAM ? STR_ERROR_THERE_IS_NO_TRAMWAY : STR_ERROR_THERE_IS_NO_ROAD); + + if (!custom_bridge_head) pieces |= axial_pieces; + + const TileIndex other_end = GetOtherTunnelBridgeEnd(tile); + const uint middle_len = GetTunnelBridgeLength(other_end, tile); + uint pieces_count = 0; + + const RoadBits other_end_existing = IsBridge(other_end) ? GetCustomBridgeHeadRoadBits(other_end, rt) : axial_pieces; + RoadBits other_end_pieces = ROAD_NONE; + if (pieces & entrance_piece) { + other_end_pieces |= MirrorRoadBits(entrance_piece); + /* if removing the other end entrance would only leave one piece, remove that too */ + if (CountBits(other_end_existing & ~other_end_pieces) == 1) { + other_end_pieces |= other_end_existing; + } + pieces_count += middle_len * 2; + if ((GetCustomBridgeHeadRoadBits(tile, other_rt) & entrance_piece) == ROAD_NONE) { + /* can't leave no entrance pieces for any road type */ + return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); + } + } + pieces_count += CountBits(pieces & existing); + pieces_count += CountBits(other_end_pieces & other_end_existing); + + cost.AddCost(pieces_count * _price[PR_CLEAR_ROAD]); if (flags & DC_EXEC) { - Company *c = Company::GetIfValid(GetRoadOwner(tile, rt)); - if (c != NULL) { - /* A full diagonal road tile has two road bits. */ - c->infrastructure.road[rt] -= len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR; - DirtyCompanyInfrastructureWindows(c->index); + SubtractRoadTunnelBridgeInfrastructure(tile, other_end); + + const RoadBits bits = existing & ~pieces; + const RoadBits other_bits = other_end_existing & ~other_end_pieces; + + if (bits == ROAD_NONE) SetRoadTypes(tile, GetRoadTypes(tile) & ~RoadTypeToRoadTypes(rt)); + if (other_bits == ROAD_NONE) SetRoadTypes(other_end, GetRoadTypes(other_end) & ~RoadTypeToRoadTypes(rt)); + + if (IsBridge(tile)) { + SetCustomBridgeHeadRoadBits(tile, rt, bits); + SetCustomBridgeHeadRoadBits(other_end, rt, other_bits); } - SetRoadTypes(other_end, GetRoadTypes(other_end) & ~RoadTypeToRoadTypes(rt)); - SetRoadTypes(tile, GetRoadTypes(tile) & ~RoadTypeToRoadTypes(rt)); + if (bits == ROAD_NONE && other_bits == ROAD_NONE) { + /* If the owner of the bridge sells all its road, also move the ownership + * to the owner of the other roadtype, unless the bridge owner is a town. */ - /* If the owner of the bridge sells all its road, also move the ownership - * to the owner of the other roadtype, unless the bridge owner is a town. */ - RoadType other_rt = (rt == ROADTYPE_ROAD) ? ROADTYPE_TRAM : ROADTYPE_ROAD; - Owner other_owner = GetRoadOwner(tile, other_rt); - if (!IsTileOwner(tile, other_owner) && !IsTileOwner(tile, OWNER_TOWN)) { - SetTileOwner(tile, other_owner); - SetTileOwner(other_end, other_owner); + Owner other_owner = GetRoadOwner(tile, other_rt); + if (!IsTileOwner(tile, other_owner) && !IsTileOwner(tile, OWNER_TOWN)) { + SetTileOwner(tile, other_owner); + SetTileOwner(other_end, other_owner); + } } /* Mark tiles dirty that have been repaved */ @@ -252,6 +291,9 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec MarkTileDirtyByTile(tile); MarkTileDirtyByTile(other_end); } + + AddRoadTunnelBridgeInfrastructure(tile, other_end); + DirtyAllCompanyInfrastructureWindows(); } } else { assert(IsDriveThroughStopTile(tile)); @@ -670,12 +712,108 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 case MP_TUNNELBRIDGE: { if (GetTunnelBridgeTransportType(tile) != TRANSPORT_ROAD) goto do_clear; - /* Only allow building the outern roadbit, so building long roads stops at existing bridges */ - if (MirrorRoadBits(DiagDirToRoadBits(GetTunnelBridgeDirection(tile))) != pieces) goto do_clear; - if (HasTileRoadType(tile, rt)) return_cmd_error(STR_ERROR_ALREADY_BUILT); - /* Don't allow adding roadtype to the bridge/tunnel when vehicles are already driving on it */ - CommandCost ret = TunnelBridgeIsFree(tile, GetOtherTunnelBridgeEnd(tile)); - if (ret.Failed()) return ret; + + const TileIndex other_end = GetOtherTunnelBridgeEnd(tile); + + if (IsBridge(tile)) { + const DiagDirection entrance_dir = GetTunnelBridgeDirection(tile); + const RoadBits entrance_piece = DiagDirToRoadBits(entrance_dir); + const RoadBits axial_pieces = AxisToRoadBits(DiagDirToAxis(entrance_dir)); + existing = GetCustomBridgeHeadRoadBits(tile, rt); + + if (!(_settings_game.construction.road_custom_bridge_heads && HasBridgeFlatRamp(tileh, DiagDirToAxis(entrance_dir)))) { + /* Ordinary bridge heads only */ + /* Only allow building the outer roadbit, so building long roads stops at existing bridges */ + if (MirrorRoadBits(entrance_piece) != pieces) goto do_clear; + pieces = axial_pieces; + } + if ((existing & pieces) == pieces) return_cmd_error(STR_ERROR_ALREADY_BUILT); + if ((pieces & ~axial_pieces) && !_settings_game.construction.build_on_slopes) { + return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); + } + if ((_invalid_tileh_slopes_road[0][tileh] & (pieces & ~entrance_piece)) != ROAD_NONE) { + return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); + } + + /* Don't allow adding roadtype to the bridge/tunnel when vehicles are already driving on it */ + CommandCost ret = TunnelBridgeIsFree(tile, other_end); + if (ret.Failed()) return ret; + + /* check if this end is already owned by someone else */ + const Owner owner = GetRoadOwner(tile, rt); + if (owner != OWNER_NONE) { + CommandCost ret = CheckOwnership(owner, tile); + if (ret.Failed()) return ret; + } + + if ((existing | pieces) == entrance_piece) { + /* + * Don't allow the custom bridge head bits to be only the entrance piece + * as this makes road vehicles go haywire + */ + pieces = axial_pieces; + } + + RoadBits added_pieces = (existing | pieces) & ~existing; + uint added_pieces_count = CountBits(added_pieces); + RoadBits other_end_added_pieces = ROAD_NONE; + RoadBits other_end_existing = ROAD_NONE; + + if (added_pieces & entrance_piece) { + /* adding road to whole bridge */ + + /* check if other end is already owned by someone else */ + const Owner other_end_owner = GetRoadOwner(other_end, rt); + if (other_end_owner != OWNER_NONE) { + CommandCost ret = CheckOwnership(other_end_owner, other_end); + if (ret.Failed()) return ret; + } + + other_end_added_pieces = MirrorRoadBits(entrance_piece); + added_pieces_count += 1 + (GetTunnelBridgeLength(tile, other_end) * 2); + + other_end_existing = GetCustomBridgeHeadRoadBits(other_end, rt); + assert((other_end_added_pieces & other_end_existing) == ROAD_NONE); + + if (other_end_existing == ROAD_NONE) { + /* + * Don't allow the other end custom bridge head bits to be only the entrance piece + * as this makes road vehicles go haywire + */ + other_end_added_pieces = axial_pieces; + added_pieces_count++; + } + } + + cost.AddCost(added_pieces_count * _price[PR_BUILD_ROAD]); + + if (flags & DC_EXEC) { + SubtractRoadTunnelBridgeInfrastructure(tile, other_end); + + SetRoadTypes(tile, GetRoadTypes(tile) | RoadTypeToRoadTypes(rt)); + SetRoadOwner(tile, rt, company); + SetCustomBridgeHeadRoadBits(tile, rt, existing | pieces); + if (other_end_added_pieces) { + SetRoadTypes(other_end, GetRoadTypes(other_end) | RoadTypeToRoadTypes(rt)); + SetRoadOwner(other_end, rt, company); + SetCustomBridgeHeadRoadBits(other_end, rt, other_end_existing | other_end_added_pieces); + } + + MarkBridgeDirty(tile); + + AddRoadTunnelBridgeInfrastructure(tile, other_end); + DirtyAllCompanyInfrastructureWindows(); + } + + return cost; + } else { // IsTunnel(tile) + /* Only allow building the outer roadbit, so building long roads stops at existing bridges */ + if (MirrorRoadBits(DiagDirToRoadBits(GetTunnelBridgeDirection(tile))) != pieces) goto do_clear; + if (HasTileRoadType(tile, rt)) return_cmd_error(STR_ERROR_ALREADY_BUILT); + /* Don't allow adding roadtype to the bridge/tunnel when vehicles are already driving on it */ + CommandCost ret = TunnelBridgeIsFree(tile, other_end); + if (ret.Failed()) return ret; + } break; } @@ -762,7 +900,7 @@ do_clear:; /* Mark tiles dirty that have been repaved */ if (IsBridge(tile)) { - MarkBridgeDirty(tile); + NOT_REACHED(); } else { MarkTileDirtyByTile(other_end); MarkTileDirtyByTile(tile); @@ -864,8 +1002,6 @@ CommandCost CmdBuildLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 p CommandCost cost(EXPENSES_CONSTRUCTION); CommandCost last_error = CMD_ERROR; TileIndex tile = start_tile; - bool had_bridge = false; - bool had_tunnel = false; bool had_success = false; bool is_ai = HasBit(p2, 6); @@ -896,27 +1032,21 @@ CommandCost CmdBuildLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 p } } else { had_success = true; - /* Only pay for the upgrade on one side of the bridges and tunnels */ - if (IsTileType(tile, MP_TUNNELBRIDGE)) { - if (IsBridge(tile)) { - if (!had_bridge || GetTunnelBridgeDirection(tile) == dir) { - cost.AddCost(ret); - } - had_bridge = true; - } else { // IsTunnel(tile) - if (!had_tunnel || GetTunnelBridgeDirection(tile) == dir) { - cost.AddCost(ret); - } - had_tunnel = true; - } - } else { - cost.AddCost(ret); - } + cost.AddCost(ret); + } + /* Do not run into or across bridges/tunnels */ + if (IsTileType(tile, MP_TUNNELBRIDGE)) { + if (GetTunnelBridgeDirection(tile) == dir) break; } if (tile == end_tile) break; tile += TileOffsByDiagDir(dir); + + /* Do not run onto a bridge/tunnel tile from below/above */ + if (IsTileType(tile, MP_TUNNELBRIDGE)) { + if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(dir)) break; + } } return had_success ? cost : last_error; @@ -1239,16 +1369,17 @@ static void DrawRoadDetail(SpriteID img, const TileInfo *ti, int dx, int dy, int * Draw ground sprite and road pieces * @param ti TileInfo */ -static void DrawRoadBits(TileInfo *ti) +void DrawRoadBits(TileInfo *ti) { - RoadBits road = GetRoadBits(ti->tile, ROADTYPE_ROAD); - RoadBits tram = GetRoadBits(ti->tile, ROADTYPE_TRAM); + const bool is_bridge = IsTileType(ti->tile, MP_TUNNELBRIDGE); + RoadBits road = is_bridge ? GetCustomBridgeHeadRoadBits(ti->tile, ROADTYPE_ROAD) : GetRoadBits(ti->tile, ROADTYPE_ROAD); + RoadBits tram = is_bridge ? GetCustomBridgeHeadRoadBits(ti->tile, ROADTYPE_TRAM) : GetRoadBits(ti->tile, ROADTYPE_TRAM); SpriteID image = 0; PaletteID pal = PAL_NONE; if (ti->tileh != SLOPE_FLAT) { - DrawFoundation(ti, GetRoadFoundation(ti->tileh, road | tram)); + DrawFoundation(ti, is_bridge ? FOUNDATION_LEVELED : GetRoadFoundation(ti->tileh, road | tram)); /* DrawFoundation() modifies ti. * Default sloped sprites.. */ @@ -1257,7 +1388,7 @@ static void DrawRoadBits(TileInfo *ti) if (image == 0) image = _road_tile_sprites_1[road != ROAD_NONE ? road : tram]; - Roadside roadside = GetRoadside(ti->tile); + Roadside roadside = is_bridge ? ROADSIDE_PAVED : GetRoadside(ti->tile); if (DrawRoadAsSnowDesert(ti->tile, roadside)) { image += 19; @@ -1285,14 +1416,14 @@ static void DrawRoadBits(TileInfo *ti) DrawGroundSprite(image, pal); } - if (road != ROAD_NONE) { + if (!is_bridge && road != ROAD_NONE) { DisallowedRoadDirections drd = GetDisallowedRoadDirections(ti->tile); if (drd != DRD_NONE) { DrawGroundSpriteAt(SPR_ONEWAY_BASE + drd - 1 + ((road == ROAD_X) ? 0 : 3), PAL_NONE, 8, 8, GetPartialPixelZ(8, 8, ti->tileh)); } } - if (HasRoadWorks(ti->tile)) { + if (!is_bridge && HasRoadWorks(ti->tile)) { /* Road works */ DrawGroundSprite((road | tram) & ROAD_X ? SPR_EXCAVATION_X : SPR_EXCAVATION_Y, PAL_NONE); return; @@ -1587,7 +1718,7 @@ static bool ClickTile_Road(TileIndex tile) } /* Converts RoadBits to TrackBits */ -static const TrackBits _road_trackbits[16] = { +extern const TrackBits _road_trackbits[16] = { TRACK_BIT_NONE, // ROAD_NONE TRACK_BIT_NONE, // ROAD_NW TRACK_BIT_NONE, // ROAD_SW diff --git a/src/road_map.cpp b/src/road_map.cpp index 4984117bab..7d6a541fa8 100644 --- a/src/road_map.cpp +++ b/src/road_map.cpp @@ -52,6 +52,7 @@ RoadBits GetAnyRoadBits(TileIndex tile, RoadType rt, bool straight_tunnel_bridge case MP_TUNNELBRIDGE: if (GetTunnelBridgeTransportType(tile) != TRANSPORT_ROAD) return ROAD_NONE; + if (IsRoadCustomBridgeHeadTile(tile)) return GetCustomBridgeHeadRoadBits(tile, rt); return straight_tunnel_bridge_entrance ? AxisToRoadBits(DiagDirToAxis(GetTunnelBridgeDirection(tile))) : DiagDirToRoadBits(ReverseDiagDir(GetTunnelBridgeDirection(tile))); diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 9b541a766b..1072c72d41 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -1143,6 +1143,8 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) * by the previous vehicle in the chain when it gets to the right place. */ if (v->IsInDepot()) return true; + bool no_advance_tile = false; + if (v->state == RVSB_WORMHOLE) { /* Vehicle is entering a depot or is on a bridge or in a tunnel */ GetNewVehiclePosResult gp = GetNewVehiclePos(v); @@ -1156,19 +1158,24 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) } if (IsTileType(gp.new_tile, MP_TUNNELBRIDGE) && HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) { - /* Vehicle has just entered a bridge or tunnel */ + if (IsRoadCustomBridgeHeadTile(gp.new_tile)) { + v->frame = 15; + no_advance_tile = true; + } else { + /* Vehicle has just entered a bridge or tunnel */ + v->x_pos = gp.x; + v->y_pos = gp.y; + v->UpdatePosition(); + v->UpdateInclination(true, true); + return true; + } + } else { v->x_pos = gp.x; v->y_pos = gp.y; v->UpdatePosition(); - v->UpdateInclination(true, true); + if ((v->vehstatus & VS_HIDDEN) == 0) v->Vehicle::UpdateViewport(true); return true; } - - v->x_pos = gp.x; - v->y_pos = gp.y; - v->UpdatePosition(); - if ((v->vehstatus & VS_HIDDEN) == 0) v->Vehicle::UpdateViewport(true); - return true; } /* Get move position data for next frame. @@ -1179,12 +1186,16 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) (_settings_game.vehicle.road_side << RVS_DRIVE_SIDE)) ^ v->overtaking][v->frame + 1]; if (rd.x & RDE_NEXT_TILE) { - TileIndex tile = v->tile + TileOffsByDiagDir((DiagDirection)(rd.x & 3)); + TileIndex tile = v->tile; + if (!no_advance_tile) tile += TileOffsByDiagDir((DiagDirection)(rd.x & 3)); Trackdir dir; if (v->IsFrontEngine()) { /* If this is the front engine, look for the right path. */ dir = RoadFindPathToDest(v, tile, (DiagDirection)(rd.x & 3)); + } else if (no_advance_tile) { + /* Follow previous vehicle out of custom bridge wormhole */ + dir = (Trackdir) prev->state; } else { dir = FollowPreviousRoadVehicle(v, prev, tile, (DiagDirection)(rd.x & 3), false); } @@ -1201,6 +1212,10 @@ again: /* When turning around we can't be overtaking. */ v->overtaking = 0; + if (no_advance_tile) { + DEBUG(misc, 0, "Road vehicle attempted to turn around on a single road piece bridge head"); + } + /* Turning around */ if (v->roadtype == ROADTYPE_TRAM) { /* Determine the road bits the tram needs to be able to turn around @@ -1213,9 +1228,16 @@ again: case TRACKDIR_RVREV_SW: needed = ROAD_NE; break; case TRACKDIR_RVREV_NW: needed = ROAD_SE; break; } - if ((v->Previous() != NULL && v->Previous()->tile == tile) || - (v->IsFrontEngine() && IsNormalRoadTile(tile) && !HasRoadWorks(tile) && - (needed & GetRoadBits(tile, ROADTYPE_TRAM)) != ROAD_NONE)) { + auto tile_turn_ok = [&]() -> bool { + if (IsNormalRoadTile(tile)) { + return !HasRoadWorks(tile) && (needed & GetRoadBits(tile, ROADTYPE_TRAM)) != ROAD_NONE; + } else if (IsRoadCustomBridgeHeadTile(tile)) { + return (needed & GetCustomBridgeHeadRoadBits(tile, ROADTYPE_TRAM)) != ROAD_NONE; + } else { + return false; + } + }; + if ((v->Previous() != NULL && v->Previous()->tile == tile) || tile_turn_ok()) { /* * Taking the 'big' corner for trams only happens when: * - The previous vehicle in this (articulated) tram chain is diff --git a/src/saveload/company_sl.cpp b/src/saveload/company_sl.cpp index 27c069fe08..57a33732c9 100644 --- a/src/saveload/company_sl.cpp +++ b/src/saveload/company_sl.cpp @@ -200,7 +200,8 @@ void AfterLoadCompanyStats() if (tile < other_end) { /* Count each tunnel/bridge TUNNELBRIDGE_TRACKBIT_FACTOR times to simulate * the higher structural maintenance needs, and don't forget the end tiles. */ - uint len = (GetTunnelBridgeLength(tile, other_end) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR; + const uint middle_len = GetTunnelBridgeLength(tile, other_end) * TUNNELBRIDGE_TRACKBIT_FACTOR; + const uint len = middle_len + (2 * TUNNELBRIDGE_TRACKBIT_FACTOR); switch (GetTunnelBridgeTransportType(tile)) { case TRANSPORT_RAIL: @@ -209,12 +210,7 @@ void AfterLoadCompanyStats() break; case TRANSPORT_ROAD: { - /* Iterate all present road types as each can have a different owner. */ - RoadType rt; - FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(tile)) { - c = Company::GetIfValid(GetRoadOwner(tile, rt)); - if (c != NULL) c->infrastructure.road[rt] += len * 2; // A full diagonal road has two road bits. - } + AddRoadTunnelBridgeInfrastructure(tile, other_end); break; } diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 9ba9b26e31..4b8845f0a4 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -52,6 +52,8 @@ TileIndex _build_tunnel_endtile; ///< The end of a tunnel; as hidden return from /** Z position of the bridge sprites relative to bridge height (downwards) */ static const int BRIDGE_Z_START = 3; +extern void DrawRoadBits(TileInfo *ti); +extern const RoadBits _invalid_tileh_slopes_road[2][15]; /** * Mark bridge tiles dirty. @@ -925,19 +927,11 @@ static CommandCost DoClearBridge(TileIndex tile, DoCommandFlag flags) if (rail) { if (Company::IsValidID(owner)) Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= len * TUNNELBRIDGE_TRACKBIT_FACTOR; } else if (GetTunnelBridgeTransportType(tile) == TRANSPORT_ROAD) { - RoadType rt; - FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(tile)) { - Company *c = Company::GetIfValid(GetRoadOwner(tile, rt)); - if (c != NULL) { - /* A full diagonal road tile has two road bits. */ - c->infrastructure.road[rt] -= len * 2 * TUNNELBRIDGE_TRACKBIT_FACTOR; - DirtyCompanyInfrastructureWindows(c->index); - } - } + SubtractRoadTunnelBridgeInfrastructure(tile, endtile); } else { // Aqueduct if (Company::IsValidID(owner)) Company::Get(owner)->infrastructure.water -= len * TUNNELBRIDGE_TRACKBIT_FACTOR; } - DirtyCompanyInfrastructureWindows(owner); + DirtyAllCompanyInfrastructureWindows(); DoClearSquare(tile); DoClearSquare(endtile); @@ -1241,6 +1235,12 @@ static void DrawTile_TunnelBridge(TileInfo *ti) DrawBridgeMiddle(ti); } else { // IsBridge(ti->tile) + if (transport_type == TRANSPORT_ROAD && IsRoadCustomBridgeHead(ti->tile)) { + DrawRoadBits(ti); + DrawBridgeMiddle(ti); + return; + } + const PalSpriteID *psid; int base_offset; bool ice = HasTunnelBridgeSnowOrDesert(ti->tile); @@ -1462,11 +1462,19 @@ void DrawBridgeMiddle(const TileInfo *ti) psid++; if (transport_type == TRANSPORT_ROAD) { - RoadTypes rts = GetRoadTypes(rampsouth); + const RoadTypes rts = GetRoadTypes(rampsouth); - if (HasBit(rts, ROADTYPE_TRAM)) { + bool has_tram = HasBit(rts, ROADTYPE_TRAM); + bool has_road = HasBit(rts, ROADTYPE_ROAD); + if (IsRoadCustomBridgeHeadTile(rampsouth)) { + RoadBits entrance_bit = DiagDirToRoadBits(GetTunnelBridgeDirection(rampsouth)); + has_tram = has_tram && (GetCustomBridgeHeadRoadBits(rampsouth, ROADTYPE_TRAM) & entrance_bit); + has_road = has_road && (GetCustomBridgeHeadRoadBits(rampsouth, ROADTYPE_ROAD) & entrance_bit); + } + + if (has_tram) { /* DrawBridgeTramBits() calls EndSpriteCombine() and StartSpriteCombine() */ - DrawBridgeTramBits(x, y, bridge_z, axis ^ 1, HasBit(rts, ROADTYPE_ROAD), false); + DrawBridgeTramBits(x, y, bridge_z, axis ^ 1, has_road, false); } else { EndSpriteCombine(); StartSpriteCombine(); @@ -1547,6 +1555,10 @@ static int GetSlopePixelZ_TunnelBridge(TileIndex tile, uint x, uint y) /* In the tunnel entrance? */ if (5 <= pos && pos <= 10) return z; } else { // IsBridge(tile) + if (IsRoadCustomBridgeHeadTile(tile)) { + return z + TILE_HEIGHT; + } + DiagDirection dir = GetTunnelBridgeDirection(tile); uint pos = (DiagDirToAxis(dir) == AXIS_X ? y : x); @@ -1574,6 +1586,7 @@ static int GetSlopePixelZ_TunnelBridge(TileIndex tile, uint x, uint y) static Foundation GetFoundation_TunnelBridge(TileIndex tile, Slope tileh) { + if (IsRoadCustomBridgeHeadTile(tile)) return FOUNDATION_LEVELED; return IsTunnel(tile) ? FOUNDATION_NONE : GetBridgeFoundation(tileh, DiagDirToAxis(GetTunnelBridgeDirection(tile))); } @@ -1654,42 +1667,102 @@ static void TileLoop_TunnelBridge(TileIndex tile) } } +extern const TrackBits _road_trackbits[16]; + static TrackStatus GetTileTrackStatus_TunnelBridge(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side) { TransportType transport_type = GetTunnelBridgeTransportType(tile); if (transport_type != mode || (transport_type == TRANSPORT_ROAD && (GetRoadTypes(tile) & sub_mode) == 0)) return 0; DiagDirection dir = GetTunnelBridgeDirection(tile); + + if (mode == TRANSPORT_ROAD && IsRoadCustomBridgeHeadTile(tile)) { + if (side != INVALID_DIAGDIR && side == dir) return 0; + TrackBits bits = TRACK_BIT_NONE; + if (sub_mode & ROADTYPES_TRAM) bits |= _road_trackbits[GetCustomBridgeHeadRoadBits(tile, ROADTYPE_TRAM)]; + if (sub_mode & ROADTYPES_ROAD) bits |= _road_trackbits[GetCustomBridgeHeadRoadBits(tile, ROADTYPE_ROAD)]; + return CombineTrackStatus(TrackBitsToTrackdirBits(bits), TRACKDIR_BIT_NONE); + } if (side != INVALID_DIAGDIR && side != ReverseDiagDir(dir)) return 0; return CombineTrackStatus(TrackBitsToTrackdirBits(DiagDirToDiagTrackBits(dir)), TRACKDIR_BIT_NONE); } +static void UpdateRoadTunnelBridgeInfrastructure(TileIndex begin, TileIndex end, bool add) { + /* A full diagonal road has two road bits. */ + const uint middle_len = 2 * GetTunnelBridgeLength(begin, end) * TUNNELBRIDGE_TRACKBIT_FACTOR; + const uint len = middle_len + (2 * TUNNELBRIDGE_TRACKBIT_FACTOR); + + /* Iterate all present road types as each can have a different owner. */ + RoadType rt; + FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(begin)) { + Company * const c = Company::GetIfValid(GetRoadOwner(begin, rt)); + if (c != NULL) { + uint infra = 0; + if (IsBridge(begin)) { + const RoadBits bits = GetCustomBridgeHeadRoadBits(begin, rt); + infra += CountBits(bits) * TUNNELBRIDGE_TRACKBIT_FACTOR; + if (bits & DiagDirToRoadBits(GetTunnelBridgeDirection(begin))) { + infra += middle_len; + } + } else { + infra += len; + } + if (add) { + c->infrastructure.road[rt] += infra; + } else { + c->infrastructure.road[rt] -= infra; + } + } + } + FOR_EACH_SET_ROADTYPE(rt, GetRoadTypes(end)) { + Company * const c = Company::GetIfValid(GetRoadOwner(end, rt)); + if (c != NULL) { + uint infra = 0; + if (IsBridge(end)) { + const RoadBits bits = GetCustomBridgeHeadRoadBits(end, rt); + infra += CountBits(bits) * TUNNELBRIDGE_TRACKBIT_FACTOR; + } + if (add) { + c->infrastructure.road[rt] += infra; + } else { + c->infrastructure.road[rt] -= infra; + } + } + } +} + +void AddRoadTunnelBridgeInfrastructure(TileIndex begin, TileIndex end) { + UpdateRoadTunnelBridgeInfrastructure(begin, end, true); +} + +void SubtractRoadTunnelBridgeInfrastructure(TileIndex begin, TileIndex end) { + UpdateRoadTunnelBridgeInfrastructure(begin, end, false); +} + static void ChangeTileOwner_TunnelBridge(TileIndex tile, Owner old_owner, Owner new_owner) { - TileIndex other_end = GetOtherTunnelBridgeEnd(tile); + const TileIndex other_end = GetOtherTunnelBridgeEnd(tile); /* Set number of pieces to zero if it's the southern tile as we * don't want to update the infrastructure counts twice. */ - uint num_pieces = tile < other_end ? (GetTunnelBridgeLength(tile, other_end) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR : 0; + const uint num_pieces = tile < other_end ? (GetTunnelBridgeLength(tile, other_end) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR : 0; + const TransportType tt = GetTunnelBridgeTransportType(tile); + + if (tt == TRANSPORT_ROAD) SubtractRoadTunnelBridgeInfrastructure(tile, other_end); for (RoadType rt = ROADTYPE_ROAD; rt < ROADTYPE_END; rt++) { /* Update all roadtypes, no matter if they are present */ if (GetRoadOwner(tile, rt) == old_owner) { - if (HasBit(GetRoadTypes(tile), rt)) { - /* Update company infrastructure counts. A full diagonal road tile has two road bits. - * No need to dirty windows here, we'll redraw the whole screen anyway. */ - Company::Get(old_owner)->infrastructure.road[rt] -= num_pieces * 2; - if (new_owner != INVALID_OWNER) Company::Get(new_owner)->infrastructure.road[rt] += num_pieces * 2; - } - SetRoadOwner(tile, rt, new_owner == INVALID_OWNER ? OWNER_NONE : new_owner); } } + if (tt == TRANSPORT_ROAD) AddRoadTunnelBridgeInfrastructure(tile, other_end); + if (!IsTileOwner(tile, old_owner)) return; /* Update company infrastructure counts for rail and water as well. * No need to dirty windows here, we'll redraw the whole screen anyway. */ - TransportType tt = GetTunnelBridgeTransportType(tile); + Company *old = Company::Get(old_owner); if (tt == TRANSPORT_RAIL) { old->infrastructure.rail[GetRailType(tile)] -= num_pieces; @@ -1883,6 +1956,14 @@ static CommandCost TerraformTile_TunnelBridge(TileIndex tile, DoCommandFlag flag int z_old; Slope tileh_old = GetTileSlope(tile, &z_old); + if (IsRoadCustomBridgeHeadTile(tile)) { + const RoadBits pieces = GetCustomBridgeHeadAllRoadBits(tile); + const RoadBits entrance_piece = DiagDirToRoadBits(direction); + if ((_invalid_tileh_slopes_road[0][tileh_new] & (pieces & ~entrance_piece)) != ROAD_NONE) { + return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); + } + } + /* Check if new slope is valid for bridges in general (so we can safely call GetBridgeFoundation()) */ if ((direction == DIAGDIR_NW) || (direction == DIAGDIR_NE)) { CheckBridgeSlopeSouth(axis, &tileh_old, &z_old); diff --git a/src/tunnelbridge_map.h b/src/tunnelbridge_map.h index 0f7f17b3ac..0437fd1e1f 100644 --- a/src/tunnelbridge_map.h +++ b/src/tunnelbridge_map.h @@ -121,4 +121,7 @@ static inline TrackBits GetTunnelBridgeReservationTrackBits(TileIndex t) return HasTunnelBridgeReservation(t) ? DiagDirToDiagTrackBits(GetTunnelBridgeDirection(t)) : TRACK_BIT_NONE; } +void AddRoadTunnelBridgeInfrastructure(TileIndex begin, TileIndex end); +void SubtractRoadTunnelBridgeInfrastructure(TileIndex begin, TileIndex end); + #endif /* TUNNELBRIDGE_MAP_H */ From 0cd99e576cac6b4548036f9e9d5f53b62ab7f721 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 5 Feb 2017 18:27:59 +0000 Subject: [PATCH 3/4] Custom bridge heads: Document landscape array use. --- docs/landscape.html | 30 ++++++++++++++++++++++++++++++ docs/landscape_grid.html | 4 +++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/docs/landscape.html b/docs/landscape.html index f60e859fc3..86d0b8057d 100644 --- a/docs/landscape.html +++ b/docs/landscape.html @@ -1443,6 +1443,36 @@
  • m1 bits 4..0: owner
  • +
  • m2 bits 7..0: custom road bridge heads (flat bridge heads only)
    + Road/tram bits below are XORed with the axial bridge direction road bits.
    + A non-custom bridge head configuration will have a value of 0. +
      +
    • m2 bits 7..4: Road bits
      + Valid only if m7 bit 6 is set +
    • +
    • m2 bits 3..0: Tram bits
      + Valid only if m7 bit 7 is set +
    • +
    + + + + + + + + + + + + + + + + + +
    bit 0: NW piece
    bit 1: SW piece
    bit 2: SE piece
    bit 3: NE piece
    +
  • m3 bits 7..4: owner of tram
  • m3 bits 3..0: track type for railway
  • m5 bit 4: pbs reservation state for railway
  • diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html index d34fe9840c..3ba984d5d8 100644 --- a/docs/landscape_grid.html +++ b/docs/landscape_grid.html @@ -9,6 +9,7 @@ span.option{ font-family: "Courier New", Courier, mono; background-color: rgb(255,255, 30); } span.free { font-family: "Courier New", Courier, mono; background-color: rgb(30, 178, 54); } span.used { font-family: "Courier New", Courier, mono; } + span.used_p{ font-family: "Courier New", Courier, mono; background-color: cyan; } td.bits { white-space: nowrap; text-align: center; font-family: "Courier New", Courier, mono; } td.caption { white-space: nowrap; text-align: left; } td li { white-space: nowrap; text-align: left; } @@ -23,6 +24,7 @@ the array so you can quickly see what is used and what is not.
    • O - bit is free
    • X - bit is used
    • +
    • P - bit is used by patch feature
    •   - bit of attribute is abused for different purposes, i.e. other bits define the actual meaning.
    • ~ - bit is accessed, but does not really have a meaning (e.g. owner of clear land is always OWNER_NONE)
    @@ -343,7 +345,7 @@ the array so you can quickly see what is used and what is not. -inherit- -inherit- -inherit- - -inherit- + OOOO OOOO PPPP PPPP -inherit- -inherit- -inherit- From 079a5a66daa223b5431992e22956faa79c54af4c Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 5 Feb 2017 18:44:27 +0000 Subject: [PATCH 4/4] Custom bridge heads: Relax ownership checks to be similar to plain roads. --- src/road_cmd.cpp | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index f6ba3381f7..48189374fb 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -739,12 +739,6 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 CommandCost ret = TunnelBridgeIsFree(tile, other_end); if (ret.Failed()) return ret; - /* check if this end is already owned by someone else */ - const Owner owner = GetRoadOwner(tile, rt); - if (owner != OWNER_NONE) { - CommandCost ret = CheckOwnership(owner, tile); - if (ret.Failed()) return ret; - } if ((existing | pieces) == entrance_piece) { /* @@ -762,13 +756,6 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 if (added_pieces & entrance_piece) { /* adding road to whole bridge */ - /* check if other end is already owned by someone else */ - const Owner other_end_owner = GetRoadOwner(other_end, rt); - if (other_end_owner != OWNER_NONE) { - CommandCost ret = CheckOwnership(other_end_owner, other_end); - if (ret.Failed()) return ret; - } - other_end_added_pieces = MirrorRoadBits(entrance_piece); added_pieces_count += 1 + (GetTunnelBridgeLength(tile, other_end) * 2); @@ -791,11 +778,11 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 SubtractRoadTunnelBridgeInfrastructure(tile, other_end); SetRoadTypes(tile, GetRoadTypes(tile) | RoadTypeToRoadTypes(rt)); - SetRoadOwner(tile, rt, company); + if (!existing) SetRoadOwner(tile, rt, company); SetCustomBridgeHeadRoadBits(tile, rt, existing | pieces); if (other_end_added_pieces) { SetRoadTypes(other_end, GetRoadTypes(other_end) | RoadTypeToRoadTypes(rt)); - SetRoadOwner(other_end, rt, company); + if (!other_end_existing) SetRoadOwner(other_end, rt, company); SetCustomBridgeHeadRoadBits(other_end, rt, other_end_existing | other_end_added_pieces); }