diff --git a/docs/newgrf-additions-nml.html b/docs/newgrf-additions-nml.html index e0ce15ac6a..0e9b5184eb 100644 --- a/docs/newgrf-additions-nml.html +++ b/docs/newgrf-additions-nml.html @@ -177,6 +177,8 @@
Towns may not modify tiles of this roadtype in any way whatsoever
NO_TUNNELS
Disallow tunnels for this roadtype
+
NO_TRAIN_COLLISION
+
Disallow collisions with trains for vehicles of this roadtype
@@ -206,6 +208,8 @@
Towns may not modify tiles of this tramtype in any way whatsoever
NO_TUNNELS
Disallow tunnels for this tramtype
+
NO_TRAIN_COLLISION
+
Disallow collisions with trains for vehicles of this tramtype
diff --git a/docs/newgrf-additions.html b/docs/newgrf-additions.html index 5eb3d14c89..20b4328648 100644 --- a/docs/newgrf-additions.html +++ b/docs/newgrf-additions.html @@ -329,6 +329,8 @@ 12Towns may not modify tiles of this road/tram type in any way whatsoever 24Disallow tunnels for this road/tram type
Support for this bit is indicated by the feature name: action0_roadtype_extra_flags, version 2. + 38Disallow collisions with trains for vehicles of this road/tram type
+ Support for this bit is indicated by the feature name: action0_roadtype_extra_flags, version 2.

This is indicated by the feature name: action0_roadtype_extra_flags, version 1

