cargodest.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 "cargodest_type.h"
00014 #include "cargodest_base.h"
00015 #include "cargodest_func.h"
00016 #include "core/bitmath_func.hpp"
00017 #include "core/random_func.hpp"
00018 #include "core/pool_func.hpp"
00019 #include "cargotype.h"
00020 #include "settings_type.h"
00021 #include "town.h"
00022 #include "industry.h"
00023 #include "window_func.h"
00024 #include "vehicle_base.h"
00025 #include "station_base.h"
00026 #include "pathfinder/yapf/yapf.h"
00027 #include "company_base.h"
00028 
00029 
00030 /* Possible link weight modifiers. */
00031 static const byte LWM_ANYWHERE      = 1; 
00032 static const byte LWM_TONY_ANY      = 2; 
00033 static const byte LWM_TONY_BIG      = 3; 
00034 static const byte LWM_TONY_CITY     = 4; 
00035 static const byte LWM_TONY_NEARBY   = 5; 
00036 static const byte LWM_INTOWN        = 8; 
00037 static const byte LWM_IND_ANY       = 2; 
00038 static const byte LWM_IND_NEARBY    = 3; 
00039 static const byte LWM_IND_PRODUCING = 4; 
00040 
00041 static const uint MAX_EXTRA_LINKS       = 2; 
00042 static const uint MAX_IND_STOCKPILE     = 1000; 
00043 
00044 static const uint BASE_TOWN_LINKS       = 0; 
00045 static const uint BASE_TOWN_LINKS_SYMM  = 1; 
00046 static const uint BASE_IND_LINKS        = 0; 
00047 static const uint BASE_IND_LINKS_TOWN   = 1; 
00048 static const uint BASE_IND_LINKS_SYMM   = 2; 
00049 static const uint BIG_TOWN_POP_MAIL     = 0; 
00050 static const uint BIG_TOWN_POP_PAX      = 1; 
00051 static const uint SCALE_TOWN            = 0; 
00052 static const uint SCALE_TOWN_BIG        = 1; 
00053 static const uint SCALE_TOWN_PAX        = 2; 
00054 static const uint SCALE_TOWN_BIG_PAX    = 3; 
00055 static const uint CARGO_SCALE_IND       = 0; 
00056 static const uint CARGO_SCALE_IND_TOWN  = 1; 
00057 static const uint MIN_WEIGHT_TOWN       = 0; 
00058 static const uint MIN_WEIGHT_TOWN_PAX   = 1; 
00059 static const uint WEIGHT_SCALE_IND_PROD = 0; 
00060 static const uint WEIGHT_SCALE_IND_PILE = 1; 
00061 
00063 bool CargoHasDestinations(CargoID cid)
00064 {
00065   const CargoSpec *spec = CargoSpec::Get(cid);
00066   switch (spec->town_effect) {
00067     case TE_PASSENGERS:
00068     case TE_MAIL:
00069       return _settings_game.economy.cargodest.mode_pax_mail != CRM_OFF;
00070 
00071     case TE_GOODS:
00072     case TE_WATER:
00073     case TE_FOOD:
00074       return _settings_game.economy.cargodest.mode_town_cargo != CRM_OFF;
00075 
00076     default:
00077       return _settings_game.economy.cargodest.mode_others != CRM_OFF;
00078   }
00079 }
00080 
00082 bool CargoDestinationsDisabled()
00083 {
00084   return _settings_game.economy.cargodest.mode_pax_mail == CRM_OFF && _settings_game.economy.cargodest.mode_town_cargo == CRM_OFF && _settings_game.economy.cargodest.mode_others == CRM_OFF;
00085 }
00086 
00088 static bool IsTownCargo(CargoID cid)
00089 {
00090   const CargoSpec *spec = CargoSpec::Get(cid);
00091   return spec->town_effect != TE_NONE;
00092 }
00093 
00095 static bool IsSymmetricCargo(CargoID cid)
00096 {
00097   const CargoSpec *spec = CargoSpec::Get(cid);
00098   return spec->town_effect == TE_PASSENGERS;
00099 }
00100 
00102 static bool IsPassengerCargo(CargoID cid)
00103 {
00104   const CargoSpec *spec = CargoSpec::Get(cid);
00105   return spec->town_effect == TE_PASSENGERS;
00106 }
00107 
00108 
00110 struct EnumRandomData {
00111   CargoSourceSink *source;
00112   TileIndex       source_xy;
00113   CargoID         cid;
00114   bool            limit_links;
00115 };
00116 
00124 static bool IsNearby(TileIndex t1, TileIndex t2, uint32 dist_square)
00125 {
00126   /* Scale distance by 1D map size to make sure that there are still
00127    * candidates left on larger maps with few towns, but don't scale
00128    * by 2D map size so the map still feels bigger. */
00129   return DistanceSquare(t1, t2) < ScaleByMapSize1D(dist_square);
00130 }
00131 
00138 static bool IsTownNearby(const Town *t, TileIndex ti)
00139 {
00140   return IsNearby(t->xy, ti, _settings_game.economy.cargodest.town_nearby_dist);
00141 }
00142 
00149 static bool IsIndustryNearby(const Industry *ind, TileIndex ti)
00150 {
00151   return IsNearby(ind->location.tile, ti, _settings_game.economy.cargodest.ind_nearby_dist);
00152 }
00153 
00155 static bool EnumAnyDest(const CargoSourceSink *dest, EnumRandomData *erd)
00156 {
00157   /* Already a destination? */
00158   if (erd->source->HasLinkTo(erd->cid, dest)) return false;
00159 
00160   /* Destination already has too many links? */
00161   if (erd->limit_links && dest->cargo_links[erd->cid].Length() > dest->num_links_expected[erd->cid] + MAX_EXTRA_LINKS) return false;
00162 
00163   return true;
00164 }
00165 
00167 static bool EnumAnyTown(const Town *t, void *data)
00168 {
00169   EnumRandomData *erd = (EnumRandomData *)data;
00170   return EnumAnyDest(t, erd) && t->AcceptsCargo(erd->cid);
00171 }
00172 
00174 static bool EnumCity(const Town *t, void *data)
00175 {
00176   return EnumAnyTown(t, data) && t->larger_town;
00177 }
00178 
00180 static bool EnumBigTown(const Town *t, void *data)
00181 {
00182   EnumRandomData *erd = (EnumRandomData *)data;
00183   return EnumAnyTown(t, erd) && (IsPassengerCargo(erd->cid) ? t->pass.old_max > _settings_game.economy.cargodest.big_town_pop[BIG_TOWN_POP_PAX] : t->mail.old_max > _settings_game.economy.cargodest.big_town_pop[BIG_TOWN_POP_MAIL]);
00184 }
00185 
00187 static bool EnumNearbyTown(const Town *t, void *data)
00188 {
00189   EnumRandomData *erd = (EnumRandomData *)data;
00190   return EnumAnyTown(t, data) && IsTownNearby(t, erd->source_xy);
00191 }
00192 
00194 static bool EnumAnyIndustry(const Industry *ind, void *data)
00195 {
00196   EnumRandomData *erd = (EnumRandomData *)data;
00197   return EnumAnyDest(ind, erd) && ind->AcceptsCargo(erd->cid);
00198 }
00199 
00201 static bool EnumNearbyIndustry(const Industry *ind, void *data)
00202 {
00203   EnumRandomData *erd = (EnumRandomData *)data;
00204   return EnumAnyIndustry(ind, data) && IsIndustryNearby(ind, erd->source_xy);
00205 }
00206 
00208 static bool EnumProducingIndustry(const Industry *ind, void *data)
00209 {
00210   return EnumAnyIndustry(ind, data) && (ind->produced_cargo[0] != CT_INVALID || ind->produced_cargo[1] != CT_INVALID);
00211 }
00212 
00214 template <typename T>
00215 static bool EnumAnySupplier(const T *css, void *data)
00216 {
00217   return css->SuppliesCargo(((EnumRandomData *)data)->cid);
00218 }
00219 
00221 static bool EnumNearbySupplier(const Industry *ind, void *data)
00222 {
00223   EnumRandomData *erd = (EnumRandomData *)data;
00224   return EnumAnySupplier(ind, data) && IsIndustryNearby(ind, erd->source_xy);
00225 }
00226 
00228 static bool EnumNearbySupplier(const Town *t, void *data)
00229 {
00230   EnumRandomData *erd = (EnumRandomData *)data;
00231   return EnumAnySupplier(t, data) && IsTownNearby(t, erd->source_xy);
00232 }
00233 
00234 
00236 static CargoSourceSink *FindTownDestination(byte &weight_mod, CargoSourceSink *source, TileIndex source_xy, CargoID cid, const uint8 destclass_chance[4], TownID skip = INVALID_TOWN)
00237 {
00238   /* Enum functions for: nearby town, city, big town, and any town. */
00239   static const Town::EnumTownProc destclass_enum[] = {
00240     &EnumNearbyTown, &EnumCity, &EnumBigTown, &EnumAnyTown
00241   };
00242   static const byte weight_mods[] = {LWM_TONY_NEARBY, LWM_TONY_CITY, LWM_TONY_BIG, LWM_TONY_ANY};
00243   assert_compile(lengthof(destclass_enum) == lengthof(weight_mods));
00244 
00245   EnumRandomData erd = {source, source_xy, cid, IsSymmetricCargo(cid)};
00246 
00247   /* Determine destination class. If no town is found in this class,
00248    * the search falls through to the following classes. */
00249   byte destclass = RandomRange(destclass_chance[3]);
00250 
00251   weight_mod = LWM_ANYWHERE;
00252   Town *dest = NULL;
00253   for (uint i = 0; i < lengthof(destclass_enum) && dest == NULL; i++) {
00254     /* Skip if destination class not reached. */
00255     if (destclass > destclass_chance[i]) continue;
00256 
00257     dest = Town::GetRandom(destclass_enum[i], skip, &erd);
00258     weight_mod = weight_mods[i];
00259   }
00260 
00261   return dest;
00262 }
00263 
00265 static CargoSourceSink *FindIndustryDestination(byte &weight_mod, CargoSourceSink *source, TileIndex source_xy, CargoID cid, IndustryID skip = INVALID_INDUSTRY)
00266 {
00267   /* Enum functions for: nearby industry, producing industry, and any industry. */
00268   static const Industry::EnumIndustryProc destclass_enum[] = {
00269     &EnumNearbyIndustry, &EnumProducingIndustry, &EnumAnyIndustry
00270   };
00271   static const byte weight_mods[] = {LWM_IND_NEARBY, LWM_IND_PRODUCING, LWM_IND_ANY};
00272   assert_compile(lengthof(destclass_enum) == lengthof(_settings_game.economy.cargodest.ind_chances));
00273 
00274   EnumRandomData erd = {source, source_xy, cid, IsSymmetricCargo(cid)};
00275 
00276   /* Determine destination class. If no industry is found in this class,
00277    * the search falls through to the following classes. */
00278   byte destclass = RandomRange(*lastof(_settings_game.economy.cargodest.ind_chances));
00279 
00280   weight_mod = LWM_ANYWHERE;
00281   Industry *dest = NULL;
00282   for (uint i = 0; i < lengthof(destclass_enum) && dest == NULL; i++) {
00283     /* Skip if destination class not reached. */
00284     if (destclass > _settings_game.economy.cargodest.ind_chances[i]) continue;
00285 
00286     dest = Industry::GetRandom(destclass_enum[i], skip, &erd);
00287     weight_mod = weight_mods[i];
00288   }
00289 
00290   return dest;
00291 }
00292 
00294 static CargoSourceSink *FindSupplySource(Industry *dest, CargoID cid)
00295 {
00296   EnumRandomData erd = {dest, dest->location.tile, cid, false};
00297 
00298   CargoSourceSink *source = NULL;
00299 
00300   /* Even chance for industry source first, town second and vice versa.
00301    * Try a nearby supplier first, then check all suppliers. */
00302   if (Chance16(1, 2)) {
00303     source = Industry::GetRandom(&EnumNearbySupplier, dest->index, &erd);
00304     if (source == NULL) source = Town::GetRandom(&EnumNearbySupplier, INVALID_TOWN, &erd);
00305     if (source == NULL) source = Industry::GetRandom(&EnumAnySupplier, dest->index, &erd);
00306     if (source == NULL) source = Town::GetRandom(&EnumAnySupplier, INVALID_TOWN, &erd);
00307   } else {
00308     source = Town::GetRandom(&EnumNearbySupplier, INVALID_TOWN, &erd);
00309     if (source == NULL) source = Industry::GetRandom(&EnumNearbySupplier, dest->index, &erd);
00310     if (source == NULL) source = Town::GetRandom(&EnumAnySupplier, INVALID_TOWN, &erd);
00311     if (source == NULL) source = Industry::GetRandom(&EnumAnySupplier, dest->index, &erd);
00312   }
00313 
00314   return source;
00315 }
00316 
00317 /* virtual */ void CargoSourceSink::CreateSpecialLinks(CargoID cid)
00318 {
00319   /* First link is for undetermined destinations. */
00320   if (this->cargo_links[cid].Length() == 0) {
00321     *this->cargo_links[cid].Append() = CargoLink(NULL, LWM_ANYWHERE);
00322   }
00323   if (this->cargo_links[cid].Get(0)->dest != NULL) {
00324     /* Insert link at first place. */
00325     *this->cargo_links[cid].Append() = *this->cargo_links[cid].Get(0);
00326     *this->cargo_links[cid].Get(0) = CargoLink(NULL, LWM_ANYWHERE);
00327   }
00328 }
00329 
00330 /* virtual */ void Town::CreateSpecialLinks(CargoID cid)
00331 {
00332   CargoSourceSink::CreateSpecialLinks(cid);
00333 
00334   if (this->AcceptsCargo(cid)) {
00335     /* Add special link for town-local demand if not already present. */
00336     if (this->cargo_links[cid].Length() < 2) *this->cargo_links[cid].Append() = CargoLink(this, LWM_INTOWN);
00337     if (this->cargo_links[cid].Get(1)->dest != this) {
00338       /* Insert link at second place. */
00339       *this->cargo_links[cid].Append() = *this->cargo_links[cid].Get(1);
00340       *this->cargo_links[cid].Get(1) = CargoLink(this, LWM_INTOWN);
00341     }
00342   } else {
00343     /* Remove link for town-local demand if present. */
00344     if (this->cargo_links[cid].Length() > 1 && this->cargo_links[cid].Get(1)->dest == this) {
00345       this->cargo_links[cid].Erase(this->cargo_links[cid].Get(1));
00346     }
00347   }
00348 }
00349 
00356 static void RemoveLowestLink(CargoSourceSink *source, CargoID cid)
00357 {
00358   uint lowest_weight = UINT_MAX;
00359   CargoLink *lowest_link = NULL;
00360 
00361   for (CargoLink *l = source->cargo_links[cid].Begin(); l != source->cargo_links[cid].End(); l++) {
00362     /* Don't remove special links. */
00363     if (l->dest == NULL || l->dest == source) continue;
00364 
00365     if (l->weight < lowest_weight) {
00366       lowest_weight = l->weight;
00367       lowest_link = l;
00368     }
00369   }
00370 
00371   if (lowest_link != NULL) {
00372     /* If this is a symmetric cargo, also remove the reverse link. */
00373     if (IsSymmetricCargo(cid) && lowest_link->dest->HasLinkTo(cid, source)) {
00374       source->num_incoming_links[cid]--;
00375       lowest_link->dest->cargo_links[cid].Erase(lowest_link->dest->cargo_links[cid].Find(CargoLink(source, LWM_ANYWHERE)));
00376     }
00377     lowest_link->dest->num_incoming_links[cid]--;
00378     source->cargo_links[cid].Erase(lowest_link);
00379   }
00380 }
00381 
00383 static void CreateNewLinks(CargoSourceSink *source, TileIndex source_xy, CargoID cid, uint chance_a, uint chance_b, const uint8 town_chance[], TownID skip_town, IndustryID skip_ind)
00384 {
00385   uint num_links = source->num_links_expected[cid];
00386 
00387   /* Remove the link with the lowest weight if the
00388    * town has more than links more than expected. */
00389   if (source->cargo_links[cid].Length() > num_links + MAX_EXTRA_LINKS) {
00390     RemoveLowestLink(source, cid);
00391   }
00392 
00393   /* Add new links until the expected link count is reached. */
00394   while (source->cargo_links[cid].Length() < num_links) {
00395     CargoSourceSink *dest = NULL;
00396     byte weight_mod = LWM_ANYWHERE;
00397 
00398     /* Chance for town/industry is chance_a/chance_b, otherwise try industry/town. */
00399     if (Chance16(chance_a, chance_b)) {
00400       dest = FindTownDestination(weight_mod, source, source_xy, cid, town_chance, skip_town);
00401       /* No town found? Try an industry. */
00402       if (dest == NULL) dest = FindIndustryDestination(weight_mod, source, source_xy, cid, skip_ind);
00403     } else {
00404       dest = FindIndustryDestination(weight_mod, source, source_xy, cid, skip_ind);
00405       /* No industry found? Try a town. */
00406       if (dest == NULL) dest = FindTownDestination(weight_mod, source, source_xy, cid, town_chance, skip_town);
00407     }
00408 
00409     /* If we didn't find a destination, break out of the loop because no
00410      * more destinations are left on the map. */
00411     if (dest == NULL) break;
00412 
00413     /* If this is a symmetric cargo and we accept it as well, create a back link. */
00414     if (IsSymmetricCargo(cid) && dest->SuppliesCargo(cid) && source->AcceptsCargo(cid)) {
00415       *dest->cargo_links[cid].Append() = CargoLink(source, weight_mod);
00416       source->num_incoming_links[cid]++;
00417     }
00418 
00419     *source->cargo_links[cid].Append() = CargoLink(dest, weight_mod);
00420     dest->num_incoming_links[cid]++;
00421   }
00422 }
00423 
00425 static void RemoveInvalidLinks(CargoSourceSink *css)
00426 {
00427   for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
00428     /* Remove outgoing links if cargo isn't supplied anymore. */
00429     if (!css->SuppliesCargo(cid)) {
00430       for (CargoLink *l = css->cargo_links[cid].Begin(); l != css->cargo_links[cid].End(); l++) {
00431         if (l->dest != NULL && l->dest != css) l->dest->num_incoming_links[cid]--;
00432       }
00433       css->cargo_links[cid].Clear();
00434       css->cargo_links_weight[cid] = 0;
00435     }
00436 
00437     /* Remove outgoing links if the dest doesn't accept the cargo anymore. */
00438     for (CargoLink *l = css->cargo_links[cid].Begin(); l != css->cargo_links[cid].End(); ) {
00439       if (l->dest != NULL && !l->dest->AcceptsCargo(cid)) {
00440         if (l->dest != css) l->dest->num_incoming_links[cid]--;
00441         css->cargo_links[cid].Erase(l);
00442       } else {
00443         l++;
00444       }
00445     }
00446   }
00447 }
00448 
00450 void UpdateExpectedLinks(Town *t)
00451 {
00452   CargoID cid;
00453 
00454   FOR_EACH_SET_CARGO_ID(cid, t->cargo_produced) {
00455     if (CargoHasDestinations(cid)) {
00456       t->CreateSpecialLinks(cid);
00457 
00458       uint max_amt = IsPassengerCargo(cid) ? t->pass.old_max : t->mail.old_max;
00459       uint big_amt = _settings_game.economy.cargodest.big_town_pop[IsPassengerCargo(cid) ? BIG_TOWN_POP_PAX : BIG_TOWN_POP_MAIL];
00460 
00461       uint num_links = _settings_game.economy.cargodest.base_town_links[IsSymmetricCargo(cid) ? BASE_TOWN_LINKS_SYMM : BASE_TOWN_LINKS];
00462       /* Add links based on the available cargo amount. */
00463       num_links += min(max_amt, big_amt) / _settings_game.economy.cargodest.pop_scale_town[IsPassengerCargo(cid) ? SCALE_TOWN_PAX : SCALE_TOWN];
00464       if (max_amt > big_amt) num_links += (max_amt - big_amt) / _settings_game.economy.cargodest.pop_scale_town[IsPassengerCargo(cid) ? SCALE_TOWN_BIG_PAX : SCALE_TOWN_BIG];
00465       /* Ensure a city has at least city_town_links more than the base value.
00466        * This improves the link distribution at the beginning of a game when
00467        * the towns are still small. */
00468       if (t->larger_town) num_links = max<uint>(num_links, _settings_game.economy.cargodest.city_town_links + _settings_game.economy.cargodest.base_town_links[IsSymmetricCargo(cid) ? BASE_TOWN_LINKS_SYMM : BASE_TOWN_LINKS]);
00469 
00470       /* Account for the two special links. */
00471       num_links++;
00472       if (t->cargo_links[cid].Length() > 1 && t->cargo_links[cid].Get(1)->dest == t) num_links++;
00473 
00474       t->num_links_expected[cid] = ClampToU16(num_links);
00475     }
00476   }
00477 }
00478 
00480 void UpdateExpectedLinks(Industry *ind)
00481 {
00482   for (uint i = 0; i < lengthof(ind->produced_cargo); i++) {
00483     CargoID cid = ind->produced_cargo[i];
00484     if (cid == INVALID_CARGO) continue;
00485 
00486     if (CargoHasDestinations(cid)) {
00487       ind->CreateSpecialLinks(cid);
00488 
00489       uint num_links;
00490       /* Use different base values for symmetric cargos, cargos
00491        * with a town effect and all other cargos. */
00492       num_links = _settings_game.economy.cargodest.base_ind_links[IsSymmetricCargo(cid) ? BASE_IND_LINKS_SYMM : (IsTownCargo(cid) ? BASE_IND_LINKS_TOWN : BASE_IND_LINKS)];
00493       /* Add links based on the average industry production. */
00494       num_links += ind->average_production[i] / _settings_game.economy.cargodest.cargo_scale_ind[IsTownCargo(cid) ? CARGO_SCALE_IND_TOWN : CARGO_SCALE_IND];
00495 
00496       /* Account for the one special link. */
00497       num_links++;
00498 
00499       ind->num_links_expected[cid] = ClampToU16(num_links);
00500     }
00501   }
00502 }
00503 
00505 void AddMissingIndustryLinks(Industry *ind)
00506 {
00507   for (uint i = 0; i < lengthof(ind->accepts_cargo); i++) {
00508     CargoID cid = ind->accepts_cargo[i];
00509     if (cid == INVALID_CARGO) continue;
00510 
00511     /* Do we already have at least one cargo source? */
00512     if (ind->num_incoming_links[cid] > 0) continue;
00513 
00514     CargoSourceSink *source = FindSupplySource(ind, cid);
00515     if (source == NULL) continue; // Too bad...
00516 
00517     if (source->cargo_links[cid].Length() >= source->num_links_expected[cid] + MAX_EXTRA_LINKS) {
00518       /* Increase the expected link count if adding another link would
00519        * exceed the count, as otherwise this (or another) link would
00520        * get removed right again. */
00521       source->num_links_expected[cid]++;
00522     }
00523 
00524     *source->cargo_links[cid].Append() = CargoLink(ind, LWM_IND_ANY);
00525     ind->num_incoming_links[cid]++;
00526 
00527     /* If this is a symmetric cargo and we produce it as well, create a back link. */
00528     if (IsSymmetricCargo(cid) && ind->SuppliesCargo(cid) && source->AcceptsCargo(cid)) {
00529       *ind->cargo_links[cid].Append() = CargoLink(source, LWM_IND_ANY);
00530       source->num_incoming_links[cid]++;
00531     }
00532   }
00533 }
00534 
00536 void UpdateCargoLinks(Town *t)
00537 {
00538   CargoID cid;
00539 
00540   FOR_EACH_SET_CARGO_ID(cid, t->cargo_produced) {
00541     if (CargoHasDestinations(cid)) {
00542       /* If this is a town cargo, 95% chance for town/industry destination and
00543        * 5% for industry/town. The reverse chance otherwise. */
00544       CreateNewLinks(t, t->xy, cid, IsTownCargo(cid) ? 19 : 1, 20, t->larger_town ? _settings_game.economy.cargodest.town_chances_city : _settings_game.economy.cargodest.town_chances_town, t->index, INVALID_INDUSTRY);
00545     }
00546   }
00547 }
00548 
00550 void UpdateCargoLinks(Industry *ind)
00551 {
00552   for (uint i = 0; i < lengthof(ind->produced_cargo); i++) {
00553     CargoID cid = ind->produced_cargo[i];
00554     if (cid == INVALID_CARGO) continue;
00555 
00556     if (CargoHasDestinations(cid)) {
00557       /* If this is a town cargo, 75% chance for town/industry destination and
00558        * 25% for industry/town. The reverse chance otherwise. */
00559       CreateNewLinks(ind, ind->location.tile, cid, IsTownCargo(cid) ? 3 : 1, 4, _settings_game.economy.cargodest.town_chances_town, INVALID_TOWN, ind->index);
00560     }
00561   }
00562 }
00563 
00564 /* virtual */ uint Town::GetDestinationWeight(CargoID cid, byte weight_mod) const
00565 {
00566   uint max_amt = IsPassengerCargo(cid) ? this->pass.old_max : this->mail.old_max;
00567   uint big_amt = _settings_game.economy.cargodest.big_town_pop[IsPassengerCargo(cid) ? BIG_TOWN_POP_PAX : BIG_TOWN_POP_MAIL];
00568 
00569   /* The weight is calculated by a piecewise function. We start with a predefined
00570    * minimum weight and then add the weight for the cargo amount up to the big
00571    * town amount. If the amount is more than the big town amount, this is also
00572    * added to the weight with a different scale factor to make sure that big towns
00573    * don't siphon the cargo away too much from the smaller destinations. */
00574   uint weight = _settings_game.economy.cargodest.min_weight_town[IsPassengerCargo(cid) ? MIN_WEIGHT_TOWN_PAX : MIN_WEIGHT_TOWN];
00575   weight += min(max_amt, big_amt) * weight_mod / _settings_game.economy.cargodest.weight_scale_town[IsPassengerCargo(cid) ? SCALE_TOWN_PAX : SCALE_TOWN];
00576   if (max_amt > big_amt) weight += (max_amt - big_amt) * weight_mod / _settings_game.economy.cargodest.weight_scale_town[IsPassengerCargo(cid) ? SCALE_TOWN_BIG_PAX : SCALE_TOWN_BIG];
00577 
00578   return weight;
00579 }
00580 
00581 /* virtual */ uint Industry::GetDestinationWeight(CargoID cid, byte weight_mod) const
00582 {
00583   uint weight = _settings_game.economy.cargodest.min_weight_ind;
00584 
00585   for (uint i = 0; i < lengthof(this->accepts_cargo); i++) {
00586     if (this->accepts_cargo[i] != cid) continue;
00587     /* Empty stockpile means more weight for the link. Stockpiles
00588      * above a fixed maximum have no further effect. */
00589     uint stockpile = ClampU(this->incoming_cargo_waiting[i], 0, MAX_IND_STOCKPILE);
00590     weight += (MAX_IND_STOCKPILE - stockpile) * weight_mod / _settings_game.economy.cargodest.weight_scale_ind[WEIGHT_SCALE_IND_PILE];
00591   }
00592 
00593   /* Add a weight for the produced cargo. Use the average production
00594    * here so the weight isn't fluctuating that much when the input
00595    * cargo isn't delivered regularly. */
00596   weight += (this->average_production[0] + this->average_production[1]) * weight_mod / _settings_game.economy.cargodest.weight_scale_ind[WEIGHT_SCALE_IND_PROD];
00597 
00598   return weight;
00599 }
00600 
00602 void UpdateLinkWeights(Town *t)
00603 {
00604   for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
00605     uint weight_sum = 0;
00606 
00607     if (t->cargo_links[cid].Length() == 0) continue;
00608 
00609     t->cargo_links[cid].Begin()->amount.NewMonth();
00610 
00611     /* Skip the special link for undetermined destinations. */
00612     for (CargoLink *l = t->cargo_links[cid].Begin() + 1; l != t->cargo_links[cid].End(); l++) {
00613       l->weight = l->dest->GetDestinationWeight(cid, l->weight_mod);
00614       weight_sum += l->weight;
00615 
00616       l->amount.NewMonth();
00617     }
00618 
00619     /* Limit the weight of the in-town link to at most 1/3 of the total weight. */
00620     if (t->cargo_links[cid].Length() > 1 && t->cargo_links[cid].Get(1)->dest == t) {
00621       uint new_weight = min(t->cargo_links[cid].Get(1)->weight, weight_sum / 3);
00622       weight_sum -= t->cargo_links[cid].Get(1)->weight - new_weight;
00623       t->cargo_links[cid].Get(1)->weight = new_weight;
00624     }
00625 
00626     /* Set weight for the undetermined destination link to random_dest_chance%. */
00627     t->cargo_links[cid].Begin()->weight = weight_sum == 0 ? 1 : (weight_sum * _settings_game.economy.cargodest.random_dest_chance) / (100 - _settings_game.economy.cargodest.random_dest_chance);
00628 
00629     t->cargo_links_weight[cid] = weight_sum + t->cargo_links[cid].Begin()->weight;
00630   }
00631 }
00632 
00634 void UpdateLinkWeights(CargoSourceSink *css)
00635 {
00636   for (uint cid = 0; cid < NUM_CARGO; cid++) {
00637     uint weight_sum = 0;
00638 
00639     if (css->cargo_links[cid].Length() == 0) continue;
00640 
00641     css->cargo_links[cid].Begin()->amount.NewMonth();
00642 
00643     for (CargoLink *l = css->cargo_links[cid].Begin() + 1; l != css->cargo_links[cid].End(); l++) {
00644       l->weight = l->dest->GetDestinationWeight(cid, l->weight_mod);
00645       weight_sum += l->weight;
00646 
00647       l->amount.NewMonth();
00648     }
00649 
00650     /* Set weight for the undetermined destination link to random_dest_chance%. */
00651     css->cargo_links[cid].Begin()->weight = weight_sum == 0 ? 1 : (weight_sum * _settings_game.economy.cargodest.random_dest_chance) / (100 - _settings_game.economy.cargodest.random_dest_chance);
00652 
00653     css->cargo_links_weight[cid] = weight_sum + css->cargo_links[cid].Begin()->weight;
00654   }
00655 }
00656 
00657 /* virtual */ CargoSourceSink::~CargoSourceSink()
00658 {
00659   if (Town::CleaningPool() || Industry::CleaningPool()) return;
00660 
00661   /* Remove all demand links having us as a destination. */
00662   Town *t;
00663   FOR_ALL_TOWNS(t) {
00664     for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
00665       if (t->HasLinkTo(cid, this)) {
00666         t->cargo_links[cid].Erase(t->cargo_links[cid].Find(CargoLink(this, LWM_ANYWHERE)));
00667         InvalidateWindowData(WC_TOWN_VIEW, t->index, 1);
00668       }
00669     }
00670   }
00671 
00672   Industry *ind;
00673   FOR_ALL_INDUSTRIES(ind) {
00674     for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
00675       if (ind->HasLinkTo(cid, this)) {
00676         ind->cargo_links[cid].Erase(ind->cargo_links[cid].Find(CargoLink(this, LWM_ANYWHERE)));
00677         InvalidateWindowData(WC_INDUSTRY_VIEW, ind->index, 1);
00678       }
00679     }
00680   }
00681 
00682   /* Decrement incoming link count for all link destinations. */
00683   for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
00684     for (CargoLink *l = this->cargo_links[cid].Begin(); l != this->cargo_links[cid].End(); l++) {
00685       if (l->dest != NULL) l->dest->num_incoming_links[cid]--;
00686     }
00687   }
00688 }
00689 
00691 void RebuildCargoLinkCounts()
00692 {
00693   /* Clear incoming link count of all towns and industries. */
00694   CargoSourceSink *source;
00695   FOR_ALL_TOWNS(source) MemSetT(source->num_incoming_links, 0, lengthof(source->num_incoming_links));
00696   FOR_ALL_INDUSTRIES(source) MemSetT(source->num_incoming_links, 0, lengthof(source->num_incoming_links));
00697 
00698   /* Count all incoming links. */
00699   FOR_ALL_TOWNS(source) {
00700     for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
00701       for (CargoLink *l = source->cargo_links[cid].Begin(); l != source->cargo_links[cid].End(); l++) {
00702         if (l->dest != NULL && l->dest != source) l->dest->num_incoming_links[cid]++;
00703       }
00704     }
00705   }
00706   FOR_ALL_INDUSTRIES(source) {
00707     for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
00708       for (CargoLink *l = source->cargo_links[cid].Begin(); l != source->cargo_links[cid].End(); l++) {
00709         if (l->dest != NULL && l->dest != source) l->dest->num_incoming_links[cid]++;
00710       }
00711     }
00712   }
00713 }
00714 
00716 void UpdateCargoLinks()
00717 {
00718   if (CargoDestinationsDisabled()) return;
00719 
00720   Town *t;
00721   Industry *ind;
00722 
00723   /* Remove links that have become invalid. */
00724   FOR_ALL_TOWNS(t) RemoveInvalidLinks(t);
00725   FOR_ALL_INDUSTRIES(ind) RemoveInvalidLinks(ind);
00726 
00727   /* Recalculate the number of expected links. */
00728   FOR_ALL_TOWNS(t) UpdateExpectedLinks(t);
00729   FOR_ALL_INDUSTRIES(ind) UpdateExpectedLinks(ind);
00730 
00731   /* Make sure each industry gets at at least some input cargo. */
00732   FOR_ALL_INDUSTRIES(ind) AddMissingIndustryLinks(ind);
00733 
00734   /* Update the demand link list. */
00735   FOR_ALL_TOWNS(t) UpdateCargoLinks(t);
00736   FOR_ALL_INDUSTRIES(ind) UpdateCargoLinks(ind);
00737 
00738   /* Recalculate links weights. */
00739   FOR_ALL_TOWNS(t) UpdateLinkWeights(t);
00740   FOR_ALL_INDUSTRIES(ind) UpdateLinkWeights(ind);
00741 
00742   InvalidateWindowClassesData(WC_TOWN_VIEW, 1);
00743   InvalidateWindowClassesData(WC_INDUSTRY_VIEW, 1);
00744 }
00745 
00752 CargoLink *CargoSourceSink::GetRandomLink(CargoID cid, bool allow_self)
00753 {
00754   /* Randomly choose a cargo link. */
00755   uint weight = RandomRange(this->cargo_links_weight[cid] - 1);
00756   uint cur_sum = 0;
00757 
00758   CargoLink *l;
00759   for (l = this->cargo_links[cid].Begin(); l != this->cargo_links[cid].End(); ++l) {
00760     cur_sum += l->weight;
00761     if (weight < cur_sum) {
00762       /* Link is valid if it is random destination or only the
00763        * local link if allowed and accepts the cargo. */
00764       if (l->dest == NULL || ((allow_self || l->dest != this) && l->dest->AcceptsCargo(cid))) break;
00765     }
00766   }
00767 
00768   return l;
00769 }
00770 
00771 
00773 /* virtual */ TileArea Town::GetTileForDestination(CargoID cid)
00774 {
00775   assert(this->cargo_accepted_weights[cid] != 0);
00776 
00777   /* Randomly choose a target square. */
00778   uint32 weight = RandomRange(this->cargo_accepted_weights[cid] - 1);
00779 
00780   /* Iterate over all grid squares till the chosen square is found. */
00781   uint32 weight_sum = 0;
00782   const TileArea &area = this->cargo_accepted.GetArea();
00783   TILE_AREA_LOOP(tile, area) {
00784     if (TileX(tile) % AcceptanceMatrix::GRID == 0 && TileY(tile) % AcceptanceMatrix::GRID == 0) {
00785       weight_sum += this->cargo_accepted_max_weight - (DistanceMax(this->xy_aligned, tile) / AcceptanceMatrix::GRID) * 2;
00786       /* Return tile area inside the grid square if this is the chosen square. */
00787       if (weight < weight_sum) return TileArea(tile + TileDiffXY(1, 1), 2, 2);
00788     }
00789   }
00790 
00791   /* Something went wrong here... */
00792   NOT_REACHED();
00793 }
00794 
00796 static bool EnumAcceptingTown(const Town *t, void *data)
00797 {
00798   return t->AcceptsCargo((CargoID)(size_t)data);
00799 }
00800 
00802 static bool EnumAcceptingIndustry(const Industry *ind, void *data)
00803 {
00804   return ind->AcceptsCargo((CargoID)(size_t)data);
00805 }
00806 
00817 bool MoveCargoWithDestinationToStationWorker(CargoID cid, uint *amount, SourceType source_type, SourceID source_id, const StationList *all_stations, TileIndex src_tile)
00818 {
00819   CargoSourceSink *source = NULL;
00820   CargoSourceSink *dest = NULL;
00821   CargoLink *l = NULL;
00822 
00823   /* Company HQ doesn't have cargo links. */
00824   if (source_type != ST_HEADQUARTERS) {
00825     source = (source_type == ST_TOWN) ? static_cast<CargoSourceSink *>(Town::Get(source_id)) : static_cast<CargoSourceSink *>(Industry::Get(source_id));
00826     /* No links yet? Create cargo without destination. */
00827     if (source->cargo_links[cid].Length() == 0) return false;
00828 
00829     /* Randomly choose a cargo link. */
00830     l = source->GetRandomLink(cid, true);
00831 
00832     if (l != source->cargo_links[cid].End()) {
00833       l->amount.new_max += *amount;
00834       dest = l->dest;
00835     }
00836   }
00837 
00838   /* No destination or random destination? Try a random town. */
00839   if (dest == NULL) dest = Town::GetRandom(&EnumAcceptingTown, INVALID_TOWN, (void *)(size_t)cid);
00840   /* No luck? Try a random industry. */
00841   if (dest == NULL) dest = Industry::GetRandom(&EnumAcceptingIndustry, INVALID_INDUSTRY, (void *)(size_t)cid);
00842   /* Still no luck, nothing left to try. */
00843   if (dest == NULL) return false;
00844 
00845   /* Pick a tile that belongs to the destination. */
00846   TileArea dest_area = dest->GetTileForDestination(cid);
00847 
00848   /* Maximum pathfinder penalty based on distance. */
00849   uint r = RandomRange(_settings_game.economy.cargodest.max_route_penalty[1]);
00850   uint max_cost = _settings_game.economy.cargodest.max_route_penalty[0] + r;
00851   max_cost *= DistanceSquare(src_tile, dest_area.tile);
00852 
00853   /* Randomly determine the routing flags for the packet.
00854    * Right now only the two lowest bits are defined. */
00855   byte flags = r & 0x3;
00856 
00857   /* Find a route to the destination. */
00858   StationID st, st_unload;
00859   bool found = false;
00860   RouteLink *route_link = YapfChooseRouteLink(cid, all_stations, src_tile, dest_area, &st, &st_unload, flags, &found, INVALID_ORDER, max_cost);
00861 
00862   if (route_link == NULL) {
00863     /* No suitable link found (or direct delivery), nothing is
00864      * moved to the station, but count it as transported anyway. */
00865     if (found && l != NULL) l->amount.new_act += *amount;
00866     *amount = 0;
00867     return true;
00868   }
00869 
00870   /* Move cargo to the station. */
00871   Station *from = Station::Get(st);
00872   *amount = UpdateStationWaiting(from, cid, *amount * from->goods[cid].rating, source_type, source_id, dest_area.tile, dest->GetType(), dest->GetID(), route_link->GetOriginOrderId(), st_unload, flags);
00873   if (found && l != NULL) l->amount.new_act += *amount;
00874 
00875   /* If this is a symmetric cargo type, try to generate some cargo going from
00876    * destination to source as well. It's no error if that is not possible. */
00877   if (IsSymmetricCargo(cid)) {
00878     /* Try to find the matching cargo link back to the source. If no
00879      * link is found, don't generate return traffic. */
00880     CargoLink *back_link = dest->cargo_links[cid].Find(CargoLink(source, LWM_ANYWHERE));
00881     if (back_link == dest->cargo_links[cid].End()) return true;
00882 
00883     back_link->amount.new_max += *amount;
00884 
00885     /* Find stations around the new source area. */
00886     StationFinder stf(dest_area);
00887     TileIndex tile = dest_area.tile;
00888 
00889     /* The the new destination area. */
00890     switch (source_type) {
00891       case ST_INDUSTRY:
00892         dest_area = static_cast<Industry *>(source)->location;
00893         break;
00894       case ST_TOWN:
00895         dest_area = TileArea(src_tile, 2, 2);
00896         break;
00897       case ST_HEADQUARTERS:
00898         dest_area = TileArea(Company::Get(source_id)->location_of_HQ, 2, 2);
00899         break;
00900     }
00901 
00902     /* Find a route and update transported amount if found. */
00903     route_link = YapfChooseRouteLink(cid, stf.GetStations(), tile, dest_area, &st, &st_unload, flags, &found, INVALID_ORDER, max_cost);
00904     if (found) back_link->amount.new_act += *amount;
00905 
00906     if (route_link != NULL) {
00907       /* Found a back link, move to station. */
00908       UpdateStationWaiting(Station::Get(st), cid, *amount * 256, dest->GetType(), dest->GetID(), dest_area.tile, source_type, source_id, route_link->GetOriginOrderId(), st_unload, flags);
00909     }
00910   }
00911 
00912   return true;
00913 }
00914 
00925 bool MoveCargoWithDestinationToStation(CargoID cid, uint *amount, SourceType source_type, SourceID source_id, const StationList *all_stations, TileIndex src_tile)
00926 {
00927   if (!CargoHasDestinations(cid)) return false;
00928 
00929   /* Split the cargo into multiple destinations for industries. */
00930   int num_packets = 1;
00931   if (source_type == ST_INDUSTRY) {
00932     if (*amount > 5) num_packets++;
00933     if (*amount > 50) num_packets += 2;
00934   }
00935 
00936   /* Generate num_packets different destinations while making
00937    * sure no cargo is lost due to rounding. */
00938   uint amount_packet = *amount / num_packets;
00939   uint last_packet = *amount - (num_packets - 1) * amount_packet;
00940   uint moved = 0;
00941   for (; num_packets > 0; num_packets--) {
00942     uint cur_amount = (num_packets == 1) ? last_packet : amount_packet;
00943 
00944     /* If we fail once, we will fail always. */
00945     if (!MoveCargoWithDestinationToStationWorker(cid, &cur_amount, source_type, source_id, all_stations, src_tile)) return false;
00946     moved += cur_amount;
00947   }
00948 
00949   *amount = moved;
00950   return true;
00951 }
00952 
00962 RouteLink *FindRouteLinkForCargo(Station *st, CargoID cid, const CargoPacket *cp, StationID *next_unload, OrderID order, bool *found)
00963 {
00964   if (cp->DestinationID() == INVALID_SOURCE) return NULL;
00965 
00966   StationList sl;
00967   *sl.Append() = st;
00968 
00969   TileArea area = (cp->DestinationType() == ST_INDUSTRY) ? Industry::Get(cp->DestinationID())->location : TileArea(cp->DestinationXY(), 2, 2);
00970   return YapfChooseRouteLink(cid, &sl, st->xy, area, NULL, next_unload, cp->Flags(), found, order);
00971 }
00972 
00973 
00974 /* Initialize the RouteLink-pool */
00975 RouteLinkPool _routelink_pool("RouteLink");
00976 INSTANTIATE_POOL_METHODS(RouteLink)
00977 
00978 
00981 RouteLink::~RouteLink()
00982 {
00983   if (RouteLink::CleaningPool()) return;
00984 
00985   if (this->GetOriginOrderId() != INVALID_ORDER) StationCargoList::InvalidateAllTo(this->GetOriginOrderId(), this->GetDestination());
00986 }
00987 
00999 void UpdateVehicleRouteLinks(const Vehicle *v, uint32 cargos, bool clear_others, Station *from, OrderID from_oid, StationID to_id, OrderID to_oid, uint32 travel_time)
01000 {
01001   for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
01002     bool has_cargo = HasBit(cargos, cid);
01003     /* Skip if cargo not carried and we aren't supposed to clear other links. */
01004     if (!clear_others && !has_cargo) continue;
01005     /* Skip cargo types that don't have destinations enabled. */
01006     if (!CargoHasDestinations(cid)) continue;
01007 
01008     RouteLinkList::iterator link;
01009     for (link = from->goods[cid].routes.begin(); link != from->goods[cid].routes.end(); ++link) {
01010       if ((*link)->GetOriginOrderId() == from_oid) {
01011         if (has_cargo) {
01012           /* Update destination if necessary. */
01013           (*link)->SetDestination(to_id, to_oid);
01014           (*link)->UpdateTravelTime(travel_time);
01015         } else {
01016           /* Remove link. */
01017           delete *link;
01018           from->goods[cid].routes.erase(link);
01019         }
01020         break;
01021       }
01022     }
01023 
01024     /* No link found? Append a new one. */
01025     if (has_cargo && link == from->goods[cid].routes.end() && RouteLink::CanAllocateItem()) {
01026       from->goods[cid].routes.push_back(new RouteLink(to_id, from_oid, to_oid, v->owner, travel_time, v->type));
01027     }
01028   }
01029 }
01030 
01036 void UpdateVehicleRouteLinks(const Vehicle *v, StationID arrived_at)
01037 {
01038   /* Only update links if we have valid previous station and orders. */
01039   if (v->last_station_loaded == INVALID_STATION || v->last_order_id == INVALID_ORDER || v->current_order.index == INVALID_ORDER) return;
01040   /* Loop? Not good. */
01041   if (v->last_station_loaded == arrived_at) return;
01042 
01043   Station *from = Station::Get(v->last_station_loaded);
01044   Station *to = Station::Get(arrived_at);
01045 
01046   /* Update incoming route link. */
01047   UpdateVehicleRouteLinks(v, v->vcache.cached_cargo_mask, false, from, v->last_order_id, arrived_at, v->current_order.index, v->travel_time);
01048 
01049   /* Update outgoing links. */
01050   CargoID cid;
01051   FOR_EACH_SET_CARGO_ID(cid, v->vcache.cached_cargo_mask) {
01052     /* Skip cargo types that don't have destinations enabled. */
01053     if (!CargoHasDestinations(cid)) continue;
01054 
01055     for (RouteLinkList::iterator link = to->goods[cid].routes.begin(); link != to->goods[cid].routes.end(); ++link) {
01056       if ((*link)->GetOriginOrderId() == v->current_order.index) {
01057         (*link)->VehicleArrived();
01058         break;
01059       }
01060     }
01061   }
01062 }
01063 
01068 void PrefillRouteLinks(const Vehicle *v)
01069 {
01070   if (CargoDestinationsDisabled()) return;
01071   if (v->orders.list == NULL || v->orders.list->GetNumOrders() < 2) return;
01072 
01073   /* Can't pre-fill if the vehicle has refit or conditional orders. */
01074   uint count = 0;
01075   Order *order;
01076   FOR_VEHICLE_ORDERS(v, order) {
01077     if (order->IsType(OT_GOTO_DEPOT) && order->IsRefit()) return;
01078     if (order->IsType(OT_CONDITIONAL)) return;
01079     if ((order->IsType(OT_IMPLICIT) || order->IsType(OT_GOTO_STATION)) && (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) == 0) count++;
01080   }
01081 
01082   /* Increment count by one to account for the circular nature of the order list. */
01083   if (count > 0) count++;
01084 
01085   /* Collect cargo types carried by all vehicles in the shared order list. */
01086   uint32 transported_cargos = 0;
01087   for (Vehicle *u = v->FirstShared(); u != NULL; u = u->NextShared()) {
01088     transported_cargos |= u->vcache.cached_cargo_mask;
01089   }
01090 
01091   /* Loop over all orders to update/pre-fill the route links. */
01092   order = v->orders.list->GetFirstOrder();
01093   Order *prev_order = NULL;
01094   do {
01095     /* Goto station or implicit order and not a go via-order, consider as destination. */
01096     if ((order->IsType(OT_IMPLICIT) || order->IsType(OT_GOTO_STATION)) && (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) == 0) {
01097       /* Previous destination is set and the new destination is different, create/update route links. */
01098       if (prev_order != NULL && prev_order != order && prev_order->GetDestination() != order->GetDestination()) {
01099         Station *from = Station::Get(prev_order->GetDestination());
01100         Station *to = Station::Get(order->GetDestination());
01101         /* A vehicle with the speed of 128 km/h-ish would take one tick for each of the
01102          * #TILE_SIZE steps per tile. For aircraft, the time needs to be scaled with the
01103          * plane speed factor. */
01104         uint time = DistanceManhattan(from->xy, to->xy) * TILE_SIZE * 128 / v->GetDisplayMaxSpeed();
01105         if (v->type == VEH_AIRCRAFT) time *= _settings_game.vehicle.plane_speed;
01106         UpdateVehicleRouteLinks(v, transported_cargos, true, from, prev_order->index, order->GetDestination(), order->index, time);
01107       }
01108 
01109       prev_order = order;
01110       count--;
01111     }
01112 
01113     /* Get next order, wrap around if necessary. */
01114     order = order->next;
01115     if (order == NULL) order = v->orders.list->GetFirstOrder();
01116   } while (count > 0);
01117 }
01118 
01123 void InvalidateStationRouteLinks(Station *station)
01124 {
01125   /* Delete all outgoing links. */
01126   for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
01127     for (RouteLinkList::iterator link = station->goods[cid].routes.begin(); link != station->goods[cid].routes.end(); ++link) {
01128       delete *link;
01129     }
01130   }
01131 
01132   /* Delete all incoming link. */
01133   Station *st_from;
01134   FOR_ALL_STATIONS(st_from) {
01135     if (st_from == station) continue;
01136 
01137     for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
01138       /* Don't increment the iterator directly in the for loop as we don't want to increment when deleting a link. */
01139       for (RouteLinkList::iterator link = st_from->goods[cid].routes.begin(); link != st_from->goods[cid].routes.end(); ) {
01140         if ((*link)->GetDestination() == station->index) {
01141           delete *link;
01142           link = st_from->goods[cid].routes.erase(link);
01143         } else {
01144           ++link;
01145         }
01146       }
01147     }
01148   }
01149 }
01150 
01155 void InvalidateOrderRouteLinks(OrderID order)
01156 {
01157   Station *st;
01158   FOR_ALL_STATIONS(st) {
01159     for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
01160       /* Don't increment the iterator directly in the for loop as we don't want to increment when deleting a link. */
01161       for (RouteLinkList::iterator link = st->goods[cid].routes.begin(); link != st->goods[cid].routes.end(); ) {
01162         if ((*link)->GetOriginOrderId() == order || (*link)->GetDestOrderId() == order) {
01163           delete *link;
01164           link = st->goods[cid].routes.erase(link);
01165         } else {
01166           ++link;
01167         }
01168       }
01169     }
01170   }
01171 }
01172 
01174 void AgeRouteLinks(Station *st)
01175 {
01176   /* Reset waiting time for all vehicles currently loading. */
01177   for (std::list<Vehicle *>::const_iterator v_itr = st->loading_vehicles.begin(); v_itr != st->loading_vehicles.end(); ++v_itr) {
01178     CargoID cid;
01179     FOR_EACH_SET_CARGO_ID(cid, (*v_itr)->vcache.cached_cargo_mask) {
01180       for (RouteLinkList::iterator link = st->goods[cid].routes.begin(); link != st->goods[cid].routes.end(); ++link) {
01181         if ((*link)->GetOriginOrderId() == (*v_itr)->last_order_id) (*link)->wait_time = 0;
01182       }
01183     }
01184   }
01185 
01186   for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
01187     /* Don't increment the iterator directly in the for loop as we don't want to increment when deleting a link. */
01188     for (RouteLinkList::iterator link = st->goods[cid].routes.begin(); link != st->goods[cid].routes.end(); ) {
01189       if ((*link)->wait_time++ > _settings_game.economy.cargodest.max_route_age) {
01190         delete *link;
01191         link = st->goods[cid].routes.erase(link);
01192       } else {
01193         ++link;
01194       }
01195     }
01196   }
01197 }

Generated on Fri Jun 3 05:18:49 2011 for OpenTTD by  doxygen 1.6.1