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 AIEventSubsidyAwarded(this->index));
00061
00062 InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
00063 }
00064
00068 void InitializeSubsidies()
00069 {
00070 _subsidy_pool.CleanPool();
00071 }
00072
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
00129 void RebuildSubsidisedSourceAndDestinationCache()
00130 {
00131 Town *t;
00132 FOR_ALL_TOWNS(t) t->part_of_subsidy = POS_NONE;
00133
00134 Industry *i;
00135 FOR_ALL_INDUSTRIES(i) i->part_of_subsidy = POS_NONE;
00136
00137 const Subsidy *s;
00138 FOR_ALL_SUBSIDIES(s) {
00139 SetPartOfSubsidyFlag(s->src_type, s->src, POS_SRC);
00140 SetPartOfSubsidyFlag(s->dst_type, s->dst, POS_DST);
00141 }
00142 }
00143
00144 void DeleteSubsidyWith(SourceType type, SourceID index)
00145 {
00146 bool dirty = false;
00147
00148 Subsidy *s;
00149 FOR_ALL_SUBSIDIES(s) {
00150 if ((s->src_type == type && s->src == index) || (s->dst_type == type && s->dst == index)) {
00151 delete s;
00152 dirty = true;
00153 }
00154 }
00155
00156 if (dirty) {
00157 InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
00158 RebuildSubsidisedSourceAndDestinationCache();
00159 }
00160 }
00161
00162 static bool CheckSubsidyDuplicate(CargoID cargo, SourceType src_type, SourceID src, SourceType dst_type, SourceID dst)
00163 {
00164 const Subsidy *s;
00165 FOR_ALL_SUBSIDIES(s) {
00166 if (s->cargo_type == cargo &&
00167 s->src_type == src_type && s->src == src &&
00168 s->dst_type == dst_type && s->dst == dst) {
00169 return true;
00170 }
00171 }
00172 return false;
00173 }
00174
00175 static Subsidy *FindSubsidyPassengerRoute()
00176 {
00177 assert(Subsidy::CanAllocateItem());
00178
00179 const Town *src = Town::GetRandom();
00180 if (src->population < SUBSIDY_PAX_MIN_POPULATION ||
00181 src->pct_pass_transported > SUBSIDY_MAX_PCT_TRANSPORTED) {
00182 return NULL;
00183 }
00184
00185 const Town *dst = Town::GetRandom();
00186 if (dst->population < SUBSIDY_PAX_MIN_POPULATION || src == dst) {
00187 return NULL;
00188 }
00189
00190 if (DistanceManhattan(src->xy, dst->xy) > SUBSIDY_MAX_DISTANCE) return NULL;
00191 if (CheckSubsidyDuplicate(CT_PASSENGERS, ST_TOWN, src->index, ST_TOWN, dst->index)) return NULL;
00192
00193 Subsidy *s = new Subsidy();
00194 s->cargo_type = CT_PASSENGERS;
00195 s->src_type = s->dst_type = ST_TOWN;
00196 s->src = src->index;
00197 s->dst = dst->index;
00198
00199 return s;
00200 }
00201
00202 static Subsidy *FindSubsidyCargoRoute()
00203 {
00204 assert(Subsidy::CanAllocateItem());
00205
00206 const Industry *i = Industry::GetRandom();
00207 if (i == NULL) return NULL;
00208
00209 CargoID cargo;
00210 uint trans, total;
00211
00212
00213 if (i->produced_cargo[1] != CT_INVALID && HasBit(Random(), 0)) {
00214 cargo = i->produced_cargo[1];
00215 trans = i->last_month_pct_transported[1];
00216 total = i->last_month_production[1];
00217 } else {
00218 cargo = i->produced_cargo[0];
00219 trans = i->last_month_pct_transported[0];
00220 total = i->last_month_production[0];
00221 }
00222
00223
00224
00225 if (total == 0 || trans > SUBSIDY_MAX_PCT_TRANSPORTED || cargo == CT_INVALID) return NULL;
00226
00227
00228 const CargoSpec *cs = CargoSpec::Get(cargo);
00229 if (cs->town_effect == TE_PASSENGERS) return NULL;
00230
00231 SourceType dst_type;
00232 SourceID dst;
00233
00234 if (cs->town_effect == TE_GOODS || cs->town_effect == TE_FOOD) {
00235
00236 dst_type = ST_TOWN;
00237 const Town *t = Town::GetRandom();
00238
00239
00240 if (t->population < SUBSIDY_CARGO_MIN_POPULATION) return NULL;
00241
00242 if (DistanceManhattan(i->location.tile, t->xy) > SUBSIDY_MAX_DISTANCE) return NULL;
00243
00244 dst = t->index;
00245 } else {
00246
00247 dst_type = ST_INDUSTRY;
00248 const Industry *i2 = Industry::GetRandom();
00249
00250
00251 if (i2 == NULL || i == i2 ||
00252 (cargo != i2->accepts_cargo[0] &&
00253 cargo != i2->accepts_cargo[1] &&
00254 cargo != i2->accepts_cargo[2])) {
00255 return NULL;
00256 }
00257
00258 if (DistanceManhattan(i->location.tile, i2->location.tile) > SUBSIDY_MAX_DISTANCE) return NULL;
00259
00260 dst = i2->index;
00261 }
00262
00263 if (CheckSubsidyDuplicate(cargo, ST_INDUSTRY, i->index, dst_type, dst)) return NULL;
00264
00265 Subsidy *s = new Subsidy();
00266 s->cargo_type = cargo;
00267 s->src_type = ST_INDUSTRY;
00268 s->src = i->index;
00269 s->dst_type = dst_type;
00270 s->dst = dst;
00271
00272 return s;
00273 }
00274
00275 void SubsidyMonthlyLoop()
00276 {
00277 bool modified = false;
00278
00279 Subsidy *s;
00280 FOR_ALL_SUBSIDIES(s) {
00281 if (--s->remaining == 0) {
00282 if (!s->IsAwarded()) {
00283 Pair reftype = SetupSubsidyDecodeParam(s, true);
00284 AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
00285 AI::BroadcastNewEvent(new AIEventSubsidyOfferExpired(s->index));
00286 } else {
00287 if (s->awarded == _local_company) {
00288 Pair reftype = SetupSubsidyDecodeParam(s, true);
00289 AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
00290 }
00291 AI::BroadcastNewEvent(new AIEventSubsidyExpired(s->index));
00292 }
00293 delete s;
00294 modified = true;
00295 }
00296 }
00297
00298 if (modified) RebuildSubsidisedSourceAndDestinationCache();
00299
00300
00301 if (Subsidy::CanAllocateItem() && Chance16(1, 4)) {
00302 uint n = 1000;
00303 do {
00304 Subsidy *s = FindSubsidyPassengerRoute();
00305 if (s == NULL) s = FindSubsidyCargoRoute();
00306 if (s != NULL) {
00307 s->remaining = SUBSIDY_OFFER_MONTHS;
00308 s->awarded = INVALID_COMPANY;
00309 Pair reftype = SetupSubsidyDecodeParam(s, false);
00310 AddNewsItem(STR_NEWS_SERVICE_SUBSIDY_OFFERED, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
00311 SetPartOfSubsidyFlag(s->src_type, s->src, POS_SRC);
00312 SetPartOfSubsidyFlag(s->dst_type, s->dst, POS_DST);
00313 AI::BroadcastNewEvent(new AIEventSubsidyOffer(s->index));
00314 modified = true;
00315 break;
00316 }
00317 } while (n--);
00318 }
00319
00320 if (modified) InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
00321 }
00322
00332 bool CheckSubsidised(CargoID cargo_type, CompanyID company, SourceType src_type, SourceID src, const Station *st)
00333 {
00334
00335 if (src == INVALID_SOURCE) return false;
00336 switch (src_type) {
00337 case ST_INDUSTRY:
00338 if (!(Industry::Get(src)->part_of_subsidy & POS_SRC)) return false;
00339 break;
00340 case ST_TOWN:
00341 if (!( Town::Get(src)->part_of_subsidy & POS_SRC)) return false;
00342 break;
00343 default: return false;
00344 }
00345
00346
00347
00348 SmallVector<const Town *, 2> towns_near;
00349 if (!st->rect.IsEmpty()) {
00350 Subsidy *s;
00351 FOR_ALL_SUBSIDIES(s) {
00352
00353 if (s->dst_type != ST_TOWN) continue;
00354 if (s->cargo_type != cargo_type || s->src_type != src_type || s->src != src) continue;
00355 if (s->IsAwarded() && s->awarded != company) continue;
00356
00357 Rect rect = st->GetCatchmentRect();
00358
00359 for (int y = rect.top; y <= rect.bottom; y++) {
00360 for (int x = rect.left; x <= rect.right; x++) {
00361 TileIndex tile = TileXY(x, y);
00362 if (!IsTileType(tile, MP_HOUSE)) continue;
00363 const Town *t = Town::GetByTile(tile);
00364 if (t->part_of_subsidy & POS_DST) towns_near.Include(t);
00365 }
00366 }
00367 break;
00368 }
00369 }
00370
00371 bool subsidised = false;
00372
00373
00374
00375 Subsidy *s;
00376 FOR_ALL_SUBSIDIES(s) {
00377 if (s->cargo_type == cargo_type && s->src_type == src_type && s->src == src && (!s->IsAwarded() || s->awarded == company)) {
00378 switch (s->dst_type) {
00379 case ST_INDUSTRY:
00380 for (const Industry * const *ip = st->industries_near.Begin(); ip != st->industries_near.End(); ip++) {
00381 if (s->dst == (*ip)->index) {
00382 assert((*ip)->part_of_subsidy & POS_DST);
00383 subsidised = true;
00384 if (!s->IsAwarded()) s->AwardTo(company);
00385 }
00386 }
00387 break;
00388 case ST_TOWN:
00389 for (const Town * const *tp = towns_near.Begin(); tp != towns_near.End(); tp++) {
00390 if (s->dst == (*tp)->index) {
00391 assert((*tp)->part_of_subsidy & POS_DST);
00392 subsidised = true;
00393 if (!s->IsAwarded()) s->AwardTo(company);
00394 }
00395 }
00396 break;
00397 default:
00398 NOT_REACHED();
00399 }
00400 }
00401 }
00402
00403 return subsidised;
00404 }