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