diff --git a/src/lang/english.txt b/src/lang/english.txt index 0a8a84a3d6..80c9dd9e3f 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5085,6 +5085,20 @@ STR_ERROR_CAN_T_COPY_ORDER_LIST :{WHITE}Can't co STR_ERROR_TOO_FAR_FROM_PREVIOUS_DESTINATION :{WHITE}... too far from previous destination STR_ERROR_AIRCRAFT_NOT_ENOUGH_RANGE :{WHITE}... aircraft has not enough range +# Extra messages which go on the third line of errors, explaining why orders failed +STR_ERROR_NO_RAIL_STATION :{WHITE}There is no railway station +STR_ERROR_NO_BUS_STATION :{WHITE}There is no bus station +STR_ERROR_NO_TRUCK_STATION :{WHITE}There is no lorry station +STR_ERROR_NO_DOCK :{WHITE}There is no dock +STR_ERROR_NO_AIRPORT :{WHITE}There is no airport/heliport +STR_ERROR_NO_STOP_COMPATIBLE_ROAD_TYPE :{WHITE}There are no stops with a compatible road type +STR_ERROR_NO_STOP_COMPATIBLE_TRAM_TYPE :{WHITE}There are no stops with a compatible tram type +STR_ERROR_NO_STOP_ARTICULATED_VEHICLE :{WHITE}There are no stops which are suitable for articulated road vehicles.{}Articulated road vehicles require a drive-through stop, not a bay stop +STR_ERROR_AIRPORT_NO_PLANES :{WHITE}This plane cannot land at this heliport +STR_ERROR_AIRPORT_NO_HELICOPTERS :{WHITE}This helicopter cannot land at this airport +STR_ERROR_NO_RAIL_WAYPOINT :{WHITE}There is no railway waypoint +STR_ERROR_NO_BUOY :{WHITE}There is no buoy + # Timetable related errors STR_ERROR_CAN_T_TIMETABLE_VEHICLE :{WHITE}Can't timetable vehicle... STR_ERROR_TIMETABLE_ONLY_WAIT_AT_STATIONS :{WHITE}Vehicles can only wait at stations diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 5ce15f93e0..56c306856f 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -758,9 +758,9 @@ CommandCost CmdInsertOrder(DoCommandFlag flags, VehicleID veh, VehicleOrderID se if (ret.Failed()) return ret; } - if (!CanVehicleUseStation(v, st)) return_cmd_error(STR_ERROR_CAN_T_ADD_ORDER); + if (!CanVehicleUseStation(v, st)) return CommandCost(STR_ERROR_CAN_T_ADD_ORDER, GetVehicleCannotUseStationReason(v, st)); for (Vehicle *u = v->FirstShared(); u != nullptr; u = u->NextShared()) { - if (!CanVehicleUseStation(u, st)) return_cmd_error(STR_ERROR_CAN_T_ADD_ORDER_SHARED); + if (!CanVehicleUseStation(u, st)) return CommandCost(STR_ERROR_CAN_T_ADD_ORDER_SHARED, GetVehicleCannotUseStationReason(u, st)); } /* Non stop only allowed for ground vehicles. */ @@ -847,7 +847,7 @@ CommandCost CmdInsertOrder(DoCommandFlag flags, VehicleID veh, VehicleOrderID se default: return CMD_ERROR; case VEH_TRAIN: { - if (!(wp->facilities & FACIL_TRAIN)) return_cmd_error(STR_ERROR_CAN_T_ADD_ORDER); + if (!(wp->facilities & FACIL_TRAIN)) return CommandCost(STR_ERROR_CAN_T_ADD_ORDER, STR_ERROR_NO_RAIL_WAYPOINT); ret = CheckOwnership(wp->owner); if (ret.Failed()) return ret; @@ -855,7 +855,7 @@ CommandCost CmdInsertOrder(DoCommandFlag flags, VehicleID veh, VehicleOrderID se } case VEH_SHIP: - if (!(wp->facilities & FACIL_DOCK)) return_cmd_error(STR_ERROR_CAN_T_ADD_ORDER); + if (!(wp->facilities & FACIL_DOCK)) return CommandCost(STR_ERROR_CAN_T_ADD_ORDER, STR_ERROR_NO_BUOY); if (wp->owner != OWNER_NONE) { ret = CheckOwnership(wp->owner); if (ret.Failed()) return ret; @@ -1533,7 +1533,7 @@ CommandCost CmdCloneOrder(DoCommandFlag flags, CloneOptions action, VehicleID ve * are temporarily invalid due to reconstruction. */ const Station *st = Station::Get(order->GetDestination()); if (CanVehicleUseStation(src, st) && !CanVehicleUseStation(dst, st)) { - return_cmd_error(STR_ERROR_CAN_T_COPY_SHARE_ORDER); + return CommandCost(STR_ERROR_CAN_T_COPY_SHARE_ORDER, GetVehicleCannotUseStationReason(dst, st)); } } @@ -1577,9 +1577,9 @@ CommandCost CmdCloneOrder(DoCommandFlag flags, CloneOptions action, VehicleID ve /* Trucks can't copy all the orders from busses (and visa versa), * and neither can helicopters and aircraft. */ for (const Order *order : src->Orders()) { - if (OrderGoesToStation(dst, order) && - !CanVehicleUseStation(dst, Station::Get(order->GetDestination()))) { - return_cmd_error(STR_ERROR_CAN_T_COPY_SHARE_ORDER); + Station *st = Station::Get(order->GetDestination()); + if (OrderGoesToStation(dst, order) && !CanVehicleUseStation(dst, st)) { + return CommandCost(STR_ERROR_CAN_T_COPY_SHARE_ORDER, GetVehicleCannotUseStationReason(dst, st)); } } diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 678b7db0ac..bf35856f88 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -2903,6 +2903,58 @@ bool CanVehicleUseStation(const Vehicle *v, const Station *st) return CanVehicleUseStation(v->engine_type, st); } +/** + * Get reason string why this station can't be used by the given vehicle. + * @param v The vehicle to test. + * @param st The station to test for. + * @return The string explaining why the vehicle cannot use the station. + */ +StringID GetVehicleCannotUseStationReason(const Vehicle *v, const Station *st) +{ + switch (v->type) { + case VEH_TRAIN: + return STR_ERROR_NO_RAIL_STATION; + + case VEH_ROAD: { + const RoadVehicle *rv = RoadVehicle::From(v); + RoadStop *rs = st->GetPrimaryRoadStop(rv->IsBus() ? ROADSTOP_BUS : ROADSTOP_TRUCK); + + StringID err = rv->IsBus() ? STR_ERROR_NO_BUS_STATION : STR_ERROR_NO_TRUCK_STATION; + + for (; rs != nullptr; rs = rs->next) { + /* Articulated vehicles cannot use bay road stops, only drive-through. Make sure the vehicle can actually use this bay stop */ + if (HasTileAnyRoadType(rs->xy, rv->compatible_roadtypes) && IsStandardRoadStopTile(rs->xy) && rv->HasArticulatedPart()) { + err = STR_ERROR_NO_STOP_ARTICULATED_VEHICLE; + continue; + } + + /* Bay stop errors take precedence, but otherwise the vehicle may not be compatible with the roadtype/tramtype of this station tile. + * We give bay stop errors precedence because they are usually a bus sent to a tram station or vice versa. */ + if (!HasTileAnyRoadType(rs->xy, rv->compatible_roadtypes) && err != STR_ERROR_NO_STOP_ARTICULATED_VEHICLE) { + err = RoadTypeIsRoad(rv->roadtype) ? STR_ERROR_NO_STOP_COMPATIBLE_ROAD_TYPE : STR_ERROR_NO_STOP_COMPATIBLE_TRAM_TYPE; + continue; + } + } + + return err; + } + + case VEH_SHIP: + return STR_ERROR_NO_DOCK; + + case VEH_AIRCRAFT: + if ((st->facilities & FACIL_AIRPORT) == 0) return STR_ERROR_NO_AIRPORT; + if (v->GetEngine()->u.air.subtype & AIR_CTOL) { + return STR_ERROR_AIRPORT_NO_PLANES; + } else { + return STR_ERROR_AIRPORT_NO_HELICOPTERS; + } + + default: + return INVALID_STRING_ID; + } +} + /** * Access the ground vehicle cache of the vehicle. * @pre The vehicle is a #GroundVehicle. diff --git a/src/vehicle_func.h b/src/vehicle_func.h index 13fc352e46..0efd4fd878 100644 --- a/src/vehicle_func.h +++ b/src/vehicle_func.h @@ -166,6 +166,7 @@ CommandCost EnsureNoTrainOnTrackBits(TileIndex tile, TrackBits track_bits); bool CanVehicleUseStation(EngineID engine_type, const struct Station *st); bool CanVehicleUseStation(const Vehicle *v, const struct Station *st); +StringID GetVehicleCannotUseStationReason(const Vehicle *v, const Station *st); void ReleaseDisastersTargetingVehicle(VehicleID vehicle);