Changeset - r28657:ee447a88ccab
[Not reviewed]
master
0 20 0
Tyler Trahan - 9 months ago 2024-02-03 13:04:24
tyler@tylertrahan.com
Feature: Order flag to unbunch vehicles at depot (#11945)
20 files changed with 293 insertions and 28 deletions:
0 comments (0 inline, 0 general)
src/aircraft_cmd.cpp
Show inline comments
 
@@ -1474,12 +1474,13 @@ void AircraftLeaveHangar(Aircraft *v, Di
 
			u->vehstatus &= ~VS_HIDDEN;
 
			u->cur_speed = 80;
 
		}
 
	}
 

	
 
	VehicleServiceInDepot(v);
 
	v->LeaveUnbunchingDepot();
 
	SetAircraftPosition(v, v->x_pos, v->y_pos, v->z_pos);
 
	InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 
	SetWindowClassesDirty(WC_AIRCRAFT_LIST);
 
}
 

	
 
////////////////////////////////////////////////////////////////////////////////
 
@@ -1518,12 +1519,15 @@ static void AircraftEventHandler_InHanga
 
	/* if we were sent to the depot, stay there */
 
	if (v->current_order.IsType(OT_GOTO_DEPOT) && (v->vehstatus & VS_STOPPED)) {
 
		v->current_order.Free();
 
		return;
 
	}
 

	
 
	/* Check if we should wait here for unbunching. */
 
	if (v->IsWaitingForUnbunching()) return;
 

	
 
	if (!v->current_order.IsType(OT_GOTO_STATION) &&
 
			!v->current_order.IsType(OT_GOTO_DEPOT))
 
		return;
 

	
 
	/* We are leaving a hangar, but have to go to the exact same one; re-enter */
 
	if (v->current_order.IsType(OT_GOTO_DEPOT) && v->current_order.GetDestination() == v->targetairport) {
src/base_consist.cpp
Show inline comments
 
@@ -39,6 +39,16 @@ void BaseConsist::CopyConsistPropertiesF
 
	if (HasBit(src->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME)) SetBit(this->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
 
	if (HasBit(src->vehicle_flags, VF_SERVINT_IS_PERCENT) != HasBit(this->vehicle_flags, VF_SERVINT_IS_PERCENT)) {
 
		ToggleBit(this->vehicle_flags, VF_SERVINT_IS_PERCENT);
 
	}
 
	if (HasBit(src->vehicle_flags, VF_SERVINT_IS_CUSTOM)) SetBit(this->vehicle_flags, VF_SERVINT_IS_CUSTOM);
 
}
 

	
 
/**
 
 * Resets all the data used for depot unbunching.
 
 */
 
void BaseConsist::ResetDepotUnbunching()
 
{
 
	this->depot_unbunching_last_departure = 0;
 
	this->depot_unbunching_next_departure = 0;
 
	this->round_trip_time = 0;
 
}
src/base_consist.h
Show inline comments
 
@@ -19,19 +19,24 @@ struct BaseConsist {
 

	
 
	/* Used for timetabling. */
 
	TimerGameTick::Ticks current_order_time;    ///< How many ticks have passed since this order started.
 
	TimerGameTick::Ticks lateness_counter;      ///< How many ticks late (or early if negative) this vehicle is.
 
	TimerGameTick::TickCounter timetable_start; ///< At what tick of TimerGameTick::counter the vehicle should start its timetable.
 

	
 
	TimerGameTick::TickCounter depot_unbunching_last_departure; ///< When the vehicle last left its unbunching depot.
 
	TimerGameTick::TickCounter depot_unbunching_next_departure; ///< When the vehicle will next try to leave its unbunching depot.
 
	TimerGameTick::Ticks round_trip_time;  ///< How many ticks for a single circumnavigation of the orders.
 

	
 
	uint16_t service_interval;            ///< The interval for (automatic) servicing; either in days or %.
 

	
 
	VehicleOrderID cur_real_order_index;///< The index to the current real (non-implicit) order
 
	VehicleOrderID cur_implicit_order_index;///< The index to the current implicit order
 

	
 
	uint16_t vehicle_flags;               ///< Used for gradual loading and other miscellaneous things (@see VehicleFlags enum)
 

	
 
	virtual ~BaseConsist() = default;
 

	
 
	void CopyConsistPropertiesFrom(const BaseConsist *src);
 
	void ResetDepotUnbunching();
 
};
 

	
 
#endif /* BASE_CONSIST_H */
src/lang/english.txt
Show inline comments
 
@@ -4373,12 +4373,13 @@ STR_VEHICLE_VIEW_ROAD_VEHICLE_STATUS_STA
 
STR_VEHICLE_VIEW_SHIP_STATE_STATUS_STOP_TOOLTIP                 :{BLACK}Current ship action - click to stop/start ship
 
STR_VEHICLE_VIEW_AIRCRAFT_STATUS_START_STOP_TOOLTIP             :{BLACK}Current aircraft action - click to stop/start aircraft
 

	
 
# Messages in the start stop button in the vehicle view
 
STR_VEHICLE_STATUS_LOADING_UNLOADING                            :{LTBLUE}Loading / Unloading
 
STR_VEHICLE_STATUS_LEAVING                                      :{LTBLUE}Leaving
 
STR_VEHICLE_STATUS_WAITING_UNBUNCHING                           :{LTBLUE}Waiting to unbunch
 
STR_VEHICLE_STATUS_CRASHED                                      :{RED}Crashed!
 
STR_VEHICLE_STATUS_BROKEN_DOWN                                  :{RED}Broken down
 
STR_VEHICLE_STATUS_STOPPED                                      :{RED}Stopped
 
STR_VEHICLE_STATUS_TRAIN_STOPPING_VEL                           :{RED}Stopping, {VELOCITY}
 
STR_VEHICLE_STATUS_TRAIN_NO_POWER                               :{RED}No power
 
STR_VEHICLE_STATUS_TRAIN_STUCK                                  :{ORANGE}Waiting for free path
 
@@ -4386,12 +4387,13 @@ STR_VEHICLE_STATUS_AIRCRAFT_TOO_FAR     
 

	
 
STR_VEHICLE_STATUS_HEADING_FOR_STATION_VEL                      :{LTBLUE}Heading for {STATION}, {VELOCITY}
 
STR_VEHICLE_STATUS_NO_ORDERS_VEL                                :{LTBLUE}No orders, {VELOCITY}
 
STR_VEHICLE_STATUS_HEADING_FOR_WAYPOINT_VEL                     :{LTBLUE}Heading for {WAYPOINT}, {VELOCITY}
 
STR_VEHICLE_STATUS_HEADING_FOR_DEPOT_VEL                        :{ORANGE}Heading for {DEPOT}, {VELOCITY}
 
STR_VEHICLE_STATUS_HEADING_FOR_DEPOT_SERVICE_VEL                :{LTBLUE}Service at {DEPOT}, {VELOCITY}
 
STR_VEHICLE_STATUS_HEADING_FOR_DEPOT_UNBUNCH_VEL                :{LTBLUE}Unbunch and service at {DEPOT}, {VELOCITY}
 

	
 
STR_VEHICLE_STATUS_CANNOT_REACH_STATION_VEL                     :{LTBLUE}Cannot reach {STATION}, {VELOCITY}
 
STR_VEHICLE_STATUS_CANNOT_REACH_WAYPOINT_VEL                    :{LTBLUE}Cannot reach {WAYPOINT}, {VELOCITY}
 
STR_VEHICLE_STATUS_CANNOT_REACH_DEPOT_VEL                       :{ORANGE}Cannot reach {DEPOT}, {VELOCITY}
 
STR_VEHICLE_STATUS_CANNOT_REACH_DEPOT_SERVICE_VEL               :{LTBLUE}Cannot reach {DEPOT}, {VELOCITY}
 

	
 
@@ -4516,13 +4518,13 @@ STR_REFIT_AIRCRAFT_REFIT_TOOLTIP        
 
STR_ORDERS_CAPTION                                              :{WHITE}{VEHICLE} (Orders)
 
STR_ORDERS_TIMETABLE_VIEW                                       :{BLACK}Timetable
 
STR_ORDERS_TIMETABLE_VIEW_TOOLTIP                               :{BLACK}Switch to the timetable view
 

	
 
STR_ORDERS_LIST_TOOLTIP                                         :{BLACK}Order list - click on an order to highlight it. Ctrl+Click to scroll to the order's destination
 
STR_ORDER_INDEX                                                 :{COMMA}:{NBSP}
 
STR_ORDER_TEXT                                                  :{STRING4} {STRING2} {STRING}
 
STR_ORDER_TEXT                                                  :{STRING4} {STRING2} {STRING} {STRING}
 

	
 
STR_ORDERS_END_OF_ORDERS                                        :- - End of Orders - -
 
STR_ORDERS_END_OF_SHARED_ORDERS                                 :- - End of Shared Orders - -
 

	
 
# Order bottom buttons
 
STR_ORDER_NON_STOP                                              :{BLACK}Non-stop
 
@@ -4550,17 +4552,17 @@ STR_ORDER_REFIT                         
 
STR_ORDER_REFIT_TOOLTIP                                         :{BLACK}Select what cargo type to refit to in this order. Ctrl+Click to remove refit instruction
 
STR_ORDER_REFIT_AUTO                                            :{BLACK}Refit at station
 
STR_ORDER_REFIT_AUTO_TOOLTIP                                    :{BLACK}Select what cargo type to refit to in this order. Ctrl+Click to remove refit instruction. Refitting at stations will only be done if the vehicle allows it
 
STR_ORDER_DROP_REFIT_AUTO                                       :Fixed cargo
 
STR_ORDER_DROP_REFIT_AUTO_ANY                                   :Available cargo
 

	
 
STR_ORDER_SERVICE                                               :{BLACK}Service
 
STR_ORDER_DROP_GO_ALWAYS_DEPOT                                  :Always go
 
STR_ORDER_DROP_SERVICE_DEPOT                                    :Service if needed
 
STR_ORDER_DROP_HALT_DEPOT                                       :Stop
 
STR_ORDER_SERVICE_TOOLTIP                                       :{BLACK}Skip this order unless a service is needed
 
STR_ORDER_DROP_UNBUNCH                                          :Unbunch
 
STR_ORDER_DEPOT_ACTION_TOOLTIP                                  :{BLACK}Select the action to take at this depot
 

	
 
STR_ORDER_CONDITIONAL_VARIABLE_TOOLTIP                          :{BLACK}Vehicle data to base jumping on
 

	
 
# Conditional order variables, must follow order of OrderConditionVariable enum
 
###length 8
 
STR_ORDER_CONDITIONAL_LOAD_PERCENTAGE                           :Load percentage
 
@@ -4623,12 +4625,14 @@ STR_ORDER_GO_TO_NEAREST_DEPOT_FORMAT    
 
STR_ORDER_GO_TO_DEPOT_FORMAT                                    :{STRING} {DEPOT}
 

	
 
STR_ORDER_REFIT_ORDER                                           :(Refit to {STRING})
 
STR_ORDER_REFIT_STOP_ORDER                                      :(Refit to {STRING} and stop)
 
STR_ORDER_STOP_ORDER                                            :(Stop)
 

	
 
STR_ORDER_WAIT_TO_UNBUNCH                                       :(wait to unbunch)
 

	
 
STR_ORDER_GO_TO_STATION                                         :{STRING} {STATION} {STRING1}
 
STR_ORDER_GO_TO_STATION_CAN_T_USE_STATION                       :{PUSH_COLOUR}{RED}(Can't use station){POP_COLOUR} {STRING} {STATION} {STRING1}
 

	
 
STR_ORDER_IMPLICIT                                              :(Implicit)
 

	
 
STR_ORDER_FULL_LOAD                                             :(Full load)
 
@@ -5092,12 +5096,20 @@ STR_ERROR_CAN_T_MOVE_VEHICLE            
 
STR_ERROR_REAR_ENGINE_FOLLOW_FRONT                              :{WHITE}The rear engine will always follow its front counterpart
 
STR_ERROR_UNABLE_TO_FIND_ROUTE_TO                               :{WHITE}Unable to find route to local depot
 
STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT                            :{WHITE}Unable to find local depot
 

	
 
STR_ERROR_DEPOT_WRONG_DEPOT_TYPE                                :Wrong depot type
 

	
 
# Depot unbunching related errors
 
STR_ERROR_UNBUNCHING_ONLY_ONE_ALLOWED                           :{WHITE}... can only have one unbunching order
 
STR_ERROR_UNBUNCHING_NO_FULL_LOAD                               :{WHITE}... cannot use full load orders when vehicle has an unbunching order
 
STR_ERROR_UNBUNCHING_NO_UNBUNCHING_FULL_LOAD                    :{WHITE}... cannot unbunch a vehicle with a full load order
 
STR_ERROR_UNBUNCHING_NO_CONDITIONAL                             :{WHITE}... cannot use conditional orders when vehicle has an unbunching order
 
STR_ERROR_UNBUNCHING_NO_UNBUNCHING_CONDITIONAL                  :{WHITE}... cannot unbunch a vehicle with a conditional order
 
STR_ERROR_UNBUNCHING_NO_SERVICE_IF_NEEDED                       :{WHITE}... vehicle must always visit the depot to unbunch there
 

	
 
# Autoreplace related errors
 
STR_ERROR_TRAIN_TOO_LONG_AFTER_REPLACEMENT                      :{WHITE}{VEHICLE} is too long after replacement
 
STR_ERROR_AUTOREPLACE_NOTHING_TO_DO                             :{WHITE}No autoreplace/renew rules applied
 
STR_ERROR_AUTOREPLACE_MONEY_LIMIT                               :(money limit)
 
STR_ERROR_AUTOREPLACE_INCOMPATIBLE_CARGO                        :{WHITE}New vehicle can't carry {STRING}
 
STR_ERROR_AUTOREPLACE_INCOMPATIBLE_REFIT                        :{WHITE}New vehicle can't do refit in order {NUM}
src/order_base.h
Show inline comments
 
@@ -141,13 +141,13 @@ public:
 
	inline OrderNonStopFlags GetNonStopType() const { return (OrderNonStopFlags)GB(this->type, 6, 2); }
 
	/** Where must we stop at the platform? */
 
	inline OrderStopLocation GetStopLocation() const { return (OrderStopLocation)GB(this->type, 4, 2); }
 
	/** What caused us going to the depot? */
 
	inline OrderDepotTypeFlags GetDepotOrderType() const { return (OrderDepotTypeFlags)GB(this->flags, 0, 3); }
 
	/** What are we going to do when in the depot. */
 
	inline OrderDepotActionFlags GetDepotActionType() const { return (OrderDepotActionFlags)GB(this->flags, 4, 3); }
 
	inline OrderDepotActionFlags GetDepotActionType() const { return (OrderDepotActionFlags)GB(this->flags, 3, 4); }
 
	/** What variable do we have to compare? */
 
	inline OrderConditionVariable GetConditionVariable() const { return (OrderConditionVariable)GB(this->dest, 11, 5); }
 
	/** What is the comparator to use? */
 
	inline OrderConditionComparator GetConditionComparator() const { return (OrderConditionComparator)GB(this->type, 5, 3); }
 
	/** Get the order to skip to. */
 
	inline VehicleOrderID GetConditionSkipToOrder() const { return this->flags; }
 
@@ -162,13 +162,13 @@ public:
 
	inline void SetNonStopType(OrderNonStopFlags non_stop_type) { SB(this->type, 6, 2, non_stop_type); }
 
	/** Set where we must stop at the platform. */
 
	inline void SetStopLocation(OrderStopLocation stop_location) { SB(this->type, 4, 2, stop_location); }
 
	/** Set the cause to go to the depot. */
 
	inline void SetDepotOrderType(OrderDepotTypeFlags depot_order_type) { SB(this->flags, 0, 3, depot_order_type); }
 
	/** Set what we are going to do in the depot. */
 
	inline void SetDepotActionType(OrderDepotActionFlags depot_service_type) { SB(this->flags, 4, 3, depot_service_type); }
 
	inline void SetDepotActionType(OrderDepotActionFlags depot_service_type) { SB(this->flags, 3, 4, depot_service_type); }
 
	/** Set variable we have to compare. */
 
	inline void SetConditionVariable(OrderConditionVariable condition_variable) { SB(this->dest, 11, 5, condition_variable); }
 
	/** Set the comparator to use. */
 
	inline void SetConditionComparator(OrderConditionComparator condition_comparator) { SB(this->type, 5, 3, condition_comparator); }
 
	/** Get the order to skip to. */
 
	inline void SetConditionSkipToOrder(VehicleOrderID order_id) { this->flags = order_id; }
src/order_cmd.cpp
Show inline comments
 
@@ -740,14 +740,23 @@ CommandCost CmdInsertOrder(DoCommandFlag
 

	
 
			/* Non stop only allowed for ground vehicles. */
 
			if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE && !v->IsGroundVehicle()) 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;
 
				case OLF_LOAD_IF_POSSIBLE:
 
				case OLFB_NO_LOAD:
 
					break;
 

	
 
				case OLFB_FULL_LOAD:
 
				case OLF_FULL_LOAD_ANY:
 
					if (v->HasUnbunchingOrder()) return_cmd_error(STR_ERROR_UNBUNCHING_NO_FULL_LOAD);
 
					break;
 

	
 
				default:
 
					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;
 
			}
 

	
 
@@ -846,12 +855,13 @@ CommandCost CmdInsertOrder(DoCommandFlag
 
		}
 

	
 
		case OT_CONDITIONAL: {
 
			VehicleOrderID skip_to = new_order.GetConditionSkipToOrder();
 
			if (skip_to != 0 && skip_to >= v->GetNumOrders()) return CMD_ERROR; // Always allow jumping to the first (even when there is no order).
 
			if (new_order.GetConditionVariable() >= OCV_END) return CMD_ERROR;
 
			if (v->HasUnbunchingOrder()) return_cmd_error(STR_ERROR_UNBUNCHING_NO_CONDITIONAL);
 

	
 
			OrderConditionComparator occ = new_order.GetConditionComparator();
 
			if (occ >= OCC_END) return CMD_ERROR;
 
			switch (new_order.GetConditionVariable()) {
 
				case OCV_REQUIRES_SERVICE:
 
					if (occ != OCC_IS_TRUE && occ != OCC_IS_FALSE) return CMD_ERROR;
 
@@ -934,12 +944,15 @@ void InsertOrder(Vehicle *v, Order *new_
 
			uint cur = u->cur_implicit_order_index + 1;
 
			/* Check if we don't go out of bound */
 
			if (cur < u->GetNumOrders()) {
 
				u->cur_implicit_order_index = cur;
 
			}
 
		}
 
		/* Unbunching data is no longer valid. */
 
		u->ResetDepotUnbunching();
 

	
 
		/* Update any possible open window of the vehicle */
 
		InvalidateVehicleOrder(u, INVALID_VEH_ORDER_ID | (sel_ord << 8));
 
	}
 

	
 
	/* As we insert an order, the order to skip to will be 'wrong'. */
 
	VehicleOrderID cur_order_id = 0;
 
@@ -1048,12 +1061,14 @@ void DeleteOrder(Vehicle *v, VehicleOrde
 
			/* Skip non-implicit orders for the implicit-order-index (e.g. if the current implicit order was deleted */
 
			while (u->cur_implicit_order_index != u->cur_real_order_index && !u->GetOrder(u->cur_implicit_order_index)->IsType(OT_IMPLICIT)) {
 
				u->cur_implicit_order_index++;
 
				if (u->cur_implicit_order_index >= u->GetNumOrders()) u->cur_implicit_order_index = 0;
 
			}
 
		}
 
		/* Unbunching data is no longer valid. */
 
		u->ResetDepotUnbunching();
 

	
 
		/* Update any possible open window of the vehicle */
 
		InvalidateVehicleOrder(u, sel_ord | (INVALID_VEH_ORDER_ID << 8));
 
	}
 

	
 
	/* As we delete an order, the order to skip to will be 'wrong'. */
 
@@ -1094,12 +1109,15 @@ CommandCost CmdSkipToOrder(DoCommandFlag
 
	if (flags & DC_EXEC) {
 
		if (v->current_order.IsType(OT_LOADING)) v->LeaveStation();
 

	
 
		v->cur_implicit_order_index = v->cur_real_order_index = sel_ord;
 
		v->UpdateRealOrderIndex();
 

	
 
		/* Unbunching data is no longer valid. */
 
		v->ResetDepotUnbunching();
 

	
 
		InvalidateVehicleOrder(v, VIWD_MODIFY_ORDERS);
 

	
 
		/* We have an aircraft/ship, they have a mini-schedule, so update them all */
 
		if (v->type == VEH_AIRCRAFT) SetWindowClassesDirty(WC_AIRCRAFT_LIST);
 
		if (v->type == VEH_SHIP) SetWindowClassesDirty(WC_SHIPS_LIST);
 
	}
 
@@ -1170,12 +1188,15 @@ CommandCost CmdMoveOrder(DoCommandFlag f
 
				u->cur_implicit_order_index = target_order;
 
			} else if (u->cur_implicit_order_index > moving_order && u->cur_implicit_order_index <= target_order) {
 
				u->cur_implicit_order_index--;
 
			} else if (u->cur_implicit_order_index < moving_order && u->cur_implicit_order_index >= target_order) {
 
				u->cur_implicit_order_index++;
 
			}
 
			/* Unbunching data is no longer valid. */
 
			u->ResetDepotUnbunching();
 

	
 

	
 
			assert(v->orders == u->orders);
 
			/* Update any possible open window of the vehicle */
 
			InvalidateVehicleOrder(u, moving_order | (target_order << 8));
 
		}
 

	
 
@@ -1271,16 +1292,33 @@ CommandCost CmdModifyOrder(DoCommandFlag
 
			break;
 

	
 
		case MOF_LOAD:
 
			if (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) return CMD_ERROR;
 
			if (data > OLFB_NO_LOAD || data == 1) return CMD_ERROR;
 
			if (data == order->GetLoadType()) return CMD_ERROR;
 
			if ((data & (OLFB_FULL_LOAD | OLF_FULL_LOAD_ANY)) && v->HasUnbunchingOrder()) return_cmd_error(STR_ERROR_UNBUNCHING_NO_FULL_LOAD);
 
			break;
 

	
 
		case MOF_DEPOT_ACTION:
 
			if (data >= DA_END) return CMD_ERROR;
 
			/* The vehicle must always go to the depot (not just if it needs servicing) in order to unbunch there. */
 
			if ((data == DA_SERVICE) && (order->GetDepotActionType() & ODATFB_UNBUNCH)) return_cmd_error(STR_ERROR_UNBUNCHING_NO_SERVICE_IF_NEEDED);
 

	
 
			/* Check if we are allowed to add unbunching. We are always allowed to remove it. */
 
			if (data == DA_UNBUNCH) {
 
				/* Only one unbunching order is allowed in a vehicle's orders. If this order already has an unbunching action, no error is needed. */
 
				if (v->HasUnbunchingOrder() && !(order->GetDepotActionType() & ODATFB_UNBUNCH)) return_cmd_error(STR_ERROR_UNBUNCHING_ONLY_ONE_ALLOWED);
 
				for (Order *o : v->Orders()) {
 
					/* We don't allow unbunching if the vehicle has a conditional order. */
 
					if (o->IsType(OT_CONDITIONAL)) return_cmd_error(STR_ERROR_UNBUNCHING_NO_UNBUNCHING_CONDITIONAL);
 
					/* We don't allow unbunching if the vehicle has a full load order. */
 
					if (o->IsType(OT_GOTO_STATION) && o->GetLoadType() & (OLFB_FULL_LOAD | OLF_FULL_LOAD_ANY)) return_cmd_error(STR_ERROR_UNBUNCHING_NO_UNBUNCHING_FULL_LOAD);
 
					/* The vehicle must always go to the depot (not just if it needs servicing) in order to unbunch there. */
 
					if (o->IsType(OT_GOTO_DEPOT) && o->GetDepotOrderType() & ODTFB_SERVICE) return_cmd_error(STR_ERROR_UNBUNCHING_NO_SERVICE_IF_NEEDED);
 
				}
 
			}
 
			break;
 

	
 
		case MOF_COND_VARIABLE:
 
			if (data >= OCV_END) return CMD_ERROR;
 
			break;
 

	
 
@@ -1347,26 +1385,34 @@ CommandCost CmdModifyOrder(DoCommandFlag
 

	
 
			case MOF_DEPOT_ACTION: {
 
				switch (data) {
 
					case DA_ALWAYS_GO:
 
						order->SetDepotOrderType((OrderDepotTypeFlags)(order->GetDepotOrderType() & ~ODTFB_SERVICE));
 
						order->SetDepotActionType((OrderDepotActionFlags)(order->GetDepotActionType() & ~ODATFB_HALT));
 
						order->SetDepotActionType((OrderDepotActionFlags)(order->GetDepotActionType() & ~ODATFB_UNBUNCH));
 
						break;
 

	
 
					case DA_SERVICE:
 
						order->SetDepotOrderType((OrderDepotTypeFlags)(order->GetDepotOrderType() | ODTFB_SERVICE));
 
						order->SetDepotActionType((OrderDepotActionFlags)(order->GetDepotActionType() & ~ODATFB_HALT));
 
						order->SetRefit(CARGO_NO_REFIT);
 
						break;
 

	
 
					case DA_STOP:
 
						order->SetDepotOrderType((OrderDepotTypeFlags)(order->GetDepotOrderType() & ~ODTFB_SERVICE));
 
						order->SetDepotActionType((OrderDepotActionFlags)(order->GetDepotActionType() & ~ODATFB_UNBUNCH));
 
						order->SetDepotActionType((OrderDepotActionFlags)(order->GetDepotActionType() | ODATFB_HALT));
 
						order->SetRefit(CARGO_NO_REFIT);
 
						break;
 

	
 
					case DA_UNBUNCH:
 
						order->SetDepotOrderType((OrderDepotTypeFlags)(order->GetDepotOrderType() & ~ODTFB_SERVICE));
 
						order->SetDepotActionType((OrderDepotActionFlags)(order->GetDepotActionType() & ~ODATFB_HALT));
 
						order->SetDepotActionType((OrderDepotActionFlags)(order->GetDepotActionType() | ODATFB_UNBUNCH));
 
						break;
 

	
 
					default:
 
						NOT_REACHED();
 
				}
 
				break;
 
			}
 

	
 
@@ -1427,12 +1473,16 @@ CommandCost CmdModifyOrder(DoCommandFlag
 
			 */
 
			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());
 
			}
 

	
 
			/* Unbunching data is no longer valid. */
 
			u->ResetDepotUnbunching();
 

	
 
			InvalidateVehicleOrder(u, VIWD_MODIFY_ORDERS);
 
		}
 
	}
 

	
 
	return CommandCost();
 
}
 
