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 22d9c0b2d3
commit e2e01eabb5
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)
{
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)) {
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<AircraftVehicleInfo>();
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<AircraftVehicleInfo>().running_cost);
return GetPrice(PR_RUNNING_AIRCRAFT, cost_factor, e->GetGRF());
}

View File

@@ -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<RailVehicleInfo>().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<RailVehicleInfo>().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<RoadVehicleInfo>().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<RoadVehicleInfo>().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;

View File

@@ -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<RailVehicleInfo>().railtypes).Any(GetAllCompatibleRailTypes(e_to->VehInfo<RailVehicleInfo>().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<RailVehicleInfo>().railveh_type == RAILVEH_WAGON) != (e_to->VehInfo<RailVehicleInfo>().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<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 */
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<AircraftVehicleInfo>().subtype & AIR_CTOL) != (e_to->VehInfo<AircraftVehicleInfo>().subtype & AIR_CTOL)) return false;
break;
default: break;

View File

@@ -145,7 +145,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<RoadVehicleInfo>().roadtype != this->sel_roadtype) continue;
}
break;

View File

@@ -729,8 +729,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<ShipVehicleInfo>().ApplyWaterClassSpeedFrac(raw_speed, true);
uint canal_speed = e->VehInfo<ShipVehicleInfo>().ApplyWaterClassSpeedFrac(raw_speed, false);
if (ocean_speed == canal_speed) {
if (te.cost != 0) {
@@ -893,10 +893,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<RailVehicleInfo>().railveh_type == RAILVEH_WAGON) {
y = DrawRailWagonPurchaseInfo(left, right, y, engine_number, &e->VehInfo<RailVehicleInfo>(), te);
} 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;
break;
@@ -928,7 +928,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<RailVehicleInfo>().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);
@@ -1413,7 +1413,7 @@ struct BuildVehicleWindow : Window {
for (const Engine *e : Engine::IterateType(VEH_TRAIN)) {
// CM for num_hidden / 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<RailVehicleInfo>();
if (this->filter.railtype != INVALID_RAILTYPE && !HasPowerOnRail(rvi->railtypes, this->filter.railtype)) continue;
if (!IsEngineBuildable(eid, VEH_TRAIN, _local_company)) continue;
@@ -1452,7 +1452,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<RailVehicleInfo>().railveh_type != RAILVEH_WAGON) num_engines++;
}
}
@@ -1490,7 +1490,7 @@ struct BuildVehicleWindow : Window {
// CM num_hidden / 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<RoadVehicleInfo>().roadtype, this->filter.roadtype)) continue;
if (!bdf.Filter(e->badges)) continue;
/* Filter by name or NewGRF extra text */

View File

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

View File

@@ -209,7 +209,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<RailVehicleInfo>().image_index)) {
w = e->GetGRF()->traininfo_vehicle_width;
if (w != VEHICLEINFO_FULL_VEHICLE_WIDTH) {
/* Hopeless.

View File

@@ -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<RailVehicleInfo>();
/* update railtype of engines intended to use elrail */
if (rv_info->intended_railtypes.Test(RAILTYPE_ELECTRIC)) {
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 */
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<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 */
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<RailVehicleInfo>(_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<RoadVehicleInfo>(_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<ShipVehicleInfo>(_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<AircraftVehicleInfo>(_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<RailVehicleInfo>().capacity == 0) return false;
break;
case VEH_ROAD:
if (this->u.road.capacity == 0) return false;
if (this->VehInfo<RoadVehicleInfo>().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<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. */
@@ -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<RailVehicleInfo>().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<RailVehicleInfo>().railveh_type == RAILVEH_MULTIHEAD) capacity += this->VehInfo<RailVehicleInfo>().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<RoadVehicleInfo>().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<ShipVehicleInfo>().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<AircraftVehicleInfo>().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<AircraftVehicleInfo>().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<RoadVehicleInfo>().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<RoadVehicleInfo>().running_cost);
break;
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;
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;
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<ShipVehicleInfo>().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<AircraftVehicleInfo>().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<RoadVehicleInfo>().cost_factor);
break;
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;
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 {
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;
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<ShipVehicleInfo>().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<AircraftVehicleInfo>().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<RailVehicleInfo>().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<RoadVehicleInfo>().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<ShipVehicleInfo>().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<AircraftVehicleInfo>().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<RailVehicleInfo>().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<RoadVehicleInfo>().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<RailVehicleInfo>().weight) << (this->VehInfo<RailVehicleInfo>().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<RoadVehicleInfo>().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<RailVehicleInfo>().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<RoadVehicleInfo>().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<AircraftVehicleInfo>().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<AircraftVehicleInfo>().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<RailVehicleInfo>().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<RailVehicleInfo>().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<RailVehicleInfo>().railtypes != RailTypes{});
RailTypes introduced = GetAllIntroducesRailTypes(e->VehInfo<RailVehicleInfo>().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<RoadVehicleInfo>().roadtype < ROADTYPE_END);
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. */
@@ -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<RailVehicleInfo>().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<RoadVehicleInfo>().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<RailVehicleInfo>().railveh_type == RAILVEH_WAGON) continue;
/* We have an available engine... yay! */
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;
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<WagonOverride> overrides{};
std::vector<BadgeID> badges{};
private:
/* Vehicle-type specific information. */
std::variant<std::monostate, RailVehicleInfo, RoadVehicleInfo, ShipVehicleInfo, AircraftVehicleInfo> 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 <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
* @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<RailVehicleInfo>();
}
inline const RoadVehicleInfo *RoadVehInfo(EngineID e)
{
return &Engine::Get(e)->u.road;
return &Engine::Get(e)->VehInfo<RoadVehicleInfo>();
}
inline const ShipVehicleInfo *ShipVehInfo(EngineID e)
{
return &Engine::Get(e)->u.ship;
return &Engine::Get(e)->VehInfo<ShipVehicleInfo>();
}
inline const AircraftVehicleInfo *AircraftVehInfo(EngineID e)
{
return &Engine::Get(e)->u.air;
return &Engine::Get(e)->VehInfo<AircraftVehicleInfo>();
}
#endif /* ENGINE_BASE_H */

View File

@@ -45,12 +45,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<RoadVehicleInfo>().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<RailVehicleInfo>().railtypes.Any());
return GetRailTypeInfo(e->VehInfo<RailVehicleInfo>().railtypes.GetNthSetBit(0).value())->strings.new_loco;
}
}
@@ -184,12 +184,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<RailVehicleInfo>().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<RailVehicleInfo>().railtypes.Test(rt)) continue;
if (!railtypes.empty()) railtypes += list_separator;
AppendStringInPlace(railtypes, GetRailTypeInfo(rt)->strings.name);
@@ -199,7 +199,7 @@ static std::string GetTrainEngineInfoString(const Engine &e)
}
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;
}

