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