Refactor Link Refresher cargo mask handling.
Add some separate helper functions. Move cargo checks out of LinkRefresher::Run.
This commit is contained in:
@@ -24,61 +24,43 @@
|
|||||||
* @param v Vehicle to refresh links for.
|
* @param v Vehicle to refresh links for.
|
||||||
* @param allow_merge If the refresher is allowed to merge or extend link graphs.
|
* @param allow_merge If the refresher is allowed to merge or extend link graphs.
|
||||||
* @param is_full_loading If the vehicle is full loading.
|
* @param is_full_loading If the vehicle is full loading.
|
||||||
|
* @param cargo_mask Mask of cargoes to refresh
|
||||||
*/
|
*/
|
||||||
/* static */ void LinkRefresher::Run(Vehicle *v, bool allow_merge, bool is_full_loading, bool check_cargo_can_leave)
|
/* static */ void LinkRefresher::Run(Vehicle *v, bool allow_merge, bool is_full_loading, uint32 cargo_mask)
|
||||||
{
|
{
|
||||||
/* If there are no orders we can't predict anything.*/
|
/* If there are no orders we can't predict anything.*/
|
||||||
if (v->orders.list == NULL) return;
|
if (v->orders.list == NULL) return;
|
||||||
|
|
||||||
|
uint32 have_cargo_mask = v->GetLastLoadingStationValidCargoMask();
|
||||||
|
|
||||||
/* Scan orders for cargo-specific load/unload, and run LinkRefresher separately for each set of cargoes where they differ. */
|
/* Scan orders for cargo-specific load/unload, and run LinkRefresher separately for each set of cargoes where they differ. */
|
||||||
uint32 cargoes_to_check = ~0;
|
while (cargo_mask != 0) {
|
||||||
while (cargoes_to_check != 0) {
|
uint32 iter_cargo_mask = cargo_mask;
|
||||||
uint32 cargo_mask = cargoes_to_check;
|
|
||||||
const CargoID first_cargo_id = FindFirstBit(cargo_mask);
|
|
||||||
for (const Order *o = v->orders.list->GetFirstOrder(); o != NULL; o = o->next) {
|
for (const Order *o = v->orders.list->GetFirstOrder(); o != NULL; o = o->next) {
|
||||||
if (o->IsType(OT_GOTO_STATION) || o->IsType(OT_IMPLICIT)) {
|
if (o->IsType(OT_GOTO_STATION) || o->IsType(OT_IMPLICIT)) {
|
||||||
if (o->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD) {
|
if (o->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD) {
|
||||||
CargoMaskValueFilter<uint>(cargo_mask, [&](CargoID cargo) -> uint {
|
CargoMaskValueFilter<uint>(iter_cargo_mask, [&](CargoID cargo) -> uint {
|
||||||
return o->GetCargoUnloadType(cargo) & (OUFB_TRANSFER | OUFB_UNLOAD | OUFB_NO_UNLOAD);
|
return o->GetCargoUnloadType(cargo) & (OUFB_TRANSFER | OUFB_UNLOAD | OUFB_NO_UNLOAD);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (o->GetLoadType() == OLFB_CARGO_TYPE_LOAD) {
|
if (o->GetLoadType() == OLFB_CARGO_TYPE_LOAD) {
|
||||||
CargoMaskValueFilter<uint>(cargo_mask, [&](CargoID cargo) -> uint {
|
CargoMaskValueFilter<uint>(iter_cargo_mask, [&](CargoID cargo) -> uint {
|
||||||
return o->GetCargoLoadType(cargo) & (OLFB_NO_LOAD);
|
return o->GetCargoLoadType(cargo) & (OLFB_NO_LOAD);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_cargo;
|
|
||||||
if (!HasBit(v->vehicle_flags, VF_LAST_LOAD_ST_SEP)) {
|
|
||||||
has_cargo = v->last_loading_station != INVALID_STATION;
|
|
||||||
} else {
|
|
||||||
has_cargo = false;
|
|
||||||
for (const Vehicle *u = v; u != NULL; u = u->Next()) {
|
|
||||||
if (u->cargo_type < NUM_CARGO && HasBit(cargo_mask, u->cargo_type) && u->last_loading_station != INVALID_STATION) {
|
|
||||||
has_cargo = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (check_cargo_can_leave && (v->current_order.IsType(OT_GOTO_STATION) || v->current_order.IsType(OT_IMPLICIT)) &&
|
|
||||||
(!v->current_order.CanLeaveWithCargo(has_cargo, first_cargo_id))) {
|
|
||||||
cargoes_to_check &= ~cargo_mask;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make sure the first order is a useful order. */
|
/* Make sure the first order is a useful order. */
|
||||||
const Order *first = v->orders.list->GetNextDecisionNode(v->GetOrder(v->cur_implicit_order_index), 0, cargo_mask);
|
const Order *first = v->orders.list->GetNextDecisionNode(v->GetOrder(v->cur_implicit_order_index), 0, iter_cargo_mask);
|
||||||
if (first != NULL) {
|
if (first != NULL) {
|
||||||
HopSet seen_hops;
|
HopSet seen_hops;
|
||||||
LinkRefresher refresher(v, &seen_hops, allow_merge, is_full_loading, cargo_mask);
|
LinkRefresher refresher(v, &seen_hops, allow_merge, is_full_loading, iter_cargo_mask);
|
||||||
|
|
||||||
refresher.RefreshLinks(first, first, has_cargo ? 1 << HAS_CARGO : 0);
|
refresher.RefreshLinks(first, first, (iter_cargo_mask & have_cargo_mask) ? 1 << HAS_CARGO : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
cargoes_to_check &= ~cargo_mask;
|
cargo_mask &= ~iter_cargo_mask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -23,7 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
class LinkRefresher {
|
class LinkRefresher {
|
||||||
public:
|
public:
|
||||||
static void Run(Vehicle *v, bool allow_merge = true, bool is_full_loading = false, bool check_cargo_can_leave = false);
|
static void Run(Vehicle *v, bool allow_merge = true, bool is_full_loading = false, uint32 cargo_mask = ~0);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
|
@@ -217,6 +217,20 @@ public:
|
|||||||
return ouf;
|
return ouf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename F> uint32 FilterLoadUnloadTypeCargoMask(F filter_func, uint32 cargo_mask = ~0)
|
||||||
|
{
|
||||||
|
if ((this->GetLoadType() == OLFB_CARGO_TYPE_LOAD) || (this->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD)) {
|
||||||
|
CargoID cargo;
|
||||||
|
uint32 output_mask = cargo_mask;
|
||||||
|
FOR_EACH_SET_BIT(cargo, cargo_mask) {
|
||||||
|
if (!filter_func(this, cargo)) ClrBit(output_mask, cargo);
|
||||||
|
}
|
||||||
|
return output_mask;
|
||||||
|
} else {
|
||||||
|
return filter_func(this, FindFirstBit(cargo_mask)) ? cargo_mask : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** At which stations must we stop? */
|
/** At which stations must we stop? */
|
||||||
inline OrderNonStopFlags GetNonStopType() const { return (OrderNonStopFlags)GB(this->type, 6, 2); }
|
inline OrderNonStopFlags GetNonStopType() const { return (OrderNonStopFlags)GB(this->type, 6, 2); }
|
||||||
/** Where must we stop at the platform? */
|
/** Where must we stop at the platform? */
|
||||||
@@ -389,6 +403,16 @@ public:
|
|||||||
void FillNextStoppingStation(const Vehicle *v, const OrderList *o, const Order *first = NULL, uint hops = 0);
|
void FillNextStoppingStation(const Vehicle *v, const OrderList *o, const Order *first = NULL, uint hops = 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename F> uint32 FilterCargoMask(F filter_func, uint32 cargo_mask = ~0)
|
||||||
|
{
|
||||||
|
CargoID cargo;
|
||||||
|
uint32 output_mask = cargo_mask;
|
||||||
|
FOR_EACH_SET_BIT(cargo, cargo_mask) {
|
||||||
|
if (!filter_func(cargo)) ClrBit(output_mask, cargo);
|
||||||
|
}
|
||||||
|
return output_mask;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T, typename F> T CargoMaskValueFilter(uint32 &cargo_mask, F filter_func)
|
template <typename T, typename F> T CargoMaskValueFilter(uint32 &cargo_mask, F filter_func)
|
||||||
{
|
{
|
||||||
CargoID first_cargo_id = FindFirstBit(cargo_mask);
|
CargoID first_cargo_id = FindFirstBit(cargo_mask);
|
||||||
|
@@ -2100,6 +2100,21 @@ void Vehicle::CancelReservation(StationID next, Station *st)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32 Vehicle::GetLastLoadingStationValidCargoMask() const
|
||||||
|
{
|
||||||
|
if (!HasBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP)) {
|
||||||
|
return (this->last_loading_station != INVALID_STATION) ? ~0 : 0;
|
||||||
|
} else {
|
||||||
|
uint32 cargo_mask = 0;
|
||||||
|
for (const Vehicle *u = this; u != NULL; u = u->Next()) {
|
||||||
|
if (u->cargo_type < NUM_CARGO && u->last_loading_station != INVALID_STATION) {
|
||||||
|
SetBit(cargo_mask, u->cargo_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cargo_mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform all actions when leaving a station.
|
* Perform all actions when leaving a station.
|
||||||
* @pre this->current_order.IsType(OT_LOADING)
|
* @pre this->current_order.IsType(OT_LOADING)
|
||||||
@@ -2114,20 +2129,45 @@ void Vehicle::LeaveStation()
|
|||||||
/* Only update the timetable if the vehicle was supposed to stop here. */
|
/* Only update the timetable if the vehicle was supposed to stop here. */
|
||||||
if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
|
if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
|
||||||
|
|
||||||
if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
|
uint32 cargoes_can_load_unload = this->current_order.FilterLoadUnloadTypeCargoMask([&](const Order *o, CargoID cargo) {
|
||||||
(this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
|
return ((o->GetCargoLoadType(cargo) & OLFB_NO_LOAD) == 0) || ((o->GetCargoUnloadType(cargo) & OUFB_NO_UNLOAD) == 0);
|
||||||
if (HasBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP) || (this->current_order.GetLoadType() == OLFB_CARGO_TYPE_LOAD) || (this->current_order.GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD)) {
|
});
|
||||||
/* Always run these, check is handled by check_cargo_can_leave param of LinkRefresher::Run */
|
uint32 has_cargo_mask = this->GetLastLoadingStationValidCargoMask();
|
||||||
|
uint32 cargoes_can_leave_with_cargo = FilterCargoMask([&](CargoID cargo) {
|
||||||
|
return this->current_order.CanLeaveWithCargo(HasBit(has_cargo_mask, cargo), cargo);
|
||||||
|
}, cargoes_can_load_unload);
|
||||||
|
|
||||||
|
if (cargoes_can_load_unload != 0) {
|
||||||
|
if (cargoes_can_leave_with_cargo != 0) {
|
||||||
|
/* Refresh next hop stats to make sure we've done that at least once
|
||||||
|
* during the stop and that refit_cap == cargo_cap for each vehicle in
|
||||||
|
* the consist. */
|
||||||
this->ResetRefitCaps();
|
this->ResetRefitCaps();
|
||||||
LinkRefresher::Run(this, true, false, true);
|
LinkRefresher::Run(this, true, false, cargoes_can_leave_with_cargo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cargoes_can_leave_with_cargo == (uint32) ~0) {
|
||||||
|
/* can leave with all cargoes */
|
||||||
|
|
||||||
|
/* if the vehicle could load here or could stop with cargo loaded set the last loading station */
|
||||||
|
this->last_loading_station = this->last_station_visited;
|
||||||
|
ClrBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP);
|
||||||
|
} else if (cargoes_can_leave_with_cargo == 0) {
|
||||||
|
/* can leave with no cargoes */
|
||||||
|
|
||||||
|
/* if the vehicle couldn't load and had to unload or transfer everything
|
||||||
|
* set the last loading station to invalid as it will leave empty. */
|
||||||
|
this->last_loading_station = INVALID_STATION;
|
||||||
|
ClrBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP);
|
||||||
|
} else {
|
||||||
|
/* mix of cargoes loadable or could not leave with all cargoes */
|
||||||
|
|
||||||
/* NB: this is saved here as we overwrite it on the first iteration of the loop below */
|
/* NB: this is saved here as we overwrite it on the first iteration of the loop below */
|
||||||
StationID head_last_loading_station = this->last_loading_station;
|
StationID head_last_loading_station = this->last_loading_station;
|
||||||
for (Vehicle *u = this; u != NULL; u = u->Next()) {
|
for (Vehicle *u = this; u != NULL; u = u->Next()) {
|
||||||
StationID last_loading_station = HasBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP) ? u->last_loading_station : head_last_loading_station;
|
StationID last_loading_station = HasBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP) ? u->last_loading_station : head_last_loading_station;
|
||||||
if ((this->current_order.GetCargoLoadType(u->cargo_type) & OLFB_NO_LOAD) == 0 ||
|
if (u->cargo_type < NUM_CARGO && HasBit(cargoes_can_load_unload, u->cargo_type)) {
|
||||||
(this->current_order.GetCargoUnloadType(u->cargo_type) & OUFB_NO_UNLOAD) == 0) {
|
if (HasBit(cargoes_can_leave_with_cargo, u->cargo_type)) {
|
||||||
if (this->current_order.CanLeaveWithCargo(last_loading_station != INVALID_STATION, u->cargo_type)) {
|
|
||||||
u->last_loading_station = this->last_station_visited;
|
u->last_loading_station = this->last_station_visited;
|
||||||
} else {
|
} else {
|
||||||
u->last_loading_station = INVALID_STATION;
|
u->last_loading_station = INVALID_STATION;
|
||||||
@@ -2137,23 +2177,6 @@ void Vehicle::LeaveStation()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
SetBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP);
|
SetBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP);
|
||||||
} else {
|
|
||||||
if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION, 0)) {
|
|
||||||
/* Refresh next hop stats to make sure we've done that at least once
|
|
||||||
* during the stop and that refit_cap == cargo_cap for each vehicle in
|
|
||||||
* the consist. */
|
|
||||||
this->ResetRefitCaps();
|
|
||||||
LinkRefresher::Run(this);
|
|
||||||
|
|
||||||
/* if the vehicle could load here or could stop with cargo loaded set the last loading station */
|
|
||||||
this->last_loading_station = this->last_station_visited;
|
|
||||||
ClrBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP);
|
|
||||||
} else {
|
|
||||||
/* if the vehicle couldn't load and had to unload or transfer everything
|
|
||||||
* set the last loading station to invalid as it will leave empty. */
|
|
||||||
this->last_loading_station = INVALID_STATION;
|
|
||||||
ClrBit(this->vehicle_flags, VF_LAST_LOAD_ST_SEP);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -279,6 +279,8 @@ public:
|
|||||||
/** We want to 'destruct' the right class. */
|
/** We want to 'destruct' the right class. */
|
||||||
virtual ~Vehicle();
|
virtual ~Vehicle();
|
||||||
|
|
||||||
|
uint32 GetLastLoadingStationValidCargoMask() const;
|
||||||
|
|
||||||
void BeginLoading();
|
void BeginLoading();
|
||||||
void CancelReservation(StationID next, Station *st);
|
void CancelReservation(StationID next, Station *st);
|
||||||
void LeaveStation();
|
void LeaveStation();
|
||||||
|
Reference in New Issue
Block a user