OpenTTD Source 20260206-master-g4d4e37dbf1
engine.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "stdafx.h"
12#include "company_func.h"
13#include "command_func.h"
14#include "news_func.h"
15#include "aircraft.h"
16#include "newgrf.h"
17#include "newgrf_engine.h"
18#include "strings_func.h"
19#include "core/random_func.hpp"
20#include "window_func.h"
21#include "autoreplace_gui.h"
22#include "string_func.h"
23#include "ai/ai.hpp"
24#include "core/pool_func.hpp"
25#include "engine_gui.h"
26#include "engine_func.h"
27#include "engine_base.h"
28#include "company_base.h"
29#include "vehicle_func.h"
31#include "error.h"
32#include "engine_base.h"
33#include "timer/timer.h"
36
37#include "table/strings.h"
38#include "table/engines.h"
39
40#include "safeguards.h"
41
42EnginePool _engine_pool("Engine");
44
45EngineOverrideManager _engine_mngr;
46
51static TimerGameCalendar::Year _year_engine_aging_stops;
52
54const uint8_t _engine_counts[4] = {
55 lengthof(_orig_rail_vehicle_info),
56 lengthof(_orig_road_vehicle_info),
57 lengthof(_orig_ship_vehicle_info),
58 lengthof(_orig_aircraft_vehicle_info),
59};
60
62const uint8_t _engine_offsets[4] = {
63 0,
64 lengthof(_orig_rail_vehicle_info),
65 lengthof(_orig_rail_vehicle_info) + lengthof(_orig_road_vehicle_info),
66 lengthof(_orig_rail_vehicle_info) + lengthof(_orig_road_vehicle_info) + lengthof(_orig_ship_vehicle_info),
67};
68
69static_assert(lengthof(_orig_rail_vehicle_info) + lengthof(_orig_road_vehicle_info) + lengthof(_orig_ship_vehicle_info) + lengthof(_orig_aircraft_vehicle_info) == lengthof(_orig_engine_info));
70
71Engine::Engine(EngineID index, VehicleType type, uint16_t local_id) : EnginePool::PoolItem<&_engine_pool>(index)
72{
73 this->type = type;
74
75 /* Called in the context of loading a savegame. The rest comes from the loader. */
76 if (type == VEH_INVALID) return;
77
78 this->grf_prop.local_id = local_id;
79 this->list_position = local_id;
80 this->preview_company = CompanyID::Invalid();
81 this->display_last_variant = EngineID::Invalid();
82
83 /* Check if this base engine is within the original engine data range */
84 if (local_id >= _engine_counts[type]) {
85 /* Initialise default type-specific information. */
86 switch (type) {
87 case VEH_TRAIN: this->vehicle_info.emplace<RailVehicleInfo>(); break;
88 case VEH_ROAD: this->vehicle_info.emplace<RoadVehicleInfo>(); break;
89 case VEH_SHIP: this->vehicle_info.emplace<ShipVehicleInfo>(); break;
90 case VEH_AIRCRAFT: this->vehicle_info.emplace<AircraftVehicleInfo>(); break;
91 default: break;
92 }
93 /* Set model life to maximum to make wagons available */
94 this->info.base_life = TimerGameCalendar::Year{0xFF};
95 /* Aircraft must have CT_INVALID as default, as there is no property */
96 this->info.cargo_type = INVALID_CARGO;
97 this->info.cargo_label = (type == VEH_AIRCRAFT) ? CT_INVALID : CT_PASSENGERS;
98 /* Set cargo aging period to the default value. */
99 this->info.cargo_age_period = Ticks::CARGO_AGING_TICKS;
100 /* Not a variant */
101 this->info.variant_id = EngineID::Invalid();
102 return;
103 }
104
105 /* Copy the original engine info for this slot */
106 this->info = _orig_engine_info[_engine_offsets[type] + local_id];
107
108 /* Copy the original engine data for this slot */
109 switch (type) {
110 default: NOT_REACHED();
111
112 case VEH_TRAIN: {
113 RailVehicleInfo &rvi = this->vehicle_info.emplace<RailVehicleInfo>(_orig_rail_vehicle_info[local_id]);
114 this->original_image_index = rvi.image_index;
115 this->info.string_id = STR_VEHICLE_NAME_TRAIN_ENGINE_RAIL_KIRBY_PAUL_TANK_STEAM + local_id;
116
117 /* Set the default model life of original wagons to "infinite" */
118 if (rvi.railveh_type == RAILVEH_WAGON) this->info.base_life = TimerGameCalendar::Year{0xFF};
119
120 break;
121 }
122
123 case VEH_ROAD: {
124 RoadVehicleInfo &rvi = this->vehicle_info.emplace<RoadVehicleInfo>(_orig_road_vehicle_info[local_id]);
125 this->original_image_index = rvi.image_index;
126 this->info.string_id = STR_VEHICLE_NAME_ROAD_VEHICLE_MPS_REGAL_BUS + local_id;
127 break;
128 }
129
130 case VEH_SHIP: {
131 ShipVehicleInfo &svi = this->vehicle_info.emplace<ShipVehicleInfo>(_orig_ship_vehicle_info[local_id]);
132 this->original_image_index = svi.image_index;
133 this->info.string_id = STR_VEHICLE_NAME_SHIP_MPS_OIL_TANKER + local_id;
134 break;
135 }
136
137 case VEH_AIRCRAFT: {
138 AircraftVehicleInfo &avi = this->vehicle_info.emplace<AircraftVehicleInfo>(_orig_aircraft_vehicle_info[local_id]);
139 this->original_image_index = avi.image_index;
140 this->info.string_id = STR_VEHICLE_NAME_AIRCRAFT_SAMPSON_U52 + local_id;
141 break;
142 }
143 }
144}
145
151{
152 return this->info.string_id != STR_NEWGRF_INVALID_ENGINE && this->info.climates.Test(_settings_game.game_creation.landscape);
153}
154
160uint32_t Engine::GetGRFID() const
161{
162 const GRFFile *file = this->GetGRF();
163 return file == nullptr ? 0 : file->grfid;
164}
165
172{
173 /* For engines that can appear in a consist (i.e. rail vehicles and (articulated) road vehicles), a capacity
174 * of zero is a special case, to define the vehicle to not carry anything. The default cargotype is still used
175 * for livery selection etc.
176 * Note: Only the property is tested. A capacity callback returning 0 does not have the same effect.
177 */
178 switch (this->type) {
179 case VEH_TRAIN:
180 if (this->VehInfo<RailVehicleInfo>().capacity == 0) return false;
181 break;
182
183 case VEH_ROAD:
184 if (this->VehInfo<RoadVehicleInfo>().capacity == 0) return false;
185 break;
186
187 case VEH_SHIP:
188 case VEH_AIRCRAFT:
189 break;
190
191 default: NOT_REACHED();
192 }
194}
195
196
204uint Engine::DetermineCapacity(const Vehicle *v, uint16_t *mail_capacity) const
205{
206 assert(v == nullptr || this->index == v->engine_type);
207 if (mail_capacity != nullptr) *mail_capacity = 0;
208
209 if (!this->CanCarryCargo()) return 0;
210
211 bool new_multipliers = this->info.misc_flags.Test(EngineMiscFlag::NoDefaultCargoMultiplier);
212 CargoType default_cargo = this->GetDefaultCargoType();
213 CargoType cargo_type = (v != nullptr) ? v->cargo_type : default_cargo;
214
215 if (mail_capacity != nullptr && this->type == VEH_AIRCRAFT && IsCargoInClass(cargo_type, CargoClass::Passengers)) {
216 *mail_capacity = GetEngineProperty(this->index, PROP_AIRCRAFT_MAIL_CAPACITY, this->VehInfo<AircraftVehicleInfo>().mail_capacity, v);
217 }
218
219 /* Check the refit capacity callback if we are not in the default configuration, or if we are using the new multiplier algorithm. */
220 if (this->info.callback_mask.Test(VehicleCallbackMask::RefitCapacity) &&
221 (new_multipliers || default_cargo != cargo_type || (v != nullptr && v->cargo_subtype != 0))) {
222 uint16_t callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, this->index, v);
223 if (callback != CALLBACK_FAILED) return callback;
224 }
225
226 /* Get capacity according to property resp. CB */
227 uint capacity;
228 uint extra_mail_cap = 0;
229 switch (this->type) {
230 case VEH_TRAIN:
231 capacity = GetEngineProperty(this->index, PROP_TRAIN_CARGO_CAPACITY, this->VehInfo<RailVehicleInfo>().capacity, v);
232
233 /* In purchase list add the capacity of the second head. Always use the plain property for this. */
234 if (v == nullptr && this->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_MULTIHEAD) capacity += this->VehInfo<RailVehicleInfo>().capacity;
235 break;
236
237 case VEH_ROAD:
238 capacity = GetEngineProperty(this->index, PROP_ROADVEH_CARGO_CAPACITY, this->VehInfo<RoadVehicleInfo>().capacity, v);
239 break;
240
241 case VEH_SHIP:
242 capacity = GetEngineProperty(this->index, PROP_SHIP_CARGO_CAPACITY, this->VehInfo<ShipVehicleInfo>().capacity, v);
243 break;
244
245 case VEH_AIRCRAFT:
246 capacity = GetEngineProperty(this->index, PROP_AIRCRAFT_PASSENGER_CAPACITY, this->VehInfo<AircraftVehicleInfo>().passenger_capacity, v);
247 if (!IsCargoInClass(cargo_type, CargoClass::Passengers)) {
248 extra_mail_cap = GetEngineProperty(this->index, PROP_AIRCRAFT_MAIL_CAPACITY, this->VehInfo<AircraftVehicleInfo>().mail_capacity, v);
249 }
250 if (IsValidCargoType(GetCargoTypeByLabel(CT_MAIL))) {
251 if (!new_multipliers && cargo_type == GetCargoTypeByLabel(CT_MAIL)) return capacity + extra_mail_cap;
252 }
253 default_cargo = GetCargoTypeByLabel(CT_PASSENGERS); // Always use 'passengers' wrt. cargo multipliers
254 break;
255
256 default: NOT_REACHED();
257 }
258
259 if (!new_multipliers) {
260 /* Use the passenger multiplier for mail as well */
261 capacity += extra_mail_cap;
262 extra_mail_cap = 0;
263 }
264
265 /* Apply multipliers depending on cargo- and vehicletype. */
266 if (new_multipliers || (this->type != VEH_SHIP && default_cargo != cargo_type)) {
267 uint16_t default_multiplier = new_multipliers ? 0x100 : CargoSpec::Get(default_cargo)->multiplier;
268 uint16_t cargo_multiplier = CargoSpec::Get(cargo_type)->multiplier;
269 capacity *= cargo_multiplier;
270 if (extra_mail_cap > 0 && IsValidCargoType(GetCargoTypeByLabel(CT_MAIL))) {
271 uint mail_multiplier = CargoSpec::Get(GetCargoTypeByLabel(CT_MAIL))->multiplier;
272 capacity += (default_multiplier * extra_mail_cap * cargo_multiplier + mail_multiplier / 2) / mail_multiplier;
273 }
274 capacity = (capacity + default_multiplier / 2) / default_multiplier;
275 }
276
277 return capacity;
278}
279
285{
286 Price base_price;
287 uint cost_factor;
288 switch (this->type) {
289 case VEH_ROAD:
290 base_price = this->VehInfo<RoadVehicleInfo>().running_cost_class;
291 if (base_price == Price::Invalid) return 0;
292 cost_factor = GetEngineProperty(this->index, PROP_ROADVEH_RUNNING_COST_FACTOR, this->VehInfo<RoadVehicleInfo>().running_cost);
293 break;
294
295 case VEH_TRAIN:
296 base_price = this->VehInfo<RailVehicleInfo>().running_cost_class;
297 if (base_price == Price::Invalid) return 0;
298 cost_factor = GetEngineProperty(this->index, PROP_TRAIN_RUNNING_COST_FACTOR, this->VehInfo<RailVehicleInfo>().running_cost);
299 break;
300
301 case VEH_SHIP:
302 base_price = Price::RunningShip;
303 cost_factor = GetEngineProperty(this->index, PROP_SHIP_RUNNING_COST_FACTOR, this->VehInfo<ShipVehicleInfo>().running_cost);
304 break;
305
306 case VEH_AIRCRAFT:
307 base_price = Price::RunningAircraft;
308 cost_factor = GetEngineProperty(this->index, PROP_AIRCRAFT_RUNNING_COST_FACTOR, this->VehInfo<AircraftVehicleInfo>().running_cost);
309 break;
310
311 default: NOT_REACHED();
312 }
313
314 return GetPrice(base_price, cost_factor, this->GetGRF(), -8);
315}
316
321Money Engine::GetCost() const
322{
323 Price base_price;
324 uint cost_factor;
325 switch (this->type) {
326 case VEH_ROAD:
327 base_price = Price::BuildVehicleRoad;
328 cost_factor = GetEngineProperty(this->index, PROP_ROADVEH_COST_FACTOR, this->VehInfo<RoadVehicleInfo>().cost_factor);
329 break;
330
331 case VEH_TRAIN:
332 if (this->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_WAGON) {
333 base_price = Price::BuildVehicleWagon;
334 cost_factor = GetEngineProperty(this->index, PROP_TRAIN_COST_FACTOR, this->VehInfo<RailVehicleInfo>().cost_factor);
335 } else {
336 base_price = Price::BuildVehicleTrain;
337 cost_factor = GetEngineProperty(this->index, PROP_TRAIN_COST_FACTOR, this->VehInfo<RailVehicleInfo>().cost_factor);
338 }
339 break;
340
341 case VEH_SHIP:
342 base_price = Price::BuildVehicleShip;
343 cost_factor = GetEngineProperty(this->index, PROP_SHIP_COST_FACTOR, this->VehInfo<ShipVehicleInfo>().cost_factor);
344 break;
345
346 case VEH_AIRCRAFT:
347 base_price = Price::BuildVehicleAircraft;
348 cost_factor = GetEngineProperty(this->index, PROP_AIRCRAFT_COST_FACTOR, this->VehInfo<AircraftVehicleInfo>().cost_factor);
349 break;
350
351 default: NOT_REACHED();
352 }
353
354 return GetPrice(base_price, cost_factor, this->GetGRF(), -8);
355}
356
362{
363 switch (this->type) {
364 case VEH_TRAIN:
365 return GetEngineProperty(this->index, PROP_TRAIN_SPEED, this->VehInfo<RailVehicleInfo>().max_speed);
366
367 case VEH_ROAD: {
368 uint max_speed = GetEngineProperty(this->index, PROP_ROADVEH_SPEED, 0);
369 return (max_speed != 0) ? max_speed * 2 : this->VehInfo<RoadVehicleInfo>().max_speed / 2;
370 }
371
372 case VEH_SHIP:
373 return GetEngineProperty(this->index, PROP_SHIP_SPEED, this->VehInfo<ShipVehicleInfo>().max_speed) / 2;
374
375 case VEH_AIRCRAFT: {
376 uint max_speed = GetEngineProperty(this->index, PROP_AIRCRAFT_SPEED, 0);
377 if (max_speed != 0) {
378 return (max_speed * 128) / 10;
379 }
380 return this->VehInfo<AircraftVehicleInfo>().max_speed;
381 }
382
383 default: NOT_REACHED();
384 }
385}
386
394{
395 /* Only trains and road vehicles have 'power'. */
396 switch (this->type) {
397 case VEH_TRAIN:
398 return GetEngineProperty(this->index, PROP_TRAIN_POWER, this->VehInfo<RailVehicleInfo>().power);
399 case VEH_ROAD:
400 return GetEngineProperty(this->index, PROP_ROADVEH_POWER, this->VehInfo<RoadVehicleInfo>().power) * 10;
401
402 default: NOT_REACHED();
403 }
404}
405
412{
413 /* Only trains and road vehicles have 'weight'. */
414 switch (this->type) {
415 case VEH_TRAIN:
416 return GetEngineProperty(this->index, PROP_TRAIN_WEIGHT, this->VehInfo<RailVehicleInfo>().weight) << (this->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_MULTIHEAD ? 1 : 0);
417 case VEH_ROAD:
418 return GetEngineProperty(this->index, PROP_ROADVEH_WEIGHT, this->VehInfo<RoadVehicleInfo>().weight) / 4;
419
420 default: NOT_REACHED();
421 }
422}
423
430{
431 /* Only trains and road vehicles have 'tractive effort'. */
432 switch (this->type) {
433 case VEH_TRAIN:
434 return (GROUND_ACCELERATION * this->GetDisplayWeight() * GetEngineProperty(this->index, PROP_TRAIN_TRACTIVE_EFFORT, this->VehInfo<RailVehicleInfo>().tractive_effort)) / 256;
435 case VEH_ROAD:
436 return (GROUND_ACCELERATION * this->GetDisplayWeight() * GetEngineProperty(this->index, PROP_ROADVEH_TRACTIVE_EFFORT, this->VehInfo<RoadVehicleInfo>().tractive_effort)) / 256;
437
438 default: NOT_REACHED();
439 }
440}
441
446TimerGameCalendar::Date Engine::GetLifeLengthInDays() const
447{
448 /* Assume leap years; this gives the player a bit more than the given amount of years, but never less. */
449 return TimerGameCalendar::Date{(this->info.lifelength + _settings_game.vehicle.extend_vehicle_life).base() * CalendarTime::DAYS_IN_LEAP_YEAR};
450}
451
456uint16_t Engine::GetRange() const
457{
458 switch (this->type) {
459 case VEH_AIRCRAFT:
460 return GetEngineProperty(this->index, PROP_AIRCRAFT_RANGE, this->VehInfo<AircraftVehicleInfo>().max_range);
461
462 default: NOT_REACHED();
463 }
464}
465
471{
472 switch (this->type) {
473 case VEH_AIRCRAFT:
474 switch (this->VehInfo<AircraftVehicleInfo>().subtype) {
475 case AIR_HELI: return STR_LIVERY_HELICOPTER;
476 case AIR_CTOL: return STR_LIVERY_SMALL_PLANE;
477 case AIR_CTOL | AIR_FAST: return STR_LIVERY_LARGE_PLANE;
478 default: NOT_REACHED();
479 }
480
481 default: NOT_REACHED();
482 }
483}
484
490bool Engine::IsVariantHidden(CompanyID c) const
491{
492 /* In case company is spectator. */
493 if (c >= MAX_COMPANIES) return false;
494
495 /* Shortcut if this engine is explicitly hidden. */
496 if (this->IsHidden(c)) return true;
497
498 /* Check for hidden parent variants. This is a bit convoluted as we must check hidden status of
499 * the last display variant rather than the actual parent variant. */
500 const Engine *re = this;
501 const Engine *ve = re->GetDisplayVariant();
502 while (!(ve->IsHidden(c)) && re->info.variant_id != EngineID::Invalid()) {
503 re = Engine::Get(re->info.variant_id);
504 ve = re->GetDisplayVariant();
505 }
506 return ve->IsHidden(c);
507}
508
513{
514 EngineID id = EngineID::Begin();
515 for (VehicleType type = VEH_TRAIN; type <= VEH_AIRCRAFT; type++) {
516 auto &map = this->mappings[type];
517 map.clear();
518 for (uint internal_id = 0; internal_id < _engine_counts[type]; internal_id++, ++id) {
519 map.emplace_back(INVALID_GRFID, internal_id, type, internal_id, id);
520 }
521 }
522}
523
533EngineID EngineOverrideManager::GetID(VehicleType type, uint16_t grf_local_id, uint32_t grfid)
534{
535 const auto &map = this->mappings[type];
536 const auto key = EngineIDMapping::Key(grfid, grf_local_id);
537 auto it = std::ranges::lower_bound(map, key, std::less{}, EngineIDMappingKeyProjection{});
538 if (it == std::end(map) || it->Key() != key) return EngineID::Invalid();
539 return it->engine;
540}
541
552EngineID EngineOverrideManager::UseUnreservedID(VehicleType type, uint16_t grf_local_id, uint32_t grfid, bool static_access)
553{
554 auto &map = _engine_mngr.mappings[type];
555 const auto key = EngineIDMapping::Key(INVALID_GRFID, grf_local_id);
556 auto it = std::ranges::lower_bound(map, key, std::less{}, EngineIDMappingKeyProjection{});
557 if (it == std::end(map) || it->Key() != key) return EngineID::Invalid();
558
559 if (!static_access && grfid != INVALID_GRFID) {
560 /* Reserve the engine slot for the new grfid. */
561 it->grfid = grfid;
562
563 /* Relocate entry to its new position in the mapping list to keep it sorted. */
564 auto p = std::ranges::lower_bound(map, EngineIDMapping::Key(grfid, grf_local_id), std::less{}, EngineIDMappingKeyProjection{});
565 it = Slide(it, std::next(it), p).first;
566 }
567
568 return it->engine;
569}
570
571void EngineOverrideManager::SetID(VehicleType type, uint16_t grf_local_id, uint32_t grfid, uint8_t substitute_id, EngineID engine)
572{
573 auto &map = this->mappings[type];
574 const auto key = EngineIDMapping::Key(grfid, grf_local_id);
575 auto it = std::ranges::lower_bound(map, key, std::less{}, EngineIDMappingKeyProjection{});
576 if (it == std::end(map) || it->Key() != key) {
577 map.emplace(it, grfid, grf_local_id, type, substitute_id, engine);
578 } else {
579 it->engine = engine;
580 }
581}
582
589{
590 for (const Vehicle *v : Vehicle::Iterate()) {
591 if (IsCompanyBuildableVehicleType(v)) return false;
592 }
593
594 /* Reset the engines, they will get new EngineIDs */
595 _engine_mngr.ResetToDefaultMapping();
597
598 return true;
599}
600
605{
607 _engine_pool.CleanPool();
608
609 for (VehicleType type = VEH_BEGIN; type != VEH_COMPANY_END; type++) {
610 const auto &mapping = _engine_mngr.mappings[type];
611
612 /* Verify that the engine override manager has at least been set up with the default engines. */
613 assert(std::size(mapping) >= _engine_counts[type]);
614
615 for (const EngineIDMapping &eid : mapping) {
616 Engine::CreateAtIndex(eid.engine, type, eid.internal_id);
617 }
618 }
619}
620
621void ShowEnginePreviewWindow(EngineID engine);
622
628static bool IsWagon(EngineID index)
629{
630 const Engine *e = Engine::Get(index);
631 return e->type == VEH_TRAIN && e->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_WAGON;
632}
633
639static void ClearLastVariant(EngineID engine_id, VehicleType type)
640{
641 for (Engine *e : Engine::IterateType(type)) {
642 if (e->display_last_variant == engine_id) e->display_last_variant = EngineID::Invalid();
643 }
644}
645
651void CalcEngineReliability(Engine *e, bool new_month)
652{
653 /* Get source engine for reliability age. This is normally our engine unless variant reliability syncing is requested. */
654 Engine *re = e;
655 while (re->info.variant_id != EngineID::Invalid() && re->info.extra_flags.Test(ExtraEngineFlag::SyncReliability)) {
656 re = Engine::Get(re->info.variant_id);
657 }
658
659 uint32_t age = re->age;
660 if (new_month && re->index > e->index && age != INT32_MAX) age++; /* parent variant's age has not yet updated. */
661
662 /* Check for early retirement */
663 if (e->company_avail.Any() && !_settings_game.vehicle.never_expire_vehicles && e->info.base_life != 0xFF) {
664 int retire_early = e->info.retire_early;
665 uint retire_early_max_age = std::max(0, e->duration_phase_1 + e->duration_phase_2 - retire_early * 12);
666 if (retire_early != 0 && age >= retire_early_max_age) {
667 /* Early retirement is enabled and we're past the date... */
669 ClearLastVariant(e->index, e->type);
671 }
672 }
673
674 if (age < e->duration_phase_1) {
675 uint start = e->reliability_start;
676 e->reliability = age * (e->reliability_max - start) / e->duration_phase_1 + start;
677 } else if ((age -= e->duration_phase_1) < e->duration_phase_2 || _settings_game.vehicle.never_expire_vehicles || e->info.base_life == 0xFF) {
678 /* We are at the peak of this engines life. It will have max reliability.
679 * This is also true if the engines never expire. They will not go bad over time */
681 } else if ((age -= e->duration_phase_2) < e->duration_phase_3) {
682 uint max = e->reliability_max;
683 e->reliability = (int)age * (int)(e->reliability_final - max) / e->duration_phase_3 + max;
684 } else {
685 /* time's up for this engine.
686 * We will now completely retire this design */
689 /* Kick this engine out of the lists */
690 ClearLastVariant(e->index, e->type);
692 }
693
694}
695
698{
699 /* Determine last engine aging year, default to 2050 as previously. */
700 _year_engine_aging_stops = TimerGameCalendar::Year{2050};
701
702 for (const Engine *e : Engine::Iterate()) {
703 const EngineInfo *ei = &e->info;
704
705 /* Exclude certain engines */
706 if (!ei->climates.Test(_settings_game.game_creation.landscape)) continue;
707 if (e->type == VEH_TRAIN && e->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_WAGON) continue;
708
709 /* Base year ending date on half the model life */
710 TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(ei->base_intro + (ei->lifelength.base() * CalendarTime::DAYS_IN_LEAP_YEAR) / 2);
711
713 }
714}
715
722void StartupOneEngine(Engine *e, const TimerGameCalendar::YearMonthDay &aging_ymd, uint32_t seed)
723{
724 const EngineInfo *ei = &e->info;
725
726 e->age = 0;
727 e->flags = {};
730
731 /* Vehicles with the same base_intro date shall be introduced at the same time.
732 * Make sure they use the same randomisation of the date. */
733 SavedRandomSeeds saved_seeds;
734 SaveRandomSeeds(&saved_seeds);
735 SetRandomSeed(_settings_game.game_creation.generation_seed ^ seed ^
736 ei->base_intro.base() ^
737 e->type ^
738 e->GetGRFID());
739 uint32_t r = Random();
740
741 /* Don't randomise the start-date in the first two years after gamestart to ensure availability
742 * of engines in early starting games.
743 * Note: TTDP uses fixed 1922 */
744 e->intro_date = ei->base_intro <= TimerGameCalendar::ConvertYMDToDate(_settings_game.game_creation.starting_year + 2, 0, 1) ? ei->base_intro : (TimerGameCalendar::Date)GB(r, 0, 9) + ei->base_intro;
746 TimerGameCalendar::YearMonthDay intro_ymd = TimerGameCalendar::ConvertDateToYMD(e->intro_date);
747 int aging_months = aging_ymd.year.base() * 12 + aging_ymd.month;
748 int intro_months = intro_ymd.year.base() * 12 + intro_ymd.month;
749 if (intro_ymd.day > 1) intro_months++; // Engines are introduced at the first month start at/after intro date.
750 e->age = aging_months - intro_months;
751 e->company_avail.Set();
753 }
754
755 /* Get parent variant index for syncing reliability via random seed. */
756 const Engine *re = e;
757 while (re->info.variant_id != EngineID::Invalid() && re->info.extra_flags.Test(ExtraEngineFlag::SyncReliability)) {
758 re = Engine::Get(re->info.variant_id);
759 }
760
761 SetRandomSeed(_settings_game.game_creation.generation_seed ^ seed ^
762 (re->index.base() << 16) ^ (re->info.base_intro.base() << 12) ^ (re->info.decay_speed << 8) ^
763 (re->info.lifelength.base() << 4) ^ re->info.retire_early ^
764 e->type ^
765 e->GetGRFID());
766
767 /* Base reliability defined as a percentage of UINT16_MAX. */
768 const uint16_t RELIABILITY_START = UINT16_MAX * 48 / 100;
769 const uint16_t RELIABILITY_MAX = UINT16_MAX * 75 / 100;
770 const uint16_t RELIABILITY_FINAL = UINT16_MAX * 25 / 100;
771
772 static_assert(RELIABILITY_START == 0x7AE0);
773 static_assert(RELIABILITY_MAX == 0xBFFF);
774 static_assert(RELIABILITY_FINAL == 0x3FFF);
775
776 r = Random();
777 /* 14 bits gives a value between 0 and 16383, which is up to an additional 25%p reliability on top of the base reliability. */
778 e->reliability_start = GB(r, 16, 14) + RELIABILITY_START;
779 e->reliability_max = GB(r, 0, 14) + RELIABILITY_MAX;
780
781 r = Random();
782 e->reliability_final = GB(r, 16, 14) + RELIABILITY_FINAL;
783
784 e->duration_phase_1 = GB(r, 0, 5) + 7;
785 e->duration_phase_2 = std::max(0, int(GB(r, 5, 4)) + ei->base_life.base() * 12 - 96);
786 e->duration_phase_3 = GB(r, 9, 7) + 120;
787
788 RestoreRandomSeeds(saved_seeds);
789
790 e->reliability_spd_dec = ei->decay_speed << 2;
791
792 /* prevent certain engines from ever appearing. */
793 if (!ei->climates.Test(_settings_game.game_creation.landscape)) {
796 }
797}
798
804{
805 /* Aging of vehicles stops, so account for that when starting late */
806 const TimerGameCalendar::Date aging_date = std::min(TimerGameCalendar::date, TimerGameCalendar::ConvertYMDToDate(_year_engine_aging_stops, 0, 1));
807 TimerGameCalendar::YearMonthDay aging_ymd = TimerGameCalendar::ConvertDateToYMD(aging_date);
808 uint32_t seed = Random();
809
810 for (Engine *e : Engine::Iterate()) {
811 StartupOneEngine(e, aging_ymd, seed);
812 }
813 for (Engine *e : Engine::Iterate()) {
814 CalcEngineReliability(e, false);
815 }
816
817 /* Update the bitmasks for the vehicle lists */
818 for (Company *c : Company::Iterate()) {
819 c->avail_railtypes = GetCompanyRailTypes(c->index);
820 c->avail_roadtypes = GetCompanyRoadTypes(c->index);
821 }
822
823 /* Invalidate any open purchase lists */
825
828}
829
835static void EnableEngineForCompany(EngineID eid, CompanyID company)
836{
837 Engine *e = Engine::Get(eid);
838 Company *c = Company::Get(company);
839
840 e->company_avail.Set(company);
841 if (e->type == VEH_TRAIN) {
843 } else if (e->type == VEH_ROAD) {
845 }
846
847 if (company == _local_company) {
849
850 /* Update the toolbar. */
855 }
856}
857
863static void DisableEngineForCompany(EngineID eid, CompanyID company)
864{
865 Engine *e = Engine::Get(eid);
866 Company *c = Company::Get(company);
867
868 e->company_avail.Reset(company);
869 if (e->type == VEH_TRAIN) {
871 } else if (e->type == VEH_ROAD) {
873 }
874
875 if (company == _local_company) {
876 ClearLastVariant(e->index, e->type);
878 }
879}
880
887static void AcceptEnginePreview(EngineID eid, CompanyID company, int recursion_depth = 0)
888{
889 Engine *e = Engine::Get(eid);
890
891 e->preview_company = CompanyID::Invalid();
892 e->preview_asked.Set();
893
894 EnableEngineForCompany(eid, company);
895
896 /* Notify preview window, that it might want to close.
897 * Note: We cannot directly close the window.
898 * In singleplayer this function is called from the preview window, so
899 * we have to use the GUI-scope scheduling of InvalidateWindowData.
900 */
902
903 /* Don't search for variants to include if we are 10 levels deep already. */
904 if (recursion_depth >= 10) return;
905
906 /* Find variants to be included in preview. */
907 for (Engine *ve : Engine::IterateType(e->type)) {
908 if (ve->index != eid && ve->info.variant_id == eid && ve->info.extra_flags.Test(ExtraEngineFlag::JoinPreview)) {
909 AcceptEnginePreview(ve->index, company, recursion_depth + 1);
910 }
911 }
912}
913
919static CompanyID GetPreviewCompany(Engine *e)
920{
921 CompanyID best_company = CompanyID::Invalid();
922
923 /* For trains the cargomask has no useful meaning, since you can attach other wagons */
924 CargoTypes cargomask = e->type != VEH_TRAIN ? GetUnionOfArticulatedRefitMasks(e->index, true) : ALL_CARGOTYPES;
925
926 int32_t best_hist = -1;
927 for (const Company *c : Company::Iterate()) {
928 if (c->block_preview == 0 && !e->preview_asked.Test(c->index) &&
929 c->old_economy[0].performance_history > best_hist) {
930
931 /* Check whether the company uses similar vehicles */
932 for (const Vehicle *v : Vehicle::Iterate()) {
933 if (v->owner != c->index || v->type != e->type) continue;
934 if (!v->GetEngine()->CanCarryCargo() || !HasBit(cargomask, v->cargo_type)) continue;
935
936 best_hist = c->old_economy[0].performance_history;
937 best_company = c->index;
938 break;
939 }
940 }
941 }
942
943 return best_company;
944}
945
953static bool IsVehicleTypeDisabled(VehicleType type, bool ai)
954{
955 switch (type) {
956 case VEH_TRAIN: return _settings_game.vehicle.max_trains == 0 || (ai && _settings_game.ai.ai_disable_veh_train);
957 case VEH_ROAD: return _settings_game.vehicle.max_roadveh == 0 || (ai && _settings_game.ai.ai_disable_veh_roadveh);
958 case VEH_SHIP: return _settings_game.vehicle.max_ships == 0 || (ai && _settings_game.ai.ai_disable_veh_ship);
959 case VEH_AIRCRAFT: return _settings_game.vehicle.max_aircraft == 0 || (ai && _settings_game.ai.ai_disable_veh_aircraft);
960
961 default: NOT_REACHED();
962 }
963}
964
966static const IntervalTimer<TimerGameCalendar> _calendar_engines_daily({TimerGameCalendar::Trigger::Day, TimerGameCalendar::Priority::Engine}, [](auto)
967{
968 for (Company *c : Company::Iterate()) {
969 c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes, TimerGameCalendar::date);
970 c->avail_roadtypes = AddDateIntroducedRoadTypes(c->avail_roadtypes, TimerGameCalendar::date);
971 }
972
974
975 for (Engine *e : Engine::Iterate()) {
976 EngineID i = e->index;
977 if (e->flags.Test(EngineFlag::ExclusivePreview)) {
978 if (e->preview_company != CompanyID::Invalid()) {
979 if (!--e->preview_wait) {
981 e->preview_company = CompanyID::Invalid();
982 }
983 } else if (e->preview_asked.Count() < MAX_COMPANIES) {
984 e->preview_company = GetPreviewCompany(e);
985
986 if (e->preview_company == CompanyID::Invalid()) {
987 e->preview_asked.Set();
988 continue;
989 }
990
991 e->preview_asked.Set(e->preview_company);
992 e->preview_wait = 20;
993 /* AIs are intentionally not skipped for preview even if they cannot build a certain
994 * vehicle type. This is done to not give poor performing human companies an "unfair"
995 * boost that they wouldn't have gotten against other human companies. The check on
996 * the line below is just to make AIs not notice that they have a preview if they
997 * cannot build the vehicle. */
998 if (!IsVehicleTypeDisabled(e->type, true)) AI::NewEvent(e->preview_company, new ScriptEventEnginePreview(i));
999 if (IsInteractiveCompany(e->preview_company)) ShowEnginePreviewWindow(i);
1000 }
1001 }
1002 }
1003});
1004
1010{
1011 for (Engine *e : Engine::Iterate()) {
1012 e->company_hidden.Reset(cid);
1013 }
1014}
1015
1023CommandCost CmdSetVehicleVisibility(DoCommandFlags flags, EngineID engine_id, bool hide)
1024{
1025 Engine *e = Engine::GetIfValid(engine_id);
1026 if (e == nullptr || _current_company >= MAX_COMPANIES) return CMD_ERROR;
1027 if (!IsEngineBuildable(e->index, e->type, _current_company)) return CMD_ERROR;
1028
1029 if (flags.Test(DoCommandFlag::Execute)) {
1032 }
1033
1034 return CommandCost();
1035}
1036
1044CommandCost CmdWantEnginePreview(DoCommandFlags flags, EngineID engine_id)
1045{
1046 Engine *e = Engine::GetIfValid(engine_id);
1047 if (e == nullptr || !e->flags.Test(EngineFlag::ExclusivePreview) || e->preview_company != _current_company) return CMD_ERROR;
1048
1050
1051 return CommandCost();
1052}
1053
1062CommandCost CmdEngineCtrl(DoCommandFlags flags, EngineID engine_id, CompanyID company_id, bool allow)
1063{
1064 if (_current_company != OWNER_DEITY) return CMD_ERROR;
1065
1066 if (!Engine::IsValidID(engine_id) || !Company::IsValidID(company_id)) return CMD_ERROR;
1067
1068 if (flags.Test(DoCommandFlag::Execute)) {
1069 if (allow) {
1070 EnableEngineForCompany(engine_id, company_id);
1071 } else {
1072 DisableEngineForCompany(engine_id, company_id);
1073 }
1074 }
1075
1076 return CommandCost();
1077}
1078
1085{
1086 EngineID index = e->index;
1087
1088 /* In case the company didn't build the vehicle during the intro period,
1089 * prevent that company from getting future intro periods for a while. */
1091 for (Company *c : Company::Iterate()) {
1092 if (!e->company_avail.Test(c->index)) continue;
1093
1094 /* Check the company's 'ALL_GROUP' group statistics. This only includes countable vehicles, which is fine
1095 * as those are the only engines that can be given exclusive previews. */
1096 if (GetGroupNumEngines(c->index, ALL_GROUP, e->index) == 0) {
1097 /* The company did not build this engine during preview. */
1098 c->block_preview = 20;
1099 }
1100 }
1101 }
1102
1105
1106 /* Now available for all companies */
1107 e->company_avail.Set();
1108
1109 /* Do not introduce new rail wagons */
1110 if (IsWagon(index)) return;
1111
1112 if (e->type == VEH_TRAIN) {
1113 /* maybe make another rail type available */
1114 assert(e->VehInfo<RailVehicleInfo>().railtypes != RailTypes{});
1116 for (Company *c : Company::Iterate()) c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes | introduced, TimerGameCalendar::date);
1117 } else if (e->type == VEH_ROAD) {
1118 /* maybe make another road type available */
1119 assert(e->VehInfo<RoadVehicleInfo>().roadtype < ROADTYPE_END);
1120 for (Company *c : Company::Iterate()) c->avail_roadtypes = AddDateIntroducedRoadTypes(c->avail_roadtypes | GetRoadTypeInfo(e->VehInfo<RoadVehicleInfo>().roadtype)->introduces_roadtypes, TimerGameCalendar::date);
1121 }
1122
1123 /* Only broadcast event if AIs are able to build this vehicle type. */
1124 if (!IsVehicleTypeDisabled(e->type, true)) AI::BroadcastNewEvent(new ScriptEventEngineAvailable(index));
1125
1126 /* Only provide the "New Vehicle available" news paper entry, if engine can be built. */
1127 if (!IsVehicleTypeDisabled(e->type, false) && !e->info.extra_flags.Test(ExtraEngineFlag::NoNews)) {
1128 AddNewsItem(GetEncodedString(STR_NEWS_NEW_VEHICLE_NOW_AVAILABLE_WITH_TYPE,
1129 GetEngineCategoryName(index),
1132 }
1133
1134 /* Update the toolbar. */
1138
1139 /* Close pending preview windows */
1141}
1142
1145{
1147 bool refresh = false;
1148 for (Engine *e : Engine::Iterate()) {
1149 /* Age the vehicle */
1150 if (e->flags.Test(EngineFlag::Available) && e->age != INT32_MAX) {
1151 e->age++;
1152 CalcEngineReliability(e, true);
1153 refresh = true;
1154 }
1155
1156 /* Do not introduce invalid engines */
1157 if (!e->IsEnabled()) continue;
1158
1159 if (!e->flags.Test(EngineFlag::Available) && TimerGameCalendar::date >= (e->intro_date + CalendarTime::DAYS_IN_YEAR)) {
1160 /* Introduce it to all companies */
1162 } else if (!e->flags.Any({EngineFlag::Available, EngineFlag::ExclusivePreview}) && TimerGameCalendar::date >= e->intro_date) {
1163 /* Introduction date has passed...
1164 * Check if it is allowed to build this vehicle type at all
1165 * based on the current game settings. If not, it does not
1166 * make sense to show the preview dialog to any company. */
1167 if (IsVehicleTypeDisabled(e->type, false)) continue;
1168
1169 /* Do not introduce new rail wagons */
1170 if (IsWagon(e->index)) continue;
1171
1172 /* Engine has no preview */
1173 if (e->info.extra_flags.Test(ExtraEngineFlag::NoPreview)) continue;
1174
1175 /* Show preview dialog to one of the companies. */
1176 e->flags.Set(EngineFlag::ExclusivePreview);
1177 e->preview_company = CompanyID::Invalid();
1178 e->preview_asked = CompanyMask{};
1179 }
1180 }
1181
1182 InvalidateWindowClassesData(WC_BUILD_VEHICLE); // rebuild the purchase list (esp. when sorted by reliability)
1183
1184 if (refresh) {
1187 }
1188 }
1189}
1190
1191static const IntervalTimer<TimerGameCalendar> _calendar_engines_monthly({TimerGameCalendar::Trigger::Month, TimerGameCalendar::Priority::Engine}, [](auto)
1192{
1194});
1195
1201static bool IsUniqueEngineName(const std::string &name)
1202{
1203 for (const Engine *e : Engine::Iterate()) {
1204 if (!e->name.empty() && e->name == name) return false;
1205 }
1206
1207 return true;
1208}
1209
1217CommandCost CmdRenameEngine(DoCommandFlags flags, EngineID engine_id, const std::string &text)
1218{
1219 Engine *e = Engine::GetIfValid(engine_id);
1220 if (e == nullptr) return CMD_ERROR;
1221
1222 bool reset = text.empty();
1223
1224 if (!reset) {
1226 if (!IsUniqueEngineName(text)) return CommandCost(STR_ERROR_NAME_MUST_BE_UNIQUE);
1227 }
1228
1229 if (flags.Test(DoCommandFlag::Execute)) {
1230 if (reset) {
1231 e->name.clear();
1232 } else {
1233 e->name = text;
1234 }
1235
1237 }
1238
1239 return CommandCost();
1240}
1241
1242
1251bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company)
1252{
1253 const Engine *e = Engine::GetIfValid(engine);
1254
1255 /* check if it's an engine that is in the engine array */
1256 if (e == nullptr) return false;
1257
1258 /* check if it's an engine of specified type */
1259 if (e->type != type) return false;
1260
1261 /* check if it's available ... */
1262 if (company == OWNER_DEITY) {
1263 /* ... for any company (preview does not count) */
1264 if (!e->flags.Test(EngineFlag::Available) || e->company_avail.None()) return false;
1265 } else {
1266 /* ... for this company */
1267 if (!e->company_avail.Test(company)) return false;
1268 }
1269
1270 if (!e->IsEnabled()) return false;
1271
1272 if (type == VEH_TRAIN && company != OWNER_DEITY) {
1273 /* Check if the rail type is available to this company */
1274 const Company *c = Company::Get(company);
1275 if (!GetAllCompatibleRailTypes(e->VehInfo<RailVehicleInfo>().railtypes).Any(c->avail_railtypes)) return false;
1276 }
1277 if (type == VEH_ROAD && company != OWNER_DEITY) {
1278 /* Check if the road type is available to this company */
1279 const Company *c = Company::Get(company);
1280 if (!GetRoadTypeInfo(e->VehInfo<RoadVehicleInfo>().roadtype)->powered_roadtypes.Any(c->avail_roadtypes)) return false;
1281 }
1282
1283 return true;
1284}
1285
1293{
1294 const Engine *e = Engine::GetIfValid(engine);
1295
1296 /* check if it's an engine that is in the engine array */
1297 if (e == nullptr) return false;
1298
1299 if (!e->CanCarryCargo()) return false;
1300
1301 const EngineInfo *ei = &e->info;
1302 if (ei->refit_mask == 0) return false;
1303
1304 /* Are there suffixes?
1305 * Note: This does not mean the suffixes are actually available for every consist at any time. */
1307
1308 /* Is there any cargo except the default cargo? */
1309 CargoType default_cargo = e->GetDefaultCargoType();
1310 CargoTypes default_cargo_mask = 0;
1311 SetBit(default_cargo_mask, default_cargo);
1312 return IsValidCargoType(default_cargo) && ei->refit_mask != default_cargo_mask;
1313}
1314
1319{
1320 TimerGameCalendar::Date min_date{INT32_MAX};
1321
1322 for (const Engine *e : Engine::Iterate()) {
1323 if (!e->IsEnabled()) continue;
1324
1325 /* Don't consider train wagons, we need a powered engine available. */
1326 if (e->type == VEH_TRAIN && e->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_WAGON) continue;
1327
1328 /* We have an available engine... yay! */
1329 if (e->flags.Test(EngineFlag::Available) && e->company_avail.Any()) return;
1330
1331 /* Okay, try to find the earliest date. */
1332 min_date = std::min(min_date, e->info.base_intro);
1333 }
1334
1335 if (min_date < INT32_MAX) {
1336 ShowErrorMessage(GetEncodedString(STR_ERROR_NO_VEHICLES_AVAILABLE_YET),
1337 GetEncodedString(STR_ERROR_NO_VEHICLES_AVAILABLE_YET_EXPLANATION, min_date), WL_WARNING);
1338 } else {
1339 ShowErrorMessage(GetEncodedString(STR_ERROR_NO_VEHICLES_AVAILABLE_AT_ALL),
1340 GetEncodedString(STR_ERROR_NO_VEHICLES_AVAILABLE_AT_ALL_EXPLANATION), WL_WARNING);
1341 }
1342}
Base functions for all AIs.
Base for aircraft.
CargoTypes GetUnionOfArticulatedRefitMasks(EngineID engine, bool include_initial_cargo_type)
Ors the refit_masks of all articulated parts.
Functions related to articulated vehicles.
void AddRemoveEngineFromAutoreplaceAndBuildWindows(VehicleType type)
When an engine is made buildable or is removed from being buildable, add/remove it from the build/aut...
Functions related to the autoreplace GUIs.
static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
static constexpr CargoLabel CT_INVALID
Invalid cargo type.
Definition cargo_type.h:70
uint8_t CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:21
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:104
static constexpr CargoLabel CT_PASSENGERS
Available types of cargo Labels may be re-used between different climates.
Definition cargo_type.h:29
@ Passengers
Passengers.
Definition cargotype.h:50
bool IsCargoInClass(CargoType cargo, CargoClasses cc)
Does cargo c have cargo class cc?
Definition cargotype.h:236
static void BroadcastNewEvent(ScriptEvent *event, CompanyID skip_company=CompanyID::Invalid())
Broadcast a new event to all active AIs.
Definition ai_core.cpp:255
static void NewEvent(CompanyID company, ScriptEvent *event)
Queue a new event for an AI.
Definition ai_core.cpp:235
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
constexpr bool None() const
Test if none of the values are set.
constexpr Timpl & Reset()
Reset all bits.
constexpr Timpl & Set()
Set all bits.
constexpr bool Any(const Timpl &other) const
Test if any of the given values are set.
Common return value for all commands.
StringID GetAircraftTypeText() const
Get the name of the aircraft type for display purposes.
Definition engine.cpp:470
uint GetPower() const
Returns the power of the engine for display and sorting purposes.
Definition engine.cpp:393
const Engine * GetDisplayVariant() const
Get the last display variant for an engine.
uint16_t GetRange() const
Get the range of an aircraft type.
Definition engine.cpp:456
uint32_t GetGRFID() const
Retrieve the GRF ID of the NewGRF the engine is tied to.
Definition engine.cpp:160
uint16_t reliability_spd_dec
Speed of reliability decay between services (per day).
Definition engine_base.h:50
Money GetCost() const
Return how much a new engine costs.
Definition engine.cpp:321
uint16_t reliability_start
Initial reliability of the engine.
Definition engine_base.h:51
TimerGameCalendar::Date intro_date
Date of introduction of the engine.
Definition engine_base.h:46
const GRFFile * GetGRF() const
Retrieve the NewGRF the engine is tied to.
static Pool::IterateWrapperFiltered< Engine, EngineTypeFilter > IterateType(VehicleType vt, size_t from=0)
Returns an iterable ensemble of all valid engines of the given type.
uint GetDisplayMaxSpeed() const
Returns max speed of the engine for display purposes.
Definition engine.cpp:361
uint DetermineCapacity(const Vehicle *v, uint16_t *mail_capacity=nullptr) const
Determines capacity of a given vehicle from scratch.
Definition engine.cpp:204
EngineFlags flags
Flags of the engine.
Definition engine_base.h:57
CompanyMask company_avail
Bit for each company whether the engine is available for that company.
Definition engine_base.h:40
uint16_t reliability_max
Maximal reliability of the engine.
Definition engine_base.h:52
uint GetDisplayWeight() const
Returns the weight of the engine for display purposes.
Definition engine.cpp:411
bool IsEnabled() const
Checks whether the engine is a valid (non-articulated part of an) engine.
Definition engine.cpp:150
VehicleType type
Vehicle type, ie VEH_ROAD, VEH_TRAIN, etc.
Definition engine_base.h:62
bool IsVariantHidden(CompanyID c) const
Check whether the engine variant chain is hidden in the GUI for the given company.
Definition engine.cpp:490
uint16_t reliability_final
Final reliability of the engine.
Definition engine_base.h:53
CompanyID preview_company
Company which is currently being offered a preview CompanyID::Invalid() means no company.
Definition engine_base.h:59
TimerGameCalendar::Date GetLifeLengthInDays() const
Returns the vehicle's (not model's!) life length in days.
Definition engine.cpp:446
uint16_t duration_phase_3
Third reliability phase in months, decaying to reliability_final.
Definition engine_base.h:56
uint16_t duration_phase_2
Second reliability phase in months, keeping reliability_max.
Definition engine_base.h:55
CargoType GetDefaultCargoType() const
Determines the default cargo type of an engine.
Definition engine_base.h:94
CompanyMask company_hidden
Bit for each company whether the engine is normally hidden in the build gui for that company.
Definition engine_base.h:41
Money GetRunningCost() const
Return how much the running costs of this engine are.
Definition engine.cpp:284
uint16_t reliability
Current reliability of the engine.
Definition engine_base.h:49
CompanyMask preview_asked
Bit for each company which has already been offered a preview.
Definition engine_base.h:42
uint GetDisplayMaxTractiveEffort() const
Returns the tractive effort of the engine for display purposes.
Definition engine.cpp:429
int32_t age
Age of the engine in months.
Definition engine_base.h:47
bool IsHidden(CompanyID c) const
Check whether the engine is hidden in the GUI for the given company.
bool CanCarryCargo() const
Determines whether an engine can carry something.
Definition engine.cpp:171
std::string name
Custom name of engine.
Definition engine_base.h:44
uint16_t duration_phase_1
First reliability phase in months, increasing reliability from reliability_start to reliability_max.
Definition engine_base.h:54
An interval timer will fire every interval, and will continue to fire until it is deleted.
Definition timer.h:76
RoadTypes powered_roadtypes
bitmask to the OTHER roadtypes on which a vehicle of THIS roadtype generates power
Definition road.h:96
static constexpr TimerGameTick::Ticks CARGO_AGING_TICKS
Cycle duration for aging cargo.
static Date ConvertYMDToDate(Year year, Month month, Day day)
Converts a tuple of Year, Month and Day to a Date.
static YearMonthDay ConvertDateToYMD(Date date)
Converts a Date to a Year, Month & Day.
static Date date
Current date in days (day counter).
static Year year
Current year, starting at 0.
Functions related to commands.
static const CommandCost CMD_ERROR
Define a default return value for a failed command.
@ Execute
execute the given command
Definition of stuff that is very close to a company, like the company struct itself.
CompanyID _local_company
Company controlled by the human player at this client. Can also be COMPANY_SPECTATOR.
CompanyID _current_company
Company currently doing an action.
Functions related to companies.
bool IsInteractiveCompany(CompanyID company)
Is the user representing company?
static constexpr Owner OWNER_DEITY
The object is owned by a superuser / goal script.
Some simple functions to help with accessing containers.
auto Slide(TIter first, TIter last, TIter position) -> std::pair< TIter, TIter >
Move elements between first and last to a new position, rotating elements in between as necessary.
Money GetPrice(Price index, uint cost_factor, const GRFFile *grf_file, int shift)
Determine a certain price.
Definition economy.cpp:940
Price
Enumeration of all base prices for use with Prices.
@ BuildVehicleWagon
Price for purchasing new wagons.
@ BuildVehicleTrain
Price for purchasing new train engines.
@ BuildVehicleShip
Price for purchasing new ships.
@ Invalid
Invalid base price.
@ BuildVehicleAircraft
Price for purchasing new aircrafts.
@ RunningAircraft
Running cost of aircrafts.
@ BuildVehicleRoad
Price for purchasing new road vehicles.
@ RunningShip
Running cost of ships.
void ClearEnginesHiddenFlagOfCompany(CompanyID cid)
Clear the 'hidden' flag for all engines of a new company.
Definition engine.cpp:1009
void SetYearEngineAgingStops()
Compute the value for _year_engine_aging_stops.
Definition engine.cpp:697
static void NewVehicleAvailable(Engine *e)
An engine has become available for general use.
Definition engine.cpp:1084
const uint8_t _engine_counts[4]
Number of engines of each vehicle type in original engine data.
Definition engine.cpp:54
void SetupEngines()
Initialise the engine pool with the data from the original vehicles.
Definition engine.cpp:604
static CompanyID GetPreviewCompany(Engine *e)
Get the best company for an engine preview.
Definition engine.cpp:919
const uint8_t _engine_offsets[4]
Offset of the first engine of each vehicle type in original engine data.
Definition engine.cpp:62
static void ClearLastVariant(EngineID engine_id, VehicleType type)
Ensure engine is not set as the last used variant for any other engine.
Definition engine.cpp:639
static bool IsVehicleTypeDisabled(VehicleType type, bool ai)
Checks if a vehicle type is disabled for all/ai companies.
Definition engine.cpp:953
static void AcceptEnginePreview(EngineID eid, CompanyID company, int recursion_depth=0)
Company company accepts engine eid for preview.
Definition engine.cpp:887
CommandCost CmdRenameEngine(DoCommandFlags flags, EngineID engine_id, const std::string &text)
Rename an engine.
Definition engine.cpp:1217
CommandCost CmdWantEnginePreview(DoCommandFlags flags, EngineID engine_id)
Accept an engine prototype.
Definition engine.cpp:1044
void CalendarEnginesMonthlyLoop()
Monthly update of the availability, reliability, and preview offers of the engines.
Definition engine.cpp:1144
static bool IsUniqueEngineName(const std::string &name)
Is name still free as name for an engine?
Definition engine.cpp:1201
bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company)
Check if an engine is buildable.
Definition engine.cpp:1251
static TimerGameCalendar::Year _year_engine_aging_stops
Year that engine aging stops.
Definition engine.cpp:51
static const IntervalTimer< TimerGameCalendar > _calendar_engines_daily({TimerGameCalendar::Trigger::Day, TimerGameCalendar::Priority::Engine}, [](auto) { for(Company *c :Company::Iterate()) { c->avail_railtypes=AddDateIntroducedRailTypes(c->avail_railtypes, TimerGameCalendar::date);c->avail_roadtypes=AddDateIntroducedRoadTypes(c->avail_roadtypes, TimerGameCalendar::date);} if(TimerGameCalendar::year >=_year_engine_aging_stops) return;for(Engine *e :Engine::Iterate()) { EngineID i=e->index;if(e->flags.Test(EngineFlag::ExclusivePreview)) { if(e->preview_company !=CompanyID::Invalid()) { if(!--e->preview_wait) { CloseWindowById(WC_ENGINE_PREVIEW, i);e->preview_company=CompanyID::Invalid();} } else if(e->preview_asked.Count()< MAX_COMPANIES) { e->preview_company=GetPreviewCompany(e);if(e->preview_company==CompanyID::Invalid()) { e->preview_asked.Set();continue;} e->preview_asked.Set(e->preview_company);e->preview_wait=20;if(!IsVehicleTypeDisabled(e->type, true)) AI::NewEvent(e->preview_company, new ScriptEventEnginePreview(i));if(IsInteractiveCompany(e->preview_company)) ShowEnginePreviewWindow(i);} } } })
Daily check to offer an exclusive engine preview to the companies.
static void DisableEngineForCompany(EngineID eid, CompanyID company)
Forbids engine eid to be used by a company company.
Definition engine.cpp:863
void StartupEngines()
Start/initialise all our engines.
Definition engine.cpp:803
static bool IsWagon(EngineID index)
Determine whether an engine type is a wagon (and not a loco).
Definition engine.cpp:628
CommandCost CmdEngineCtrl(DoCommandFlags flags, EngineID engine_id, CompanyID company_id, bool allow)
Allow or forbid a specific company to use an engine.
Definition engine.cpp:1062
void StartupOneEngine(Engine *e, const TimerGameCalendar::YearMonthDay &aging_ymd, uint32_t seed)
Start/initialise one engine.
Definition engine.cpp:722
void CheckEngines()
Check for engines that have an appropriate availability.
Definition engine.cpp:1318
bool IsEngineRefittable(EngineID engine)
Check if an engine is refittable.
Definition engine.cpp:1292
CommandCost CmdSetVehicleVisibility(DoCommandFlags flags, EngineID engine_id, bool hide)
Set the visibility of an engine.
Definition engine.cpp:1023
void CalcEngineReliability(Engine *e, bool new_month)
Update Engine::reliability and (if needed) update the engine GUIs.
Definition engine.cpp:651
static void EnableEngineForCompany(EngineID eid, CompanyID company)
Allows engine eid to be used by a company company.
Definition engine.cpp:835
Base class for engines.
Functions related to engines.
StringID GetEngineCategoryName(EngineID engine)
Return the category of an engine.
Engine GUI functions, used by build_vehicle_gui and autoreplace_gui.
@ AIR_CTOL
Conventional Take Off and Landing, i.e. planes.
@ SyncReliability
Engine reliability will be synced with variant parent.
@ NoNews
No 'new vehicle' news will be generated.
@ JoinPreview
Engine will join exclusive preview with variant parent.
@ NoPreview
No exclusive preview will be offered.
@ NoDefaultCargoMultiplier
Use the new capacity algorithm. The default cargotype of the vehicle does not affect capacity multipl...
static const uint MAX_LENGTH_ENGINE_NAME_CHARS
The maximum length of an engine name in characters including '\0'.
PoolID< uint16_t, struct EngineIDTag, 64000, 0xFFFF > EngineID
Unique identification number of an engine.
Definition engine_type.h:26
uint64_t PackEngineNameDParam(EngineID engine_id, EngineNameContext context, uint32_t extra_data=0)
Combine an engine ID and a name context to an engine name dparam.
@ Available
This vehicle is available to everyone.
@ ExclusivePreview
This vehicle is in the exclusive preview stage, either being used or being offered to a company.
@ PreviewNews
Name is shown in exclusive preview or newspaper.
@ RAILVEH_WAGON
simple wagon, not motorized
Definition engine_type.h:34
@ RAILVEH_MULTIHEAD
indicates a combination of two locomotives
Definition engine_type.h:33
This file contains all the data for vehicles.
Functions related to errors.
@ WL_WARNING
Other information.
Definition error.h:25
void ShowErrorMessage(EncodedString &&summary_msg, int x, int y, CommandCost &cc)
Display an error message in a window.
uint GetGroupNumEngines(CompanyID company, GroupID id_g, EngineID id_e)
Get the number of engines with EngineID id_e in the group with GroupID id_g and its sub-groups.
void MarkWholeScreenDirty()
This function mark the whole screen as dirty.
Definition gfx.cpp:1554
static constexpr GroupID ALL_GROUP
All vehicles are in this group.
Definition group_type.h:17
Base for the NewGRF implementation.
void ReloadNewGRFData()
Reload all NewGRF files during a running game.
@ RefitCapacity
Cargo capacity after refit.
@ CargoSuffix
Show suffix after cargo name.
@ CBID_VEHICLE_REFIT_CAPACITY
Refit capacity, the passed vehicle needs to have its ->cargo_type set to the cargo we are refitting t...
static const uint CALLBACK_FAILED
Different values for Callback result evaluations.
uint16_t GetVehicleCallback(CallbackID callback, uint32_t param1, uint32_t param2, EngineID engine, const Vehicle *v, std::span< int32_t > regs100)
Evaluate a newgrf callback for vehicles.
Functions for NewGRF engines.
@ PROP_AIRCRAFT_PASSENGER_CAPACITY
Passenger Capacity.
@ PROP_ROADVEH_WEIGHT
Weight in 1/4 t.
@ PROP_TRAIN_COST_FACTOR
Purchase cost (if dualheaded: sum of both vehicles).
@ PROP_TRAIN_WEIGHT
Weight in t (if dualheaded: for each single vehicle).
@ PROP_AIRCRAFT_RANGE
Aircraft range.
@ PROP_TRAIN_CARGO_CAPACITY
Capacity (if dualheaded: for each single vehicle).
@ PROP_AIRCRAFT_RUNNING_COST_FACTOR
Yearly runningcost.
@ PROP_ROADVEH_RUNNING_COST_FACTOR
Yearly runningcost.
@ PROP_TRAIN_TRACTIVE_EFFORT
Tractive effort coefficient in 1/256.
@ PROP_SHIP_CARGO_CAPACITY
Capacity.
@ PROP_ROADVEH_TRACTIVE_EFFORT
Tractive effort coefficient in 1/256.
@ PROP_SHIP_COST_FACTOR
Purchase cost.
@ PROP_ROADVEH_CARGO_CAPACITY
Capacity.
@ PROP_AIRCRAFT_SPEED
Max. speed: 1 unit = 8 mph = 12.8 km-ish/h.
@ PROP_AIRCRAFT_MAIL_CAPACITY
Mail Capacity.
@ PROP_SHIP_SPEED
Max. speed: 1 unit = 1/3.2 mph = 0.5 km-ish/h.
@ PROP_SHIP_RUNNING_COST_FACTOR
Yearly runningcost.
@ PROP_ROADVEH_COST_FACTOR
Purchase cost.
@ PROP_ROADVEH_POWER
Power in 10 HP.
@ PROP_AIRCRAFT_COST_FACTOR
Purchase cost.
@ PROP_TRAIN_RUNNING_COST_FACTOR
Yearly runningcost (if dualheaded: sum of both vehicles).
@ PROP_TRAIN_POWER
Power in hp (if dualheaded: sum of both vehicles).
@ PROP_TRAIN_SPEED
Max. speed: 1 unit = 1/1.6 mph = 1 km-ish/h.
@ PROP_ROADVEH_SPEED
Max. speed: 1 unit = 1/0.8 mph = 2 km-ish/h.
Functions related to news.
void AddNewsItem(EncodedString &&headline, NewsType type, NewsStyle style, NewsFlags flags, NewsReference ref1={}, NewsReference ref2={}, std::unique_ptr< NewsAllocatedData > &&data=nullptr, AdviceType advice_type=AdviceType::Invalid)
Add a new newsitem to be shown.
Definition news_gui.cpp:902
@ NewVehicles
New vehicle has become available.
Definition news_type.h:42
@ Vehicle
Vehicle news item. (new engine available).
Definition news_type.h:81
Some methods of Pool are placed here in order to reduce compilation time and binary size.
#define INSTANTIATE_POOL_METHODS(name)
Force instantiation of pool methods so we don't get linker errors.
RailTypes GetCompanyRailTypes(CompanyID company, bool introduces)
Get the rail types the given company can build.
Definition rail.cpp:135
RailTypes AddDateIntroducedRailTypes(RailTypes current, TimerGameCalendar::Date date)
Add the rail types that are to be introduced at the given date.
Definition rail.cpp:102
RailTypes GetAllIntroducesRailTypes(RailTypes railtypes)
Returns all introduced railtypes for a set of railtypes.
Definition rail.h:336
RailTypes GetAllCompatibleRailTypes(RailTypes railtypes)
Returns all compatible railtypes for a set of railtypes.
Definition rail.h:312
EnumBitSet< RailType, uint64_t > RailTypes
Allow incrementing of Track variables.
Definition rail_type.h:38
void SetRandomSeed(uint32_t seed)
(Re)set the state of the random number generators.
Pseudo random number generator.
void SaveRandomSeeds(SavedRandomSeeds *storage)
Saves the current seeds.
void RestoreRandomSeeds(const SavedRandomSeeds &storage)
Restores previously saved seeds.
RoadTypes GetCompanyRoadTypes(CompanyID company, bool introduces)
Get the road types the given company can build.
Definition road.cpp:210
RoadTypes AddDateIntroducedRoadTypes(RoadTypes current, TimerGameCalendar::Date date)
Add the road types that are to be introduced at the given date.
Definition road.cpp:177
const RoadTypeInfo * GetRoadTypeInfo(RoadType roadtype)
Returns a pointer to the Roadtype information for a given roadtype.
Definition road.h:215
@ ROADTYPE_END
Used for iterations.
Definition road_type.h:27
A number of safeguards to prevent using unsafe methods.
GameSettings _settings_game
Game settings of a running game or the scenario editor.
Definition settings.cpp:61
Definition of base types and functions in a cross-platform compatible way.
#define lengthof(array)
Return the length of an fixed size array.
Definition stdafx.h:271
size_t Utf8StringLength(std::string_view str)
Get the length of an UTF-8 encoded string in number of characters and thus not the number of bytes th...
Definition string.cpp:349
Functions related to low-level strings.
EncodedString GetEncodedString(StringID str)
Encode a string with no parameters into an encoded string.
Definition strings.cpp:90
Functions related to OTTD's strings.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Information about a aircraft vehicle.
uint16_t multiplier
Capacity multiplier for vehicles. (8 fractional bits).
Definition cargotype.h:80
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:137
RoadTypes avail_roadtypes
Road types available to this company.
RailTypes avail_railtypes
Rail types available to this company.
Projection to get a unique key of an EngineIDMapping, used for sorting in EngineOverrideManager.
Information about a vehicle.
TimerGameCalendar::Year base_life
Basic duration of engine availability (without random parts). 0xFF means infinite life.
LandscapeTypes climates
Climates supported by the engine.
EngineID variant_id
Engine variant ID. If set, will be treated specially in purchase lists.
VehicleCallbackMasks callback_mask
Bitmask of vehicle callbacks that have to be called.
TimerGameCalendar::Date base_intro
Basic date of engine introduction (without random parts).
int8_t retire_early
Number of years early to retire vehicle.
TimerGameCalendar::Year lifelength
Lifetime of a single vehicle.
Stores the mapping of EngineID to the internal id of newgrfs.
static bool ResetToCurrentNewGRFConfig()
Tries to reset the engine mapping to match the current NewGRF configuration.
Definition engine.cpp:588
void ResetToDefaultMapping()
Initializes the EngineOverrideManager with the default engines.
Definition engine.cpp:512
EngineID UseUnreservedID(VehicleType type, uint16_t grf_local_id, uint32_t grfid, bool static_access)
Look for an unreserved EngineID matching the local id, and reserve it if found.
Definition engine.cpp:552
EngineID GetID(VehicleType type, uint16_t grf_local_id, uint32_t grfid)
Looks up an EngineID in the EngineOverrideManager.
Definition engine.cpp:533
Dynamic data of a loaded NewGRF.
Definition newgrf.h:117
static Pool::IterateWrapper< Vehicle > Iterate(size_t from=0)
static T * CreateAtIndex(EngineID index, Targs &&... args)
static Engine * Get(auto index)
static bool IsValidID(auto index)
static Engine * GetIfValid(auto index)
Information about a rail vehicle.
Definition engine_type.h:74
RailTypes railtypes
Railtypes, mangled if elrail is disabled.
Definition engine_type.h:78
Information about a road vehicle.
RoadType roadtype
Road type.
Stores the state of all random number generators.
Information about a ship vehicle.
Definition engine_type.h:99
Vehicle data structure.
EngineID engine_type
The type of engine used for this vehicle.
CargoType cargo_type
type of cargo this vehicle is carrying
uint8_t cargo_subtype
Used for livery refits (NewGRF variations).
Definition of Interval and OneShot timers.
Definition of the game-calendar-timer.
Definition of the tick-based game-timer.
@ TRANSPORT_ROAD
Transport by road vehicle.
@ TRANSPORT_WATER
Transport over water.
@ TRANSPORT_AIR
Transport through air.
Functions related to vehicles.
bool IsCompanyBuildableVehicleType(VehicleType type)
Is the given vehicle type buildable by a company?
VehicleType
Available vehicle types.
@ VEH_INVALID
Non-existing type of vehicle.
@ VEH_ROAD
Road vehicle type.
@ VEH_AIRCRAFT
Aircraft vehicle type.
@ VEH_SHIP
Ship vehicle type.
@ VEH_TRAIN
Train vehicle type.
@ VEH_COMPANY_END
Last company-ownable type.
static const int GROUND_ACCELERATION
Acceleration due to gravity, 9.8 m/s^2.
void CloseWindowById(WindowClass cls, WindowNumber number, bool force, int data)
Close a window by its class and window number (if it is open).
Definition window.cpp:1198
void CloseWindowByClass(WindowClass cls, int data)
Close all windows of a given class.
Definition window.cpp:1211
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting).
Definition window.cpp:3218
void InvalidateWindowData(WindowClass cls, WindowNumber number, int data, bool gui_scope)
Mark window data of the window of a given class and specific window number as invalid (in need of re-...
Definition window.cpp:3310
void InvalidateWindowClassesData(WindowClass cls, int data, bool gui_scope)
Mark window data of all windows of a given class as invalid (in need of re-computing) Note that by de...
Definition window.cpp:3328
Window functions not directly related to making/drawing windows.
@ WC_BUILD_TOOLBAR
Build toolbar; Window numbers:
Definition window_type.h:78
@ WC_REPLACE_VEHICLE
Replace vehicle window; Window numbers:
@ WC_ENGINE_PREVIEW
Engine preview window; Window numbers:
@ WC_MAIN_TOOLBAR
Main toolbar (the long bar at the top); Window numbers:
Definition window_type.h:63
@ WC_BUILD_VEHICLE
Build vehicle; Window numbers: