From 2cbbae16da7f9cbc6736c9e01c9e75ddf08a030d Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Tue, 15 Feb 2022 18:21:32 +0000 Subject: [PATCH] Add roadstop GRF properties to set bridge heights and disallowed pillars --- src/lang/english.txt | 1 + src/newgrf.cpp | 20 ++++++++++++++++++++ src/newgrf_extension.cpp | 2 ++ src/newgrf_extension.h | 2 ++ src/newgrf_roadstop.h | 9 +++++++++ src/settings.cpp | 13 +++++++++++++ src/station_cmd.cpp | 32 ++++++++++++++++++++++---------- src/table/newgrf_debug_data.h | 2 +- src/table/settings/settings.ini | 2 ++ src/tunnelbridge_cmd.cpp | 7 ++++--- src/waypoint_cmd.cpp | 4 ++-- 11 files changed, 78 insertions(+), 16 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index e61a6c4ff3..311017f1cc 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2450,6 +2450,7 @@ STR_CONFIG_SETTING_ALLOW_GRF_STATIONS_UNDER_BRIDGES_HELPTEXT :If enabled, New STR_CONFIG_SETTING_ALLOW_ROAD_STATIONS_UNDER_BRIDGES :Allow road/tram stops under bridges: {STRING2} STR_CONFIG_SETTING_ALLOW_ROAD_STATIONS_UNDER_BRIDGES_HELPTEXT :Allow road/tram stops under bridges. Drive through stops require 1 height level of clearance, drive in bays require 2 height levels of clearance. +STR_CONFIG_SETTING_ALLOW_ROAD_STATIONS_UNDER_BRIDGES_HELPTEXT_EXTRA :{STRING}{}{}NewGRF road/tram stops may override this setting and allow or disallow custom stops under bridges, and set custom height clearance requirements. STR_CONFIG_SETTING_ALLOW_DOCKS_UNDER_BRIDGES :Allow docks under bridges: {STRING2} STR_CONFIG_SETTING_ALLOW_DOCKS_UNDER_BRIDGES_HELPTEXT :Allow docks under bridges. Docks require 2 height levels of clearance.{}This may result in graphical issues. diff --git a/src/newgrf.cpp b/src/newgrf.cpp index be53e438b9..7daecd300a 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -5065,6 +5065,26 @@ static ChangeInfoResult RoadStopChangeInfo(uint id, int numinfo, int prop, const rs->flags = (uint8)buf->ReadDWord(); // Future-proofing, size this as 4 bytes, but we only need one byte's worth of flags at present break; + case A0RPI_ROADSTOP_MIN_BRIDGE_HEIGHT: + if (MappedPropertyLengthMismatch(buf, 6, mapping_entry)) break; + FALLTHROUGH; + case 0x13: // Minimum height for a bridge above + SetBit(rs->internal_flags, RSIF_BRIDGE_HEIGHTS_SET); + for (uint i = 0; i < 6; i++) { + rs->bridge_height[i] = buf->ReadByte(); + } + break; + + case A0RPI_ROADSTOP_DISALLOWED_BRIDGE_PILLARS: + if (MappedPropertyLengthMismatch(buf, 6, mapping_entry)) break; + FALLTHROUGH; + case 0x14: // Disallowed bridge pillars + SetBit(rs->internal_flags, RSIF_BRIDGE_DISALLOWED_PILLARS_SET); + for (uint i = 0; i < 6; i++) { + rs->bridge_disallowed_pillars[i] = buf->ReadByte(); + } + break; + default: ret = CIR_UNKNOWN; break; diff --git a/src/newgrf_extension.cpp b/src/newgrf_extension.cpp index 2ebdb7cae2..6f40f576d2 100644 --- a/src/newgrf_extension.cpp +++ b/src/newgrf_extension.cpp @@ -101,6 +101,8 @@ extern const GRFPropertyMapDefinition _grf_action0_remappable_properties[] = { GRFPropertyMapDefinition(GSF_ROADSTOPS, A0RPI_ROADSTOP_ANIMATION_TRIGGERS, "roadstop_animation_triggers"), GRFPropertyMapDefinition(GSF_ROADSTOPS, A0RPI_ROADSTOP_CALLBACK_MASK, "roadstop_callback_mask"), GRFPropertyMapDefinition(GSF_ROADSTOPS, A0RPI_ROADSTOP_GENERAL_FLAGS, "roadstop_general_flags"), + GRFPropertyMapDefinition(GSF_ROADSTOPS, A0RPI_ROADSTOP_MIN_BRIDGE_HEIGHT, "roadstop_min_bridge_height"), + GRFPropertyMapDefinition(GSF_ROADSTOPS, A0RPI_ROADSTOP_DISALLOWED_BRIDGE_PILLARS, "roadstop_disallowed_bridge_pillars"), GRFPropertyMapDefinition(), }; diff --git a/src/newgrf_extension.h b/src/newgrf_extension.h index c68c916270..530f75ddb0 100644 --- a/src/newgrf_extension.h +++ b/src/newgrf_extension.h @@ -50,6 +50,8 @@ enum Action0RemapPropertyIds { A0RPI_ROADSTOP_ANIMATION_TRIGGERS, A0RPI_ROADSTOP_CALLBACK_MASK, A0RPI_ROADSTOP_GENERAL_FLAGS, + A0RPI_ROADSTOP_MIN_BRIDGE_HEIGHT, + A0RPI_ROADSTOP_DISALLOWED_BRIDGE_PILLARS, }; diff --git a/src/newgrf_roadstop.h b/src/newgrf_roadstop.h index 43121b845b..092b3a1948 100644 --- a/src/newgrf_roadstop.h +++ b/src/newgrf_roadstop.h @@ -67,6 +67,11 @@ enum RoadStopSpecFlags { RSF_CB141_RANDOM_BITS, ///< Callback 141 needs random bits. }; +enum RoadStopSpecIntlFlags { + RSIF_BRIDGE_HEIGHTS_SET, ///< byte bridge_height[6] is set. + RSIF_BRIDGE_DISALLOWED_PILLARS_SET, ///< byte bridge_disallowed_pillars[6] is set. +}; + /** Scope resolver for road stops. */ struct RoadStopScopeResolver : public ScopeResolver { TileIndex tile; ///< %Tile of the station. @@ -131,11 +136,15 @@ struct RoadStopSpec { RoadStopDrawMode draw_mode = ROADSTOP_DRAW_MODE_ROAD | ROADSTOP_DRAW_MODE_OVERLAY; uint8 callback_mask = 0; uint8 flags = 0; + uint8 internal_flags = 0; ///< Bitmask of internal spec flags (RoadStopSpecIntlFlags) CargoTypes cargo_triggers = 0; ///< Bitmask of cargo types which cause trigger re-randomizing AnimationInfo animation; + byte bridge_height[6]; ///< Minimum height for a bridge above, 0 for none + byte bridge_disallowed_pillars[6]; ///< Disallowed pillar flags for a bridge above + static const RoadStopSpec *Get(uint16 index); }; diff --git a/src/settings.cpp b/src/settings.cpp index 204fbe5084..2e773d7a67 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1788,6 +1788,19 @@ static bool SpriteZoomMinSettingGUI(SettingOnGuiCtrlData &data) } } +static bool AllowRoadStopsUnderBridgesSettingGUI(SettingOnGuiCtrlData &data) +{ + switch (data.type) { + case SOGCT_DESCRIPTION_TEXT: + SetDParam(0, data.text); + data.text = STR_CONFIG_SETTING_ALLOW_ROAD_STATIONS_UNDER_BRIDGES_HELPTEXT_EXTRA; + return true; + + default: + return false; + } +} + /* End - GUI callbacks */ /** diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 798d198b08..2b5e478361 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -946,18 +946,28 @@ CommandCost IsRailStationBridgeAboveOk(TileIndex tile, const StationSpec *statsp GetBridgeType(southern_bridge_end), GetTunnelBridgeTransportType(southern_bridge_end)); } -CommandCost IsRoadStopBridgeAboveOK(TileIndex tile, bool drive_through, DiagDirection entrance, +CommandCost IsRoadStopBridgeAboveOK(TileIndex tile, const RoadStopSpec *spec, bool drive_through, DiagDirection entrance, TileIndex northern_bridge_end, TileIndex southern_bridge_end, int bridge_height, BridgeType bridge_type, TransportType bridge_transport_type) { - if (!_settings_game.construction.allow_road_stops_under_bridges) return CommandCost(INVALID_STRING_ID); + if (spec && HasBit(spec->internal_flags, RSIF_BRIDGE_HEIGHTS_SET)) { + int height = spec->bridge_height[drive_through ? (GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET + DiagDirToAxis(entrance)) : entrance]; + if (height == 0) return CommandCost(INVALID_STRING_ID); + if (GetTileMaxZ(tile) + height > bridge_height) { + return CommandCost(STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION); + } + } else { + if (!_settings_game.construction.allow_road_stops_under_bridges) return CommandCost(INVALID_STRING_ID); - if (GetTileMaxZ(tile) + (drive_through ? 1 : 2) > bridge_height) { - return CommandCost(STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION); + if (GetTileMaxZ(tile) + (drive_through ? 1 : 2) > bridge_height) { + return CommandCost(STR_ERROR_BRIDGE_TOO_LOW_FOR_STATION); + } } BridgePiecePillarFlags disallowed_pillar_flags = (BridgePiecePillarFlags) 0; - if (drive_through) { + if (spec && HasBit(spec->internal_flags, RSIF_BRIDGE_DISALLOWED_PILLARS_SET)) { + disallowed_pillar_flags = (BridgePiecePillarFlags) spec->bridge_disallowed_pillars[drive_through ? (GFX_TRUCK_BUS_DRIVETHROUGH_OFFSET + DiagDirToAxis(entrance)) : entrance]; + } else if (drive_through) { disallowed_pillar_flags = (BridgePiecePillarFlags) (DiagDirToAxis(entrance) == AXIS_X ? 0x50 : 0xA0); } else { SetBit(disallowed_pillar_flags, 4 + entrance); @@ -1066,6 +1076,7 @@ static CommandCost CheckFlatLandRailStation(TileArea tile_area, DoCommandFlag fl /** * Checks if a road stop can be built at the given tile. * @param tile_area Area to check. + * @param spec Road stop spec. * @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. @@ -1076,20 +1087,21 @@ static CommandCost CheckFlatLandRailStation(TileArea tile_area, DoCommandFlag fl * @param require_road Is existing road required. * @return The cost in case of success, or an error code if it failed. */ -CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags, uint invalid_dirs, bool is_drive_through, StationType station_type, Axis axis, StationID *station, RoadType rt, bool require_road) +CommandCost CheckFlatLandRoadStop(TileArea tile_area, const RoadStopSpec *spec, DoCommandFlag flags, uint invalid_dirs, bool is_drive_through, StationType station_type, Axis axis, StationID *station, RoadType rt, bool require_road) { CommandCost cost(EXPENSES_CONSTRUCTION); int allowed_z = -1; for (TileIndex cur_tile : tile_area) { - CommandCost ret = CheckBuildableTile(cur_tile, invalid_dirs, allowed_z, !is_drive_through, !_settings_game.construction.allow_road_stops_under_bridges); + bool allow_under_bridge = _settings_game.construction.allow_road_stops_under_bridges || (spec != nullptr && HasBit(spec->internal_flags, RSIF_BRIDGE_HEIGHTS_SET)); + CommandCost ret = CheckBuildableTile(cur_tile, invalid_dirs, allowed_z, !is_drive_through, !allow_under_bridge); if (ret.Failed()) return ret; cost.AddCost(ret); - if (_settings_game.construction.allow_road_stops_under_bridges && IsBridgeAbove(cur_tile)) { + if (allow_under_bridge && IsBridgeAbove(cur_tile)) { TileIndex southern_bridge_end = GetSouthernBridgeEnd(cur_tile); TileIndex northern_bridge_end = GetNorthernBridgeEnd(cur_tile); - CommandCost bridge_ret = IsRoadStopBridgeAboveOK(cur_tile, is_drive_through, (DiagDirection) FindFirstBit(invalid_dirs), + CommandCost bridge_ret = IsRoadStopBridgeAboveOK(cur_tile, spec, is_drive_through, (DiagDirection) FindFirstBit(invalid_dirs), northern_bridge_end, southern_bridge_end, GetBridgeHeight(southern_bridge_end), GetBridgeType(southern_bridge_end), GetTunnelBridgeTransportType(southern_bridge_end)); if (bridge_ret.Failed()) return bridge_ret; @@ -2114,7 +2126,7 @@ CommandCost CmdBuildRoadStop(TileIndex tile, DoCommandFlag flags, uint32 p1, uin /* Total road stop cost. */ CommandCost cost(EXPENSES_CONSTRUCTION, roadstop_area.w * roadstop_area.h * _price[type ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS]); StationID est = INVALID_STATION; - ret = CheckFlatLandRoadStop(roadstop_area, flags, is_drive_through ? 5 << axis : 1 << ddir, is_drive_through, type ? STATION_TRUCK : STATION_BUS, axis, &est, rt, false); + ret = CheckFlatLandRoadStop(roadstop_area, roadstopspec, flags, is_drive_through ? 5 << axis : 1 << ddir, is_drive_through, type ? STATION_TRUCK : STATION_BUS, axis, &est, rt, false); if (ret.Failed()) return ret; cost.AddCost(ret); diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h index 21cf4b8616..e1dbbe50e8 100644 --- a/src/table/newgrf_debug_data.h +++ b/src/table/newgrf_debug_data.h @@ -1433,7 +1433,7 @@ class NIHRoadStop : public NIHelper { output.print(buffer); seprintf(buffer, lastof(buffer), " spec: stop type: %X, draw mode: %X, cargo triggers: " OTTD_PRINTFHEX64, spec->stop_type, spec->draw_mode, spec->cargo_triggers); output.print(buffer); - seprintf(buffer, lastof(buffer), " spec: callback mask: %X, flags: %X", spec->callback_mask, spec->flags); + seprintf(buffer, lastof(buffer), " spec: callback mask: %X, flags: %X, intl flags: %X", spec->callback_mask, spec->flags, spec->internal_flags); output.print(buffer); seprintf(buffer, lastof(buffer), " animation: frames: %u, status: %u, speed: %u, triggers: 0x%X", spec->animation.frames, spec->animation.status, spec->animation.speed, spec->animation.triggers); output.print(buffer); diff --git a/src/table/settings/settings.ini b/src/table/settings/settings.ini index b893187e73..5727f2f379 100644 --- a/src/table/settings/settings.ini +++ b/src/table/settings/settings.ini @@ -82,6 +82,7 @@ static int64 LinkGraphDistModeXrefChillPP(int64 val); static bool LinkGraphDistributionSettingGUI(SettingOnGuiCtrlData &data); static bool OrderTownGrowthRate(SettingOnGuiCtrlData &data); static bool SpriteZoomMinSettingGUI(SettingOnGuiCtrlData &data); +static bool AllowRoadStopsUnderBridgesSettingGUI(SettingOnGuiCtrlData &data); /* End - GUI callbacks */ @@ -2044,6 +2045,7 @@ cat = SC_ADVANCED str = STR_CONFIG_SETTING_ALLOW_ROAD_STATIONS_UNDER_BRIDGES strhelp = STR_CONFIG_SETTING_ALLOW_ROAD_STATIONS_UNDER_BRIDGES_HELPTEXT patxname = ""allow_stations_under_bridges.construction.allow_road_stops_under_bridges"" +guiproc = AllowRoadStopsUnderBridgesSettingGUI [SDT_BOOL] var = construction.allow_docks_under_bridges diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index e5ecd4003a..413e4e9cb7 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -49,6 +49,7 @@ #include "newgrf_station.h" #include "station_func.h" #include "tracerestrict.h" +#include "newgrf_roadstop.h" #include "table/strings.h" #include "table/bridge_land.h" @@ -69,7 +70,7 @@ extern const RoadBits _invalid_tileh_slopes_road[2][15]; extern CommandCost IsRailStationBridgeAboveOk(TileIndex tile, const StationSpec *statspec, byte layout, TileIndex northern_bridge_end, TileIndex southern_bridge_end, int bridge_height, BridgeType bridge_type, TransportType bridge_transport_type); -extern CommandCost IsRoadStopBridgeAboveOK(TileIndex tile, bool drive_through, DiagDirection entrance, +extern CommandCost IsRoadStopBridgeAboveOK(TileIndex tile, const RoadStopSpec *spec, bool drive_through, DiagDirection entrance, TileIndex northern_bridge_end, TileIndex southern_bridge_end, int bridge_height, BridgeType bridge_type, TransportType bridge_transport_type); @@ -551,7 +552,7 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u case STATION_BUS: case STATION_TRUCK: case STATION_ROADWAYPOINT: { - CommandCost ret = IsRoadStopBridgeAboveOK(tile, IsDriveThroughStopTile(tile), GetRoadStopDir(tile), + CommandCost ret = IsRoadStopBridgeAboveOK(tile, GetRoadStopSpec(tile), IsDriveThroughStopTile(tile), GetRoadStopDir(tile), tile_start, tile_end, z_start + 1, bridge_type, transport_type); if (ret.Failed()) { if (ret.GetErrorMessage() != INVALID_STRING_ID) return ret; @@ -675,7 +676,7 @@ CommandCost CmdBuildBridge(TileIndex end_tile, DoCommandFlag flags, uint32 p1, u case STATION_BUS: case STATION_TRUCK: case STATION_ROADWAYPOINT: { - CommandCost ret = IsRoadStopBridgeAboveOK(tile, IsDriveThroughStopTile(tile), GetRoadStopDir(tile), + CommandCost ret = IsRoadStopBridgeAboveOK(tile, GetRoadStopSpec(tile), IsDriveThroughStopTile(tile), GetRoadStopDir(tile), tile_start, tile_end, z_start + 1, bridge_type, transport_type); if (ret.Failed()) { if (ret.GetErrorMessage() != INVALID_STRING_ID) return ret; diff --git a/src/waypoint_cmd.cpp b/src/waypoint_cmd.cpp index 237c26a420..baf7e989e3 100644 --- a/src/waypoint_cmd.cpp +++ b/src/waypoint_cmd.cpp @@ -380,8 +380,8 @@ CommandCost CmdBuildRoadWaypoint(TileIndex start_tile, DoCommandFlag flags, uint /* Total road stop cost. */ CommandCost cost(EXPENSES_CONSTRUCTION, roadstop_area.w * roadstop_area.h * _price[PR_BUILD_STATION_TRUCK]); StationID est = INVALID_STATION; - extern CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags, uint invalid_dirs, bool is_drive_through, StationType station_type, Axis axis, StationID *station, RoadType rt, bool require_road); - CommandCost ret = CheckFlatLandRoadStop(roadstop_area, flags, 5 << axis, true, STATION_ROADWAYPOINT, axis, &est, INVALID_ROADTYPE, true); + extern CommandCost CheckFlatLandRoadStop(TileArea tile_area, const RoadStopSpec *spec, DoCommandFlag flags, uint invalid_dirs, bool is_drive_through, StationType station_type, Axis axis, StationID *station, RoadType rt, bool require_road); + CommandCost ret = CheckFlatLandRoadStop(roadstop_area, spec, flags, 5 << axis, true, STATION_ROADWAYPOINT, axis, &est, INVALID_ROADTYPE, true); if (ret.Failed()) return ret; cost.AddCost(ret);