OpenTTD Source 20260711-master-g3fb3006dff
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
59{
61 static constexpr VehicleTypeIndexArray<uint8_t> ENGINE_COUNTS = {
62 lengthof(_orig_rail_vehicle_info),
63 lengthof(_orig_road_vehicle_info),
64 lengthof(_orig_ship_vehicle_info),
65 lengthof(_orig_aircraft_vehicle_info),
66 };
67
69 return ENGINE_COUNTS[type];
70}
71
78{
80 static constexpr VehicleTypeIndexArray<uint8_t> ENGINE_OFFSETS = {
81 0,
82 lengthof(_orig_rail_vehicle_info),
83 lengthof(_orig_rail_vehicle_info) + lengthof(_orig_road_vehicle_info),
84 lengthof(_orig_rail_vehicle_info) + lengthof(_orig_road_vehicle_info) + lengthof(_orig_ship_vehicle_info),
85 };
86
88 return ENGINE_OFFSETS[type];
89}
90
91static_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));
92
93Engine::Engine(EngineID index, VehicleType type, uint16_t local_id) : EnginePool::PoolItem<&_engine_pool>(index)
94{
95 this->type = type;
96
97 /* Called in the context of loading a savegame. The rest comes from the loader. */
98 if (type == VehicleType::Invalid) return;
99
100 this->grf_prop.local_id = local_id;
101 this->list_position = local_id;
102 this->preview_company = CompanyID::Invalid();
103 this->display_last_variant = EngineID::Invalid();
104
105 /* Check if this base engine is within the original engine data range */
106 if (local_id >= GetOriginalEngineCount(type)) {
107 /* Initialise default type-specific information. */
108 switch (type) {
109 case VehicleType::Train: this->vehicle_info.emplace<RailVehicleInfo>(); break;
110 case VehicleType::Road: this->vehicle_info.emplace<RoadVehicleInfo>(); break;
111 case VehicleType::Ship: this->vehicle_info.emplace<ShipVehicleInfo>(); break;
112 case VehicleType::Aircraft: this->vehicle_info.emplace<AircraftVehicleInfo>(); break;
113 default: break;
114 }
115 /* Set model life to maximum to make wagons available */
116 this->info.base_life = TimerGameCalendar::Year{0xFF};
117 /* Aircraft must have CT_INVALID as default, as there is no property */
118 this->info.cargo_type = INVALID_CARGO;
119 this->info.cargo_label = (type == VehicleType::Aircraft) ? CT_INVALID : CT_PASSENGERS;
120 /* Set cargo aging period to the default value. */
121 this->info.cargo_age_period = Ticks::CARGO_AGING_TICKS;
122 /* Not a variant */
123 this->info.variant_id = EngineID::Invalid();
124 return;
125 }
126
127 /* Copy the original engine info for this slot */
128 this->info = _orig_engine_info[GetOriginalEngineOffset(type) + local_id];
129
130 /* Copy the original engine data for this slot */
131 switch (type) {
132 default: NOT_REACHED();
133
134 case VehicleType::Train: {
135 RailVehicleInfo &rvi = this->vehicle_info.emplace<RailVehicleInfo>(_orig_rail_vehicle_info[local_id]);
136 this->original_image_index = rvi.image_index;
137 this->info.string_id = STR_VEHICLE_NAME_TRAIN_ENGINE_RAIL_KIRBY_PAUL_TANK_STEAM + local_id;
138
139 /* Set the default model life of original wagons to "infinite" */
140 if (rvi.railveh_type == RailVehicleType::Wagon) this->info.base_life = TimerGameCalendar::Year{0xFF};
141
142 break;
143 }
144
145 case VehicleType::Road: {
146 RoadVehicleInfo &rvi = this->vehicle_info.emplace<RoadVehicleInfo>(_orig_road_vehicle_info[local_id]);
147 this->original_image_index = rvi.image_index;
148 this->info.string_id = STR_VEHICLE_NAME_ROAD_VEHICLE_MPS_REGAL_BUS + local_id;
149 break;
150 }
151
152 case VehicleType::Ship: {
153 ShipVehicleInfo &svi = this->vehicle_info.emplace<ShipVehicleInfo>(_orig_ship_vehicle_info[local_id]);
154 this->original_image_index = svi.image_index;
155 this->info.string_id = STR_VEHICLE_NAME_SHIP_MPS_OIL_TANKER + local_id;
156 break;
157 }
158
160 AircraftVehicleInfo &avi = this->vehicle_info.emplace<AircraftVehicleInfo>(_orig_aircraft_vehicle_info[local_id]);
161 this->original_image_index = avi.image_index;
162 this->info.string_id = STR_VEHICLE_NAME_AIRCRAFT_SAMPSON_U52 + local_id;
163 break;
164 }
165 }
166}
167
173{
174 return this->info.string_id != STR_NEWGRF_INVALID_ENGINE && this->info.climates.Test(_settings_game.game_creation.landscape);
175}
176
183{
184 const GRFFile *file = this->GetGRF();
185 return file == nullptr ? GrfID{} : file->grfid;
186}
187
194{
195 /* For engines that can appear in a consist (i.e. rail vehicles and (articulated) road vehicles), a capacity
196 * of zero is a special case, to define the vehicle to not carry anything. The default cargotype is still used
197 * for livery selection etc.
198 * Note: Only the property is tested. A capacity callback returning 0 does not have the same effect.
199 */
200 switch (this->type) {
202 if (this->VehInfo<RailVehicleInfo>().capacity == 0) return false;
203 break;
204
206 if (this->VehInfo<RoadVehicleInfo>().capacity == 0) return false;
207 break;
208
211 break;
212
213 default: NOT_REACHED();
214 }
216}
217
218
226uint Engine::DetermineCapacity(const Vehicle *v, uint16_t *mail_capacity) const
227{
228 assert(v == nullptr || this->index == v->engine_type);
229 if (mail_capacity != nullptr) *mail_capacity = 0;
230
231 if (!this->CanCarryCargo()) return 0;
232
233 bool new_multipliers = this->info.misc_flags.Test(EngineMiscFlag::NoDefaultCargoMultiplier);
234 CargoType default_cargo = this->GetDefaultCargoType();
235 CargoType cargo_type = (v != nullptr) ? v->cargo_type : default_cargo;
236
237 if (mail_capacity != nullptr && this->type == VehicleType::Aircraft && IsCargoInClass(cargo_type, CargoClass::Passengers)) {
238 *mail_capacity = GetEngineProperty(this->index, PROP_AIRCRAFT_MAIL_CAPACITY, this->VehInfo<AircraftVehicleInfo>().mail_capacity, v);
239 }
240
241 /* Check the refit capacity callback if we are not in the default configuration, or if we are using the new multiplier algorithm. */
242 if (this->info.callback_mask.Test(VehicleCallbackMask::RefitCapacity) &&
243 (new_multipliers || default_cargo != cargo_type || (v != nullptr && v->cargo_subtype != 0))) {
244 uint16_t callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, this->index, v);
245 if (callback != CALLBACK_FAILED) return callback;
246 }
247
248 /* Get capacity according to property resp. CB */
249 uint capacity;
250 uint extra_mail_cap = 0;
251 switch (this->type) {
253 capacity = GetEngineProperty(this->index, PROP_TRAIN_CARGO_CAPACITY, this->VehInfo<RailVehicleInfo>().capacity, v);
254
255 /* In purchase list add the capacity of the second head. Always use the plain property for this. */
256 if (v == nullptr && this->VehInfo<RailVehicleInfo>().railveh_type == RailVehicleType::Multihead) capacity += this->VehInfo<RailVehicleInfo>().capacity;
257 break;
258
260 capacity = GetEngineProperty(this->index, PROP_ROADVEH_CARGO_CAPACITY, this->VehInfo<RoadVehicleInfo>().capacity, v);
261 break;
262
264 capacity = GetEngineProperty(this->index, PROP_SHIP_CARGO_CAPACITY, this->VehInfo<ShipVehicleInfo>().capacity, v);
265 break;
266
268 capacity = GetEngineProperty(this->index, PROP_AIRCRAFT_PASSENGER_CAPACITY, this->VehInfo<AircraftVehicleInfo>().passenger_capacity, v);
269 if (!IsCargoInClass(cargo_type, CargoClass::Passengers)) {
270 extra_mail_cap = GetEngineProperty(this->index, PROP_AIRCRAFT_MAIL_CAPACITY, this->VehInfo<AircraftVehicleInfo>().mail_capacity, v);
271 }
272 if (IsValidCargoType(GetCargoTypeByLabel(CT_MAIL))) {
273 if (!new_multipliers && cargo_type == GetCargoTypeByLabel(CT_MAIL)) return capacity + extra_mail_cap;
274 }
275 default_cargo = GetCargoTypeByLabel(CT_PASSENGERS); // Always use 'passengers' wrt. cargo multipliers
276 break;
277
278 default: NOT_REACHED();
279 }
280
281 if (!new_multipliers) {
282 /* Use the passenger multiplier for mail as well */
283 capacity += extra_mail_cap;
284 extra_mail_cap = 0;
285 }
286
287 /* Apply multipliers depending on cargo- and vehicletype. */
288 if (new_multipliers || (this->type != VehicleType::Ship && default_cargo != cargo_type)) {
289 uint16_t default_multiplier = new_multipliers ? 0x100 : CargoSpec::Get(default_cargo)->multiplier;
290 uint16_t cargo_multiplier = CargoSpec::Get(cargo_type)->multiplier;
291 capacity *= cargo_multiplier;
292 if (extra_mail_cap > 0 && IsValidCargoType(GetCargoTypeByLabel(CT_MAIL))) {
293 uint mail_multiplier = CargoSpec::Get(GetCargoTypeByLabel(CT_MAIL))->multiplier;
294 capacity += (default_multiplier * extra_mail_cap * cargo_multiplier + mail_multiplier / 2) / mail_multiplier;
295 }
296 capacity = (capacity + default_multiplier / 2) / default_multiplier;
297 }
298
299 return capacity;
300}
301
307{
308 Price base_price;
309 uint cost_factor;
310 switch (this->type) {
312 base_price = this->VehInfo<RoadVehicleInfo>().running_cost_class;
313 if (base_price == Price::Invalid) return 0;
314 cost_factor = GetEngineProperty(this->index, PROP_ROADVEH_RUNNING_COST_FACTOR, this->VehInfo<RoadVehicleInfo>().running_cost);
315 break;
316
318 base_price = this->VehInfo<RailVehicleInfo>().running_cost_class;
319 if (base_price == Price::Invalid) return 0;
320 cost_factor = GetEngineProperty(this->index, PROP_TRAIN_RUNNING_COST_FACTOR, this->VehInfo<RailVehicleInfo>().running_cost);
321 break;
322
324 base_price = Price::RunningShip;
325 cost_factor = GetEngineProperty(this->index, PROP_SHIP_RUNNING_COST_FACTOR, this->VehInfo<ShipVehicleInfo>().running_cost);
326 break;
327
329 base_price = Price::RunningAircraft;
330 cost_factor = GetEngineProperty(this->index, PROP_AIRCRAFT_RUNNING_COST_FACTOR, this->VehInfo<AircraftVehicleInfo>().running_cost);
331 break;
332
333 default: NOT_REACHED();
334 }
335
336 return GetPrice(base_price, cost_factor, this->GetGRF(), -8);
337}
338
343Money Engine::GetCost() const
344{
345 Price base_price;
346 uint cost_factor;
347 switch (this->type) {
349 base_price = Price::BuildVehicleRoad;
350 cost_factor = GetEngineProperty(this->index, PROP_ROADVEH_COST_FACTOR, this->VehInfo<RoadVehicleInfo>().cost_factor);
351 break;
352
354 if (this->VehInfo<RailVehicleInfo>().railveh_type == RailVehicleType::Wagon) {
355 base_price = Price::BuildVehicleWagon;
356 cost_factor = GetEngineProperty(this->index, PROP_TRAIN_COST_FACTOR, this->VehInfo<RailVehicleInfo>().cost_factor);
357 } else {
358 base_price = Price::BuildVehicleTrain;
359 cost_factor = GetEngineProperty(this->index, PROP_TRAIN_COST_FACTOR, this->VehInfo<RailVehicleInfo>().cost_factor);
360 }
361 break;
362
364 base_price = Price::BuildVehicleShip;
365 cost_factor = GetEngineProperty(this->index, PROP_SHIP_COST_FACTOR, this->VehInfo<ShipVehicleInfo>().cost_factor);
366 break;
367
369 base_price = Price::BuildVehicleAircraft;
370 cost_factor = GetEngineProperty(this->index, PROP_AIRCRAFT_COST_FACTOR, this->VehInfo<AircraftVehicleInfo>().cost_factor);
371 break;
372
373 default: NOT_REACHED();
374 }
375
376 return GetPrice(base_price, cost_factor, this->GetGRF(), -8);
377}
378
384{
385 switch (this->type) {
387 return GetEngineProperty(this->index, PROP_TRAIN_SPEED, this->VehInfo<RailVehicleInfo>().max_speed);
388
389 case VehicleType::Road: {
390 uint max_speed = GetEngineProperty(this->index, PROP_ROADVEH_SPEED, 0);
391 return (max_speed != 0) ? max_speed * 2 : this->VehInfo<RoadVehicleInfo>().max_speed / 2;
392 }
393
395 return GetEngineProperty(this->index, PROP_SHIP_SPEED, this->VehInfo<ShipVehicleInfo>().max_speed) / 2;
396
398 uint max_speed = GetEngineProperty(this->index, PROP_AIRCRAFT_SPEED, 0);
399 if (max_speed != 0) {
400 return (max_speed * 128) / 10;
401 }
402 return this->VehInfo<AircraftVehicleInfo>().max_speed;
403 }
404
405 default: NOT_REACHED();
406 }
407}
408
416{
417 /* Only trains and road vehicles have 'power'. */
418 switch (this->type) {
420 return GetEngineProperty(this->index, PROP_TRAIN_POWER, this->VehInfo<RailVehicleInfo>().power);
422 return GetEngineProperty(this->index, PROP_ROADVEH_POWER, this->VehInfo<RoadVehicleInfo>().power) * 10;
423
424 default: NOT_REACHED();
425 }
426}
427
434{
435 /* Only trains and road vehicles have 'weight'. */
436 switch (this->type) {
438 return GetEngineProperty(this->index, PROP_TRAIN_WEIGHT, this->VehInfo<RailVehicleInfo>().weight) << (this->VehInfo<RailVehicleInfo>().railveh_type == RailVehicleType::Multihead ? 1 : 0);
440 return GetEngineProperty(this->index, PROP_ROADVEH_WEIGHT, this->VehInfo<RoadVehicleInfo>().weight) / 4;
441
442 default: NOT_REACHED();
443 }
444}
445
452{
453 /* Only trains and road vehicles have 'tractive effort'. */
454 switch (this->type) {
456 return (GROUND_ACCELERATION * this->GetDisplayWeight() * GetEngineProperty(this->index, PROP_TRAIN_TRACTIVE_EFFORT, this->VehInfo<RailVehicleInfo>().tractive_effort)) / 256;
458 return (GROUND_ACCELERATION * this->GetDisplayWeight() * GetEngineProperty(this->index, PROP_ROADVEH_TRACTIVE_EFFORT, this->VehInfo<RoadVehicleInfo>().tractive_effort)) / 256;
459
460 default: NOT_REACHED();
461 }
462}
463
469{
470 /* Assume leap years; this gives the player a bit more than the given amount of years, but never less. */
471 return TimerGameCalendar::Date{(this->info.lifelength + _settings_game.vehicle.extend_vehicle_life).base() * CalendarTime::DAYS_IN_LEAP_YEAR};
472}
473
478uint16_t Engine::GetRange() const
479{
480 if (!_settings_game.vehicle.aircraft_range) return 0;
481 switch (this->type) {
483 return GetEngineProperty(this->index, PROP_AIRCRAFT_RANGE, this->VehInfo<AircraftVehicleInfo>().max_range);
484
485 default: NOT_REACHED();
486 }
487}
488
494{
495 switch (this->type) {
497 switch (this->VehInfo<AircraftVehicleInfo>().subtype) {
498 case AIR_HELI: return STR_LIVERY_HELICOPTER;
499 case AIR_CTOL: return STR_LIVERY_SMALL_PLANE;
500 case AIR_CTOL | AIR_FAST: return STR_LIVERY_LARGE_PLANE;
501 default: NOT_REACHED();
502 }
503
504 default: NOT_REACHED();
505 }
506}
507
513bool Engine::IsVariantHidden(CompanyID c) const
514{
515 /* In case company is spectator. */
516 if (c >= MAX_COMPANIES) return false;
517
518 /* Shortcut if this engine is explicitly hidden. */
519 if (this->IsHidden(c)) return true;
520
521 /* Check for hidden parent variants. This is a bit convoluted as we must check hidden status of
522 * the last display variant rather than the actual parent variant. */
523 const Engine *re = this;
524 const Engine *ve = re->GetDisplayVariant();
525 while (!(ve->IsHidden(c)) && re->info.variant_id != EngineID::Invalid()) {
526 re = Engine::Get(re->info.variant_id);
527 ve = re->GetDisplayVariant();
528 }
529 return ve->IsHidden(c);
530}
531
536{
537 EngineID id = EngineID::Begin();
539 auto &map = this->mappings[type];
540 map.clear();
541 for (uint internal_id = 0; internal_id < GetOriginalEngineCount(type); internal_id++, ++id) {
542 map.emplace_back(INVALID_GRFID, internal_id, type, internal_id, id);
543 }
544 }
545}
546
556EngineID EngineOverrideManager::GetID(VehicleType type, uint16_t grf_local_id, GrfID grfid)
557{
558 const auto &map = this->mappings[type];
559 const auto key = EngineIDMapping::Key(grfid, grf_local_id);
560 auto it = std::ranges::lower_bound(map, key, std::less{}, EngineIDMappingKeyProjection{});
561 if (it == std::end(map) || it->Key() != key) return EngineID::Invalid();
562 return it->engine;
563}
564
575EngineID EngineOverrideManager::UseUnreservedID(VehicleType type, uint16_t grf_local_id, GrfID grfid, bool static_access)
576{
577 auto &map = _engine_mngr.mappings[type];
578 const auto key = EngineIDMapping::Key(INVALID_GRFID, grf_local_id);
579 auto it = std::ranges::lower_bound(map, key, std::less{}, EngineIDMappingKeyProjection{});
580 if (it == std::end(map) || it->Key() != key) return EngineID::Invalid();
581
582 if (!static_access && grfid != INVALID_GRFID) {
583 /* Reserve the engine slot for the new grfid. */
584 it->grfid = grfid;
585
586 /* Relocate entry to its new position in the mapping list to keep it sorted. */
587 auto p = std::ranges::lower_bound(map, EngineIDMapping::Key(grfid, grf_local_id), std::less{}, EngineIDMappingKeyProjection{});
588 it = Slide(it, std::next(it), p).first;
589 }
590
591 return it->engine;
592}
593
602void EngineOverrideManager::SetID(VehicleType type, uint16_t grf_local_id, GrfID grfid, uint8_t substitute_id, EngineID engine)
603{
604 auto &map = this->mappings[type];
605 const auto key = EngineIDMapping::Key(grfid, grf_local_id);
606 auto it = std::ranges::lower_bound(map, key, std::less{}, EngineIDMappingKeyProjection{});
607 if (it == std::end(map) || it->Key() != key) {
608 map.emplace(it, grfid, grf_local_id, type, substitute_id, engine);
609 } else {
610 it->engine = engine;
611 }
612}
613
620{
621 for (const Vehicle *v : Vehicle::Iterate()) {
622 if (IsCompanyBuildableVehicleType(v)) return false;
623 }
624
625 /* Reset the engines, they will get new EngineIDs */
626 _engine_mngr.ResetToDefaultMapping();
628
629 return true;
630}
631
636{
637 CloseWindowByClass(WindowClass::EnginePreview);
638 _engine_pool.CleanPool();
639
641 const auto &mapping = _engine_mngr.mappings[type];
642
643 /* Verify that the engine override manager has at least been set up with the default engines. */
644 assert(std::size(mapping) >= GetOriginalEngineCount(type));
645
646 for (const EngineIDMapping &eid : mapping) {
647 Engine::CreateAtIndex(eid.engine, type, eid.internal_id);
648 }
649 }
650}
651
652void ShowEnginePreviewWindow(EngineID engine);
653
659static bool IsWagon(EngineID index)
660{
661 const Engine *e = Engine::Get(index);
662 return e->type == VehicleType::Train && e->VehInfo<RailVehicleInfo>().railveh_type == RailVehicleType::Wagon;
663}
664
670static void ClearLastVariant(EngineID engine_id, VehicleType type)
671{
672 for (Engine *e : Engine::IterateType(type)) {
673 if (e->display_last_variant == engine_id) e->display_last_variant = EngineID::Invalid();
674 }
675}
676
682void CalcEngineReliability(Engine *e, bool new_month)
683{
684 /* Get source engine for reliability age. This is normally our engine unless variant reliability syncing is requested. */
685 Engine *re = e;
686 while (re->info.variant_id != EngineID::Invalid() && re->info.extra_flags.Test(ExtraEngineFlag::SyncReliability)) {
687 re = Engine::Get(re->info.variant_id);
688 }
689
690 uint32_t age = re->age;
691 if (new_month && re->index > e->index && age != INT32_MAX) age++; /* parent variant's age has not yet updated. */
692
693 /* Check for early retirement */
694 if (e->company_avail.Any() && !_settings_game.vehicle.never_expire_vehicles && e->info.base_life != 0xFF) {
695 int retire_early = e->info.retire_early;
696 uint retire_early_max_age = std::max(0, e->duration_phase_1 + e->duration_phase_2 - retire_early * 12);
697 if (retire_early != 0 && age >= retire_early_max_age) {
698 /* Early retirement is enabled and we're past the date... */
700 ClearLastVariant(e->index, e->type);
702 }
703 }
704
705 if (age < e->duration_phase_1) {
706 uint start = e->reliability_start;
707 e->reliability = age * (e->reliability_max - start) / e->duration_phase_1 + start;
708 } else if ((age -= e->duration_phase_1) < e->duration_phase_2 || _settings_game.vehicle.never_expire_vehicles || e->info.base_life == 0xFF) {
709 /* We are at the peak of this engines life. It will have max reliability.
710 * This is also true if the engines never expire. They will not go bad over time */
712 } else if ((age -= e->duration_phase_2) < e->duration_phase_3) {
713 uint max = e->reliability_max;
714 e->reliability = (int)age * (int)(e->reliability_final - max) / e->duration_phase_3 + max;
715 } else {
716 /* time's up for this engine.
717 * We will now completely retire this design */
720 /* Kick this engine out of the lists */
721 ClearLastVariant(e->index, e->type);
723 }
724
725}
726
729{
730 /* Determine last engine aging year, default to 2050 as previously. */
732
733 for (const Engine *e : Engine::Iterate()) {
734 const EngineInfo *ei = &e->info;
735
736 /* Exclude certain engines */
737 if (!ei->climates.Test(_settings_game.game_creation.landscape)) continue;
738 if (e->type == VehicleType::Train && e->VehInfo<RailVehicleInfo>().railveh_type == RailVehicleType::Wagon) continue;
739
740 /* Base year ending date on half the model life */
741 TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(ei->base_intro + (ei->lifelength.base() * CalendarTime::DAYS_IN_LEAP_YEAR) / 2);
742
744 }
745}
746
753void StartupOneEngine(Engine *e, const TimerGameCalendar::YearMonthDay &aging_ymd, uint32_t seed)
754{
755 const EngineInfo *ei = &e->info;
756
757 e->age = 0;
758 e->flags = {};
761
762 /* Vehicles with the same base_intro date shall be introduced at the same time.
763 * Make sure they use the same randomisation of the date. */
764 SavedRandomSeeds saved_seeds;
765 SaveRandomSeeds(&saved_seeds);
766 SetRandomSeed(_settings_game.game_creation.generation_seed ^ seed ^
767 ei->base_intro.base() ^
768 to_underlying(e->type) ^
769 e->GetGRFID());
770 uint32_t r = Random();
771
772 /* Don't randomise the start-date in the first two years after gamestart to ensure availability
773 * of engines in early starting games.
774 * Note: TTDP uses fixed 1922 */
775 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;
777 TimerGameCalendar::YearMonthDay intro_ymd = TimerGameCalendar::ConvertDateToYMD(e->intro_date);
778 int aging_months = aging_ymd.year.base() * 12 + aging_ymd.month;
779 int intro_months = intro_ymd.year.base() * 12 + intro_ymd.month;
780 if (intro_ymd.day > 1) intro_months++; // Engines are introduced at the first month start at/after intro date.
781 e->age = aging_months - intro_months;
782 e->company_avail.Set();
784 }
785
786 /* Get parent variant index for syncing reliability via random seed. */
787 const Engine *re = e;
788 while (re->info.variant_id != EngineID::Invalid() && re->info.extra_flags.Test(ExtraEngineFlag::SyncReliability)) {
789 re = Engine::Get(re->info.variant_id);
790 }
791
792 SetRandomSeed(_settings_game.game_creation.generation_seed ^ seed ^
793 (re->index.base() << 16) ^ (re->info.base_intro.base() << 12) ^ (re->info.decay_speed << 8) ^
794 (re->info.lifelength.base() << 4) ^ re->info.retire_early ^
795 to_underlying(e->type) ^
796 e->GetGRFID());
797
798 /* Base reliability defined as a percentage of UINT16_MAX. */
799 const uint16_t RELIABILITY_START = UINT16_MAX * 48 / 100;
800 const uint16_t RELIABILITY_MAX = UINT16_MAX * 75 / 100;
801 const uint16_t RELIABILITY_FINAL = UINT16_MAX * 25 / 100;
802
803 static_assert(RELIABILITY_START == 0x7AE0);
804 static_assert(RELIABILITY_MAX == 0xBFFF);
805 static_assert(RELIABILITY_FINAL == 0x3FFF);
806
807 r = Random();
808 /* 14 bits gives a value between 0 and 16383, which is up to an additional 25%p reliability on top of the base reliability. */
809 e->reliability_start = GB(r, 16, 14) + RELIABILITY_START;
810 e->reliability_max = GB(r, 0, 14) + RELIABILITY_MAX;
811
812 r = Random();
813 e->reliability_final = GB(r, 16, 14) + RELIABILITY_FINAL;
814
815 e->duration_phase_1 = GB(r, 0, 5) + 7;
816 e->duration_phase_2 = std::max(0, int(GB(r, 5, 4)) + ei->base_life.base() * 12 - 96);
817 e->duration_phase_3 = GB(r, 9, 7) + 120;
818
819 RestoreRandomSeeds(saved_seeds);
820
821 e->reliability_spd_dec = ei->decay_speed << 2;
822
823 /* prevent certain engines from ever appearing. */
824 if (!ei->climates.Test(_settings_game.game_creation.landscape)) {
827 }
828}
829
835{
836 /* Aging of vehicles stops, so account for that when starting late */
838 TimerGameCalendar::YearMonthDay aging_ymd = TimerGameCalendar::ConvertDateToYMD(aging_date);
839 uint32_t seed = Random();
840
841 for (Engine *e : Engine::Iterate()) {
842 StartupOneEngine(e, aging_ymd, seed);
843 }
844 for (Engine *e : Engine::Iterate()) {
845 CalcEngineReliability(e, false);
846 }
847
848 /* Update the bitmasks for the vehicle lists */
849 for (Company *c : Company::Iterate()) {
850 c->avail_railtypes = GetCompanyRailTypes(c->index);
851 c->avail_roadtypes = GetCompanyRoadTypes(c->index);
852 }
853
854 /* Invalidate any open purchase lists */
855 InvalidateWindowClassesData(WindowClass::BuildVehicle);
856
857 SetWindowClassesDirty(WindowClass::BuildVehicle);
858 SetWindowClassesDirty(WindowClass::ReplaceVehicle);
859}
860
866static void EnableEngineForCompany(EngineID eid, CompanyID company)
867{
868 Engine *e = Engine::Get(eid);
869 Company *c = Company::Get(company);
870
871 e->company_avail.Set(company);
872 if (e->type == VehicleType::Train) {
874 } else if (e->type == VehicleType::Road) {
876 }
877
878 if (company == _local_company) {
880
881 /* Update the toolbar. */
882 InvalidateWindowData(WindowClass::MainToolbar, 0);
883 if (e->type == VehicleType::Road) InvalidateWindowData(WindowClass::BuildToolbar, TransportType::Road);
884 if (e->type == VehicleType::Ship) InvalidateWindowData(WindowClass::BuildToolbar, TransportType::Water);
885 if (e->type == VehicleType::Aircraft) InvalidateWindowData(WindowClass::BuildToolbar, TransportType::Air);
886 }
887}
888
894static void DisableEngineForCompany(EngineID eid, CompanyID company)
895{
896 Engine *e = Engine::Get(eid);
897 Company *c = Company::Get(company);
898
899 e->company_avail.Reset(company);
900 if (e->type == VehicleType::Train) {
902 } else if (e->type == VehicleType::Road) {
904 }
905
906 if (company == _local_company) {
907 ClearLastVariant(e->index, e->type);
909 }
910}
911
918static void AcceptEnginePreview(EngineID eid, CompanyID company, int recursion_depth = 0)
919{
920 Engine *e = Engine::Get(eid);
921
922 e->preview_company = CompanyID::Invalid();
923 e->preview_asked.Set();
924
925 EnableEngineForCompany(eid, company);
926
927 /* Notify preview window to remove this engine.
928 * Note: We cannot directly close the window.
929 * In singleplayer this function is called from the preview window, so
930 * we have to use the GUI-scope scheduling of InvalidateWindowData.
931 */
932 InvalidateWindowClassesData(WindowClass::EnginePreview);
933
934 /* Don't search for variants to include if we are 10 levels deep already. */
935 if (recursion_depth >= 10) return;
936
937 /* Find variants to be included in preview. */
938 for (Engine *ve : Engine::IterateType(e->type)) {
939 if (ve->index != eid && ve->info.variant_id == eid && ve->info.extra_flags.Test(ExtraEngineFlag::JoinPreview)) {
940 AcceptEnginePreview(ve->index, company, recursion_depth + 1);
941 }
942 }
943}
944
950static CompanyID GetPreviewCompany(Engine *e)
951{
952 CompanyID best_company = CompanyID::Invalid();
953
954 /* For trains the cargomask has no useful meaning, since you can attach other wagons */
955 CargoTypes cargomask = e->type != VehicleType::Train ? GetUnionOfArticulatedRefitMasks(e->index, true) : ALL_CARGOTYPES;
956
957 int32_t best_hist = -1;
958 for (const Company *c : Company::Iterate()) {
959 if (c->block_preview == 0 && !e->preview_asked.Test(c->index) &&
960 c->old_economy[0].performance_history > best_hist) {
961
962 /* Check whether the company uses similar vehicles */
963 for (const Vehicle *v : Vehicle::Iterate()) {
964 if (v->owner != c->index || v->type != e->type) continue;
965 if (!v->GetEngine()->CanCarryCargo() || !cargomask.Test(v->cargo_type)) continue;
966
967 best_hist = c->old_economy[0].performance_history;
968 best_company = c->index;
969 break;
970 }
971 }
972 }
973
974 return best_company;
975}
976
984static bool IsVehicleTypeDisabled(VehicleType type, bool ai)
985{
986 switch (type) {
987 case VehicleType::Train: return _settings_game.vehicle.max_trains == 0 || (ai && _settings_game.ai.ai_disable_veh_train);
988 case VehicleType::Road: return _settings_game.vehicle.max_roadveh == 0 || (ai && _settings_game.ai.ai_disable_veh_roadveh);
989 case VehicleType::Ship: return _settings_game.vehicle.max_ships == 0 || (ai && _settings_game.ai.ai_disable_veh_ship);
990 case VehicleType::Aircraft: return _settings_game.vehicle.max_aircraft == 0 || (ai && _settings_game.ai.ai_disable_veh_aircraft);
991
992 default: NOT_REACHED();
993 }
994}
995
997static const IntervalTimer<TimerGameCalendar> _calendar_engines_daily({TimerGameCalendar::Trigger::Day, TimerGameCalendar::Priority::Engine}, [](auto)
998{
999 for (Company *c : Company::Iterate()) {
1000 c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes, TimerGameCalendar::date);
1001 c->avail_roadtypes = AddDateIntroducedRoadTypes(c->avail_roadtypes, TimerGameCalendar::date);
1002 }
1003
1005
1006 for (Engine *e : Engine::Iterate()) {
1007 EngineID i = e->index;
1008 if (e->flags.Test(EngineFlag::ExclusivePreview)) {
1009 if (e->preview_company != CompanyID::Invalid()) {
1010 if (!--e->preview_wait) {
1011 InvalidateWindowClassesData(WindowClass::EnginePreview);
1012 e->preview_company = CompanyID::Invalid();
1013 }
1014 } else if (e->preview_asked.Count() < MAX_COMPANIES) {
1015 e->preview_company = GetPreviewCompany(e);
1016
1017 if (e->preview_company == CompanyID::Invalid()) {
1018 e->preview_asked.Set();
1019 continue;
1020 }
1021
1022 e->preview_asked.Set(e->preview_company);
1023 e->preview_wait = 20;
1024 /* AIs are intentionally not skipped for preview even if they cannot build a certain
1025 * vehicle type. This is done to not give poor performing human companies an "unfair"
1026 * boost that they wouldn't have gotten against other human companies. The check on
1027 * the line below is just to make AIs not notice that they have a preview if they
1028 * cannot build the vehicle. */
1029 if (!IsVehicleTypeDisabled(e->type, true)) AI::NewEvent(e->preview_company, new ScriptEventEnginePreview(i));
1030 if (IsInteractiveCompany(e->preview_company)) ShowEnginePreviewWindow(i);
1031 }
1032 }
1033 }
1034});
1035
1041{
1042 for (Engine *e : Engine::Iterate()) {
1043 e->company_hidden.Reset(cid);
1044 }
1045}
1046
1055{
1056 Engine *e = Engine::GetIfValid(engine_id);
1057 if (e == nullptr || _current_company >= MAX_COMPANIES) return CMD_ERROR;
1058 if (!IsEngineBuildable(e->index, e->type, _current_company)) return CMD_ERROR;
1059
1060 if (flags.Test(DoCommandFlag::Execute)) {
1063 }
1064
1065 return CommandCost();
1066}
1067
1076{
1077 Engine *e = Engine::GetIfValid(engine_id);
1078 if (e == nullptr || !e->flags.Test(EngineFlag::ExclusivePreview) || e->preview_company != _current_company) return CMD_ERROR;
1079
1081
1082 return CommandCost();
1083}
1084
1093CommandCost CmdEngineCtrl(DoCommandFlags flags, EngineID engine_id, CompanyID company_id, bool allow)
1094{
1095 if (_current_company != OWNER_DEITY) return CMD_ERROR;
1096
1097 if (!Engine::IsValidID(engine_id) || !Company::IsValidID(company_id)) return CMD_ERROR;
1098
1099 if (flags.Test(DoCommandFlag::Execute)) {
1100 if (allow) {
1101 EnableEngineForCompany(engine_id, company_id);
1102 } else {
1103 DisableEngineForCompany(engine_id, company_id);
1104 }
1105 }
1106
1107 return CommandCost();
1108}
1109
1116{
1117 EngineID index = e->index;
1118
1119 /* In case the company didn't build the vehicle during the intro period,
1120 * prevent that company from getting future intro periods for a while. */
1122 for (Company *c : Company::Iterate()) {
1123 if (!e->company_avail.Test(c->index)) continue;
1124
1125 /* Check the company's 'ALL_GROUP' group statistics. This only includes countable vehicles, which is fine
1126 * as those are the only engines that can be given exclusive previews. */
1127 if (GetGroupNumEngines(c->index, ALL_GROUP, e->index) == 0) {
1128 /* The company did not build this engine during preview. */
1129 c->block_preview = 20;
1130 }
1131 }
1132 }
1133
1136
1137 /* Now available for all companies */
1138 e->company_avail.Set();
1139
1140 /* Do not introduce new rail wagons */
1141 if (IsWagon(index)) return;
1142
1143 if (e->type == VehicleType::Train) {
1144 /* maybe make another rail type available */
1145 assert(e->VehInfo<RailVehicleInfo>().railtypes != RailTypes{});
1147 for (Company *c : Company::Iterate()) c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes | introduced, TimerGameCalendar::date);
1148 } else if (e->type == VehicleType::Road) {
1149 /* maybe make another road type available */
1150 assert(e->VehInfo<RoadVehicleInfo>().roadtype < ROADTYPE_END);
1151 for (Company *c : Company::Iterate()) c->avail_roadtypes = AddDateIntroducedRoadTypes(c->avail_roadtypes | GetRoadTypeInfo(e->VehInfo<RoadVehicleInfo>().roadtype)->introduces_roadtypes, TimerGameCalendar::date);
1152 }
1153
1154 /* Only broadcast event if AIs are able to build this vehicle type. */
1155 if (!IsVehicleTypeDisabled(e->type, true)) AI::BroadcastNewEvent(new ScriptEventEngineAvailable(index));
1156
1157 /* Only provide the "New Vehicle available" news paper entry, if engine can be built. */
1158 if (!IsVehicleTypeDisabled(e->type, false) && !e->info.extra_flags.Test(ExtraEngineFlag::NoNews)) {
1159 AddNewsItem(GetEncodedString(STR_NEWS_NEW_VEHICLE_NOW_AVAILABLE_WITH_TYPE,
1160 GetEngineCategoryName(index),
1163 }
1164
1165 /* Update the toolbar. */
1166 if (e->type == VehicleType::Road) InvalidateWindowData(WindowClass::BuildToolbar, TransportType::Road);
1167 if (e->type == VehicleType::Ship) InvalidateWindowData(WindowClass::BuildToolbar, TransportType::Water);
1168 if (e->type == VehicleType::Aircraft) InvalidateWindowData(WindowClass::BuildToolbar, TransportType::Air);
1169
1170 /* Remove from preview windows */
1171 InvalidateWindowClassesData(WindowClass::EnginePreview);
1172}
1173
1176{
1178 bool refresh = false;
1179 for (Engine *e : Engine::Iterate()) {
1180 /* Age the vehicle */
1181 if (e->flags.Test(EngineFlag::Available) && e->age != INT32_MAX) {
1182 e->age++;
1183 CalcEngineReliability(e, true);
1184 refresh = true;
1185 }
1186
1187 /* Do not introduce invalid engines */
1188 if (!e->IsEnabled()) continue;
1189
1190 if (!e->flags.Test(EngineFlag::Available) && TimerGameCalendar::date >= (e->intro_date + CalendarTime::DAYS_IN_YEAR)) {
1191 /* Introduce it to all companies */
1193 } else if (!e->flags.Any({EngineFlag::Available, EngineFlag::ExclusivePreview}) && TimerGameCalendar::date >= e->intro_date) {
1194 /* Introduction date has passed...
1195 * Check if it is allowed to build this vehicle type at all
1196 * based on the current game settings. If not, it does not
1197 * make sense to show the preview dialog to any company. */
1198 if (IsVehicleTypeDisabled(e->type, false)) continue;
1199
1200 /* Do not introduce new rail wagons */
1201 if (IsWagon(e->index)) continue;
1202
1203 /* Engine has no preview */
1204 if (e->info.extra_flags.Test(ExtraEngineFlag::NoPreview)) continue;
1205
1206 /* Show preview dialog to one of the companies. */
1207 e->flags.Set(EngineFlag::ExclusivePreview);
1208 e->preview_company = CompanyID::Invalid();
1209 e->preview_asked = CompanyMask{};
1210 }
1211 }
1212
1213 InvalidateWindowClassesData(WindowClass::BuildVehicle); // rebuild the purchase list (esp. when sorted by reliability)
1214
1215 if (refresh) {
1216 SetWindowClassesDirty(WindowClass::BuildVehicle);
1217 SetWindowClassesDirty(WindowClass::ReplaceVehicle);
1218 }
1219 }
1220}
1221
1222static const IntervalTimer<TimerGameCalendar> _calendar_engines_monthly({TimerGameCalendar::Trigger::Month, TimerGameCalendar::Priority::Engine}, [](auto)
1223{
1225});
1226
1232static bool IsUniqueEngineName(const std::string &name)
1233{
1234 for (const Engine *e : Engine::Iterate()) {
1235 if (!e->name.empty() && e->name == name) return false;
1236 }
1237
1238 return true;
1239}
1240
1248CommandCost CmdRenameEngine(DoCommandFlags flags, EngineID engine_id, const std::string &text)
1249{
1250 Engine *e = Engine::GetIfValid(engine_id);
1251 if (e == nullptr) return CMD_ERROR;
1252
1253 bool reset = text.empty();
1254
1255 if (!reset) {
1257 if (!IsUniqueEngineName(text)) return CommandCost(STR_ERROR_NAME_MUST_BE_UNIQUE);
1258 }
1259
1260 if (flags.Test(DoCommandFlag::Execute)) {
1261 if (reset) {
1262 e->name.clear();
1263 } else {
1264 e->name = text;
1265 }
1266
1268 }
1269
1270 return CommandCost();
1271}
1272
1273
1282bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company)
1283{
1284 const Engine *e = Engine::GetIfValid(engine);
1285
1286 /* check if it's an engine that is in the engine array */
1287 if (e == nullptr) return false;
1288
1289 /* check if it's an engine of specified type */
1290 if (e->type != type) return false;
1291
1292 /* check if it's available ... */
1293 if (company == OWNER_DEITY) {
1294 /* ... for any company (preview does not count) */
1295 if (!e->flags.Test(EngineFlag::Available) || e->company_avail.None()) return false;
1296 } else {
1297 /* ... for this company */
1298 if (!e->company_avail.Test(company)) return false;
1299 }
1300
1301 if (!e->IsEnabled()) return false;
1302
1303 if (type == VehicleType::Train && company != OWNER_DEITY) {
1304 /* Check if the rail type is available to this company */
1305 const Company *c = Company::Get(company);
1306 if (!GetAllCompatibleRailTypes(e->VehInfo<RailVehicleInfo>().railtypes).Any(c->avail_railtypes)) return false;
1307 }
1308 if (type == VehicleType::Road && company != OWNER_DEITY) {
1309 /* Check if the road type is available to this company */
1310 const Company *c = Company::Get(company);
1311 if (!GetRoadTypeInfo(e->VehInfo<RoadVehicleInfo>().roadtype)->powered_roadtypes.Any(c->avail_roadtypes)) return false;
1312 }
1313
1314 return true;
1315}
1316
1324{
1325 const Engine *e = Engine::GetIfValid(engine);
1326
1327 /* check if it's an engine that is in the engine array */
1328 if (e == nullptr) return false;
1329
1330 if (!e->CanCarryCargo()) return false;
1331
1332 const EngineInfo *ei = &e->info;
1333 if (ei->refit_mask.None()) return false;
1334
1335 /* Are there suffixes?
1336 * Note: This does not mean the suffixes are actually available for every consist at any time. */
1338
1339 /* Is there any cargo except the default cargo? */
1340 CargoType default_cargo = e->GetDefaultCargoType();
1341 return IsValidCargoType(default_cargo) && ei->refit_mask != default_cargo;
1342}
1343
1348{
1349 TimerGameCalendar::Date min_date{INT32_MAX};
1350
1351 for (const Engine *e : Engine::Iterate()) {
1352 if (!e->IsEnabled()) continue;
1353
1354 /* Don't consider train wagons, we need a powered engine available. */
1355 if (e->type == VehicleType::Train && e->VehInfo<RailVehicleInfo>().railveh_type == RailVehicleType::Wagon) continue;
1356
1357 /* We have an available engine... yay! */
1358 if (e->flags.Test(EngineFlag::Available) && e->company_avail.Any()) return;
1359
1360 /* Okay, try to find the earliest date. */
1361 min_date = std::min(min_date, e->info.base_intro);
1362 }
1363
1364 if (min_date < INT32_MAX) {
1365 ShowErrorMessage(GetEncodedString(STR_ERROR_NO_VEHICLES_AVAILABLE_YET),
1366 GetEncodedString(STR_ERROR_NO_VEHICLES_AVAILABLE_YET_EXPLANATION, min_date), WarningLevel::Warning);
1367 } else {
1368 ShowErrorMessage(GetEncodedString(STR_ERROR_NO_VEHICLES_AVAILABLE_AT_ALL),
1369 GetEncodedString(STR_ERROR_NO_VEHICLES_AVAILABLE_AT_ALL_EXPLANATION), WarningLevel::Warning);
1370 }
1371}
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.
static constexpr CargoLabel CT_INVALID
Invalid cargo type.
Definition cargo_type.h:72
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Definition cargo_type.h:110
EnumBitSet< CargoType, uint64_t > CargoTypes
Bitset of CargoType elements.
Definition cargo_type.h:113
static constexpr CargoLabel CT_PASSENGERS
Available types of cargo Labels may be re-used between different climates.
Definition cargo_type.h:31
CargoType
Cargo slots to indicate a cargo type within a game.
Definition cargo_type.h:22
@ Passengers
Passengers.
Definition cargotype.h:51
bool IsCargoInClass(CargoType cargo, CargoClasses cc)
Does cargo c have cargo class cc?
Definition cargotype.h:238
static void BroadcastNewEvent(ScriptEvent *event, CompanyID skip_company=CompanyID::Invalid())
Broadcast a new event to all active AIs.
Definition ai_core.cpp:250
static void NewEvent(CompanyID company, ScriptEvent *event)
Queue a new event for an AI.
Definition ai_core.cpp:231
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:493
uint GetPower() const
Returns the power of the engine for display and sorting purposes.
Definition engine.cpp:415
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:478
uint16_t reliability_spd_dec
Speed of reliability decay between services (per day).
Definition engine_base.h:51
Money GetCost() const
Return how much a new engine costs.
Definition engine.cpp:343
uint16_t reliability_start
Initial reliability of the engine.
Definition engine_base.h:52
TimerGameCalendar::Date intro_date
Date of introduction of the engine.
Definition engine_base.h:47
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:383
GrfID GetGRFID() const
Retrieve the GRF ID of the NewGRF the engine is tied to.
Definition engine.cpp:182
uint DetermineCapacity(const Vehicle *v, uint16_t *mail_capacity=nullptr) const
Determines capacity of a given vehicle from scratch.
Definition engine.cpp:226
EngineFlags flags
Flags of the engine.
Definition engine_base.h:58
CompanyMask company_avail
Bit for each company whether the engine is available for that company.
Definition engine_base.h:41
uint16_t reliability_max
Maximal reliability of the engine.
Definition engine_base.h:53
uint GetDisplayWeight() const
Returns the weight of the engine for display purposes.
Definition engine.cpp:433
bool IsEnabled() const
Checks whether the engine is a valid (non-articulated part of an) engine.
Definition engine.cpp:172
VehicleType type
Vehicle type, ie VehicleType::Road, VehicleType::Train, etc.
Definition engine_base.h:63
bool IsVariantHidden(CompanyID c) const
Check whether the engine variant chain is hidden in the GUI for the given company.
Definition engine.cpp:513
uint16_t reliability_final
Final reliability of the engine.
Definition engine_base.h:54
CompanyID preview_company
Company which is currently being offered a preview CompanyID::Invalid() means no company.
Definition engine_base.h:60
TimerGameCalendar::Date GetLifeLengthInDays() const
Returns the vehicle's (not model's!) life length in days.
Definition engine.cpp:468
uint16_t duration_phase_3
Third reliability phase in months, decaying to reliability_final.
Definition engine_base.h:57
uint16_t duration_phase_2
Second reliability phase in months, keeping reliability_max.
Definition engine_base.h:56
CargoType GetDefaultCargoType() const
Determines the default cargo type of an engine.
Definition engine_base.h:95
CompanyMask company_hidden
Bit for each company whether the engine is normally hidden in the build gui for that company.
Definition engine_base.h:42
Money GetRunningCost() const
Return how much the running costs of this engine are.
Definition engine.cpp:306
uint16_t reliability
Current reliability of the engine.
Definition engine_base.h:50
CompanyMask preview_asked
Bit for each company which has already been offered a preview.
Definition engine_base.h:43
uint GetDisplayMaxTractiveEffort() const
Returns the tractive effort of the engine for display purposes.
Definition engine.cpp:451
int32_t age
Age of the engine in months.
Definition engine_base.h:48
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:193
std::string name
Custom name of engine.
Definition engine_base.h:45
uint16_t duration_phase_1
First reliability phase in months, increasing reliability from reliability_start to reliability_max.
Definition engine_base.h:55
Iterate a range of enum values.
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:98
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
EnumBitSet< DoCommandFlag, uint16_t > DoCommandFlags
Bitset of DoCommandFlag elements.
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:936
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:1040
void SetYearEngineAgingStops()
Compute the value for _year_engine_aging_stops.
Definition engine.cpp:728
static void NewVehicleAvailable(Engine *e)
An engine has become available for general use.
Definition engine.cpp:1115
void SetupEngines()
Initialise the engine pool with the data from the original vehicles.
Definition engine.cpp:635
static CompanyID GetPreviewCompany(Engine *e)
Get the best company for an engine preview.
Definition engine.cpp:950
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:670
uint8_t GetOriginalEngineOffset(VehicleType type)
Get the index offset for original engines of a VehicleType.
Definition engine.cpp:77
static bool IsVehicleTypeDisabled(VehicleType type, bool ai)
Checks if a vehicle type is disabled for all/ai companies.
Definition engine.cpp:984
static void AcceptEnginePreview(EngineID eid, CompanyID company, int recursion_depth=0)
Company company accepts engine eid for preview.
Definition engine.cpp:918
CommandCost CmdRenameEngine(DoCommandFlags flags, EngineID engine_id, const std::string &text)
Rename an engine.
Definition engine.cpp:1248
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(WindowClass::EnginePreview);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.
CommandCost CmdWantEnginePreview(DoCommandFlags flags, EngineID engine_id)
Accept an engine prototype.
Definition engine.cpp:1075
void CalendarEnginesMonthlyLoop()
Monthly update of the availability, reliability, and preview offers of the engines.
Definition engine.cpp:1175
static bool IsUniqueEngineName(const std::string &name)
Is name still free as name for an engine?
Definition engine.cpp:1232
bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company)
Check if an engine is buildable.
Definition engine.cpp:1282
uint8_t GetOriginalEngineCount(VehicleType type)
Get the number of original engines for a VehicleType.
Definition engine.cpp:58
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:894
void StartupEngines()
Start/initialise all our engines.
Definition engine.cpp:834
static bool IsWagon(EngineID index)
Determine whether an engine type is a wagon (and not a loco).
Definition engine.cpp:659
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:1093
void StartupOneEngine(Engine *e, const TimerGameCalendar::YearMonthDay &aging_ymd, uint32_t seed)
Start/initialise one engine.
Definition engine.cpp:753
void CheckEngines()
Check for engines that have an appropriate availability.
Definition engine.cpp:1347
bool IsEngineRefittable(EngineID engine)
Check if an engine is refittable.
Definition engine.cpp:1323
CommandCost CmdSetVehicleVisibility(DoCommandFlags flags, EngineID engine_id, bool hide)
Set the visibility of an engine.
Definition engine.cpp:1054
void CalcEngineReliability(Engine *e, bool new_month)
Update Engine::reliability and (if needed) update the engine GUIs.
Definition engine.cpp:682
static void EnableEngineForCompany(EngineID eid, CompanyID company)
Allows engine eid to be used by a company company.
Definition engine.cpp:866
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.
@ Multihead
indicates a combination of two locomotives
Definition engine_type.h:33
@ Wagon
simple wagon, not motorized
Definition engine_type.h:34
This file contains all the data for vehicles.
constexpr std::underlying_type_t< enum_type > to_underlying(enum_type e)
Implementation of std::to_underlying (from C++23).
Definition enum_type.hpp:21
Functions related to errors.
@ 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:1553
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.
uint32_t GrfID
The unique identifier of a NewGRF.
Definition newgrf_type.h:15
static const GrfID INVALID_GRFID
An invalid NewGRF.
Definition newgrf_type.h:17
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:917
@ 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:339
RailTypes GetAllCompatibleRailTypes(RailTypes railtypes)
Returns all compatible railtypes for a set of railtypes.
Definition rail.h:315
EnumBitSet< RailType, uint64_t > RailTypes
Bitset of RailType elements.
Definition rail_type.h:36
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:217
@ 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:83
static CargoSpec * Get(size_t index)
Retrieve cargo details for the given cargo type.
Definition cargotype.h:141
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.
static uint64_t Key(GrfID grfid, uint16_t internal_id)
Create a 64 bit key from the GRFID and internal ID for mappings.
uint64_t Key() const
Create a 64 bit key from this mapping.
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:619
void ResetToDefaultMapping()
Initializes the EngineOverrideManager with the default engines.
Definition engine.cpp:535
EngineID GetID(VehicleType type, uint16_t grf_local_id, GrfID grfid)
Looks up an EngineID in the EngineOverrideManager.
Definition engine.cpp:556
void SetID(VehicleType type, uint16_t grf_local_id, GrfID grfid, uint8_t substitute_id, EngineID engine)
Create an override to the given engine.
Definition engine.cpp:602
EngineID UseUnreservedID(VehicleType type, uint16_t grf_local_id, GrfID grfid, bool static_access)
Look for an unreserved EngineID matching the local id, and reserve it if found.
Definition engine.cpp:575
Dynamic data of a loaded NewGRF.
Definition newgrf.h:124
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
RailVehicleType railveh_type
Type of rail vehicle.
Definition engine_type.h:76
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.
@ Water
Transport over water.
@ Air
Transport through air.
@ Road
Transport by road vehicle.
Functions related to vehicles.
bool IsCompanyBuildableVehicleType(VehicleType type)
Is the given vehicle type buildable by a company?
VehicleType
Available vehicle types.
@ Ship
Ship vehicle type.
@ Invalid
Non-existing type of vehicle.
@ CompanyEnd
Last company-ownable type.
@ Aircraft
Aircraft vehicle type.
@ Road
Road vehicle type.
@ Train
Train vehicle type.
EnumIndexArray< T, VehicleType, Tend > VehicleTypeIndexArray
Array with VehicleType as index.
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:1214
void SetWindowClassesDirty(WindowClass cls)
Mark all windows of a particular class as dirty (in need of repainting).
Definition window.cpp:3223
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:3315
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:3333
Window functions not directly related to making/drawing windows.