cargopacket.cpp

Go to the documentation of this file.
00001 /* $Id$ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * 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.
00006  * 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.
00007  * 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/>.
00008  */
00009 
00012 #include "stdafx.h"
00013 #include "station_base.h"
00014 #include "core/pool_func.hpp"
00015 #include "core/random_func.hpp"
00016 #include "economy_base.h"
00017 
00018 /* Initialize the cargopacket-pool */
00019 CargoPacketPool _cargopacket_pool("CargoPacket");
00020 INSTANTIATE_POOL_METHODS(CargoPacket)
00021 
00022 
00025 CargoPacket::CargoPacket()
00026 {
00027   this->source_type = ST_INDUSTRY;
00028   this->source_id   = INVALID_SOURCE;
00029 }
00030 
00042 CargoPacket::CargoPacket(StationID source, TileIndex source_xy, uint16 count, SourceType source_type, SourceID source_id) :
00043   feeder_share(0),
00044   count(count),
00045   days_in_transit(0),
00046   source_id(source_id),
00047   source(source),
00048   source_xy(source_xy),
00049   loaded_at_xy(0)
00050 {
00051   assert(count != 0);
00052   this->source_type  = source_type;
00053 }
00054 
00069 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) :
00070     feeder_share(feeder_share),
00071     count(count),
00072     days_in_transit(days_in_transit),
00073     source_id(source_id),
00074     source(source),
00075     source_xy(source_xy),
00076     loaded_at_xy(loaded_at_xy)
00077 {
00078   assert(count != 0);
00079   this->source_type = source_type;
00080 }
00081 
00087 inline CargoPacket *CargoPacket::Split(uint new_size)
00088 {
00089   if (!CargoPacket::CanAllocateItem()) return NULL;
00090 
00091   Money fs = this->feeder_share * new_size / static_cast<uint>(this->count);
00092   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);
00093   this->feeder_share -= fs;
00094   this->count -= new_size;
00095   return cp_new;
00096 }
00097 
00102 inline void CargoPacket::Merge(CargoPacket *cp)
00103 {
00104   this->count += cp->count;
00105   this->feeder_share += cp->feeder_share;
00106   delete cp;
00107 }
00108 
00114 /* static */ void CargoPacket::InvalidateAllFrom(SourceType src_type, SourceID src)
00115 {
00116   CargoPacket *cp;
00117   FOR_ALL_CARGOPACKETS(cp) {
00118     if (cp->source_type == src_type && cp->source_id == src) cp->source_id = INVALID_SOURCE;
00119   }
00120 }
00121 
00126 /* static */ void CargoPacket::InvalidateAllFrom(StationID sid)
00127 {
00128   CargoPacket *cp;
00129   FOR_ALL_CARGOPACKETS(cp) {
00130     if (cp->source == sid) cp->source = INVALID_STATION;
00131   }
00132 }
00133 
00134 /*
00135  *
00136  * Cargo list implementation
00137  *
00138  */
00139 
00143 template <class Tinst, class Tcont>
00144 CargoList<Tinst, Tcont>::~CargoList()
00145 {
00146   for (Iterator it(this->packets.begin()); it != this->packets.end(); ++it) {
00147     delete *it;
00148   }
00149 }
00150 
00155 template <class Tinst, class Tcont>
00156 void CargoList<Tinst, Tcont>::OnCleanPool()
00157 {
00158   this->packets.clear();
00159 }
00160 
00166 template <class Tinst, class Tcont>
00167 void CargoList<Tinst, Tcont>::RemoveFromCache(const CargoPacket *cp)
00168 {
00169   this->count                 -= cp->count;
00170   this->cargo_days_in_transit -= cp->days_in_transit * cp->count;
00171 }
00172 
00178 template <class Tinst, class Tcont>
00179 void CargoList<Tinst, Tcont>::AddToCache(const CargoPacket *cp)
00180 {
00181   this->count                 += cp->count;
00182   this->cargo_days_in_transit += cp->days_in_transit * cp->count;
00183 }
00184 
00195 void VehicleCargoList::Append(CargoPacket *cp, bool update_cache)
00196 {
00197   assert(cp != NULL);
00198   if (update_cache) this->AddToCache(cp);
00199   for (CargoPacketList::reverse_iterator it(this->packets.rbegin()); it != this->packets.rend(); it++) {
00200     CargoPacket *icp = *it;
00201     if (VehicleCargoList::AreMergable(icp, cp) && icp->count + cp->count <= CargoPacket::MAX_COUNT) {
00202       icp->Merge(cp);
00203       return;
00204     }
00205   }
00206 
00207   /* The packet could not be merged with another one */
00208   this->packets.push_back(cp);
00209 }
00210 
00216 template <class Tinst, class Tcont>
00217 void CargoList<Tinst, Tcont>::Truncate(uint max_remaining)
00218 {
00219   for (Iterator it(packets.begin()); it != packets.end(); /* done during loop*/) {
00220     CargoPacket *cp = *it;
00221     if (max_remaining == 0) {
00222       /* Nothing should remain, just remove the packets. */
00223       it = this->packets.erase(it);
00224       static_cast<Tinst *>(this)->RemoveFromCache(cp);
00225       delete cp;
00226       continue;
00227     }
00228 
00229     uint local_count = cp->count;
00230     if (local_count > max_remaining) {
00231       uint diff = local_count - max_remaining;
00232       this->count -= diff;
00233       this->cargo_days_in_transit -= cp->days_in_transit * diff;
00234       cp->count = max_remaining;
00235       max_remaining = 0;
00236     } else {
00237       max_remaining -= local_count;
00238     }
00239     ++it;
00240   }
00241 }
00242 
00247 void VehicleCargoList::Reserve(CargoPacket *cp)
00248 {
00249   assert(cp != NULL);
00250   this->AddToCache(cp);
00251   this->reserved_count += cp->count;
00252   this->reserved.push_back(cp);
00253 }
00254 
00260 void VehicleCargoList::Unreserve(StationID next, StationCargoList *dest)
00261 {
00262   Iterator it(this->reserved.begin());
00263   while (it != this->reserved.end()) {
00264     CargoPacket *cp = *it;
00265     this->RemoveFromCache(cp);
00266     this->reserved_count -= cp->count;
00267     dest->Append(next, cp);
00268     it = this->reserved.erase(it);
00269   }
00270 }
00271 
00277 uint VehicleCargoList::LoadReserved(uint max_move)
00278 {
00279   uint orig_max = max_move;
00280   Iterator it(this->reserved.begin());
00281   while (it != this->reserved.end() && max_move > 0) {
00282     CargoPacket *cp = *it;
00283     if (cp->count <= max_move) {
00284       /* Can move the complete packet */
00285       max_move -= cp->count;
00286       it = this->reserved.erase(it);
00287       this->reserved_count -= cp->count;
00288       this->Append(cp, false);
00289     } else if (CargoPacket::CanAllocateItem()) {
00290       cp->count -= max_move;
00291       CargoPacket *cp_new = new CargoPacket(max_move, cp->days_in_transit, cp->source, cp->source_xy, cp->loaded_at_xy, 0, cp->source_type, cp->source_id);
00292       this->Append(cp_new, false);
00293       this->reserved_count -= max_move;
00294       max_move = 0;
00295     }
00296   }
00297   return orig_max - max_move;
00298 }
00299 
00310 template<class Tinst, class Tcont>
00311 uint CargoList<Tinst, Tcont>::MovePacket(VehicleCargoList *dest, Iterator &it, uint cap, TileIndex load_place, bool reserve)
00312 {
00313   CargoPacket *packet = this->RemovePacket(it, cap, load_place);
00314   uint ret = packet->count;
00315   if (reserve) {
00316     dest->Reserve(packet);
00317   } else {
00318     dest->Append(packet);
00319   }
00320   return ret;
00321 }
00322 
00332 template<class Tinst, class Tcont>
00333 uint CargoList<Tinst, Tcont>::MovePacket(StationCargoList *dest, StationID next, Iterator &it, uint cap)
00334 {
00335   CargoPacket *packet = this->RemovePacket(it, cap);
00336   uint ret = packet->count;
00337   dest->Append(next, packet);
00338   return ret;
00339 }
00340 
00350 template<class Tinst, class Tcont>
00351 CargoPacket *CargoList<Tinst, Tcont>::RemovePacket(Iterator &it, uint cap, TileIndex load_place)
00352 {
00353   CargoPacket *packet = *it;
00354   /* Load the packet if possible. */
00355   if (packet->count > cap) {
00356     /* Packet needs to be split. */
00357     packet = packet->Split(cap);
00358 
00359     /* We could not allocate a CargoPacket? Is the map that full?
00360      * Just remove the whole packet and drop some cargo then. */
00361     if (packet == NULL) {
00362       packet = *it;
00363       uint dropped = packet->count - cap;
00364       this->count -= dropped;
00365       this->cargo_days_in_transit -= dropped * packet->days_in_transit;
00366       packet->count = cap;
00367       it = this->packets.erase(it);
00368     } else {
00369       assert(packet->count == cap);
00370       ++it;
00371     }
00372   } else {
00373     it = this->packets.erase(it);
00374   }
00375   static_cast<Tinst *>(this)->RemoveFromCache(packet);
00376   if (load_place != INVALID_TILE) {
00377     packet->loaded_at_xy = load_place;
00378   }
00379   return packet;
00380 }
00381 
00385 template <class Tinst, class Tcont>
00386 void CargoList<Tinst, Tcont>::InvalidateCache()
00387 {
00388   this->count = 0;
00389   this->cargo_days_in_transit = 0;
00390 
00391   for (ConstIterator it(this->packets.begin()); it != this->packets.end(); it++) {
00392     static_cast<Tinst *>(this)->AddToCache(*it);
00393   }
00394 }
00395 
00399 VehicleCargoList::~VehicleCargoList()
00400 {
00401   for (Iterator it(this->reserved.begin()); it != this->reserved.end(); ++it) {
00402     delete *it;
00403   }
00404 }
00405 
00414 uint VehicleCargoList::DeliverPacket(Iterator &it, uint cap, CargoPayment *payment)
00415 {
00416   CargoPacket *p = *it;
00417   uint unloaded = 0;
00418   if (p->count <= cap) {
00419     payment->PayFinalDelivery(p, p->count);
00420     it = this->packets.erase(it);
00421     this->RemoveFromCache(p);
00422     unloaded = p->count;
00423     delete p;
00424   } else {
00425     payment->PayFinalDelivery(p, cap);
00426     this->count -= cap;
00427     this->cargo_days_in_transit -= cap * p->days_in_transit;
00428     this->feeder_share -= p->feeder_share;
00429     p->feeder_share = 0;
00430     p->count -= cap;
00431     unloaded = cap;
00432     ++it;
00433   }
00434   return unloaded;
00435 }
00436 
00443 uint VehicleCargoList::KeepPacket(Iterator &it)
00444 {
00445   CargoPacket *cp = *it;
00446   this->reserved.push_back(cp);
00447   this->reserved_count += cp->count;
00448   it = this->packets.erase(it);
00449   return cp->count;
00450 }
00451 
00463 uint VehicleCargoList::TransferPacket(Iterator &it, uint cap, StationCargoList *dest, CargoPayment *payment, StationID next)
00464 {
00465   CargoPacket *cp = this->RemovePacket(it, cap);
00466   cp->feeder_share += payment->PayTransfer(cp, cp->count);
00467   uint ret = cp->count;
00468   dest->Append(next, cp);
00469   return ret;
00470 }
00471 
00481 UnloadType StationCargoList::WillUnloadOld(byte flags, StationID source)
00482 {
00483   /* Try to unload cargo. */
00484   bool move = (flags & (UL_DELIVER | UL_ACCEPTED | UL_TRANSFER)) != 0;
00485   /* Try to deliver cargo if unloading. */
00486   bool deliver = (flags & UL_ACCEPTED) && !(flags & UL_TRANSFER) && (source != this->station->index);
00487   /* Transfer cargo if delivery was unsuccessful. */
00488   bool transfer = (flags & (UL_TRANSFER | UL_DELIVER)) != 0;
00489   if (move) {
00490     if (deliver) {
00491       return UL_DELIVER;
00492     } else if (transfer) {
00493       return UL_TRANSFER;
00494     } else {
00495       /* This case is for (non-)delivery to the source station without special flags.
00496        * Like the code in MoveTo did, we keep the packet in this case. */
00497       return UL_KEEP;
00498     }
00499   } else {
00500     return UL_KEEP;
00501   }
00502 }
00503 
00517 UnloadType StationCargoList::WillUnloadCargoDist(byte flags, StationID next, StationID via, StationID source)
00518 {
00519   if (via == this->station->index) {
00520     /* This is the final destination, deliver ... */
00521     if (flags & UL_TRANSFER) {
00522       /* .. except if explicitly told not to do so ... */
00523       return UL_TRANSFER;
00524     } else if (flags & UL_ACCEPTED) {
00525       return UL_DELIVER;
00526     } else if (flags & UL_DELIVER) {
00527       /* .. or if the station suddenly doesn't accept our cargo, but we have an explicit deliver order... */
00528       return UL_TRANSFER;
00529     } else {
00530       /* .. or else if it doesn't accept. */
00531       return UL_KEEP;
00532     }
00533   } else {
00534     /* Packet has to travel on, find out if it can stay on board. */
00535     if (flags & UL_DELIVER) {
00536       /* Order overrides cargodist:
00537        * Play by the old loading rules here as player is interfering with cargodist.
00538        * Try to deliver, as move has been forced upon us. */
00539       if ((flags & UL_ACCEPTED) && !(flags & UL_TRANSFER) && source != this->station->index) {
00540         return UL_DELIVER;
00541       } else {
00542         /* Transfer cargo, as delivering didn't work. */
00543         return UL_TRANSFER;
00544       }
00545     } else if (flags & UL_TRANSFER) {
00546       /* Transfer forced. */
00547       return UL_TRANSFER;
00548     } else if (next == via && next != INVALID_STATION) {
00549       /* Vehicle goes to the packet's next hop or has nondeterministic order: keep the packet. */
00550       return UL_KEEP;
00551     } else {
00552       /* Vehicle goes somewhere else, transfer the packet. */
00553       return UL_TRANSFER;
00554     }
00555   }
00556 }
00557 
00564 void VehicleCargoList::SwapReserved()
00565 {
00566   assert(this->packets.empty());
00567   this->packets.swap(this->reserved);
00568   this->reserved_count = 0;
00569 }
00570 
00592 uint StationCargoList::TakeFrom(VehicleCargoList *source, uint max_unload, OrderUnloadFlags order_flags, StationID next, bool has_stopped, CargoPayment *payment)
00593 {
00594   uint remaining_unload = max_unload;
00595   uint unloaded;
00596   byte flags = this->GetUnloadFlags(order_flags);
00597   GoodsEntry *dest = &this->station->goods[this->cargo];
00598   UnloadType action;
00599 
00600   for (VehicleCargoList::Iterator c = source->packets.begin(); c != source->packets.end() && remaining_unload > 0;) {
00601     StationID cargo_source = (*c)->source;
00602     FlowStatMap::const_iterator flows_it = dest->flows.find(cargo_source);
00603     StationID via;
00604     if (flows_it != dest->flows.end()) {
00605       via = flows_it->second.GetVia();
00606       /* Use cargodist unloading. */
00607       action = this->WillUnloadCargoDist(flags, next, via, cargo_source);
00608     } else {
00609       via = INVALID_STATION;
00610       /* There is no plan: use normal unloading. */
00611       action = this->WillUnloadOld(flags, cargo_source);
00612     }
00613 
00614     switch (action) {
00615       case UL_DELIVER:
00616         unloaded = source->DeliverPacket(c, remaining_unload, payment);
00617         remaining_unload -= unloaded;
00618         break;
00619       case UL_TRANSFER:
00620         /* TransferPacket may split the packet and return the transferred part. */
00621         if (via == this->station->index) via = flows_it->second.GetVia(via);
00622         unloaded = source->TransferPacket(c, remaining_unload, this, payment, via);
00623         remaining_unload -= unloaded;
00624         break;
00625       case UL_KEEP:
00626         unloaded = source->KeepPacket(c);
00627         break;
00628       default:
00629         NOT_REACHED();
00630     }
00631   }
00632   return max_unload - remaining_unload;
00633 }
00634 
00638 void VehicleCargoList::OnCleanPool()
00639 {
00640   this->reserved.clear();
00641   this->Parent::OnCleanPool();
00642 }
00643 
00649 void VehicleCargoList::RemoveFromCache(const CargoPacket *cp)
00650 {
00651   this->feeder_share -= cp->feeder_share;
00652   this->Parent::RemoveFromCache(cp);
00653 }
00654 
00660 void VehicleCargoList::AddToCache(const CargoPacket *cp)
00661 {
00662   this->feeder_share += cp->feeder_share;
00663   this->Parent::AddToCache(cp);
00664 }
00665 
00672 uint VehicleCargoList::MoveTo(VehicleCargoList *dest, uint cap)
00673 {
00674   uint orig_cap = cap;
00675   Iterator it = packets.begin();
00676   while (it != packets.end() && cap > 0) {
00677     cap -= MovePacket(dest, it, cap);
00678   }
00679   return orig_cap - cap;
00680 }
00681 
00685 void VehicleCargoList::AgeCargo()
00686 {
00687   for (ConstIterator it(this->packets.begin()); it != this->packets.end(); it++) {
00688     CargoPacket *cp = *it;
00689     /* If we're at the maximum, then we can't increase no more. */
00690     if (cp->days_in_transit == 0xFF) continue;
00691 
00692     cp->days_in_transit++;
00693     this->cargo_days_in_transit += cp->count;
00694   }
00695 }
00696 
00697 /*
00698  *
00699  * Station cargo list implementation
00700  *
00701  */
00702 
00708 inline byte StationCargoList::GetUnloadFlags(OrderUnloadFlags order_flags)
00709 {
00710   byte flags = 0;
00711   if (HasBit(this->station->goods[this->cargo].acceptance_pickup, GoodsEntry::GES_ACCEPTANCE)) {
00712     flags |= UL_ACCEPTED;
00713   }
00714   if (order_flags & OUFB_UNLOAD) {
00715     flags |= UL_DELIVER;
00716   }
00717   if (order_flags & OUFB_TRANSFER) {
00718     flags |= UL_TRANSFER;
00719   }
00720   return flags;
00721 }
00722 
00731 void StationCargoList::Append(StationID next, CargoPacket *cp)
00732 {
00733   assert(cp != NULL);
00734   this->AddToCache(cp);
00735 
00736   StationCargoPacketMap::List &list = this->packets[next];
00737   for (StationCargoPacketMap::List::reverse_iterator it(list.rbegin()); it != list.rend(); it++) {
00738     CargoPacket *icp = *it;
00739     if (StationCargoList::AreMergable(icp, cp) && icp->count + cp->count <= CargoPacket::MAX_COUNT) {
00740       icp->Merge(cp);
00741       return;
00742     }
00743   }
00744 
00745   /* The packet could not be merged with another one */
00746   list.push_back(cp);
00747 }
00748 
00758 uint StationCargoList::MovePackets(VehicleCargoList *dest, uint cap, Iterator begin, Iterator end, bool reserve)
00759 {
00760   uint orig_cap = cap;
00761   while (begin != end && cap > 0) {
00762     cap -= this->MovePacket(dest, begin, cap, this->station->xy, reserve);
00763   }
00764   return orig_cap - cap;
00765 }
00766 
00775 uint StationCargoList::MoveTo(VehicleCargoList *dest, uint cap, StationID next, bool reserve)
00776 {
00777   uint orig_cap = cap;
00778   std::pair<Iterator, Iterator> bounds(this->packets.equal_range(next));
00779   cap -= this->MovePackets(dest, cap, bounds.first, bounds.second, reserve);
00780   if (next != INVALID_STATION && cap > 0) {
00781     bounds = this->packets.equal_range(INVALID_STATION);
00782     cap -= this->MovePackets(dest, cap, bounds.first, bounds.second, reserve);
00783   }
00784   return orig_cap - cap;
00785 }
00786 
00791 void StationCargoList::RerouteStalePackets(StationID to)
00792 {
00793   std::pair<Iterator, Iterator> range(this->packets.equal_range(to));
00794   for (Iterator it(range.first); it != range.second && it.GetKey() == to;) {
00795     CargoPacket *packet = *it;
00796     it = this->packets.erase(it);
00797     StationID next = this->station->goods[this->cargo].GetVia(packet->source, this->station->index);
00798     assert(next != to);
00799 
00800     /* Legal, as insert doesn't invalidate iterators in the MultiMap, however
00801      * this might insert the packet between range.first and range.second (which might be end())
00802      * This is why we check for GetKey above to avoid infinite loops. */
00803     this->packets.Insert(next, packet);
00804   }
00805 }
00806 
00814 void StationCargoList::CountAndTruncate(uint max_remaining, StationCargoAmountMap &cargo_per_source)
00815 {
00816   uint prev_count = this->count;
00817   uint loop = 0;
00818   while (this->count > max_remaining) {
00819     for (Iterator it(this->packets.begin()); it != this->packets.end();) {
00820       CargoPacket *packet = *it;
00821       if (loop == 0) cargo_per_source[packet->source] += packet->count;
00822 
00823       if (RandomRange(prev_count) < max_remaining) {
00824         ++it;
00825         continue;
00826       }
00827 
00828       uint diff = this->count - max_remaining;
00829       if (packet->count > diff) {
00830         packet->count -= diff;
00831         this->count = max_remaining;
00832         this->cargo_days_in_transit -= packet->days_in_transit * diff;
00833         if (loop > 0) {
00834           return;
00835         } else {
00836           ++it;
00837         }
00838       } else {
00839         it = this->packets.erase(it);
00840         this->RemoveFromCache(packet);
00841         delete packet;
00842       }
00843     }
00844     loop++;
00845   }
00846 }
00847 
00851 void VehicleCargoList::InvalidateCache()
00852 {
00853   this->feeder_share = 0;
00854   this->reserved_count = 0;
00855   this->Parent::InvalidateCache();
00856   for (ConstIterator it(this->reserved.begin()); it != this->reserved.end(); it++) {
00857     this->AddToCache(*it);
00858     this->reserved_count += (*it)->count;
00859   }
00860 }
00861 
00867 void StationCargoList::AssignTo(Station *station, CargoID cargo)
00868 {
00869   assert(this->station == NULL);
00870   assert(station != NULL && cargo != INVALID_CARGO);
00871   this->station = station;
00872   this->cargo = cargo;
00873 }
00874 
00875 
00876 /*
00877  * We have to instantiate everything we want to be usable.
00878  */
00879 template class CargoList<VehicleCargoList, CargoPacketList>;
00880 template class CargoList<StationCargoList, StationCargoPacketMap>;