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 @@
1 | 2 | Towns may not modify tiles of this road/tram type in any way whatsoever |
2 | 4 | Disallow tunnels for this road/tram type
Support for this bit is indicated by the feature name: action0_roadtype_extra_flags, version 2. |
+ 3 | 8 | Disallow 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;