@@ -1832,12 +1882,15 @@ void DeleteVehicleOrders(Vehicle *v, boo
 
	} else if (v->orders != nullptr) {
 
		/* Remove the orders */
 
		v->orders->FreeChain(keep_orderlist);
 
		if (!keep_orderlist) v->orders = nullptr;
 
	}
 

	
 
	/* Unbunching data is no longer valid. */
 
	v->ResetDepotUnbunching();
 

	
 
	if (reset_order_indices) {
 
		v->cur_implicit_order_index = v->cur_real_order_index = 0;
 
		if (v->current_order.IsType(OT_LOADING)) {
 
			CancelLoadingDueToDeletedOrder(v);
 
		}
 
	}
src/order_gui.cpp
Show inline comments
 
@@ -181,12 +181,13 @@ extern uint ConvertSpeedToDisplaySpeed(u
 
extern uint ConvertDisplaySpeedToSpeed(uint speed, VehicleType type);
 

	
 
static const StringID _order_depot_action_dropdown[] = {
 
	STR_ORDER_DROP_GO_ALWAYS_DEPOT,
 
	STR_ORDER_DROP_SERVICE_DEPOT,
 
	STR_ORDER_DROP_HALT_DEPOT,
 
	STR_ORDER_DROP_UNBUNCH,
 
	INVALID_STRING_ID
 
};
 

	
 
static int DepotActionStringIndex(const Order *order)
 
{
 
	if (order->GetDepotActionType() & ODATFB_HALT) {
 
@@ -240,17 +241,18 @@ void DrawOrderString(const Vehicle *v, c
 

	
 
	SetDParam(0, order_index + 1);
 
	DrawString(left, rtl ? right - 2 * sprite_size.width - 3 : middle, y, STR_ORDER_INDEX, colour, SA_RIGHT | SA_FORCE);
 

	
 
	SetDParam(5, STR_EMPTY);
 
	SetDParam(8, STR_EMPTY);
 
	SetDParam(9, STR_EMPTY);
 

	
 
	/* Check range for aircraft. */
 
	if (v->type == VEH_AIRCRAFT && Aircraft::From(v)->GetRange() > 0 && order->IsGotoOrder()) {
 
		const Order *next = order->next != nullptr ? order->next : v->GetFirstOrder();
 
		if (GetOrderDistance(order, next, v) > Aircraft::From(v)->acache.cached_max_range_sqr) SetDParam(8, STR_ORDER_OUT_OF_RANGE);
 
		if (GetOrderDistance(order, next, v) > Aircraft::From(v)->acache.cached_max_range_sqr) SetDParam(9, STR_ORDER_OUT_OF_RANGE);
 
	}
 

	
 
	switch (order->GetType()) {
 
		case OT_DUMMY:
 
			SetDParam(0, STR_INVALID_ORDER);
 
			SetDParam(1, order->GetDestination());
 
@@ -329,12 +331,18 @@ void DrawOrderString(const Vehicle *v, c
 

	
 
			/* Do not show refitting in the depot in the timetable window. */
 
			if (!timetable && order->IsRefit()) {
 
				SetDParam(5, (order->GetDepotActionType() & ODATFB_HALT) ? STR_ORDER_REFIT_STOP_ORDER : STR_ORDER_REFIT_ORDER);
 
				SetDParam(6, CargoSpec::Get(order->GetRefitCargo())->name);
 
			}
 

	
 
			/* Do not show unbunching in the depot in the timetable window. */
 
			if (!timetable && (order->GetDepotActionType() & ODATFB_UNBUNCH)) {
 
				SetDParam(8, STR_ORDER_WAIT_TO_UNBUNCH);
 
			}
 

	
 
			break;
 

	
 
		case OT_GOTO_WAYPOINT:
 
			SetDParam(0, (order->GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS) ? STR_ORDER_GO_NON_STOP_TO_WAYPOINT : STR_ORDER_GO_TO_WAYPOINT);
 
			SetDParam(1, order->GetDestination());
 
			break;
 
@@ -938,13 +946,12 @@ public:
 
			}
 
		}
 

	
 
		/* First row. */
 
		this->RaiseWidget(WID_O_FULL_LOAD);
 
		this->RaiseWidget(WID_O_UNLOAD);
 
		this->RaiseWidget(WID_O_SERVICE);
 

	
 
		/* Selection widgets. */
 
		/* Train or road vehicle. */
 
		NWidgetStacked *train_row_sel = this->GetWidget<NWidgetStacked>(WID_O_SEL_TOP_ROW_GROUNDVEHICLE);
 
		NWidgetStacked *left_sel      = this->GetWidget<NWidgetStacked>(WID_O_SEL_TOP_LEFT);
 
		NWidgetStacked *middle_sel    = this->GetWidget<NWidgetStacked>(WID_O_SEL_TOP_MIDDLE);
 
@@ -1024,13 +1031,12 @@ public:
 
					}
 
					/* Disable refit button if the order is no 'always go' order.
 
					 * However, keep the service button enabled for refit-orders to allow clearing refits (without knowing about ctrl). */
 
					this->SetWidgetDisabledState(WID_O_REFIT,
 
							(order->GetDepotOrderType() & ODTFB_SERVICE) || (order->GetDepotActionType() & ODATFB_HALT) ||
 
							(!this->can_do_refit && !order->IsRefit()));
 
					this->SetWidgetLoweredState(WID_O_SERVICE, order->GetDepotOrderType() & ODTFB_SERVICE);
 
					break;
 

	
 
				case OT_CONDITIONAL: {
 
					if (row_sel != nullptr) {
 
						row_sel->SetDisplayedPlane(DP_ROW_CONDITIONAL);
 
					} else {
 
@@ -1152,12 +1158,30 @@ public:
 
				break;
 
			}
 

	
 
			case WID_O_CAPTION:
 
				SetDParam(0, this->vehicle->index);
 
				break;
 

	
 
			case WID_O_DEPOT_ACTION: {
 
				VehicleOrderID sel = this->OrderGetSel();
 
				const Order *order = this->vehicle->GetOrder(sel);
 
				if (order == nullptr || !order->IsType(OT_GOTO_DEPOT)) break;
 

	
 
				/* Select the current action selected in the dropdown. The flags don't match the dropdown so we can't just use an index. */
 
				if (order->GetDepotOrderType() & ODTFB_SERVICE) {
 
					SetDParam(0, STR_ORDER_DROP_SERVICE_DEPOT);
 
				} else if (order->GetDepotActionType() & ODATFB_HALT) {
 
					SetDParam(0, STR_ORDER_DROP_HALT_DEPOT);
 
				} else if (order->GetDepotActionType() & ODATFB_UNBUNCH) {
 
					SetDParam(0, STR_ORDER_DROP_UNBUNCH);
 
				} else {
 
					SetDParam(0, STR_ORDER_DROP_GO_ALWAYS_DEPOT);
 
				}
 
				break;
 
			}
 
		}
 
	}
 

	
 
	void OnClick([[maybe_unused]] Point pt, WidgetID widget, [[maybe_unused]] int click_count) override
 
	{
 
		switch (widget) {
 
@@ -1270,18 +1294,14 @@ public:
 
				break;
 

	
 
			case WID_O_REFIT:
 
				this->OrderClick_Refit(0, false);
 
				break;
 

	
 
			case WID_O_SERVICE:
 
				if (this->GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) {
 
					this->OrderClick_Service(-1);
 
				} else {
 
					ShowDropDownMenu(this, _order_depot_action_dropdown, DepotActionStringIndex(this->vehicle->GetOrder(this->OrderGetSel())), WID_O_SERVICE, 0, 0);
 
				}
 
			case WID_O_DEPOT_ACTION:
 
				ShowDropDownMenu(this, _order_depot_action_dropdown, DepotActionStringIndex(this->vehicle->GetOrder(this->OrderGetSel())), WID_O_DEPOT_ACTION, 0, 0);
 
				break;
 

	
 
			case WID_O_REFIT_DROPDOWN:
 
				if (this->GetWidget<NWidgetLeaf>(widget)->ButtonHit(pt)) {
 
					this->OrderClick_Refit(0, true);
 
				} else {
 
@@ -1370,13 +1390,13 @@ public:
 
					case 2: this->OrderClick_Goto(OPOS_CONDITIONAL); break;
 
					case 3: this->OrderClick_Goto(OPOS_SHARE); break;
 
					default: NOT_REACHED();
 
				}
 
				break;
 

	
 
			case WID_O_SERVICE:
 
			case WID_O_DEPOT_ACTION:
 
				this->OrderClick_Service(index);
 
				break;
 

	
 
			case WID_O_REFIT_DROPDOWN:
 
				this->OrderClick_Refit(index, true);
 
				break;
 
@@ -1590,14 +1610,14 @@ static constexpr NWidgetPart _nested_ord
 
					NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_REFIT), SetMinimalSize(93, 12), SetFill(1, 0),
 
															SetDataTip(STR_ORDER_REFIT, STR_ORDER_REFIT_TOOLTIP), SetResize(1, 0),
 
				EndContainer(),
 
				NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_TOP_MIDDLE),
 
					NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_UNLOAD), SetMinimalSize(93, 12), SetFill(1, 0),
 
															SetDataTip(STR_ORDER_TOGGLE_UNLOAD, STR_ORDER_TOOLTIP_UNLOAD), SetResize(1, 0),
 
					NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_SERVICE), SetMinimalSize(93, 12), SetFill(1, 0),
 
															SetDataTip(STR_ORDER_SERVICE, STR_ORDER_SERVICE_TOOLTIP), SetResize(1, 0),
 
					NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_DEPOT_ACTION), SetMinimalSize(93, 12), SetFill(1, 0),
 
															SetDataTip(STR_JUST_STRING, STR_ORDER_DEPOT_ACTION_TOOLTIP), SetResize(1, 0),
 
				EndContainer(),
 
				NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_TOP_RIGHT),
 
					NWidget(WWT_PANEL, COLOUR_GREY), SetMinimalSize(93, 12), SetFill(1, 0), SetResize(1, 0), EndContainer(),
 
					NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_REFIT_DROPDOWN), SetMinimalSize(93, 12), SetFill(1, 0),
 
															SetDataTip(STR_ORDER_REFIT_AUTO, STR_ORDER_REFIT_AUTO_TOOLTIP), SetResize(1, 0),
 
				EndContainer(),
 
