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