Implement 'full load primary' for station orders

From: Michał Mirosław <mirq-linux@rere.qmqm.pl>


---

 src/ai/api/ai_order.cpp    |   31 +++++++++++++++++++++++++++-
 src/economy.cpp            |   16 +++++++++------
 src/lang/english.txt       |    5 +++++
 src/order_base.h           |   18 ++++++++++++++++-
 src/order_cmd.cpp          |   15 ++++++--------
 src/order_gui.cpp          |   38 +++++++++++++++++++++++++----------
 src/order_type.h           |   18 +++++++++--------
 src/saveload/afterload.cpp |   48 ++++++++++++++++++++------------------------
 src/saveload/order_sl.cpp  |   33 ++++++++++++++++++++++++++++--
 src/saveload/saveload.cpp  |    2 +-
 src/vehicle.cpp            |   32 ++++++++++++++++++++++++++++-
 src/vehicle_base.h         |    2 ++
 12 files changed, 190 insertions(+), 68 deletions(-)


diff --git a/src/ai/api/ai_order.cpp b/src/ai/api/ai_order.cpp
index fba80ec..b17bc08 100644
--- a/src/ai/api/ai_order.cpp
+++ b/src/ai/api/ai_order.cpp
@@ -224,7 +224,20 @@ static const Order *ResolveOrder(VehicleID vehicle_id, AIOrder::OrderPosition or
 			break;
 
 		case OT_GOTO_STATION:
-			order_flags |= (AIOrderFlags)(order->GetLoadType()   << 5);
+			switch (order->GetLoadType()) {
+				case OLF_FULL_LOAD:
+					order_flags |= AIOF_FULL_LOAD;
+					break;
+				case OLF_NO_LOAD:
+					order_flags |= AIOF_NO_LOAD;
+					break;
+				case OLF_FULL_LOAD_ANY:
+				case OLF_FULL_LOAD_PRIMARY:
+					order_flags |= AIOF_FULL_LOAD_ANY;
+					break;
+				default:
+					break;
+			}
 			order_flags |= (AIOrderFlags)(order->GetUnloadType() << 2);
 			break;
 
@@ -460,7 +473,21 @@ static void _DoCommandReturnSetOrderFlags(class AIInstance *instance)
 				return AIObject::DoCommand(0, vehicle_id | (order_position << 16), (order_flags & AIOF_UNLOAD_FLAGS) << 2 | MOF_UNLOAD, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnSetOrderFlags);
 			}
 			if ((current & AIOF_LOAD_FLAGS) != (order_flags & AIOF_LOAD_FLAGS)) {
-				return AIObject::DoCommand(0, vehicle_id | (order_position << 16), (order_flags & AIOF_LOAD_FLAGS) >> 1 | MOF_LOAD, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnSetOrderFlags);
+				OrderLoadFlags olf;
+				if (order_flags & AIOF_NO_LOAD) {
+					olf = OLF_NO_LOAD;
+				} else switch (order_flags & AIOF_LOAD_FLAGS) {
+					case AIOF_FULL_LOAD:
+						olf = OLF_FULL_LOAD;
+						break;
+					case AIOF_FULL_LOAD_ANY:
+						olf = OLF_FULL_LOAD_ANY;
+						break;
+					default:
+						olf = OLF_LOAD_IF_POSSIBLE;
+						break;
+				}
+				return AIObject::DoCommand(0, vehicle_id | (order_position << 16), (olf << 4) | MOF_LOAD, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnSetOrderFlags);
 			}
 			break;
 
diff --git a/src/economy.cpp b/src/economy.cpp
index 0e574f5..8c31040 100644
--- a/src/economy.cpp
+++ b/src/economy.cpp
@@ -1219,7 +1219,7 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
 		}
 
 		/* Do not pick up goods when we have no-load set. */
-		if (u->current_order.GetLoadType() & OLFB_NO_LOAD) continue;
+		if (u->current_order.GetLoadType() == OLF_NO_LOAD) continue;
 
 		/* update stats */
 		int t;
