changeset 11188:9e451d497d94 draft

(svn r15541) -Revert (r15399): 'v->cargo_type' is also used in other places, which cannot accept CT_INVALID. -Codechange: Add Engine::GetDefaultCargoType() and Engine::CanCarryCargo() and use them. -Fix [FS#2617]: When articulated parts have no available default cargo, use the cargo type of the first part for livery selection. -Change: To decide whether a vehicle is refittable do not test its current capacity for being zero, but always use the 'capacity property'. Note: The property is used unmodifed without calling CB 15/36. By setting it to a non-zero value and returning zero in the callback vehicles can be refitted to/from zero capacity for e.g. livery effects. Note: It is intentional that you cannot control refittability by CB 36.
author frosch <frosch@openttd.org>
date Sat, 21 Feb 2009 12:52:41 +0000
parents de2a88d84521
children c993bfe070b7
files bin/ai/regression/regression.txt src/ai/api/ai_engine.cpp src/ai/api/ai_event_types.cpp src/aircraft_cmd.cpp src/articulated_vehicles.cpp src/build_vehicle_gui.cpp src/engine.cpp src/engine_base.h src/engine_func.h src/engine_gui.cpp src/newgrf_engine.cpp src/roadveh_cmd.cpp src/ship_cmd.cpp src/train_cmd.cpp src/vehicle.cpp
diffstat 15 files changed, 171 insertions(+), 163 deletions(-) [+]
line wrap: on
line diff
--- a/bin/ai/regression/regression.txt
+++ b/bin/ai/regression/regression.txt
@@ -1085,7 +1085,7 @@
   Engine 0
     IsValidEngine():    true
     GetName():          Kirby Paul Tank (Steam)
-    GetCargoType():     0
+    GetCargoType():     255
     CanRefitCargo():    false
     GetCapacity():      -1
     GetReliability():   75
@@ -1229,7 +1229,7 @@
   Engine 8
     IsValidEngine():    true
     GetName():          Chaney 'Jubilee' (Steam)
-    GetCargoType():     0
+    GetCargoType():     255
     CanRefitCargo():    false
     GetCapacity():      -1
     GetReliability():   79
@@ -1247,7 +1247,7 @@
   Engine 9
     IsValidEngine():    true
     GetName():          Ginzu 'A4' (Steam)
-    GetCargoType():     0
+    GetCargoType():     255
     CanRefitCargo():    false
     GetCapacity():      -1
     GetReliability():   83
--- a/src/ai/api/ai_engine.cpp
+++ b/src/ai/api/ai_engine.cpp
@@ -33,28 +33,10 @@
 {
 	if (!IsValidEngine(engine_id)) return CT_INVALID;
 
-	switch (::GetEngine(engine_id)->type) {
-		case VEH_ROAD: {
-			const RoadVehicleInfo *vi = ::RoadVehInfo(engine_id);
-			return vi->cargo_type;
-		} break;
-
-		case VEH_TRAIN: {
-			const RailVehicleInfo *vi = ::RailVehInfo(engine_id);
-			return vi->cargo_type;
-		} break;
+	const Engine *e = ::GetEngine(engine_id);
+	if (!e->CanCarryCargo()) return CT_INVALID;
 
-		case VEH_SHIP: {
-			const ShipVehicleInfo *vi = ::ShipVehInfo(engine_id);
-			return vi->cargo_type;
-		} break;
-
-		case VEH_AIRCRAFT: {
-			return CT_PASSENGERS;
-		} break;
-
-		default: NOT_REACHED();
-	}
+	return e->GetDefaultCargoType();
 }
 
 /* static */ bool AIEngine::CanRefitCargo(EngineID engine_id, CargoID cargo_id)
--- a/src/ai/api/ai_event_types.cpp
+++ b/src/ai/api/ai_event_types.cpp
@@ -21,28 +21,9 @@
 
 CargoID AIEventEnginePreview::GetCargoType()
 {
-	switch (::GetEngine(engine)->type) {
-		case VEH_ROAD: {
-			const RoadVehicleInfo *vi = ::RoadVehInfo(engine);
-			return vi->cargo_type;
-		} break;
-
-		case VEH_TRAIN: {
-			const RailVehicleInfo *vi = ::RailVehInfo(engine);
-			return vi->cargo_type;
-		} break;
-
-		case VEH_SHIP: {
-			const ShipVehicleInfo *vi = ::ShipVehInfo(engine);
-			return vi->cargo_type;
-		} break;
-
-		case VEH_AIRCRAFT: {
-			return CT_PASSENGERS;
-		} break;
-
-		default: NOT_REACHED();
-	}
+	const Engine *e = ::GetEngine(engine);
+	if (!e->CanCarryCargo()) return CT_INVALID;
+	return e->GetDefaultCargoType();
 }
 
 int32 AIEventEnginePreview::GetCapacity()
--- a/src/aircraft_cmd.cpp
+++ b/src/aircraft_cmd.cpp
@@ -254,6 +254,9 @@
 	const Engine *e = GetEngine(p1);
 	CommandCost value(EXPENSES_NEW_VEHICLES, e->GetCost());
 
+	/* Engines without valid cargo should not be available */
+	if (e->GetDefaultCargoType() == CT_INVALID) return CMD_ERROR;
+
 	/* to just query the cost, it is not neccessary to have a valid tile (automation/AI) */
 	if (flags & DC_QUERY_COST) return value;
 
@@ -309,7 +312,7 @@
 		v->cargo_cap = avi->passenger_capacity;
 		u->cargo_cap = avi->mail_capacity;
 
-		v->cargo_type = CT_PASSENGERS;
+		v->cargo_type = e->GetDefaultCargoType();
 		u->cargo_type = CT_MAIL;
 
 		v->cargo_subtype = 0;
@@ -334,17 +337,9 @@
 		u->subtype = AIR_SHADOW;
 		u->UpdateDeltaXY(INVALID_DIR);
 
-		/* Danger, Will Robinson!
-		 * If the aircraft is refittable, but cannot be refitted to
-		 * passengers, we select the cargo type from the refit mask.
-		 * This is a fairly nasty hack to get around the fact that TTD
-		 * has no default cargo type specifier for planes... */
-		CargoID cargo = FindFirstRefittableCargo(p1);
-		if (cargo != CT_INVALID && cargo != CT_PASSENGERS) {
+		if (v->cargo_type != CT_PASSENGERS) {
 			uint16 callback = CALLBACK_FAILED;
 
-			v->cargo_type = cargo;
-
 			if (HasBit(EngInfo(p1)->callbackmask, CBM_VEHICLE_REFIT_CAPACITY)) {
 				callback = GetVehicleCallback(CBID_VEHICLE_REFIT_CAPACITY, 0, 0, v->engine_type, v);
 			}
--- a/src/articulated_vehicles.cpp
+++ b/src/articulated_vehicles.cpp
@@ -5,6 +5,7 @@
 #include "stdafx.h"
 #include "train.h"
 #include "roadveh.h"
+#include "aircraft.h"
 #include "newgrf_engine.h"
 #include "vehicle_func.h"
 
@@ -45,30 +46,22 @@
  */
 static inline uint16 GetVehicleDefaultCapacity(EngineID engine, VehicleType type, CargoID *cargo_type)
 {
+	const Engine *e = GetEngine(engine);
+	CargoID cargo = (e->CanCarryCargo() ? e->GetDefaultCargoType() : (CargoID)CT_INVALID);
+	if (cargo_type != NULL) *cargo_type = cargo;
+	if (cargo == CT_INVALID) return 0;
 	switch (type) {
-		case VEH_TRAIN: {
-			const RailVehicleInfo *rvi = RailVehInfo(engine);
-			if (cargo_type != NULL) *cargo_type = rvi->cargo_type;
-			return GetEngineProperty(engine, 0x14, rvi->capacity) + (rvi->railveh_type == RAILVEH_MULTIHEAD ? rvi->capacity : 0);
-		}
+		case VEH_TRAIN:
+			return GetEngineProperty(engine, 0x14, e->u.rail.capacity) + (e->u.rail.railveh_type == RAILVEH_MULTIHEAD ? e->u.rail.capacity : 0);
 
-		case VEH_ROAD: {
-			const RoadVehicleInfo *rvi = RoadVehInfo(engine);
-			if (cargo_type != NULL) *cargo_type = rvi->cargo_type;
-			return GetEngineProperty(engine, 0x0F, rvi->capacity);
-		}
+		case VEH_ROAD:
+			return GetEngineProperty(engine, 0x0F, e->u.road.capacity);
 
-		case VEH_SHIP: {
-			const ShipVehicleInfo *svi = ShipVehInfo(engine);
-			if (cargo_type != NULL) *cargo_type = svi->cargo_type;
-			return GetEngineProperty(engine, 0x0D, svi->capacity);
-		}
+		case VEH_SHIP:
+			return GetEngineProperty(engine, 0x0D, e->u.ship.capacity);
 
-		case VEH_AIRCRAFT: {
-			const AircraftVehicleInfo *avi = AircraftVehInfo(engine);
-			if (cargo_type != NULL) *cargo_type = CT_PASSENGERS;
-			return avi->passenger_capacity;
-		}
+		case VEH_AIRCRAFT:
+			return AircraftDefaultCargoCapacity(cargo, &e->u.air);
 
 		default: NOT_REACHED();
 	}
@@ -264,28 +257,30 @@
 		u->cur_image = 0xAC2;
 		u->random_bits = VehicleRandomBits();
 
+		const Engine *e_artic = GetEngine(engine_type);
 		switch (type) {
 			default: NOT_REACHED();
 
-			case VEH_TRAIN: {
-				const RailVehicleInfo *rvi_artic = RailVehInfo(engine_type);
-
+			case VEH_TRAIN:
 				u = new (u) Train();
 				previous->SetNext(u);
 				u->u.rail.track = v->u.rail.track;
 				u->u.rail.railtype = v->u.rail.railtype;
 				u->u.rail.first_engine = v->engine_type;
 
-				u->spritenum = rvi_artic->image_index;
-				u->cargo_type = rvi_artic->cargo_type;
-				u->cargo_cap = rvi_artic->capacity;  // Callback 36 is called when the consist is finished
+				u->spritenum = e_artic->u.rail.image_index;
+				if (e_artic->CanCarryCargo()) {
+					u->cargo_type = e_artic->GetDefaultCargoType();
+					u->cargo_cap = e_artic->u.rail.capacity;  // Callback 36 is called when the consist is finished
+				} else {
+					u->cargo_type = v->cargo_type; // Needed for livery selection
+					u->cargo_cap = 0;
+				}
 
 				SetArticulatedPart(u);
-			} break;
+				break;
 
-			case VEH_ROAD: {
-				const RoadVehicleInfo *rvi_artic = RoadVehInfo(engine_type);
-
+			case VEH_ROAD:
 				u = new (u) RoadVehicle();
 				previous->SetNext(u);
 				u->u.road.first_engine = v->engine_type;
@@ -295,12 +290,17 @@
 				u->u.road.roadtype = v->u.road.roadtype;
 				u->u.road.compatible_roadtypes = v->u.road.compatible_roadtypes;
 
-				u->spritenum = rvi_artic->image_index;
-				u->cargo_type = rvi_artic->cargo_type;
-				u->cargo_cap = rvi_artic->capacity;  // Callback 36 is called when the consist is finished
+				u->spritenum = e_artic->u.road.image_index;
+				if (e_artic->CanCarryCargo()) {
+					u->cargo_type = e_artic->GetDefaultCargoType();
+					u->cargo_cap = e_artic->u.road.capacity;  // Callback 36 is called when the consist is finished
+				} else {
+					u->cargo_type = v->cargo_type; // Needed for livery selection
+					u->cargo_cap = 0;
+				}
 
 				SetRoadVehArticPart(u);
-			} break;
+				break;
 		}
 
 		if (flip_image) u->spritenum++;
--- a/src/build_vehicle_gui.cpp
+++ b/src/build_vehicle_gui.cpp
@@ -484,7 +484,8 @@
 	/* Wagon weight - (including cargo) */
 	uint weight = e->GetDisplayWeight();
 	SetDParam(0, weight);
-	SetDParam(1, (GetCargo(rvi->cargo_type)->weight * GetEngineProperty(engine_number, 0x14, rvi->capacity) >> 4) + weight);
+	uint cargo_weight = (e->CanCarryCargo() ? GetCargo(e->GetDefaultCargoType())->weight * GetEngineProperty(engine_number, 0x14, rvi->capacity) >> 4 : 0);
+	SetDParam(1, cargo_weight + weight);
 	DrawString(x, y, STR_PURCHASE_INFO_WEIGHT_CWEIGHT, TC_FROMSTRING);
 	y += 10;
 
@@ -582,7 +583,7 @@
 	y += 10;
 
 	/* Cargo type + capacity */
-	SetDParam(0, svi->cargo_type);
+	SetDParam(0, e->GetDefaultCargoType());
 	SetDParam(1, GetEngineProperty(engine_number, 0x0D, svi->capacity));
 	SetDParam(2, IsEngineRefittable(engine_number) ? STR_9842_REFITTABLE : STR_EMPTY);
 	DrawString(x, y, STR_PURCHASE_INFO_CAPACITY, TC_FROMSTRING);
@@ -599,8 +600,8 @@
 /* Draw aircraft specific details */
 static int DrawAircraftPurchaseInfo(int x, int y, EngineID engine_number, const AircraftVehicleInfo *avi)
 {
-	CargoID cargo;
 	const Engine *e = GetEngine(engine_number);
+	CargoID cargo = e->GetDefaultCargoType();
 
 	/* Purchase cost - Max speed */
 	SetDParam(0, e->GetCost());
@@ -609,7 +610,6 @@
 	y += 10;
 
 	/* Cargo capacity */
-	cargo = FindFirstRefittableCargo(engine_number);
 	if (cargo == CT_INVALID || cargo == CT_PASSENGERS) {
 		SetDParam(0, avi->passenger_capacity);
 		SetDParam(1, avi->mail_capacity);
--- a/src/engine.cpp
+++ b/src/engine.cpp
@@ -113,6 +113,66 @@
 	free(this->name);
 }
 
+/**
+ * Determines the default cargo type of an engine.
+ *
+ * Usually a valid cargo is returned, even though the vehicle has zero capacity, and can therefore not carry anything. But the cargotype is still used
+ * for livery selection etc..
+ *
+ * Vehicles with CT_INVALID as default cargo are usally not available, but it can appear as default cargo of articulated parts.
+ *
+ * @return The default cargo type.
+ * @see CanCarryCargo
+ */
+CargoID Engine::GetDefaultCargoType() const
+{
+	switch (this->type) {
+		case VEH_TRAIN:
+			return this->u.rail.cargo_type;
+
+		case VEH_ROAD:
+			return this->u.road.cargo_type;
+
+		case VEH_SHIP:
+			return this->u.ship.cargo_type;
+
+		case VEH_AIRCRAFT:
+			return FindFirstRefittableCargo(this->index);
+
+		default: NOT_REACHED();
+	}
+}
+
+/**
+ * Determines whether an engine can carry something.
+ * A vehicle cannot carry anything if its capacity is zero, or none of the possible cargos is available in the climate.
+ * @return true if the vehicle can carry something.
+ */
+bool Engine::CanCarryCargo() const
+{
+	/* For engines that can appear in a consist (i.e. rail vehicles and (articulated) road vehicles), a capacity
+	 * of zero is a special case, to define the vehicle to not carry anything. The default cargotype is still used
+	 * for livery selection etc.
+	 * Note: Only the property is tested. A capacity callback returning 0 does not have the same effect.
+	 */
+	switch (this->type) {
+		case VEH_TRAIN:
+			if (this->u.rail.capacity == 0) return false;
+			break;
+
+		case VEH_ROAD:
+			if (this->u.road.capacity == 0) return false;
+			break;
+
+		case VEH_SHIP:
+		case VEH_AIRCRAFT:
+			break;
+
+		default: NOT_REACHED();
+	}
+	return this->GetDefaultCargoType() != CT_INVALID;
+}
+
 Money Engine::GetRunningCost() const
 {
 	switch (this->type) {
@@ -668,6 +728,8 @@
 
 	if (e->type == VEH_SHIP && !e->u.ship.refittable) return false;
 
+	if (!e->CanCarryCargo()) return false;
+
 	const EngineInfo *ei = &e->info;
 	if (ei->refit_mask == 0) return false;
 
@@ -676,35 +738,6 @@
 	if (HasBit(ei->callbackmask, CBM_VEHICLE_CARGO_SUFFIX)) return true;
 
 	/* Is there any cargo except the default cargo? */
-	CargoID default_cargo = GetEngineCargoType(engine);
+	CargoID default_cargo = e->GetDefaultCargoType();
 	return default_cargo != CT_INVALID && ei->refit_mask != 1U << default_cargo;
 }
-
-/** Get the default cargo type for a certain engine type
- * @param engine The ID to get the cargo for
- * @return The cargo type. CT_INVALID means no cargo capacity
- */
-CargoID GetEngineCargoType(EngineID engine)
-{
-	assert(IsEngineIndex(engine));
-
-	switch (GetEngine(engine)->type) {
-		case VEH_TRAIN:
-			if (RailVehInfo(engine)->capacity == 0) return CT_INVALID;
-			return RailVehInfo(engine)->cargo_type;
-
-		case VEH_ROAD:
-			if (RoadVehInfo(engine)->capacity == 0) return CT_INVALID;
-			return RoadVehInfo(engine)->cargo_type;
-
-		case VEH_SHIP:
-			if (ShipVehInfo(engine)->capacity == 0) return CT_INVALID;
-			return ShipVehInfo(engine)->cargo_type;
-
-		case VEH_AIRCRAFT:
-			/* all aircraft starts as passenger planes with cargo capacity */
-			return CT_PASSENGERS;
-
-		default: NOT_REACHED(); return CT_INVALID;
-	}
-}
--- a/src/engine_base.h
+++ b/src/engine_base.h
@@ -50,6 +50,8 @@
 
 	inline bool IsValid() const { return this->info.climates != 0; }
 
+	CargoID GetDefaultCargoType() const;
+	bool CanCarryCargo() const;
 	Money GetRunningCost() const;
 	Money GetCost() const;
 	uint GetDisplayMaxSpeed() const;
--- a/src/engine_func.h
+++ b/src/engine_func.h
@@ -21,7 +21,6 @@
 
 bool IsEngineBuildable(EngineID engine, VehicleType type, CompanyID company);
 bool IsEngineRefittable(EngineID engine);
-CargoID GetEngineCargoType(EngineID engine);
 void SetCachedEngineCounts();
 void SetYearEngineAgingStops();
 void StartupOneEngine(Engine *e, Date aging_date);
--- a/src/engine_gui.cpp
+++ b/src/engine_gui.cpp
@@ -123,7 +123,6 @@
 
 static void DrawTrainEngineInfo(EngineID engine, int x, int y, int maxw)
 {
-	const RailVehicleInfo *rvi = RailVehInfo(engine);
 	const Engine *e = GetEngine(engine);
 
 	SetDParam(0, e->GetCost());
@@ -135,7 +134,7 @@
 
 	uint capacity = GetTotalCapacityOfArticulatedParts(engine, VEH_TRAIN);
 	if (capacity != 0) {
-		SetDParam(5, rvi->cargo_type);
+		SetDParam(5, e->GetDefaultCargoType());
 		SetDParam(6, capacity);
 	} else {
 		SetDParam(5, CT_INVALID);
@@ -159,14 +158,18 @@
 
 static void DrawRoadVehEngineInfo(EngineID engine, int x, int y, int maxw)
 {
-	const RoadVehicleInfo *rvi = RoadVehInfo(engine);
 	const Engine *e = GetEngine(engine);
 
 	SetDParam(0, e->GetCost());
 	SetDParam(1, e->GetDisplayMaxSpeed());
 	SetDParam(2, e->GetRunningCost());
-	SetDParam(3, rvi->cargo_type);
-	SetDParam(4, GetTotalCapacityOfArticulatedParts(engine, VEH_ROAD));
+	uint capacity = GetTotalCapacityOfArticulatedParts(engine, VEH_ROAD);
+	if (capacity != 0) {
+		SetDParam(3, e->GetDefaultCargoType());
+		SetDParam(4, capacity);
+	} else {
+		SetDParam(3, CT_INVALID);
+	}
 
 	DrawStringMultiCenter(x, y, STR_902A_COST_SPEED_RUNNING_COST, maxw);
 }
@@ -178,7 +181,7 @@
 
 	SetDParam(0, e->GetCost());
 	SetDParam(1, e->GetDisplayMaxSpeed());
-	SetDParam(2, svi->cargo_type);
+	SetDParam(2, e->GetDefaultCargoType());
 	SetDParam(3, GetEngineProperty(engine, 0x0D, svi->capacity));
 	SetDParam(4, e->GetRunningCost());
 	DrawStringMultiCenter(x, y, STR_982E_COST_MAX_SPEED_CAPACITY, maxw);
--- a/src/newgrf_engine.cpp
+++ b/src/newgrf_engine.cpp
@@ -850,12 +850,7 @@
 
 	const Engine *e = GetEngine(engine);
 
-	/* Engines are not available, when their cargo is not available.
-	 * But that does not apply to articulated parts. */
-	if (cargo == CT_INVALID) cargo = CT_DEFAULT;
-
 	assert(cargo < lengthof(e->group));
-
 	group = e->group[cargo];
 	if (group != NULL) return group;
 
--- a/src/roadveh_cmd.cpp
+++ b/src/roadveh_cmd.cpp
@@ -166,6 +166,9 @@
 	if (!IsEngineBuildable(p1, VEH_ROAD, _current_company)) return_cmd_error(STR_ROAD_VEHICLE_NOT_AVAILABLE);
 
 	const Engine *e = GetEngine(p1);
+	/* Engines without valid cargo should not be available */
+	if (e->GetDefaultCargoType() == CT_INVALID) return CMD_ERROR;
+
 	CommandCost cost(EXPENSES_NEW_VEHICLES, e->GetCost());
 	if (flags & DC_QUERY_COST) return cost;
 
@@ -217,7 +220,7 @@
 		v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
 
 		v->spritenum = rvi->image_index;
-		v->cargo_type = rvi->cargo_type;
+		v->cargo_type = e->GetDefaultCargoType();
 		v->cargo_subtype = 0;
 		v->cargo_cap = rvi->capacity;
 //		v->cargo_count = 0;
@@ -262,7 +265,8 @@
 
 		/* Call callback 36s after the whole consist has been constructed */
 		for (Vehicle *u = v; u != NULL; u = u->Next()) {
-			u->cargo_cap = GetVehicleProperty(u, 0x0F, u->cargo_cap);
+			/* Cargo capacity is zero if and only if the vehicle cannot carry anything */
+			if (u->cargo_cap != 0) u->cargo_cap = GetVehicleProperty(u, 0x0F, u->cargo_cap);
 		}
 
 		VehiclePositionChanged(v);
@@ -2009,7 +2013,8 @@
 		 * [Refit] button near each wagon. */
 		if (!CanRefitTo(v->engine_type, new_cid)) continue;
 
-		if (v->cargo_cap == 0) continue;
+		const Engine *e = GetEngine(v->engine_type);
+		if (!e->CanCarryCargo()) continue;
 
 		if (HasBit(EngInfo(v->engine_type)->callbackmask, CBM_VEHICLE_REFIT_CAPACITY)) {
 			/* Back up the cargo type */
@@ -2028,14 +2033,13 @@
 
 		if (capacity == CALLBACK_FAILED) {
 			/* callback failed or not used, use default capacity */
-			const RoadVehicleInfo *rvi = RoadVehInfo(v->engine_type);
 
-			CargoID old_cid = rvi->cargo_type;
+			CargoID old_cid = e->GetDefaultCargoType();
 			/* normally, the capacity depends on the cargo type, a vehicle can
 			 * carry twice as much mail/goods as normal cargo, and four times as
 			 * many passengers
 			 */
-			capacity = GetVehicleProperty(v, 0x0F, rvi->capacity);
+			capacity = GetVehicleProperty(v, 0x0F, e->u.road.capacity);
 			switch (old_cid) {
 				case CT_PASSENGERS: break;
 				case CT_MAIL:
--- a/src/ship_cmd.cpp
+++ b/src/ship_cmd.cpp
@@ -737,6 +737,9 @@
 	const Engine *e = GetEngine(p1);
 	CommandCost value(EXPENSES_NEW_VEHICLES, e->GetCost());
 
+	/* Engines without valid cargo should not be available */
+	if (e->GetDefaultCargoType() == CT_INVALID) return CMD_ERROR;
+
 	if (flags & DC_QUERY_COST) return value;
 
 	/* The ai_new queries the vehicle cost before building the route,
@@ -772,7 +775,7 @@
 		v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
 
 		v->spritenum = svi->image_index;
-		v->cargo_type = svi->cargo_type;
+		v->cargo_type = e->GetDefaultCargoType();
 		v->cargo_subtype = 0;
 		v->cargo_cap = svi->capacity;
 		v->value = value.GetCost();
--- a/src/train_cmd.cpp
+++ b/src/train_cmd.cpp
@@ -265,7 +265,8 @@
 	}
 
 	for (Vehicle *u = v; u != NULL; u = u->Next()) {
-		const RailVehicleInfo *rvi_u = RailVehInfo(u->engine_type);
+		const Engine *e_u = GetEngine(u->engine_type);
+		const RailVehicleInfo *rvi_u = &e_u->u.rail;
 
 		if (!HasBit(EngInfo(u->engine_type)->misc_flags, EF_RAIL_TILTS)) train_can_tilt = false;
 
@@ -326,7 +327,7 @@
 			}
 		}
 
-		if (u->cargo_type == rvi_u->cargo_type && u->cargo_subtype == 0) {
+		if (e_u->CanCarryCargo() && u->cargo_type == e_u->GetDefaultCargoType() && u->cargo_subtype == 0) {
 			/* Set cargo capacity if we've not been refitted */
 			u->cargo_cap = GetVehicleProperty(u, 0x14, rvi_u->capacity);
 		}
@@ -581,8 +582,12 @@
 
 static CommandCost CmdBuildRailWagon(EngineID engine, TileIndex tile, DoCommandFlag flags)
 {
-	const RailVehicleInfo *rvi = RailVehInfo(engine);
-	CommandCost value(EXPENSES_NEW_VEHICLES, GetEngine(engine)->GetCost());
+	const Engine *e = GetEngine(engine);
+	const RailVehicleInfo *rvi = &e->u.rail;
+	CommandCost value(EXPENSES_NEW_VEHICLES, e->GetCost());
+
+	/* Engines without valid cargo should not be available */
+	if (e->GetDefaultCargoType() == CT_INVALID) return CMD_ERROR;
 
 	if (flags & DC_QUERY_COST) return value;
 
@@ -643,7 +648,7 @@
 			InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 		}
 
-		v->cargo_type = rvi->cargo_type;
+		v->cargo_type = e->GetDefaultCargoType();
 //		v->cargo_subtype = 0;
 		v->cargo_cap = rvi->capacity;
 		v->value = value.GetCost();
@@ -733,6 +738,9 @@
 	const Engine *e = GetEngine(p1);
 	CommandCost value(EXPENSES_NEW_VEHICLES, e->GetCost());
 
+	/* Engines with CT_INVALID should not be available */
+	if (e->GetDefaultCargoType() == CT_INVALID) return CMD_ERROR;
+
 	if (flags & DC_QUERY_COST) return value;
 
 	/* Check if the train is actually being built in a depot belonging
@@ -783,7 +791,7 @@
 		v->u.rail.track = TRACK_BIT_DEPOT;
 		v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL;
 		v->spritenum = rvi->image_index;
-		v->cargo_type = rvi->cargo_type;
+		v->cargo_type = e->GetDefaultCargoType();
 //		v->cargo_subtype = 0;
 		v->cargo_cap = rvi->capacity;
 		v->max_speed = rvi->max_speed;
@@ -2015,7 +2023,8 @@
 		 * some nice [Refit] button near each wagon. --pasky */
 		if (!CanRefitTo(v->engine_type, new_cid)) continue;
 
-		if (v->cargo_cap != 0) {
+		const Engine *e = GetEngine(v->engine_type);
+		if (e->CanCarryCargo()) {
 			uint16 amount = CALLBACK_FAILED;
 
 			if (HasBit(EngInfo(v->engine_type)->callbackmask, CBM_VEHICLE_REFIT_CAPACITY)) {
@@ -2032,13 +2041,12 @@
 			}
 
 			if (amount == CALLBACK_FAILED) { // callback failed or not used, use default
-				const RailVehicleInfo *rvi = RailVehInfo(v->engine_type);
-				CargoID old_cid = rvi->cargo_type;
+				CargoID old_cid = e->GetDefaultCargoType();
 				/* normally, the capacity depends on the cargo type, a rail vehicle can
 				 * carry twice as much mail/goods as normal cargo, and four times as
 				 * many passengers
 				 */
-				amount = rvi->capacity;
+				amount = e->u.rail.capacity;
 				switch (old_cid) {
 					case CT_PASSENGERS: break;
 					case CT_MAIL:
--- a/src/vehicle.cpp
+++ b/src/vehicle.cpp
@@ -1296,7 +1296,8 @@
 					break;
 				}
 			} else {
-				CargoID initial_cargo = GetEngineCargoType(v->engine_type);
+				const Engine *e = GetEngine(v->engine_type);
+				CargoID initial_cargo = (e->CanCarryCargo() ? e->GetDefaultCargoType() : (CargoID)CT_INVALID);
 
 				if (v->cargo_type != initial_cargo && initial_cargo != CT_INVALID) {
 					total_cost.AddCost(GetRefitCost(v->engine_type));
@@ -1828,7 +1829,8 @@
 	 * whether any _other_ liveries are in use. */
 	if (c->livery[LS_DEFAULT].in_use && (_settings_client.gui.liveries == 2 || (_settings_client.gui.liveries == 1 && company == _local_company))) {
 		/* Determine the livery scheme to use */
-		switch (GetEngine(engine_type)->type) {
+		const Engine *e = GetEngine(engine_type);
+		switch (e->type) {
 			default: NOT_REACHED();
 			case VEH_TRAIN: {
 				const RailVehicleInfo *rvi = RailVehInfo(engine_type);
@@ -1836,11 +1838,13 @@
 					/* Wagonoverrides use the coloir 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;
+					e = GetEngine(engine_type);
 					rvi = RailVehInfo(engine_type);
 					/* Note: Luckily cargo_type is not needed for engines */
 				}
 
-				if (cargo_type == CT_INVALID) cargo_type = rvi->cargo_type;
+				if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
+				if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
 				if (rvi->railveh_type == RAILVEH_WAGON) {
 					if (!GetCargo(cargo_type)->is_freight) {
 						if (parent_engine_type == INVALID_ENGINE) {
@@ -1877,10 +1881,11 @@
 				/* Always use the livery of the front */
 				if (v != NULL && parent_engine_type != INVALID_ENGINE) {
 					engine_type = parent_engine_type;
+					e = GetEngine(engine_type);
 					cargo_type = v->First()->cargo_type;
 				}
-				const RoadVehicleInfo *rvi = RoadVehInfo(engine_type);
-				if (cargo_type == CT_INVALID) cargo_type = rvi->cargo_type;
+				if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
+				if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
 
 				/* Important: Use Tram Flag of front part. Luckily engine_type refers to the front part here. */
 				if (HasBit(EngInfo(engine_type)->misc_flags, EF_ROAD_TRAM)) {
@@ -1894,16 +1899,14 @@
 			}
 
 			case VEH_SHIP: {
-				const ShipVehicleInfo *svi = ShipVehInfo(engine_type);
-				if (cargo_type == CT_INVALID) cargo_type = svi->cargo_type;
+				if (cargo_type == CT_INVALID) cargo_type = e->GetDefaultCargoType();
+				if (cargo_type == CT_INVALID) cargo_type = CT_GOODS; // The vehicle does not carry anything, let's pick some freight cargo
 				scheme = IsCargoInClass(cargo_type, CC_PASSENGERS) ? LS_PASSENGER_SHIP : LS_FREIGHT_SHIP;
 				break;
 			}
 
 			case VEH_AIRCRAFT: {
-				const AircraftVehicleInfo *avi = AircraftVehInfo(engine_type);
-				if (cargo_type == CT_INVALID) cargo_type = CT_PASSENGERS;
-				switch (avi->subtype) {
+				switch (e->u.air.subtype) {
 					case AIR_HELI: scheme = LS_HELICOPTER; break;
 					case AIR_CTOL: scheme = LS_SMALL_PLANE; break;
 					case AIR_CTOL | AIR_FAST: scheme = LS_LARGE_PLANE; break;