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.
This commit is contained in:
Jonathan G Rennison 2023-11-20 13:16:28 +00:00 committed by GitHub
parent 96d98d08c8
commit 9822fa6584
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 66 additions and 15 deletions

View File

@ -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;
}
/**

View File

@ -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);

View File

@ -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.

View File

@ -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;
}
}
}