@@ -1668,14 +1688,14 @@ static constexpr NWidgetPart _nested_ord
 
													SetDataTip(STR_ORDER_REFIT_AUTO, STR_ORDER_REFIT_AUTO_TOOLTIP), SetResize(1, 0),
 
			EndContainer(),
 
			/* Refit + service buttons. */
 
			NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
 
				NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_REFIT), SetMinimalSize(186, 12), SetFill(1, 0),
 
													SetDataTip(STR_ORDER_REFIT, STR_ORDER_REFIT_TOOLTIP), SetResize(1, 0),
 
				NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_SERVICE), SetMinimalSize(124, 12), SetFill(1, 0),
 
													SetDataTip(STR_ORDER_SERVICE, STR_ORDER_SERVICE_TOOLTIP), SetResize(1, 0),
 
				NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_DEPOT_ACTION), SetMinimalSize(124, 12), SetFill(1, 0),
 
													SetDataTip(STR_JUST_STRING, STR_ORDER_DEPOT_ACTION_TOOLTIP), SetResize(1, 0),
 
			EndContainer(),
 

	
 
			/* Buttons for setting a condition. */
 
			NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
 
				NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_O_COND_VARIABLE), SetMinimalSize(124, 12), SetFill(1, 0),
 
													SetDataTip(STR_NULL, STR_ORDER_CONDITIONAL_VARIABLE_TOOLTIP), SetResize(1, 0),
