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