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