mirror of https://github.com/OpenTTD/OpenTTD
Feature: Allow stations under bridges.
Bridges above stations will have pillars excluded if they conflict with the station layout. Based on the system implemented in JGRPP. Co-authored-by: <peter1138@openttd.org>pull/14477/head
parent
00631729a4
commit
065f88eec8
|
@ -59,7 +59,7 @@ inline const BridgeSpec *GetBridgeSpec(BridgeType i)
|
|||
return &_bridge[i];
|
||||
}
|
||||
|
||||
void DrawBridgeMiddle(const TileInfo *ti);
|
||||
void DrawBridgeMiddle(const TileInfo *ti, BridgePillarFlags blocked_pillars);
|
||||
|
||||
CommandCost CheckBridgeAvailability(BridgeType bridge_type, uint bridge_len, DoCommandFlags flags = {});
|
||||
int CalcBridgeLenCostFactor(int x);
|
||||
|
|
|
@ -383,9 +383,9 @@ void ShowBuildBridgeWindow(TileIndex start, TileIndex end, TransportType transpo
|
|||
CommandCost ret = Command<CMD_BUILD_BRIDGE>::Do(CommandFlagsToDCFlags(GetCommandFlags<CMD_BUILD_BRIDGE>()) | DoCommandFlag::QueryCost, end, start, transport_type, 0, road_rail_type);
|
||||
|
||||
GUIBridgeList bl;
|
||||
if (ret.Failed()) {
|
||||
errmsg = ret.GetErrorMessage();
|
||||
} else {
|
||||
if (ret.Failed()) errmsg = ret.GetErrorMessage();
|
||||
bool query_per_bridge_type = errmsg == STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION;
|
||||
if (ret.Succeeded() || query_per_bridge_type) {
|
||||
/* check which bridges can be built */
|
||||
const uint tot_bridgedata_len = CalcBridgeLenCostFactor(bridge_len + 2);
|
||||
|
||||
|
@ -415,11 +415,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<CMD_BUILD_BRIDGE>::Do(CommandFlagsToDCFlags(GetCommandFlags<CMD_BUILD_BRIDGE>()) | 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,13 +430,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)
|
||||
{
|
||||
errmsg = type_check.GetErrorMessage();
|
||||
}
|
||||
if (!any_available && type_errmsg != INVALID_STRING_ID) errmsg = type_errmsg;
|
||||
}
|
||||
|
||||
if (!bl.empty()) {
|
||||
|
|
|
@ -152,7 +152,7 @@ static void DrawTile_Clear(TileInfo *ti)
|
|||
break;
|
||||
}
|
||||
|
||||
DrawBridgeMiddle(ti);
|
||||
DrawBridgeMiddle(ti, {});
|
||||
}
|
||||
|
||||
static int GetSlopePixelZ_Clear(TileIndex tile, uint x, uint y, bool)
|
||||
|
|
|
@ -5244,6 +5244,7 @@ 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
|
||||
|
||||
# Tunnel related errors
|
||||
STR_ERROR_CAN_T_BUILD_TUNNEL_HERE :{WHITE}Can't build tunnel here...
|
||||
|
|
|
@ -483,7 +483,7 @@ static void DrawTile_Object(TileInfo *ti)
|
|||
DrawNewObjectTile(ti, spec);
|
||||
}
|
||||
|
||||
DrawBridgeMiddle(ti);
|
||||
DrawBridgeMiddle(ti, {});
|
||||
}
|
||||
|
||||
static int GetSlopePixelZ_Object(TileIndex tile, uint x, uint y, bool)
|
||||
|
|
|
@ -2525,7 +2525,7 @@ static void DrawTile_Track(TileInfo *ti)
|
|||
|
||||
DrawRailTileSeq(ti, dts, TO_BUILDINGS, relocation, 0, _drawtile_track_palette);
|
||||
}
|
||||
DrawBridgeMiddle(ti);
|
||||
DrawBridgeMiddle(ti, {});
|
||||
}
|
||||
|
||||
void DrawTrainDepotSprite(int x, int y, int dir, RailType railtype)
|
||||
|
|
|
@ -1858,7 +1858,7 @@ static void DrawTile_Road(TileInfo *ti)
|
|||
break;
|
||||
}
|
||||
}
|
||||
DrawBridgeMiddle(ti);
|
||||
DrawBridgeMiddle(ti, {});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -406,6 +406,7 @@ enum SaveLoadVersion : uint16_t {
|
|||
SLV_FACE_STYLES, ///< 355 PR#14319 Addition of face styles, replacing gender and ethnicity.
|
||||
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_STATIONS_UNDER_BRIDGES, ///< 358 PR#14477 Allow stations under bridges.
|
||||
|
||||
SL_MAX_VERSION, ///< Highest possible saveload version
|
||||
};
|
||||
|
|
|
@ -77,6 +77,8 @@
|
|||
|
||||
#include "safeguards.h"
|
||||
|
||||
static StationSpec::TileFlags GetStationTileFlags(StationGfx gfx, const StationSpec *statspec);
|
||||
|
||||
/**
|
||||
* Static instance of FlowStat::SharesMap.
|
||||
* Note: This instance is created on task start.
|
||||
|
@ -864,6 +866,29 @@ static CommandCost CheckFlatLandAirport(AirportTileTableIterator tile_iter, DoCo
|
|||
return cost;
|
||||
}
|
||||
|
||||
static CommandCost IsRailStationBridgeAboveOk(TileIndex tile, const StationSpec *statspec, uint8_t layout, int bridge_height)
|
||||
{
|
||||
if (statspec == nullptr) {
|
||||
/* Default stations/waypoints */
|
||||
int height_above = layout < 4 ? 2 : 5;
|
||||
if (GetTileMaxZ(tile) + height_above > bridge_height) return CommandCost(STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION);
|
||||
} else {
|
||||
int height_above = layout < std::size(statspec->tilespecs) ? statspec->tilespecs[layout].height : 0;
|
||||
if (height_above == 0) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
|
||||
if (GetTileMaxZ(tile) + height_above > bridge_height) return CommandCost(STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION);
|
||||
}
|
||||
|
||||
return CommandCost();
|
||||
}
|
||||
|
||||
CommandCost IsRailStationBridgeAboveOk(TileIndex tile, const StationSpec *statspec, uint8_t layout)
|
||||
{
|
||||
if (!IsBridgeAbove(tile)) return CommandCost();
|
||||
|
||||
TileIndex rampsouth = GetSouthernBridgeEnd(tile);
|
||||
return IsRailStationBridgeAboveOk(tile, statspec, layout, GetBridgeHeight(rampsouth));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a rail station can be built at the given tile.
|
||||
* @param tile_cur Tile to check.
|
||||
|
@ -888,7 +913,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());
|
||||
|
||||
|
@ -949,6 +974,33 @@ static CommandCost CheckFlatLandRailStation(TileIndex tile_cur, TileIndex north_
|
|||
return cost;
|
||||
}
|
||||
|
||||
static CommandCost IsRoadStopBridgeAboveOk(TileIndex tile, const RoadStopSpec *spec, bool drive_through, DiagDirection entrance, int bridge_height)
|
||||
{
|
||||
uint layout = drive_through ? (GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET + DiagDirToAxis(entrance)) : entrance;
|
||||
|
||||
if (spec == nullptr) {
|
||||
if (GetTileMaxZ(tile) + (drive_through ? 1 : 2) > bridge_height) {
|
||||
return CommandCost(STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION);
|
||||
}
|
||||
} else {
|
||||
int height = spec->tilespecs[layout].height;
|
||||
if (height == 0) return CommandCost(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
|
||||
if (GetTileMaxZ(tile) + height > bridge_height) {
|
||||
return CommandCost(STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION);
|
||||
}
|
||||
}
|
||||
|
||||
return CommandCost();
|
||||
}
|
||||
|
||||
static CommandCost IsRoadStopBridgeAboveOk(TileIndex tile, const RoadStopSpec *spec, bool drive_through, DiagDirection entrance)
|
||||
{
|
||||
if (!IsBridgeAbove(tile)) return CommandCost();
|
||||
|
||||
TileIndex rampsouth = GetSouthernBridgeEnd(tile);
|
||||
return IsRoadStopBridgeAboveOk(tile, spec, drive_through, entrance, GetBridgeHeight(rampsouth));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a road stop can be built at the given tile.
|
||||
* @param cur_tile Tile to check.
|
||||
|
@ -962,14 +1014,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 = IsRoadStopBridgeAboveOk(cur_tile, spec, is_drive_through, DiagDirection{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. */
|
||||
|
@ -1362,6 +1417,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());
|
||||
|
@ -1392,8 +1450,29 @@ 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
|
||||
|
||||
std::vector<uint8_t> layouts(numtracks * plat_len);
|
||||
GetStationLayout(layouts.data(), numtracks, plat_len, statspec);
|
||||
|
||||
{
|
||||
auto it = std::begin(layouts);
|
||||
TileIndex tile_track = tile_org;
|
||||
for (uint i = 0; i < numtracks; i++) {
|
||||
TileIndex tile = tile_track;
|
||||
for (uint j = 0; j < plat_len; j++) {
|
||||
uint8_t layout = ((*it++) & ~1) + axis; // Adjust layout piece to match axis.
|
||||
ret = IsRailStationBridgeAboveOk(tile, statspec, layout);
|
||||
if (ret.Failed()) return ret;
|
||||
tile += tile_delta;
|
||||
}
|
||||
tile_track += track_delta;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if we can allocate a custom stationspec to this station */
|
||||
int specindex = AllocateSpecToStation(statspec, st, flags.Test(DoCommandFlag::Execute));
|
||||
if (specindex == -1) return CommandCost(STR_ERROR_TOO_MANY_STATION_SPECS);
|
||||
|
||||
|
@ -1423,13 +1502,7 @@ 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<uint8_t> layouts(numtracks * plat_len);
|
||||
GetStationLayout(layouts.data(), numtracks, plat_len, statspec);
|
||||
|
||||
uint8_t numtracks_orig = numtracks;
|
||||
|
||||
Company *c = Company::Get(st->owner);
|
||||
|
@ -1440,6 +1513,7 @@ CommandCost CmdBuildRailStation(DoCommandFlags flags, TileIndex tile_org, RailTy
|
|||
int w = plat_len;
|
||||
do {
|
||||
uint8_t layout = layouts[layout_idx++];
|
||||
|
||||
if (IsRailStationTile(tile) && HasStationReservation(tile)) {
|
||||
/* Check for trains having a reservation for this tile. */
|
||||
Train *v = GetTrainForReservation(tile, AxisToTrack(GetRailStationAxis(tile)));
|
||||
|
@ -1913,7 +1987,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 +2001,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 +2082,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;
|
||||
|
@ -3073,6 +3147,51 @@ bool SplitGroundSpriteForOverlay(const TileInfo *ti, SpriteID *ground, RailTrack
|
|||
return true;
|
||||
}
|
||||
|
||||
static BridgePillarFlags GetRailStationBlockedPillars(const StationSpec *statspec, uint8_t layout)
|
||||
{
|
||||
static const BridgePillarFlags default_pillar_flags[] = {
|
||||
{BridgePillarFlag::EdgeSW, BridgePillarFlag::EdgeNE}, // X empty
|
||||
{BridgePillarFlag::EdgeNW, BridgePillarFlag::EdgeSE}, // Y empty
|
||||
{BridgePillarFlag::EdgeSW, BridgePillarFlag::EdgeNE}, // X small
|
||||
{BridgePillarFlag::EdgeNW, BridgePillarFlag::EdgeSE}, // Y small
|
||||
{BridgePillarFlag::EdgeSW, BridgePillarFlag::EdgeNE, BridgePillarFlag::EdgeSE, BridgePillarFlag::CornerE, BridgePillarFlag::CornerS}, // X large
|
||||
{BridgePillarFlag::EdgeNW, BridgePillarFlag::EdgeSE, BridgePillarFlag::EdgeSW, BridgePillarFlag::CornerS, BridgePillarFlag::CornerW}, // Y large
|
||||
{BridgePillarFlag::EdgeSW, BridgePillarFlag::EdgeNE, BridgePillarFlag::EdgeNW, BridgePillarFlag::CornerN, BridgePillarFlag::CornerW}, // X large
|
||||
{BridgePillarFlag::EdgeNW, BridgePillarFlag::EdgeSE, BridgePillarFlag::EdgeNE, BridgePillarFlag::CornerN, BridgePillarFlag::CornerE}, // Y large
|
||||
};
|
||||
|
||||
if (statspec == nullptr) {
|
||||
/* Default stations/waypoints */
|
||||
if (layout < 8) return default_pillar_flags[layout];
|
||||
return {};
|
||||
}
|
||||
if (layout < std::size(statspec->tilespecs) && statspec->tilespecs[layout].disallowed_pillars != BRIDGEPILLARFLAGS_ALL) {
|
||||
/* Pllar flags set by NewGRF */
|
||||
return statspec->tilespecs[layout].disallowed_pillars;
|
||||
}
|
||||
if (GetStationTileFlags(layout, statspec).Test(StationSpec::TileFlag::Blocked)) {
|
||||
/* Blocked station tile. */
|
||||
return {};
|
||||
}
|
||||
|
||||
/* Non-blocked station tile. */
|
||||
return default_pillar_flags[layout % 2];
|
||||
}
|
||||
|
||||
static BridgePillarFlags GetRoadStopBlockedPillars(const RoadStopSpec *spec, bool drive_through, uint8_t layout)
|
||||
{
|
||||
static constexpr BridgePillarFlags default_pillar_flags_x{BridgePillarFlag::EdgeSW, BridgePillarFlag::EdgeNE};
|
||||
static constexpr BridgePillarFlags default_pillar_flags_y{BridgePillarFlag::EdgeNW, BridgePillarFlag::EdgeSE};
|
||||
|
||||
if (spec != nullptr && spec->tilespecs[layout].disallowed_pillars != BRIDGEPILLARFLAGS_ALL) {
|
||||
return spec->tilespecs[layout].disallowed_pillars;
|
||||
}
|
||||
if (drive_through) {
|
||||
return HasBit(layout, 0) ? default_pillar_flags_y : default_pillar_flags_x;
|
||||
}
|
||||
return {BridgePillarFlag::EdgeNE + (DiagDirection)layout};
|
||||
}
|
||||
|
||||
static void DrawTile_Station(TileInfo *ti)
|
||||
{
|
||||
const NewGRFSpriteLayout *layout = nullptr;
|
||||
|
@ -3086,6 +3205,7 @@ static void DrawTile_Station(TileInfo *ti)
|
|||
BaseStation *st = nullptr;
|
||||
const StationSpec *statspec = nullptr;
|
||||
uint tile_layout = 0;
|
||||
BridgePillarFlags blocked_pillars = {};
|
||||
|
||||
if (HasStationRail(ti->tile)) {
|
||||
rti = GetRailTypeInfo(GetRailType(ti->tile));
|
||||
|
@ -3114,6 +3234,7 @@ static void DrawTile_Station(TileInfo *ti)
|
|||
}
|
||||
}
|
||||
}
|
||||
blocked_pillars = GetRailStationBlockedPillars(statspec, GetStationGfx(ti->tile));
|
||||
} else {
|
||||
total_offset = 0;
|
||||
}
|
||||
|
@ -3358,6 +3479,7 @@ draw_default_foundation:
|
|||
uint sprite_offset = GetDriveThroughStopAxis(ti->tile) == AXIS_X ? 1 : 0;
|
||||
DrawRoadOverlays(ti, PAL_NONE, road_rti, tram_rti, sprite_offset, sprite_offset);
|
||||
}
|
||||
blocked_pillars = GetRoadStopBlockedPillars(stopspec, true, view);
|
||||
} else {
|
||||
/* Non-drivethrough road stops are only valid for roads. */
|
||||
assert(road_rt != INVALID_ROADTYPE && tram_rt == INVALID_ROADTYPE);
|
||||
|
@ -3366,6 +3488,7 @@ draw_default_foundation:
|
|||
SpriteID ground = GetCustomRoadSprite(road_rti, ti->tile, ROTSG_ROADSTOP);
|
||||
DrawGroundSprite(ground + view, PAL_NONE);
|
||||
}
|
||||
blocked_pillars = GetRoadStopBlockedPillars(stopspec, false, view);
|
||||
}
|
||||
|
||||
if (stopspec == nullptr || !stopspec->flags.Test(RoadStopSpecFlag::NoCatenary)) {
|
||||
|
@ -3380,6 +3503,7 @@ draw_default_foundation:
|
|||
}
|
||||
|
||||
DrawRailTileSeq(ti, t, TO_BUILDINGS, total_offset, relocation, palette);
|
||||
DrawBridgeMiddle(ti, blocked_pillars);
|
||||
}
|
||||
|
||||
void StationPickerDrawSprite(int x, int y, StationType st, RailType railtype, RoadType roadtype, int image)
|
||||
|
@ -5162,6 +5286,40 @@ uint FlowStatMap::GetFlowFromVia(StationID from, StationID via) const
|
|||
return i->second.GetShare(via);
|
||||
}
|
||||
|
||||
static CommandCost CheckBuildAbove_Station(TileIndex tile, DoCommandFlags flags, Axis, int height)
|
||||
{
|
||||
switch (GetStationType(tile)) {
|
||||
case StationType::Rail:
|
||||
case StationType::RailWaypoint: {
|
||||
CommandCost ret = IsRailStationBridgeAboveOk(tile, GetStationSpec(tile), GetStationGfx(tile), height + 1);
|
||||
if (ret.Failed()) {
|
||||
if (ret.GetErrorMessage() != INVALID_STRING_ID) return ret;
|
||||
break;
|
||||
}
|
||||
return CommandCost();
|
||||
}
|
||||
|
||||
case StationType::Bus:
|
||||
case StationType::Truck:
|
||||
case StationType::RoadWaypoint: {
|
||||
CommandCost ret = IsRoadStopBridgeAboveOk(tile, GetRoadStopSpec(tile), IsDriveThroughStopTile(tile),
|
||||
IsDriveThroughStopTile(tile) ? AxisToDiagDir(GetDriveThroughStopAxis(tile)) : GetBayRoadStopDir(tile), height + 1);
|
||||
if (ret.Failed()) {
|
||||
if (ret.GetErrorMessage() != INVALID_STRING_ID) return ret;
|
||||
break;
|
||||
}
|
||||
return CommandCost();
|
||||
}
|
||||
|
||||
case StationType::Buoy:
|
||||
return CommandCost();
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
|
||||
}
|
||||
|
||||
extern const TileTypeProcs _tile_type_station_procs = {
|
||||
DrawTile_Station, // draw_tile_proc
|
||||
GetSlopePixelZ_Station, // get_slope_z_proc
|
||||
|
@ -5177,5 +5335,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
|
||||
};
|
||||
|
|
|
@ -753,7 +753,7 @@ static constexpr BridgePillarFlags BRIDGEPILLARFLAGS_ALL_CORNERS = {
|
|||
};
|
||||
|
||||
/** Pillar flags for bridges which have pillars on the all corners on each piece. */
|
||||
static constexpr std::array<std::array<BridgePillarFlags, AXIS_END>, NUM_BRIDGE_PIECES - 1> ALL_PILLARS = {{
|
||||
static const std::array<std::array<BridgePillarFlags, AXIS_END>, NUM_BRIDGE_PIECES - 1> ALL_PILLARS = {{
|
||||
{{BRIDGEPILLARFLAGS_ALL_CORNERS, BRIDGEPILLARFLAGS_ALL_CORNERS}},
|
||||
{{BRIDGEPILLARFLAGS_ALL_CORNERS, BRIDGEPILLARFLAGS_ALL_CORNERS}},
|
||||
{{BRIDGEPILLARFLAGS_ALL_CORNERS, BRIDGEPILLARFLAGS_ALL_CORNERS}},
|
||||
|
@ -763,7 +763,7 @@ static constexpr std::array<std::array<BridgePillarFlags, AXIS_END>, NUM_BRIDGE_
|
|||
}};
|
||||
|
||||
/** Pillar flags for suspension style bridges. */
|
||||
static constexpr std::array<std::array<BridgePillarFlags, AXIS_END>, NUM_BRIDGE_PIECES - 1> SUSPENSION_PILLARS = {{
|
||||
static const std::array<std::array<BridgePillarFlags, AXIS_END>, NUM_BRIDGE_PIECES - 1> SUSPENSION_PILLARS = {{
|
||||
{{{BridgePillarFlag::CornerW, BridgePillarFlag::CornerS}, {BridgePillarFlag::CornerS, BridgePillarFlag::CornerE}}},
|
||||
{{{BridgePillarFlag::CornerE, BridgePillarFlag::CornerN}, {BridgePillarFlag::CornerW, BridgePillarFlag::CornerN}}},
|
||||
{{{BridgePillarFlag::CornerE, BridgePillarFlag::CornerN}, {BridgePillarFlag::CornerW, BridgePillarFlag::CornerN}}},
|
||||
|
@ -773,7 +773,7 @@ static constexpr std::array<std::array<BridgePillarFlags, AXIS_END>, NUM_BRIDGE_
|
|||
}};
|
||||
|
||||
/** Pillar flags for cantilever style bridges. */
|
||||
static constexpr std::array<std::array<BridgePillarFlags, AXIS_END>, NUM_BRIDGE_PIECES - 1> CANTILEVER_PILLARS = {{
|
||||
static const std::array<std::array<BridgePillarFlags, AXIS_END>, NUM_BRIDGE_PIECES - 1> CANTILEVER_PILLARS = {{
|
||||
{{{}, {}}},
|
||||
{{{BridgePillarFlag::CornerE, BridgePillarFlag::CornerN}, {BridgePillarFlag::CornerW, BridgePillarFlag::CornerN}}},
|
||||
{{{BridgePillarFlag::CornerE, BridgePillarFlag::CornerN}, {BridgePillarFlag::CornerW, BridgePillarFlag::CornerN}}},
|
||||
|
|
|
@ -441,6 +441,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);
|
||||
if (ret.Failed()) return ret;
|
||||
cost.AddCost(ret.GetCost());
|
||||
}
|
||||
} else {
|
||||
/* Build a new bridge. */
|
||||
|
||||
|
@ -1418,7 +1426,7 @@ static void DrawTile_TunnelBridge(TileInfo *ti)
|
|||
AddSortableSpriteToDraw(SPR_EMPTY_BOUNDING_BOX, PAL_NONE, *ti, rear_sep[tunnelbridge_direction]);
|
||||
AddSortableSpriteToDraw(SPR_EMPTY_BOUNDING_BOX, PAL_NONE, *ti, front_sep[tunnelbridge_direction]);
|
||||
|
||||
DrawBridgeMiddle(ti);
|
||||
DrawBridgeMiddle(ti, {});
|
||||
} else { // IsBridge(ti->tile)
|
||||
DrawFoundation(ti, GetBridgeFoundation(ti->tileh, DiagDirToAxis(tunnelbridge_direction)));
|
||||
|
||||
|
@ -1509,7 +1517,7 @@ static void DrawTile_TunnelBridge(TileInfo *ti)
|
|||
}
|
||||
}
|
||||
|
||||
DrawBridgeMiddle(ti);
|
||||
DrawBridgeMiddle(ti, {});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1547,11 +1555,26 @@ static BridgePieces CalcBridgePiece(uint north, uint south)
|
|||
}
|
||||
}
|
||||
|
||||
BridgePillarFlags GetBridgeTilePillarFlags(TileIndex tile, TileIndex rampnorth, TileIndex rampsouth, BridgeType type, TransportType transport_type)
|
||||
{
|
||||
if (transport_type == TRANSPORT_WATER) return BRIDGEPILLARFLAGS_ALL_CORNERS;
|
||||
|
||||
const BridgeSpec *spec = GetBridgeSpec(type);
|
||||
if (!spec->ctrl_flags.Test(BridgeSpecCtrlFlag::InvalidPillarFlags)) {
|
||||
BridgePieces piece = CalcBridgePiece(GetTunnelBridgeLength(tile, rampnorth) + 1, GetTunnelBridgeLength(tile, rampsouth) + 1);
|
||||
Axis axis = TileX(rampnorth) == TileX(rampsouth) ? AXIS_Y : AXIS_X;
|
||||
|
||||
return spec->pillar_flags[piece][axis == AXIS_Y ? 1 : 0];
|
||||
}
|
||||
|
||||
return BRIDGEPILLARFLAGS_ALL_CORNERS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the middle bits of a bridge.
|
||||
* @param ti Tile information of the tile to draw it on.
|
||||
*/
|
||||
void DrawBridgeMiddle(const TileInfo *ti)
|
||||
void DrawBridgeMiddle(const TileInfo *ti, BridgePillarFlags blocked_pillars)
|
||||
{
|
||||
/* Sectional view of bridge bounding boxes:
|
||||
*
|
||||
|
@ -1575,6 +1598,7 @@ void DrawBridgeMiddle(const TileInfo *ti)
|
|||
TileIndex rampsouth = GetSouthernBridgeEnd(ti->tile);
|
||||
TransportType transport_type = GetTunnelBridgeTransportType(rampsouth);
|
||||
Axis axis = GetBridgeAxis(ti->tile);
|
||||
BridgePillarFlags pillars;
|
||||
|
||||
uint base_offset = GetBridgeMiddleAxisBaseOffset(axis);
|
||||
std::span<const PalSpriteID> psid;
|
||||
|
@ -1584,9 +1608,11 @@ void DrawBridgeMiddle(const TileInfo *ti)
|
|||
drawfarpillar = !HasBit(GetBridgeSpec(bridge_type)->flags, 0);
|
||||
base_offset += GetBridgeSpriteTableBaseOffset(transport_type, rampsouth);
|
||||
psid = GetBridgeSpriteTable(bridge_type, CalcBridgePiece(GetTunnelBridgeLength(ti->tile, rampnorth) + 1, GetTunnelBridgeLength(ti->tile, rampsouth) + 1));
|
||||
pillars = GetBridgeTilePillarFlags(ti->tile, rampnorth, rampsouth, bridge_type, transport_type);
|
||||
} else {
|
||||
drawfarpillar = true;
|
||||
psid = _aqueduct_sprite_table_middle;
|
||||
pillars = BRIDGEPILLARFLAGS_ALL_CORNERS;
|
||||
}
|
||||
psid = psid.subspan(base_offset, 3);
|
||||
|
||||
|
@ -1655,6 +1681,7 @@ void DrawBridgeMiddle(const TileInfo *ti)
|
|||
/* Do not draw anything more if bridges are invisible */
|
||||
if (IsInvisibilitySet(TO_BRIDGES)) return;
|
||||
|
||||
if (blocked_pillars.Any(pillars)) return;
|
||||
DrawBridgePillars(psid[2], ti, axis, drawfarpillar, x, y, z);
|
||||
}
|
||||
|
||||
|
|
|
@ -926,12 +926,12 @@ static void DrawTile_Water(TileInfo *ti)
|
|||
switch (GetWaterTileType(ti->tile)) {
|
||||
case WATER_TILE_CLEAR:
|
||||
DrawWaterClassGround(ti);
|
||||
DrawBridgeMiddle(ti);
|
||||
DrawBridgeMiddle(ti, {});
|
||||
break;
|
||||
|
||||
case WATER_TILE_COAST: {
|
||||
DrawShoreTile(ti->tileh);
|
||||
DrawBridgeMiddle(ti);
|
||||
DrawBridgeMiddle(ti, {});
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -174,15 +174,15 @@ 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 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);
|
||||
|
||||
/**
|
||||
|
@ -231,12 +231,22 @@ 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);
|
||||
std::vector<uint8_t> layout(count);
|
||||
if (spec != nullptr) {
|
||||
/* For NewGRF waypoints we like to have their style. */
|
||||
GetStationLayout(layout.data(), count, 1, spec);
|
||||
}
|
||||
|
||||
/* 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, layout[i]);
|
||||
if (ret.Failed()) return ret;
|
||||
}
|
||||
|
||||
Waypoint *wp = nullptr;
|
||||
|
@ -285,12 +295,6 @@ CommandCost CmdBuildRailWaypoint(DoCommandFlags flags, TileIndex start_tile, Axi
|
|||
|
||||
wp->UpdateVirtCoord();
|
||||
|
||||
const StationSpec *spec = StationClass::Get(spec_class)->GetSpec(spec_index);
|
||||
std::vector<uint8_t> layout(count);
|
||||
if (spec != nullptr) {
|
||||
/* For NewGRF waypoints we like to have their style. */
|
||||
GetStationLayout(layout.data(), count, 1, spec);
|
||||
}
|
||||
uint8_t map_spec_index = AllocateSpecToStation(spec, wp, true);
|
||||
|
||||
Company *c = Company::Get(wp->owner);
|
||||
|
@ -364,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;
|
||||
|
|
Loading…
Reference in New Issue