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 "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   /* 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 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   /* if mode is false, use the singular form */
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     /* Try to get a town from the demand destinations. */
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   /* Randomize cargo type */
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   /* Quit if no production in this industry
00226    * or if the pct transported is already large enough */
00227   if (total == 0 || trans > SUBSIDY_MAX_PCT_TRANSPORTED || cargo == CT_INVALID) return NULL;
00228 
00229   /* Don't allow passengers subsidies from industry */
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     /* The destination is a town */
00238     dst_type = ST_TOWN;
00239     const Town *t = NULL;
00240     if (CargoHasDestinations(cargo)) {
00241       /* Try to get a town from the demand destinations. */
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     /* Only want big towns */
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     /* The destination is an industry */
00257     dst_type = ST_INDUSTRY;
00258     const Industry *i2 = NULL;
00259     if (CargoHasDestinations(cargo)) {
00260       /* Try to get a town from the demand destinations. */
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     /* The industry must accept the cargo */
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   /* 25% chance to go on */
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   /* If the source isn't subsidised, don't continue */
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   /* Remember all towns near this station (at least one house in its catchment radius)
00366    * which are destination of subsidised path. Do that only if needed */
00367   SmallVector<const Town *, 2> towns_near;
00368   if (!st->rect.IsEmpty()) {
00369     Subsidy *s;
00370     FOR_ALL_SUBSIDIES(s) {
00371       /* Don't create the cache if there is no applicable subsidy with town as destination */
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   /* Check if there's a (new) subsidy that applies. There can be more subsidies triggered by this delivery!
00393    * Think about the case that subsidies are A->B and A->C and station has both B and C in its catchment area */
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 }

Generated on Mon May 9 05:19:01 2011 for OpenTTD by  doxygen 1.6.1