# subsidy.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
    00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    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 "strings_func.h"
    00020 #include "window_func.h"
    00021 #include "subsidy_base.h"
    00022 #include "subsidy_func.h"
    00023 #include "core/pool_func.hpp"
    00024 #include "core/random_func.hpp"
    00025 #include "game/game.hpp"
    00026 #include "command_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   /* Add a news item */
    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 ScriptEventSubsidyAwarded(this->index));
    00062   Game::NewEvent(new ScriptEventSubsidyAwarded(this->index));
    00063
    00064   InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
    00065 }
    00066
    00073 Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode)
    00074 {
    00075   NewsReferenceType reftype1 = NR_NONE;
    00076   NewsReferenceType reftype2 = NR_NONE;
    00077
    00078   /* if mode is false, use the singular form */
    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
    00130 void RebuildSubsidisedSourceAndDestinationCache()
    00131 {
    00132   Town *t;
    00133   FOR_ALL_TOWNS(t) t->part_of_subsidy = POS_NONE;
    00134
    00135   Industry *i;
    00136   FOR_ALL_INDUSTRIES(i) i->part_of_subsidy = POS_NONE;
    00137
    00138   const Subsidy *s;
    00139   FOR_ALL_SUBSIDIES(s) {
    00140     SetPartOfSubsidyFlag(s->src_type, s->src, POS_SRC);
    00141     SetPartOfSubsidyFlag(s->dst_type, s->dst, POS_DST);
    00142   }
    00143 }
    00144
    00150 void DeleteSubsidyWith(SourceType type, SourceID index)
    00151 {
    00152   bool dirty = false;
    00153
    00154   Subsidy *s;
    00155   FOR_ALL_SUBSIDIES(s) {
    00156     if ((s->src_type == type && s->src == index) || (s->dst_type == type && s->dst == index)) {
    00157       delete s;
    00158       dirty = true;
    00159     }
    00160   }
    00161
    00162   if (dirty) {
    00163     InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
    00164     RebuildSubsidisedSourceAndDestinationCache();
    00165   }
    00166 }
    00167
    00177 static bool CheckSubsidyDuplicate(CargoID cargo, SourceType src_type, SourceID src, SourceType dst_type, SourceID dst)
    00178 {
    00179   const Subsidy *s;
    00180   FOR_ALL_SUBSIDIES(s) {
    00181     if (s->cargo_type == cargo &&
    00182         s->src_type == src_type && s->src == src &&
    00183         s->dst_type == dst_type && s->dst == dst) {
    00184       return true;
    00185     }
    00186   }
    00187   return false;
    00188 }
    00189
    00198 static bool CheckSubsidyDistance(SourceType src_type, SourceID src, SourceType dst_type, SourceID dst)
    00199 {
    00200   TileIndex tile_src = (src_type == ST_TOWN) ? Town::Get(src)->xy : Industry::Get(src)->location.tile;
    00201   TileIndex tile_dst = (dst_type == ST_TOWN) ? Town::Get(dst)->xy : Industry::Get(dst)->location.tile;
    00202
    00203   return (DistanceManhattan(tile_src, tile_dst) <= SUBSIDY_MAX_DISTANCE);
    00204 }
    00205
    00214 void CreateSubsidy(CargoID cid, SourceType src_type, SourceID src, SourceType dst_type, SourceID dst)
    00215 {
    00216   Subsidy *s = new Subsidy();
    00217   s->cargo_type = cid;
    00218   s->src_type = src_type;
    00219   s->src = src;
    00220   s->dst_type = dst_type;
    00221   s->dst = dst;
    00222   s->remaining = SUBSIDY_OFFER_MONTHS;
    00223   s->awarded = INVALID_COMPANY;
    00224
    00225   Pair reftype = SetupSubsidyDecodeParam(s, false);
    00226   AddNewsItem(STR_NEWS_SERVICE_SUBSIDY_OFFERED, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
    00227   SetPartOfSubsidyFlag(s->src_type, s->src, POS_SRC);
    00228   SetPartOfSubsidyFlag(s->dst_type, s->dst, POS_DST);
    00229   AI::BroadcastNewEvent(new ScriptEventSubsidyOffer(s->index));
    00230   Game::NewEvent(new ScriptEventSubsidyOffer(s->index));
    00231
    00232   InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
    00233 }
    00234
    00249 CommandCost CmdCreateSubsidy(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00250 {
    00251   if (!Subsidy::CanAllocateItem()) return CMD_ERROR;
    00252
    00253   CargoID cid = GB(p1, 24, 8);
    00254   SourceType src_type = (SourceType)GB(p1, 0, 8);
    00255   SourceID src = GB(p1, 8, 16);
    00256   SourceType dst_type = (SourceType)GB(p2, 0, 8);
    00257   SourceID dst = GB(p2, 8, 16);
    00258
    00259   if (_current_company != OWNER_DEITY) return CMD_ERROR;
    00260
    00261   if (cid >= NUM_CARGO || !::CargoSpec::Get(cid)->IsValid()) return CMD_ERROR;
    00262
    00263   switch (src_type) {
    00264     case ST_TOWN:
    00265       if (!Town::IsValidID(src)) return CMD_ERROR;
    00266       break;
    00267     case ST_INDUSTRY:
    00268       if (!Industry::IsValidID(src)) return CMD_ERROR;
    00269       break;
    00270     default:
    00271       return CMD_ERROR;
    00272   }
    00273   switch (dst_type) {
    00274     case ST_TOWN:
    00275       if (!Town::IsValidID(dst)) return CMD_ERROR;
    00276       break;
    00277     case ST_INDUSTRY:
    00278       if (!Industry::IsValidID(dst)) return CMD_ERROR;
    00279       break;
    00280     default:
    00281       return CMD_ERROR;
    00282   }
    00283
    00284   if (flags & DC_EXEC) {
    00285     CreateSubsidy(cid, src_type, src, dst_type, dst);
    00286   }
    00287
    00288   return CommandCost();
    00289 }
    00290
    00295 bool FindSubsidyPassengerRoute()
    00296 {
    00297   if (!Subsidy::CanAllocateItem()) return false;
    00298
    00299   const Town *src = Town::GetRandom();
    00300   if (src->population < SUBSIDY_PAX_MIN_POPULATION ||
    00301       src->GetPercentTransported(CT_PASSENGERS) > SUBSIDY_MAX_PCT_TRANSPORTED) {
    00302     return false;
    00303   }
    00304
    00305   const Town *dst = Town::GetRandom();
    00306   if (dst->population < SUBSIDY_PAX_MIN_POPULATION || src == dst) {
    00307     return false;
    00308   }
    00309
    00310   if (DistanceManhattan(src->xy, dst->xy) > SUBSIDY_MAX_DISTANCE) return false;
    00311   if (CheckSubsidyDuplicate(CT_PASSENGERS, ST_TOWN, src->index, ST_TOWN, dst->index)) return false;
    00312
    00313   CreateSubsidy(CT_PASSENGERS, ST_TOWN, src->index, ST_TOWN, dst->index);
    00314
    00315   return true;
    00316 }
    00317
    00318 bool FindSubsidyCargoDestination(CargoID cid, SourceType src_type, SourceID src);
    00319
    00320
    00325 bool FindSubsidyTownCargoRoute()
    00326 {
    00327   if (!Subsidy::CanAllocateItem()) return false;
    00328
    00329   SourceType src_type = ST_TOWN;
    00330
    00331   /* Select a random town. */
    00332   const Town *src_town = Town::GetRandom();
    00333
    00334   uint32 town_cargo_produced = src_town->cargo_produced;
    00335
    00336   /* Passenger subsidies are not handled here. */
    00337   ClrBit(town_cargo_produced, CT_PASSENGERS);
    00338
    00339   /* Choose a random cargo that is produced in the town. */
    00340   uint8 cargo_number = RandomRange(CountBits(town_cargo_produced));
    00341   CargoID cid;
    00342   FOR_EACH_SET_CARGO_ID(cid, town_cargo_produced) {
    00343     if (cargo_number == 0) break;
    00344     cargo_number--;
    00345   }
    00346
    00347   /* Avoid using invalid NewGRF cargoes. */
    00348   if (!CargoSpec::Get(cid)->IsValid()) return false;
    00349
    00350   /* Quit if the percentage transported is large enough. */
    00351   if (src_town->GetPercentTransported(cid) > SUBSIDY_MAX_PCT_TRANSPORTED) return false;
    00352
    00353   SourceID src = src_town->index;
    00354
    00355   return FindSubsidyCargoDestination(cid, src_type, src);
    00356 }
    00357
    00362 bool FindSubsidyIndustryCargoRoute()
    00363 {
    00364   if (!Subsidy::CanAllocateItem()) return false;
    00365
    00366   SourceType src_type = ST_INDUSTRY;
    00367
    00368   /* Select a random industry. */
    00369   const Industry *src_ind = Industry::GetRandom();
    00370   if (src_ind == NULL) return false;
    00371
    00372   uint trans, total;
    00373
    00374   CargoID cid;
    00375
    00376   /* Randomize cargo type */
    00377   if (src_ind->produced_cargo[1] != CT_INVALID && HasBit(Random(), 0)) {
    00378     cid = src_ind->produced_cargo[1];
    00379     trans = src_ind->last_month_pct_transported[1];
    00380     total = src_ind->last_month_production[1];
    00381   } else {
    00382     cid = src_ind->produced_cargo[0];
    00383     trans = src_ind->last_month_pct_transported[0];
    00384     total = src_ind->last_month_production[0];
    00385   }
    00386
    00387   /* Quit if no production in this industry
    00388    * or if the pct transported is already large enough */
    00389   if (total == 0 || trans > SUBSIDY_MAX_PCT_TRANSPORTED || cid == CT_INVALID) return false;
    00390
    00391   SourceID src = src_ind->index;
    00392
    00393   return FindSubsidyCargoDestination(cid, src_type, src);
    00394 }
    00395
    00403 bool FindSubsidyCargoDestination(CargoID cid, SourceType src_type, SourceID src)
    00404 {
    00405   /* Choose a random destination. Only consider towns if they can accept the cargo. */
    00406   SourceType dst_type = (HasBit(_town_cargoes_accepted, cid) && Chance16(1, 2)) ? ST_TOWN : ST_INDUSTRY;
    00407
    00408   SourceID dst;
    00409   switch (dst_type) {
    00410     case ST_TOWN: {
    00411       /* Select a random town. */
    00412       const Town *dst_town = Town::GetRandom();
    00413
    00414       /* Check if the town can accept this cargo. */
    00415       if (!HasBit(dst_town->cargo_accepted_total, cid)) return false;
    00416
    00417       dst = dst_town->index;
    00418       break;
    00419     }
    00420
    00421     case ST_INDUSTRY: {
    00422       /* Select a random industry. */
    00423       const Industry *dst_ind = Industry::GetRandom();
    00424
    00425       /* The industry must accept the cargo */
    00426       if (dst_ind == NULL ||
    00427           (cid != dst_ind->accepts_cargo[0] &&
    00428            cid != dst_ind->accepts_cargo[1] &&
    00429            cid != dst_ind->accepts_cargo[2])) {
    00430         return false;
    00431       }
    00432
    00433       dst = dst_ind->index;
    00434       break;
    00435     }
    00436
    00437     default: NOT_REACHED();
    00438   }
    00439
    00440   /* Check that the source and the destination are not the same. */
    00441   if (src_type == dst_type && src == dst) return false;
    00442
    00443   /* Check distance between source and destination. */
    00444   if (!CheckSubsidyDistance(src_type, src, dst_type, dst)) return false;
    00445
    00446   /* Avoid duplicate subsidies. */
    00447   if (CheckSubsidyDuplicate(cid, src_type, src, dst_type, dst)) return false;
    00448
    00449   CreateSubsidy(cid, src_type, src, dst_type, dst);
    00450
    00451   return true;
    00452 }
    00453
    00455 void SubsidyMonthlyLoop()
    00456 {
    00457   bool modified = false;
    00458
    00459   Subsidy *s;
    00460   FOR_ALL_SUBSIDIES(s) {
    00461     if (--s->remaining == 0) {
    00462       if (!s->IsAwarded()) {
    00463         Pair reftype = SetupSubsidyDecodeParam(s, true);
    00464         AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
    00465         AI::BroadcastNewEvent(new ScriptEventSubsidyOfferExpired(s->index));
    00466         Game::NewEvent(new ScriptEventSubsidyOfferExpired(s->index));
    00467       } else {
    00468         if (s->awarded == _local_company) {
    00469           Pair reftype = SetupSubsidyDecodeParam(s, true);
    00470           AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
    00471         }
    00472         AI::BroadcastNewEvent(new ScriptEventSubsidyExpired(s->index));
    00473         Game::NewEvent(new ScriptEventSubsidyExpired(s->index));
    00474       }
    00475       delete s;
    00476       modified = true;
    00477     }
    00478   }
    00479
    00480   if (modified) RebuildSubsidisedSourceAndDestinationCache();
    00481
    00482   bool passenger_subsidy = false;
    00483   bool town_subsidy = false;
    00484   bool industry_subsidy = false;
    00485
    00486   int random_chance = RandomRange(16);
    00487
    00488   if (random_chance < 2) {
    00489     /* There is a 1/8 chance each month of generating a passenger subsidy. */
    00490     int n = 1000;
    00491
    00492     do {
    00493       passenger_subsidy = FindSubsidyPassengerRoute();
    00494     } while (!passenger_subsidy && n--);
    00495   } else if (random_chance == 2) {
    00496     /* Cargo subsidies with a town as a source have a 1/16 chance. */
    00497     int n = 1000;
    00498
    00499     do {
    00500       town_subsidy = FindSubsidyTownCargoRoute();
    00501     } while (!town_subsidy && n--);
    00502   } else if (random_chance == 3) {
    00503     /* Cargo subsidies with an industry as a source have a 1/16 chance. */
    00504     int n = 1000;
    00505
    00506     do {
    00507       industry_subsidy = FindSubsidyIndustryCargoRoute();
    00508     } while (!industry_subsidy && n--);
    00509   }
    00510
    00511   modified |= passenger_subsidy || town_subsidy || industry_subsidy;
    00512
    00513   if (modified) InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
    00514 }
    00515
    00525 bool CheckSubsidised(CargoID cargo_type, CompanyID company, SourceType src_type, SourceID src, const Station *st)
    00526 {
    00527   /* If the source isn't subsidised, don't continue */
    00528   if (src == INVALID_SOURCE) return false;
    00529   switch (src_type) {
    00530     case ST_INDUSTRY:
    00531       if (!(Industry::Get(src)->part_of_subsidy & POS_SRC)) return false;
    00532       break;
    00533     case ST_TOWN:
    00534       if (!(    Town::Get(src)->part_of_subsidy & POS_SRC)) return false;
    00535       break;
    00536     default: return false;
    00537   }
    00538
    00539   /* Remember all towns near this station (at least one house in its catchment radius)
    00540    * which are destination of subsidised path. Do that only if needed */
    00541   SmallVector<const Town *, 2> towns_near;
    00542   if (!st->rect.IsEmpty()) {
    00543     Subsidy *s;
    00544     FOR_ALL_SUBSIDIES(s) {
    00545       /* Don't create the cache if there is no applicable subsidy with town as destination */
    00546       if (s->dst_type != ST_TOWN) continue;
    00547       if (s->cargo_type != cargo_type || s->src_type != src_type || s->src != src) continue;
    00548       if (s->IsAwarded() && s->awarded != company) continue;
    00549
    00550       Rect rect = st->GetCatchmentRect();
    00551
    00552       for (int y = rect.top; y <= rect.bottom; y++) {
    00553         for (int x = rect.left; x <= rect.right; x++) {
    00554           TileIndex tile = TileXY(x, y);
    00555           if (!IsTileType(tile, MP_HOUSE)) continue;
    00556           const Town *t = Town::GetByTile(tile);
    00557           if (t->part_of_subsidy & POS_DST) towns_near.Include(t);
    00558         }
    00559       }
    00560       break;
    00561     }
    00562   }
    00563
    00564   bool subsidised = false;
    00565
    00566   /* Check if there's a (new) subsidy that applies. There can be more subsidies triggered by this delivery!
    00567    * Think about the case that subsidies are A->B and A->C and station has both B and C in its catchment area */
    00568   Subsidy *s;
    00569   FOR_ALL_SUBSIDIES(s) {
    00570     if (s->cargo_type == cargo_type && s->src_type == src_type && s->src == src && (!s->IsAwarded() || s->awarded == company)) {
    00571       switch (s->dst_type) {
    00572         case ST_INDUSTRY:
    00573           for (const Industry * const *ip = st->industries_near.Begin(); ip != st->industries_near.End(); ip++) {
    00574             if (s->dst == (*ip)->index) {
    00575               assert((*ip)->part_of_subsidy & POS_DST);
    00576               subsidised = true;
    00577               if (!s->IsAwarded()) s->AwardTo(company);
    00578             }
    00579           }
    00580           break;
    00581         case ST_TOWN:
    00582           for (const Town * const *tp = towns_near.Begin(); tp != towns_near.End(); tp++) {
    00583             if (s->dst == (*tp)->index) {
    00584               assert((*tp)->part_of_subsidy & POS_DST);
    00585               subsidised = true;
    00586               if (!s->IsAwarded()) s->AwardTo(company);
    00587             }
    00588           }
    00589           break;
    00590         default:
    00591           NOT_REACHED();
    00592       }
    00593     }
    00594   }
    00595
    00596   return subsidised;
    00597 }