Initial LinkRefresher support for cargo-specific links.
This commit is contained in:
@@ -25,19 +25,69 @@
|
||||
* @param allow_merge If the refresher is allowed to merge or extend link graphs.
|
||||
* @param is_full_loading If the vehicle is full loading.
|
||||
*/
|
||||
/* static */ void LinkRefresher::Run(Vehicle *v, bool allow_merge, bool is_full_loading)
|
||||
/* static */ void LinkRefresher::Run(Vehicle *v, bool allow_merge, bool is_full_loading, bool check_cargo_can_leave)
|
||||
{
|
||||
/* If there are no orders we can't predict anything.*/
|
||||
if (v->orders.list == NULL) return;
|
||||
|
||||
/* Make sure the first order is a useful order. */
|
||||
const Order *first = v->orders.list->GetNextDecisionNode(v->GetOrder(v->cur_implicit_order_index), 0);
|
||||
if (first == NULL) return;
|
||||
/* 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);
|
||||
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) {
|
||||
OrderUnloadFlags ouf = o->GetCargoUnloadType(first_cargo_id);
|
||||
uint32 other_cargo_mask = cargo_mask;
|
||||
ClrBit(other_cargo_mask, first_cargo_id);
|
||||
CargoID cargo;
|
||||
FOR_EACH_SET_BIT(cargo, other_cargo_mask) {
|
||||
if (((ouf ^ o->GetCargoUnloadType(cargo)) & (OUFB_TRANSFER | OUFB_UNLOAD | OUFB_NO_UNLOAD)) != 0) ClrBit(cargo_mask, cargo);
|
||||
}
|
||||
}
|
||||
if (o->GetLoadType() == OLFB_CARGO_TYPE_LOAD) {
|
||||
OrderLoadFlags olf = o->GetCargoLoadType(first_cargo_id);
|
||||
uint32 other_cargo_mask = cargo_mask;
|
||||
ClrBit(other_cargo_mask, first_cargo_id);
|
||||
CargoID cargo;
|
||||
FOR_EACH_SET_BIT(cargo, other_cargo_mask) {
|
||||
if (((olf ^ o->GetCargoLoadType(cargo)) & (OLFB_NO_LOAD)) != 0) ClrBit(cargo_mask, cargo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HopSet seen_hops;
|
||||
LinkRefresher refresher(v, &seen_hops, allow_merge, is_full_loading);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refresher.RefreshLinks(first, first, v->last_loading_station != INVALID_STATION ? 1 << HAS_CARGO : 0);
|
||||
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);
|
||||
if (first != NULL) {
|
||||
HopSet seen_hops;
|
||||
LinkRefresher refresher(v, &seen_hops, allow_merge, is_full_loading, cargo_mask);
|
||||
|
||||
refresher.RefreshLinks(first, first, has_cargo ? 1 << HAS_CARGO : 0);
|
||||
}
|
||||
|
||||
cargoes_to_check &= ~cargo_mask;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,9 +118,9 @@ bool LinkRefresher::Hop::operator<(const Hop &other) const
|
||||
* @param allow_merge If the refresher is allowed to merge or extend link graphs.
|
||||
* @param is_full_loading If the vehicle is full loading.
|
||||
*/
|
||||
LinkRefresher::LinkRefresher(Vehicle *vehicle, HopSet *seen_hops, bool allow_merge, bool is_full_loading) :
|
||||
LinkRefresher::LinkRefresher(Vehicle *vehicle, HopSet *seen_hops, bool allow_merge, bool is_full_loading, uint32 cargo_mask) :
|
||||
vehicle(vehicle), seen_hops(seen_hops), cargo(CT_INVALID), allow_merge(allow_merge),
|
||||
is_full_loading(is_full_loading)
|
||||
is_full_loading(is_full_loading), cargo_mask(cargo_mask)
|
||||
{
|
||||
memset(this->capacities, 0, sizeof(this->capacities));
|
||||
|
||||
@@ -175,8 +225,10 @@ const Order *LinkRefresher::PredictNextOrder(const Order *cur, const Order *next
|
||||
SetBit(flags, USE_NEXT);
|
||||
|
||||
if (next->IsType(OT_CONDITIONAL)) {
|
||||
uint32 this_cargo_mask = this->cargo_mask;
|
||||
const Order *skip_to = this->vehicle->orders.list->GetNextDecisionNode(
|
||||
this->vehicle->orders.list->GetOrderAt(next->GetConditionSkipToOrder()), num_hops);
|
||||
this->vehicle->orders.list->GetOrderAt(next->GetConditionSkipToOrder()), num_hops, this_cargo_mask);
|
||||
assert(this_cargo_mask == this->cargo_mask);
|
||||
if (skip_to != NULL && num_hops < this->vehicle->orders.list->GetNumOrders()) {
|
||||
/* Make copies of capacity tracking lists. There is potential
|
||||
* for optimization here: If the vehicle never refits we don't
|
||||
@@ -189,8 +241,10 @@ const Order *LinkRefresher::PredictNextOrder(const Order *cur, const Order *next
|
||||
|
||||
/* Reassign next with the following stop. This can be a station or a
|
||||
* depot.*/
|
||||
uint32 this_cargo_mask = this->cargo_mask;
|
||||
next = this->vehicle->orders.list->GetNextDecisionNode(
|
||||
this->vehicle->orders.list->GetNext(next), num_hops++);
|
||||
this->vehicle->orders.list->GetNext(next), num_hops++, this_cargo_mask);
|
||||
assert(this_cargo_mask == this->cargo_mask);
|
||||
}
|
||||
return next;
|
||||
}
|
||||
@@ -208,6 +262,8 @@ void LinkRefresher::RefreshStats(const Order *cur, const Order *next)
|
||||
for (CargoID c = 0; c < NUM_CARGO; c++) {
|
||||
/* Refresh the link and give it a minimum capacity. */
|
||||
|
||||
if (!HasBit(this->cargo_mask, c)) continue;
|
||||
|
||||
uint cargo_quantity = this->capacities[c];
|
||||
if (cargo_quantity == 0) continue;
|
||||
|
||||
@@ -218,7 +274,7 @@ void LinkRefresher::RefreshStats(const Order *cur, const Order *next)
|
||||
}
|
||||
|
||||
/* A link is at least partly restricted if a vehicle can't load at its source. */
|
||||
EdgeUpdateMode restricted_mode = (cur->GetLoadType() & OLFB_NO_LOAD) == 0 ?
|
||||
EdgeUpdateMode restricted_mode = (cur->GetCargoLoadType(c) & OLFB_NO_LOAD) == 0 ?
|
||||
EUM_UNRESTRICTED : EUM_RESTRICTED;
|
||||
|
||||
/* If the vehicle is currently full loading, increase the capacities at the station
|
||||
@@ -309,7 +365,7 @@ void LinkRefresher::RefreshLinks(const Order *cur, const Order *next, uint8 flag
|
||||
}
|
||||
|
||||
if (cur->IsType(OT_GOTO_STATION) || cur->IsType(OT_IMPLICIT)) {
|
||||
if (cur->CanLeaveWithCargo(HasBit(flags, HAS_CARGO))) {
|
||||
if (cur->CanLeaveWithCargo(HasBit(flags, HAS_CARGO), FindFirstBit(this->cargo_mask))) {
|
||||
SetBit(flags, HAS_CARGO);
|
||||
this->RefreshStats(cur, next);
|
||||
} else {
|
||||
|
@@ -23,7 +23,7 @@
|
||||
*/
|
||||
class LinkRefresher {
|
||||
public:
|
||||
static void Run(Vehicle *v, bool allow_merge = true, bool is_full_loading = false);
|
||||
static void Run(Vehicle *v, bool allow_merge = true, bool is_full_loading = false, bool check_cargo_can_leave = false);
|
||||
|
||||
protected:
|
||||
/**
|
||||
@@ -89,8 +89,9 @@ protected:
|
||||
CargoID cargo; ///< Cargo given in last refit order.
|
||||
bool allow_merge; ///< If the refresher is allowed to merge or extend link graphs.
|
||||
bool is_full_loading; ///< If the vehicle is full loading.
|
||||
uint32 cargo_mask; ///< Bit-mask of cargo IDs to refresh.
|
||||
|
||||
LinkRefresher(Vehicle *v, HopSet *seen_hops, bool allow_merge, bool is_full_loading);
|
||||
LinkRefresher(Vehicle *v, HopSet *seen_hops, bool allow_merge, bool is_full_loading, uint32 cargo_mask);
|
||||
|
||||
bool HandleRefit(CargoID refit_cargo);
|
||||
void ResetRefit();
|
||||
|
@@ -300,8 +300,7 @@ public:
|
||||
inline void SetMaxSpeed(uint16 speed) { this->max_speed = speed; }
|
||||
|
||||
bool ShouldStopAtStation(const Vehicle *v, StationID station) const;
|
||||
bool CanLoadOrUnload() const;
|
||||
bool CanLeaveWithCargo(bool has_cargo) const;
|
||||
bool CanLeaveWithCargo(bool has_cargo, CargoID cargo) const;
|
||||
|
||||
TileIndex GetLocation(const Vehicle *v, bool airport = false) const;
|
||||
|
||||
@@ -429,7 +428,7 @@ public:
|
||||
inline VehicleOrderID GetNumManualOrders() const { return this->num_manual_orders; }
|
||||
|
||||
CargoMaskedStationIDStack GetNextStoppingStation(const Vehicle *v, uint32 cargo_mask, const Order *first = NULL, uint hops = 0) const;
|
||||
const Order *GetNextDecisionNode(const Order *next, uint hops) const;
|
||||
const Order *GetNextDecisionNode(const Order *next, uint hops, uint32 &cargo_mask) const;
|
||||
|
||||
void InsertOrderAt(Order *new_order, int index);
|
||||
void DeleteOrderAt(int index);
|
||||
|
@@ -380,14 +380,17 @@ Order *OrderList::GetOrderAt(int index) const
|
||||
* or refit at a depot or evaluate a non-trivial condition.
|
||||
* @param next The order to start looking at.
|
||||
* @param hops The number of orders we have already looked at.
|
||||
* @param cargo_mask The bit set of cargoes that the we are looking at, this may be reduced to indicate the set of cargoes that the result is valid for.
|
||||
* @return Either of
|
||||
* \li a station order
|
||||
* \li a refitting depot order
|
||||
* \li a non-trivial conditional order
|
||||
* \li NULL if the vehicle won't stop anymore.
|
||||
*/
|
||||
const Order *OrderList::GetNextDecisionNode(const Order *next, uint hops) const
|
||||
const Order *OrderList::GetNextDecisionNode(const Order *next, uint hops, uint32 &cargo_mask) const
|
||||
{
|
||||
assert(cargo_mask != 0);
|
||||
|
||||
if (hops > this->GetNumOrders() || next == NULL) return NULL;
|
||||
|
||||
if (next->IsType(OT_CONDITIONAL)) {
|
||||
@@ -397,7 +400,7 @@ const Order *OrderList::GetNextDecisionNode(const Order *next, uint hops) const
|
||||
* the same as regular order progression. */
|
||||
return this->GetNextDecisionNode(
|
||||
this->GetOrderAt(next->GetConditionSkipToOrder()),
|
||||
hops + 1);
|
||||
hops + 1, cargo_mask);
|
||||
}
|
||||
|
||||
if (next->IsType(OT_GOTO_DEPOT)) {
|
||||
@@ -405,8 +408,30 @@ const Order *OrderList::GetNextDecisionNode(const Order *next, uint hops) const
|
||||
if (next->IsRefit()) return next;
|
||||
}
|
||||
|
||||
if (!next->CanLoadOrUnload()) {
|
||||
return this->GetNextDecisionNode(this->GetNext(next), hops + 1);
|
||||
bool can_load_or_unload = false;
|
||||
if ((next->IsType(OT_GOTO_STATION) || next->IsType(OT_IMPLICIT)) &&
|
||||
(next->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) == 0) {
|
||||
if (next->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD || next->GetLoadType() == OLFB_CARGO_TYPE_LOAD) {
|
||||
/* This is a cargo-specific load/unload order.
|
||||
* If the first cargo is both a no-load and no-unload order, skip it.
|
||||
* Drop cargoes which don't match the first one. */
|
||||
CargoID first_cargo_id = FindFirstBit(cargo_mask);
|
||||
can_load_or_unload = ((next->GetCargoLoadType(first_cargo_id) & OLFB_NO_LOAD) == 0 || (next->GetCargoUnloadType(first_cargo_id) & OUFB_NO_UNLOAD) == 0);
|
||||
uint32 other_cargo_mask = cargo_mask;
|
||||
ClrBit(other_cargo_mask, first_cargo_id);
|
||||
CargoID cargo;
|
||||
FOR_EACH_SET_BIT(cargo, other_cargo_mask) {
|
||||
if (can_load_or_unload != ((next->GetCargoLoadType(cargo) & OLFB_NO_LOAD) == 0 || (next->GetCargoUnloadType(cargo) & OUFB_NO_UNLOAD) == 0)) {
|
||||
ClrBit(cargo_mask, cargo);
|
||||
}
|
||||
}
|
||||
} else if ((next->GetLoadType() & OLFB_NO_LOAD) == 0 || (next->GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
|
||||
can_load_or_unload = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!can_load_or_unload) {
|
||||
return this->GetNextDecisionNode(this->GetNext(next), hops + 1, cargo_mask);
|
||||
}
|
||||
|
||||
return next;
|
||||
@@ -425,9 +450,6 @@ const Order *OrderList::GetNextDecisionNode(const Order *next, uint hops) const
|
||||
CargoMaskedStationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, uint32 cargo_mask, const Order *first, uint hops) const
|
||||
{
|
||||
assert(cargo_mask != 0);
|
||||
CargoID first_cargo_id = FindFirstBit(cargo_mask);
|
||||
uint32 other_cargo_mask = cargo_mask;
|
||||
ClrBit(other_cargo_mask, first_cargo_id);
|
||||
|
||||
const Order *next = first;
|
||||
if (first == NULL) {
|
||||
@@ -445,15 +467,15 @@ CargoMaskedStationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, ui
|
||||
}
|
||||
|
||||
do {
|
||||
next = this->GetNextDecisionNode(next, ++hops);
|
||||
next = this->GetNextDecisionNode(next, ++hops, cargo_mask);
|
||||
|
||||
/* Resolve possibly nested conditionals by estimation. */
|
||||
while (next != NULL && next->IsType(OT_CONDITIONAL)) {
|
||||
/* We return both options of conditional orders. */
|
||||
const Order *skip_to = this->GetNextDecisionNode(
|
||||
this->GetOrderAt(next->GetConditionSkipToOrder()), hops);
|
||||
this->GetOrderAt(next->GetConditionSkipToOrder()), hops, cargo_mask);
|
||||
const Order *advance = this->GetNextDecisionNode(
|
||||
this->GetNext(next), hops);
|
||||
this->GetNext(next), hops, cargo_mask);
|
||||
if (advance == NULL || advance == first || skip_to == advance) {
|
||||
next = (skip_to == first) ? NULL : skip_to;
|
||||
} else if (skip_to == NULL || skip_to == first) {
|
||||
@@ -471,36 +493,23 @@ CargoMaskedStationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, ui
|
||||
|
||||
if (next == NULL) return CargoMaskedStationIDStack(cargo_mask, INVALID_STATION);
|
||||
|
||||
if ((next->IsType(OT_GOTO_STATION) || next->IsType(OT_IMPLICIT)) && (next->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD || next->GetLoadType() == OLFB_CARGO_TYPE_LOAD)) {
|
||||
/* This is a cargo-specific load/unload order.
|
||||
* If the first cargo is both a no-load and no-unload order, skip it.
|
||||
* Drop cargoes which don't match the first one. */
|
||||
bool should_drop = next->GetCargoLoadType(first_cargo_id) & OLFB_NO_LOAD && next->GetCargoUnloadType(first_cargo_id) & OUFB_NO_UNLOAD;
|
||||
CargoID cargo;
|
||||
FOR_EACH_SET_BIT(cargo, other_cargo_mask) {
|
||||
if (should_drop != (next->GetCargoLoadType(cargo) & OLFB_NO_LOAD && next->GetCargoUnloadType(cargo) & OUFB_NO_UNLOAD)) {
|
||||
ClrBit(cargo_mask, cargo);
|
||||
}
|
||||
}
|
||||
other_cargo_mask &= cargo_mask;
|
||||
if (should_drop) continue;
|
||||
}
|
||||
|
||||
/* Don't return a next stop if the vehicle has to unload everything. */
|
||||
if ((next->IsType(OT_GOTO_STATION) || next->IsType(OT_IMPLICIT)) &&
|
||||
next->GetDestination() == v->last_station_visited) {
|
||||
CargoID first_cargo_id = FindFirstBit(cargo_mask);
|
||||
bool invalid = ((next->GetCargoUnloadType(first_cargo_id) & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0);
|
||||
if (next->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD) {
|
||||
/* This is a cargo-specific load/unload order.
|
||||
* Don't return a next stop if first cargo has transfer or unload set.
|
||||
* Drop cargoes which don't match the first one. */
|
||||
uint32 other_cargo_mask = cargo_mask;
|
||||
ClrBit(other_cargo_mask, first_cargo_id);
|
||||
CargoID cargo;
|
||||
FOR_EACH_SET_BIT(cargo, other_cargo_mask) {
|
||||
if (invalid != ((next->GetCargoUnloadType(cargo) & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0)) {
|
||||
ClrBit(cargo_mask, cargo);
|
||||
}
|
||||
}
|
||||
other_cargo_mask &= cargo_mask;
|
||||
}
|
||||
if (invalid) return CargoMaskedStationIDStack(cargo_mask, INVALID_STATION);
|
||||
}
|
||||
@@ -2339,22 +2348,14 @@ bool Order::ShouldStopAtStation(const Vehicle *v, StationID station) const
|
||||
!(this->GetNonStopType() & (is_dest_station ? ONSF_NO_STOP_AT_DESTINATION_STATION : ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS));
|
||||
}
|
||||
|
||||
bool Order::CanLoadOrUnload() const
|
||||
{
|
||||
return (this->IsType(OT_GOTO_STATION) || this->IsType(OT_IMPLICIT)) &&
|
||||
(this->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) == 0 &&
|
||||
((this->GetLoadType() & OLFB_NO_LOAD) == 0 ||
|
||||
(this->GetUnloadType() & OUFB_NO_UNLOAD) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* A vehicle can leave the current station with cargo if:
|
||||
* 1. it can load cargo here OR
|
||||
* 2a. it could leave the last station with cargo AND
|
||||
* 2b. it doesn't have to unload all cargo here.
|
||||
*/
|
||||
bool Order::CanLeaveWithCargo(bool has_cargo) const
|
||||
bool Order::CanLeaveWithCargo(bool has_cargo, CargoID cargo) const
|
||||
{
|
||||
return (this->GetLoadType() & OLFB_NO_LOAD) == 0 || (has_cargo &&
|
||||
(this->GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) == 0);
|
||||
return (this->GetCargoLoadType(cargo) & OLFB_NO_LOAD) == 0 || (has_cargo &&
|
||||
(this->GetCargoUnloadType(cargo) & (OUFB_UNLOAD | OUFB_TRANSFER)) == 0);
|
||||
}
|
||||
|
@@ -3563,28 +3563,6 @@ void IncreaseStats(Station *st, CargoID cargo, StationID next_station_id, uint c
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase capacity for all link stats associated with vehicles in the given consist.
|
||||
* @param st Station to get the link stats from.
|
||||
* @param front First vehicle in the consist.
|
||||
* @param next_station_id Station the consist will be travelling to next.
|
||||
*/
|
||||
void IncreaseStats(Station *st, const Vehicle *front, StationID next_station_id)
|
||||
{
|
||||
for (const Vehicle *v = front; v != NULL; v = v->Next()) {
|
||||
if (v->refit_cap > 0) {
|
||||
/* The cargo count can indeed be higher than the refit_cap if
|
||||
* wagons have been auto-replaced and subsequently auto-
|
||||
* refitted to a higher capacity. The cargo gets redistributed
|
||||
* among the wagons in that case.
|
||||
* As usage is not such an important figure anyway we just
|
||||
* ignore the additional cargo then.*/
|
||||
IncreaseStats(st, v->cargo_type, next_station_id, v->refit_cap,
|
||||
min(v->refit_cap, v->cargo.StoredCount()), EUM_INCREASE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* called for every station each tick */
|
||||
static void StationHandleSmallTick(BaseStation *st)
|
||||
{
|
||||
|
@@ -1332,6 +1332,15 @@ CommandCost CmdMoveRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, u
|
||||
CheckCargoCapacity(dst_head);
|
||||
}
|
||||
|
||||
if (src_head != NULL) {
|
||||
src_head->last_loading_station = INVALID_STATION;
|
||||
ClrBit(src_head->vehicle_flags, VF_LAST_LOAD_ST_SEP);
|
||||
}
|
||||
if (dst_head != NULL) {
|
||||
dst_head->last_loading_station = INVALID_STATION;
|
||||
ClrBit(dst_head->vehicle_flags, VF_LAST_LOAD_ST_SEP);
|
||||
}
|
||||
|
||||
if (src_head != NULL) src_head->First()->MarkDirty();
|
||||
if (dst_head != NULL) dst_head->First()->MarkDirty();
|
||||
|
||||
|
@@ -1460,6 +1460,7 @@ void VehicleEnterDepot(Vehicle *v)
|
||||
* before the stop to the station after the stop can't be predicted
|
||||
* we shouldn't construct it when the vehicle visits the next stop. */
|
||||
v->last_loading_station = INVALID_STATION;
|
||||
ClrBit(v->vehicle_flags, VF_LAST_LOAD_ST_SEP);
|
||||
if (v->owner == _local_company) {
|
||||
SetDParam(0, v->index);
|
||||
AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
|
||||
@@ -1930,6 +1931,33 @@ void Vehicle::DeleteUnreachedImplicitOrders()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase capacity for all link stats associated with vehicles in the given consist.
|
||||
* @param st Station to get the link stats from.
|
||||
* @param front First vehicle in the consist.
|
||||
* @param next_station_id Station the consist will be travelling to next.
|
||||
*/
|
||||
static void VehicleIncreaseStats(const Vehicle *front)
|
||||
{
|
||||
for (const Vehicle *v = front; v != NULL; v = v->Next()) {
|
||||
StationID last_loading_station = HasBit(front->vehicle_flags, VF_LAST_LOAD_ST_SEP) ? v->last_loading_station : front->last_loading_station;
|
||||
if (v->refit_cap > 0 &&
|
||||
last_loading_station != INVALID_STATION &&
|
||||
last_loading_station != front->last_station_visited &&
|
||||
((front->current_order.GetCargoLoadType(v->cargo_type) & OLFB_NO_LOAD) == 0 ||
|
||||
(front->current_order.GetCargoUnloadType(v->cargo_type) & OUFB_NO_UNLOAD) == 0)) {
|
||||
/* The cargo count can indeed be higher than the refit_cap if
|
||||
* wagons have been auto-replaced and subsequently auto-
|
||||
* refitted to a higher capacity. The cargo gets redistributed
|
||||
* among the wagons in that case.
|
||||
* As usage is not such an important figure anyway we just
|
||||
* ignore the additional cargo then.*/
|
||||
IncreaseStats(Station::Get(last_loading_station), v->cargo_type, front->last_station_visited, v->refit_cap,
|
||||
min(v->refit_cap, v->cargo.StoredCount()), EUM_INCREASE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare everything to begin the loading when arriving at a station.
|
||||
* @pre IsTileType(this->tile, MP_STATION) || this->type == VEH_SHIP.
|
||||
@@ -2040,12 +2068,7 @@ void Vehicle::BeginLoading()
|
||||
this->current_order.MakeLoading(false);
|
||||
}
|
||||
|
||||
if (this->last_loading_station != INVALID_STATION &&
|
||||
this->last_loading_station != this->last_station_visited &&
|
||||
((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
|
||||
(this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0)) {
|
||||
IncreaseStats(Station::Get(this->last_loading_station), this, this->last_station_visited);
|
||||
}
|
||||
VehicleIncreaseStats(this);
|
||||
|
||||
PrepareUnload(this);
|
||||
|
||||
@@ -2093,19 +2116,44 @@ void Vehicle::LeaveStation()
|
||||
|
||||
if ((this->current_order.GetLoadType() & OLFB_NO_LOAD) == 0 ||
|
||||
(this->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
|
||||
if (this->current_order.CanLeaveWithCargo(this->last_loading_station != INVALID_STATION)) {
|
||||
/* 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. */
|
||||
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 */
|
||||
this->ResetRefitCaps();
|
||||
LinkRefresher::Run(this);
|
||||
LinkRefresher::Run(this, true, false, true);
|
||||
|
||||
/* 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;
|
||||
/* 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)) {
|
||||
u->last_loading_station = this->last_station_visited;
|
||||
} else {
|
||||
u->last_loading_station = INVALID_STATION;
|
||||
}
|
||||
} else {
|
||||
u->last_loading_station = last_loading_station;
|
||||
}
|
||||
}
|
||||
SetBit(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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -51,6 +51,8 @@ enum VehicleFlags {
|
||||
VF_PATHFINDER_LOST, ///< Vehicle's pathfinder is lost.
|
||||
VF_SERVINT_IS_CUSTOM, ///< Service interval is custom.
|
||||
VF_SERVINT_IS_PERCENT, ///< Service interval is percent.
|
||||
|
||||
VF_LAST_LOAD_ST_SEP = 13, ///< Each vehicle of this chain has its last_loading_station field set separately
|
||||
};
|
||||
|
||||
/** Bit numbers used to indicate which of the #NewGRFCache values are valid. */
|
||||
@@ -243,7 +245,7 @@ public:
|
||||
byte waiting_triggers; ///< Triggers to be yet matched before rerandomizing the random bits.
|
||||
|
||||
StationID last_station_visited; ///< The last station we stopped at.
|
||||
StationID last_loading_station; ///< Last station the vehicle has stopped at and could possibly leave from with any cargo loaded.
|
||||
StationID last_loading_station; ///< Last station the vehicle has stopped at and could possibly leave from with any cargo loaded. (See VF_LAST_LOAD_ST_SEP).
|
||||
|
||||
CargoID cargo_type; ///< type of cargo this vehicle is carrying
|
||||
byte cargo_subtype; ///< Used for livery refits (NewGRF variations)
|
||||
|
Reference in New Issue
Block a user