Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Trivial autoreplace of mixed cargo articulated engines #11253

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
46 changes: 40 additions & 6 deletions src/articulated_vehicles.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -226,30 +256,34 @@ 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;
}
}
}

v = v->HasArticulatedPart() ? v->GetNextArticulatedPart() : nullptr;
} while (v != nullptr);

if (cargo_type != nullptr) *cargo_type = first_cargo;
return false;
return cargoes;
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/articulated_vehicles.h
Expand Up @@ -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);
Expand Down
11 changes: 10 additions & 1 deletion src/autoreplace_cmd.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -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.
Expand Down
21 changes: 14 additions & 7 deletions src/vehicle.cpp
Expand Up @@ -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;
}
}
}

Expand Down