@@ -1319,11 +1319,15 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
 	} else {
 		bool finished_loading = true;
 		if (v->current_order.GetLoadType() & OLFB_FULL_LOAD) {
-			if (v->current_order.GetLoadType() == OLF_FULL_LOAD_ANY) {
-				/* if the aircraft carries passengers and is NOT full, then
-				 * continue loading, no matter how much mail is in */
-				if ((v->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS) && v->cargo_cap > v->cargo.Count()) ||
-						(cargo_not_full && (cargo_full & ~cargo_not_full) == 0)) { // There are stull non-full cargos
+			/* Full and not full means some wagons were not fully loaded, yet. */
+			cargo_full &= ~cargo_not_full;
+
+			if (v->current_order.GetLoadType() == OLF_FULL_LOAD_PRIMARY) {
+				if ((cargo_full & v->GetPrimaryCargoMask()) == 0) {
+					finished_loading = false;
+				}
+			} else if (v->current_order.GetLoadType() == OLF_FULL_LOAD_ANY) {
+				if (cargo_full == 0) {
 					finished_loading = false;
 				}
 			} else if (cargo_not_full != 0) {
diff --git a/src/lang/english.txt b/src/lang/english.txt
index 1204202..7df501e 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -3017,6 +3017,7 @@ STR_ORDER_TOGGLE_FULL_LOAD                                      :{BLACK}Full loa
 STR_ORDER_DROP_LOAD_IF_POSSIBLE                                 :Load if available
 STR_ORDER_DROP_FULL_LOAD_ALL                                    :Full load all cargo
 STR_ORDER_DROP_FULL_LOAD_ANY                                    :Full load any cargo
+STR_ORDER_DROP_FULL_LOAD_PRIMARY                                :Full load primary cargo
 STR_ORDER_DROP_NO_LOADING                                       :No loading
 STR_ORDER_TOOLTIP_FULL_LOAD                                     :{BLACK}Change the loading behaviour of the highlighted order
 
@@ -3095,18 +3096,22 @@ STR_ORDER_STOP_ORDER                                            :(Stop)
 STR_ORDER_GO_TO_STATION                                         :{STRING} {STATION} {STRING}
 
 STR_ORDER_FULL_LOAD                                             :(Full load)
+STR_ORDER_FULL_LOAD_PRIMARY                                     :(Full load primary cargo)
 STR_ORDER_FULL_LOAD_ANY                                         :(Full load any cargo)
 STR_ORDER_NO_LOAD                                               :(No loading)
 STR_ORDER_UNLOAD                                                :(Unload and take cargo)
 STR_ORDER_UNLOAD_FULL_LOAD                                      :(Unload and wait for full load)
+STR_ORDER_UNLOAD_FULL_LOAD_PRIMARY                              :(Unload and wait for primary full load)
 STR_ORDER_UNLOAD_FULL_LOAD_ANY                                  :(Unload and wait for any full load)
 STR_ORDER_UNLOAD_NO_LOAD                                        :(Unload and leave empty)
 STR_ORDER_TRANSFER                                              :(Transfer and take cargo)
 STR_ORDER_TRANSFER_FULL_LOAD                                    :(Transfer and wait for full load)
+STR_ORDER_TRANSFER_FULL_LOAD_PRIMARY                            :(Transfer and wait for primary full load)
 STR_ORDER_TRANSFER_FULL_LOAD_ANY                                :(Transfer and wait for any full load)
 STR_ORDER_TRANSFER_NO_LOAD                                      :(Transfer and leave empty)
 STR_ORDER_NO_UNLOAD                                             :(No unload and take cargo)
 STR_ORDER_NO_UNLOAD_FULL_LOAD                                   :(No unload and wait for full load)
+STR_ORDER_NO_UNLOAD_FULL_LOAD_PRIMARY                           :(No unload and wait for primary full load)
 STR_ORDER_NO_UNLOAD_FULL_LOAD_ANY                               :(No unload and wait for any full load)
 
 STR_ORDER_STOP_LOCATION_NEAR_END                                :[near end]
diff --git a/src/order_base.h b/src/order_base.h
index e75733a..36a250c 100644
--- a/src/order_base.h
+++ b/src/order_base.h
@@ -250,7 +250,9 @@ public:
 	 * Converts this order from an old savegame's version;
 	 * it moves all bits to the new location.
 	 */
-	void ConvertFromOldSavegame();
+	void ConvertFromOldSavegame_pre93();
+	void ConvertFromOldSavegame_93();
+	void ConvertFromOldSavegame_94to131();
 };
 
 /** Shared order list linking together the linked list of orders and the list
@@ -425,4 +427,18 @@ public:
 #define FOR_ALL_ORDER_LISTS_FROM(var, start) FOR_ALL_ITEMS_FROM(OrderList, orderlist_index, var, start)
 #define FOR_ALL_ORDER_LISTS(var) FOR_ALL_ORDER_LISTS_FROM(var, 0)
 
+static inline bool IsOrderLoadTypeValid(unsigned data)
+{
+	switch (data) {
+		case OLF_LOAD_IF_POSSIBLE:
+		case OLF_FULL_LOAD:
+		case OLF_FULL_LOAD_ANY:
+		case OLF_FULL_LOAD_PRIMARY:
+		case OLF_NO_LOAD:
+			return true;
+		default:
+			return false;
+	}
+}
+
 #endif /* ORDER_H */
diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp
index 9172657..617d6d1 100644
--- a/src/order_cmd.cpp
+++ b/src/order_cmd.cpp
@@ -475,13 +475,10 @@ CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
 			if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE && v->type != VEH_TRAIN && v->type != VEH_ROAD) return CMD_ERROR;
 
 			/* No load and no unload are mutual exclusive. */
-			if ((new_order.GetLoadType() & OLFB_NO_LOAD) && (new_order.GetUnloadType() & OUFB_NO_UNLOAD)) return CMD_ERROR;
+			if ((new_order.GetLoadType() == OLF_NO_LOAD) && (new_order.GetUnloadType() & OUFB_NO_UNLOAD)) return CMD_ERROR;
 
 			/* Filter invalid load/unload types. */
-			switch (new_order.GetLoadType()) {
-				case OLF_LOAD_IF_POSSIBLE: case OLFB_FULL_LOAD: case OLF_FULL_LOAD_ANY: case OLFB_NO_LOAD: break;
-				default: return CMD_ERROR;
-			}
+			if (!IsOrderLoadTypeValid(new_order.GetLoadType())) return CMD_ERROR;
 			switch (new_order.GetUnloadType()) {
 				case OUF_UNLOAD_IF_POSSIBLE: case OUFB_UNLOAD: case OUFB_TRANSFER: case OUFB_NO_UNLOAD: break;
 				default: return CMD_ERROR;
@@ -952,7 +949,7 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
 			break;
 
 		case MOF_LOAD:
-			if (data > OLFB_NO_LOAD || data == 1) return CMD_ERROR;
+			if (!IsOrderLoadTypeValid(data)) return CMD_ERROR;
 			if (data == order->GetLoadType()) return CMD_ERROR;
 			break;
 
@@ -1011,14 +1008,14 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
 
 			case MOF_UNLOAD:
 				order->SetUnloadType((OrderUnloadFlags)data);
-				if ((data & OUFB_NO_UNLOAD) != 0 && (order->GetLoadType() & OLFB_NO_LOAD) != 0) {
-					order->SetLoadType((OrderLoadFlags)(order->GetLoadType() & ~OLFB_NO_LOAD));
+				if ((data & OUFB_NO_UNLOAD) != 0 && (order->GetLoadType() == OLF_NO_LOAD)) {
+					order->SetLoadType(OLF_LOAD_IF_POSSIBLE);
 				}
 				break;
 
 			case MOF_LOAD:
 				order->SetLoadType((OrderLoadFlags)data);
-				if ((data & OLFB_NO_LOAD) != 0 && (order->GetUnloadType() & OUFB_NO_UNLOAD) != 0) {
+				if ((data == OLF_NO_LOAD) && (order->GetUnloadType() & OUFB_NO_UNLOAD) != 0) {
 					/* No load + no unload isn't compatible */
 					order->SetUnloadType((OrderUnloadFlags)(order->GetUnloadType() & ~OUFB_NO_UNLOAD));
 				}
diff --git a/src/order_gui.cpp b/src/order_gui.cpp
index 608ac3d..7939361 100644
--- a/src/order_gui.cpp
+++ b/src/order_gui.cpp
@@ -59,36 +59,41 @@ enum OrderWindowWidgets {
 	ORDER_WIDGET_SHARED_ORDER_LIST,
 };
 
+/** Index OrderLoadFlags into _station_load_types[unload][] */
+static const int _station_load_type_index[16] = {
+	0, 4, -1, -1,	// OLF_LOAD_IF_POSSIBLE, OLF_NO_LOAD, x, x
+	-1, 3, 2, 1,	// x, OLF_FULL_LOAD_ANY, OLF_FULL_LOAD_PRIMARY, OLF_FULL_LOAD
+	-1, -1, -1, -1,	// x, x, x, x
+	-1, -1, -1, -1,	// x, x, x, x
+};
+
 /** Order load types that could be given to station orders. */
 static const StringID _station_load_types[][5] = {
 	{
 		STR_EMPTY,
-		INVALID_STRING_ID,
 		STR_ORDER_FULL_LOAD,
+		STR_ORDER_FULL_LOAD_PRIMARY,
 		STR_ORDER_FULL_LOAD_ANY,
 		STR_ORDER_NO_LOAD,
 	}, {
 		STR_ORDER_UNLOAD,
-		INVALID_STRING_ID,
 		STR_ORDER_UNLOAD_FULL_LOAD,
+		STR_ORDER_UNLOAD_FULL_LOAD_PRIMARY,
 		STR_ORDER_UNLOAD_FULL_LOAD_ANY,
 		STR_ORDER_UNLOAD_NO_LOAD,
 	}, {
 		STR_ORDER_TRANSFER,
-		INVALID_STRING_ID,
 		STR_ORDER_TRANSFER_FULL_LOAD,
+		STR_ORDER_TRANSFER_FULL_LOAD_PRIMARY,
 		STR_ORDER_TRANSFER_FULL_LOAD_ANY,
 		STR_ORDER_TRANSFER_NO_LOAD,
 	}, {
 		/* Unload and transfer do not work together. */
 		INVALID_STRING_ID,
-		INVALID_STRING_ID,
-		INVALID_STRING_ID,
-		INVALID_STRING_ID,
 	}, {
 		STR_ORDER_NO_UNLOAD,
-		INVALID_STRING_ID,
 		STR_ORDER_NO_UNLOAD_FULL_LOAD,
+		STR_ORDER_NO_UNLOAD_FULL_LOAD_PRIMARY,
 		STR_ORDER_NO_UNLOAD_FULL_LOAD_ANY,
 		INVALID_STRING_ID,
 	}
@@ -104,12 +109,19 @@ static const StringID _order_non_stop_drowdown[] = {
 
 static const StringID _order_full_load_drowdown[] = {
 	STR_ORDER_DROP_LOAD_IF_POSSIBLE,
-	STR_EMPTY,
 	STR_ORDER_DROP_FULL_LOAD_ALL,
+	STR_ORDER_DROP_FULL_LOAD_PRIMARY,
 	STR_ORDER_DROP_FULL_LOAD_ANY,
 	STR_ORDER_DROP_NO_LOADING,
 	INVALID_STRING_ID
 };
+static const OrderLoadFlags _order_full_load_menu_value[] = {
+	OLF_LOAD_IF_POSSIBLE,
+	OLF_FULL_LOAD,
+	OLF_FULL_LOAD_PRIMARY,
+	OLF_FULL_LOAD_ANY,
+	OLF_NO_LOAD,
+};
 
 static const StringID _order_unload_drowdown[] = {
 	STR_ORDER_DROP_UNLOAD_IF_ACCEPTED,
@@ -226,7 +238,7 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int
 					SetTimetableParams(6, 7, order->wait_time);
 				}
 			} else {
-				SetDParam(3, (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) ? STR_EMPTY : _station_load_types[unload][load]);
+				SetDParam(3, (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) ? STR_EMPTY : _station_load_types[unload][_station_load_type_index[load]]);
 				if (v->type == VEH_TRAIN && (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) == 0) {
 					SetDParam(5, order->GetStopLocation() + STR_ORDER_STOP_LOCATION_NEAR_END);
 				}
@@ -548,11 +560,15 @@ private:
 		VehicleOrderID sel_ord = this->OrderGetSel();
 		const Order *order = this->vehicle->GetOrder(sel_ord);
 
-		if (order == NULL || order->GetLoadType() == load_type) return;
+		if (order == NULL) return;
 
 		if (load_type < 0) {
 			load_type = order->GetLoadType() == OLF_LOAD_IF_POSSIBLE ? OLF_FULL_LOAD_ANY : OLF_LOAD_IF_POSSIBLE;
+		} else {
+			load_type = _order_full_load_menu_value[load_type];
+			if (order->GetLoadType() == load_type) return;
 		}
+
 		DoCommandP(this->vehicle->tile, this->vehicle->index + (sel_ord << 16), MOF_LOAD | (load_type << 4), CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER));
 	}
 
@@ -1075,7 +1091,7 @@ public:
 				if (GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) {
 					this->OrderClick_FullLoad(-1);
 				} else {
-					ShowDropDownMenu(this, _order_full_load_drowdown, this->vehicle->GetOrder(this->OrderGetSel())->GetLoadType(), ORDER_WIDGET_FULL_LOAD, 0, 2);
+					ShowDropDownMenu(this, _order_full_load_drowdown, this->vehicle->GetOrder(this->OrderGetSel())->GetLoadType(), ORDER_WIDGET_FULL_LOAD, 0, 0);
 				}
 				break;
 
diff --git a/src/order_type.h b/src/order_type.h
index 38b5581..a9e2274 100644
--- a/src/order_type.h
+++ b/src/order_type.h
@@ -46,7 +46,7 @@ typedef SimpleTinyEnumT<OrderType, byte> OrderTypeByte;
 
 
 /**
- * Flags related to the unloading order.
+ * Flags related to the unloading order (4 bits).
  */
 enum OrderUnloadFlags {
 	OUF_UNLOAD_IF_POSSIBLE = 0,      ///< Unload all cargo that the station accepts.
@@ -56,13 +56,15 @@ enum OrderUnloadFlags {
 };
 
 /**
- * Flags related to the loading order.
+ * Flags related to the loading order (4 bits).
  */
 enum OrderLoadFlags {
-	OLF_LOAD_IF_POSSIBLE = 0,      ///< Load as long as there is cargo that fits in the train.
-	OLFB_FULL_LOAD       = 1 << 1, ///< Full load the complete the consist.
-	OLF_FULL_LOAD_ANY    = 3,      ///< Full load the a single cargo of the consist.
-	OLFB_NO_LOAD         = 4,      ///< Do not load anything.
+	OLF_LOAD_IF_POSSIBLE  = 0,                ///< Load as long as there is cargo that fits in the train.
+	OLF_NO_LOAD           = 1,                ///< Do not load anything.
+	OLFB_FULL_LOAD        = 1 << 2,           ///< Full load any cargo if the consist (category flag).
+	OLF_FULL_LOAD         = OLFB_FULL_LOAD|3, ///< Full load complete consist.
+	OLF_FULL_LOAD_PRIMARY = OLFB_FULL_LOAD|2, ///< Full load primary (major) cargo of the consist.
+	OLF_FULL_LOAD_ANY     = OLFB_FULL_LOAD|1, ///< Full load any single cargo of the consist.
 };
 
 /**
@@ -139,8 +141,8 @@ enum OrderConditionComparator {
 enum ModifyOrderFlags {
 	MOF_NON_STOP,        ///< Passes an OrderNonStopFlags.
 	MOF_STOP_LOCATION,   ///< Passes an OrderStopLocation.
-	MOF_UNLOAD,          ///< Passes an OrderUnloadType.
-	MOF_LOAD,            ///< Passes an OrderLoadType
+	MOF_UNLOAD,          ///< Passes an OrderUnloadFlags.
+	MOF_LOAD,            ///< Passes an OrderLoadFlags.
 	MOF_DEPOT_ACTION,    ///< Selects the OrderDepotAction
 	MOF_COND_VARIABLE,   ///< A conditional variable changes.
 	MOF_COND_COMPARATOR, ///< A comparator changes.
diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp
index beba40d..f08031b 100644
--- a/src/saveload/afterload.cpp
+++ b/src/saveload/afterload.cpp
@@ -1444,38 +1444,34 @@ bool AfterLoadGame()
 	}
 
 
-	if (CheckSavegameVersion(93)) {
-		/* Rework of orders. */
+	if (CheckSavegameVersion(132)) {
+		void (Order::*ConvertFromOldSavegame)();
 		Order *order;
-		FOR_ALL_ORDERS(order) order->ConvertFromOldSavegame();
-
 		Vehicle *v;
-		FOR_ALL_VEHICLES(v) {
-			if (v->orders.list != NULL && v->orders.list->GetFirstOrder() != NULL && v->orders.list->GetFirstOrder()->IsType(OT_NOTHING)) {
-				v->orders.list->FreeChain();
-				v->orders.list = NULL;
-			}
 
-			v->current_order.ConvertFromOldSavegame();
-			if (v->type == VEH_ROAD && v->IsPrimaryVehicle() && v->FirstShared() == v) {
-				FOR_VEHICLE_ORDERS(v, order) order->SetNonStopType(ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS);
-			}
-		}
-	} else if (CheckSavegameVersion(94)) {
-		/* Unload and transfer are now mutual exclusive. */
-		Order *order;
-		FOR_ALL_ORDERS(order) {
-			if ((order->GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) == (OUFB_UNLOAD | OUFB_TRANSFER)) {
-				order->SetUnloadType(OUFB_TRANSFER);
-				order->SetLoadType(OLFB_NO_LOAD);
+		if (CheckSavegameVersion(93)) {
+			ConvertFromOldSavegame = &Order::ConvertFromOldSavegame_pre93;
+
+			FOR_ALL_VEHICLES(v) {
+				if (v->orders.list != NULL && v->orders.list->GetFirstOrder() != NULL && v->orders.list->GetFirstOrder()->IsType(OT_NOTHING)) {
+					v->orders.list->FreeChain();
+					v->orders.list = NULL;
+				}
 			}
+		} else if (CheckSavegameVersion(94)) {
+			ConvertFromOldSavegame = &Order::ConvertFromOldSavegame_93;
+		} else {
+			ConvertFromOldSavegame = &Order::ConvertFromOldSavegame_94to131;
 		}
 
-		Vehicle *v;
-		FOR_ALL_VEHICLES(v) {
-			if ((v->current_order.GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) == (OUFB_UNLOAD | OUFB_TRANSFER)) {
-				v->current_order.SetUnloadType(OUFB_TRANSFER);
-				v->current_order.SetLoadType(OLFB_NO_LOAD);
+		FOR_ALL_ORDERS(order) (order->*ConvertFromOldSavegame)();
+		FOR_ALL_VEHICLES(v) (v->current_order.*ConvertFromOldSavegame)();
+
+		if (CheckSavegameVersion(93)) {
+			FOR_ALL_ROADVEHICLES(v) {
+				if (v->IsPrimaryVehicle() && v->FirstShared() == v) {
+					FOR_VEHICLE_ORDERS(v, order) order->SetNonStopType(ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS);
+				}
 			}
 		}
 	}
diff --git a/src/saveload/order_sl.cpp b/src/saveload/order_sl.cpp
index 05e7ece..705d517 100644
--- a/src/saveload/order_sl.cpp
+++ b/src/saveload/order_sl.cpp
@@ -15,7 +15,7 @@
 
 #include "saveload.h"
 
-void Order::ConvertFromOldSavegame()
+void Order::ConvertFromOldSavegame_pre93()
 {
 	uint8 old_flags = this->flags;
 	this->flags = 0;
@@ -37,12 +37,12 @@ void Order::ConvertFromOldSavegame()
 	if (this->GetType() != OT_GOTO_DEPOT) {
 		/* Then the load flags */
 		if ((old_flags & 2) != 0) { // OFB_UNLOAD
-			this->SetLoadType(OLFB_NO_LOAD);
+			this->SetLoadType(OLF_NO_LOAD);
 		} else if ((old_flags & 4) == 0) { // !OFB_FULL_LOAD
 			this->SetLoadType(OLF_LOAD_IF_POSSIBLE);
 		} else {
 			/* old OTTD versions stored full_load_any in config file - assume it was enabled when loading */
-			this->SetLoadType(_settings_client.gui.sg_full_load_any || CheckSavegameVersion(22) ? OLF_FULL_LOAD_ANY : OLFB_FULL_LOAD);
+			this->SetLoadType(_settings_client.gui.sg_full_load_any || CheckSavegameVersion(22) ? OLF_FULL_LOAD_ANY : OLF_FULL_LOAD);
 		}
 
 		if (this->IsType(OT_GOTO_STATION)) this->SetStopLocation(OSL_PLATFORM_FAR_END);
@@ -66,6 +66,33 @@ void Order::ConvertFromOldSavegame()
 	}
 }
 
+void Order::ConvertFromOldSavegame_94to131()
+{
+	const OrderLoadFlags full_loads[4] = {
+		OLF_LOAD_IF_POSSIBLE, OLF_LOAD_IF_POSSIBLE /* invalid */,
+		OLF_FULL_LOAD, OLF_FULL_LOAD_ANY
+	};
+
+	uint olt = this->GetLoadType();
+
+	if (olt & 4) {	// OLFB_NO_LOAD
+		this->SetLoadType(OLF_NO_LOAD);
+	} else {	// OLFB_FULL_LOAD, OLF_FULL_LOAD_ANY
+		this->SetLoadType(full_loads[olt & 3]);
+	}
+}
+
+void Order::ConvertFromOldSavegame_93()
+{
+	this->ConvertFromOldSavegame_94to131();
+
+	/* Unload and transfer are now mutual exclusive. */
+	if ((this->GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) == (OUFB_UNLOAD | OUFB_TRANSFER)) {
+		this->SetUnloadType(OUFB_TRANSFER);
+		this->SetLoadType(OLF_NO_LOAD);
+	}
+}
+
 /** Unpacks a order from savegames with version 4 and lower
  * @param packed packed order
  * @return unpacked order
diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp
index d00aefd..b501885 100644
--- a/src/saveload/saveload.cpp
+++ b/src/saveload/saveload.cpp
@@ -47,7 +47,7 @@
 
 #include "saveload_internal.h"
 
-extern const uint16 SAVEGAME_VERSION = 131;
+extern const uint16 SAVEGAME_VERSION = 132;
 
 SavegameType _savegame_type; ///< type of savegame we are loading
 
diff --git a/src/vehicle.cpp b/src/vehicle.cpp
index 9e0e7b8..c91b9e7 100644
--- a/src/vehicle.cpp
+++ b/src/vehicle.cpp
@@ -906,7 +906,7 @@ uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *colour)
 		max += v->cargo_cap;
 		if (v->cargo_cap != 0 && colour != NULL) {
 			unloading += HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) ? 1 : 0;
-			loading |= !(u->current_order.GetLoadType() & OLFB_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
+			loading |= (u->current_order.GetLoadType() != OLF_NO_LOAD) && st->goods[v->cargo_type].days_since_pickup != 255;
 			cars++;
 		}
 	}
@@ -1778,3 +1778,33 @@ bool CanVehicleUseStation(const Vehicle *v, const Station *st)
 
 	return CanVehicleUseStation(v->engine_type, st);
 }
+
+uint32 Vehicle::GetPrimaryCargoMask() const
+{
+	Vehicle *v;
+	uint32 cargo_cap[NUM_CARGO];
+
+	memset(cargo_cap, 0, sizeof(cargo_cap));
+
+	FOR_ALL_VEHICLES_IN_CHAIN(v, this->First()) {
+		if (v->cargo_type < lengthof(cargo_cap)) {
+			cargo_cap[v->cargo_type] += v->cargo_cap;
+		}
+	}
+
+	uint32 max = 0;
+	uint32 mask = 0;
+
+	for (CargoID c = 0; c < lengthof(cargo_cap); ++c) {
+		if (cargo_cap[c] > max) {
+			max = cargo_cap[c];
+			mask = 1 << c;
+		} else if (cargo_cap[c] == max) {
+			mask |= 1 << c;
+		}
+	}
+
+	/* TODO: should cache this */
+	return mask;
+}
+
diff --git a/src/vehicle_base.h b/src/vehicle_base.h
index f01a6e9..bbbc320 100644
--- a/src/vehicle_base.h
+++ b/src/vehicle_base.h
@@ -572,10 +572,12 @@ public:
 	}
 
 	bool IsEngineCountable() const;
+	uint32 GetPrimaryCargoMask() const;
 };
 
 #define FOR_ALL_VEHICLES_FROM(var, start) FOR_ALL_ITEMS_FROM(Vehicle, vehicle_index, var, start)
 #define FOR_ALL_VEHICLES(var) FOR_ALL_VEHICLES_FROM(var, 0)
+#define FOR_ALL_VEHICLES_IN_CHAIN(var, first) for (var = (first); var != NULL; var = var->Next())
 
 /**
  * Class defining several overloaded accessors so we don't
