diff --git a/src/articulated_vehicles.cpp b/src/articulated_vehicles.cpp index 271f0c5a0c..194f581a62 100644 --- a/src/articulated_vehicles.cpp +++ b/src/articulated_vehicles.cpp @@ -8,6 +8,7 @@ /** @file articulated_vehicles.cpp Implementation of articulated vehicles. */ #include "stdafx.h" +#include "core/bitmath_func.hpp" #include "core/random_func.hpp" #include "train.h" #include "roadveh.h" @@ -200,6 +201,35 @@ CargoArray GetCapacityOfArticulatedParts(EngineID engine) return capacity; } +/** + * Get the cargo mask of the parts of a given engine. + * @param engine The engine to get the capacities from. + * @return The cargo mask. + */ +CargoTypes GetCargoTypesOfArticulatedParts(EngineID engine) +{ + CargoTypes cargoes = 0; + const Engine *e = Engine::Get(engine); + + CargoID cargo_type; + uint16 cargo_capacity = GetVehicleDefaultCapacity(engine, &cargo_type); + if (cargo_type < NUM_CARGO && cargo_capacity > 0) SetBit(cargoes, cargo_type); + + if (!e->IsArticulatedCallbackVehicleType()) return cargoes; + + if (!HasBit(e->info.callback_mask, CBM_VEHICLE_ARTIC_ENGINE)) return cargoes; + + for (uint i = 1; i < MAX_ARTICULATED_PARTS; i++) { + EngineID artic_engine = GetNextArticulatedPart(i, engine); + if (artic_engine == INVALID_ENGINE) break; + + cargo_capacity = GetVehicleDefaultCapacity(artic_engine, &cargo_type); + if (cargo_type < NUM_CARGO && cargo_capacity > 0) SetBit(cargoes, cargo_type); + } + + return cargoes; +} + /** * Checks whether any of the articulated parts is refittable * @param engine the first part @@ -277,24 +307,27 @@ CargoTypes GetIntersectionOfArticulatedRefitMasks(EngineID engine, bool include_ return intersection_mask; } - /** - * Tests if all parts of an articulated vehicle are refitted to the same cargo. + * Get cargo mask of all cargoes carried by an articulated vehicle. * Note: Vehicles not carrying anything are ignored * @param v the first vehicle in the chain * @param cargo_type returns the common CargoID if needed. (CT_INVALID if no part is carrying something or they are carrying different things) - * @return true if some parts are carrying different cargoes, false if all parts are carrying the same (nothing is also the same) + * @return cargo mask, may be 0 if the no vehicle parts have cargo capacity */ -bool IsArticulatedVehicleCarryingDifferentCargoes(const Vehicle *v, CargoID *cargo_type) +CargoTypes GetCargoTypesOfArticulatedVehicle(const Vehicle *v, CargoID *cargo_type) { + CargoTypes cargoes = 0; CargoID first_cargo = CT_INVALID; do { if (v->cargo_type != CT_INVALID && v->GetEngine()->CanCarryCargo()) { + SetBit(cargoes, v->cargo_type); if (first_cargo == CT_INVALID) first_cargo = v->cargo_type; if (first_cargo != v->cargo_type) { - if (cargo_type != nullptr) *cargo_type = CT_INVALID; - return true; + if (cargo_type != nullptr) { + *cargo_type = CT_INVALID; + cargo_type = nullptr; + } } } @@ -302,7 +335,7 @@ bool IsArticulatedVehicleCarryingDifferentCargoes(const Vehicle *v, CargoID *car } while (v != nullptr); if (cargo_type != nullptr) *cargo_type = first_cargo; - return false; + return cargoes; } /** @@ -314,7 +347,7 @@ bool IsArticulatedVehicleCarryingDifferentCargoes(const Vehicle *v, CargoID *car CargoID GetOverallCargoOfArticulatedVehicle(const Vehicle *v) { CargoID cargo_id; - IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_id); + GetCargoTypesOfArticulatedVehicle(v, &cargo_id); return cargo_id; } diff --git a/src/articulated_vehicles.h b/src/articulated_vehicles.h index 559a82cb2c..e287787413 100644 --- a/src/articulated_vehicles.h +++ b/src/articulated_vehicles.h @@ -17,11 +17,12 @@ uint CountArticulatedParts(EngineID engine_type, bool purchase_window); void GetArticulatedPartsEngineIDs(EngineID engine_type, bool purchase_window, std::vector &ids); CargoArray GetCapacityOfArticulatedParts(EngineID engine); +CargoTypes GetCargoTypesOfArticulatedParts(EngineID engine); void AddArticulatedParts(Vehicle *first); void GetArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type, CargoTypes *union_mask, CargoTypes *intersection_mask); CargoTypes GetUnionOfArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type); CargoTypes GetIntersectionOfArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type); -bool IsArticulatedVehicleCarryingDifferentCargoes(const Vehicle *v, CargoID *cargo_type); +CargoTypes GetCargoTypesOfArticulatedVehicle(const Vehicle *v, CargoID *cargo_type); CargoID GetOverallCargoOfArticulatedVehicle(const Vehicle *v); bool IsArticulatedVehicleRefittable(EngineID engine); bool IsArticulatedEngine(EngineID engine_type); diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index 00b665d84e..434f925ee5 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -17,6 +17,7 @@ #include "autoreplace_gui.h" #include "articulated_vehicles.h" #include "tracerestrict.h" +#include "core/bitmath_func.hpp" #include "core/random_func.hpp" #include "vehiclelist.h" #include "road.h" @@ -232,7 +233,15 @@ static CargoID GetNewCargoTypeForReplace(Vehicle *v, EngineID engine_type, bool if (union_mask == 0) return CT_NO_REFIT; // Don't try to refit an engine with no cargo capacity CargoID cargo_type; - if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) return CT_INVALID; // We cannot refit to mixed cargoes in an automated way + CargoTypes cargo_mask = GetCargoTypesOfArticulatedVehicle(v, &cargo_type); + if (!HasAtMostOneBit(cargo_mask)) { + CargoTypes new_engine_default_cargoes = GetCargoTypesOfArticulatedParts(engine_type); + if ((cargo_mask & new_engine_default_cargoes) == cargo_mask) { + return CT_NO_REFIT; // engine_type is already a mixed cargo type which matches the incoming vehicle by default, no refit required + } + + return CT_INVALID; // We cannot refit to mixed cargoes in an automated way + } if (cargo_type == CT_INVALID) { if (v->type != VEH_TRAIN) return CT_NO_REFIT; // If the vehicle does not carry anything at all, every replacement is fine. diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 5983266f06..44a561c2db 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -291,13 +291,20 @@ bool Vehicle::NeedsServicing() const /* Is there anything to refit? */ if (union_mask != 0) { CargoID cargo_type; - /* We cannot refit to mixed cargoes in an automated way */ - if (IsArticulatedVehicleCarryingDifferentCargoes(v, &cargo_type)) continue; - - /* Did the old vehicle carry anything? */ - if (cargo_type != CT_INVALID) { - /* We can't refit the vehicle to carry the cargo we want */ - if (!HasBit(available_cargo_types, cargo_type)) continue; + CargoTypes cargo_mask = GetCargoTypesOfArticulatedVehicle(v, &cargo_type); + if (!HasAtMostOneBit(cargo_mask)) { + CargoTypes new_engine_default_cargoes = GetCargoTypesOfArticulatedParts(engine_type); + if ((cargo_mask & new_engine_default_cargoes) != cargo_mask) { + /* We cannot refit to mixed cargoes in an automated way */ + continue; + } + /* engine_type is already a mixed cargo type which matches the incoming vehicle by default, no refit required */ + } else { + /* Did the old vehicle carry anything? */ + if (cargo_type != CT_INVALID) { + /* We can't refit the vehicle to carry the cargo we want */ + if (!HasBit(available_cargo_types, cargo_type)) continue; + } } }