From 42c9f84d74d198529fccb98d65270b12be7d0253 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Wed, 24 Sep 2025 22:44:41 +0100 Subject: [PATCH] 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. --- src/aircraft_cmd.cpp | 6 +- src/articulated_vehicles.cpp | 8 +- src/autoreplace_cmd.cpp | 8 +- src/autoreplace_gui.cpp | 2 +- src/build_vehicle_gui.cpp | 18 ++-- src/depot_gui.cpp | 2 +- src/elrail.cpp | 2 +- src/engine.cpp | 135 ++++++++++++++-------------- src/engine_base.h | 35 +++++--- src/engine_gui.cpp | 12 +-- src/engine_type.h | 34 +++++-- src/newgrf.cpp | 32 +++---- src/newgrf/newgrf_act0_aircraft.cpp | 2 +- src/newgrf/newgrf_act0_roadvehs.cpp | 2 +- src/newgrf/newgrf_act0_ships.cpp | 2 +- src/newgrf/newgrf_act0_trains.cpp | 2 +- src/newgrf_engine.cpp | 2 +- src/rail.cpp | 4 +- src/road.cpp | 4 +- src/roadveh_cmd.cpp | 14 +-- src/saveload/vehicle_sl.cpp | 2 +- src/ship_cmd.cpp | 6 +- src/train_cmd.cpp | 18 ++-- src/vehicle.cpp | 26 +++--- src/vehicle_base.h | 20 ----- src/vehicle_cmd.cpp | 8 +- 26 files changed, 208 insertions(+), 198 deletions(-) diff --git a/src/aircraft_cmd.cpp b/src/aircraft_cmd.cpp index 7a1b8b7a8c..91f0f7968f 100644 --- a/src/aircraft_cmd.cpp +++ b/src/aircraft_cmd.cpp @@ -200,7 +200,7 @@ void GetRotorImage(const Aircraft *v, EngineImageType image_type, VehicleSpriteS static void GetAircraftIcon(EngineID engine, EngineImageType image_type, VehicleSpriteSeq *result) { const Engine *e = Engine::Get(engine); - uint8_t spritenum = e->u.air.image_index; + uint8_t spritenum = e->VehInfo().image_index; if (IsCustomVehicleSpriteNum(spritenum)) { 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) { - const AircraftVehicleInfo *avi = &e->u.air; + const AircraftVehicleInfo *avi = &e->VehInfo(); const Station *st = Station::GetByTile(tile); /* Prevent building aircraft types at places which can't handle them */ @@ -438,7 +438,7 @@ static void CheckIfAircraftNeedsService(Aircraft *v) Money Aircraft::GetRunningCost() const { 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().running_cost); return GetPrice(PR_RUNNING_AIRCRAFT, cost_factor, e->GetGRF()); } diff --git a/src/articulated_vehicles.cpp b/src/articulated_vehicles.cpp index 9ffc0a6e8f..08c3c4416c 100644 --- a/src/articulated_vehicles.cpp +++ b/src/articulated_vehicles.cpp @@ -365,10 +365,10 @@ void AddArticulatedParts(Vehicle *first) t->track = front->track; t->railtypes = front->railtypes; - t->spritenum = e_artic->u.rail.image_index; + t->spritenum = e_artic->VehInfo().image_index; if (e_artic->CanCarryCargo()) { 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().capacity; // Callback 36 is called when the consist is finished } else { t->cargo_type = front->cargo_type; // Needed for livery selection t->cargo_cap = 0; @@ -392,11 +392,11 @@ void AddArticulatedParts(Vehicle *first) rv->roadtype = front->roadtype; rv->compatible_roadtypes = front->compatible_roadtypes; - rv->spritenum = e_artic->u.road.image_index; + rv->spritenum = e_artic->VehInfo().image_index; if (e_artic->CanCarryCargo()) { rv->cargo_type = e_artic->GetDefaultCargoType(); 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().capacity; // Callback 36 is called when the consist is finished } else { rv->cargo_type = front->cargo_type; // Needed for livery selection rv->cargo_cap = 0; diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index 0798c679b3..454450c642 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -71,16 +71,16 @@ bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company) switch (type) { case VEH_TRAIN: { /* 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().railtypes).Any(GetAllCompatibleRailTypes(e_to->VehInfo().railtypes))) return false; /* 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().railveh_type == RAILVEH_WAGON) != (e_to->VehInfo().railveh_type == RAILVEH_WAGON)) return false; break; } case VEH_ROAD: /* 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().roadtype)->powered_roadtypes.Any(GetRoadTypeInfo(e_to->VehInfo().roadtype)->powered_roadtypes)) return false; /* 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; @@ -88,7 +88,7 @@ bool CheckAutoreplaceValidity(EngineID from, EngineID to, CompanyID company) case VEH_AIRCRAFT: /* 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().subtype & AIR_CTOL) != (e_to->VehInfo().subtype & AIR_CTOL)) return false; break; default: break; diff --git a/src/autoreplace_gui.cpp b/src/autoreplace_gui.cpp index 081a8a8ace..67f9707769 100644 --- a/src/autoreplace_gui.cpp +++ b/src/autoreplace_gui.cpp @@ -140,7 +140,7 @@ class ReplaceVehicleWindow : public Window { case VEH_ROAD: if (draw_left && this->sel_roadtype != INVALID_ROADTYPE) { /* Ensure that the roadtype is specific to the selected one */ - if (e->u.road.roadtype != this->sel_roadtype) continue; + if (e->VehInfo().roadtype != this->sel_roadtype) continue; } break; diff --git a/src/build_vehicle_gui.cpp b/src/build_vehicle_gui.cpp index a40ad4ef35..0702fdeee5 100644 --- a/src/build_vehicle_gui.cpp +++ b/src/build_vehicle_gui.cpp @@ -726,8 +726,8 @@ static int DrawShipPurchaseInfo(int left, int right, int y, EngineID engine_numb /* Purchase cost - Max speed */ uint raw_speed = e->GetDisplayMaxSpeed(); - uint ocean_speed = e->u.ship.ApplyWaterClassSpeedFrac(raw_speed, true); - uint canal_speed = e->u.ship.ApplyWaterClassSpeedFrac(raw_speed, false); + uint ocean_speed = e->VehInfo().ApplyWaterClassSpeedFrac(raw_speed, true); + uint canal_speed = e->VehInfo().ApplyWaterClassSpeedFrac(raw_speed, false); if (ocean_speed == canal_speed) { if (te.cost != 0) { @@ -885,10 +885,10 @@ int DrawVehiclePurchaseInfo(int left, int right, int y, EngineID engine_number, switch (e->type) { default: NOT_REACHED(); case VEH_TRAIN: - if (e->u.rail.railveh_type == RAILVEH_WAGON) { - y = DrawRailWagonPurchaseInfo(left, right, y, engine_number, &e->u.rail, te); + if (e->VehInfo().railveh_type == RAILVEH_WAGON) { + y = DrawRailWagonPurchaseInfo(left, right, y, engine_number, &e->VehInfo(), te); } else { - y = DrawRailEnginePurchaseInfo(left, right, y, engine_number, &e->u.rail, te); + y = DrawRailEnginePurchaseInfo(left, right, y, engine_number, &e->VehInfo(), te); } articulated_cargo = true; 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. */ - if (e->type != VEH_TRAIN || e->u.rail.railveh_type != RAILVEH_WAGON) { + if (e->type != VEH_TRAIN || e->VehInfo().railveh_type != RAILVEH_WAGON) { /* Design date - Life length */ DrawString(left, right, y, GetString(STR_PURCHASE_INFO_DESIGNED_LIFE, ymd.year, TimerGameCalendar::DateToYear(e->GetLifeLengthInDays()))); y += GetCharacterHeight(FS_NORMAL); @@ -1395,7 +1395,7 @@ struct BuildVehicleWindow : Window { for (const Engine *e : Engine::IterateType(VEH_TRAIN)) { if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue; EngineID eid = e->index; - const RailVehicleInfo *rvi = &e->u.rail; + const RailVehicleInfo *rvi = &e->VehInfo(); if (this->filter.railtype != INVALID_RAILTYPE && !HasPowerOnRail(rvi->railtypes, this->filter.railtype)) 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()) { const Engine *e = Engine::Get(variant); 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().railveh_type != RAILVEH_WAGON) num_engines++; } } @@ -1461,7 +1461,7 @@ struct BuildVehicleWindow : Window { if (!this->show_hidden_engines && e->IsVariantHidden(_local_company)) continue; EngineID eid = e->index; 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().roadtype, this->filter.roadtype)) continue; if (!bdf.Filter(e->badges)) continue; /* Filter by name or NewGRF extra text */ diff --git a/src/depot_gui.cpp b/src/depot_gui.cpp index 8306ec8b30..18856c2a95 100644 --- a/src/depot_gui.cpp +++ b/src/depot_gui.cpp @@ -229,7 +229,7 @@ void InitDepotWindowBlockSizes() if (!e->IsEnabled()) continue; uint w = TRAININFO_DEFAULT_VEHICLE_WIDTH; - if (e->GetGRF() != nullptr && IsCustomVehicleSpriteNum(e->u.rail.image_index)) { + if (e->GetGRF() != nullptr && IsCustomVehicleSpriteNum(e->VehInfo().image_index)) { w = e->GetGRF()->traininfo_vehicle_width; if (w != VEHICLEINFO_FULL_VEHICLE_WIDTH) { /* Hopeless. diff --git a/src/elrail.cpp b/src/elrail.cpp index 720da0f26b..14b145713c 100644 --- a/src/elrail.cpp +++ b/src/elrail.cpp @@ -582,7 +582,7 @@ void UpdateDisableElrailSettingState(bool disable, bool update_vehicles) { /* walk through all train engines */ for (Engine *e : Engine::IterateType(VEH_TRAIN)) { - RailVehicleInfo *rv_info = &e->u.rail; + RailVehicleInfo *rv_info = &e->VehInfo(); /* update railtype of engines intended to use elrail */ if (rv_info->intended_railtypes.Test(RAILTYPE_ELECTRIC)) { rv_info->railtypes.Set(RAILTYPE_ELECTRIC, !disable); diff --git a/src/engine.cpp b/src/engine.cpp index 68b9e1b0d1..15f0cfa5e9 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -78,24 +78,19 @@ Engine::Engine(VehicleType type, uint16_t local_id) /* Check if this base engine is within the original engine data range */ if (local_id >= _engine_counts[type]) { - /* 'power' defaults to zero, so we also have to default to 'wagon' */ - if (type == VEH_TRAIN) this->u.rail.railveh_type = RAILVEH_WAGON; + /* Initialise default type-specific information. */ + switch (type) { + case VEH_TRAIN: this->vehicle_info.emplace(); break; + case VEH_ROAD: this->vehicle_info.emplace(); break; + case VEH_SHIP: this->vehicle_info.emplace(); break; + case VEH_AIRCRAFT: this->vehicle_info.emplace(); break; + default: break; + } /* Set model life to maximum to make wagons available */ 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 */ this->info.cargo_type = INVALID_CARGO; 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. */ this->info.cargo_age_period = Ticks::CARGO_AGING_TICKS; /* Not a variant */ @@ -110,33 +105,37 @@ Engine::Engine(VehicleType type, uint16_t local_id) switch (type) { default: NOT_REACHED(); - case VEH_TRAIN: - this->u.rail = _orig_rail_vehicle_info[local_id]; - this->original_image_index = this->u.rail.image_index; + case VEH_TRAIN: { + RailVehicleInfo &rvi = this->vehicle_info.emplace(_orig_rail_vehicle_info[local_id]); + this->original_image_index = rvi.image_index; 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" */ - 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; + } - case VEH_ROAD: - this->u.road = _orig_road_vehicle_info[local_id]; - this->original_image_index = this->u.road.image_index; + case VEH_ROAD: { + RoadVehicleInfo &rvi = this->vehicle_info.emplace(_orig_road_vehicle_info[local_id]); + this->original_image_index = rvi.image_index; this->info.string_id = STR_VEHICLE_NAME_ROAD_VEHICLE_MPS_REGAL_BUS + local_id; break; + } - case VEH_SHIP: - this->u.ship = _orig_ship_vehicle_info[local_id]; - this->original_image_index = this->u.ship.image_index; + case VEH_SHIP: { + ShipVehicleInfo &svi = this->vehicle_info.emplace(_orig_ship_vehicle_info[local_id]); + this->original_image_index = svi.image_index; this->info.string_id = STR_VEHICLE_NAME_SHIP_MPS_OIL_TANKER + local_id; break; + } - case VEH_AIRCRAFT: - this->u.air = _orig_aircraft_vehicle_info[local_id]; - this->original_image_index = this->u.air.image_index; + case VEH_AIRCRAFT: { + AircraftVehicleInfo &avi = this->vehicle_info.emplace(_orig_aircraft_vehicle_info[local_id]); + this->original_image_index = avi.image_index; this->info.string_id = STR_VEHICLE_NAME_AIRCRAFT_SAMPSON_U52 + local_id; break; + } } } @@ -174,11 +173,11 @@ bool Engine::CanCarryCargo() const */ switch (this->type) { case VEH_TRAIN: - if (this->u.rail.capacity == 0) return false; + if (this->VehInfo().capacity == 0) return false; break; case VEH_ROAD: - if (this->u.road.capacity == 0) return false; + if (this->VehInfo().capacity == 0) return false; break; 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; 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().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. */ @@ -225,24 +224,24 @@ uint Engine::DetermineCapacity(const Vehicle *v, uint16_t *mail_capacity) const uint extra_mail_cap = 0; switch (this->type) { 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().capacity, v); /* 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().railveh_type == RAILVEH_MULTIHEAD) capacity += this->VehInfo().capacity; break; 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().capacity, v); break; 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().capacity, v); break; 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().passenger_capacity, v); 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().mail_capacity, v); } if (IsValidCargoType(GetCargoTypeByLabel(CT_MAIL))) { if (!new_multipliers && cargo_type == GetCargoTypeByLabel(CT_MAIL)) return capacity + extra_mail_cap; @@ -284,25 +283,25 @@ Money Engine::GetRunningCost() const uint cost_factor; switch (this->type) { case VEH_ROAD: - base_price = this->u.road.running_cost_class; + base_price = this->VehInfo().running_cost_class; 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().running_cost); break; case VEH_TRAIN: - base_price = this->u.rail.running_cost_class; + base_price = this->VehInfo().running_cost_class; 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().running_cost); break; case VEH_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().running_cost); break; case VEH_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().running_cost); break; default: NOT_REACHED(); @@ -322,27 +321,27 @@ Money Engine::GetCost() const switch (this->type) { case VEH_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().cost_factor); break; case VEH_TRAIN: - if (this->u.rail.railveh_type == RAILVEH_WAGON) { + if (this->VehInfo().railveh_type == RAILVEH_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().cost_factor); } else { 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().cost_factor); } break; case VEH_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().cost_factor); break; case VEH_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().cost_factor); break; default: NOT_REACHED(); @@ -359,22 +358,22 @@ uint Engine::GetDisplayMaxSpeed() const { switch (this->type) { case VEH_TRAIN: - return GetEngineProperty(this->index, PROP_TRAIN_SPEED, this->u.rail.max_speed); + return GetEngineProperty(this->index, PROP_TRAIN_SPEED, this->VehInfo().max_speed); case VEH_ROAD: { 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().max_speed / 2; } 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().max_speed) / 2; case VEH_AIRCRAFT: { uint max_speed = GetEngineProperty(this->index, PROP_AIRCRAFT_SPEED, 0); if (max_speed != 0) { return (max_speed * 128) / 10; } - return this->u.air.max_speed; + return this->VehInfo().max_speed; } default: NOT_REACHED(); @@ -392,9 +391,9 @@ uint Engine::GetPower() const /* Only trains and road vehicles have 'power'. */ switch (this->type) { case VEH_TRAIN: - return GetEngineProperty(this->index, PROP_TRAIN_POWER, this->u.rail.power); + return GetEngineProperty(this->index, PROP_TRAIN_POWER, this->VehInfo().power); case VEH_ROAD: - return GetEngineProperty(this->index, PROP_ROADVEH_POWER, this->u.road.power) * 10; + return GetEngineProperty(this->index, PROP_ROADVEH_POWER, this->VehInfo().power) * 10; default: NOT_REACHED(); } @@ -410,9 +409,9 @@ uint Engine::GetDisplayWeight() const /* Only trains and road vehicles have 'weight'. */ switch (this->type) { 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().weight) << (this->VehInfo().railveh_type == RAILVEH_MULTIHEAD ? 1 : 0); case VEH_ROAD: - return GetEngineProperty(this->index, PROP_ROADVEH_WEIGHT, this->u.road.weight) / 4; + return GetEngineProperty(this->index, PROP_ROADVEH_WEIGHT, this->VehInfo().weight) / 4; default: NOT_REACHED(); } @@ -428,9 +427,9 @@ uint Engine::GetDisplayMaxTractiveEffort() const /* Only trains and road vehicles have 'tractive effort'. */ switch (this->type) { 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().tractive_effort)) / 256; 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().tractive_effort)) / 256; default: NOT_REACHED(); } @@ -454,7 +453,7 @@ uint16_t Engine::GetRange() const { switch (this->type) { case VEH_AIRCRAFT: - return GetEngineProperty(this->index, PROP_AIRCRAFT_RANGE, this->u.air.max_range); + return GetEngineProperty(this->index, PROP_AIRCRAFT_RANGE, this->VehInfo().max_range); default: NOT_REACHED(); } @@ -468,7 +467,7 @@ StringID Engine::GetAircraftTypeText() const { switch (this->type) { case VEH_AIRCRAFT: - switch (this->u.air.subtype) { + switch (this->VehInfo().subtype) { case AIR_HELI: return STR_LIVERY_HELICOPTER; case AIR_CTOL: return STR_LIVERY_SMALL_PLANE; case AIR_CTOL | AIR_FAST: return STR_LIVERY_LARGE_PLANE; @@ -625,7 +624,7 @@ void ShowEnginePreviewWindow(EngineID engine); static bool IsWagon(EngineID 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().railveh_type == RAILVEH_WAGON; } /** @@ -700,7 +699,7 @@ void SetYearEngineAgingStops() /* Exclude certain engines */ 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().railveh_type == RAILVEH_WAGON) continue; /* 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); @@ -1107,13 +1106,13 @@ static void NewVehicleAvailable(Engine *e) if (e->type == VEH_TRAIN) { /* maybe make another rail type available */ - assert(e->u.rail.railtypes != RailTypes{}); - RailTypes introduced = GetAllIntroducesRailTypes(e->u.rail.railtypes); + assert(e->VehInfo().railtypes != RailTypes{}); + RailTypes introduced = GetAllIntroducesRailTypes(e->VehInfo().railtypes); for (Company *c : Company::Iterate()) c->avail_railtypes = AddDateIntroducedRailTypes(c->avail_railtypes | introduced, TimerGameCalendar::date); } else if (e->type == VEH_ROAD) { /* maybe make another road type available */ - assert(e->u.road.roadtype < ROADTYPE_END); - for (Company *c : Company::Iterate()) c->avail_roadtypes = AddDateIntroducedRoadTypes(c->avail_roadtypes | GetRoadTypeInfo(e->u.road.roadtype)->introduces_roadtypes, TimerGameCalendar::date); + assert(e->VehInfo().roadtype < ROADTYPE_END); + for (Company *c : Company::Iterate()) c->avail_roadtypes = AddDateIntroducedRoadTypes(c->avail_roadtypes | GetRoadTypeInfo(e->VehInfo().roadtype)->introduces_roadtypes, TimerGameCalendar::date); } /* 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) { /* Check if the rail type is available to this company */ const Company *c = Company::Get(company); - if (!GetAllCompatibleRailTypes(e->u.rail.railtypes).Any(c->avail_railtypes)) return false; + if (!GetAllCompatibleRailTypes(e->VehInfo().railtypes).Any(c->avail_railtypes)) return false; } if (type == VEH_ROAD && company != OWNER_DEITY) { /* Check if the road type is available to this 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().roadtype)->powered_roadtypes.Any(c->avail_roadtypes)) return false; } return true; @@ -1319,7 +1318,7 @@ void CheckEngines() if (!e->IsEnabled()) continue; /* 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().railveh_type == RAILVEH_WAGON) continue; /* We have an available engine... yay! */ if (e->flags.Test(EngineFlag::Available) && e->company_avail.Any()) return; diff --git a/src/engine_base.h b/src/engine_base.h index 82ea41cd8f..d8e6052524 100644 --- a/src/engine_base.h +++ b/src/engine_base.h @@ -35,7 +35,8 @@ using EngineDisplayFlags = EnumBitSet; typedef Pool EnginePool; 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_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. @@ -64,13 +65,6 @@ struct Engine : EnginePool::PoolItem<&_engine_pool> { EngineID display_last_variant = EngineID::Invalid(); ///< NOSAVE client-side-only last variant selected. EngineInfo info{}; - union { - RailVehicleInfo rail; - RoadVehicleInfo road; - ShipVehicleInfo ship; - AircraftVehicleInfo air; - } u{}; - uint16_t list_position = 0; /* NewGRF related data */ @@ -78,6 +72,11 @@ struct Engine : EnginePool::PoolItem<&_engine_pool> { std::vector overrides{}; std::vector badges{}; +private: + /* Vehicle-type specific information. */ + std::variant vehicle_info{}; + +public: Engine() {} Engine(VehicleType type, uint16_t local_id); 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; } }; + template + inline T &VehInfo() + { + return std::get(this->vehicle_info); + } + + template + inline const T &VehInfo() const + { + return std::get(this->vehicle_info); + } + /** * Returns an iterable ensemble of all valid engines of the given type * @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) { - return &Engine::Get(e)->u.rail; + return &Engine::Get(e)->VehInfo(); } inline const RoadVehicleInfo *RoadVehInfo(EngineID e) { - return &Engine::Get(e)->u.road; + return &Engine::Get(e)->VehInfo(); } inline const ShipVehicleInfo *ShipVehInfo(EngineID e) { - return &Engine::Get(e)->u.ship; + return &Engine::Get(e)->VehInfo(); } inline const AircraftVehicleInfo *AircraftVehInfo(EngineID e) { - return &Engine::Get(e)->u.air; + return &Engine::Get(e)->VehInfo(); } #endif /* ENGINE_BASE_H */ diff --git a/src/engine_gui.cpp b/src/engine_gui.cpp index 210cf3bdf1..19a4eff047 100644 --- a/src/engine_gui.cpp +++ b/src/engine_gui.cpp @@ -43,12 +43,12 @@ StringID GetEngineCategoryName(EngineID engine) switch (e->type) { default: NOT_REACHED(); case VEH_ROAD: - return GetRoadTypeInfo(e->u.road.roadtype)->strings.new_engine; + return GetRoadTypeInfo(e->VehInfo().roadtype)->strings.new_engine; case VEH_AIRCRAFT: return STR_ENGINE_PREVIEW_AIRCRAFT; case VEH_SHIP: return STR_ENGINE_PREVIEW_SHIP; case VEH_TRAIN: - assert(e->u.rail.railtypes.Any()); - return GetRailTypeInfo(e->u.rail.railtypes.GetNthSetBit(0).value())->strings.new_loco; + assert(e->VehInfo().railtypes.Any()); + return GetRailTypeInfo(e->VehInfo().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 << '\n'; - if (e.u.rail.railtypes.Count() > 1) { + if (e.VehInfo().railtypes.Count() > 1) { std::string railtypes{}; std::string_view list_separator = GetListSeparator(); for (const auto &rt : _sorted_railtypes) { - if (!e.u.rail.railtypes.Test(rt)) continue; + if (!e.VehInfo().railtypes.Test(rt)) continue; if (!railtypes.empty()) railtypes += list_separator; AppendStringInPlace(railtypes, GetRailTypeInfo(rt)->strings.name); @@ -197,7 +197,7 @@ static std::string GetTrainEngineInfoString(const Engine &e) } bool is_maglev = true; - for (RailType rt : e.u.rail.railtypes) { + for (RailType rt : e.VehInfo().railtypes) { is_maglev &= GetRailTypeInfo(rt)->acceleration_type == VehicleAccelerationModel::Maglev; } diff --git a/src/engine_type.h b/src/engine_type.h index 1fefbe1ba1..640a99b73f 100644 --- a/src/engine_type.h +++ b/src/engine_type.h @@ -25,7 +25,7 @@ /** Unique identification number of an engine. */ using EngineID = PoolID; -struct Engine; +class Engine; /** Available types of rail vehicles. */ enum RailVehicleTypes : uint8_t { @@ -50,10 +50,30 @@ enum class VehicleAccelerationModel : uint8_t { 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. */ struct RailVehicleInfo { 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. RailTypes railtypes{}; ///< Railtypes, mangled if elrail is 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. 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 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 tractive_effort = 0; ///< Tractive effort coefficient uint8_t air_drag = 0; ///< Coefficient of air drag @@ -80,12 +100,12 @@ struct ShipVehicleInfo { uint8_t image_index = 0; uint8_t cost_factor = 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 capacity = 0; SoundID sfx{}; 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 canal_speed_frac = 0; ///< Fraction of maximum speed for canal/river tiles. @@ -133,9 +153,9 @@ struct RoadVehicleInfo { uint8_t capacity = 0; uint8_t weight = 0; ///< Weight in 1/4t 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 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 RoadType roadtype{}; ///< Road type }; diff --git a/src/newgrf.cpp b/src/newgrf.cpp index b6444a8e06..26a6a66fea 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -272,7 +272,7 @@ Engine *GetNewEngine(const GRFFile *file, VehicleType type, uint16_t internal_id } if (type == VEH_TRAIN) { _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().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); @@ -427,7 +427,7 @@ void ResetNewGRFData() /* Fill rail type label temporary data for default trains */ for (const Engine *e : Engine::IterateType(VEH_TRAIN)) { _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().railtypes) _gted[e->index].railtypelabels.push_back(GetRailTypeInfo(rt)->label); } /* Reset GRM reservations */ @@ -661,7 +661,7 @@ static void CalculateRefitMasks() /* If the NewGRF did not set any cargo properties, we apply default values. */ if (_gted[engine].defaultcargo_grf == nullptr) { /* 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().capacity != 0) { static constexpr LandscapeType T = LandscapeType::Temperate; static constexpr LandscapeType A = LandscapeType::Arctic; static constexpr LandscapeType S = LandscapeType::Tropic; @@ -716,8 +716,8 @@ static void CalculateRefitMasks() } break; } - e->u.ship.old_refittable = true; - } else if (e->type == VEH_TRAIN && e->u.rail.railveh_type != RAILVEH_WAGON) { + e->VehInfo().old_refittable = true; + } else if (e->type == VEH_TRAIN && e->VehInfo().railveh_type != RAILVEH_WAGON) { /* 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. */ _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. * 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().old_refittable) && IsValidCargoType(ei->cargo_type) && !HasBit(ei->refit_mask, ei->cargo_type)) { ei->cargo_type = INVALID_CARGO; } @@ -819,7 +819,7 @@ static void CalculateRefitMasks() 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().railveh_type != RAILVEH_WAGON && e->VehInfo().capacity == 0) { /* 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 * cargo_label not being CT_INVALID). */ @@ -830,7 +830,7 @@ static void CalculateRefitMasks() if (!IsValidCargoType(ei->cargo_type)) ei->climates = {}; /* Clear refit_mask for not refittable ships */ - if (e->type == VEH_SHIP && !e->u.ship.old_refittable) { + if (e->type == VEH_SHIP && !e->VehInfo().old_refittable) { ei->refit_mask = 0; } } @@ -867,16 +867,16 @@ static void FinaliseEngineArray() switch (e->type) { case VEH_TRAIN: - for (RailType rt : e->u.rail.railtypes) { + for (RailType rt : e->VehInfo().railtypes) { AppendCopyableBadgeList(e->badges, GetRailTypeInfo(rt)->badges, GSF_TRAINS); } 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().roadtype)->badges, GSF_ROADVEHICLES); break; default: break; } /* 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().railveh_type != RAILVEH_WAGON) { LiveryScheme ls = GetEngineLiveryScheme(e->index, EngineID::Invalid(), nullptr); SetBit(_loaded_newgrf_features.used_liveries, ls); /* 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)) { if (_gted[e->index].rv_max_speed != 0) { /* 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().max_speed = _gted[e->index].rv_max_speed * 4; } RoadTramType rtt = e->info.misc_flags.Test(EngineMiscFlag::RoadIsTram) ? RTT_TRAM : RTT_ROAD; const GRFFile *file = e->GetGRF(); if (file == nullptr || _gted[e->index].roadtramtype == 0) { - e->u.road.roadtype = (rtt == RTT_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD; + e->VehInfo().roadtype = (rtt == RTT_TRAM) ? ROADTYPE_TRAM : ROADTYPE_ROAD; continue; } @@ -1713,7 +1713,7 @@ static void AfterLoadGRFs() RoadTypeLabel rtl = (*list)[_gted[e->index].roadtramtype]; RoadType rt = GetRoadTypeByLabel(rtl); if (rt != INVALID_ROADTYPE && GetRoadTramType(rt) == rtt) { - e->u.road.roadtype = rt; + e->VehInfo().roadtype = rt; continue; } } @@ -1730,8 +1730,8 @@ static void AfterLoadGRFs() } if (railtypes.Any()) { - e->u.rail.railtypes = railtypes; - e->u.rail.intended_railtypes = railtypes; + e->VehInfo().railtypes = railtypes; + e->VehInfo().intended_railtypes = railtypes; } else { /* Rail type is not available, so disable this engine */ e->info.climates = {}; diff --git a/src/newgrf/newgrf_act0_aircraft.cpp b/src/newgrf/newgrf_act0_aircraft.cpp index d246544784..d4e7df1298 100644 --- a/src/newgrf/newgrf_act0_aircraft.cpp +++ b/src/newgrf/newgrf_act0_aircraft.cpp @@ -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 EngineInfo *ei = &e->info; - AircraftVehicleInfo *avi = &e->u.air; + AircraftVehicleInfo *avi = &e->VehInfo(); switch (prop) { case 0x08: { // Sprite ID diff --git a/src/newgrf/newgrf_act0_roadvehs.cpp b/src/newgrf/newgrf_act0_roadvehs.cpp index e8cf67f6bb..34fd862c98 100644 --- a/src/newgrf/newgrf_act0_roadvehs.cpp +++ b/src/newgrf/newgrf_act0_roadvehs.cpp @@ -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 EngineInfo *ei = &e->info; - RoadVehicleInfo *rvi = &e->u.road; + RoadVehicleInfo *rvi = &e->VehInfo(); switch (prop) { case 0x05: // Road/tram type diff --git a/src/newgrf/newgrf_act0_ships.cpp b/src/newgrf/newgrf_act0_ships.cpp index 0e1bfe0b17..3b9c32b15c 100644 --- a/src/newgrf/newgrf_act0_ships.cpp +++ b/src/newgrf/newgrf_act0_ships.cpp @@ -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 EngineInfo *ei = &e->info; - ShipVehicleInfo *svi = &e->u.ship; + ShipVehicleInfo *svi = &e->VehInfo(); switch (prop) { case 0x08: { // Sprite ID diff --git a/src/newgrf/newgrf_act0_trains.cpp b/src/newgrf/newgrf_act0_trains.cpp index 8f8a4c4121..0e44e3a1b5 100644 --- a/src/newgrf/newgrf_act0_trains.cpp +++ b/src/newgrf/newgrf_act0_trains.cpp @@ -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 EngineInfo *ei = &e->info; - RailVehicleInfo *rvi = &e->u.rail; + RailVehicleInfo *rvi = &e->VehInfo(); switch (prop) { case 0x05: { // Track type diff --git a/src/newgrf_engine.cpp b/src/newgrf_engine.cpp index 5d1f7ba7fe..c1f355fe1c 100644 --- a/src/newgrf_engine.cpp +++ b/src/newgrf_engine.cpp @@ -1138,7 +1138,7 @@ static void GetRotorOverrideSprite(EngineID engine, const struct Aircraft *v, En /* Only valid for helicopters */ assert(e->type == VEH_AIRCRAFT); - assert(!(e->u.air.subtype & AIR_CTOL)); + assert(!(e->VehInfo().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()'. * TTDPatch copies some variables between the vehicles each time, to somehow synchronize the rotor vehicle with the primary vehicle. diff --git a/src/rail.cpp b/src/rail.cpp index 852a4dce6f..d9e41087e3 100644 --- a/src/rail.cpp +++ b/src/rail.cpp @@ -129,7 +129,7 @@ RailTypes GetCompanyRailTypes(CompanyID company, bool introduces) if (ei->climates.Test(_settings_game.game_creation.landscape) && (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(); if (rvi->railveh_type != RAILVEH_WAGON) { assert(rvi->railtypes.Any()); @@ -159,7 +159,7 @@ RailTypes GetRailTypes(bool introduces) const EngineInfo *ei = &e->info; if (!ei->climates.Test(_settings_game.game_creation.landscape)) continue; - const RailVehicleInfo *rvi = &e->u.rail; + const RailVehicleInfo *rvi = &e->VehInfo(); if (rvi->railveh_type != RAILVEH_WAGON) { assert(rvi->railtypes.Any()); if (introduces) { diff --git a/src/road.cpp b/src/road.cpp index 27a6a9bb78..5b796f12ea 100644 --- a/src/road.cpp +++ b/src/road.cpp @@ -204,7 +204,7 @@ RoadTypes GetCompanyRoadTypes(CompanyID company, bool introduces) if (ei->climates.Test(_settings_game.game_creation.landscape) && (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(); assert(rvi->roadtype < ROADTYPE_END); if (introduces) { rts.Set(GetRoadTypeInfo(rvi->roadtype)->introduces_roadtypes); @@ -231,7 +231,7 @@ RoadTypes GetRoadTypes(bool introduces) const EngineInfo *ei = &e->info; if (!ei->climates.Test(_settings_game.game_creation.landscape)) continue; - const RoadVehicleInfo *rvi = &e->u.road; + const RoadVehicleInfo *rvi = &e->VehInfo(); assert(rvi->roadtype < ROADTYPE_END); if (introduces) { rts.Set(GetRoadTypeInfo(rvi->roadtype)->introduces_roadtypes); diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 2117b85159..70717f15b8 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -105,7 +105,7 @@ int RoadVehicle::GetDisplayImageWidth(Point *offset) const static void GetRoadVehIcon(EngineID engine, EngineImageType image_type, VehicleSpriteSeq *result) { const Engine *e = Engine::Get(engine); - uint8_t spritenum = e->u.road.image_index; + uint8_t spritenum = e->VehInfo().image_index; if (IsCustomVehicleSpriteNum(spritenum)) { GetCustomVehicleIcon(engine, DIR_W, image_type, result); @@ -203,7 +203,7 @@ static uint GetRoadVehLength(const RoadVehicle *v) /* Use callback 11 */ 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().shorten_factor; if (veh_len != 0) { 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) { /* Check that the vehicle can drive on the road in question */ - RoadType rt = e->u.road.roadtype; + RoadType rt = e->VehInfo().roadtype; const RoadTypeInfo *rti = GetRoadTypeInfo(rt); if (!HasTileAnyRoadType(tile, rti->powered_roadtypes)) return CommandCost(STR_ERROR_DEPOT_WRONG_DEPOT_TYPE); if (flags.Test(DoCommandFlag::Execute)) { - const RoadVehicleInfo *rvi = &e->u.road; + const RoadVehicleInfo *rvi = &e->VehInfo(); RoadVehicle *v = new RoadVehicle(); *ret = v; @@ -1645,12 +1645,12 @@ static bool RoadVehController(RoadVehicle *v) Money RoadVehicle::GetRunningCost() const { const Engine *e = this->GetEngine(); - if (e->u.road.running_cost_class == INVALID_PRICE) return 0; + if (e->VehInfo().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().running_cost); if (cost_factor == 0) return 0; - return GetPrice(e->u.road.running_cost_class, cost_factor, e->GetGRF()); + return GetPrice(e->VehInfo().running_cost_class, cost_factor, e->GetGRF()); } bool RoadVehicle::Tick() diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp index 51ef51069d..3532c6ce87 100644 --- a/src/saveload/vehicle_sl.cpp +++ b/src/saveload/vehicle_sl.cpp @@ -439,7 +439,7 @@ void AfterLoadVehiclesPhase2(bool part_of_load) if (rv->IsFrontEngine()) { 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().roadtype; rv->compatible_roadtypes = GetRoadTypeInfo(rv->roadtype)->powered_roadtypes; RoadTramType rtt = GetRoadTramType(rv->roadtype); for (RoadVehicle *u = rv; u != nullptr; u = u->Next()) { diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index 434f852763..2591099c95 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -81,7 +81,7 @@ static inline TrackBits GetTileShipTrackStatus(TileIndex tile) static void GetShipIcon(EngineID engine, EngineImageType image_type, VehicleSpriteSeq *result) { const Engine *e = Engine::Get(engine); - uint8_t spritenum = e->u.ship.image_index; + uint8_t spritenum = e->VehInfo().image_index; if (IsCustomVehicleSpriteNum(spritenum)) { GetCustomVehicleIcon(engine, DIR_W, image_type, result); @@ -247,7 +247,7 @@ void Ship::UpdateCache() Money Ship::GetRunningCost() const { 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().running_cost); 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 y; - const ShipVehicleInfo *svi = &e->u.ship; + const ShipVehicleInfo *svi = &e->VehInfo(); Ship *v = new Ship(); *ret = v; diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 93a5483cfc..136f321c71 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -146,7 +146,7 @@ void Train::ConsistChanged(ConsistChangeFlags allowed_changes) for (Train *u = this; u != nullptr; u = u->Next()) { const Engine *e_u = u->GetEngine(); - const RailVehicleInfo *rvi_u = &e_u->u.rail; + const RailVehicleInfo *rvi_u = &e_u->VehInfo(); 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()); @@ -441,7 +441,7 @@ int Train::GetCursorImageOffset() const int reference_width = TRAININFO_DEFAULT_VEHICLE_WIDTH; const Engine *e = this->GetEngine(); - if (e->GetGRF() != nullptr && IsCustomVehicleSpriteNum(e->u.rail.image_index)) { + if (e->GetGRF() != nullptr && IsCustomVehicleSpriteNum(e->VehInfo().image_index)) { reference_width = e->GetGRF()->traininfo_vehicle_width; } @@ -461,7 +461,7 @@ int Train::GetDisplayImageWidth(Point *offset) const int vehicle_pitch = 0; const Engine *e = this->GetEngine(); - if (e->GetGRF() != nullptr && IsCustomVehicleSpriteNum(e->u.rail.image_index)) { + if (e->GetGRF() != nullptr && IsCustomVehicleSpriteNum(e->VehInfo().image_index)) { reference_width = e->GetGRF()->traininfo_vehicle_width; 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); Direction dir = rear_head ? DIR_E : DIR_W; - uint8_t spritenum = e->u.rail.image_index; + uint8_t spritenum = e->VehInfo().image_index; if (IsCustomVehicleSpriteNum(spritenum)) { GetCustomVehicleIcon(engine, dir, image_type, result); @@ -636,7 +636,7 @@ static std::vector GetFreeWagonsInDepot(TileIndex tile) */ static CommandCost CmdBuildRailWagon(DoCommandFlags flags, TileIndex tile, const Engine *e, Vehicle **ret) { - const RailVehicleInfo *rvi = &e->u.rail; + const RailVehicleInfo *rvi = &e->VehInfo(); /* Check that the wagon can drive on the track in question */ 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) { - const RailVehicleInfo *rvi = &e->u.rail; + const RailVehicleInfo *rvi = &e->VehInfo(); if (rvi->railveh_type == RAILVEH_WAGON) return CmdBuildRailWagon(flags, tile, e, ret); @@ -4074,15 +4074,15 @@ Money Train::GetRunningCost() const do { const Engine *e = v->GetEngine(); - if (e->u.rail.running_cost_class == INVALID_PRICE) continue; + if (e->VehInfo().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().running_cost); if (cost_factor == 0) continue; /* Halve running cost for multiheaded parts */ if (v->IsMultiheaded()) cost_factor /= 2; - cost += GetPrice(e->u.rail.running_cost_class, cost_factor, e->GetGRF()); + cost += GetPrice(e->VehInfo().running_cost_class, cost_factor, e->GetGRF()); } while ((v = v->GetNextVehicle()) != nullptr); return cost; diff --git a/src/vehicle.cpp b/src/vehicle.cpp index e6573b3726..bb1973e56e 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1925,7 +1925,7 @@ bool CanBuildVehicleInfrastructure(VehicleType type, uint8_t subtype) if (max > 0) { /* Can we actually build the vehicle 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().roadtype) != (RoadTramType)subtype) continue; if (e->company_avail.Test(_local_company)) return true; } return false; @@ -1955,7 +1955,7 @@ LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_ switch (e->type) { default: NOT_REACHED(); 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().railveh_type != RAILVEH_WAGON))) { /* Wagonoverrides use the colour scheme of the front engine. * Articulated parts use the colour scheme of the first part. (Not supported for articulated wagons) */ 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 = GetCargoTypeByLabel(CT_GOODS); // The vehicle does not carry anything, let's pick some freight cargo assert(IsValidCargoType(cargo_type)); - if (e->u.rail.railveh_type == RAILVEH_WAGON) { + if (e->VehInfo().railveh_type == RAILVEH_WAGON) { if (!CargoSpec::Get(cargo_type)->is_freight) { if (parent_engine_type == EngineID::Invalid()) { return LS_PASSENGER_WAGON_STEAM; @@ -1987,7 +1987,7 @@ LiveryScheme GetEngineLiveryScheme(EngineID engine_type, EngineID parent_engine_ } else { bool is_mu = e->info.misc_flags.Test(EngineMiscFlag::RailIsMU); - switch (e->u.rail.engclass) { + switch (e->VehInfo().engclass) { default: NOT_REACHED(); case EC_STEAM: return LS_STEAM; 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; case VEH_AIRCRAFT: - switch (e->u.air.subtype) { + switch (e->VehInfo().subtype) { case AIR_HELI: return LS_HELICOPTER; case AIR_CTOL: return LS_SMALL_PLANE; case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE; @@ -2641,9 +2641,9 @@ void Vehicle::UpdateVisualEffect(bool allow_power_change) /* Evaluate properties */ uint8_t visual_effect; switch (e->type) { - case VEH_TRAIN: visual_effect = e->u.rail.visual_effect; break; - case VEH_ROAD: visual_effect = e->u.road.visual_effect; break; - case VEH_SHIP: visual_effect = e->u.ship.visual_effect; break; + case VEH_TRAIN: visual_effect = e->VehInfo().visual_effect; break; + case VEH_ROAD: visual_effect = e->VehInfo().visual_effect; break; + case VEH_SHIP: visual_effect = e->VehInfo().visual_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)) { /* Only train engines have default effects. * 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().railveh_type == RAILVEH_WAGON || !IsInsideMM(e->VehInfo().engclass, EC_STEAM, EC_MONORAIL)) { if (visual_effect == VE_DEFAULT) { visual_effect = 1 << VE_DISABLE_EFFECT; } else { @@ -2679,9 +2679,9 @@ void Vehicle::UpdateVisualEffect(bool allow_power_change) } else { if (visual_effect == VE_DEFAULT) { /* 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().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().engclass - EC_STEAM + VE_TYPE_STEAM); } } @@ -3063,7 +3063,7 @@ bool CanVehicleUseStation(EngineID engine_type, const Station *st) case VEH_AIRCRAFT: 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().subtype & AIR_CTOL ? AirportFTAClass::Flag::Airplanes : AirportFTAClass::Flag::Helicopters); default: return false; @@ -3124,7 +3124,7 @@ StringID GetVehicleCannotUseStationReason(const Vehicle *v, const Station *st) case VEH_AIRCRAFT: if (!st->facilities.Test(StationFacility::Airport)) return STR_ERROR_NO_AIRPORT; - if (v->GetEngine()->u.air.subtype & AIR_CTOL) { + if (v->GetEngine()->VehInfo().subtype & AIR_CTOL) { return STR_ERROR_AIRPORT_NO_PLANES; } else { return STR_ERROR_AIRPORT_NO_HELICOPTERS; diff --git a/src/vehicle_base.h b/src/vehicle_base.h index 8fe5ec0f6d..aef3b4f246 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -64,26 +64,6 @@ struct NewGRFCache { 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. */ enum VisualEffectSpawnModel : uint8_t { VESM_NONE = 0, ///< No visual effect diff --git a/src/vehicle_cmd.cpp b/src/vehicle_cmd.cpp index c27fb92ded..c5a93b0f37 100644 --- a/src/vehicle_cmd.cpp +++ b/src/vehicle_cmd.cpp @@ -122,10 +122,10 @@ std::tuple CmdBuildVehicle(D /* Check whether the number of vehicles we need to build can be built according to pool space. */ uint num_vehicles; 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().railveh_type == RAILVEH_MULTIHEAD ? 2 : 1) + CountArticulatedParts(eid, false); break; case VEH_ROAD: num_vehicles = 1 + CountArticulatedParts(eid, false); 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().subtype & AIR_CTOL ? 2 : 3; break; 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, {} }; @@ -133,7 +133,7 @@ std::tuple CmdBuildVehicle(D /* 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 * 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().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 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; case VEH_TRAIN: - base_price = (e->u.rail.railveh_type == RAILVEH_WAGON) ? PR_BUILD_VEHICLE_WAGON : PR_BUILD_VEHICLE_TRAIN; + base_price = (e->VehInfo().railveh_type == RAILVEH_WAGON) ? PR_BUILD_VEHICLE_WAGON : PR_BUILD_VEHICLE_TRAIN; cost_factor <<= 1; expense_type = EXPENSES_TRAIN_RUN; break;