# 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 }