(svn r3218) -Feature: Multiheaded train engines will now stay in the same train
authorbjarni
Fri, 18 Nov 2005 23:41:03 +0000
changeset 2676 2ba71e034d97
parent 2675 269c38d892f9
child 2677 2c1fcffb304c
(svn r3218) -Feature: Multiheaded train engines will now stay in the same train
This means that any user attempt to remove a rear engine will tell the user to move the front engine instead
This fixes the assert when moving multiheaded engines (introduced in r3144)
Note: to make old savegames use this feature, some engines might be turned around in order to link engines in pairs

-Codechange: train subtype is now a bitmask
This allows fast access to info like if it is a wagon or engine and if it is in front and so on
Note: savegame version bump
economy.c
lang/english.txt
main_gui.c
network_server.c
order_gui.c
player_gui.c
saveload.c
station_cmd.c
train.h
train_cmd.c
train_gui.c
tunnelbridge_cmd.c
vehicle.c
vehicle.h
vehicle_gui.c
viewport.c
water_cmd.c
--- a/economy.c	Fri Nov 18 20:28:55 2005 +0000
+++ b/economy.c	Fri Nov 18 23:41:03 2005 +0000
@@ -26,6 +26,7 @@
 #include "variables.h"
 #include "vehicle_gui.h"
 #include "ai/ai.h"
