diff options
Diffstat (limited to 'src/cargopacket.cpp')
-rw-r--r-- | src/cargopacket.cpp | 374 |
1 files changed, 267 insertions, 107 deletions
diff --git a/src/cargopacket.cpp b/src/cargopacket.cpp index 56ab6b91c..16a5e61b1 100644 --- a/src/cargopacket.cpp +++ b/src/cargopacket.cpp @@ -10,7 +10,9 @@ /** @file cargopacket.cpp Implementation of the cargo packets. */ #include "stdafx.h" +#include "station_base.h" #include "core/pool_func.hpp" +#include "core/random_func.hpp" #include "economy_base.h" #include "cargoaction.h" #include "order_type.h" @@ -151,8 +153,8 @@ void CargoPacket::Reduce(uint count) /** * Destroy the cargolist ("frees" all cargo packets). */ -template <class Tinst> -CargoList<Tinst>::~CargoList() +template <class Tinst, class Tcont> +CargoList<Tinst, Tcont>::~CargoList() { for (Iterator it(this->packets.begin()); it != this->packets.end(); ++it) { delete *it; @@ -163,8 +165,8 @@ CargoList<Tinst>::~CargoList() * Empty the cargo list, but don't free the cargo packets; * the cargo packets are cleaned by CargoPacket's CleanPool. */ -template <class Tinst> -void CargoList<Tinst>::OnCleanPool() +template <class Tinst, class Tcont> +void CargoList<Tinst, Tcont>::OnCleanPool() { this->packets.clear(); } @@ -175,8 +177,8 @@ void CargoList<Tinst>::OnCleanPool() * @param cp Packet to be removed from cache. * @param count Amount of cargo from the given packet to be removed. */ -template <class Tinst> -void CargoList<Tinst>::RemoveFromCache(const CargoPacket *cp, uint count) +template <class Tinst, class Tcont> +void CargoList<Tinst, Tcont>::RemoveFromCache(const CargoPacket *cp, uint count) { assert(count <= cp->count); this->count -= count; @@ -188,83 +190,16 @@ void CargoList<Tinst>::RemoveFromCache(const CargoPacket *cp, uint count) * Increases count and days_in_transit. * @param cp New packet to be inserted. */ -template <class Tinst> -void CargoList<Tinst>::AddToCache(const CargoPacket *cp) +template <class Tinst, class Tcont> +void CargoList<Tinst, Tcont>::AddToCache(const CargoPacket *cp) { this->count += cp->count; this->cargo_days_in_transit += cp->days_in_transit * cp->count; } -/** - * Truncates the cargo in this list to the given amount. It leaves the - * first cargo entities and removes max_move from the back of the list. - * @param max_move Maximum amount of entities to be removed from the list. - * @return Amount of entities actually moved. - */ -template <class Tinst> -uint CargoList<Tinst>::Truncate(uint max_move) -{ - max_move = min(this->count, max_move); - this->PopCargo(CargoRemoval<Tinst>(static_cast<Tinst *>(this), max_move)); - return max_move; -} - -/** - * Shifts cargo from the front of the packet list and applies some action to it. - * @tparam Taction Action class or function to be used. It should define - * "bool operator()(CargoPacket *)". If true is returned the - * cargo packet will be removed from the list. Otherwise it - * will be kept and the loop will be aborted. - * @param action Action instance to be applied. - */ -template <class Tinst> -template <class Taction> -void CargoList<Tinst>::ShiftCargo(Taction action) -{ - Iterator it(this->packets.begin()); - while (it != this->packets.end() && action.MaxMove() > 0) { - CargoPacket *cp = *it; - if (action(cp)) { - it = this->packets.erase(it); - } else { - break; - } - } -} - -/** - * Pops cargo from the back of the packet list and applies some action to it. - * @tparam Taction Action class or function to be used. It should define - * "bool operator()(CargoPacket *)". If true is returned the - * cargo packet will be removed from the list. Otherwise it - * will be kept and the loop will be aborted. - * @param action Action instance to be applied. - */ -template <class Tinst> -template <class Taction> -void CargoList<Tinst>::PopCargo(Taction action) -{ - if (this->packets.empty()) return; - Iterator it(--(this->packets.end())); - Iterator begin(this->packets.begin()); - while (action.MaxMove() > 0) { - CargoPacket *cp = *it; - if (action(cp)) { - if (it != begin) { - this->packets.erase(it--); - } else { - this->packets.erase(it); - break; - } - } else { - break; - } - } -} - /** Invalidates the cached data and rebuilds it. */ -template <class Tinst> -void CargoList<Tinst>::InvalidateCache() +template <class Tinst, class Tcont> +void CargoList<Tinst, Tcont>::InvalidateCache() { this->count = 0; this->cargo_days_in_transit = 0; @@ -281,11 +216,11 @@ void CargoList<Tinst>::InvalidateCache() * @param cp Packet to be eliminated. * @return If the packets could be merged. */ -template <class Tinst> -/* static */ bool CargoList<Tinst>::TryMerge(CargoPacket *icp, CargoPacket *cp) +template <class Tinst, class Tcont> +/* static */ bool CargoList<Tinst, Tcont>::TryMerge(CargoPacket *icp, CargoPacket *cp) { if (Tinst::AreMergable(icp, cp) && - icp->count + cp->count <= CargoPacket::MAX_COUNT) { + icp->count + cp->count <= CargoPacket::MAX_COUNT) { icp->Merge(cp); return true; } else { @@ -341,6 +276,57 @@ void VehicleCargoList::Append(CargoPacket *cp, MoveToAction action) } /** + * Shifts cargo from the front of the packet list and applies some action to it. + * @tparam Taction Action class or function to be used. It should define + * "bool operator()(CargoPacket *)". If true is returned the + * cargo packet will be removed from the list. Otherwise it + * will be kept and the loop will be aborted. + * @param action Action instance to be applied. + */ +template<class Taction> +void VehicleCargoList::ShiftCargo(Taction action) +{ + Iterator it(this->packets.begin()); + while (it != this->packets.end() && action.MaxMove() > 0) { + CargoPacket *cp = *it; + if (action(cp)) { + it = this->packets.erase(it); + } else { + break; + } + } +} + +/** + * Pops cargo from the back of the packet list and applies some action to it. + * @tparam Taction Action class or function to be used. It should define + * "bool operator()(CargoPacket *)". If true is returned the + * cargo packet will be removed from the list. Otherwise it + * will be kept and the loop will be aborted. + * @param action Action instance to be applied. + */ +template<class Taction> +void VehicleCargoList::PopCargo(Taction action) +{ + if (this->packets.empty()) return; + Iterator it(--(this->packets.end())); + Iterator begin(this->packets.begin()); + while (action.MaxMove() > 0) { + CargoPacket *cp = *it; + if (action(cp)) { + if (it != begin) { + this->packets.erase(it--); + } else { + this->packets.erase(it); + break; + } + } else { + break; + } + } +} + +/** * Update the cached values to reflect the removal of this packet or part of it. * Decreases count, feeder share and days_in_transit. * @param cp Packet to be removed from cache. @@ -406,16 +392,36 @@ void VehicleCargoList::AgeCargo() } /** + * Sets loaded_at_xy to the current station for all cargo to be transfered. + * This is done when stopping or skipping while the vehicle is unloading. In + * that case the vehicle will get part of its transfer credits early and it may + * get more transfer credits than it's entitled to. + * @param xy New loaded_at_xy for the cargo. + */ +void VehicleCargoList::SetTransferLoadPlace(TileIndex xy) +{ + uint sum = 0; + for (Iterator it = this->packets.begin(); sum < this->action_counts[MTA_TRANSFER]; ++it) { + CargoPacket *cp = *it; + cp->loaded_at_xy = xy; + sum += cp->count; + } +} + +/** * Stages cargo for unloading. The cargo is sorted so that packets to be * transferred, delivered or kept are in consecutive chunks in the list. At the * same time the designation_counts are updated to reflect the size of those * chunks. * @param accepted If the cargo will be accepted at the station. * @param current_station ID of the station. + * @param next_station ID of the station the vehicle will go to next. * @param order_flags OrderUnloadFlags that will apply to the unload operation. + * @param ge GoodsEntry for getting the flows. + * @param payment Payment object for registering transfers. * return If any cargo will be unloaded. */ -bool VehicleCargoList::Stage(bool accepted, StationID current_station, uint8 order_flags) +bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationID next_station, uint8 order_flags, const GoodsEntry *ge, CargoPayment *payment) { this->AssertCountConsistency(); assert(this->action_counts[MTA_LOAD] == 0); @@ -423,20 +429,59 @@ bool VehicleCargoList::Stage(bool accepted, StationID current_station, uint8 ord Iterator deliver = this->packets.end(); Iterator it = this->packets.begin(); uint sum = 0; + + bool force_keep = (order_flags & OUFB_NO_UNLOAD) != 0; + bool force_unload = (order_flags & OUFB_UNLOAD) != 0; + bool force_transfer = (order_flags & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0; + assert(this->count > 0 || it == this->packets.end()); while (sum < this->count) { CargoPacket *cp = *it; + this->packets.erase(it++); - if ((order_flags & OUFB_TRANSFER) != 0 || (!accepted && (order_flags & OUFB_UNLOAD) != 0)) { - this->packets.push_front(cp); - this->action_counts[MTA_TRANSFER] += cp->count; - } else if (accepted && current_station != cp->source && (order_flags & OUFB_NO_UNLOAD) == 0) { - this->packets.insert(deliver, cp); - this->action_counts[MTA_DELIVER] += cp->count; + StationID cargo_next = INVALID_STATION; + MoveToAction action = MTA_LOAD; + if (force_keep) { + action = MTA_KEEP; + } else if (force_unload && accepted && cp->source != current_station) { + 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); } else { - this->packets.push_back(cp); - if (deliver == this->packets.end()) --deliver; - this->action_counts[MTA_KEEP] += cp->count; + cargo_next = ge->GetVia(cp->source); + if (cargo_next == INVALID_STATION) { + 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) { + action = MTA_KEEP; + } else { + action = MTA_TRANSFER; + } + } + Money share; + switch (action) { + case MTA_KEEP: + this->packets.push_back(cp); + if (deliver == this->packets.end()) --deliver; + break; + case MTA_DELIVER: + this->packets.insert(deliver, cp); + break; + case MTA_TRANSFER: + this->packets.push_front(cp); + /* Add feeder share here to allow reusing field for next station. */ + share = payment->PayTransfer(cp, cp->count); + cp->AddFeederShare(share); + this->feeder_share += share; + cp->next_station = cargo_next; + break; + default: + NOT_REACHED(); } + this->action_counts[action] += cp->count; sum += cp->count; } this->AssertCountConsistency(); @@ -468,14 +513,15 @@ uint VehicleCargoList::Reassign(uint max_move, MoveToAction from, MoveToAction t /** * Returns reserved cargo to the station and removes it from the cache. - * @param dest Station the cargo is returned to. * @param max_move Maximum amount of cargo to move. + * @param dest Station the cargo is returned to. + * @param ID of next the station the cargo wants to go next. * @return Amount of cargo actually returned. */ -uint VehicleCargoList::Return(uint max_move, StationCargoList *dest) +uint VehicleCargoList::Return(uint max_move, StationCargoList *dest, StationID next) { max_move = min(this->action_counts[MTA_LOAD], max_move); - this->PopCargo(CargoReturn(this, dest, max_move)); + this->PopCargo(CargoReturn(this, dest, max_move, next)); return max_move; } @@ -505,7 +551,7 @@ uint VehicleCargoList::Unload(uint max_move, StationCargoList *dest, CargoPaymen uint moved = 0; if (this->action_counts[MTA_TRANSFER] > 0) { uint move = min(this->action_counts[MTA_TRANSFER], max_move); - this->ShiftCargo(CargoTransfer(this, dest, move, payment)); + this->ShiftCargo(CargoTransfer(this, dest, move)); moved += move; } if (this->action_counts[MTA_TRANSFER] == 0 && this->action_counts[MTA_DELIVER] > 0 && moved < max_move) { @@ -516,6 +562,19 @@ uint VehicleCargoList::Unload(uint max_move, StationCargoList *dest, CargoPaymen return moved; } +/** + * Truncates the cargo in this list to the given amount. It leaves the + * first cargo entities and removes max_move from the back of the list. + * @param max_move Maximum amount of entities to be removed from the list. + * @return Amount of entities actually moved. + */ +uint VehicleCargoList::Truncate(uint max_move) +{ + max_move = min(this->count, max_move); + this->PopCargo(CargoRemoval<VehicleCargoList>(this, max_move)); + return max_move; +} + /* * * Station cargo list implementation. @@ -523,24 +582,112 @@ uint VehicleCargoList::Unload(uint max_move, StationCargoList *dest, CargoPaymen */ /** - * Appends the given cargo packet. Tries to merge it with another one in the - * packets list. If no fitting packet is found, appends it. + * Appends the given cargo packet to the range of packets with the same next station * @warning After appending this packet may not exist anymore! * @note Do not use the cargo packet anymore after it has been appended to this CargoList! - * @param cp Cargo packet to add. + * @param next the next hop + * @param cp the cargo packet to add * @pre cp != NULL */ -void StationCargoList::Append(CargoPacket *cp) +void StationCargoList::Append(CargoPacket *cp, StationID next) { assert(cp != NULL); this->AddToCache(cp); - for (List::reverse_iterator it(this->packets.rbegin()); it != this->packets.rend(); it++) { + StationCargoPacketMap::List &list = this->packets[next]; + for (StationCargoPacketMap::List::reverse_iterator it(list.rbegin()); + it != list.rend(); it++) { if (StationCargoList::TryMerge(*it, cp)) return; } /* The packet could not be merged with another one */ - this->packets.push_back(cp); + list.push_back(cp); +} + +/** + * Shifts cargo from the front of the packet list for a specific station and + * applies some action to it. + * @tparam Taction Action class or function to be used. It should define + * "bool operator()(CargoPacket *)". If true is returned the + * cargo packet will be removed from the list. Otherwise it + * will be kept and the loop will be aborted. + * @param action Action instance to be applied. + * @param next Next hop the cargo wants to visit. + * @return True if all packets with the given next hop have been removed, + * False otherwise. + */ +template <class Taction> +bool StationCargoList::ShiftCargo(Taction &action, StationID next) +{ + std::pair<Iterator, Iterator> range(this->packets.equal_range(next)); + for (Iterator it(range.first); it != range.second && it.GetKey() == next;) { + if (action.MaxMove() == 0) return false; + CargoPacket *cp = *it; + if (action(cp)) { + it = this->packets.erase(it); + } else { + return false; + } + } + return true; +} + +/** + * Shifts cargo from the front of the packet list for a specific station and + * and optional also from the list for "any station", then applies some action + * to it. + * @tparam Taction Action class or function to be used. It should define + * "bool operator()(CargoPacket *)". If true is returned the + * cargo packet will be removed from the list. Otherwise it + * will be kept and the loop will be aborted. + * @param action Action instance to be applied. + * @param next Next hop the cargo wants to visit. + * @param include_invalid If cargo from the INVALID_STATION list should be + * used if necessary. + * @return Amount of cargo actually moved. + */ +template <class Taction> +uint StationCargoList::ShiftCargo(Taction action, StationID next, bool include_invalid) +{ + uint max_move = action.MaxMove(); + if (this->ShiftCargo(action, next) && include_invalid && action.MaxMove() > 0) { + this->ShiftCargo(action, INVALID_STATION); + } + return max_move - action.MaxMove(); +} + +/** + * Truncates where each destination loses roughly the same percentage of its + * cargo. This is done by randomizing the selection of packets to be removed. + * @param max_move Maximum amount of cargo to remove. + * @return Amount of cargo actually moved. + */ +uint StationCargoList::Truncate(uint max_move) +{ + max_move = min(max_move, this->count); + uint prev_count = this->count; + uint moved = 0; + while (max_move > moved) { + for (Iterator it(this->packets.begin()); it != this->packets.end();) { + if (prev_count > max_move && RandomRange(prev_count) < prev_count - max_move) { + ++it; + continue; + } + CargoPacket *cp = *it; + uint diff = max_move - moved; + if (cp->count > diff) { + this->RemoveFromCache(cp, diff); + cp->Reduce(diff); + return moved + diff; + } else { + it = this->packets.erase(it); + moved += cp->count; + this->RemoveFromCache(cp, cp->count); + delete cp; + } + } + } + return moved; } /** @@ -550,10 +697,10 @@ void StationCargoList::Append(CargoPacket *cp) * @param load_place Tile index of the current station. * @return Amount of cargo actually reserved. */ -uint StationCargoList::Reserve(uint max_move, VehicleCargoList *dest, TileIndex load_place) +uint StationCargoList::Reserve(uint max_move, VehicleCargoList *dest, TileIndex load_place, StationID next) { max_move = min(this->count, max_move); - this->ShiftCargo(CargoReservation(this, dest, max_move, load_place)); + this->ShiftCargo(CargoReservation(this, dest, max_move, load_place), next, true); return max_move; } @@ -564,21 +711,34 @@ uint StationCargoList::Reserve(uint max_move, VehicleCargoList *dest, TileIndex * @param max_move Amount of cargo to load. * @return Amount of cargo actually loaded. */ -uint StationCargoList::Load(uint max_move, VehicleCargoList *dest, TileIndex load_place) +uint StationCargoList::Load(uint max_move, VehicleCargoList *dest, TileIndex load_place, StationID next_station) { uint move = min(dest->ActionCount(VehicleCargoList::MTA_LOAD), max_move); if (move > 0) { this->reserved_count -= move; dest->Reassign(move, VehicleCargoList::MTA_LOAD, VehicleCargoList::MTA_KEEP); + return move; } else { move = min(this->count, max_move); - this->ShiftCargo(CargoLoad(this, dest, move, load_place)); + return this->ShiftCargo(CargoLoad(this, dest, move, load_place), next_station, true); } - return move; +} + +/** + * Routes packets with station "avoid" as next hop to a different place. + * @param max_move Maximum amount of cargo to move. + * @param dest List to append the cargo to. + * @param avoid Station to exclude from routing and current next hop of packets to reroute. + * @param avoid2 Additional station to exclude from routing. + * @oaram ge GoodsEntry to get the routing info from. + */ +uint StationCargoList::Reroute(uint max_move, StationCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge) +{ + return this->ShiftCargo(CargoReroute(this, dest, max_move, avoid, avoid2, ge), avoid, false); } /* * We have to instantiate everything we want to be usable. */ -template class CargoList<VehicleCargoList>; -template class CargoList<StationCargoList>; +template class CargoList<VehicleCargoList, CargoPacketList>; +template class CargoList<StationCargoList, StationCargoPacketMap>; |