(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.
This commit is contained in:
frosch 2011-01-31 20:44:15 +00:00
parent 67a5cd0b18
commit a97d52a29a
17 changed files with 242 additions and 97 deletions

View File

@ -132,7 +132,7 @@ static const Order *ResolveOrder(VehicleID vehicle_id, AIOrder::OrderPosition or
if (!AIVehicle::IsValidVehicle(vehicle_id)) return ORDER_INVALID; if (!AIVehicle::IsValidVehicle(vehicle_id)) return ORDER_INVALID;
if (order_position == ORDER_CURRENT) { 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); const Order *order = ::Vehicle::Get(vehicle_id)->GetOrder(0);
if (order == NULL) return ORDER_INVALID; if (order == NULL) return ORDER_INVALID;
int num_automatic_orders = 0; int num_automatic_orders = 0;

View File

@ -690,7 +690,7 @@ static uint32 VehicleGetVariable(const ResolverObject *object, byte variable, by
case 0x0A: return v->current_order.MapOldOrder(); case 0x0A: return v->current_order.MapOldOrder();
case 0x0B: return v->current_order.GetDestination(); case 0x0B: return v->current_order.GetDestination();
case 0x0C: return v->GetNumOrders(); case 0x0C: return v->GetNumOrders();
case 0x0D: return v->cur_order_index; case 0x0D: return v->cur_real_order_index;
case 0x10: case 0x10:
case 0x11: { case 0x11: {
uint ticks; uint ticks;

View File

@ -44,7 +44,7 @@ OrderBackup::OrderBackup(const Vehicle *v, uint32 user)
{ {
this->user = user; this->user = user;
this->tile = v->tile; this->tile = v->tile;
this->orderindex = v->cur_order_index; this->orderindex = v->cur_auto_order_index;
this->group = v->group_id; this->group = v->group_id;
this->service_interval = v->service_interval; this->service_interval = v->service_interval;
@ -87,7 +87,10 @@ void OrderBackup::DoRestore(Vehicle *v)
} }
uint num_orders = v->GetNumOrders(); 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; v->service_interval = this->service_interval;
/* Restore vehicle group */ /* Restore vehicle group */

View File

@ -824,12 +824,21 @@ void InsertOrder(Vehicle *v, Order *new_o, VehicleOrderID sel_ord)
assert(v->orders.list == u->orders.list); assert(v->orders.list == u->orders.list);
/* If there is added an order before the current one, we need /* If there is added an order before the current one, we need
* to update the selected order */ * to update the selected order. We do not change automatic/real order indices though.
if (sel_ord <= u->cur_order_index) { * If the new order is between the current auto order and real order, the auto order will
uint cur = u->cur_order_index + 1; * 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 */ /* Check if we don't go out of bound */
if (cur < u->GetNumOrders()) { 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 */ /* Update any possible open window of the vehicle */
@ -917,14 +926,31 @@ void DeleteOrder(Vehicle *v, VehicleOrderID sel_ord)
/* NON-stop flag is misused to see if a train is in a station that is /* NON-stop flag is misused to see if a train is in a station that is
* on his order list or not */ * 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); u->current_order.SetNonStopType(ONSF_STOP_EVERYWHERE);
/* When full loading, "cancel" that order so the vehicle doesn't /* When full loading, "cancel" that order so the vehicle doesn't
* stay indefinitely at this station anymore. */ * stay indefinitely at this station anymore. */
if (u->current_order.GetLoadType() & OLFB_FULL_LOAD) u->current_order.SetLoadType(OLF_LOAD_IF_POSSIBLE); 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 */ /* Update any possible open window of the vehicle */
InvalidateVehicleOrder(u, sel_ord | (INVALID_VEH_ORDER_ID << 8)); InvalidateVehicleOrder(u, sel_ord | (INVALID_VEH_ORDER_ID << 8));
@ -965,13 +991,14 @@ CommandCost CmdSkipToOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
Vehicle *v = Vehicle::GetIfValid(veh_id); 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); CommandCost ret = CheckOwnership(v->owner);
if (ret.Failed()) return ret; if (ret.Failed()) return ret;
if (flags & DC_EXEC) { 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(); if (v->current_order.IsType(OT_LOADING)) v->LeaveStation();
@ -1027,13 +1054,36 @@ CommandCost CmdMoveOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
DeleteOrderWarnings(u); DeleteOrderWarnings(u);
for (; u != NULL; u = u->NextShared()) { for (; u != NULL; u = u->NextShared()) {
/* Update the current order */ /* Update the current order.
if (u->cur_order_index == moving_order) { * There are multiple ways to move orders, which result in cur_auto_order_index
u->cur_order_index = target_order; * and cur_real_order_index to not longer make any sense. E.g. moving another
} else if (u->cur_order_index > moving_order && u->cur_order_index <= target_order) { * real order between them.
u->cur_order_index--; *
} else if (u->cur_order_index < moving_order && u->cur_order_index >= target_order) { * Basically one could choose to preserve either of them, but not both.
u->cur_order_index++; * 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); assert(v->orders.list == u->orders.list);
@ -1289,7 +1339,7 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
* so do not care and those orders should not be active * so do not care and those orders should not be active
* when this function is called. * 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.IsType(OT_GOTO_STATION) || u->current_order.IsType(OT_LOADING)) &&
u->current_order.GetLoadType() != order->GetLoadType()) { u->current_order.GetLoadType() != order->GetLoadType()) {
u->current_order.SetLoadType(order->GetLoadType()); u->current_order.SetLoadType(order->GetLoadType());
@ -1468,7 +1518,7 @@ CommandCost CmdOrderRefit(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
InvalidateVehicleOrder(u, -2); InvalidateVehicleOrder(u, -2);
/* If the vehicle already got the current depot set as current order, then update current order as well */ /* 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); u->current_order.SetRefit(cargo, subtype);
} }
} }
@ -1742,7 +1792,7 @@ bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth)
case OT_GOTO_DEPOT: case OT_GOTO_DEPOT:
if ((order->GetDepotOrderType() & ODTFB_SERVICE) && !v->NeedsServicing()) { if ((order->GetDepotOrderType() & ODTFB_SERVICE) && !v->NeedsServicing()) {
UpdateVehicleTimetable(v, true); UpdateVehicleTimetable(v, true);
v->IncrementOrderIndex(); v->IncrementRealOrderIndex();
break; break;
} }
@ -1771,7 +1821,7 @@ bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth)
} }
UpdateVehicleTimetable(v, true); UpdateVehicleTimetable(v, true);
v->IncrementOrderIndex(); v->IncrementRealOrderIndex();
} else { } else {
if (v->type != VEH_AIRCRAFT) { if (v->type != VEH_AIRCRAFT) {
v->dest_tile = Depot::Get(order->GetDestination())->xy; v->dest_tile = Depot::Get(order->GetDestination())->xy;
@ -1787,12 +1837,15 @@ bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth)
case OT_CONDITIONAL: { case OT_CONDITIONAL: {
VehicleOrderID next_order = ProcessConditionalOrder(order, v); VehicleOrderID next_order = ProcessConditionalOrder(order, v);
if (next_order != INVALID_VEH_ORDER_ID) { 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); UpdateVehicleTimetable(v, false);
v->cur_order_index = next_order; v->cur_auto_order_index = v->cur_real_order_index = next_order;
v->current_order_time += v->GetOrder(next_order)->travel_time; v->UpdateRealOrderIndex();
v->current_order_time += v->GetOrder(v->cur_real_order_index)->travel_time;
} else { } else {
UpdateVehicleTimetable(v, true); UpdateVehicleTimetable(v, true);
v->IncrementOrderIndex(); v->IncrementRealOrderIndex();
} }
break; break;
} }
@ -1802,18 +1855,22 @@ bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth)
return false; 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 */ /* Get the current order */
order = v->GetNextManualOrder(v->cur_order_index); order = v->GetOrder(v->cur_real_order_index);
if (order == NULL) { if (order->IsType(OT_AUTOMATIC)) {
order = v->GetNextManualOrder(0); assert(v->GetNumManualOrders() == 0);
if (order == NULL) { order = NULL;
v->current_order.Free();
v->dest_tile = 0;
return false;
}
} }
if (order == NULL) {
v->current_order.Free();
v->dest_tile = 0;
return false;
}
v->current_order = *order; v->current_order = *order;
return UpdateOrderDest(v, order, conditional_depth + 1); return UpdateOrderDest(v, order, conditional_depth + 1);
} }
@ -1863,13 +1920,17 @@ bool ProcessOrders(Vehicle *v)
* visited station will cause the vehicle to still stop. */ * visited station will cause the vehicle to still stop. */
v->last_station_visited = v->current_order.GetDestination(); v->last_station_visited = v->current_order.GetDestination();
UpdateVehicleTimetable(v, true); UpdateVehicleTimetable(v, true);
v->IncrementOrderIndex(); v->IncrementAutoOrderIndex();
} }
/* Get the current order */ /* 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 no order, do nothing. */
if (order == NULL || (v->type == VEH_AIRCRAFT && !CheckForValidOrders(v))) { if (order == NULL || (v->type == VEH_AIRCRAFT && !CheckForValidOrders(v))) {

View File

@ -195,8 +195,11 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int
SpriteID sprite = rtl ? SPR_ARROW_LEFT : SPR_ARROW_RIGHT; SpriteID sprite = rtl ? SPR_ARROW_LEFT : SPR_ARROW_RIGHT;
Dimension sprite_size = GetSpriteSize(sprite); Dimension sprite_size = GetSpriteSize(sprite);
if (v->cur_order_index == order_index) { 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 - 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; TextColour colour = TC_BLACK;
@ -207,7 +210,7 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int
} }
SetDParam(0, order_index + 1); 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); SetDParam(5, STR_EMPTY);
@ -684,10 +687,10 @@ private:
void OrderClick_Skip(int i) void OrderClick_Skip(int i)
{ {
/* Don't skip when there's nothing to skip */ /* 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; 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)); 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 @@ public:
bool rtl = _current_text_dir == TD_RTL; bool rtl = _current_text_dir == TD_RTL;
SetDParam(0, 99); 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 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; int y = r.top + WD_FRAMERECT_TOP;

View File

@ -525,7 +525,7 @@ TileIndex RoadVehicle::GetOrderStationLocation(StationID station)
const Station *st = Station::Get(station); const Station *st = Station::Get(station);
if (!CanVehicleUseStation(this, st)) { if (!CanVehicleUseStation(this, st)) {
/* There is no stop left at the station, so don't even TRY to go there */ /* There is no stop left at the station, so don't even TRY to go there */
this->IncrementOrderIndex(); this->IncrementRealOrderIndex();
return 0; return 0;
} }

View File

@ -2536,6 +2536,14 @@ bool AfterLoadGame()
* it should have set v->z_pos correctly. */ * 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)); 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 */ /* Road stops is 'only' updating some caches */

View File

@ -1138,7 +1138,7 @@ static const OldChunks vehicle_chunk[] = {
OCL_VAR ( OC_UINT16, 1, &_old_order ), OCL_VAR ( OC_UINT16, 1, &_old_order ),
OCL_NULL ( 1 ), ///< num_orders, now calculated 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_TILE, Vehicle, dest_tile ),
OCL_SVAR( OC_UINT16, Vehicle, load_unload_ticks ), OCL_SVAR( OC_UINT16, Vehicle, load_unload_ticks ),
OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, date_of_last_service ), OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, date_of_last_service ),

View File

@ -222,8 +222,9 @@
* 155 21453 * 155 21453
* 156 21728 * 156 21728
* 157 21862 * 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 SavegameType _savegame_type; ///< type of savegame we are loading

View File

@ -487,7 +487,8 @@ const SaveLoad *GetVehicleDescription(VehicleType vt)
SLE_VAR(Vehicle, tick_counter, SLE_UINT8), SLE_VAR(Vehicle, tick_counter, SLE_UINT8),
SLE_CONDVAR(Vehicle, running_ticks, SLE_UINT8, 88, SL_MAX_VERSION), 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 */ /* num_orders is now part of OrderList and is not saved but counted */
SLE_CONDNULL(1, 0, 104), SLE_CONDNULL(1, 0, 104),

View File

@ -234,7 +234,7 @@ TileIndex Ship::GetOrderStationLocation(StationID station)
if (st->dock_tile != INVALID_TILE) { if (st->dock_tile != INVALID_TILE) {
return TILE_ADD(st->dock_tile, ToTileIndexDiff(GetDockOffset(st->dock_tile))); return TILE_ADD(st->dock_tile, ToTileIndexDiff(GetDockOffset(st->dock_tile)));
} else { } else {
this->IncrementOrderIndex(); this->IncrementRealOrderIndex();
return 0; return 0;
} }
} }
@ -480,7 +480,7 @@ static void ShipController(Ship *v)
/* We got within 3 tiles of our target buoy, so let's skip to our /* We got within 3 tiles of our target buoy, so let's skip to our
* next order */ * next order */
UpdateVehicleTimetable(v, true); UpdateVehicleTimetable(v, true);
v->IncrementOrderIndex(); v->IncrementRealOrderIndex();
v->current_order.MakeDummy(); v->current_order.MakeDummy();
} else { } else {
/* Non-buoy orders really need to reach the tile */ /* Non-buoy orders really need to reach the tile */
@ -500,7 +500,7 @@ static void ShipController(Ship *v)
v->BeginLoading(); v->BeginLoading();
} else { // leave stations without docks right aways } else { // leave stations without docks right aways
v->current_order.MakeLeaveStation(); v->current_order.MakeLeaveStation();
v->IncrementOrderIndex(); v->IncrementRealOrderIndex();
} }
} }
} }

