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 if (route_link == NULL) {
00863
00864
00865 if (found && l != NULL) l->amount.new_act += *amount;
00866 *amount = 0;
00867 return true;
00868 }
00869
00870
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
00876
00877 if (IsSymmetricCargo(cid)) {
00878
00879
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
00886 StationFinder stf(dest_area);
00887 TileIndex tile = dest_area.tile;
00888
00889
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
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
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
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
00937
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
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
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
01004 if (!clear_others && !has_cargo) continue;
01005
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
01013 (*link)->SetDestination(to_id, to_oid);
01014 (*link)->UpdateTravelTime(travel_time);
01015 } else {
01016
01017 delete *link;
01018 from->goods[cid].routes.erase(link);
01019 }
01020 break;
01021 }
01022 }
01023
01024
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
01039 if (v->last_station_loaded == INVALID_STATION || v->last_order_id == INVALID_ORDER || v->current_order.index == INVALID_ORDER) return;
01040
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
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
01050 CargoID cid;
01051 FOR_EACH_SET_CARGO_ID(cid, v->vcache.cached_cargo_mask) {
01052
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
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
01083 if (count > 0) count++;
01084
01085
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
01092 order = v->orders.list->GetFirstOrder();
01093 Order *prev_order = NULL;
01094 do {
01095
01096 if ((order->IsType(OT_IMPLICIT) || order->IsType(OT_GOTO_STATION)) && (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) == 0) {
01097
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
01102
01103
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
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
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
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
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
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
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
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 }