cargopacket.cpp

Go to the documentation of this file.
00001 /* $Id: cargopacket.cpp 25910 2013-10-23 19:04:06Z fonsinchen $ */
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 #include "cargoaction.h"
00018 #include "order_type.h"
00019 
00020 /* Initialize the cargopacket-pool */
00021 CargoPacketPool _cargopacket_pool("CargoPacket");
00022 INSTANTIATE_POOL_METHODS(CargoPacket)
00023 
00024 
00027 CargoPacket::CargoPacket()
00028 {
00029   this->source_type = ST_INDUSTRY;
00030   this->source_id   = INVALID_SOURCE;
00031 }
00032 
00044 CargoPacket::CargoPacket(StationID source, TileIndex source_xy, uint16 count, SourceType source_type, SourceID source_id) :
00045   feeder_share(0),
00046   count(count),
00047   days_in_transit(0),
00048   source_id(source_id),
00049   source(source),
00050   source_xy(source_xy),
00051   loaded_at_xy(0)
00052 {
00053   assert(count != 0);
00054   this->source_type  = source_type;
00055 }
00056 
00071 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) :
00072     feeder_share(feeder_share),
00073     count(count),
00074     days_in_transit(days_in_transit),
00075     source_id(source_id),
00076     source(source),
00077     source_xy(source_xy),
00078     loaded_at_xy(loaded_at_xy)
00079 {
00080   assert(count != 0);
00081   this->source_type = source_type;
00082 }
00083 
00089 CargoPacket *CargoPacket::Split(uint new_size)
00090 {
00091   if (!CargoPacket::CanAllocateItem()) return NULL;
00092 
00093   Money fs = this->FeederShare(new_size);
00094   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);
00095   this->feeder_share -= fs;
00096   this->count -= new_size;
00097   return cp_new;
00098 }
00099 
00104 void CargoPacket::Merge(CargoPacket *cp)
00105 {
00106   this->count += cp->count;
00107   this->feeder_share += cp->feeder_share;
00108   delete cp;
00109 }
00110 
00115 void CargoPacket::Reduce(uint count)
00116 {
00117   assert(count < this->count);
00118   this->feeder_share -= this->FeederShare(count);
00119   this->count -= count;
00120 }
00121 
00127 /* static */ void CargoPacket::InvalidateAllFrom(SourceType src_type, SourceID src)
00128 {
00129   CargoPacket *cp;
00130   FOR_ALL_CARGOPACKETS(cp) {
00131     if (cp->source_type == src_type && cp->source_id == src) cp->source_id = INVALID_SOURCE;
00132   }
00133 }
00134 
00139 /* static */ void CargoPacket::InvalidateAllFrom(StationID sid)
00140 {
00141   CargoPacket *cp;
00142   FOR_ALL_CARGOPACKETS(cp) {
00143     if (cp->source == sid) cp->source = INVALID_STATION;
00144   }
00145 }
00146 
00147 /*
00148  *
00149  * Cargo list implementation
00150  *
00151  */
00152 
00156 template <class Tinst, class Tcont>
00157 CargoList<Tinst, Tcont>::~CargoList()
00158 {
00159   for (Iterator it(this->packets.begin()); it != this->packets.end(); ++it) {
00160     delete *it;
00161   }
00162 }
00163 
00168 template <class Tinst, class Tcont>
00169 void CargoList<Tinst, Tcont>::OnCleanPool()
00170 {
00171   this->packets.clear();
00172 }
00173 
00180 template <class Tinst, class Tcont>
00181 void CargoList<Tinst, Tcont>::RemoveFromCache(const CargoPacket *cp, uint count)
00182 {
00183   assert(count <= cp->count);
00184   this->count                 -= count;
00185   this->cargo_days_in_transit -= cp->days_in_transit * count;
00186 }
00187 
00193 template <class Tinst, class Tcont>
00194 void CargoList<Tinst, Tcont>::AddToCache(const CargoPacket *cp)
00195 {
00196   this->count                 += cp->count;
00197   this->cargo_days_in_transit += cp->days_in_transit * cp->count;
00198 }
00199 
00201 template <class Tinst, class Tcont>
00202 void CargoList<Tinst, Tcont>::InvalidateCache()
00203 {
00204   this->count = 0;
00205   this->cargo_days_in_transit = 0;
00206 
00207   for (ConstIterator it(this->packets.begin()); it != this->packets.end(); it++) {
00208     static_cast<Tinst *>(this)->AddToCache(*it);
00209   }
00210 }
00211 
00219 template <class Tinst, class Tcont>
00220 /* static */ bool CargoList<Tinst, Tcont>::TryMerge(CargoPacket *icp, CargoPacket *cp)
00221 {
00222   if (Tinst::AreMergable(icp, cp) &&
00223       icp->count + cp->count <= CargoPacket::MAX_COUNT) {
00224     icp->Merge(cp);
00225     return true;
00226   } else {
00227     return false;
00228   }
00229 }
00230 
00231 /*
00232  *
00233  * Vehicle cargo list implementation.
00234  *
00235  */
00236 
00252 void VehicleCargoList::Append(CargoPacket *cp, MoveToAction action)
00253 {
00254   assert(cp != NULL);
00255   assert(action == MTA_LOAD ||
00256       (action == MTA_KEEP && this->action_counts[MTA_LOAD] == 0));
00257   this->AddToMeta(cp, action);
00258 
00259   if (this->count == cp->count) {
00260     this->packets.push_back(cp);
00261     return;
00262   }
00263 
00264   uint sum = cp->count;
00265   for (ReverseIterator it(this->packets.rbegin()); it != this->packets.rend(); it++) {
00266     CargoPacket *icp = *it;
00267     if (VehicleCargoList::TryMerge(icp, cp)) return;
00268     sum += icp->count;
00269     if (sum >= this->action_counts[action]) {
00270       this->packets.push_back(cp);
00271       return;
00272     }
00273   }
00274 
00275   NOT_REACHED();
00276 }
00277 
00286 template<class Taction>
00287 void VehicleCargoList::ShiftCargo(Taction action)
00288 {
00289   Iterator it(this->packets.begin());
00290   while (it != this->packets.end() && action.MaxMove() > 0) {
00291     CargoPacket *cp = *it;
00292     if (action(cp)) {
00293       it = this->packets.erase(it);
00294     } else {
00295       break;
00296     }
00297   }
00298 }
00299 
00308 template<class Taction>
00309 void VehicleCargoList::PopCargo(Taction action)
00310 {
00311   if (this->packets.empty()) return;
00312   Iterator it(--(this->packets.end()));
00313   Iterator begin(this->packets.begin());
00314   while (action.MaxMove() > 0) {
00315     CargoPacket *cp = *it;
00316     if (action(cp)) {
00317       if (it != begin) {
00318         this->packets.erase(it--);
00319       } else {
00320         this->packets.erase(it);
00321         break;
00322       }
00323     } else {
00324       break;
00325     }
00326   }
00327 }
00328 
00335 void VehicleCargoList::RemoveFromCache(const CargoPacket *cp, uint count)
00336 {
00337   this->feeder_share -= cp->FeederShare(count);
00338   this->Parent::RemoveFromCache(cp, count);
00339 }
00340 
00346 void VehicleCargoList::AddToCache(const CargoPacket *cp)
00347 {
00348   this->feeder_share += cp->feeder_share;
00349   this->Parent::AddToCache(cp);
00350 }
00351 
00358 void VehicleCargoList::RemoveFromMeta(const CargoPacket *cp, MoveToAction action, uint count)
00359 {
00360   this->AssertCountConsistency();
00361   this->RemoveFromCache(cp, count);
00362   this->action_counts[action] -= count;
00363   this->AssertCountConsistency();
00364 }
00365 
00371 void VehicleCargoList::AddToMeta(const CargoPacket *cp, MoveToAction action)
00372 {
00373   this->AssertCountConsistency();
00374   this->AddToCache(cp);
00375   this->action_counts[action] += cp->count;
00376   this->AssertCountConsistency();
00377 }
00378 
00382 void VehicleCargoList::AgeCargo()
00383 {
00384   for (ConstIterator it(this->packets.begin()); it != this->packets.end(); it++) {
00385     CargoPacket *cp = *it;
00386     /* If we're at the maximum, then we can't increase no more. */
00387     if (cp->days_in_transit == 0xFF) continue;
00388 
00389     cp->days_in_transit++;
00390     this->cargo_days_in_transit += cp->count;
00391   }
00392 }
00393 
00401 void VehicleCargoList::SetTransferLoadPlace(TileIndex xy)
00402 {
00403   uint sum = 0;
00404   for (Iterator it = this->packets.begin(); sum < this->action_counts[MTA_TRANSFER]; ++it) {
00405     CargoPacket *cp = *it;
00406     cp->loaded_at_xy = xy;
00407     sum += cp->count;
00408   }
00409 }
00410 
00420 /* static */ VehicleCargoList::MoveToAction VehicleCargoList::ChooseAction(const CargoPacket *cp, StationID cargo_next,
00421     StationID current_station, bool accepted, StationIDStack next_station)
00422 {
00423   if (cargo_next == INVALID_STATION) {
00424     return (accepted && cp->source != current_station) ? MTA_DELIVER : MTA_KEEP;
00425   } else if (cargo_next == current_station) {
00426     return MTA_DELIVER;
00427   } else if (next_station.Contains(cargo_next)) {
00428     return MTA_KEEP;
00429   } else {
00430     return MTA_TRANSFER;
00431   }
00432 }
00433 
00447 bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationIDStack next_station, uint8 order_flags, const GoodsEntry *ge, CargoPayment *payment)
00448 {
00449   this->AssertCountConsistency();
00450   assert(this->action_counts[MTA_LOAD] == 0);
00451   this->action_counts[MTA_TRANSFER] = this->action_counts[MTA_DELIVER] = this->action_counts[MTA_KEEP] = 0;
00452   Iterator deliver = this->packets.end();
00453   Iterator it = this->packets.begin();
00454   uint sum = 0;
00455 
00456   bool force_keep = (order_flags & OUFB_NO_UNLOAD) != 0;
00457   bool force_unload = (order_flags & OUFB_UNLOAD) != 0;
00458   bool force_transfer = (order_flags & (OUFB_TRANSFER | OUFB_UNLOAD)) != 0;
00459   assert(this->count > 0 || it == this->packets.end());
00460   while (sum < this->count) {
00461     CargoPacket *cp = *it;
00462 
00463     this->packets.erase(it++);
00464     StationID cargo_next = INVALID_STATION;
00465     MoveToAction action = MTA_LOAD;
00466     if (force_keep) {
00467       action = MTA_KEEP;
00468     } else if (force_unload && accepted && cp->source != current_station) {
00469       action = MTA_DELIVER;
00470     } else if (force_transfer) {
00471       action = MTA_TRANSFER;
00472       /* We cannot send the cargo to any of the possible next hops and
00473        * also not to the current station. */
00474       FlowStatMap::const_iterator flow_it(ge->flows.find(cp->source));
00475       if (flow_it == ge->flows.end()) {
00476         cargo_next = INVALID_STATION;
00477       } else {
00478         FlowStat new_shares = flow_it->second;
00479         new_shares.ChangeShare(current_station, INT_MIN);
00480         StationIDStack excluded = next_station;
00481         while (!excluded.IsEmpty() && !new_shares.GetShares()->empty()) {
00482           new_shares.ChangeShare(excluded.Pop(), INT_MIN);
00483         }
00484         if (new_shares.GetShares()->empty()) {
00485           cargo_next = INVALID_STATION;
00486         } else {
00487           cargo_next = new_shares.GetVia();
00488         }
00489       }
00490     } else {
00491       /* Rewrite an invalid source station to some random other one to
00492        * avoid keeping the cargo in the vehicle forever. */
00493       if (cp->source == INVALID_STATION && !ge->flows.empty()) {
00494         cp->source = ge->flows.begin()->first;
00495       }
00496       bool restricted = false;
00497       FlowStatMap::const_iterator flow_it(ge->flows.find(cp->source));
00498       if (flow_it == ge->flows.end()) {
00499         cargo_next = INVALID_STATION;
00500       } else {
00501         cargo_next = flow_it->second.GetViaWithRestricted(restricted);
00502       }
00503       action = VehicleCargoList::ChooseAction(cp, cargo_next, current_station, accepted, next_station);
00504       if (restricted && action == MTA_TRANSFER) {
00505         /* If the flow is restricted we can't transfer to it. Choose an
00506          * unrestricted one instead. */
00507         cargo_next = flow_it->second.GetVia();
00508         action = VehicleCargoList::ChooseAction(cp, cargo_next, current_station, accepted, next_station);
00509       }
00510     }
00511     Money share;
00512     switch (action) {
00513       case MTA_KEEP:
00514         this->packets.push_back(cp);
00515         if (deliver == this->packets.end()) --deliver;
00516         break;
00517       case MTA_DELIVER:
00518         this->packets.insert(deliver, cp);
00519         break;
00520       case MTA_TRANSFER:
00521         this->packets.push_front(cp);
00522         /* Add feeder share here to allow reusing field for next station. */
00523         share = payment->PayTransfer(cp, cp->count);
00524         cp->AddFeederShare(share);
00525         this->feeder_share += share;
00526         cp->next_station = cargo_next;
00527         break;
00528       default:
00529         NOT_REACHED();
00530     }
00531     this->action_counts[action] += cp->count;
00532     sum += cp->count;
00533   }
00534   this->AssertCountConsistency();
00535   return this->action_counts[MTA_DELIVER] > 0 || this->action_counts[MTA_TRANSFER] > 0;
00536 }
00537 
00539 void VehicleCargoList::InvalidateCache()
00540 {
00541   this->feeder_share = 0;
00542   this->Parent::InvalidateCache();
00543 }
00544 
00552 uint VehicleCargoList::Reassign(uint max_move, MoveToAction from, MoveToAction to)
00553 {
00554   max_move = min(this->action_counts[from], max_move);
00555   assert(Delta((int)from, (int)to) == 1);
00556   this->action_counts[from] -= max_move;
00557   this->action_counts[to] += max_move;
00558   return max_move;
00559 }
00560 
00568 uint VehicleCargoList::Return(uint max_move, StationCargoList *dest, StationID next)
00569 {
00570   max_move = min(this->action_counts[MTA_LOAD], max_move);
00571   this->PopCargo(CargoReturn(this, dest, max_move, next));
00572   return max_move;
00573 }
00574 
00581 uint VehicleCargoList::Shift(uint max_move, VehicleCargoList *dest)
00582 {
00583   max_move = min(this->count, max_move);
00584   this->PopCargo(CargoShift(this, dest, max_move));
00585   return max_move;
00586 }
00587 
00596 uint VehicleCargoList::Unload(uint max_move, StationCargoList *dest, CargoPayment *payment)
00597 {
00598   uint moved = 0;
00599   if (this->action_counts[MTA_TRANSFER] > 0) {
00600     uint move = min(this->action_counts[MTA_TRANSFER], max_move);
00601     this->ShiftCargo(CargoTransfer(this, dest, move));
00602     moved += move;
00603   }
00604   if (this->action_counts[MTA_TRANSFER] == 0 && this->action_counts[MTA_DELIVER] > 0 && moved < max_move) {
00605     uint move = min(this->action_counts[MTA_DELIVER], max_move - moved);
00606     this->ShiftCargo(CargoDelivery(this, move, payment));
00607     moved += move;
00608   }
00609   return moved;
00610 }
00611 
00618 uint VehicleCargoList::Truncate(uint max_move)
00619 {
00620   max_move = min(this->count, max_move);
00621   this->PopCargo(CargoRemoval<VehicleCargoList>(this, max_move));
00622   return max_move;
00623 }
00624 
00633 uint VehicleCargoList::Reroute(uint max_move, VehicleCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge)
00634 {
00635   max_move = min(this->action_counts[MTA_TRANSFER], max_move);
00636   this->ShiftCargo(VehicleCargoReroute(this, dest, max_move, avoid, avoid2, ge));
00637   return max_move;
00638 }
00639 
00640 /*
00641  *
00642  * Station cargo list implementation.
00643  *
00644  */
00645 
00654 void StationCargoList::Append(CargoPacket *cp, StationID next)
00655 {
00656   assert(cp != NULL);
00657   this->AddToCache(cp);
00658 
00659   StationCargoPacketMap::List &list = this->packets[next];
00660   for (StationCargoPacketMap::List::reverse_iterator it(list.rbegin());
00661       it != list.rend(); it++) {
00662     if (StationCargoList::TryMerge(*it, cp)) return;
00663   }
00664 
00665   /* The packet could not be merged with another one */
00666   list.push_back(cp);
00667 }
00668 
00681 template <class Taction>
00682 bool StationCargoList::ShiftCargo(Taction &action, StationID next)
00683 {
00684   std::pair<Iterator, Iterator> range(this->packets.equal_range(next));
00685   for (Iterator it(range.first); it != range.second && it.GetKey() == next;) {
00686     if (action.MaxMove() == 0) return false;
00687     CargoPacket *cp = *it;
00688     if (action(cp)) {
00689       it = this->packets.erase(it);
00690     } else {
00691       return false;
00692     }
00693   }
00694   return true;
00695 }
00696 
00711 template <class Taction>
00712 uint StationCargoList::ShiftCargo(Taction action, StationIDStack next, bool include_invalid)
00713 {
00714   uint max_move = action.MaxMove();
00715   while (!next.IsEmpty()) {
00716     this->ShiftCargo(action, next.Pop());
00717     if (action.MaxMove() == 0) break;
00718   }
00719   if (include_invalid && action.MaxMove() > 0) {
00720     this->ShiftCargo(action, INVALID_STATION);
00721   }
00722   return max_move - action.MaxMove();
00723 }
00724 
00733 uint StationCargoList::Truncate(uint max_move, StationCargoAmountMap *cargo_per_source)
00734 {
00735   max_move = min(max_move, this->count);
00736   uint prev_count = this->count;
00737   uint moved = 0;
00738   uint loop = 0;
00739   bool do_count = cargo_per_source != NULL;
00740   while (max_move > moved) {
00741     for (Iterator it(this->packets.begin()); it != this->packets.end();) {
00742       CargoPacket *cp = *it;
00743       if (prev_count > max_move && RandomRange(prev_count) < prev_count - max_move) {
00744         if (do_count && loop == 0) {
00745           (*cargo_per_source)[cp->source] += cp->count;
00746         }
00747         ++it;
00748         continue;
00749       }
00750       uint diff = max_move - moved;
00751       if (cp->count > diff) {
00752         if (diff > 0) {
00753           this->RemoveFromCache(cp, diff);
00754           cp->Reduce(diff);
00755           moved += diff;
00756         }
00757         if (loop > 0) {
00758           if (do_count) (*cargo_per_source)[cp->source] -= diff;
00759           return moved;
00760         } else {
00761           if (do_count) (*cargo_per_source)[cp->source] += cp->count;
00762           ++it;
00763         }
00764       } else {
00765         it = this->packets.erase(it);
00766         if (do_count && loop > 0) {
00767           (*cargo_per_source)[cp->source] -= cp->count;
00768         }
00769         moved += cp->count;
00770         this->RemoveFromCache(cp, cp->count);
00771         delete cp;
00772       }
00773     }
00774     loop++;
00775   }
00776   return moved;
00777 }
00778 
00787 uint StationCargoList::Reserve(uint max_move, VehicleCargoList *dest, TileIndex load_place, StationIDStack next_station)
00788 {
00789   return this->ShiftCargo(CargoReservation(this, dest, max_move, load_place), next_station, true);
00790 }
00791 
00804 uint StationCargoList::Load(uint max_move, VehicleCargoList *dest, TileIndex load_place, StationIDStack next_station)
00805 {
00806   uint move = min(dest->ActionCount(VehicleCargoList::MTA_LOAD), max_move);
00807   if (move > 0) {
00808     this->reserved_count -= move;
00809     dest->Reassign(move, VehicleCargoList::MTA_LOAD, VehicleCargoList::MTA_KEEP);
00810     return move;
00811   } else {
00812     return this->ShiftCargo(CargoLoad(this, dest, max_move, load_place), next_station, true);
00813   }
00814 }
00815 
00824 uint StationCargoList::Reroute(uint max_move, StationCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge)
00825 {
00826   return this->ShiftCargo(StationCargoReroute(this, dest, max_move, avoid, avoid2, ge), avoid, false);
00827 }
00828 
00829 /*
00830  * We have to instantiate everything we want to be usable.
00831  */
00832 template class CargoList<VehicleCargoList, CargoPacketList>;
00833 template class CargoList<StationCargoList, StationCargoPacketMap>;