00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "company_func.h"
00014 #include "industry.h"
00015 #include "town.h"
00016 #include "news_func.h"
00017 #include "ai/ai.hpp"
00018 #include "station_base.h"
00019 #include "strings_func.h"
00020 #include "window_func.h"
00021 #include "subsidy_base.h"
00022 #include "subsidy_func.h"
00023 #include "core/pool_func.hpp"
00024 #include "core/random_func.hpp"
00025 #include "game/game.hpp"
00026 #include "command_func.h"
00027
00028 #include "table/strings.h"
00029
00030 SubsidyPool _subsidy_pool("Subsidy");
00031 INSTANTIATE_POOL_METHODS(Subsidy)
00032
00033
00037 void Subsidy::AwardTo(CompanyID company)
00038 {
00039 assert(!this->IsAwarded());
00040
00041 this->awarded = company;
00042 this->remaining = SUBSIDY_CONTRACT_MONTHS;
00043
00044 char company_name[MAX_LENGTH_COMPANY_NAME_CHARS * MAX_CHAR_LENGTH];
00045 SetDParam(0, company);
00046 GetString(company_name, STR_COMPANY_NAME, lastof(company_name));
00047
00048 char *cn = strdup(company_name);
00049
00050
00051 Pair reftype = SetupSubsidyDecodeParam(this, false);
00052 InjectDParam(1);
00053
00054 SetDParamStr(0, cn);
00055 AddNewsItem(
00056 STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF + _settings_game.difficulty.subsidy_multiplier,
00057 NT_SUBSIDIES, NF_NORMAL,
00058 (NewsReferenceType)reftype.a, this->src, (NewsReferenceType)reftype.b, this->dst,
00059 cn
00060 );
00061 AI::BroadcastNewEvent(new ScriptEventSubsidyAwarded(this->index));
00062 Game::NewEvent(new ScriptEventSubsidyAwarded(this->index));
00063
00064 InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
00065 }
00066
00073 Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode)
00074 {
00075 NewsReferenceType reftype1 = NR_NONE;
00076 NewsReferenceType reftype2 = NR_NONE;
00077
00078
00079 const CargoSpec *cs = CargoSpec::Get(s->cargo_type);
00080 SetDParam(0, mode ? cs->name : cs->name_single);
00081
00082 switch (s->src_type) {
00083 case ST_INDUSTRY:
00084 reftype1 = NR_INDUSTRY;
00085 SetDParam(1, STR_INDUSTRY_NAME);
00086 break;
00087 case ST_TOWN:
00088 reftype1 = NR_TOWN;
00089 SetDParam(1, STR_TOWN_NAME);
00090 break;
00091 default: NOT_REACHED();
00092 }
00093 SetDParam(2, s->src);
00094
00095 switch (s->dst_type) {
00096 case ST_INDUSTRY:
00097 reftype2 = NR_INDUSTRY;
00098 SetDParam(4, STR_INDUSTRY_NAME);
00099 break;
00100 case ST_TOWN:
00101 reftype2 = NR_TOWN;
00102 SetDParam(4, STR_TOWN_NAME);
00103 break;
00104 default: NOT_REACHED();
00105 }
00106 SetDParam(5, s->dst);
00107
00108 Pair p;
00109 p.a = reftype1;
00110 p.b = reftype2;
00111 return p;
00112 }
00113
00120 static inline void SetPartOfSubsidyFlag(SourceType type, SourceID index, PartOfSubsidy flag)
00121 {
00122 switch (type) {
00123 case ST_INDUSTRY: Industry::Get(index)->part_of_subsidy |= flag; return;
00124 case ST_TOWN: Town::Get(index)->cache.part_of_subsidy |= flag; return;
00125 default: NOT_REACHED();
00126 }
00127 }
00128
00130 void RebuildSubsidisedSourceAndDestinationCache()
00131 {
00132 Town *t;
00133 FOR_ALL_TOWNS(t) t->cache.part_of_subsidy = POS_NONE;
00134
00135 Industry *i;
00136 FOR_ALL_INDUSTRIES(i) i->part_of_subsidy = POS_NONE;
00137
00138 const Subsidy *s;
00139 FOR_ALL_SUBSIDIES(s) {
00140 SetPartOfSubsidyFlag(s->src_type, s->src, POS_SRC);
00141 SetPartOfSubsidyFlag(s->dst_type, s->dst, POS_DST);
00142 }
00143 }
00144
00150 void DeleteSubsidyWith(SourceType type, SourceID index)
00151 {
00152 bool dirty = false;
00153
00154 Subsidy *s;
00155 FOR_ALL_SUBSIDIES(s) {
00156 if ((s->src_type == type && s->src == index) || (s->dst_type == type && s->dst == index)) {
00157 delete s;
00158 dirty = true;
00159 }
00160 }
00161
00162 if (dirty) {
00163 InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
00164 RebuildSubsidisedSourceAndDestinationCache();
00165 }
00166 }
00167
00177 static bool CheckSubsidyDuplicate(CargoID cargo, SourceType src_type, SourceID src, SourceType dst_type, SourceID dst)
00178 {
00179 const Subsidy *s;
00180 FOR_ALL_SUBSIDIES(s) {
00181 if (s->cargo_type == cargo &&
00182 s->src_type == src_type && s->src == src &&
00183 s->dst_type == dst_type && s->dst == dst) {
00184 return true;
00185 }
00186 }
00187 return false;
00188 }
00189
00198 static bool CheckSubsidyDistance(SourceType src_type, SourceID src, SourceType dst_type, SourceID dst)
00199 {
00200 TileIndex tile_src = (src_type == ST_TOWN) ? Town::Get(src)->xy : Industry::Get(src)->location.tile;
00201 TileIndex tile_dst = (dst_type == ST_TOWN) ? Town::Get(dst)->xy : Industry::Get(dst)->location.tile;
00202
00203 return (DistanceManhattan(tile_src, tile_dst) <= SUBSIDY_MAX_DISTANCE);
00204 }
00205
00214 void CreateSubsidy(CargoID cid, SourceType src_type, SourceID src, SourceType dst_type, SourceID dst)
00215 {
00216 Subsidy *s = new Subsidy();
00217 s->cargo_type = cid;
00218 s->src_type = src_type;
00219 s->src = src;
00220 s->dst_type = dst_type;
00221 s->dst = dst;
00222 s->remaining = SUBSIDY_OFFER_MONTHS;
00223 s->awarded = INVALID_COMPANY;
00224
00225 Pair reftype = SetupSubsidyDecodeParam(s, false);
00226 AddNewsItem(STR_NEWS_SERVICE_SUBSIDY_OFFERED, NT_SUBSIDIES, NF_NORMAL, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
00227 SetPartOfSubsidyFlag(s->src_type, s->src, POS_SRC);
00228 SetPartOfSubsidyFlag(s->dst_type, s->dst, POS_DST);
00229 AI::BroadcastNewEvent(new ScriptEventSubsidyOffer(s->index));
00230 Game::NewEvent(new ScriptEventSubsidyOffer(s->index));
00231
00232 InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
00233 }
00234
00249 CommandCost CmdCreateSubsidy(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00250 {
00251 if (!Subsidy::CanAllocateItem()) return CMD_ERROR;
00252
00253 CargoID cid = GB(p1, 24, 8);
00254 SourceType src_type = (SourceType)GB(p1, 0, 8);
00255 SourceID src = GB(p1, 8, 16);
00256 SourceType dst_type = (SourceType)GB(p2, 0, 8);
00257 SourceID dst = GB(p2, 8, 16);
00258
00259 if (_current_company != OWNER_DEITY) return CMD_ERROR;
00260
00261 if (cid >= NUM_CARGO || !::CargoSpec::Get(cid)->IsValid()) return CMD_ERROR;
00262
00263 switch (src_type) {
00264 case ST_TOWN:
00265 if (!Town::IsValidID(src)) return CMD_ERROR;
00266 break;
00267 case ST_INDUSTRY:
00268 if (!Industry::IsValidID(src)) return CMD_ERROR;
00269 break;
00270 default:
00271 return CMD_ERROR;
00272 }
00273 switch (dst_type) {
00274 case ST_TOWN:
00275 if (!Town::IsValidID(dst)) return CMD_ERROR;
00276 break;
00277 case ST_INDUSTRY:
00278 if (!Industry::IsValidID(dst)) return CMD_ERROR;
00279 break;
00280 default:
00281 return CMD_ERROR;
00282 }
00283
00284 if (flags & DC_EXEC) {
00285 CreateSubsidy(cid, src_type, src, dst_type, dst);
00286 }
00287
00288 return CommandCost();
00289 }
00290
00295 bool FindSubsidyPassengerRoute()
00296 {
00297 if (!Subsidy::CanAllocateItem()) return false;
00298
00299 const Town *src = Town::GetRandom();
00300 if (src->cache.population < SUBSIDY_PAX_MIN_POPULATION ||
00301 src->GetPercentTransported(CT_PASSENGERS) > SUBSIDY_MAX_PCT_TRANSPORTED) {
00302 return false;
00303 }
00304
00305 const Town *dst = Town::GetRandom();
00306 if (dst->cache.population < SUBSIDY_PAX_MIN_POPULATION || src == dst) {
00307 return false;
00308 }
00309
00310 if (DistanceManhattan(src->xy, dst->xy) > SUBSIDY_MAX_DISTANCE) return false;
00311 if (CheckSubsidyDuplicate(CT_PASSENGERS, ST_TOWN, src->index, ST_TOWN, dst->index)) return false;
00312
00313 CreateSubsidy(CT_PASSENGERS, ST_TOWN, src->index, ST_TOWN, dst->index);
00314
00315 return true;
00316 }
00317
00318 bool FindSubsidyCargoDestination(CargoID cid, SourceType src_type, SourceID src);
00319
00320
00325 bool FindSubsidyTownCargoRoute()
00326 {
00327 if (!Subsidy::CanAllocateItem()) return false;
00328
00329 SourceType src_type = ST_TOWN;
00330
00331
00332 const Town *src_town = Town::GetRandom();
00333
00334 uint32 town_cargo_produced = src_town->cargo_produced;
00335
00336
00337 ClrBit(town_cargo_produced, CT_PASSENGERS);
00338
00339
00340 if (town_cargo_produced == 0) return false;
00341
00342
00343 uint8 cargo_number = RandomRange(CountBits(town_cargo_produced));
00344 CargoID cid;
00345 FOR_EACH_SET_CARGO_ID(cid, town_cargo_produced) {
00346 if (cargo_number == 0) break;
00347 cargo_number--;
00348 }
00349
00350
00351 if (!CargoSpec::Get(cid)->IsValid() ||
00352 _settings_game.linkgraph.GetDistributionType(cid) != DT_MANUAL) {
00353 return false;
00354 }
00355
00356
00357 if (src_town->GetPercentTransported(cid) > SUBSIDY_MAX_PCT_TRANSPORTED) return false;
00358
00359 SourceID src = src_town->index;
00360
00361 return FindSubsidyCargoDestination(cid, src_type, src);
00362 }
00363
00368 bool FindSubsidyIndustryCargoRoute()
00369 {
00370 if (!Subsidy::CanAllocateItem()) return false;
00371
00372 SourceType src_type = ST_INDUSTRY;
00373
00374
00375 const Industry *src_ind = Industry::GetRandom();
00376 if (src_ind == NULL) return false;
00377
00378 uint trans, total;
00379
00380 CargoID cid;
00381
00382
00383 if (src_ind->produced_cargo[1] != CT_INVALID && HasBit(Random(), 0)) {
00384 cid = src_ind->produced_cargo[1];
00385 trans = src_ind->last_month_pct_transported[1];
00386 total = src_ind->last_month_production[1];
00387 } else {
00388 cid = src_ind->produced_cargo[0];
00389 trans = src_ind->last_month_pct_transported[0];
00390 total = src_ind->last_month_production[0];
00391 }
00392
00393
00394
00395
00396 if (total == 0 || trans > SUBSIDY_MAX_PCT_TRANSPORTED ||
00397 cid == CT_INVALID ||
00398 _settings_game.linkgraph.GetDistributionType(cid) != DT_MANUAL) {
00399 return false;
00400 }
00401
00402 SourceID src = src_ind->index;
00403
00404 return FindSubsidyCargoDestination(cid, src_type, src);
00405 }
00406
00414 bool FindSubsidyCargoDestination(CargoID cid, SourceType src_type, SourceID src)
00415 {
00416
00417 SourceType dst_type = (HasBit(_town_cargoes_accepted, cid) && Chance16(1, 2)) ? ST_TOWN : ST_INDUSTRY;
00418
00419 SourceID dst;
00420 switch (dst_type) {
00421 case ST_TOWN: {
00422
00423 const Town *dst_town = Town::GetRandom();
00424
00425
00426 if (!HasBit(dst_town->cargo_accepted_total, cid)) return false;
00427
00428 dst = dst_town->index;
00429 break;
00430 }
00431
00432 case ST_INDUSTRY: {
00433
00434 const Industry *dst_ind = Industry::GetRandom();
00435
00436
00437 if (dst_ind == NULL ||
00438 (cid != dst_ind->accepts_cargo[0] &&
00439 cid != dst_ind->accepts_cargo[1] &&
00440 cid != dst_ind->accepts_cargo[2])) {
00441 return false;
00442 }
00443
00444 dst = dst_ind->index;
00445 break;
00446 }
00447
00448 default: NOT_REACHED();
00449 }
00450
00451
00452 if (src_type == dst_type && src == dst) return false;
00453
00454
00455 if (!CheckSubsidyDistance(src_type, src, dst_type, dst)) return false;
00456
00457
00458 if (CheckSubsidyDuplicate(cid, src_type, src, dst_type, dst)) return false;
00459
00460 CreateSubsidy(cid, src_type, src, dst_type, dst);
00461
00462 return true;
00463 }
00464
00466 void SubsidyMonthlyLoop()
00467 {
00468 bool modified = false;
00469
00470 Subsidy *s;
00471 FOR_ALL_SUBSIDIES(s) {
00472 if (--s->remaining == 0) {
00473 if (!s->IsAwarded()) {
00474 Pair reftype = SetupSubsidyDecodeParam(s, true);
00475 AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NT_SUBSIDIES, NF_NORMAL, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
00476 AI::BroadcastNewEvent(new ScriptEventSubsidyOfferExpired(s->index));
00477 Game::NewEvent(new ScriptEventSubsidyOfferExpired(s->index));
00478 } else {
00479 if (s->awarded == _local_company) {
00480 Pair reftype = SetupSubsidyDecodeParam(s, true);
00481 AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NT_SUBSIDIES, NF_NORMAL, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
00482 }
00483 AI::BroadcastNewEvent(new ScriptEventSubsidyExpired(s->index));
00484 Game::NewEvent(new ScriptEventSubsidyExpired(s->index));
00485 }
00486 delete s;
00487 modified = true;
00488 }
00489 }
00490
00491 if (modified) {
00492 RebuildSubsidisedSourceAndDestinationCache();
00493 } else if (_settings_game.linkgraph.distribution_pax != DT_MANUAL &&
00494 _settings_game.linkgraph.distribution_mail != DT_MANUAL &&
00495 _settings_game.linkgraph.distribution_armoured != DT_MANUAL &&
00496 _settings_game.linkgraph.distribution_default != DT_MANUAL) {
00497
00498
00499 return;
00500 }
00501
00502 bool passenger_subsidy = false;
00503 bool town_subsidy = false;
00504 bool industry_subsidy = false;
00505
00506 int random_chance = RandomRange(16);
00507
00508 if (random_chance < 2 && _settings_game.linkgraph.distribution_pax == DT_MANUAL) {
00509
00510 int n = 1000;
00511
00512 do {
00513 passenger_subsidy = FindSubsidyPassengerRoute();
00514 } while (!passenger_subsidy && n--);
00515 } else if (random_chance == 2) {
00516
00517 int n = 1000;
00518
00519 do {
00520 town_subsidy = FindSubsidyTownCargoRoute();
00521 } while (!town_subsidy && n--);
00522 } else if (random_chance == 3) {
00523
00524 int n = 1000;
00525
00526 do {
00527 industry_subsidy = FindSubsidyIndustryCargoRoute();
00528 } while (!industry_subsidy && n--);
00529 }
00530
00531 modified |= passenger_subsidy || town_subsidy || industry_subsidy;
00532
00533 if (modified) InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
00534 }
00535
00545 bool CheckSubsidised(CargoID cargo_type, CompanyID company, SourceType src_type, SourceID src, const Station *st)
00546 {
00547
00548 if (src == INVALID_SOURCE) return false;
00549 switch (src_type) {
00550 case ST_INDUSTRY:
00551 if (!(Industry::Get(src)->part_of_subsidy & POS_SRC)) return false;
00552 break;
00553 case ST_TOWN:
00554 if (!(Town::Get(src)->cache.part_of_subsidy & POS_SRC)) return false;
00555 break;
00556 default: return false;
00557 }
00558
00559
00560
00561 SmallVector<const Town *, 2> towns_near;
00562 if (!st->rect.IsEmpty()) {
00563 Subsidy *s;
00564 FOR_ALL_SUBSIDIES(s) {
00565
00566 if (s->dst_type != ST_TOWN) continue;
00567 if (s->cargo_type != cargo_type || s->src_type != src_type || s->src != src) continue;
00568 if (s->IsAwarded() && s->awarded != company) continue;
00569
00570 Rect rect = st->GetCatchmentRect();
00571
00572 for (int y = rect.top; y <= rect.bottom; y++) {
00573 for (int x = rect.left; x <= rect.right; x++) {
00574 TileIndex tile = TileXY(x, y);
00575 if (!IsTileType(tile, MP_HOUSE)) continue;
00576 const Town *t = Town::GetByTile(tile);
00577 if (t->cache.part_of_subsidy & POS_DST) towns_near.Include(t);
00578 }
00579 }
00580 break;
00581 }
00582 }
00583
00584 bool subsidised = false;
00585
00586
00587
00588 Subsidy *s;
00589 FOR_ALL_SUBSIDIES(s) {
00590 if (s->cargo_type == cargo_type && s->src_type == src_type && s->src == src && (!s->IsAwarded() || s->awarded == company)) {
00591 switch (s->dst_type) {
00592 case ST_INDUSTRY:
00593 for (const Industry * const *ip = st->industries_near.Begin(); ip != st->industries_near.End(); ip++) {
00594 if (s->dst == (*ip)->index) {
00595 assert((*ip)->part_of_subsidy & POS_DST);
00596 subsidised = true;
00597 if (!s->IsAwarded()) s->AwardTo(company);
00598 }
00599 }
00600 break;
00601 case ST_TOWN:
00602 for (const Town * const *tp = towns_near.Begin(); tp != towns_near.End(); tp++) {
00603 if (s->dst == (*tp)->index) {
00604 assert((*tp)->cache.part_of_subsidy & POS_DST);
00605 subsidised = true;
00606 if (!s->IsAwarded()) s->AwardTo(company);
00607 }
00608 }
00609 break;
00610 default:
00611 NOT_REACHED();
00612 }
00613 }
00614 }
00615
00616 return subsidised;
00617 }