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