# engine.cpp

Go to the documentation of this file.

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