From 9822fa658428c6e4370201536af7c02a2f10620d Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Mon, 20 Nov 2023 13:16:28 +0000 Subject: [PATCH] Fix: Trivial autoreplace of mixed cargo articulated engines (#11253) Do not fail autoreplace/autorenew of mixed cargo articulated engines due to an inability to refit to mixed cargoes, when no refit is required because the target engine already has a suitable set of cargoes. Notably, this allows autorenew (autoreplace to same engine type) to succeed. --- src/articulated_vehicles.cpp | 46 +++++++++++++++++++++++++++++++----- src/articulated_vehicles.h | 3 ++- src/autoreplace_cmd.cpp | 11 ++++++++- src/vehicle.cpp | 21 ++++++++++------ 4 files changed, 66 insertions(+), 15 deletions(-) diff --git a/src/articulated_vehicles.cpp b/src/articulated_vehicles.cpp index cfaab25986..51953b1601 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" @@ -161,6 +162,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_t cargo_capacity = GetVehicleDefaultCapacity(engine, &cargo_type); + if (cargo_type < NUM_CARGO && cargo_capacity > 0) SetBit(cargoes, cargo_type); + + if (!e->IsGroundVehicle()) 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 @@ -226,22 +256,26 @@ CargoTypes GetUnionOfArticulatedRefitMasks(EngineID engine, bool include_initial } /** - * 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 (IsValidCargoID(v->cargo_type) && v->GetEngine()->CanCarryCargo()) { + SetBit(cargoes, v->cargo_type); if (!IsValidCargoID(first_cargo)) 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; + } } } @@ -249,7 +283,7 @@ bool IsArticulatedVehicleCarryingDifferentCargoes(const Vehicle *v, CargoID *car } while (v != nullptr); if (cargo_type != nullptr) *cargo_type = first_cargo; - return false; + return cargoes; } /** diff --git a/src/articulated_vehicles.h b/src/articulated_vehicles.h index 819c27d3c1..91195dfe8b 100644 --- a/src/articulated_vehicles.h +++ b/src/articulated_vehicles.h @@ -15,10 +15,11 @@ uint CountArticulatedParts(EngineID engine_type, bool purchase_window); 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); -bool IsArticulatedVehicleCarryingDifferentCargoes(const Vehicle *v, CargoID *cargo_type); +CargoTypes GetCargoTypesOfArticulatedVehicle(const Vehicle *v, CargoID *cargo_type); bool IsArticulatedVehicleRefittable(EngineID engine); bool IsArticulatedEngine(EngineID engine_type); void CheckConsistencyOfArticulatedVehicle(const Vehicle *v); diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index 3f98666ec1..18883d009e 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -16,6 +16,7 @@ #include "autoreplace_func.h" #include "autoreplace_gui.h" #include "articulated_vehicles.h" +#include "core/bitmath_func.hpp" #include "core/random_func.hpp" #include "vehiclelist.h" #include "road.h" @@ -236,7 +237,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 (!IsValidCargoID(cargo_type)) { 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 e2cf5ba841..609c7a1a3e 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -229,13 +229,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 (IsValidCargoID(cargo_type)) { - /* 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(new_engine); + 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 (IsValidCargoID(cargo_type)) { + /* We can't refit the vehicle to carry the cargo we want */ + if (!HasBit(available_cargo_types, cargo_type)) continue; + } } }