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 
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[MAX_LENGTH_COMPANY_NAME_CHARS * MAX_CHAR_LENGTH];
00044   SetDParam(0, company);
00045   GetString(company_name, STR_COMPANY_NAME, lastof(company_name));
00046 
00047   char *cn = strdup(company_name);
00048 
00049   /* Add a news item */
00050   Pair reftype = SetupSubsidyDecodeParam(this, false);
00051   InjectDParam(1);
00052 
00053   SetDParamStr(0, cn);
00054   AddNewsItem(
00055     STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF + _settings_game.difficulty.subsidy_multiplier,
00056     NS_SUBSIDIES,
00057     (NewsReferenceType)reftype.a, this->src, (NewsReferenceType)reftype.b, this->dst,
00058     cn
00059   );
00060   AI::BroadcastNewEvent(new AIEventSubsidyAwarded(this->index));
00061 
00062   InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
00063 }
00064 
00065 Pair SetupSubsidyDecodeParam(const Subsidy *s, bool mode)
00066 {
00067   NewsReferenceType reftype1 = NR_NONE;
00068   NewsReferenceType reftype2 = NR_NONE;
00069 
00070   /* if mode is false, use the singular form */
00071   const CargoSpec *cs = CargoSpec::Get(s->cargo_type);
00072   SetDParam(0, mode ? cs->name : cs->name_single);
00073 
00074   switch (s->src_type) {
00075     case ST_INDUSTRY:
00076       reftype1 = NR_INDUSTRY;
00077       SetDParam(1, STR_INDUSTRY_NAME);
00078       break;
00079     case ST_TOWN:
00080       reftype1 = NR_TOWN;
00081       SetDParam(1, STR_TOWN_NAME);
00082       break;
00083     default: NOT_REACHED();
00084   }
00085   SetDParam(2, s->src);
00086 
00087   switch (s->dst_type) {
00088     case ST_INDUSTRY:
00089       reftype2 = NR_INDUSTRY;
00090       SetDParam(4, STR_INDUSTRY_NAME);
00091       break;
00092     case ST_TOWN:
00093       reftype2 = NR_TOWN;
00094       SetDParam(4, STR_TOWN_NAME);
00095       break;
00096     default: NOT_REACHED();
00097   }
00098   SetDParam(5, s->dst);
00099 
00100   Pair p;
00101   p.a = reftype1;
00102   p.b = reftype2;
00103   return p;
00104 }
00105 
00112 static inline void SetPartOfSubsidyFlag(SourceType type, SourceID index, PartOfSubsidy flag)
00113 {
00114   switch (type) {
00115     case ST_INDUSTRY: Industry::Get(index)->part_of_subsidy |= flag; return;
00116     case ST_TOWN:         Town::Get(index)->part_of_subsidy |= flag; return;
00117     default: NOT_REACHED();
00118   }
00119 }
00120 
00121 void RebuildSubsidisedSourceAndDestinationCache()
00122 {
00123   Town *t;
00124   FOR_ALL_TOWNS(t) t->part_of_subsidy = POS_NONE;
00125 
00126   Industry *i;
00127   FOR_ALL_INDUSTRIES(i) i->part_of_subsidy = POS_NONE;
00128 
00129   const Subsidy *s;
00130   FOR_ALL_SUBSIDIES(s) {
00131     SetPartOfSubsidyFlag(s->src_type, s->src, POS_SRC);
00132     SetPartOfSubsidyFlag(s->dst_type, s->dst, POS_DST);
00133   }
00134 }
00135 
00136 void DeleteSubsidyWith(SourceType type, SourceID index)
00137 {
00138   bool dirty = false;
00139 
00140   Subsidy *s;
00141   FOR_ALL_SUBSIDIES(s) {
00142     if ((s->src_type == type && s->src == index) || (s->dst_type == type && s->dst == index)) {
00143       delete s;
00144       dirty = true;
00145     }
00146   }
00147 
00148   if (dirty) {
00149     InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
00150     RebuildSubsidisedSourceAndDestinationCache();
00151   }
00152 }
00153 
00154 static bool CheckSubsidyDuplicate(CargoID cargo, SourceType src_type, SourceID src, SourceType dst_type, SourceID dst)
00155 {
00156   const Subsidy *s;
00157   FOR_ALL_SUBSIDIES(s) {
00158     if (s->cargo_type == cargo &&
00159         s->src_type == src_type && s->src == src &&
00160         s->dst_type == dst_type && s->dst == dst) {
00161       return true;
00162     }
00163   }
00164   return false;
00165 }
00166 
00167 static Subsidy *FindSubsidyPassengerRoute()
00168 {
00169   assert(Subsidy::CanAllocateItem());
00170 
00171   const Town *src = Town::GetRandom();
00172   if (src->population < SUBSIDY_PAX_MIN_POPULATION ||
00173       src->pct_pass_transported > SUBSIDY_MAX_PCT_TRANSPORTED) {
00174     return NULL;
00175   }
00176 
00177   const Town *dst = Town::GetRandom();
00178   if (dst->population < SUBSIDY_PAX_MIN_POPULATION || src == dst) {
00179     return NULL;
00180   }
00181 
00182   if (DistanceManhattan(src->xy, dst->xy) > SUBSIDY_MAX_DISTANCE) return NULL;
00183   if (CheckSubsidyDuplicate(CT_PASSENGERS, ST_TOWN, src->index, ST_TOWN, dst->index)) return NULL;
00184 
00185   Subsidy *s = new Subsidy();
00186   s->cargo_type = CT_PASSENGERS;
00187   s->src_type = s->dst_type = ST_TOWN;
00188   s->src = src->index;
00189   s->dst = dst->index;
00190 
00191   return s;
00192 }
00193 
00194 static Subsidy *FindSubsidyCargoRoute()
00195 {
00196   assert(Subsidy::CanAllocateItem());
00197 
00198   const Industry *i = Industry::GetRandom();
00199   if (i == NULL) return NULL;
00200 
00201   CargoID cargo;
00202   uint trans, total;
00203 
00204   /* Randomize cargo type */
00205   if (i->produced_cargo[1] != CT_INVALID && HasBit(Random(), 0)) {
00206     cargo = i->produced_cargo[1];
00207     trans = i->last_month_pct_transported[1];
00208     total = i->last_month_production[1];
00209   } else {
00210     cargo = i->produced_cargo[0];
00211     trans = i->last_month_pct_transported[0];
00212     total = i->last_month_production[0];
00213   }
00214 
00215   /* Quit if no production in this industry
00216    * or if the pct transported is already large enough */
00217   if (total == 0 || trans > SUBSIDY_MAX_PCT_TRANSPORTED || cargo == CT_INVALID) return NULL;
00218 
00219   /* Don't allow passengers subsidies from industry */
00220   const CargoSpec *cs = CargoSpec::Get(cargo);
00221   if (cs->town_effect == TE_PASSENGERS) return NULL;
00222 
00223   SourceType dst_type;
00224   SourceID dst;
00225 
00226   if (cs->town_effect == TE_GOODS || cs->town_effect == TE_FOOD) {
00227     /*  The destination is a town */
00228     dst_type = ST_TOWN;
00229     const Town *t = Town::GetRandom();
00230 
00231     /* Only want big towns */
00232     if (t->population < SUBSIDY_CARGO_MIN_POPULATION) return NULL;
00233 
00234     if (DistanceManhattan(i->location.tile, t->xy) > SUBSIDY_MAX_DISTANCE) return NULL;
00235 
00236     dst = t->index;
00237   } else {
00238     /* The destination is an industry */
00239     dst_type = ST_INDUSTRY;
00240     const Industry *i2 = Industry::GetRandom();
00241 
00242     /* The industry must accept the cargo */
00243     if (i2 == NULL || i == i2 ||
00244         (cargo != i2->accepts_cargo[0] &&
00245          cargo != i2->accepts_cargo[1] &&
00246          cargo != i2->accepts_cargo[2])) {
00247       return NULL;
00248     }
00249 
00250     if (DistanceManhattan(i->location.tile, i2->location.tile) > SUBSIDY_MAX_DISTANCE) return NULL;
00251 
00252     dst = i2->index;
00253   }
00254 
00255   if (CheckSubsidyDuplicate(cargo, ST_INDUSTRY, i->index, dst_type, dst)) return NULL;
00256 
00257   Subsidy *s = new Subsidy();
00258   s->cargo_type = cargo;
00259   s->src_type = ST_INDUSTRY;
00260   s->src = i->index;
00261   s->dst_type = dst_type;
00262   s->dst = dst;
00263 
00264   return s;
00265 }
00266 
00267 void SubsidyMonthlyLoop()
00268 {
00269   bool modified = false;
00270 
00271   Subsidy *s;
00272   FOR_ALL_SUBSIDIES(s) {
00273     if (--s->remaining == 0) {
00274       if (!s->IsAwarded()) {
00275         Pair reftype = SetupSubsidyDecodeParam(s, true);
00276         AddNewsItem(STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
00277         AI::BroadcastNewEvent(new AIEventSubsidyOfferExpired(s->index));
00278       } else {
00279         if (s->awarded == _local_company) {
00280           Pair reftype = SetupSubsidyDecodeParam(s, true);
00281           AddNewsItem(STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
00282         }
00283         AI::BroadcastNewEvent(new AIEventSubsidyExpired(s->index));
00284       }
00285       delete s;
00286       modified = true;
00287     }
00288   }
00289 
00290   if (modified) RebuildSubsidisedSourceAndDestinationCache();
00291 
00292   /* 25% chance to go on */
00293   if (Subsidy::CanAllocateItem() && Chance16(1, 4)) {
00294     uint n = 1000;
00295     do {
00296       Subsidy *s = FindSubsidyPassengerRoute();
00297       if (s == NULL) s = FindSubsidyCargoRoute();
00298       if (s != NULL) {
00299         s->remaining = SUBSIDY_OFFER_MONTHS;
00300         s->awarded = INVALID_COMPANY;
00301         Pair reftype = SetupSubsidyDecodeParam(s, false);
00302         AddNewsItem(STR_NEWS_SERVICE_SUBSIDY_OFFERED, NS_SUBSIDIES, (NewsReferenceType)reftype.a, s->src, (NewsReferenceType)reftype.b, s->dst);
00303         SetPartOfSubsidyFlag(s->src_type, s->src, POS_SRC);
00304         SetPartOfSubsidyFlag(s->dst_type, s->dst, POS_DST);
00305         AI::BroadcastNewEvent(new AIEventSubsidyOffer(s->index));
00306         modified = true;
00307         break;
00308       }
00309     } while (n--);
00310   }
00311 
00312   if (modified) InvalidateWindowData(WC_SUBSIDIES_LIST, 0);
00313 }
00314 
00324 bool CheckSubsidised(CargoID cargo_type, CompanyID company, SourceType src_type, SourceID src, const Station *st)
00325 {
00326   /* If the source isn't subsidised, don't continue */
00327   if (src == INVALID_SOURCE) return false;
00328   switch (src_type) {
00329     case ST_INDUSTRY:
00330       if (!(Industry::Get(src)->part_of_subsidy & POS_SRC)) return false;
00331       break;
00332     case ST_TOWN:
00333       if (!(    Town::Get(src)->part_of_subsidy & POS_SRC)) return false;
00334       break;
00335     default: return false;
00336   }
00337 
00338   /* Remember all towns near this station (at least one house in its catchment radius)
00339    * which are destination of subsidised path. Do that only if needed */
00340   SmallVector<const Town *, 2> towns_near;
00341   if (!st->rect.IsEmpty()) {
00342     Subsidy *s;
00343     FOR_ALL_SUBSIDIES(s) {
00344       /* Don't create the cache if there is no applicable subsidy with town as destination */
00345       if (s->dst_type != ST_TOWN) continue;
00346       if (s->cargo_type != cargo_type || s->src_type != src_type || s->src != src) continue;
00347       if (s->IsAwarded() && s->awarded != company) continue;
00348 
00349       Rect rect = st->GetCatchmentRect();
00350 
00351       for (int y = rect.top; y <= rect.bottom; y++) {
00352         for (int x = rect.left; x <= rect.right; x++) {
00353           TileIndex tile = TileXY(x, y);
00354           if (!IsTileType(tile, MP_HOUSE)) continue;
00355           const Town *t = Town::GetByTile(tile);
00356           if (t->part_of_subsidy & POS_DST) towns_near.Include(t);
00357         }
00358       }
00359       break;
00360     }
00361   }
00362 
00363   bool subsidised = false;
00364 
00365   /* Check if there's a (new) subsidy that applies. There can be more subsidies triggered by this delivery!
00366    * Think about the case that subsidies are A->B and A->C and station has both B and C in its catchment area */
00367   Subsidy *s;
00368   FOR_ALL_SUBSIDIES(s) {
00369     if (s->cargo_type == cargo_type && s->src_type == src_type && s->src == src && (!s->IsAwarded() || s->awarded == company)) {
00370       switch (s->dst_type) {
00371         case ST_INDUSTRY:
00372           for (const Industry * const *ip = st->industries_near.Begin(); ip != st->industries_near.End(); ip++) {
00373             if (s->dst == (*ip)->index) {
00374               assert((*ip)->part_of_subsidy & POS_DST);
00375               subsidised = true;
00376               if (!s->IsAwarded()) s->AwardTo(company);
00377             }
00378           }
00379           break;
00380         case ST_TOWN:
00381           for (const Town * const *tp = towns_near.Begin(); tp != towns_near.End(); tp++) {
00382             if (s->dst == (*tp)->index) {
00383               assert((*tp)->part_of_subsidy & POS_DST);
00384               subsidised = true;
00385               if (!s->IsAwarded()) s->AwardTo(company);
00386             }
00387           }
00388           break;
00389         default:
00390           NOT_REACHED();
00391       }
00392     }
00393   }
00394 
00395   return subsidised;
00396 }

Generated on Sun Jun 5 04:20:04 2011 for OpenTTD by  doxygen 1.6.1