src/order_type.h
Show inline comments
 
@@ -100,12 +100,13 @@ enum OrderDepotTypeFlags {
 
 * Actions that can be performed when the vehicle enters the depot.
 
 */
 
enum OrderDepotActionFlags {
 
	ODATF_SERVICE_ONLY   = 0,      ///< Only service the vehicle.
 
	ODATFB_HALT          = 1 << 0, ///< Service the vehicle and then halt it.
 
	ODATFB_NEAREST_DEPOT = 1 << 1, ///< Send the vehicle to the nearest depot.
 
	ODATFB_UNBUNCH       = 1 << 2, ///< Service the vehicle and then unbunch it.
 
};
 
DECLARE_ENUM_AS_BIT_SET(OrderDepotActionFlags)
 

	
 
/**
 
 * Variables (of a vehicle) to 'cause' skipping on.
 
 */
 
@@ -157,12 +158,13 @@ enum ModifyOrderFlags : byte {
 
 * Depot action to switch to when doing a #MOF_DEPOT_ACTION.
 
 */
 
enum OrderDepotAction {
 
	DA_ALWAYS_GO, ///< Always go to the depot
 
	DA_SERVICE,   ///< Service only if needed
 
	DA_STOP,      ///< Go to the depot and stop there
 
	DA_UNBUNCH,   ///< Go to the depot and unbunch
 
	DA_END
 
};
 

	
 
/**
 
 * Enumeration for the data to set in #CmdChangeTimetable.
 
 */
src/roadveh_cmd.cpp
Show inline comments
 
@@ -384,13 +384,18 @@ CommandCost CmdTurnRoadVeh(DoCommandFlag
 
	}
 

	
 
	if (IsNormalRoadTile(v->tile) && GetDisallowedRoadDirections(v->tile) != DRD_NONE) return CMD_ERROR;
 

	
 
	if (IsTileType(v->tile, MP_TUNNELBRIDGE) && DirToDiagDir(v->direction) == GetTunnelBridgeDirection(v->tile)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) v->reverse_ctr = 180;
 
	if (flags & DC_EXEC) {
 
		v->reverse_ctr = 180;
 

	
 
		/* Unbunching data is no longer valid. */
 
		v->ResetDepotUnbunching();
 
	}
 

	
 
	return CommandCost();
 
}
 

	
 

	
 
void RoadVehicle::MarkDirty()
 
@@ -1027,12 +1032,13 @@ bool RoadVehLeaveDepot(RoadVehicle *v, b
 
			return true;
 
		}
 

	
 
		if (RoadVehFindCloseTo(v, x, y, v->direction, false) != nullptr) return true;
 

	
 
		VehicleServiceInDepot(v);
 
		v->LeaveUnbunchingDepot();
 

	
 
		StartRoadVehSound(v);
 

	
 
		/* Vehicle is about to leave a depot */
 
		v->cur_speed = 0;
 
	}
 
