Update readme and changelog for v1.27.0
[openttd-joker.git] / src / cargopacket.cpp
bloba855e96b53e915ad085e70a590cccdd5da8221f3
1 /* $Id$ */
3 /*
4 * This file is part of OpenTTD.
5 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 */
10 /** @file cargopacket.cpp Implementation of the cargo packets. */
12 #include "stdafx.h"
13 #include "station_base.h"
14 #include "core/pool_func.hpp"
15 #include "core/random_func.hpp"
16 #include "economy_base.h"
17 #include "cargoaction.h"
18 #include "order_type.h"
20 #include <vector>
22 #include "safeguards.h"
24 /* Initialize the cargopacket-pool */
25 CargoPacketPool _cargopacket_pool("CargoPacket");
26 INSTANTIATE_POOL_METHODS(CargoPacket)
28 /**
29 * Create a new packet for savegame loading.
31 CargoPacket::CargoPacket()
33 this->source_type = ST_INDUSTRY;
34 this->source_id = INVALID_SOURCE;
37 /**
38 * Creates a new cargo packet.
39 * @param source Source station of the packet.
40 * @param source_xy Source location of the packet.
41 * @param count Number of cargo entities to put in this packet.
42 * @param source_type 'Type' of source the packet comes from (for subsidies).
43 * @param source_id Actual source of the packet (for subsidies).
44 * @pre count != 0
45 * @note We have to zero memory ourselves here because we are using a 'new'
46 * that, in contrary to all other pools, does not memset to 0.
48 CargoPacket::CargoPacket(StationID source, TileIndex source_xy, uint16 count, SourceType source_type, SourceID source_id) :
49 feeder_share(0),
50 count(count),
51 days_in_transit(0),
52 source_id(source_id),
53 source(source),
54 source_xy(source_xy),
55 loaded_at_xy(0)
57 assert(count != 0);
58 this->source_type = source_type;
61 /**
62 * Creates a new cargo packet. Initializes the fields that cannot be changed later.
63 * Used when loading or splitting packets.
64 * @param count Number of cargo entities to put in this packet.
65 * @param days_in_transit Number of days the cargo has been in transit.
66 * @param source Station the cargo was initially loaded.
67 * @param source_xy Station location the cargo was initially loaded.
68 * @param loaded_at_xy Location the cargo was loaded last.
69 * @param feeder_share Feeder share the packet has already accumulated.
70 * @param source_type 'Type' of source the packet comes from (for subsidies).
71 * @param source_id Actual source of the packet (for subsidies).
72 * @note We have to zero memory ourselves here because we are using a 'new'
73 * that, in contrary to all other pools, does not memset to 0.
75 CargoPacket::CargoPacket(uint16 count, byte days_in_transit, StationID source, TileIndex source_xy, TileIndex loaded_at_xy, Money feeder_share, SourceType source_type, SourceID source_id) :
76 feeder_share(feeder_share),
77 count(count),
78 days_in_transit(days_in_transit),
79 source_id(source_id),
80 source(source),
81 source_xy(source_xy),
82 loaded_at_xy(loaded_at_xy)
84 assert(count != 0);
85 this->source_type = source_type;
88 /**
89 * Split this packet in two and return the split off part.
90 * @param new_size Size of the split part.
91 * @return Split off part, or nullptr if no packet could be allocated!
93 CargoPacket *CargoPacket::Split(uint new_size)
95 if (!CargoPacket::CanAllocateItem()) return nullptr;
97 Money fs = this->FeederShare(new_size);
98 CargoPacket *cp_new = new CargoPacket(new_size, this->days_in_transit, this->source, this->source_xy, this->loaded_at_xy, fs, this->source_type, this->source_id);
99 this->feeder_share -= fs;
100 this->count -= new_size;
101 return cp_new;
105 * Merge another packet into this one.
106 * @param cp Packet to be merged in.
108 void CargoPacket::Merge(CargoPacket *cp)
110 this->count += cp->count;
111 this->feeder_share += cp->feeder_share;
112 delete cp;
116 * Reduce the packet by the given amount and remove the feeder share.
117 * @param count Amount to be removed.
119 void CargoPacket::Reduce(uint count)
121 assert(count < this->count);
122 this->feeder_share -= this->FeederShare(count);
123 this->count -= count;
127 * Invalidates (sets source_id to INVALID_SOURCE) all cargo packets from given source.
128 * @param src_type Type of source.
129 * @param src Index of source.
131 /* static */ void CargoPacket::InvalidateAllFrom(SourceType src_type, SourceID src)
133 CargoPacket *cp;
134 FOR_ALL_CARGOPACKETS(cp) {
135 if (cp->source_type == src_type && cp->source_id == src) cp->source_id = INVALID_SOURCE;
140 * Invalidates (sets source to INVALID_STATION) all cargo packets from given station.
141 * @param sid Station that gets removed.
143 /* static */ void CargoPacket::InvalidateAllFrom(StationID sid)
145 CargoPacket *cp;
146 FOR_ALL_CARGOPACKETS(cp) {
147 if (cp->source == sid) cp->source = INVALID_STATION;
153 * Cargo list implementation
158 * Destroy the cargolist ("frees" all cargo packets).
160 template <class Tinst, class Tcont>
161 CargoList<Tinst, Tcont>::~CargoList()
163 for (Iterator it(this->packets.begin()); it != this->packets.end(); ++it) {
164 delete *it;
169 * Empty the cargo list, but don't free the cargo packets;
170 * the cargo packets are cleaned by CargoPacket's CleanPool.
172 template <class Tinst, class Tcont>
173 void CargoList<Tinst, Tcont>::OnCleanPool()
175 this->packets.clear();
179 * Update the cached values to reflect the removal of this packet or part of it.
180 * Decreases count and days_in_transit.
181 * @param cp Packet to be removed from cache.
182 * @param count Amount of cargo from the given packet to be removed.
184 template <class Tinst, class Tcont>
185 void CargoList<Tinst, Tcont>::RemoveFromCache(const CargoPacket *cp, uint count)
187 assert(count <= cp->count);
188 this->count -= count;
189 this->cargo_days_in_transit -= cp->days_in_transit * count;
193 * Update the cache to reflect adding of this packet.
194 * Increases count and days_in_transit.
195 * @param cp New packet to be inserted.
197 template <class Tinst, class Tcont>
198 void CargoList<Tinst, Tcont>::AddToCache(const CargoPacket *cp)
200 this->count += cp->count;
201 this->cargo_days_in_transit += cp->days_in_transit * cp->count;
204 /** Invalidates the cached data and rebuilds it. */
205 template <class Tinst, class Tcont>
206 void CargoList<Tinst, Tcont>::InvalidateCache()
208 this->count = 0;
209 this->cargo_days_in_transit = 0;
211 for (ConstIterator it(this->packets.begin()); it != this->packets.end(); it++) {
212 static_cast<Tinst *>(this)->AddToCache(*it);
217 * Tries to merge the second packet into the first and return if that was
218 * successful.
219 * @param icp Packet to be merged into.
220 * @param cp Packet to be eliminated.
221 * @return If the packets could be merged.
223 template <class Tinst, class Tcont>
224 /* static */ bool CargoList<Tinst, Tcont>::TryMerge(CargoPacket *icp, CargoPacket *cp)
226 if (Tinst::AreMergable(icp, cp) &&
227 icp->count + cp->count <= CargoPacket::MAX_COUNT) {
228 icp->Merge(cp);
229 return true;
230 } else {
231 return false;
237 * Vehicle cargo list implementation.
242 * Appends the given cargo packet. Tries to merge it with another one in the
243 * packets list. If no fitting packet is found, appends it. You can only append
244 * packets to the ranges of packets designated for keeping or loading.
245 * Furthermore if there are already packets reserved for loading you cannot
246 * directly add packets to the "keep" list. You first have to load the reserved
247 * ones.
248 * @warning After appending this packet may not exist anymore!
249 * @note Do not use the cargo packet anymore after it has been appended to this CargoList!
250 * @param cp Cargo packet to add.
251 * @param action Either MTA_KEEP if you want to add the packet directly or MTA_LOAD
252 * if you want to reserve it first.
253 * @pre cp != nullptr
254 * @pre action == MTA_LOAD || (action == MTA_KEEP && this->designation_counts[MTA_LOAD] == 0)
256 void VehicleCargoList::Append(CargoPacket *cp, MoveToAction action)
258 assert(cp != nullptr);
259 assert(action == MTA_LOAD ||
260 (action == MTA_KEEP && this->action_counts[MTA_LOAD] == 0));
261 this->AddToMeta(cp, action);
263 if (this->count == cp->count) {
264 this->packets.push_back(cp);
265 return;
268 uint sum = cp->count;
269 for (ReverseIterator it(this->packets.rbegin()); it != this->packets.rend(); it++) {
270 CargoPacket *icp = *it;
271 if (VehicleCargoList::TryMerge(icp, cp)) return;
272 sum += icp->count;
273 if (sum >= this->action_counts[action]) {
274 this->packets.push_back(cp);
275 return;
279 NOT_REACHED();
283 * Shifts cargo from the front of the packet list and applies some action to it.
284 * @tparam Taction Action class or function to be used. It should define
285 * "bool operator()(CargoPacket *)". If true is returned the
286 * cargo packet will be removed from the list. Otherwise it
287 * will be kept and the loop will be aborted.
288 * @param action Action instance to be applied.
290 template<class Taction>
291 void VehicleCargoList::ShiftCargo(Taction action)
293 std::vector<CargoPacket*> packets_to_erase;
294 std::vector<CargoPacket*> packets_copy(this->packets.begin(), this->packets.end());
296 auto it = packets_copy.begin();
297 while (it != packets_copy.end() && action.MaxMove() > 0) {
298 CargoPacket *cp = *it;
299 if (action(cp)) {
300 packets_to_erase.push_back(*it);
301 ++it;
302 } else {
303 break;
307 for (auto cp : packets_to_erase) {
308 this->packets.erase(std::find(this->packets.begin(), this->packets.end(), cp));
313 * Pops cargo from the back of the packet list and applies some action to it.
314 * @tparam Taction Action class or function to be used. It should define
315 * "bool operator()(CargoPacket *)". If true is returned the
316 * cargo packet will be removed from the list. Otherwise it
317 * will be kept and the loop will be aborted.
318 * @param action Action instance to be applied.
320 template<class Taction>
321 void VehicleCargoList::PopCargo(Taction action)
323 if (this->packets.empty()) return;
324 for (auto it = this->packets.end(); it != this->packets.begin();) {
325 if (action.MaxMove() <= 0) break;
326 --it;
327 CargoPacket *cp = *it;
328 if (action(cp)) {
329 it = this->packets.erase(it);
330 } else {
331 break;
337 * Update the cached values to reflect the removal of this packet or part of it.
338 * Decreases count, feeder share and days_in_transit.
339 * @param cp Packet to be removed from cache.
340 * @param count Amount of cargo from the given packet to be removed.
342 void VehicleCargoList::RemoveFromCache(const CargoPacket *cp, uint count)
344 this->feeder_share -= cp->FeederShare(count);
345 this->Parent::RemoveFromCache(cp, count);
349 * Update the cache to reflect adding of this packet.
350 * Increases count, feeder share and days_in_transit.
351 * @param cp New packet to be inserted.
353 void VehicleCargoList::AddToCache(const CargoPacket *cp)
355 this->feeder_share += cp->feeder_share;
356 this->Parent::AddToCache(cp);
360 * Removes a packet or part of it from the metadata.
361 * @param cp Packet to be removed.
362 * @param action MoveToAction of the packet (for updating the counts).
363 * @param count Amount of cargo to be removed.
365 void VehicleCargoList::RemoveFromMeta(const CargoPacket *cp, MoveToAction action, uint count)
367 assert(count <= this->action_counts[action]);
368 this->AssertCountConsistency();
369 this->RemoveFromCache(cp, count);
370 this->action_counts[action] -= count;
371 this->AssertCountConsistency();
375 * Adds a packet to the metadata.
376 * @param cp Packet to be added.
377 * @param action MoveToAction of the packet.
379 void VehicleCargoList::AddToMeta(const CargoPacket *cp, MoveToAction action)
381 this->AssertCountConsistency();
382 this->AddToCache(cp);
383 this->action_counts[action] += cp->count;
384 this->AssertCountConsistency();
388 * Ages the all cargo in this list.
390 void VehicleCargoList::AgeCargo()
392 for (ConstIterator it(this->packets.begin()); it != this->packets.end(); it++) {
393 CargoPacket *cp = *it;
394 /* If we're at the maximum, then we can't increase no more. */
395 if (cp->days_in_transit == 0xFF) continue;
397 cp->days_in_transit++;
398 this->cargo_days_in_transit += cp->count;
403 * Sets loaded_at_xy to the current station for all cargo to be transfered.
404 * This is done when stopping or skipping while the vehicle is unloading. In
405 * that case the vehicle will get part of its transfer credits early and it may
406 * get more transfer credits than it's entitled to.
407 * @param xy New loaded_at_xy for the cargo.
409 void VehicleCargoList::SetTransferLoadPlace(TileIndex xy)
411 uint sum = 0;
412 for (Iterator it = this->packets.begin(); sum < this->action_counts[MTA_TRANSFER]; ++it) {
413 CargoPacket *cp = *it;
414 cp->loaded_at_xy = xy;
415 sum += cp->count;
420 * Choose action to be performed with the given cargo packet.
421 * @param cp The packet.
422 * @param cargo_next Next hop the cargo wants to pass.
423 * @param current_station Current station of the vehicle carrying the cargo.
424 * @param accepted If the cargo is accepted at the current station.
425 * @param next_station Next station(s) the vehicle may stop at.
426 * @return MoveToAction to be performed.
428 /* static */ VehicleCargoList::MoveToAction VehicleCargoList::ChooseAction(const CargoPacket *cp, StationID cargo_next,
429 StationID current_station, bool accepted, StationIDStack next_station)
431 if (cargo_next == INVALID_STATION) {
432 return (accepted && cp->source != current_station) ? MTA_DELIVER : MTA_KEEP;
433 } else if (cargo_next == current_station) {
434 return MTA_DELIVER;
435 } else if (next_station.Contains(cargo_next)) {
436 return MTA_KEEP;
437 } else {
438 return MTA_TRANSFER;
443 * Stages cargo for unloading. The cargo is sorted so that packets to be
444 * transferred, delivered or kept are in consecutive chunks in the list. At the
445 * same time the designation_counts are updated to reflect the size of those
446 * chunks.
447 * @param accepted If the cargo will be accepted at the station.
448 * @param current_station ID of the station.
449 * @param next_station ID of the station the vehicle will go to next.
450 * @param order_flags OrderUnloadFlags that will apply to the unload operation.
451 * @param ge GoodsEntry for getting the flows.
452 * @param payment Payment object for registering transfers.
453 * return If any cargo will be unloaded.
455 bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationIDStack next_station, uint8 order_flags, const GoodsEntry *ge, CargoPayment *payment)
457 this->AssertCountConsistency();
458 assert(this->action_counts[MTA_LOAD] == 0);
459 this->action_counts[MTA_TRANSFER] = this->action_counts[MTA_DELIVER] = this->action_counts[MTA_KEEP] = 0;
460 Iterator it = this->packets.begin();
461 uint sum = 0;
462 CargoPacketList transfer_deliver;
463 std::vector<CargoPacket *> keep;
465 bool force_keep = (order_flags & OUFB_NO_UNLOAD) != 0;
466 bool force_unload = (order_flags & OUFB_UNLOAD) != 0;
467 bool force_transfer = (order_flags & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0;
468 assert(this->count > 0 || it == this->packets.end());
469 while (sum < this->count) {
470 CargoPacket *cp = *it;
472 it = this->packets.erase(it);
473 StationID cargo_next = INVALID_STATION;
474 MoveToAction action = MTA_LOAD;
475 if (force_keep) {
476 action = MTA_KEEP;
477 } else if (force_unload && accepted && cp->source != current_station) {
478 action = MTA_DELIVER;
479 } else if (force_transfer) {
480 action = MTA_TRANSFER;
481 /* We cannot send the cargo to any of the possible next hops and
482 * also not to the current station. */
483 FlowStatMap::const_iterator flow_it(ge->flows.find(cp->source));
484 if (flow_it == ge->flows.end()) {
485 cargo_next = INVALID_STATION;
486 } else {
487 FlowStat new_shares = flow_it->second;
488 new_shares.ChangeShare(current_station, INT_MIN);
489 StationIDStack excluded = next_station;
490 while (!excluded.IsEmpty() && !new_shares.GetShares()->empty()) {
491 new_shares.ChangeShare(excluded.Pop(), INT_MIN);
493 if (new_shares.GetShares()->empty()) {
494 cargo_next = INVALID_STATION;
495 } else {
496 cargo_next = new_shares.GetVia();
499 } else {
500 /* Rewrite an invalid source station to some random other one to
501 * avoid keeping the cargo in the vehicle forever. */
502 if (cp->source == INVALID_STATION && !ge->flows.empty()) {
503 cp->source = ge->flows.begin()->first;
505 bool restricted = false;
506 FlowStatMap::const_iterator flow_it(ge->flows.find(cp->source));
507 if (flow_it == ge->flows.end()) {
508 cargo_next = INVALID_STATION;
509 } else {
510 cargo_next = flow_it->second.GetViaWithRestricted(restricted);
512 action = VehicleCargoList::ChooseAction(cp, cargo_next, current_station, accepted, next_station);
513 if (restricted && action == MTA_TRANSFER) {
514 /* If the flow is restricted we can't transfer to it. Choose an
515 * unrestricted one instead. */
516 cargo_next = flow_it->second.GetVia();
517 action = VehicleCargoList::ChooseAction(cp, cargo_next, current_station, accepted, next_station);
520 Money share;
521 switch (action) {
522 case MTA_KEEP:
523 keep.push_back(cp);
524 break;
525 case MTA_DELIVER:
526 transfer_deliver.push_back(cp);
527 break;
528 case MTA_TRANSFER:
529 transfer_deliver.push_front(cp);
530 /* Add feeder share here to allow reusing field for next station. */
531 share = payment->PayTransfer(cp, cp->count);
532 cp->AddFeederShare(share);
533 this->feeder_share += share;
534 cp->next_station = cargo_next;
535 break;
536 default:
537 NOT_REACHED();
539 this->action_counts[action] += cp->count;
540 sum += cp->count;
542 assert(this->packets.empty());
543 this->packets = std::move(transfer_deliver);
544 this->packets.insert(this->packets.end(), keep.begin(), keep.end());
545 this->AssertCountConsistency();
546 this->InvalidateCache();
547 return this->action_counts[MTA_DELIVER] > 0 || this->action_counts[MTA_TRANSFER] > 0;
550 /** Invalidates the cached data and rebuild it. */
551 void VehicleCargoList::InvalidateCache()
553 this->feeder_share = 0;
554 this->Parent::InvalidateCache();
558 * Moves some cargo from one designation to another. You can only move
559 * between adjacent designations. E.g. you can keep cargo that was previously
560 * reserved (MTA_LOAD), but you can't reserve cargo that's marked as to be
561 * delivered. Furthermore, as this method doesn't change the actual packets,
562 * you cannot move cargo from or to MTA_TRANSFER. You need a specialized
563 * template method for that.
564 * @tparam from Previous designation of cargo.
565 * @tparam to New designation of cargo.
566 * @param max_move Maximum amount of cargo to reassign.
567 * @return Amount of cargo actually reassigned.
569 template<VehicleCargoList::MoveToAction Tfrom, VehicleCargoList::MoveToAction Tto>
570 uint VehicleCargoList::Reassign(uint max_move, TileOrStationID)
572 assert_tcompile(Tfrom != MTA_TRANSFER && Tto != MTA_TRANSFER);
573 assert_tcompile(Tfrom - Tto == 1 || Tto - Tfrom == 1);
574 max_move = min(this->action_counts[Tfrom], max_move);
575 this->action_counts[Tfrom] -= max_move;
576 this->action_counts[Tto] += max_move;
577 return max_move;
581 * Reassign cargo from MTA_DELIVER to MTA_TRANSFER and take care of the next
582 * station the cargo wants to visit.
583 * @param max_move Maximum amount of cargo to reassign.
584 * @param next_station Station to record as next hop in the reassigned packets.
585 * @return Amount of cargo actually reassigned.
587 template<>
588 uint VehicleCargoList::Reassign<VehicleCargoList::MTA_DELIVER, VehicleCargoList::MTA_TRANSFER>(uint max_move, TileOrStationID next_station)
590 max_move = min(this->action_counts[MTA_DELIVER], max_move);
592 uint sum = 0;
594 std::list<CargoPacket*> packets_to_insert;
596 for (auto it(this->packets.begin()); sum < this->action_counts[MTA_TRANSFER] + max_move;) {
597 CargoPacket* cp = *it++;
598 sum += cp->Count();
599 if (sum <= this->action_counts[MTA_TRANSFER]) continue;
600 if (sum > this->action_counts[MTA_TRANSFER] + max_move) {
601 CargoPacket* cp_split = cp->Split(sum - this->action_counts[MTA_TRANSFER] + max_move);
602 sum -= cp_split->Count();
603 packets_to_insert.push_back(cp_split);
605 cp->next_station = next_station;
608 for (auto packet : packets_to_insert) {
609 this->packets.push_back(packet);
612 this->action_counts[MTA_DELIVER] -= max_move;
613 this->action_counts[MTA_TRANSFER] += max_move;
615 return max_move;
619 * Returns reserved cargo to the station and removes it from the cache.
620 * @param max_move Maximum amount of cargo to move.
621 * @param dest Station the cargo is returned to.
622 * @param ID of next the station the cargo wants to go next.
623 * @return Amount of cargo actually returned.
625 uint VehicleCargoList::Return(uint max_move, StationCargoList *dest, StationID next)
627 max_move = min(this->action_counts[MTA_LOAD], max_move);
628 this->PopCargo(CargoReturn(this, dest, max_move, next));
629 return max_move;
633 * Shifts cargo between two vehicles.
634 * @param dest Other vehicle's cargo list.
635 * @param max_move Maximum amount of cargo to be moved.
636 * @return Amount of cargo actually moved.
638 uint VehicleCargoList::Shift(uint max_move, VehicleCargoList *dest)
640 max_move = min(this->count, max_move);
641 this->PopCargo(CargoShift(this, dest, max_move));
642 return max_move;
646 * Unloads cargo at the given station. Deliver or transfer, depending on the
647 * ranges defined by designation_counts.
648 * @param dest StationCargoList to add transferred cargo to.
649 * @param max_move Maximum amount of cargo to move.
650 * @param payment Payment object to register payments in.
651 * @return Amount of cargo actually unloaded.
653 uint VehicleCargoList::Unload(uint max_move, StationCargoList *dest, CargoPayment *payment)
655 uint moved = 0;
656 if (this->action_counts[MTA_TRANSFER] > 0) {
657 uint move = min(this->action_counts[MTA_TRANSFER], max_move);
658 this->ShiftCargo(CargoTransfer(this, dest, move));
659 moved += move;
661 if (this->action_counts[MTA_TRANSFER] == 0 && this->action_counts[MTA_DELIVER] > 0 && moved < max_move) {
662 uint move = min(this->action_counts[MTA_DELIVER], max_move - moved);
663 this->ShiftCargo(CargoDelivery(this, move, payment));
664 moved += move;
666 return moved;
670 * Truncates the cargo in this list to the given amount. It leaves the
671 * first cargo entities and removes max_move from the back of the list.
672 * @param max_move Maximum amount of entities to be removed from the list.
673 * @return Amount of entities actually moved.
675 uint VehicleCargoList::Truncate(uint max_move)
677 max_move = min(this->count, max_move);
678 if (max_move > this->ActionCount(MTA_KEEP)) this->KeepAll();
679 this->PopCargo(CargoRemoval<VehicleCargoList>(this, max_move));
680 return max_move;
684 * Routes packets with station "avoid" as next hop to a different place.
685 * @param max_move Maximum amount of cargo to move.
686 * @param dest List to prepend the cargo to.
687 * @param avoid Station to exclude from routing and current next hop of packets to reroute.
688 * @param avoid2 Additional station to exclude from routing.
689 * @oaram ge GoodsEntry to get the routing info from.
691 uint VehicleCargoList::Reroute(uint max_move, VehicleCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge)
693 max_move = min(this->action_counts[MTA_TRANSFER], max_move);
694 this->ShiftCargo(VehicleCargoReroute(this, dest, max_move, avoid, avoid2, ge));
695 return max_move;
700 * Station cargo list implementation.
705 * Appends the given cargo packet to the range of packets with the same next station
706 * @warning After appending this packet may not exist anymore!
707 * @note Do not use the cargo packet anymore after it has been appended to this CargoList!
708 * @param next the next hop
709 * @param cp the cargo packet to add
710 * @pre cp != nullptr
712 void StationCargoList::Append(CargoPacket *cp, StationID next)
714 assert(cp != nullptr);
715 this->AddToCache(cp);
717 StationCargoPacketMap::List &list = this->packets[next];
718 for (StationCargoPacketMap::List::reverse_iterator it(list.rbegin());
719 it != list.rend(); it++) {
720 if (StationCargoList::TryMerge(*it, cp)) return;
723 /* The packet could not be merged with another one */
724 list.push_back(cp);
728 * Shifts cargo from the front of the packet list for a specific station and
729 * applies some action to it.
730 * @tparam Taction Action class or function to be used. It should define
731 * "bool operator()(CargoPacket *)". If true is returned the
732 * cargo packet will be removed from the list. Otherwise it
733 * will be kept and the loop will be aborted.
734 * @param action Action instance to be applied.
735 * @param next Next hop the cargo wants to visit.
736 * @return True if all packets with the given next hop have been removed,
737 * False otherwise.
739 template <class Taction>
740 bool StationCargoList::ShiftCargo(Taction &action, StationID next)
742 std::pair<Iterator, Iterator> range(this->packets.equal_range(next));
743 for (Iterator it(range.first); it != range.second && it.GetKey() == next;) {
744 if (action.MaxMove() == 0) return false;
745 CargoPacket *cp = *it;
746 if (action(cp)) {
747 it = this->packets.erase(it);
748 } else {
749 return false;
752 return true;
756 * Shifts cargo from the front of the packet list for a specific station and
757 * and optional also from the list for "any station", then applies some action
758 * to it.
759 * @tparam Taction Action class or function to be used. It should define
760 * "bool operator()(CargoPacket *)". If true is returned the
761 * cargo packet will be removed from the list. Otherwise it
762 * will be kept and the loop will be aborted.
763 * @param action Action instance to be applied.
764 * @param next Next hop the cargo wants to visit.
765 * @param include_invalid If cargo from the INVALID_STATION list should be
766 * used if necessary.
767 * @return Amount of cargo actually moved.
769 template <class Taction>
770 uint StationCargoList::ShiftCargo(Taction action, StationIDStack next, bool include_invalid)
772 uint max_move = action.MaxMove();
773 while (!next.IsEmpty()) {
774 this->ShiftCargo(action, next.Pop());
775 if (action.MaxMove() == 0) break;
777 if (include_invalid && action.MaxMove() > 0) {
778 this->ShiftCargo(action, INVALID_STATION);
780 return max_move - action.MaxMove();
784 * Truncates where each destination loses roughly the same percentage of its
785 * cargo. This is done by randomizing the selection of packets to be removed.
786 * Optionally count the cargo by origin station.
787 * @param max_move Maximum amount of cargo to remove.
788 * @param cargo_per_source Container for counting the cargo by origin.
789 * @return Amount of cargo actually moved.
791 uint StationCargoList::Truncate(uint max_move, StationCargoAmountMap *cargo_per_source)
793 max_move = min(max_move, this->count);
794 uint prev_count = this->count;
795 uint moved = 0;
796 uint loop = 0;
797 bool do_count = cargo_per_source != nullptr;
798 while (max_move > moved) {
799 for (Iterator it(this->packets.begin()); it != this->packets.end();) {
800 CargoPacket *cp = *it;
801 if (prev_count > max_move && RandomRange(prev_count) < prev_count - max_move) {
802 if (do_count && loop == 0) {
803 (*cargo_per_source)[cp->source] += cp->count;
805 ++it;
806 continue;
808 uint diff = max_move - moved;
809 if (cp->count > diff) {
810 if (diff > 0) {
811 this->RemoveFromCache(cp, diff);
812 cp->Reduce(diff);
813 moved += diff;
815 if (loop > 0) {
816 if (do_count) (*cargo_per_source)[cp->source] -= diff;
817 return moved;
818 } else {
819 if (do_count) (*cargo_per_source)[cp->source] += cp->count;
820 ++it;
822 } else {
823 it = this->packets.erase(it);
824 if (do_count && loop > 0) {
825 (*cargo_per_source)[cp->source] -= cp->count;
827 moved += cp->count;
828 this->RemoveFromCache(cp, cp->count);
829 delete cp;
832 loop++;
834 return moved;
838 * Reserves cargo for loading onto the vehicle.
839 * @param max_move Maximum amount of cargo to reserve.
840 * @param dest VehicleCargoList to reserve for.
841 * @param load_place Tile index of the current station.
842 * @param next_station Next station(s) the loading vehicle will visit.
843 * @return Amount of cargo actually reserved.
845 uint StationCargoList::Reserve(uint max_move, VehicleCargoList *dest, TileIndex load_place, StationIDStack next_station)
847 return this->ShiftCargo(CargoReservation(this, dest, max_move, load_place), next_station, true);
851 * Loads cargo onto a vehicle. If the vehicle has reserved cargo load that.
852 * Otherwise load cargo from the station.
853 * @param max_move Amount of cargo to load.
854 * @param dest Vehicle cargo list where the cargo resides.
855 * @param load_place The new loaded_at_xy to be assigned to packets being moved.
856 * @param next_station Next station(s) the loading vehicle will visit.
857 * @return Amount of cargo actually loaded.
858 * @note Vehicles may or may not reserve, depending on their orders. The two
859 * modes of loading are exclusive, though. If cargo is reserved we don't
860 * need to load unreserved cargo.
862 uint StationCargoList::Load(uint max_move, VehicleCargoList *dest, TileIndex load_place, StationIDStack next_station)
864 uint move = min(dest->ActionCount(VehicleCargoList::MTA_LOAD), max_move);
865 if (move > 0) {
866 this->reserved_count -= move;
867 dest->Reassign<VehicleCargoList::MTA_LOAD, VehicleCargoList::MTA_KEEP>(move);
868 return move;
869 } else {
870 return this->ShiftCargo(CargoLoad(this, dest, max_move, load_place), next_station, true);
875 * Routes packets with station "avoid" as next hop to a different place.
876 * @param max_move Maximum amount of cargo to move.
877 * @param dest List to append the cargo to.
878 * @param avoid Station to exclude from routing and current next hop of packets to reroute.
879 * @param avoid2 Additional station to exclude from routing.
880 * @oaram ge GoodsEntry to get the routing info from.
882 uint StationCargoList::Reroute(uint max_move, StationCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge)
884 return this->ShiftCargo(StationCargoReroute(this, dest, max_move, avoid, avoid2, ge), avoid, false);
888 * We have to instantiate everything we want to be usable.
890 template class CargoList<VehicleCargoList, CargoPacketList>;
891 template class CargoList<StationCargoList, StationCargoPacketMap>;
892 template uint VehicleCargoList::Reassign<VehicleCargoList::MTA_DELIVER, VehicleCargoList::MTA_KEEP>(uint, TileOrStationID);