00001
00002
00003
00004
00005
00006
00007
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
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
00127
00128
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
00158 if (erd->source->HasLinkTo(erd->cid, dest)) return false;
00159
00160
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
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
00248
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
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
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
00277
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
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
00301
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 void CargoSourceSink::CreateSpecialLinks(CargoID cid)
00318 {
00319
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
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 void Town::CreateSpecialLinks(CargoID cid)
00331 {
00332 CargoSourceSink::CreateSpecialLinks(cid);
00333
00334 if (this->AcceptsCargo(cid)) {
00335
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
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
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
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
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
00388
00389 if (source->cargo_links[cid].Length() > num_links + MAX_EXTRA_LINKS) {
00390 RemoveLowestLink(source, cid);
00391 }
00392
00393
00394 while (source->cargo_links[cid].Length() < num_links) {
00395 CargoSourceSink *dest = NULL;
00396 byte weight_mod = LWM_ANYWHERE;
00397
00398
00399 if (Chance16(chance_a, chance_b)) {
00400 dest = FindTownDestination(weight_mod, source, source_xy, cid, town_chance, skip_town);
00401
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
00406 if (dest == NULL) dest = FindTownDestination(weight_mod, source, source_xy, cid, town_chance, skip_town);
00407 }
00408
00409
00410
00411 if (dest == NULL) break;
00412
00413
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
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
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
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
00466
00467
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
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
00491
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
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
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
00512 if (ind->num_incoming_links[cid] > 0) continue;
00513
00514 CargoSourceSink *source = FindSupplySource(ind, cid);
00515 if (source == NULL) continue;
00516
00517 if (source->cargo_links[cid].Length() >= source->num_links_expected[cid] + MAX_EXTRA_LINKS) {
00518
00519
00520
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
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
00543
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
00558
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 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
00570
00571
00572
00573
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 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
00588
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
00594
00595
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
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
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
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
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 CargoSourceSink::~CargoSourceSink()
00658 {
00659 if (Town::CleaningPool() || Industry::CleaningPool()) return;
00660
00661
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
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
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
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
00724 FOR_ALL_TOWNS(t) RemoveInvalidLinks(t);
00725 FOR_ALL_INDUSTRIES(ind) RemoveInvalidLinks(ind);
00726
00727
00728 FOR_ALL_TOWNS(t) UpdateExpectedLinks(t);
00729 FOR_ALL_INDUSTRIES(ind) UpdateExpectedLinks(ind);
00730
00731
00732 FOR_ALL_INDUSTRIES(ind) AddMissingIndustryLinks(ind);
00733
00734
00735 FOR_ALL_TOWNS(t) UpdateCargoLinks(t);
00736 FOR_ALL_INDUSTRIES(ind) UpdateCargoLinks(ind);
00737
00738
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
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
00763
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 TileArea Town::GetTileForDestination(CargoID cid)
00774 {
00775 assert(this->cargo_accepted_weights[cid] != 0);
00776
00777
00778 uint32 weight = RandomRange(this->cargo_accepted_weights[cid] - 1);
00779
00780
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
00787 if (weight < weight_sum) return TileArea(tile + TileDiffXY(1, 1), 2, 2);
00788 }
00789 }
00790
00791
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
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
00827 if (source->cargo_links[cid].Length() == 0) return false;
00828
00829
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
00839 if (dest == NULL) dest = Town::GetRandom(&EnumAcceptingTown, INVALID_TOWN, (void *)(size_t)cid);
00840
00841 if (dest == NULL) dest = Industry::GetRandom(&EnumAcceptingIndustry, INVALID_INDUSTRY, (void *)(size_t)cid);
00842
00843 if (dest == NULL) return false;
00844
00845
00846 TileArea dest_area = dest->GetTileForDestination(cid);
00847
00848
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
00854
00855 byte flags = r & 0x3;
00856
00857
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
00863
00864 if (found && l != NULL) l->amount.new_act += *amount * (route_link == NULL ? 256 : Station::Get(st)->goods[cid].rating + 1) / 256;
00865
00866 if (route_link == NULL) {
00867
00868
00869 *amount = 0;
00870 return true;
00871 }
00872
00873
00874 Station *from = Station::Get(st);
00875 *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);
00876
00877
00878
00879 if (IsSymmetricCargo(cid)) {
00880
00881
00882 CargoLink *back_link = dest->cargo_links[cid].Find(CargoLink(source, LWM_ANYWHERE));
00883 if (back_link == dest->cargo_links[cid].End()) return true;
00884
00885 back_link->amount.new_max += *amount;
00886
00887
00888 StationFinder stf(dest_area);
00889 TileIndex tile = dest_area.tile;
00890
00891
00892 switch (source_type) {
00893 case ST_INDUSTRY:
00894 dest_area = static_cast<Industry *>(source)->location;
00895 break;
00896 case ST_TOWN:
00897 dest_area = TileArea(src_tile, 2, 2);
00898 break;
00899 case ST_HEADQUARTERS:
00900 dest_area = TileArea(Company::Get(source_id)->location_of_HQ, 2, 2);
00901 break;
00902 }
00903
00904
00905 route_link = YapfChooseRouteLink(cid, stf.GetStations(), tile, dest_area, &st, &st_unload, flags, &found, INVALID_ORDER, max_cost);
00906 if (found) back_link->amount.new_act += *amount;
00907
00908 if (route_link != NULL) {
00909
00910 UpdateStationWaiting(Station::Get(st), cid, *amount * 256, dest->GetType(), dest->GetID(), dest_area.tile, source_type, source_id, route_link->GetOriginOrderId(), st_unload, flags);
00911 }
00912 }
00913
00914 return true;
00915 }
00916
00927 bool MoveCargoWithDestinationToStation(CargoID cid, uint *amount, SourceType source_type, SourceID source_id, const StationList *all_stations, TileIndex src_tile)
00928 {
00929 if (!CargoHasDestinations(cid)) return false;
00930
00931
00932 int num_packets = 1;
00933 if (source_type == ST_INDUSTRY) {
00934 if (*amount > 5) num_packets++;
00935 if (*amount > 50) num_packets += 2;
00936 }
00937
00938
00939
00940 uint amount_packet = *amount / num_packets;
00941 uint last_packet = *amount - (num_packets - 1) * amount_packet;
00942 uint moved = 0;
00943 for (; num_packets > 0; num_packets--) {
00944 uint cur_amount = (num_packets == 1) ? last_packet : amount_packet;
00945
00946
00947 if (!MoveCargoWithDestinationToStationWorker(cid, &cur_amount, source_type, source_id, all_stations, src_tile)) return false;
00948 moved += cur_amount;
00949 }
00950
00951 *amount = moved;
00952 return true;
00953 }
00954
00964 RouteLink *FindRouteLinkForCargo(Station *st, CargoID cid, const CargoPacket *cp, StationID *next_unload, OrderID order, bool *found)
00965 {
00966 if (cp->DestinationID() == INVALID_SOURCE) return NULL;
00967
00968 StationList sl;
00969 *sl.Append() = st;
00970
00971 TileArea area = (cp->DestinationType() == ST_INDUSTRY) ? Industry::Get(cp->DestinationID())->location : TileArea(cp->DestinationXY(), 2, 2);
00972 return YapfChooseRouteLink(cid, &sl, st->xy, area, NULL, next_unload, cp->Flags(), found, order);
00973 }
00974
00975
00976
00977 RouteLinkPool _routelink_pool("RouteLink");
00978 INSTANTIATE_POOL_METHODS(RouteLink)
00979
00980
00983 RouteLink::~RouteLink()
00984 {
00985 if (RouteLink::CleaningPool()) return;
00986
00987 if (this->GetOriginOrderId() != INVALID_ORDER) StationCargoList::InvalidateAllTo(this->GetOriginOrderId(), this->GetDestination());
00988 }
00989
01001 void UpdateVehicleRouteLinks(const Vehicle *v, uint32 cargos, bool clear_others, Station *from, OrderID from_oid, StationID to_id, OrderID to_oid, uint32 travel_time)
01002 {
01003 for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
01004 bool has_cargo = HasBit(cargos, cid);
01005
01006 if (!clear_others && !has_cargo) continue;
01007
01008 if (!CargoHasDestinations(cid)) continue;
01009
01010 RouteLinkList::iterator link;
01011 for (link = from->goods[cid].routes.begin(); link != from->goods[cid].routes.end(); ++link) {
01012 if ((*link)->GetOriginOrderId() == from_oid) {
01013 if (has_cargo) {
01014
01015 (*link)->SetDestination(to_id, to_oid);
01016 (*link)->UpdateTravelTime(travel_time);
01017 } else {
01018
01019 delete *link;
01020 from->goods[cid].routes.erase(link);
01021 }
01022 break;
01023 }
01024 }
01025
01026
01027 if (has_cargo && link == from->goods[cid].routes.end() && RouteLink::CanAllocateItem()) {
01028 from->goods[cid].routes.push_back(new RouteLink(to_id, from_oid, to_oid, v->owner, travel_time, v->type));
01029 }
01030 }
01031 }
01032
01038 void UpdateVehicleRouteLinks(const Vehicle *v, StationID arrived_at)
01039 {
01040
01041 if (v->last_station_loaded == INVALID_STATION || v->last_order_id == INVALID_ORDER || v->current_order.index == INVALID_ORDER) return;
01042
01043 if (v->last_station_loaded == arrived_at) return;
01044
01045 Station *from = Station::Get(v->last_station_loaded);
01046 Station *to = Station::Get(arrived_at);
01047
01048
01049 UpdateVehicleRouteLinks(v, v->vcache.cached_cargo_mask, false, from, v->last_order_id, arrived_at, v->current_order.index, v->travel_time);
01050
01051
01052 CargoID cid;
01053 FOR_EACH_SET_CARGO_ID(cid, v->vcache.cached_cargo_mask) {
01054
01055 if (!CargoHasDestinations(cid)) continue;
01056
01057 for (RouteLinkList::iterator link = to->goods[cid].routes.begin(); link != to->goods[cid].routes.end(); ++link) {
01058 if ((*link)->GetOriginOrderId() == v->current_order.index) {
01059 (*link)->VehicleArrived();
01060 break;
01061 }
01062 }
01063 }
01064 }
01065
01070 void PrefillRouteLinks(const Vehicle *v)
01071 {
01072 if (CargoDestinationsDisabled()) return;
01073 if (v->orders.list == NULL || v->orders.list->GetNumOrders() < 2) return;
01074
01075
01076 uint count = 0;
01077 Order *order;
01078 FOR_VEHICLE_ORDERS(v, order) {
01079 if (order->IsType(OT_GOTO_DEPOT) && order->IsRefit()) return;
01080 if (order->IsType(OT_CONDITIONAL)) return;
01081 if ((order->IsType(OT_AUTOMATIC) || order->IsType(OT_GOTO_STATION)) && (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) == 0) count++;
01082 }
01083
01084
01085 if (count > 0) count++;
01086
01087
01088 uint32 transported_cargos = 0;
01089 for (Vehicle *u = v->FirstShared(); u != NULL; u = u->NextShared()) {
01090 transported_cargos |= u->vcache.cached_cargo_mask;
01091 }
01092
01093
01094 order = v->orders.list->GetFirstOrder();
01095 Order *prev_order = NULL;
01096 do {
01097
01098 if ((order->IsType(OT_AUTOMATIC) || order->IsType(OT_GOTO_STATION)) && (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) == 0) {
01099
01100 if (prev_order != NULL && prev_order != order && prev_order->GetDestination() != order->GetDestination()) {
01101 Station *from = Station::Get(prev_order->GetDestination());
01102 Station *to = Station::Get(order->GetDestination());
01103
01104
01105
01106 uint time = DistanceManhattan(from->xy, to->xy) * TILE_SIZE * 128 / v->GetDisplayMaxSpeed();
01107 if (v->type == VEH_AIRCRAFT) time *= _settings_game.vehicle.plane_speed;
01108 UpdateVehicleRouteLinks(v, transported_cargos, true, from, prev_order->index, order->GetDestination(), order->index, time);
01109 }
01110
01111 prev_order = order;
01112 count--;
01113 }
01114
01115
01116 order = order->next;
01117 if (order == NULL) order = v->orders.list->GetFirstOrder();
01118 } while (count > 0);
01119 }
01120
01125 void InvalidateStationRouteLinks(Station *station)
01126 {
01127
01128 for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
01129 for (RouteLinkList::iterator link = station->goods[cid].routes.begin(); link != station->goods[cid].routes.end(); ++link) {
01130 delete *link;
01131 }
01132 }
01133
01134
01135 Station *st_from;
01136 FOR_ALL_STATIONS(st_from) {
01137 if (st_from == station) continue;
01138
01139 for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
01140
01141 for (RouteLinkList::iterator link = st_from->goods[cid].routes.begin(); link != st_from->goods[cid].routes.end(); ) {
01142 if ((*link)->GetDestination() == station->index) {
01143 delete *link;
01144 link = st_from->goods[cid].routes.erase(link);
01145 } else {
01146 ++link;
01147 }
01148 }
01149 }
01150 }
01151 }
01152
01157 void InvalidateOrderRouteLinks(OrderID order)
01158 {
01159 Station *st;
01160 FOR_ALL_STATIONS(st) {
01161 for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
01162
01163 for (RouteLinkList::iterator link = st->goods[cid].routes.begin(); link != st->goods[cid].routes.end(); ) {
01164 if ((*link)->GetOriginOrderId() == order || (*link)->GetDestOrderId() == order) {
01165 delete *link;
01166 link = st->goods[cid].routes.erase(link);
01167 } else {
01168 ++link;
01169 }
01170 }
01171 }
01172 }
01173 }
01174
01176 void AgeRouteLinks(Station *st)
01177 {
01178
01179 for (std::list<Vehicle *>::const_iterator v_itr = st->loading_vehicles.begin(); v_itr != st->loading_vehicles.end(); ++v_itr) {
01180 CargoID cid;
01181 FOR_EACH_SET_CARGO_ID(cid, (*v_itr)->vcache.cached_cargo_mask) {
01182 for (RouteLinkList::iterator link = st->goods[cid].routes.begin(); link != st->goods[cid].routes.end(); ++link) {
01183 if ((*link)->GetOriginOrderId() == (*v_itr)->last_order_id) (*link)->wait_time = 0;
01184 }
01185 }
01186 }
01187
01188 for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
01189
01190 for (RouteLinkList::iterator link = st->goods[cid].routes.begin(); link != st->goods[cid].routes.end(); ) {
01191 if ((*link)->wait_time++ > _settings_game.economy.cargodest.max_route_age) {
01192 delete *link;
01193 link = st->goods[cid].routes.erase(link);
01194 } else {
01195 ++link;
01196 }
01197 }
01198 }
01199 }