@@ -1584,13 +1590,17 @@ static bool RoadVehController(RoadVehicl
 

	
 
	ProcessOrders(v);
 
	v->HandleLoading();
 

	
 
	if (v->current_order.IsType(OT_LOADING)) return true;
 

	
 
	if (v->IsInDepot() && RoadVehLeaveDepot(v, true)) return true;
 
	if (v->IsInDepot()) {
 
		/* Check if we should wait here for unbunching. */
 
		if (v->IsWaitingForUnbunching()) return true;
 
		if (RoadVehLeaveDepot(v, true)) return true;
 
	}
 

	
 
	v->ShowVisualEffect();
 

	
 
	/* Check how far the vehicle needs to proceed */
 
	int j = v->UpdateSpeed();
 

	
src/saveload/afterload.cpp
Show inline comments
 
@@ -1762,12 +1762,18 @@ bool AfterLoadGame()
 
		for (Vehicle *v : Vehicle::Iterate()) {
 
			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);
 
			}
 
		}
 
	} else if (IsSavegameVersionBefore(SLV_DEPOT_UNBUNCHING)) {
 
		/* OrderDepotActionFlags were moved, instead of starting at bit 4 they now start at bit 3. */
 
		for (Order *order : Order::Iterate()) {
 
			if (!order->IsType(OT_GOTO_DEPOT)) continue;
 
			order->SetDepotActionType((OrderDepotActionFlags)(order->GetDepotActionType() >> 1));
 
		}
 
	}
 

	
 
	/* The water class was moved/unified. */
 
	if (IsSavegameVersionBefore(SLV_146)) {
 
		for (auto t : Map::Iterate()) {
 
			switch (GetTileType(t)) {
src/saveload/saveload.h
Show inline comments
 
@@ -371,12 +371,13 @@ enum SaveLoadVersion : uint16_t {
 
	SLV_ECONOMY_DATE,                       ///< 326  PR#10700 Split calendar and economy timers and dates.
 
	SLV_ECONOMY_MODE_TIMEKEEPING_UNITS,     ///< 327  PR#11341 Mode to display economy measurements in wallclock units.
 
	SLV_CALENDAR_SUB_DATE_FRACT,            ///< 328  PR#11428 Add sub_date_fract to measure calendar days.
 
	SLV_SHIP_ACCELERATION,                  ///< 329  PR#10734 Start using Vehicle's acceleration field for ships too.
 

	
 
	SLV_MAX_LOAN_FOR_COMPANY,               ///< 330  PR#11224 Separate max loan for each company.
 
	SLV_DEPOT_UNBUNCHING,                   ///< 330  PR#11945 Allow unbunching shared order vehicles at a depot.
 

	
 
	SL_MAX_VERSION,                         ///< Highest possible saveload version
 
};
 

	
 
/** Save or load result codes. */
 
enum SaveOrLoadResult {
src/saveload/vehicle_sl.cpp
Show inline comments
 
@@ -735,12 +735,16 @@ public:
 
		SLE_CONDVAR(Vehicle, group_id,              SLE_UINT16,                  SLV_60, SL_MAX_VERSION),
 

	
 
		SLE_CONDVAR(Vehicle, current_order_time,    SLE_FILE_U32 | SLE_VAR_I32,  SLV_67, SLV_TIMETABLE_TICKS_TYPE),
 
		SLE_CONDVAR(Vehicle, current_order_time,    SLE_INT32,                   SLV_TIMETABLE_TICKS_TYPE, SL_MAX_VERSION),
 
		SLE_CONDVAR(Vehicle, last_loading_tick,     SLE_UINT64,                   SLV_LAST_LOADING_TICK, SL_MAX_VERSION),
 
		SLE_CONDVAR(Vehicle, lateness_counter,      SLE_INT32,                   SLV_67, SL_MAX_VERSION),
 

	
 
		SLE_CONDVAR(Vehicle, depot_unbunching_last_departure, SLE_UINT64,        SLV_DEPOT_UNBUNCHING, SL_MAX_VERSION),
 
		SLE_CONDVAR(Vehicle, depot_unbunching_next_departure, SLE_UINT64,        SLV_DEPOT_UNBUNCHING, SL_MAX_VERSION),
 
		SLE_CONDVAR(Vehicle, round_trip_time,       SLE_INT32,                   SLV_DEPOT_UNBUNCHING, SL_MAX_VERSION),
 
	};
 
#if defined(_MSC_VER) && (_MSC_VER == 1915 || _MSC_VER == 1916)
 
		return description;
 
	}
 
#endif
 
	inline const static SaveLoadCompatTable compat_description = _vehicle_common_sl_compat;
src/ship_cmd.cpp
Show inline comments
 
@@ -384,12 +384,15 @@ static bool CheckReverseShip(const Ship 
 
}
 

	
 
static bool CheckShipLeaveDepot(Ship *v)
 
{
 
	if (!v->IsChainInDepot()) return false;
 

	
 
	/* Check if we should wait here for unbunching. */
 
	if (v->IsWaitingForUnbunching()) return true;
 

	
 
	/* We are leaving a depot, but have to go to the exact same one; re-enter */
 
	if (v->current_order.IsType(OT_GOTO_DEPOT) &&
 
			IsShipDepotTile(v->tile) && GetDepotIndex(v->tile) == v->current_order.GetDestination()) {
 
		VehicleEnterDepot(v);
 
		return true;
 
	}
 
@@ -430,14 +433,15 @@ static bool CheckShipLeaveDepot(Ship *v)
 
	v->vehstatus &= ~VS_HIDDEN;
 

	
 
	v->cur_speed = 0;
 
	v->UpdateViewport(true, true);
 
	SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
 

	
 
	VehicleServiceInDepot(v);
 
	v->LeaveUnbunchingDepot();
 
	v->PlayLeaveStationSound();
 
	VehicleServiceInDepot(v);
 
	InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 
	SetWindowClassesDirty(WC_SHIPS_LIST);
 

	
 
	return false;
 
}
 

	
src/timetable_cmd.cpp
Show inline comments
 
@@ -202,12 +202,18 @@ CommandCost CmdChangeTimetable(DoCommand
 
				}
 
				break;
 

	
 
			default:
 
				break;
 
		}
 

	
 
		/* Unbunching data is no longer valid for any vehicle in this shared order group. */
 
		Vehicle *u = v->FirstShared();
 
		for (; u != nullptr; u = u->NextShared()) {
 
			u->ResetDepotUnbunching();
 
		}
 
	}
 

	
 
	return CommandCost();
 
}
 

	
 
