Fix 3ac1a2f1e4: Game crash due to invalid vehicle type information. (#14628)

Use std::variant instead of union for vehicle info.

RailVehicleInfo is now non-POD so using in a union causes undefined behaviour.
This commit is contained in:
Peter Nelson
2025-09-24 22:44:41 +01:00
committed by dP
parent 88d3faa050
commit 3bb1b847d3
27 changed files with 209 additions and 199 deletions

View File

@@ -200,7 +200,7 @@ void GetRotorImage(const Aircraft *v, EngineImageType image_type, VehicleSpriteS
static void GetAircraftIcon(EngineID engine, EngineImageType image_type, VehicleSpriteSeq *result) static void GetAircraftIcon(EngineID engine, EngineImageType image_type, VehicleSpriteSeq *result)
{ {
const Engine *e = Engine::Get(engine); const Engine *e = Engine::Get(engine);
uint8_t spritenum = e->u.air.image_index; uint8_t spritenum = e->VehInfo<AircraftVehicleInfo>().image_index;
if (IsCustomVehicleSpriteNum(spritenum)) { if (IsCustomVehicleSpriteNum(spritenum)) {
GetCustomVehicleIcon(engine, DIR_W, image_type, result); GetCustomVehicleIcon(engine, DIR_W, image_type, result);
@@ -267,7 +267,7 @@ void GetAircraftSpriteSize(EngineID engine, uint &width, uint &height, int &xoff
*/ */
CommandCost CmdBuildAircraft(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret) CommandCost CmdBuildAircraft(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret)
{ {
const AircraftVehicleInfo *avi = &e->u.air; const AircraftVehicleInfo *avi = &e->VehInfo<AircraftVehicleInfo>();
const Station *st = Station::GetByTile(tile); const Station *st = Station::GetByTile(tile);
/* Prevent building aircraft types at places which can't handle them */ /* Prevent building aircraft types at places which can't handle them */
@@ -438,7 +438,7 @@ static void CheckIfAircraftNeedsService(Aircraft *v)
Money Aircraft::GetRunningCost() const Money Aircraft::GetRunningCost() const
{ {
const Engine *e = this->GetEngine(); const Engine *e = this->GetEngine();
uint cost_factor = GetVehicleProperty(this, PROP_AIRCRAFT_RUNNING_COST_FACTOR, e->u.air.running_cost); uint cost_factor = GetVehicleProperty(this, PROP_AIRCRAFT_RUNNING_COST_FACTOR, e->VehInfo<AircraftVehicleInfo>().running_cost);
return GetPrice(PR_RUNNING_AIRCRAFT, cost_factor, e->GetGRF()); return GetPrice(PR_RUNNING_AIRCRAFT, cost_factor, e->GetGRF());
} }

View File

@@ -365,10 +365,10 @@ void AddArticulatedParts(Vehicle *first)
t->track = front->track; t->track = front->track;
t->railtypes = front->railtypes; t->railtypes = front->railtypes;
t->spritenum = e_artic->u.rail.image_index; t->spritenum = e_artic->VehInfo<RailVehicleInfo>().image_index;
if (e_artic->CanCarryCargo()) { if (e_artic->CanCarryCargo()) {
t->cargo_type = e_artic->GetDefaultCargoType(); t->cargo_type = e_artic->GetDefaultCargoType();
t->cargo_cap = e_artic->u.rail.capacity; // Callback 36 is called when the consist is finished t->cargo_cap = e_artic->VehInfo<RailVehicleInfo>().capacity; // Callback 36 is called when the consist is finished
} else { } else {
t->cargo_type = front->cargo_type; // Needed for livery selection t->cargo_type = front->cargo_type; // Needed for livery selection
t->cargo_cap = 0; t->cargo_cap = 0;
@@ -392,11 +392,11 @@ void AddArticulatedParts(Vehicle *first)
rv->roadtype = front->roadtype; rv->roadtype = front->roadtype;
rv->compatible_roadtypes = front->compatible_roadtypes; rv->compatible_roadtypes = front->compatible_roadtypes;
rv->spritenum = e_artic->u.road.image_index; rv->spritenum = e_artic->VehInfo<RoadVehicleInfo>().image_index;
if (e_artic->CanCarryCargo()) { if (e_artic->CanCarryCargo()) {
rv->cargo_type = e_artic->GetDefaultCargoType(); rv->cargo_type = e_artic->GetDefaultCargoType();
assert(IsValidCargoType(rv->cargo_type)); assert(IsValidCargoType(rv->cargo_type));
rv->cargo_cap = e_artic->u.road.capacity; // Callback 36 is called when the consist is finished rv->cargo_cap = e_artic->VehInfo<RoadVehicleInfo>().capacity; // Callback 36 is called when the consist is finished
} else { } else {
rv->cargo_type = front->cargo_type; // Needed for livery selection rv->cargo_type = front->cargo_type; // Needed for livery selection
rv->cargo_cap = 0; rv->cargo_cap = 0;

View File

@@ -71,16 +71,16 @@ bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company)
switch (type) { switch (type) {
case VEH_TRAIN: { case VEH_TRAIN: {
/* make sure the railtypes are compatible */ /* make sure the railtypes are compatible */
if (!GetAllCompatibleRailTypes(e_from->u.rail.railtypes).Any(GetAllCompatibleRailTypes(e_to->u.rail.railtypes))) return false; if (!GetAllCompatibleRailTypes(e_from->VehInfo<RailVehicleInfo>().railtypes).Any(GetAllCompatibleRailTypes(e_to->VehInfo<RailVehicleInfo>().railtypes))) return false;
/* make sure we do not replace wagons with engines or vice versa */ /* make sure we do not replace wagons with engines or vice versa */
if ((e_from->u.rail.railveh_type == RAILVEH_WAGON) != (e_to->u.rail.railveh_type == RAILVEH_WAGON)) return false; if ((e_from->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_WAGON) != (e_to->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_WAGON)) return false;
break; break;
} }
case VEH_ROAD: case VEH_ROAD:
/* make sure the roadtypes are compatible */ /* make sure the roadtypes are compatible */
if (!GetRoadTypeInfo(e_from->u.road.roadtype)->powered_roadtypes.Any(GetRoadTypeInfo(e_to->u.road.roadtype)->powered_roadtypes)) return false; if (!GetRoadTypeInfo(e_from->VehInfo<RoadVehicleInfo>().roadtype)->powered_roadtypes.Any(GetRoadTypeInfo(e_to->VehInfo<RoadVehicleInfo>().roadtype)->powered_roadtypes)) return false;
/* make sure that we do not replace a tram with a normal road vehicles or vice versa */ /* make sure that we do not replace a tram with a normal road vehicles or vice versa */
if (e_from->info.misc_flags.Test(EngineMiscFlag::RoadIsTram) != e_to->info.misc_flags.Test(EngineMiscFlag::RoadIsTram)) return false; if (e_from->info.misc_flags.Test(EngineMiscFlag::RoadIsTram) != e_to->info.misc_flags.Test(EngineMiscFlag::RoadIsTram)) return false;
@@ -88,7 +88,7 @@ bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company)
case VEH_AIRCRAFT: case VEH_AIRCRAFT:
/* make sure that we do not replace a plane with a helicopter or vice versa */ /* make sure that we do not replace a plane with a helicopter or vice versa */
if ((e_from->u.air.subtype & AIR_CTOL) != (e_to->u.air.subtype & AIR_CTOL)) return false; if ((e_from->VehInfo<AircraftVehicleInfo>().subtype & AIR_CTOL) != (e_to->VehInfo<AircraftVehicleInfo>().subtype & AIR_CTOL)) return false;
break; break;
default: break; default: break;

View File

@@ -140,7 +140,7 @@ class ReplaceVehicleWindow : public Window {
case VEH_ROAD: case VEH_ROAD:
if (draw_left && this->sel_roadtype != INVALID_ROADTYPE) { if (draw_left && this->sel_roadtype != INVALID_ROADTYPE) {
/* Ensure that the roadtype is specific to the selected one */ /* Ensure that the roadtype is specific to the selected one */
if (e->u.road.roadtype != this->sel_roadtype) continue; if (e->VehInfo<RoadVehicleInfo>().roadtype != this->sel_roadtype) continue;
} }
break; break;

View File

@@ -726,8 +726,8 @@ static int DrawShipPurchaseInfo(int left, int right, int y, EngineID engine_numb
/* Purchase cost - Max speed */ /* Purchase cost - Max speed */
uint raw_speed = e->GetDisplayMaxSpeed(); uint raw_speed = e->GetDisplayMaxSpeed();
uint ocean_speed = e->u.ship.ApplyWaterClassSpeedFrac(raw_speed, true); uint ocean_speed = e->VehInfo<ShipVehicleInfo>().ApplyWaterClassSpeedFrac(raw_speed, true);
uint canal_speed = e->u.ship.ApplyWaterClassSpeedFrac(raw_speed, false); uint canal_speed = e->VehInfo<ShipVehicleInfo>().ApplyWaterClassSpeedFrac(raw_speed, false);
if (ocean_speed == canal_speed) { if (ocean_speed == canal_speed) {
if (te.cost != 0) { if (te.cost != 0) {
@@ -885,10 +885,10 @@ int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number,
switch (e->type) { switch (e->type) {
default: NOT_REACHED(); default: NOT_REACHED();
case VEH_TRAIN: case VEH_TRAIN:
if (e->u.rail.railveh_type == RAILVEH_WAGON) { if (e->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_WAGON) {
y = DrawRailWagonPurchaseInfo(left, right, y, engine_number, &e->u.rail, te); y = DrawRailWagonPurchaseInfo(left, right, y, engine_number, &e->VehInfo<RailVehicleInfo>(), te);
} else { } else {
y = DrawRailEnginePurchaseInfo(left, right, y, engine_number, &e->u.rail, te); y = DrawRailEnginePurchaseInfo(left, right, y, engine_number, &e->VehInfo<RailVehicleInfo>(), te);
} }
articulated_cargo = true; articulated_cargo = true;
break; break;
@@ -920,7 +920,7 @@ int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number,
} }
/* Draw details that apply to all types except rail wagons. */ /* Draw details that apply to all types except rail wagons. */
if (e->type != VEH_TRAIN || e->u.rail.railveh_type != RAILVEH_WAGON) { if (e->type != VEH_TRAIN || e->VehInfo<RailVehicleInfo>().railveh_type != RAILVEH_WAGON) {
/* Design date - Life length */ /* Design date - Life length */
DrawString(left, right, y, GetString(STR_PURCHASE_INFO_DESIGNED_LIFE, ymd.year, TimerGameCalendar::DateToYear(e->GetLifeLengthInDays()))); DrawString(left, right, y, GetString(STR_PURCHASE_INFO_DESIGNED_LIFE, ymd.year, TimerGameCalendar::DateToYear(e->GetLifeLengthInDays())));
y += GetCharacterHeight(FS_NORMAL); y += GetCharacterHeight(FS_NORMAL);
@@ -1395,7 +1395,7 @@ struct BuildVehicleWindow : Window {
for (const Engine *e : Engine::IterateType(VEH_TRAIN)) { for (const Engine *e : Engine::IterateType(VEH_TRAIN)) {
if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue; if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue;
EngineID eid = e->index; EngineID eid = e->index;
const RailVehicleInfo *rvi = &e->u.rail; const RailVehicleInfo *rvi = &e->VehInfo<RailVehicleInfo>();
if (this->filter.railtype != INVALID_RAILTYPE && !HasPowerOnRail(rvi->railtypes, this->filter.railtype)) continue; if (this->filter.railtype != INVALID_RAILTYPE && !HasPowerOnRail(rvi->railtypes, this->filter.railtype)) continue;
if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue; if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue;
@@ -1426,7 +1426,7 @@ struct BuildVehicleWindow : Window {
if (std::ranges::find(list, variant, &GUIEngineListItem::engine_id) == list.end()) { if (std::ranges::find(list, variant, &GUIEngineListItem::engine_id) == list.end()) {
const Engine *e = Engine::Get(variant); const Engine *e = Engine::Get(variant);
list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlag::Shaded, 0); list.emplace_back(variant, e->info.variant_id, e->display_flags | EngineDisplayFlag::Shaded, 0);
if (e->u.rail.railveh_type != RAILVEH_WAGON) num_engines++; if (e->VehInfo<RailVehicleInfo>().railveh_type != RAILVEH_WAGON) num_engines++;
} }
} }
@@ -1461,7 +1461,7 @@ struct BuildVehicleWindow : Window {
if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue; if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue;
EngineID eid = e->index; EngineID eid = e->index;
if (!IsEngineBuildable(eid, VEH_ROAD, _local_company)) continue; if (!IsEngineBuildable(eid, VEH_ROAD, _local_company)) continue;
if (this->filter.roadtype != INVALID_ROADTYPE && !HasPowerOnRoad(e->u.road.roadtype, this->filter.roadtype)) continue; if (this->filter.roadtype != INVALID_ROADTYPE && !HasPowerOnRoad(e->VehInfo<RoadVehicleInfo>().roadtype, this->filter.roadtype)) continue;
if (!bdf.Filter(e->badges)) continue; if (!bdf.Filter(e->badges)) continue;
/* Filter by name or NewGRF extra text */ /* Filter by name or NewGRF extra text */

View File

@@ -267,7 +267,7 @@ void WriteEngineInfo(JsonWriter &j) {
j.end_dict(); j.end_dict();
} }
{ {
const RailVehicleInfo *rvi = &e->u.rail; const RailVehicleInfo *rvi = &e->VehInfo<RailVehicleInfo>();
j.begin_dict_with_key("rail"); j.begin_dict_with_key("rail");
JKV(j, rvi->image_index); JKV(j, rvi->image_index);
JKV(j, rvi->railveh_type); JKV(j, rvi->railveh_type);

View File

@@ -229,7 +229,7 @@ void InitDepotWindowBlockSizes()
if (!e->IsEnabled()) continue; if (!e->IsEnabled()) continue;
uint w = TRAININFO_DEFAULT_VEHICLE_WIDTH; uint w = TRAININFO_DEFAULT_VEHICLE_WIDTH;
if (e->GetGRF() != nullptr && IsCustomVehicleSpriteNum(e->u.rail.image_index)) { if (e->GetGRF() != nullptr && IsCustomVehicleSpriteNum(e->VehInfo<RailVehicleInfo>().image_index)) {
w = e->GetGRF()->traininfo_vehicle_width; w = e->GetGRF()->traininfo_vehicle_width;
if (w != VEHICLEINFO_FULL_VEHICLE_WIDTH) { if (w != VEHICLEINFO_FULL_VEHICLE_WIDTH) {
/* Hopeless. /* Hopeless.

View File

@@ -582,7 +582,7 @@ void UpdateDisableElrailSettingState(bool disable, bool update_vehicles)
{ {
/* walk through all train engines */ /* walk through all train engines */
for (Engine *e : Engine::IterateType(VEH_TRAIN)) { for (Engine *e : Engine::IterateType(VEH_TRAIN)) {
RailVehicleInfo *rv_info = &e->u.rail; RailVehicleInfo *rv_info = &e->VehInfo<RailVehicleInfo>();
/* update railtype of engines intended to use elrail */ /* update railtype of engines intended to use elrail */
if (rv_info->intended_railtypes.Test(RAILTYPE_ELECTRIC)) { if (rv_info->intended_railtypes.Test(RAILTYPE_ELECTRIC)) {
rv_info->railtypes.Set(RAILTYPE_ELECTRIC, !disable); rv_info->railtypes.Set(RAILTYPE_ELECTRIC, !disable);

View File

@@ -78,24 +78,19 @@ Engine::Engine(VehicleType type, uint16_t local_id)
/* Check if this base engine is within the original engine data range */ /* Check if this base engine is within the original engine data range */
if (local_id >= _engine_counts[type]) { if (local_id >= _engine_counts[type]) {
/* 'power' defaults to zero, so we also have to default to 'wagon' */ /* Initialise default type-specific information. */
if (type == VEH_TRAIN) this->u.rail.railveh_type = RAILVEH_WAGON; switch (type) {
case VEH_TRAIN: this->vehicle_info.emplace<RailVehicleInfo>(); break;
case VEH_ROAD: this->vehicle_info.emplace<RoadVehicleInfo>(); break;
case VEH_SHIP: this->vehicle_info.emplace<ShipVehicleInfo>(); break;
case VEH_AIRCRAFT: this->vehicle_info.emplace<AircraftVehicleInfo>(); break;
default: break;
}
/* Set model life to maximum to make wagons available */ /* Set model life to maximum to make wagons available */
this->info.base_life = TimerGameCalendar::Year{0xFF}; this->info.base_life = TimerGameCalendar::Year{0xFF};
/* Set road vehicle tractive effort to the default value */
if (type == VEH_ROAD) this->u.road.tractive_effort = 0x4C;
/* Aircraft must have CT_INVALID as default, as there is no property */ /* Aircraft must have CT_INVALID as default, as there is no property */
this->info.cargo_type = INVALID_CARGO; this->info.cargo_type = INVALID_CARGO;
this->info.cargo_label = (type == VEH_AIRCRAFT) ? CT_INVALID : CT_PASSENGERS; this->info.cargo_label = (type == VEH_AIRCRAFT) ? CT_INVALID : CT_PASSENGERS;
/* Ships must have a non-zero acceleration. */
if (type == VEH_SHIP) this->u.ship.acceleration = 1;
/* Set visual effect to the default value */
switch (type) {
case VEH_TRAIN: this->u.rail.visual_effect = VE_DEFAULT; break;
case VEH_ROAD: this->u.road.visual_effect = VE_DEFAULT; break;
case VEH_SHIP: this->u.ship.visual_effect = VE_DEFAULT; break;
default: break; // The aircraft, disasters and especially visual effects have no NewGRF configured visual effects
}
/* Set cargo aging period to the default value. */ /* Set cargo aging period to the default value. */
this->info.cargo_age_period = Ticks::CARGO_AGING_TICKS; this->info.cargo_age_period = Ticks::CARGO_AGING_TICKS;
/* Not a variant */ /* Not a variant */
@@ -110,33 +105,37 @@ Engine::Engine(VehicleType type, uint16_t local_id)
switch (type) { switch (type) {
default: NOT_REACHED(); default: NOT_REACHED();
case VEH_TRAIN: case VEH_TRAIN: {
this->u.rail = _orig_rail_vehicle_info[local_id]; RailVehicleInfo &rvi = this->vehicle_info.emplace<RailVehicleInfo>(_orig_rail_vehicle_info[local_id]);
this->original_image_index = this->u.rail.image_index; this->original_image_index = rvi.image_index;
this->info.string_id = STR_VEHICLE_NAME_TRAIN_ENGINE_RAIL_KIRBY_PAUL_TANK_STEAM + local_id; this->info.string_id = STR_VEHICLE_NAME_TRAIN_ENGINE_RAIL_KIRBY_PAUL_TANK_STEAM + local_id;
/* Set the default model life of original wagons to "infinite" */ /* Set the default model life of original wagons to "infinite" */
if (this->u.rail.railveh_type == RAILVEH_WAGON) this->info.base_life = TimerGameCalendar::Year{0xFF}; if (rvi.railveh_type == RAILVEH_WAGON) this->info.base_life = TimerGameCalendar::Year{0xFF};
break; break;
}
case VEH_ROAD: case VEH_ROAD: {
this->u.road = _orig_road_vehicle_info[local_id]; RoadVehicleInfo &rvi = this->vehicle_info.emplace<RoadVehicleInfo>(_orig_road_vehicle_info[local_id]);
this->original_image_index = this->u.road.image_index; this->original_image_index = rvi.image_index;
this->info.string_id = STR_VEHICLE_NAME_ROAD_VEHICLE_MPS_REGAL_BUS + local_id; this->info.string_id = STR_VEHICLE_NAME_ROAD_VEHICLE_MPS_REGAL_BUS + local_id;
break; break;
}
case VEH_SHIP: case VEH_SHIP: {
this->u.ship = _orig_ship_vehicle_info[local_id]; ShipVehicleInfo &svi = this->vehicle_info.emplace<ShipVehicleInfo>(_orig_ship_vehicle_info[local_id]);
this->original_image_index = this->u.ship.image_index; this->original_image_index = svi.image_index;
this->info.string_id = STR_VEHICLE_NAME_SHIP_MPS_OIL_TANKER + local_id; this->info.string_id = STR_VEHICLE_NAME_SHIP_MPS_OIL_TANKER + local_id;
break; break;
}
case VEH_AIRCRAFT: case VEH_AIRCRAFT: {
this->u.air = _orig_aircraft_vehicle_info[local_id]; AircraftVehicleInfo &avi = this->vehicle_info.emplace<AircraftVehicleInfo>(_orig_aircraft_vehicle_info[local_id]);
this->original_image_index = this->u.air.image_index; this->original_image_index = avi.image_index;
this->info.string_id = STR_VEHICLE_NAME_AIRCRAFT_SAMPSON_U52 + local_id; this->info.string_id = STR_VEHICLE_NAME_AIRCRAFT_SAMPSON_U52 + local_id;
break; break;
}
} }
} }
@@ -174,11 +173,11 @@ bool Engine::CanCarryCargo() const
*/ */
switch (this->type) { switch (this->type) {
case VEH_TRAIN: case VEH_TRAIN:
if (this->u.rail.capacity == 0) return false; if (this->VehInfo<RailVehicleInfo>().capacity == 0) return false;
break; break;
case VEH_ROAD: case VEH_ROAD:
if (this->u.road.capacity == 0) return false; if (this->VehInfo<RoadVehicleInfo>().capacity == 0) return false;
break; break;
case VEH_SHIP: case VEH_SHIP:
@@ -210,7 +209,7 @@ uint Engine::DetermineCapacity(const Vehicle *v, uint16_t *mail_capacity) const
CargoType cargo_type = (v != nullptr) ? v->cargo_type : default_cargo; CargoType cargo_type = (v != nullptr) ? v->cargo_type : default_cargo;
if (mail_capacity != nullptr && this->type == VEH_AIRCRAFT && IsCargoInClass(cargo_type, CargoClass::Passengers)) { if (mail_capacity != nullptr && this->type == VEH_AIRCRAFT && IsCargoInClass(cargo_type, CargoClass::Passengers)) {
*mail_capacity = GetEngineProperty(this->index, PROP_AIRCRAFT_MAIL_CAPACITY, this->u.air.mail_capacity, v); *mail_capacity = GetEngineProperty(this->index, PROP_AIRCRAFT_MAIL_CAPACITY, this->VehInfo<AircraftVehicleInfo>().mail_capacity, v);
} }
/* Check the refit capacity callback if we are not in the default configuration, or if we are using the new multiplier algorithm. */ /* Check the refit capacity callback if we are not in the default configuration, or if we are using the new multiplier algorithm. */
@@ -225,24 +224,24 @@ uint Engine::DetermineCapacity(const Vehicle *v, uint16_t *mail_capacity) const
uint extra_mail_cap = 0; uint extra_mail_cap = 0;
switch (this->type) { switch (this->type) {
case VEH_TRAIN: case VEH_TRAIN:
capacity = GetEngineProperty(this->index, PROP_TRAIN_CARGO_CAPACITY, this->u.rail.capacity, v); capacity = GetEngineProperty(this->index, PROP_TRAIN_CARGO_CAPACITY, this->VehInfo<RailVehicleInfo>().capacity, v);
/* In purchase list add the capacity of the second head. Always use the plain property for this. */ /* In purchase list add the capacity of the second head. Always use the plain property for this. */
if (v == nullptr && this->u.rail.railveh_type == RAILVEH_MULTIHEAD) capacity += this->u.rail.capacity; if (v == nullptr && this->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_MULTIHEAD) capacity += this->VehInfo<RailVehicleInfo>().capacity;
break; break;
case VEH_ROAD: case VEH_ROAD:
capacity = GetEngineProperty(this->index, PROP_ROADVEH_CARGO_CAPACITY, this->u.road.capacity, v); capacity = GetEngineProperty(this->index, PROP_ROADVEH_CARGO_CAPACITY, this->VehInfo<RoadVehicleInfo>().capacity, v);
break; break;
case VEH_SHIP: case VEH_SHIP:
capacity = GetEngineProperty(this->index, PROP_SHIP_CARGO_CAPACITY, this->u.ship.capacity, v); capacity = GetEngineProperty(this->index, PROP_SHIP_CARGO_CAPACITY, this->VehInfo<ShipVehicleInfo>().capacity, v);
break; break;
case VEH_AIRCRAFT: case VEH_AIRCRAFT:
capacity = GetEngineProperty(this->index, PROP_AIRCRAFT_PASSENGER_CAPACITY, this->u.air.passenger_capacity, v); capacity = GetEngineProperty(this->index, PROP_AIRCRAFT_PASSENGER_CAPACITY, this->VehInfo<AircraftVehicleInfo>().passenger_capacity, v);
if (!IsCargoInClass(cargo_type, CargoClass::Passengers)) { if (!IsCargoInClass(cargo_type, CargoClass::Passengers)) {
extra_mail_cap = GetEngineProperty(this->index, PROP_AIRCRAFT_MAIL_CAPACITY, this->u.air.mail_capacity, v); extra_mail_cap = GetEngineProperty(this->index, PROP_AIRCRAFT_MAIL_CAPACITY, this->VehInfo<AircraftVehicleInfo>().mail_capacity, v);
} }
if (IsValidCargoType(GetCargoTypeByLabel(CT_MAIL))) { if (IsValidCargoType(GetCargoTypeByLabel(CT_MAIL))) {
if (!new_multipliers && cargo_type == GetCargoTypeByLabel(CT_MAIL)) return capacity + extra_mail_cap; if (!new_multipliers && cargo_type == GetCargoTypeByLabel(CT_MAIL)) return capacity + extra_mail_cap;
@@ -284,25 +283,25 @@ Money Engine::GetRunningCost() const
uint cost_factor; uint cost_factor;
switch (this->type) { switch (this->type) {
case VEH_ROAD: case VEH_ROAD:
base_price = this->u.road.running_cost_class; base_price = this->VehInfo<RoadVehicleInfo>().running_cost_class;
if (base_price == INVALID_PRICE) return 0; if (base_price == INVALID_PRICE) return 0;
cost_factor = GetEngineProperty(this->index, PROP_ROADVEH_RUNNING_COST_FACTOR, this->u.road.running_cost); cost_factor = GetEngineProperty(this->index, PROP_ROADVEH_RUNNING_COST_FACTOR, this->VehInfo<RoadVehicleInfo>().running_cost);
break; break;
case VEH_TRAIN: case VEH_TRAIN:
base_price = this->u.rail.running_cost_class; base_price = this->VehInfo<RailVehicleInfo>().running_cost_class;
if (base_price == INVALID_PRICE) return 0; if (base_price == INVALID_PRICE) return 0;
cost_factor = GetEngineProperty(this->index, PROP_TRAIN_RUNNING_COST_FACTOR, this->u.rail.running_cost); cost_factor = GetEngineProperty(this->index, PROP_TRAIN_RUNNING_COST_FACTOR, this->VehInfo<RailVehicleInfo>().running_cost);
break; break;
case VEH_SHIP: case VEH_SHIP:
base_price = PR_RUNNING_SHIP; base_price = PR_RUNNING_SHIP;
cost_factor = GetEngineProperty(this->index, PROP_SHIP_RUNNING_COST_FACTOR, this->u.ship.running_cost); cost_factor = GetEngineProperty(this->index, PROP_SHIP_RUNNING_COST_FACTOR, this->VehInfo<ShipVehicleInfo>().running_cost);
break; break;
case VEH_AIRCRAFT: case VEH_AIRCRAFT:
base_price = PR_RUNNING_AIRCRAFT; base_price = PR_RUNNING_AIRCRAFT;
cost_factor = GetEngineProperty(this->index, PROP_AIRCRAFT_RUNNING_COST_FACTOR, this->u.air.running_cost); cost_factor = GetEngineProperty(this->index, PROP_AIRCRAFT_RUNNING_COST_FACTOR, this->VehInfo<AircraftVehicleInfo>().running_cost);
break; break;
default: NOT_REACHED(); default: NOT_REACHED();
@@ -322,27 +321,27 @@ Money Engine::GetCost() const
switch (this->type) { switch (this->type) {
case VEH_ROAD: case VEH_ROAD:
base_price = PR_BUILD_VEHICLE_ROAD; base_price = PR_BUILD_VEHICLE_ROAD;
cost_factor = GetEngineProperty(this->index, PROP_ROADVEH_COST_FACTOR, this->u.road.cost_factor); cost_factor = GetEngineProperty(this->index, PROP_ROADVEH_COST_FACTOR, this->VehInfo<RoadVehicleInfo>().cost_factor);
break; break;
case VEH_TRAIN: case VEH_TRAIN:
if (this->u.rail.railveh_type == RAILVEH_WAGON) { if (this->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_WAGON) {
base_price = PR_BUILD_VEHICLE_WAGON; base_price = PR_BUILD_VEHICLE_WAGON;
cost_factor = GetEngineProperty(this->index, PROP_TRAIN_COST_FACTOR, this->u.rail.cost_factor); cost_factor = GetEngineProperty(this->index, PROP_TRAIN_COST_FACTOR, this->VehInfo<RailVehicleInfo>().cost_factor);
} else { } else {
base_price = PR_BUILD_VEHICLE_TRAIN; base_price = PR_BUILD_VEHICLE_TRAIN;
cost_factor = GetEngineProperty(this->index, PROP_TRAIN_COST_FACTOR, this->u.rail.cost_factor); cost_factor = GetEngineProperty(this->index, PROP_TRAIN_COST_FACTOR, this->VehInfo<RailVehicleInfo>().cost_factor);
} }
break; break;
case VEH_SHIP: case VEH_SHIP:
base_price = PR_BUILD_VEHICLE_SHIP; base_price = PR_BUILD_VEHICLE_SHIP;
cost_factor = GetEngineProperty(this->index, PROP_SHIP_COST_FACTOR, this->u.ship.cost_factor); cost_factor = GetEngineProperty(this->index, PROP_SHIP_COST_FACTOR, this->VehInfo<ShipVehicleInfo>().cost_factor);
break; break;
case VEH_AIRCRAFT: case VEH_AIRCRAFT:
base_price = PR_BUILD_VEHICLE_AIRCRAFT; base_price = PR_BUILD_VEHICLE_AIRCRAFT;
cost_factor = GetEngineProperty(this->index, PROP_AIRCRAFT_COST_FACTOR, this->u.air.cost_factor); cost_factor = GetEngineProperty(this->index, PROP_AIRCRAFT_COST_FACTOR, this->VehInfo<AircraftVehicleInfo>().cost_factor);
break; break;
default: NOT_REACHED(); default: NOT_REACHED();
@@ -359,22 +358,22 @@ uint Engine::GetDisplayMaxSpeed() const
{ {
switch (this->type) { switch (this->type) {
case VEH_TRAIN: case VEH_TRAIN:
return GetEngineProperty(this->index, PROP_TRAIN_SPEED, this->u.rail.max_speed); return GetEngineProperty(this->index, PROP_TRAIN_SPEED, this->VehInfo<RailVehicleInfo>().max_speed);
case VEH_ROAD: { case VEH_ROAD: {
uint max_speed = GetEngineProperty(this->index, PROP_ROADVEH_SPEED, 0); uint max_speed = GetEngineProperty(this->index, PROP_ROADVEH_SPEED, 0);
return (max_speed != 0) ? max_speed * 2 : this->u.road.max_speed / 2; return (max_speed != 0) ? max_speed * 2 : this->VehInfo<RoadVehicleInfo>().max_speed / 2;
} }
case VEH_SHIP: case VEH_SHIP:
return GetEngineProperty(this->index, PROP_SHIP_SPEED, this->u.ship.max_speed) / 2; return GetEngineProperty(this->index, PROP_SHIP_SPEED, this->VehInfo<ShipVehicleInfo>().max_speed) / 2;
case VEH_AIRCRAFT: { case VEH_AIRCRAFT: {
uint max_speed = GetEngineProperty(this->index, PROP_AIRCRAFT_SPEED, 0); uint max_speed = GetEngineProperty(this->index, PROP_AIRCRAFT_SPEED, 0);
if (max_speed != 0) { if (max_speed != 0) {
return (max_speed * 128) / 10; return (max_speed * 128) / 10;
} }
return this->u.air.max_speed; return this->VehInfo<AircraftVehicleInfo>().max_speed;
} }
default: NOT_REACHED(); default: NOT_REACHED();
@@ -392,9 +391,9 @@ uint Engine::GetPower() const
/* Only trains and road vehicles have 'power'. */ /* Only trains and road vehicles have 'power'. */
switch (this->type) { switch (this->type) {
case VEH_TRAIN: case VEH_TRAIN:
return GetEngineProperty(this->index, PROP_TRAIN_POWER, this->u.rail.power); return GetEngineProperty(this->index, PROP_TRAIN_POWER, this->VehInfo<RailVehicleInfo>().power);
case VEH_ROAD: case VEH_ROAD:
return GetEngineProperty(this->index, PROP_ROADVEH_POWER, this->u.road.power) * 10; return GetEngineProperty(this->index, PROP_ROADVEH_POWER, this->VehInfo<RoadVehicleInfo>().power) * 10;
default: NOT_REACHED(); default: NOT_REACHED();
} }
@@ -410,9 +409,9 @@ uint Engine::GetDisplayWeight() const
/* Only trains and road vehicles have 'weight'. */ /* Only trains and road vehicles have 'weight'. */
switch (this->type) { switch (this->type) {
case VEH_TRAIN: case VEH_TRAIN:
return GetEngineProperty(this->index, PROP_TRAIN_WEIGHT, this->u.rail.weight) << (this->u.rail.railveh_type == RAILVEH_MULTIHEAD ? 1 : 0); return GetEngineProperty(this->index, PROP_TRAIN_WEIGHT, this->VehInfo<RailVehicleInfo>().weight) << (this->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_MULTIHEAD ? 1 : 0);
case VEH_ROAD: case VEH_ROAD:
return GetEngineProperty(this->index, PROP_ROADVEH_WEIGHT, this->u.road.weight) / 4; return GetEngineProperty(this->index, PROP_ROADVEH_WEIGHT, this->VehInfo<RoadVehicleInfo>().weight) / 4;
default: NOT_REACHED(); default: NOT_REACHED();
} }
@@ -428,9 +427,9 @@ uint Engine::GetDisplayMaxTractiveEffort() const
/* Only trains and road vehicles have 'tractive effort'. */ /* Only trains and road vehicles have 'tractive effort'. */
switch (this->type) { switch (this->type) {
case VEH_TRAIN: case VEH_TRAIN:
return (GROUND_ACCELERATION * this->GetDisplayWeight() * GetEngineProperty(this->index, PROP_TRAIN_TRACTIVE_EFFORT, this->u.rail.tractive_effort)) / 256; return (GROUND_ACCELERATION * this->GetDisplayWeight() * GetEngineProperty(this->index, PROP_TRAIN_TRACTIVE_EFFORT, this->VehInfo<RailVehicleInfo>().tractive_effort)) / 256;
case VEH_ROAD: case VEH_ROAD:
return (GROUND_ACCELERATION * this->GetDisplayWeight() * GetEngineProperty(this->index, PROP_ROADVEH_TRACTIVE_EFFORT, this->u.road.tractive_effort)) / 256; return (GROUND_ACCELERATION * this->GetDisplayWeight() * GetEngineProperty(this->index, PROP_ROADVEH_TRACTIVE_EFFORT, this->VehInfo<RoadVehicleInfo>().tractive_effort)) / 256;
default: NOT_REACHED(); default: NOT_REACHED();
} }
@@ -454,7 +453,7 @@ uint16_t Engine::GetRange() const
{ {
switch (this->type) { switch (this->type) {
case VEH_AIRCRAFT: case VEH_AIRCRAFT:
return GetEngineProperty(this->index, PROP_AIRCRAFT_RANGE, this->u.air.max_range); return GetEngineProperty(this->index, PROP_AIRCRAFT_RANGE, this->VehInfo<AircraftVehicleInfo>().max_range);
default: NOT_REACHED(); default: NOT_REACHED();
} }
@@ -468,7 +467,7 @@ StringID Engine::GetAircraftTypeText() const
{ {
switch (this->type) { switch (this->type) {
case VEH_AIRCRAFT: case VEH_AIRCRAFT:
switch (this->u.air.subtype) { switch (this->VehInfo<AircraftVehicleInfo>().subtype) {
case AIR_HELI: return STR_LIVERY_HELICOPTER; case AIR_HELI: return STR_LIVERY_HELICOPTER;
case AIR_CTOL: return STR_LIVERY_SMALL_PLANE; case AIR_CTOL: return STR_LIVERY_SMALL_PLANE;
case AIR_CTOL | AIR_FAST: return STR_LIVERY_LARGE_PLANE; case AIR_CTOL | AIR_FAST: return STR_LIVERY_LARGE_PLANE;
@@ -625,7 +624,7 @@ void ShowEnginePreviewWindow(EngineID engine);
static bool IsWagon(EngineID index) static bool IsWagon(EngineID index)
{ {
const Engine *e = Engine::Get(index); const Engine *e = Engine::Get(index);
return e->type == VEH_TRAIN && e->u.rail.railveh_type == RAILVEH_WAGON; return e->type == VEH_TRAIN && e->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_WAGON;
} }
/** /**
@@ -700,7 +699,7 @@ void SetYearEngineAgingStops()
/* Exclude certain engines */ /* Exclude certain engines */
if (!ei->climates.Test(_settings_game.game_creation.landscape)) continue; if (!ei->climates.Test(_settings_game.game_creation.landscape)) continue;
if (e->type == VEH_TRAIN && e->u.rail.railveh_type == RAILVEH_WAGON) continue; if (e->type == VEH_TRAIN && e->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_WAGON) continue;
/* Base year ending date on half the model life */ /* Base year ending date on half the model life */
TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(ei->base_intro + (ei->lifelength.base() * CalendarTime::DAYS_IN_LEAP_YEAR) / 2); TimerGameCalendar::YearMonthDay ymd = TimerGameCalendar::ConvertDateToYMD(ei->base_intro + (ei->lifelength.base() * CalendarTime::DAYS_IN_LEAP_YEAR) / 2);
@@ -1107,13 +1106,13 @@ static void NewVehicleAvailable(Engine *e)
if (e->type == VEH_TRAIN) { if (e->type == VEH_TRAIN) {
/* maybe make another rail type available */ /* maybe make another rail type available */
assert(e->u.rail.railtypes != RailTypes{}); assert(e->VehInfo<RailVehicleInfo>().railtypes != RailTypes{});
RailTypes introduced = GetAllIntroducesRailTypes(e->u.rail.railtypes); RailTypes introduced = GetAllIntroducesRailTypes(e->VehInfo<RailVehicleInfo>().railtypes);
for (Company *c : Company::Iterate()) c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes | introduced, TimerGameCalendar::date); for (Company *c : Company::Iterate()) c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes | introduced, TimerGameCalendar::date);
} else if (e->type == VEH_ROAD) { } else if (e->type == VEH_ROAD) {
/* maybe make another road type available */ /* maybe make another road type available */
assert(e->u.road.roadtype < ROADTYPE_END); assert(e->VehInfo<RoadVehicleInfo>().roadtype < ROADTYPE_END);
for (Company *c : Company::Iterate()) c->avail_roadtypes = AddDateIntroducedRoadTypes(c->avail_roadtypes | GetRoadTypeInfo(e->u.road.roadtype)->introduces_roadtypes, TimerGameCalendar::date); for (Company *c : Company::Iterate()) c->avail_roadtypes = AddDateIntroducedRoadTypes(c->avail_roadtypes | GetRoadTypeInfo(e->VehInfo<RoadVehicleInfo>().roadtype)->introduces_roadtypes, TimerGameCalendar::date);
} }
/* Only broadcast event if AIs are able to build this vehicle type. */ /* Only broadcast event if AIs are able to build this vehicle type. */
@@ -1268,12 +1267,12 @@ bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company)
if (type == VEH_TRAIN && company != OWNER_DEITY) { if (type == VEH_TRAIN && company != OWNER_DEITY) {
/* Check if the rail type is available to this company */ /* Check if the rail type is available to this company */
const Company *c = Company::Get(company); const Company *c = Company::Get(company);
if (!GetAllCompatibleRailTypes(e->u.rail.railtypes).Any(c->avail_railtypes)) return false; if (!GetAllCompatibleRailTypes(e->VehInfo<RailVehicleInfo>().railtypes).Any(c->avail_railtypes)) return false;
} }
if (type == VEH_ROAD && company != OWNER_DEITY) { if (type == VEH_ROAD && company != OWNER_DEITY) {
/* Check if the road type is available to this company */ /* Check if the road type is available to this company */
const Company *c = Company::Get(company); const Company *c = Company::Get(company);
if (!GetRoadTypeInfo(e->u.road.roadtype)->powered_roadtypes.Any(c->avail_roadtypes)) return false; if (!GetRoadTypeInfo(e->VehInfo<RoadVehicleInfo>().roadtype)->powered_roadtypes.Any(c->avail_roadtypes)) return false;
} }
return true; return true;
@@ -1319,7 +1318,7 @@ void CheckEngines()
if (!e->IsEnabled()) continue; if (!e->IsEnabled()) continue;
/* Don't consider train wagons, we need a powered engine available. */ /* Don't consider train wagons, we need a powered engine available. */
if (e->type == VEH_TRAIN && e->u.rail.railveh_type == RAILVEH_WAGON) continue; if (e->type == VEH_TRAIN && e->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_WAGON) continue;
/* We have an available engine... yay! */ /* We have an available engine... yay! */
if (e->flags.Test(EngineFlag::Available) && e->company_avail.Any()) return; if (e->flags.Test(EngineFlag::Available) && e->company_avail.Any()) return;

View File

@@ -35,7 +35,8 @@ using EngineDisplayFlags = EnumBitSet<EngineDisplayFlag, uint8_t>;
typedef Pool<Engine, EngineID, 64> EnginePool; typedef Pool<Engine, EngineID, 64> EnginePool;
extern EnginePool _engine_pool; extern EnginePool _engine_pool;
struct Engine : EnginePool::PoolItem<&_engine_pool> { class Engine : public EnginePool::PoolItem<&_engine_pool> {
public:
CompanyMask company_avail{}; ///< Bit for each company whether the engine is available for that company. CompanyMask company_avail{}; ///< Bit for each company whether the engine is available for that company.
CompanyMask company_hidden{}; ///< Bit for each company whether the engine is normally hidden in the build gui for that company. CompanyMask company_hidden{}; ///< Bit for each company whether the engine is normally hidden in the build gui for that company.
CompanyMask preview_asked{}; ///< Bit for each company which has already been offered a preview. CompanyMask preview_asked{}; ///< Bit for each company which has already been offered a preview.
@@ -64,13 +65,6 @@ struct Engine : EnginePool::PoolItem<&_engine_pool> {
EngineID display_last_variant = EngineID::Invalid(); ///< NOSAVE client-side-only last variant selected. EngineID display_last_variant = EngineID::Invalid(); ///< NOSAVE client-side-only last variant selected.
EngineInfo info{}; EngineInfo info{};
union {
RailVehicleInfo rail;
RoadVehicleInfo road;
ShipVehicleInfo ship;
AircraftVehicleInfo air;
} u{};
uint16_t list_position = 0; uint16_t list_position = 0;
/* NewGRF related data */ /* NewGRF related data */
@@ -78,6 +72,11 @@ struct Engine : EnginePool::PoolItem<&_engine_pool> {
std::vector<WagonOverride> overrides{}; std::vector<WagonOverride> overrides{};
std::vector<BadgeID> badges{}; std::vector<BadgeID> badges{};
private:
/* Vehicle-type specific information. */
std::variant<std::monostate, RailVehicleInfo, RoadVehicleInfo, ShipVehicleInfo, AircraftVehicleInfo> vehicle_info{};
public:
Engine() {} Engine() {}
Engine(VehicleType type, uint16_t local_id); Engine(VehicleType type, uint16_t local_id);
bool IsEnabled() const; bool IsEnabled() const;
@@ -177,6 +176,18 @@ struct Engine : EnginePool::PoolItem<&_engine_pool> {
bool operator() (size_t index) { return Engine::Get(index)->type == this->vt; } bool operator() (size_t index) { return Engine::Get(index)->type == this->vt; }
}; };
template <typename T>
inline T &VehInfo()
{
return std::get<T>(this->vehicle_info);
}
template <typename T>
inline const T &VehInfo() const
{
return std::get<T>(this->vehicle_info);
}
/** /**
* Returns an iterable ensemble of all valid engines of the given type * Returns an iterable ensemble of all valid engines of the given type
* @param vt the VehicleType for engines to be valid * @param vt the VehicleType for engines to be valid
@@ -234,22 +245,22 @@ inline const EngineInfo *EngInfo(EngineID e)
inline const RailVehicleInfo *RailVehInfo(EngineID e) inline const RailVehicleInfo *RailVehInfo(EngineID e)
{ {
return &Engine::Get(e)->u.rail; return &Engine::Get(e)->VehInfo<RailVehicleInfo>();
} }
inline const RoadVehicleInfo *RoadVehInfo(EngineID e) inline const RoadVehicleInfo *RoadVehInfo(EngineID e)
{ {
return &Engine::Get(e)->u.road; return &Engine::Get(e)->VehInfo<RoadVehicleInfo>();
} }
inline const ShipVehicleInfo *ShipVehInfo(EngineID e) inline const ShipVehicleInfo *ShipVehInfo(EngineID e)
{ {
return &Engine::Get(e)->u.ship; return &Engine::Get(e)->VehInfo<ShipVehicleInfo>();
} }
inline const AircraftVehicleInfo *AircraftVehInfo(EngineID e) inline const AircraftVehicleInfo *AircraftVehInfo(EngineID e)
{ {
return &Engine::Get(e)->u.air; return &Engine::Get(e)->VehInfo<AircraftVehicleInfo>();
} }
#endif /* ENGINE_BASE_H */ #endif /* ENGINE_BASE_H */

View File

@@ -43,12 +43,12 @@ StringID GetEngineCategoryName(EngineID engine)
switch (e->type) { switch (e->type) {
default: NOT_REACHED(); default: NOT_REACHED();
case VEH_ROAD: case VEH_ROAD:
return GetRoadTypeInfo(e->u.road.roadtype)->strings.new_engine; return GetRoadTypeInfo(e->VehInfo<RoadVehicleInfo>().roadtype)->strings.new_engine;
case VEH_AIRCRAFT: return STR_ENGINE_PREVIEW_AIRCRAFT; case VEH_AIRCRAFT: return STR_ENGINE_PREVIEW_AIRCRAFT;
case VEH_SHIP: return STR_ENGINE_PREVIEW_SHIP; case VEH_SHIP: return STR_ENGINE_PREVIEW_SHIP;
case VEH_TRAIN: case VEH_TRAIN:
assert(e->u.rail.railtypes.Any()); assert(e->VehInfo<RailVehicleInfo>().railtypes.Any());
return GetRailTypeInfo(e->u.rail.railtypes.GetNthSetBit(0).value())->strings.new_loco; return GetRailTypeInfo(e->VehInfo<RailVehicleInfo>().railtypes.GetNthSetBit(0).value())->strings.new_loco;
} }
} }
@@ -182,12 +182,12 @@ static std::string GetTrainEngineInfoString(const Engine &e)
res << GetString(STR_ENGINE_PREVIEW_COST_WEIGHT, e.GetCost(), e.GetDisplayWeight()); res << GetString(STR_ENGINE_PREVIEW_COST_WEIGHT, e.GetCost(), e.GetDisplayWeight());
res << '\n'; res << '\n';
if (e.u.rail.railtypes.Count() > 1) { if (e.VehInfo<RailVehicleInfo>().railtypes.Count() > 1) {
std::string railtypes{}; std::string railtypes{};
std::string_view list_separator = GetListSeparator(); std::string_view list_separator = GetListSeparator();
for (const auto &rt : _sorted_railtypes) { for (const auto &rt : _sorted_railtypes) {
if (!e.u.rail.railtypes.Test(rt)) continue; if (!e.VehInfo<RailVehicleInfo>().railtypes.Test(rt)) continue;
if (!railtypes.empty()) railtypes += list_separator; if (!railtypes.empty()) railtypes += list_separator;
AppendStringInPlace(railtypes, GetRailTypeInfo(rt)->strings.name); AppendStringInPlace(railtypes, GetRailTypeInfo(rt)->strings.name);
@@ -197,7 +197,7 @@ static std::string GetTrainEngineInfoString(const Engine &e)
} }
bool is_maglev = true; bool is_maglev = true;
for (RailType rt : e.u.rail.railtypes) { for (RailType rt : e.VehInfo<RailVehicleInfo>().railtypes) {
is_maglev &= GetRailTypeInfo(rt)->acceleration_type == VehicleAccelerationModel::Maglev; is_maglev &= GetRailTypeInfo(rt)->acceleration_type == VehicleAccelerationModel::Maglev;
} }

View File

@@ -25,7 +25,7 @@
/** Unique identification number of an engine. */ /** Unique identification number of an engine. */
using EngineID = PoolID<uint16_t, struct EngineIDTag, 64000, 0xFFFF>; using EngineID = PoolID<uint16_t, struct EngineIDTag, 64000, 0xFFFF>;
struct Engine; class Engine;
/** Available types of rail vehicles. */ /** Available types of rail vehicles. */
enum RailVehicleTypes : uint8_t { enum RailVehicleTypes : uint8_t {
@@ -50,10 +50,30 @@ enum class VehicleAccelerationModel : uint8_t {
Maglev, ///< Maglev acceleration model. Maglev, ///< Maglev acceleration model.
}; };
/** Meaning of the various bits of the visual effect. */
enum VisualEffect : uint8_t {
VE_OFFSET_START = 0, ///< First bit that contains the offset (0 = front, 8 = centre, 15 = rear)
VE_OFFSET_COUNT = 4, ///< Number of bits used for the offset
VE_OFFSET_CENTRE = 8, ///< Value of offset corresponding to a position above the centre of the vehicle
VE_TYPE_START = 4, ///< First bit used for the type of effect
VE_TYPE_COUNT = 2, ///< Number of bits used for the effect type
VE_TYPE_DEFAULT = 0, ///< Use default from engine class
VE_TYPE_STEAM = 1, ///< Steam plumes
VE_TYPE_DIESEL = 2, ///< Diesel fumes
VE_TYPE_ELECTRIC = 3, ///< Electric sparks
VE_DISABLE_EFFECT = 6, ///< Flag to disable visual effect
VE_ADVANCED_EFFECT = VE_DISABLE_EFFECT, ///< Flag for advanced effects
VE_DISABLE_WAGON_POWER = 7, ///< Flag to disable wagon power
VE_DEFAULT = 0xFF, ///< Default value to indicate that visual effect should be based on engine class
};
/** Information about a rail vehicle. */ /** Information about a rail vehicle. */
struct RailVehicleInfo { struct RailVehicleInfo {
uint8_t image_index = 0; uint8_t image_index = 0;
RailVehicleTypes railveh_type{}; RailVehicleTypes railveh_type = RAILVEH_WAGON;
uint8_t cost_factor = 0; ///< Purchase cost factor; For multiheaded engines the sum of both engine prices. uint8_t cost_factor = 0; ///< Purchase cost factor; For multiheaded engines the sum of both engine prices.
RailTypes railtypes{}; ///< Railtypes, mangled if elrail is disabled. RailTypes railtypes{}; ///< Railtypes, mangled if elrail is disabled.
RailTypes intended_railtypes{}; ///< Intended railtypes, regardless of elrail being enabled or disabled. RailTypes intended_railtypes{}; ///< Intended railtypes, regardless of elrail being enabled or disabled.
@@ -67,7 +87,7 @@ struct RailVehicleInfo {
uint8_t capacity = 0; ///< Cargo capacity of vehicle; For multiheaded engines the capacity of each single engine. uint8_t capacity = 0; ///< Cargo capacity of vehicle; For multiheaded engines the capacity of each single engine.
uint16_t pow_wag_power = 0; ///< Extra power applied to consist if wagon should be powered uint16_t pow_wag_power = 0; ///< Extra power applied to consist if wagon should be powered
uint8_t pow_wag_weight = 0; ///< Extra weight applied to consist if wagon should be powered uint8_t pow_wag_weight = 0; ///< Extra weight applied to consist if wagon should be powered
uint8_t visual_effect = 0; ///< Bitstuffed NewGRF visual effect data uint8_t visual_effect = VE_DEFAULT; ///< Bitstuffed NewGRF visual effect data
uint8_t shorten_factor = 0; ///< length on main map for this type is 8 - shorten_factor uint8_t shorten_factor = 0; ///< length on main map for this type is 8 - shorten_factor
uint8_t tractive_effort = 0; ///< Tractive effort coefficient uint8_t tractive_effort = 0; ///< Tractive effort coefficient
uint8_t air_drag = 0; ///< Coefficient of air drag uint8_t air_drag = 0; ///< Coefficient of air drag
@@ -80,12 +100,12 @@ struct ShipVehicleInfo {
uint8_t image_index = 0; uint8_t image_index = 0;
uint8_t cost_factor = 0; uint8_t cost_factor = 0;
uint8_t running_cost = 0; uint8_t running_cost = 0;
uint8_t acceleration = 0; ///< Acceleration (1 unit = 1/3.2 mph per tick = 0.5 km-ish/h per tick) uint8_t acceleration = 1; ///< Acceleration (1 unit = 1/3.2 mph per tick = 0.5 km-ish/h per tick)
uint16_t max_speed = 0; ///< Maximum speed (1 unit = 1/3.2 mph = 0.5 km-ish/h) uint16_t max_speed = 0; ///< Maximum speed (1 unit = 1/3.2 mph = 0.5 km-ish/h)
uint16_t capacity = 0; uint16_t capacity = 0;
SoundID sfx{}; SoundID sfx{};
bool old_refittable = 0; ///< Is ship refittable; only used during initialisation. Later use EngineInfo::refit_mask. bool old_refittable = 0; ///< Is ship refittable; only used during initialisation. Later use EngineInfo::refit_mask.
uint8_t visual_effect = 0; ///< Bitstuffed NewGRF visual effect data uint8_t visual_effect = VE_DEFAULT; ///< Bitstuffed NewGRF visual effect data
uint8_t ocean_speed_frac = 0; ///< Fraction of maximum speed for ocean tiles. uint8_t ocean_speed_frac = 0; ///< Fraction of maximum speed for ocean tiles.
uint8_t canal_speed_frac = 0; ///< Fraction of maximum speed for canal/river tiles. uint8_t canal_speed_frac = 0; ///< Fraction of maximum speed for canal/river tiles.
@@ -133,9 +153,9 @@ struct RoadVehicleInfo {
uint8_t capacity = 0; uint8_t capacity = 0;
uint8_t weight = 0; ///< Weight in 1/4t units uint8_t weight = 0; ///< Weight in 1/4t units
uint8_t power = 0; ///< Power in 10hp units uint8_t power = 0; ///< Power in 10hp units
uint8_t tractive_effort = 0; ///< Coefficient of tractive effort uint8_t tractive_effort = 0x4C; ///< Coefficient of tractive effort
uint8_t air_drag = 0; ///< Coefficient of air drag uint8_t air_drag = 0; ///< Coefficient of air drag
uint8_t visual_effect = 0; ///< Bitstuffed NewGRF visual effect data uint8_t visual_effect = VE_DEFAULT; ///< Bitstuffed NewGRF visual effect data
uint8_t shorten_factor = 0; ///< length on main map for this type is 8 - shorten_factor uint8_t shorten_factor = 0; ///< length on main map for this type is 8 - shorten_factor
RoadType roadtype{}; ///< Road type RoadType roadtype{}; ///< Road type
}; };

View File

@@ -272,7 +272,7 @@ Engine *GetNewEngine(const GRFFile *file, VehicleType type, uint16_t internal_id
} }
if (type == VEH_TRAIN) { if (type == VEH_TRAIN) {
_gted[e->index].railtypelabels.clear(); _gted[e->index].railtypelabels.clear();
for (RailType rt : e->u.rail.railtypes) _gted[e->index].railtypelabels.push_back(GetRailTypeInfo(rt)->label); for (RailType rt : e->VehInfo<RailVehicleInfo>().railtypes) _gted[e->index].railtypelabels.push_back(GetRailTypeInfo(rt)->label);
} }
GrfMsg(5, "Created new engine at index {} for GRFID {:x}, type {}, index {}", e->index, std::byteswap(file->grfid), type, internal_id); GrfMsg(5, "Created new engine at index {} for GRFID {:x}, type {}, index {}", e->index, std::byteswap(file->grfid), type, internal_id);
@@ -427,7 +427,7 @@ void ResetNewGRFData()
/* Fill rail type label temporary data for default trains */ /* Fill rail type label temporary data for default trains */
for (const Engine *e : Engine::IterateType(VEH_TRAIN)) { for (const Engine *e : Engine::IterateType(VEH_TRAIN)) {
_gted[e->index].railtypelabels.clear(); _gted[e->index].railtypelabels.clear();
for (RailType rt : e->u.rail.railtypes) _gted[e->index].railtypelabels.push_back(GetRailTypeInfo(rt)->label); for (RailType rt : e->VehInfo<RailVehicleInfo>().railtypes) _gted[e->index].railtypelabels.push_back(GetRailTypeInfo(rt)->label);
} }
/* Reset GRM reservations */ /* Reset GRM reservations */
@@ -661,7 +661,7 @@ static void CalculateRefitMasks()
/* If the NewGRF did not set any cargo properties, we apply default values. */ /* If the NewGRF did not set any cargo properties, we apply default values. */
if (_gted[engine].defaultcargo_grf == nullptr) { if (_gted[engine].defaultcargo_grf == nullptr) {
/* If the vehicle has any capacity, apply the default refit masks */ /* If the vehicle has any capacity, apply the default refit masks */
if (e->type != VEH_TRAIN || e->u.rail.capacity != 0) { if (e->type != VEH_TRAIN || e->VehInfo<RailVehicleInfo>().capacity != 0) {
static constexpr LandscapeType T = LandscapeType::Temperate; static constexpr LandscapeType T = LandscapeType::Temperate;
static constexpr LandscapeType A = LandscapeType::Arctic; static constexpr LandscapeType A = LandscapeType::Arctic;
static constexpr LandscapeType S = LandscapeType::Tropic; static constexpr LandscapeType S = LandscapeType::Tropic;
@@ -716,8 +716,8 @@ static void CalculateRefitMasks()
} }
break; break;
} }
e->u.ship.old_refittable = true; e->VehInfo<ShipVehicleInfo>().old_refittable = true;
} else if (e->type == VEH_TRAIN && e->u.rail.railveh_type != RAILVEH_WAGON) { } else if (e->type == VEH_TRAIN && e->VehInfo<RailVehicleInfo>().railveh_type != RAILVEH_WAGON) {
/* Train engines default to all cargoes, so you can build single-cargo consists with fast engines. /* Train engines default to all cargoes, so you can build single-cargo consists with fast engines.
* Trains loading multiple cargoes may start stations accepting unwanted cargoes. */ * Trains loading multiple cargoes may start stations accepting unwanted cargoes. */
_gted[engine].cargo_allowed = {CargoClass::Passengers, CargoClass::Mail, CargoClass::Armoured, CargoClass::Express, CargoClass::Bulk, CargoClass::PieceGoods, CargoClass::Liquid}; _gted[engine].cargo_allowed = {CargoClass::Passengers, CargoClass::Mail, CargoClass::Armoured, CargoClass::Express, CargoClass::Bulk, CargoClass::PieceGoods, CargoClass::Liquid};
@@ -792,7 +792,7 @@ static void CalculateRefitMasks()
/* Ensure that the vehicle is either not refittable, or that the default cargo is one of the refittable cargoes. /* Ensure that the vehicle is either not refittable, or that the default cargo is one of the refittable cargoes.
* Note: Vehicles refittable to no cargo are handle differently to vehicle refittable to a single cargo. The latter might have subtypes. */ * Note: Vehicles refittable to no cargo are handle differently to vehicle refittable to a single cargo. The latter might have subtypes. */
if (!only_defaultcargo && (e->type != VEH_SHIP || e->u.ship.old_refittable) && IsValidCargoType(ei->cargo_type) && !HasBit(ei->refit_mask, ei->cargo_type)) { if (!only_defaultcargo && (e->type != VEH_SHIP || e->VehInfo<ShipVehicleInfo>().old_refittable) && IsValidCargoType(ei->cargo_type) && !HasBit(ei->refit_mask, ei->cargo_type)) {
ei->cargo_type = INVALID_CARGO; ei->cargo_type = INVALID_CARGO;
} }
@@ -819,7 +819,7 @@ static void CalculateRefitMasks()
ei->cargo_type = (CargoType)FindFirstBit(ei->refit_mask); ei->cargo_type = (CargoType)FindFirstBit(ei->refit_mask);
} }
} }
if (!IsValidCargoType(ei->cargo_type) && e->type == VEH_TRAIN && e->u.rail.railveh_type != RAILVEH_WAGON && e->u.rail.capacity == 0) { if (!IsValidCargoType(ei->cargo_type) && e->type == VEH_TRAIN && e->VehInfo<RailVehicleInfo>().railveh_type != RAILVEH_WAGON && e->VehInfo<RailVehicleInfo>().capacity == 0) {
/* For train engines which do not carry cargo it does not matter if their cargo type is invalid. /* For train engines which do not carry cargo it does not matter if their cargo type is invalid.
* Fallback to the first available instead, if the cargo type has not been changed (as indicated by * Fallback to the first available instead, if the cargo type has not been changed (as indicated by
* cargo_label not being CT_INVALID). */ * cargo_label not being CT_INVALID). */
@@ -830,7 +830,7 @@ static void CalculateRefitMasks()
if (!IsValidCargoType(ei->cargo_type)) ei->climates = {}; if (!IsValidCargoType(ei->cargo_type)) ei->climates = {};
/* Clear refit_mask for not refittable ships */ /* Clear refit_mask for not refittable ships */
if (e->type == VEH_SHIP && !e->u.ship.old_refittable) { if (e->type == VEH_SHIP && !e->VehInfo<ShipVehicleInfo>().old_refittable) {
ei->refit_mask = 0; ei->refit_mask = 0;
} }
} }
@@ -867,16 +867,16 @@ static void FinaliseEngineArray()
switch (e->type) { switch (e->type) {
case VEH_TRAIN: case VEH_TRAIN:
for (RailType rt : e->u.rail.railtypes) { for (RailType rt : e->VehInfo<RailVehicleInfo>().railtypes) {
AppendCopyableBadgeList(e->badges, GetRailTypeInfo(rt)->badges, GSF_TRAINS); AppendCopyableBadgeList(e->badges, GetRailTypeInfo(rt)->badges, GSF_TRAINS);
} }
break; break;
case VEH_ROAD: AppendCopyableBadgeList(e->badges, GetRoadTypeInfo(e->u.road.roadtype)->badges, GSF_ROADVEHICLES); break; case VEH_ROAD: AppendCopyableBadgeList(e->badges, GetRoadTypeInfo(e->VehInfo<RoadVehicleInfo>().roadtype)->badges, GSF_ROADVEHICLES); break;
default: break; default: break;
} }
/* Skip wagons, there livery is defined via the engine */ /* Skip wagons, there livery is defined via the engine */
if (e->type != VEH_TRAIN || e->u.rail.railveh_type != RAILVEH_WAGON) { if (e->type != VEH_TRAIN || e->VehInfo<RailVehicleInfo>().railveh_type != RAILVEH_WAGON) {
LiveryScheme ls = GetEngineLiveryScheme(e->index, EngineID::Invalid(), nullptr); LiveryScheme ls = GetEngineLiveryScheme(e->index, EngineID::Invalid(), nullptr);
SetBit(_loaded_newgrf_features.used_liveries, ls); SetBit(_loaded_newgrf_features.used_liveries, ls);
/* Note: For ships and roadvehicles we assume that they cannot be refitted between passenger and freight */ /* Note: For ships and roadvehicles we assume that they cannot be refitted between passenger and freight */
@@ -1693,14 +1693,14 @@ static void AfterLoadGRFs()
for (Engine *e : Engine::IterateType(VEH_ROAD)) { for (Engine *e : Engine::IterateType(VEH_ROAD)) {
if (_gted[e->index].rv_max_speed != 0) { if (_gted[e->index].rv_max_speed != 0) {
/* Set RV maximum speed from the mph/0.8 unit value */ /* Set RV maximum speed from the mph/0.8 unit value */
e->u.road.max_speed = _gted[e->index].rv_max_speed * 4; e->VehInfo<RoadVehicleInfo>().max_speed = _gted[e->index].rv_max_speed * 4;
} }
RoadTramType rtt = e->info.misc_flags.Test(EngineMiscFlag::RoadIsTram) ? RTT_TRAM : RTT_ROAD; RoadTramType rtt = e->info.misc_flags.Test(EngineMiscFlag::RoadIsTram) ? RTT_TRAM : RTT_ROAD;
const GRFFile *file = e->GetGRF(); const GRFFile *file = e->GetGRF();
if (file == nullptr || _gted[e->index].roadtramtype == 0) { if (file == nullptr || _gted[e->index].roadtramtype == 0) {
e->u.road.roadtype = (rtt == RTT_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD; e->VehInfo<RoadVehicleInfo>().roadtype = (rtt == RTT_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD;
continue; continue;
} }
@@ -1713,7 +1713,7 @@ static void AfterLoadGRFs()
RoadTypeLabel rtl = (*list)[_gted[e->index].roadtramtype]; RoadTypeLabel rtl = (*list)[_gted[e->index].roadtramtype];
RoadType rt = GetRoadTypeByLabel(rtl); RoadType rt = GetRoadTypeByLabel(rtl);
if (rt != INVALID_ROADTYPE && GetRoadTramType(rt) == rtt) { if (rt != INVALID_ROADTYPE && GetRoadTramType(rt) == rtt) {
e->u.road.roadtype = rt; e->VehInfo<RoadVehicleInfo>().roadtype = rt;
continue; continue;
} }
} }
@@ -1730,8 +1730,8 @@ static void AfterLoadGRFs()
} }
if (railtypes.Any()) { if (railtypes.Any()) {
e->u.rail.railtypes = railtypes; e->VehInfo<RailVehicleInfo>().railtypes = railtypes;
e->u.rail.intended_railtypes = railtypes; e->VehInfo<RailVehicleInfo>().intended_railtypes = railtypes;
} else { } else {
/* Rail type is not available, so disable this engine */ /* Rail type is not available, so disable this engine */
e->info.climates = {}; e->info.climates = {};

View File

@@ -35,7 +35,7 @@ static ChangeInfoResult AircraftVehicleChangeInfo(uint first, uint last, int pro
if (e == nullptr) return CIR_INVALID_ID; // No engine could be allocated, so neither can any next vehicles if (e == nullptr) return CIR_INVALID_ID; // No engine could be allocated, so neither can any next vehicles
EngineInfo *ei = &e->info; EngineInfo *ei = &e->info;
AircraftVehicleInfo *avi = &e->u.air; AircraftVehicleInfo *avi = &e->VehInfo<AircraftVehicleInfo>();
switch (prop) { switch (prop) {
case 0x08: { // Sprite ID case 0x08: { // Sprite ID

View File

@@ -36,7 +36,7 @@ static ChangeInfoResult RoadVehicleChangeInfo(uint first, uint last, int prop, B
if (e == nullptr) return CIR_INVALID_ID; // No engine could be allocated, so neither can any next vehicles if (e == nullptr) return CIR_INVALID_ID; // No engine could be allocated, so neither can any next vehicles
EngineInfo *ei = &e->info; EngineInfo *ei = &e->info;
RoadVehicleInfo *rvi = &e->u.road; RoadVehicleInfo *rvi = &e->VehInfo<RoadVehicleInfo>();
switch (prop) { switch (prop) {
case 0x05: // Road/tram type case 0x05: // Road/tram type

View File

@@ -37,7 +37,7 @@ static ChangeInfoResult ShipVehicleChangeInfo(uint first, uint last, int prop, B
if (e == nullptr) return CIR_INVALID_ID; // No engine could be allocated, so neither can any next vehicles if (e == nullptr) return CIR_INVALID_ID; // No engine could be allocated, so neither can any next vehicles
EngineInfo *ei = &e->info; EngineInfo *ei = &e->info;
ShipVehicleInfo *svi = &e->u.ship; ShipVehicleInfo *svi = &e->VehInfo<ShipVehicleInfo>();
switch (prop) { switch (prop) {
case 0x08: { // Sprite ID case 0x08: { // Sprite ID

View File

@@ -35,7 +35,7 @@ ChangeInfoResult RailVehicleChangeInfo(uint first, uint last, int prop, ByteRead
if (e == nullptr) return CIR_INVALID_ID; // No engine could be allocated, so neither can any next vehicles if (e == nullptr) return CIR_INVALID_ID; // No engine could be allocated, so neither can any next vehicles
EngineInfo *ei = &e->info; EngineInfo *ei = &e->info;
RailVehicleInfo *rvi = &e->u.rail; RailVehicleInfo *rvi = &e->VehInfo<RailVehicleInfo>();
switch (prop) { switch (prop) {
case 0x05: { // Track type case 0x05: { // Track type

View File

@@ -1138,7 +1138,7 @@ static void GetRotorOverrideSprite(EngineID engine, const struct Aircraft *v, En
/* Only valid for helicopters */ /* Only valid for helicopters */
assert(e->type == VEH_AIRCRAFT); assert(e->type == VEH_AIRCRAFT);
assert(!(e->u.air.subtype & AIR_CTOL)); assert(!(e->VehInfo<AircraftVehicleInfo>().subtype & AIR_CTOL));
/* We differ from TTDPatch by resolving the sprite using the primary vehicle 'v', and not using the rotor vehicle 'v->Next()->Next()'. /* We differ from TTDPatch by resolving the sprite using the primary vehicle 'v', and not using the rotor vehicle 'v->Next()->Next()'.
* TTDPatch copies some variables between the vehicles each time, to somehow synchronize the rotor vehicle with the primary vehicle. * TTDPatch copies some variables between the vehicles each time, to somehow synchronize the rotor vehicle with the primary vehicle.

View File

@@ -129,7 +129,7 @@ RailTypes GetCompanyRailTypes(CompanyID company, bool introduces)
if (ei->climates.Test(_settings_game.game_creation.landscape) && if (ei->climates.Test(_settings_game.game_creation.landscape) &&
(e->company_avail.Test(company) || TimerGameCalendar::date >= e->intro_date + CalendarTime::DAYS_IN_YEAR)) { (e->company_avail.Test(company) || TimerGameCalendar::date >= e->intro_date + CalendarTime::DAYS_IN_YEAR)) {
const RailVehicleInfo *rvi = &e->u.rail; const RailVehicleInfo *rvi = &e->VehInfo<RailVehicleInfo>();
if (rvi->railveh_type != RAILVEH_WAGON) { if (rvi->railveh_type != RAILVEH_WAGON) {
assert(rvi->railtypes.Any()); assert(rvi->railtypes.Any());
@@ -159,7 +159,7 @@ RailTypes GetRailTypes(bool introduces)
const EngineInfo *ei = &e->info; const EngineInfo *ei = &e->info;
if (!ei->climates.Test(_settings_game.game_creation.landscape)) continue; if (!ei->climates.Test(_settings_game.game_creation.landscape)) continue;
const RailVehicleInfo *rvi = &e->u.rail; const RailVehicleInfo *rvi = &e->VehInfo<RailVehicleInfo>();
if (rvi->railveh_type != RAILVEH_WAGON) { if (rvi->railveh_type != RAILVEH_WAGON) {
assert(rvi->railtypes.Any()); assert(rvi->railtypes.Any());
if (introduces) { if (introduces) {

View File

@@ -204,7 +204,7 @@ RoadTypes GetCompanyRoadTypes(CompanyID company, bool introduces)
if (ei->climates.Test(_settings_game.game_creation.landscape) && if (ei->climates.Test(_settings_game.game_creation.landscape) &&
(e->company_avail.Test(company) || TimerGameCalendar::date >= e->intro_date + CalendarTime::DAYS_IN_YEAR)) { (e->company_avail.Test(company) || TimerGameCalendar::date >= e->intro_date + CalendarTime::DAYS_IN_YEAR)) {
const RoadVehicleInfo *rvi = &e->u.road; const RoadVehicleInfo *rvi = &e->VehInfo<RoadVehicleInfo>();
assert(rvi->roadtype < ROADTYPE_END); assert(rvi->roadtype < ROADTYPE_END);
if (introduces) { if (introduces) {
rts.Set(GetRoadTypeInfo(rvi->roadtype)->introduces_roadtypes); rts.Set(GetRoadTypeInfo(rvi->roadtype)->introduces_roadtypes);
@@ -231,7 +231,7 @@ RoadTypes GetRoadTypes(bool introduces)
const EngineInfo *ei = &e->info; const EngineInfo *ei = &e->info;
if (!ei->climates.Test(_settings_game.game_creation.landscape)) continue; if (!ei->climates.Test(_settings_game.game_creation.landscape)) continue;
const RoadVehicleInfo *rvi = &e->u.road; const RoadVehicleInfo *rvi = &e->VehInfo<RoadVehicleInfo>();
assert(rvi->roadtype < ROADTYPE_END); assert(rvi->roadtype < ROADTYPE_END);
if (introduces) { if (introduces) {
rts.Set(GetRoadTypeInfo(rvi->roadtype)->introduces_roadtypes); rts.Set(GetRoadTypeInfo(rvi->roadtype)->introduces_roadtypes);

View File

@@ -105,7 +105,7 @@ int RoadVehicle::GetDisplayImageWidth(Point *offset) const
static void GetRoadVehIcon(EngineID engine, EngineImageType image_type, VehicleSpriteSeq *result) static void GetRoadVehIcon(EngineID engine, EngineImageType image_type, VehicleSpriteSeq *result)
{ {
const Engine *e = Engine::Get(engine); const Engine *e = Engine::Get(engine);
uint8_t spritenum = e->u.road.image_index; uint8_t spritenum = e->VehInfo<RoadVehicleInfo>().image_index;
if (IsCustomVehicleSpriteNum(spritenum)) { if (IsCustomVehicleSpriteNum(spritenum)) {
GetCustomVehicleIcon(engine, DIR_W, image_type, result); GetCustomVehicleIcon(engine, DIR_W, image_type, result);
@@ -203,7 +203,7 @@ static uint GetRoadVehLength(const RoadVehicle *v)
/* Use callback 11 */ /* Use callback 11 */
veh_len = GetVehicleCallback(CBID_VEHICLE_LENGTH, 0, 0, v->engine_type, v); veh_len = GetVehicleCallback(CBID_VEHICLE_LENGTH, 0, 0, v->engine_type, v);
} }
if (veh_len == CALLBACK_FAILED) veh_len = e->u.road.shorten_factor; if (veh_len == CALLBACK_FAILED) veh_len = e->VehInfo<RoadVehicleInfo>().shorten_factor;
if (veh_len != 0) { if (veh_len != 0) {
length -= Clamp(veh_len, 0, VEHICLE_LENGTH - 1); length -= Clamp(veh_len, 0, VEHICLE_LENGTH - 1);
} }
@@ -263,12 +263,12 @@ void RoadVehUpdateCache(RoadVehicle *v, bool same_length)
CommandCost CmdBuildRoadVehicle(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret) CommandCost CmdBuildRoadVehicle(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret)
{ {
/* Check that the vehicle can drive on the road in question */ /* Check that the vehicle can drive on the road in question */
RoadType rt = e->u.road.roadtype; RoadType rt = e->VehInfo<RoadVehicleInfo>().roadtype;
const RoadTypeInfo *rti = GetRoadTypeInfo(rt); const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
if (!HasTileAnyRoadType(tile, rti->powered_roadtypes)) return CommandCost(STR_ERROR_DEPOT_WRONG_DEPOT_TYPE); if (!HasTileAnyRoadType(tile, rti->powered_roadtypes)) return CommandCost(STR_ERROR_DEPOT_WRONG_DEPOT_TYPE);
if (flags.Test(DoCommandFlag::Execute)) { if (flags.Test(DoCommandFlag::Execute)) {
const RoadVehicleInfo *rvi = &e->u.road; const RoadVehicleInfo *rvi = &e->VehInfo<RoadVehicleInfo>();
RoadVehicle *v = new RoadVehicle(); RoadVehicle *v = new RoadVehicle();
*ret = v; *ret = v;
@@ -1645,12 +1645,12 @@ static bool RoadVehController(RoadVehicle *v)
Money RoadVehicle::GetRunningCost() const Money RoadVehicle::GetRunningCost() const
{ {
const Engine *e = this->GetEngine(); const Engine *e = this->GetEngine();
if (e->u.road.running_cost_class == INVALID_PRICE) return 0; if (e->VehInfo<RoadVehicleInfo>().running_cost_class == INVALID_PRICE) return 0;
uint cost_factor = GetVehicleProperty(this, PROP_ROADVEH_RUNNING_COST_FACTOR, e->u.road.running_cost); uint cost_factor = GetVehicleProperty(this, PROP_ROADVEH_RUNNING_COST_FACTOR, e->VehInfo<RoadVehicleInfo>().running_cost);
if (cost_factor == 0) return 0; if (cost_factor == 0) return 0;
return GetPrice(e->u.road.running_cost_class, cost_factor, e->GetGRF()); return GetPrice(e->VehInfo<RoadVehicleInfo>().running_cost_class, cost_factor, e->GetGRF());
} }
bool RoadVehicle::Tick() bool RoadVehicle::Tick()

View File

@@ -439,7 +439,7 @@ void AfterLoadVehiclesPhase2(bool part_of_load)
if (rv->IsFrontEngine()) { if (rv->IsFrontEngine()) {
rv->gcache.last_speed = rv->cur_speed; // update displayed road vehicle speed rv->gcache.last_speed = rv->cur_speed; // update displayed road vehicle speed
rv->roadtype = Engine::Get(rv->engine_type)->u.road.roadtype; rv->roadtype = Engine::Get(rv->engine_type)->VehInfo<RoadVehicleInfo>().roadtype;
rv->compatible_roadtypes = GetRoadTypeInfo(rv->roadtype)->powered_roadtypes; rv->compatible_roadtypes = GetRoadTypeInfo(rv->roadtype)->powered_roadtypes;
RoadTramType rtt = GetRoadTramType(rv->roadtype); RoadTramType rtt = GetRoadTramType(rv->roadtype);
for (RoadVehicle *u = rv; u != nullptr; u = u->Next()) { for (RoadVehicle *u = rv; u != nullptr; u = u->Next()) {

View File

@@ -81,7 +81,7 @@ static inline TrackBits GetTileShipTrackStatus(TileIndex tile)
static void GetShipIcon(EngineID engine, EngineImageType image_type, VehicleSpriteSeq *result) static void GetShipIcon(EngineID engine, EngineImageType image_type, VehicleSpriteSeq *result)
{ {
const Engine *e = Engine::Get(engine); const Engine *e = Engine::Get(engine);
uint8_t spritenum = e->u.ship.image_index; uint8_t spritenum = e->VehInfo<ShipVehicleInfo>().image_index;
if (IsCustomVehicleSpriteNum(spritenum)) { if (IsCustomVehicleSpriteNum(spritenum)) {
GetCustomVehicleIcon(engine, DIR_W, image_type, result); GetCustomVehicleIcon(engine, DIR_W, image_type, result);
@@ -247,7 +247,7 @@ void Ship::UpdateCache()
Money Ship::GetRunningCost() const Money Ship::GetRunningCost() const
{ {
const Engine *e = this->GetEngine(); const Engine *e = this->GetEngine();
uint cost_factor = GetVehicleProperty(this, PROP_SHIP_RUNNING_COST_FACTOR, e->u.ship.running_cost); uint cost_factor = GetVehicleProperty(this, PROP_SHIP_RUNNING_COST_FACTOR, e->VehInfo<ShipVehicleInfo>().running_cost);
return GetPrice(PR_RUNNING_SHIP, cost_factor, e->GetGRF()); return GetPrice(PR_RUNNING_SHIP, cost_factor, e->GetGRF());
} }
@@ -886,7 +886,7 @@ CommandCost CmdBuildShip(DoCommandFlags flags, TileIndex tile, const Engine *e,
int x; int x;
int y; int y;
const ShipVehicleInfo *svi = &e->u.ship; const ShipVehicleInfo *svi = &e->VehInfo<ShipVehicleInfo>();
Ship *v = new Ship(); Ship *v = new Ship();
*ret = v; *ret = v;

View File

@@ -146,7 +146,7 @@ void Train::ConsistChanged(ConsistChangeFlags allowed_changes)
for (Train *u = this; u != nullptr; u = u->Next()) { for (Train *u = this; u != nullptr; u = u->Next()) {
const Engine *e_u = u->GetEngine(); const Engine *e_u = u->GetEngine();
const RailVehicleInfo *rvi_u = &e_u->u.rail; const RailVehicleInfo *rvi_u = &e_u->VehInfo<RailVehicleInfo>();
if (!e_u->info.misc_flags.Test(EngineMiscFlag::RailTilts)) train_can_tilt = false; if (!e_u->info.misc_flags.Test(EngineMiscFlag::RailTilts)) train_can_tilt = false;
min_curve_speed_mod = std::min(min_curve_speed_mod, u->GetCurveSpeedModifier()); min_curve_speed_mod = std::min(min_curve_speed_mod, u->GetCurveSpeedModifier());
@@ -441,7 +441,7 @@ int Train::GetCursorImageOffset() const
int reference_width = TRAININFO_DEFAULT_VEHICLE_WIDTH; int reference_width = TRAININFO_DEFAULT_VEHICLE_WIDTH;
const Engine *e = this->GetEngine(); const Engine *e = this->GetEngine();
if (e->GetGRF() != nullptr && IsCustomVehicleSpriteNum(e->u.rail.image_index)) { if (e->GetGRF() != nullptr && IsCustomVehicleSpriteNum(e->VehInfo<RailVehicleInfo>().image_index)) {
reference_width = e->GetGRF()->traininfo_vehicle_width; reference_width = e->GetGRF()->traininfo_vehicle_width;
} }
@@ -461,7 +461,7 @@ int Train::GetDisplayImageWidth(Point *offset) const
int vehicle_pitch = 0; int vehicle_pitch = 0;
const Engine *e = this->GetEngine(); const Engine *e = this->GetEngine();
if (e->GetGRF() != nullptr && IsCustomVehicleSpriteNum(e->u.rail.image_index)) { if (e->GetGRF() != nullptr && IsCustomVehicleSpriteNum(e->VehInfo<RailVehicleInfo>().image_index)) {
reference_width = e->GetGRF()->traininfo_vehicle_width; reference_width = e->GetGRF()->traininfo_vehicle_width;
vehicle_pitch = e->GetGRF()->traininfo_vehicle_pitch; vehicle_pitch = e->GetGRF()->traininfo_vehicle_pitch;
} }
@@ -515,7 +515,7 @@ static void GetRailIcon(EngineID engine, bool rear_head, int &y, EngineImageType
{ {
const Engine *e = Engine::Get(engine); const Engine *e = Engine::Get(engine);
Direction dir = rear_head ? DIR_E : DIR_W; Direction dir = rear_head ? DIR_E : DIR_W;
uint8_t spritenum = e->u.rail.image_index; uint8_t spritenum = e->VehInfo<RailVehicleInfo>().image_index;
if (IsCustomVehicleSpriteNum(spritenum)) { if (IsCustomVehicleSpriteNum(spritenum)) {
GetCustomVehicleIcon(engine, dir, image_type, result); GetCustomVehicleIcon(engine, dir, image_type, result);
@@ -636,7 +636,7 @@ static std::vector<VehicleID> GetFreeWagonsInDepot(TileIndex tile)
*/ */
static CommandCost CmdBuildRailWagon(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret) static CommandCost CmdBuildRailWagon(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret)
{ {
const RailVehicleInfo *rvi = &e->u.rail; const RailVehicleInfo *rvi = &e->VehInfo<RailVehicleInfo>();
/* Check that the wagon can drive on the track in question */ /* Check that the wagon can drive on the track in question */
if (!IsCompatibleRail(rvi->railtypes, GetRailType(tile))) return CMD_ERROR; if (!IsCompatibleRail(rvi->railtypes, GetRailType(tile))) return CMD_ERROR;
@@ -770,7 +770,7 @@ static void AddRearEngineToMultiheadedTrain(Train *v)
*/ */
CommandCost CmdBuildRailVehicle(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret) CommandCost CmdBuildRailVehicle(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret)
{ {
const RailVehicleInfo *rvi = &e->u.rail; const RailVehicleInfo *rvi = &e->VehInfo<RailVehicleInfo>();
if (rvi->railveh_type == RAILVEH_WAGON) return CmdBuildRailWagon(flags, tile, e, ret); if (rvi->railveh_type == RAILVEH_WAGON) return CmdBuildRailWagon(flags, tile, e, ret);
@@ -4074,15 +4074,15 @@ Money Train::GetRunningCost() const
do { do {
const Engine *e = v->GetEngine(); const Engine *e = v->GetEngine();
if (e->u.rail.running_cost_class == INVALID_PRICE) continue; if (e->VehInfo<RailVehicleInfo>().running_cost_class == INVALID_PRICE) continue;
uint cost_factor = GetVehicleProperty(v, PROP_TRAIN_RUNNING_COST_FACTOR, e->u.rail.running_cost); uint cost_factor = GetVehicleProperty(v, PROP_TRAIN_RUNNING_COST_FACTOR, e->VehInfo<RailVehicleInfo>().running_cost);
if (cost_factor == 0) continue; if (cost_factor == 0) continue;
/* Halve running cost for multiheaded parts */ /* Halve running cost for multiheaded parts */
if (v->IsMultiheaded()) cost_factor /= 2; if (v->IsMultiheaded()) cost_factor /= 2;
cost += GetPrice(e->u.rail.running_cost_class, cost_factor, e->GetGRF()); cost += GetPrice(e->VehInfo<RailVehicleInfo>().running_cost_class, cost_factor, e->GetGRF());
} while ((v = v->GetNextVehicle()) != nullptr); } while ((v = v->GetNextVehicle()) != nullptr);
return cost; return cost;

View File

@@ -1925,7 +1925,7 @@ bool CanBuildVehicleInfrastructure(VehicleType type, uint8_t subtype)
if (max > 0) { if (max > 0) {
/* Can we actually build the vehicle type? */ /* Can we actually build the vehicle type? */
for (const Engine *e : Engine::IterateType(type)) { for (const Engine *e : Engine::IterateType(type)) {
if (type == VEH_ROAD && GetRoadTramType(e->u.road.roadtype) != (RoadTramType)subtype) continue; if (type == VEH_ROAD && GetRoadTramType(e->VehInfo<RoadVehicleInfo>().roadtype) != (RoadTramType)subtype) continue;
if (e->company_avail.Test(_local_company)) return true; if (e->company_avail.Test(_local_company)) return true;
} }
return false; return false;
@@ -1955,7 +1955,7 @@ LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_
switch (e->type) { switch (e->type) {
default: NOT_REACHED(); default: NOT_REACHED();
case VEH_TRAIN: case VEH_TRAIN:
if (v != nullptr && parent_engine_type != EngineID::Invalid() && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->u.rail.railveh_type != RAILVEH_WAGON))) { if (v != nullptr && parent_engine_type != EngineID::Invalid() && (UsesWagonOverride(v) || (v->IsArticulatedPart() && e->VehInfo<RailVehicleInfo>().railveh_type != RAILVEH_WAGON))) {
/* Wagonoverrides use the colour scheme of the front engine. /* Wagonoverrides use the colour scheme of the front engine.
* Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */ * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */
engine_type = parent_engine_type; engine_type = parent_engine_type;
@@ -1966,7 +1966,7 @@ LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_
if (!IsValidCargoType(cargo_type)) cargo_type = e->GetDefaultCargoType(); if (!IsValidCargoType(cargo_type)) cargo_type = e->GetDefaultCargoType();
if (!IsValidCargoType(cargo_type)) cargo_type = GetCargoTypeByLabel(CT_GOODS); // The vehicle does not carry anything, let's pick some freight cargo if (!IsValidCargoType(cargo_type)) cargo_type = GetCargoTypeByLabel(CT_GOODS); // The vehicle does not carry anything, let's pick some freight cargo
assert(IsValidCargoType(cargo_type)); assert(IsValidCargoType(cargo_type));
if (e->u.rail.railveh_type == RAILVEH_WAGON) { if (e->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_WAGON) {
if (!CargoSpec::Get(cargo_type)->is_freight) { if (!CargoSpec::Get(cargo_type)->is_freight) {
if (parent_engine_type == EngineID::Invalid()) { if (parent_engine_type == EngineID::Invalid()) {
return LS_PASSENGER_WAGON_STEAM; return LS_PASSENGER_WAGON_STEAM;
@@ -1987,7 +1987,7 @@ LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_
} else { } else {
bool is_mu = e->info.misc_flags.Test(EngineMiscFlag::RailIsMU); bool is_mu = e->info.misc_flags.Test(EngineMiscFlag::RailIsMU);
switch (e->u.rail.engclass) { switch (e->VehInfo<RailVehicleInfo>().engclass) {
default: NOT_REACHED(); default: NOT_REACHED();
case EC_STEAM: return LS_STEAM; case EC_STEAM: return LS_STEAM;
case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL; case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
@@ -2024,7 +2024,7 @@ LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_
return IsCargoInClass(cargo_type, CargoClass::Passengers) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP; return IsCargoInClass(cargo_type, CargoClass::Passengers) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
case VEH_AIRCRAFT: case VEH_AIRCRAFT:
switch (e->u.air.subtype) { switch (e->VehInfo<AircraftVehicleInfo>().subtype) {
case AIR_HELI: return LS_HELICOPTER; case AIR_HELI: return LS_HELICOPTER;
case AIR_CTOL: return LS_SMALL_PLANE; case AIR_CTOL: return LS_SMALL_PLANE;
case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE; case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
@@ -2641,9 +2641,9 @@ void Vehicle::UpdateVisualEffect(bool allow_power_change)
/* Evaluate properties */ /* Evaluate properties */
uint8_t visual_effect; uint8_t visual_effect;
switch (e->type) { switch (e->type) {
case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break; case VEH_TRAIN: visual_effect = e->VehInfo<RailVehicleInfo>().visual_effect; break;
case VEH_ROAD: visual_effect = e->u.road.visual_effect; break; case VEH_ROAD: visual_effect = e->VehInfo<RoadVehicleInfo>().visual_effect; break;
case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break; case VEH_SHIP: visual_effect = e->VehInfo<ShipVehicleInfo>().visual_effect; break;
default: visual_effect = 1 << VE_DISABLE_EFFECT; break; default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
} }
@@ -2670,7 +2670,7 @@ void Vehicle::UpdateVisualEffect(bool allow_power_change)
(!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) { (!HasBit(visual_effect, VE_DISABLE_EFFECT) && GB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT) == VE_TYPE_DEFAULT)) {
/* Only train engines have default effects. /* Only train engines have default effects.
* Note: This is independent of whether the engine is a front engine or articulated part or whatever. */ * Note: This is independent of whether the engine is a front engine or articulated part or whatever. */
if (e->type != VEH_TRAIN || e->u.rail.railveh_type == RAILVEH_WAGON || !IsInsideMM(e->u.rail.engclass, EC_STEAM, EC_MONORAIL)) { if (e->type != VEH_TRAIN || e->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_WAGON || !IsInsideMM(e->VehInfo<RailVehicleInfo>().engclass, EC_STEAM, EC_MONORAIL)) {
if (visual_effect == VE_DEFAULT) { if (visual_effect == VE_DEFAULT) {
visual_effect = 1 << VE_DISABLE_EFFECT; visual_effect = 1 << VE_DISABLE_EFFECT;
} else { } else {
@@ -2679,9 +2679,9 @@ void Vehicle::UpdateVisualEffect(bool allow_power_change)
} else { } else {
if (visual_effect == VE_DEFAULT) { if (visual_effect == VE_DEFAULT) {
/* Also set the offset */ /* Also set the offset */
visual_effect = (VE_OFFSET_CENTRE - (e->u.rail.engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START; visual_effect = (VE_OFFSET_CENTRE - (e->VehInfo<RailVehicleInfo>().engclass == EC_STEAM ? 4 : 0)) << VE_OFFSET_START;
} }
SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->u.rail.engclass - EC_STEAM + VE_TYPE_STEAM); SB(visual_effect, VE_TYPE_START, VE_TYPE_COUNT, e->VehInfo<RailVehicleInfo>().engclass - EC_STEAM + VE_TYPE_STEAM);
} }
} }
@@ -3063,7 +3063,7 @@ bool CanVehicleUseStation(EngineID engine_type, const Station *st)
case VEH_AIRCRAFT: case VEH_AIRCRAFT:
return st->facilities.Test(StationFacility::Airport) && return st->facilities.Test(StationFacility::Airport) &&
st->airport.GetFTA()->flags.Test(e->u.air.subtype & AIR_CTOL ? AirportFTAClass::Flag::Airplanes : AirportFTAClass::Flag::Helicopters); st->airport.GetFTA()->flags.Test(e->VehInfo<AircraftVehicleInfo>().subtype & AIR_CTOL ? AirportFTAClass::Flag::Airplanes : AirportFTAClass::Flag::Helicopters);
default: default:
return false; return false;
@@ -3124,7 +3124,7 @@ StringID GetVehicleCannotUseStationReason(const Vehicle *v, const Station *st)
case VEH_AIRCRAFT: case VEH_AIRCRAFT:
if (!st->facilities.Test(StationFacility::Airport)) return STR_ERROR_NO_AIRPORT; if (!st->facilities.Test(StationFacility::Airport)) return STR_ERROR_NO_AIRPORT;
if (v->GetEngine()->u.air.subtype & AIR_CTOL) { if (v->GetEngine()->VehInfo<AircraftVehicleInfo>().subtype & AIR_CTOL) {
return STR_ERROR_AIRPORT_NO_PLANES; return STR_ERROR_AIRPORT_NO_PLANES;
} else { } else {
return STR_ERROR_AIRPORT_NO_HELICOPTERS; return STR_ERROR_AIRPORT_NO_HELICOPTERS;

View File

@@ -64,26 +64,6 @@ struct NewGRFCache {
auto operator<=>(const NewGRFCache &) const = default; auto operator<=>(const NewGRFCache &) const = default;
}; };
/** Meaning of the various bits of the visual effect. */
enum VisualEffect : uint8_t {
VE_OFFSET_START = 0, ///< First bit that contains the offset (0 = front, 8 = centre, 15 = rear)
VE_OFFSET_COUNT = 4, ///< Number of bits used for the offset
VE_OFFSET_CENTRE = 8, ///< Value of offset corresponding to a position above the centre of the vehicle
VE_TYPE_START = 4, ///< First bit used for the type of effect
VE_TYPE_COUNT = 2, ///< Number of bits used for the effect type
VE_TYPE_DEFAULT = 0, ///< Use default from engine class
VE_TYPE_STEAM = 1, ///< Steam plumes
VE_TYPE_DIESEL = 2, ///< Diesel fumes
VE_TYPE_ELECTRIC = 3, ///< Electric sparks
VE_DISABLE_EFFECT = 6, ///< Flag to disable visual effect
VE_ADVANCED_EFFECT = VE_DISABLE_EFFECT, ///< Flag for advanced effects
VE_DISABLE_WAGON_POWER = 7, ///< Flag to disable wagon power
VE_DEFAULT = 0xFF, ///< Default value to indicate that visual effect should be based on engine class
};
/** Models for spawning visual effects. */ /** Models for spawning visual effects. */
enum VisualEffectSpawnModel : uint8_t { enum VisualEffectSpawnModel : uint8_t {
VESM_NONE = 0, ///< No visual effect VESM_NONE = 0, ///< No visual effect

View File

@@ -122,10 +122,10 @@ std::tuple<CommandCost, VehicleID, uint, uint16_t, CargoArray> CmdBuildVehicle(D
/* Check whether the number of vehicles we need to build can be built according to pool space. */ /* Check whether the number of vehicles we need to build can be built according to pool space. */
uint num_vehicles; uint num_vehicles;
switch (type) { switch (type) {
case VEH_TRAIN: num_vehicles = (e->u.rail.railveh_type == RAILVEH_MULTIHEAD ? 2 : 1) + CountArticulatedParts(eid, false); break; case VEH_TRAIN: num_vehicles = (e->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_MULTIHEAD ? 2 : 1) + CountArticulatedParts(eid, false); break;
case VEH_ROAD: num_vehicles = 1 + CountArticulatedParts(eid, false); break; case VEH_ROAD: num_vehicles = 1 + CountArticulatedParts(eid, false); break;
case VEH_SHIP: num_vehicles = 1; break; case VEH_SHIP: num_vehicles = 1; break;
case VEH_AIRCRAFT: num_vehicles = e->u.air.subtype & AIR_CTOL ? 2 : 3; break; case VEH_AIRCRAFT: num_vehicles = e->VehInfo<AircraftVehicleInfo>().subtype & AIR_CTOL ? 2 : 3; break;
default: NOT_REACHED(); // Safe due to IsDepotTile() default: NOT_REACHED(); // Safe due to IsDepotTile()
} }
if (!Vehicle::CanAllocateItem(num_vehicles)) return { CommandCost(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME), VehicleID::Invalid(), 0, 0, {} }; if (!Vehicle::CanAllocateItem(num_vehicles)) return { CommandCost(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME), VehicleID::Invalid(), 0, 0, {} };
@@ -133,7 +133,7 @@ std::tuple<CommandCost, VehicleID, uint, uint16_t, CargoArray> CmdBuildVehicle(D
/* Check whether we can allocate a unit number. Autoreplace does not allocate /* Check whether we can allocate a unit number. Autoreplace does not allocate
* an unit number as it will (always) reuse the one of the replaced vehicle * an unit number as it will (always) reuse the one of the replaced vehicle
* and (train) wagons don't have an unit number in any scenario. */ * and (train) wagons don't have an unit number in any scenario. */
UnitID unit_num = (flags.Test(DoCommandFlag::QueryCost) || flags.Test(DoCommandFlag::AutoReplace) || (type == VEH_TRAIN && e->u.rail.railveh_type == RAILVEH_WAGON)) ? 0 : GetFreeUnitNumber(type); UnitID unit_num = (flags.Test(DoCommandFlag::QueryCost) || flags.Test(DoCommandFlag::AutoReplace) || (type == VEH_TRAIN && e->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_WAGON)) ? 0 : GetFreeUnitNumber(type);
if (unit_num == UINT16_MAX) return { CommandCost(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME), VehicleID::Invalid(), 0, 0, {} }; if (unit_num == UINT16_MAX) return { CommandCost(STR_ERROR_TOO_MANY_VEHICLES_IN_GAME), VehicleID::Invalid(), 0, 0, {} };
/* If we are refitting we need to temporarily purchase the vehicle to be able to /* If we are refitting we need to temporarily purchase the vehicle to be able to
@@ -325,7 +325,7 @@ static CommandCost GetRefitCost(const Vehicle *v, EngineID engine_type, CargoTyp
break; break;
case VEH_TRAIN: case VEH_TRAIN:
base_price = (e->u.rail.railveh_type == RAILVEH_WAGON) ? PR_BUILD_VEHICLE_WAGON : PR_BUILD_VEHICLE_TRAIN; base_price = (e->VehInfo<RailVehicleInfo>().railveh_type == RAILVEH_WAGON) ? PR_BUILD_VEHICLE_WAGON : PR_BUILD_VEHICLE_TRAIN;
cost_factor <<= 1; cost_factor <<= 1;
expense_type = EXPENSES_TRAIN_RUN; expense_type = EXPENSES_TRAIN_RUN;
break; break;