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 "core/pool_func.hpp"
00014 #include "economy_base.h"
00015 #include "station_base.h"
00016 #include "cargodest_func.h"
00017 #include "cargodest_base.h"
00018 #include "settings_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   this->dest_xy     = INVALID_TILE;
00032   this->dest_id     = INVALID_SOURCE;
00033   this->dest_type   = ST_INDUSTRY;
00034   this->flags       = 0;
00035   this->next_order  = INVALID_ORDER;
00036   this->next_station = INVALID_STATION;
00037 }
00038 
00056 CargoPacket::CargoPacket(StationID source, TileIndex source_xy, uint16 count, SourceType source_type, SourceID source_id, TileIndex dest_xy, SourceType dest_type, SourceID dest_id, OrderID next_order, StationID next_station, byte flags) :
00057   feeder_share(0),
00058   count(count),
00059   days_in_transit(0),
00060   source_id(source_id),
00061   source(source),
00062   source_xy(source_xy),
00063   loaded_at_xy(0),
00064   dest_xy(dest_xy),
00065   dest_id(dest_id),
00066   flags(flags),
00067   next_order(next_order),
00068   next_station(next_station)
00069 {
00070   assert(count != 0);
00071   this->source_type  = source_type;
00072   this->dest_type    = dest_type;
00073 }
00074 
00095 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, TileIndex dest_xy, SourceType dest_type, SourceID dest_id, OrderID next_order, StationID next_station, byte flags) :
00096     feeder_share(feeder_share),
00097     count(count),
00098     days_in_transit(days_in_transit),
00099     source_id(source_id),
00100     source(source),
00101     source_xy(source_xy),
00102     loaded_at_xy(loaded_at_xy),
00103     dest_xy(dest_xy),
00104     dest_id(dest_id),
00105     flags(flags),
00106     next_order(next_order),
00107     next_station(next_station)
00108 {
00109   assert(count != 0);
00110   this->source_type = source_type;
00111   this->dest_type   = dest_type;
00112 }
00113 
00119 FORCEINLINE CargoPacket *CargoPacket::Split(uint new_size)
00120 {
00121   if (!CargoPacket::CanAllocateItem()) return NULL;
00122 
00123   Money fs = this->feeder_share * new_size / static_cast<uint>(this->count);
00124   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, this->dest_xy, this->dest_type, this->dest_id, this->next_order, this->next_station, this->flags);
00125   this->feeder_share -= fs;
00126   this->count -= new_size;
00127   return cp_new;
00128 }
00129 
00134 FORCEINLINE void CargoPacket::Merge(CargoPacket *cp)
00135 {
00136   this->count += cp->count;
00137   this->feeder_share += cp->feeder_share;
00138   delete cp;
00139 }
00140 
00146 /* static */ void CargoPacket::InvalidateAllFrom(SourceType src_type, SourceID src)
00147 {
00148   /* Clear next hop of those packets that loose their destination. */
00149   StationCargoList::InvalidateAllTo(src_type, src);
00150 
00151   CargoPacket *cp;
00152   FOR_ALL_CARGOPACKETS(cp) {
00153     if (cp->source_type == src_type && cp->source_id == src) cp->source_id = INVALID_SOURCE;
00154     if (cp->dest_type == src_type && cp->dest_id == src) {
00155       cp->dest_id = INVALID_SOURCE;
00156       cp->dest_xy = INVALID_TILE;
00157     }
00158   }
00159 }
00160 
00165 /* static */ void CargoPacket::InvalidateAllFrom(StationID sid)
00166 {
00167   CargoPacket *cp;
00168   FOR_ALL_CARGOPACKETS(cp) {
00169     if (cp->source == sid) cp->source = INVALID_STATION;
00170     if (cp->next_station == sid) cp->next_station = INVALID_STATION;
00171   }
00172 }
00173 
00174 /*
00175  *
00176  * Cargo list implementation
00177  *
00178  */
00179 
00183 template <class Tinst>
00184 CargoList<Tinst>::~CargoList()
00185 {
00186   for (Iterator it(this->packets.begin()); it != this->packets.end(); ++it) {
00187     delete *it;
00188   }
00189 }
00190 
00195 template <class Tinst>
00196 void CargoList<Tinst>::OnCleanPool()
00197 {
00198   this->packets.clear();
00199 }
00200 
00206 template <class Tinst>
00207 void CargoList<Tinst>::RemoveFromCache(const CargoPacket *cp)
00208 {
00209   this->count                 -= cp->count;
00210   this->cargo_days_in_transit -= cp->days_in_transit * cp->count;
00211 }
00212 
00218 template <class Tinst>
00219 void CargoList<Tinst>::AddToCache(const CargoPacket *cp)
00220 {
00221   this->count                 += cp->count;
00222   this->cargo_days_in_transit += cp->days_in_transit * cp->count;
00223 }
00224 
00233 template <class Tinst>
00234 void CargoList<Tinst>::Append(CargoPacket *cp)
00235 {
00236   assert(cp != NULL);
00237   static_cast<Tinst *>(this)->AddToCache(cp);
00238 
00239   for (List::reverse_iterator it(this->packets.rbegin()); it != this->packets.rend(); it++) {
00240     CargoPacket *icp = *it;
00241     if (Tinst::AreMergable(icp, cp) && icp->count + cp->count <= CargoPacket::MAX_COUNT) {
00242       icp->Merge(cp);
00243       return;
00244     }
00245   }
00246 
00247   /* The packet could not be merged with another one */
00248   this->packets.push_back(cp);
00249 }
00250 
00256 template <class Tinst>
00257 void CargoList<Tinst>::Truncate(uint max_remaining)
00258 {
00259   for (Iterator it(packets.begin()); it != packets.end(); /* done during loop*/) {
00260     CargoPacket *cp = *it;
00261     if (max_remaining == 0) {
00262       /* Nothing should remain, just remove the packets. */
00263       this->packets.erase(it++);
00264       static_cast<Tinst *>(this)->RemoveFromCache(cp);
00265       delete cp;
00266       continue;
00267     }
00268 
00269     uint local_count = cp->count;
00270     if (local_count > max_remaining) {
00271       uint diff = local_count - max_remaining;
00272       this->count -= diff;
00273       this->cargo_days_in_transit -= cp->days_in_transit * diff;
00274       static_cast<Tinst *>(this)->RemoveFromCacheLocal(cp, diff);
00275       cp->count = max_remaining;
00276       max_remaining = 0;
00277     } else {
00278       max_remaining -= local_count;
00279     }
00280     ++it;
00281   }
00282 }
00283 
00305 template <class Tinst>
00306 template <class Tother_inst>
00307 bool CargoList<Tinst>::MoveTo(Tother_inst *dest, uint max_move, MoveToAction mta, CargoPayment *payment, StationID st, OrderID cur_order, CargoID cid, bool *did_transfer)
00308 {
00309   assert(mta == MTA_FINAL_DELIVERY || dest != NULL);
00310   assert(mta == MTA_UNLOAD || mta == MTA_CARGO_LOAD || payment != NULL);
00311   assert(st != INVALID_STATION || (mta != MTA_CARGO_LOAD && payment == NULL));
00312 
00313 restart:;
00314   Iterator it(this->packets.begin());
00315   while (it != this->packets.end() && max_move > 0) {
00316     CargoPacket *cp = *it;
00317     MoveToAction cp_mta = mta;
00318     OrderID current_next_order = cp->NextHop();
00319     StationID current_next_unload = cp->NextStation();
00320 
00321     if (cp_mta == MTA_CARGO_LOAD) {
00322       /* Invalid next hop but valid destination? Recompute next hop. */
00323       if (current_next_order == INVALID_ORDER && cp->DestinationID() != INVALID_SOURCE) {
00324         if (!this->UpdateCargoNextHop(cp, Station::Get(st), cid)) {
00325           /* Failed to find destination, drop packet. */
00326           it = this->packets.erase(it);
00327           continue;
00328         }
00329         current_next_order = cp->NextHop();
00330         current_next_unload = cp->NextStation();
00331       }
00332 
00333       /* Loading and not for the current vehicle? Skip. */
00334       if (current_next_order != cur_order) {
00335         ++it;
00336         continue;
00337       }
00338     }
00339 
00340     /* Has this packet a destination and are we unloading to a station (not autoreplace)? */
00341     if (cp->DestinationID() != INVALID_SOURCE && cp_mta != MTA_CARGO_LOAD && payment != NULL) {
00342       /* Not forced unload and not for unloading at this station? Skip the packet. */
00343       if (cp_mta != MTA_UNLOAD && cp->NextStation() != INVALID_STATION && cp->NextStation() != st) {
00344         ++it;
00345         continue;
00346       }
00347 
00348       Station *station = Station::Get(st);
00349 
00350       bool found;
00351       StationID next_unload;
00352       RouteLink *link = FindRouteLinkForCargo(station, cid, cp, &next_unload, cur_order, &found);
00353       if (!found) {
00354         /* Sorry, link to destination vanished, make cargo disappear. */
00355         static_cast<Tinst *>(this)->RemoveFromCache(cp);
00356         delete cp;
00357         it = this->packets.erase(it);
00358         continue;
00359       }
00360 
00361       if (link != NULL) {
00362         /* Not final destination. */
00363         if (link->GetOriginOrderId() == cur_order && cp_mta != MTA_UNLOAD) {
00364           /* Cargo should stay on the vehicle and not forced unloading? Skip. */
00365           ++it;
00366           continue;
00367         }
00368         /* Force transfer and update next hop. */
00369         cp_mta = MTA_TRANSFER;
00370         current_next_order = link->GetOriginOrderId();
00371         current_next_unload = next_unload;
00372       } else {
00373         /* Final destination, deliver. */
00374         cp_mta = MTA_FINAL_DELIVERY;
00375       }
00376     } else if (cp_mta == MTA_NO_ACTION || (cp->source == st && cp_mta == MTA_FINAL_DELIVERY)) {
00377       /* Skip cargo that is not accepted or originated from this station. */
00378       ++it;
00379       continue;
00380     }
00381 
00382     if (did_transfer != NULL && cp_mta == MTA_TRANSFER) *did_transfer = true;
00383 
00384     if (cp->count <= max_move) {
00385       /* Can move the complete packet */
00386       max_move -= cp->count;
00387       this->packets.erase(it++);
00388       static_cast<Tinst *>(this)->RemoveFromCache(cp);
00389       cp->next_order = current_next_order;
00390       cp->next_station = current_next_unload;
00391       switch (cp_mta) {
00392         case MTA_FINAL_DELIVERY:
00393           payment->PayFinalDelivery(cp, cp->count);
00394           delete cp;
00395           continue; // of the loop
00396 
00397         case MTA_CARGO_LOAD:
00398           cp->loaded_at_xy = Station::Get(st)->xy;
00399           break;
00400 
00401         case MTA_TRANSFER:
00402           cp->feeder_share += payment->PayTransfer(cp, cp->count);
00403           break;
00404 
00405         default:
00406           break;
00407       }
00408       dest->Append(cp);
00409       continue;
00410     }
00411 
00412     /* Can move only part of the packet */
00413     if (cp_mta == MTA_FINAL_DELIVERY) {
00414       /* Final delivery doesn't need package splitting. */
00415       payment->PayFinalDelivery(cp, max_move);
00416 
00417       /* Remove the delivered data from the cache */
00418       uint left = cp->count - max_move;
00419       cp->count = max_move;
00420       static_cast<Tinst *>(this)->RemoveFromCache(cp);
00421 
00422       /* Final delivery payment pays the feeder share, so we have to
00423        * reset that so it is not 'shown' twice for partial unloads. */
00424       cp->feeder_share = 0;
00425       cp->count = left;
00426     } else {
00427       /* But... the rest needs package splitting. */
00428       CargoPacket *cp_new = cp->Split(max_move);
00429 
00430       /* We could not allocate a CargoPacket? Is the map that full? */
00431       if (cp_new == NULL) return false;
00432 
00433       static_cast<Tinst *>(this)->RemoveFromCache(cp_new); // this reflects the changes in cp.
00434       cp_new->next_order = current_next_order;
00435       cp_new->next_station = current_next_unload;
00436 
00437       if (cp_mta == MTA_TRANSFER) {
00438         /* Add the feeder share before inserting in dest. */
00439         cp_new->feeder_share += payment->PayTransfer(cp_new, max_move);
00440       } else if (cp_mta == MTA_CARGO_LOAD) {
00441         cp_new->loaded_at_xy = Station::Get(st)->xy;
00442       }
00443 
00444       dest->Append(cp_new);
00445     }
00446 
00447     max_move = 0;
00448   }
00449 
00450   if (max_move > 0 && mta == MTA_CARGO_LOAD && cur_order != INVALID_ORDER) {
00451     /* We loaded all packets for the next hop, now load all packets without destination. */
00452     cur_order = INVALID_ORDER;
00453     goto restart;
00454   }
00455 
00456   return it != packets.end();
00457 }
00458 
00460 template <class Tinst>
00461 void CargoList<Tinst>::InvalidateCache()
00462 {
00463   this->count = 0;
00464   this->cargo_days_in_transit = 0;
00465 
00466   for (ConstIterator it(this->packets.begin()); it != this->packets.end(); it++) {
00467     static_cast<Tinst *>(this)->AddToCache(*it);
00468   }
00469 }
00470 
00476 void VehicleCargoList::RemoveFromCache(const CargoPacket *cp)
00477 {
00478   this->feeder_share -= cp->feeder_share;
00479   this->Parent::RemoveFromCache(cp);
00480 }
00481 
00487 void VehicleCargoList::AddToCache(const CargoPacket *cp)
00488 {
00489   this->feeder_share += cp->feeder_share;
00490   this->Parent::AddToCache(cp);
00491 }
00492 
00496 void VehicleCargoList::AgeCargo()
00497 {
00498   for (ConstIterator it(this->packets.begin()); it != this->packets.end(); it++) {
00499     CargoPacket *cp = *it;
00500     /* If we're at the maximum, then we can't increase no more. */
00501     if (cp->days_in_transit == 0xFF) continue;
00502 
00503     cp->days_in_transit++;
00504     this->cargo_days_in_transit += cp->count;
00505   }
00506 }
00507 
00509 void VehicleCargoList::InvalidateCache()
00510 {
00511   this->feeder_share = 0;
00512   this->Parent::InvalidateCache();
00513 }
00514 
00516 void VehicleCargoList::InvalidateNextStation()
00517 {
00518   for (VehicleCargoList::ConstIterator it = this->packets.begin(); it != this->packets.end(); ++it) {
00519     (*it)->next_station = INVALID_STATION;
00520   }
00521 }
00522 
00528 void StationCargoList::RemoveFromCacheLocal(const CargoPacket *cp, uint amount)
00529 {
00530   this->order_cache[cp->next_order] -= amount;
00531   if (this->order_cache[cp->next_order] == 0) this->order_cache.erase(cp->next_order);
00532 }
00533 
00539 void StationCargoList::RemoveFromCache(const CargoPacket *cp)
00540 {
00541   this->RemoveFromCacheLocal(cp, cp->count);
00542   this->Parent::RemoveFromCache(cp);
00543 }
00544 
00550 void StationCargoList::AddToCache(const CargoPacket *cp)
00551 {
00552   this->order_cache[cp->next_order] += cp->count;
00553   this->Parent::AddToCache(cp);
00554 }
00555 
00557 void StationCargoList::InvalidateCache()
00558 {
00559   this->order_cache.clear();
00560   this->Parent::InvalidateCache();
00561 }
00562 
00570 bool StationCargoList::UpdateCargoNextHop(CargoPacket *cp, Station *st, CargoID cid)
00571 {
00572   StationID next_unload;
00573   RouteLink *l = FindRouteLinkForCargo(st, cid, cp, &next_unload);
00574 
00575   if (l == NULL) {
00576     /* No link to destination, drop packet. */
00577     this->RemoveFromCache(cp);
00578     delete cp;
00579     return false;
00580   }
00581 
00582   /* Update next hop info. */
00583   this->RemoveFromCache(cp);
00584   cp->next_station = next_unload;
00585   cp->next_order = l->GetOriginOrderId();
00586   this->AddToCache(cp);
00587 
00588   return true;
00589 }
00590 
00596 void StationCargoList::UpdateCargoNextHop(Station *st, CargoID cid)
00597 {
00598   int count = 0;
00599   StationCargoList::Iterator iter;
00600   for (iter = this->packets.begin(); count < this->next_start + _settings_game.economy.cargodest.route_recalc_chunk && iter != this->packets.end(); count++) {
00601     if (count < this->next_start) continue;
00602     if ((*iter)->DestinationID() != INVALID_SOURCE) {
00603       if (this->UpdateCargoNextHop(*iter, st, cid)) {
00604         ++iter;
00605       } else {
00606         iter = this->packets.erase(iter);
00607       }
00608     } else {
00609       ++iter;
00610     }
00611   }
00612 
00613   /* Update start counter for next loop. */
00614   this->next_start = count;
00615   if (this->next_start >= this->packets.size()) this->next_start = 0;
00616 }
00617 
00623 /* static */ void StationCargoList::InvalidateAllTo(OrderID order, StationID st_unload)
00624 {
00625   Station *st;
00626   FOR_ALL_STATIONS(st) {
00627     for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
00628       for (StationCargoList::Iterator it = st->goods[cid].cargo.packets.begin(); it != st->goods[cid].cargo.packets.end(); ++it) {
00629         CargoPacket *cp = *it;
00630         if (cp->next_order == order || cp->next_station == st_unload) {
00631           /* Invalidate both order and unload station as both likely
00632            * don't make sense anymore. */
00633           st->goods[cid].cargo.RemoveFromCache(cp);
00634           cp->next_order = INVALID_ORDER;
00635           cp->next_station = INVALID_STATION;
00636           st->goods[cid].cargo.AddToCache(cp);
00637         }
00638       }
00639     }
00640   }
00641 }
00642 
00647 /* static */ void StationCargoList::InvalidateAllTo(SourceType type, SourceID dest)
00648 {
00649   Station *st;
00650   FOR_ALL_STATIONS(st) {
00651     for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
00652       for (StationCargoList::Iterator it = st->goods[cid].cargo.packets.begin(); it != st->goods[cid].cargo.packets.end(); ++it) {
00653         CargoPacket *cp = *it;
00654         if (cp->dest_id == dest && cp->dest_type == type) {
00655           /* Invalidate both next order and unload station as we
00656            * want the packets to be not routed anymore. */
00657           st->goods[cid].cargo.RemoveFromCache(cp);
00658           cp->next_order = INVALID_ORDER;
00659           cp->next_station = INVALID_STATION;
00660           st->goods[cid].cargo.AddToCache(cp);
00661         }
00662       }
00663     }
00664   }
00665 }
00666 
00667 /*
00668  * We have to instantiate everything we want to be usable.
00669  */
00670 template class CargoList<VehicleCargoList>;
00671 template class CargoList<StationCargoList>;
00672 
00674 template bool CargoList<VehicleCargoList>::MoveTo(VehicleCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, StationID st, OrderID cur_order, CargoID cid, bool *did_transfer);
00676 template bool CargoList<VehicleCargoList>::MoveTo(StationCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, StationID st, OrderID cur_order, CargoID cid, bool *did_transfer);
00678 template bool CargoList<StationCargoList>::MoveTo(VehicleCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, StationID st, OrderID cur_order, CargoID cid, bool *did_transfer);

Generated on Sun May 8 07:30:10 2011 for OpenTTD by  doxygen 1.6.1