View File

@ -33,7 +33,7 @@ static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint16 time
v->orders.list->UpdateOrderTimetable(delta); v->orders.list->UpdateOrderTimetable(delta);
for (v = v->FirstShared(); v != NULL; v = v->NextShared()) { 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) { if (is_journey) {
v->current_order.travel_time = time; v->current_order.travel_time = time;
} else { } else {
@ -248,7 +248,7 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling)
bool just_started = false; bool just_started = false;
/* This vehicle is arriving at the first destination in the timetable. */ /* 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 /* 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 * the vehicle last arrived at the first destination, update it to the
* current time. Otherwise set the late counter appropriately to when * current time. Otherwise set the late counter appropriately to when
@ -288,10 +288,10 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling)
* processing of different orders when filling the timetable. */ * processing of different orders when filling the timetable. */
time_taken = CeilDiv(max(time_taken, 1U), DAY_TICKS) * DAY_TICKS; 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 /* 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 * this code. So obviously, we have completed our round: So turn autofill
* off again. */ * off again. */

View File

@ -206,7 +206,7 @@ struct TimetableWindow : Window {
bool travelling = (!v->current_order.IsType(OT_LOADING) || v->current_order.GetNonStopType() == ONSF_STOP_EVERYWHERE); bool travelling = (!v->current_order.IsType(OT_LOADING) || v->current_order.GetNonStopType() == ONSF_STOP_EVERYWHERE);
Ticks start_time = _date_fract - v->current_order_time; 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); return (travelling && v->lateness_counter < 0);
} }
@ -425,7 +425,7 @@ struct TimetableWindow : Window {
if (total_time <= 0 || v->GetNumOrders() <= 1 || !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED)) break; if (total_time <= 0 || v->GetNumOrders() <= 1 || !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED)) break;
TimetableArrivalDeparture *arr_dep = AllocaM(TimetableArrivalDeparture, v->GetNumOrders()); 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; VehicleOrderID earlyID = BuildArrivalDepartureList(v, arr_dep) ? cur_order : (VehicleOrderID)INVALID_VEH_ORDER_ID;

