diff --git a/src/lang/english.txt b/src/lang/english.txt index 65167a4911..a8ab88dbb4 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1886,7 +1886,7 @@ STR_CONFIG_SETTING_ALLOW_GRF_OBJECTS_UNDER_BRIDGES :Allow all NewGR STR_CONFIG_SETTING_ALLOW_GRF_OBJECTS_UNDER_BRIDGES_HELPTEXT :Allow all NewGRF objects to be built under bridges, even where not otherwise enabled by the GRF.{}This may result in graphical issues. STR_CONFIG_SETTING_ALLOW_STATIONS_UNDER_BRIDGES :Allow stations under bridges: {STRING2} -STR_CONFIG_SETTING_ALLOW_STATIONS_UNDER_BRIDGES_HELPTEXT :Allow stations to be built under bridges (excluding airports).{}This may result in graphical issues. +STR_CONFIG_SETTING_ALLOW_STATIONS_UNDER_BRIDGES_HELPTEXT :Allow stations to be built under bridges (excluding airports, and rail stations/waypoints where the required bridge clearance is set by the GRF).{}This may result in graphical issues. STR_CONFIG_SETTING_QUERY_CAPTION :{WHITE}Change setting value diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 4fc1813546..efabc5fa4c 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -1965,7 +1965,7 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte break; case 0x0E: // Define custom layout - statspec->copied_layouts = false; + ClrBit(statspec->internal_flags, SSIF_COPIED_LAYOUTS); while (buf->HasData()) { byte length = buf->ReadByte(); @@ -2028,7 +2028,7 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte statspec->lengths = srcstatspec->lengths; statspec->platforms = srcstatspec->platforms; statspec->layouts = srcstatspec->layouts; - statspec->copied_layouts = true; + SetBit(statspec->internal_flags, SSIF_COPIED_LAYOUTS); break; } @@ -2086,6 +2086,13 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte } break; + case 0x1B: // Minimum height for a bridge above + SetBit(statspec->internal_flags, SSIF_BRIDGE_HEIGHTS_SET); + for (uint i = 0; i < 8; i++) { + statspec->bridge_height[i] = buf->ReadByte(); + } + break; + default: ret = CIR_UNKNOWN; break; @@ -7927,7 +7934,7 @@ static void ResetCustomStations() delete[] statspec->renderdata; /* Release platforms and layouts */ - if (!statspec->copied_layouts) { + if (!HasBit(statspec->internal_flags, SSIF_COPIED_LAYOUTS)) { for (uint l = 0; l < statspec->lengths; l++) { for (uint p = 0; p < statspec->platforms[l]; p++) { free(statspec->layouts[l][p]); diff --git a/src/newgrf_station.h b/src/newgrf_station.h index 06b83f57a9..fe5f4a5c2c 100644 --- a/src/newgrf_station.h +++ b/src/newgrf_station.h @@ -111,6 +111,11 @@ enum StationRandomTrigger { SRT_PATH_RESERVATION, ///< Trigger platform when train reserves path. }; +enum StationSpecIntlFlags { + SSIF_COPIED_LAYOUTS, ///< Copied StationLayout **layouts. + SSIF_BRIDGE_HEIGHTS_SET, ///< byte bridge_height[8] is set. +}; + /* Station layout for given dimensions - it is a two-dimensional array * where index is computed as (x * platforms) + platform. */ typedef byte *StationLayout; @@ -164,13 +169,14 @@ struct StationSpec { byte pylons; ///< Bitmask of base tiles (0 - 7) which should contain elrail pylons byte wires; ///< Bitmask of base tiles (0 - 7) which should contain elrail wires byte blocked; ///< Bitmask of base tiles (0 - 7) which are blocked to trains + byte bridge_height[8]; ///< Minimum height for a bridge above, 0 for none AnimationInfo animation; byte lengths; byte *platforms; StationLayout **layouts; - bool copied_layouts; + byte internal_flags; ///< Bitmask of internal spec flags (StationSpecIntlFlags) }; /** Struct containing information relating to station classes. */ diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 500cfb942d..6b05f8a232 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -775,6 +775,18 @@ CommandCost CheckBuildableTile(TileIndex tile, uint invalid_dirs, int &allowed_z return cost; } +bool IsRailStationBridgeAboveOk(TileIndex tile, const StationSpec *statspec, byte layout) +{ + assert(layout < 8); + if (!IsBridgeAbove(tile)) return true; + + if (statspec && HasBit(statspec->internal_flags, SSIF_BRIDGE_HEIGHTS_SET)) { + return (GetTileMaxZ(tile) + statspec->bridge_height[layout] <= GetBridgeHeight(GetSouthernBridgeEnd(tile))); + } else { + return _settings_game.construction.allow_stations_under_bridges; + } +} + /** * Checks if a rail station can be built at the given area. * @param tile_area Area to check. @@ -799,7 +811,7 @@ static CommandCost CheckFlatLandRailStation(TileArea tile_area, DoCommandFlag fl bool slope_cb = statspec != NULL && HasBit(statspec->callback_mask, CBM_STATION_SLOPE_CHECK); TILE_AREA_LOOP(tile_cur, tile_area) { - CommandCost ret = CheckBuildableTile(tile_cur, invalid_dirs, allowed_z, false, !_settings_game.construction.allow_stations_under_bridges); + CommandCost ret = CheckBuildableTile(tile_cur, invalid_dirs, allowed_z, false, false); if (ret.Failed()) return ret; cost.AddCost(ret); @@ -1255,6 +1267,26 @@ CommandCost CmdBuildRailStation(TileIndex tile_org, DoCommandFlag flags, uint32 /* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */ StationID est = INVALID_STATION; SmallVector affected_vehicles; + + const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index); + + TileIndexDiff tile_delta = (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); + byte *layout_ptr = AllocaM(byte, numtracks * plat_len); + GetStationLayout(layout_ptr, numtracks, plat_len, statspec); + + { + TileIndex tile_track = tile_org; + byte *check_layout_ptr = layout_ptr; + for (uint i = 0; i < numtracks; i++) { + TileIndex tile = tile_track; + for (uint j = 0; j < plat_len; j++) { + if (!IsRailStationBridgeAboveOk(tile, statspec, *check_layout_ptr++)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); + tile += tile_delta; + } + tile_track += tile_delta ^ TileDiffXY(1, 1); // perpendicular to tile_delta + } + } + /* Clear the land below the station. */ CommandCost cost = CheckFlatLandRailStation(new_location, flags, axis, &est, rt, affected_vehicles, spec_class, spec_index, plat_len, numtracks); if (cost.Failed()) return cost; @@ -1275,7 +1307,6 @@ CommandCost CmdBuildRailStation(TileIndex tile_org, DoCommandFlag flags, uint32 } /* Check if we can allocate a custom stationspec to this station */ - const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index); int specindex = AllocateSpecToStation(statspec, st, (flags & DC_EXEC) != 0); if (specindex == -1) return_cmd_error(STR_ERROR_TOO_MANY_STATION_SPECS); @@ -1295,8 +1326,7 @@ CommandCost CmdBuildRailStation(TileIndex tile_org, DoCommandFlag flags, uint32 } if (flags & DC_EXEC) { - TileIndexDiff tile_delta; - byte *layout_ptr; + byte numtracks_orig; Track track; @@ -1311,12 +1341,8 @@ CommandCost CmdBuildRailStation(TileIndex tile_org, DoCommandFlag flags, uint32 st->cached_anim_triggers |= statspec->animation.triggers; } - tile_delta = (axis == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); track = AxisToTrack(axis); - layout_ptr = AllocaM(byte, numtracks * plat_len); - GetStationLayout(layout_ptr, numtracks, plat_len, statspec); - numtracks_orig = numtracks; Company *c = Company::Get(st->owner); diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index c50146203b..9dd6af840d 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -47,6 +47,7 @@ #include "station_map.h" #include "industry_map.h" #include "object_map.h" +#include "newgrf_station.h" #include "table/strings.h" #include "table/bridge_land.h" @@ -502,7 +503,27 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u } case MP_STATION: { - if (!_settings_game.construction.allow_stations_under_bridges || IsAirport(tile)) goto not_valid_below; + switch (GetStationType(tile)) { + case STATION_AIRPORT: + goto not_valid_below; + + case STATION_RAIL: + case STATION_WAYPOINT: { + const StationSpec *statspec = GetStationSpec(tile); + if (statspec && HasBit(statspec->internal_flags, SSIF_BRIDGE_HEIGHTS_SET)) { + uint layout = GetStationGfx (tile); + assert(layout < 8); + if (GetTileMaxZ(tile) + statspec->bridge_height[layout] > z_start + 1) goto not_valid_below; + } else if (!_settings_game.construction.allow_stations_under_bridges) { + goto not_valid_below; + } + break; + } + + default: + if (!_settings_game.construction.allow_stations_under_bridges) goto not_valid_below; + break; + } break; } diff --git a/src/waypoint_cmd.cpp b/src/waypoint_cmd.cpp index be0809a764..7c03e433bf 100644 --- a/src/waypoint_cmd.cpp +++ b/src/waypoint_cmd.cpp @@ -133,14 +133,13 @@ static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID * return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); } - if (IsBridgeAbove(tile) && !_settings_game.construction.allow_stations_under_bridges) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); - return CommandCost(); } extern void GetStationLayout(byte *layout, int numtracks, int plat_len, const StationSpec *statspec); extern CommandCost FindJoiningWaypoint(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Waypoint **wp); extern CommandCost CanExpandRailStation(const BaseStation *st, TileArea &new_ta, Axis axis); +extern bool IsRailStationBridgeAboveOk(TileIndex tile, const StationSpec *statspec, byte layout); /** * Convert existing rail to waypoint. Eg build a waypoint station over @@ -187,6 +186,16 @@ CommandCost CmdBuildRailWaypoint(TileIndex start_tile, DoCommandFlag flags, uint if (distant_join && (!_settings_game.station.distant_join_stations || !Waypoint::IsValidID(station_to_join))) return CMD_ERROR; + const StationSpec *spec = StationClass::Get(spec_class)->GetSpec(spec_index); + byte *layout_ptr = AllocaM(byte, count); + if (spec == NULL) { + /* The layout must be 0 for the 'normal' waypoints by design. */ + memset(layout_ptr, 0, count); + } else { + /* But for NewGRF waypoints we like to have their style. */ + GetStationLayout(layout_ptr, count, 1, spec); + } + /* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */ StationID est = INVALID_STATION; @@ -196,6 +205,7 @@ CommandCost CmdBuildRailWaypoint(TileIndex start_tile, DoCommandFlag flags, uint TileIndex tile = start_tile + i * offset; CommandCost ret = IsValidTileForWaypoint(tile, axis, &est); if (ret.Failed()) return ret; + if (!IsRailStationBridgeAboveOk(tile, spec, layout_ptr[i])) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); } Waypoint *wp = NULL; @@ -245,15 +255,6 @@ CommandCost CmdBuildRailWaypoint(TileIndex start_tile, DoCommandFlag flags, uint wp->UpdateVirtCoord(); - const StationSpec *spec = StationClass::Get(spec_class)->GetSpec(spec_index); - byte *layout_ptr = AllocaM(byte, count); - if (spec == NULL) { - /* The layout must be 0 for the 'normal' waypoints by design. */ - memset(layout_ptr, 0, count); - } else { - /* But for NewGRF waypoints we like to have their style. */ - GetStationLayout(layout_ptr, count, 1, spec); - } byte map_spec_index = AllocateSpecToStation(spec, wp, true); Company *c = Company::Get(wp->owner);