diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 3681b4a878..d9353527c9 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -2027,6 +2027,70 @@ static Vehicle *UpdateTrainPowerProc(Vehicle *v, void *data) return nullptr; } +struct EnsureNoIncompatibleRailtypeTrainOnGroundData { + int z; + RailType type; +}; + +static Vehicle *EnsureNoIncompatibleRailtypeTrainProc(Vehicle *v, void *data) +{ + const EnsureNoIncompatibleRailtypeTrainOnGroundData *procdata = (EnsureNoIncompatibleRailtypeTrainOnGroundData *)data; + + if (v->z_pos > procdata->z) return nullptr; + if (HasBit(Train::From(v)->First()->compatible_railtypes, procdata->type)) return nullptr; + + return v; +} + +CommandCost EnsureNoIncompatibleRailtypeTrainOnGround(TileIndex tile, RailType type) +{ + EnsureNoIncompatibleRailtypeTrainOnGroundData data = { + GetTileMaxPixelZ(tile), + type + }; + + if (HasVehicleOnPos(tile, VEH_TRAIN, &data, &EnsureNoIncompatibleRailtypeTrainProc)) { + return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY); + } + return CommandCost(); +} + +struct EnsureNoIncompatibleRailtypeTrainOnTrackBitsData { + TrackBits track_bits; + RailType type; +}; + +static Vehicle *EnsureNoIncompatibleRailtypeTrainOnTrackProc(Vehicle *v, void *data) +{ + const EnsureNoIncompatibleRailtypeTrainOnTrackBitsData *procdata = (EnsureNoIncompatibleRailtypeTrainOnTrackBitsData *)data; + TrackBits rail_bits = procdata->track_bits; + + Train *t = Train::From(v); + if (HasBit(t->First()->compatible_railtypes, procdata->type)) return nullptr; + if (rail_bits & TRACK_BIT_WORMHOLE) { + if (t->track & TRACK_BIT_WORMHOLE) return v; + rail_bits &= ~TRACK_BIT_WORMHOLE; + } else if (t->track & TRACK_BIT_WORMHOLE) { + return nullptr; + } + if ((t->track != rail_bits) && !TracksOverlap(t->track | rail_bits)) return nullptr; + + return v; +} + +CommandCost EnsureNoIncompatibleRailtypeTrainOnTrackBits(TileIndex tile, TrackBits track_bits, RailType type) +{ + EnsureNoIncompatibleRailtypeTrainOnTrackBitsData data = { + track_bits, + type + }; + + if (HasVehicleOnPos(tile, VEH_TRAIN, &data, &EnsureNoIncompatibleRailtypeTrainOnTrackProc)) { + return_cmd_error(STR_ERROR_TRAIN_IN_THE_WAY); + } + return CommandCost(); +} + /** * Convert one rail type to the other. You can convert normal rail to * monorail/maglev easily or vice-versa. @@ -2119,7 +2183,7 @@ CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 * Tunnels and bridges have special check later */ if (tt != MP_TUNNELBRIDGE) { if (!IsCompatibleRail(type, totype) || !IsCompatibleRail(secondary_type, totype)) { - CommandCost ret = IsPlainRailTile(tile) ? EnsureNoTrainOnTrackBits(tile, GetTrackBits(tile)) : EnsureNoVehicleOnGround(tile); + CommandCost ret = IsPlainRailTile(tile) ? EnsureNoIncompatibleRailtypeTrainOnTrackBits(tile, GetTrackBits(tile), totype) : EnsureNoIncompatibleRailtypeTrainOnGround(tile, totype); if (ret.Failed()) { error = ret; continue;