refresh.cpp

00001 /* $Id: refresh.cpp 26283 2014-01-28 19:49:43Z 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 "../core/bitmath_func.hpp"
00014 #include "../station_func.h"
00015 #include "../engine_base.h"
00016 #include "../vehicle_func.h"
00017 #include "refresh.h"
00018 #include "linkgraph.h"
00019 
00025 /* static */ void LinkRefresher::Run(Vehicle *v, bool allow_merge)
00026 {
00027   /* If there are no orders we can't predict anything.*/
00028   if (v->orders.list == NULL) return;
00029 
00030   /* Make sure the first order is a useful order. */
00031   const Order *first = v->orders.list->GetNextDecisionNode(v->GetOrder(v->cur_implicit_order_index), 0);
00032   if (first == NULL) return;
00033 
00034   HopSet seen_hops;
00035   LinkRefresher refresher(v, &seen_hops, allow_merge);
00036 
00037   refresher.RefreshLinks(first, first, v->last_loading_station != INVALID_STATION ? 1 << HAS_CARGO : 0);
00038 }
00039 
00045 bool LinkRefresher::Hop::operator<(const Hop &other) const
00046 {
00047   if (this->from < other.from) {
00048     return true;
00049   } else if (this->from > other.from) {
00050     return false;
00051   }
00052   if (this->to < other.to) {
00053     return true;
00054   } else if (this->to > other.to) {
00055     return false;
00056   }
00057   return this->cargo < other.cargo;
00058 }
00059 
00067 LinkRefresher::LinkRefresher(Vehicle *vehicle, HopSet *seen_hops, bool allow_merge) :
00068     vehicle(vehicle), seen_hops(seen_hops), cargo(CT_INVALID), allow_merge(allow_merge)
00069 {
00070   /* Assemble list of capacities and set last loading stations to 0. */
00071   for (Vehicle *v = this->vehicle; v != NULL; v = v->Next()) {
00072     this->refit_capacities.push_back(RefitDesc(v->cargo_type, v->cargo_cap, v->refit_cap));
00073     if (v->refit_cap > 0) this->capacities[v->cargo_type] += v->refit_cap;
00074   }
00075 }
00076 
00081 void LinkRefresher::HandleRefit(const Order *next)
00082 {
00083   this->cargo = next->GetRefitCargo();
00084   RefitList::iterator refit_it = this->refit_capacities.begin();
00085   for (Vehicle *v = this->vehicle; v != NULL; v = v->Next()) {
00086     const Engine *e = Engine::Get(v->engine_type);
00087     if (!HasBit(e->info.refit_mask, this->cargo)) {
00088       ++refit_it;
00089       continue;
00090     }
00091 
00092     /* Back up the vehicle's cargo type */
00093     CargoID temp_cid = v->cargo_type;
00094     byte temp_subtype = v->cargo_subtype;
00095     v->cargo_type = this->cargo;
00096     v->cargo_subtype = GetBestFittingSubType(v, v, this->cargo);
00097 
00098     uint16 mail_capacity = 0;
00099     uint amount = e->DetermineCapacity(v, &mail_capacity);
00100 
00101     /* Restore the original cargo type */
00102     v->cargo_type = temp_cid;
00103     v->cargo_subtype = temp_subtype;
00104 
00105     /* Skip on next refit. */
00106     if (this->cargo != refit_it->cargo && refit_it->remaining > 0) {
00107       this->capacities[refit_it->cargo] -= refit_it->remaining;
00108       refit_it->remaining = 0;
00109     } else if (amount < refit_it->remaining) {
00110       this->capacities[refit_it->cargo] -= refit_it->remaining - amount;
00111       refit_it->remaining = amount;
00112     }
00113     refit_it->capacity = amount;
00114     refit_it->cargo = this->cargo;
00115 
00116     ++refit_it;
00117 
00118     /* Special case for aircraft with mail. */
00119     if (v->type == VEH_AIRCRAFT) {
00120       if (mail_capacity < refit_it->remaining) {
00121         this->capacities[refit_it->cargo] -= refit_it->remaining - mail_capacity;
00122         refit_it->remaining = mail_capacity;
00123       }
00124       refit_it->capacity = mail_capacity;
00125       break; // aircraft have only one vehicle
00126     }
00127   }
00128 }
00129 
00133 void LinkRefresher::ResetRefit()
00134 {
00135   for (RefitList::iterator it(this->refit_capacities.begin()); it != this->refit_capacities.end(); ++it) {
00136     if (it->remaining == it->capacity) continue;
00137     this->capacities[it->cargo] += it->capacity - it->remaining;
00138     it->remaining = it->capacity;
00139   }
00140 }
00141 
00151 const Order *LinkRefresher::PredictNextOrder(const Order *cur, const Order *next, uint8 flags, uint num_hops)
00152 {
00153   /* next is good if it's either NULL (then the caller will stop the
00154    * evaluation) or if it's not conditional and the caller allows it to be
00155    * chosen (by setting USE_NEXT). */
00156   while (next != NULL && (!HasBit(flags, USE_NEXT) || next->IsType(OT_CONDITIONAL))) {
00157 
00158     /* After the first step any further non-conditional order is good,
00159      * regardless of previous USE_NEXT settings. The case of cur and next or
00160      * their respective stations being equal is handled elsewhere. */
00161     SetBit(flags, USE_NEXT);
00162 
00163     if (next->IsType(OT_CONDITIONAL)) {
00164       const Order *skip_to = this->vehicle->orders.list->GetNextDecisionNode(
00165           this->vehicle->orders.list->GetOrderAt(next->GetConditionSkipToOrder()), num_hops);
00166       if (skip_to != NULL && num_hops < this->vehicle->orders.list->GetNumOrders()) {
00167         /* Make copies of capacity tracking lists. There is potential
00168          * for optimization here: If the vehicle never refits we don't
00169          * need to copy anything. Also, if we've seen the branched link
00170          * before we don't need to branch at all. */
00171         LinkRefresher branch(*this);
00172         branch.RefreshLinks(cur, skip_to, flags, num_hops + 1);
00173       }
00174     }
00175 
00176     /* Reassign next with the following stop. This can be a station or a
00177      * depot.*/
00178     next = this->vehicle->orders.list->GetNextDecisionNode(
00179         this->vehicle->orders.list->GetNext(next), num_hops++);
00180   }
00181   return next;
00182 }
00183 
00189 void LinkRefresher::RefreshStats(const Order *cur, const Order *next)
00190 {
00191   StationID next_station = next->GetDestination();
00192   Station *st = Station::GetIfValid(cur->GetDestination());
00193   if (st != NULL && next_station != INVALID_STATION && next_station != st->index) {
00194     for (CapacitiesMap::const_iterator i = this->capacities.begin(); i != this->capacities.end(); ++i) {
00195       /* Refresh the link and give it a minimum capacity. */
00196 
00197       if (i->second == 0) continue;
00198       CargoID c = i->first;
00199 
00200       /* If not allowed to merge link graphs, make sure the stations are
00201        * already in the same link graph. */
00202       if (!this->allow_merge && st->goods[c].link_graph != Station::Get(next_station)->goods[c].link_graph) {
00203         continue;
00204       }
00205 
00206       /* A link is at least partly restricted if a
00207        * vehicle can't load at its source. */
00208       IncreaseStats(st, c, next_station, i->second,
00209           (cur->GetLoadType() & OLFB_NO_LOAD) == 0 ? LinkGraph::REFRESH_UNRESTRICTED : LinkGraph::REFRESH_RESTRICTED);
00210     }
00211   }
00212 }
00213 
00225 void LinkRefresher::RefreshLinks(const Order *cur, const Order *next, uint8 flags, uint num_hops)
00226 {
00227   while (next != NULL) {
00228 
00229     /* If the refit cargo is CT_AUTO_REFIT, we're optimistic and assume the
00230      * cargo will stay the same. The point of this method is to avoid
00231      * deadlocks due to vehicles waiting for cargo that isn't being routed,
00232      * yet. That situation will not occur if the vehicle is actually
00233      * carrying a different cargo in the end. */
00234     if ((next->IsType(OT_GOTO_DEPOT) || next->IsType(OT_GOTO_STATION)) &&
00235         next->IsRefit() && !next->IsAutoRefit()) {
00236       SetBit(flags, WAS_REFIT);
00237       this->HandleRefit(next);
00238     }
00239 
00240     /* Only reset the refit capacities if the "previous" next is a station,
00241      * meaning that either the vehicle was refit at the previous station or
00242      * it wasn't at all refit during the current hop. */
00243     if (HasBit(flags, WAS_REFIT) && (next->IsType(OT_GOTO_STATION) || next->IsType(OT_IMPLICIT))) {
00244       SetBit(flags, RESET_REFIT);
00245     } else {
00246       ClrBit(flags, RESET_REFIT);
00247     }
00248 
00249     next = this->PredictNextOrder(cur, next, flags, num_hops);
00250     if (next == NULL) break;
00251     Hop hop(cur->index, next->index, this->cargo);
00252     if (this->seen_hops->find(hop) != this->seen_hops->end()) {
00253       break;
00254     } else {
00255       this->seen_hops->insert(hop);
00256     }
00257 
00258     /* Don't use the same order again, but choose a new one in the next round. */
00259     ClrBit(flags, USE_NEXT);
00260 
00261     /* Skip resetting and link refreshing if next order won't do anything with cargo. */
00262     if (!next->IsType(OT_GOTO_STATION) && !next->IsType(OT_IMPLICIT)) continue;
00263 
00264     if (HasBit(flags, RESET_REFIT)) {
00265       this->ResetRefit();
00266       ClrBit(flags, RESET_REFIT);
00267       ClrBit(flags, WAS_REFIT);
00268     }
00269 
00270     if (cur->IsType(OT_GOTO_STATION) || cur->IsType(OT_IMPLICIT)) {
00271       if (cur->CanLeaveWithCargo(HasBit(flags, HAS_CARGO))) {
00272         SetBit(flags, HAS_CARGO);
00273         this->RefreshStats(cur, next);
00274       } else {
00275         ClrBit(flags, HAS_CARGO);
00276       }
00277     }
00278 
00279     /* "cur" is only assigned here if the stop is a station so that
00280      * whenever stats are to be increased two stations can be found. */
00281     cur = next;
00282   }
00283 }