/**
 
@@ -269,24 +275,29 @@ CommandCost CmdSetVehicleOnTime(DoComman
 
				/* A vehicle can't be late if its timetable hasn't started. */
 
				if (!HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED)) continue;
 

	
 
				if (u->lateness_counter > most_late) {
 
					most_late = u->lateness_counter;
 
				}
 

	
 
				/* Unbunching data is no longer valid. */
 
				u->ResetDepotUnbunching();
 
			}
 
			if (most_late > 0) {
 
				for (Vehicle *u = v->FirstShared(); u != nullptr; u = u->NextShared()) {
 
					/* A vehicle can't be late if its timetable hasn't started. */
 
					if (!HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED)) continue;
 

	
 
					u->lateness_counter -= most_late;
 
					SetWindowDirty(WC_VEHICLE_TIMETABLE, u->index);
 
				}
 
			}
 
		} else {
 
			v->lateness_counter = 0;
 
			/* Unbunching data is no longer valid. */
 
			v->ResetDepotUnbunching();
 
			SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
 
		}
 
	}
 

	
 
	return CommandCost();
 
}
 
@@ -380,17 +391,20 @@ CommandCost CmdSetTimetableStart(DoComma
 
			std::sort(vehs.begin(), vehs.end(), &VehicleTimetableSorter);
 
		}
 

	
 
		int idx = 0;
 

	
 
		for (Vehicle *w : vehs) {
 

	
 
			w->lateness_counter = 0;
 
			ClrBit(w->vehicle_flags, VF_TIMETABLE_STARTED);
 
			/* Do multiplication, then division to reduce rounding errors. */
 
			w->timetable_start = start_tick + (idx * total_duration / num_vehs);
 

	
 
			/* Unbunching data is no longer valid. */
 
			v->ResetDepotUnbunching();
 

	
 
			SetWindowDirty(WC_VEHICLE_TIMETABLE, w->index);
 
			++idx;
 
		}
 

	
 
	}
 

	
src/train_cmd.cpp
Show inline comments
 
@@ -2109,12 +2109,15 @@ CommandCost CmdReverseTrainDirection(DoC
 
			} else {
 
				v->cur_speed = 0;
 
				v->SetLastSpeed();
 
				HideFillingPercent(&v->fill_percent_te_id);
 
				ReverseTrainDirection(v);
 
			}
 

	
 
			/* Unbunching data is no longer valid. */
 
			v->ResetDepotUnbunching();
 
		}
 
	}
 
	return CommandCost();
 
}
 

	
 
