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
00068 Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode)
00069 {
00070 NewsReferenceType reftype1 = NR_NONE;
00071 NewsReferenceType reftype2 = NR_NONE;
00072
00073
00074 const CargoSpec *cs = CargoSpec::Get(s->cargo_type);
00075 SetDParam(0, mode ? cs->name : cs->name_single);
00076
00077 switch (s->src_type) {
00078 case ST_INDUSTRY:
00079 reftype1 = NR_INDUSTRY;
00080 SetDParam(1, STR_INDUSTRY_NAME);
00081 break;
00082 case ST_TOWN:
00083 reftype1 = NR_TOWN;
00084 SetDParam(1, STR_TOWN_NAME);
00085 break;
00086 default: NOT_REACHED();
00087 }
00088 SetDParam(2, s->src);
00089
00090 switch (s->dst_type) {
00091 case ST_INDUSTRY:
00092 reftype2 = NR_INDUSTRY;
00093 SetDParam(4, STR_INDUSTRY_NAME);
00094 break;
00095 case ST_TOWN:
00096 reftype2 = NR_TOWN;
00097 SetDParam(4, STR_TOWN_NAME);
00098 break;
00099 default: NOT_REACHED();
00100 }
00101 SetDParam(5, s->dst);
00102
00103 Pair p;
00104 p.a = reftype1;
00105 p.b = reftype2;
00106 return p;
00107 }
00108
00115 static inline void SetPartOfSubsidyFlag(SourceType type, SourceID index, PartOfSubsidy flag)
00116 {
00117 switch (type) {
00118 case ST_INDUSTRY: Industry::Get(index)->part_of_subsidy |= flag; return;
00119 case ST_TOWN: Town::Get(index)->part_of_subsidy |= flag; return;
00120 default: NOT_REACHED();
00121 }
00122 }
00123
00124 void RebuildSubsidisedSourceAndDestinationCache()
00125 {
00126 Town *t;
00127 FOR_ALL_TOWNS(t) t->part_of_subsidy = POS_NONE;
00128
00129 Industry *i;
00130 FOR_ALL_INDUSTRIES(i) i->part_of_subsidy = POS_NONE;
00131
00132 const Subsidy *s;
00133 FOR_ALL_SUBSIDIES(s) {
00134 SetPartOfSubsidyFlag(s->src_type, s->src, POS_SRC);
00135 SetPartOfSubsidyFlag(s->dst_type, s->dst, POS_DST);
00136 }
00137 }
00138
00139 void DeleteSubsidyWith(SourceType type, SourceID index)
00140 {
00141 bool dirty = false;
00142
00143 Subsidy *s;
00144 FOR_ALL_SUBSIDIES(s) {
00145 if ((s->src_type == type && s->src == index) || (s->dst_type == type && s->dst == index)) {
00146 delete s;
00147 dirty = true;
00148 }
00149 }
00150
00151 if (dirty) {
00152 InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
00153 RebuildSubsidisedSourceAndDestinationCache();
00154 }
00155 }
00156
00157 static bool CheckSubsidyDuplicate(CargoID cargo, SourceType src_type, SourceID src, SourceType dst_type, SourceID dst)
00158 {
00159 const Subsidy *s;
00160 FOR_ALL_SUBSIDIES(s) {
00161 if (s->cargo_type == cargo &&
00162 s->src_type == src_type && s->src == src &&
00163 s->dst_type == dst_type && s->dst == dst) {
00164 return true;
00165 }
00166 }
00167 return false;
00168 }
00169
00177 static bool CheckSubsidyDistance(SourceType src_type, SourceID src, SourceType dst_type, SourceID dst)
00178 {
00179 TileIndex tile_src = (src_type == ST_TOWN) ? Town::Get(src)->xy : Industry::Get(src)->location.tile;
00180 TileIndex tile_dst = (dst_type == ST_TOWN) ? Town::Get(dst)->xy : Industry::Get(dst)->location.tile;
00181
00182 return (DistanceManhattan(tile_src, tile_dst) <= SUBSIDY_MAX_DISTANCE);
00183 }
00184
00192 void CreateSubsidy(CargoID cid, SourceType src_type, SourceID src, SourceType dst_type, SourceID dst)
00193 {
00194 Subsidy *s = new Subsidy();
00195 s->cargo_type = cid;
00196 s->src_type = src_type;
00197 s->src = src;
00198 s->dst_type = dst_type;
00199 s->dst = dst;
00200 s->remaining = SUBSIDY_OFFER_MONTHS;
00201 s->awarded = INVALID_COMPANY;
00202
00203 Pair reftype = SetupSubsidyDecodeParam(s, false);
00204 AddNewsItem(STR_NEWS_SERVICE_SUBSIDY_OFFERED, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
00205 SetPartOfSubsidyFlag(s->src_type, s->src, POS_SRC);
00206 SetPartOfSubsidyFlag(s->dst_type, s->dst, POS_DST);
00207 AI::BroadcastNewEvent(new ScriptEventSubsidyOffer(s->index));
00208 Game::NewEvent(new ScriptEventSubsidyOffer(s->index));
00209
00210 InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
00211 }
00212
00227 CommandCost CmdCreateSubsidy(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00228 {
00229 if (!Subsidy::CanAllocateItem()) return CMD_ERROR;
00230
00231 CargoID cid = GB(p1, 24, 8);
00232 SourceType src_type = (SourceType)GB(p1, 0, 8);
00233 SourceID src = GB(p1, 8, 16);
00234 SourceType dst_type = (SourceType)GB(p2, 0, 8);
00235 SourceID dst = GB(p2, 8, 16);
00236
00237 if (_current_company != OWNER_DEITY) return CMD_ERROR;
00238
00239 if (cid >= NUM_CARGO || !::CargoSpec::Get(cid)->IsValid()) return CMD_ERROR;
00240
00241 switch (src_type) {
00242 case ST_TOWN:
00243 if (!Town::IsValidID(src)) return CMD_ERROR;
00244 break;
00245 case ST_INDUSTRY:
00246 if (!Industry::IsValidID(src)) return CMD_ERROR;
00247 break;
00248 default:
00249 return CMD_ERROR;
00250 }
00251 switch (dst_type) {
00252 case ST_TOWN:
00253 if (!Town::IsValidID(dst)) return CMD_ERROR;
00254 break;
00255 case ST_INDUSTRY:
00256 if (!Industry::IsValidID(dst)) return CMD_ERROR;
00257 break;
00258 default:
00259 return CMD_ERROR;
00260 }
00261
00262 if (flags & DC_EXEC) {
00263 CreateSubsidy(cid, src_type, src, dst_type, dst);
00264 }
00265
00266 return CommandCost();
00267 }
00268
00272 bool FindSubsidyPassengerRoute()
00273 {
00274 if (!Subsidy::CanAllocateItem()) return false;
00275
00276 const Town *src = Town::GetRandom();
00277 if (src->population < SUBSIDY_PAX_MIN_POPULATION ||
00278 src->GetPercentTransported(CT_PASSENGERS) > SUBSIDY_MAX_PCT_TRANSPORTED) {
00279 return false;
00280 }
00281
00282 const Town *dst = Town::GetRandom();
00283 if (dst->population < SUBSIDY_PAX_MIN_POPULATION || src == dst) {
00284 return false;
00285 }
00286
00287 if (DistanceManhattan(src->xy, dst->xy) > SUBSIDY_MAX_DISTANCE) return false;
00288 if (CheckSubsidyDuplicate(CT_PASSENGERS, ST_TOWN, src->index, ST_TOWN, dst->index)) return false;
00289
00290 CreateSubsidy(CT_PASSENGERS, ST_TOWN, src->index, ST_TOWN, dst->index);
00291
00292 return true;
00293 }
00294
00295 bool FindSubsidyCargoDestination(CargoID cid, SourceType src_type, SourceID src);
00296
00297
00301 bool FindSubsidyTownCargoRoute()
00302 {
00303 if (!Subsidy::CanAllocateItem()) return false;
00304
00305 SourceType src_type = ST_TOWN;
00306
00307
00308 const Town *src_town = Town::GetRandom();
00309
00310 uint32 town_cargo_produced = src_town->cargo_produced;
00311
00312
00313 ClrBit(town_cargo_produced, CT_PASSENGERS);
00314
00315
00316 uint8 cargo_number = RandomRange(CountBits(town_cargo_produced));
00317 CargoID cid;
00318 FOR_EACH_SET_CARGO_ID(cid, town_cargo_produced) {
00319 if (cargo_number == 0) break;
00320 cargo_number--;
00321 }
00322
00323
00324 if (!CargoSpec::Get(cid)->IsValid()) return false;
00325
00326
00327 if (src_town->GetPercentTransported(cid) > SUBSIDY_MAX_PCT_TRANSPORTED) return false;
00328
00329 SourceID src = src_town->index;
00330
00331 return FindSubsidyCargoDestination(cid, src_type, src);
00332 }
00333
00337 bool FindSubsidyIndustryCargoRoute()
00338 {
00339 if (!Subsidy::CanAllocateItem()) return false;
00340
00341 SourceType src_type = ST_INDUSTRY;
00342
00343
00344 const Industry *src_ind = Industry::GetRandom();
00345 if (src_ind == NULL) return false;
00346
00347 uint trans, total;
00348
00349 CargoID cid;
00350
00351
00352 if (src_ind->produced_cargo[1] != CT_INVALID && HasBit(Random(), 0)) {
00353 cid = src_ind->produced_cargo[1];
00354 trans = src_ind->last_month_pct_transported[1];
00355 total = src_ind->last_month_production[1];
00356 } else {
00357 cid = src_ind->produced_cargo[0];
00358 trans = src_ind->last_month_pct_transported[0];
00359 total = src_ind->last_month_production[0];
00360 }
00361
00362
00363
00364 if (total == 0 || trans > SUBSIDY_MAX_PCT_TRANSPORTED || cid == CT_INVALID) return false;
00365
00366 SourceID src = src_ind->index;
00367
00368 return FindSubsidyCargoDestination(cid, src_type, src);
00369 }
00370
00377 bool FindSubsidyCargoDestination(CargoID cid, SourceType src_type, SourceID src)
00378 {
00379
00380 SourceType dst_type = (HasBit(_town_cargoes_accepted, cid) && Chance16(1, 2)) ? ST_TOWN : ST_INDUSTRY;
00381
00382 SourceID dst;
00383 switch (dst_type) {
00384 case ST_TOWN: {
00385
00386 const Town *dst_town = Town::GetRandom();
00387
00388
00389 if (!HasBit(dst_town->cargo_accepted_total, cid)) return false;
00390
00391 dst = dst_town->index;
00392 break;
00393 }
00394
00395 case ST_INDUSTRY: {
00396
00397 const Industry *dst_ind = Industry::GetRandom();
00398
00399
00400 if (dst_ind == NULL ||
00401 (cid != dst_ind->accepts_cargo[0] &&
00402 cid != dst_ind->accepts_cargo[1] &&
00403 cid != dst_ind->accepts_cargo[2])) {
00404 return false;
00405 }
00406
00407 dst = dst_ind->index;
00408 break;
00409 }
00410
00411 default: NOT_REACHED();
00412 }
00413
00414
00415 if (src_type == dst_type && src == dst) return false;
00416
00417
00418 if (!CheckSubsidyDistance(src_type, src, dst_type, dst)) return false;
00419
00420
00421 if (CheckSubsidyDuplicate(cid, src_type, src, dst_type, dst)) return false;
00422
00423 CreateSubsidy(cid, src_type, src, dst_type, dst);
00424
00425 return true;
00426 }
00427
00428 void SubsidyMonthlyLoop()
00429 {
00430 bool modified = false;
00431
00432 Subsidy *s;
00433 FOR_ALL_SUBSIDIES(s) {
00434 if (--s->remaining == 0) {
00435 if (!s->IsAwarded()) {
00436 Pair reftype = SetupSubsidyDecodeParam(s, true);
00437 AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
00438 AI::BroadcastNewEvent(new ScriptEventSubsidyOfferExpired(s->index));
00439 Game::NewEvent(new ScriptEventSubsidyOfferExpired(s->index));
00440 } else {
00441 if (s->awarded == _local_company) {
00442 Pair reftype = SetupSubsidyDecodeParam(s, true);
00443 AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
00444 }
00445 AI::BroadcastNewEvent(new ScriptEventSubsidyExpired(s->index));
00446 Game::NewEvent(new ScriptEventSubsidyExpired(s->index));
00447 }
00448 delete s;
00449 modified = true;
00450 }
00451 }
00452
00453 if (modified) RebuildSubsidisedSourceAndDestinationCache();
00454
00455 bool passenger_subsidy = false;
00456 bool town_subsidy = false;
00457 bool industry_subsidy = false;
00458
00459 int random_chance = RandomRange(16);
00460
00461 if (random_chance < 2) {
00462
00463 int n = 1000;
00464
00465 do {
00466 passenger_subsidy = FindSubsidyPassengerRoute();
00467 } while (!passenger_subsidy && n--);
00468 } else if (random_chance == 2) {
00469
00470 int n = 1000;
00471
00472 do {
00473 town_subsidy = FindSubsidyTownCargoRoute();
00474 } while (!town_subsidy && n--);
00475 } else if (random_chance == 3) {
00476
00477 int n = 1000;
00478
00479 do {
00480 industry_subsidy = FindSubsidyTownCargoRoute();
00481 } while (!industry_subsidy && n--);
00482 }
00483
00484 modified |= passenger_subsidy || town_subsidy || industry_subsidy;
00485
00486 if (modified) InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
00487 }
00488
00498 bool CheckSubsidised(CargoID cargo_type, CompanyID company, SourceType src_type, SourceID src, const Station *st)
00499 {
00500
00501 if (src == INVALID_SOURCE) return false;
00502 switch (src_type) {
00503 case ST_INDUSTRY:
00504 if (!(Industry::Get(src)->part_of_subsidy & POS_SRC)) return false;
00505 break;
00506 case ST_TOWN:
00507 if (!( Town::Get(src)->part_of_subsidy & POS_SRC)) return false;
00508 break;
00509 default: return false;
00510 }
00511
00512
00513
00514 SmallVector<const Town *, 2> towns_near;
00515 if (!st->rect.IsEmpty()) {
00516 Subsidy *s;
00517 FOR_ALL_SUBSIDIES(s) {
00518
00519 if (s->dst_type != ST_TOWN) continue;
00520 if (s->cargo_type != cargo_type || s->src_type != src_type || s->src != src) continue;
00521 if (s->IsAwarded() && s->awarded != company) continue;
00522
00523 Rect rect = st->GetCatchmentRect();
00524
00525 for (int y = rect.top; y <= rect.bottom; y++) {
00526 for (int x = rect.left; x <= rect.right; x++) {
00527 TileIndex tile = TileXY(x, y);
00528 if (!IsTileType(tile, MP_HOUSE)) continue;
00529 const Town *t = Town::GetByTile(tile);
00530 if (t->part_of_subsidy & POS_DST) towns_near.Include(t);
00531 }
00532 }
00533 break;
00534 }
00535 }
00536
00537 bool subsidised = false;
00538
00539
00540
00541 Subsidy *s;
00542 FOR_ALL_SUBSIDIES(s) {
00543 if (s->cargo_type == cargo_type && s->src_type == src_type && s->src == src && (!s->IsAwarded() || s->awarded == company)) {
00544 switch (s->dst_type) {
00545 case ST_INDUSTRY:
00546 for (const Industry * const *ip = st->industries_near.Begin(); ip != st->industries_near.End(); ip++) {
00547 if (s->dst == (*ip)->index) {
00548 assert((*ip)->part_of_subsidy & POS_DST);
00549 subsidised = true;
00550 if (!s->IsAwarded()) s->AwardTo(company);
00551 }
00552 }
00553 break;
00554 case ST_TOWN:
00555 for (const Town * const *tp = towns_near.Begin(); tp != towns_near.End(); tp++) {
00556 if (s->dst == (*tp)->index) {
00557 assert((*tp)->part_of_subsidy & POS_DST);
00558 subsidised = true;
00559 if (!s->IsAwarded()) s->AwardTo(company);
00560 }
00561 }
00562 break;
00563 default:
00564 NOT_REACHED();
00565 }
00566 }
00567 }
00568
00569 return subsidised;
00570 }