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
00027 #include "table/strings.h"
00028
00029 SubsidyPool _subsidy_pool("Subsidy");
00030 INSTANTIATE_POOL_METHODS(Subsidy)
00031
00032
00036 void Subsidy::AwardTo(CompanyID company)
00037 {
00038 assert(!this->IsAwarded());
00039
00040 this->awarded = company;
00041 this->remaining = SUBSIDY_CONTRACT_MONTHS;
00042
00043 char company_name[MAX_LENGTH_COMPANY_NAME_CHARS * MAX_CHAR_LENGTH];
00044 SetDParam(0, company);
00045 GetString(company_name, STR_COMPANY_NAME, lastof(company_name));
00046
00047 char *cn = strdup(company_name);
00048
00049
00050 Pair reftype = SetupSubsidyDecodeParam(this, false);
00051 InjectDParam(1);
00052
00053 SetDParamStr(0, cn);
00054 AddNewsItem(
00055 STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF + _settings_game.difficulty.subsidy_multiplier,
00056 NS_SUBSIDIES,
00057 (NewsReferenceType)reftype.a, this->src, (NewsReferenceType)reftype.b, this->dst,
00058 cn
00059 );
00060 AI::BroadcastNewEvent(new ScriptEventSubsidyAwarded(this->index));
00061
00062 InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
00063 }
00064
00065 Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode)
00066 {
00067 NewsReferenceType reftype1 = NR_NONE;
00068 NewsReferenceType reftype2 = NR_NONE;
00069
00070
00071 const CargoSpec *cs = CargoSpec::Get(s->cargo_type);
00072 SetDParam(0, mode ? cs->name : cs->name_single);
00073
00074 switch (s->src_type) {
00075 case ST_INDUSTRY:
00076 reftype1 = NR_INDUSTRY;
00077 SetDParam(1, STR_INDUSTRY_NAME);
00078 break;
00079 case ST_TOWN:
00080 reftype1 = NR_TOWN;
00081 SetDParam(1, STR_TOWN_NAME);
00082 break;
00083 default: NOT_REACHED();
00084 }
00085 SetDParam(2, s->src);
00086
00087 switch (s->dst_type) {
00088 case ST_INDUSTRY:
00089 reftype2 = NR_INDUSTRY;
00090 SetDParam(4, STR_INDUSTRY_NAME);
00091 break;
00092 case ST_TOWN:
00093 reftype2 = NR_TOWN;
00094 SetDParam(4, STR_TOWN_NAME);
00095 break;
00096 default: NOT_REACHED();
00097 }
00098 SetDParam(5, s->dst);
00099
00100 Pair p;
00101 p.a = reftype1;
00102 p.b = reftype2;
00103 return p;
00104 }
00105
00112 static inline void SetPartOfSubsidyFlag(SourceType type, SourceID index, PartOfSubsidy flag)
00113 {
00114 switch (type) {
00115 case ST_INDUSTRY: Industry::Get(index)->part_of_subsidy |= flag; return;
00116 case ST_TOWN: Town::Get(index)->part_of_subsidy |= flag; return;
00117 default: NOT_REACHED();
00118 }
00119 }
00120
00121 void RebuildSubsidisedSourceAndDestinationCache()
00122 {
00123 Town *t;
00124 FOR_ALL_TOWNS(t) t->part_of_subsidy = POS_NONE;
00125
00126 Industry *i;
00127 FOR_ALL_INDUSTRIES(i) i->part_of_subsidy = POS_NONE;
00128
00129 const Subsidy *s;
00130 FOR_ALL_SUBSIDIES(s) {
00131 SetPartOfSubsidyFlag(s->src_type, s->src, POS_SRC);
00132 SetPartOfSubsidyFlag(s->dst_type, s->dst, POS_DST);
00133 }
00134 }
00135
00136 void DeleteSubsidyWith(SourceType type, SourceID index)
00137 {
00138 bool dirty = false;
00139
00140 Subsidy *s;
00141 FOR_ALL_SUBSIDIES(s) {
00142 if ((s->src_type == type && s->src == index) || (s->dst_type == type && s->dst == index)) {
00143 delete s;
00144 dirty = true;
00145 }
00146 }
00147
00148 if (dirty) {
00149 InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
00150 RebuildSubsidisedSourceAndDestinationCache();
00151 }
00152 }
00153
00154 static bool CheckSubsidyDuplicate(CargoID cargo, SourceType src_type, SourceID src, SourceType dst_type, SourceID dst)
00155 {
00156 const Subsidy *s;
00157 FOR_ALL_SUBSIDIES(s) {
00158 if (s->cargo_type == cargo &&
00159 s->src_type == src_type && s->src == src &&
00160 s->dst_type == dst_type && s->dst == dst) {
00161 return true;
00162 }
00163 }
00164 return false;
00165 }
00166
00174 static bool CheckSubsidyDistance(SourceType src_type, SourceID src, SourceType dst_type, SourceID dst)
00175 {
00176 TileIndex tile_src = (src_type == ST_TOWN) ? Town::Get(src)->xy : Industry::Get(src)->location.tile;
00177 TileIndex tile_dst = (dst_type == ST_TOWN) ? Town::Get(dst)->xy : Industry::Get(dst)->location.tile;
00178
00179 return (DistanceManhattan(tile_src, tile_dst) <= SUBSIDY_MAX_DISTANCE);
00180 }
00181
00189 void CreateSubsidy(CargoID cid, SourceType src_type, SourceID src, SourceType dst_type, SourceID dst)
00190 {
00191 Subsidy *s = new Subsidy();
00192 s->cargo_type = cid;
00193 s->src_type = src_type;
00194 s->src = src;
00195 s->dst_type = dst_type;
00196 s->dst = dst;
00197 s->remaining = SUBSIDY_OFFER_MONTHS;
00198 s->awarded = INVALID_COMPANY;
00199
00200 Pair reftype = SetupSubsidyDecodeParam(s, false);
00201 AddNewsItem(STR_NEWS_SERVICE_SUBSIDY_OFFERED, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
00202 SetPartOfSubsidyFlag(s->src_type, s->src, POS_SRC);
00203 SetPartOfSubsidyFlag(s->dst_type, s->dst, POS_DST);
00204 AI::BroadcastNewEvent(new ScriptEventSubsidyOffer(s->index));
00205 }
00206
00207
00211 bool FindSubsidyPassengerRoute()
00212 {
00213 if (!Subsidy::CanAllocateItem()) return false;
00214
00215 const Town *src = Town::GetRandom();
00216 if (src->population < SUBSIDY_PAX_MIN_POPULATION ||
00217 src->GetPercentTransported(CT_PASSENGERS) > SUBSIDY_MAX_PCT_TRANSPORTED) {
00218 return false;
00219 }
00220
00221 const Town *dst = Town::GetRandom();
00222 if (dst->population < SUBSIDY_PAX_MIN_POPULATION || src == dst) {
00223 return false;
00224 }
00225
00226 if (DistanceManhattan(src->xy, dst->xy) > SUBSIDY_MAX_DISTANCE) return false;
00227 if (CheckSubsidyDuplicate(CT_PASSENGERS, ST_TOWN, src->index, ST_TOWN, dst->index)) return false;
00228
00229 CreateSubsidy(CT_PASSENGERS, ST_TOWN, src->index, ST_TOWN, dst->index);
00230
00231 return true;
00232 }
00233
00234 bool FindSubsidyCargoDestination(CargoID cid, SourceType src_type, SourceID src);
00235
00236
00240 bool FindSubsidyTownCargoRoute()
00241 {
00242 if (!Subsidy::CanAllocateItem()) return false;
00243
00244 SourceType src_type = ST_TOWN;
00245
00246
00247 const Town *src_town = Town::GetRandom();
00248
00249 uint32 town_cargo_produced = src_town->cargo_produced;
00250
00251
00252 ClrBit(town_cargo_produced, CT_PASSENGERS);
00253
00254
00255 uint8 cargo_number = RandomRange(CountBits(town_cargo_produced));
00256 CargoID cid;
00257 FOR_EACH_SET_CARGO_ID(cid, town_cargo_produced) {
00258 if (cargo_number == 0) break;
00259 cargo_number--;
00260 }
00261
00262
00263 if (!CargoSpec::Get(cid)->IsValid()) return false;
00264
00265
00266 if (src_town->GetPercentTransported(cid) > SUBSIDY_MAX_PCT_TRANSPORTED) return false;
00267
00268 SourceID src = src_town->index;
00269
00270 return FindSubsidyCargoDestination(cid, src_type, src);
00271 }
00272
00276 bool FindSubsidyIndustryCargoRoute()
00277 {
00278 if (!Subsidy::CanAllocateItem()) return false;
00279
00280 SourceType src_type = ST_INDUSTRY;
00281
00282
00283 const Industry *src_ind = Industry::GetRandom();
00284 if (src_ind == NULL) return false;
00285
00286 uint trans, total;
00287
00288 CargoID cid;
00289
00290
00291 if (src_ind->produced_cargo[1] != CT_INVALID && HasBit(Random(), 0)) {
00292 cid = src_ind->produced_cargo[1];
00293 trans = src_ind->last_month_pct_transported[1];
00294 total = src_ind->last_month_production[1];
00295 } else {
00296 cid = src_ind->produced_cargo[0];
00297 trans = src_ind->last_month_pct_transported[0];
00298 total = src_ind->last_month_production[0];
00299 }
00300
00301
00302
00303 if (total == 0 || trans > SUBSIDY_MAX_PCT_TRANSPORTED || cid == CT_INVALID) return false;
00304
00305 SourceID src = src_ind->index;
00306
00307 return FindSubsidyCargoDestination(cid, src_type, src);
00308 }
00309
00316 bool FindSubsidyCargoDestination(CargoID cid, SourceType src_type, SourceID src)
00317 {
00318
00319 SourceType dst_type = (HasBit(_town_cargos_accepted, cid) && Chance16(1, 2)) ? ST_TOWN : ST_INDUSTRY;
00320
00321 SourceID dst;
00322 switch (dst_type) {
00323 case ST_TOWN: {
00324
00325 const Town *dst_town = Town::GetRandom();
00326
00327
00328 if (!HasBit(dst_town->cargo_accepted_total, cid)) return false;
00329
00330 dst = dst_town->index;
00331 break;
00332 }
00333
00334 case ST_INDUSTRY: {
00335
00336 const Industry *dst_ind = Industry::GetRandom();
00337
00338
00339 if (dst_ind == NULL ||
00340 (cid != dst_ind->accepts_cargo[0] &&
00341 cid != dst_ind->accepts_cargo[1] &&
00342 cid != dst_ind->accepts_cargo[2])) {
00343 return false;
00344 }
00345
00346 dst = dst_ind->index;
00347 break;
00348 }
00349
00350 default: NOT_REACHED();
00351 }
00352
00353
00354 if (src_type == dst_type && src == dst) return false;
00355
00356
00357 if (!CheckSubsidyDistance(src_type, src, dst_type, dst)) return false;
00358
00359
00360 if (CheckSubsidyDuplicate(cid, src_type, src, dst_type, dst)) return false;
00361
00362 CreateSubsidy(cid, src_type, src, dst_type, dst);
00363
00364 return true;
00365 }
00366
00367 void SubsidyMonthlyLoop()
00368 {
00369 bool modified = false;
00370
00371 Subsidy *s;
00372 FOR_ALL_SUBSIDIES(s) {
00373 if (--s->remaining == 0) {
00374 if (!s->IsAwarded()) {
00375 Pair reftype = SetupSubsidyDecodeParam(s, true);
00376 AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
00377 AI::BroadcastNewEvent(new ScriptEventSubsidyOfferExpired(s->index));
00378 } else {
00379 if (s->awarded == _local_company) {
00380 Pair reftype = SetupSubsidyDecodeParam(s, true);
00381 AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
00382 }
00383 AI::BroadcastNewEvent(new ScriptEventSubsidyExpired(s->index));
00384 }
00385 delete s;
00386 modified = true;
00387 }
00388 }
00389
00390 if (modified) RebuildSubsidisedSourceAndDestinationCache();
00391
00392 bool passenger_subsidy = false;
00393 bool town_subsidy = false;
00394 bool industry_subsidy = false;
00395
00396 int random_chance = RandomRange(16);
00397
00398 if (random_chance < 2) {
00399
00400 int n = 1000;
00401
00402 do {
00403 passenger_subsidy = FindSubsidyPassengerRoute();
00404 } while (!passenger_subsidy && n--);
00405 } else if (random_chance == 2) {
00406
00407 int n = 1000;
00408
00409 do {
00410 town_subsidy = FindSubsidyTownCargoRoute();
00411 } while (!town_subsidy && n--);
00412 } else if (random_chance == 3) {
00413
00414 int n = 1000;
00415
00416 do {
00417 industry_subsidy = FindSubsidyTownCargoRoute();
00418 } while (!industry_subsidy && n--);
00419 }
00420
00421 modified |= passenger_subsidy || town_subsidy || industry_subsidy;
00422
00423 if (modified) InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
00424 }
00425
00435 bool CheckSubsidised(CargoID cargo_type, CompanyID company, SourceType src_type, SourceID src, const Station *st)
00436 {
00437
00438 if (src == INVALID_SOURCE) return false;
00439 switch (src_type) {
00440 case ST_INDUSTRY:
00441 if (!(Industry::Get(src)->part_of_subsidy & POS_SRC)) return false;
00442 break;
00443 case ST_TOWN:
00444 if (!( Town::Get(src)->part_of_subsidy & POS_SRC)) return false;
00445 break;
00446 default: return false;
00447 }
00448
00449
00450
00451 SmallVector<const Town *, 2> towns_near;
00452 if (!st->rect.IsEmpty()) {
00453 Subsidy *s;
00454 FOR_ALL_SUBSIDIES(s) {
00455
00456 if (s->dst_type != ST_TOWN) continue;
00457 if (s->cargo_type != cargo_type || s->src_type != src_type || s->src != src) continue;
00458 if (s->IsAwarded() && s->awarded != company) continue;
00459
00460 Rect rect = st->GetCatchmentRect();
00461
00462 for (int y = rect.top; y <= rect.bottom; y++) {
00463 for (int x = rect.left; x <= rect.right; x++) {
00464 TileIndex tile = TileXY(x, y);
00465 if (!IsTileType(tile, MP_HOUSE)) continue;
00466 const Town *t = Town::GetByTile(tile);
00467 if (t->part_of_subsidy & POS_DST) towns_near.Include(t);
00468 }
00469 }
00470 break;
00471 }
00472 }
00473
00474 bool subsidised = false;
00475
00476
00477
00478 Subsidy *s;
00479 FOR_ALL_SUBSIDIES(s) {
00480 if (s->cargo_type == cargo_type && s->src_type == src_type && s->src == src && (!s->IsAwarded() || s->awarded == company)) {
00481 switch (s->dst_type) {
00482 case ST_INDUSTRY:
00483 for (const Industry * const *ip = st->industries_near.Begin(); ip != st->industries_near.End(); ip++) {
00484 if (s->dst == (*ip)->index) {
00485 assert((*ip)->part_of_subsidy & POS_DST);
00486 subsidised = true;
00487 if (!s->IsAwarded()) s->AwardTo(company);
00488 }
00489 }
00490 break;
00491 case ST_TOWN:
00492 for (const Town * const *tp = towns_near.Begin(); tp != towns_near.End(); tp++) {
00493 if (s->dst == (*tp)->index) {
00494 assert((*tp)->part_of_subsidy & POS_DST);
00495 subsidised = true;
00496 if (!s->IsAwarded()) s->AwardTo(company);
00497 }
00498 }
00499 break;
00500 default:
00501 NOT_REACHED();
00502 }
00503 }
00504 }
00505
00506 return subsidised;
00507 }