/**
 
@@ -2139,12 +2142,15 @@ CommandCost CmdForceTrainProceed(DoComma
 
		 * If we are marked stuck we would want to force the train
 
		 * to proceed to the next signal. In the other cases we
 
		 * would like to pass the signal at danger and run till the
 
		 * next signal we encounter. */
 
		t->force_proceed = t->force_proceed == TFP_SIGNAL ? TFP_NONE : HasBit(t->flags, VRF_TRAIN_STUCK) || t->IsChainInDepot() ? TFP_STUCK : TFP_SIGNAL;
 
		SetWindowDirty(WC_VEHICLE_VIEW, t->index);
 

	
 
		/* Unbunching data is no longer valid. */
 
		t->ResetDepotUnbunching();
 
	}
 

	
 
	return CommandCost();
 
}
 

	
 
/**
 
@@ -2272,12 +2278,15 @@ static bool CheckTrainStayInDepot(Train 
 
	if (v->gcache.cached_power == 0) {
 
		v->vehstatus |= VS_STOPPED;
 
		SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
 
		return true;
 
	}
 

	
 
	/* Check if we should wait here for unbunching. */
 
	if (v->IsWaitingForUnbunching()) return true;
 

	
 
	SigSegState seg_state;
 

	
 
	if (v->force_proceed == TFP_NONE) {
 
		/* force proceed was not pressed */
 
		if (++v->wait_counter < 37) {
 
			SetWindowClassesDirty(WC_TRAINS_LIST);
 
@@ -2312,14 +2321,15 @@ static bool CheckTrainStayInDepot(Train 
 
	}
 

	
 
	SetDepotReservation(v->tile, true);
 
	if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile);
 

	
 
	VehicleServiceInDepot(v);
 
	v->LeaveUnbunchingDepot();
 
	v->PlayLeaveStationSound();
 
	SetWindowClassesDirty(WC_TRAINS_LIST);
 
	v->PlayLeaveStationSound();
 

	
 
	v->track = TRACK_BIT_X;
 
	if (v->direction & 2) v->track = TRACK_BIT_Y;
 

	
 
	v->vehstatus &= ~VS_HIDDEN;
 
	v->cur_speed = 0;
src/vehicle.cpp
Show inline comments
 
@@ -807,12 +807,16 @@ void Vehicle::HandlePathfindingResult(bo
 
	if (HasBit(this->vehicle_flags, VF_PATHFINDER_LOST)) return;
 

	
 
	/* It is first time the problem occurred, set the "lost" flag. */
 
	SetBit(this->vehicle_flags, VF_PATHFINDER_LOST);
 
	SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
 
	InvalidateWindowClassesData(GetWindowClassForVehicleType(this->type));
 

	
 
	/* Unbunching data is no longer valid. */
 
	this->ResetDepotUnbunching();
 

	
 
	/* Notify user about the event. */
 
	AI::NewEvent(this->owner, new ScriptEventVehicleLost(this->index));
 
	if (_settings_client.gui.lost_vehicle_warn && this->owner == _local_company) {
 
		SetDParam(0, this->index);
 
		AddVehicleAdviceNewsItem(STR_NEWS_VEHICLE_IS_LOST, this->index);
 
	}
 
@@ -1632,18 +1636,29 @@ void VehicleEnterDepot(Vehicle *v)
 
			/* Vehicles are always stopped on entering depots. Do not restart this one. */
 
			_vehicles_to_autoreplace[v] = false;
 
			/* Invalidate last_loading_station. As the link from the station
 
			 * before the stop to the station after the stop can't be predicted
 
			 * we shouldn't construct it when the vehicle visits the next stop. */
 
			v->last_loading_station = INVALID_STATION;
 

	
 
			/* Clear unbunching data. */
 
			v->ResetDepotUnbunching();
 

	
 
			/* Announce that the vehicle is waiting to players and AIs. */
 
			if (v->owner == _local_company) {
 
				SetDParam(0, v->index);
 
				AddVehicleAdviceNewsItem(STR_NEWS_TRAIN_IS_WAITING + v->type, v->index);
 
			}
 
			AI::NewEvent(v->owner, new ScriptEventVehicleWaitingInDepot(v->index));
 
		}
 

	
 
		/* If we've entered our unbunching depot, record the round trip duration. */
 
		if (v->current_order.GetDepotActionType() & ODATFB_UNBUNCH && v->depot_unbunching_last_departure > 0) {
 
			v->round_trip_time = (TimerGameTick::counter - v->depot_unbunching_last_departure);
 
		}
 

	
 
		v->current_order.MakeDummy();
 
	}
 
}
 

	
 

	
 
/**
 
@@ -2400,12 +2415,91 @@ void Vehicle::HandleLoading(bool mode)
 
	}
 

	
 
	this->IncrementImplicitOrderIndex();
 
}
 

	
 
/**
 
 * Check if the current vehicle has an unbunching order.
 
 * @return true Iff this vehicle has an unbunching order.
 
 */
 
bool Vehicle::HasUnbunchingOrder() const
 
{
 
	for (Order *o : this->Orders()) {
 
		if (o->IsType(OT_GOTO_DEPOT) && o->GetDepotActionType() & ODATFB_UNBUNCH) return true;
 
	}
 
	return false;
 
}
 

	
 
/**
 
 * Leave an unbunching depot and calculate the next departure time for shared order vehicles.
 
 */
 
void Vehicle::LeaveUnbunchingDepot()
 
{
 
	/* Set the start point for this round trip time. */
 
	this->depot_unbunching_last_departure = TimerGameTick::counter;
 

	
 
	/* Tell the timetable we are now "on time." */
 
	this->lateness_counter = 0;
 
	SetWindowDirty(WC_VEHICLE_TIMETABLE, this->index);
 

	
 
	/* Find the average travel time of vehicles that we share orders with. */
 
	uint num_vehicles = 0;
 
	TimerGameTick::Ticks total_travel_time = 0;
 

	
 
	Vehicle *u = this->FirstShared();
 
	for (; u != nullptr; u = u->NextShared()) {
 
		/* Ignore vehicles that are manually stopped or crashed. */
 
		if (u->vehstatus & (VS_STOPPED | VS_CRASHED)) continue;
 

	
 
		num_vehicles++;
 
		total_travel_time += u->round_trip_time;
 
	}
 

	
 
	/* Make sure we cannot divide by 0. */
 
	num_vehicles = std::max(num_vehicles, 1u);
 

	
 
	/* Calculate the separation by finding the average travel time, then calculating equal separation (minimum 1 tick) between vehicles. */
 
	TimerGameTick::Ticks separation = std::max((total_travel_time / num_vehicles / num_vehicles), 1u);
 
	TimerGameTick::TickCounter next_departure = TimerGameTick::counter + separation;
 

	
 
	/* Set the departure time of all vehicles that we share orders with. */
 
	u = this->FirstShared();
 
	for (; u != nullptr; u = u->NextShared()) {
 
		/* Ignore vehicles that are manually stopped or crashed. */
 
		if (u->vehstatus & (VS_STOPPED | VS_CRASHED)) continue;
 

	
 
		u->depot_unbunching_next_departure = next_departure;
 
	}
 
}
 

	
 
/**
 
 * Check whether a vehicle inside a depot is waiting for unbunching.
 
 * @return True if the vehicle must continue waiting, or false if it may try to leave the depot.
 
 */
 
bool Vehicle::IsWaitingForUnbunching() const
 
{
 
	assert(this->IsInDepot());
 

	
 
	/* Don't bother if there are no vehicles sharing orders. */
 
	if (!this->IsOrderListShared()) return false;
 

	
 
	/* Don't do anything if there aren't enough orders. */
 
	if (this->GetNumOrders() <= 1) return false;
 

	
 
	/*
 
	 * Make sure this is the correct depot for unbunching.
 
	 * If we are headed for the first order, we must wrap around back to the last order.
 
	 */
 
	bool is_first_order = (this->GetOrder(this->cur_real_order_index) == this->GetFirstOrder());
 
	Order *previous_order = (is_first_order) ? this->GetLastOrder() : this->GetOrder(this->cur_real_order_index - 1);
 
	if (previous_order == nullptr || !previous_order->IsType(OT_GOTO_DEPOT) || !(previous_order->GetDepotActionType() & ODATFB_UNBUNCH)) return false;
 

	
 
	return (this->depot_unbunching_next_departure > TimerGameTick::counter);
 
};
 

	
 
