# engine.cpp

Go to the documentation of this file.

    00001 /* $Id: engine.cpp 18330 2009-11-28 20:35:25Z 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 "gfx_func.h"
    00022 #include "core/random_func.hpp"
    00023 #include "window_func.h"
    00024 #include "date_func.h"
    00025 #include "autoreplace_gui.h"
    00026 #include "string_func.h"
    00027 #include "ai/ai.hpp"
    00028 #include "core/pool_func.hpp"
    00029 #include "engine_gui.h"
    00030
    00031 #include "table/strings.h"
    00032 #include "table/engines.h"
    00033
    00034 EnginePool _engine_pool("Engine");
    00035 INSTANTIATE_POOL_METHODS(Engine)
    00036
    00037 EngineOverrideManager _engine_mngr;
    00038
    00041 static Year _year_engine_aging_stops;
    00042
    00044 const uint8 _engine_counts[4] = {
    00045   lengthof(_orig_rail_vehicle_info),
    00046   lengthof(_orig_road_vehicle_info),
    00047   lengthof(_orig_ship_vehicle_info),
    00048   lengthof(_orig_aircraft_vehicle_info),
    00049 };
    00050
    00052 const uint8 _engine_offsets[4] = {
    00053   0,
    00054   lengthof(_orig_rail_vehicle_info),
    00055   lengthof(_orig_rail_vehicle_info) + lengthof(_orig_road_vehicle_info),
    00056   lengthof(_orig_rail_vehicle_info) + lengthof(_orig_road_vehicle_info) + lengthof(_orig_ship_vehicle_info),
    00057 };
    00058
    00059 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));
    00060
    00061 const uint EngineOverrideManager::NUM_DEFAULT_ENGINES = _engine_counts[VEH_TRAIN] + _engine_counts[VEH_ROAD] + _engine_counts[VEH_SHIP] + _engine_counts[VEH_AIRCRAFT];
    00062
    00063 Engine::Engine() :
    00064   name(NULL),
    00065   overrides_count(0),
    00066   overrides(NULL)
    00067 {
    00068 }
    00069
    00070 Engine::Engine(VehicleType type, EngineID base)
    00071 {
    00072   this->type = type;
    00073   this->internal_id = base;
    00074   this->list_position = base;
    00075
    00076   /* Check if this base engine is within the original engine data range */
    00077   if (base >= _engine_counts[type]) {
    00078     /* Mark engine as valid anyway */
    00079     this->info.climates = 0x80;
    00080     /* Set model life to maximum to make wagons available */
    00081     this->info.base_life = 0xFF;
    00082     return;
    00083   }
    00084
    00085   /* Copy the original engine info for this slot */
    00086   this->info = _orig_engine_info[_engine_offsets[type] + base];
    00087
    00088   /* Copy the original engine data for this slot */
    00089   switch (type) {
    00090     default: NOT_REACHED();
    00091
    00092     case VEH_TRAIN:
    00093       this->u.rail = _orig_rail_vehicle_info[base];
    00094       this->original_image_index = this->u.rail.image_index;
    00095       this->info.string_id = STR_VEHICLE_NAME_TRAIN_ENGINE_RAIL_KIRBY_PAUL_TANK_STEAM + base;
    00096
    00097       /* Set the default model life of original wagons to "infinite" */
    00098       if (this->u.rail.railveh_type == RAILVEH_WAGON) this->info.base_life = 0xFF;
    00099
    00100       break;
    00101
    00102     case VEH_ROAD:
    00103       this->u.road = _orig_road_vehicle_info[base];
    00104       this->original_image_index = this->u.road.image_index;
    00105       this->info.string_id = STR_VEHICLE_NAME_ROAD_VEHICLE_MPS_REGAL_BUS + base;
    00106       break;
    00107
    00108     case VEH_SHIP:
    00109       this->u.ship = _orig_ship_vehicle_info[base];
    00110       this->original_image_index = this->u.ship.image_index;
    00111       this->info.string_id = STR_VEHICLE_NAME_SHIP_MPS_OIL_TANKER + base;
    00112       break;
    00113
    00114     case VEH_AIRCRAFT:
    00115       this->u.air = _orig_aircraft_vehicle_info[base];
    00116       this->original_image_index = this->u.air.image_index;
    00117       this->info.string_id = STR_VEHICLE_NAME_AIRCRAFT_SAMPSON_U52 + base;
    00118       break;
    00119   }
    00120 }
    00121
    00122 Engine::~Engine()
    00123 {
    00124   UnloadWagonOverrides(this);
    00125   free(this->name);
    00126 }
    00127
    00133 bool Engine::CanCarryCargo() const
    00134 {
    00135   /* For engines that can appear in a consist (i.e. rail vehicles and (articulated) road vehicles), a capacity
    00136    * of zero is a special case, to define the vehicle to not carry anything. The default cargotype is still used
    00137    * for livery selection etc.
    00138    * Note: Only the property is tested. A capacity callback returning 0 does not have the same effect.
    00139    */
    00140   switch (this->type) {
    00141     case VEH_TRAIN:
    00142       if (this->u.rail.capacity == 0) return false;
    00143       break;
    00144
    00145     case VEH_ROAD:
    00146       if (this->u.road.capacity == 0) return false;
    00147       break;
    00148
    00149     case VEH_SHIP:
    00150     case VEH_AIRCRAFT:
    00151       break;
    00152
    00153     default: NOT_REACHED();
    00154   }
    00155   return this->GetDefaultCargoType() != CT_INVALID;
    00156 }
    00157
    00170 uint Engine::GetDisplayDefaultCapacity(uint16 *mail_capacity) const
    00171 {
    00172   if (mail_capacity != NULL) *mail_capacity = 0;
    00173   if (!this->CanCarryCargo()) return 0;
    00174   switch (type) {
    00175     case VEH_TRAIN:
    00176       return GetEngineProperty(this->index, PROP_TRAIN_CARGO_CAPACITY, this->u.rail.capacity) + (this->u.rail.railveh_type == RAILVEH_MULTIHEAD ? this->u.rail.capacity : 0);
    00177
    00178     case VEH_ROAD:
    00179       return GetEngineProperty(this->index, PROP_ROADVEH_CARGO_CAPACITY, this->u.road.capacity);
    00180
    00181     case VEH_SHIP:
    00182       return GetEngineProperty(this->index, PROP_SHIP_CARGO_CAPACITY, this->u.ship.capacity);
    00183
    00184     case VEH_AIRCRAFT: {
    00185       uint capacity = this->u.air.passenger_capacity;
    00186       CargoID cargo = this->GetDefaultCargoType();
    00187       if (IsCargoInClass(cargo, CC_PASSENGERS)) {
    00188         if (mail_capacity != NULL) *mail_capacity = this->u.air.mail_capacity;
    00189       } else {
    00190         capacity += this->u.air.mail_capacity;
    00191       }
    00192       switch (cargo) {
    00193         case CT_PASSENGERS:
    00194         case CT_MAIL:       return capacity;
    00195         case CT_GOODS:      return capacity / 2;
    00196         default:            return capacity / 4;
    00197       }
    00198     }
    00199
    00200     default: NOT_REACHED();
    00201   }
    00202 }
    00203
    00204 Money Engine::GetRunningCost() const
    00205 {
    00206   Price base_price;
    00207   uint cost_factor;
    00208   switch (this->type) {
    00209     case VEH_ROAD:
    00210       base_price = this->u.road.running_cost_class;
    00211       if (base_price == INVALID_PRICE) return 0;
    00212       cost_factor = GetEngineProperty(this->index, PROP_ROADVEH_RUNNING_COST_FACTOR, this->u.road.running_cost);
    00213       break;
    00214
    00215     case VEH_TRAIN:
    00216       base_price = this->u.rail.running_cost_class;
    00217       if (base_price == INVALID_PRICE) return 0;
    00218       cost_factor = GetEngineProperty(this->index, PROP_TRAIN_RUNNING_COST_FACTOR, this->u.rail.running_cost);
    00219       break;
    00220
    00221     case VEH_SHIP:
    00222       base_price = PR_RUNNING_SHIP;
    00223       cost_factor = GetEngineProperty(this->index, PROP_SHIP_RUNNING_COST_FACTOR, this->u.ship.running_cost);
    00224       break;
    00225
    00226     case VEH_AIRCRAFT:
    00227       base_price = PR_RUNNING_AIRCRAFT;
    00228       cost_factor = GetEngineProperty(this->index, PROP_AIRCRAFT_RUNNING_COST_FACTOR, this->u.air.running_cost);
    00229       break;
    00230
    00231     default: NOT_REACHED();
    00232   }
    00233
    00234   return GetPrice(base_price, cost_factor, this->grffile, -8);
    00235 }
    00236
    00237 Money Engine::GetCost() const
    00238 {
    00239   Price base_price;
    00240   uint cost_factor;
    00241   switch (this->type) {
    00242     case VEH_ROAD:
    00243       base_price = PR_BUILD_VEHICLE_ROAD;
    00244       cost_factor = GetEngineProperty(this->index, PROP_ROADVEH_COST_FACTOR, this->u.road.cost_factor);
    00245       break;
    00246
    00247     case VEH_TRAIN:
    00248       if (this->u.rail.railveh_type == RAILVEH_WAGON) {
    00249         base_price = PR_BUILD_VEHICLE_WAGON;
    00250         cost_factor = GetEngineProperty(this->index, PROP_TRAIN_COST_FACTOR, this->u.rail.cost_factor);
    00251       } else {
    00252         base_price = PR_BUILD_VEHICLE_TRAIN;
    00253         cost_factor = GetEngineProperty(this->index, PROP_TRAIN_COST_FACTOR, this->u.rail.cost_factor);
    00254       }
    00255       break;
    00256
    00257     case VEH_SHIP:
    00258       base_price = PR_BUILD_VEHICLE_SHIP;
    00259       cost_factor = GetEngineProperty(this->index, PROP_SHIP_COST_FACTOR, this->u.ship.cost_factor);
    00260       break;
    00261
    00262     case VEH_AIRCRAFT:
    00263       base_price = PR_BUILD_VEHICLE_AIRCRAFT;
    00264       cost_factor = GetEngineProperty(this->index, PROP_AIRCRAFT_COST_FACTOR, this->u.air.cost_factor);
    00265       break;
    00266
    00267     default: NOT_REACHED();
    00268   }
    00269
    00270   return GetPrice(base_price, cost_factor, this->grffile, -8);
    00271 }
    00272
    00277 uint Engine::GetDisplayMaxSpeed() const
    00278 {
    00279   switch (this->type) {
    00280     case VEH_TRAIN:
    00281       return GetEngineProperty(this->index, PROP_TRAIN_SPEED, this->u.rail.max_speed);
    00282
    00283     case VEH_ROAD:
    00284       return this->u.road.max_speed / 2;
    00285
    00286     case VEH_SHIP:
    00287       return GetEngineProperty(this->index, PROP_SHIP_SPEED, this->u.ship.max_speed) / 2;
    00288
    00289     case VEH_AIRCRAFT:
    00290       return this->u.air.max_speed;
    00291
    00292     default: NOT_REACHED();
    00293   }
    00294 }
    00295
    00296 uint Engine::GetPower() const
    00297 {
    00298   /* Currently only trains have 'power' */
    00299   switch (this->type) {
    00300     case VEH_TRAIN:
    00301       return GetEngineProperty(this->index, PROP_TRAIN_POWER, this->u.rail.power);
    00302
    00303     default: NOT_REACHED();
    00304   }
    00305 }
    00306
    00312 uint Engine::GetDisplayWeight() const
    00313 {
    00314   /* Currently only trains have 'weight' */
    00315   switch (this->type) {
    00316     case VEH_TRAIN:
    00317       return GetEngineProperty(this->index, PROP_TRAIN_WEIGHT, this->u.rail.weight) << (this->u.rail.railveh_type == RAILVEH_MULTIHEAD ? 1 : 0);
    00318
    00319     default: NOT_REACHED();
    00320   }
    00321 }
    00322
    00328 uint Engine::GetDisplayMaxTractiveEffort() const
    00329 {
    00330   /* Currently only trains have 'tractive effort' */
    00331   switch (this->type) {
    00332     case VEH_TRAIN:
    00333       return (10 * this->GetDisplayWeight() * GetEngineProperty(this->index, PROP_TRAIN_TRACTIVE_EFFORT, this->u.rail.tractive_effort)) / 256;
    00334
    00335     default: NOT_REACHED();
    00336   }
    00337 }
    00338
    00343 Date Engine::GetLifeLengthInDays() const
    00344 {
    00345   /* Assume leap years; this gives the player a bit more than the given amount of years, but never less. */
    00346   return (this->info.lifelength + _settings_game.vehicle.extend_vehicle_life) * DAYS_IN_LEAP_YEAR;
    00347 }
    00348
    00352 void EngineOverrideManager::ResetToDefaultMapping()
    00353 {
    00354   this->Clear();
    00355   for (VehicleType type = VEH_TRAIN; type <= VEH_AIRCRAFT; type++) {
    00356     for (uint internal_id = 0; internal_id < _engine_counts[type]; internal_id++) {
    00357       EngineIDMapping *eid = this->Append();
    00358       eid->type            = type;
    00359       eid->grfid           = INVALID_GRFID;
    00360       eid->internal_id     = internal_id;
    00361       eid->substitute_id   = internal_id;
    00362     }
    00363   }
    00364 }
    00365
    00375 EngineID EngineOverrideManager::GetID(VehicleType type, uint16 grf_local_id, uint32 grfid)
    00376 {
    00377   const EngineIDMapping *end = this->End();
    00378   EngineID index = 0;
    00379   for (const EngineIDMapping *eid = this->Begin(); eid != end; eid++, index++) {
    00380     if (eid->type == type && eid->grfid == grfid && eid->internal_id == grf_local_id) {
    00381       return index;
    00382     }
    00383   }
    00384   return INVALID_ENGINE;
    00385 }
    00386
    00389 void SetCachedEngineCounts()
    00390 {
    00391   size_t engines = Engine::GetPoolSize();
    00392
    00393   /* Set up the engine count for all companies */
    00394   Company *c;
    00395   FOR_ALL_COMPANIES(c) {
    00396     free(c->num_engines);
    00397     c->num_engines = CallocT<EngineID>(engines);
    00398   }
    00399
    00400   /* Recalculate */
    00401   Group *g;
    00402   FOR_ALL_GROUPS(g) {
    00403     free(g->num_engines);
    00404     g->num_engines = CallocT<EngineID>(engines);
    00405   }
    00406
    00407   const Vehicle *v;
    00408   FOR_ALL_VEHICLES(v) {
    00409     if (!v->IsEngineCountable()) continue;
    00410
    00411     assert(v->engine_type < engines);
    00412
    00413     Company::Get(v->owner)->num_engines[v->engine_type]++;
    00414
    00415     if (v->group_id == DEFAULT_GROUP) continue;
    00416
    00417     g = Group::Get(v->group_id);
    00418     assert(v->type == g->vehicle_type);
    00419     assert(v->owner == g->owner);
    00420
    00421     g->num_engines[v->engine_type]++;
    00422   }
    00423 }
    00424
    00425 void SetupEngines()
    00426 {
    00427   _engine_pool.CleanPool();
    00428
    00429   assert(_engine_mngr.Length() >= _engine_mngr.NUM_DEFAULT_ENGINES);
    00430   const EngineIDMapping *end = _engine_mngr.End();
    00431   uint index = 0;
    00432   for (const EngineIDMapping *eid = _engine_mngr.Begin(); eid != end; eid++, index++) {
    00433     const Engine *e = new Engine(eid->type, eid->internal_id);
    00434     assert(e->index == index);
    00435   }
    00436 }
    00437
    00438
    00439 void ShowEnginePreviewWindow(EngineID engine);
    00440
    00441 /* Determine if an engine type is a wagon (and not a loco) */
    00442 static bool IsWagon(EngineID index)
    00443 {
    00444   const Engine *e = Engine::Get(index);
    00445   return e->type == VEH_TRAIN && e->u.rail.railveh_type == RAILVEH_WAGON;
    00446 }
    00447
    00448 static void CalcEngineReliability(Engine *e)
    00449 {
    00450   uint age = e->age;
    00451
    00452   /* Check for early retirement */
    00453   if (e->company_avail != 0 && !_settings_game.vehicle.never_expire_vehicles && e->info.base_life != 0xFF) {
    00454     int retire_early = e->info.retire_early;
    00455     uint retire_early_max_age = max(0, e->duration_phase_1 + e->duration_phase_2 - retire_early * 12);
    00456     if (retire_early != 0 && age >= retire_early_max_age) {
    00457       /* Early retirement is enabled and we're past the date... */
    00458       e->company_avail = 0;
    00459       AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
    00460     }
    00461   }
    00462
    00463   if (age < e->duration_phase_1) {
    00464     uint start = e->reliability_start;
    00465     e->reliability = age * (e->reliability_max - start) / e->duration_phase_1 + start;
    00466   } else if ((age -= e->duration_phase_1) < e->duration_phase_2 || _settings_game.vehicle.never_expire_vehicles || e->info.base_life == 0xFF) {
    00467     /* We are at the peak of this engines life. It will have max reliability.
    00468      * This is also true if the engines never expire. They will not go bad over time */
    00469     e->reliability = e->reliability_max;
    00470   } else if ((age -= e->duration_phase_2) < e->duration_phase_3) {
    00471     uint max = e->reliability_max;
    00472     e->reliability = (int)age * (int)(e->reliability_final - max) / e->duration_phase_3 + max;
    00473   } else {
    00474     /* time's up for this engine.
    00475      * We will now completely retire this design */
    00476     e->company_avail = 0;
    00477     e->reliability = e->reliability_final;
    00478     /* Kick this engine out of the lists */
    00479     AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
    00480   }
    00481   SetWindowClassesDirty(WC_BUILD_VEHICLE); // Update to show the new reliability
    00482   SetWindowClassesDirty(WC_REPLACE_VEHICLE);
    00483 }
    00484
    00485 void SetYearEngineAgingStops()
    00486 {
    00487   /* Determine last engine aging year, default to 2050 as previously. */
    00488   _year_engine_aging_stops = 2050;
    00489
    00490   const Engine *e;
    00491   FOR_ALL_ENGINES(e) {
    00492     const EngineInfo *ei = &e->info;
    00493
    00494     /* Exclude certain engines */
    00495     if (!HasBit(ei->climates, _settings_game.game_creation.landscape)) continue;
    00496     if (e->type == VEH_TRAIN && e->u.rail.railveh_type == RAILVEH_WAGON) continue;
    00497
    00498     /* Base year ending date on half the model life */
    00499     YearMonthDay ymd;
    00500     ConvertDateToYMD(ei->base_intro + (ei->lifelength * DAYS_IN_LEAP_YEAR) / 2, &ymd);
    00501
    00502     _year_engine_aging_stops = max(_year_engine_aging_stops, ymd.year);
    00503   }
    00504 }
    00505
    00506 void StartupOneEngine(Engine *e, Date aging_date)
    00507 {
    00508   const EngineInfo *ei = &e->info;
    00509   uint32 r;
    00510
    00511   e->age = 0;
    00512   e->flags = 0;
    00513   e->company_avail = 0;
    00514
    00515   /* Don't randomise the start-date in the first two years after gamestart to ensure availability
    00516    * of engines in early starting games.
    00517    * Note: TTDP uses fixed 1922 */
    00518   r = Random();
    00519   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;
    00520   if (e->intro_date <= _date) {
    00521     e->age = (aging_date - e->intro_date) >> 5;
    00522     e->company_avail = (CompanyMask)-1;
    00523     e->flags |= ENGINE_AVAILABLE;
    00524   }
    00525
    00526   e->reliability_start = GB(r, 16, 14) + 0x7AE0;
    00527   r = Random();
    00528   e->reliability_max   = GB(r,  0, 14) + 0xBFFF;
    00529   e->reliability_final = GB(r, 16, 14) + 0x3FFF;
    00530
    00531   r = Random();
    00532   e->duration_phase_1 = GB(r, 0, 5) + 7;
    00533   e->duration_phase_2 = GB(r, 5, 4) + ei->base_life * 12 - 96;
    00534   e->duration_phase_3 = GB(r, 9, 7) + 120;
    00535
    00536   e->reliability_spd_dec = ei->decay_speed << 2;
    00537
    00538   CalcEngineReliability(e);
    00539
    00540   /* prevent certain engines from ever appearing. */
    00541   if (!HasBit(ei->climates, _settings_game.game_creation.landscape)) {
    00542     e->flags |= ENGINE_AVAILABLE;
    00543     e->company_avail = 0;
    00544   }
    00545 }
    00546
    00547 void StartupEngines()
    00548 {
    00549   Engine *e;
    00550   /* Aging of vehicles stops, so account for that when starting late */
    00551   const Date aging_date = min(_date, ConvertYMDToDate(_year_engine_aging_stops, 0, 1));
    00552
    00553   FOR_ALL_ENGINES(e) {
    00554     StartupOneEngine(e, aging_date);
    00555   }
    00556
    00557   /* Update the bitmasks for the vehicle lists */
    00558   Company *c;
    00559   FOR_ALL_COMPANIES(c) {
    00560     c->avail_railtypes = GetCompanyRailtypes(c->index);
    00561     c->avail_roadtypes = GetCompanyRoadtypes(c->index);
    00562   }
    00563 }
    00564
    00565 static void AcceptEnginePreview(EngineID eid, CompanyID company)
    00566 {
    00567   Engine *e = Engine::Get(eid);
    00568   Company *c = Company::Get(company);
    00569
    00570   SetBit(e->company_avail, company);
    00571   if (e->type == VEH_TRAIN) {
    00572     assert(e->u.rail.railtype < RAILTYPE_END);
    00573     SetBit(c->avail_railtypes, e->u.rail.railtype);
    00574   } else if (e->type == VEH_ROAD) {
    00575     SetBit(c->avail_roadtypes, HasBit(e->info.misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD);
    00576   }
    00577
    00578   e->preview_company_rank = 0xFF;
    00579   if (company == _local_company) {
    00580     AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
    00581   }
    00582 }
    00583
    00584 static CompanyID GetBestCompany(uint8 pp)
    00585 {
    00586   const Company *c;
    00587   int32 best_hist;
    00588   CompanyID best_company;
    00589   CompanyMask mask = 0;
    00590
    00591   do {
    00592     best_hist = -1;
    00593     best_company = INVALID_COMPANY;
    00594     FOR_ALL_COMPANIES(c) {
    00595       if (c->block_preview == 0 && !HasBit(mask, c->index) &&
    00596           c->old_economy[0].performance_history > best_hist) {
    00597         best_hist = c->old_economy[0].performance_history;
    00598         best_company = c->index;
    00599       }
    00600     }
    00601
    00602     if (best_company == INVALID_COMPANY) return INVALID_COMPANY;
    00603
    00604     SetBit(mask, best_company);
    00605   } while (--pp != 0);
    00606
    00607   return best_company;
    00608 }
    00609
    00610 void EnginesDailyLoop()
    00611 {
    00612   if (_cur_year >= _year_engine_aging_stops) return;
    00613
    00614   Engine *e;
    00615   FOR_ALL_ENGINES(e) {
    00616     EngineID i = e->index;
    00617     if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) {
    00618       if (e->flags & ENGINE_OFFER_WINDOW_OPEN) {
    00619         if (e->preview_company_rank != 0xFF && !--e->preview_wait) {
    00620           e->flags &= ~ENGINE_OFFER_WINDOW_OPEN;
    00621           DeleteWindowById(WC_ENGINE_PREVIEW, i);
    00622           e->preview_company_rank++;
    00623         }
    00624       } else if (e->preview_company_rank != 0xFF) {
    00625         CompanyID best_company = GetBestCompany(e->preview_company_rank);
    00626
    00627         if (best_company == INVALID_COMPANY) {
    00628           e->preview_company_rank = 0xFF;
    00629           continue;
    00630         }
    00631
    00632         e->flags |= ENGINE_OFFER_WINDOW_OPEN;
    00633         e->preview_wait = 20;
    00634         AI::NewEvent(best_company, new AIEventEnginePreview(i));
    00635         if (IsInteractiveCompany(best_company)) ShowEnginePreviewWindow(i);
    00636       }
    00637     }
    00638   }
    00639 }
    00640
    00650 CommandCost CmdWantEnginePreview(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00651 {
    00652   Engine *e = Engine::GetIfValid(p1);
    00653   if (e == NULL || GetBestCompany(e->preview_company_rank) != _current_company) return CMD_ERROR;
    00654
    00655   if (flags & DC_EXEC) AcceptEnginePreview(p1, _current_company);
    00656
    00657   return CommandCost();
    00658 }
    00659
    00660 static void NewVehicleAvailable(Engine *e)
    00661 {
    00662   Vehicle *v;
    00663   Company *c;
    00664   EngineID index = e->index;
    00665
    00666   /* In case the company didn't build the vehicle during the intro period,
    00667    * prevent that company from getting future intro periods for a while. */
    00668   if (e->flags & ENGINE_EXCLUSIVE_PREVIEW) {
    00669     FOR_ALL_COMPANIES(c) {
    00670       uint block_preview = c->block_preview;
    00671
    00672       if (!HasBit(e->company_avail, c->index)) continue;
    00673
    00674       /* We assume the user did NOT build it.. prove me wrong ;) */
    00675       c->block_preview = 20;
    00676
    00677       FOR_ALL_VEHICLES(v) {
    00678         if (v->type == VEH_TRAIN || v->type == VEH_ROAD || v->type == VEH_SHIP ||
    00679             (v->type == VEH_AIRCRAFT && Aircraft::From(v)->IsNormalAircraft())) {
    00680           if (v->owner == c->index && v->engine_type == index) {
    00681             /* The user did prove me wrong, so restore old value */
    00682             c->block_preview = block_preview;
    00683             break;
    00684           }
    00685         }
    00686       }
    00687     }
    00688   }
    00689
    00690   e->flags = (e->flags & ~ENGINE_EXCLUSIVE_PREVIEW) | ENGINE_AVAILABLE;
    00691   AddRemoveEngineFromAutoreplaceAndBuildWindows(e->type);
    00692
    00693   /* Now available for all companies */
    00694   e->company_avail = (CompanyMask)-1;
    00695
    00696   /* Do not introduce new rail wagons */
    00697   if (IsWagon(index)) return;
    00698
    00699   if (e->type == VEH_TRAIN) {
    00700     /* maybe make another rail type available */
    00701     RailType railtype = e->u.rail.railtype;
    00702     assert(railtype < RAILTYPE_END);
    00703     FOR_ALL_COMPANIES(c) SetBit(c->avail_railtypes, railtype);
    00704   } else if (e->type == VEH_ROAD) {
    00705     /* maybe make another road type available */
    00706     FOR_ALL_COMPANIES(c) SetBit(c->avail_roadtypes, HasBit(e->info.misc_flags, EF_ROAD_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD);
    00707   }
    00708
    00709   AI::BroadcastNewEvent(new AIEventEngineAvailable(index));
    00710
    00711   SetDParam(0, GetEngineCategoryName(index));
    00712   SetDParam(1, index);
    00713   AddNewsItem(STR_NEWS_NEW_VEHICLE_NOW_AVAILABLE_WITH_TYPE, NS_NEW_VEHICLES, NR_ENGINE, index);
    00714 }
    00715
    00716 void EnginesMonthlyLoop()
    00717 {
    00718   if (_cur_year < _year_engine_aging_stops) {
    00719     Engine *e;
    00720     FOR_ALL_ENGINES(e) {
    00721       /* Age the vehicle */
    00722       if ((e->flags & ENGINE_AVAILABLE) && e->age != 0xFFFF) {
    00723         e->age++;
    00724         CalcEngineReliability(e);
    00725       }
    00726
    00727       if (!(e->flags & ENGINE_AVAILABLE) && _date >= (e->intro_date + DAYS_IN_YEAR)) {
    00728         /* Introduce it to all companies */
    00729         NewVehicleAvailable(e);
    00730       } else if (!(e->flags & (ENGINE_AVAILABLE | ENGINE_EXCLUSIVE_PREVIEW)) && _date >= e->intro_date) {
    00731         /* Introduction date has passed.. show introducing dialog to one companies. */
    00732         e->flags |= ENGINE_EXCLUSIVE_PREVIEW;
    00733
    00734         /* Do not introduce new rail wagons */
    00735         if (!IsWagon(e->index))
    00736           e->preview_company_rank = 1; // Give to the company with the highest rating.
    00737       }
    00738     }
    00739   }
    00740 }
    00741
    00742 static bool IsUniqueEngineName(const char *name)
    00743 {
    00744   const Engine *e;
    00745
    00746   FOR_ALL_ENGINES(e) {
    00747     if (e->name != NULL && strcmp(e->name, name) == 0) return false;
    00748   }
    00749
    00750   return true;
    00751 }
    00752
    00761 CommandCost CmdRenameEngine(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
    00762 {
    00763   Engine *e = Engine::GetIfValid(p1);
    00764   if (e == NULL) return CMD_ERROR;
    00765
    00766   bool reset = StrEmpty(text);
    00767
    00768   if (!reset) {
    00769     if (strlen(text) >= MAX_LENGTH_ENGINE_NAME_BYTES) return CMD_ERROR;
    00770     if (!IsUniqueEngineName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE);
    00771   }
    00772
    00773   if (flags & DC_EXEC) {
    00774     free(e->name);
    00775
    00776     if (reset) {
    00777       e->name = NULL;
    00778     } else {
    00779       e->name = strdup(text);
    00780     }
    00781
    00782     MarkWholeScreenDirty();
    00783   }
    00784
    00785   return CommandCost();
    00786 }
    00787
    00788
    00796 bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company)
    00797 {
    00798   const Engine *e = Engine::GetIfValid(engine);
    00799
    00800   /* check if it's an engine that is in the engine array */
    00801   if (e == NULL) return false;
    00802
    00803   /* check if it's an engine of specified type */
    00804   if (e->type != type) return false;
    00805
    00806   /* check if it's available */
    00807   if (!HasBit(e->company_avail, company)) return false;
    00808
    00809   if (type == VEH_TRAIN) {
    00810     /* Check if the rail type is available to this company */
    00811     const Company *c = Company::Get(company);
    00812     if (((GetRailTypeInfo(e->u.rail.railtype))->compatible_railtypes & c->avail_railtypes) == 0) return false;
    00813   }
    00814
    00815   return true;
    00816 }
    00817
    00824 bool IsEngineRefittable(EngineID engine)
    00825 {
    00826   const Engine *e = Engine::GetIfValid(engine);
    00827
    00828   /* check if it's an engine that is in the engine array */
    00829   if (e == NULL) return false;
    00830
    00831   if (!e->CanCarryCargo()) return false;
    00832
    00833   const EngineInfo *ei = &e->info;
    00834   if (ei->refit_mask == 0) return false;
    00835
    00836   /* Are there suffixes?
    00837    * Note: This does not mean the suffixes are actually available for every consist at any time. */
    00838   if (HasBit(ei->callback_mask, CBM_VEHICLE_CARGO_SUFFIX)) return true;
    00839
    00840   /* Is there any cargo except the default cargo? */
    00841   CargoID default_cargo = e->GetDefaultCargoType();
    00842   return default_cargo != CT_INVALID && ei->refit_mask != 1U << default_cargo;
    00843 }