View File

@@ -25,7 +25,7 @@
/** Unique identification number of an engine. */
using EngineID = PoolID<uint16_t, struct EngineIDTag, 64000, 0xFFFF>;
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
};

View File

@@ -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<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);
@@ -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<RailVehicleInfo>().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<RailVehicleInfo>().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<ShipVehicleInfo>().old_refittable = true;
} 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.
* 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<ShipVehicleInfo>().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<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.
* 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<ShipVehicleInfo>().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<RailVehicleInfo>().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<RoadVehicleInfo>().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<RailVehicleInfo>().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<RoadVehicleInfo>().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<RoadVehicleInfo>().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<RoadVehicleInfo>().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<RailVehicleInfo>().railtypes = railtypes;
e->VehInfo<RailVehicleInfo>().intended_railtypes = railtypes;
} else {
/* Rail type is not available, so disable this engine */
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
EngineInfo *ei = &e->info;
AircraftVehicleInfo *avi = &e->u.air;
AircraftVehicleInfo *avi = &e->VehInfo<AircraftVehicleInfo>();
switch (prop) {
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
EngineInfo *ei = &e->info;
RoadVehicleInfo *rvi = &e->u.road;
RoadVehicleInfo *rvi = &e->VehInfo<RoadVehicleInfo>();
switch (prop) {
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
EngineInfo *ei = &e->info;
ShipVehicleInfo *svi = &e->u.ship;
ShipVehicleInfo *svi = &e->VehInfo<ShipVehicleInfo>();
switch (prop) {
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
EngineInfo *ei = &e->info;
RailVehicleInfo *rvi = &e->u.rail;
RailVehicleInfo *rvi = &e->VehInfo<RailVehicleInfo>();
switch (prop) {
case 0x05: { // Track type

View File

@@ -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<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()'.
* 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) &&
(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) {
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<RailVehicleInfo>();
if (rvi->railveh_type != RAILVEH_WAGON) {
assert(rvi->railtypes.Any());
if (introduces) {

View File

@@ -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<RoadVehicleInfo>();
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<RoadVehicleInfo>();
assert(rvi->roadtype < ROADTYPE_END);
if (introduces) {
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)
{
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)) {
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<RoadVehicleInfo>().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<RoadVehicleInfo>().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<RoadVehicleInfo>();
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<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;
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()

View File

@@ -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<RoadVehicleInfo>().roadtype;
rv->compatible_roadtypes = GetRoadTypeInfo(rv->roadtype)->powered_roadtypes;
RoadTramType rtt = GetRoadTramType(rv->roadtype);
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)
{
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)) {
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<ShipVehicleInfo>().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<ShipVehicleInfo>();
Ship *v = new Ship();
*ret = v;

View File

@@ -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<RailVehicleInfo>();
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<RailVehicleInfo>().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<RailVehicleInfo>().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<RailVehicleInfo>().image_index;
if (IsCustomVehicleSpriteNum(spritenum)) {
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)
{
const RailVehicleInfo *rvi = &e->u.rail;
const RailVehicleInfo *rvi = &e->VehInfo<RailVehicleInfo>();
/* 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<RailVehicleInfo>();
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<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;
/* 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<RailVehicleInfo>().running_cost_class, cost_factor, e->GetGRF());
} while ((v = v->GetNextVehicle()) != nullptr);
return cost;

View File

@@ -1926,7 +1926,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<RoadVehicleInfo>().roadtype) != (RoadTramType)subtype) continue;
if (e->company_avail.Test(_local_company)) return true;
}
return false;
@@ -1956,7 +1956,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<RailVehicleInfo>().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;
@@ -1967,7 +1967,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<RailVehicleInfo>().railveh_type == RAILVEH_WAGON) {
if (!CargoSpec::Get(cargo_type)->is_freight) {
if (parent_engine_type == EngineID::Invalid()) {
return LS_PASSENGER_WAGON_STEAM;
@@ -1988,7 +1988,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<RailVehicleInfo>().engclass) {
default: NOT_REACHED();
case EC_STEAM: return LS_STEAM;
case EC_DIESEL: return is_mu ? LS_DMU : LS_DIESEL;
@@ -2025,7 +2025,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<AircraftVehicleInfo>().subtype) {
case AIR_HELI: return LS_HELICOPTER;
case AIR_CTOL: return LS_SMALL_PLANE;
case AIR_CTOL | AIR_FAST: return LS_LARGE_PLANE;
@@ -2642,9 +2642,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<RailVehicleInfo>().visual_effect; break;
case VEH_ROAD: visual_effect = e->VehInfo<RoadVehicleInfo>().visual_effect; break;
case VEH_SHIP: visual_effect = e->VehInfo<ShipVehicleInfo>().visual_effect; break;
default: visual_effect = 1 << VE_DISABLE_EFFECT; break;
}
@@ -2671,7 +2671,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<RailVehicleInfo>().railveh_type == RAILVEH_WAGON || !IsInsideMM(e->VehInfo<RailVehicleInfo>().engclass, EC_STEAM, EC_MONORAIL)) {
if (visual_effect == VE_DEFAULT) {
visual_effect = 1 << VE_DISABLE_EFFECT;
} else {
@@ -2680,9 +2680,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<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);
}
}
@@ -3064,7 +3064,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<AircraftVehicleInfo>().subtype & AIR_CTOL ? AirportFTAClass::Flag::Airplanes : AirportFTAClass::Flag::Helicopters);
default:
return false;
@@ -3125,7 +3125,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<AircraftVehicleInfo>().subtype & AIR_CTOL) {
return STR_ERROR_AIRPORT_NO_PLANES;
} else {
return STR_ERROR_AIRPORT_NO_HELICOPTERS;

View File

@@ -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

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. */
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<RailVehicleInfo>().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<AircraftVehicleInfo>().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<CommandCost, VehicleID, uint, uint16_t, CargoArray> 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<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 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<RailVehicleInfo>().railveh_type == RAILVEH_WAGON) ? PR_BUILD_VEHICLE_WAGON : PR_BUILD_VEHICLE_TRAIN;
cost_factor <<= 1;
expense_type = EXPENSES_TRAIN_RUN;
break;