engine.cpp

Go to the documentation of this file.
00001 /* $Id: engine.cpp 21842 2011-01-18 21:30:59Z rubidium $ */
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 "command_func.h"
00015 #include "news_func.h"
00016 #include "aircraft.h"
00017 #include "newgrf.h"
00018 #include "newgrf_engine.h"
00019 #include "group.h"
00020 #include "strings_func.h"
00021 #include "core/random_func.hpp"
00022 #include "window_func.h"
00023 #include "date_func.h"
00024 #include "autoreplace_gui.h"
00025 #include "string_func.h"
00026 #include "ai/ai.hpp"
00027 #include "core/pool_func.hpp"
00028 #include "engine_gui.h"
00029 #include "engine_func.h"
00030 #include "engine_base.h"
00031 #include "company_base.h"
00032 
00033 #include "table/strings.h"
00034 #include "table/engines.h"
00035 
00036 EnginePool _engine_pool("Engine");
00037 INSTANTIATE_POOL_METHODS(Engine)
00038 
00039 EngineOverrideManager _engine_mngr;
00040 
00045 static Year _year_engine_aging_stops;
00046 
00050 static uint16 _introduced_railtypes;
00051 
00053 const uint8 _engine_counts[4] = {
00054   lengthof(_orig_rail_vehicle_info),
00055   lengthof(_orig_road_vehicle_info),
00056   lengthof(_orig_ship_vehicle_info),
00057   lengthof(_orig_aircraft_vehicle_info),
00058 };
00059 
00061 const uint8 _engine_offsets[4] = {
00062   0,
00063   lengthof(_orig_rail_vehicle_info),
00064   lengthof(_orig_rail_vehicle_info) + lengthof(_orig_road_vehicle_info),
00065   lengthof(_orig_rail_vehicle_info) + lengthof(_orig_road_vehicle_info) + lengthof(_orig_ship_vehicle_info),
00066 };
00067 
00068 assert_compile(lengthof(_orig_rail_vehicle_info) + lengthof(_orig_road_vehicle_info) + lengthof(_orig_ship_vehicle_info) + lengthof(_orig_aircraft_vehicle_info) == lengthof(_orig_engine_info));
00069 
00070 const uint EngineOverrideManager::NUM_DEFAULT_ENGINES = _engine_counts[VEH_TRAIN] + _engine_counts[VEH_ROAD] + _engine_counts[VEH_SHIP] + _engine_counts[VEH_AIRCRAFT];
00071 
00072 Engine::Engine() :
00073   name(NULL),
00074   overrides_count(0),
00075   overrides(NULL)
00076 {
00077 }
00078 
00079 Engine::Engine(VehicleType type, EngineID base)
00080 {
00081   this->type = type;
00082   this->grf_prop.local_id = base;
00083   this->list_position = base;
00084 
00085   /* Check if this base engine is within the original engine data range */
00086   if (base >= _engine_counts[type]) {
00087     /* Mark engine as valid anyway */
00088     this->info.climates = 0x80;
00089     /* Set model life to maximum to make wagons available */
00090     this->info.base_life = 0xFF;
00091     /* Set road vehicle tractive effort to the default value */
00092     if (type == VEH_ROAD) this->u.road.tractive_effort = 0x4C;
00093     /* Set visual effect to the default value */
00094     switch (type) {
00095       case VEH_TRAIN: this->u.rail.visual_effect = VE_DEFAULT; break;
00096       case VEH_ROAD:  this->u.road.visual_effect = VE_DEFAULT; break;
00097       case VEH_SHIP:  this->u.ship.visual_effect = VE_DEFAULT; break;
00098       default: break; // The aircraft, disasters and especially visual effects have no NewGRF configured visual effects
00099     }
00100     return;
00101   }
00102 
00103   /* Copy the original engine info for this slot */
00104   this->info = _orig_engine_info[_engine_offsets[type] + base];
00105 
00106   /* Copy the original engine data for this slot */
00107   switch (type) {
00108     default: NOT_REACHED();
00109 
00110     case VEH_TRAIN:
00111       this->u.rail = _orig_rail_vehicle_info[base];
00112       this->original_image_index = this->u.rail.image_index;
00113       this->info.string_id = STR_VEHICLE_NAME_TRAIN_ENGINE_RAIL_KIRBY_PAUL_TANK_STEAM + base;
00114 
00115       /* Set the default model life of original wagons to "infinite" */
00116       if (this->u.rail.railveh_type == RAILVEH_WAGON) this->info.base_life = 0xFF;
00117 
00118       break;
00119 
00120     case VEH_ROAD:
00121       this->u.road = _orig_road_vehicle_info[base];
00122       this->original_image_index = this->u.road.image_index;
00123       this->info.string_id = STR_VEHICLE_NAME_ROAD_VEHICLE_MPS_REGAL_BUS + base;
00124       break;
00125 
00126     case VEH_SHIP:
00127       this->u.ship = _orig_ship_vehicle_info[base];
00128       this->original_image_index = this->u.ship.image_index;
00129       this->info.string_id = STR_VEHICLE_NAME_SHIP_MPS_OIL_TANKER + base;
00130       break;
00131 
00132     case VEH_AIRCRAFT:
00133       this->u.air = _orig_aircraft_vehicle_info[base];
00134       this->original_image_index = this->u.air.image_index;
00135       this->info.string_id = STR_VEHICLE_NAME_AIRCRAFT_SAMPSON_U52 + base;
00136       break;
00137   }
00138 }
00139 
00140 Engine::~Engine()
00141 {
00142   UnloadWagonOverrides(this);
00143   free(this->name);
00144 }
00145 
00151 bool Engine::CanCarryCargo() const
00152 {
00153   /* For engines that can appear in a consist (i.e. rail vehicles and (articulated) road vehicles), a capacity
00154    * of zero is a special case, to define the vehicle to not carry anything. The default cargotype is still used
00155    * for livery selection etc.
00156    * Note: Only the property is tested. A capacity callback returning 0 does not have the same effect.
00157    */
00158   switch (this->type) {
00159     case VEH_TRAIN:
00160       if (this->u.rail.capacity == 0) return false;
00161       break;
00162 
00163     case VEH_ROAD:
00164       if (this->u.road.capacity == 0) return false;
00165       break;
00166 
00167     case VEH_SHIP:
00168     case VEH_AIRCRAFT:
00169       break;
00170 
00171     default: NOT_REACHED();
00172   }
00173   return this->GetDefaultCargoType() != CT_INVALID;
00174 }
00175 
00188 uint Engine::GetDisplayDefaultCapacity(uint16 *mail_capacity) const
00189 {
00190   if (mail_capacity != NULL) *mail_capacity = 0;
00191   if (!this->CanCarryCargo()) return 0;
00192   switch (type) {
00193     case VEH_TRAIN:
00194       return GetEngineProperty(this->index, PROP_TRAIN_CARGO_CAPACITY, this->u.rail.capacity) + (this->u.rail.railveh_type == RAILVEH_MULTIHEAD ? this->u.rail.capacity : 0);
00195 
00196     case VEH_ROAD:
00197       return GetEngineProperty(this->index, PROP_ROADVEH_CARGO_CAPACITY, this->u.road.capacity);
00198 
00199     case VEH_SHIP:
00200       return GetEngineProperty(this->index, PROP_SHIP_CARGO_CAPACITY, this->u.ship.capacity);
00201 
00202     case VEH_AIRCRAFT: {
00203       uint capacity = GetEngineProperty(this->index, PROP_AIRCRAFT_PASSENGER_CAPACITY, this->u.air.passenger_capacity);
00204       CargoID cargo = this->GetDefaultCargoType();
00205       if (IsCargoInClass(cargo, CC_PASSENGERS)) {
00206         if (mail_capacity != NULL) *mail_capacity = GetEngineProperty(this->index, PROP_AIRCRAFT_MAIL_CAPACITY, this->u.air.mail_capacity);
00207       } else {
00208         capacity += GetEngineProperty(this->index, PROP_AIRCRAFT_MAIL_CAPACITY, this->u.air.mail_capacity);
00209       }
00210       switch (cargo) {
00211         case CT_PASSENGERS:
00212         case CT_MAIL:       return capacity;
00213         case CT_GOODS:      return capacity / 2;
00214         default:            return capacity / 4;
00215       }
00216     }
00217 
00218     default: NOT_REACHED();
00219   }
00220 }
00221 
00226 Money Engine::GetRunningCost() const
00227 {
00228   Price base_price;
00229   uint cost_factor;
00230   switch (this->type) {
00231     case VEH_ROAD:
00232       base_price = this->u.road.running_cost_class;
00233       if (base_price == INVALID_PRICE) return 0;
00234       cost_factor = GetEngineProperty(this->index, PROP_ROADVEH_RUNNING_COST_FACTOR, this->u.road.running_cost);
00235       break;
00236 
00237     case VEH_TRAIN:
00238       base_price = this->u.rail.running_cost_class;
00239       if (base_price == INVALID_PRICE) return 0;
00240       cost_factor = GetEngineProperty(this->index, PROP_TRAIN_RUNNING_COST_FACTOR, this->u.rail.running_cost);
00241       break;
00242 
00243     case VEH_SHIP:
00244       base_price = PR_RUNNING_SHIP;
00245       cost_factor = GetEngineProperty(this->index, PROP_SHIP_RUNNING_COST_FACTOR, this->u.ship.running_cost);
00246       break;
00247 
00248     case VEH_AIRCRAFT:
00249       base_price = PR_RUNNING_AIRCRAFT;
00250       cost_factor = GetEngineProperty(this->index, PROP_AIRCRAFT_RUNNING_COST_FACTOR, this->u.air.running_cost);
00251       break;
00252 
00253     default: NOT_REACHED();
00254   }
00255 
00256   return GetPrice(base_price, cost_factor, this->grf_prop.grffile, -8);
00257 }
00258 
00263 Money Engine::GetCost() const
00264 {
00265   Price base_price;
00266   uint cost_factor;
00267   switch (this->type) {
00268     case VEH_ROAD:
00269       base_price = PR_BUILD_VEHICLE_ROAD;
00270       cost_factor = GetEngineProperty(this->index, PROP_ROADVEH_COST_FACTOR, this->u.road.cost_factor);
00271       break;
00272 
00273     case VEH_TRAIN:
00274       if (this->u.rail.railveh_type == RAILVEH_WAGON) {
00275         base_price = PR_BUILD_VEHICLE_WAGON;
00276         cost_factor = GetEngineProperty(this->index, PROP_TRAIN_COST_FACTOR, this->u.rail.cost_factor);
00277       } else {
00278         base_price = PR_BUILD_VEHICLE_TRAIN;
00279         cost_factor = GetEngineProperty(this->index, PROP_TRAIN_COST_FACTOR, this->u.rail.cost_factor);
00280       }
00281       break;
00282 
00283     case VEH_SHIP:
00284       base_price = PR_BUILD_VEHICLE_SHIP;
00285       cost_factor = GetEngineProperty(this->index, PROP_SHIP_COST_FACTOR, this->u.ship.cost_factor);
00286       break;
00287 
00288     case VEH_AIRCRAFT:
00289       base_price = PR_BUILD_VEHICLE_AIRCRAFT;
00290       cost_factor = GetEngineProperty(this->index, PROP_AIRCRAFT_COST_FACTOR, this->u.air.cost_factor);
00291       break;
00292 
00293     default: NOT_REACHED();
00294   }
00295 
00296   return GetPrice(base_price, cost_factor, this->grf_prop.grffile, -8);
00297 }
00298 
00303 uint Engine::GetDisplayMaxSpeed() const
00304 {
00305   switch (this->type) {
00306     case VEH_TRAIN:
00307       return GetEngineProperty(this->index, PROP_TRAIN_SPEED, this->u.rail.max_speed);
00308 
00309     case VEH_ROAD: {
00310       uint max_speed = GetEngineProperty(this->index, PROP_ROADVEH_SPEED, 0);
00311       return (max_speed != 0) ? max_speed * 2 : this->u.road.max_speed / 2;
00312     }
00313 
00314     case VEH_SHIP:
00315       return GetEngineProperty(this->index, PROP_SHIP_SPEED, this->u.ship.max_speed) / 2;
00316 
00317     case VEH_AIRCRAFT: {
00318       uint max_speed = GetEngineProperty(this->index, PROP_AIRCRAFT_SPEED, 0);
00319       if (max_speed != 0) {
00320         return (max_speed * 128) / 10;
00321       }
00322       return this->u.air.max_speed;
00323     }
00324 
00325     default: NOT_REACHED();
00326   }
00327 }
00328 
00335 uint Engine::GetPower() const
00336 {
00337   /* Only trains and road vehicles have 'power'. */
00338   switch (this->type) {
00339     case VEH_TRAIN:
00340       return GetEngineProperty(this->index, PROP_TRAIN_POWER, this->u.rail.power);
00341     case VEH_ROAD:
00342       return GetEngineProperty(this->index, PROP_ROADVEH_POWER, this->u.road.power) * 10;
00343 
00344     default: NOT_REACHED();
00345   }
00346 }
00347 
00353 uint Engine::GetDisplayWeight() const
00354 {
00355   /* Only trains and road vehicles have 'weight'. */
00356   switch (this->type) {
00357     case VEH_TRAIN:
00358       return GetEngineProperty(this->index, PROP_TRAIN_WEIGHT, this->u.rail.weight) << (this->u.rail.railveh_type == RAILVEH_MULTIHEAD ? 1 : 0);
00359     case VEH_ROAD:
00360       return GetEngineProperty(this->index, PROP_ROADVEH_WEIGHT, this->u.road.weight) / 4;
00361 
00362     default: NOT_REACHED();
00363   }
00364 }
00365 
00371 uint Engine::GetDisplayMaxTractiveEffort() const
00372 {
00373   /* Only trains and road vehicles have 'tractive effort'. */
00374   switch (this->type) {
00375     case VEH_TRAIN:
00376       return (10 * this->GetDisplayWeight() * GetEngineProperty(this->index, PROP_TRAIN_TRACTIVE_EFFORT, this->u.rail.tractive_effort)) / 256;
00377     case VEH_ROAD:
00378       return (10 * this->GetDisplayWeight() * GetEngineProperty(this->index, PROP_ROADVEH_TRACTIVE_EFFORT, this->u.road.tractive_effort)) / 256;
00379 
00380     default: NOT_REACHED();
00381   }
00382 }
00383 
00388 Date Engine::GetLifeLengthInDays() const
00389 {
00390   /* Assume leap years; this gives the player a bit more than the given amount of years, but never less. */
00391   return (this->info.lifelength + _settings_game.vehicle.extend_vehicle_life) * DAYS_IN_LEAP_YEAR;
00392 }
00393 
00397 void EngineOverrideManager::ResetToDefaultMapping()
00398 {
00399   this->Clear();
00400   for (VehicleType type = VEH_TRAIN; type <= VEH_AIRCRAFT; type++) {
00401     for (uint internal_id = 0; internal_id < _engine_counts[type]; internal_id++) {
00402       EngineIDMapping *eid = this->Append();
00403       eid->type            = type;
00404       eid->grfid           = INVALID_GRFID;
00405       eid->internal_id     = internal_id;
00406       eid->substitute_id   = internal_id;
00407     }
00408   }
00409 }
00410 
00420 EngineID EngineOverrideManager::GetID(VehicleType type, uint16 grf_local_id, uint32 grfid)
00421 {
00422   const EngineIDMapping *end = this->End();
00423   EngineID index = 0;
00424   for (const EngineIDMapping *eid = this->Begin(); eid != end; eid++, index++) {
00425     if (eid->type == type && eid->grfid == grfid && eid->internal_id == grf_local_id) {
00426       return index;
00427     }
00428   }
00429   return INVALID_ENGINE;
00430 }
00431 
00435 void SetCachedEngineCounts()
00436 {
00437   size_t engines = Engine::GetPoolSize();
00438 
00439   /* Set up the engine count for all companies */
00440   Company *c;
00441   FOR_ALL_COMPANIES(c) {
00442     free(c->num_engines);
00443     c->num_engines = CallocT<EngineID>(engines);
00444   }
00445 
00446   /* Recalculate */
00447   Group *g;
00448   FOR_ALL_GROUPS(g) {
00449     free(g->num_engines);
00450     g->num_engines = CallocT<EngineID>(engines);
00451   }
00452 
00453   const Vehicle *v;
00454   FOR_ALL_VEHICLES(v) {
00455     if (!v->IsEngineCountable()) continue;
00456 
00457     assert(v->engine_type < engines);
00458 
00459     Company::Get(v->owner)->num_engines[v->engine_type]++;
00460 
00461     if (v->group_id == DEFAULT_GROUP) continue;
00462 
00463     g = Group::Get(v->group_id);
00464     assert(v->type == g->vehicle_type);
00465     assert(v->owner == g->owner);
00466 
00467     g->num_engines[v->engine_type]++;
00468   }
00469 }
00470 
00471 void SetupEngines()
00472 {
00473   _engine_pool.CleanPool();
00474 
00475   assert(_engine_mngr.Length() >= _engine_mngr.NUM_DEFAULT_ENGINES);
00476   const EngineIDMapping *end = _engine_mngr.End();
00477   uint index = 0;
00478   for (const EngineIDMapping *eid = _engine_mngr.Begin(); eid != end; eid++, index++) {
00479     const Engine *e = new Engine(eid->type, eid->internal_id);
00480     assert(e->index == index);
00481   }
00482 
00483   _introduced_railtypes = 0;
00484 }
00485 
00489 static void CheckRailIntroduction()
00490 {
00491   /* All railtypes have been introduced. */
00492   if (_introduced_railtypes == UINT16_MAX || Company::GetPoolSize() == 0) return;
00493 
00494   /* We need to find the railtypes that are known to all companies. */
00495   RailTypes rts = (RailTypes)UINT16_MAX;
00496 
00497   /* We are at, or past the introduction date of the rail. */
00498   Company *c;
00499   FOR_ALL_COMPANIES(c) {
00500     c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes, _date);
00501     rts &= c->avail_railtypes;
00502   }
00503 
00504   _introduced_railtypes |= rts;
00505 }
00506 
00507 void ShowEnginePreviewWindow(EngineID engine);
00508 
00509 /* Determine if an engine type is a wagon (and not a loco) */
00510 static bool IsWagon(EngineID index)
00511 {
00512   const Engine *e = Engine::Get(index);
00513   return e->type == VEH_TRAIN && e->u.rail.railveh_type == RAILVEH_WAGON;
00514 }
00515 
00516 static void CalcEngineReliability(Engine *e)
00517 {
00518   uint age = e->age;
00519 
00520   /* Check for early retirement */
00521   if (e->company_avail != 0 && !_settings_game.vehicle.never_expire_vehicles && e->info.base_life != 0xFF) {
00522     int retire_early = e->info.retire_early;
00523     uint retire_early_max_age = max(0, e->duration_phase_1 + e->duration_phase_2 - retire_early * 12);
00524     if (retire_early != 0 && age >= retire_early_max_age) {
00525       /* Early retirement is enabled and we're past the date... */
00526       e->company_avail = 0;
00527       AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
00528     }
00529   }
00530 
00531   if (age < e->duration_phase_1) {
00532     uint start = e->reliability_start;
00533     e->reliability = age * (e->reliability_max - start) / e->duration_phase_1 + start;
00534   } else if ((age -= e->duration_phase_1) < e->duration_phase_2 || _settings_game.vehicle.never_expire_vehicles || e->info.base_life == 0xFF) {
00535     /* We are at the peak of this engines life. It will have max reliability.
00536      * This is also true if the engines never expire. They will not go bad over time */
00537     e->reliability = e->reliability_max;
00538   } else if ((age -= e->duration_phase_2) < e->duration_phase_3) {
00539     uint max = e->reliability_max;
00540     e->reliability = (int)age * (int)(e->reliability_final - max) / e->duration_phase_3 + max;
00541   } else {
00542     /* time's up for this engine.
00543      * We will now completely retire this design */
00544     e->company_avail = 0;
00545     e->reliability = e->reliability_final;
00546     /* Kick this engine out of the lists */
00547     AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
00548   }
00549   SetWindowClassesDirty(WC_BUILD_VEHICLE); // Update to show the new reliability
00550   SetWindowClassesDirty(WC_REPLACE_VEHICLE);
00551 }
00552 
00554 void SetYearEngineAgingStops()
00555 {
00556   /* Determine last engine aging year, default to 2050 as previously. */
00557   _year_engine_aging_stops = 2050;
00558 
00559   const Engine *e;
00560   FOR_ALL_ENGINES(e) {
00561     const EngineInfo *ei = &e->info;
00562 
00563     /* Exclude certain engines */
00564     if (!HasBit(ei->climates, _settings_game.game_creation.landscape)) continue;
00565     if (e->type == VEH_TRAIN && e->u.rail.railveh_type == RAILVEH_WAGON) continue;
00566 
00567     /* Base year ending date on half the model life */
00568     YearMonthDay ymd;
00569     ConvertDateToYMD(ei->base_intro + (ei->lifelength * DAYS_IN_LEAP_YEAR) / 2, &ymd);
00570 
00571     _year_engine_aging_stops = max(_year_engine_aging_stops, ymd.year);
00572   }
00573 }
00574 
00575 void StartupOneEngine(Engine *e, Date aging_date)
00576 {
00577   const EngineInfo *ei = &e->info;
00578 
00579   e->age = 0;
00580   e->flags = 0;
00581   e->company_avail = 0;
00582 
00583   /* Don't randomise the start-date in the first two years after gamestart to ensure availability
00584    * of engines in early starting games.
00585    * Note: TTDP uses fixed 1922 */
00586   uint32 r = Random();
00587   e->intro_date = ei->base_intro <= ConvertYMDToDate(_settings_game.game_creation.starting_year + 2, 0, 1) ? ei->base_intro : (Date)GB(r, 0, 9) + ei->base_intro;
00588   if (e->intro_date <= _date) {
00589     e->age = (aging_date - e->intro_date) >> 5;
00590     e->company_avail = (CompanyMask)-1;
00591     e->flags |= ENGINE_AVAILABLE;
00592   }
00593 
00594   e->reliability_start = GB(r, 16, 14) + 0x7AE0;
00595   r = Random();
00596   e->reliability_max   = GB(r,  0, 14) + 0xBFFF;
00597   e->reliability_final = GB(r, 16, 14) + 0x3FFF;
00598 
00599   r = Random();
00600   e->duration_phase_1 = GB(r, 0, 5) + 7;
00601   e->duration_phase_2 = GB(r, 5, 4) + ei->base_life * 12 - 96;
00602   e->duration_phase_3 = GB(r, 9, 7) + 120;
00603 
00604   e->reliability_spd_dec = ei->decay_speed << 2;
00605 
00606   CalcEngineReliability(e);
00607 
00608   /* prevent certain engines from ever appearing. */
00609   if (!HasBit(ei->climates, _settings_game.game_creation.landscape)) {
00610     e->flags |= ENGINE_AVAILABLE;
00611     e->company_avail = 0;
00612   }
00613 }
00614 
00615 void StartupEngines()
00616 {
00617   Engine *e;
00618   /* Aging of vehicles stops, so account for that when starting late */
00619   const Date aging_date = min(_date, ConvertYMDToDate(_year_engine_aging_stops, 0, 1));
00620 
00621   FOR_ALL_ENGINES(e) {
00622     StartupOneEngine(e, aging_date);
00623   }
00624 
00625   /* Update the bitmasks for the vehicle lists */
00626   Company *c;
00627   FOR_ALL_COMPANIES(c) {
00628     c->avail_railtypes = GetCompanyRailtypes(c->index);
00629     c->avail_roadtypes = GetCompanyRoadtypes(c->index);
00630   }
00631 
00632   /* Rail types that are invalid or never introduced are marked as
00633    * being introduced upon start. That way we can easily check whether
00634    * there is any date related introduction that is still going to
00635    * happen somewhere in the future. */
00636   for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
00637     const RailtypeInfo *rti = GetRailTypeInfo(rt);
00638     if (rti->label != 0 && IsInsideMM(rti->introduction_date, 0, MAX_DAY)) continue;
00639 
00640     SetBit(_introduced_railtypes, rt);
00641   }
00642 
00643   CheckRailIntroduction();
00644 
00645   /* Invalidate any open purchase lists */
00646   InvalidateWindowClassesData(WC_BUILD_VEHICLE);
00647 }
00648 
00649 static void AcceptEnginePreview(EngineID eid, CompanyID company)
00650 {
00651   Engine *e = Engine::Get(eid);
00652   Company *c = Company::Get(company);
00653 
00654   SetBit(e->company_avail, company);
00655   if (e->type == VEH_TRAIN) {
00656     assert(e->u.rail.railtype < RAILTYPE_END);
00657     c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes | GetRailTypeInfo(e->u.rail.railtype)->introduces_railtypes, _date);
00658   } else if (e->type == VEH_ROAD) {
00659     SetBit(c->avail_roadtypes, HasBit(e->info.misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD);
00660   }
00661 
00662   e->preview_company_rank = 0xFF;
00663   if (company == _local_company) {
00664     AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
00665   }
00666 
00667   /* Update the toolbar. */
00668   if (e->type == VEH_ROAD) InvalidateWindowData(WC_BUILD_TOOLBAR, TRANSPORT_ROAD);
00669   if (e->type == VEH_SHIP) InvalidateWindowData(WC_BUILD_TOOLBAR, TRANSPORT_WATER);
00670 }
00671 
00677 static CompanyID GetBestCompany(uint8 pp)
00678 {
00679   CompanyID best_company;
00680   CompanyMask mask = 0;
00681 
00682   do {
00683     int32 best_hist = -1;
00684     best_company = INVALID_COMPANY;
00685 
00686     const Company *c;
00687     FOR_ALL_COMPANIES(c) {
00688       if (c->block_preview == 0 && !HasBit(mask, c->index) &&
00689           c->old_economy[0].performance_history > best_hist) {
00690         best_hist = c->old_economy[0].performance_history;
00691         best_company = c->index;
00692       }
00693     }
00694 
00695     if (best_company == INVALID_COMPANY) return INVALID_COMPANY;
00696 
00697     SetBit(mask, best_company);
00698   } while (--pp != 0);
00699 
00700   return best_company;
00701 }
00702 
00704 void EnginesDailyLoop()
00705 {
00706   CheckRailIntroduction();
00707 
00708   if (_cur_year >= _year_engine_aging_stops) return;
00709 
00710   Engine *e;
00711   FOR_ALL_ENGINES(e) {
00712     EngineID i = e->index;
00713     if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) {
00714       if (e->flags & ENGINE_OFFER_WINDOW_OPEN) {
00715         if (e->preview_company_rank != 0xFF && !--e->preview_wait) {
00716           e->flags &= ~ENGINE_OFFER_WINDOW_OPEN;
00717           DeleteWindowById(WC_ENGINE_PREVIEW, i);
00718           e->preview_company_rank++;
00719         }
00720       } else if (e->preview_company_rank != 0xFF) {
00721         CompanyID best_company = GetBestCompany(e->preview_company_rank);
00722 
00723         if (best_company == INVALID_COMPANY) {
00724           e->preview_company_rank = 0xFF;
00725           continue;
00726         }
00727 
00728         e->flags |= ENGINE_OFFER_WINDOW_OPEN;
00729         e->preview_wait = 20;
00730         AI::NewEvent(best_company, new AIEventEnginePreview(i));
00731         if (IsInteractiveCompany(best_company)) ShowEnginePreviewWindow(i);
00732       }
00733     }
00734   }
00735 }
00736 
00747 CommandCost CmdWantEnginePreview(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00748 {
00749   Engine *e = Engine::GetIfValid(p1);
00750   if (e == NULL || GetBestCompany(e->preview_company_rank) != _current_company) return CMD_ERROR;
00751 
00752   if (flags & DC_EXEC) AcceptEnginePreview(p1, _current_company);
00753 
00754   return CommandCost();
00755 }
00756 
00762 static void NewVehicleAvailable(Engine *e)
00763 {
00764   Vehicle *v;
00765   Company *c;
00766   EngineID index = e->index;
00767 
00768   /* In case the company didn't build the vehicle during the intro period,
00769    * prevent that company from getting future intro periods for a while. */
00770   if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) {
00771     FOR_ALL_COMPANIES(c) {
00772       uint block_preview = c->block_preview;
00773 
00774       if (!HasBit(e->company_avail, c->index)) continue;
00775 
00776       /* We assume the user did NOT build it.. prove me wrong ;) */
00777       c->block_preview = 20;
00778 
00779       FOR_ALL_VEHICLES(v) {
00780         if (v->type == VEH_TRAIN || v->type == VEH_ROAD || v->type == VEH_SHIP ||
00781             (v->type == VEH_AIRCRAFT && Aircraft::From(v)->IsNormalAircraft())) {
00782           if (v->owner == c->index && v->engine_type == index) {
00783             /* The user did prove me wrong, so restore old value */
00784             c->block_preview = block_preview;
00785             break;
00786           }
00787         }
00788       }
00789     }
00790   }
00791 
00792   e->flags = (e->flags & ~ENGINE_EXCLUSIVE_PREVIEW) | ENGINE_AVAILABLE;
00793   AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
00794 
00795   /* Now available for all companies */
00796   e->company_avail = (CompanyMask)-1;
00797 
00798   /* Do not introduce new rail wagons */
00799   if (IsWagon(index)) return;
00800 
00801   if (e->type == VEH_TRAIN) {
00802     /* maybe make another rail type available */
00803     RailType railtype = e->u.rail.railtype;
00804     assert(railtype < RAILTYPE_END);
00805     FOR_ALL_COMPANIES(c) c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes | GetRailTypeInfo(e->u.rail.railtype)->introduces_railtypes, _date);
00806   } else if (e->type == VEH_ROAD) {
00807     /* maybe make another road type available */
00808     FOR_ALL_COMPANIES(c) SetBit(c->avail_roadtypes, HasBit(e->info.misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD);
00809   }
00810 
00811   AI::BroadcastNewEvent(new AIEventEngineAvailable(index));
00812 
00813   SetDParam(0, GetEngineCategoryName(index));
00814   SetDParam(1, index);
00815   AddNewsItem(STR_NEWS_NEW_VEHICLE_NOW_AVAILABLE_WITH_TYPE, NS_NEW_VEHICLES, NR_ENGINE, index);
00816 
00817   /* Update the toolbar. */
00818   if (e->type == VEH_ROAD) InvalidateWindowData(WC_BUILD_TOOLBAR, TRANSPORT_ROAD);
00819   if (e->type == VEH_SHIP) InvalidateWindowData(WC_BUILD_TOOLBAR, TRANSPORT_WATER);
00820 }
00821 
00822 void EnginesMonthlyLoop()
00823 {
00824   if (_cur_year < _year_engine_aging_stops) {
00825     Engine *e;
00826     FOR_ALL_ENGINES(e) {
00827       /* Age the vehicle */
00828       if ((e->flags & ENGINE_AVAILABLE) && e->age != MAX_DAY) {
00829         e->age++;
00830         CalcEngineReliability(e);
00831       }
00832 
00833       if (!(e->flags & ENGINE_AVAILABLE) && _date >= (e->intro_date + DAYS_IN_YEAR)) {
00834         /* Introduce it to all companies */
00835         NewVehicleAvailable(e);
00836       } else if (!(e->flags & (ENGINE_AVAILABLE | ENGINE_EXCLUSIVE_PREVIEW)) && _date >= e->intro_date) {
00837         /* Introduction date has passed.. show introducing dialog to one companies. */
00838         e->flags |= ENGINE_EXCLUSIVE_PREVIEW;
00839 
00840         /* Do not introduce new rail wagons */
00841         if (!IsWagon(e->index)) {
00842           e->preview_company_rank = 1; // Give to the company with the highest rating.
00843         }
00844       }
00845     }
00846   }
00847 }
00848 
00849 static bool IsUniqueEngineName(const char *name)
00850 {
00851   const Engine *e;
00852 
00853   FOR_ALL_ENGINES(e) {
00854     if (e->name != NULL && strcmp(e->name, name) == 0) return false;
00855   }
00856 
00857   return true;
00858 }
00859 
00869 CommandCost CmdRenameEngine(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00870 {
00871   Engine *e = Engine::GetIfValid(p1);
00872   if (e == NULL) return CMD_ERROR;
00873 
00874   bool reset = StrEmpty(text);
00875 
00876   if (!reset) {
00877     if (Utf8StringLength(text) >= MAX_LENGTH_ENGINE_NAME_CHARS) return CMD_ERROR;
00878     if (!IsUniqueEngineName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE);
00879   }
00880 
00881   if (flags & DC_EXEC) {
00882     free(e->name);
00883 
00884     if (reset) {
00885       e->name = NULL;
00886     } else {
00887       e->name = strdup(text);
00888     }
00889 
00890     MarkWholeScreenDirty();
00891   }
00892 
00893   return CommandCost();
00894 }
00895 
00896 
00905 bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company)
00906 {
00907   const Engine *e = Engine::GetIfValid(engine);
00908 
00909   /* check if it's an engine that is in the engine array */
00910   if (e == NULL) return false;
00911 
00912   /* check if it's an engine of specified type */
00913   if (e->type != type) return false;
00914 
00915   /* check if it's available */
00916   if (!HasBit(e->company_avail, company)) return false;
00917 
00918   if (e->info.string_id == STR_NEWGRF_INVALID_ENGINE) return false;
00919 
00920   if (type == VEH_TRAIN) {
00921     /* Check if the rail type is available to this company */
00922     const Company *c = Company::Get(company);
00923     if (((GetRailTypeInfo(e->u.rail.railtype))->compatible_railtypes & c->avail_railtypes) == 0) return false;
00924   }
00925 
00926   return true;
00927 }
00928 
00935 bool IsEngineRefittable(EngineID engine)
00936 {
00937   const Engine *e = Engine::GetIfValid(engine);
00938 
00939   /* check if it's an engine that is in the engine array */
00940   if (e == NULL) return false;
00941 
00942   if (!e->CanCarryCargo()) return false;
00943 
00944   const EngineInfo *ei = &e->info;
00945   if (ei->refit_mask == 0) return false;
00946 
00947   /* Are there suffixes?
00948    * Note: This does not mean the suffixes are actually available for every consist at any time. */
00949   if (HasBit(ei->callback_mask, CBM_VEHICLE_CARGO_SUFFIX)) return true;
00950 
00951   /* Is there any cargo except the default cargo? */
00952   CargoID default_cargo = e->GetDefaultCargoType();
00953   return default_cargo != CT_INVALID && ei->refit_mask != 1U << default_cargo;
00954 }

Generated on Fri Feb 4 20:53:40 2011 for OpenTTD by  doxygen 1.6.1