OpenTTD Source 20260421-master-gc2fbc6fdeb
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
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
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 if (!_settings_game.vehicle.aircraft_range) return 0;
459 switch (this->type) {
460 case VEH_AIRCRAFT:
461 return GetEngineProperty(this->index, PROP_AIRCRAFT_RANGE, this->VehInfo<AircraftVehicleInfo>().max_range);
462
463 default: NOT_REACHED();
464 }
465}
466
472{
473 switch (this->type) {
474 case VEH_AIRCRAFT:
475 switch (this->VehInfo<AircraftVehicleInfo>().subtype) {
476 case AIR_HELI: return STR_LIVERY_HELICOPTER;
477 case AIR_CTOL: return STR_LIVERY_SMALL_PLANE;
478 case AIR_CTOL | AIR_FAST: return STR_LIVERY_LARGE_PLANE;
479 default: NOT_REACHED();
480 }
481
482 default: NOT_REACHED();
483 }
484}
485
491bool Engine::IsVariantHidden(CompanyID c) const
492{
493 /* In case company is spectator. */
494 if (c >= MAX_COMPANIES) return false;
495
496 /* Shortcut if this engine is explicitly hidden. */
497 if (this->IsHidden(c)) return true;
498
499 /* Check for hidden parent variants. This is a bit convoluted as we must check hidden status of
500 * the last display variant rather than the actual parent variant. */
501 const Engine *re = this;
502 const Engine *ve = re->GetDisplayVariant();
503 while (!(ve->IsHidden(c)) && re->info.variant_id != EngineID::Invalid()) {
504 re = Engine::Get(re->info.variant_id);
505 ve = re->GetDisplayVariant();
506 }
507 return ve->IsHidden(c);
508}
509
514{
515 EngineID id = EngineID::Begin();
516 for (VehicleType type = VEH_TRAIN; type <= VEH_AIRCRAFT; type++) {
517 auto &map = this->mappings[type];
518 map.clear();
519 for (uint internal_id = 0; internal_id < _engine_counts[type]; internal_id++, ++id) {
520 map.emplace_back(INVALID_GRFID, internal_id, type, internal_id, id);
521 }
522 }
523}
524
534EngineID EngineOverrideManager::GetID(VehicleType type, uint16_t grf_local_id, uint32_t grfid)
535{
536 const auto &map = this->mappings[type];
537 const auto key = EngineIDMapping::Key(grfid, grf_local_id);
538 auto it = std::ranges::lower_bound(map, key, std::less{}, EngineIDMappingKeyProjection{});
539 if (it == std::end(map) || it->Key() != key) return EngineID::Invalid();
540 return it->engine;
541}
542
553EngineID EngineOverrideManager::UseUnreservedID(VehicleType type, uint16_t grf_local_id, uint32_t grfid, bool static_access)
554{
555 auto &map = _engine_mngr.mappings[type];
556 const auto key = EngineIDMapping::Key(INVALID_GRFID, grf_local_id);
557 auto it = std::ranges::lower_bound(map, key, std::less{}, EngineIDMappingKeyProjection{});
558 if (it == std::end(map) || it->Key() != key) return EngineID::Invalid();
559
560 if (!static_access && grfid != INVALID_GRFID) {
561 /* Reserve the engine slot for the new grfid. */
562 it->grfid = grfid;
563
564 /* Relocate entry to its new position in the mapping list to keep it sorted. */
565 auto p = std::ranges::lower_bound(map, EngineIDMapping::Key(grfid, grf_local_id), std::less{}, EngineIDMappingKeyProjection{});
566 it = Slide(it, std::next(it), p).first;
567 }
568
569 return it->engine;
570}
571
572void EngineOverrideManager::SetID(VehicleType type, uint16_t grf_local_id, uint32_t grfid, uint8_t substitute_id, EngineID engine)
573{
574 auto &map = this->mappings[type];
575 const auto key = EngineIDMapping::Key(grfid, grf_local_id);
576 auto it = std::ranges::lower_bound(map, key, std::less{}, EngineIDMappingKeyProjection{});
577 if (it == std::end(map) || it->Key() != key) {
578 map.emplace(it, grfid, grf_local_id, type, substitute_id, engine);
579 } else {
580 it->engine = engine;
581 }
582}
583
590{
591 for (const Vehicle *v : Vehicle::Iterate()) {
592 if (IsCompanyBuildableVehicleType(v)) return false;
593 }
594
595 /* Reset the engines, they will get new EngineIDs */
596 _engine_mngr.ResetToDefaultMapping();
598
599 return true;
600}
601
606{
608 _engine_pool.CleanPool();
609
610 for (VehicleType type = VEH_BEGIN; type != VEH_COMPANY_END; type++) {
611 const auto &mapping = _engine_mngr.mappings[type];
612
613 /* Verify that the engine override manager has at least been set up with the default engines. */
614 assert(std::size(mapping) >= _engine_counts[type]);
615
616 for (const EngineIDMapping &eid : mapping) {
617 Engine::CreateAtIndex(eid.engine, type, eid.internal_id);
618 }
619 }
620}
621
622void ShowEnginePreviewWindow(EngineID engine);
623
629static bool IsWagon(EngineID index)
630{
631 const Engine *e = Engine::Get(index);
632 return e->type == VEH_TRAIN && e->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_WAGON;
633}
634
640static void ClearLastVariant(EngineID engine_id, VehicleType type)
641{
642 for (Engine *e : Engine::IterateType(type)) {
643 if (e->display_last_variant == engine_id) e->display_last_variant = EngineID::Invalid();
644 }
645}
646
652void CalcEngineReliability(Engine *e, bool new_month)
653{
654 /* Get source engine for reliability age. This is normally our engine unless variant reliability syncing is requested. */
655 Engine *re = e;
656 while (re->info.variant_id != EngineID::Invalid() && re->info.extra_flags.Test(ExtraEngineFlag::SyncReliability)) {
657 re = Engine::Get(re->info.variant_id);
658 }
659
660 uint32_t age = re->age;
661 if (new_month && re->index > e->index && age != INT32_MAX) age++; /* parent variant's age has not yet updated. */
662
663 /* Check for early retirement */
664 if (e->company_avail.Any() && !_settings_game.vehicle.never_expire_vehicles && e->info.base_life != 0xFF) {
665 int retire_early = e->info.retire_early;
666 uint retire_early_max_age = std::max(0, e->duration_phase_1 + e->duration_phase_2 - retire_early * 12);
667 if (retire_early != 0 && age >= retire_early_max_age) {
668 /* Early retirement is enabled and we're past the date... */
670 ClearLastVariant(e->index, e->type);
672 }
673 }
674
675 if (age < e->duration_phase_1) {
676 uint start = e->reliability_start;
677 e->reliability = age * (e->reliability_max - start) / e->duration_phase_1 + start;
678 } else if ((age -= e->duration_phase_1) < e->duration_phase_2 || _settings_game.vehicle.never_expire_vehicles || e->info.base_life == 0xFF) {
679 /* We are at the peak of this engines life. It will have max reliability.
680 * This is also true if the engines never expire. They will not go bad over time */
682 } else if ((age -= e->duration_phase_2) < e->duration_phase_3) {
683 uint max = e->reliability_max;
684 e->reliability = (int)age * (int)(e->reliability_final - max) / e->duration_phase_3 + max;
685 } else {
686 /* time's up for this engine.
687 * We will now completely retire this design */
690 /* Kick this engine out of the lists */
691 ClearLastVariant(e->index, e->type);
693 }
694
695}
696
699{
700 /* Determine last engine aging year, default to 2050 as previously. */
702
703 for (const Engine *e : Engine::Iterate()) {
704 const EngineInfo *ei = &e->info;
705
706 /* Exclude certain engines */
707 if (!ei->climates.Test(_settings_game.game_creation.landscape)) continue;
708 if (e->type == VEH_TRAIN && e->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_WAGON) continue;
709
710 /* Base year ending date on half the model life */
711 TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(ei->base_intro + (ei->lifelength.base() * CalendarTime::DAYS_IN_LEAP_YEAR) / 2);
712
714 }
715}
716
723void StartupOneEngine(Engine *e, const TimerGameCalendar::YearMonthDay &aging_ymd, uint32_t seed)
724{
725 const EngineInfo *ei = &e->info;
726
727 e->age = 0;
728 e->flags = {};
731
732 /* Vehicles with the same base_intro date shall be introduced at the same time.
733 * Make sure they use the same randomisation of the date. */
734 SavedRandomSeeds saved_seeds;
735 SaveRandomSeeds(&saved_seeds);
736 SetRandomSeed(_settings_game.game_creation.generation_seed ^ seed ^
737 ei->base_intro.base() ^
738 e->type ^
739 e->GetGRFID());
740 uint32_t r = Random();
741
742 /* Don't randomise the start-date in the first two years after gamestart to ensure availability
743 * of engines in early starting games.
744 * Note: TTDP uses fixed 1922 */
745 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;
747 TimerGameCalendar::YearMonthDay intro_ymd = TimerGameCalendar::ConvertDateToYMD(e->intro_date);
748 int aging_months = aging_ymd.year.base() * 12 + aging_ymd.month;
749 int intro_months = intro_ymd.year.base() * 12 + intro_ymd.month;
750 if (intro_ymd.day > 1) intro_months++; // Engines are introduced at the first month start at/after intro date.
751 e->age = aging_months - intro_months;
752 e->company_avail.Set();
754 }
755
756 /* Get parent variant index for syncing reliability via random seed. */
757 const Engine *re = e;
758 while (re->info.variant_id != EngineID::Invalid() && re->info.extra_flags.Test(ExtraEngineFlag::SyncReliability)) {
759 re = Engine::Get(re->info.variant_id);
760 }
761
762 SetRandomSeed(_settings_game.game_creation.generation_seed ^ seed ^
763 (re->index.base() << 16) ^ (re->info.base_intro.base() << 12) ^ (re->info.decay_speed << 8) ^
764 (re->info.lifelength.base() << 4) ^ re->info.retire_early ^
765 e->type ^
766 e->GetGRFID());
767
768 /* Base reliability defined as a percentage of UINT16_MAX. */
769 const uint16_t RELIABILITY_START = UINT16_MAX * 48 / 100;
770 const uint16_t RELIABILITY_MAX = UINT16_MAX * 75 / 100;
771 const uint16_t RELIABILITY_FINAL = UINT16_MAX * 25 / 100;
772
773 static_assert(RELIABILITY_START == 0x7AE0);
774 static_assert(RELIABILITY_MAX == 0xBFFF);
775 static_assert(RELIABILITY_FINAL == 0x3FFF);
776
777 r = Random();
778 /* 14 bits gives a value between 0 and 16383, which is up to an additional 25%p reliability on top of the base reliability. */
779 e->reliability_start = GB(r, 16, 14) + RELIABILITY_START;
780 e->reliability_max = GB(r, 0, 14) + RELIABILITY_MAX;
781
782 r = Random();
783 e->reliability_final = GB(r, 16, 14) + RELIABILITY_FINAL;
784
785 e->duration_phase_1 = GB(r, 0, 5) + 7;
786 e->duration_phase_2 = std::max(0, int(GB(r, 5, 4)) + ei->base_life.base() * 12 - 96);
787 e->duration_phase_3 = GB(r, 9, 7) + 120;
788
789 RestoreRandomSeeds(saved_seeds);
790
791 e->reliability_spd_dec = ei->decay_speed << 2;
792
793 /* prevent certain engines from ever appearing. */
794 if (!ei->climates.Test(_settings_game.game_creation.landscape)) {
797 }
798}
799
805{
806 /* Aging of vehicles stops, so account for that when starting late */
808 TimerGameCalendar::YearMonthDay aging_ymd = TimerGameCalendar::ConvertDateToYMD(aging_date);
809 uint32_t seed = Random();
810
811 for (Engine *e : Engine::Iterate()) {
812 StartupOneEngine(e, aging_ymd, seed);
813 }
814 for (Engine *e : Engine::Iterate()) {
815 CalcEngineReliability(e, false);
816 }
817
818 /* Update the bitmasks for the vehicle lists */
819 for (Company *c : Company::Iterate()) {
820 c->avail_railtypes = GetCompanyRailTypes(c->index);
821 c->avail_roadtypes = GetCompanyRoadTypes(c->index);
822 }
823
824 /* Invalidate any open purchase lists */
826
829}
830
836static void EnableEngineForCompany(EngineID eid, CompanyID company)
837{
838 Engine *e = Engine::Get(eid);
839 Company *c = Company::Get(company);
840
841 e->company_avail.Set(company);
842 if (e->type == VEH_TRAIN) {
844 } else if (e->type == VEH_ROAD) {
846 }
847
848 if (company == _local_company) {
850
851 /* Update the toolbar. */
856 }
857}
858
864static void DisableEngineForCompany(EngineID eid, CompanyID company)
865{
866 Engine *e = Engine::Get(eid);
867 Company *c = Company::Get(company);
868
869 e->company_avail.Reset(company);
870 if (e->type == VEH_TRAIN) {
872 } else if (e->type == VEH_ROAD) {
874 }
875
876 if (company == _local_company) {
877 ClearLastVariant(e->index, e->type);
879 }
880}
881
888static void AcceptEnginePreview(EngineID eid, CompanyID company, int recursion_depth = 0)
889{
890 Engine *e = Engine::Get(eid);
891
892 e->preview_company = CompanyID::Invalid();
893 e->preview_asked.Set();
894
895 EnableEngineForCompany(eid, company);
896
897 /* Notify preview window to remove this engine.
898 * Note: We cannot directly close the window.
899 * In singleplayer this function is called from the preview window, so
900 * we have to use the GUI-scope scheduling of InvalidateWindowData.
901 */
903
904 /* Don't search for variants to include if we are 10 levels deep already. */
905 if (recursion_depth >= 10) return;
906
907 /* Find variants to be included in preview. */
908 for (Engine *ve : Engine::IterateType(e->type)) {
909 if (ve->index != eid && ve->info.variant_id == eid && ve->info.extra_flags.Test(ExtraEngineFlag::JoinPreview)) {
910 AcceptEnginePreview(ve->index, company, recursion_depth + 1);
911 }
912 }
913}
914
920static CompanyID GetPreviewCompany(Engine *e)
921{
922 CompanyID best_company = CompanyID::Invalid();
923
924 /* For trains the cargomask has no useful meaning, since you can attach other wagons */
925 CargoTypes cargomask = e->type != VEH_TRAIN ? GetUnionOfArticulatedRefitMasks(e->index, true) : ALL_CARGOTYPES;
926
927 int32_t best_hist = -1;
928 for (const Company *c : Company::Iterate()) {
929 if (c->block_preview == 0 && !e->preview_asked.Test(c->index) &&
930 c->old_economy[0].performance_history > best_hist) {
931
932 /* Check whether the company uses similar vehicles */
933 for (const Vehicle *v : Vehicle::Iterate()) {
934 if (v->owner != c->index || v->type != e->type) continue;
935 if (!v->GetEngine()->CanCarryCargo() || !HasBit(cargomask, v->cargo_type)) continue;
936
937 best_hist = c->old_economy[0].performance_history;
938 best_company = c->index;
939 break;
940 }
941 }
942 }
943
944 return best_company;
945}
946
954static bool IsVehicleTypeDisabled(VehicleType type, bool ai)
955{
956 switch (type) {
957 case VEH_TRAIN: return _settings_game.vehicle.max_trains == 0 || (ai && _settings_game.ai.ai_disable_veh_train);
958 case VEH_ROAD: return _settings_game.vehicle.max_roadveh == 0 || (ai && _settings_game.ai.ai_disable_veh_roadveh);
959 case VEH_SHIP: return _settings_game.vehicle.max_ships == 0 || (ai && _settings_game.ai.ai_disable_veh_ship);
960 case VEH_AIRCRAFT: return _settings_game.vehicle.max_aircraft == 0 || (ai && _settings_game.ai.ai_disable_veh_aircraft);
961
962 default: NOT_REACHED();
963 }
964}
965
967static const IntervalTimer<TimerGameCalendar> _calendar_engines_daily({TimerGameCalendar::Trigger::Day, TimerGameCalendar::Priority::Engine}, [](auto)
968{
969 for (Company *c : Company::Iterate()) {
970 c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes, TimerGameCalendar::date);
971 c->avail_roadtypes = AddDateIntroducedRoadTypes(c->avail_roadtypes, TimerGameCalendar::date);
972 }
973
975
976 for (Engine *e : Engine::Iterate()) {
977 EngineID i = e->index;
978 if (e->flags.Test(EngineFlag::ExclusivePreview)) {
979 if (e->preview_company != CompanyID::Invalid()) {
980 if (!--e->preview_wait) {
982 e->preview_company = CompanyID::Invalid();
983 }
984 } else if (e->preview_asked.Count() < MAX_COMPANIES) {
985 e->preview_company = GetPreviewCompany(e);
986
987 if (e->preview_company == CompanyID::Invalid()) {
988 e->preview_asked.Set();
989 continue;
990 }
991
992 e->preview_asked.Set(e->preview_company);
993 e->preview_wait = 20;
994 /* AIs are intentionally not skipped for preview even if they cannot build a certain
995 * vehicle type. This is done to not give poor performing human companies an "unfair"
996 * boost that they wouldn't have gotten against other human companies. The check on
997 * the line below is just to make AIs not notice that they have a preview if they
998 * cannot build the vehicle. */
999 if (!IsVehicleTypeDisabled(e->type, true)) AI::NewEvent(e->preview_company, new ScriptEventEnginePreview(i));
1000 if (IsInteractiveCompany(e->preview_company)) ShowEnginePreviewWindow(i);
1001 }
1002 }
1003 }
1004});
1005
1011{
1012 for (Engine *e : Engine::Iterate()) {
1013 e->company_hidden.Reset(cid);
1014 }
1015}
1016
1024CommandCost CmdSetVehicleVisibility(DoCommandFlags flags, EngineID engine_id, bool hide)
1025{
1026 Engine *e = Engine::GetIfValid(engine_id);
1027 if (e == nullptr || _current_company >= MAX_COMPANIES) return CMD_ERROR;
1028 if (!IsEngineBuildable(e->index, e->type, _current_company)) return CMD_ERROR;
1029
1030 if (flags.Test(DoCommandFlag::Execute)) {
1033 }
1034
1035 return CommandCost();
1036}
1037
1045CommandCost CmdWantEnginePreview(DoCommandFlags flags, EngineID engine_id)
1046{
1047 Engine *e = Engine::GetIfValid(engine_id);
1048 if (e == nullptr || !e->flags.Test(EngineFlag::ExclusivePreview) || e->preview_company != _current_company) return CMD_ERROR;
1049
1051
1052 return CommandCost();
1053}
1054
1063CommandCost CmdEngineCtrl(DoCommandFlags flags, EngineID engine_id, CompanyID company_id, bool allow)
1064{
1065 if (_current_company != OWNER_DEITY) return CMD_ERROR;
1066
1067 if (!Engine::IsValidID(engine_id) || !Company::IsValidID(company_id)) return CMD_ERROR;
1068
1069 if (flags.Test(DoCommandFlag::Execute)) {
1070 if (allow) {
1071 EnableEngineForCompany(engine_id, company_id);
1072 } else {
1073 DisableEngineForCompany(engine_id, company_id);
1074 }
1075 }
1076
1077 return CommandCost();
1078}
1079
1086{
1087 EngineID index = e->index;
1088
1089 /* In case the company didn't build the vehicle during the intro period,
1090 * prevent that company from getting future intro periods for a while. */
1092 for (Company *c : Company::Iterate()) {
1093 if (!e->company_avail.Test(c->index)) continue;
1094
1095 /* Check the company's 'ALL_GROUP' group statistics. This only includes countable vehicles, which is fine
1096 * as those are the only engines that can be given exclusive previews. */
1097 if (GetGroupNumEngines(c->index, ALL_GROUP, e->index) == 0) {
1098 /* The company did not build this engine during preview. */
1099 c->block_preview = 20;
1100 }
1101 }
1102 }
1103
1106
1107 /* Now available for all companies */
1108 e->company_avail.Set();
1109
1110 /* Do not introduce new rail wagons */
1111 if (IsWagon(index)) return;
1112
1113 if (e->type == VEH_TRAIN) {
1114 /* maybe make another rail type available */
1115 assert(e->VehInfo<RailVehicleInfo>().railtypes != RailTypes{});
1117 for (Company *c : Company::Iterate()) c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes | introduced, TimerGameCalendar::date);
1118 } else if (e->type == VEH_ROAD) {
1119 /* maybe make another road type available */
1120 assert(e->VehInfo<RoadVehicleInfo>().roadtype < ROADTYPE_END);
1121 for (Company *c : Company::Iterate()) c->avail_roadtypes = AddDateIntroducedRoadTypes(c->avail_roadtypes | GetRoadTypeInfo(e->VehInfo<RoadVehicleInfo>().roadtype)->introduces_roadtypes, TimerGameCalendar::date);
1122 }
1123
1124 /* Only broadcast event if AIs are able to build this vehicle type. */
1125 if (!IsVehicleTypeDisabled(e->type, true)) AI::BroadcastNewEvent(new ScriptEventEngineAvailable(index));
1126
1127 /* Only provide the "New Vehicle available" news paper entry, if engine can be built. */
1128 if (!IsVehicleTypeDisabled(e->type, false) && !e->info.extra_flags.Test(ExtraEngineFlag::NoNews)) {
1129 AddNewsItem(GetEncodedString(STR_NEWS_NEW_VEHICLE_NOW_AVAILABLE_WITH_TYPE,
1130 GetEngineCategoryName(index),
1133 }
1134
1135 /* Update the toolbar. */
1139
1140 /* Remove from preview windows */
1142}
1143
1146{
1148 bool refresh = false;
1149 for (Engine *e : Engine::Iterate()) {
1150 /* Age the vehicle */
1151 if (e->flags.Test(EngineFlag::Available) && e->age != INT32_MAX) {
1152 e->age++;
1153 CalcEngineReliability(e, true);
1154 refresh = true;
1155 }
1156
1157 /* Do not introduce invalid engines */
1158 if (!e->IsEnabled()) continue;
1159
1160 if (!e->flags.Test(EngineFlag::Available) && TimerGameCalendar::date >= (e->intro_date + CalendarTime::DAYS_IN_YEAR)) {
1161 /* Introduce it to all companies */
1163 } else if (!e->flags.Any({EngineFlag::Available, EngineFlag::ExclusivePreview}) && TimerGameCalendar::date >= e->intro_date) {
1164 /* Introduction date has passed...
1165 * Check if it is allowed to build this vehicle type at all
1166 * based on the current game settings. If not, it does not
1167 * make sense to show the preview dialog to any company. */
1168 if (IsVehicleTypeDisabled(e->type, false)) continue;
1169
1170 /* Do not introduce new rail wagons */
1171 if (IsWagon(e->index)) continue;
1172
1173 /* Engine has no preview */
1174 if (e->info.extra_flags.Test(ExtraEngineFlag::NoPreview)) continue;
1175
1176 /* Show preview dialog to one of the companies. */
1177 e->flags.Set(EngineFlag::ExclusivePreview);
1178 e->preview_company = CompanyID::Invalid();
1179 e->preview_asked = CompanyMask{};
1180 }
1181 }
1182
1183 InvalidateWindowClassesData(WC_BUILD_VEHICLE); // rebuild the purchase list (esp. when sorted by reliability)
1184
1185 if (refresh) {
1188 }
1189 }
1190}
1191
1192static const IntervalTimer<TimerGameCalendar> _calendar_engines_monthly({TimerGameCalendar::Trigger::Month, TimerGameCalendar::Priority::Engine}, [](auto)
1193{
1195});
1196
1202static bool IsUniqueEngineName(const std::string &name)
1203{
1204 for (const Engine *e : Engine::Iterate()) {
1205 if (!e->name.empty() && e->name == name) return false;
1206 }
1207
1208 return true;
1209}
1210
1218CommandCost CmdRenameEngine(DoCommandFlags flags, EngineID engine_id, const std::string &text)
1219{
1220 Engine *e = Engine::GetIfValid(engine_id);
1221 if (e == nullptr) return CMD_ERROR;
1222
1223 bool reset = text.empty();
1224
1225 if (!reset) {
1227 if (!IsUniqueEngineName(text)) return CommandCost(STR_ERROR_NAME_MUST_BE_UNIQUE);
1228 }
1229
1230 if (flags.Test(DoCommandFlag::Execute)) {
1231 if (reset) {
1232 e->name.clear();
1233 } else {
1234 e->name = text;
1235 }
1236
1238 }
1239
1240 return CommandCost();
1241}
1242
1243
1252bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company)
1253{
1254 const Engine *e = Engine::GetIfValid(engine);
1255
1256 /* check if it's an engine that is in the engine array */
1257 if (e == nullptr) return false;
1258
1259 /* check if it's an engine of specified type */
1260 if (e->type != type) return false;
1261
1262 /* check if it's available ... */
1263 if (company == OWNER_DEITY) {
1264 /* ... for any company (preview does not count) */
1265 if (!e->flags.Test(EngineFlag::Available) || e->company_avail.None()) return false;
1266 } else {
1267 /* ... for this company */
1268 if (!e->company_avail.Test(company)) return false;
1269 }
1270
1271 if (!e->IsEnabled()) return false;
1272
1273 if (type == VEH_TRAIN && company != OWNER_DEITY) {
1274 /* Check if the rail type is available to this company */
1275 const Company *c = Company::Get(company);
1276 if (!GetAllCompatibleRailTypes(e->VehInfo<RailVehicleInfo>().railtypes).Any(c->avail_railtypes)) return false;
1277 }
1278 if (type == VEH_ROAD && company != OWNER_DEITY) {
1279 /* Check if the road type is available to this company */
1280 const Company *c = Company::Get(company);
1281 if (!GetRoadTypeInfo(e->VehInfo<RoadVehicleInfo>().roadtype)->powered_roadtypes.Any(c->avail_roadtypes)) return false;
1282 }
1283
1284 return true;
1285}
1286
1294{
1295 const Engine *e = Engine::GetIfValid(engine);
1296
1297 /* check if it's an engine that is in the engine array */
1298 if (e == nullptr) return false;
1299
1300 if (!e->CanCarryCargo()) return false;
1301
1302 const EngineInfo *ei = &e->info;
1303 if (ei->refit_mask == 0) return false;
1304
1305 /* Are there suffixes?
1306 * Note: This does not mean the suffixes are actually available for every consist at any time. */
1308
1309 /* Is there any cargo except the default cargo? */
1310 CargoType default_cargo = e->GetDefaultCargoType();
1311 CargoTypes default_cargo_mask = 0;
1312 SetBit(default_cargo_mask, default_cargo);
1313 return IsValidCargoType(default_cargo) && ei->refit_mask != default_cargo_mask;
1314}
1315
1320{
1321 TimerGameCalendar::Date min_date{INT32_MAX};
1322
1323 for (const Engine *e : Engine::Iterate()) {
1324 if (!e->IsEnabled()) continue;
1325
1326 /* Don't consider train wagons, we need a powered engine available. */
1327 if (e->type == VEH_TRAIN && e->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_WAGON) continue;
1328
1329 /* We have an available engine... yay! */
1330 if (e->flags.Test(EngineFlag::Available) && e->company_avail.Any()) return;
1331
1332 /* Okay, try to find the earliest date. */
1333 min_date = std::min(min_date, e->info.base_intro);
1334 }
1335
1336 if (min_date < INT32_MAX) {
1337 ShowErrorMessage(GetEncodedString(STR_ERROR_NO_VEHICLES_AVAILABLE_YET),
1338 GetEncodedString(STR_ERROR_NO_VEHICLES_AVAILABLE_YET_EXPLANATION, min_date), WL_WARNING);
1339 } else {
1340 ShowErrorMessage(GetEncodedString(STR_ERROR_NO_VEHICLES_AVAILABLE_AT_ALL),
1341 GetEncodedString(STR_ERROR_NO_VEHICLES_AVAILABLE_AT_ALL_EXPLANATION), WL_WARNING);
1342 }
1343}
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:108
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:51
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:471
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:491
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.
StrongType::Typedef< int32_t, struct YearTag< struct Calendar >, StrongType::Compare, StrongType::Integer > Year
StrongType::Typedef< int32_t, DateTag< struct Calendar >, StrongType::Compare, StrongType::Integer > Date
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:1010
void SetYearEngineAgingStops()
Compute the value for _year_engine_aging_stops.
Definition engine.cpp:698
static void NewVehicleAvailable(Engine *e)
An engine has become available for general use.
Definition engine.cpp:1085
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:605
static CompanyID GetPreviewCompany(Engine *e)
Get the best company for an engine preview.
Definition engine.cpp:920
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:640
static bool IsVehicleTypeDisabled(VehicleType type, bool ai)
Checks if a vehicle type is disabled for all/ai companies.
Definition engine.cpp:954
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) { InvalidateWindowClassesData(WC_ENGINE_PREVIEW);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 AcceptEnginePreview(EngineID eid, CompanyID company, int recursion_depth=0)
Company company accepts engine eid for preview.
Definition engine.cpp:888
CommandCost CmdRenameEngine(DoCommandFlags flags, EngineID engine_id, const std::string &text)
Rename an engine.
Definition engine.cpp:1218
CommandCost CmdWantEnginePreview(DoCommandFlags flags, EngineID engine_id)
Accept an engine prototype.
Definition engine.cpp:1045
void CalendarEnginesMonthlyLoop()
Monthly update of the availability, reliability, and preview offers of the engines.
Definition engine.cpp:1145
static bool IsUniqueEngineName(const std::string &name)
Is name still free as name for an engine?
Definition engine.cpp:1202
bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company)
Check if an engine is buildable.
Definition engine.cpp:1252
static TimerGameCalendar::Year _year_engine_aging_stops
Year that engine aging stops.
Definition engine.cpp:51
static void DisableEngineForCompany(EngineID eid, CompanyID company)
Forbids engine eid to be used by a company company.
Definition engine.cpp:864
void StartupEngines()
Start/initialise all our engines.
Definition engine.cpp:804
static bool IsWagon(EngineID index)
Determine whether an engine type is a wagon (and not a loco).
Definition engine.cpp:629
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:1063
void StartupOneEngine(Engine *e, const TimerGameCalendar::YearMonthDay &aging_ymd, uint32_t seed)
Start/initialise one engine.
Definition engine.cpp:723
void CheckEngines()
Check for engines that have an appropriate availability.
Definition engine.cpp:1319
bool IsEngineRefittable(EngineID engine)
Check if an engine is refittable.
Definition engine.cpp:1293
CommandCost CmdSetVehicleVisibility(DoCommandFlags flags, EngineID engine_id, bool hide)
Set the visibility of an engine.
Definition engine.cpp:1024
void CalcEngineReliability(Engine *e, bool new_month)
Update Engine::reliability and (if needed) update the engine GUIs.
Definition engine.cpp:652
static void EnableEngineForCompany(EngineID eid, CompanyID company)
Allows engine eid to be used by a company company.
Definition engine.cpp:836
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 StringParameter.
@ 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:914
@ 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:137
RailTypes AddDateIntroducedRailTypes(RailTypes current, TimerGameCalendar::Date date)
Add the rail types that are to be introduced at the given date.
Definition rail.cpp:104
RailTypes GetAllIntroducesRailTypes(RailTypes railtypes)
Returns all introduced railtypes for a set of railtypes.
Definition rail.h:337
RailTypes GetAllCompatibleRailTypes(RailTypes railtypes)
Returns all compatible railtypes for a set of railtypes.
Definition rail.h:313
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:351
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:81
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:139
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:589
void ResetToDefaultMapping()
Initializes the EngineOverrideManager with the default engines.
Definition engine.cpp:513
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:553
EngineID GetID(VehicleType type, uint16_t grf_local_id, uint32_t grfid)
Looks up an EngineID in the EngineOverrideManager.
Definition engine.cpp:534
Dynamic data of a loaded NewGRF.
Definition newgrf.h:118
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 CloseWindowByClass(WindowClass cls, int data)
Close all windows of a given class.
Definition window.cpp:1222
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting).
Definition window.cpp:3230
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:3322
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:3340
Window functions not directly related to making/drawing windows.
@ WC_BUILD_TOOLBAR
Build toolbar; Window numbers:
Definition window_type.h:79
@ 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:64
@ WC_BUILD_VEHICLE
Build vehicle; Window numbers: