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 NS_SUBSIDIES,
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, NS_SUBSIDIES, (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()) return false;
00352
00353
00354 if (src_town->GetPercentTransported(cid) > SUBSIDY_MAX_PCT_TRANSPORTED) return false;
00355
00356 SourceID src = src_town->index;
00357
00358 return FindSubsidyCargoDestination(cid, src_type, src);
00359 }
00360
00365 bool FindSubsidyIndustryCargoRoute()
00366 {
00367 if (!Subsidy::CanAllocateItem()) return false;
00368
00369 SourceType src_type = ST_INDUSTRY;
00370
00371
00372 const Industry *src_ind = Industry::GetRandom();
00373 if (src_ind == NULL) return false;
00374
00375 uint trans, total;
00376
00377 CargoID cid;
00378
00379
00380 if (src_ind->produced_cargo[1] != CT_INVALID && HasBit(Random(), 0)) {
00381 cid = src_ind->produced_cargo[1];
00382 trans = src_ind->last_month_pct_transported[1];
00383 total = src_ind->last_month_production[1];
00384 } else {
00385 cid = src_ind->produced_cargo[0];
00386 trans = src_ind->last_month_pct_transported[0];
00387 total = src_ind->last_month_production[0];
00388 }
00389
00390
00391
00392 if (total == 0 || trans > SUBSIDY_MAX_PCT_TRANSPORTED || cid == CT_INVALID) return false;
00393
00394 SourceID src = src_ind->index;
00395
00396 return FindSubsidyCargoDestination(cid, src_type, src);
00397 }
00398
00406 bool FindSubsidyCargoDestination(CargoID cid, SourceType src_type, SourceID src)
00407 {
00408
00409 SourceType dst_type = (HasBit(_town_cargoes_accepted, cid) && Chance16(1, 2)) ? ST_TOWN : ST_INDUSTRY;
00410
00411 SourceID dst;
00412 switch (dst_type) {
00413 case ST_TOWN: {
00414
00415 const Town *dst_town = Town::GetRandom();
00416
00417
00418 if (!HasBit(dst_town->cargo_accepted_total, cid)) return false;
00419
00420 dst = dst_town->index;
00421 break;
00422 }
00423
00424 case ST_INDUSTRY: {
00425
00426 const Industry *dst_ind = Industry::GetRandom();
00427
00428
00429 if (dst_ind == NULL ||
00430 (cid != dst_ind->accepts_cargo[0] &&
00431 cid != dst_ind->accepts_cargo[1] &&
00432 cid != dst_ind->accepts_cargo[2])) {
00433 return false;
00434 }
00435
00436 dst = dst_ind->index;
00437 break;
00438 }
00439
00440 default: NOT_REACHED();
00441 }
00442
00443
00444 if (src_type == dst_type && src == dst) return false;
00445
00446
00447 if (!CheckSubsidyDistance(src_type, src, dst_type, dst)) return false;
00448
00449
00450 if (CheckSubsidyDuplicate(cid, src_type, src, dst_type, dst)) return false;
00451
00452 CreateSubsidy(cid, src_type, src, dst_type, dst);
00453
00454 return true;
00455 }
00456
00458 void SubsidyMonthlyLoop()
00459 {
00460 bool modified = false;
00461
00462 Subsidy *s;
00463 FOR_ALL_SUBSIDIES(s) {
00464 if (--s->remaining == 0) {
00465 if (!s->IsAwarded()) {
00466 Pair reftype = SetupSubsidyDecodeParam(s, true);
00467 AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
00468 AI::BroadcastNewEvent(new ScriptEventSubsidyOfferExpired(s->index));
00469 Game::NewEvent(new ScriptEventSubsidyOfferExpired(s->index));
00470 } else {
00471 if (s->awarded == _local_company) {
00472 Pair reftype = SetupSubsidyDecodeParam(s, true);
00473 AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
00474 }
00475 AI::BroadcastNewEvent(new ScriptEventSubsidyExpired(s->index));
00476 Game::NewEvent(new ScriptEventSubsidyExpired(s->index));
00477 }
00478 delete s;
00479 modified = true;
00480 }
00481 }
00482
00483 if (modified) RebuildSubsidisedSourceAndDestinationCache();
00484
00485 bool passenger_subsidy = false;
00486 bool town_subsidy = false;
00487 bool industry_subsidy = false;
00488
00489 int random_chance = RandomRange(16);
00490
00491 if (random_chance < 2) {
00492
00493 int n = 1000;
00494
00495 do {
00496 passenger_subsidy = FindSubsidyPassengerRoute();
00497 } while (!passenger_subsidy && n--);
00498 } else if (random_chance == 2) {
00499
00500 int n = 1000;
00501
00502 do {
00503 town_subsidy = FindSubsidyTownCargoRoute();
00504 } while (!town_subsidy && n--);
00505 } else if (random_chance == 3) {
00506
00507 int n = 1000;
00508
00509 do {
00510 industry_subsidy = FindSubsidyIndustryCargoRoute();
00511 } while (!industry_subsidy && n--);
00512 }
00513
00514 modified |= passenger_subsidy || town_subsidy || industry_subsidy;
00515
00516 if (modified) InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
00517 }
00518
00528 bool CheckSubsidised(CargoID cargo_type, CompanyID company, SourceType src_type, SourceID src, const Station *st)
00529 {
00530
00531 if (src == INVALID_SOURCE) return false;
00532 switch (src_type) {
00533 case ST_INDUSTRY:
00534 if (!(Industry::Get(src)->part_of_subsidy & POS_SRC)) return false;
00535 break;
00536 case ST_TOWN:
00537 if (!(Town::Get(src)->cache.part_of_subsidy & POS_SRC)) return false;
00538 break;
00539 default: return false;
00540 }
00541
00542
00543
00544 SmallVector<const Town *, 2> towns_near;
00545 if (!st->rect.IsEmpty()) {
00546 Subsidy *s;
00547 FOR_ALL_SUBSIDIES(s) {
00548
00549 if (s->dst_type != ST_TOWN) continue;
00550 if (s->cargo_type != cargo_type || s->src_type != src_type || s->src != src) continue;
00551 if (s->IsAwarded() && s->awarded != company) continue;
00552
00553 Rect rect = st->GetCatchmentRect();
00554
00555 for (int y = rect.top; y <= rect.bottom; y++) {
00556 for (int x = rect.left; x <= rect.right; x++) {
00557 TileIndex tile = TileXY(x, y);
00558 if (!IsTileType(tile, MP_HOUSE)) continue;
00559 const Town *t = Town::GetByTile(tile);
00560 if (t->cache.part_of_subsidy & POS_DST) towns_near.Include(t);
00561 }
00562 }
00563 break;
00564 }
00565 }
00566
00567 bool subsidised = false;
00568
00569
00570
00571 Subsidy *s;
00572 FOR_ALL_SUBSIDIES(s) {
00573 if (s->cargo_type == cargo_type && s->src_type == src_type && s->src == src && (!s->IsAwarded() || s->awarded == company)) {
00574 switch (s->dst_type) {
00575 case ST_INDUSTRY:
00576 for (const Industry * const *ip = st->industries_near.Begin(); ip != st->industries_near.End(); ip++) {
00577 if (s->dst == (*ip)->index) {
00578 assert((*ip)->part_of_subsidy & POS_DST);
00579 subsidised = true;
00580 if (!s->IsAwarded()) s->AwardTo(company);
00581 }
00582 }
00583 break;
00584 case ST_TOWN:
00585 for (const Town * const *tp = towns_near.Begin(); tp != towns_near.End(); tp++) {
00586 if (s->dst == (*tp)->index) {
00587 assert((*tp)->cache.part_of_subsidy & POS_DST);
00588 subsidised = true;
00589 if (!s->IsAwarded()) s->AwardTo(company);
00590 }
00591 }
00592 break;
00593 default:
00594 NOT_REACHED();
00595 }
00596 }
00597 }
00598
00599 return subsidised;
00600 }