diff --git a/src/cargopacket.cpp b/src/cargopacket.cpp index 6a37cb4887..9a1ba8568b 100644 --- a/src/cargopacket.cpp +++ b/src/cargopacket.cpp @@ -421,7 +421,7 @@ void VehicleCargoList::SetTransferLoadPlace(TileIndex xy) * @param payment Payment object for registering transfers. * return If any cargo will be unloaded. */ -bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationID next_station, uint8 order_flags, const GoodsEntry *ge, CargoPayment *payment) +bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationIDStack next_station, uint8 order_flags, const GoodsEntry *ge, CargoPayment *payment) { this->AssertCountConsistency(); assert(this->action_counts[MTA_LOAD] == 0); @@ -446,9 +446,24 @@ bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationID action = MTA_DELIVER; } else if (force_transfer) { action = MTA_TRANSFER; - cargo_next = ge->GetVia(cp->source, current_station, next_station); - assert((cargo_next != next_station || cargo_next == INVALID_STATION) && - cargo_next != current_station); + /* We cannot send the cargo to any of the possible next hops and + * also not to the current station. */ + FlowStatMap::const_iterator flow_it(ge->flows.find(cp->source)); + if (flow_it == ge->flows.end()) { + cargo_next = INVALID_STATION; + } else { + FlowStat new_shares = flow_it->second; + new_shares.ChangeShare(current_station, INT_MIN); + StationIDStack excluded = next_station; + while (!excluded.IsEmpty()) { + new_shares.ChangeShare(excluded.Pop(), INT_MIN); + } + if (new_shares.GetShares()->empty()) { + cargo_next = INVALID_STATION; + } else { + cargo_next = new_shares.GetVia(); + } + } } else { /* Rewrite an invalid source station to some random other one to * avoid keeping the cargo in the vehicle forever. */ @@ -460,7 +475,7 @@ bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationID action = (accepted && cp->source != current_station) ? MTA_DELIVER : MTA_KEEP; } else if (cargo_next == current_station) { action = MTA_DELIVER; - } else if (cargo_next == next_station) { + } else if (next_station.Contains(cargo_next)) { action = MTA_KEEP; } else { action = MTA_TRANSFER; @@ -667,10 +682,14 @@ bool StationCargoList::ShiftCargo(Taction &action, StationID next) * @return Amount of cargo actually moved. */ template -uint StationCargoList::ShiftCargo(Taction action, StationID next, bool include_invalid) +uint StationCargoList::ShiftCargo(Taction action, StationIDStack next, bool include_invalid) { uint max_move = action.MaxMove(); - if (this->ShiftCargo(action, next) && include_invalid && action.MaxMove() > 0) { + while (!next.IsEmpty()) { + this->ShiftCargo(action, next.Pop()); + if (action.MaxMove() == 0) break; + } + if (include_invalid && action.MaxMove() > 0) { this->ShiftCargo(action, INVALID_STATION); } return max_move - action.MaxMove(); @@ -735,10 +754,10 @@ uint StationCargoList::Truncate(uint max_move, StationCargoAmountMap *cargo_per_ * @param max_move Maximum amount of cargo to reserve. * @param dest VehicleCargoList to reserve for. * @param load_place Tile index of the current station. - * @param next_station Next station the loading vehicle will visit. + * @param next_station Next station(s) the loading vehicle will visit. * @return Amount of cargo actually reserved. */ -uint StationCargoList::Reserve(uint max_move, VehicleCargoList *dest, TileIndex load_place, StationID next_station) +uint StationCargoList::Reserve(uint max_move, VehicleCargoList *dest, TileIndex load_place, StationIDStack next_station) { return this->ShiftCargo(CargoReservation(this, dest, max_move, load_place), next_station, true); } @@ -749,13 +768,13 @@ uint StationCargoList::Reserve(uint max_move, VehicleCargoList *dest, TileIndex * @param max_move Amount of cargo to load. * @param dest Vehicle cargo list where the cargo resides. * @param load_place The new loaded_at_xy to be assigned to packets being moved. - * @param next_station Next station the loading vehicle will visit. + * @param next_station Next station(s) the loading vehicle will visit. * @return Amount of cargo actually loaded. * @note Vehicles may or may not reserve, depending on their orders. The two * modes of loading are exclusive, though. If cargo is reserved we don't * need to load unreserved cargo. */ -uint StationCargoList::Load(uint max_move, VehicleCargoList *dest, TileIndex load_place, StationID next_station) +uint StationCargoList::Load(uint max_move, VehicleCargoList *dest, TileIndex load_place, StationIDStack next_station) { uint move = min(dest->ActionCount(VehicleCargoList::MTA_LOAD), max_move); if (move > 0) { diff --git a/src/cargopacket.h b/src/cargopacket.h index c4ea9e144b..3ae9e9927c 100644 --- a/src/cargopacket.h +++ b/src/cargopacket.h @@ -409,7 +409,7 @@ public: void SetTransferLoadPlace(TileIndex xy); - bool Stage(bool accepted, StationID current_station, StationID next_station, uint8 order_flags, const GoodsEntry *ge, CargoPayment *payment); + bool Stage(bool accepted, StationID current_station, StationIDStack next_station, uint8 order_flags, const GoodsEntry *ge, CargoPayment *payment); /** * Marks all cargo in the vehicle as to be kept. This is mostly useful for @@ -483,7 +483,7 @@ public: bool ShiftCargo(Taction &action, StationID next); template - uint ShiftCargo(Taction action, StationID next, bool include_invalid); + uint ShiftCargo(Taction action, StationIDStack next, bool include_invalid); void Append(CargoPacket *cp, StationID next); @@ -492,9 +492,13 @@ public: * @param next Station the cargo is headed for. * @return If there is any cargo for that station. */ - inline bool HasCargoFor(StationID next) const + inline bool HasCargoFor(StationIDStack next) const { - return this->packets.find(next) != this->packets.end(); + while (!next.IsEmpty()) { + if (this->packets.find(next.Pop()) != this->packets.end()) return true; + } + /* Packets for INVALID_STTION can go anywhere. */ + return this->packets.find(INVALID_STATION) != this->packets.end(); } /** @@ -539,8 +543,8 @@ public: * amount of cargo to be moved. Second parameter is destination (if * applicable), return value is amount of cargo actually moved. */ - uint Reserve(uint max_move, VehicleCargoList *dest, TileIndex load_place, StationID next); - uint Load(uint max_move, VehicleCargoList *dest, TileIndex load_place, StationID next); + uint Reserve(uint max_move, VehicleCargoList *dest, TileIndex load_place, StationIDStack next); + uint Load(uint max_move, VehicleCargoList *dest, TileIndex load_place, StationIDStack next); uint Truncate(uint max_move = UINT_MAX, StationCargoAmountMap *cargo_per_source = NULL); uint Reroute(uint max_move, StationCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge); diff --git a/src/economy.cpp b/src/economy.cpp index e936b5678a..270cedc674 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -1232,7 +1232,7 @@ void PrepareUnload(Vehicle *front_v) assert(CargoPayment::CanAllocateItem()); front_v->cargo_payment = new CargoPayment(front_v); - StationID next_station = front_v->GetNextStoppingStation(); + StationIDStack next_station = front_v->GetNextStoppingStation(); if (front_v->orders.list == NULL || (front_v->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) { Station *st = Station::Get(front_v->last_station_visited); for (Vehicle *v = front_v; v != NULL; v = v->Next()) { @@ -1295,9 +1295,9 @@ static uint GetLoadAmount(Vehicle *v) * @param st Station where the consist is loading at the moment. * @param u Front of the loading vehicle consist. * @param consist_capleft If given, save free capacities after reserving there. - * @param next_station Station the vehicle will stop at next. + * @param next_station Station(s) the vehicle will stop at next. */ -static void ReserveConsist(Station *st, Vehicle *u, CargoArray *consist_capleft, StationID next_station) +static void ReserveConsist(Station *st, Vehicle *u, CargoArray *consist_capleft, StationIDStack next_station) { Vehicle *next_cargo = u; uint32 seen_cargos = 0; @@ -1363,7 +1363,7 @@ static void LoadUnloadVehicle(Vehicle *front) StationID last_visited = front->last_station_visited; Station *st = Station::Get(last_visited); - StationID next_station = front->GetNextStoppingStation(); + StationIDStack next_station = front->GetNextStoppingStation(); bool use_autorefit = front->current_order.IsRefit() && front->current_order.GetRefitCargo() == CT_AUTO_REFIT; CargoArray consist_capleft; if (_settings_game.order.improved_load && @@ -1497,8 +1497,7 @@ static void LoadUnloadVehicle(Vehicle *front) CargoID cid; new_cid = v_start->cargo_type; FOR_EACH_SET_CARGO_ID(cid, refit_mask) { - if (st->goods[cid].cargo.HasCargoFor(next_station) || - st->goods[cid].cargo.HasCargoFor(INVALID_STATION)) { + if (st->goods[cid].cargo.HasCargoFor(next_station)) { /* Try to find out if auto-refitting would succeed. In case the refit is allowed, * the returned refit capacity will be greater than zero. */ DoCommand(v_start->tile, v_start->index, cid | 1U << 6 | 0xFF << 8 | 1U << 16, DC_QUERY_COST, GetCmdRefitVeh(v_start)); // Auto-refit and only this vehicle including artic parts. diff --git a/src/order_base.h b/src/order_base.h index 9965b9e5f1..85d8a05a79 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -263,7 +263,7 @@ public: */ inline VehicleOrderID GetNumManualOrders() const { return this->num_manual_orders; } - StationID GetNextStoppingStation(const Vehicle *v, const Order *first = NULL) const; + StationIDStack GetNextStoppingStation(const Vehicle *v, const Order *first = NULL) const; const Order *GetNextDecisionNode(const Order *next, uint hops) const; void InsertOrderAt(Order *new_order, int index); diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 52dd284965..c72f0d0874 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -350,44 +350,6 @@ Order *OrderList::GetOrderAt(int index) const return order; } -/** - * Choose between the two possible next orders so that the given consist can - * load most cargo. - * @param v Head of the consist. - * @param o1 First order to choose from. - * @param o2 Second order to choose from. - * @return The best option of o1 and o2 and (recursively) possibly other conditionals. - */ -StationID OrderList::GetBestLoadableNext(const Vehicle *v, const Order *o2, const Order *o1) const -{ - SmallMap capacities; - v->GetConsistFreeCapacities(capacities); - uint loadable1 = 0; - uint loadable2 = 0; - StationID st1 = this->GetNextStoppingStation(v, o1); - StationID st2 = this->GetNextStoppingStation(v, o2); - const Station *cur_station = Station::Get(v->last_station_visited); - for (SmallPair *i = capacities.Begin(); i != capacities.End(); ++i) { - const StationCargoPacketMap *loadable_packets = cur_station->goods[i->first].cargo.Packets(); - uint loadable_cargo = 0; - std::pair p = - loadable_packets->equal_range(st1); - for (StationCargoPacketMap::const_iterator j = p.first; j != p.second; ++j) { - loadable_cargo = (*j)->Count(); - } - loadable1 += min(i->second, loadable_cargo); - - loadable_cargo = 0; - p = loadable_packets->equal_range(st2); - for (StationCargoPacketMap::const_iterator j = p.first; j != p.second; ++j) { - loadable_cargo = (*j)->Count(); - } - loadable2 += min(i->second, loadable_cargo); - } - if (loadable1 == loadable2) return RandomRange(2) == 0 ? st1 : st2; - return loadable1 > loadable2 ? st1 : st2; -} - /** * Get the next order which will make the given vehicle stop at a station * or refit at a depot or evaluate a non-trivial condition. @@ -432,9 +394,8 @@ const Order *OrderList::GetNextDecisionNode(const Order *next, uint hops) const * @return Next stoppping station or INVALID_STATION. * @pre The vehicle is currently loading and v->last_station_visited is meaningful. * @note This function may draw a random number. Don't use it from the GUI. - * @see OrderList::GetBestLoadableNext */ -StationID OrderList::GetNextStoppingStation(const Vehicle *v, const Order *first) const +StationIDStack OrderList::GetNextStoppingStation(const Vehicle *v, const Order *first) const { const Order *next = first; @@ -472,7 +433,10 @@ StationID OrderList::GetNextStoppingStation(const Vehicle *v, const Order *first } else if (skip_to == NULL) { next = advance; } else { - return this->GetBestLoadableNext(v, skip_to, advance); + StationIDStack st1 = this->GetNextStoppingStation(v, skip_to); + StationIDStack st2 = this->GetNextStoppingStation(v, advance); + while (!st2.IsEmpty()) st1.Push(st2.Pop()); + return st1; } } else { /* Otherwise we're optimistic and expect that the diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 19f7b3e645..b9797bdf3e 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -4057,8 +4057,9 @@ uint FlowStat::GetShare(StationID st) const } /** - * Get a station a package can be routed to, but exclude the given one. + * Get a station a package can be routed to, but exclude the given ones. * @param excluded StationID not to be selected. + * @param excluded2 Another StationID not to be selected. * @return A station ID from the shares map. */ StationID FlowStat::GetVia(StationID excluded, StationID excluded2) const diff --git a/src/vehicle_base.h b/src/vehicle_base.h index ef5afa5682..ddd74cec13 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -628,7 +628,7 @@ public: * Get the next station the vehicle will stop at. * @return ID of the next station the vehicle will stop at or INVALID_STATION. */ - inline StationID GetNextStoppingStation() const + inline StationIDStack GetNextStoppingStation() const { return (this->orders.list == NULL) ? INVALID_STATION : this->orders.list->GetNextStoppingStation(this); }