diff --git a/src/linkgraph/refresh.cpp b/src/linkgraph/refresh.cpp index f872fc1bfd..c28876fca0 100644 --- a/src/linkgraph/refresh.cpp +++ b/src/linkgraph/refresh.cpp @@ -24,61 +24,43 @@ * @param v Vehicle to refresh links for. * @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 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 (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. */ - uint32 cargoes_to_check = ~0; - while (cargoes_to_check != 0) { - uint32 cargo_mask = cargoes_to_check; - const CargoID first_cargo_id = FindFirstBit(cargo_mask); + while (cargo_mask != 0) { + uint32 iter_cargo_mask = cargo_mask; 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->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD) { - CargoMaskValueFilter(cargo_mask, [&](CargoID cargo) -> uint { + CargoMaskValueFilter(iter_cargo_mask, [&](CargoID cargo) -> uint { return o->GetCargoUnloadType(cargo) & (OUFB_TRANSFER | OUFB_UNLOAD | OUFB_NO_UNLOAD); }); } if (o->GetLoadType() == OLFB_CARGO_TYPE_LOAD) { - CargoMaskValueFilter(cargo_mask, [&](CargoID cargo) -> uint { + CargoMaskValueFilter(iter_cargo_mask, [&](CargoID cargo) -> uint { 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. */ - 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) { 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; } } diff --git a/src/linkgraph/refresh.h b/src/linkgraph/refresh.h index 3d30853274..0b834a3e54 100644 --- a/src/linkgraph/refresh.h +++ b/src/linkgraph/refresh.h @@ -23,7 +23,7 @@ */ class LinkRefresher { 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: /** diff --git a/src/order_base.h b/src/order_base.h index 7be22873a3..2e1a864783 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -217,6 +217,20 @@ public: return ouf; } + template 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? */ inline OrderNonStopFlags GetNonStopType() const { return (OrderNonStopFlags)GB(this->type, 6, 2); } /** 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); }; +template 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 T CargoMaskValueFilter(uint32 &cargo_mask, F filter_func) { CargoID first_cargo_id = FindFirstBit(cargo_mask); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 44f279a95f..8965f4a4a2 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -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. * @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. */ if (this->current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false); - if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 || - (this->current_order.GetUnloadType() & 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 cargoes_can_load_unload = this->current_order.FilterLoadUnloadTypeCargoMask([&](const Order *o, CargoID cargo) { + return ((o->GetCargoLoadType(cargo) & OLFB_NO_LOAD) == 0) || ((o->GetCargoUnloadType(cargo) & OUFB_NO_UNLOAD) == 0); + }); + 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(); - 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 */ StationID head_last_loading_station = this->last_loading_station; 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; - if ((this->current_order.GetCargoLoadType(u->cargo_type) & OLFB_NO_LOAD) == 0 || - (this->current_order.GetCargoUnloadType(u->cargo_type) & OUFB_NO_UNLOAD) == 0) { - if (this->current_order.CanLeaveWithCargo(last_loading_station != INVALID_STATION, u->cargo_type)) { + if (u->cargo_type < NUM_CARGO && HasBit(cargoes_can_load_unload, u->cargo_type)) { + if (HasBit(cargoes_can_leave_with_cargo, u->cargo_type)) { u->last_loading_station = this->last_station_visited; } else { u->last_loading_station = INVALID_STATION; @@ -2137,23 +2177,6 @@ void Vehicle::LeaveStation() } } 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); - } } } diff --git a/src/vehicle_base.h b/src/vehicle_base.h index 555ab54bf9..785f605446 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -279,6 +279,8 @@ public: /** We want to 'destruct' the right class. */ virtual ~Vehicle(); + uint32 GetLastLoadingStationValidCargoMask() const; + void BeginLoading(); void CancelReservation(StationID next, Station *st); void LeaveStation();