/**
 
 * Send this vehicle to the depot using the given command(s).
 
 * @param flags   the command flags (like execute and such).
 
 * @param command the command to execute.
 
 * @return the cost of the depot action.
 
 */
 
CommandCost Vehicle::SendToDepot(DoCommandFlag flags, DepotCommand command)
 
@@ -2413,12 +2507,15 @@ CommandCost Vehicle::SendToDepot(DoComma
 
	CommandCost ret = CheckOwnership(this->owner);
 
	if (ret.Failed()) return ret;
 

	
 
	if (this->vehstatus & VS_CRASHED) return CMD_ERROR;
 
	if (this->IsStoppedInDepot()) return CMD_ERROR;
 

	
 
	/* No matter why we're headed to the depot, unbunching data is no longer valid. */
 
	if (flags & DC_EXEC) this->ResetDepotUnbunching();
 

	
 
	if (this->current_order.IsType(OT_GOTO_DEPOT)) {
 
		bool halt_in_depot = (this->current_order.GetDepotActionType() & ODATFB_HALT) != 0;
 
		if (((command & DepotCommand::Service) != DepotCommand::None) == halt_in_depot) {
 
			/* We called with a different DEPOT_SERVICE setting.
 
			 * Now we change the setting to apply the new one and let the vehicle head for the same depot.
 
			 * Note: the if is (true for requesting service == true for ordered to stop in depot)          */
src/vehicle_base.h
Show inline comments
 
@@ -814,12 +814,16 @@ public:
 
	inline bool ServiceIntervalIsPercent() const { return HasBit(this->vehicle_flags, VF_SERVINT_IS_PERCENT); }
 

	
 
	inline void SetServiceIntervalIsCustom(bool on) { SB(this->vehicle_flags, VF_SERVINT_IS_CUSTOM, 1, on); }
 

	
 
	inline void SetServiceIntervalIsPercent(bool on) { SB(this->vehicle_flags, VF_SERVINT_IS_PERCENT, 1, on); }
 

	
 
	bool HasUnbunchingOrder() const;
 
	void LeaveUnbunchingDepot();
 
	bool IsWaitingForUnbunching() const;
 

	
 
private:
 
	/**
 
	 * Advance cur_real_order_index to the next real order.
 
	 * cur_implicit_order_index is not touched.
 
	 */
 
	void SkipToNextRealOrderIndex()
src/vehicle_cmd.cpp
Show inline comments
 
@@ -626,12 +626,16 @@ CommandCost CmdStartStopVehicle(DoComman
 

	
 
	if (flags & DC_EXEC) {
 
		if (v->IsStoppedInDepot() && (flags & DC_AUTOREPLACE) == 0) DeleteVehicleNews(veh_id, STR_NEWS_TRAIN_IS_WAITING + v->type);
 

	
 
		v->vehstatus ^= VS_STOPPED;
 
		if (v->type != VEH_TRAIN) v->cur_speed = 0; // trains can stop 'slowly'
 

	
 
		/* Unbunching data is no longer valid. */
 
		v->ResetDepotUnbunching();
 

	
 
		v->MarkDirty();
 
		SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
 
		SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
 
		SetWindowClassesDirty(GetWindowClassForVehicleType(v->type));
 
		InvalidateWindowData(WC_VEHICLE_VIEW, v->index);
 
	}
src/vehicle_gui.cpp
Show inline comments
 
@@ -3084,13 +3084,13 @@ public:
 
	}
 

	
 
	void DrawWidget(const Rect &r, WidgetID widget) const override
 
	{
 
		if (widget != WID_VV_START_STOP) return;
 

	
 
		const Vehicle *v = Vehicle::Get(this->window_number);
 
		Vehicle *v = Vehicle::Get(this->window_number);
 
		StringID str;
 
		TextColour text_colour = TC_FROMSTRING;
 
		if (v->vehstatus & VS_CRASHED) {
 
			str = STR_VEHICLE_STATUS_CRASHED;
 
		} else if (v->type != VEH_AIRCRAFT && v->breakdown_ctr == 1) { // check for aircraft necessary?
 
			str = STR_VEHICLE_STATUS_BROKEN_DOWN;
 
@@ -3110,12 +3110,14 @@ public:
 
				str = STR_VEHICLE_STATUS_STOPPED;
 
			}
 
		} else if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_TRAIN_STUCK) && !v->current_order.IsType(OT_LOADING)) {
 
			str = STR_VEHICLE_STATUS_TRAIN_STUCK;
 
		} else if (v->type == VEH_AIRCRAFT && HasBit(Aircraft::From(v)->flags, VAF_DEST_TOO_FAR) && !v->current_order.IsType(OT_LOADING)) {
 
			str = STR_VEHICLE_STATUS_AIRCRAFT_TOO_FAR;
 
		} else if (v->IsInDepot() && v->IsWaitingForUnbunching()) {
 
			str = STR_VEHICLE_STATUS_WAITING_UNBUNCHING;
 
		} else { // vehicle is in a "normal" state, show current order
 
			if (mouse_over_start_stop) {
 
				if (v->vehstatus & VS_STOPPED) {
 
					text_colour = TC_RED | TC_FORCED;
 
				} else if (v->type == VEH_TRAIN && HasBit(Train::From(v)->flags, VRF_TRAIN_STUCK) && !v->current_order.IsType(OT_LOADING)) {
 
					text_colour = TC_ORANGE | TC_FORCED;
 
@@ -3140,12 +3142,14 @@ public:
 
						 * It is primarily to guard for the case that there is no
 
						 * depot with index 0, which would be used as fallback for
 
						 * evaluating the string in the status bar. */
 
						str = STR_EMPTY;
 
					} else if (v->current_order.GetDepotActionType() & ODATFB_HALT) {
 
						str = HasBit(v->vehicle_flags, VF_PATHFINDER_LOST) ? STR_VEHICLE_STATUS_CANNOT_REACH_DEPOT_VEL : STR_VEHICLE_STATUS_HEADING_FOR_DEPOT_VEL;
 
					} else if (v->current_order.GetDepotActionType() & ODATFB_UNBUNCH) {
 
						str = HasBit(v->vehicle_flags, VF_PATHFINDER_LOST) ? STR_VEHICLE_STATUS_CANNOT_REACH_DEPOT_SERVICE_VEL : STR_VEHICLE_STATUS_HEADING_FOR_DEPOT_UNBUNCH_VEL;
 
					} else {
 
						str = HasBit(v->vehicle_flags, VF_PATHFINDER_LOST) ? STR_VEHICLE_STATUS_CANNOT_REACH_DEPOT_SERVICE_VEL : STR_VEHICLE_STATUS_HEADING_FOR_DEPOT_SERVICE_VEL;
 
					}
 
					break;
 
				}
 

	
src/widgets/order_widget.h
Show inline comments
 
@@ -17,17 +17,18 @@ enum OrderWidgets : WidgetID {
 
	WID_O_ORDER_LIST,                ///< Order list panel.
 
	WID_O_SCROLLBAR,                 ///< Order list scrollbar.
 
	WID_O_SKIP,                      ///< Skip current order.
 
	WID_O_DELETE,                    ///< Delete selected order.
 
	WID_O_STOP_SHARING,              ///< Stop sharing orders.
 
	WID_O_NON_STOP,                  ///< Goto non-stop to destination.
 
	WID_O_DEPOT_UNBUNCHING,          ///< Toggle unbunching.
 
	WID_O_GOTO,                      ///< Goto destination.
 
	WID_O_FULL_LOAD,                 ///< Select full load.
 
	WID_O_UNLOAD,                    ///< Select unload.
 
	WID_O_REFIT,                     ///< Select refit.
 
	WID_O_SERVICE,                   ///< Select service (at depot).
 
	WID_O_DEPOT_ACTION,              ///< Dropdown to select the depot action (stop, service if needed, unbunch).
 
	WID_O_REFIT_DROPDOWN,            ///< Open refit options.
 
	WID_O_COND_VARIABLE,             ///< Choose condition variable.
 
	WID_O_COND_COMPARATOR,           ///< Choose condition type.
 
	WID_O_COND_VALUE,                ///< Choose condition value.
 
	WID_O_SEL_TOP_LEFT,              ///< #NWID_SELECTION widget for left part of the top row of the 'your train' order window.
 
	WID_O_SEL_TOP_MIDDLE,            ///< #NWID_SELECTION widget for middle part of the top row of the 'your train' order window.
0 comments (0 inline, 0 general)