View File

@ -2291,7 +2291,7 @@ public:
old_order(_v->current_order), old_order(_v->current_order),
old_dest_tile(_v->dest_tile), old_dest_tile(_v->dest_tile),
old_last_station_visited(_v->last_station_visited), old_last_station_visited(_v->last_station_visited),
index(_v->cur_order_index) index(_v->cur_real_order_index)
{ {
} }
@ -2348,7 +2348,7 @@ public:
/* Don't increment inside the while because otherwise conditional /* Don't increment inside the while because otherwise conditional
* orders can lead to an infinite loop. */ * orders can lead to an infinite loop. */
++this->index; ++this->index;
} while (this->index != this->v->cur_order_index); } while (this->index != this->v->cur_real_order_index);
return false; return false;
} }
@ -2602,7 +2602,7 @@ TileIndex Train::GetOrderStationLocation(StationID station)
const Station *st = Station::Get(station); const Station *st = Station::Get(station);
if (!(st->facilities & FACIL_TRAIN)) { if (!(st->facilities & FACIL_TRAIN)) {
/* The destination station has no trainstation tiles. */ /* The destination station has no trainstation tiles. */
this->IncrementOrderIndex(); this->IncrementRealOrderIndex();
return 0; return 0;
} }

View File

@ -1266,7 +1266,7 @@ void VehicleEnterDepot(Vehicle *v)
if (v->current_order.IsType(OT_GOTO_DEPOT)) { if (v->current_order.IsType(OT_GOTO_DEPOT)) {
SetWindowDirty(WC_VEHICLE_VIEW, v->index); 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; Order t = v->current_order;
v->current_order.MakeDummy(); v->current_order.MakeDummy();
@ -1303,7 +1303,7 @@ void VehicleEnterDepot(Vehicle *v)
/* Part of orders */ /* Part of orders */
v->DeleteUnreachedAutoOrders(); v->DeleteUnreachedAutoOrders();
UpdateVehicleTimetable(v, true); UpdateVehicleTimetable(v, true);
v->IncrementOrderIndex(); v->IncrementAutoOrderIndex();
} }
if (t.GetDepotActionType() & ODATFB_HALT) { if (t.GetDepotActionType() & ODATFB_HALT) {
/* Vehicles are always stopped on entering depots. Do not restart this one. */ /* Vehicles are always stopped on entering depots. Do not restart this one. */
@ -1797,11 +1797,25 @@ uint GetVehicleCapacity(const Vehicle *v, uint16 *mail_capacity)
*/ */
void Vehicle::DeleteUnreachedAutoOrders() void Vehicle::DeleteUnreachedAutoOrders()
{ {
const Order *order = this->GetOrder(this->cur_order_index); const Order *order = this->GetOrder(this->cur_auto_order_index);
while (order != NULL && order->IsType(OT_AUTOMATIC)) { while (order != NULL) {
/* Delete order effectively deletes order, so get the next before deleting it. */ if (this->cur_auto_order_index == this->cur_real_order_index) break;
order = order->next;
DeleteOrder(this, this->cur_order_index); 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 @@ void Vehicle::BeginLoading()
this->current_order.GetDestination() == this->last_station_visited) { this->current_order.GetDestination() == this->last_station_visited) {
this->DeleteUnreachedAutoOrders(); 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); this->current_order.MakeLoading(true);
UpdateVehicleTimetable(this, true); UpdateVehicleTimetable(this, true);
@ -1832,14 +1846,14 @@ void Vehicle::BeginLoading()
/* We weren't scheduled to stop here. Insert an automatic order /* 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 * to show that we are stopping here, but only do that if the order
* list isn't empty. */ * 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 && if (in_list != NULL && this->orders.list->GetNumOrders() < MAX_VEH_ORDER_ID &&
(!in_list->IsType(OT_AUTOMATIC) || (!in_list->IsType(OT_AUTOMATIC) ||
in_list->GetDestination() != this->last_station_visited)) { in_list->GetDestination() != this->last_station_visited)) {
Order *auto_order = new Order(); Order *auto_order = new Order();
auto_order->MakeAutomatic(this->last_station_visited); auto_order->MakeAutomatic(this->last_station_visited);
InsertOrder(this, auto_order, this->cur_order_index); InsertOrder(this, auto_order, this->cur_auto_order_index);
if (this->cur_order_index > 0) --this->cur_order_index; if (this->cur_auto_order_index > 0) --this->cur_auto_order_index;
} }
this->current_order.MakeLoading(false); this->current_order.MakeLoading(false);
} }
@ -1913,7 +1927,7 @@ void Vehicle::HandleLoading(bool mode)
default: return; default: return;
} }
this->IncrementOrderIndex(); this->IncrementAutoOrderIndex();
} }
/** /**
@ -1948,7 +1962,7 @@ CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
if (flags & DC_EXEC) { if (flags & DC_EXEC) {
/* If the orders to 'goto depot' are in the orders list (forced servicing), /* If the orders to 'goto depot' are in the orders list (forced servicing),
* then skip to the next order; effectively cancelling this forced service */ * 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(); this->current_order.MakeDummy();
SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH); SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
@ -2261,20 +2275,6 @@ void Vehicle::RemoveFromShared()
this->previous_shared = NULL; 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() void VehiclesYearlyLoop()
{ {
Vehicle *v; Vehicle *v;

View File

@ -222,7 +222,8 @@ public:
byte vehstatus; ///< Status byte vehstatus; ///< Status
Order current_order; ///< The current order (+ status, like: loading) 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 { union {
OrderList *list; ///< Pointer to the order list for this vehicle OrderList *list; ///< Pointer to the order list for this vehicle
@ -549,7 +550,8 @@ public:
{ {
this->unitnumber = src->unitnumber; 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->current_order = src->current_order;
this->dest_tile = src->dest_tile; this->dest_tile = src->dest_tile;
@ -599,17 +601,85 @@ public:
void UpdateVisualEffect(bool allow_power_change = true); void UpdateVisualEffect(bool allow_power_change = true);
void ShowVisualEffect() const; 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. * Note: current_order is not invalidated.
*/ */
void IncrementOrderIndex() void IncrementAutoOrderIndex()
{ {
this->cur_order_index++; if (this->cur_auto_order_index == this->cur_real_order_index) {
if (this->cur_order_index >= this->GetNumOrders()) this->cur_order_index = 0; /* 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); 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()
{
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;
}
}
/** /**
* Returns order 'index' of a vehicle or NULL when it doesn't exists * Returns order 'index' of a vehicle or NULL when it doesn't exists
* @param index the order to fetch * @param index the order to fetch
@ -620,8 +690,6 @@ public:
return (this->orders.list == NULL) ? NULL : this->orders.list->GetOrderAt(index); 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 * Returns the last order of a vehicle, or NULL if it doesn't exists
* @return last order of a vehicle, if available * @return last order of a vehicle, if available

View File

@ -1187,7 +1187,7 @@ static void DrawSmallOrderList(const Vehicle *v, int left, int right, int y, Veh
VehicleOrderID oid = start; VehicleOrderID oid = start;
do { 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)) { if (order->IsType(OT_GOTO_STATION)) {
SetDParam(0, order->GetDestination()); SetDParam(0, order->GetDestination());
@ -1294,7 +1294,7 @@ void BaseVehicleListWindow::DrawVehicleListItems(VehicleID selected_vehicle, int
DrawString(text_left, text_right, y, STR_TINY_GROUP, TC_BLACK); 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()) { if (v->IsInDepot()) {
str = STR_BLUE_COMMA; str = STR_BLUE_COMMA;