+#include "train.h"
 
 // Score info
 const ScoreInfo _score_info[] = {
@@ -129,7 +130,7 @@
 		FOR_ALL_VEHICLES(v) {
 			if (v->owner != owner)
 				continue;
-			if ((v->type == VEH_Train && v->subtype == TS_Front_Engine) ||
+			if ((v->type == VEH_Train && IsFrontEngine(v)) ||
 					v->type == VEH_Road ||
 					(v->type == VEH_Aircraft && v->subtype<=2) ||
 					v->type == VEH_Ship) {
@@ -313,7 +314,7 @@
 			if (v->owner == new_player) {
 				switch (v->type) {
 					case VEH_Train:
-						if (v->subtype == TS_Front_Engine) num_train++;
+						if (IsFrontEngine(v)) num_train++;
 						break;
 					case VEH_Road:
 						num_road++;
@@ -338,7 +339,7 @@
 					DeleteVehicle(v);
 				} else {
 					v->owner = new_player;
-					if (v->type == VEH_Train && v->subtype == TS_Front_Engine)
+					if (v->type == VEH_Train && IsFrontEngine(v))
 						v->unitnumber = ++num_train;
 					else if (v->type == VEH_Road)
 						v->unitnumber = ++num_road;
@@ -1289,7 +1290,7 @@
 	}
 
 	FOR_ALL_VEHICLES(x) {
-		if ((x->type != VEH_Train || x->subtype == TS_Front_Engine) && // for all locs
+		if ((x->type != VEH_Train || IsFrontEngine(x)) && // for all locs
 				u->last_station_visited == x->last_station_visited && // at the same station
 				!(x->vehstatus & VS_STOPPED) && // not stopped
 				x->current_order.type == OT_LOADING && // loading
--- a/lang/english.txt	Fri Nov 18 20:28:55 2005 +0000
+++ b/lang/english.txt	Fri Nov 18 23:41:03 2005 +0000
@@ -2405,6 +2405,7 @@
 STR_8834_CAN_T_DELETE_THIS_ORDER                                :{WHITE}Can't delete this order...
 STR_8835_CAN_T_MODIFY_THIS_ORDER                                :{WHITE}Can't modify this order...
 STR_8837_CAN_T_MOVE_VEHICLE                                     :{WHITE}Can't move vehicle...
+STR_REAR_ENGINE_FOLLOW_FRONT_ERROR                              :{WHITE}The rear engine will always follow its front counterpart
 STR_8838_N_A                                                    :N/A{SKIP}
 STR_8839_CAN_T_SELL_RAILROAD_VEHICLE                            :{WHITE}Can't sell railway vehicle...
 STR_883A_UNABLE_TO_FIND_ROUTE_TO                                :{WHITE}Unable to find route to local depot
--- a/main_gui.c	Fri Nov 18 20:28:55 2005 +0000
+++ b/main_gui.c	Fri Nov 18 23:41:03 2005 +0000
@@ -26,6 +26,7 @@
 #include "signs.h"
 #include "waypoint.h"
 #include "variables.h"
+#include "train.h"
 
 #include "network_data.h"
 #include "network_client.h"
@@ -799,7 +800,7 @@
 	int dis = -1;
 
 	FOR_ALL_VEHICLES(v) {
-		if (v->type == VEH_Train && v->subtype == TS_Front_Engine) CLRBIT(dis, v->owner);
+		if (v->type == VEH_Train && IsFrontEngine(v)) CLRBIT(dis, v->owner);
 	}
 	PopupMainPlayerToolbMenu(w, 310, 13, dis);
 }
--- a/network_server.c	Fri Nov 18 20:28:55 2005 +0000
+++ b/network_server.c	Fri Nov 18 23:41:03 2005 +0000
@@ -5,6 +5,7 @@
 #include "string.h"
 #include "strings.h"
 #include "network_data.h"
+#include "train.h"
 
 #ifdef ENABLE_NETWORK
 
@@ -1271,7 +1272,7 @@
 		if (v->owner < MAX_PLAYERS)
 			switch (v->type) {
 				case VEH_Train:
-					if (v->subtype == TS_Front_Engine)
+					if (IsFrontEngine(v))
 						_network_player_info[v->owner].num_vehicle[0]++;
 					break;
 				case VEH_Road:
--- a/order_gui.c	Fri Nov 18 20:28:55 2005 +0000
+++ b/order_gui.c	Fri Nov 18 23:41:03 2005 +0000
@@ -17,6 +17,7 @@
 #include "viewport.h"
 #include "depot.h"
 #include "waypoint.h"
+#include "train.h"
 
 static int OrderGetSel(const Window* w)
 {
@@ -282,9 +283,9 @@
 {
 	if (u->type != v->type) return false;
 
-	if (u->type == VEH_Train && u->subtype != TS_Front_Engine) {
+	if (u->type == VEH_Train && !IsFrontEngine(u)) {
 		u = GetFirstVehicleInChain(u);
-		if (u->subtype != TS_Front_Engine) return false;
+		if (!IsFrontEngine(u)) return false;
 	}
 
 	// v is vehicle getting orders. Only copy/clone orders if vehicle doesn't have any orders yet
--- a/player_gui.c	Fri Nov 18 20:28:55 2005 +0000
+++ b/player_gui.c	Fri Nov 18 23:41:03 2005 +0000
@@ -15,6 +15,7 @@
 #include "economy.h"
 #include "network.h"
 #include "variables.h"
+#include "train.h"
 
 #ifdef ENABLE_NETWORK
 #include "network_data.h"
@@ -432,7 +433,7 @@
 	FOR_ALL_VEHICLES(v) {
 		if (v->owner == player) {
 			switch (v->type) {
-				case VEH_Train:    if (v->subtype == TS_Front_Engine) train++; break;
+				case VEH_Train:    if (IsFrontEngine(v)) train++; break;
 				case VEH_Road:     road++; break;
 				case VEH_Aircraft: if (v->subtype <= 2) air++; break;
 				case VEH_Ship:     ship++; break;
--- a/saveload.c	Fri Nov 18 20:28:55 2005 +0000
+++ b/saveload.c	Fri Nov 18 23:41:03 2005 +0000
@@ -30,7 +30,7 @@
 
 enum {
 	SAVEGAME_MAJOR_VERSION = 17,
-	SAVEGAME_MINOR_VERSION = 0,
+	SAVEGAME_MINOR_VERSION = 1,
 
 	SAVEGAME_LOADABLE_VERSION = (SAVEGAME_MAJOR_VERSION << 8) + SAVEGAME_MINOR_VERSION
 };
--- a/station_cmd.c	Fri Nov 18 20:28:55 2005 +0000
+++ b/station_cmd.c	Fri Nov 18 23:41:03 2005 +0000
@@ -26,6 +26,7 @@
 #include "sprite.h"
 #include "depot.h"
 #include "pbs.h"
+#include "train.h"
 
 enum {
 	/* Max stations: 64000 (64 * 1000) */
@@ -2255,7 +2256,7 @@
 	byte dir;
 
 	if (v->type == VEH_Train) {
-		if (IS_BYTE_INSIDE(_m[tile].m5, 0, 8) && v->subtype == TS_Front_Engine &&
+		if (IS_BYTE_INSIDE(_m[tile].m5, 0, 8) && IsFrontEngine(v) &&
 				!IsCompatibleTrainStationTile(tile + TileOffsByDir(v->direction >> 1), tile)) {
 
 			station_id = _m[tile].m2;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/train.h	Fri Nov 18 23:41:03 2005 +0000
@@ -0,0 +1,208 @@
+/* $Id$ */
+
+#ifndef TRAIN_H
+#define TRAIN_H
+
+#include "stdafx.h"
+#include "vehicle.h"
+
+
+/*
+ * enum to handle train subtypes
+ * Do not access it directly unless you have to. Use the access functions below
+ * This is an enum to tell what bit to access as it is a bitmask
+ */
+
+typedef enum TrainSubtypes {
+	Train_Front             = 0, // Leading engine of a train
+	Train_Articulated_Part  = 1, // Articulated part of an engine
+	Train_Wagon             = 2, // Wagon
+	Train_Engine            = 3, // Engine, that can be front engines, but might be placed behind another engine
+	Train_Free_Wagon        = 4, // First in a wagon chain (in depot)
+	Train_Multiheaded       = 5, // Engine is a multiheaded
+} TrainSubtype;
+
+
+/** Check if a vehicle is front engine
+ * @param v vehicle to check
+ * @return Returns true if vehicle is a front engine
+ */
+static inline bool IsFrontEngine(const Vehicle *v)
+{
+	return HASBIT(v->subtype, Train_Front);
+}
+
+/** Set front engine state
+ * @param v vehicle to change
+ */
+static inline void SetFrontEngine(Vehicle *v)
+{
+	SETBIT(v->subtype, Train_Front);
+}
+
+/** Remove the front engine state
+ * @param v vehicle to change
+ */
+static inline void ClearFrontEngine(Vehicle *v)
+{
+	CLRBIT(v->subtype, Train_Front);
+}
+
+/** Check if a vehicle is an articulated part of an engine
+ * @param v vehicle to check
+ * @return Returns true if vehicle is an articulated part
+ */
+static inline bool IsArticulatedPart(const Vehicle *v)
+{
+	return HASBIT(v->subtype, Train_Articulated_Part);
+}
+
+/** Set a vehicle to be an articulated part
+ * @param v vehicle to change
+ */
+static inline void SetArticulatedPart(Vehicle *v)
+{
+	SETBIT(v->subtype, Train_Articulated_Part);
+}
+
+/** Clear a vehicle from being an articulated part
+ * @param v vehicle to change
+ */
+static inline void ClearArticulatedPart(Vehicle *v)
+{
+	CLRBIT(v->subtype, Train_Articulated_Part);
+}
+
+/** Check if a vehicle is a wagon
+ * @param v vehicle to check
+ * @return Returns true if vehicle is a wagon
+ */
+static inline bool IsTrainWagon(const Vehicle *v)
+{
+	return HASBIT(v->subtype, Train_Wagon);
+}
+
+/** Set a vehicle to be a wagon
+ * @param v vehicle to change
+ */
+static inline void SetTrainWagon(Vehicle *v)
+{
+	SETBIT(v->subtype, Train_Wagon);
+}
+
+/** Clear wagon property
+ * @param v vehicle to change
+ */
+static inline void ClearTrainWagon(Vehicle *v)
+{
+	CLRBIT(v->subtype, Train_Wagon);
+}
+
+/** Check if a vehicle is an engine (can be first in a train)
+ * @param v vehicle to check
+ * @return Returns true if vehicle is an engine
+ */
+static inline bool IsTrainEngine(const Vehicle *v)
+{
+	return HASBIT(v->subtype, Train_Engine);
+}
+
+/** Set engine status
+ * @param v vehicle to change
+ */
+static inline void SetTrainEngine(Vehicle *v)
+{
+	SETBIT(v->subtype, Train_Engine);
+}
+
+/** Clear engine status
+ * @param v vehicle to change
+ */
+static inline void ClearTrainEngine(Vehicle *v)
+{
+	CLRBIT(v->subtype, Train_Engine);
+}
+
+/** Check if a vehicle is a free wagon (got no engine in front of it)
+ * @param v vehicle to check
+ * @return Returns true if vehicle is a free wagon
+ */
+static inline bool IsFreeWagon(const Vehicle *v)
+{
+	return HASBIT(v->subtype, Train_Free_Wagon);
+}
+
+/** Set if a vehicle is a free wagon
+ * @param v vehicle to change
+ */
+static inline void SetFreeWagon(Vehicle *v)
+{
+	SETBIT(v->subtype, Train_Free_Wagon);
+}
+
+/** Clear a vehicle from being a free wagon
+ * @param v vehicle to change
+ */
+static inline void ClearFreeWagon(Vehicle *v)
+{
+	CLRBIT(v->subtype, Train_Free_Wagon);
+}
+
+/** Check if a vehicle is a multiheaded engine
+ * @param v vehicle to check
+ * @return Returns true if vehicle is a multiheaded engine
+ */
+static inline bool IsMultiheaded(const Vehicle *v)
+{
+	return HASBIT(v->subtype, Train_Multiheaded);
+}
+
+/** Set if a vehicle is a multiheaded engine
+ * @param v vehicle to change
+ */
+static inline void SetMultiheaded(Vehicle *v)
+{
+	SETBIT(v->subtype, Train_Multiheaded);
+}
+
+/** Clear multiheaded engine property
+ * @param v vehicle to change
+ */
+static inline void ClearMultiheaded(Vehicle *v)
+{
+	CLRBIT(v->subtype, Train_Multiheaded);
+}
+
+/** Get the next real (non-articulated part) vehicle in the consist.
+ * @param v Vehicle.
+ * @return Next vehicle in the consist.
+ */
+static inline Vehicle *GetNextVehicle(const Vehicle *v)
+{
+	Vehicle *u = v->next;
+	while (u != NULL && IsArticulatedPart(u)) {
+		u = u->next;
+	}
+	return u;
+}
+
+/** Check if an engine has an articulated part.
+ * @param v Vehicle.
+ * @return True if the engine has an articulated part.
+ */
+static inline bool EngineHasArticPart(const Vehicle *v)
+{
+	return (v->next != NULL && IsArticulatedPart(v->next));
+}
+
+/** Get the last part of a multi-part engine.
+ * @param v Vehicle.
+ * @return Last part of the engine.
+ */
+static inline Vehicle *GetLastEnginePart(Vehicle *v)
+{
+	while (EngineHasArticPart(v)) v = v->next;
+	return v;
+}
+
+#endif /* TRAIN_H */
--- a/train_cmd.c	Fri Nov 18 20:28:55 2005 +0000
+++ b/train_cmd.c	Fri Nov 18 23:41:03 2005 +0000
@@ -22,9 +22,7 @@
 #include "debug.h"
 #include "waypoint.h"
 #include "vehicle_gui.h"
-
-#define IS_FIRSTHEAD_SPRITE(spritenum) \
-	(is_custom_sprite(spritenum) ? IS_CUSTOM_FIRSTHEAD_SPRITE(spritenum) : _engine_sprite_add[spritenum] == 0)
+#include "train.h"
 
 static bool TrainCheckIfLineEnds(Vehicle *v);
 static void TrainController(Vehicle *v);
@@ -50,7 +48,7 @@
 		vweight += (_cargoc.weights[u->cargo_type] * u->cargo_count) / 16;
 
 		// Vehicle weight is not added for articulated parts.
-		if (u->subtype != TS_Artic_Part) {
+		if (!IsArticulatedPart(u)) {
 			// vehicle weight is the sum of the weight of the vehicle and the weight of its cargo
 			vweight += rvi->weight;
 
@@ -86,10 +84,10 @@
 
 	assert(v->type == VEH_Train);
 
-	assert(v->subtype == TS_Front_Engine || v->subtype == TS_Free_Car);
+	assert(IsFrontEngine(v) || IsFreeWagon(v));
 
 	rvi_v = RailVehInfo(v->engine_type);
-	first_engine = (v->subtype == TS_Front_Engine) ? v->engine_type : INVALID_VEHICLE;
+	first_engine = IsFrontEngine(v) ? v->engine_type : INVALID_VEHICLE;
 	v->u.rail.cached_total_length = 0;
 
 	for (u = v; u != NULL; u = u->next) {
@@ -102,7 +100,7 @@
 		if (rvi_u->visual_effect != 0) {
 			u->u.rail.cached_vis_effect = rvi_u->visual_effect;
 		} else {
-			if (rvi_u->flags & RVI_WAGON || u->subtype == TS_Artic_Part) {
+			if (IsTrainWagon(u) || IsArticulatedPart(u)) {
 				// Wagons and articulated parts have no effect by default
 				u->u.rail.cached_vis_effect = 0x40;
 			} else if (rvi_u->engclass == 0) {
@@ -114,7 +112,7 @@
 			}
 		}
 
-		if (u->subtype != TS_Artic_Part) {
+		if (!IsArticulatedPart(u)) {
 			// power is the sum of the powers of all engines and powered wagons in the consist
 			power += rvi_u->power;
 
@@ -277,7 +275,7 @@
 
 	max_speed += (max_speed / 2) * v->u.rail.railtype;
 
-	if (IsTileType(v->tile, MP_STATION) && v->subtype == TS_Front_Engine) {
+	if (IsTileType(v->tile, MP_STATION) && IsFrontEngine(v)) {
 		if (TrainShouldStop(v, v->tile)) {
 			int station_length = 0;
 			TileIndex tile = v->tile;
@@ -361,7 +359,7 @@
 	uint power = 0;
 	uint weight = 0;
 
-	assert(v->subtype == TS_Front_Engine);
+	assert(IsFrontEngine(v));
 
 	weight = v->u.rail.cached_weight;
 	power = v->u.rail.cached_power;
@@ -491,7 +489,8 @@
 		u->engine_type = engine_type;
 		u->value = 0;
 		u->type = VEH_Train;
-		u->subtype = TS_Artic_Part;
+		u->subtype = 0;
+		SetArticulatedPart(u);
 		u->cur_image = 0xAC2;
 
 		VehiclePositionChanged(u);
@@ -531,7 +530,7 @@
 
 			FOR_ALL_VEHICLES(w) {
 				if (w->type == VEH_Train && w->tile == tile &&
-				    w->subtype == TS_Free_Car && w->engine_type == engine) {
+				    IsFreeWagon(w) && w->engine_type == engine) {
 					u = GetLastVehicleInChain(w);
 					break;
 				}
@@ -555,10 +554,12 @@
 			v->u.rail.track = 0x80;
 			v->vehstatus = VS_HIDDEN | VS_DEFPAL;
 
-			v->subtype = TS_Free_Car;
+			v->subtype = 0;
+			SetTrainWagon(v);
 			if (u != NULL) {
 				u->next = v;
-				v->subtype = TS_Not_First;
+			} else {
+				SetFreeWagon(v);
 			}
 
 			v->cargo_type = rvi->cargo_type;
@@ -593,11 +594,11 @@
 	const Vehicle* v;
 
 	FOR_ALL_VEHICLES(v) {
-		if (v->type == VEH_Train && v->subtype == TS_Free_Car &&
+		if (v->type == VEH_Train && IsFreeWagon(v) &&
 				v->tile == u->tile &&
 				v->u.rail.track == 0x80) {
-			if (DoCommandByTile(0, v->index | (u->index << 16), 1, DC_EXEC,
-					CMD_MOVE_RAIL_VEHICLE) == CMD_ERROR)
+			if (CmdFailed(DoCommandByTile(0, v->index | (u->index << 16), 1, DC_EXEC,
+					CMD_MOVE_RAIL_VEHICLE)))
 				break;
 		}
 	}
@@ -638,7 +639,8 @@
 	u->z_height = 6;
 	u->u.rail.track = 0x80;
 	u->vehstatus = v->vehstatus & ~VS_STOPPED;
-	u->subtype = TS_Not_First;
+	u->subtype = 0;
+	SetMultiheaded(u);
 	u->spritenum = v->spritenum + 1;
 	u->cargo_type = v->cargo_type;
 	u->cargo_cap = v->cargo_cap;
@@ -750,13 +752,24 @@
 			v->type = VEH_Train;
 			v->cur_image = 0xAC2;
 
+			v->subtype = 0;
+			SetFrontEngine(v);
+			SetTrainEngine(v);
+
 			v->u.rail.shortest_platform[0] = 255;
 			v->u.rail.shortest_platform[1] = 0;
 
 			VehiclePositionChanged(v);
 
 			if (rvi->flags & RVI_MULTIHEAD && !HASBIT(p2, 0)) {
+				SetMultiheaded(v);
 				AddRearEngineToMultiheadedTrain(vl[0], vl[1], true);
+				/* Now we need to link the front and rear engines together
+				 * other_multiheaded_part is the pointer that links to the other half of the engine
+				 * vl[0] is the front and vl[1] is the rear
+				 */
+				vl[0]->u.rail.other_multiheaded_part = vl[1];
+				vl[1]->u.rail.other_multiheaded_part = vl[0];
 			} else {
 				AddArticulatedParts(rvi, vl);
 			}
@@ -800,7 +813,7 @@
 	for (; v != NULL; v = v->next) {
 		count++;
 		if (v->u.rail.track != 0x80 || v->tile != tile ||
-				(v->subtype == TS_Front_Engine && !(v->vehstatus & VS_STOPPED))) {
+				(IsFrontEngine(v) && !(v->vehstatus & VS_STOPPED))) {
 			_error_message = STR_881A_TRAINS_CAN_ONLY_BE_ALTERED;
 			return -1;
 		}
@@ -824,7 +837,8 @@
 		v = GetNextVehicle(v);
 		if (v == NULL) return NULL;
 
-		v->subtype = TS_Free_Car;
+		if (IsTrainWagon(v)) SetFreeWagon(v);
+
 		return v;
 	}
 
@@ -840,7 +854,7 @@
 	TileIndex tile = src->tile;
 
 	FOR_ALL_VEHICLES(dst) {
-		if (dst->type == VEH_Train && dst->subtype == TS_Free_Car &&
+		if (dst->type == VEH_Train && IsFreeWagon(dst) &&
 				dst->tile == tile) {
 			// check so all vehicles in the line have the same engine.
 			Vehicle *v = dst;
@@ -855,6 +869,45 @@
 	return NULL;
 }
 
+/*
+ * add a vehicle v behind vehicle dest
+ * use this function since it sets flags as needed
+ */
+static void AddWagonToConsist(Vehicle *v, Vehicle *dest)
+{
+	UnlinkWagon(v, GetFirstVehicleInChain(v));
+	if (dest == NULL) return;
+
+	v->next = dest->next;
+	dest->next = v;
+	ClearFreeWagon(v);
+	ClearFrontEngine(v);
+}
+
+/*
+ * move around on the train so rear engines are placed correctly according to the other engines
+ * always call with the front engine
+ */
+static void NormaliseTrainConsist(Vehicle *v)
+{
+	Vehicle *u;
+
+	if (IsFreeWagon(v)) return;
+
+	assert(IsFrontEngine(v));
+
+	for(; v != NULL; v = GetNextVehicle(v)) {
+		if (!IsMultiheaded(v) || !IsTrainEngine(v)) continue;
+
+		/* make sure that there are no free cars before next engine */
+		for(u = v; u->next != NULL && !IsTrainEngine(u->next); u = u->next);
+
+		if (u == v->u.rail.other_multiheaded_part) continue;
+		AddWagonToConsist(v->u.rail.other_multiheaded_part, u);
+
+	}
+}
+
 /** Move a rail vehicle around inside the depot.
  * @param x,y unused
  * @param p1 various bitstuffed elements
@@ -867,7 +920,6 @@
 	VehicleID s = GB(p1, 0, 16);
 	VehicleID d = GB(p1, 16, 16);
 	Vehicle *src, *dst, *src_head, *dst_head;
-	bool is_loco;
 
 	if (!IsVehicleIndex(s)) return CMD_ERROR;
 
@@ -875,20 +927,18 @@
 
 	if (src->type != VEH_Train) return CMD_ERROR;
 
-	is_loco = !(RailVehInfo(src->engine_type)->flags & RVI_WAGON) && IS_FIRSTHEAD_SPRITE(src->spritenum);
-
 	// if nothing is selected as destination, try and find a matching vehicle to drag to.
 	if (d == INVALID_VEHICLE) {
 		dst = NULL;
-		if (!is_loco) dst = FindGoodVehiclePos(src);
+		if (!IsTrainEngine(src)) dst = FindGoodVehiclePos(src);
 	} else {
 		dst = GetVehicle(d);
 	}
 
 	// if an articulated part is being handled, deal with its parent vehicle
-	while (src->subtype == TS_Artic_Part) src = GetPrevVehicleInChain(src);
+	while (IsArticulatedPart(src)) src = GetPrevVehicleInChain(src);
 	if (dst != NULL) {
-		while (dst->subtype == TS_Artic_Part) dst = GetPrevVehicleInChain(dst);
+		while (IsArticulatedPart(dst)) dst = GetPrevVehicleInChain(dst);
 	}
 
 	// don't move the same vehicle..
@@ -907,14 +957,42 @@
 		dst = GetLastEnginePart(dst);
 	}
 
-	/* clear the ->first cache */
-	{
-		Vehicle *u;
-
-		for (u = src_head; u != NULL; u = u->next) u->first = NULL;
-		for (u = dst_head; u != NULL; u = u->next) u->first = NULL;
+	if (dst != NULL && IsMultiheaded(dst) && !IsTrainEngine(dst) && IsTrainWagon(src)) {
+		/* We are moving a wagon to the rear part of a multiheaded engine */
+		if (dst->next == NULL) {
+			/* It's the last one, so we will add the wagon just before the rear engine */
+			dst = GetPrevVehicleInChain(dst);
+			// if dst is NULL, it means that dst got a rear multiheaded engine as first engine. We can't use that
+			if (dst == NULL) return CMD_ERROR;
+		} else {
+			/* there are more units on this train, so we will add the wagon after the next one*/
+			dst = dst->next;
+		}
 	}
 
+	if (IsTrainEngine(src) && dst_head != NULL) {
+		/* we need to make sure that we didn't place it between a pair of multiheaded engines */
+		Vehicle *u, *engine = NULL;
+
+		for(u = dst_head; u != NULL; u = u->next) {
+			if (IsTrainEngine(u) && IsMultiheaded(u) && u->u.rail.other_multiheaded_part != NULL) {
+				engine = u;
+			}
+				if (engine != NULL && engine->u.rail.other_multiheaded_part == u) {
+					engine = NULL;
+				}
+				if (u == dst) {
+					if (engine != NULL) {
+					dst = engine->u.rail.other_multiheaded_part;
+					}
+					break;
+				}
+
+		}
+	}
+
+	if (IsMultiheaded(src) && !IsTrainEngine(src)) return_cmd_error(STR_REAR_ENGINE_FOLLOW_FRONT_ERROR);
+
 	/* check if all vehicles in the source train are stopped inside a depot */
 	if (CheckTrainStoppedInDepot(src_head) < 0) return CMD_ERROR;
 
@@ -924,18 +1002,9 @@
 		int num = CheckTrainStoppedInDepot(dst_head);
 		if (num < 0) return CMD_ERROR;
 
-		if (num > (_patches.mammoth_trains ? 100 : 9) && dst_head->subtype == TS_Front_Engine )
+		if (num > (_patches.mammoth_trains ? 100 : 9) && IsFrontEngine(dst_head))
 			return_cmd_error(STR_8819_TRAIN_TOO_LONG);
 
-		// if it's a multiheaded vehicle we're dragging to, drag to the vehicle before..
-		while (IS_CUSTOM_SECONDHEAD_SPRITE(dst->spritenum) || (
-			!is_custom_sprite(dst->spritenum) && _engine_sprite_add[dst->spritenum] != 0)
-		) {
-			Vehicle *v = GetPrevVehicleInChain(dst);
-			if (v == NULL || src == v) break;
-			dst = v;
-		}
-
 		assert(dst_head->tile == src_head->tile);
 	}
 
@@ -943,7 +1012,7 @@
 	if (HASBIT(p2, 0) && src_head == dst_head) return 0;
 
 	// moving a loco to a new line?, then we need to assign a unitnumber.
-	if (dst == NULL && src->subtype != TS_Front_Engine && is_loco) {
+	if (dst == NULL && !IsFrontEngine(src) && IsTrainEngine(src)) {
 		UnitID unit_num = GetFreeUnitNumber(VEH_Train);
 		if (unit_num > _patches.max_trains)
 			return_cmd_error(STR_00E1_TOO_MANY_VEHICLES_IN_GAME);
@@ -955,8 +1024,13 @@
 
 	/* do it? */
 	if (flags & DC_EXEC) {
-		Vehicle *new_front = GetNextVehicle(src);	//used if next in line should make a train on it's own
-		bool make_new_front = src->subtype == TS_Front_Engine;
+		/* clear the ->first cache */
+		{
+			Vehicle *u;
+
+			for (u = src_head; u != NULL; u = u->next) u->first = NULL;
+			for (u = dst_head; u != NULL; u = u->next) u->first = NULL;
+		}
 
 		if (HASBIT(p2, 0)) {
 			// unlink ALL wagons
@@ -977,26 +1051,27 @@
 		}
 
 		if (dst == NULL) {
-			// move the train to an empty line. for locomotives, we set the type to 0. for wagons, 4.
-			if (is_loco) {
-				if (src->subtype != TS_Front_Engine) {
+			// move the train to an empty line. for locomotives, we set the type to TS_Front. for wagons, 4.
+			if (IsTrainEngine(src)) {
+				if (!IsFrontEngine(src)) {
 					// setting the type to 0 also involves setting up the orders field.
-					src->subtype = TS_Front_Engine;
+					SetFrontEngine(src);
 					assert(src->orders == NULL);
 					src->num_orders = 0;
 				}
 			} else {
-				src->subtype = TS_Free_Car;
+				SetFreeWagon(src);
 			}
 			dst_head = src;
 		} else {
-			if (src->subtype == TS_Front_Engine) {
+			if (IsFrontEngine(src)) {
 				// the vehicle was previously a loco. need to free the order list and delete vehicle windows etc.
 				DeleteWindowById(WC_VEHICLE_VIEW, src->index);
 				DeleteVehicleOrders(src);
 			}
 
-			src->subtype = TS_Not_First;
+			ClearFrontEngine(src);
+			ClearFreeWagon(src);
 			src->unitnumber = 0; // doesn't occupy a unitnumber anymore.
 
 			// link in the wagon(s) in the chain.
@@ -1008,19 +1083,45 @@
 			}
 			dst->next = src;
 		}
+		if (src->u.rail.other_multiheaded_part != NULL) {
+			if (src->u.rail.other_multiheaded_part == src_head) {
+				src_head = src_head->next;
+			}
+			AddWagonToConsist(src->u.rail.other_multiheaded_part, src);
+		}
+
+		if (HASBIT(p2, 0) && src_head != NULL && src_head != src) {
+			/* if we stole a rear multiheaded engine, we better give it back to the front end */
+			Vehicle *engine = NULL, *u;
+			for (u = src_head; u != NULL; u = u->next) {
+				if (IsMultiheaded(u)) {
+					if (IsTrainEngine(u)) {
+						engine = u;
+						continue;
+					}
+					/* we got the rear engine to match with the front one */
+					engine = NULL;
+				}
+			}
+			if (engine != NULL && engine->u.rail.other_multiheaded_part != NULL) {
+				AddWagonToConsist(engine->u.rail.other_multiheaded_part, engine);
+				// previous line set the front engine to the old front. We need to clear that
+				engine->u.rail.other_multiheaded_part->first = NULL;
+			}
+		}
 
 		/* If there is an engine behind first_engine we moved away, it should become new first_engine
 		 * To do this, CmdMoveRailVehicle must be called once more
-		 * since we set p2 to a condition that makes the statement false, we can't loop forever with this one */
-		if (make_new_front && new_front != NULL && !(HASBIT(p2, 0))) {
-			if (!(RailVehInfo(new_front->engine_type)->flags & RVI_WAGON)) {
-				CmdMoveRailVehicle(x, y, flags, new_front->index | (INVALID_VEHICLE << 16), 1);
-			}
+		 * we can't loop forever here because next time we reach this line we will have a front engine */
+		if (src_head != NULL && !IsFrontEngine(src_head) && IsTrainEngine(src_head)) {
+			CmdMoveRailVehicle(x, y, flags, src_head->index | (INVALID_VEHICLE << 16), 1);
+			src_head = NULL;	// don't do anything more to this train since the new call will do it
 		}
 
 		if (src_head) {
+			NormaliseTrainConsist(src_head);
 			TrainConsistChanged(src_head);
-			if (src_head->subtype == TS_Front_Engine) {
+			if (IsFrontEngine(src_head)) {
 				UpdateTrainAcceleration(src_head);
 				InvalidateWindow(WC_VEHICLE_DETAILS, src_head->index);
 				/* Update the refit button and window */
@@ -1032,8 +1133,9 @@
 		};
 
 		if (dst_head) {
+			NormaliseTrainConsist(dst_head);
 			TrainConsistChanged(dst_head);
-			if (dst_head->subtype == TS_Front_Engine) {
+			if (IsFrontEngine(dst_head)) {
 				UpdateTrainAcceleration(dst_head);
 				InvalidateWindow(WC_VEHICLE_DETAILS, dst_head->index);
 				/* Update the refit button and window */
@@ -1074,27 +1176,6 @@
 	return 0;
 }
 
-/**
- * Search for a matching rear-engine of a dual-headed train.
- * Do this as if you would find matching parentheses. If a new
- * engine is 'started', first 'close' that before 'closing' our
- * searched engine
- */
-Vehicle* GetRearEngine(const Vehicle* v)
-{
-	Vehicle *u;
-	int en_count = 1;
-
-	for (u = v->next; u != NULL; u = u->next) {
-		if (u->engine_type == v->engine_type) { // find matching engine
-			en_count += (IS_FIRSTHEAD_SPRITE(u->spritenum)) ? +1 : -1;
-
-			if (en_count == 0) return (Vehicle *)u;
-		}
-	}
-	return NULL;
-}
-
 /** Sell a (single) train wagon/engine.
  * @param x,y unused
  * @param p1 the wagon/engine index
@@ -1108,6 +1189,7 @@
 int32 CmdSellRailWagon(int x, int y, uint32 flags, uint32 p1, uint32 p2)
 {
 	Vehicle *v, *tmp, *first;
+	Vehicle *new_f = NULL;
 	int32 cost = 0;
 
 	if (!IsVehicleIndex(p1) || p2 > 2) return CMD_ERROR;
@@ -1118,14 +1200,16 @@
 
 	SET_EXPENSES_TYPE(EXPENSES_NEW_VEHICLES);
 
-	while (v->subtype == TS_Artic_Part) v = GetPrevVehicleInChain(v);
+	while (IsArticulatedPart(v)) v = GetPrevVehicleInChain(v);
 	first = GetFirstVehicleInChain(v);
 
 	// make sure the vehicle is stopped in the depot
 	if (CheckTrainStoppedInDepot(first) < 0) return CMD_ERROR;
 
+	if (IsMultiheaded(v) && !IsTrainEngine(v)) return_cmd_error(STR_REAR_ENGINE_FOLLOW_FRONT_ERROR);
+
 	if (flags & DC_EXEC) {
-		if (v == first && first->subtype == TS_Front_Engine) {
+		if (v == first && IsFrontEngine(first)) {
 			DeleteWindowById(WC_VEHICLE_VIEW, first->index);
 		}
 		if (IsLocalPlayer() && (p1 == 1 || !(RailVehInfo(v->engine_type)->flags & RVI_WAGON))) {
@@ -1141,20 +1225,22 @@
 			byte ori_subtype = v->subtype; // backup subtype of deleted wagon in case DeleteVehicle() changes
 
 			/* 1. Delete the engine, if it is dualheaded also delete the matching
-			* rear engine of the loco (from the point of deletion onwards) */
-			Vehicle* rear = (RailVehInfo(v->engine_type)->flags & RVI_MULTIHEAD) ? GetRearEngine(v) : NULL;
+			 * rear engine of the loco (from the point of deletion onwards) */
+			Vehicle *rear = (IsMultiheaded(v) &&
+				IsTrainEngine(v)) ? v->u.rail.other_multiheaded_part : NULL;
+
 			if (rear != NULL) {
-				cost -= v->value;
+				cost -= rear->value;
 				if (flags & DC_EXEC) {
-					v = UnlinkWagon(rear, v);
+					UnlinkWagon(rear, first);
 					DeleteVehicle(rear);
 				}
 			}
 
 			/* 2. We are selling the first engine, some special action might be required
-				* here, so take attention */
+			 * here, so take attention */
 			if ((flags & DC_EXEC) && v == first) {
-				Vehicle *new_f = GetNextVehicle(first);
+				new_f = GetNextVehicle(first);
 
 				/* 2.1 If the first wagon is sold, update the first-> pointers to NULL */
 				for (tmp = first; tmp != NULL; tmp = tmp->next) tmp->first = NULL;
@@ -1163,7 +1249,7 @@
 					* if the second wagon (which will be first) is an engine. If it is one,
 					* promote it as a new train, retaining the unitnumber, orders */
 				if (new_f != NULL) {
-					if (!(RailVehInfo(new_f->engine_type)->flags & RVI_WAGON) && IS_FIRSTHEAD_SPRITE(new_f->spritenum)) {
+					if (IsTrainEngine(new_f)) {
 						switch_engine = true;
 						/* Copy important data from the front engine */
 						new_f->unitnumber = first->unitnumber;
@@ -1185,12 +1271,13 @@
 
 				/* 4 If the second wagon was an engine, update it to front_engine
 					* which UnlinkWagon() has changed to TS_Free_Car */
-				if (switch_engine) first->subtype = TS_Front_Engine;
+				if (switch_engine) SetFrontEngine(first);
 
 				/* 5. If the train still exists, update its acceleration, window, etc. */
 				if (first != NULL) {
+					NormaliseTrainConsist(first);
 					TrainConsistChanged(first);
-					if (first->subtype == TS_Front_Engine) {
+					if (IsFrontEngine(first)) {
 						InvalidateWindow(WC_VEHICLE_DETAILS, first->index);
 						InvalidateWindow(WC_VEHICLE_REFIT, first->index);
 						UpdateTrainAcceleration(first);
@@ -1199,10 +1286,10 @@
 
 
 				/* (6.) Borked AI. If it sells an engine it expects all wagons lined
-				* up on a new line to be added to the newly built loco. Replace it is.
-				* Totally braindead cause building a new engine adds all loco-less
-				* engines to its train anyways */
-				if (p2 == 2 && ori_subtype == TS_Front_Engine) {
+				 * up on a new line to be added to the newly built loco. Replace it is.
+				 * Totally braindead cause building a new engine adds all loco-less
+				 * engines to its train anyways */
+				if (p2 == 2 && HASBIT(ori_subtype, Train_Front)) {
 					for (v = first; v != NULL; v = tmp) {
 						tmp = GetNextVehicle(v);
 						DoCommandByTile(v->tile, v->index | INVALID_VEHICLE << 16, 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
@@ -1211,31 +1298,26 @@
 			}
 		} break;
 		case 1: { /* Delete wagon and all wagons after it given certain criteria */
-			/* 1. Count the number for first and rear engines for dualheads
-			* to be able to deduce which ones go with which ones */
-			int enf_count = 0;
-			int enr_count = 0;
-			for (tmp = first; tmp != NULL; tmp = GetNextVehicle(tmp)) {
-				if (RailVehInfo(tmp->engine_type)->flags & RVI_MULTIHEAD)
-					(IS_FIRSTHEAD_SPRITE(tmp->spritenum)) ? enf_count++ : enr_count++;
-			}
-
-			/* 2. Start deleting every vehicle after the selected one
-			* If we encounter a matching rear-engine to a front-engine
-			* earlier in the chain (before deletion), leave it alone */
+			/* Start deleting every vehicle after the selected one
+			 * If we encounter a matching rear-engine to a front-engine
+			 * earlier in the chain (before deletion), leave it alone */
 			for (; v != NULL; v = tmp) {
 				tmp = GetNextVehicle(v);
 
-				if (RailVehInfo(v->engine_type)->flags & RVI_MULTIHEAD) {
-					if (IS_FIRSTHEAD_SPRITE(v->spritenum)) {
-						/* Always delete newly encountered front-engines */
-						enf_count--;
-					} else if (enr_count > enf_count) {
-						/* More rear engines than front engines means this rear-engine does
-						 * not belong to any front-engine; delete */
-						enr_count--;
-					} else {
-						/* Otherwise leave it alone */
+				if (IsMultiheaded(v)) {
+					if (IsTrainEngine(v)) {
+						/* We got a front engine of a multiheaded set. Now we will sell the rear end too */
+						Vehicle *rear = v->u.rail.other_multiheaded_part;
+
+						if (rear != NULL) {
+							cost -= rear->value;
+							if (flags & DC_EXEC) {
+								first = UnlinkWagon(rear, first);
+								DeleteVehicle(rear);
+							}
+						}
+					} else if (v->u.rail.other_multiheaded_part != NULL) {
+						/* The front to this engine is earlier in this train. Do nothing */
 						continue;
 					}
 				}
@@ -1249,9 +1331,12 @@
 
 			/* 3. If it is still a valid train after selling, update its acceleration and cached values */
 			if ((flags & DC_EXEC) && first != NULL) {
+				NormaliseTrainConsist(first);
 				TrainConsistChanged(first);
-				if (first->subtype == TS_Front_Engine)
+				if (IsFrontEngine(first))
 					UpdateTrainAcceleration(first);
+				InvalidateWindow(WC_VEHICLE_DETAILS, first->index);
+				InvalidateWindow(WC_VEHICLE_REFIT, first->index);
 			}
 		} break;
 	}
@@ -2620,7 +2705,7 @@
 			// tracks over roads, do owner check of tracks
 			return
 				IsTileOwner(tile, v->owner) && (
-					v->subtype != TS_Front_Engine ||
+					!IsFrontEngine(v) ||
 					IsCompatibleRail(v->u.rail.railtype, GB(_m[tile].m4, 0, 4))
 				);
 
@@ -2630,7 +2715,7 @@
 
 	return
 		IsTileOwner(tile, v->owner) && (
-			v->subtype != TS_Front_Engine ||
+			!IsFrontEngine(v) ||
 			IsCompatibleRail(v->u.rail.railtype, GetRailType(tile))
 		);
 }
@@ -2741,7 +2826,7 @@
 
 /*
  * Checks whether the specified train has a collision with another vehicle. If
- * so, destroys this vehicle, and the other vehicle if its subtype is 0 (TS_Front_Engine).
+ * so, destroys this vehicle, and the other vehicle if its subtype has TS_Front.
  * Reports the incident in a flashy news item, modifies station ratings and
  * plays a sound.
  */
@@ -2778,7 +2863,7 @@
 		num += 2 + CountPassengersInTrain(coll);
 
 	SetVehicleCrashed(v);
-	if (coll->subtype == TS_Front_Engine) SetVehicleCrashed(coll);
+	if (IsFrontEngine(coll)) SetVehicleCrashed(coll);
 
 	SetDParam(0, num);
 	AddNewsItem(STR_8868_TRAIN_CRASH_DIE_IN_FIREBALL,
@@ -2800,7 +2885,7 @@
 {
 	const VehicleAtSignalData* vasd = data;
 
-	if (v->type == VEH_Train && v->subtype == TS_Front_Engine &&
+	if (v->type == VEH_Train && IsFrontEngine(v) &&
 			v->tile == vasd->tile) {
 		byte diff = (v->direction - vasd->direction + 2) & 7;
 
@@ -2970,7 +3055,7 @@
 					goto invalid_rail;
 				}
 
-				if (v->subtype == TS_Front_Engine) v->load_unload_time_rem = 0;
+				if (IsFrontEngine(v)) v->load_unload_time_rem = 0;
 
 				if (!(r&0x4)) {
 					v->tile = gp.new_tile;
@@ -2978,7 +3063,7 @@
 					assert(v->u.rail.track);
 				}
 
-				if (v->subtype == TS_Front_Engine)
+				if (IsFrontEngine(v))
 				TrainMovedChangeSignals(gp.new_tile, enterdir);
 
 				/* Signals can only change when the first
@@ -3433,13 +3518,13 @@
 
 	v->tick_counter++;
 
-	if (v->subtype == TS_Front_Engine) {
+	if (IsFrontEngine(v)) {
 		TrainLocoHandler(v, false);
 
 		// make sure vehicle wasn't deleted.
-		if (v->type == VEH_Train && v->subtype == TS_Front_Engine)
+		if (v->type == VEH_Train && IsFrontEngine(v))
 			TrainLocoHandler(v, true);
-	} else if (v->subtype == TS_Free_Car && HASBITS(v->vehstatus, VS_CRASHED)) {
+	} else if (IsFreeWagon(v) && HASBITS(v->vehstatus, VS_CRASHED)) {
 		// Delete flooded standalone wagon
 		if (++v->u.rail.crash_anim_pos >= 4400)
 			DeleteVehicle(v);
@@ -3460,7 +3545,7 @@
 {
 	SetSignalsOnBothDir(tile, _depot_track_ind[GB(_m[tile].m5, 0, 2)]);
 
-	if (v->subtype != TS_Front_Engine) v = GetFirstVehicleInChain(v);
+	if (!IsFrontEngine(v)) v = GetFirstVehicleInChain(v);
 
 	VehicleServiceInDepot(v);
 
@@ -3565,7 +3650,7 @@
 	if ((++v->day_counter & 7) == 0)
 		DecreaseVehicleValue(v);
 
-	if (v->subtype == TS_Front_Engine) {
+	if (IsFrontEngine(v)) {
 		CheckVehicleBreakdown(v);
 		AgeVehicle(v);
 
@@ -3610,7 +3695,7 @@
 	Vehicle *v;
 
 	FOR_ALL_VEHICLES(v) {
-		if (v->type == VEH_Train && v->subtype == TS_Front_Engine) {
+		if (v->type == VEH_Train && IsFrontEngine(v)) {
 
 			// show warning if train is not generating enough income last 2 years (corresponds to a red icon in the vehicle list)
 			if (_patches.train_income_warn && v->owner == _local_player && v->age >= 730 && v->profit_this_year < 0) {
--- a/train_gui.c	Fri Nov 18 20:28:55 2005 +0000
+++ b/train_gui.c	Fri Nov 18 23:41:03 2005 +0000
@@ -18,6 +18,7 @@
 #include "engine.h"
 #include "vehicle_gui.h"
 #include "depot.h"
+#include "train.h"
 
 int _traininfo_vehicle_pitch = 0;
 
@@ -128,7 +129,7 @@
 	// find a locomotive in the depot.
 	found = NULL;
 	FOR_ALL_VEHICLES(v) {
-		if (v->type == VEH_Train && v->subtype == TS_Front_Engine &&
+		if (v->type == VEH_Train && IsFrontEngine(v) &&
 				v->tile == tile &&
 				v->u.rail.track == 0x80) {
 			if (found != NULL) // must be exactly one.
@@ -392,12 +393,12 @@
 	hnum = 8;
 	FOR_ALL_VEHICLES(v) {
 		if (v->type == VEH_Train &&
-			  (v->subtype == TS_Front_Engine || v->subtype == TS_Free_Car) &&
+			  (IsFrontEngine(v) || IsFreeWagon(v)) &&
 				v->tile == tile &&
 				v->u.rail.track == 0x80) {
 			num++;
 			// determine number of items in the X direction.
-			if (v->subtype == TS_Front_Engine) {
+			if (IsFrontEngine(v)) {
 				hnum = max(hnum, v->u.rail.cached_total_length);
 			}
 		}
@@ -422,7 +423,7 @@
 
 	// draw all trains
 	FOR_ALL_VEHICLES(v) {
-		if (v->type == VEH_Train && v->subtype == TS_Front_Engine &&
+		if (v->type == VEH_Train && IsFrontEngine(v) &&
 				v->tile == tile && v->u.rail.track == 0x80 &&
 				--num < 0 && num >= -w->vscroll.cap) {
 			DrawTrainImage(v, x+21, y, w->hscroll.cap, w->hscroll.pos, WP(w,traindepot_d).sel);
@@ -443,7 +444,7 @@
 
 	// draw all remaining vehicles
 	FOR_ALL_VEHICLES(v) {
-		if (v->type == VEH_Train && v->subtype == TS_Free_Car &&
+		if (v->type == VEH_Train && IsFreeWagon(v) &&
 				v->tile == tile && v->u.rail.track == 0x80 &&
 				--num < 0 && num >= -w->vscroll.cap) {
 			DrawTrainImage(v, x+50, y, w->hscroll.cap - 1, 0, WP(w,traindepot_d).sel);
@@ -482,7 +483,7 @@
 	/* go through all the locomotives */
 	FOR_ALL_VEHICLES(v) {
 		if (v->type == VEH_Train &&
-				v->subtype == TS_Front_Engine &&
+				IsFrontEngine(v) &&
 				v->tile == w->window_number &&
 				v->u.rail.track == 0x80 &&
 				--row < 0) {
@@ -496,7 +497,7 @@
 	/* and then the list of free wagons */
 	FOR_ALL_VEHICLES(v) {
 		if (v->type == VEH_Train &&
-				v->subtype == TS_Free_Car &&
+				IsFreeWagon(v) &&
 				v->tile == w->window_number &&
 				v->u.rail.track == 0x80 &&
 				--row < 0)
@@ -513,7 +514,7 @@
 	d->head = d->wagon = v;
 
 	/* either pressed the flag or the number, but only when it's a loco */
-	if (x < 0 && v->subtype == TS_Front_Engine)
+	if (x < 0 && IsFrontEngine(v))
 		return (x >= -10) ? -2 : -1;
 
 	// skip vehicles that are scrolled off the left side
@@ -526,7 +527,7 @@
 	}
 
 	// if an articulated part was selected, find its parent
-	while (v != NULL && v->subtype == TS_Artic_Part) v = GetPrevVehicleInChain(v);
+	while (v != NULL && IsArticulatedPart(v)) v = GetPrevVehicleInChain(v);
 
 	d->wagon = v;
 
@@ -539,7 +540,7 @@
 
 	v = GetVehicle(sel);
 
-	if (/*v->subtype == TS_Front_Engine ||*/ v == wagon)
+	if (v == wagon)
 		return;
 
 	if (wagon == NULL) {
@@ -605,10 +606,10 @@
 	if (v == NULL || v->type != VEH_Train) return;
 
 	// for train vehicles: subtype 0 for locs and not zero for others
-	if (v->subtype != TS_Front_Engine) {
+	if (!IsFrontEngine(v)) {
 		v = GetFirstVehicleInChain(v);
 		// Do nothing when clicking on a train in depot with no loc attached
-		if (v->subtype != TS_Front_Engine) return;
+		if (!IsFrontEngine(v)) return;
 	}
 
 	DoCommandP(w->window_number, v->index, _ctrl_pressed ? 1 : 0, CcCloneTrain,
@@ -707,7 +708,7 @@
 
 			sell_cmd = (e->click.widget == 5 || _ctrl_pressed) ? 1 : 0;
 
-			if (v->subtype != TS_Front_Engine) {
+			if (!IsFrontEngine(v)) {
 				DoCommandP(v->tile, v->index, sell_cmd, NULL, CMD_SELL_RAIL_WAGON | CMD_MSG(STR_8839_CAN_T_SELL_RAILROAD_VEHICLE));
 			} else {
 				_backup_orders_tile = v->tile;
@@ -728,7 +729,7 @@
 						sel != INVALID_VEHICLE) {
 					if (gdvp.wagon == NULL || gdvp.wagon->index != sel) {
 						TrainDepotMoveVehicle(gdvp.wagon, sel, gdvp.head);
-					} else if (gdvp.head != NULL && gdvp.head->subtype == TS_Front_Engine) {
+					} else if (gdvp.head != NULL && IsFrontEngine(gdvp.head)) {
 						ShowTrainViewWindow(gdvp.head);
 					}
 				}
@@ -1201,7 +1202,7 @@
 					DrawTrainImage(u, x + WagonLengthToPixels(dx), y, 1, 0, INVALID_VEHICLE);
 					dx += u->u.rail.cached_veh_length;
 					u = u->next;
-				} while (u != NULL && u->subtype == TS_Artic_Part);
+				} while (u != NULL && IsArticulatedPart(u));
 				_train_details_drawer_proc[WP(w,traindetails_d).tab](v, x + WagonLengthToPixels(dx) + 2, y + 2);
 				y += 14;
 			}
@@ -1464,7 +1465,7 @@
 
 				v = GetVehicle(vl->sort_list[id_v].index);
 
-				assert(v->type == VEH_Train && v->subtype == TS_Front_Engine && v->owner == owner);
+				assert(v->type == VEH_Train && IsFrontEngine(v) && v->owner == owner);
 
 				ShowTrainViewWindow(v);
 			}
--- a/tunnelbridge_cmd.c	Fri Nov 18 20:28:55 2005 +0000
+++ b/tunnelbridge_cmd.c	Fri Nov 18 23:41:03 2005 +0000
@@ -22,6 +22,7 @@
 #include "debug.h"
 #include "variables.h"
 #include "bridge.h"
+#include "train.h"
 
 #include "table/bridge_land.h"
 
@@ -1479,7 +1480,7 @@
 			vdir = v->direction >> 1;
 
 			if (v->u.rail.track != 0x40 && dir == vdir) {
-				if (v->subtype == TS_Front_Engine && fc == _tunnel_fractcoord_1[dir]) {
+				if (IsFrontEngine(v) && fc == _tunnel_fractcoord_1[dir]) {
 					if (v->spritenum < 4)
 						SndPlayVehicleFx(SND_05_TRAIN_THROUGH_TUNNEL, v);
 					return 0;
@@ -1534,7 +1535,7 @@
 			}
 		}
 	} else if (_m[tile].m5 & 0x80) {
-		if (v->type == VEH_Road || (v->type == VEH_Train && v->subtype == TS_Front_Engine)) {
+		if (v->type == VEH_Road || (v->type == VEH_Train && IsFrontEngine(v))) {
 			uint h;
 
 			if (GetTileSlope(tile, &h) != 0)
--- a/vehicle.c	Fri Nov 18 20:28:55 2005 +0000
+++ b/vehicle.c	Fri Nov 18 23:41:03 2005 +0000
@@ -23,6 +23,7 @@
 #include "station.h"
 #include "gui.h"
 #include "rail.h"
+#include "train.h"
 
 #define INVALID_COORD (-0x8000)
 #define GEN_HASH(x,y) (((x & 0x1F80)>>7) + ((y & 0xFC0)))
@@ -238,7 +239,7 @@
 			v->left_coord = INVALID_COORD;
 			VehiclePositionChanged(v);
 
-			if (v->type == VEH_Train && (v->subtype == TS_Front_Engine || v->subtype == TS_Free_Car))
+			if (v->type == VEH_Train && (IsFrontEngine(v) || IsFreeWagon(v)))
 				TrainConsistChanged(v);
 		}
 	}
@@ -503,7 +504,7 @@
 	assert(v != NULL);
 
 	if (v->first != NULL) {
-		if (v->first->subtype == TS_Front_Engine) return v->first;
+		if (IsFrontEngine(v->first)) return v->first;
 
 		DEBUG(misc, 0) ("v->first cache faulty. We shouldn't be here, rebuilding cache!");
 	}
@@ -517,7 +518,7 @@
 	while ((u = GetPrevVehicleInChain_bruteforce(v)) != NULL) v = u;
 
 	/* Set the first pointer of all vehicles in that chain to the first wagon */
-	if (v->subtype == TS_Front_Engine)
+	if (IsFrontEngine(v))
 		for (u = (Vehicle *)v; u != NULL; u = u->next) u->first = (Vehicle *)v;
 
 	return (Vehicle*)v;
@@ -1490,10 +1491,13 @@
 {
 	switch (v->type) {
 		case VEH_Train:
-			if (RailVehInfo(v->engine_type)->flags & RVI_MULTIHEAD) {
-				return GetRearEngine(v);
+			if (IsMultiheaded(v)) {
+				if (!IsTrainEngine(v))
+					return v->u.rail.other_multiheaded_part;
+				else
+					return NULL;
 			}
-			if (v->next != NULL && v->next->subtype == TS_Artic_Part) return v->next;
+			if (v->next != NULL && IsArticulatedPart(v->next)) return v->next;
 			break;
 
 		case VEH_Aircraft:
@@ -1538,7 +1542,7 @@
 
 	if (!CheckOwnership(v->owner)) return CMD_ERROR;
 
-	if (v->type == VEH_Train && v->subtype != TS_Front_Engine) return CMD_ERROR;
+	if (v->type == VEH_Train && !IsFrontEngine(v)) return CMD_ERROR;
 
 	// check that we can allocate enough vehicles
 	if (!(flags & DC_EXEC)) {
@@ -1555,7 +1559,13 @@
 	v = v_front;
 
 	do {
-		cost = DoCommand(x, y, v->engine_type, 3, flags, CMD_BUILD_VEH(v->type));
+
+		if (IsMultiheaded(v) && !IsTrainEngine(v)) {
+			/* we build the rear ends of multiheaded trains with the front ones */
+			continue;
+		}
+
+		cost = DoCommand(x, y, v->engine_type, 2, flags, CMD_BUILD_VEH(v->type));
 
 		if (CmdFailed(cost)) return cost;
 
@@ -1570,7 +1580,7 @@
 				}
 			}
 
-			if (v->type == VEH_Train && v->subtype != TS_Front_Engine) {
+			if (v->type == VEH_Train && !IsFrontEngine(v)) {
 				// this s a train car
 				// add this unit to the end of the train
 				DoCommand(x, y, (w_rear->index << 16) | w->index, 1, flags, CMD_MOVE_RAIL_VEHICLE);
@@ -1583,18 +1593,9 @@
 		}
 	} while (v->type == VEH_Train && (v = GetNextVehicle(v)) != NULL);
 
-	if (flags & DC_EXEC) {
-		v = v_front;
-		w = w_front;
-		if (v->type == VEH_Train) {
-			_new_train_id = w_front->index;  // _new_train_id needs to be the front engine due to the callback function
-
-			while (w != NULL && v != NULL) { // checking both just in case something went wrong
-				w->spritenum = v->spritenum; // makes sure that multiheaded engines are facing the correct way
-				w = w->next;
-				v = v->next;
-			}
-		}
+	if (flags & DC_EXEC && v_front->type == VEH_Train) {
+		// _new_train_id needs to be the front engine due to the callback function
+		_new_train_id = w_front->index;
 	}
 	return total_cost;
 }
@@ -1665,12 +1666,12 @@
 
 		MoveVehicleCargo(new_v, old_v);
 
-		if (old_v->type == VEH_Train && old_v->u.rail.first_engine != INVALID_VEHICLE) {
+		if (old_v->type == VEH_Train && !IsFrontEngine(old_v)) {
 			/* this is a railcar. We need to move the car into the train
 			 * We add the new engine after the old one instead of replacing it. It will give the same result anyway when we
 			 * sell the old engine in a moment
 			 */
-			DoCommand(0, 0, (old_v->index << 16) | new_v->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
+			DoCommand(0, 0, (GetPrevVehicleInChain(old_v)->index << 16) | new_v->index, 1, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
 		} else {
 			// copy/clone the orders
 			DoCommand(0, 0, (old_v->index << 16) | new_v->index, IsOrderListShared(old_v) ? CO_SHARE : CO_COPY, DC_EXEC, CMD_CLONE_ORDER);
@@ -1737,6 +1738,11 @@
 		cost = 0;
 		w = v;
 		do {
+			if (w->type == VEH_Train && IsMultiheaded(w) && !IsTrainEngine(w)) {
+				/* we build the rear ends of multiheaded trains with the front ones */
+				continue;
+			}
+
 			// check if the vehicle should be replaced
 			if (!p->engine_renew ||
 					w->age - w->max_age < (p->engine_renew_months * 30) || // replace if engine is too old
@@ -1745,13 +1751,6 @@
 					continue;
 			}
 
-			/* if we are looking at the rear end of a multiheaded locomotive, skip it */
-			if (w->type == VEH_Train) {
-				const RailVehicleInfo *rvi = RailVehInfo(w->engine_type);
-				if (rvi->flags & RVI_MULTIHEAD && rvi->image_index == w->spritenum - 1)
-					continue;
-			}
-
 			/* Now replace the vehicle */
 			temp_cost = ReplaceVehicle(&w, flags);
 
@@ -2109,8 +2108,9 @@
 	SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRail,pbs_end_trackdir), SLE_UINT8, 2, 255),
 	SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRail,shortest_platform[0]), SLE_UINT8, 2, 255),	// added with 16.1, but was blank since 2
 	SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRail,shortest_platform[1]), SLE_UINT8, 2, 255),	// added with 16.1, but was blank since 2
-	// reserve extra space in savegame here. (currently 5 bytes)
-	SLE_CONDARR(NullStruct,null,SLE_FILE_U8 | SLE_VAR_NULL, 5, 2, 255),
+	SLE_CONDREFX(offsetof(Vehicle,u)+offsetof(VehicleRail,other_multiheaded_part), REF_VEHICLE, 2, 255),	// added with 17.1, but was blank since 2
+	// reserve extra space in savegame here. (currently 3 bytes)
+	SLE_CONDARR(NullStruct,null,SLE_FILE_U8 | SLE_VAR_NULL, 3, 2, 255),
 
 	SLE_END()
 };
@@ -2262,6 +2262,105 @@
 	}
 }
 
+/*
+ *  Converts all trains to the new subtype format introduced in savegame 16.2
+ *  It also links multiheaded engines or make them forget they are multiheaded if no suitable partner is found
+ */
+static inline void ConvertOldMultiheadToNew(void)
+{
+	Vehicle *v;
+	FOR_ALL_VEHICLES(v) {
+		if (v->type == VEH_Train) {
+			v->u.rail.other_multiheaded_part = NULL;
+			SETBIT(v->subtype, 7);	// indicates that it's the old format and needs to be converted in the next loop
+		}
+	}
+
+	FOR_ALL_VEHICLES(v) {
+		if (v->type == VEH_Train) {
+			if (HASBIT(v->subtype, 7) && ((v->subtype & ~0x80) == 0 || (v->subtype & ~0x80) == 4)) {
+				Vehicle *u = v;
+
+				BEGIN_ENUM_WAGONS(u)
+					const RailVehicleInfo *rvi = RailVehInfo(u->engine_type);
+				CLRBIT(u->subtype, 7);
+				switch (u->subtype) {
+					case 0:	/* TS_Front_Engine */
+						if (rvi->flags & RVI_MULTIHEAD) {
+							SetMultiheaded(u);
+						}
+						SetFrontEngine(u);
+						SetTrainEngine(u);
+						break;
+					case 1:	/* TS_Artic_Part */
+						u->subtype = 0;
+						SetArticulatedPart(u);
+						break;
+					case 2:	/* TS_Not_First */
+						u->subtype = 0;
+						if (rvi->flags & RVI_WAGON) {
+							// normal wagon
+							SetTrainWagon(u);
+							break;
+						}
+							if (rvi->flags & RVI_MULTIHEAD && rvi->image_index == u->spritenum - 1) {
+								// rear end of a multiheaded engine
+								SetMultiheaded(u);
+								break;
+							}
+							if (rvi->flags & RVI_MULTIHEAD) {
+								SetMultiheaded(u);
+							}
+							SetTrainEngine(u);
+						break;
+					case 4:	/* TS_Free_Car */
+						u->subtype = 0;
+						SetTrainWagon(u);
+						SetFreeWagon(u);
+						break;
+					default: NOT_REACHED(); break;
+				}
+				END_ENUM_WAGONS(u)
+					u = v;
+				BEGIN_ENUM_WAGONS(u)
+					const RailVehicleInfo *rvi = RailVehInfo(u->engine_type);
+
+				if (u->u.rail.other_multiheaded_part != NULL) continue;
+
+				if (rvi->flags & RVI_MULTIHEAD) {
+					if (!IsTrainEngine(u)) {
+						/* we got a rear car without a front car. We will convert it to a front one */
+						SetTrainEngine(u);
+						u->spritenum--;
+					}
+
+					{
+						Vehicle *w;
+
+						for(w = u->next; w != NULL && (w->engine_type != u->engine_type || w->u.rail.other_multiheaded_part != NULL); w = GetNextVehicle(w));
+						if (w != NULL) {
+							/* we found a car to partner with this engine. Now we will make sure it face the right way */
+							if (IsTrainEngine(w)) {
+								ClearTrainEngine(w);
+								w->spritenum++;
+							}
+						}
+
+						if (w != NULL) {
+							w->u.rail.other_multiheaded_part = u;
+							u->u.rail.other_multiheaded_part = w;
+						} else {
+							/* we got a front car and no rear cars. We will fake this one for forget that it should have been multiheaded */
+							ClearMultiheaded(u);
+						}
+					}
+				}
+				END_ENUM_WAGONS(u)
+			}
+		}
+	}
+}
+
 // Will be called when vehicles need to be loaded.
 static void Load_VEHS(void)
 {
@@ -2311,6 +2410,11 @@
 			}
 		}
 	}
+
+	/* Connect front and rear engines of multiheaded trains and converts subtype to the new format */
+	if (_sl_full_version < 0x1101) {
+		ConvertOldMultiheadToNew();
+	}
 }
 
 const ChunkHandler _veh_chunk_handlers[] = {
--- a/vehicle.h	Fri Nov 18 20:28:55 2005 +0000
+++ b/vehicle.h	Fri Nov 18 23:41:03 2005 +0000
@@ -27,14 +27,6 @@
 	VS_CRASHED = 0x80,
 };
 
-// 1 and 3 do not appear to be used
-typedef enum TrainSubtypes {
-	TS_Front_Engine = 0, // Leading engine of a train
-	TS_Artic_Part = 1,   // Articulated part of an engine
-	TS_Not_First = 2,    // Wagon or additional engine
-	TS_Free_Car = 4,     // First in a wagon chain (in depot)
-} TrainSubtype;
-
 /* Effect vehicle types */
 typedef enum EffectVehicle {
 	EV_CHIMNEY_SMOKE   = 0,
@@ -93,6 +85,9 @@
 	  *   skip station and alike by setting it to 0. That way we will ensure that a complete loop is used to find the shortest station
 	  */
 	byte shortest_platform[2];
+
+	// Link between the two ends of a multiheaded engine
+	Vehicle *other_multiheaded_part;
 } VehicleRail;
 
 enum {
@@ -311,7 +306,6 @@
 void CheckVehicleBreakdown(Vehicle *v);
 void AgeVehicle(Vehicle *v);
 void VehicleEnteredDepotThisTick(Vehicle *v);
-Vehicle* GetRearEngine(const Vehicle* v);
 
 void BeginVehicleMove(Vehicle *v);
 void EndVehicleMove(Vehicle *v);
@@ -399,41 +393,6 @@
 	return index < GetVehiclePoolSize();
 }
 
-/**
- * Get the next real (non-articulated part) vehicle in the consist.
- * @param v Vehicle.
- * @return Next vehicle in the consist.
- */
-static inline Vehicle *GetNextVehicle(const Vehicle *v)
-{
-	Vehicle *u = v->next;
-	while (u != NULL && u->subtype == TS_Artic_Part) {
-		u = u->next;
-	}
-	return u;
-}
-
-/**
- * Check if an engine has an articulated part.
- * @param v Vehicle.
- * @return True if the engine has an articulated part.
- */
-static inline bool EngineHasArticPart(const Vehicle *v)
-{
-	return (v->next != NULL && v->next->subtype == TS_Artic_Part);
-}
-
-/**
- * Get the last part of a multi-part engine.
- * @param v Vehicle.
- * @return Last part of the engine.
- */
-static inline Vehicle *GetLastEnginePart(Vehicle *v)
-{
-	while (EngineHasArticPart(v)) v = v->next;
-	return v;
-}
-
 /* Returns order 'index' of a vehicle or NULL when it doesn't exists */
 static inline Order *GetVehicleOrder(const Vehicle *v, int index)
 {
--- a/vehicle_gui.c	Fri Nov 18 20:28:55 2005 +0000
+++ b/vehicle_gui.c	Fri Nov 18 23:41:03 2005 +0000
@@ -18,6 +18,7 @@
 #include "variables.h"
 #include "vehicle_gui.h"
 #include "viewport.h"
+#include "train.h"
 
 Sorting _sorting;
 
@@ -105,7 +106,7 @@
 
 void BuildVehicleList(vehiclelist_d* vl, int type, PlayerID owner, StationID station)
 {
-	uint subtype = (type != VEH_Aircraft) ? TS_Front_Engine : 2;
+	uint subtype = (type != VEH_Aircraft) ? Train_Front : 2;
 	uint n = 0;
 	uint i;
 
@@ -122,7 +123,9 @@
 	if (station != INVALID_STATION) {
 		const Vehicle *v;
 		FOR_ALL_VEHICLES(v) {
-			if (v->type == type && v->subtype <= subtype) {
+			if (v->type == type && (
+				(type == VEH_Train && IsFrontEngine(v)) ||
+				(type != VEH_Train && v->subtype <= subtype))) {
 				const Order *order;
 
 				FOR_VEHICLE_ORDERS(v, order) {
@@ -138,7 +141,9 @@
 	} else {
 		const Vehicle *v;
 		FOR_ALL_VEHICLES(v) {
-			if (v->type == type && v->subtype <= subtype && v->owner == owner) {
+			if (v->type == type && v->owner == owner && (
+				(type == VEH_Train && IsFrontEngine(v)) ||
+				(type != VEH_Train && v->subtype <= subtype))) {
 				_vehicle_sort[n].index = v->index;
 				_vehicle_sort[n].owner = v->owner;
 				++n;
--- a/viewport.c	Fri Nov 18 20:28:55 2005 +0000
+++ b/viewport.c	Fri Nov 18 23:41:03 2005 +0000
@@ -19,6 +19,7 @@
 #include "signs.h"
 #include "waypoint.h"
 #include "variables.h"
+#include "train.h"
 
 #define VIEWPORT_DRAW_MEM (65536 * 2)
 
@@ -1700,7 +1701,7 @@
 
 static void SafeShowTrainViewWindow(const Vehicle* v)
 {
-  if (v->subtype != TS_Front_Engine) v = GetFirstVehicleInChain(v);
+  if (!IsFrontEngine(v)) v = GetFirstVehicleInChain(v);
   ShowTrainViewWindow(v);
 }
 
--- a/water_cmd.c	Fri Nov 18 20:28:55 2005 +0000
+++ b/water_cmd.c	Fri Nov 18 23:41:03 2005 +0000
@@ -15,6 +15,7 @@
 #include "sound.h"
 #include "depot.h"
 #include "vehicle_gui.h"
+#include "train.h"
 
 const SpriteID _water_shore_sprites[15] = {
 	0,
@@ -598,7 +599,7 @@
 
 			v = GetFirstVehicleInChain(v);
 			u = v;
-			if (v->subtype == TS_Front_Engine) pass = 4; // driver
+			if (IsFrontEngine(v)) pass = 4; // driver
 
 			// crash all wagons, and count passangers
 			BEGIN_ENUM_WAGONS(v)