diff --git a/src/bridge.h b/src/bridge.h index c9e7200698..ee26a2d369 100644 --- a/src/bridge.h +++ b/src/bridge.h @@ -37,6 +37,29 @@ constexpr uint SPRITES_PER_BRIDGE_PIECE = 32; ///< Number of sprites there are p typedef uint BridgeType; ///< Bridge spec number. +/** + * Actions that can be performed when the vehicle enters the depot. + */ +enum BridgePiecePillarFlags { + BPPF_CORNER_W = 1 << 0, + BPPF_CORNER_S = 1 << 1, + BPPF_CORNER_E = 1 << 2, + BPPF_CORNER_N = 1 << 3, + BPPF_ALL_CORNERS = 0xF, + BPPF_EDGE_NE = 1 << 4, + BPPF_EDGE_SE = 1 << 5, + BPPF_EDGE_SW = 1 << 6, + BPPF_EDGE_NW = 1 << 7, +}; +DECLARE_ENUM_AS_BIT_SET(BridgePiecePillarFlags) + +enum BridgeSpecCtrlFlags { + BSCF_CUSTOM_PILLAR_FLAGS, + BSCF_INVALID_PILLAR_FLAGS, + BSCF_NOT_AVAILABLE_TOWN, + BSCF_NOT_AVAILABLE_AI_GS, +}; + /** * Struct containing information about a single bridge type */ @@ -52,6 +75,8 @@ struct BridgeSpec { StringID transport_name[2]; ///< description of the bridge, when built for road or rail std::vector> sprite_table; ///< table of sprites for drawing the bridge uint8_t flags; ///< bit 0 set: disable drawing of far pillars. + uint8_t ctrl_flags; ///< control flags + uint8_t pillar_flags[12]; ///< bridge pillar flags: 6 x pairs of x and y flags }; extern BridgeSpec _bridge[MAX_BRIDGES]; @@ -75,6 +100,15 @@ void DrawBridgeMiddle(const TileInfo *ti); CommandCost CheckBridgeAvailability(BridgeType bridge_type, uint bridge_len, DoCommandFlags flags = {}); int CalcBridgeLenCostFactor(int x); +BridgePiecePillarFlags GetBridgeTilePillarFlags(TileIndex tile, TileIndex northern_bridge_end, TileIndex southern_bridge_end, BridgeType bridge_type, TransportType bridge_transport_type); + +struct BridgePieceDebugInfo { + BridgePieces piece; + BridgePiecePillarFlags pillar_flags; + uint pillar_index; +}; +BridgePieceDebugInfo GetBridgePieceDebugInfo(TileIndex tile); + void ResetBridges(); #endif /* BRIDGE_H */ diff --git a/src/bridge_gui.cpp b/src/bridge_gui.cpp index f77337014e..251f7a352e 100644 --- a/src/bridge_gui.cpp +++ b/src/bridge_gui.cpp @@ -382,6 +382,8 @@ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transpo StringID errmsg = INVALID_STRING_ID; CommandCost ret = Command::Do(CommandFlagsToDCFlags(GetCommandFlags()) | DoCommandFlag::QueryCost, end, start, transport_type, 0, road_rail_type); + const bool query_per_bridge_type = ret.Failed() && (ret.GetErrorMessage() == STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION || ret.GetErrorMessage() == STR_ERROR_BRIDGE_PILLARS_OBSTRUCT_STATION); + GUIBridgeList bl; if (ret.Failed()) { errmsg = ret.GetErrorMessage(); @@ -415,11 +417,13 @@ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transpo } bool any_available = false; + StringID type_errmsg = INVALID_STRING_ID; CommandCost type_check; /* loop for all bridgetypes */ for (BridgeType brd_type = 0; brd_type != MAX_BRIDGES; brd_type++) { type_check = CheckBridgeAvailability(brd_type, bridge_len); if (type_check.Succeeded()) { + if (query_per_bridge_type && Command::Do(CommandFlagsToDCFlags(GetCommandFlags()) | DoCommandFlag::QueryCost, end, start, transport_type, brd_type, road_rail_type).Failed()) continue; /* bridge is accepted, add to list */ BuildBridgeData &item = bl.emplace_back(); item.index = brd_type; @@ -428,10 +432,12 @@ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transpo * bridge itself (not computed with DoCommandFlag::QueryCost) */ item.cost = ret.GetCost() + (((int64_t)tot_bridgedata_len * _price[PR_BUILD_BRIDGE] * item.spec->price) >> 8) + infra_cost; any_available = true; + } else if (type_check.GetErrorMessage() != INVALID_STRING_ID && !query_per_bridge_type) { + type_errmsg = type_check.GetErrorMessage(); } } /* give error cause if no bridges available here*/ - if (!any_available) + if (!any_available && type_errmsg != INVALID_STRING_ID) errmsg = type_errmsg; { errmsg = type_check.GetErrorMessage(); } diff --git a/src/lang/english.txt b/src/lang/english.txt index e1ec3f2a95..5327dc3037 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5240,6 +5240,8 @@ STR_ERROR_START_AND_END_MUST_BE_IN :{WHITE}Start an STR_ERROR_ENDS_OF_BRIDGE_MUST_BOTH :{WHITE}... ends of bridge must both be on land STR_ERROR_BRIDGE_TOO_LONG :{WHITE}... bridge too long STR_ERROR_BRIDGE_THROUGH_MAP_BORDER :{WHITE}Bridge would end out of the map +STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION :{WHITE}Bridge is too low for station +STR_ERROR_BRIDGE_PILLARS_OBSTRUCT_STATION :{WHITE}Bridge pillars obstruct station # Tunnel related errors STR_ERROR_CAN_T_BUILD_TUNNEL_HERE :{WHITE}Can't build tunnel here... diff --git a/src/newgrf_roadstop.h b/src/newgrf_roadstop.h index 690c9adadb..d7631a5f27 100644 --- a/src/newgrf_roadstop.h +++ b/src/newgrf_roadstop.h @@ -71,6 +71,12 @@ enum class RoadStopSpecFlag : uint8_t { }; using RoadStopSpecFlags = EnumBitSet; +enum class RoadStopSpecIntlFlag : uint8_t { + BridgeHeightsSet, ///< bridge_height[6] is set. + BridgeDisallowedPillarsSet, ///< bridge_disallowed_pillars[6] is set. +}; +using RoadStopSpecIntlFlags = EnumBitSet; + enum RoadStopView : uint8_t { RSV_BAY_NE = 0, ///< Bay road stop, facing Northeast RSV_BAY_SE = 1, ///< Bay road stop, facing Southeast @@ -133,6 +139,7 @@ struct RoadStopSpec : NewGRFSpecBase { RoadStopDrawModes draw_mode = {RoadStopDrawMode::Road, RoadStopDrawMode::Overlay}; RoadStopCallbackMasks callback_mask{}; RoadStopSpecFlags flags{}; + RoadStopSpecIntlFlags internal_flags{}; CargoTypes cargo_triggers = 0; ///< Bitmask of cargo types which cause trigger re-randomizing @@ -144,6 +151,8 @@ struct RoadStopSpec : NewGRFSpecBase { uint8_t build_cost_multiplier = 16; ///< Build cost multiplier per tile. uint8_t clear_cost_multiplier = 16; ///< Clear cost multiplier per tile. + uint8_t height; ///< The height of this structure, in heightlevels; max MAX_TILE_HEIGHT. + std::vector badges; /** diff --git a/src/newgrf_station.h b/src/newgrf_station.h index 1b74f90e80..7237b8e035 100644 --- a/src/newgrf_station.h +++ b/src/newgrf_station.h @@ -118,6 +118,12 @@ enum class StationSpecFlag : uint8_t { }; using StationSpecFlags = EnumBitSet; +enum class StationSpecIntlFlag : uint8_t { + BridgeHeightsSet, ///< bridge_height[8] is set. + BridgeDisallowedPillarsSet, ///< bridge_disallowed_pillars[8] is set. +}; +using StationSpecIntlFlags = EnumBitSet; + /** Station specification. */ struct StationSpec : NewGRFSpecBase { StationSpec() : name(0), @@ -162,6 +168,12 @@ struct StationSpec : NewGRFSpecBase { StationSpecFlags flags; ///< Bitmask of flags, bit 0: use different sprite set; bit 1: divide cargo about by station size + struct BridgeAboveFlags { + uint8_t height = UINT8_MAX; ///< Minimum height for a bridge above, 0 for none + uint8_t disallowed_pillars = 0; ///< Disallowed pillar flags for a bridge above + }; + std::vector bridge_above_flags; ///< List of bridge above flags. + enum class TileFlag : uint8_t { Pylons = 0, ///< Tile should contain catenary pylons. NoWires = 1, ///< Tile should NOT contain catenary wires. @@ -172,10 +184,18 @@ struct StationSpec : NewGRFSpecBase { AnimationInfo animation; + StationSpecIntlFlags internal_flags{}; ///< Bitmask of internal spec flags + /** Custom platform layouts, keyed by platform and length combined. */ std::unordered_map> layouts; std::vector badges; + + BridgeAboveFlags GetBridgeAboveFlags(uint gfx) const + { + if (gfx < this->bridge_above_flags.size()) return this->bridge_above_flags[gfx]; + return {}; + } }; /** Class containing information relating to station classes. */ diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index a28153602f..235fc98a00 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -949,125 +949,6 @@ static CommandCost CheckFlatLandRailStation(TileIndex tile_cur, TileIndex north_ return cost; } -/** - * Checks if a road stop can be built at the given tile. - * @param cur_tile Tile to check. - * @param allowed_z Height allowed for the tile. If allowed_z is negative, it will be set to the height of this tile. - * @param flags Operation to perform. - * @param invalid_dirs Prohibited directions (set of DiagDirections). - * @param is_drive_through True if trying to build a drive-through station. - * @param station_type Station type (bus, truck or road waypoint). - * @param axis Axis of a drive-through road stop. - * @param station StationID to be queried and returned if available. - * @param rt Road type to build, may be INVALID_ROADTYPE if an existing road is required. - * @return The cost in case of success, or an error code if it failed. - */ -static CommandCost CheckFlatLandRoadStop(TileIndex cur_tile, int &allowed_z, DoCommandFlags flags, DiagDirections invalid_dirs, bool is_drive_through, StationType station_type, Axis axis, StationID *station, RoadType rt) -{ - CommandCost cost(EXPENSES_CONSTRUCTION); - - CommandCost ret = CheckBuildableTile(cur_tile, invalid_dirs, allowed_z, !is_drive_through); - if (ret.Failed()) return ret; - cost.AddCost(ret.GetCost()); - - /* If station is set, then we have special handling to allow building on top of already existing stations. - * Station points to StationID::Invalid() if we can build on any station. - * Or it points to a station if we're only allowed to build on exactly that station. */ - if (station != nullptr && IsTileType(cur_tile, MP_STATION)) { - if (!IsAnyRoadStop(cur_tile)) { - return ClearTile_Station(cur_tile, DoCommandFlag::Auto); // Get error message. - } else { - if (station_type != GetStationType(cur_tile) || - is_drive_through != IsDriveThroughStopTile(cur_tile)) { - return ClearTile_Station(cur_tile, DoCommandFlag::Auto); // Get error message. - } - /* Drive-through station in the wrong direction. */ - if (is_drive_through && IsDriveThroughStopTile(cur_tile) && GetDriveThroughStopAxis(cur_tile) != axis) { - return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION); - } - StationID st = GetStationIndex(cur_tile); - if (*station == StationID::Invalid()) { - *station = st; - } else if (*station != st) { - return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING); - } - } - } else { - bool build_over_road = is_drive_through && IsNormalRoadTile(cur_tile); - /* Road bits in the wrong direction. */ - RoadBits rb = IsNormalRoadTile(cur_tile) ? GetAllRoadBits(cur_tile) : ROAD_NONE; - if (build_over_road && (rb & (axis == AXIS_X ? ROAD_Y : ROAD_X)) != 0) { - /* Someone was pedantic and *NEEDED* three fracking different error messages. */ - switch (CountBits(rb)) { - case 1: - return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION); - - case 2: - if (rb == ROAD_X || rb == ROAD_Y) return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION); - return CommandCost(STR_ERROR_DRIVE_THROUGH_CORNER); - - default: // 3 or 4 - return CommandCost(STR_ERROR_DRIVE_THROUGH_JUNCTION); - } - } - - if (build_over_road) { - /* There is a road, check if we can build road+tram stop over it. */ - RoadType road_rt = GetRoadType(cur_tile, RTT_ROAD); - if (road_rt != INVALID_ROADTYPE) { - Owner road_owner = GetRoadOwner(cur_tile, RTT_ROAD); - if (road_owner == OWNER_TOWN) { - if (!_settings_game.construction.road_stop_on_town_road) return CommandCost(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD); - } else if (!_settings_game.construction.road_stop_on_competitor_road && road_owner != OWNER_NONE) { - ret = CheckOwnership(road_owner); - if (ret.Failed()) return ret; - } - uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_ROAD)); - - if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt) && !HasPowerOnRoad(rt, road_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD); - - if (GetDisallowedRoadDirections(cur_tile) != DRD_NONE && road_owner != OWNER_TOWN) { - ret = CheckOwnership(road_owner); - if (ret.Failed()) return ret; - } - - cost.AddCost(RoadBuildCost(road_rt) * (2 - num_pieces)); - } else if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt)) { - cost.AddCost(RoadBuildCost(rt) * 2); - } - - /* There is a tram, check if we can build road+tram stop over it. */ - RoadType tram_rt = GetRoadType(cur_tile, RTT_TRAM); - if (tram_rt != INVALID_ROADTYPE) { - Owner tram_owner = GetRoadOwner(cur_tile, RTT_TRAM); - if (Company::IsValidID(tram_owner) && - (!_settings_game.construction.road_stop_on_competitor_road || - /* Disallow breaking end-of-line of someone else - * so trams can still reverse on this tile. */ - HasExactlyOneBit(GetRoadBits(cur_tile, RTT_TRAM)))) { - ret = CheckOwnership(tram_owner); - if (ret.Failed()) return ret; - } - uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_TRAM)); - - if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt) && !HasPowerOnRoad(rt, tram_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD); - - cost.AddCost(RoadBuildCost(tram_rt) * (2 - num_pieces)); - } else if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt)) { - cost.AddCost(RoadBuildCost(rt) * 2); - } - } else if (rt == INVALID_ROADTYPE) { - return CommandCost(STR_ERROR_THERE_IS_NO_ROAD); - } else { - ret = Command::Do(flags, cur_tile); - if (ret.Failed()) return ret; - cost.AddCost(ret.GetCost()); - cost.AddCost(RoadBuildCost(rt) * 2); - } - } - - return cost; -} /** * Check whether we can expand the rail part of the given station. @@ -1324,6 +1205,224 @@ void SetRailStationTileFlags(TileIndex tile, const StationSpec *statspec) SetStationTileHaveWires(tile, !flags.Test(StationSpec::TileFlag::NoWires)); } +CommandCost IsRailStationBridgeAboveOk(TileIndex tile, const StationSpec *statspec, uint8_t layout, TileIndex northern_bridge_end, TileIndex southern_bridge_end, int bridge_height, + BridgeType bridge_type, TransportType bridge_transport_type) +{ + if (statspec != nullptr && statspec->internal_flags.Test(StationSpecIntlFlag::BridgeHeightsSet)) { + int height_above = statspec->GetBridgeAboveFlags(layout).height; + if (height_above == 0) return CommandCost(INVALID_STRING_ID); + if (GetTileMaxZ(tile) + height_above > bridge_height) { + return CommandCost(STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION); + } + } else if (!statspec) { + /* Default stations/waypoints */ + const int height = layout < 4 ? 2 : 5; + if (GetTileMaxZ(tile) + height > bridge_height) return CommandCost(STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION); + } else { + return CommandCost(INVALID_STRING_ID); + } + + BridgePiecePillarFlags disallowed_pillar_flags; + if (statspec != nullptr && statspec->internal_flags.Test(StationSpecIntlFlag::BridgeDisallowedPillarsSet)) { + /* Pillar flags set by NewGRF */ + disallowed_pillar_flags = (BridgePiecePillarFlags) statspec->GetBridgeAboveFlags(layout).disallowed_pillars; + } else if (!statspec) { + /* Default stations/waypoints */ + if (layout < 8) { + static const uint8_t st_flags[8] = { 0x50, 0xA0, 0x50, 0xA0, 0x50 | 0x26, 0xA0 | 0x1C, 0x50 | 0x89, 0xA0 | 0x43 }; + disallowed_pillar_flags = (BridgePiecePillarFlags) st_flags[layout]; + } else { + disallowed_pillar_flags = (BridgePiecePillarFlags) 0; + } + } else if (GetStationTileFlags(layout, statspec).Test(StationSpec::TileFlag::Blocked)) { + /* Non-track station tiles */ + disallowed_pillar_flags = (BridgePiecePillarFlags) 0; + } else { + /* Tracked station tiles */ + const Axis axis = HasBit(layout, 0) ? AXIS_Y : AXIS_X; + disallowed_pillar_flags = (BridgePiecePillarFlags) (axis == AXIS_X ? 0x50 : 0xA0); + } + + if ((GetBridgeTilePillarFlags(tile, northern_bridge_end, southern_bridge_end, bridge_type, bridge_transport_type) & disallowed_pillar_flags) == 0) { + return CommandCost(); + } else { + return CommandCost(STR_ERROR_BRIDGE_PILLARS_OBSTRUCT_STATION); + } +} + +CommandCost IsRailStationBridgeAboveOk(TileIndex tile, const StationSpec *statspec, uint8_t layout) +{ + if (!IsBridgeAbove(tile)) return CommandCost(); + + TileIndex southern_bridge_end = GetSouthernBridgeEnd(tile); + TileIndex northern_bridge_end = GetNorthernBridgeEnd(tile); + return IsRailStationBridgeAboveOk(tile, statspec, layout, northern_bridge_end, southern_bridge_end, GetBridgeHeight(southern_bridge_end), + GetBridgeType(southern_bridge_end), GetTunnelBridgeTransportType(southern_bridge_end)); +} + +CommandCost IsRoadStopBridgeAboveOK(TileIndex tile, const RoadStopSpec *spec, bool drive_through, DiagDirection entrance, + TileIndex northern_bridge_end, TileIndex southern_bridge_end, int bridge_height, + BridgeType bridge_type, TransportType bridge_transport_type) +{ + if (spec != nullptr && spec->internal_flags.Test(RoadStopSpecIntlFlag::BridgeHeightsSet)) { + int height = spec->bridge_height[drive_through ? (GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET + DiagDirToAxis(entrance)) : entrance]; + if (height == 0) return CommandCost(INVALID_STRING_ID); + if (GetTileMaxZ(tile) + height > bridge_height) { + return CommandCost(STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION); + } + } else { + return CommandCost(INVALID_STRING_ID); + + if (GetTileMaxZ(tile) + (drive_through ? 1 : 2) > bridge_height) { + return CommandCost(STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION); + } + } + + BridgePiecePillarFlags disallowed_pillar_flags = (BridgePiecePillarFlags) 0; + if (spec != nullptr && spec->internal_flags.Test(RoadStopSpecIntlFlag::BridgeDisallowedPillarsSet)) { + disallowed_pillar_flags = (BridgePiecePillarFlags) spec->bridge_disallowed_pillars[drive_through ? (GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET + DiagDirToAxis(entrance)) : entrance]; + } else if (drive_through) { + disallowed_pillar_flags = (BridgePiecePillarFlags) (DiagDirToAxis(entrance) == AXIS_X ? 0x50 : 0xA0); + } else { + SetBit(disallowed_pillar_flags, 4 + entrance); + } + if ((GetBridgeTilePillarFlags(tile, northern_bridge_end, southern_bridge_end, bridge_type, bridge_transport_type) & disallowed_pillar_flags) == 0) { + return CommandCost(); + } else { + return CommandCost(STR_ERROR_BRIDGE_PILLARS_OBSTRUCT_STATION); + } +} + +/** + * Checks if a road stop can be built at the given tile. + * @param cur_tile Tile to check. + * @param allowed_z Height allowed for the tile. If allowed_z is negative, it will be set to the height of this tile. + * @param flags Operation to perform. + * @param invalid_dirs Prohibited directions (set of DiagDirections). + * @param is_drive_through True if trying to build a drive-through station. + * @param station_type Station type (bus, truck or road waypoint). + * @param axis Axis of a drive-through road stop. + * @param station StationID to be queried and returned if available. + * @param rt Road type to build, may be INVALID_ROADTYPE if an existing road is required. + * @return The cost in case of success, or an error code if it failed. + */ +static CommandCost CheckFlatLandRoadStop(TileIndex cur_tile, int &allowed_z, const RoadStopSpec *spec, DoCommandFlags flags, DiagDirections invalid_dirs, bool is_drive_through, StationType station_type, Axis axis, StationID *station, RoadType rt) +{ + CommandCost cost(EXPENSES_CONSTRUCTION); + + bool allow_under_bridge = spec != nullptr && spec->internal_flags.Test(RoadStopSpecIntlFlag::BridgeHeightsSet); + CommandCost ret = CheckBuildableTile(cur_tile, invalid_dirs, allowed_z, !is_drive_through, true); + if (ret.Failed()) return ret; + cost.AddCost(ret.GetCost()); + + if (allow_under_bridge && IsBridgeAbove(cur_tile)) { + TileIndex southern_bridge_end = GetSouthernBridgeEnd(cur_tile); + TileIndex northern_bridge_end = GetNorthernBridgeEnd(cur_tile); + CommandCost bridge_ret = IsRoadStopBridgeAboveOK(cur_tile, spec, is_drive_through, DiagDirection::DIAGDIR_NE /*obviously wrong, but how do you get the "first bit" from invalid_dirs? and how would that be correct?? */, + northern_bridge_end, southern_bridge_end, GetBridgeHeight(southern_bridge_end), + GetBridgeType(southern_bridge_end), GetTunnelBridgeTransportType(southern_bridge_end)); + if (bridge_ret.Failed()) return bridge_ret; + } + + /* If station is set, then we have special handling to allow building on top of already existing stations. + * Station points to StationID::Invalid() if we can build on any station. + * Or it points to a station if we're only allowed to build on exactly that station. */ + if (station != nullptr && IsTileType(cur_tile, MP_STATION)) { + if (!IsAnyRoadStop(cur_tile)) { + return ClearTile_Station(cur_tile, DoCommandFlag::Auto); // Get error message. + } else { + if (station_type != GetStationType(cur_tile) || + is_drive_through != IsDriveThroughStopTile(cur_tile)) { + return ClearTile_Station(cur_tile, DoCommandFlag::Auto); // Get error message. + } + /* Drive-through station in the wrong direction. */ + if (is_drive_through && IsDriveThroughStopTile(cur_tile) && GetDriveThroughStopAxis(cur_tile) != axis) { + return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION); + } + StationID st = GetStationIndex(cur_tile); + if (*station == StationID::Invalid()) { + *station = st; + } else if (*station != st) { + return CommandCost(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING); + } + } + } else { + bool build_over_road = is_drive_through && IsNormalRoadTile(cur_tile); + /* Road bits in the wrong direction. */ + RoadBits rb = IsNormalRoadTile(cur_tile) ? GetAllRoadBits(cur_tile) : ROAD_NONE; + if (build_over_road && (rb & (axis == AXIS_X ? ROAD_Y : ROAD_X)) != 0) { + /* Someone was pedantic and *NEEDED* three fracking different error messages. */ + switch (CountBits(rb)) { + case 1: + return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION); + + case 2: + if (rb == ROAD_X || rb == ROAD_Y) return CommandCost(STR_ERROR_DRIVE_THROUGH_DIRECTION); + return CommandCost(STR_ERROR_DRIVE_THROUGH_CORNER); + + default: // 3 or 4 + return CommandCost(STR_ERROR_DRIVE_THROUGH_JUNCTION); + } + } + + if (build_over_road) { + /* There is a road, check if we can build road+tram stop over it. */ + RoadType road_rt = GetRoadType(cur_tile, RTT_ROAD); + if (road_rt != INVALID_ROADTYPE) { + Owner road_owner = GetRoadOwner(cur_tile, RTT_ROAD); + if (road_owner == OWNER_TOWN) { + if (!_settings_game.construction.road_stop_on_town_road) return CommandCost(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD); + } else if (!_settings_game.construction.road_stop_on_competitor_road && road_owner != OWNER_NONE) { + ret = CheckOwnership(road_owner); + if (ret.Failed()) return ret; + } + uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_ROAD)); + + if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt) && !HasPowerOnRoad(rt, road_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD); + + if (GetDisallowedRoadDirections(cur_tile) != DRD_NONE && road_owner != OWNER_TOWN) { + ret = CheckOwnership(road_owner); + if (ret.Failed()) return ret; + } + + cost.AddCost(RoadBuildCost(road_rt) * (2 - num_pieces)); + } else if (rt != INVALID_ROADTYPE && RoadTypeIsRoad(rt)) { + cost.AddCost(RoadBuildCost(rt) * 2); + } + + /* There is a tram, check if we can build road+tram stop over it. */ + RoadType tram_rt = GetRoadType(cur_tile, RTT_TRAM); + if (tram_rt != INVALID_ROADTYPE) { + Owner tram_owner = GetRoadOwner(cur_tile, RTT_TRAM); + if (Company::IsValidID(tram_owner) && + (!_settings_game.construction.road_stop_on_competitor_road || + /* Disallow breaking end-of-line of someone else + * so trams can still reverse on this tile. */ + HasExactlyOneBit(GetRoadBits(cur_tile, RTT_TRAM)))) { + ret = CheckOwnership(tram_owner); + if (ret.Failed()) return ret; + } + uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_TRAM)); + + if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt) && !HasPowerOnRoad(rt, tram_rt)) return CommandCost(STR_ERROR_NO_SUITABLE_ROAD); + + cost.AddCost(RoadBuildCost(tram_rt) * (2 - num_pieces)); + } else if (rt != INVALID_ROADTYPE && RoadTypeIsTram(rt)) { + cost.AddCost(RoadBuildCost(rt) * 2); + } + } else if (rt == INVALID_ROADTYPE) { + return CommandCost(STR_ERROR_THERE_IS_NO_ROAD); + } else { + ret = Command::Do(flags, cur_tile); + if (ret.Failed()) return ret; + cost.AddCost(ret.GetCost()); + cost.AddCost(RoadBuildCost(rt) * 2); + } + } + + return cost; +} + /** * Build rail station * @param flags operation to perform @@ -1362,6 +1461,9 @@ CommandCost CmdBuildRailStation(DoCommandFlags flags, TileIndex tile_org, RailTy w_org = numtracks; } + /* Check if the first tile and the last tile are valid */ + if (!IsValidTile(tile_org) || TileAddWrap(tile_org, w_org - 1, h_org - 1) == INVALID_TILE) return CMD_ERROR; + bool reuse = (station_to_join != NEW_STATION); if (!reuse) station_to_join = StationID::Invalid(); bool distant_join = (station_to_join != StationID::Invalid()); @@ -1376,6 +1478,33 @@ CommandCost CmdBuildRailStation(DoCommandFlags flags, TileIndex tile_org, RailTy /* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */ StationID est = StationID::Invalid(); std::vector affected_vehicles; + + const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index); + + TileIndexDiff tile_delta = TileOffsByAxis(axis); // offset to go to the next platform tile + TileIndexDiff track_delta = TileOffsByAxis(OtherAxis(axis)); // offset to go to the next track + std::vector layout_buffer; + layout_buffer.resize(numtracks * plat_len); + //TempBufferST layout_buffer(numtracks * plat_len); + GetStationLayout(&layout_buffer[0], numtracks, plat_len, statspec); + + { + TileIndex tile_track = tile_org; + uint8_t *check_layout_ptr = &layout_buffer[0]; + for (uint i = 0; i < numtracks; i++) { + TileIndex tile = tile_track; + for (uint j = 0; j < plat_len; j++) { + CommandCost ret = IsRailStationBridgeAboveOk(tile, statspec, *check_layout_ptr++); + if (ret.Failed()) { + //return CommandCost::DualErrorMessage(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST, ret.GetErrorMessage()); + return ret; + } + tile += tile_delta; + } + tile_track += track_delta; + } + } + /* Add construction and clearing expenses. */ CommandCost cost = CalculateRailStationCost(new_location, flags, axis, &est, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks); if (cost.Failed()) return cost; @@ -1393,7 +1522,6 @@ CommandCost CmdBuildRailStation(DoCommandFlags flags, TileIndex tile_org, RailTy } /* Check if we can allocate a custom stationspec to this station */ - const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index); int specindex = AllocateSpecToStation(statspec, st, flags.Test(DoCommandFlag::Execute)); if (specindex == -1) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS); @@ -1423,8 +1551,6 @@ CommandCost CmdBuildRailStation(DoCommandFlags flags, TileIndex tile_org, RailTy st->cached_anim_triggers.Set(statspec->animation.triggers); } - TileIndexDiff tile_delta = TileOffsByAxis(axis); // offset to go to the next platform tile - TileIndexDiff track_delta = TileOffsByAxis(OtherAxis(axis)); // offset to go to the next track Track track = AxisToTrack(axis); std::vector layouts(numtracks * plat_len); @@ -1913,7 +2039,7 @@ static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID statio * @param unit_cost The cost to build one road stop of the current type. * @return The cost in case of success, or an error code if it failed. */ -CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlags flags, bool is_drive_through, StationType station_type, Axis axis, DiagDirection ddir, StationID *est, RoadType rt, Money unit_cost) +CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlags flags, bool is_drive_through, StationType station_type, const RoadStopSpec *roadstopspec, Axis axis, DiagDirection ddir, StationID *est, RoadType rt, Money unit_cost) { DiagDirections invalid_dirs{}; if (is_drive_through) { @@ -1927,7 +2053,7 @@ CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlags flags, bool int allowed_z = -1; CommandCost cost(EXPENSES_CONSTRUCTION); for (TileIndex cur_tile : tile_area) { - CommandCost ret = CheckFlatLandRoadStop(cur_tile, allowed_z, flags, invalid_dirs, is_drive_through, station_type, axis, est, rt); + CommandCost ret = CheckFlatLandRoadStop(cur_tile, allowed_z, roadstopspec, flags, invalid_dirs, is_drive_through, station_type, axis, est, rt); if (ret.Failed()) return ret; bool is_preexisting_roadstop = IsTileType(cur_tile, MP_STATION) && IsAnyRoadStop(cur_tile); @@ -2008,7 +2134,7 @@ CommandCost CmdBuildRoadStop(DoCommandFlags flags, TileIndex tile, uint8_t width unit_cost = _price[is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS]; } StationID est = StationID::Invalid(); - CommandCost cost = CalculateRoadStopCost(roadstop_area, flags, is_drive_through, is_truck_stop ? StationType::Truck : StationType::Bus, axis, ddir, &est, rt, unit_cost); + CommandCost cost = CalculateRoadStopCost(roadstop_area, flags, is_drive_through, is_truck_stop ? StationType::Truck : StationType::Bus, roadstopspec, axis, ddir, &est, rt, unit_cost); if (cost.Failed()) return cost; Station *st = nullptr; @@ -3380,6 +3506,7 @@ draw_default_foundation: } DrawRailTileSeq(ti, t, TO_BUILDINGS, total_offset, relocation, palette); + DrawBridgeMiddle(ti); } void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, RoadType roadtype, int image) diff --git a/src/table/bridge_land.h b/src/table/bridge_land.h index e6f4f7f185..6a9ca8d677 100644 --- a/src/table/bridge_land.h +++ b/src/table/bridge_land.h @@ -740,8 +740,12 @@ static const std::span> _bridge_sprite_table[ * @param nrl description of the rail bridge in query tool * @param nrd description of the road bridge in query tool */ -#define MBR(y, mnl, mxl, p, mxs, spr, plt, dsc, nrl, nrd) \ - {TimerGameCalendar::Year{y}, mnl, mxl, p, mxs, spr, plt, dsc, { nrl, nrd }, {}, 0} +#define MBR(y, mnl, mxl, p, mxs, spr, plt, dsc, nrl, nrd, pillars) \ + {TimerGameCalendar::Year{y}, mnl, mxl, p, mxs, spr, plt, dsc, { nrl, nrd }, {}, 0, 0, pillars} + +#define ALL_PILLARS { 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F } +#define SUSP_PILLARS { 0x03, 0x06, 0x0C, 0x09, 0x0C, 0x09, 0x03, 0x06, 0x0F, 0x0F, 0x00, 0x00 } +#define CANT_PILLARS { 0x00, 0x00, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09, 0x0C, 0x09 } const BridgeSpec _orig_bridge[] = { /* @@ -755,45 +759,48 @@ const BridgeSpec _orig_bridge[] = { string with description name on rail name on road | | | | */ MBR( 0, 0, 0xFFFF, 80, 32, 0xA24, PAL_NONE, - STR_BRIDGE_NAME_WOODEN, STR_LAI_BRIDGE_DESCRIPTION_RAIL_WOODEN, STR_LAI_BRIDGE_DESCRIPTION_ROAD_WOODEN), + STR_BRIDGE_NAME_WOODEN, STR_LAI_BRIDGE_DESCRIPTION_RAIL_WOODEN, STR_LAI_BRIDGE_DESCRIPTION_ROAD_WOODEN, ALL_PILLARS), MBR( 0, 0, 2, 112, 48, 0xA26, PALETTE_TO_STRUCT_RED, - STR_BRIDGE_NAME_CONCRETE, STR_LAI_BRIDGE_DESCRIPTION_RAIL_CONCRETE, STR_LAI_BRIDGE_DESCRIPTION_ROAD_CONCRETE), + STR_BRIDGE_NAME_CONCRETE, STR_LAI_BRIDGE_DESCRIPTION_RAIL_CONCRETE, STR_LAI_BRIDGE_DESCRIPTION_ROAD_CONCRETE, ALL_PILLARS), MBR(1930, 0, 5, 144, 64, 0xA25, PAL_NONE, - STR_BRIDGE_NAME_GIRDER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_GIRDER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_GIRDER_STEEL), + STR_BRIDGE_NAME_GIRDER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_GIRDER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_GIRDER_STEEL, ALL_PILLARS), MBR( 0, 2, 10, 168, 80, 0xA22, PALETTE_TO_STRUCT_CONCRETE, - STR_BRIDGE_NAME_SUSPENSION_CONCRETE, STR_LAI_BRIDGE_DESCRIPTION_RAIL_SUSPENSION_CONCRETE, STR_LAI_BRIDGE_DESCRIPTION_ROAD_SUSPENSION_CONCRETE), + STR_BRIDGE_NAME_SUSPENSION_CONCRETE, STR_LAI_BRIDGE_DESCRIPTION_RAIL_SUSPENSION_CONCRETE, STR_LAI_BRIDGE_DESCRIPTION_ROAD_SUSPENSION_CONCRETE, SUSP_PILLARS), MBR(1930, 3, 0xFFFF, 185, 96, 0xA22, PAL_NONE, - STR_BRIDGE_NAME_SUSPENSION_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_SUSPENSION_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_SUSPENSION_STEEL), + STR_BRIDGE_NAME_SUSPENSION_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_SUSPENSION_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_SUSPENSION_STEEL, SUSP_PILLARS), MBR(1930, 3, 0xFFFF, 192, 112, 0xA22, PALETTE_TO_STRUCT_YELLOW, - STR_BRIDGE_NAME_SUSPENSION_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_SUSPENSION_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_SUSPENSION_STEEL), + STR_BRIDGE_NAME_SUSPENSION_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_SUSPENSION_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_SUSPENSION_STEEL, SUSP_PILLARS), MBR(1930, 3, 7, 224, 160, 0xA23, PAL_NONE, - STR_BRIDGE_NAME_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_CANTILEVER_STEEL), + STR_BRIDGE_NAME_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_CANTILEVER_STEEL, CANT_PILLARS), MBR(1930, 3, 8, 232, 208, 0xA23, PALETTE_TO_STRUCT_BROWN, - STR_BRIDGE_NAME_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_CANTILEVER_STEEL), + STR_BRIDGE_NAME_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_CANTILEVER_STEEL, CANT_PILLARS), MBR(1930, 3, 9, 248, 240, 0xA23, PALETTE_TO_STRUCT_RED, - STR_BRIDGE_NAME_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_CANTILEVER_STEEL), + STR_BRIDGE_NAME_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_CANTILEVER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_CANTILEVER_STEEL, CANT_PILLARS), MBR(1930, 0, 2, 240, 256, 0xA27, PAL_NONE, - STR_BRIDGE_NAME_GIRDER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_GIRDER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_GIRDER_STEEL), + STR_BRIDGE_NAME_GIRDER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_GIRDER_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_GIRDER_STEEL, ALL_PILLARS), MBR(1995, 2, 0xFFFF, 255, 320, 0xA28, PAL_NONE, - STR_BRIDGE_NAME_TUBULAR_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_TUBULAR_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_TUBULAR_STEEL), + STR_BRIDGE_NAME_TUBULAR_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_TUBULAR_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_TUBULAR_STEEL, CANT_PILLARS), MBR(2005, 2, 0xFFFF, 380, 512, 0xA28, PALETTE_TO_STRUCT_YELLOW, - STR_BRIDGE_NAME_TUBULAR_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_TUBULAR_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_TUBULAR_STEEL), + STR_BRIDGE_NAME_TUBULAR_STEEL, STR_LAI_BRIDGE_DESCRIPTION_RAIL_TUBULAR_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_TUBULAR_STEEL, CANT_PILLARS), MBR(2010, 2, 0xFFFF, 510, 608, 0xA28, PALETTE_TO_STRUCT_CONCRETE, - STR_BRIDGE_TUBULAR_SILICON, STR_LAI_BRIDGE_DESCRIPTION_RAIL_TUBULAR_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_TUBULAR_STEEL) + STR_BRIDGE_TUBULAR_SILICON, STR_LAI_BRIDGE_DESCRIPTION_RAIL_TUBULAR_STEEL, STR_LAI_BRIDGE_DESCRIPTION_ROAD_TUBULAR_STEEL, CANT_PILLARS) }; +#undef CANT_PILLARS +#undef SUSP_PILLARS +#undef ALL_PILLARS #undef MBR #undef MN #undef MR diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 458fa5b785..c19f9e8dad 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -39,10 +39,13 @@ #include "object_base.h" #include "water.h" #include "company_gui.h" +#include "newgrf_roadstop.h" #include "station_func.h" +#include "station_map.h" #include "tunnelbridge_cmd.h" #include "landscape_cmd.h" #include "terraform_cmd.h" +#include "newgrf_station.h" #include "table/strings.h" #include "table/bridge_land.h" @@ -55,6 +58,12 @@ 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 CommandCost IsRailStationBridgeAboveOk(TileIndex tile, const StationSpec *statspec, uint8_t layout, TileIndex northern_bridge_end, TileIndex southern_bridge_end, int bridge_height, + BridgeType bridge_type, TransportType bridge_transport_type); + +extern CommandCost IsRoadStopBridgeAboveOK(TileIndex tile, const RoadStopSpec *spec, bool drive_through, DiagDirection entrance, + TileIndex northern_bridge_end, TileIndex southern_bridge_end, int bridge_height, + BridgeType bridge_type, TransportType bridge_transport_type); /** * Mark bridge tiles dirty. @@ -391,6 +400,48 @@ CommandCost CmdBuildBridge(DoCommandFlags flags, TileIndex tile_end, TileIndex t /* If bridge belonged to bankrupt company, it has a new owner now */ is_new_owner = (owner == OWNER_NONE); if (is_new_owner) owner = company; + + TileIndexDiff delta = (direction == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); + for (TileIndex tile = tile_start + delta; tile != tile_end; tile += delta) { + if (IsTileType(tile, MP_STATION)) { + switch (GetStationType(tile)) { + case StationType::Rail: + case StationType::RailWaypoint: { + CommandCost ret = IsRailStationBridgeAboveOk(tile, GetStationSpec(tile), GetStationGfx(tile), tile_start, tile_end, z_start + 1, bridge_type, transport_type); + if (ret.Failed()) { + if (ret.GetErrorMessage() != INVALID_STRING_ID) return ret; + ret = Command::Do(flags, tile); + if (ret.Failed()) return ret; + } + break; + } + + case StationType::Bus: + case StationType::Truck: + case StationType::RoadWaypoint: { + CommandCost ret = IsRoadStopBridgeAboveOK(tile, GetRoadStopSpec(tile), IsDriveThroughStopTile(tile), IsDriveThroughStopTile(tile) ? AxisToDiagDir(GetDriveThroughStopAxis(tile)) : GetBayRoadStopDir(tile), + tile_start, tile_end, z_start + 1, bridge_type, transport_type); + if (ret.Failed()) { + if (ret.GetErrorMessage() != INVALID_STRING_ID) return ret; + ret = Command::Do(flags, tile); + if (ret.Failed()) return ret; + } + break; + } + + case StationType::Buoy: + /* Buoys are always allowed */ + break; + + default: + /*if (!(GetStationType(tile) == StationType::Dock && _settings_game.construction.allow_docks_under_bridges)) { + CommandCost ret = Command::Do(flags, tile); + if (ret.Failed()) return ret; + }*/ + break; + } + } + } } else { /* Build a new bridge. */ @@ -471,6 +522,45 @@ CommandCost CmdBuildBridge(DoCommandFlags flags, TileIndex tile_end, TileIndex t break; } + case MP_STATION: { + switch (GetStationType(tile)) { + case StationType::Airport: + goto not_valid_below; + + case StationType::Rail: + case StationType::RailWaypoint: { + CommandCost ret = IsRailStationBridgeAboveOk(tile, GetStationSpec(tile), GetStationGfx(tile), tile_start, tile_end, z_start + 1, bridge_type, transport_type); + if (ret.Failed()) { + if (ret.GetErrorMessage() != INVALID_STRING_ID) return ret; + goto not_valid_below; + } + break; + } + + case StationType::Bus: + case StationType::Truck: + case StationType::RoadWaypoint: { + CommandCost ret = IsRoadStopBridgeAboveOK(tile, GetRoadStopSpec(tile), IsDriveThroughStopTile(tile), IsDriveThroughStopTile(tile) ? AxisToDiagDir(GetDriveThroughStopAxis(tile)) : GetBayRoadStopDir(tile), + tile_start, tile_end, z_start + 1, bridge_type, transport_type); + if (ret.Failed()) { + if (ret.GetErrorMessage() != INVALID_STRING_ID) return ret; + goto not_valid_below; + } + break; + } + + case StationType::Buoy: + /* Buoys are always allowed */ + break; + + default: + //if (!(GetStationType(tile) == StationType::Dock && _settings_game.construction.allow_docks_under_bridges)) goto not_valid_below; + break; + } + break; + } + + case MP_CLEAR: break; @@ -1521,6 +1611,49 @@ static BridgePieces CalcBridgePiece(uint north, uint south) } } +BridgePiecePillarFlags GetBridgeTilePillarFlags(TileIndex tile, TileIndex northern_bridge_end, TileIndex southern_bridge_end, BridgeType bridge_type, TransportType bridge_transport_type) +{ + if (bridge_transport_type == TRANSPORT_WATER) return BPPF_ALL_CORNERS; + + BridgePieces piece = CalcBridgePiece( + GetTunnelBridgeLength(tile, northern_bridge_end) + 1, + GetTunnelBridgeLength(tile, southern_bridge_end) + 1 + ); + assert(piece < BRIDGE_PIECE_HEAD); + + const BridgeSpec *spec = GetBridgeSpec(bridge_type); + const Axis axis = TileX(northern_bridge_end) == TileX(southern_bridge_end) ? AXIS_Y : AXIS_X; + if (!HasBit(spec->ctrl_flags, BSCF_INVALID_PILLAR_FLAGS)) { + return (BridgePiecePillarFlags) spec->pillar_flags[piece * 2 + (axis == AXIS_Y ? 1 : 0)]; + } else { + uint base_offset; + if (bridge_transport_type == TRANSPORT_RAIL) { + base_offset = GetRailTypeInfo(GetRailType(southern_bridge_end))->bridge_offset; + } else { + base_offset = 8; + } + + const PalSpriteID *psid = &GetBridgeSpriteTable(bridge_type, piece)[base_offset]; + if (axis == AXIS_Y) psid += 4; + return (BridgePiecePillarFlags) (psid[2].sprite != 0 ? BPPF_ALL_CORNERS : 0); + } +} + +BridgePieceDebugInfo GetBridgePieceDebugInfo(TileIndex tile) +{ + TileIndex rampnorth = GetNorthernBridgeEnd(tile); + TileIndex rampsouth = GetSouthernBridgeEnd(tile); + + BridgePieces piece = CalcBridgePiece( + GetTunnelBridgeLength(tile, rampnorth) + 1, + GetTunnelBridgeLength(tile, rampsouth) + 1 + ); + BridgePiecePillarFlags pillar_flags = GetBridgeTilePillarFlags(tile, rampnorth, rampsouth, GetBridgeType(rampnorth), GetTunnelBridgeTransportType(rampnorth)); + const Axis axis = TileX(rampnorth) == TileX(rampsouth) ? AXIS_Y : AXIS_X; + uint pillar_index = piece * 2 + (axis == AXIS_Y ? 1 : 0); + return { piece, pillar_flags, pillar_index }; +} + /** * Draw the middle bits of a bridge. * @param ti Tile information of the tile to draw it on. diff --git a/src/waypoint_cmd.cpp b/src/waypoint_cmd.cpp index 8814cfc0a7..d1e3d527b1 100644 --- a/src/waypoint_cmd.cpp +++ b/src/waypoint_cmd.cpp @@ -182,7 +182,9 @@ static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID * extern void GetStationLayout(uint8_t *layout, uint numtracks, uint plat_len, const StationSpec *statspec); extern CommandCost FindJoiningWaypoint(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Waypoint **wp, bool is_road); extern CommandCost CanExpandRailStation(const BaseStation *st, TileArea &new_ta); -extern CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlags flags, bool is_drive_through, StationType station_type, Axis axis, DiagDirection ddir, StationID *est, RoadType rt, Money unit_cost); +extern CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlags flags, bool is_drive_through, StationType station_type, const RoadStopSpec *roadstopspec, Axis axis, DiagDirection ddir, StationID *est, RoadType rt, Money unit_cost); +extern CommandCost IsRailStationBridgeAboveOk(TileIndex tile, const StationSpec *statspec, uint8_t layout); + extern CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index); /** @@ -220,6 +222,17 @@ CommandCost CmdBuildRailWaypoint(DoCommandFlags flags, TileIndex start_tile, Axi if (distant_join && (!_settings_game.station.distant_join_stations || !Waypoint::IsValidID(station_to_join))) return CMD_ERROR; + const StationSpec *spec = StationClass::Get(spec_class)->GetSpec(spec_index); + std::vector layout_ptr; + layout_ptr.resize(count); + if (spec == nullptr) { + /* The layout must be 0 for the 'normal' waypoints by design. */ + //memset(layout_ptr, 0, count); + } else { + /* But for NewGRF waypoints we like to have their style. */ + GetStationLayout(&layout_ptr[0], count, 1, spec); + } + TileArea new_location(start_tile, width, height); /* only AddCost for non-existing waypoints */ @@ -237,6 +250,10 @@ CommandCost CmdBuildRailWaypoint(DoCommandFlags flags, TileIndex start_tile, Axi TileIndex tile = start_tile + i * offset; CommandCost ret = IsValidTileForWaypoint(tile, axis, &est); if (ret.Failed()) return ret; + ret = IsRailStationBridgeAboveOk(tile, spec, layout_ptr[i]); + if (ret.Failed()) { + return ret; + } } Waypoint *wp = nullptr; @@ -364,7 +381,7 @@ CommandCost CmdBuildRoadWaypoint(DoCommandFlags flags, TileIndex start_tile, Axi unit_cost = _price[PR_BUILD_STATION_TRUCK]; } StationID est = StationID::Invalid(); - CommandCost cost = CalculateRoadStopCost(roadstop_area, flags, true, StationType::RoadWaypoint, axis, AxisToDiagDir(axis), &est, INVALID_ROADTYPE, unit_cost); + CommandCost cost = CalculateRoadStopCost(roadstop_area, flags, true, StationType::RoadWaypoint, roadstopspec, axis, AxisToDiagDir(axis), &est, INVALID_ROADTYPE, unit_cost); if (cost.Failed()) return cost; Waypoint *wp = nullptr;