diff --git a/src/lang/english.txt b/src/lang/english.txt index 15a254781b..a4d8465619 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5262,6 +5262,11 @@ 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_TOO_LOW_FOR_ROADSTOP :{WHITE}Bridge is too low for road stop +STR_ERROR_BRIDGE_TOO_LOW_FOR_BUOY :{WHITE}Bridge is too low for buoy +STR_ERROR_BRIDGE_TOO_LOW_FOR_RAIL_WAYPOINT :{WHITE}Bridge is too low for rail waypoint +STR_ERROR_BRIDGE_TOO_LOW_FOR_ROAD_WAYPOINT :{WHITE}Bridge is too low for road waypoint # Tunnel related errors STR_ERROR_CAN_T_BUILD_TUNNEL_HERE :{WHITE}Can't build tunnel here... diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index 24096c8e7a..e5c2abc93f 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -407,6 +407,7 @@ enum SaveLoadVersion : uint16_t { SLV_INDUSTRY_NUM_VALID_HISTORY, ///< 356 PR#14416 Store number of valid history records for industries. SLV_INDUSTRY_ACCEPTED_HISTORY, ///< 357 PR#14321 Add per-industry history of cargo delivered and waiting. SLV_TOWN_SUPPLY_HISTORY, ///< 358 PR#14461 Town supply history. + SLV_STATIONS_UNDER_BRIDGES, ///< 359 PR#14477 Allow stations under bridges. SL_MAX_VERSION, ///< Highest possible saveload version }; diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 5bf8a33679..ff920c72e8 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -73,6 +73,7 @@ #include "widgets/station_widget.h" #include "table/strings.h" +#include "table/station_land.h" #include @@ -865,6 +866,95 @@ static CommandCost CheckFlatLandAirport(AirportTileTableIterator tile_iter, DoCo return cost; } +/** + * Get station-type-specific string for a bridge that is too low. + * @param type Station type. + * @return bridge too low string. + */ +static StringID GetBridgeTooLowMessageForStationType(StationType type) +{ + static constexpr std::array too_low_msgs = { + STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION, // Rail + INVALID_STRING_ID, // Airport + STR_ERROR_BRIDGE_TOO_LOW_FOR_ROADSTOP, // Truck + STR_ERROR_BRIDGE_TOO_LOW_FOR_ROADSTOP, // Bus + INVALID_STRING_ID, // Oilrig + INVALID_STRING_ID, // Dock + STR_ERROR_BRIDGE_TOO_LOW_FOR_BUOY, // Buoy + STR_ERROR_BRIDGE_TOO_LOW_FOR_RAIL_WAYPOINT, // RailWaypoint + STR_ERROR_BRIDGE_TOO_LOW_FOR_ROAD_WAYPOINT, // RoadWaypoint + }; + return too_low_msgs[to_underlying(type)]; +}; + +/** + * Test if a bridge can be built above a station. + * @param tile Tile to test. + * @param spec Custom station spec to test. + * @param type Type of station. + * @param layout Layout piece of road station to test. + * @param bridge_height Height of bridge to test. + * @param disallowed_msg Error message if bridge is disallowed. + * @return Command result. + */ +static CommandCost IsStationBridgeAboveOk(TileIndex tile, std::span bridgeable_info, StationType type, StationGfx layout, int bridge_height, StringID disallowed_msg = INVALID_STRING_ID) +{ + int height = layout < std::size(bridgeable_info) ? bridgeable_info[layout].height : 0; + + if (height == 0) { + if (disallowed_msg != INVALID_STRING_ID) return CommandCost{disallowed_msg}; + /* Get normal error message associated with clearing the tile. */ + return Command::Do(DoCommandFlag::Auto, tile); + } + if (GetTileMaxZ(tile) + height > bridge_height) return CommandCost{GetBridgeTooLowMessageForStationType(type)}; + + return CommandCost{}; +} + +/** + * Get bridgeable tile information for a station type. + * @param type Station type. + * @return bridgeable tile information. + */ +static std::span GetStationBridgeableTileInfo(StationType type) +{ + return _station_bridgeable_info[to_underlying(type)]; +} + +/** + * Test if a rail station can be built below a bridge. + * @param tile Tile to test. + * @param spec Custom station spec to test. + * @param type Type of rail station. + * @param layout Layout piece of station to test. + * @return Command result. + */ +CommandCost IsRailStationBridgeAboveOk(TileIndex tile, const StationSpec *spec, StationType type, StationGfx layout) +{ + if (!IsBridgeAbove(tile)) return CommandCost(); + + TileIndex rampsouth = GetSouthernBridgeEnd(tile); + auto bridgeable_info = spec == nullptr ? GetStationBridgeableTileInfo(type) : spec->bridgeable_info; + return IsStationBridgeAboveOk(tile, bridgeable_info, type, layout, GetBridgeHeight(rampsouth), STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); +} + +/** + * Test if a road station can be built below a bridge. + * @param tile Tile to test. + * @param spec Custom roadstop spec to test. + * @param type Type of road station. + * @param layout Layout piece of station to test. + * @return Command result. + */ +CommandCost IsRoadStationBridgeAboveOk(TileIndex tile, const RoadStopSpec *spec, StationType type, StationGfx layout) +{ + if (!IsBridgeAbove(tile)) return CommandCost(); + + TileIndex rampsouth = GetSouthernBridgeEnd(tile); + auto bridgeable_info = spec == nullptr ? GetStationBridgeableTileInfo(type) : spec->bridgeable_info; + return IsStationBridgeAboveOk(tile, bridgeable_info, type, layout, GetBridgeHeight(rampsouth), STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); +} + /** * Checks if a rail station can be built at the given tile. * @param tile_cur Tile to check. @@ -889,7 +979,7 @@ static CommandCost CheckFlatLandRailStation(TileIndex tile_cur, TileIndex north_ const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index); bool slope_cb = statspec != nullptr && statspec->callback_mask.Test(StationCallbackMask::SlopeCheck); - CommandCost ret = CheckBuildableTile(tile_cur, invalid_dirs, allowed_z, false); + CommandCost ret = CheckBuildableTile(tile_cur, invalid_dirs, allowed_z, false, false); if (ret.Failed()) return ret; cost.AddCost(ret.GetCost()); @@ -954,6 +1044,7 @@ static CommandCost CheckFlatLandRailStation(TileIndex tile_cur, TileIndex north_ * 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 spec Spec of road stop to be built. * @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. @@ -963,14 +1054,17 @@ static CommandCost CheckFlatLandRailStation(TileIndex tile_cur, TileIndex north_ * @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) +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); - CommandCost ret = CheckBuildableTile(cur_tile, invalid_dirs, allowed_z, !is_drive_through); + CommandCost ret = CheckBuildableTile(cur_tile, invalid_dirs, allowed_z, !is_drive_through, false); if (ret.Failed()) return ret; cost.AddCost(ret.GetCost()); + ret = IsRoadStationBridgeAboveOk(cur_tile, spec, station_type, is_drive_through ? GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET + axis : FindFirstBit(invalid_dirs.base())); + if (ret.Failed()) return 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. */ @@ -1346,6 +1440,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,8 +1473,24 @@ CommandCost CmdBuildRailStation(DoCommandFlags flags, TileIndex tile_org, RailTy if (ret.Failed()) return ret; } - /* Check if we can allocate a custom stationspec to this station */ 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 + + RailStationTileLayout stl{statspec, numtracks, plat_len}; + auto it = stl.begin(); + TileIndex tile_track = tile_org; + for (uint i = 0; i != numtracks; ++i) { + TileIndex tile = tile_track; + for (uint j = 0; j != plat_len; ++j) { + ret = IsRailStationBridgeAboveOk(tile, statspec, StationType::Rail, *it++ + axis); + if (ret.Failed()) return ret; + tile += tile_delta; + } + tile_track += track_delta; + } + + /* Check if we can allocate a custom stationspec to this station */ auto specindex = AllocateSpecToStation(statspec, st, flags.Test(DoCommandFlag::Execute)); if (!specindex.has_value()) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS); @@ -1407,21 +1520,15 @@ 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); - RailStationTileLayout stl{statspec, numtracks, plat_len}; auto it = stl.begin(); - uint8_t numtracks_orig = numtracks; - Company *c = Company::Get(st->owner); TileIndex tile_track = tile_org; - do { + for (uint i = 0; i != numtracks; ++i) { TileIndex tile = tile_track; - int w = plat_len; - do { + for (uint j = 0; j != plat_len; ++j) { if (IsRailStationTile(tile) && HasStationReservation(tile)) { /* Check for trains having a reservation for this tile. */ Train *v = GetTrainForReservation(tile, AxisToTrack(GetRailStationAxis(tile))); @@ -1451,7 +1558,7 @@ CommandCost CmdBuildRailStation(DoCommandFlags flags, TileIndex tile_org, RailTy if (statspec != nullptr) { /* Use a fixed axis for GetPlatformInfo as our platforms / numtracks are always the right way around */ - uint32_t platinfo = GetPlatformInfo(AXIS_X, GetStationGfx(tile), plat_len, numtracks_orig, plat_len - w, numtracks_orig - numtracks, false); + uint32_t platinfo = GetPlatformInfo(AXIS_X, GetStationGfx(tile), plat_len, numtracks, j, i, false); /* As the station is not yet completely finished, the station does not yet exist. */ uint16_t callback = GetStationCallback(CBID_STATION_BUILD_TILE_LAYOUT, platinfo, 0, statspec, nullptr, tile); @@ -1473,11 +1580,11 @@ CommandCost CmdBuildRailStation(DoCommandFlags flags, TileIndex tile_org, RailTy c->infrastructure.station++; tile += tile_delta; - } while (--w); + } AddTrackToSignalBuffer(tile_track, track, _current_company); YapfNotifyTrackLayoutChange(tile_track, track); tile_track += track_delta; - } while (--numtracks); + } for (uint i = 0; i < affected_vehicles.size(); ++i) { /* Restore reservations of trains. */ @@ -1487,9 +1594,9 @@ CommandCost CmdBuildRailStation(DoCommandFlags flags, TileIndex tile_org, RailTy /* Check whether we need to expand the reservation of trains already on the station. */ TileArea update_reservation_area; if (axis == AXIS_X) { - update_reservation_area = TileArea(tile_org, 1, numtracks_orig); + update_reservation_area = TileArea(tile_org, 1, numtracks); } else { - update_reservation_area = TileArea(tile_org, numtracks_orig, 1); + update_reservation_area = TileArea(tile_org, numtracks, 1); } for (TileIndex tile : update_reservation_area) { @@ -1889,6 +1996,7 @@ static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID statio * @param flags Operation to perform. * @param is_drive_through True if trying to build a drive-through station. * @param station_type Station type (bus, truck or road waypoint). + * @param roadstopspec Spec of road stop being built. * @param axis Axis of a drive-through road stop. * @param ddir Entrance direction (#DiagDirection) for normal stops. Converted to the axis for drive-through stops. * @param station StationID to be queried and returned if available. @@ -1896,7 +2004,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) { @@ -1910,7 +2018,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); @@ -1991,7 +2099,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; @@ -2976,8 +3084,6 @@ static CommandCost RemoveDock(TileIndex tile, DoCommandFlags flags) return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_STATION_DOCK]); } -#include "table/station_land.h" - /** * Get station tile layout for a station type and its station gfx. * @param st Station type to draw. @@ -3056,6 +3162,18 @@ bool SplitGroundSpriteForOverlay(const TileInfo *ti, SpriteID *ground, RailTrack return true; } +/** + * Get blocked pillar information for a station tile. + * @param bridgeable_info + * @param layout Tile layout of rail station. + * @return blocked pillar information. + */ +static BridgePillarFlags GetStationBlockedPillars(std::span bridgeable_info, uint8_t layout) +{ + if (layout < std::size(bridgeable_info)) return bridgeable_info[layout].disallowed_pillars; + return BRIDGEPILLARFLAGS_ALL; +} + /** * Draw custom station foundations for a NewGRF station if provided. * @param statspec Custom NewGRF station. @@ -3137,6 +3255,7 @@ static void DrawTile_Station(TileInfo *ti) BaseStation *st = nullptr; const StationSpec *statspec = nullptr; uint tile_layout = 0; + auto bridgeable_info = GetStationBridgeableTileInfo(GetStationType(ti->tile)); if (HasStationRail(ti->tile)) { rti = GetRailTypeInfo(GetRailType(ti->tile)); @@ -3165,6 +3284,7 @@ static void DrawTile_Station(TileInfo *ti) } } } + if (statspec != nullptr) bridgeable_info = statspec->bridgeable_info; } else { total_offset = 0; } @@ -3355,6 +3475,7 @@ static void DrawTile_Station(TileInfo *ti) DrawGroundSprite(ground + view, PAL_NONE); } } + if (stopspec != nullptr) bridgeable_info = stopspec->bridgeable_info; if (stopspec == nullptr || !stopspec->flags.Test(RoadStopSpecFlag::NoCatenary)) { /* Draw road, tram catenary */ @@ -3368,6 +3489,7 @@ static void DrawTile_Station(TileInfo *ti) } DrawRailTileSeq(ti, t, TO_BUILDINGS, total_offset, relocation, palette); + DrawBridgeMiddle(ti, GetStationBlockedPillars(bridgeable_info, GetStationGfx(ti->tile))); } void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, RoadType roadtype, int image) @@ -5150,6 +5272,29 @@ uint FlowStatMap::GetFlowFromVia(StationID from, StationID via) const return i->second.GetShare(via); } +static CommandCost CheckBuildAbove_Station(TileIndex tile, DoCommandFlags, Axis, int height) +{ + StationType type = GetStationType(tile); + auto bridgeable_info = GetStationBridgeableTileInfo(type); + + switch (type) { + case StationType::Rail: + case StationType::RailWaypoint: + if (const StationSpec *spec = GetStationSpec(tile); spec != nullptr) bridgeable_info = spec->bridgeable_info; + break; + + case StationType::Bus: + case StationType::Truck: + case StationType::RoadWaypoint: + if (const RoadStopSpec *spec = GetRoadStopSpec(tile); spec != nullptr) bridgeable_info = spec->bridgeable_info; + break; + + default: break; + } + + return IsStationBridgeAboveOk(tile, bridgeable_info, type, GetStationGfx(tile), height); +} + extern const TileTypeProcs _tile_type_station_procs = { DrawTile_Station, // draw_tile_proc GetSlopePixelZ_Station, // get_slope_z_proc @@ -5165,5 +5310,5 @@ extern const TileTypeProcs _tile_type_station_procs = { VehicleEnter_Station, // vehicle_enter_tile_proc GetFoundation_Station, // get_foundation_proc TerraformTile_Station, // terraform_tile_proc - nullptr, // check_build_above_proc + CheckBuildAbove_Station, // check_build_above_proc }; diff --git a/src/table/station_land.h b/src/table/station_land.h index f4e4b4ac92..b33bf8ff4e 100644 --- a/src/table/station_land.h +++ b/src/table/station_land.h @@ -902,3 +902,53 @@ static const std::array, to_underlying(Stati _station_display_datas_waypoint, _station_display_datas_road_waypoint, }}; + +static const BridgeableTileInfo _station_bridgeable_info_rail[] = { + {2, {BridgePillarFlag::EdgeSW, BridgePillarFlag::EdgeNE}}, // X-axis empty platform. + {2, {BridgePillarFlag::EdgeNW, BridgePillarFlag::EdgeSE}}, // Y-axis empty platform. + {2, {BridgePillarFlag::EdgeSW, BridgePillarFlag::EdgeNE}}, // X-axis small building. + {2, {BridgePillarFlag::EdgeNW, BridgePillarFlag::EdgeSE}}, // Y-axis small building. + {5, {BridgePillarFlag::EdgeSW, BridgePillarFlag::EdgeNE, BridgePillarFlag::EdgeSE, BridgePillarFlag::CornerE, BridgePillarFlag::CornerS}}, // X large building north. + {5, {BridgePillarFlag::EdgeNW, BridgePillarFlag::EdgeSE, BridgePillarFlag::EdgeSW, BridgePillarFlag::CornerS, BridgePillarFlag::CornerW}}, // Y large building north. + {5, {BridgePillarFlag::EdgeSW, BridgePillarFlag::EdgeNE, BridgePillarFlag::EdgeNW, BridgePillarFlag::CornerN, BridgePillarFlag::CornerW}}, // X large building south. + {5, {BridgePillarFlag::EdgeNW, BridgePillarFlag::EdgeSE, BridgePillarFlag::EdgeNE, BridgePillarFlag::CornerN, BridgePillarFlag::CornerE}}, // Y large building south. +}; + +static const BridgeableTileInfo _station_bridgeable_info_waypoint[] = { + {2, {BridgePillarFlag::EdgeSW, BridgePillarFlag::EdgeNE}}, // X-axis waypoint. + {2, {BridgePillarFlag::EdgeNW, BridgePillarFlag::EdgeSE}}, // Y-axis waypoint. +}; + +static const BridgeableTileInfo _station_bridgeable_info_buoy[] = { + {1, {}}, +}; + +static const BridgeableTileInfo _station_bridgeable_info_roadstop[] = { + {2, {BridgePillarFlag::EdgeNE}}, // NE bay. + {2, {BridgePillarFlag::EdgeSE}}, // SE bay. + {2, {BridgePillarFlag::EdgeSW}}, // SW bay. + {2, {BridgePillarFlag::EdgeNW}}, // NW bay. + {2, {BridgePillarFlag::EdgeSW, BridgePillarFlag::EdgeNE}}, // X-axis drive-through. + {2, {BridgePillarFlag::EdgeNW, BridgePillarFlag::EdgeSE}}, // Y-axis drive-through. +}; + +static const BridgeableTileInfo _station_bridgeable_info_road_waypoint[] = { + {}, // NE bay (unused) + {}, // SE bay (unused) + {}, // SW bay (unused) + {}, // NW bay (unused) + {2, {BridgePillarFlag::EdgeSW, BridgePillarFlag::EdgeNE}}, // X-axis waypoint. + {2, {BridgePillarFlag::EdgeNW, BridgePillarFlag::EdgeSE}}, // Y-axis waypoint. +}; + +static const std::array, to_underlying(StationType::End)> _station_bridgeable_info = {{ + _station_bridgeable_info_rail, // Rail + {}, // Airport + _station_bridgeable_info_roadstop, // Truck + _station_bridgeable_info_roadstop, // Bus + {}, // Oilrig + {}, // Dock + _station_bridgeable_info_buoy, // Buoy + _station_bridgeable_info_waypoint, // RailWaypoint + _station_bridgeable_info_road_waypoint, // RoadWaypoint +}}; diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 92da362954..d107d8434d 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -280,7 +280,6 @@ static CommandCost CheckBuildAbove(TileIndex tile, DoCommandFlags flags, Axis ax if (_tile_type_procs[GetTileType(tile)]->check_build_above_proc != nullptr) { return _tile_type_procs[GetTileType(tile)]->check_build_above_proc(tile, flags, axis, height); } - /* A tile without a handler must be cleared. */ return Command::Do(flags, tile); } @@ -441,6 +440,14 @@ 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; + + /* Check if the new bridge is compatible with tiles underneath. */ + TileIndexDiff delta = (direction == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); + for (TileIndex tile = tile_start + delta; tile != tile_end; tile += delta) { + CommandCost ret = CheckBuildAbove(tile, flags, direction, z_start + 1); + if (ret.Failed()) return ret; + cost.AddCost(ret.GetCost()); + } } else { /* Build a new bridge. */ @@ -491,7 +498,7 @@ CommandCost CmdBuildBridge(DoCommandFlags flags, TileIndex tile_end, TileIndex t return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); } - ret = CheckBuildAbove(tile, flags, direction, z_start); + ret = CheckBuildAbove(tile, flags, direction, z_start + 1); if (ret.Failed()) return ret; cost.AddCost(ret.GetCost()); diff --git a/src/waypoint_cmd.cpp b/src/waypoint_cmd.cpp index a26b82a0c6..25b3a5383a 100644 --- a/src/waypoint_cmd.cpp +++ b/src/waypoint_cmd.cpp @@ -175,14 +175,14 @@ static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID * return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED); } - if (IsBridgeAbove(tile)) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); - return CommandCost(); } 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 *spec, StationType type, StationGfx layout); + extern CommandCost RemoveRoadWaypointStop(TileIndex tile, DoCommandFlags flags, int replacement_spec_index); /** @@ -231,12 +231,19 @@ CommandCost CmdBuildRailWaypoint(DoCommandFlags flags, TileIndex start_tile, Axi /* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */ StationID est = StationID::Invalid(); + const StationSpec *spec = StationClass::Get(spec_class)->GetSpec(spec_index); + RailStationTileLayout stl{spec, count, 1}; + auto it = stl.begin(); + /* Check whether the tiles we're building on are valid rail or not. */ TileIndexDiff offset = TileOffsByAxis(OtherAxis(axis)); for (int i = 0; i < count; i++) { TileIndex tile = start_tile + i * offset; CommandCost ret = IsValidTileForWaypoint(tile, axis, &est); if (ret.Failed()) return ret; + + ret = IsRailStationBridgeAboveOk(tile, spec, StationType::RailWaypoint, *it++); + if (ret.Failed()) return ret; } Waypoint *wp = nullptr; @@ -264,7 +271,6 @@ CommandCost CmdBuildRailWaypoint(DoCommandFlags flags, TileIndex start_tile, Axi if (!Waypoint::CanAllocateItem()) return CommandCost(STR_ERROR_TOO_MANY_STATIONS_LOADING); } - const StationSpec *spec = StationClass::Get(spec_class)->GetSpec(spec_index); auto specindex = AllocateSpecToStation(spec, wp, flags.Test(DoCommandFlag::Execute)); if (!specindex.has_value()) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS); @@ -289,7 +295,6 @@ CommandCost CmdBuildRailWaypoint(DoCommandFlags flags, TileIndex start_tile, Axi wp->UpdateVirtCoord(); - RailStationTileLayout stl{spec, count, 1}; auto it = stl.begin(); Company *c = Company::Get(wp->owner); @@ -363,7 +368,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;