mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-08-22 22:19:08 +00:00
Feature: Allow stations and roadstops under bridges.
Bridges above stations will have pillars excluded if they conflict with the station layout. Partly based on the system implemented in JGRPP. Co-authored-by: <su@angel-island.zone>
This commit is contained in:
@@ -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_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_TOO_LONG :{WHITE}... bridge too long
|
||||||
STR_ERROR_BRIDGE_THROUGH_MAP_BORDER :{WHITE}Bridge would end out of the map
|
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
|
# Tunnel related errors
|
||||||
STR_ERROR_CAN_T_BUILD_TUNNEL_HERE :{WHITE}Can't build tunnel here...
|
STR_ERROR_CAN_T_BUILD_TUNNEL_HERE :{WHITE}Can't build tunnel here...
|
||||||
|
@@ -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_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_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_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
|
SL_MAX_VERSION, ///< Highest possible saveload version
|
||||||
};
|
};
|
||||||
|
@@ -73,6 +73,7 @@
|
|||||||
#include "widgets/station_widget.h"
|
#include "widgets/station_widget.h"
|
||||||
|
|
||||||
#include "table/strings.h"
|
#include "table/strings.h"
|
||||||
|
#include "table/station_land.h"
|
||||||
|
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
|
|
||||||
@@ -865,6 +866,95 @@ static CommandCost CheckFlatLandAirport(AirportTileTableIterator tile_iter, DoCo
|
|||||||
return cost;
|
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<StringID, to_underlying(StationType::End)> 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<const BridgeableTileInfo> 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<CMD_LANDSCAPE_CLEAR>::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<const BridgeableTileInfo> 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.
|
* Checks if a rail station can be built at the given tile.
|
||||||
* @param tile_cur Tile to check.
|
* @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);
|
const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index);
|
||||||
bool slope_cb = statspec != nullptr && statspec->callback_mask.Test(StationCallbackMask::SlopeCheck);
|
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;
|
if (ret.Failed()) return ret;
|
||||||
cost.AddCost(ret.GetCost());
|
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.
|
* Checks if a road stop can be built at the given tile.
|
||||||
* @param cur_tile Tile to check.
|
* @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 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 flags Operation to perform.
|
||||||
* @param invalid_dirs Prohibited directions (set of DiagDirections).
|
* @param invalid_dirs Prohibited directions (set of DiagDirections).
|
||||||
* @param is_drive_through True if trying to build a drive-through station.
|
* @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.
|
* @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.
|
* @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 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;
|
if (ret.Failed()) return ret;
|
||||||
cost.AddCost(ret.GetCost());
|
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.
|
/* 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.
|
* 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. */
|
* 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;
|
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);
|
bool reuse = (station_to_join != NEW_STATION);
|
||||||
if (!reuse) station_to_join = StationID::Invalid();
|
if (!reuse) station_to_join = StationID::Invalid();
|
||||||
bool distant_join = (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;
|
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);
|
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));
|
auto specindex = AllocateSpecToStation(statspec, st, flags.Test(DoCommandFlag::Execute));
|
||||||
if (!specindex.has_value()) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS);
|
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);
|
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);
|
Track track = AxisToTrack(axis);
|
||||||
|
|
||||||
RailStationTileLayout stl{statspec, numtracks, plat_len};
|
|
||||||
auto it = stl.begin();
|
auto it = stl.begin();
|
||||||
|
|
||||||
uint8_t numtracks_orig = numtracks;
|
|
||||||
|
|
||||||
Company *c = Company::Get(st->owner);
|
Company *c = Company::Get(st->owner);
|
||||||
TileIndex tile_track = tile_org;
|
TileIndex tile_track = tile_org;
|
||||||
do {
|
for (uint i = 0; i != numtracks; ++i) {
|
||||||
TileIndex tile = tile_track;
|
TileIndex tile = tile_track;
|
||||||
int w = plat_len;
|
for (uint j = 0; j != plat_len; ++j) {
|
||||||
do {
|
|
||||||
if (IsRailStationTile(tile) && HasStationReservation(tile)) {
|
if (IsRailStationTile(tile) && HasStationReservation(tile)) {
|
||||||
/* Check for trains having a reservation for this tile. */
|
/* Check for trains having a reservation for this tile. */
|
||||||
Train *v = GetTrainForReservation(tile, AxisToTrack(GetRailStationAxis(tile)));
|
Train *v = GetTrainForReservation(tile, AxisToTrack(GetRailStationAxis(tile)));
|
||||||
@@ -1451,7 +1558,7 @@ CommandCost CmdBuildRailStation(DoCommandFlags flags, TileIndex tile_org, RailTy
|
|||||||
|
|
||||||
if (statspec != nullptr) {
|
if (statspec != nullptr) {
|
||||||
/* Use a fixed axis for GetPlatformInfo as our platforms / numtracks are always the right way around */
|
/* 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. */
|
/* 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);
|
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++;
|
c->infrastructure.station++;
|
||||||
|
|
||||||
tile += tile_delta;
|
tile += tile_delta;
|
||||||
} while (--w);
|
}
|
||||||
AddTrackToSignalBuffer(tile_track, track, _current_company);
|
AddTrackToSignalBuffer(tile_track, track, _current_company);
|
||||||
YapfNotifyTrackLayoutChange(tile_track, track);
|
YapfNotifyTrackLayoutChange(tile_track, track);
|
||||||
tile_track += track_delta;
|
tile_track += track_delta;
|
||||||
} while (--numtracks);
|
}
|
||||||
|
|
||||||
for (uint i = 0; i < affected_vehicles.size(); ++i) {
|
for (uint i = 0; i < affected_vehicles.size(); ++i) {
|
||||||
/* Restore reservations of trains. */
|
/* 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. */
|
/* Check whether we need to expand the reservation of trains already on the station. */
|
||||||
TileArea update_reservation_area;
|
TileArea update_reservation_area;
|
||||||
if (axis == AXIS_X) {
|
if (axis == AXIS_X) {
|
||||||
update_reservation_area = TileArea(tile_org, 1, numtracks_orig);
|
update_reservation_area = TileArea(tile_org, 1, numtracks);
|
||||||
} else {
|
} else {
|
||||||
update_reservation_area = TileArea(tile_org, numtracks_orig, 1);
|
update_reservation_area = TileArea(tile_org, numtracks, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (TileIndex tile : update_reservation_area) {
|
for (TileIndex tile : update_reservation_area) {
|
||||||
@@ -1889,6 +1996,7 @@ static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID statio
|
|||||||
* @param flags Operation to perform.
|
* @param flags Operation to perform.
|
||||||
* @param is_drive_through True if trying to build a drive-through station.
|
* @param is_drive_through True if trying to build a drive-through station.
|
||||||
* @param station_type Station type (bus, truck or road waypoint).
|
* @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 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 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.
|
* @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.
|
* @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.
|
* @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{};
|
DiagDirections invalid_dirs{};
|
||||||
if (is_drive_through) {
|
if (is_drive_through) {
|
||||||
@@ -1910,7 +2018,7 @@ CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlags flags, bool
|
|||||||
int allowed_z = -1;
|
int allowed_z = -1;
|
||||||
CommandCost cost(EXPENSES_CONSTRUCTION);
|
CommandCost cost(EXPENSES_CONSTRUCTION);
|
||||||
for (TileIndex cur_tile : tile_area) {
|
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;
|
if (ret.Failed()) return ret;
|
||||||
|
|
||||||
bool is_preexisting_roadstop = IsTileType(cur_tile, MP_STATION) && IsAnyRoadStop(cur_tile);
|
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];
|
unit_cost = _price[is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS];
|
||||||
}
|
}
|
||||||
StationID est = StationID::Invalid();
|
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;
|
if (cost.Failed()) return cost;
|
||||||
|
|
||||||
Station *st = nullptr;
|
Station *st = nullptr;
|
||||||
@@ -2976,8 +3084,6 @@ static CommandCost RemoveDock(TileIndex tile, DoCommandFlags flags)
|
|||||||
return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_STATION_DOCK]);
|
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.
|
* Get station tile layout for a station type and its station gfx.
|
||||||
* @param st Station type to draw.
|
* @param st Station type to draw.
|
||||||
@@ -3056,6 +3162,18 @@ bool SplitGroundSpriteForOverlay(const TileInfo *ti, SpriteID *ground, RailTrack
|
|||||||
return true;
|
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<const BridgeableTileInfo> 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.
|
* Draw custom station foundations for a NewGRF station if provided.
|
||||||
* @param statspec Custom NewGRF station.
|
* @param statspec Custom NewGRF station.
|
||||||
@@ -3137,6 +3255,7 @@ static void DrawTile_Station(TileInfo *ti)
|
|||||||
BaseStation *st = nullptr;
|
BaseStation *st = nullptr;
|
||||||
const StationSpec *statspec = nullptr;
|
const StationSpec *statspec = nullptr;
|
||||||
uint tile_layout = 0;
|
uint tile_layout = 0;
|
||||||
|
auto bridgeable_info = GetStationBridgeableTileInfo(GetStationType(ti->tile));
|
||||||
|
|
||||||
if (HasStationRail(ti->tile)) {
|
if (HasStationRail(ti->tile)) {
|
||||||
rti = GetRailTypeInfo(GetRailType(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 {
|
} else {
|
||||||
total_offset = 0;
|
total_offset = 0;
|
||||||
}
|
}
|
||||||
@@ -3355,6 +3475,7 @@ static void DrawTile_Station(TileInfo *ti)
|
|||||||
DrawGroundSprite(ground + view, PAL_NONE);
|
DrawGroundSprite(ground + view, PAL_NONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (stopspec != nullptr) bridgeable_info = stopspec->bridgeable_info;
|
||||||
|
|
||||||
if (stopspec == nullptr || !stopspec->flags.Test(RoadStopSpecFlag::NoCatenary)) {
|
if (stopspec == nullptr || !stopspec->flags.Test(RoadStopSpecFlag::NoCatenary)) {
|
||||||
/* Draw road, tram catenary */
|
/* Draw road, tram catenary */
|
||||||
@@ -3368,6 +3489,7 @@ static void DrawTile_Station(TileInfo *ti)
|
|||||||
}
|
}
|
||||||
|
|
||||||
DrawRailTileSeq(ti, t, TO_BUILDINGS, total_offset, relocation, palette);
|
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)
|
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);
|
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 = {
|
extern const TileTypeProcs _tile_type_station_procs = {
|
||||||
DrawTile_Station, // draw_tile_proc
|
DrawTile_Station, // draw_tile_proc
|
||||||
GetSlopePixelZ_Station, // get_slope_z_proc
|
GetSlopePixelZ_Station, // get_slope_z_proc
|
||||||
@@ -5165,5 +5310,5 @@ extern const TileTypeProcs _tile_type_station_procs = {
|
|||||||
VehicleEnter_Station, // vehicle_enter_tile_proc
|
VehicleEnter_Station, // vehicle_enter_tile_proc
|
||||||
GetFoundation_Station, // get_foundation_proc
|
GetFoundation_Station, // get_foundation_proc
|
||||||
TerraformTile_Station, // terraform_tile_proc
|
TerraformTile_Station, // terraform_tile_proc
|
||||||
nullptr, // check_build_above_proc
|
CheckBuildAbove_Station, // check_build_above_proc
|
||||||
};
|
};
|
||||||
|
@@ -902,3 +902,53 @@ static const std::array<std::span<const DrawTileSpriteSpan>, to_underlying(Stati
|
|||||||
_station_display_datas_waypoint,
|
_station_display_datas_waypoint,
|
||||||
_station_display_datas_road_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<std::span<const BridgeableTileInfo>, 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
|
||||||
|
}};
|
||||||
|
@@ -280,7 +280,6 @@ static CommandCost CheckBuildAbove(TileIndex tile, DoCommandFlags flags, Axis ax
|
|||||||
if (_tile_type_procs[GetTileType(tile)]->check_build_above_proc != nullptr) {
|
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);
|
return _tile_type_procs[GetTileType(tile)]->check_build_above_proc(tile, flags, axis, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* A tile without a handler must be cleared. */
|
/* A tile without a handler must be cleared. */
|
||||||
return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
|
return Command<CMD_LANDSCAPE_CLEAR>::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 */
|
/* If bridge belonged to bankrupt company, it has a new owner now */
|
||||||
is_new_owner = (owner == OWNER_NONE);
|
is_new_owner = (owner == OWNER_NONE);
|
||||||
if (is_new_owner) owner = company;
|
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 {
|
} else {
|
||||||
/* Build a new bridge. */
|
/* 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);
|
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;
|
if (ret.Failed()) return ret;
|
||||||
cost.AddCost(ret.GetCost());
|
cost.AddCost(ret.GetCost());
|
||||||
|
|
||||||
|
@@ -175,14 +175,14 @@ static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID *
|
|||||||
return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
|
return CommandCost(STR_ERROR_FLAT_LAND_REQUIRED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsBridgeAbove(tile)) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
|
|
||||||
|
|
||||||
return CommandCost();
|
return CommandCost();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern CommandCost FindJoiningWaypoint(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Waypoint **wp, bool is_road);
|
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 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);
|
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) */
|
/* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */
|
||||||
StationID est = StationID::Invalid();
|
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. */
|
/* Check whether the tiles we're building on are valid rail or not. */
|
||||||
TileIndexDiff offset = TileOffsByAxis(OtherAxis(axis));
|
TileIndexDiff offset = TileOffsByAxis(OtherAxis(axis));
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
TileIndex tile = start_tile + i * offset;
|
TileIndex tile = start_tile + i * offset;
|
||||||
CommandCost ret = IsValidTileForWaypoint(tile, axis, &est);
|
CommandCost ret = IsValidTileForWaypoint(tile, axis, &est);
|
||||||
if (ret.Failed()) return ret;
|
if (ret.Failed()) return ret;
|
||||||
|
|
||||||
|
ret = IsRailStationBridgeAboveOk(tile, spec, StationType::RailWaypoint, *it++);
|
||||||
|
if (ret.Failed()) return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
Waypoint *wp = nullptr;
|
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);
|
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));
|
auto specindex = AllocateSpecToStation(spec, wp, flags.Test(DoCommandFlag::Execute));
|
||||||
if (!specindex.has_value()) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS);
|
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();
|
wp->UpdateVirtCoord();
|
||||||
|
|
||||||
RailStationTileLayout stl{spec, count, 1};
|
|
||||||
auto it = stl.begin();
|
auto it = stl.begin();
|
||||||
|
|
||||||
Company *c = Company::Get(wp->owner);
|
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];
|
unit_cost = _price[PR_BUILD_STATION_TRUCK];
|
||||||
}
|
}
|
||||||
StationID est = StationID::Invalid();
|
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;
|
if (cost.Failed()) return cost;
|
||||||
|
|
||||||
Waypoint *wp = nullptr;
|
Waypoint *wp = nullptr;
|
||||||
|
Reference in New Issue
Block a user