subsidy.cpp

Go to the documentation of this file.
00001 /* $Id: subsidy.cpp 23628 2011-12-19 21:01:12Z truebrain $ */
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 "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 "game/game.hpp"
00027 #include "command_func.h"
00028 
00029 #include "table/strings.h"
00030 
00031 SubsidyPool _subsidy_pool("Subsidy");
00032 INSTANTIATE_POOL_METHODS(Subsidy)
00033 
00034 
00038 void Subsidy::AwardTo(CompanyID company)
00039 {
00040   assert(!this->IsAwarded());
00041 
00042   this->awarded = company;
00043   this->remaining = SUBSIDY_CONTRACT_MONTHS;
00044 
00045   char company_name[MAX_LENGTH_COMPANY_NAME_CHARS * MAX_CHAR_LENGTH];
00046   SetDParam(0, company);
00047   GetString(company_name, STR_COMPANY_NAME, lastof(company_name));
00048 
00049   char *cn = strdup(company_name);
00050 
00051   /* Add a news item */
00052   Pair reftype = SetupSubsidyDecodeParam(this, false);
00053   InjectDParam(1);
00054 
00055   SetDParamStr(0, cn);
00056   AddNewsItem(
00057     STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF + _settings_game.difficulty.subsidy_multiplier,
00058     NS_SUBSIDIES,
00059     (NewsReferenceType)reftype.a, this->src, (NewsReferenceType)reftype.b, this->dst,
00060     cn
00061   );
00062   AI::BroadcastNewEvent(new ScriptEventSubsidyAwarded(this->index));
00063   Game::NewEvent(new ScriptEventSubsidyAwarded(this->index));
00064 
00065   InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
00066 }
00067 
00068 Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode)
00069 {
00070   NewsReferenceType reftype1 = NR_NONE;
00071   NewsReferenceType reftype2 = NR_NONE;
00072 
00073   /* if mode is false, use the singular form */
00074   const CargoSpec *cs = CargoSpec::Get(s->cargo_type);
00075   SetDParam(0, mode ? cs->name : cs->name_single);
00076 
00077   switch (s->src_type) {
00078     case ST_INDUSTRY:
00079       reftype1 = NR_INDUSTRY;
00080       SetDParam(1, STR_INDUSTRY_NAME);
00081       break;
00082     case ST_TOWN:
00083       reftype1 = NR_TOWN;
00084       SetDParam(1, STR_TOWN_NAME);
00085       break;
00086     default: NOT_REACHED();
00087   }
00088   SetDParam(2, s->src);
00089 
00090   switch (s->dst_type) {
00091     case ST_INDUSTRY:
00092       reftype2 = NR_INDUSTRY;
00093       SetDParam(4, STR_INDUSTRY_NAME);
00094       break;
00095     case ST_TOWN:
00096       reftype2 = NR_TOWN;
00097       SetDParam(4, STR_TOWN_NAME);
00098       break;
00099     default: NOT_REACHED();
00100   }
00101   SetDParam(5, s->dst);
00102 
00103   Pair p;
00104   p.a = reftype1;
00105   p.b = reftype2;
00106   return p;
00107 }
00108 
00115 static inline void SetPartOfSubsidyFlag(SourceType type, SourceID index, PartOfSubsidy flag)
00116 {
00117   switch (type) {
00118     case ST_INDUSTRY: Industry::Get(index)->part_of_subsidy |= flag; return;
00119     case ST_TOWN:         Town::Get(index)->part_of_subsidy |= flag; return;
00120     default: NOT_REACHED();
00121   }
00122 }
00123 
00124 void RebuildSubsidisedSourceAndDestinationCache()
00125 {
00126   Town *t;
00127   FOR_ALL_TOWNS(t) t->part_of_subsidy = POS_NONE;
00128 
00129   Industry *i;
00130   FOR_ALL_INDUSTRIES(i) i->part_of_subsidy = POS_NONE;
00131 
00132   const Subsidy *s;
00133   FOR_ALL_SUBSIDIES(s) {
00134     SetPartOfSubsidyFlag(s->src_type, s->src, POS_SRC);
00135     SetPartOfSubsidyFlag(s->dst_type, s->dst, POS_DST);
00136   }
00137 }
00138 
00139 void DeleteSubsidyWith(SourceType type, SourceID index)
00140 {
00141   bool dirty = false;
00142 
00143   Subsidy *s;
00144   FOR_ALL_SUBSIDIES(s) {
00145     if ((s->src_type == type && s->src == index) || (s->dst_type == type && s->dst == index)) {
00146       delete s;
00147       dirty = true;
00148     }
00149   }
00150 
00151   if (dirty) {
00152     InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
00153     RebuildSubsidisedSourceAndDestinationCache();
00154   }
00155 }
00156 
00157 static bool CheckSubsidyDuplicate(CargoID cargo, SourceType src_type, SourceID src, SourceType dst_type, SourceID dst)
00158 {
00159   const Subsidy *s;
00160   FOR_ALL_SUBSIDIES(s) {
00161     if (s->cargo_type == cargo &&
00162         s->src_type == src_type && s->src == src &&
00163         s->dst_type == dst_type && s->dst == dst) {
00164       return true;
00165     }
00166   }
00167   return false;
00168 }
00169 
00177 static bool CheckSubsidyDistance(SourceType src_type, SourceID src, SourceType dst_type, SourceID dst)
00178 {
00179   TileIndex tile_src = (src_type == ST_TOWN) ? Town::Get(src)->xy : Industry::Get(src)->location.tile;
00180   TileIndex tile_dst = (dst_type == ST_TOWN) ? Town::Get(dst)->xy : Industry::Get(dst)->location.tile;
00181 
00182   return (DistanceManhattan(tile_src, tile_dst) <= SUBSIDY_MAX_DISTANCE);
00183 }
00184 
00192 void CreateSubsidy(CargoID cid, SourceType src_type, SourceID src, SourceType dst_type, SourceID dst)
00193 {
00194   Subsidy *s = new Subsidy();
00195   s->cargo_type = cid;
00196   s->src_type = src_type;
00197   s->src = src;
00198   s->dst_type = dst_type;
00199   s->dst = dst;
00200   s->remaining = SUBSIDY_OFFER_MONTHS;
00201   s->awarded = INVALID_COMPANY;
00202 
00203   Pair reftype = SetupSubsidyDecodeParam(s, false);
00204   AddNewsItem(STR_NEWS_SERVICE_SUBSIDY_OFFERED, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
00205   SetPartOfSubsidyFlag(s->src_type, s->src, POS_SRC);
00206   SetPartOfSubsidyFlag(s->dst_type, s->dst, POS_DST);
00207   AI::BroadcastNewEvent(new ScriptEventSubsidyOffer(s->index));
00208   Game::NewEvent(new ScriptEventSubsidyOffer(s->index));
00209 
00210   InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
00211 }
00212 
00227 CommandCost CmdCreateSubsidy(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00228 {
00229   if (!Subsidy::CanAllocateItem()) return CMD_ERROR;
00230 
00231   CargoID cid = GB(p1, 24, 8);
00232   SourceType src_type = (SourceType)GB(p1, 0, 8);
00233   SourceID src = GB(p1, 8, 16);
00234   SourceType dst_type = (SourceType)GB(p2, 0, 8);
00235   SourceID dst = GB(p2, 8, 16);
00236 
00237   if (_current_company != OWNER_DEITY) return CMD_ERROR;
00238 
00239   if (cid >= NUM_CARGO || !::CargoSpec::Get(cid)->IsValid()) return CMD_ERROR;
00240 
00241   switch (src_type) {
00242     case ST_TOWN:
00243       if (!Town::IsValidID(src)) return CMD_ERROR;
00244       break;
00245     case ST_INDUSTRY:
00246       if (!Industry::IsValidID(src)) return CMD_ERROR;
00247       break;
00248     default:
00249       return CMD_ERROR;
00250   }
00251   switch (dst_type) {
00252     case ST_TOWN:
00253       if (!Town::IsValidID(dst)) return CMD_ERROR;
00254       break;
00255     case ST_INDUSTRY:
00256       if (!Industry::IsValidID(dst)) return CMD_ERROR;
00257       break;
00258     default:
00259       return CMD_ERROR;
00260   }
00261 
00262   if (flags & DC_EXEC) {
00263     CreateSubsidy(cid, src_type, src, dst_type, dst);
00264   }
00265 
00266   return CommandCost();
00267 }
00268 
00272 bool FindSubsidyPassengerRoute()
00273 {
00274   if (!Subsidy::CanAllocateItem()) return false;
00275 
00276   const Town *src = Town::GetRandom();
00277   if (src->population < SUBSIDY_PAX_MIN_POPULATION ||
00278       src->GetPercentTransported(CT_PASSENGERS) > SUBSIDY_MAX_PCT_TRANSPORTED) {
00279     return false;
00280   }
00281 
00282   const Town *dst = Town::GetRandom();
00283   if (dst->population < SUBSIDY_PAX_MIN_POPULATION || src == dst) {
00284     return false;
00285   }
00286 
00287   if (DistanceManhattan(src->xy, dst->xy) > SUBSIDY_MAX_DISTANCE) return false;
00288   if (CheckSubsidyDuplicate(CT_PASSENGERS, ST_TOWN, src->index, ST_TOWN, dst->index)) return false;
00289 
00290   CreateSubsidy(CT_PASSENGERS, ST_TOWN, src->index, ST_TOWN, dst->index);
00291 
00292   return true;
00293 }
00294 
00295 bool FindSubsidyCargoDestination(CargoID cid, SourceType src_type, SourceID src);
00296 
00297 
00301 bool FindSubsidyTownCargoRoute()
00302 {
00303   if (!Subsidy::CanAllocateItem()) return false;
00304 
00305   SourceType src_type = ST_TOWN;
00306 
00307   /* Select a random town. */
00308   const Town *src_town = Town::GetRandom();
00309 
00310   uint32 town_cargo_produced = src_town->cargo_produced;
00311 
00312   /* Passenger subsidies are not handled here. */
00313   ClrBit(town_cargo_produced, CT_PASSENGERS);
00314 
00315   /* Choose a random cargo that is produced in the town. */
00316   uint8 cargo_number = RandomRange(CountBits(town_cargo_produced));
00317   CargoID cid;
00318   FOR_EACH_SET_CARGO_ID(cid, town_cargo_produced) {
00319     if (cargo_number == 0) break;
00320     cargo_number--;
00321   }
00322 
00323   /* Avoid using invalid NewGRF cargoes. */
00324   if (!CargoSpec::Get(cid)->IsValid()) return false;
00325 
00326   /* Quit if the percentage transported is large enough. */
00327   if (src_town->GetPercentTransported(cid) > SUBSIDY_MAX_PCT_TRANSPORTED) return false;
00328 
00329   SourceID src = src_town->index;
00330 
00331   return FindSubsidyCargoDestination(cid, src_type, src);
00332 }
00333 
00337 bool FindSubsidyIndustryCargoRoute()
00338 {
00339   if (!Subsidy::CanAllocateItem()) return false;
00340 
00341   SourceType src_type = ST_INDUSTRY;
00342 
00343   /* Select a random industry. */
00344   const Industry *src_ind = Industry::GetRandom();
00345   if (src_ind == NULL) return false;
00346 
00347   uint trans, total;
00348 
00349   CargoID cid;
00350 
00351   /* Randomize cargo type */
00352   if (src_ind->produced_cargo[1] != CT_INVALID && HasBit(Random(), 0)) {
00353     cid = src_ind->produced_cargo[1];
00354     trans = src_ind->last_month_pct_transported[1];
00355     total = src_ind->last_month_production[1];
00356   } else {
00357     cid = src_ind->produced_cargo[0];
00358     trans = src_ind->last_month_pct_transported[0];
00359     total = src_ind->last_month_production[0];
00360   }
00361 
00362   /* Quit if no production in this industry
00363    * or if the pct transported is already large enough */
00364   if (total == 0 || trans > SUBSIDY_MAX_PCT_TRANSPORTED || cid == CT_INVALID) return false;
00365 
00366   SourceID src = src_ind->index;
00367 
00368   return FindSubsidyCargoDestination(cid, src_type, src);
00369 }
00370 
00377 bool FindSubsidyCargoDestination(CargoID cid, SourceType src_type, SourceID src)
00378 {
00379   /* Choose a random destination. Only consider towns if they can accept the cargo. */
00380   SourceType dst_type = (HasBit(_town_cargoes_accepted, cid) && Chance16(1, 2)) ? ST_TOWN : ST_INDUSTRY;
00381 
00382   SourceID dst;
00383   switch (dst_type) {
00384     case ST_TOWN: {
00385       /* Select a random town. */
00386       const Town *dst_town = Town::GetRandom();
00387 
00388       /* Check if the town can accept this cargo. */
00389       if (!HasBit(dst_town->cargo_accepted_total, cid)) return false;
00390 
00391       dst = dst_town->index;
00392       break;
00393     }
00394 
00395     case ST_INDUSTRY: {
00396       /* Select a random industry. */
00397       const Industry *dst_ind = Industry::GetRandom();
00398 
00399       /* The industry must accept the cargo */
00400       if (dst_ind == NULL ||
00401           (cid != dst_ind->accepts_cargo[0] &&
00402            cid != dst_ind->accepts_cargo[1] &&
00403            cid != dst_ind->accepts_cargo[2])) {
00404         return false;
00405       }
00406 
00407       dst = dst_ind->index;
00408       break;
00409     }
00410 
00411     default: NOT_REACHED();
00412   }
00413 
00414   /* Check that the source and the destination are not the same. */
00415   if (src_type == dst_type && src == dst) return false;
00416 
00417   /* Check distance between source and destination. */
00418   if (!CheckSubsidyDistance(src_type, src, dst_type, dst)) return false;
00419 
00420   /* Avoid duplicate subsidies. */
00421   if (CheckSubsidyDuplicate(cid, src_type, src, dst_type, dst)) return false;
00422 
00423   CreateSubsidy(cid, src_type, src, dst_type, dst);
00424 
00425   return true;
00426 }
00427 
00428 void SubsidyMonthlyLoop()
00429 {
00430   bool modified = false;
00431 
00432   Subsidy *s;
00433   FOR_ALL_SUBSIDIES(s) {
00434     if (--s->remaining == 0) {
00435       if (!s->IsAwarded()) {
00436         Pair reftype = SetupSubsidyDecodeParam(s, true);
00437         AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
00438         AI::BroadcastNewEvent(new ScriptEventSubsidyOfferExpired(s->index));
00439         Game::NewEvent(new ScriptEventSubsidyOfferExpired(s->index));
00440       } else {
00441         if (s->awarded == _local_company) {
00442           Pair reftype = SetupSubsidyDecodeParam(s, true);
00443           AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
00444         }
00445         AI::BroadcastNewEvent(new ScriptEventSubsidyExpired(s->index));
00446         Game::NewEvent(new ScriptEventSubsidyExpired(s->index));
00447       }
00448       delete s;
00449       modified = true;
00450     }
00451   }
00452 
00453   if (modified) RebuildSubsidisedSourceAndDestinationCache();
00454 
00455   bool passenger_subsidy = false;
00456   bool town_subsidy = false;
00457   bool industry_subsidy = false;
00458 
00459   int random_chance = RandomRange(16);
00460 
00461   if (random_chance < 2) {
00462     /* There is a 1/8 chance each month of generating a passenger subsidy. */
00463     int n = 1000;
00464 
00465     do {
00466       passenger_subsidy = FindSubsidyPassengerRoute();
00467     } while (!passenger_subsidy && n--);
00468   } else if (random_chance == 2) {
00469     /* Cargo subsidies with a town as a source have a 1/16 chance. */
00470     int n = 1000;
00471 
00472     do {
00473       town_subsidy = FindSubsidyTownCargoRoute();
00474     } while (!town_subsidy && n--);
00475   } else if (random_chance == 3) {
00476     /* Cargo subsidies with an industry as a source have a 1/16 chance. */
00477     int n = 1000;
00478 
00479     do {
00480       industry_subsidy = FindSubsidyTownCargoRoute();
00481     } while (!industry_subsidy && n--);
00482   }
00483 
00484   modified |= passenger_subsidy || town_subsidy || industry_subsidy;
00485 
00486   if (modified) InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
00487 }
00488 
00498 bool CheckSubsidised(CargoID cargo_type, CompanyID company, SourceType src_type, SourceID src, const Station *st)
00499 {
00500   /* If the source isn't subsidised, don't continue */
00501   if (src == INVALID_SOURCE) return false;
00502   switch (src_type) {
00503     case ST_INDUSTRY:
00504       if (!(Industry::Get(src)->part_of_subsidy & POS_SRC)) return false;
00505       break;
00506     case ST_TOWN:
00507       if (!(    Town::Get(src)->part_of_subsidy & POS_SRC)) return false;
00508       break;
00509     default: return false;
00510   }
00511 
00512   /* Remember all towns near this station (at least one house in its catchment radius)
00513    * which are destination of subsidised path. Do that only if needed */
00514   SmallVector<const Town *, 2> towns_near;
00515   if (!st->rect.IsEmpty()) {
00516     Subsidy *s;
00517     FOR_ALL_SUBSIDIES(s) {
00518       /* Don't create the cache if there is no applicable subsidy with town as destination */
00519       if (s->dst_type != ST_TOWN) continue;
00520       if (s->cargo_type != cargo_type || s->src_type != src_type || s->src != src) continue;
00521       if (s->IsAwarded() && s->awarded != company) continue;
00522 
00523       Rect rect = st->GetCatchmentRect();
00524 
00525       for (int y = rect.top; y <= rect.bottom; y++) {
00526         for (int x = rect.left; x <= rect.right; x++) {
00527           TileIndex tile = TileXY(x, y);
00528           if (!IsTileType(tile, MP_HOUSE)) continue;
00529           const Town *t = Town::GetByTile(tile);
00530           if (t->part_of_subsidy & POS_DST) towns_near.Include(t);
00531         }
00532       }
00533       break;
00534     }
00535   }
00536 
00537   bool subsidised = false;
00538 
00539   /* Check if there's a (new) subsidy that applies. There can be more subsidies triggered by this delivery!
00540    * Think about the case that subsidies are A->B and A->C and station has both B and C in its catchment area */
00541   Subsidy *s;
00542   FOR_ALL_SUBSIDIES(s) {
00543     if (s->cargo_type == cargo_type && s->src_type == src_type && s->src == src && (!s->IsAwarded() || s->awarded == company)) {
00544       switch (s->dst_type) {
00545         case ST_INDUSTRY:
00546           for (const Industry * const *ip = st->industries_near.Begin(); ip != st->industries_near.End(); ip++) {
00547             if (s->dst == (*ip)->index) {
00548               assert((*ip)->part_of_subsidy & POS_DST);
00549               subsidised = true;
00550               if (!s->IsAwarded()) s->AwardTo(company);
00551             }
00552           }
00553           break;
00554         case ST_TOWN:
00555           for (const Town * const *tp = towns_near.Begin(); tp != towns_near.End(); tp++) {
00556             if (s->dst == (*tp)->index) {
00557               assert((*tp)->part_of_subsidy & POS_DST);
00558               subsidised = true;
00559               if (!s->IsAwarded()) s->AwardTo(company);
00560             }
00561           }
00562           break;
00563         default:
00564           NOT_REACHED();
00565       }
00566     }
00567   }
00568 
00569   return subsidised;
00570 }