changeset 17194:b590eca5d805 draft

(svn r21933) -Codechange: Split cur_order_index into cur_auto_order_index and cur_real_order_index to keep track of the current real order in an unambiguous way. -Fix [FS#4440]: Automatic orders behave now stable wrt. service orders and are not added or removed depending on the need of servicing. -Fix: Various other issues with automatic orders, e.g. vehicles getting stuck with "no orders" when there are automatic orders at the end of the order list.
author frosch <frosch@openttd.org>
date Mon, 31 Jan 2011 20:44:15 +0000
parents f84b0a9115e7
children 42bf74a7086d
files src/ai/api/ai_order.cpp src/newgrf_engine.cpp src/order_backup.cpp src/order_cmd.cpp src/order_gui.cpp src/roadveh_cmd.cpp src/saveload/afterload.cpp src/saveload/oldloader_sl.cpp src/saveload/saveload.cpp src/saveload/vehicle_sl.cpp src/ship_cmd.cpp src/timetable_cmd.cpp src/timetable_gui.cpp src/train_cmd.cpp src/vehicle.cpp src/vehicle_base.h src/vehicle_gui.cpp
diffstat 17 files changed, 242 insertions(+), 97 deletions(-) [+]
line wrap: on
line diff
--- a/src/ai/api/ai_order.cpp
+++ b/src/ai/api/ai_order.cpp
@@ -132,7 +132,7 @@
 	if (!AIVehicle::IsValidVehicle(vehicle_id)) return ORDER_INVALID;
 
 	if (order_position == ORDER_CURRENT) {
-		int cur_order_pos = ::Vehicle::Get(vehicle_id)->cur_order_index;
+		int cur_order_pos = ::Vehicle::Get(vehicle_id)->cur_real_order_index;
 		const Order *order = ::Vehicle::Get(vehicle_id)->GetOrder(0);
 		if (order == NULL) return ORDER_INVALID;
 		int num_automatic_orders = 0;
--- a/src/newgrf_engine.cpp
+++ b/src/newgrf_engine.cpp
@@ -690,7 +690,7 @@
 		case 0x0A: return v->current_order.MapOldOrder();
 		case 0x0B: return v->current_order.GetDestination();
 		case 0x0C: return v->GetNumOrders();
-		case 0x0D: return v->cur_order_index;
+		case 0x0D: return v->cur_real_order_index;
 		case 0x10:
 		case 0x11: {
 			uint ticks;
--- a/src/order_backup.cpp
+++ b/src/order_backup.cpp
@@ -44,7 +44,7 @@
 {
 	this->user             = user;
 	this->tile             = v->tile;
-	this->orderindex       = v->cur_order_index;
+	this->orderindex       = v->cur_auto_order_index;
 	this->group            = v->group_id;
 	this->service_interval = v->service_interval;
 
@@ -87,7 +87,10 @@
 	}
 
 	uint num_orders = v->GetNumOrders();
-	if (num_orders != 0) v->cur_order_index = this->orderindex % num_orders;
+	if (num_orders != 0) {
+		v->cur_real_order_index = v->cur_auto_order_index = this->orderindex % num_orders;
+		v->UpdateRealOrderIndex();
+	}
 	v->service_interval = this->service_interval;
 
 	/* Restore vehicle group */
--- a/src/order_cmd.cpp
+++ b/src/order_cmd.cpp
@@ -824,12 +824,21 @@
 		assert(v->orders.list == u->orders.list);
 
 		/* If there is added an order before the current one, we need
-		 * to update the selected order */
-		if (sel_ord <= u->cur_order_index) {
-			uint cur = u->cur_order_index + 1;
+		 * to update the selected order. We do not change automatic/real order indices though.
+		 * If the new order is between the current auto order and real order, the auto order will
+		 * later skip the inserted order. */
+		if (sel_ord <= u->cur_real_order_index) {
+			uint cur = u->cur_real_order_index + 1;
 			/* Check if we don't go out of bound */
 			if (cur < u->GetNumOrders()) {
-				u->cur_order_index = cur;
+				u->cur_real_order_index = cur;
+			}
+		}
+		if (sel_ord <= u->cur_auto_order_index) {
+			uint cur = u->cur_auto_order_index + 1;
+			/* Check if we don't go out of bound */
+			if (cur < u->GetNumOrders()) {
+				u->cur_auto_order_index = cur;
 			}
 		}
 		/* Update any possible open window of the vehicle */
@@ -917,14 +926,31 @@
 
 		/* NON-stop flag is misused to see if a train is in a station that is
 		 * on his order list or not */
-		if (sel_ord == u->cur_order_index && u->current_order.IsType(OT_LOADING)) {
+		if (sel_ord == u->cur_real_order_index && u->current_order.IsType(OT_LOADING)) {
 			u->current_order.SetNonStopType(ONSF_STOP_EVERYWHERE);
 			/* When full loading, "cancel" that order so the vehicle doesn't
 			 * stay indefinitely at this station anymore. */
 			if (u->current_order.GetLoadType() & OLFB_FULL_LOAD) u->current_order.SetLoadType(OLF_LOAD_IF_POSSIBLE);
 		}
 
-		if (sel_ord < u->cur_order_index) u->cur_order_index--;
+		if (sel_ord < u->cur_real_order_index) {
+			u->cur_real_order_index--;
+		} else if (sel_ord == u->cur_real_order_index) {
+			u->UpdateRealOrderIndex();
+		}
+
+		if (sel_ord < u->cur_auto_order_index) {
+			u->cur_auto_order_index--;
+		} else if (sel_ord == u->cur_auto_order_index) {
+			/* Make sure the index is valid */
+			if (u->cur_auto_order_index >= u->GetNumOrders()) u->cur_auto_order_index = 0;
+
+			/* Skip non-automatic orders for the auto-order-index (e.g. if the current auto order was deleted */
+			while (u->cur_auto_order_index != u->cur_real_order_index && !u->GetOrder(u->cur_auto_order_index)->IsType(OT_AUTOMATIC)) {
+				u->cur_auto_order_index++;
+				if (u->cur_auto_order_index >= u->GetNumOrders()) u->cur_auto_order_index = 0;
+			}
+		}
 
 		/* Update any possible open window of the vehicle */
 		InvalidateVehicleOrder(u, sel_ord | (INVALID_VEH_ORDER_ID << 8));
@@ -965,13 +991,14 @@
 
 	Vehicle *v = Vehicle::GetIfValid(veh_id);
 
-	if (v == NULL || !v->IsPrimaryVehicle() || sel_ord == v->cur_order_index || sel_ord >= v->GetNumOrders() || v->GetNumOrders() < 2) return CMD_ERROR;
+	if (v == NULL || !v->IsPrimaryVehicle() || sel_ord == v->cur_auto_order_index || sel_ord >= v->GetNumOrders() || v->GetNumOrders() < 2) return CMD_ERROR;
 
 	CommandCost ret = CheckOwnership(v->owner);
 	if (ret.Failed()) return ret;
 
 	if (flags & DC_EXEC) {
-		v->cur_order_index = sel_ord;
+		v->cur_auto_order_index = v->cur_real_order_index = sel_ord;
+		v->UpdateRealOrderIndex();
 
 		if (v->current_order.IsType(OT_LOADING)) v->LeaveStation();
 
@@ -1027,13 +1054,36 @@
 		DeleteOrderWarnings(u);
 
 		for (; u != NULL; u = u->NextShared()) {
-			/* Update the current order */
-			if (u->cur_order_index == moving_order) {
-				u->cur_order_index = target_order;
-			} else if (u->cur_order_index > moving_order && u->cur_order_index <= target_order) {
-				u->cur_order_index--;
-			} else if (u->cur_order_index < moving_order && u->cur_order_index >= target_order) {
-				u->cur_order_index++;
+			/* Update the current order.
+			 * There are multiple ways to move orders, which result in cur_auto_order_index
+			 * and cur_real_order_index to not longer make any sense. E.g. moving another
+			 * real order between them.
+			 *
+			 * Basically one could choose to preserve either of them, but not both.
+			 * While both ways are suitable in this or that case from a human point of view, neither
+			 * of them makes really sense.
+			 * However, from an AI point of view, preserving cur_real_order_index is the most
+			 * predictable and transparent behaviour.
+			 *
+			 * With that decision it basically does not matter what we do to cur_auto_order_index.
+			 * If we change orders between the auto- and real-index, the auto orders are mostly likely
+			 * completely out-dated anyway. So, keep it simple and just keep cur_auto_order_index as well.
+			 * The worst which can happen is that a lot of automatic orders are removed when reaching current_order.
+			 */
+			if (u->cur_real_order_index == moving_order) {
+				u->cur_real_order_index = target_order;
+			} else if (u->cur_real_order_index > moving_order && u->cur_real_order_index <= target_order) {
+				u->cur_real_order_index--;
+			} else if (u->cur_real_order_index < moving_order && u->cur_real_order_index >= target_order) {
+				u->cur_real_order_index++;
+			}
+
+			if (u->cur_auto_order_index == moving_order) {
+				u->cur_auto_order_index = target_order;
+			} else if (u->cur_auto_order_index > moving_order && u->cur_auto_order_index <= target_order) {
+				u->cur_auto_order_index--;
+			} else if (u->cur_auto_order_index < moving_order && u->cur_auto_order_index >= target_order) {
+				u->cur_auto_order_index++;
 			}
 
 			assert(v->orders.list == u->orders.list);
@@ -1289,7 +1339,7 @@
 			 * so do not care and those orders should not be active
 			 * when this function is called.
 			 */
-			if (sel_ord == u->cur_order_index &&
+			if (sel_ord == u->cur_real_order_index &&
 					(u->current_order.IsType(OT_GOTO_STATION) || u->current_order.IsType(OT_LOADING)) &&
 					u->current_order.GetLoadType() != order->GetLoadType()) {
 				u->current_order.SetLoadType(order->GetLoadType());
@@ -1468,7 +1518,7 @@
 			InvalidateVehicleOrder(u, -2);
 
 			/* If the vehicle already got the current depot set as current order, then update current order as well */
-			if (u->cur_order_index == order_number && (u->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS)) {
+			if (u->cur_real_order_index == order_number && (u->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS)) {
 				u->current_order.SetRefit(cargo, subtype);
 			}
 		}
@@ -1742,7 +1792,7 @@
 		case OT_GOTO_DEPOT:
 			if ((order->GetDepotOrderType() & ODTFB_SERVICE) && !v->NeedsServicing()) {
 				UpdateVehicleTimetable(v, true);
-				v->IncrementOrderIndex();
+				v->IncrementRealOrderIndex();
 				break;
 			}
 
@@ -1771,7 +1821,7 @@
 				}
 
 				UpdateVehicleTimetable(v, true);
-				v->IncrementOrderIndex();
+				v->IncrementRealOrderIndex();
 			} else {
 				if (v->type != VEH_AIRCRAFT) {
 					v->dest_tile = Depot::Get(order->GetDestination())->xy;
@@ -1787,12 +1837,15 @@
 		case OT_CONDITIONAL: {
 			VehicleOrderID next_order = ProcessConditionalOrder(order, v);
 			if (next_order != INVALID_VEH_ORDER_ID) {
+				/* Jump to next_order. cur_auto_order_index becomes exactly that order,
+				 * cur_real_order_index might come after next_order. */
 				UpdateVehicleTimetable(v, false);
-				v->cur_order_index = next_order;
-				v->current_order_time += v->GetOrder(next_order)->travel_time;
+				v->cur_auto_order_index = v->cur_real_order_index = next_order;
+				v->UpdateRealOrderIndex();
+				v->current_order_time += v->GetOrder(v->cur_real_order_index)->travel_time;
 			} else {
 				UpdateVehicleTimetable(v, true);
-				v->IncrementOrderIndex();
+				v->IncrementRealOrderIndex();
 			}
 			break;
 		}
@@ -1802,18 +1855,22 @@
 			return false;
 	}
 
-	assert(v->cur_order_index < v->GetNumOrders());
+	assert(v->cur_auto_order_index < v->GetNumOrders());
+	assert(v->cur_real_order_index < v->GetNumOrders());
 
 	/* Get the current order */
-	order = v->GetNextManualOrder(v->cur_order_index);
+	order = v->GetOrder(v->cur_real_order_index);
+	if (order->IsType(OT_AUTOMATIC)) {
+		assert(v->GetNumManualOrders() == 0);
+		order = NULL;
+	}
+
 	if (order == NULL) {
-		order = v->GetNextManualOrder(0);
-		if (order == NULL) {
-			v->current_order.Free();
-			v->dest_tile = 0;
-			return false;
-		}
+		v->current_order.Free();
+		v->dest_tile = 0;
+		return false;
 	}
+
 	v->current_order = *order;
 	return UpdateOrderDest(v, order, conditional_depth + 1);
 }
@@ -1863,13 +1920,17 @@
 		 * visited station will cause the vehicle to still stop. */
 		v->last_station_visited = v->current_order.GetDestination();
 		UpdateVehicleTimetable(v, true);
-		v->IncrementOrderIndex();
+		v->IncrementAutoOrderIndex();
 	}
 
 	/* Get the current order */
-	if (v->cur_order_index >= v->GetNumOrders()) v->cur_order_index = 0;
+	v->UpdateRealOrderIndex();
 
-	const Order *order = v->GetNextManualOrder(v->cur_order_index);
+	const Order *order = v->GetOrder(v->cur_real_order_index);
+	if (order->IsType(OT_AUTOMATIC)) {
+		assert(v->GetNumManualOrders() == 0);
+		order = NULL;
+	}
 
 	/* If no order, do nothing. */
 	if (order == NULL || (v->type == VEH_AIRCRAFT && !CheckForValidOrders(v))) {
--- a/src/order_gui.cpp
+++ b/src/order_gui.cpp
@@ -195,8 +195,11 @@
 
 	SpriteID sprite = rtl ? SPR_ARROW_LEFT : SPR_ARROW_RIGHT;
 	Dimension sprite_size = GetSpriteSize(sprite);
-	if (v->cur_order_index == order_index) {
-		DrawSprite(sprite, PAL_NONE, rtl ? right - sprite_size.width : left, y + ((int)FONT_HEIGHT_NORMAL - (int)sprite_size.height) / 2);
+	if (v->cur_real_order_index == order_index) {
+		DrawSprite(sprite, PAL_NONE, rtl ? right -     sprite_size.width : left,                     y + ((int)FONT_HEIGHT_NORMAL - (int)sprite_size.height) / 2);
+		DrawSprite(sprite, PAL_NONE, rtl ? right - 2 * sprite_size.width : left + sprite_size.width, y + ((int)FONT_HEIGHT_NORMAL - (int)sprite_size.height) / 2);
+	} else if (v->cur_auto_order_index == order_index) {
+		DrawSprite(sprite, PAL_NONE, rtl ? right -     sprite_size.width : left,                     y + ((int)FONT_HEIGHT_NORMAL - (int)sprite_size.height) / 2);
 	}
 
 	TextColour colour = TC_BLACK;
@@ -207,7 +210,7 @@
 	}
 
 	SetDParam(0, order_index + 1);
-	DrawString(left, rtl ? right - sprite_size.width - 3 : middle, y, STR_ORDER_INDEX, colour, SA_RIGHT | SA_FORCE);
+	DrawString(left, rtl ? right - 2 * sprite_size.width - 3 : middle, y, STR_ORDER_INDEX, colour, SA_RIGHT | SA_FORCE);
 
 	SetDParam(5, STR_EMPTY);
 
@@ -684,10 +687,10 @@
 	void OrderClick_Skip(int i)
 	{
 		/* Don't skip when there's nothing to skip */
-		if (_ctrl_pressed && this->vehicle->cur_order_index == this->OrderGetSel()) return;
+		if (_ctrl_pressed && this->vehicle->cur_auto_order_index == this->OrderGetSel()) return;
 		if (this->vehicle->GetNumOrders() <= 1) return;
 
-		DoCommandP(this->vehicle->tile, this->vehicle->index, _ctrl_pressed ? this->OrderGetSel() : ((this->vehicle->cur_order_index + 1) % this->vehicle->GetNumOrders()),
+		DoCommandP(this->vehicle->tile, this->vehicle->index, _ctrl_pressed ? this->OrderGetSel() : ((this->vehicle->cur_auto_order_index + 1) % this->vehicle->GetNumOrders()),
 				CMD_SKIP_TO_ORDER | CMD_MSG(_ctrl_pressed ? STR_ERROR_CAN_T_SKIP_TO_ORDER : STR_ERROR_CAN_T_SKIP_ORDER));
 	}
 
@@ -1026,7 +1029,7 @@
 
 		bool rtl = _current_text_dir == TD_RTL;
 		SetDParam(0, 99);
-		int index_column_width = GetStringBoundingBox(STR_ORDER_INDEX).width + GetSpriteSize(rtl ? SPR_ARROW_RIGHT : SPR_ARROW_LEFT).width + 3;
+		int index_column_width = GetStringBoundingBox(STR_ORDER_INDEX).width + 2 * GetSpriteSize(rtl ? SPR_ARROW_RIGHT : SPR_ARROW_LEFT).width + 3;
 		int middle = rtl ? r.right - WD_FRAMETEXT_RIGHT - index_column_width : r.left + WD_FRAMETEXT_LEFT + index_column_width;
 
 		int y = r.top + WD_FRAMERECT_TOP;
--- a/src/roadveh_cmd.cpp
+++ b/src/roadveh_cmd.cpp
@@ -525,7 +525,7 @@
 	const Station *st = Station::Get(station);
 	if (!CanVehicleUseStation(this, st)) {
 		/* There is no stop left at the station, so don't even TRY to go there */
-		this->IncrementOrderIndex();
+		this->IncrementRealOrderIndex();
 		return 0;
 	}
 
--- a/src/saveload/afterload.cpp
+++ b/src/saveload/afterload.cpp
@@ -2536,6 +2536,14 @@
 			 * it should have set v->z_pos correctly. */
 			assert(v->tile != TileVirtXY(v->x_pos, v->y_pos) || v->z_pos == GetSlopeZ(v->x_pos, v->y_pos));
 		}
+
+		/* Fill Vehicle::cur_real_order_index */
+		FOR_ALL_VEHICLES(v) {
+			if (!v->IsPrimaryVehicle()) continue;
+
+			v->cur_real_order_index = v->cur_auto_order_index;
+			v->UpdateRealOrderIndex();
+		}
 	}
 
 	/* Road stops is 'only' updating some caches */
--- a/src/saveload/oldloader_sl.cpp
+++ b/src/saveload/oldloader_sl.cpp
@@ -1138,7 +1138,7 @@
 	OCL_VAR ( OC_UINT16,   1, &_old_order ),
 
 	OCL_NULL ( 1 ), ///< num_orders, now calculated
-	OCL_SVAR(  OC_UINT8, Vehicle, cur_order_index ),
+	OCL_SVAR(  OC_UINT8, Vehicle, cur_auto_order_index ),
 	OCL_SVAR(   OC_TILE, Vehicle, dest_tile ),
 	OCL_SVAR( OC_UINT16, Vehicle, load_unload_ticks ),
 	OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, date_of_last_service ),
--- a/src/saveload/saveload.cpp
+++ b/src/saveload/saveload.cpp
@@ -222,8 +222,9 @@
  *  155   21453
  *  156   21728
  *  157   21862
+ *  158   21933
  */
-extern const uint16 SAVEGAME_VERSION = 157; ///< Current savegame version of OpenTTD.
+extern const uint16 SAVEGAME_VERSION = 158; ///< Current savegame version of OpenTTD.
 
 SavegameType _savegame_type; ///< type of savegame we are loading
 
--- a/src/saveload/vehicle_sl.cpp
+++ b/src/saveload/vehicle_sl.cpp
@@ -487,7 +487,8 @@
 		     SLE_VAR(Vehicle, tick_counter,          SLE_UINT8),
 		 SLE_CONDVAR(Vehicle, running_ticks,         SLE_UINT8,                   88, SL_MAX_VERSION),
 
-		     SLE_VAR(Vehicle, cur_order_index,       SLE_UINT8),
+		     SLE_VAR(Vehicle, cur_auto_order_index,  SLE_UINT8),
+		 SLE_CONDVAR(Vehicle, cur_real_order_index,  SLE_UINT8,                  158, SL_MAX_VERSION),
 		/* num_orders is now part of OrderList and is not saved but counted */
 		SLE_CONDNULL(1,                                                            0, 104),
 
--- a/src/ship_cmd.cpp
+++ b/src/ship_cmd.cpp
@@ -234,7 +234,7 @@
 	if (st->dock_tile != INVALID_TILE) {
 		return TILE_ADD(st->dock_tile, ToTileIndexDiff(GetDockOffset(st->dock_tile)));
 	} else {
-		this->IncrementOrderIndex();
+		this->IncrementRealOrderIndex();
 		return 0;
 	}
 }
@@ -480,7 +480,7 @@
 						/* We got within 3 tiles of our target buoy, so let's skip to our
 						 * next order */
 						UpdateVehicleTimetable(v, true);
-						v->IncrementOrderIndex();
+						v->IncrementRealOrderIndex();
 						v->current_order.MakeDummy();
 					} else {
 						/* Non-buoy orders really need to reach the tile */
@@ -500,7 +500,7 @@
 									v->BeginLoading();
 								} else { // leave stations without docks right aways
 									v->current_order.MakeLeaveStation();
-									v->IncrementOrderIndex();
+									v->IncrementRealOrderIndex();
 								}
 							}
 						}
--- a/src/timetable_cmd.cpp
+++ b/src/timetable_cmd.cpp
@@ -33,7 +33,7 @@
 	v->orders.list->UpdateOrderTimetable(delta);
 
 	for (v = v->FirstShared(); v != NULL; v = v->NextShared()) {
-		if (v->cur_order_index == order_number && v->current_order.Equals(*order)) {
+		if (v->cur_real_order_index == order_number && v->current_order.Equals(*order)) {
 			if (is_journey) {
 				v->current_order.travel_time = time;
 			} else {
@@ -248,7 +248,7 @@
 	bool just_started = false;
 
 	/* This vehicle is arriving at the first destination in the timetable. */
-	if (v->cur_order_index == first_manual_order && travelling) {
+	if (v->cur_real_order_index == first_manual_order && travelling) {
 		/* If the start date hasn't been set, or it was set automatically when
 		 * the vehicle last arrived at the first destination, update it to the
 		 * current time. Otherwise set the late counter appropriately to when
@@ -288,10 +288,10 @@
 			 * processing of different orders when filling the timetable. */
 			time_taken = CeilDiv(max(time_taken, 1U), DAY_TICKS) * DAY_TICKS;
 
-			ChangeTimetable(v, v->cur_order_index, time_taken, travelling);
+			ChangeTimetable(v, v->cur_real_order_index, time_taken, travelling);
 		}
 
-		if (v->cur_order_index == first_manual_order && travelling) {
+		if (v->cur_real_order_index == first_manual_order && travelling) {
 			/* If we just started we would have returned earlier and have not reached
 			 * this code. So obviously, we have completed our round: So turn autofill
 			 * off again. */
--- a/src/timetable_gui.cpp
+++ b/src/timetable_gui.cpp
@@ -206,7 +206,7 @@
 		bool travelling = (!v->current_order.IsType(OT_LOADING) || v->current_order.GetNonStopType() == ONSF_STOP_EVERYWHERE);
 		Ticks start_time = _date_fract - v->current_order_time;
 
-		FillTimetableArrivalDepartureTable(v, v->cur_order_index % v->GetNumOrders(), travelling, table, start_time);
+		FillTimetableArrivalDepartureTable(v, v->cur_real_order_index % v->GetNumOrders(), travelling, table, start_time);
 
 		return (travelling && v->lateness_counter < 0);
 	}
@@ -425,7 +425,7 @@
 				if (total_time <= 0 || v->GetNumOrders() <= 1 || !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED)) break;
 
 				TimetableArrivalDeparture *arr_dep = AllocaM(TimetableArrivalDeparture, v->GetNumOrders());
-				const VehicleOrderID cur_order = v->cur_order_index % v->GetNumOrders();
+				const VehicleOrderID cur_order = v->cur_real_order_index % v->GetNumOrders();
 
 				VehicleOrderID earlyID = BuildArrivalDepartureList(v, arr_dep) ? cur_order : (VehicleOrderID)INVALID_VEH_ORDER_ID;
 
--- a/src/train_cmd.cpp
+++ b/src/train_cmd.cpp
@@ -2291,7 +2291,7 @@
 		old_order(_v->current_order),
 		old_dest_tile(_v->dest_tile),
 		old_last_station_visited(_v->last_station_visited),
-		index(_v->cur_order_index)
+		index(_v->cur_real_order_index)
 	{
 	}
 
@@ -2348,7 +2348,7 @@
 			/* Don't increment inside the while because otherwise conditional
 			 * orders can lead to an infinite loop. */
 			++this->index;
-		} while (this->index != this->v->cur_order_index);
+		} while (this->index != this->v->cur_real_order_index);
 
 		return false;
 	}
@@ -2602,7 +2602,7 @@
 	const Station *st = Station::Get(station);
 	if (!(st->facilities & FACIL_TRAIN)) {
 		/* The destination station has no trainstation tiles. */
-		this->IncrementOrderIndex();
+		this->IncrementRealOrderIndex();
 		return 0;
 	}
 
--- a/src/vehicle.cpp
+++ b/src/vehicle.cpp
@@ -1266,7 +1266,7 @@
 	if (v->current_order.IsType(OT_GOTO_DEPOT)) {
 		SetWindowDirty(WC_VEHICLE_VIEW, v->index);
 
-		const Order *real_order = v->GetNextManualOrder(v->cur_order_index);
+		const Order *real_order = v->GetOrder(v->cur_real_order_index);
 		Order t = v->current_order;
 		v->current_order.MakeDummy();
 
@@ -1303,7 +1303,7 @@
 			/* Part of orders */
 			v->DeleteUnreachedAutoOrders();
 			UpdateVehicleTimetable(v, true);
-			v->IncrementOrderIndex();
+			v->IncrementAutoOrderIndex();
 		}
 		if (t.GetDepotActionType() & ODATFB_HALT) {
 			/* Vehicles are always stopped on entering depots. Do not restart this one. */
@@ -1797,11 +1797,25 @@
  */
 void Vehicle::DeleteUnreachedAutoOrders()
 {
-	const Order *order = this->GetOrder(this->cur_order_index);
-	while (order != NULL && order->IsType(OT_AUTOMATIC)) {
-		/* Delete order effectively deletes order, so get the next before deleting it. */
-		order = order->next;
-		DeleteOrder(this, this->cur_order_index);
+	const Order *order = this->GetOrder(this->cur_auto_order_index);
+	while (order != NULL) {
+		if (this->cur_auto_order_index == this->cur_real_order_index) break;
+
+		if (order->IsType(OT_AUTOMATIC)) {
+			/* Delete order effectively deletes order, so get the next before deleting it. */
+			order = order->next;
+			DeleteOrder(this, this->cur_auto_order_index);
+		} else {
+			/* Skip non-automatic orders, e.g. service-orders */
+			order = order->next;
+			this->cur_auto_order_index++;
+		}
+
+		/* Wrap around */
+		if (order == NULL) {
+			order = this->GetOrder(0);
+			this->cur_auto_order_index = 0;
+		}
 	}
 }
 
@@ -1817,7 +1831,7 @@
 			this->current_order.GetDestination() == this->last_station_visited) {
 		this->DeleteUnreachedAutoOrders();
 
-		/* Now cur_order_index points to the destination station, and we can start loading */
+		/* Now both order indices point to the destination station, and we can start loading */
 		this->current_order.MakeLoading(true);
 		UpdateVehicleTimetable(this, true);
 
@@ -1832,14 +1846,14 @@
 		/* We weren't scheduled to stop here. Insert an automatic order
 		 * to show that we are stopping here, but only do that if the order
 		 * list isn't empty. */
-		Order *in_list = this->GetOrder(this->cur_order_index);
+		Order *in_list = this->GetOrder(this->cur_auto_order_index);
 		if (in_list != NULL && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID &&
 				(!in_list->IsType(OT_AUTOMATIC) ||
 				in_list->GetDestination() != this->last_station_visited)) {
 			Order *auto_order = new Order();
 			auto_order->MakeAutomatic(this->last_station_visited);
-			InsertOrder(this, auto_order, this->cur_order_index);
-			if (this->cur_order_index > 0) --this->cur_order_index;
+			InsertOrder(this, auto_order, this->cur_auto_order_index);
+			if (this->cur_auto_order_index > 0) --this->cur_auto_order_index;
 		}
 		this->current_order.MakeLoading(false);
 	}
@@ -1913,7 +1927,7 @@
 		default: return;
 	}
 
-	this->IncrementOrderIndex();
+	this->IncrementAutoOrderIndex();
 }
 
 /**
@@ -1948,7 +1962,7 @@
 		if (flags & DC_EXEC) {
 			/* If the orders to 'goto depot' are in the orders list (forced servicing),
 			 * then skip to the next order; effectively cancelling this forced service */
-			if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementOrderIndex();
+			if (this->current_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) this->IncrementRealOrderIndex();
 
 			this->current_order.MakeDummy();
 			SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
@@ -2261,20 +2275,6 @@
 	this->previous_shared = NULL;
 }
 
-/**
- * Get the next manual (not OT_AUTOMATIC) order after the one at the given index.
- * @param index The index to start searching at.
- * @return The next manual order at or after index or NULL if there is none.
- */
-Order *Vehicle::GetNextManualOrder(int index) const
-{
-	Order *order = this->GetOrder(index);
-	while (order != NULL && order->IsType(OT_AUTOMATIC)) {
-		order = order->next;
-	}
-	return order;
-}
-
 void VehiclesYearlyLoop()
 {
 	Vehicle *v;
--- a/src/vehicle_base.h
+++ b/src/vehicle_base.h
@@ -222,7 +222,8 @@
 
 	byte vehstatus;                     ///< Status
 	Order current_order;                ///< The current order (+ status, like: loading)
-	VehicleOrderID cur_order_index;     ///< The index to the current order
+	VehicleOrderID cur_real_order_index;///< The index to the current real (non-automatic) order
+	VehicleOrderID cur_auto_order_index;///< The index to the current automatic order
 
 	union {
 		OrderList *list;            ///< Pointer to the order list for this vehicle
@@ -549,7 +550,8 @@
 	{
 		this->unitnumber = src->unitnumber;
 
-		this->cur_order_index = src->cur_order_index;
+		this->cur_real_order_index = src->cur_real_order_index;
+		this->cur_auto_order_index = src->cur_auto_order_index;
 		this->current_order = src->current_order;
 		this->dest_tile  = src->dest_tile;
 
@@ -599,15 +601,83 @@
 	void UpdateVisualEffect(bool allow_power_change = true);
 	void ShowVisualEffect() const;
 
+private:
 	/**
-	 * Increments cur_order_index, keeps care of the wrap-around and invalidates the GUI.
+	 * Advance cur_real_order_index to the next real order.
+	 * cur_auto_order_index is not touched.
+	 */
+	void SkipToNextRealOrderIndex()
+	{
+		if (this->GetNumManualOrders() > 0) {
+			/* Advance to next real order */
+			do {
+				this->cur_real_order_index++;
+				if (this->cur_real_order_index >= this->GetNumOrders()) this->cur_real_order_index = 0;
+			} while (this->GetOrder(this->cur_real_order_index)->IsType(OT_AUTOMATIC));
+		} else {
+			this->cur_real_order_index = 0;
+		}
+	}
+
+public:
+	/**
+	 * Increments cur_auto_order_index, keeps care of the wrap-around and invalidates the GUI.
+	 * cur_real_order_index is incremented as well, if needed.
 	 * Note: current_order is not invalidated.
 	 */
-	void IncrementOrderIndex()
+	void IncrementAutoOrderIndex()
+	{
+		if (this->cur_auto_order_index == this->cur_real_order_index) {
+			/* Increment real order index as well */
+			this->SkipToNextRealOrderIndex();
+		}
+
+		assert(this->cur_real_order_index == 0 || this->cur_real_order_index < this->GetNumOrders());
+
+		/* Advance to next automatic order */
+		do {
+			this->cur_auto_order_index++;
+			if (this->cur_auto_order_index >= this->GetNumOrders()) this->cur_auto_order_index = 0;
+		} while (this->cur_auto_order_index != this->cur_real_order_index && !this->GetOrder(this->cur_auto_order_index)->IsType(OT_AUTOMATIC));
+
+		InvalidateVehicleOrder(this, 0);
+	}
+
+	/**
+	 * Advanced cur_real_order_index to the next real order, keeps care of the wrap-around and invalidates the GUI.
+	 * cur_auto_order_index is incremented as well, if it was equal to cur_real_order_index, i.e. cur_real_order_index is skipped
+	 * but not any automatic orders.
+	 * Note: current_order is not invalidated.
+	 */
+	void IncrementRealOrderIndex()
 	{
-		this->cur_order_index++;
-		if (this->cur_order_index >= this->GetNumOrders()) this->cur_order_index = 0;
-		InvalidateVehicleOrder(this, 0);
+		if (this->cur_auto_order_index == this->cur_real_order_index) {
+			/* Increment both real and auto order */
+			this->IncrementAutoOrderIndex();
+		} else {
+			/* Increment real order only */
+			this->SkipToNextRealOrderIndex();
+			InvalidateVehicleOrder(this, 0);
+		}
+	}
+
+	/**
+	 * Skip automatic orders until cur_real_order_index is a non-automatic order.
+	 */
+	void UpdateRealOrderIndex()
+	{
+		/* Make sure the index is valid */
+		if (this->cur_real_order_index >= this->GetNumOrders()) this->cur_real_order_index = 0;
+
+		if (this->GetNumManualOrders() > 0) {
+			/* Advance to next real order */
+			while (this->GetOrder(this->cur_real_order_index)->IsType(OT_AUTOMATIC)) {
+				this->cur_real_order_index++;
+				if (this->cur_real_order_index >= this->GetNumOrders()) this->cur_real_order_index = 0;
+			}
+		} else {
+			this->cur_real_order_index = 0;
+		}
 	}
 
 	/**
@@ -620,8 +690,6 @@
 		return (this->orders.list == NULL) ? NULL : this->orders.list->GetOrderAt(index);
 	}
 
-	Order *GetNextManualOrder(int index) const;
-
 	/**
 	 * Returns the last order of a vehicle, or NULL if it doesn't exists
 	 * @return last order of a vehicle, if available
--- a/src/vehicle_gui.cpp
+++ b/src/vehicle_gui.cpp
@@ -1187,7 +1187,7 @@
 	VehicleOrderID oid = start;
 
 	do {
-		if (oid == v->cur_order_index) DrawString(left, right, y, STR_TINY_RIGHT_ARROW, TC_BLACK);
+		if (oid == v->cur_real_order_index) DrawString(left, right, y, STR_TINY_RIGHT_ARROW, TC_BLACK);
 
 		if (order->IsType(OT_GOTO_STATION)) {
 			SetDParam(0, order->GetDestination());
@@ -1294,7 +1294,7 @@
 			DrawString(text_left, text_right, y, STR_TINY_GROUP, TC_BLACK);
 		}
 
-		if (show_orderlist) DrawSmallOrderList(v, orderlist_left, orderlist_right, y, v->cur_order_index);
+		if (show_orderlist) DrawSmallOrderList(v, orderlist_left, orderlist_right, y, v->cur_real_order_index);
 
 		if (v->IsInDepot()) {
 			str = STR_BLUE_COMMA;