diff --git a/src/road.h b/src/road.h index 6abe4d2c4f..7ff8e64350 100644 --- a/src/road.h +++ b/src/road.h @@ -56,6 +56,7 @@ enum RoadTypeExtraFlags { RXTF_NOT_AVAILABLE_AI_GS = 0, ///< Bit number for unavailable for AI/GS RXTF_NO_TOWN_MODIFICATION, ///< Bit number for no town modification RXTF_NO_TUNNELS, ///< Bit number for no tunnels + RXTF_NO_TRAIN_COLLISION, ///< Bit number for no train collision RXTFB_NONE = 0, ///< All flags cleared. }; @@ -346,5 +347,6 @@ bool HasAnyRoadTypesAvail(CompanyID company, RoadTramType rtt); extern std::vector _sorted_roadtypes; extern RoadTypes _roadtypes_hidden_mask; extern std::array _collision_mode_roadtypes; +extern RoadTypes _roadtypes_non_train_colliding; #endif /* ROAD_H */ diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index 0b5515f276..de7aec0d4b 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -52,6 +52,7 @@ RoadTypeInfo _roadtypes[ROADTYPE_END]; std::vector _sorted_roadtypes; RoadTypes _roadtypes_hidden_mask; std::array _collision_mode_roadtypes; +RoadTypes _roadtypes_non_train_colliding; /** * Bitmap of road/tram types. @@ -138,10 +139,12 @@ void InitRoadTypes() void InitRoadTypesCaches() { std::fill(_collision_mode_roadtypes.begin(), _collision_mode_roadtypes.end(), ROADTYPES_NONE); + _roadtypes_non_train_colliding = ROADTYPES_NONE; for (RoadType rt = ROADTYPE_BEGIN; rt != ROADTYPE_END; rt++) { const RoadTypeInfo &rti = _roadtypes[rt]; SetBit(_collision_mode_roadtypes[rti.collision_mode], rt); + if (HasBit(rti.extra_flags, RXTF_NO_TRAIN_COLLISION)) SetBit(_roadtypes_non_train_colliding, rt); } } @@ -2300,15 +2303,22 @@ static void DrawTile_Road(TileInfo *ti, DrawTileProcParams params) SpriteID rail = GetCustomRailSprite(rti, ti->tile, RTSG_CROSSING) + axis; DrawGroundSprite(rail, pal); + auto is_usable_crossing = [&](TileIndex t) -> bool { + if (HasRoadTypeRoad(t) && !HasBit(_roadtypes_non_train_colliding, GetRoadTypeRoad(t))) return true; + if (HasRoadTypeTram(t) && !HasBit(_roadtypes_non_train_colliding, GetRoadTypeTram(t))) return true; + return false; + }; - if (_settings_game.vehicle.adjacent_crossings) { + if (!is_usable_crossing(ti->tile)) { + /* Do not draw crossing overlays */ + } else if (_settings_game.vehicle.adjacent_crossings) { const Axis axis = GetCrossingRoadAxis(ti->tile); const DiagDirection dir1 = AxisToDiagDir(axis); const DiagDirection dir2 = ReverseDiagDir(dir1); uint adjacent_diagdirs = 0; for (DiagDirection dir : { dir1, dir2 }) { const TileIndex t = TileAddByDiagDir(ti->tile, dir); - if (t < MapSize() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis) { + if (t < MapSize() && IsLevelCrossingTile(t) && GetCrossingRoadAxis(t) == axis && is_usable_crossing(t)) { SetBit(adjacent_diagdirs, dir); } } @@ -2656,7 +2666,7 @@ static TrackStatus GetTileTrackStatus_Road(TileIndex tile, TransportType mode, u break; case TRANSPORT_ROAD: { - RoadTramType rtt = (RoadTramType)sub_mode; + RoadTramType rtt = (RoadTramType)GB(sub_mode, 0, 8); if (!HasTileRoadType(tile, rtt)) break; switch (GetRoadTileType(tile)) { case ROAD_TILE_NORMAL: { @@ -2704,7 +2714,13 @@ static TrackStatus GetTileTrackStatus_Road(TileIndex tile, TransportType mode, u if (side != INVALID_DIAGDIR && axis != DiagDirToAxis(side)) break; trackdirbits = TrackBitsToTrackdirBits(AxisToTrackBits(axis)); - if (IsCrossingBarred(tile)) { + auto is_non_colliding = [&]() -> bool { + uint8 rtfield = GB(sub_mode, 8, 8); + if (rtfield == 0) return false; + RoadType rt = (RoadType)(rtfield - 1); + return HasBit(_roadtypes_non_train_colliding, rt); + }; + if (IsCrossingBarred(tile) && !is_non_colliding()) { red_signals = trackdirbits; auto mask_red_signal_bits_if_crossing_barred = [&](TileIndex t, TrackdirBits mask) { if (IsLevelCrossingTile(t) && IsCrossingBarred(t)) red_signals &= mask; diff --git a/src/roadveh.h b/src/roadveh.h index d503c7c90e..64920d9fc0 100644 --- a/src/roadveh.h +++ b/src/roadveh.h @@ -155,6 +155,7 @@ struct RoadVehicle FINAL : public GroundVehicle { inline bool IsRoadVehicleOnLevelCrossing() const { + if (HasBit(_roadtypes_non_train_colliding, this->roadtype)) return false; for (const RoadVehicle *u = this; u != nullptr; u = u->Next()) { if (IsLevelCrossingTile(u->tile)) return true; } diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index c98f196849..d591f334cb 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -631,6 +631,8 @@ static void RoadVehCrash(RoadVehicle *v) static bool RoadVehCheckTrainCrash(RoadVehicle *v) { + if (HasBit(_roadtypes_non_train_colliding, v->roadtype)) return false; + for (RoadVehicle *u = v; u != nullptr; u = u->Next()) { if (u->state == RVSB_WORMHOLE) continue; @@ -911,7 +913,7 @@ static Vehicle *EnumFindVehBlockingOvertakeBehind(Vehicle *v, void *data) static bool CheckRoadInfraUnsuitableForOvertaking(OvertakeData *od) { if (!HasTileAnyRoadType(od->tile, od->v->compatible_roadtypes)) return true; - TrackStatus ts = GetTileTrackStatus(od->tile, TRANSPORT_ROAD, GetRoadTramType(od->v->roadtype)); + TrackStatus ts = GetTileTrackStatus(od->tile, TRANSPORT_ROAD, ((od->v->roadtype + 1) << 8) | GetRoadTramType(od->v->roadtype)); TrackdirBits trackdirbits = TrackStatusToTrackdirBits(ts); TrackdirBits red_signals = TrackStatusToRedSignals(ts); // barred level crossing TrackBits trackbits = TrackdirBitsToTrackBits(trackdirbits); @@ -1167,7 +1169,7 @@ static Trackdir RoadFindPathToDest(RoadVehicle *v, TileIndex tile, DiagDirection Trackdir best_track; bool path_found = true; - TrackStatus ts = GetTileTrackStatus(tile, TRANSPORT_ROAD, GetRoadTramType(v->roadtype)); + TrackStatus ts = GetTileTrackStatus(tile, TRANSPORT_ROAD, ((v->roadtype + 1) << 8) | GetRoadTramType(v->roadtype)); TrackdirBits red_signals = TrackStatusToRedSignals(ts); // crossing TrackdirBits trackdirs = TrackStatusToTrackdirBits(ts); diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index d3edc4655b..4bf2be175e 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -3877,7 +3877,7 @@ bool AfterLoadGame() if (SlXvIsFeatureMissing(XSLFI_SAFER_CROSSINGS)) { for (TileIndex t = 0; t < map_size; t++) { if (IsLevelCrossingTile(t)) { - SetCrossingOccupiedByRoadVehicle(t, EnsureNoRoadVehicleOnGround(t).Failed()); + SetCrossingOccupiedByRoadVehicle(t, IsTrainCollidableRoadVehicleOnGround(t)); } } } diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index fce7657d79..fb7d9ed2ee 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -3792,7 +3792,7 @@ static TrackStatus GetTileTrackStatus_Station(TileIndex tile, TransportType mode case TRANSPORT_ROAD: if (IsAnyRoadStop(tile)) { - RoadTramType rtt = (RoadTramType)sub_mode; + RoadTramType rtt = (RoadTramType)GB(sub_mode, 0, 8); if (!HasTileRoadType(tile, rtt)) break; DiagDirection dir = GetRoadStopDir(tile); diff --git a/src/table/newgrf_debug_data.h b/src/table/newgrf_debug_data.h index 553e67df0e..4defcfef55 100644 --- a/src/table/newgrf_debug_data.h +++ b/src/table/newgrf_debug_data.h @@ -1791,10 +1791,11 @@ class NIHRoadType : public NIHelper { HasBit(rti->flags, ROTF_HIDDEN) ? 'h' : '-', HasBit(rti->flags, ROTF_TOWN_BUILD) ? 'T' : '-'); output.print(buffer); - seprintf(buffer, lastof(buffer), " Extra Flags: %c%c%c", + seprintf(buffer, lastof(buffer), " Extra Flags: %c%c%c%c", HasBit(rti->extra_flags, RXTF_NOT_AVAILABLE_AI_GS) ? 's' : '-', HasBit(rti->extra_flags, RXTF_NO_TOWN_MODIFICATION) ? 't' : '-', - HasBit(rti->extra_flags, RXTF_NO_TUNNELS) ? 'T' : '-'); + HasBit(rti->extra_flags, RXTF_NO_TUNNELS) ? 'T' : '-', + HasBit(rti->extra_flags, RXTF_NO_TRAIN_COLLISION) ? 'c' : '-'); output.print(buffer); seprintf(buffer, lastof(buffer), " Collision mode: %u", rti->collision_mode); output.print(buffer); diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index e24c0acb65..605490f984 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -2729,7 +2729,7 @@ void MarkDirtyAdjacentLevelCrossingTilesOnAddRemove(TileIndex tile, Axis road_ax bool IsCrossingOccupiedByRoadVehicle(TileIndex t) { if (!IsCrossingPossiblyOccupiedByRoadVehicle(t)) return false; - const bool occupied = EnsureNoRoadVehicleOnGround(t).Failed(); + const bool occupied = IsTrainCollidableRoadVehicleOnGround(t); SetCrossingOccupiedByRoadVehicle(t, occupied); return occupied; } diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 85d0d27adf..400fe8969b 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -2857,13 +2857,13 @@ extern const TrackBits _road_trackbits[16]; static TrackStatus GetTileTrackStatus_TunnelBridge(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side) { TransportType transport_type = GetTunnelBridgeTransportType(tile); - if (transport_type != mode || (transport_type == TRANSPORT_ROAD && !HasTileRoadType(tile, (RoadTramType)sub_mode))) return 0; + if (transport_type != mode || (transport_type == TRANSPORT_ROAD && !HasTileRoadType(tile, (RoadTramType)GB(sub_mode, 0, 8)))) return 0; DiagDirection dir = GetTunnelBridgeDirection(tile); if (side != INVALID_DIAGDIR && side == dir) return 0; if (mode == TRANSPORT_ROAD && IsRoadCustomBridgeHeadTile(tile)) { - TrackBits bits = _road_trackbits[GetCustomBridgeHeadRoadBits(tile, (RoadTramType)sub_mode)]; + TrackBits bits = _road_trackbits[GetCustomBridgeHeadRoadBits(tile, (RoadTramType)GB(sub_mode, 0, 8))]; return CombineTrackStatus(TrackBitsToTrackdirBits(bits), TRACKDIR_BIT_NONE); } return CombineTrackStatus(TrackBitsToTrackdirBits(mode == TRANSPORT_RAIL ? GetTunnelBridgeTrackBits(tile) : DiagDirToDiagTrackBits(dir)), TRACKDIR_BIT_NONE); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 7ef8264928..e7502ebf0a 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -561,6 +561,22 @@ static Vehicle *EnsureNoVehicleProcZ(Vehicle *v, void *data) return v; } +/** + * Callback that returns 'real' train-collidable road vehicles lower or at height \c *(int*)data . + * @param v Vehicle to examine. + * @param data Pointer to height data. + * @return \a v if conditions are met, else \c nullptr. + */ +static Vehicle *EnsureNoTrainCollidableRoadVehicleProcZ(Vehicle *v, void *data) +{ + int z = static_cast(reinterpret_cast(data)); + + if (v->z_pos > z) return nullptr; + if (HasBit(_roadtypes_non_train_colliding, RoadVehicle::From(v)->roadtype)) return nullptr; + + return v; +} + /** * Callback that returns 'real' vehicles lower or at height \c *(int*)data . * @param v Vehicle to examine. @@ -623,6 +639,13 @@ CommandCost EnsureNoRoadVehicleOnGround(TileIndex tile) return CommandCost(); } +bool IsTrainCollidableRoadVehicleOnGround(TileIndex tile) +{ + int z = GetTileMaxPixelZ(tile); + + return VehicleFromPos(tile, VEH_ROAD, reinterpret_cast(static_cast(z)), &EnsureNoTrainCollidableRoadVehicleProcZ, true) != nullptr; +} + struct GetVehicleTunnelBridgeProcData { const Vehicle *v; TileIndex t; diff --git a/src/vehicle_func.h b/src/vehicle_func.h index 6ae95c9fa6..61485152f0 100644 --- a/src/vehicle_func.h +++ b/src/vehicle_func.h @@ -241,6 +241,7 @@ static inline uint32 GetCmdSendToDepot(const BaseVehicle *v) CommandCost EnsureNoVehicleOnGround(TileIndex tile); CommandCost EnsureNoRoadVehicleOnGround(TileIndex tile); +bool IsTrainCollidableRoadVehicleOnGround(TileIndex tile); CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits); extern VehicleID _new_vehicle_id;