Merge branch 'multi-sd' into jgrpp
This commit is contained in:
		@@ -269,10 +269,12 @@ CommandProc CmdScheduledDispatch;
 | 
				
			|||||||
CommandProcEx CmdScheduledDispatchAdd;
 | 
					CommandProcEx CmdScheduledDispatchAdd;
 | 
				
			||||||
CommandProc CmdScheduledDispatchRemove;
 | 
					CommandProc CmdScheduledDispatchRemove;
 | 
				
			||||||
CommandProc CmdScheduledDispatchSetDuration;
 | 
					CommandProc CmdScheduledDispatchSetDuration;
 | 
				
			||||||
CommandProc CmdScheduledDispatchSetStartDate;
 | 
					CommandProcEx CmdScheduledDispatchSetStartDate;
 | 
				
			||||||
CommandProc CmdScheduledDispatchSetDelay;
 | 
					CommandProc CmdScheduledDispatchSetDelay;
 | 
				
			||||||
CommandProc CmdScheduledDispatchResetLastDispatch;
 | 
					CommandProc CmdScheduledDispatchResetLastDispatch;
 | 
				
			||||||
CommandProc CmdScheduledDispatchClear;
 | 
					CommandProc CmdScheduledDispatchClear;
 | 
				
			||||||
 | 
					CommandProcEx CmdScheduledDispatchAddNewSchedule;
 | 
				
			||||||
 | 
					CommandProc CmdScheduledDispatchRemoveSchedule;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CommandProc CmdAddPlan;
 | 
					CommandProc CmdAddPlan;
 | 
				
			||||||
CommandProcEx CmdAddPlanLine;
 | 
					CommandProcEx CmdAddPlanLine;
 | 
				
			||||||
@@ -509,6 +511,8 @@ static const Command _command_proc_table[] = {
 | 
				
			|||||||
	DEF_CMD(CmdScheduledDispatchSetDelay,                      0, CMDT_ROUTE_MANAGEMENT      ), // CMD_SCHEDULED_DISPATCH_SET_DELAY
 | 
						DEF_CMD(CmdScheduledDispatchSetDelay,                      0, CMDT_ROUTE_MANAGEMENT      ), // CMD_SCHEDULED_DISPATCH_SET_DELAY
 | 
				
			||||||
	DEF_CMD(CmdScheduledDispatchResetLastDispatch,             0, CMDT_ROUTE_MANAGEMENT      ), // CMD_SCHEDULED_DISPATCH_RESET_LAST_DISPATCH
 | 
						DEF_CMD(CmdScheduledDispatchResetLastDispatch,             0, CMDT_ROUTE_MANAGEMENT      ), // CMD_SCHEDULED_DISPATCH_RESET_LAST_DISPATCH
 | 
				
			||||||
	DEF_CMD(CmdScheduledDispatchClear,                         0, CMDT_ROUTE_MANAGEMENT      ), // CMD_SCHEDULED_DISPATCH_CLEAR
 | 
						DEF_CMD(CmdScheduledDispatchClear,                         0, CMDT_ROUTE_MANAGEMENT      ), // CMD_SCHEDULED_DISPATCH_CLEAR
 | 
				
			||||||
 | 
						DEF_CMD(CmdScheduledDispatchAddNewSchedule,                0, CMDT_ROUTE_MANAGEMENT      ), // CMD_SCHEDULED_DISPATCH_ADD_NEW_SCHEDULE
 | 
				
			||||||
 | 
						DEF_CMD(CmdScheduledDispatchRemoveSchedule,                0, CMDT_ROUTE_MANAGEMENT      ), // CMD_SCHEDULED_DISPATCH_REMOVE_SCHEDULE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	DEF_CMD(CmdAddPlan,                              CMD_NO_TEST, CMDT_OTHER_MANAGEMENT      ), // CMD_ADD_PLAN
 | 
						DEF_CMD(CmdAddPlan,                              CMD_NO_TEST, CMDT_OTHER_MANAGEMENT      ), // CMD_ADD_PLAN
 | 
				
			||||||
	DEF_CMD(CmdAddPlanLine,                          CMD_NO_TEST, CMDT_OTHER_MANAGEMENT      ), // CMD_ADD_PLAN_LINE
 | 
						DEF_CMD(CmdAddPlanLine,                          CMD_NO_TEST, CMDT_OTHER_MANAGEMENT      ), // CMD_ADD_PLAN_LINE
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -154,4 +154,7 @@ CommandCallback CcDeleteVirtualTrain;
 | 
				
			|||||||
CommandCallback CcAddVirtualEngine;
 | 
					CommandCallback CcAddVirtualEngine;
 | 
				
			||||||
CommandCallback CcMoveNewVirtualEngine;
 | 
					CommandCallback CcMoveNewVirtualEngine;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* schdispatch_gui.cpp */
 | 
				
			||||||
 | 
					CommandCallback CcAddNewSchDispatchSchedule;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* COMMAND_FUNC_H */
 | 
					#endif /* COMMAND_FUNC_H */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -450,6 +450,8 @@ enum Commands {
 | 
				
			|||||||
	CMD_SCHEDULED_DISPATCH_SET_DELAY,           ///< scheduled dispatch set maximum allow delay
 | 
						CMD_SCHEDULED_DISPATCH_SET_DELAY,           ///< scheduled dispatch set maximum allow delay
 | 
				
			||||||
	CMD_SCHEDULED_DISPATCH_RESET_LAST_DISPATCH, ///< scheduled dispatch reset last dispatch date
 | 
						CMD_SCHEDULED_DISPATCH_RESET_LAST_DISPATCH, ///< scheduled dispatch reset last dispatch date
 | 
				
			||||||
	CMD_SCHEDULED_DISPATCH_CLEAR,               ///< scheduled dispatch clear schedule
 | 
						CMD_SCHEDULED_DISPATCH_CLEAR,               ///< scheduled dispatch clear schedule
 | 
				
			||||||
 | 
						CMD_SCHEDULED_DISPATCH_ADD_NEW_SCHEDULE,    ///< scheduled dispatch add new schedule
 | 
				
			||||||
 | 
						CMD_SCHEDULED_DISPATCH_REMOVE_SCHEDULE,     ///< scheduled dispatch remove schedule
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	CMD_ADD_PLAN,
 | 
						CMD_ADD_PLAN,
 | 
				
			||||||
	CMD_ADD_PLAN_LINE,
 | 
						CMD_ADD_PLAN_LINE,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,6 +33,8 @@
 | 
				
			|||||||
#include "cargo_type.h"
 | 
					#include "cargo_type.h"
 | 
				
			||||||
#include "departures_func.h"
 | 
					#include "departures_func.h"
 | 
				
			||||||
#include "departures_type.h"
 | 
					#include "departures_type.h"
 | 
				
			||||||
 | 
					#include "tracerestrict.h"
 | 
				
			||||||
 | 
					#include "3rdparty/cpp-btree/btree_set.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <map>
 | 
					#include <map>
 | 
				
			||||||
#include <set>
 | 
					#include <set>
 | 
				
			||||||
@@ -40,7 +42,7 @@
 | 
				
			|||||||
#include <algorithm>
 | 
					#include <algorithm>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* A cache of used departure time for scheduled dispatch in departure time calculation */
 | 
					/* A cache of used departure time for scheduled dispatch in departure time calculation */
 | 
				
			||||||
typedef std::map<uint32, std::set<DateTicksScaled>> schdispatch_cache_t;
 | 
					typedef std::map<const DispatchSchedule *, btree::btree_set<DateTicksScaled>> schdispatch_cache_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** A scheduled order. */
 | 
					/** A scheduled order. */
 | 
				
			||||||
typedef struct OrderDate
 | 
					typedef struct OrderDate
 | 
				
			||||||
@@ -79,30 +81,36 @@ static bool IsArrival(const Order *order, StationID station) {
 | 
				
			|||||||
			!(order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION));
 | 
								!(order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static uint8 GetDepartureConditionalOrderMode(const Order *order)
 | 
					static uint8 GetDepartureConditionalOrderMode(const Order *order, DateTicksScaled eval_date)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (order->GetConditionVariable() == OCV_UNCONDITIONALLY) return 1;
 | 
						if (order->GetConditionVariable() == OCV_UNCONDITIONALLY) return 1;
 | 
				
			||||||
 | 
						if (order->GetConditionVariable() == OCV_TIME_DATE) {
 | 
				
			||||||
 | 
							int value = GetTraceRestrictTimeDateValueFromDate(static_cast<TraceRestrictTimeDateValueField>(order->GetConditionValue()), eval_date);
 | 
				
			||||||
 | 
							return OrderConditionCompare(order->GetConditionComparator(), value, order->GetXData()) ? 1 : 2;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return _settings_client.gui.departure_conditionals;
 | 
						return _settings_client.gui.departure_conditionals;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline bool VehicleSetNextDepartureTime(DateTicks *previous_departure, uint *waiting_time, const DateTicksScaled date_only_scaled, const Vehicle *v, const Order *order, bool arrived_at_timing_point, schdispatch_cache_t &dept_schedule_last)
 | 
					static inline bool VehicleSetNextDepartureTime(DateTicks *previous_departure, uint *waiting_time, const DateTicksScaled date_only_scaled, const Vehicle *v, const Order *order, bool arrived_at_timing_point, schdispatch_cache_t &dept_schedule_last)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)) {
 | 
						if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)) {
 | 
				
			||||||
		/* Loop over all order to find the first waiting order */
 | 
							auto is_current_implicit_order = [&v](const Order *o) -> bool {
 | 
				
			||||||
		for (int j = 0; j < v->orders.list->GetNumOrders(); ++j) {
 | 
								if (v->cur_implicit_order_index >= v->orders.list->GetNumOrders()) return false;
 | 
				
			||||||
			Order* iterating_order = v->orders.list->GetOrderAt(j);
 | 
								return v->orders.list->GetOrderAt(v->cur_implicit_order_index) == o;
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* This condition means that we want departure time for the dispatch order */
 | 
				
			||||||
 | 
							/* but not if the vehicle has arrived at the dispatch order because the timetable is already shifted */
 | 
				
			||||||
 | 
							if (order->IsScheduledDispatchOrder(true) && !(arrived_at_timing_point && is_current_implicit_order(order))) {
 | 
				
			||||||
 | 
								const DispatchSchedule &ds = v->orders.list->GetDispatchScheduleByIndex(order->GetDispatchScheduleIndex());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (iterating_order->IsWaitTimetabled() && !iterating_order->IsType(OT_IMPLICIT)) {
 | 
					 | 
				
			||||||
				/* This condition means that we want departure time for the first order */
 | 
					 | 
				
			||||||
				/* but not if the vehicle has arrived at the first order because the timetable is already shifted */
 | 
					 | 
				
			||||||
				if (iterating_order == order && !(arrived_at_timing_point && v->cur_implicit_order_index == j)) {
 | 
					 | 
				
			||||||
			DateTicksScaled actual_departure    = -1;
 | 
								DateTicksScaled actual_departure    = -1;
 | 
				
			||||||
					const DateTicksScaled begin_time    = v->orders.list->GetScheduledDispatchStartTick();
 | 
								const DateTicksScaled begin_time    = ds.GetScheduledDispatchStartTick();
 | 
				
			||||||
					const uint32 dispatch_duration      = v->orders.list->GetScheduledDispatchDuration();
 | 
								const uint32 dispatch_duration      = ds.GetScheduledDispatchDuration();
 | 
				
			||||||
					const int32 max_delay               = v->orders.list->GetScheduledDispatchDelay();
 | 
								const int32 max_delay               = ds.GetScheduledDispatchDelay();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			/* Earliest possible departure according to schedue */
 | 
								/* Earliest possible departure according to schedue */
 | 
				
			||||||
					DateTicksScaled earliest_departure = begin_time + v->orders.list->GetScheduledDispatchLastDispatch();
 | 
								DateTicksScaled earliest_departure = begin_time + ds.GetScheduledDispatchLastDispatch();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			/* Earliest possible departure according to vehicle current timetable */
 | 
								/* Earliest possible departure according to vehicle current timetable */
 | 
				
			||||||
			if (earliest_departure + max_delay < date_only_scaled + *previous_departure + order->GetTravelTime()) {
 | 
								if (earliest_departure + max_delay < date_only_scaled + *previous_departure + order->GetTravelTime()) {
 | 
				
			||||||
@@ -110,8 +118,10 @@ static inline bool VehicleSetNextDepartureTime(DateTicks *previous_departure, ui
 | 
				
			|||||||
				/* -1 because this number is actually a moment before actual departure */
 | 
									/* -1 because this number is actually a moment before actual departure */
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								btree::btree_set<DateTicksScaled> &slot_cache = dept_schedule_last[&ds];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			/* Find next available slots */
 | 
								/* Find next available slots */
 | 
				
			||||||
					for (auto current_offset : v->orders.list->GetScheduledDispatch()) {
 | 
								for (auto current_offset : ds.GetScheduledDispatch()) {
 | 
				
			||||||
				if (current_offset >= dispatch_duration) continue;
 | 
									if (current_offset >= dispatch_duration) continue;
 | 
				
			||||||
				DateTicksScaled current_departure = begin_time + current_offset;
 | 
									DateTicksScaled current_departure = begin_time + current_offset;
 | 
				
			||||||
				while (current_departure <= earliest_departure) {
 | 
									while (current_departure <= earliest_departure) {
 | 
				
			||||||
@@ -119,7 +129,7 @@ static inline bool VehicleSetNextDepartureTime(DateTicks *previous_departure, ui
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				/* Make sure the slots has not already been used previously in this departure board calculation */
 | 
									/* Make sure the slots has not already been used previously in this departure board calculation */
 | 
				
			||||||
						while (dept_schedule_last[v->orders.list->index].count(current_departure) > 0) {
 | 
									while (slot_cache.count(current_departure) > 0) {
 | 
				
			||||||
					current_departure += dispatch_duration;
 | 
										current_departure += dispatch_duration;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -130,21 +140,18 @@ static inline bool VehicleSetNextDepartureTime(DateTicks *previous_departure, ui
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			*waiting_time = order->GetWaitTime() + actual_departure - date_only_scaled - *previous_departure - order->GetTravelTime();
 | 
								*waiting_time = order->GetWaitTime() + actual_departure - date_only_scaled - *previous_departure - order->GetTravelTime();
 | 
				
			||||||
			*previous_departure = actual_departure - date_only_scaled + order->GetWaitTime();
 | 
								*previous_departure = actual_departure - date_only_scaled + order->GetWaitTime();
 | 
				
			||||||
					dept_schedule_last[v->orders.list->index].insert(actual_departure);
 | 
								slot_cache.insert(actual_departure);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			/* Return true means that vehicle lateness should be clear from this point onward */
 | 
								/* Return true means that vehicle lateness should be clear from this point onward */
 | 
				
			||||||
			return true;
 | 
								return true;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* This is special case for proper calculation of arrival time. */
 | 
							/* This is special case for proper calculation of arrival time. */
 | 
				
			||||||
				if (arrived_at_timing_point && v->cur_implicit_order_index == j) {
 | 
							if (arrived_at_timing_point && v->cur_implicit_order_index < v->orders.list->GetNumOrders() && v->orders.list->GetOrderAt(v->cur_implicit_order_index)->IsScheduledDispatchOrder(true)) {
 | 
				
			||||||
			*previous_departure += order->GetTravelTime() + order->GetWaitTime();
 | 
								*previous_departure += order->GetTravelTime() + order->GetWaitTime();
 | 
				
			||||||
			*waiting_time = -v->lateness_counter + order->GetWaitTime();
 | 
								*waiting_time = -v->lateness_counter + order->GetWaitTime();
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
				break;
 | 
					 | 
				
			||||||
			} /* if it is first waiting order */
 | 
					 | 
				
			||||||
		} /* for in order list */
 | 
					 | 
				
			||||||
	} /* if vehicle is on scheduled dispatch */
 | 
						} /* if vehicle is on scheduled dispatch */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Not using schedule for this departure time */
 | 
						/* Not using schedule for this departure time */
 | 
				
			||||||
@@ -297,14 +304,9 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
 | 
				
			|||||||
					should_reset_lateness = true;
 | 
										should_reset_lateness = true;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				/* If the scheduled departure date is too far in the future, stop. */
 | 
					 | 
				
			||||||
				if (start_date - v->lateness_counter > max_date) {
 | 
					 | 
				
			||||||
					break;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				/* If the order is a conditional branch, handle it. */
 | 
									/* If the order is a conditional branch, handle it. */
 | 
				
			||||||
				if (order->IsType(OT_CONDITIONAL)) {
 | 
									if (order->IsType(OT_CONDITIONAL)) {
 | 
				
			||||||
					switch(GetDepartureConditionalOrderMode(order)) {
 | 
										switch(GetDepartureConditionalOrderMode(order, start_date + date_only_scaled)) {
 | 
				
			||||||
							case 0: {
 | 
												case 0: {
 | 
				
			||||||
								/* Give up */
 | 
													/* Give up */
 | 
				
			||||||
								break;
 | 
													break;
 | 
				
			||||||
@@ -328,12 +330,18 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
 | 
				
			|||||||
								if (status != D_CANCELLED) {
 | 
													if (status != D_CANCELLED) {
 | 
				
			||||||
									status = D_TRAVELLING;
 | 
														status = D_TRAVELLING;
 | 
				
			||||||
								}
 | 
													}
 | 
				
			||||||
 | 
													start_date -= order->GetWaitTime(); /* Added previously in VehicleSetNextDepartureTime */
 | 
				
			||||||
								order = (order->next == nullptr) ? v->GetFirstOrder() : order->next;
 | 
													order = (order->next == nullptr) ? v->GetFirstOrder() : order->next;
 | 
				
			||||||
								continue;
 | 
													continue;
 | 
				
			||||||
							}
 | 
												}
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									/* If the scheduled departure date is too far in the future, stop. */
 | 
				
			||||||
 | 
									if (start_date - v->lateness_counter > max_date) {
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				/* Skip it if it's an automatic order. */
 | 
									/* Skip it if it's an automatic order. */
 | 
				
			||||||
				if (order->IsType(OT_IMPLICIT)) {
 | 
									if (order->IsType(OT_IMPLICIT)) {
 | 
				
			||||||
					order = (order->next == nullptr) ? v->GetFirstOrder() : order->next;
 | 
										order = (order->next == nullptr) ? v->GetFirstOrder() : order->next;
 | 
				
			||||||
@@ -464,17 +472,23 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
				/* If the order is a conditional branch, handle it. */
 | 
									/* If the order is a conditional branch, handle it. */
 | 
				
			||||||
				if (order->IsType(OT_CONDITIONAL)) {
 | 
									if (order->IsType(OT_CONDITIONAL)) {
 | 
				
			||||||
					switch(GetDepartureConditionalOrderMode(order)) {
 | 
										switch (GetDepartureConditionalOrderMode(order, c.scheduled_date != 0 ? c.scheduled_date : _scaled_date_ticks)) {
 | 
				
			||||||
							case 0: {
 | 
												case 0: {
 | 
				
			||||||
								/* Give up */
 | 
													/* Give up */
 | 
				
			||||||
								break;
 | 
													break;
 | 
				
			||||||
							}
 | 
												}
 | 
				
			||||||
							case 1: {
 | 
												case 1: {
 | 
				
			||||||
								/* Take the branch */
 | 
													/* Take the branch */
 | 
				
			||||||
 | 
													if (c.scheduled_date != 0 && (order->GetWaitTime() != 0 || order->IsWaitTimetabled())) {
 | 
				
			||||||
 | 
														c.scheduled_date += order->GetWaitTime();
 | 
				
			||||||
 | 
													} else {
 | 
				
			||||||
 | 
														c.scheduled_date = 0;
 | 
				
			||||||
 | 
													}
 | 
				
			||||||
								order = least_order->v->GetOrder(order->GetConditionSkipToOrder());
 | 
													order = least_order->v->GetOrder(order->GetConditionSkipToOrder());
 | 
				
			||||||
								if (order == nullptr) {
 | 
													if (order == nullptr) {
 | 
				
			||||||
									break;
 | 
														break;
 | 
				
			||||||
								}
 | 
													}
 | 
				
			||||||
 | 
													if (c.scheduled_date != 0) c.scheduled_date -= order->GetTravelTime();
 | 
				
			||||||
								continue;
 | 
													continue;
 | 
				
			||||||
							}
 | 
												}
 | 
				
			||||||
							case 2: {
 | 
												case 2: {
 | 
				
			||||||
@@ -520,7 +534,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
 | 
				
			|||||||
						order->GetType() != OT_IMPLICIT) ||
 | 
											order->GetType() != OT_IMPLICIT) ||
 | 
				
			||||||
						order->GetNonStopType() == ONSF_NO_STOP_AT_ANY_STATION ||
 | 
											order->GetNonStopType() == ONSF_NO_STOP_AT_ANY_STATION ||
 | 
				
			||||||
						order->GetNonStopType() == ONSF_NO_STOP_AT_DESTINATION_STATION) {
 | 
											order->GetNonStopType() == ONSF_NO_STOP_AT_DESTINATION_STATION) {
 | 
				
			||||||
					c.scheduled_date += order->GetWaitTime();
 | 
										if (c.scheduled_date != 0) c.scheduled_date += order->GetWaitTime();
 | 
				
			||||||
					order = (order->next == nullptr) ? least_order->v->GetFirstOrder() : order->next;
 | 
										order = (order->next == nullptr) ? least_order->v->GetFirstOrder() : order->next;
 | 
				
			||||||
					continue;
 | 
										continue;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
@@ -552,7 +566,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
 | 
				
			|||||||
					break;
 | 
										break;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				c.scheduled_date += order->GetWaitTime();
 | 
									if (c.scheduled_date != 0) c.scheduled_date += order->GetWaitTime();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				/* Get the next order, which may be the vehicle's first order. */
 | 
									/* Get the next order, which may be the vehicle's first order. */
 | 
				
			||||||
				order = (order->next == nullptr) ? least_order->v->GetFirstOrder() : order->next;
 | 
									order = (order->next == nullptr) ? least_order->v->GetFirstOrder() : order->next;
 | 
				
			||||||
@@ -702,7 +716,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
 | 
				
			|||||||
		for (int i = least_order->v->GetNumOrders(); i > 0; --i) {
 | 
							for (int i = least_order->v->GetNumOrders(); i > 0; --i) {
 | 
				
			||||||
			/* If the order is a conditional branch, handle it. */
 | 
								/* If the order is a conditional branch, handle it. */
 | 
				
			||||||
			if (order->IsType(OT_CONDITIONAL)) {
 | 
								if (order->IsType(OT_CONDITIONAL)) {
 | 
				
			||||||
				switch(GetDepartureConditionalOrderMode(order)) {
 | 
									switch(GetDepartureConditionalOrderMode(order, least_order->expected_date)) {
 | 
				
			||||||
						case 0: {
 | 
											case 0: {
 | 
				
			||||||
							/* Give up */
 | 
												/* Give up */
 | 
				
			||||||
							break;
 | 
												break;
 | 
				
			||||||
@@ -714,6 +728,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
 | 
				
			|||||||
								break;
 | 
													break;
 | 
				
			||||||
							}
 | 
												}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												least_order->expected_date -= order->GetTravelTime(); /* Added in next VehicleSetNextDepartureTime */
 | 
				
			||||||
							if (VehicleSetNextDepartureTime(&least_order->expected_date, &least_order->scheduled_waiting_time, date_only_scaled, least_order->v, order, false, schdispatch_last_planned_dispatch)) {
 | 
												if (VehicleSetNextDepartureTime(&least_order->expected_date, &least_order->scheduled_waiting_time, date_only_scaled, least_order->v, order, false, schdispatch_last_planned_dispatch)) {
 | 
				
			||||||
								least_order->lateness = 0;
 | 
													least_order->lateness = 0;
 | 
				
			||||||
							}
 | 
												}
 | 
				
			||||||
@@ -722,6 +737,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vector<const Vehi
 | 
				
			|||||||
						}
 | 
											}
 | 
				
			||||||
						case 2: {
 | 
											case 2: {
 | 
				
			||||||
							/* Do not take the branch */
 | 
												/* Do not take the branch */
 | 
				
			||||||
 | 
												least_order->expected_date -= order->GetWaitTime(); /* Added previously in VehicleSetNextDepartureTime */
 | 
				
			||||||
							order = (order->next == nullptr) ? least_order->v->GetFirstOrder() : order->next;
 | 
												order = (order->next == nullptr) ? least_order->v->GetFirstOrder() : order->next;
 | 
				
			||||||
							if (VehicleSetNextDepartureTime(&least_order->expected_date, &least_order->scheduled_waiting_time, date_only_scaled, least_order->v, order, false, schdispatch_last_planned_dispatch)) {
 | 
												if (VehicleSetNextDepartureTime(&least_order->expected_date, &least_order->scheduled_waiting_time, date_only_scaled, least_order->v, order, false, schdispatch_last_planned_dispatch)) {
 | 
				
			||||||
								least_order->lateness = 0;
 | 
													least_order->lateness = 0;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5701,8 +5701,9 @@ STR_TIMETABLE_AUTO_SEPARATION_TOOLTIP                           :{BLACK}Automati
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
STR_TIMETABLE_SCHEDULED_DISPATCH                                :{BLACK}Scheduled Dispatch
 | 
					STR_TIMETABLE_SCHEDULED_DISPATCH                                :{BLACK}Scheduled Dispatch
 | 
				
			||||||
STR_TIMETABLE_SCHEDULED_DISPATCH_TOOLTIP                        :{BLACK}Open scheduled dispatch windows for automatic setting of timetable start time
 | 
					STR_TIMETABLE_SCHEDULED_DISPATCH_TOOLTIP                        :{BLACK}Open scheduled dispatch windows for automatic setting of timetable start time
 | 
				
			||||||
STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER                          :[scheduled dispatch]
 | 
					STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER                          :{PUSH_COLOUR}{YELLOW}[{STRING1}scheduled dispatch]{POP_COLOUR}
 | 
				
			||||||
STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME             :{PUSH_COLOUR}{RED}[scheduled dispatch - no wait time timetabled]{POP_COLOUR}
 | 
					STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME             :{PUSH_COLOUR}{RED}[{STRING1}scheduled dispatch - no wait time timetabled]{POP_COLOUR}
 | 
				
			||||||
 | 
					STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_SCHEDULE_INDEX           :{NUM}{NBSP}-{NBSP}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
STR_TIMETABLE_EXPECTED                                          :{BLACK}Expected
 | 
					STR_TIMETABLE_EXPECTED                                          :{BLACK}Expected
 | 
				
			||||||
STR_TIMETABLE_SCHEDULED                                         :{BLACK}Scheduled
 | 
					STR_TIMETABLE_SCHEDULED                                         :{BLACK}Scheduled
 | 
				
			||||||
@@ -5721,6 +5722,11 @@ STR_TIMETABLE_LEAVE_EARLY_FULL_ALL                              :Leave early if
 | 
				
			|||||||
STR_TIMETABLE_EXTRA_DROP_DOWN                                   :{BLACK}Extra
 | 
					STR_TIMETABLE_EXTRA_DROP_DOWN                                   :{BLACK}Extra
 | 
				
			||||||
STR_TIMETABLE_EXTRA_DROP_DOWN_TOOLTIP                           :{BLACK}Extra Options{}{}Leave as timetabled: Vehicles wait until their timetabled departure time before leaving (default).{}Leave Early: Vehicles leave as soon as loading/unloading is done, possibly before the timetabled departure time.{}Leave Early if Any Full: As above, if any cargo is fully loaded.{}Leave Early if All Full: As above, if all cargoes are fully loaded.
 | 
					STR_TIMETABLE_EXTRA_DROP_DOWN_TOOLTIP                           :{BLACK}Extra Options{}{}Leave as timetabled: Vehicles wait until their timetabled departure time before leaving (default).{}Leave Early: Vehicles leave as soon as loading/unloading is done, possibly before the timetabled departure time.{}Leave Early if Any Full: As above, if any cargo is fully loaded.{}Leave Early if All Full: As above, if all cargoes are fully loaded.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					STR_TIMETABLE_ASSIGN_SCHEDULE_DROP_DOWN                         :{BLACK}Assign Schedule
 | 
				
			||||||
 | 
					STR_TIMETABLE_ASSIGN_SCHEDULE_DROP_DOWN_TOOLTIP                 :{BLACK}Assign or unassign a scheduled dispatch schedule to the selected order
 | 
				
			||||||
 | 
					STR_TIMETABLE_ASSIGN_SCHEDULE_NONE                              :No Schedule
 | 
				
			||||||
 | 
					STR_TIMETABLE_ASSIGN_SCHEDULE_ID                                :Schedule {NUM}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
STR_TIMETABLE_ARRIVAL_ABBREVIATION                              :A:
 | 
					STR_TIMETABLE_ARRIVAL_ABBREVIATION                              :A:
 | 
				
			||||||
STR_TIMETABLE_DEPARTURE_ABBREVIATION                            :D:
 | 
					STR_TIMETABLE_DEPARTURE_ABBREVIATION                            :D:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -5730,10 +5736,13 @@ STR_TIMETABLE_AUTOSEP_SINGLE_VEH                                :{BLACK}This tim
 | 
				
			|||||||
STR_TIMETABLE_WARNING_AUTOSEP_CONDITIONAL                       :{BLACK}Cannot auto separate timetable: conditional order(s) present
 | 
					STR_TIMETABLE_WARNING_AUTOSEP_CONDITIONAL                       :{BLACK}Cannot auto separate timetable: conditional order(s) present
 | 
				
			||||||
STR_TIMETABLE_WARNING_AUTOSEP_MISSING_TIMINGS                   :{BLACK}Cannot auto separate timetable: wait or travel time(s) missing
 | 
					STR_TIMETABLE_WARNING_AUTOSEP_MISSING_TIMINGS                   :{BLACK}Cannot auto separate timetable: wait or travel time(s) missing
 | 
				
			||||||
STR_TIMETABLE_WARNING_FULL_LOAD                                 :{BLACK}Timetabling full-load orders is not recommended
 | 
					STR_TIMETABLE_WARNING_FULL_LOAD                                 :{BLACK}Timetabling full-load orders is not recommended
 | 
				
			||||||
STR_TIMETABLE_WARNING_AUTOFILL_CONDITIONAL                      :{BLACK}Autofill will only update taken branch of conditional orders.
 | 
					STR_TIMETABLE_WARNING_AUTOFILL_CONDITIONAL                      :{BLACK}Autofill will only update taken branch of conditional orders
 | 
				
			||||||
STR_TIMETABLE_NON_TIMETABLED_BRANCH                             :{BLACK}Not all conditional order branch-taken travel times are timetabled.
 | 
					STR_TIMETABLE_NON_TIMETABLED_BRANCH                             :{BLACK}Not all conditional order branch-taken travel times are timetabled
 | 
				
			||||||
STR_TIMETABLE_WARNING_NO_SCHEDULED_DISPATCH_ORDER               :{BLACK}No order is suitable for use with scheduled dispatch.
 | 
					STR_TIMETABLE_WARNING_NO_SCHEDULED_DISPATCH_ORDER_ASSIGNED      :{BLACK}The dispatch schedule is not assigned to an order
 | 
				
			||||||
STR_TIMETABLE_WARNING_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME     :{BLACK}The scheduled dispatch order should have a timetabled wait time.
 | 
					STR_TIMETABLE_WARNING_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME     :{BLACK}The scheduled dispatch order should have a timetabled wait time
 | 
				
			||||||
 | 
					STR_TIMETABLE_WARNING_SCHEDULE_ID                               :{BLACK}Schedule {NUM}: {STRING}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					STR_TIMETABLE_WARNINGS_OMITTED                                  :{BLACK}{NUM} further warnings omitted...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Date window (for timetable)
 | 
					# Date window (for timetable)
 | 
				
			||||||
STR_DATE_CAPTION                                                :{WHITE}Set date
 | 
					STR_DATE_CAPTION                                                :{WHITE}Set date
 | 
				
			||||||
@@ -7115,7 +7124,17 @@ STR_SCHDISPATCH_RESET_LAST_DISPATCH_TOOLTIP                     :{BLACK}Reset wh
 | 
				
			|||||||
STR_SCHDISPATCH_CLEAR                                           :{BLACK}Clear Departure Slots
 | 
					STR_SCHDISPATCH_CLEAR                                           :{BLACK}Clear Departure Slots
 | 
				
			||||||
STR_SCHDISPATCH_CLEAR_TOOLTIP                                   :{BLACK}Clear all departure slots from the schedule.
 | 
					STR_SCHDISPATCH_CLEAR_TOOLTIP                                   :{BLACK}Clear all departure slots from the schedule.
 | 
				
			||||||
STR_SCHDISPATCH_MANAGE                                          :{BLACK}Manage Schedule
 | 
					STR_SCHDISPATCH_MANAGE                                          :{BLACK}Manage Schedule
 | 
				
			||||||
STR_SCHDISPATCH_MANAGE_TOOLTIP                                  :{STRING}{}{}{STRING}
 | 
					STR_SCHDISPATCH_MANAGE_TOOLTIP                                  :{STRING}{}{}{STRING}{}{}{STRING}
 | 
				
			||||||
 | 
					STR_SCHDISPATCH_PREV_SCHEDULE                                   :{BLACK}Previous
 | 
				
			||||||
 | 
					STR_SCHDISPATCH_PREV_SCHEDULE_TOOLTIP                           :{BLACK}Go to previous dispatch schedule
 | 
				
			||||||
 | 
					STR_SCHDISPATCH_NEXT_SCHEDULE                                   :{BLACK}Next
 | 
				
			||||||
 | 
					STR_SCHDISPATCH_NEXT_SCHEDULE_TOOLTIP                           :{BLACK}Go to next dispatch schedule
 | 
				
			||||||
 | 
					STR_SCHDISPATCH_ADD_SCHEDULE                                    :{BLACK}Add
 | 
				
			||||||
 | 
					STR_SCHDISPATCH_ADD_SCHEDULE_TOOLTIP                            :{BLACK}Add a new dispatch schedule
 | 
				
			||||||
 | 
					STR_SCHDISPATCH_REMOVE_SCHEDULE                                 :{BLACK}Remove Current Schedule
 | 
				
			||||||
 | 
					STR_SCHDISPATCH_REMOVE_SCHEDULE_TOOLTIP                         :{BLACK}Remove this dispatch schedule entirely.
 | 
				
			||||||
 | 
					STR_SCHDISPATCH_NO_SCHEDULES                                    :{BLACK}No Schedules
 | 
				
			||||||
 | 
					STR_SCHDISPATCH_SCHEDULE_ID                                     :{BLACK}Schedule {NUM} of {NUM}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_PAST                     :{BLACK}Last departure at {DATE_WALLCLOCK_TINY}.
 | 
					STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_PAST                     :{BLACK}Last departure at {DATE_WALLCLOCK_TINY}.
 | 
				
			||||||
STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_FUTURE                   :{BLACK}Last departure has not left yet, it will depart at {DATE_WALLCLOCK_TINY}.
 | 
					STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_FUTURE                   :{BLACK}Last departure has not left yet, it will depart at {DATE_WALLCLOCK_TINY}.
 | 
				
			||||||
@@ -7127,10 +7146,19 @@ STR_SCHDISPATCH_SUMMARY_NOT_ENABLED                             :{BLACK}This sch
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
STR_SCHDISPATCH_SLOT_OUTSIDE_SCHEDULE                           :{BLACK}One or more departure slots are outside the schedule duration.
 | 
					STR_SCHDISPATCH_SLOT_OUTSIDE_SCHEDULE                           :{BLACK}One or more departure slots are outside the schedule duration.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					STR_SCHDISPATCH_NOT_ASSIGNED_TO_ORDER                           :{BLACK}This dispatch schedule is not assigned to an order.
 | 
				
			||||||
 | 
					STR_SCHDISPATCH_ASSIGNED_TO_ORDER                               :{BLACK}This dispatch schedule is assigned to order {NUM}: {STRING1}.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
STR_SCHDISPATCH_ADD_DEPARTURE_SLOTS_START                       :{BLACK}Start:
 | 
					STR_SCHDISPATCH_ADD_DEPARTURE_SLOTS_START                       :{BLACK}Start:
 | 
				
			||||||
STR_SCHDISPATCH_ADD_DEPARTURE_SLOTS_STEP                        :{BLACK}Period:
 | 
					STR_SCHDISPATCH_ADD_DEPARTURE_SLOTS_STEP                        :{BLACK}Period:
 | 
				
			||||||
STR_SCHDISPATCH_ADD_DEPARTURE_SLOTS_END                         :{BLACK}End:
 | 
					STR_SCHDISPATCH_ADD_DEPARTURE_SLOTS_END                         :{BLACK}End:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					STR_SCHDISPATCH_QUERY_CLEAR_SCHEDULE_CAPTION                    :{WHITE}Clear Departure Slots
 | 
				
			||||||
 | 
					STR_SCHDISPATCH_QUERY_CLEAR_SCHEDULE_TEXT                       :{WHITE}Are you sure you want to clear all {NUM} departure slot{P "" s} from this schedule?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					STR_SCHDISPATCH_QUERY_REMOVE_SCHEDULE_CAPTION                   :{WHITE}Delete Schedule
 | 
				
			||||||
 | 
					STR_SCHDISPATCH_QUERY_REMOVE_SCHEDULE_TEXT                      :{WHITE}Are you sure you want to delete this schedule (containing  {NUM} departure slot{P "" s})?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
STR_ERROR_SCHDISPATCH_TRIED_TO_ADD_TOO_MANY_SLOTS               :{WHITE}Tried to add too many departure slots at once
 | 
					STR_ERROR_SCHDISPATCH_TRIED_TO_ADD_TOO_MANY_SLOTS               :{WHITE}Tried to add too many departure slots at once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Modifier key toggle window
 | 
					# Modifier key toggle window
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6681,8 +6681,8 @@ STR_TIMETABLE_AUTO_SEPARATION                                   :{BLACK}Auto Sep
 | 
				
			|||||||
STR_TIMETABLE_AUTO_SEPARATION_TOOLTIP                           :{BLACK}Automáticamente axusta a hora de comezo dos horarios para asegurar a separación dos vehículos
 | 
					STR_TIMETABLE_AUTO_SEPARATION_TOOLTIP                           :{BLACK}Automáticamente axusta a hora de comezo dos horarios para asegurar a separación dos vehículos
 | 
				
			||||||
STR_TIMETABLE_SCHEDULED_DISPATCH                                :{BLACK}Saída programada
 | 
					STR_TIMETABLE_SCHEDULED_DISPATCH                                :{BLACK}Saída programada
 | 
				
			||||||
STR_TIMETABLE_SCHEDULED_DISPATCH_TOOLTIP                        :{BLACK}Abrir fiestras de saídas programadas para configurar automáticamente a hora de inicio dos horarios
 | 
					STR_TIMETABLE_SCHEDULED_DISPATCH_TOOLTIP                        :{BLACK}Abrir fiestras de saídas programadas para configurar automáticamente a hora de inicio dos horarios
 | 
				
			||||||
STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER                          :[saída programada]
 | 
					STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER                          :{PUSH_COLOUR}{YELLOW}[{STRING}saída programada]{POP_COLOUR}
 | 
				
			||||||
STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME             :{PUSH_COLOUR}{RED}[saída programadda - sen tempo de espera no horario]{POP_COLOUR}
 | 
					STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME             :{PUSH_COLOUR}{RED}[{STRING}saída programadda - sen tempo de espera no horario]{POP_COLOUR}
 | 
				
			||||||
STR_TIMETABLE_LOCK_ORDER_TIME_TOOLTIP                           :{BLACK}Bloquear/desbloquear a cantidade de tempo para a orde resaltada (Ctrl+Click fixa o estado de bloqueo para todas as ordes).{}Cando estea bloqueado, a hora non se modificará mediante o autocompletado ou automatización do horario.
 | 
					STR_TIMETABLE_LOCK_ORDER_TIME_TOOLTIP                           :{BLACK}Bloquear/desbloquear a cantidade de tempo para a orde resaltada (Ctrl+Click fixa o estado de bloqueo para todas as ordes).{}Cando estea bloqueado, a hora non se modificará mediante o autocompletado ou automatización do horario.
 | 
				
			||||||
STR_TIMETABLE_LEAVE_EARLY_ORDER                                 :[saír cedo]
 | 
					STR_TIMETABLE_LEAVE_EARLY_ORDER                                 :[saír cedo]
 | 
				
			||||||
STR_TIMETABLE_LEAVE_EARLY_ORDER_FULL_ANY                        :[saír cedo se calquera cargamento está cheo]
 | 
					STR_TIMETABLE_LEAVE_EARLY_ORDER_FULL_ANY                        :[saír cedo se calquera cargamento está cheo]
 | 
				
			||||||
@@ -6701,7 +6701,6 @@ STR_TIMETABLE_WARNING_AUTOSEP_MISSING_TIMINGS                   :{BLACK}Non se p
 | 
				
			|||||||
STR_TIMETABLE_WARNING_FULL_LOAD                                 :{BLACK}Non se recomenda a programación de ordes a carga completa
 | 
					STR_TIMETABLE_WARNING_FULL_LOAD                                 :{BLACK}Non se recomenda a programación de ordes a carga completa
 | 
				
			||||||
STR_TIMETABLE_WARNING_AUTOFILL_CONDITIONAL                      :{BLACK}O enchemento automático só actualizará as ordes condicionais da rama escollida
 | 
					STR_TIMETABLE_WARNING_AUTOFILL_CONDITIONAL                      :{BLACK}O enchemento automático só actualizará as ordes condicionais da rama escollida
 | 
				
			||||||
STR_TIMETABLE_NON_TIMETABLED_BRANCH                             :{BLACK}Non todas as ordes da rama condicional escollida están programadas.
 | 
					STR_TIMETABLE_NON_TIMETABLED_BRANCH                             :{BLACK}Non todas as ordes da rama condicional escollida están programadas.
 | 
				
			||||||
STR_TIMETABLE_WARNING_NO_SCHEDULED_DISPATCH_ORDER               :{BLACK}Non hai unha orde axeitada para usar coa saída programada.
 | 
					 | 
				
			||||||
STR_TIMETABLE_WARNING_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME     :{BLACK}A orde de saída programada debería ter un temepo de espera programado.
 | 
					STR_TIMETABLE_WARNING_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME     :{BLACK}A orde de saída programada debería ter un temepo de espera programado.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
STR_DATE_MINUTES_MINUTE_TOOLTIP                                 :{BLACK}Seleccionar minuto
 | 
					STR_DATE_MINUTES_MINUTE_TOOLTIP                                 :{BLACK}Seleccionar minuto
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5479,8 +5479,8 @@ STR_TIMETABLE_AUTO_SEPARATION_TOOLTIP                           :{BLACK}Passt au
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
STR_TIMETABLE_SCHEDULED_DISPATCH                                :{BLACK}Geplante Beförderung
 | 
					STR_TIMETABLE_SCHEDULED_DISPATCH                                :{BLACK}Geplante Beförderung
 | 
				
			||||||
STR_TIMETABLE_SCHEDULED_DISPATCH_TOOLTIP                        :{BLACK}Öffnet das Fenster für die geplante Beförderung, um den Beginn des Fahrplans automatisch zu setzen
 | 
					STR_TIMETABLE_SCHEDULED_DISPATCH_TOOLTIP                        :{BLACK}Öffnet das Fenster für die geplante Beförderung, um den Beginn des Fahrplans automatisch zu setzen
 | 
				
			||||||
STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER                          :[Geplante Beförderung]
 | 
					STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER                          :{PUSH_COLOUR}{YELLOW}[{STRING}Geplante Beförderung]{POP_COLOUR}
 | 
				
			||||||
STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME             :{PUSH_COLOUR}{RED}[Geplante Beförderung - keine Wartezeit geplant]{POP_COLOUR}
 | 
					STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME             :{PUSH_COLOUR}{RED}[{STRING}Geplante Beförderung - keine Wartezeit geplant]{POP_COLOUR}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
STR_TIMETABLE_EXPECTED                                          :{BLACK}Voraussichtlich
 | 
					STR_TIMETABLE_EXPECTED                                          :{BLACK}Voraussichtlich
 | 
				
			||||||
STR_TIMETABLE_SCHEDULED                                         :{BLACK}Fahrplanmäßig
 | 
					STR_TIMETABLE_SCHEDULED                                         :{BLACK}Fahrplanmäßig
 | 
				
			||||||
@@ -5510,7 +5510,6 @@ STR_TIMETABLE_WARNING_AUTOSEP_MISSING_TIMINGS                   :{BLACK}Kann Fah
 | 
				
			|||||||
STR_TIMETABLE_WARNING_FULL_LOAD                                 :{BLACK}Volllade-Aufträge zu planen ist nicht empfohlen
 | 
					STR_TIMETABLE_WARNING_FULL_LOAD                                 :{BLACK}Volllade-Aufträge zu planen ist nicht empfohlen
 | 
				
			||||||
STR_TIMETABLE_WARNING_AUTOFILL_CONDITIONAL                      :{BLACK}Es werden nur bedingte Sprünge erfasst, die genommen wurden
 | 
					STR_TIMETABLE_WARNING_AUTOFILL_CONDITIONAL                      :{BLACK}Es werden nur bedingte Sprünge erfasst, die genommen wurden
 | 
				
			||||||
STR_TIMETABLE_NON_TIMETABLED_BRANCH                             :{BLACK}Nicht alle Reisezeiten der bedingten Sprünge sind geplant
 | 
					STR_TIMETABLE_NON_TIMETABLED_BRANCH                             :{BLACK}Nicht alle Reisezeiten der bedingten Sprünge sind geplant
 | 
				
			||||||
STR_TIMETABLE_WARNING_NO_SCHEDULED_DISPATCH_ORDER               :{BLACK}Kein Auftrag ist geeignet für die Nutzung der geplanten Beförderung
 | 
					 | 
				
			||||||
STR_TIMETABLE_WARNING_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME     :{BLACK}Der geplant beförderte Auftrag sollte eine geplante Wartezeit haben
 | 
					STR_TIMETABLE_WARNING_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME     :{BLACK}Der geplant beförderte Auftrag sollte eine geplante Wartezeit haben
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Date window (for timetable)
 | 
					# Date window (for timetable)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5697,8 +5697,8 @@ STR_TIMETABLE_AUTO_SEPARATION_TOOLTIP                           :{BLACK}시간
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
STR_TIMETABLE_SCHEDULED_DISPATCH                                :{BLACK}배차 일정
 | 
					STR_TIMETABLE_SCHEDULED_DISPATCH                                :{BLACK}배차 일정
 | 
				
			||||||
STR_TIMETABLE_SCHEDULED_DISPATCH_TOOLTIP                        :{BLACK}시간표 시작 시간 자동 설정을 위한 '배차 일정' 창을 엽니다.
 | 
					STR_TIMETABLE_SCHEDULED_DISPATCH_TOOLTIP                        :{BLACK}시간표 시작 시간 자동 설정을 위한 '배차 일정' 창을 엽니다.
 | 
				
			||||||
STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER                          :[배차 기준]
 | 
					STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER                          :{PUSH_COLOUR}{YELLOW}[배{STRING}차 기준]{POP_COLOUR}
 | 
				
			||||||
STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME             :{PUSH_COLOUR}{RED}[배차 기준 - 대기 시간표가 작성되지 않음]{POP_COLOUR}
 | 
					STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME             :{PUSH_COLOUR}{RED}[{STRING}배차 기준 - 대기 시간표가 작성되지 않음]{POP_COLOUR}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
STR_TIMETABLE_EXPECTED                                          :{BLACK}예정일 기준
 | 
					STR_TIMETABLE_EXPECTED                                          :{BLACK}예정일 기준
 | 
				
			||||||
STR_TIMETABLE_SCHEDULED                                         :{BLACK}예정 소요시간 기준
 | 
					STR_TIMETABLE_SCHEDULED                                         :{BLACK}예정 소요시간 기준
 | 
				
			||||||
@@ -5728,7 +5728,6 @@ STR_TIMETABLE_WARNING_AUTOSEP_MISSING_TIMINGS                   :{BLACK}자동 
 | 
				
			|||||||
STR_TIMETABLE_WARNING_FULL_LOAD                                 :{BLACK}'가득 싣기'가 설정되어 있는 경로에 시간표를 지정하는 것은 권장하지 않습니다.
 | 
					STR_TIMETABLE_WARNING_FULL_LOAD                                 :{BLACK}'가득 싣기'가 설정되어 있는 경로에 시간표를 지정하는 것은 권장하지 않습니다.
 | 
				
			||||||
STR_TIMETABLE_WARNING_AUTOFILL_CONDITIONAL                      :{BLACK}자동 시간 설정은 조건부 경로 중 한 경로만 채울 것입니다.
 | 
					STR_TIMETABLE_WARNING_AUTOFILL_CONDITIONAL                      :{BLACK}자동 시간 설정은 조건부 경로 중 한 경로만 채울 것입니다.
 | 
				
			||||||
STR_TIMETABLE_NON_TIMETABLED_BRANCH                             :{BLACK}하나 이상의 조건부 경로 운행 시간이 채워지지 않았습니다.
 | 
					STR_TIMETABLE_NON_TIMETABLED_BRANCH                             :{BLACK}하나 이상의 조건부 경로 운행 시간이 채워지지 않았습니다.
 | 
				
			||||||
STR_TIMETABLE_WARNING_NO_SCHEDULED_DISPATCH_ORDER               :{BLACK}배차 일정을 사용하기에 적합한 경로가 존재하지 않습니다.
 | 
					 | 
				
			||||||
STR_TIMETABLE_WARNING_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME     :{BLACK}배차 기준은 대기 시간이 작성되어 있어야 합니다.
 | 
					STR_TIMETABLE_WARNING_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME     :{BLACK}배차 기준은 대기 시간이 작성되어 있어야 합니다.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Date window (for timetable)
 | 
					# Date window (for timetable)
 | 
				
			||||||
@@ -7111,7 +7110,7 @@ STR_SCHDISPATCH_RESET_LAST_DISPATCH_TOOLTIP                     :{BLACK}최근 
 | 
				
			|||||||
STR_SCHDISPATCH_CLEAR                                           :{BLACK}출발 슬롯 초기화
 | 
					STR_SCHDISPATCH_CLEAR                                           :{BLACK}출발 슬롯 초기화
 | 
				
			||||||
STR_SCHDISPATCH_CLEAR_TOOLTIP                                   :{BLACK}일정에서 모든 출발 슬롯을 제거합니다.
 | 
					STR_SCHDISPATCH_CLEAR_TOOLTIP                                   :{BLACK}일정에서 모든 출발 슬롯을 제거합니다.
 | 
				
			||||||
STR_SCHDISPATCH_MANAGE                                          :{BLACK}일정 관리
 | 
					STR_SCHDISPATCH_MANAGE                                          :{BLACK}일정 관리
 | 
				
			||||||
STR_SCHDISPATCH_MANAGE_TOOLTIP                                  :{STRING}{}{}{STRING}
 | 
					STR_SCHDISPATCH_MANAGE_TOOLTIP                                  :{STRING}{}{}{STRING}{}{}{STRING}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_PAST                     :{BLACK}최근 출발: {DATE_WALLCLOCK_TINY}
 | 
					STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_PAST                     :{BLACK}최근 출발: {DATE_WALLCLOCK_TINY}
 | 
				
			||||||
STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_FUTURE                   :{BLACK}최근 배차된 열차가 아직 출발하지 않았습니다. {DATE_WALLCLOCK_TINY}에 출발할 예정입니다.
 | 
					STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_FUTURE                   :{BLACK}최근 배차된 열차가 아직 출발하지 않았습니다. {DATE_WALLCLOCK_TINY}에 출발할 예정입니다.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -69,11 +69,7 @@ OrderBackup::OrderBackup(const Vehicle *v, uint32 user)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		if (v->orders.list != nullptr && HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)) {
 | 
							if (v->orders.list != nullptr && HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)) {
 | 
				
			||||||
			SetBit(this->vehicle_flags, VF_SCHEDULED_DISPATCH);
 | 
								SetBit(this->vehicle_flags, VF_SCHEDULED_DISPATCH);
 | 
				
			||||||
			this->scheduled_dispatch = v->orders.list->GetScheduledDispatch();
 | 
								this->dispatch_schedules = v->orders.list->GetScheduledDispatchScheduleSet();
 | 
				
			||||||
			this->scheduled_dispatch_duration = v->orders.list->GetScheduledDispatchDuration();
 | 
					 | 
				
			||||||
			this->scheduled_dispatch_start_date = v->orders.list->GetScheduledDispatchStartDatePart();
 | 
					 | 
				
			||||||
			this->scheduled_dispatch_start_full_date_fract = v->orders.list->GetScheduledDispatchStartDateFractPart();
 | 
					 | 
				
			||||||
			this->scheduled_dispatch_max_delay = v->orders.list->GetScheduledDispatchDelay();
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -95,12 +91,7 @@ void OrderBackup::DoRestore(Vehicle *v)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		if (HasBit(this->vehicle_flags, VF_SCHEDULED_DISPATCH)) {
 | 
							if (HasBit(this->vehicle_flags, VF_SCHEDULED_DISPATCH)) {
 | 
				
			||||||
			SetBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH);
 | 
								SetBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH);
 | 
				
			||||||
			v->orders.list->SetScheduledDispatchDuration(this->scheduled_dispatch_duration);
 | 
								v->orders.list->GetScheduledDispatchScheduleSet() = std::move(this->dispatch_schedules);
 | 
				
			||||||
			v->orders.list->SetScheduledDispatchDelay(this->scheduled_dispatch_max_delay);
 | 
					 | 
				
			||||||
			v->orders.list->SetScheduledDispatchStartDate(this->scheduled_dispatch_start_date,
 | 
					 | 
				
			||||||
					this->scheduled_dispatch_start_full_date_fract);
 | 
					 | 
				
			||||||
			v->orders.list->SetScheduledDispatchLastDispatch(0);
 | 
					 | 
				
			||||||
			v->orders.list->SetScheduledDispatch(std::move(this->scheduled_dispatch));
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* Make sure buoys/oil rigs are updated in the station list. */
 | 
							/* Make sure buoys/oil rigs are updated in the station list. */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,6 +15,7 @@
 | 
				
			|||||||
#include "tile_type.h"
 | 
					#include "tile_type.h"
 | 
				
			||||||
#include "vehicle_type.h"
 | 
					#include "vehicle_type.h"
 | 
				
			||||||
#include "base_consist.h"
 | 
					#include "base_consist.h"
 | 
				
			||||||
 | 
					#include "order_base.h"
 | 
				
			||||||
#include "saveload/saveload_common.h"
 | 
					#include "saveload/saveload_common.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Unique identifier for an order backup. */
 | 
					/** Unique identifier for an order backup. */
 | 
				
			||||||
@@ -43,6 +44,7 @@ private:
 | 
				
			|||||||
	friend SaveLoadTable GetOrderBackupDescription(); ///< Saving and loading of order backups.
 | 
						friend SaveLoadTable GetOrderBackupDescription(); ///< Saving and loading of order backups.
 | 
				
			||||||
	friend upstream_sl::SaveLoadTable upstream_sl::GetOrderBackupDescription(); ///< Saving and loading of order backups.
 | 
						friend upstream_sl::SaveLoadTable upstream_sl::GetOrderBackupDescription(); ///< Saving and loading of order backups.
 | 
				
			||||||
	friend void Load_BKOR();   ///< Creating empty orders upon savegame loading.
 | 
						friend void Load_BKOR();   ///< Creating empty orders upon savegame loading.
 | 
				
			||||||
 | 
						friend void Save_BKOR();   ///< Saving orders upon savegame saving.
 | 
				
			||||||
	friend upstream_sl::BKORChunkHandler;
 | 
						friend upstream_sl::BKORChunkHandler;
 | 
				
			||||||
	uint32 user;               ///< The user that requested the backup.
 | 
						uint32 user;               ///< The user that requested the backup.
 | 
				
			||||||
	TileIndex tile;            ///< Tile of the depot where the order was changed.
 | 
						TileIndex tile;            ///< Tile of the depot where the order was changed.
 | 
				
			||||||
@@ -51,12 +53,7 @@ private:
 | 
				
			|||||||
	const Vehicle *clone;      ///< Vehicle this vehicle was a clone of.
 | 
						const Vehicle *clone;      ///< Vehicle this vehicle was a clone of.
 | 
				
			||||||
	Order *orders;             ///< The actual orders if the vehicle was not a clone.
 | 
						Order *orders;             ///< The actual orders if the vehicle was not a clone.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	std::vector<uint32> scheduled_dispatch;    ///< Scheduled dispatch time
 | 
						std::vector<DispatchSchedule> dispatch_schedules; ///< Scheduled dispatch schedules
 | 
				
			||||||
	uint32 scheduled_dispatch_duration;        ///< Scheduled dispatch duration
 | 
					 | 
				
			||||||
	Date scheduled_dispatch_start_date;        ///< Scheduled dispatch start date
 | 
					 | 
				
			||||||
	uint16 scheduled_dispatch_start_full_date_fract;///< Scheduled dispatch start full date fraction;
 | 
					 | 
				
			||||||
	                                           /// this count to (DAY_TICK * _settings_game.economy.day_length_factor)
 | 
					 | 
				
			||||||
	int32 scheduled_dispatch_max_delay;        ///< Maximum allowed delay
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	static uint update_counter;
 | 
						static uint update_counter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										238
									
								
								src/order_base.h
									
									
									
									
									
								
							
							
						
						
									
										238
									
								
								src/order_base.h
									
									
									
									
									
								
							@@ -53,6 +53,7 @@ void ClearOrderDestinationRefcountMap();
 | 
				
			|||||||
struct OrderExtraInfo {
 | 
					struct OrderExtraInfo {
 | 
				
			||||||
	uint8 cargo_type_flags[NUM_CARGO] = {}; ///< Load/unload types for each cargo type.
 | 
						uint8 cargo_type_flags[NUM_CARGO] = {}; ///< Load/unload types for each cargo type.
 | 
				
			||||||
	uint32 xdata = 0;                       ///< Extra arbitrary data
 | 
						uint32 xdata = 0;                       ///< Extra arbitrary data
 | 
				
			||||||
 | 
						uint16 dispatch_index = 0;              ///< Scheduled dispatch index + 1
 | 
				
			||||||
	uint8 xflags = 0;                       ///< Extra flags
 | 
						uint8 xflags = 0;                       ///< Extra flags
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -476,7 +477,7 @@ public:
 | 
				
			|||||||
	/** Set if the wait time is fixed */
 | 
						/** Set if the wait time is fixed */
 | 
				
			||||||
	inline void SetWaitFixed(bool fixed)
 | 
						inline void SetWaitFixed(bool fixed)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (!this->IsType(OT_CONDITIONAL) && fixed != this->IsWaitFixed()) SB(this->GetXFlagsRef(), 1, 1, fixed ? 1 : 0);
 | 
							if (fixed != this->IsWaitFixed()) SB(this->GetXFlagsRef(), 1, 1, fixed ? 1 : 0);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Does this order have a fixed travel time? */
 | 
						/** Does this order have a fixed travel time? */
 | 
				
			||||||
@@ -526,6 +527,24 @@ public:
 | 
				
			|||||||
		return true;
 | 
							return true;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						inline int GetDispatchScheduleIndex() const
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return this->extra != nullptr ? (int)this->extra->dispatch_index - 1 : -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						inline void SetDispatchScheduleIndex(int schedule_index)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (schedule_index != this->GetDispatchScheduleIndex()) {
 | 
				
			||||||
 | 
								this->CheckExtraInfoAlloced();
 | 
				
			||||||
 | 
								this->extra->dispatch_index = (uint16)(schedule_index + 1);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						inline bool IsScheduledDispatchOrder(bool require_wait_timetabled) const
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return this->extra != nullptr && this->extra->dispatch_index > 0 && (!require_wait_timetabled || this->IsWaitTimetabled());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void AssignOrder(const Order &other);
 | 
						void AssignOrder(const Order &other);
 | 
				
			||||||
	bool Equals(const Order &other) const;
 | 
						bool Equals(const Order &other) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -587,6 +606,124 @@ template <typename T, typename F> T CargoMaskValueFilter(CargoTypes &cargo_mask,
 | 
				
			|||||||
	return value;
 | 
						return value;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct DispatchSchedule {
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						friend SaveLoadTable GetDispatchScheduleDescription();              ///< Saving and loading of dispatch schedules
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::vector<uint32> scheduled_dispatch;                             ///< Scheduled dispatch time
 | 
				
			||||||
 | 
						uint32 scheduled_dispatch_duration = 0;                             ///< Scheduled dispatch duration
 | 
				
			||||||
 | 
						Date scheduled_dispatch_start_date = -1;                            ///< Scheduled dispatch start date
 | 
				
			||||||
 | 
						uint16 scheduled_dispatch_start_full_date_fract = 0;                ///< Scheduled dispatch start full date fraction;
 | 
				
			||||||
 | 
						                                                                    ///  this counts to (DAY_TICK * _settings_game.economy.day_length_factor)
 | 
				
			||||||
 | 
						int32 scheduled_dispatch_last_dispatch = 0;                         ///< Last vehicle dispatched offset
 | 
				
			||||||
 | 
						int32 scheduled_dispatch_max_delay = 0;                             ///< Maximum allowed delay
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						inline void CopyBasicFields(const DispatchSchedule &other)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							this->scheduled_dispatch_duration              = other.scheduled_dispatch_duration;
 | 
				
			||||||
 | 
							this->scheduled_dispatch_start_date            = other.scheduled_dispatch_start_date;
 | 
				
			||||||
 | 
							this->scheduled_dispatch_start_full_date_fract = other.scheduled_dispatch_start_full_date_fract;
 | 
				
			||||||
 | 
							this->scheduled_dispatch_last_dispatch         = other.scheduled_dispatch_last_dispatch;
 | 
				
			||||||
 | 
							this->scheduled_dispatch_max_delay             = other.scheduled_dispatch_max_delay;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Get the vector of all scheduled dispatch slot
 | 
				
			||||||
 | 
						 * @return  first scheduled dispatch
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						inline const std::vector<uint32> &GetScheduledDispatch() const { return this->scheduled_dispatch; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void SetScheduledDispatch(std::vector<uint32> dispatch_list);
 | 
				
			||||||
 | 
						void AddScheduledDispatch(uint32 offset);
 | 
				
			||||||
 | 
						void RemoveScheduledDispatch(uint32 offset);
 | 
				
			||||||
 | 
						void ClearScheduledDispatch() { this->scheduled_dispatch.clear(); }
 | 
				
			||||||
 | 
						bool UpdateScheduledDispatchToDate(DateTicksScaled now);
 | 
				
			||||||
 | 
						void UpdateScheduledDispatch(const Vehicle *v);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Set the scheduled dispatch duration, in scaled tick
 | 
				
			||||||
 | 
						 * @param  duration  New duration
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						inline void SetScheduledDispatchDuration(uint32 duration) { this->scheduled_dispatch_duration = duration; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Get the scheduled dispatch duration, in scaled tick
 | 
				
			||||||
 | 
						 * @return  scheduled dispatch duration
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						inline uint32 GetScheduledDispatchDuration() const { return this->scheduled_dispatch_duration; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Set the scheduled dispatch start
 | 
				
			||||||
 | 
						 * @param  start New start date
 | 
				
			||||||
 | 
						 * @param  fract New start full date fraction, see \c CmdScheduledDispatchSetStartDate
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						inline void SetScheduledDispatchStartDate(Date start_date, uint16 start_full_date_fract)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							this->scheduled_dispatch_start_date = start_date;
 | 
				
			||||||
 | 
							this->scheduled_dispatch_start_full_date_fract = start_full_date_fract;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Get the scheduled dispatch start date part
 | 
				
			||||||
 | 
						 * @return  scheduled dispatch start date part
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						inline Date GetScheduledDispatchStartDatePart() const { return this->scheduled_dispatch_start_date; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Get the scheduled dispatch start date fract part
 | 
				
			||||||
 | 
						 * @return  scheduled dispatch start date fract part
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						inline uint16 GetScheduledDispatchStartDateFractPart() const { return this->scheduled_dispatch_start_full_date_fract; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Get the scheduled dispatch start date, in absolute scaled tick
 | 
				
			||||||
 | 
						 * @return  scheduled dispatch start date
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						inline DateTicksScaled GetScheduledDispatchStartTick() const { return SchdispatchConvertToScaledTick(this->scheduled_dispatch_start_date, this->scheduled_dispatch_start_full_date_fract); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Whether the scheduled dispatch setting is valid
 | 
				
			||||||
 | 
						 * @return  scheduled dispatch start date fraction
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						inline bool IsScheduledDispatchValid() const { return this->scheduled_dispatch_start_date >= 0 && this->scheduled_dispatch_duration > 0; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Set the scheduled dispatch last dispatch offset, in scaled tick
 | 
				
			||||||
 | 
						 * @param  duration  New last dispatch offset
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						inline void SetScheduledDispatchLastDispatch(int32 offset) { this->scheduled_dispatch_last_dispatch = offset; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Get the scheduled dispatch last dispatch offset, in scaled tick
 | 
				
			||||||
 | 
						 * @return  scheduled dispatch last dispatch
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						inline int32 GetScheduledDispatchLastDispatch() const { return this->scheduled_dispatch_last_dispatch; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Set the scheduled dispatch maximum allowed delay, in scaled tick
 | 
				
			||||||
 | 
						 * @param  delay  New maximum allow delay
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						inline void SetScheduledDispatchDelay(int32 delay) { this->scheduled_dispatch_max_delay = delay; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Get the scheduled dispatch maximum alowed delay, in scaled tick
 | 
				
			||||||
 | 
						 * @return  scheduled dispatch last dispatch
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						inline int32 GetScheduledDispatchDelay() const { return this->scheduled_dispatch_max_delay; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						inline void BorrowSchedule(DispatchSchedule &other)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							this->CopyBasicFields(other);
 | 
				
			||||||
 | 
							this->scheduled_dispatch = std::move(other.scheduled_dispatch);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						inline void ReturnSchedule(DispatchSchedule &other)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							other.scheduled_dispatch = std::move(this->scheduled_dispatch);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Shared order list linking together the linked list of orders and the list
 | 
					 * Shared order list linking together the linked list of orders and the list
 | 
				
			||||||
 *  of vehicles sharing this order list.
 | 
					 *  of vehicles sharing this order list.
 | 
				
			||||||
@@ -611,21 +748,13 @@ private:
 | 
				
			|||||||
	Ticks timetable_duration;         ///< NOSAVE: Total timetabled duration of the order list.
 | 
						Ticks timetable_duration;         ///< NOSAVE: Total timetabled duration of the order list.
 | 
				
			||||||
	Ticks total_duration;             ///< NOSAVE: Total (timetabled or not) duration of the order list.
 | 
						Ticks total_duration;             ///< NOSAVE: Total (timetabled or not) duration of the order list.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	std::vector<uint32> scheduled_dispatch;    ///< Scheduled dispatch time
 | 
						std::vector<DispatchSchedule> dispatch_schedules; ///< Scheduled dispatch schedules
 | 
				
			||||||
	uint32 scheduled_dispatch_duration;        ///< Scheduled dispatch duration
 | 
					 | 
				
			||||||
	Date scheduled_dispatch_start_date;        ///< Scheduled dispatch start date
 | 
					 | 
				
			||||||
	uint16 scheduled_dispatch_start_full_date_fract;///< Scheduled dispatch start full date fraction;
 | 
					 | 
				
			||||||
	                                           /// this count to (DAY_TICK * _settings_game.economy.day_length_factor)
 | 
					 | 
				
			||||||
	int32 scheduled_dispatch_last_dispatch;    ///< Last vehicle dispatched offset
 | 
					 | 
				
			||||||
	int32 scheduled_dispatch_max_delay;        ///< Maximum allowed delay
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	/** Default constructor producing an invalid order list. */
 | 
						/** Default constructor producing an invalid order list. */
 | 
				
			||||||
	OrderList(VehicleOrderID num_orders = INVALID_VEH_ORDER_ID)
 | 
						OrderList(VehicleOrderID num_orders = INVALID_VEH_ORDER_ID)
 | 
				
			||||||
		: first(nullptr), num_manual_orders(0), num_vehicles(0), first_shared(nullptr),
 | 
							: first(nullptr), num_manual_orders(0), num_vehicles(0), first_shared(nullptr),
 | 
				
			||||||
		  timetable_duration(0), total_duration(0), scheduled_dispatch_duration(0),
 | 
							  timetable_duration(0), total_duration(0) { }
 | 
				
			||||||
		  scheduled_dispatch_start_date(-1), scheduled_dispatch_start_full_date_fract(0),
 | 
					 | 
				
			||||||
		  scheduled_dispatch_last_dispatch(0), scheduled_dispatch_max_delay(0) { }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Create an order list with the given order chain for the given vehicle.
 | 
						 * Create an order list with the given order chain for the given vehicle.
 | 
				
			||||||
@@ -752,90 +881,13 @@ public:
 | 
				
			|||||||
	void DebugCheckSanity() const;
 | 
						void DebugCheckSanity() const;
 | 
				
			||||||
	bool CheckOrderListIndexing() const;
 | 
						bool CheckOrderListIndexing() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						inline std::vector<DispatchSchedule> &GetScheduledDispatchScheduleSet() { return this->dispatch_schedules; }
 | 
				
			||||||
	 * Get the vector of all scheduled dispatch slot
 | 
						inline const std::vector<DispatchSchedule> &GetScheduledDispatchScheduleSet() const { return this->dispatch_schedules; }
 | 
				
			||||||
	 * @return  first scheduled dispatch
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	inline const std::vector<uint32> &GetScheduledDispatch() const { return this->scheduled_dispatch; }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void SetScheduledDispatch(std::vector<uint32> dispatch_list);
 | 
						inline uint GetScheduledDispatchScheduleCount() const { return (uint)this->dispatch_schedules.size(); }
 | 
				
			||||||
	void AddScheduledDispatch(uint32 offset);
 | 
					 | 
				
			||||||
	void RemoveScheduledDispatch(uint32 offset);
 | 
					 | 
				
			||||||
	void ClearScheduledDispatch() { this->scheduled_dispatch.clear(); }
 | 
					 | 
				
			||||||
	void UpdateScheduledDispatch();
 | 
					 | 
				
			||||||
	void ResetScheduledDispatch();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Set the scheduled dispatch duration, in scaled tick
 | 
					 | 
				
			||||||
	 * @param  duration  New duration
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	inline void SetScheduledDispatchDuration(uint32 duration) { this->scheduled_dispatch_duration = duration; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Get the scheduled dispatch duration, in scaled tick
 | 
					 | 
				
			||||||
	 * @return  scheduled dispatch duration
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	inline uint32 GetScheduledDispatchDuration() const { return this->scheduled_dispatch_duration; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Set the scheduled dispatch start
 | 
					 | 
				
			||||||
	 * @param  start New start date
 | 
					 | 
				
			||||||
	 * @param  fract New start full date fraction, see \c CmdScheduledDispatchSetStartDate
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	inline void SetScheduledDispatchStartDate(Date start_date, uint16 start_full_date_fract)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		this->scheduled_dispatch_start_date = start_date;
 | 
					 | 
				
			||||||
		this->scheduled_dispatch_start_full_date_fract = start_full_date_fract;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Get the scheduled dispatch start date part
 | 
					 | 
				
			||||||
	 * @return  scheduled dispatch start date part
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	inline Date GetScheduledDispatchStartDatePart() const { return this->scheduled_dispatch_start_date; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Get the scheduled dispatch start date fract part
 | 
					 | 
				
			||||||
	 * @return  scheduled dispatch start date fract part
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	inline uint16 GetScheduledDispatchStartDateFractPart() const { return this->scheduled_dispatch_start_full_date_fract; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Get the scheduled dispatch start date, in absolute scaled tick
 | 
					 | 
				
			||||||
	 * @return  scheduled dispatch start date
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	inline DateTicksScaled GetScheduledDispatchStartTick() const { return SchdispatchConvertToScaledTick(this->scheduled_dispatch_start_date, this->scheduled_dispatch_start_full_date_fract); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Whether the scheduled dispatch setting is valid
 | 
					 | 
				
			||||||
	 * @return  scheduled dispatch start date fraction
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	inline bool IsScheduledDispatchValid() const { return this->scheduled_dispatch_start_date >= 0 && this->scheduled_dispatch_duration > 0; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Set the scheduled dispatch last dispatch offset, in scaled tick
 | 
					 | 
				
			||||||
	 * @param  duration  New last dispatch offset
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	inline void SetScheduledDispatchLastDispatch(int32 offset) { this->scheduled_dispatch_last_dispatch = offset; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Get the scheduled dispatch last dispatch offset, in scaled tick
 | 
					 | 
				
			||||||
	 * @return  scheduled dispatch last dispatch
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	inline int32 GetScheduledDispatchLastDispatch() const { return this->scheduled_dispatch_last_dispatch; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Set the scheduled dispatch maximum allowed delay, in scaled tick
 | 
					 | 
				
			||||||
	 * @param  delay  New maximum allow delay
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	inline void SetScheduledDispatchDelay(int32 delay) { this->scheduled_dispatch_max_delay = delay; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Get the scheduled dispatch maximum alowed delay, in scaled tick
 | 
					 | 
				
			||||||
	 * @return  scheduled dispatch last dispatch
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	inline int32 GetScheduledDispatchDelay() const { return this->scheduled_dispatch_max_delay; }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						inline DispatchSchedule &GetDispatchScheduleByIndex(uint index) { return this->dispatch_schedules[index]; }
 | 
				
			||||||
 | 
						inline const DispatchSchedule &GetDispatchScheduleByIndex(uint index) const { return this->dispatch_schedules[index]; }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ShiftOrderDates(int interval);
 | 
					void ShiftOrderDates(int interval);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -338,6 +338,7 @@ Order::Order(uint64 packed)
 | 
				
			|||||||
void InvalidateVehicleOrder(const Vehicle *v, int data)
 | 
					void InvalidateVehicleOrder(const Vehicle *v, int data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	SetWindowDirty(WC_VEHICLE_VIEW, v->index);
 | 
						SetWindowDirty(WC_VEHICLE_VIEW, v->index);
 | 
				
			||||||
 | 
						SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (data != 0) {
 | 
						if (data != 0) {
 | 
				
			||||||
		/* Calls SetDirty() too */
 | 
							/* Calls SetDirty() too */
 | 
				
			||||||
@@ -370,7 +371,7 @@ void Order::AssignOrder(const Order &other)
 | 
				
			|||||||
	this->travel_time = other.travel_time;
 | 
						this->travel_time = other.travel_time;
 | 
				
			||||||
	this->max_speed   = other.max_speed;
 | 
						this->max_speed   = other.max_speed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (other.extra != nullptr && (this->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD || this->GetLoadType() == OLFB_CARGO_TYPE_LOAD || other.extra->xdata != 0 || other.extra->xflags != 0)) {
 | 
						if (other.extra != nullptr && (this->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD || this->GetLoadType() == OLFB_CARGO_TYPE_LOAD || other.extra->xdata != 0 || other.extra->xflags != 0 || other.extra->dispatch_index != 0)) {
 | 
				
			||||||
		this->AllocExtraInfo();
 | 
							this->AllocExtraInfo();
 | 
				
			||||||
		*(this->extra) = *(other.extra);
 | 
							*(this->extra) = *(other.extra);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
@@ -1894,6 +1895,9 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
 | 
				
			|||||||
					if (order->IsWaitTimetabled() || order->GetWaitTime() > 0) {
 | 
										if (order->IsWaitTimetabled() || order->GetWaitTime() > 0) {
 | 
				
			||||||
						DoCommandEx(tile, v->index | (MTF_WAIT_TIME << 28) | (1 << 31), 0, p3, flags, CMD_CHANGE_TIMETABLE);
 | 
											DoCommandEx(tile, v->index | (MTF_WAIT_TIME << 28) | (1 << 31), 0, p3, flags, CMD_CHANGE_TIMETABLE);
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
										if (order->IsScheduledDispatchOrder(false)) {
 | 
				
			||||||
 | 
											DoCommandEx(tile, v->index | (MTF_ASSIGN_SCHEDULE << 28), -1, p3, flags, CMD_CHANGE_TIMETABLE);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2390,12 +2394,7 @@ CommandCost CmdCloneOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
 | 
				
			|||||||
				/* Copy over scheduled dispatch data */
 | 
									/* Copy over scheduled dispatch data */
 | 
				
			||||||
				assert(dst->orders.list != nullptr);
 | 
									assert(dst->orders.list != nullptr);
 | 
				
			||||||
				if (src->orders.list != nullptr) {
 | 
									if (src->orders.list != nullptr) {
 | 
				
			||||||
					dst->orders.list->SetScheduledDispatchDuration(src->orders.list->GetScheduledDispatchDuration());
 | 
										dst->orders.list->GetScheduledDispatchScheduleSet() = src->orders.list->GetScheduledDispatchScheduleSet();
 | 
				
			||||||
					dst->orders.list->SetScheduledDispatchDelay(src->orders.list->GetScheduledDispatchDelay());
 | 
					 | 
				
			||||||
					dst->orders.list->SetScheduledDispatchStartDate(src->orders.list->GetScheduledDispatchStartDatePart(),
 | 
					 | 
				
			||||||
							src->orders.list->GetScheduledDispatchStartDateFractPart());
 | 
					 | 
				
			||||||
					dst->orders.list->SetScheduledDispatchLastDispatch(0);
 | 
					 | 
				
			||||||
					dst->orders.list->SetScheduledDispatch(src->orders.list->GetScheduledDispatch());
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				/* Set automation bit if target has it. */
 | 
									/* Set automation bit if target has it. */
 | 
				
			||||||
@@ -2701,7 +2700,7 @@ static bool CheckForValidOrders(const Vehicle *v)
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * Compare the variable and value based on the given comparator.
 | 
					 * Compare the variable and value based on the given comparator.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static bool OrderConditionCompare(OrderConditionComparator occ, int variable, int value)
 | 
					bool OrderConditionCompare(OrderConditionComparator occ, int variable, int value)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	switch (occ) {
 | 
						switch (occ) {
 | 
				
			||||||
		case OCC_EQUALS:      return variable == value;
 | 
							case OCC_EQUALS:      return variable == value;
 | 
				
			||||||
@@ -3223,8 +3222,10 @@ CommandCost CmdMassChangeOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, u
 | 
				
			|||||||
void ShiftOrderDates(int interval)
 | 
					void ShiftOrderDates(int interval)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	for (OrderList *orderlist : OrderList::Iterate()) {
 | 
						for (OrderList *orderlist : OrderList::Iterate()) {
 | 
				
			||||||
		if (orderlist->GetScheduledDispatchStartDatePart() >= 0) {
 | 
							for (DispatchSchedule &ds : orderlist->GetScheduledDispatchScheduleSet()) {
 | 
				
			||||||
			orderlist->SetScheduledDispatchStartDate(orderlist->GetScheduledDispatchStartDatePart() + interval, orderlist->GetScheduledDispatchStartDateFractPart());
 | 
								if (ds.GetScheduledDispatchStartDatePart() >= 0) {
 | 
				
			||||||
 | 
									ds.SetScheduledDispatchStartDate(ds.GetScheduledDispatchStartDatePart() + interval, ds.GetScheduledDispatchStartDateFractPart());
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,5 +32,6 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int
 | 
				
			|||||||
#define MAX_SERVINT_DAYS   800
 | 
					#define MAX_SERVINT_DAYS   800
 | 
				
			||||||
 | 
					
 | 
				
			||||||
uint16 GetServiceIntervalClamped(uint interval, bool ispercent);
 | 
					uint16 GetServiceIntervalClamped(uint interval, bool ispercent);
 | 
				
			||||||
 | 
					bool OrderConditionCompare(OrderConditionComparator occ, int variable, int value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* ORDER_FUNC_H */
 | 
					#endif /* ORDER_FUNC_H */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1029,7 +1029,7 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (timetable && order->GetWaitTime() > 0) {
 | 
								if (timetable && (order->IsWaitTimetabled() || order->GetWaitTime() > 0)) {
 | 
				
			||||||
				SetDParam(7, order->IsWaitTimetabled() ? STR_TIMETABLE_AND_TRAVEL_FOR : STR_TIMETABLE_AND_TRAVEL_FOR_ESTIMATED);
 | 
									SetDParam(7, order->IsWaitTimetabled() ? STR_TIMETABLE_AND_TRAVEL_FOR : STR_TIMETABLE_AND_TRAVEL_FOR_ESTIMATED);
 | 
				
			||||||
				SetTimetableParams(8, order->GetWaitTime());
 | 
									SetTimetableParams(8, order->GetWaitTime());
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
@@ -1057,12 +1057,14 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int
 | 
				
			|||||||
	if (timetable && timetable_wait_time_valid && order->GetLeaveType() != OLT_NORMAL && edge != 0) {
 | 
						if (timetable && timetable_wait_time_valid && order->GetLeaveType() != OLT_NORMAL && edge != 0) {
 | 
				
			||||||
		edge = DrawString(rtl ? left : edge + 3, rtl ? edge - 3 : right, y, STR_TIMETABLE_LEAVE_EARLY_ORDER + order->GetLeaveType() - OLT_LEAVE_EARLY, colour);
 | 
							edge = DrawString(rtl ? left : edge + 3, rtl ? edge - 3 : right, y, STR_TIMETABLE_LEAVE_EARLY_ORDER + order->GetLeaveType() - OLT_LEAVE_EARLY, colour);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (timetable && HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) && v->GetFirstWaitingLocation(false) == order_index && edge != 0) {
 | 
						if (timetable && HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) && order->IsScheduledDispatchOrder(false) && edge != 0) {
 | 
				
			||||||
		StringID str = order->IsWaitTimetabled() ? STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER : STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME;
 | 
							StringID str = order->IsWaitTimetabled() ? STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER : STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME;
 | 
				
			||||||
 | 
							SetDParam(0, v->orders.list->GetScheduledDispatchScheduleCount() > 1 ? STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_SCHEDULE_INDEX : STR_EMPTY);
 | 
				
			||||||
 | 
							SetDParam(1, order->GetDispatchScheduleIndex() + 1);
 | 
				
			||||||
		edge = DrawString(rtl ? left : edge + 3, rtl ? edge - 3 : right, y, str, colour);
 | 
							edge = DrawString(rtl ? left : edge + 3, rtl ? edge - 3 : right, y, str, colour);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (timetable && timetable_wait_time_valid && order->IsWaitFixed() && edge != 0) {
 | 
						if (timetable && (timetable_wait_time_valid || order->IsType(OT_CONDITIONAL)) && order->IsWaitFixed() && edge != 0) {
 | 
				
			||||||
		Dimension lock_d = GetSpriteSize(SPR_LOCK);
 | 
							Dimension lock_d = GetSpriteSize(SPR_LOCK);
 | 
				
			||||||
		DrawPixelInfo tmp_dpi;
 | 
							DrawPixelInfo tmp_dpi;
 | 
				
			||||||
		if (FillDrawPixelInfo(&tmp_dpi, rtl ? left : middle, y, rtl ? middle - left : right - middle, lock_d.height)) {
 | 
							if (FillDrawPixelInfo(&tmp_dpi, rtl ? left : middle, y, rtl ? middle - left : right - middle, lock_d.height)) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -238,6 +238,7 @@ enum ModifyTimetableFlags {
 | 
				
			|||||||
	MTF_SET_WAIT_FIXED,///< Set wait time fixed flag state.
 | 
						MTF_SET_WAIT_FIXED,///< Set wait time fixed flag state.
 | 
				
			||||||
	MTF_SET_TRAVEL_FIXED,///< Set travel time fixed flag state.
 | 
						MTF_SET_TRAVEL_FIXED,///< Set travel time fixed flag state.
 | 
				
			||||||
	MTF_SET_LEAVE_TYPE,///< Passes an OrderLeaveType.
 | 
						MTF_SET_LEAVE_TYPE,///< Passes an OrderLeaveType.
 | 
				
			||||||
 | 
						MTF_ASSIGN_SCHEDULE, ///< Assign a dispatch schedule.
 | 
				
			||||||
	MTF_END
 | 
						MTF_END
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
template <> struct EnumPropsT<ModifyTimetableFlags> : MakeEnumPropsT<ModifyTimetableFlags, byte, MTF_WAIT_TIME, MTF_END, MTF_END, 3> {};
 | 
					template <> struct EnumPropsT<ModifyTimetableFlags> : MakeEnumPropsT<ModifyTimetableFlags, byte, MTF_WAIT_TIME, MTF_END, MTF_END, 3> {};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3999,6 +3999,22 @@ bool AfterLoadGame()
 | 
				
			|||||||
		_settings_game.vehicle.through_load_speed_limit = 15;
 | 
							_settings_game.vehicle.through_load_speed_limit = 15;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (SlXvIsFeaturePresent(XSLFI_SCHEDULED_DISPATCH, 1, 2)) {
 | 
				
			||||||
 | 
							for (OrderList *order_list : OrderList::Iterate()) {
 | 
				
			||||||
 | 
								if (order_list->GetScheduledDispatchScheduleCount() == 1) {
 | 
				
			||||||
 | 
									const DispatchSchedule &ds = order_list->GetDispatchScheduleByIndex(0);
 | 
				
			||||||
 | 
									if (!ds.IsScheduledDispatchValid() && ds.GetScheduledDispatch().empty()) {
 | 
				
			||||||
 | 
										order_list->GetScheduledDispatchScheduleSet().clear();
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										VehicleOrderID idx = order_list->GetFirstSharedVehicle()->GetFirstWaitingLocation(false);
 | 
				
			||||||
 | 
										if (idx != INVALID_VEH_ORDER_ID) {
 | 
				
			||||||
 | 
											order_list->GetOrderAt(idx)->SetDispatchScheduleIndex(0);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	InitializeRoadGUI();
 | 
						InitializeRoadGUI();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* This needs to be done after conversion. */
 | 
						/* This needs to be done after conversion. */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -109,7 +109,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
 | 
				
			|||||||
	{ XSLFI_STATION_CATCHMENT_INC,  XSCF_NULL,                1,   1, "station_catchment_inc",     nullptr, nullptr, nullptr        },
 | 
						{ XSLFI_STATION_CATCHMENT_INC,  XSCF_NULL,                1,   1, "station_catchment_inc",     nullptr, nullptr, nullptr        },
 | 
				
			||||||
	{ XSLFI_CUSTOM_BRIDGE_HEADS,    XSCF_NULL,                4,   4, "custom_bridge_heads",       nullptr, nullptr, nullptr        },
 | 
						{ XSLFI_CUSTOM_BRIDGE_HEADS,    XSCF_NULL,                4,   4, "custom_bridge_heads",       nullptr, nullptr, nullptr        },
 | 
				
			||||||
	{ XSLFI_CHUNNEL,                XSCF_NULL,                2,   2, "chunnel",                   nullptr, nullptr, "TUNN"      },
 | 
						{ XSLFI_CHUNNEL,                XSCF_NULL,                2,   2, "chunnel",                   nullptr, nullptr, "TUNN"      },
 | 
				
			||||||
	{ XSLFI_SCHEDULED_DISPATCH,     XSCF_NULL,                2,   2, "scheduled_dispatch",        nullptr, nullptr, nullptr        },
 | 
						{ XSLFI_SCHEDULED_DISPATCH,     XSCF_NULL,                3,   3, "scheduled_dispatch",        nullptr, nullptr, nullptr        },
 | 
				
			||||||
	{ XSLFI_MORE_TOWN_GROWTH_RATES, XSCF_NULL,                1,   1, "more_town_growth_rates",    nullptr, nullptr, nullptr        },
 | 
						{ XSLFI_MORE_TOWN_GROWTH_RATES, XSCF_NULL,                1,   1, "more_town_growth_rates",    nullptr, nullptr, nullptr        },
 | 
				
			||||||
	{ XSLFI_MULTIPLE_DOCKS,         XSCF_NULL,                2,   2, "multiple_docks",            nullptr, nullptr, nullptr        },
 | 
						{ XSLFI_MULTIPLE_DOCKS,         XSCF_NULL,                2,   2, "multiple_docks",            nullptr, nullptr, nullptr        },
 | 
				
			||||||
	{ XSLFI_TIMETABLE_EXTRA,        XSCF_NULL,                7,   7, "timetable_extra",           nullptr, nullptr, "ORDX"      },
 | 
						{ XSLFI_TIMETABLE_EXTRA,        XSCF_NULL,                7,   7, "timetable_extra",           nullptr, nullptr, "ORDX"      },
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -217,6 +217,7 @@ const SaveLoadTable GetOrderExtraInfoDescription()
 | 
				
			|||||||
		SLE_CONDARR_X(OrderExtraInfo, cargo_type_flags, SLE_UINT8, NUM_CARGO, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CARGO_TYPE_ORDERS, 3)),
 | 
							SLE_CONDARR_X(OrderExtraInfo, cargo_type_flags, SLE_UINT8, NUM_CARGO, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CARGO_TYPE_ORDERS, 3)),
 | 
				
			||||||
		SLE_CONDVAR_X(OrderExtraInfo, xflags,           SLE_UINT8,            SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA)),
 | 
							SLE_CONDVAR_X(OrderExtraInfo, xflags,           SLE_UINT8,            SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA)),
 | 
				
			||||||
		SLE_CONDVAR_X(OrderExtraInfo, xdata,           SLE_UINT32,            SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_EXTRA_DATA)),
 | 
							SLE_CONDVAR_X(OrderExtraInfo, xdata,           SLE_UINT32,            SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_EXTRA_DATA)),
 | 
				
			||||||
 | 
							SLE_CONDVAR_X(OrderExtraInfo, dispatch_index,  SLE_UINT16,            SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 3)),
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return _order_extra_info_desc;
 | 
						return _order_extra_info_desc;
 | 
				
			||||||
@@ -255,16 +256,24 @@ static void Ptrs_ORDR()
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SaveLoadTable GetDispatchScheduleDescription()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						static const SaveLoad _order_extra_info_desc[] = {
 | 
				
			||||||
 | 
							SLE_VARVEC(DispatchSchedule, scheduled_dispatch,                    SLE_UINT32),
 | 
				
			||||||
 | 
							SLE_VAR(DispatchSchedule, scheduled_dispatch_duration,              SLE_UINT32),
 | 
				
			||||||
 | 
							SLE_VAR(DispatchSchedule, scheduled_dispatch_start_date,            SLE_INT32),
 | 
				
			||||||
 | 
							SLE_VAR(DispatchSchedule, scheduled_dispatch_start_full_date_fract, SLE_UINT16),
 | 
				
			||||||
 | 
							SLE_VAR(DispatchSchedule, scheduled_dispatch_last_dispatch,         SLE_INT32),
 | 
				
			||||||
 | 
							SLE_VAR(DispatchSchedule, scheduled_dispatch_max_delay,             SLE_INT32),
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return _order_extra_info_desc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SaveLoadTable GetOrderListDescription()
 | 
					SaveLoadTable GetOrderListDescription()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	static const SaveLoad _orderlist_desc[] = {
 | 
						static const SaveLoad _orderlist_desc[] = {
 | 
				
			||||||
		      SLE_REF(OrderList, first,                                    REF_ORDER),
 | 
							      SLE_REF(OrderList, first,                                    REF_ORDER),
 | 
				
			||||||
		SLE_CONDVARVEC_X(OrderList, scheduled_dispatch,                    SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH)),
 | 
					 | 
				
			||||||
		SLE_CONDVAR_X(OrderList, scheduled_dispatch_duration,              SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH)),
 | 
					 | 
				
			||||||
		SLE_CONDVAR_X(OrderList, scheduled_dispatch_start_date,            SLE_INT32,  SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH)),
 | 
					 | 
				
			||||||
		SLE_CONDVAR_X(OrderList, scheduled_dispatch_start_full_date_fract, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH)),
 | 
					 | 
				
			||||||
		SLE_CONDVAR_X(OrderList, scheduled_dispatch_last_dispatch,         SLE_INT32,  SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH)),
 | 
					 | 
				
			||||||
		SLE_CONDVAR_X(OrderList, scheduled_dispatch_max_delay,             SLE_INT32,  SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH)),
 | 
					 | 
				
			||||||
		SLEG_CONDVAR_X(_jokerpp_separation_mode,                           SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)),
 | 
							SLEG_CONDVAR_X(_jokerpp_separation_mode,                           SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)),
 | 
				
			||||||
		SLE_CONDNULL_X(21,                                                             SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)),
 | 
							SLE_CONDNULL_X(21,                                                             SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)),
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
@@ -276,7 +285,14 @@ static void Save_ORDL()
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	for (OrderList *list : OrderList::Iterate()) {
 | 
						for (OrderList *list : OrderList::Iterate()) {
 | 
				
			||||||
		SlSetArrayIndex(list->index);
 | 
							SlSetArrayIndex(list->index);
 | 
				
			||||||
 | 
							SlAutolength([](void *data) {
 | 
				
			||||||
 | 
								OrderList *list = static_cast<OrderList *>(data);
 | 
				
			||||||
			SlObject(list, GetOrderListDescription());
 | 
								SlObject(list, GetOrderListDescription());
 | 
				
			||||||
 | 
								SlWriteUint32(list->GetScheduledDispatchScheduleCount());
 | 
				
			||||||
 | 
								for (DispatchSchedule &ds : list->GetScheduledDispatchScheduleSet()) {
 | 
				
			||||||
 | 
									SlObject(&ds, GetDispatchScheduleDescription());
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}, list);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -297,8 +313,14 @@ static void Load_ORDL()
 | 
				
			|||||||
				_jokerpp_non_auto_separation.push_back(list);
 | 
									_jokerpp_non_auto_separation.push_back(list);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if (SlXvIsFeaturePresent(XSLFI_SCHEDULED_DISPATCH)) {
 | 
				
			||||||
 | 
								uint count = SlXvIsFeaturePresent(XSLFI_SCHEDULED_DISPATCH, 3) ? SlReadUint32() : 1;
 | 
				
			||||||
 | 
								list->GetScheduledDispatchScheduleSet().resize(count);
 | 
				
			||||||
 | 
								for (DispatchSchedule &ds : list->GetScheduledDispatchScheduleSet()) {
 | 
				
			||||||
 | 
									SlObject(&ds, GetDispatchScheduleDescription());
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Ptrs_ORDL()
 | 
					void Ptrs_ORDL()
 | 
				
			||||||
@@ -330,17 +352,13 @@ SaveLoadTable GetOrderBackupDescription()
 | 
				
			|||||||
		 SLE_CONDVAR(OrderBackup, vehicle_flags,            SLE_FILE_U8 | SLE_VAR_U16, SLV_176, SLV_180),
 | 
							 SLE_CONDVAR(OrderBackup, vehicle_flags,            SLE_FILE_U8 | SLE_VAR_U16, SLV_176, SLV_180),
 | 
				
			||||||
		 SLE_CONDVAR(OrderBackup, vehicle_flags,            SLE_UINT16,                SLV_180, SL_MAX_VERSION),
 | 
							 SLE_CONDVAR(OrderBackup, vehicle_flags,            SLE_UINT16,                SLV_180, SL_MAX_VERSION),
 | 
				
			||||||
		     SLE_REF(OrderBackup, orders,                   REF_ORDER),
 | 
							     SLE_REF(OrderBackup, orders,                   REF_ORDER),
 | 
				
			||||||
		SLE_CONDVARVEC_X(OrderBackup, scheduled_dispatch,                    SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 2)),
 | 
							SLE_CONDNULL_X(18,                                                      SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 2, 2)),
 | 
				
			||||||
		SLE_CONDVAR_X(OrderBackup, scheduled_dispatch_duration,              SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 2)),
 | 
					 | 
				
			||||||
		SLE_CONDVAR_X(OrderBackup, scheduled_dispatch_start_date,            SLE_INT32,  SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 2)),
 | 
					 | 
				
			||||||
		SLE_CONDVAR_X(OrderBackup, scheduled_dispatch_start_full_date_fract, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 2)),
 | 
					 | 
				
			||||||
		SLE_CONDVAR_X(OrderBackup, scheduled_dispatch_max_delay,             SLE_INT32,  SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 2)),
 | 
					 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return _order_backup_desc;
 | 
						return _order_backup_desc;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void Save_BKOR()
 | 
					void Save_BKOR()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	/* We only save this when we're a network server
 | 
						/* We only save this when we're a network server
 | 
				
			||||||
	 * as we want this information on our clients. For
 | 
						 * as we want this information on our clients. For
 | 
				
			||||||
@@ -349,7 +367,14 @@ static void Save_BKOR()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	for (OrderBackup *ob : OrderBackup::Iterate()) {
 | 
						for (OrderBackup *ob : OrderBackup::Iterate()) {
 | 
				
			||||||
		SlSetArrayIndex(ob->index);
 | 
							SlSetArrayIndex(ob->index);
 | 
				
			||||||
 | 
							SlAutolength([](void *data) {
 | 
				
			||||||
 | 
								OrderBackup *ob = static_cast<OrderBackup *>(data);
 | 
				
			||||||
			SlObject(ob, GetOrderBackupDescription());
 | 
								SlObject(ob, GetOrderBackupDescription());
 | 
				
			||||||
 | 
								SlWriteUint32((uint)ob->dispatch_schedules.size());
 | 
				
			||||||
 | 
								for (DispatchSchedule &ds : ob->dispatch_schedules) {
 | 
				
			||||||
 | 
									SlObject(&ds, GetDispatchScheduleDescription());
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}, ob);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -361,6 +386,13 @@ void Load_BKOR()
 | 
				
			|||||||
		/* set num_orders to 0 so it's a valid OrderList */
 | 
							/* set num_orders to 0 so it's a valid OrderList */
 | 
				
			||||||
		OrderBackup *ob = new (index) OrderBackup();
 | 
							OrderBackup *ob = new (index) OrderBackup();
 | 
				
			||||||
		SlObject(ob, GetOrderBackupDescription());
 | 
							SlObject(ob, GetOrderBackupDescription());
 | 
				
			||||||
 | 
							if (SlXvIsFeaturePresent(XSLFI_SCHEDULED_DISPATCH, 3)) {
 | 
				
			||||||
 | 
								uint count = SlReadUint32();
 | 
				
			||||||
 | 
								ob->dispatch_schedules.resize(count);
 | 
				
			||||||
 | 
								for (DispatchSchedule &ds : ob->dispatch_schedules) {
 | 
				
			||||||
 | 
									SlObject(&ds, GetDispatchScheduleDescription());
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -327,6 +327,14 @@ DECLARE_ENUM_AS_BIT_SET(SaveLoadChunkExtHeaderFlags)
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
#define SLE_VEC(base, variable, type) SLE_CONDVEC(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION)
 | 
					#define SLE_VEC(base, variable, type) SLE_CONDVEC(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Storage of a variable vector in every savegame version.
 | 
				
			||||||
 | 
					 * @param base     Name of the class or struct containing the list.
 | 
				
			||||||
 | 
					 * @param variable Name of the variable in the class or struct referenced by \a base.
 | 
				
			||||||
 | 
					 * @param type     Storage of the data in memory and in the savegame.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define SLE_VARVEC(base, variable, type) SLE_CONDVARVEC(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Empty space in every savegame version.
 | 
					 * Empty space in every savegame version.
 | 
				
			||||||
 * @param length Length of the empty space.
 | 
					 * @param length Length of the empty space.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,6 +15,7 @@
 | 
				
			|||||||
#include "settings_type.h"
 | 
					#include "settings_type.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void ShowSchdispatchWindow(const Vehicle *v);
 | 
					void ShowSchdispatchWindow(const Vehicle *v);
 | 
				
			||||||
 | 
					void SchdispatchInvalidateWindows(const Vehicle *v);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Convert date and full date fraction to DateTicksScaled
 | 
					 * Convert date and full date fraction to DateTicksScaled
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -59,9 +59,8 @@ CommandCost CmdScheduledDispatch(TileIndex tile, DoCommandFlag flags, uint32 p1,
 | 
				
			|||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				ClrBit(v2->vehicle_flags, VF_SCHEDULED_DISPATCH);
 | 
									ClrBit(v2->vehicle_flags, VF_SCHEDULED_DISPATCH);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			SetWindowDirty(WC_VEHICLE_TIMETABLE, v2->index);
 | 
					 | 
				
			||||||
			SetWindowDirty(WC_SCHDISPATCH_SLOTS, v2->index);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							SetTimetableWindowsDirty(v, true);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return CommandCost();
 | 
						return CommandCost();
 | 
				
			||||||
@@ -82,6 +81,7 @@ CommandCost CmdScheduledDispatch(TileIndex tile, DoCommandFlag flags, uint32 p1,
 | 
				
			|||||||
CommandCost CmdScheduledDispatchAdd(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, uint64 p3, const char *text, uint32 binary_length)
 | 
					CommandCost CmdScheduledDispatchAdd(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, uint64 p3, const char *text, uint32 binary_length)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	VehicleID veh = GB(p1, 0, 20);
 | 
						VehicleID veh = GB(p1, 0, 20);
 | 
				
			||||||
 | 
						uint schedule_index = GB(p1, 20, 12);
 | 
				
			||||||
	uint32 offset = GB(p3, 0, 32);
 | 
						uint32 offset = GB(p3, 0, 32);
 | 
				
			||||||
	uint32 extra_slots = GB(p3, 32, 16);
 | 
						uint32 extra_slots = GB(p3, 32, 16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -93,16 +93,19 @@ CommandCost CmdScheduledDispatchAdd(TileIndex tile, DoCommandFlag flags, uint32
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if (v->orders.list == nullptr) return CMD_ERROR;
 | 
						if (v->orders.list == nullptr) return CMD_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (extra_slots > 512) return_cmd_error(STR_ERROR_SCHDISPATCH_TRIED_TO_ADD_TOO_MANY_SLOTS);
 | 
						if (extra_slots > 512) return_cmd_error(STR_ERROR_SCHDISPATCH_TRIED_TO_ADD_TOO_MANY_SLOTS);
 | 
				
			||||||
	if (extra_slots > 0 && offset == 0) return CMD_ERROR;
 | 
						if (extra_slots > 0 && offset == 0) return CMD_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (flags & DC_EXEC) {
 | 
						if (flags & DC_EXEC) {
 | 
				
			||||||
		v->orders.list->AddScheduledDispatch(p2);
 | 
							DispatchSchedule &ds = v->orders.list->GetDispatchScheduleByIndex(schedule_index);
 | 
				
			||||||
 | 
							ds.AddScheduledDispatch(p2);
 | 
				
			||||||
		for (uint i = 0; i < extra_slots; i++) {
 | 
							for (uint i = 0; i < extra_slots; i++) {
 | 
				
			||||||
			p2 += offset;
 | 
								p2 += offset;
 | 
				
			||||||
			v->orders.list->AddScheduledDispatch(p2);
 | 
								ds.AddScheduledDispatch(p2);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index);
 | 
							SetTimetableWindowsDirty(v, true);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return CommandCost();
 | 
						return CommandCost();
 | 
				
			||||||
@@ -120,6 +123,7 @@ CommandCost CmdScheduledDispatchAdd(TileIndex tile, DoCommandFlag flags, uint32
 | 
				
			|||||||
CommandCost CmdScheduledDispatchRemove(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 | 
					CommandCost CmdScheduledDispatchRemove(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	VehicleID veh = GB(p1, 0, 20);
 | 
						VehicleID veh = GB(p1, 0, 20);
 | 
				
			||||||
 | 
						uint schedule_index = GB(p1, 20, 12);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Vehicle *v = Vehicle::GetIfValid(veh);
 | 
						Vehicle *v = Vehicle::GetIfValid(veh);
 | 
				
			||||||
	if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
 | 
						if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
 | 
				
			||||||
@@ -129,9 +133,11 @@ CommandCost CmdScheduledDispatchRemove(TileIndex tile, DoCommandFlag flags, uint
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if (v->orders.list == nullptr) return CMD_ERROR;
 | 
						if (v->orders.list == nullptr) return CMD_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (flags & DC_EXEC) {
 | 
						if (flags & DC_EXEC) {
 | 
				
			||||||
		v->orders.list->RemoveScheduledDispatch(p2);
 | 
							v->orders.list->GetDispatchScheduleByIndex(schedule_index).RemoveScheduledDispatch(p2);
 | 
				
			||||||
		SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index);
 | 
							SetTimetableWindowsDirty(v, true);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return CommandCost();
 | 
						return CommandCost();
 | 
				
			||||||
@@ -150,6 +156,7 @@ CommandCost CmdScheduledDispatchRemove(TileIndex tile, DoCommandFlag flags, uint
 | 
				
			|||||||
CommandCost CmdScheduledDispatchSetDuration(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 | 
					CommandCost CmdScheduledDispatchSetDuration(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	VehicleID veh = GB(p1, 0, 20);
 | 
						VehicleID veh = GB(p1, 0, 20);
 | 
				
			||||||
 | 
						uint schedule_index = GB(p1, 20, 12);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Vehicle *v = Vehicle::GetIfValid(veh);
 | 
						Vehicle *v = Vehicle::GetIfValid(veh);
 | 
				
			||||||
	if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
 | 
						if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
 | 
				
			||||||
@@ -159,10 +166,13 @@ CommandCost CmdScheduledDispatchSetDuration(TileIndex tile, DoCommandFlag flags,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if (v->orders.list == nullptr) return CMD_ERROR;
 | 
						if (v->orders.list == nullptr) return CMD_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (flags & DC_EXEC) {
 | 
						if (flags & DC_EXEC) {
 | 
				
			||||||
		v->orders.list->SetScheduledDispatchDuration(p2);
 | 
							DispatchSchedule &ds = v->orders.list->GetDispatchScheduleByIndex(schedule_index);
 | 
				
			||||||
		v->orders.list->UpdateScheduledDispatch();
 | 
							ds.SetScheduledDispatchDuration(p2);
 | 
				
			||||||
		SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index);
 | 
							ds.UpdateScheduledDispatch(nullptr);
 | 
				
			||||||
 | 
							SetTimetableWindowsDirty(v, true);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return CommandCost();
 | 
						return CommandCost();
 | 
				
			||||||
@@ -173,23 +183,22 @@ CommandCost CmdScheduledDispatchSetDuration(TileIndex tile, DoCommandFlag flags,
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 * The parameter is quite tricky. The default maximum of daylength factor is 125,
 | 
					 * The parameter is quite tricky. The default maximum of daylength factor is 125,
 | 
				
			||||||
 * and with DAY_TICKS of 74 the result (maximum scaled tick per day) fits in 14 bit.
 | 
					 * and with DAY_TICKS of 74 the result (maximum scaled tick per day) fits in 14 bit.
 | 
				
			||||||
 * Vehicle index in p1 takes 20 bit, so we have 12 bit here. The MSB of the fraction is stored here.
 | 
					 | 
				
			||||||
 * The 2-bit LSB is stored in MSB of p2, which is start date. The default date is stored in int32,
 | 
					 | 
				
			||||||
 * which only have topmost bit available. However, if the date reached 31 bits, that means it is over 1,000,000 years,
 | 
					 | 
				
			||||||
 * so I think it is safe to steal another bit here.
 | 
					 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * See also the static_assert at the top of the file.
 | 
					 * See also the static_assert at the top of the file.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param tile Not used.
 | 
					 * @param tile Not used.
 | 
				
			||||||
 * @param flags Operation to perform.
 | 
					 * @param flags Operation to perform.
 | 
				
			||||||
 * @param p1 MSB of Start Full Date Fraction || Vehicle index
 | 
					 * @param p1 Vehicle index
 | 
				
			||||||
 * @param p2 LSB of Start Full Date Fraction || Date to add.
 | 
					 * @param p2 Date to add.
 | 
				
			||||||
 | 
					 * @param p3 various bitstuffed elements
 | 
				
			||||||
 | 
					 *  - p3 = (bit 0 - 15)  - Full date fraction
 | 
				
			||||||
 * @param text unused
 | 
					 * @param text unused
 | 
				
			||||||
 * @return the cost of this operation or an error
 | 
					 * @return the cost of this operation or an error
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
CommandCost CmdScheduledDispatchSetStartDate(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 | 
					CommandCost CmdScheduledDispatchSetStartDate(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, uint64 p3, const char *text, uint32 binary_length)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	VehicleID veh = GB(p1, 0, 20);
 | 
						VehicleID veh = GB(p1, 0, 20);
 | 
				
			||||||
 | 
						uint schedule_index = GB(p1, 20, 12);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Vehicle *v = Vehicle::GetIfValid(veh);
 | 
						Vehicle *v = Vehicle::GetIfValid(veh);
 | 
				
			||||||
	if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
 | 
						if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
 | 
				
			||||||
@@ -199,13 +208,16 @@ CommandCost CmdScheduledDispatchSetStartDate(TileIndex tile, DoCommandFlag flags
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if (v->orders.list == nullptr) return CMD_ERROR;
 | 
						if (v->orders.list == nullptr) return CMD_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int32 date = (int32) GB(p2, 0, 30);
 | 
						if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR;
 | 
				
			||||||
	uint16 full_date_fract = (GB(p1, 20, 12) << 2) + GB(p2, 30, 2);
 | 
					
 | 
				
			||||||
 | 
						int32 date = (int32)p2;
 | 
				
			||||||
 | 
						uint16 full_date_fract = GB(p3, 0, 16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (flags & DC_EXEC) {
 | 
						if (flags & DC_EXEC) {
 | 
				
			||||||
		v->orders.list->SetScheduledDispatchStartDate(date, full_date_fract);
 | 
							DispatchSchedule &ds = v->orders.list->GetDispatchScheduleByIndex(schedule_index);
 | 
				
			||||||
		v->orders.list->UpdateScheduledDispatch();
 | 
							ds.SetScheduledDispatchStartDate(date, full_date_fract);
 | 
				
			||||||
		SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index);
 | 
							ds.UpdateScheduledDispatch(nullptr);
 | 
				
			||||||
 | 
							SetTimetableWindowsDirty(v, true);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return CommandCost();
 | 
						return CommandCost();
 | 
				
			||||||
@@ -224,6 +236,7 @@ CommandCost CmdScheduledDispatchSetStartDate(TileIndex tile, DoCommandFlag flags
 | 
				
			|||||||
CommandCost CmdScheduledDispatchSetDelay(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 | 
					CommandCost CmdScheduledDispatchSetDelay(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	VehicleID veh = GB(p1, 0, 20);
 | 
						VehicleID veh = GB(p1, 0, 20);
 | 
				
			||||||
 | 
						uint schedule_index = GB(p1, 20, 12);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Vehicle *v = Vehicle::GetIfValid(veh);
 | 
						Vehicle *v = Vehicle::GetIfValid(veh);
 | 
				
			||||||
	if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
 | 
						if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
 | 
				
			||||||
@@ -233,9 +246,11 @@ CommandCost CmdScheduledDispatchSetDelay(TileIndex tile, DoCommandFlag flags, ui
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if (v->orders.list == nullptr) return CMD_ERROR;
 | 
						if (v->orders.list == nullptr) return CMD_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (flags & DC_EXEC) {
 | 
						if (flags & DC_EXEC) {
 | 
				
			||||||
		v->orders.list->SetScheduledDispatchDelay(p2);
 | 
							v->orders.list->GetDispatchScheduleByIndex(schedule_index).SetScheduledDispatchDelay(p2);
 | 
				
			||||||
		SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index);
 | 
							SetTimetableWindowsDirty(v, true);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return CommandCost();
 | 
						return CommandCost();
 | 
				
			||||||
@@ -259,6 +274,7 @@ CommandCost CmdScheduledDispatchSetDelay(TileIndex tile, DoCommandFlag flags, ui
 | 
				
			|||||||
CommandCost CmdScheduledDispatchResetLastDispatch(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 | 
					CommandCost CmdScheduledDispatchResetLastDispatch(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	VehicleID veh = GB(p1, 0, 20);
 | 
						VehicleID veh = GB(p1, 0, 20);
 | 
				
			||||||
 | 
						uint schedule_index = GB(p1, 20, 12);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Vehicle *v = Vehicle::GetIfValid(veh);
 | 
						Vehicle *v = Vehicle::GetIfValid(veh);
 | 
				
			||||||
	if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
 | 
						if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
 | 
				
			||||||
@@ -268,9 +284,11 @@ CommandCost CmdScheduledDispatchResetLastDispatch(TileIndex tile, DoCommandFlag
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if (v->orders.list == nullptr) return CMD_ERROR;
 | 
						if (v->orders.list == nullptr) return CMD_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (flags & DC_EXEC) {
 | 
						if (flags & DC_EXEC) {
 | 
				
			||||||
		v->orders.list->SetScheduledDispatchLastDispatch(0);
 | 
							v->orders.list->GetDispatchScheduleByIndex(schedule_index).SetScheduledDispatchLastDispatch(0);
 | 
				
			||||||
		SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index);
 | 
							SetTimetableWindowsDirty(v, true);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return CommandCost();
 | 
						return CommandCost();
 | 
				
			||||||
@@ -289,6 +307,7 @@ CommandCost CmdScheduledDispatchResetLastDispatch(TileIndex tile, DoCommandFlag
 | 
				
			|||||||
CommandCost CmdScheduledDispatchClear(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 | 
					CommandCost CmdScheduledDispatchClear(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	VehicleID veh = GB(p1, 0, 20);
 | 
						VehicleID veh = GB(p1, 0, 20);
 | 
				
			||||||
 | 
						uint schedule_index = GB(p1, 20, 12);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Vehicle *v = Vehicle::GetIfValid(veh);
 | 
						Vehicle *v = Vehicle::GetIfValid(veh);
 | 
				
			||||||
	if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
 | 
						if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
 | 
				
			||||||
@@ -298,9 +317,94 @@ CommandCost CmdScheduledDispatchClear(TileIndex tile, DoCommandFlag flags, uint3
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if (v->orders.list == nullptr) return CMD_ERROR;
 | 
						if (v->orders.list == nullptr) return CMD_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (flags & DC_EXEC) {
 | 
						if (flags & DC_EXEC) {
 | 
				
			||||||
		v->orders.list->ClearScheduledDispatch();
 | 
							v->orders.list->GetDispatchScheduleByIndex(schedule_index).ClearScheduledDispatch();
 | 
				
			||||||
		SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index);
 | 
							SetTimetableWindowsDirty(v, true);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return CommandCost();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Add a new scheduled dispatch schedule
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param tile Not used.
 | 
				
			||||||
 | 
					 * @param flags Operation to perform.
 | 
				
			||||||
 | 
					 * @param p1 Vehicle index
 | 
				
			||||||
 | 
					 * @param p2 Duration, in scaled tick
 | 
				
			||||||
 | 
					 * @param p3 various bitstuffed elements
 | 
				
			||||||
 | 
					 *  - p3 = (bit 0 - 31)  - Start date
 | 
				
			||||||
 | 
					 *  - p3 = (bit 32 - 47) - Full date fraction
 | 
				
			||||||
 | 
					 * @param text unused
 | 
				
			||||||
 | 
					 * @return the cost of this operation or an error
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					CommandCost CmdScheduledDispatchAddNewSchedule(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, uint64 p3, const char *text, uint32 binary_length)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						VehicleID veh = GB(p1, 0, 20);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Vehicle *v = Vehicle::GetIfValid(veh);
 | 
				
			||||||
 | 
						if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CommandCost ret = CheckOwnership(v->owner);
 | 
				
			||||||
 | 
						if (ret.Failed()) return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (v->orders.list == nullptr) return CMD_ERROR;
 | 
				
			||||||
 | 
						if (v->orders.list->GetScheduledDispatchScheduleCount() >= 4096) return CMD_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int32 date = GB(p3, 0, 32);
 | 
				
			||||||
 | 
						uint16 full_date_fract = GB(p3, 32, 16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (flags & DC_EXEC) {
 | 
				
			||||||
 | 
							v->orders.list->GetScheduledDispatchScheduleSet().emplace_back();
 | 
				
			||||||
 | 
							DispatchSchedule &ds = v->orders.list->GetScheduledDispatchScheduleSet().back();
 | 
				
			||||||
 | 
							ds.SetScheduledDispatchDuration(p2);
 | 
				
			||||||
 | 
							ds.SetScheduledDispatchStartDate(date, full_date_fract);
 | 
				
			||||||
 | 
							ds.UpdateScheduledDispatch(nullptr);
 | 
				
			||||||
 | 
							SetTimetableWindowsDirty(v, true);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return CommandCost();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Remove scheduled dispatch schedule
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @param tile Not used.
 | 
				
			||||||
 | 
					 * @param flags Operation to perform.
 | 
				
			||||||
 | 
					 * @param p1 Vehicle index
 | 
				
			||||||
 | 
					 * @param p2 Not used
 | 
				
			||||||
 | 
					 * @param text unused
 | 
				
			||||||
 | 
					 * @return the cost of this operation or an error
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					CommandCost CmdScheduledDispatchRemoveSchedule(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						VehicleID veh = GB(p1, 0, 20);
 | 
				
			||||||
 | 
						uint schedule_index = GB(p1, 20, 12);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Vehicle *v = Vehicle::GetIfValid(veh);
 | 
				
			||||||
 | 
						if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						CommandCost ret = CheckOwnership(v->owner);
 | 
				
			||||||
 | 
						if (ret.Failed()) return ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (v->orders.list == nullptr) return CMD_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (flags & DC_EXEC) {
 | 
				
			||||||
 | 
							std::vector<DispatchSchedule> &scheds = v->orders.list->GetScheduledDispatchScheduleSet();
 | 
				
			||||||
 | 
							scheds.erase(scheds.begin() + schedule_index);
 | 
				
			||||||
 | 
							for (Order *o = v->GetFirstOrder(); o != nullptr; o = o->next) {
 | 
				
			||||||
 | 
								int idx = o->GetDispatchScheduleIndex();
 | 
				
			||||||
 | 
								if (idx == (int)schedule_index) {
 | 
				
			||||||
 | 
									o->SetDispatchScheduleIndex(-1);
 | 
				
			||||||
 | 
								} else if (idx > (int)schedule_index) {
 | 
				
			||||||
 | 
									o->SetDispatchScheduleIndex(idx - 1);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							SchdispatchInvalidateWindows(v);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return CommandCost();
 | 
						return CommandCost();
 | 
				
			||||||
@@ -310,18 +414,18 @@ CommandCost CmdScheduledDispatchClear(TileIndex tile, DoCommandFlag flags, uint3
 | 
				
			|||||||
 * Set scheduled dispatch slot list.
 | 
					 * Set scheduled dispatch slot list.
 | 
				
			||||||
 * @param dispatch_list The offset time list, must be correctly sorted.
 | 
					 * @param dispatch_list The offset time list, must be correctly sorted.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void OrderList::SetScheduledDispatch(std::vector<uint32> dispatch_list)
 | 
					void DispatchSchedule::SetScheduledDispatch(std::vector<uint32> dispatch_list)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	this->scheduled_dispatch = std::move(dispatch_list);
 | 
						this->scheduled_dispatch = std::move(dispatch_list);
 | 
				
			||||||
	assert(std::is_sorted(this->scheduled_dispatch.begin(), this->scheduled_dispatch.end()));
 | 
						assert(std::is_sorted(this->scheduled_dispatch.begin(), this->scheduled_dispatch.end()));
 | 
				
			||||||
	if (this->IsScheduledDispatchValid()) this->UpdateScheduledDispatch();
 | 
						if (this->IsScheduledDispatchValid()) this->UpdateScheduledDispatch(nullptr);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Add new scheduled dispatch slot at offsets time.
 | 
					 * Add new scheduled dispatch slot at offsets time.
 | 
				
			||||||
 * @param offset The offset time to add.
 | 
					 * @param offset The offset time to add.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void OrderList::AddScheduledDispatch(uint32 offset)
 | 
					void DispatchSchedule::AddScheduledDispatch(uint32 offset)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	/* Maintain sorted list status */
 | 
						/* Maintain sorted list status */
 | 
				
			||||||
	auto insert_position = std::lower_bound(this->scheduled_dispatch.begin(), this->scheduled_dispatch.end(), offset);
 | 
						auto insert_position = std::lower_bound(this->scheduled_dispatch.begin(), this->scheduled_dispatch.end(), offset);
 | 
				
			||||||
@@ -329,14 +433,14 @@ void OrderList::AddScheduledDispatch(uint32 offset)
 | 
				
			|||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	this->scheduled_dispatch.insert(insert_position, offset);
 | 
						this->scheduled_dispatch.insert(insert_position, offset);
 | 
				
			||||||
	this->UpdateScheduledDispatch();
 | 
						this->UpdateScheduledDispatch(nullptr);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Remove scheduled dispatch slot at offsets time.
 | 
					 * Remove scheduled dispatch slot at offsets time.
 | 
				
			||||||
 * @param offset The offset time to remove.
 | 
					 * @param offset The offset time to remove.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void OrderList::RemoveScheduledDispatch(uint32 offset)
 | 
					void DispatchSchedule::RemoveScheduledDispatch(uint32 offset)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	/* Maintain sorted list status */
 | 
						/* Maintain sorted list status */
 | 
				
			||||||
	auto erase_position = std::lower_bound(this->scheduled_dispatch.begin(), this->scheduled_dispatch.end(), offset);
 | 
						auto erase_position = std::lower_bound(this->scheduled_dispatch.begin(), this->scheduled_dispatch.end(), offset);
 | 
				
			||||||
@@ -346,14 +450,11 @@ void OrderList::RemoveScheduledDispatch(uint32 offset)
 | 
				
			|||||||
	this->scheduled_dispatch.erase(erase_position);
 | 
						this->scheduled_dispatch.erase(erase_position);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					bool DispatchSchedule::UpdateScheduledDispatchToDate(DateTicksScaled now)
 | 
				
			||||||
 * Update the scheduled dispatch start time to be the most recent possible.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
void OrderList::UpdateScheduledDispatch()
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	bool update_windows = false;
 | 
						bool update_windows = false;
 | 
				
			||||||
	if (this->GetScheduledDispatchStartTick() == 0) {
 | 
						if (this->GetScheduledDispatchStartTick() == 0) {
 | 
				
			||||||
		int64 start = _scaled_date_ticks - (_scaled_date_ticks % this->GetScheduledDispatchDuration());
 | 
							int64 start = now - (now % this->GetScheduledDispatchDuration());
 | 
				
			||||||
		SchdispatchConvertToFullDateFract(
 | 
							SchdispatchConvertToFullDateFract(
 | 
				
			||||||
				start,
 | 
									start,
 | 
				
			||||||
				&this->scheduled_dispatch_start_date, &this->scheduled_dispatch_start_full_date_fract);
 | 
									&this->scheduled_dispatch_start_date, &this->scheduled_dispatch_start_full_date_fract);
 | 
				
			||||||
@@ -367,7 +468,7 @@ void OrderList::UpdateScheduledDispatch()
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	/* Most of the time this loop does not runs. It makes sure start date in in past */
 | 
						/* Most of the time this loop does not runs. It makes sure start date in in past */
 | 
				
			||||||
	while (this->GetScheduledDispatchStartTick() > _scaled_date_ticks) {
 | 
						while (this->GetScheduledDispatchStartTick() > now) {
 | 
				
			||||||
		OverflowSafeInt32 last_dispatch = this->scheduled_dispatch_last_dispatch;
 | 
							OverflowSafeInt32 last_dispatch = this->scheduled_dispatch_last_dispatch;
 | 
				
			||||||
		last_dispatch += this->GetScheduledDispatchDuration();
 | 
							last_dispatch += this->GetScheduledDispatchDuration();
 | 
				
			||||||
		this->scheduled_dispatch_last_dispatch = last_dispatch;
 | 
							this->scheduled_dispatch_last_dispatch = last_dispatch;
 | 
				
			||||||
@@ -377,7 +478,7 @@ void OrderList::UpdateScheduledDispatch()
 | 
				
			|||||||
		update_windows = true;
 | 
							update_windows = true;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	/* Most of the time this loop runs once. It makes sure the start date is as close to current time as possible. */
 | 
						/* Most of the time this loop runs once. It makes sure the start date is as close to current time as possible. */
 | 
				
			||||||
	while (this->GetScheduledDispatchStartTick() + this->GetScheduledDispatchDuration() <= _scaled_date_ticks) {
 | 
						while (this->GetScheduledDispatchStartTick() + this->GetScheduledDispatchDuration() <= now) {
 | 
				
			||||||
		OverflowSafeInt32 last_dispatch = this->scheduled_dispatch_last_dispatch;
 | 
							OverflowSafeInt32 last_dispatch = this->scheduled_dispatch_last_dispatch;
 | 
				
			||||||
		last_dispatch -= this->GetScheduledDispatchDuration();
 | 
							last_dispatch -= this->GetScheduledDispatchDuration();
 | 
				
			||||||
		this->scheduled_dispatch_last_dispatch = last_dispatch;
 | 
							this->scheduled_dispatch_last_dispatch = last_dispatch;
 | 
				
			||||||
@@ -386,47 +487,15 @@ void OrderList::UpdateScheduledDispatch()
 | 
				
			|||||||
				&this->scheduled_dispatch_start_date, &this->scheduled_dispatch_start_full_date_fract);
 | 
									&this->scheduled_dispatch_start_date, &this->scheduled_dispatch_start_full_date_fract);
 | 
				
			||||||
		update_windows = true;
 | 
							update_windows = true;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (update_windows) InvalidateWindowClassesData(WC_SCHDISPATCH_SLOTS, VIWD_MODIFY_ORDERS);
 | 
						return update_windows;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Reset the scheduled dispatch schedule.
 | 
					 * Update the scheduled dispatch start time to be the most recent possible.
 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This only occurs during initialization of the scheduled dispatch for each shared order. Basically we set
 | 
					 | 
				
			||||||
 * proper default value for start time and duration
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void OrderList::ResetScheduledDispatch()
 | 
					void DispatchSchedule::UpdateScheduledDispatch(const Vehicle *v)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	uint32 windex = this->first_shared->index;
 | 
						if (this->UpdateScheduledDispatchToDate(_scaled_date_ticks) && v != nullptr) {
 | 
				
			||||||
 | 
							SetTimetableWindowsDirty(v, true);
 | 
				
			||||||
	Date start_date;
 | 
					 | 
				
			||||||
	uint16 start_full_date_fract;
 | 
					 | 
				
			||||||
	uint32 duration;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (_settings_time.time_in_minutes) {
 | 
					 | 
				
			||||||
		/* Set to 00:00 of today, and 1 day */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		DateTicksScaled val;
 | 
					 | 
				
			||||||
		val = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), 0, 0);
 | 
					 | 
				
			||||||
		val -= _settings_time.clock_offset;
 | 
					 | 
				
			||||||
		val *= _settings_time.ticks_per_minute;
 | 
					 | 
				
			||||||
		SchdispatchConvertToFullDateFract(val, &start_date, &start_full_date_fract);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		duration = 24 * 60 * _settings_time.ticks_per_minute;
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		/* Set Jan 1st and 365 day */
 | 
					 | 
				
			||||||
		start_date = DAYS_TILL(_cur_year);
 | 
					 | 
				
			||||||
		start_full_date_fract = 0;
 | 
					 | 
				
			||||||
		duration = 365*DAY_TICKS;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	DoCommandP(0, windex, duration, CMD_SCHEDULED_DISPATCH_SET_DURATION | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	uint32 p1 = 0, p2 = 0;
 | 
					 | 
				
			||||||
	SB(p1, 0, 20, windex);
 | 
					 | 
				
			||||||
	SB(p1, 20, 12, GB(start_full_date_fract, 2, 12));
 | 
					 | 
				
			||||||
	SB(p2, 0, 30, start_date);
 | 
					 | 
				
			||||||
	SB(p2, 30, 2, GB(start_full_date_fract, 0, 2));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	DoCommandP(0, p1, p2, CMD_SCHEDULED_DISPATCH_SET_START_DATE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,6 +43,11 @@ enum SchdispatchWidgets {
 | 
				
			|||||||
    WID_SCHDISPATCH_SUMMARY_PANEL,   ///< Summary panel
 | 
					    WID_SCHDISPATCH_SUMMARY_PANEL,   ///< Summary panel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	WID_SCHDISPATCH_ENABLED,         ///< Enable button.
 | 
						WID_SCHDISPATCH_ENABLED,         ///< Enable button.
 | 
				
			||||||
 | 
						WID_SCHDISPATCH_HEADER,          ///< Header text.
 | 
				
			||||||
 | 
						WID_SCHDISPATCH_PREV,            ///< Previous schedule.
 | 
				
			||||||
 | 
						WID_SCHDISPATCH_NEXT,            ///< Next schedule.
 | 
				
			||||||
 | 
						WID_SCHDISPATCH_ADD_SCHEDULE,    ///< Add schedule.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	WID_SCHDISPATCH_ADD,             ///< Add Departure Time button
 | 
						WID_SCHDISPATCH_ADD,             ///< Add Departure Time button
 | 
				
			||||||
	WID_SCHDISPATCH_SET_DURATION,    ///< Duration button
 | 
						WID_SCHDISPATCH_SET_DURATION,    ///< Duration button
 | 
				
			||||||
	WID_SCHDISPATCH_SET_START_DATE,  ///< Start Date button
 | 
						WID_SCHDISPATCH_SET_START_DATE,  ///< Start Date button
 | 
				
			||||||
@@ -52,22 +57,16 @@ enum SchdispatchWidgets {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Callback for when a time has been chosen to start the schedule
 | 
					 * Callback for when a time has been chosen to start the schedule
 | 
				
			||||||
 * @param windex The windows index
 | 
					 * @param p1 The p1 parameter to send to CmdScheduledDispatchSetStartDate
 | 
				
			||||||
 * @param date the actually chosen date
 | 
					 * @param date the actually chosen date
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void SetScheduleStartDateIntl(uint32 windex, DateTicksScaled date)
 | 
					static void SetScheduleStartDateIntl(uint32 p1, DateTicksScaled date)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	Date start_date;
 | 
						Date start_date;
 | 
				
			||||||
	uint16 start_full_date_fract;
 | 
						uint16 start_full_date_fract;
 | 
				
			||||||
	SchdispatchConvertToFullDateFract(date, &start_date, &start_full_date_fract);
 | 
						SchdispatchConvertToFullDateFract(date, &start_date, &start_full_date_fract);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	uint32 p1 = 0, p2 = 0;
 | 
						DoCommandPEx(0, p1, start_date, start_full_date_fract, CMD_SCHEDULED_DISPATCH_SET_START_DATE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE), nullptr, nullptr, 0);
 | 
				
			||||||
	SB(p1, 0, 20, windex);
 | 
					 | 
				
			||||||
	SB(p1, 20, 12, GB(start_full_date_fract, 2, 12));
 | 
					 | 
				
			||||||
	SB(p2, 0, 30, start_date);
 | 
					 | 
				
			||||||
	SB(p2, 30, 2, GB(start_full_date_fract, 0, 2));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	DoCommandP(0, p1, p2, CMD_SCHEDULED_DISPATCH_SET_START_DATE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -88,12 +87,15 @@ static void SetScheduleStartDateCallback(const Window *w, DateTicksScaled date)
 | 
				
			|||||||
static void ScheduleAddIntl(uint32 p1, DateTicksScaled date, uint extra_slots, uint offset)
 | 
					static void ScheduleAddIntl(uint32 p1, DateTicksScaled date, uint extra_slots, uint offset)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	VehicleID veh = GB(p1, 0, 20);
 | 
						VehicleID veh = GB(p1, 0, 20);
 | 
				
			||||||
 | 
						uint schedule_index = GB(p1, 20, 12);
 | 
				
			||||||
	Vehicle *v = Vehicle::GetIfValid(veh);
 | 
						Vehicle *v = Vehicle::GetIfValid(veh);
 | 
				
			||||||
	if (v == nullptr || !v->IsPrimaryVehicle()) return;
 | 
						if (v == nullptr || !v->IsPrimaryVehicle() || schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const DispatchSchedule &ds = v->orders.list->GetDispatchScheduleByIndex(schedule_index);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Make sure the time is the closest future to the timetable start */
 | 
						/* Make sure the time is the closest future to the timetable start */
 | 
				
			||||||
	DateTicksScaled start_tick = v->orders.list->GetScheduledDispatchStartTick();
 | 
						DateTicksScaled start_tick = ds.GetScheduledDispatchStartTick();
 | 
				
			||||||
	uint32 duration = v->orders.list->GetScheduledDispatchDuration();
 | 
						uint32 duration = ds.GetScheduledDispatchDuration();
 | 
				
			||||||
	while (date > start_tick) date -= duration;
 | 
						while (date > start_tick) date -= duration;
 | 
				
			||||||
	while (date < start_tick) date += duration;
 | 
						while (date < start_tick) date += duration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -104,7 +106,7 @@ static void ScheduleAddIntl(uint32 p1, DateTicksScaled date, uint extra_slots, u
 | 
				
			|||||||
		extra_slots = std::min<uint>(extra_slots, UINT16_MAX);
 | 
							extra_slots = std::min<uint>(extra_slots, UINT16_MAX);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	DoCommandPEx(0, v->index, (uint32)(date - start_tick), (((uint64)extra_slots) << 32) | offset, CMD_SCHEDULED_DISPATCH_ADD | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE), nullptr, nullptr, 0);
 | 
						DoCommandPEx(0, p1, (uint32)(date - start_tick), (((uint64)extra_slots) << 32) | offset, CMD_SCHEDULED_DISPATCH_ADD | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE), nullptr, nullptr, 0);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -152,8 +154,38 @@ static int CalculateMaxRequiredVehicle(Ticks timetable_duration, uint32 schedule
 | 
				
			|||||||
	return vehicle_count;
 | 
						return vehicle_count;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct SchdispatchWindow : Window {
 | 
					static void AddNewScheduledDispatchSchedule(VehicleID vindex)
 | 
				
			||||||
	const Vehicle *vehicle; ///< Vehicle monitored by the window.
 | 
					{
 | 
				
			||||||
 | 
						Date start_date;
 | 
				
			||||||
 | 
						uint16 start_full_date_fract;
 | 
				
			||||||
 | 
						uint32 duration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (_settings_time.time_in_minutes) {
 | 
				
			||||||
 | 
							/* Set to 00:00 of today, and 1 day */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							DateTicksScaled val;
 | 
				
			||||||
 | 
							val = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), 0, 0);
 | 
				
			||||||
 | 
							val -= _settings_time.clock_offset;
 | 
				
			||||||
 | 
							val *= _settings_time.ticks_per_minute;
 | 
				
			||||||
 | 
							SchdispatchConvertToFullDateFract(val, &start_date, &start_full_date_fract);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							duration = 24 * 60 * _settings_time.ticks_per_minute;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							/* Set Jan 1st and 365 day */
 | 
				
			||||||
 | 
							start_date = DAYS_TILL(_cur_year);
 | 
				
			||||||
 | 
							start_full_date_fract = 0;
 | 
				
			||||||
 | 
							duration = 365 * DAY_TICKS;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint64 p3 = 0;
 | 
				
			||||||
 | 
						SB(p3, 0, 32, start_date);
 | 
				
			||||||
 | 
						SB(p3, 32, 16, start_full_date_fract);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DoCommandPEx(0, vindex, duration, p3, CMD_SCHEDULED_DISPATCH_ADD_NEW_SCHEDULE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE), CcAddNewSchDispatchSchedule, nullptr, 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SchdispatchWindow : GeneralVehicleWindow {
 | 
				
			||||||
 | 
						int schedule_index;
 | 
				
			||||||
	int clicked_widget;     ///< The widget that was clicked (used to determine what to do in OnQueryTextFinished)
 | 
						int clicked_widget;     ///< The widget that was clicked (used to determine what to do in OnQueryTextFinished)
 | 
				
			||||||
	Scrollbar *vscroll;     ///< Verticle scrollbar
 | 
						Scrollbar *vscroll;     ///< Verticle scrollbar
 | 
				
			||||||
	uint num_columns;       ///< Number of columns.
 | 
						uint num_columns;       ///< Number of columns.
 | 
				
			||||||
@@ -161,16 +193,18 @@ struct SchdispatchWindow : Window {
 | 
				
			|||||||
	uint item_count = 0;     ///< Number of scheduled item
 | 
						uint item_count = 0;     ///< Number of scheduled item
 | 
				
			||||||
	bool last_departure_future; ///< True if last departure is currently displayed in the future
 | 
						bool last_departure_future; ///< True if last departure is currently displayed in the future
 | 
				
			||||||
	uint warning_count = 0;
 | 
						uint warning_count = 0;
 | 
				
			||||||
 | 
						bool no_order_warning_pad = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	SchdispatchWindow(WindowDesc *desc, WindowNumber window_number) :
 | 
						SchdispatchWindow(WindowDesc *desc, WindowNumber window_number) :
 | 
				
			||||||
			Window(desc),
 | 
								GeneralVehicleWindow(desc, Vehicle::Get(window_number))
 | 
				
			||||||
			vehicle(Vehicle::Get(window_number))
 | 
					 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		this->CreateNestedTree();
 | 
							this->CreateNestedTree();
 | 
				
			||||||
		this->vscroll = this->GetScrollbar(WID_SCHDISPATCH_V_SCROLL);
 | 
							this->vscroll = this->GetScrollbar(WID_SCHDISPATCH_V_SCROLL);
 | 
				
			||||||
		this->FinishInitNested(window_number);
 | 
							this->FinishInitNested(window_number);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this->owner = this->vehicle->owner;
 | 
							this->owner = this->vehicle->owner;
 | 
				
			||||||
 | 
							this->schedule_index = -1;
 | 
				
			||||||
 | 
							this->AutoSelectSchedule();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	~SchdispatchWindow()
 | 
						~SchdispatchWindow()
 | 
				
			||||||
@@ -188,8 +222,30 @@ struct SchdispatchWindow : Window {
 | 
				
			|||||||
	enum ManagementDropdown {
 | 
						enum ManagementDropdown {
 | 
				
			||||||
		SCH_MD_RESET_LAST_DISPATCHED,
 | 
							SCH_MD_RESET_LAST_DISPATCHED,
 | 
				
			||||||
		SCH_MD_CLEAR_SCHEDULE,
 | 
							SCH_MD_CLEAR_SCHEDULE,
 | 
				
			||||||
 | 
							SCH_MD_REMOVE_SCHEDULE,
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool IsScheduleSelected() const
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return this->vehicle->orders.list != nullptr && this->schedule_index >= 0 && (uint)this->schedule_index < this->vehicle->orders.list->GetScheduledDispatchScheduleCount();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void AutoSelectSchedule()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (!this->IsScheduleSelected()) {
 | 
				
			||||||
 | 
								if (this->vehicle->orders.list != nullptr && this->vehicle->orders.list->GetScheduledDispatchScheduleCount() > 0) {
 | 
				
			||||||
 | 
									this->schedule_index = Clamp<int>(this->schedule_index, 0, this->vehicle->orders.list->GetScheduledDispatchScheduleCount() - 1);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									this->schedule_index = -1;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const DispatchSchedule &GetSelectedSchedule() const
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return this->vehicle->orders.list->GetDispatchScheduleByIndex(this->schedule_index);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
 | 
						virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		switch (widget) {
 | 
							switch (widget) {
 | 
				
			||||||
@@ -217,7 +273,12 @@ struct SchdispatchWindow : Window {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			case WID_SCHDISPATCH_SUMMARY_PANEL:
 | 
								case WID_SCHDISPATCH_SUMMARY_PANEL:
 | 
				
			||||||
				size->height = WD_FRAMERECT_TOP + 5 * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM;
 | 
									size->height = WD_FRAMERECT_TOP + 6 * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM;
 | 
				
			||||||
 | 
									uint warning_count = this->warning_count;
 | 
				
			||||||
 | 
									if (this->no_order_warning_pad) {
 | 
				
			||||||
 | 
										warning_count++;
 | 
				
			||||||
 | 
										size->height -= FONT_HEIGHT_NORMAL;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
				if (warning_count > 0) {
 | 
									if (warning_count > 0) {
 | 
				
			||||||
					const Dimension warning_dimensions = GetSpriteSize(SPR_WARNING_SIGN);
 | 
										const Dimension warning_dimensions = GetSpriteSize(SPR_WARNING_SIGN);
 | 
				
			||||||
					size->height += warning_count * std::max<int>(warning_dimensions.height, FONT_HEIGHT_NORMAL);
 | 
										size->height += warning_count * std::max<int>(warning_dimensions.height, FONT_HEIGHT_NORMAL);
 | 
				
			||||||
@@ -232,8 +293,8 @@ struct SchdispatchWindow : Window {
 | 
				
			|||||||
	void CountItem()
 | 
						void CountItem()
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		this->item_count = 0;
 | 
							this->item_count = 0;
 | 
				
			||||||
		if (this->vehicle->orders.list != nullptr) {
 | 
							if (this->IsScheduleSelected()) {
 | 
				
			||||||
			this->item_count = (uint)this->vehicle->orders.list->GetScheduledDispatch().size();
 | 
								this->item_count = (uint)this->GetSelectedSchedule().GetScheduledDispatch().size();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -247,6 +308,7 @@ struct SchdispatchWindow : Window {
 | 
				
			|||||||
		switch (data) {
 | 
							switch (data) {
 | 
				
			||||||
			case VIWD_MODIFY_ORDERS:
 | 
								case VIWD_MODIFY_ORDERS:
 | 
				
			||||||
				if (!gui_scope) break;
 | 
									if (!gui_scope) break;
 | 
				
			||||||
 | 
									this->AutoSelectSchedule();
 | 
				
			||||||
				this->ReInit();
 | 
									this->ReInit();
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -257,9 +319,15 @@ struct SchdispatchWindow : Window {
 | 
				
			|||||||
		const Vehicle *v = this->vehicle;
 | 
							const Vehicle *v = this->vehicle;
 | 
				
			||||||
		CountItem();
 | 
							CountItem();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this->SetWidgetDisabledState(WID_SCHDISPATCH_ENABLED, (v->owner != _local_company) || HasBit(v->vehicle_flags, VF_TIMETABLE_SEPARATION));
 | 
							bool unusable = (v->owner != _local_company) || (v->orders.list == nullptr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		bool disabled = (v->owner != _local_company) || !HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) || (v->orders.list == nullptr);
 | 
							this->SetWidgetDisabledState(WID_SCHDISPATCH_ENABLED, unusable || HasBit(v->vehicle_flags, VF_TIMETABLE_SEPARATION));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this->SetWidgetDisabledState(WID_SCHDISPATCH_PREV, v->orders.list == nullptr || this->schedule_index <= 0);
 | 
				
			||||||
 | 
							this->SetWidgetDisabledState(WID_SCHDISPATCH_NEXT, v->orders.list == nullptr || this->schedule_index >= (int)(v->orders.list->GetScheduledDispatchScheduleCount() - 1));
 | 
				
			||||||
 | 
							this->SetWidgetDisabledState(WID_SCHDISPATCH_ADD_SCHEDULE, unusable || v->orders.list->GetScheduledDispatchScheduleCount() >= 4096);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							bool disabled = unusable || !HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)  || !this->IsScheduleSelected();
 | 
				
			||||||
		this->SetWidgetDisabledState(WID_SCHDISPATCH_ADD, disabled);
 | 
							this->SetWidgetDisabledState(WID_SCHDISPATCH_ADD, disabled);
 | 
				
			||||||
		this->SetWidgetDisabledState(WID_SCHDISPATCH_SET_DURATION, disabled);
 | 
							this->SetWidgetDisabledState(WID_SCHDISPATCH_SET_DURATION, disabled);
 | 
				
			||||||
		this->SetWidgetDisabledState(WID_SCHDISPATCH_SET_START_DATE, disabled);
 | 
							this->SetWidgetDisabledState(WID_SCHDISPATCH_SET_START_DATE, disabled);
 | 
				
			||||||
@@ -275,7 +343,19 @@ struct SchdispatchWindow : Window {
 | 
				
			|||||||
	virtual void SetStringParameters(int widget) const override
 | 
						virtual void SetStringParameters(int widget) const override
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		switch (widget) {
 | 
							switch (widget) {
 | 
				
			||||||
			case WID_SCHDISPATCH_CAPTION: SetDParam(0, this->vehicle->index); break;
 | 
								case WID_SCHDISPATCH_CAPTION:
 | 
				
			||||||
 | 
									SetDParam(0, this->vehicle->index);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case WID_SCHDISPATCH_HEADER:
 | 
				
			||||||
 | 
									if (this->IsScheduleSelected()) {
 | 
				
			||||||
 | 
										SetDParam(0, STR_SCHDISPATCH_SCHEDULE_ID);
 | 
				
			||||||
 | 
										SetDParam(1, this->schedule_index + 1);
 | 
				
			||||||
 | 
										SetDParam(2, this->vehicle->orders.list->GetScheduledDispatchScheduleCount());
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										SetDParam(0, STR_SCHDISPATCH_NO_SCHEDULES);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -293,10 +373,11 @@ struct SchdispatchWindow : Window {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			case WID_SCHDISPATCH_MANAGEMENT: {
 | 
								case WID_SCHDISPATCH_MANAGEMENT: {
 | 
				
			||||||
				uint64 params[2];
 | 
									uint64 params[3];
 | 
				
			||||||
				params[0] = STR_SCHDISPATCH_RESET_LAST_DISPATCH_TOOLTIP;
 | 
									params[0] = STR_SCHDISPATCH_RESET_LAST_DISPATCH_TOOLTIP;
 | 
				
			||||||
				params[1] = STR_SCHDISPATCH_CLEAR_TOOLTIP;
 | 
									params[1] = STR_SCHDISPATCH_CLEAR_TOOLTIP;
 | 
				
			||||||
				GuiShowTooltips(this, STR_SCHDISPATCH_MANAGE_TOOLTIP, 2, params, close_cond);
 | 
									params[2] = STR_SCHDISPATCH_REMOVE_SCHEDULE_TOOLTIP;
 | 
				
			||||||
 | 
									GuiShowTooltips(this, STR_SCHDISPATCH_MANAGE_TOOLTIP, 3, params, close_cond);
 | 
				
			||||||
				return true;
 | 
									return true;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -332,9 +413,9 @@ struct SchdispatchWindow : Window {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	virtual void OnGameTick() override
 | 
						virtual void OnGameTick() override
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		const Vehicle *v = this->vehicle;
 | 
							if (HasBit(this->vehicle->vehicle_flags, VF_SCHEDULED_DISPATCH) && this->IsScheduleSelected()) {
 | 
				
			||||||
		if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) && v->orders.list != nullptr) {
 | 
								const DispatchSchedule &ds = this->GetSelectedSchedule();
 | 
				
			||||||
			if (((v->orders.list->GetScheduledDispatchStartTick() + v->orders.list->GetScheduledDispatchLastDispatch()) > _scaled_date_ticks) != this->last_departure_future) {
 | 
								if (((ds.GetScheduledDispatchStartTick() + ds.GetScheduledDispatchLastDispatch()) > _scaled_date_ticks) != this->last_departure_future) {
 | 
				
			||||||
				SetWidgetDirty(WID_SCHDISPATCH_SUMMARY_PANEL);
 | 
									SetWidgetDirty(WID_SCHDISPATCH_SUMMARY_PANEL);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -347,7 +428,7 @@ struct SchdispatchWindow : Window {
 | 
				
			|||||||
		switch (widget) {
 | 
							switch (widget) {
 | 
				
			||||||
			case WID_SCHDISPATCH_MATRIX: {
 | 
								case WID_SCHDISPATCH_MATRIX: {
 | 
				
			||||||
				/* If order is not initialized, don't draw */
 | 
									/* If order is not initialized, don't draw */
 | 
				
			||||||
				if (v->orders.list == nullptr) break;
 | 
									if (!this->IsScheduleSelected()) break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				bool rtl = _current_text_dir == TD_RTL;
 | 
									bool rtl = _current_text_dir == TD_RTL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -355,19 +436,21 @@ struct SchdispatchWindow : Window {
 | 
				
			|||||||
				const NWidgetCore *wid = this->GetWidget<NWidgetCore>(WID_SCHDISPATCH_MATRIX);
 | 
									const NWidgetCore *wid = this->GetWidget<NWidgetCore>(WID_SCHDISPATCH_MATRIX);
 | 
				
			||||||
				const uint16 rows_in_display = wid->current_y / wid->resize_y;
 | 
									const uint16 rows_in_display = wid->current_y / wid->resize_y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									const DispatchSchedule &ds = this->GetSelectedSchedule();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				uint num = this->vscroll->GetPosition() * this->num_columns;
 | 
									uint num = this->vscroll->GetPosition() * this->num_columns;
 | 
				
			||||||
				if (num >= v->orders.list->GetScheduledDispatch().size()) break;
 | 
									if (num >= ds.GetScheduledDispatch().size()) break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				const uint maxval = std::min<uint>(this->item_count, num + (rows_in_display * this->num_columns));
 | 
									const uint maxval = std::min<uint>(this->item_count, num + (rows_in_display * this->num_columns));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				auto current_schedule = v->orders.list->GetScheduledDispatch().begin() + num;
 | 
									auto current_schedule = ds.GetScheduledDispatch().begin() + num;
 | 
				
			||||||
				const DateTicksScaled start_tick = v->orders.list->GetScheduledDispatchStartTick();
 | 
									const DateTicksScaled start_tick = ds.GetScheduledDispatchStartTick();
 | 
				
			||||||
				const DateTicksScaled end_tick = v->orders.list->GetScheduledDispatchStartTick() + v->orders.list->GetScheduledDispatchDuration();
 | 
									const DateTicksScaled end_tick = ds.GetScheduledDispatchStartTick() + ds.GetScheduledDispatchDuration();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				for (int y = r.top + 1; num < maxval; y += this->resize.step_height) { /* Draw the rows */
 | 
									for (int y = r.top + 1; num < maxval; y += this->resize.step_height) { /* Draw the rows */
 | 
				
			||||||
					for (byte i = 0; i < this->num_columns && num < maxval; i++, num++) {
 | 
										for (byte i = 0; i < this->num_columns && num < maxval; i++, num++) {
 | 
				
			||||||
						/* Draw all departure time in the current row */
 | 
											/* Draw all departure time in the current row */
 | 
				
			||||||
						if (current_schedule != v->orders.list->GetScheduledDispatch().end()) {
 | 
											if (current_schedule != ds.GetScheduledDispatch().end()) {
 | 
				
			||||||
							int x = r.left + (rtl ? (this->num_columns - i - 1) : i) * this->resize.step_width;
 | 
												int x = r.left + (rtl ? (this->num_columns - i - 1) : i) * this->resize.step_width;
 | 
				
			||||||
							DateTicksScaled draw_time = start_tick + *current_schedule;
 | 
												DateTicksScaled draw_time = start_tick + *current_schedule;
 | 
				
			||||||
							this->DrawScheduledTime(draw_time, x, x + this->resize.step_width - 1, y, draw_time >= end_tick ? TC_RED : TC_BLACK);
 | 
												this->DrawScheduledTime(draw_time, x, x + this->resize.step_width - 1, y, draw_time >= end_tick ? TC_RED : TC_BLACK);
 | 
				
			||||||
@@ -381,51 +464,14 @@ struct SchdispatchWindow : Window {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			case WID_SCHDISPATCH_SUMMARY_PANEL: {
 | 
								case WID_SCHDISPATCH_SUMMARY_PANEL: {
 | 
				
			||||||
 | 
					 | 
				
			||||||
				int y = r.top + WD_FRAMERECT_TOP;
 | 
									int y = r.top + WD_FRAMERECT_TOP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (!HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) || v->orders.list == nullptr) {
 | 
									if (!HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) || !this->IsScheduleSelected()) {
 | 
				
			||||||
					y += FONT_HEIGHT_NORMAL;
 | 
										y += FONT_HEIGHT_NORMAL;
 | 
				
			||||||
					DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SCHDISPATCH_SUMMARY_NOT_ENABLED);
 | 
										DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SCHDISPATCH_SUMMARY_NOT_ENABLED);
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
 | 
										const DispatchSchedule &ds = this->GetSelectedSchedule();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					const DateTicksScaled last_departure = v->orders.list->GetScheduledDispatchStartTick() + v->orders.list->GetScheduledDispatchLastDispatch();
 | 
					 | 
				
			||||||
					SetDParam(0, last_departure);
 | 
					 | 
				
			||||||
					const_cast<SchdispatchWindow*>(this)->last_departure_future = (last_departure > _scaled_date_ticks);
 | 
					 | 
				
			||||||
					DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y,
 | 
					 | 
				
			||||||
							this->last_departure_future ? STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_FUTURE : STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_PAST);
 | 
					 | 
				
			||||||
					y += FONT_HEIGHT_NORMAL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					bool have_conditional = false;
 | 
					 | 
				
			||||||
					for (int n = 0; n < v->GetNumOrders(); n++) {
 | 
					 | 
				
			||||||
						const Order *order = v->GetOrder(n);
 | 
					 | 
				
			||||||
						if (order->IsType(OT_CONDITIONAL)) {
 | 
					 | 
				
			||||||
							have_conditional = true;
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					if (!have_conditional) {
 | 
					 | 
				
			||||||
						const int required_vehicle = CalculateMaxRequiredVehicle(v->orders.list->GetTimetableTotalDuration(), v->orders.list->GetScheduledDispatchDuration(), v->orders.list->GetScheduledDispatch());
 | 
					 | 
				
			||||||
						if (required_vehicle > 0) {
 | 
					 | 
				
			||||||
							SetDParam(0, required_vehicle);
 | 
					 | 
				
			||||||
							DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SCHDISPATCH_SUMMARY_L1);
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					y += FONT_HEIGHT_NORMAL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					SetTimetableParams(0, v->orders.list->GetScheduledDispatchDuration(), true);
 | 
					 | 
				
			||||||
					DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SCHDISPATCH_SUMMARY_L2);
 | 
					 | 
				
			||||||
					y += FONT_HEIGHT_NORMAL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					SetDParam(0, v->orders.list->GetScheduledDispatchStartTick());
 | 
					 | 
				
			||||||
					SetDParam(1, v->orders.list->GetScheduledDispatchStartTick() + v->orders.list->GetScheduledDispatchDuration());
 | 
					 | 
				
			||||||
					DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SCHDISPATCH_SUMMARY_L3);
 | 
					 | 
				
			||||||
					y += FONT_HEIGHT_NORMAL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					SetTimetableParams(0, v->orders.list->GetScheduledDispatchDelay());
 | 
					 | 
				
			||||||
					DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SCHDISPATCH_SUMMARY_L4);
 | 
					 | 
				
			||||||
					y += FONT_HEIGHT_NORMAL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					uint warnings = 0;
 | 
					 | 
				
			||||||
					auto draw_warning = [&](StringID text) {
 | 
										auto draw_warning = [&](StringID text) {
 | 
				
			||||||
						const Dimension warning_dimensions = GetSpriteSize(SPR_WARNING_SIGN);
 | 
											const Dimension warning_dimensions = GetSpriteSize(SPR_WARNING_SIGN);
 | 
				
			||||||
						int step_height = std::max<int>(warning_dimensions.height, FONT_HEIGHT_NORMAL);
 | 
											int step_height = std::max<int>(warning_dimensions.height, FONT_HEIGHT_NORMAL);
 | 
				
			||||||
@@ -440,20 +486,95 @@ struct SchdispatchWindow : Window {
 | 
				
			|||||||
						}
 | 
											}
 | 
				
			||||||
						DrawString(left, right, y + (step_height - FONT_HEIGHT_NORMAL) / 2, text);
 | 
											DrawString(left, right, y + (step_height - FONT_HEIGHT_NORMAL) / 2, text);
 | 
				
			||||||
						y += step_height;
 | 
											y += step_height;
 | 
				
			||||||
						warnings++;
 | 
					 | 
				
			||||||
					};
 | 
										};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					uint32 duration = v->orders.list->GetScheduledDispatchDuration();
 | 
										bool have_conditional = false;
 | 
				
			||||||
					for (uint32 slot : v->orders.list->GetScheduledDispatch()) {
 | 
										int schedule_order_index = -1;
 | 
				
			||||||
 | 
										for (int n = 0; n < v->GetNumOrders(); n++) {
 | 
				
			||||||
 | 
											const Order *order = v->GetOrder(n);
 | 
				
			||||||
 | 
											if (order->IsType(OT_CONDITIONAL)) {
 | 
				
			||||||
 | 
												have_conditional = true;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											if (order->GetDispatchScheduleIndex() == this->schedule_index) {
 | 
				
			||||||
 | 
												schedule_order_index = n;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										bool no_order_warning_pad = false;
 | 
				
			||||||
 | 
										if (schedule_order_index < 0) {
 | 
				
			||||||
 | 
											draw_warning(STR_SCHDISPATCH_NOT_ASSIGNED_TO_ORDER);
 | 
				
			||||||
 | 
											no_order_warning_pad = true;
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											const Order *order = v->GetOrder(schedule_order_index);
 | 
				
			||||||
 | 
											SetDParam(0, schedule_order_index + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											switch (order->GetType()) {
 | 
				
			||||||
 | 
												case OT_GOTO_STATION:
 | 
				
			||||||
 | 
													SetDParam(1, STR_STATION_NAME);
 | 
				
			||||||
 | 
													SetDParam(2, order->GetDestination());
 | 
				
			||||||
 | 
													break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												case OT_GOTO_WAYPOINT:
 | 
				
			||||||
 | 
													SetDParam(1, STR_WAYPOINT_NAME);
 | 
				
			||||||
 | 
													SetDParam(2, order->GetDestination());
 | 
				
			||||||
 | 
													break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												case OT_GOTO_DEPOT:
 | 
				
			||||||
 | 
													SetDParam(1, STR_DEPOT_NAME);
 | 
				
			||||||
 | 
													SetDParam(2, order->GetDestination());
 | 
				
			||||||
 | 
													break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
												default:
 | 
				
			||||||
 | 
													SetDParam(1, STR_INVALID_ORDER);
 | 
				
			||||||
 | 
													break;
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SCHDISPATCH_ASSIGNED_TO_ORDER);
 | 
				
			||||||
 | 
											y += FONT_HEIGHT_NORMAL;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										const DateTicksScaled last_departure = ds.GetScheduledDispatchStartTick() + ds.GetScheduledDispatchLastDispatch();
 | 
				
			||||||
 | 
										SetDParam(0, last_departure);
 | 
				
			||||||
 | 
										const_cast<SchdispatchWindow*>(this)->last_departure_future = (last_departure > _scaled_date_ticks);
 | 
				
			||||||
 | 
										DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y,
 | 
				
			||||||
 | 
												this->last_departure_future ? STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_FUTURE : STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_PAST);
 | 
				
			||||||
 | 
										y += FONT_HEIGHT_NORMAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if (!have_conditional) {
 | 
				
			||||||
 | 
											const int required_vehicle = CalculateMaxRequiredVehicle(v->orders.list->GetTimetableTotalDuration(), ds.GetScheduledDispatchDuration(), ds.GetScheduledDispatch());
 | 
				
			||||||
 | 
											if (required_vehicle > 0) {
 | 
				
			||||||
 | 
												SetDParam(0, required_vehicle);
 | 
				
			||||||
 | 
												DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SCHDISPATCH_SUMMARY_L1);
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										y += FONT_HEIGHT_NORMAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										SetTimetableParams(0, ds.GetScheduledDispatchDuration(), true);
 | 
				
			||||||
 | 
										DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SCHDISPATCH_SUMMARY_L2);
 | 
				
			||||||
 | 
										y += FONT_HEIGHT_NORMAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										SetDParam(0, ds.GetScheduledDispatchStartTick());
 | 
				
			||||||
 | 
										SetDParam(1, ds.GetScheduledDispatchStartTick() + ds.GetScheduledDispatchDuration());
 | 
				
			||||||
 | 
										DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SCHDISPATCH_SUMMARY_L3);
 | 
				
			||||||
 | 
										y += FONT_HEIGHT_NORMAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										SetTimetableParams(0, ds.GetScheduledDispatchDelay());
 | 
				
			||||||
 | 
										DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SCHDISPATCH_SUMMARY_L4);
 | 
				
			||||||
 | 
										y += FONT_HEIGHT_NORMAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										uint32 duration = ds.GetScheduledDispatchDuration();
 | 
				
			||||||
 | 
										uint warnings = 0;
 | 
				
			||||||
 | 
										for (uint32 slot : ds.GetScheduledDispatch()) {
 | 
				
			||||||
						if (slot >= duration) {
 | 
											if (slot >= duration) {
 | 
				
			||||||
							draw_warning(STR_SCHDISPATCH_SLOT_OUTSIDE_SCHEDULE);
 | 
												draw_warning(STR_SCHDISPATCH_SLOT_OUTSIDE_SCHEDULE);
 | 
				
			||||||
 | 
												warnings++;
 | 
				
			||||||
							break;
 | 
												break;
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					if (warnings != this->warning_count) {
 | 
										if (warnings != this->warning_count || no_order_warning_pad != this->no_order_warning_pad) {
 | 
				
			||||||
						SchdispatchWindow *mutable_this = const_cast<SchdispatchWindow *>(this);
 | 
											SchdispatchWindow *mutable_this = const_cast<SchdispatchWindow *>(this);
 | 
				
			||||||
						mutable_this->warning_count = warnings;
 | 
											mutable_this->warning_count = warnings;
 | 
				
			||||||
 | 
											mutable_this->no_order_warning_pad = no_order_warning_pad;
 | 
				
			||||||
						mutable_this->ReInit();
 | 
											mutable_this->ReInit();
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
@@ -470,6 +591,8 @@ struct SchdispatchWindow : Window {
 | 
				
			|||||||
	 */
 | 
						 */
 | 
				
			||||||
	void TimeClick(int x, int y)
 | 
						void TimeClick(int x, int y)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 | 
							if (!this->IsScheduleSelected()) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		const NWidgetCore *matrix_widget = this->GetWidget<NWidgetCore>(WID_SCHDISPATCH_MATRIX);
 | 
							const NWidgetCore *matrix_widget = this->GetWidget<NWidgetCore>(WID_SCHDISPATCH_MATRIX);
 | 
				
			||||||
		/* In case of RTL the widgets are swapped as a whole */
 | 
							/* In case of RTL the widgets are swapped as a whole */
 | 
				
			||||||
		if (_current_text_dir == TD_RTL) x = matrix_widget->current_x - x;
 | 
							if (_current_text_dir == TD_RTL) x = matrix_widget->current_x - x;
 | 
				
			||||||
@@ -484,10 +607,12 @@ struct SchdispatchWindow : Window {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		uint pos = ((row + this->vscroll->GetPosition()) * this->num_columns) + xt;
 | 
							uint pos = ((row + this->vscroll->GetPosition()) * this->num_columns) + xt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (pos >= this->item_count) return;
 | 
							const DispatchSchedule &ds = this->GetSelectedSchedule();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (pos >= this->item_count || pos >= ds.GetScheduledDispatch().size()) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (xm <= this->header_width) {
 | 
							if (xm <= this->header_width) {
 | 
				
			||||||
			DoCommandP(0, this->vehicle->index, this->vehicle->orders.list->GetScheduledDispatch()[pos], CMD_SCHEDULED_DISPATCH_REMOVE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
 | 
								DoCommandP(0, this->vehicle->index | (this->schedule_index << 20), ds.GetScheduledDispatch()[pos], CMD_SCHEDULED_DISPATCH_REMOVE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -522,30 +647,35 @@ struct SchdispatchWindow : Window {
 | 
				
			|||||||
				uint32 p2 = 0;
 | 
									uint32 p2 = 0;
 | 
				
			||||||
				if (!HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)) SetBit(p2, 0);
 | 
									if (!HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)) SetBit(p2, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (!v->orders.list->IsScheduledDispatchValid()) v->orders.list->ResetScheduledDispatch();
 | 
					 | 
				
			||||||
				DoCommandP(0, v->index, p2, CMD_SCHEDULED_DISPATCH | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
 | 
									DoCommandP(0, v->index, p2, CMD_SCHEDULED_DISPATCH | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
 | 
				
			||||||
 | 
									if (HasBit(p2, 0) && this->vehicle->orders.list != nullptr && this->vehicle->orders.list->GetScheduledDispatchScheduleCount() == 0) {
 | 
				
			||||||
 | 
										AddNewScheduledDispatchSchedule(v->index);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			case WID_SCHDISPATCH_ADD: {
 | 
								case WID_SCHDISPATCH_ADD: {
 | 
				
			||||||
 | 
									if (!this->IsScheduleSelected()) break;
 | 
				
			||||||
				if (_settings_time.time_in_minutes && _ctrl_pressed) {
 | 
									if (_settings_time.time_in_minutes && _ctrl_pressed) {
 | 
				
			||||||
					void ShowScheduledDispatchAddSlotsWindow(SchdispatchWindow *parent, int window_number);
 | 
										void ShowScheduledDispatchAddSlotsWindow(SchdispatchWindow *parent, int window_number);
 | 
				
			||||||
					ShowScheduledDispatchAddSlotsWindow(this, v->index);
 | 
										ShowScheduledDispatchAddSlotsWindow(this, v->index);
 | 
				
			||||||
				} else if (_settings_time.time_in_minutes && _settings_client.gui.timetable_start_text_entry) {
 | 
									} else if (_settings_time.time_in_minutes && _settings_client.gui.timetable_start_text_entry) {
 | 
				
			||||||
					ShowQueryString(STR_EMPTY, STR_SCHDISPATCH_ADD_CAPTION, 31, this, CS_NUMERAL, QSF_NONE);
 | 
										ShowQueryString(STR_EMPTY, STR_SCHDISPATCH_ADD_CAPTION, 31, this, CS_NUMERAL, QSF_NONE);
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					ShowSetDateWindow(this, v->index, _scaled_date_ticks, _cur_year, _cur_year + 15, ScheduleAddCallback, STR_SCHDISPATCH_ADD, STR_SCHDISPATCH_ADD_TOOLTIP);
 | 
										ShowSetDateWindow(this, v->index | (this->schedule_index << 20), _scaled_date_ticks, _cur_year, _cur_year + 15, ScheduleAddCallback, STR_SCHDISPATCH_ADD, STR_SCHDISPATCH_ADD_TOOLTIP);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			case WID_SCHDISPATCH_SET_DURATION: {
 | 
								case WID_SCHDISPATCH_SET_DURATION: {
 | 
				
			||||||
				SetDParam(0, ProcessDurationForQueryString(v->orders.list->GetScheduledDispatchDuration()));
 | 
									if (!this->IsScheduleSelected()) break;
 | 
				
			||||||
 | 
									SetDParam(0, ProcessDurationForQueryString(this->GetSelectedSchedule().GetScheduledDispatchDuration()));
 | 
				
			||||||
				ShowQueryString(STR_JUST_INT, STR_SCHDISPATCH_DURATION_CAPTION_MINUTE + this->GetQueryStringCaptionOffset(), 31, this, CS_NUMERAL, QSF_NONE);
 | 
									ShowQueryString(STR_JUST_INT, STR_SCHDISPATCH_DURATION_CAPTION_MINUTE + this->GetQueryStringCaptionOffset(), 31, this, CS_NUMERAL, QSF_NONE);
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			case WID_SCHDISPATCH_SET_START_DATE: {
 | 
								case WID_SCHDISPATCH_SET_START_DATE: {
 | 
				
			||||||
 | 
									if (!this->IsScheduleSelected()) break;
 | 
				
			||||||
				if (_settings_time.time_in_minutes && _settings_client.gui.timetable_start_text_entry) {
 | 
									if (_settings_time.time_in_minutes && _settings_client.gui.timetable_start_text_entry) {
 | 
				
			||||||
					uint64 time = _scaled_date_ticks;
 | 
										uint64 time = _scaled_date_ticks;
 | 
				
			||||||
					time /= _settings_time.ticks_per_minute;
 | 
										time /= _settings_time.ticks_per_minute;
 | 
				
			||||||
@@ -555,40 +685,88 @@ struct SchdispatchWindow : Window {
 | 
				
			|||||||
					SetDParam(0, time);
 | 
										SetDParam(0, time);
 | 
				
			||||||
					ShowQueryString(STR_JUST_INT, STR_SCHDISPATCH_START_CAPTION_MINUTE, 31, this, CS_NUMERAL, QSF_ACCEPT_UNCHANGED);
 | 
										ShowQueryString(STR_JUST_INT, STR_SCHDISPATCH_START_CAPTION_MINUTE, 31, this, CS_NUMERAL, QSF_ACCEPT_UNCHANGED);
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					ShowSetDateWindow(this, v->index, _scaled_date_ticks, _cur_year, _cur_year + 15, SetScheduleStartDateCallback, STR_SCHDISPATCH_SET_START, STR_SCHDISPATCH_START_TOOLTIP);
 | 
										ShowSetDateWindow(this, v->index | (this->schedule_index << 20), _scaled_date_ticks, _cur_year, _cur_year + 15, SetScheduleStartDateCallback, STR_SCHDISPATCH_SET_START, STR_SCHDISPATCH_START_TOOLTIP);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			case WID_SCHDISPATCH_SET_DELAY: {
 | 
								case WID_SCHDISPATCH_SET_DELAY: {
 | 
				
			||||||
				SetDParam(0, ProcessDurationForQueryString(v->orders.list->GetScheduledDispatchDelay()));
 | 
									if (!this->IsScheduleSelected()) break;
 | 
				
			||||||
 | 
									SetDParam(0, ProcessDurationForQueryString(this->GetSelectedSchedule().GetScheduledDispatchDelay()));
 | 
				
			||||||
				ShowQueryString(STR_JUST_INT, STR_SCHDISPATCH_DELAY_CAPTION_MINUTE + this->GetQueryStringCaptionOffset(), 31, this, CS_NUMERAL, QSF_NONE);
 | 
									ShowQueryString(STR_JUST_INT, STR_SCHDISPATCH_DELAY_CAPTION_MINUTE + this->GetQueryStringCaptionOffset(), 31, this, CS_NUMERAL, QSF_NONE);
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			case WID_SCHDISPATCH_MANAGEMENT: {
 | 
								case WID_SCHDISPATCH_MANAGEMENT: {
 | 
				
			||||||
 | 
									if (!this->IsScheduleSelected()) break;
 | 
				
			||||||
				DropDownList list;
 | 
									DropDownList list;
 | 
				
			||||||
				list.emplace_back(new DropDownListStringItem(STR_SCHDISPATCH_RESET_LAST_DISPATCH, SCH_MD_RESET_LAST_DISPATCHED, false));
 | 
									list.emplace_back(new DropDownListStringItem(STR_SCHDISPATCH_RESET_LAST_DISPATCH, SCH_MD_RESET_LAST_DISPATCHED, false));
 | 
				
			||||||
				list.emplace_back(new DropDownListStringItem(STR_SCHDISPATCH_CLEAR, SCH_MD_CLEAR_SCHEDULE, false));
 | 
									list.emplace_back(new DropDownListStringItem(STR_SCHDISPATCH_CLEAR, SCH_MD_CLEAR_SCHEDULE, false));
 | 
				
			||||||
 | 
									list.emplace_back(new DropDownListStringItem(STR_SCHDISPATCH_REMOVE_SCHEDULE, SCH_MD_REMOVE_SCHEDULE, false));
 | 
				
			||||||
				ShowDropDownList(this, std::move(list), -1, WID_SCHDISPATCH_MANAGEMENT);
 | 
									ShowDropDownList(this, std::move(list), -1, WID_SCHDISPATCH_MANAGEMENT);
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case WID_SCHDISPATCH_PREV:
 | 
				
			||||||
 | 
									if (!this->IsScheduleSelected()) break;
 | 
				
			||||||
 | 
									if (this->schedule_index > 0) this->schedule_index--;
 | 
				
			||||||
 | 
									this->ReInit();
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case WID_SCHDISPATCH_NEXT:
 | 
				
			||||||
 | 
									if (!this->IsScheduleSelected()) break;
 | 
				
			||||||
 | 
									if (this->schedule_index < (int)(this->vehicle->orders.list->GetScheduledDispatchScheduleCount() - 1)) this->schedule_index++;
 | 
				
			||||||
 | 
									this->ReInit();
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case WID_SCHDISPATCH_ADD_SCHEDULE:
 | 
				
			||||||
 | 
									AddNewScheduledDispatchSchedule(this->vehicle->index);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this->SetDirty();
 | 
							this->SetDirty();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static void ClearScheduleCallback(Window *win, bool confirmed)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (confirmed) {
 | 
				
			||||||
 | 
								SchdispatchWindow *w = (SchdispatchWindow*)win;
 | 
				
			||||||
 | 
								if (w->IsScheduleSelected()) {
 | 
				
			||||||
 | 
									DoCommandP(0, w->vehicle->index | (w->schedule_index << 20), 0, CMD_SCHEDULED_DISPATCH_CLEAR | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static void RemoveScheduleCallback(Window *win, bool confirmed)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (confirmed) {
 | 
				
			||||||
 | 
								SchdispatchWindow *w = (SchdispatchWindow*)win;
 | 
				
			||||||
 | 
								if (w->IsScheduleSelected()) {
 | 
				
			||||||
 | 
									DoCommandP(0, w->vehicle->index | (w->schedule_index << 20), 0, CMD_SCHEDULED_DISPATCH_REMOVE_SCHEDULE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void OnDropdownSelect(int widget, int index) override
 | 
						void OnDropdownSelect(int widget, int index) override
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		switch (widget) {
 | 
							switch (widget) {
 | 
				
			||||||
			case WID_SCHDISPATCH_MANAGEMENT: {
 | 
								case WID_SCHDISPATCH_MANAGEMENT: {
 | 
				
			||||||
 | 
									if (!this->IsScheduleSelected()) break;
 | 
				
			||||||
				switch((ManagementDropdown)index) {
 | 
									switch((ManagementDropdown)index) {
 | 
				
			||||||
					case SCH_MD_RESET_LAST_DISPATCHED:
 | 
										case SCH_MD_RESET_LAST_DISPATCHED:
 | 
				
			||||||
						DoCommandP(0, this->vehicle->index, 0, CMD_SCHEDULED_DISPATCH_RESET_LAST_DISPATCH | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
 | 
											DoCommandP(0, this->vehicle->index | (this->schedule_index << 20), 0, CMD_SCHEDULED_DISPATCH_RESET_LAST_DISPATCH | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
 | 
				
			||||||
						break;
 | 
											break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					case SCH_MD_CLEAR_SCHEDULE:
 | 
										case SCH_MD_CLEAR_SCHEDULE:
 | 
				
			||||||
						DoCommandP(0, this->vehicle->index, 0, CMD_SCHEDULED_DISPATCH_CLEAR | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
 | 
											if (this->GetSelectedSchedule().GetScheduledDispatch().empty()) return;
 | 
				
			||||||
 | 
											SetDParam(0, (uint)this->GetSelectedSchedule().GetScheduledDispatch().size());
 | 
				
			||||||
 | 
											ShowQuery(STR_SCHDISPATCH_QUERY_CLEAR_SCHEDULE_CAPTION, STR_SCHDISPATCH_QUERY_CLEAR_SCHEDULE_TEXT, this, ClearScheduleCallback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										case SCH_MD_REMOVE_SCHEDULE:
 | 
				
			||||||
 | 
											SetDParam(0, (uint)this->GetSelectedSchedule().GetScheduledDispatch().size());
 | 
				
			||||||
 | 
											ShowQuery(STR_SCHDISPATCH_QUERY_REMOVE_SCHEDULE_CAPTION, STR_SCHDISPATCH_QUERY_REMOVE_SCHEDULE_TEXT, this, RemoveScheduleCallback);
 | 
				
			||||||
						break;
 | 
											break;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -607,6 +785,7 @@ struct SchdispatchWindow : Window {
 | 
				
			|||||||
			default: NOT_REACHED();
 | 
								default: NOT_REACHED();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			case WID_SCHDISPATCH_ADD: {
 | 
								case WID_SCHDISPATCH_ADD: {
 | 
				
			||||||
 | 
									if (!this->IsScheduleSelected()) break;
 | 
				
			||||||
				char *end;
 | 
									char *end;
 | 
				
			||||||
				int32 val = StrEmpty(str) ? -1 : strtoul(str, &end, 10);
 | 
									int32 val = StrEmpty(str) ? -1 : strtoul(str, &end, 10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -616,12 +795,13 @@ struct SchdispatchWindow : Window {
 | 
				
			|||||||
					DateTicksScaled slot = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), hours, minutes);
 | 
										DateTicksScaled slot = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), hours, minutes);
 | 
				
			||||||
					slot -= _settings_time.clock_offset;
 | 
										slot -= _settings_time.clock_offset;
 | 
				
			||||||
					slot *= _settings_time.ticks_per_minute;
 | 
										slot *= _settings_time.ticks_per_minute;
 | 
				
			||||||
					ScheduleAddIntl(v->index, slot, 0, 0);
 | 
										ScheduleAddIntl(v->index | (this->schedule_index << 20), slot, 0, 0);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			case WID_SCHDISPATCH_SET_START_DATE: {
 | 
								case WID_SCHDISPATCH_SET_START_DATE: {
 | 
				
			||||||
 | 
									if (!this->IsScheduleSelected()) break;
 | 
				
			||||||
				char *end;
 | 
									char *end;
 | 
				
			||||||
				int32 val = StrEmpty(str) ? -1 : strtoul(str, &end, 10);
 | 
									int32 val = StrEmpty(str) ? -1 : strtoul(str, &end, 10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -631,30 +811,32 @@ struct SchdispatchWindow : Window {
 | 
				
			|||||||
					DateTicksScaled start = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), hours, minutes);
 | 
										DateTicksScaled start = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), hours, minutes);
 | 
				
			||||||
					start -= _settings_time.clock_offset;
 | 
										start -= _settings_time.clock_offset;
 | 
				
			||||||
					start *= _settings_time.ticks_per_minute;
 | 
										start *= _settings_time.ticks_per_minute;
 | 
				
			||||||
					SetScheduleStartDateIntl(v->index, start);
 | 
										SetScheduleStartDateIntl(v->index | (this->schedule_index << 20), start);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			case WID_SCHDISPATCH_SET_DURATION: {
 | 
								case WID_SCHDISPATCH_SET_DURATION: {
 | 
				
			||||||
 | 
									if (!this->IsScheduleSelected()) break;
 | 
				
			||||||
				int32 val = StrEmpty(str) ? 0 : strtoul(str, nullptr, 10);
 | 
									int32 val = StrEmpty(str) ? 0 : strtoul(str, nullptr, 10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (val > 0) {
 | 
									if (val > 0) {
 | 
				
			||||||
					if (!_settings_client.gui.timetable_in_ticks) val *= DATE_UNIT_SIZE;
 | 
										if (!_settings_client.gui.timetable_in_ticks) val *= DATE_UNIT_SIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					DoCommandP(0, v->index, val, CMD_SCHEDULED_DISPATCH_SET_DURATION | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
 | 
										DoCommandP(0, v->index | (this->schedule_index << 20), val, CMD_SCHEDULED_DISPATCH_SET_DURATION | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			case WID_SCHDISPATCH_SET_DELAY: {
 | 
								case WID_SCHDISPATCH_SET_DELAY: {
 | 
				
			||||||
 | 
									if (!this->IsScheduleSelected()) break;
 | 
				
			||||||
				char *end;
 | 
									char *end;
 | 
				
			||||||
				int32 val = StrEmpty(str) ? -1 : strtoul(str, &end, 10);
 | 
									int32 val = StrEmpty(str) ? -1 : strtoul(str, &end, 10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if (val >= 0 && end && *end == 0) {
 | 
									if (val >= 0 && end && *end == 0) {
 | 
				
			||||||
					if (!_settings_client.gui.timetable_in_ticks) val *= DATE_UNIT_SIZE;
 | 
										if (!_settings_client.gui.timetable_in_ticks) val *= DATE_UNIT_SIZE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					DoCommandP(0, v->index, val, CMD_SCHEDULED_DISPATCH_SET_DELAY | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
 | 
										DoCommandP(0, v->index | (this->schedule_index << 20), val, CMD_SCHEDULED_DISPATCH_SET_DELAY | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -685,15 +867,25 @@ struct SchdispatchWindow : Window {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	void AddMultipleDepartureSlots(uint start, uint step, uint end)
 | 
						void AddMultipleDepartureSlots(uint start, uint step, uint end)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (end < start || step == 0) return;
 | 
							if (end < start || step == 0 || !this->IsScheduleSelected()) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		DateTicksScaled slot = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), 0, start);
 | 
							DateTicksScaled slot = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), 0, start);
 | 
				
			||||||
		slot -= _settings_time.clock_offset;
 | 
							slot -= _settings_time.clock_offset;
 | 
				
			||||||
		slot *= _settings_time.ticks_per_minute;
 | 
							slot *= _settings_time.ticks_per_minute;
 | 
				
			||||||
		ScheduleAddIntl(this->vehicle->index, slot, (end - start) / step, step * _settings_time.ticks_per_minute);
 | 
							ScheduleAddIntl(this->vehicle->index | (this->schedule_index << 20), slot, (end - start) / step, step * _settings_time.ticks_per_minute);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void CcAddNewSchDispatchSchedule(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2, uint64 p3, uint32 cmd)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						SchdispatchWindow *w = dynamic_cast<SchdispatchWindow*>(FindWindowById(WC_SCHDISPATCH_SLOTS, p1));
 | 
				
			||||||
 | 
						if (w != nullptr) {
 | 
				
			||||||
 | 
							w->schedule_index = INT_MAX;
 | 
				
			||||||
 | 
							w->AutoSelectSchedule();
 | 
				
			||||||
 | 
							w->ReInit();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const NWidgetPart _nested_schdispatch_widgets[] = {
 | 
					static const NWidgetPart _nested_schdispatch_widgets[] = {
 | 
				
			||||||
	NWidget(NWID_HORIZONTAL),
 | 
						NWidget(NWID_HORIZONTAL),
 | 
				
			||||||
		NWidget(WWT_CLOSEBOX, COLOUR_GREY),
 | 
							NWidget(WWT_CLOSEBOX, COLOUR_GREY),
 | 
				
			||||||
@@ -702,6 +894,16 @@ static const NWidgetPart _nested_schdispatch_widgets[] = {
 | 
				
			|||||||
		NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
 | 
							NWidget(WWT_DEFSIZEBOX, COLOUR_GREY),
 | 
				
			||||||
		NWidget(WWT_STICKYBOX, COLOUR_GREY),
 | 
							NWidget(WWT_STICKYBOX, COLOUR_GREY),
 | 
				
			||||||
	EndContainer(),
 | 
						EndContainer(),
 | 
				
			||||||
 | 
						NWidget(WWT_PANEL, COLOUR_GREY),
 | 
				
			||||||
 | 
							NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
 | 
				
			||||||
 | 
								NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_ENABLED), SetDataTip(STR_SCHDISPATCH_ENABLED, STR_SCHDISPATCH_ENABLED_TOOLTIP), SetFill(1, 1), SetResize(1, 0),
 | 
				
			||||||
 | 
								NWidget(WWT_TEXT, COLOUR_GREY, WID_SCHDISPATCH_HEADER), SetAlignment(SA_CENTER), SetDataTip(STR_JUST_STRING2, STR_NULL), SetFill(1, 1), SetResize(1, 0),
 | 
				
			||||||
 | 
								NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
 | 
				
			||||||
 | 
									NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_PREV), SetDataTip(STR_SCHDISPATCH_PREV_SCHEDULE, STR_SCHDISPATCH_PREV_SCHEDULE_TOOLTIP), SetFill(1, 1), SetResize(1, 0),
 | 
				
			||||||
 | 
									NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_NEXT), SetDataTip(STR_SCHDISPATCH_NEXT_SCHEDULE, STR_SCHDISPATCH_NEXT_SCHEDULE_TOOLTIP), SetFill(1, 1), SetResize(1, 0),
 | 
				
			||||||
 | 
									NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_ADD_SCHEDULE), SetDataTip(STR_SCHDISPATCH_ADD_SCHEDULE, STR_SCHDISPATCH_ADD_SCHEDULE_TOOLTIP), SetFill(1, 1), SetResize(1, 0),
 | 
				
			||||||
 | 
								EndContainer(),
 | 
				
			||||||
 | 
							EndContainer(),
 | 
				
			||||||
		NWidget(NWID_HORIZONTAL),
 | 
							NWidget(NWID_HORIZONTAL),
 | 
				
			||||||
			NWidget(WWT_MATRIX, COLOUR_GREY, WID_SCHDISPATCH_MATRIX), SetResize(1, 1), SetScrollbar(WID_SCHDISPATCH_V_SCROLL),
 | 
								NWidget(WWT_MATRIX, COLOUR_GREY, WID_SCHDISPATCH_MATRIX), SetResize(1, 1), SetScrollbar(WID_SCHDISPATCH_V_SCROLL),
 | 
				
			||||||
			NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_SCHDISPATCH_V_SCROLL),
 | 
								NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_SCHDISPATCH_V_SCROLL),
 | 
				
			||||||
@@ -709,8 +911,8 @@ static const NWidgetPart _nested_schdispatch_widgets[] = {
 | 
				
			|||||||
		NWidget(WWT_PANEL, COLOUR_GREY, WID_SCHDISPATCH_SUMMARY_PANEL), SetMinimalSize(400, 22), SetResize(1, 0), EndContainer(),
 | 
							NWidget(WWT_PANEL, COLOUR_GREY, WID_SCHDISPATCH_SUMMARY_PANEL), SetMinimalSize(400, 22), SetResize(1, 0), EndContainer(),
 | 
				
			||||||
		NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
 | 
							NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
 | 
				
			||||||
			NWidget(NWID_VERTICAL, NC_EQUALSIZE),
 | 
								NWidget(NWID_VERTICAL, NC_EQUALSIZE),
 | 
				
			||||||
			NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_ENABLED), SetDataTip(STR_SCHDISPATCH_ENABLED, STR_SCHDISPATCH_ENABLED_TOOLTIP), SetFill(1, 1), SetResize(1, 0),
 | 
					 | 
				
			||||||
				NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_ADD), SetDataTip(STR_SCHDISPATCH_ADD, STR_SCHDISPATCH_ADD_TOOLTIP), SetFill(1, 1), SetResize(1, 0),
 | 
									NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_ADD), SetDataTip(STR_SCHDISPATCH_ADD, STR_SCHDISPATCH_ADD_TOOLTIP), SetFill(1, 1), SetResize(1, 0),
 | 
				
			||||||
 | 
									NWidget(NWID_SPACER), SetFill(1, 1),
 | 
				
			||||||
			EndContainer(),
 | 
								EndContainer(),
 | 
				
			||||||
			NWidget(NWID_VERTICAL, NC_EQUALSIZE),
 | 
								NWidget(NWID_VERTICAL, NC_EQUALSIZE),
 | 
				
			||||||
					NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_SET_DURATION), SetDataTip(STR_SCHDISPATCH_DURATION, STR_SCHDISPATCH_DURATION_TOOLTIP), SetFill(1, 1), SetResize(1, 0),
 | 
										NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_SET_DURATION), SetDataTip(STR_SCHDISPATCH_DURATION, STR_SCHDISPATCH_DURATION_TOOLTIP), SetFill(1, 1), SetResize(1, 0),
 | 
				
			||||||
@@ -722,6 +924,7 @@ static const NWidgetPart _nested_schdispatch_widgets[] = {
 | 
				
			|||||||
			EndContainer(),
 | 
								EndContainer(),
 | 
				
			||||||
			NWidget(WWT_RESIZEBOX, COLOUR_GREY),
 | 
								NWidget(WWT_RESIZEBOX, COLOUR_GREY),
 | 
				
			||||||
		EndContainer(),
 | 
							EndContainer(),
 | 
				
			||||||
 | 
						EndContainer(),
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static WindowDesc _schdispatch_desc(
 | 
					static WindowDesc _schdispatch_desc(
 | 
				
			||||||
@@ -946,3 +1149,16 @@ void ShowScheduledDispatchAddSlotsWindow(SchdispatchWindow *parent, int window_n
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	new ScheduledDispatchAddSlotsWindow(&_scheduled_dispatch_add_desc, window_number, parent);
 | 
						new ScheduledDispatchAddSlotsWindow(&_scheduled_dispatch_add_desc, window_number, parent);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SchdispatchInvalidateWindows(const Vehicle *v)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						v = v->FirstShared();
 | 
				
			||||||
 | 
						for (Window *w : Window::IterateFromBack()) {
 | 
				
			||||||
 | 
							if (w->window_class == WC_VEHICLE_TIMETABLE) {
 | 
				
			||||||
 | 
								if (static_cast<GeneralVehicleWindow *>(w)->vehicle->FirstShared() == v) w->SetDirty();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (w->window_class == WC_SCHDISPATCH_SLOTS) {
 | 
				
			||||||
 | 
								if (static_cast<GeneralVehicleWindow *>(w)->vehicle->FirstShared() == v) w->InvalidateData(VIWD_MODIFY_ORDERS, false);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,7 @@
 | 
				
			|||||||
void ShowTimetableWindow(const Vehicle *v);
 | 
					void ShowTimetableWindow(const Vehicle *v);
 | 
				
			||||||
void UpdateVehicleTimetable(Vehicle *v, bool travelling);
 | 
					void UpdateVehicleTimetable(Vehicle *v, bool travelling);
 | 
				
			||||||
void SetTimetableParams(int first_param, Ticks ticks, bool long_mode = false);
 | 
					void SetTimetableParams(int first_param, Ticks ticks, bool long_mode = false);
 | 
				
			||||||
 | 
					void SetTimetableWindowsDirty(const Vehicle *v, bool include_scheduled_dispatch = false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct TimetableProgress {
 | 
					struct TimetableProgress {
 | 
				
			||||||
	VehicleID id;
 | 
						VehicleID id;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,7 +48,7 @@ static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint32 val,
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			order->SetWaitTime(val);
 | 
								order->SetWaitTime(val);
 | 
				
			||||||
			order->SetWaitTimetabled(timetabled);
 | 
								order->SetWaitTimetabled(timetabled);
 | 
				
			||||||
			if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) && timetabled && order->IsWaitTimetabled() && v->GetFirstWaitingLocation(true) == order_number) {
 | 
								if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) && timetabled && order->IsScheduledDispatchOrder(true)) {
 | 
				
			||||||
				for (Vehicle *u = v->FirstShared(); u != nullptr; u = u->NextShared()) {
 | 
									for (Vehicle *u = v->FirstShared(); u != nullptr; u = u->NextShared()) {
 | 
				
			||||||
					if (u->cur_implicit_order_index == order_number && (u->last_station_visited == order->GetDestination())) {
 | 
										if (u->cur_implicit_order_index == order_number && (u->last_station_visited == order->GetDestination())) {
 | 
				
			||||||
						u->lateness_counter += timetable_delta;
 | 
											u->lateness_counter += timetable_delta;
 | 
				
			||||||
@@ -84,12 +84,26 @@ static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint32 val,
 | 
				
			|||||||
			order->SetLeaveType((OrderLeaveType)val);
 | 
								order->SetLeaveType((OrderLeaveType)val);
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case MTF_ASSIGN_SCHEDULE:
 | 
				
			||||||
 | 
								if ((int)val >= 0) {
 | 
				
			||||||
 | 
									for (int n = 0; n < v->GetNumOrders(); n++) {
 | 
				
			||||||
 | 
										Order *o = v->GetOrder(n);
 | 
				
			||||||
 | 
										if (o->GetDispatchScheduleIndex() == (int)val) {
 | 
				
			||||||
 | 
											o->SetDispatchScheduleIndex(-1);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								order->SetDispatchScheduleIndex((int)val);
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			NOT_REACHED();
 | 
								NOT_REACHED();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	v->orders.list->UpdateTotalDuration(total_delta);
 | 
						v->orders.list->UpdateTotalDuration(total_delta);
 | 
				
			||||||
	v->orders.list->UpdateTimetableDuration(timetable_delta);
 | 
						v->orders.list->UpdateTimetableDuration(timetable_delta);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SetTimetableWindowsDirty(v, mtf == MTF_ASSIGN_SCHEDULE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (v = v->FirstShared(); v != nullptr; v = v->NextShared()) {
 | 
						for (v = v->FirstShared(); v != nullptr; v = v->NextShared()) {
 | 
				
			||||||
		if (v->cur_real_order_index == order_number && v->current_order.Equals(*order)) {
 | 
							if (v->cur_real_order_index == order_number && v->current_order.Equals(*order)) {
 | 
				
			||||||
			switch (mtf) {
 | 
								switch (mtf) {
 | 
				
			||||||
@@ -119,11 +133,14 @@ static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint32 val,
 | 
				
			|||||||
					v->current_order.SetLeaveType((OrderLeaveType)val);
 | 
										v->current_order.SetLeaveType((OrderLeaveType)val);
 | 
				
			||||||
					break;
 | 
										break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									case MTF_ASSIGN_SCHEDULE:
 | 
				
			||||||
 | 
										v->current_order.SetDispatchScheduleIndex((int)val);
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				default:
 | 
									default:
 | 
				
			||||||
					NOT_REACHED();
 | 
										NOT_REACHED();
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -170,6 +187,7 @@ CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, u
 | 
				
			|||||||
	bool wait_fixed = order->IsWaitFixed();
 | 
						bool wait_fixed = order->IsWaitFixed();
 | 
				
			||||||
	bool travel_fixed = order->IsTravelFixed();
 | 
						bool travel_fixed = order->IsTravelFixed();
 | 
				
			||||||
	OrderLeaveType leave_type = order->GetLeaveType();
 | 
						OrderLeaveType leave_type = order->GetLeaveType();
 | 
				
			||||||
 | 
						int dispatch_index = order->GetDispatchScheduleIndex();
 | 
				
			||||||
	switch (mtf) {
 | 
						switch (mtf) {
 | 
				
			||||||
		case MTF_WAIT_TIME:
 | 
							case MTF_WAIT_TIME:
 | 
				
			||||||
			wait_time = p2;
 | 
								wait_time = p2;
 | 
				
			||||||
@@ -199,6 +217,11 @@ CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, u
 | 
				
			|||||||
			if (leave_type >= OLT_END) return CMD_ERROR;
 | 
								if (leave_type >= OLT_END) return CMD_ERROR;
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case MTF_ASSIGN_SCHEDULE:
 | 
				
			||||||
 | 
								dispatch_index = (int)p2;
 | 
				
			||||||
 | 
								if (dispatch_index < -1 || dispatch_index >= (int)v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			NOT_REACHED();
 | 
								NOT_REACHED();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -223,9 +246,26 @@ CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, u
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (dispatch_index != order->GetDispatchScheduleIndex()) {
 | 
				
			||||||
 | 
							switch (order->GetType()) {
 | 
				
			||||||
 | 
								case OT_GOTO_STATION:
 | 
				
			||||||
 | 
									if (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) {
 | 
				
			||||||
 | 
										if (mtf == MTF_ASSIGN_SCHEDULE && dispatch_index == -1) break;
 | 
				
			||||||
 | 
										return_cmd_error(STR_ERROR_TIMETABLE_NOT_STOPPING_HERE);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case OT_GOTO_DEPOT:
 | 
				
			||||||
 | 
								case OT_GOTO_WAYPOINT:
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								default: return_cmd_error(STR_ERROR_TIMETABLE_ONLY_WAIT_AT_STATIONS);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (travel_time != order->GetTravelTime() && order->IsType(OT_CONDITIONAL)) return CMD_ERROR;
 | 
						if (travel_time != order->GetTravelTime() && order->IsType(OT_CONDITIONAL)) return CMD_ERROR;
 | 
				
			||||||
 | 
						if (travel_fixed != order->IsTravelFixed() && order->IsType(OT_CONDITIONAL)) return CMD_ERROR;
 | 
				
			||||||
	if (max_speed != order->GetMaxSpeed() && (order->IsType(OT_CONDITIONAL) || v->type == VEH_AIRCRAFT)) return CMD_ERROR;
 | 
						if (max_speed != order->GetMaxSpeed() && (order->IsType(OT_CONDITIONAL) || v->type == VEH_AIRCRAFT)) return CMD_ERROR;
 | 
				
			||||||
	if (wait_fixed != order->IsWaitFixed() && order->IsType(OT_CONDITIONAL)) return CMD_ERROR;
 | 
					 | 
				
			||||||
	if (leave_type != order->GetLeaveType() && order->IsType(OT_CONDITIONAL)) return CMD_ERROR;
 | 
						if (leave_type != order->GetLeaveType() && order->IsType(OT_CONDITIONAL)) return CMD_ERROR;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (flags & DC_EXEC) {
 | 
						if (flags & DC_EXEC) {
 | 
				
			||||||
@@ -268,6 +308,12 @@ CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, u
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case MTF_ASSIGN_SCHEDULE:
 | 
				
			||||||
 | 
									if (dispatch_index != order->GetDispatchScheduleIndex()) {
 | 
				
			||||||
 | 
										ChangeTimetable(v, order_number, dispatch_index, MTF_ASSIGN_SCHEDULE, true);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			default:
 | 
								default:
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -424,8 +470,10 @@ CommandCost CmdSetTimetableStart(TileIndex tile, DoCommandFlag flags, uint32 p1,
 | 
				
			|||||||
			for (Vehicle *w = v->orders.list->GetFirstSharedVehicle(); w != nullptr; w = w->NextShared()) {
 | 
								for (Vehicle *w = v->orders.list->GetFirstSharedVehicle(); w != nullptr; w = w->NextShared()) {
 | 
				
			||||||
				vehs.push_back(w);
 | 
									vehs.push_back(w);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								SetTimetableWindowsDirty(v);
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			vehs.push_back(v);
 | 
								vehs.push_back(v);
 | 
				
			||||||
 | 
								SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		int total_duration = v->orders.list->GetTimetableTotalDuration();
 | 
							int total_duration = v->orders.list->GetTimetableTotalDuration();
 | 
				
			||||||
@@ -438,7 +486,6 @@ CommandCost CmdSetTimetableStart(TileIndex tile, DoCommandFlag flags, uint32 p1,
 | 
				
			|||||||
		int idx = vehs.begin() - std::find(vehs.begin(), vehs.end(), v);
 | 
							int idx = vehs.begin() - std::find(vehs.begin(), vehs.end(), v);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for (Vehicle *w : vehs) {
 | 
							for (Vehicle *w : vehs) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
			w->lateness_counter = 0;
 | 
								w->lateness_counter = 0;
 | 
				
			||||||
			ClrBit(w->vehicle_flags, VF_TIMETABLE_STARTED);
 | 
								ClrBit(w->vehicle_flags, VF_TIMETABLE_STARTED);
 | 
				
			||||||
			/* Do multiplication, then division to reduce rounding errors. */
 | 
								/* Do multiplication, then division to reduce rounding errors. */
 | 
				
			||||||
@@ -448,7 +495,6 @@ CommandCost CmdSetTimetableStart(TileIndex tile, DoCommandFlag flags, uint32 p1,
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
			w->timetable_start = tt_start / _settings_game.economy.day_length_factor;
 | 
								w->timetable_start = tt_start / _settings_game.economy.day_length_factor;
 | 
				
			||||||
			w->timetable_start_subticks = tt_start % _settings_game.economy.day_length_factor;
 | 
								w->timetable_start_subticks = tt_start % _settings_game.economy.day_length_factor;
 | 
				
			||||||
			SetWindowDirty(WC_VEHICLE_TIMETABLE, w->index);
 | 
					 | 
				
			||||||
			++idx;
 | 
								++idx;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -506,8 +552,8 @@ CommandCost CmdAutofillTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1,
 | 
				
			|||||||
				ClrBit(v2->vehicle_flags, VF_AUTOFILL_TIMETABLE);
 | 
									ClrBit(v2->vehicle_flags, VF_AUTOFILL_TIMETABLE);
 | 
				
			||||||
				ClrBit(v2->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
 | 
									ClrBit(v2->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			SetWindowDirty(WC_VEHICLE_TIMETABLE, v2->index);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							SetTimetableWindowsDirty(v);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return CommandCost();
 | 
						return CommandCost();
 | 
				
			||||||
@@ -563,8 +609,8 @@ CommandCost CmdAutomateTimetable(TileIndex index, DoCommandFlag flags, uint32 p1
 | 
				
			|||||||
					v2->current_loading_time = 0;
 | 
										v2->current_loading_time = 0;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			SetWindowDirty(WC_VEHICLE_TIMETABLE, v2->index);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							SetTimetableWindowsDirty(v);
 | 
				
			||||||
		if (!HasBit(p2, 0) && !HasBit(p2, 1)) {
 | 
							if (!HasBit(p2, 0) && !HasBit(p2, 1)) {
 | 
				
			||||||
			OrderList *orders = v->orders.list;
 | 
								OrderList *orders = v->orders.list;
 | 
				
			||||||
			if (orders != nullptr) {
 | 
								if (orders != nullptr) {
 | 
				
			||||||
@@ -607,9 +653,8 @@ CommandCost CmdTimetableSeparation(TileIndex tile, DoCommandFlag flags, uint32 p
 | 
				
			|||||||
				ClrBit(v2->vehicle_flags, VF_TIMETABLE_SEPARATION);
 | 
									ClrBit(v2->vehicle_flags, VF_TIMETABLE_SEPARATION);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			v2->ClearSeparation();
 | 
								v2->ClearSeparation();
 | 
				
			||||||
			SetWindowDirty(WC_VEHICLE_TIMETABLE, v2->index);
 | 
					 | 
				
			||||||
			SetWindowDirty(WC_SCHDISPATCH_SLOTS, v2->index);
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							SetTimetableWindowsDirty(v, true);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return CommandCost();
 | 
						return CommandCost();
 | 
				
			||||||
@@ -733,28 +778,23 @@ void UpdateSeparationOrder(Vehicle *v_start)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool IsVehicleAtFirstWaitingLocation(const Vehicle *v)
 | 
					DateTicksScaled GetScheduledDispatchTime(const DispatchSchedule &ds, DateTicksScaled leave_time)
 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return (v->cur_implicit_order_index == v->GetFirstWaitingLocation(true));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static DateTicksScaled GetScheduledDispatchTime(Vehicle *v, int wait_offset)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	DateTicksScaled first_slot          = -1;
 | 
						DateTicksScaled first_slot          = -1;
 | 
				
			||||||
	const DateTicksScaled begin_time    = v->orders.list->GetScheduledDispatchStartTick();
 | 
						const DateTicksScaled begin_time    = ds.GetScheduledDispatchStartTick();
 | 
				
			||||||
	const int32 last_dispatched_offset  = v->orders.list->GetScheduledDispatchLastDispatch();
 | 
						const int32 last_dispatched_offset  = ds.GetScheduledDispatchLastDispatch();
 | 
				
			||||||
	const uint32 dispatch_duration      = v->orders.list->GetScheduledDispatchDuration();
 | 
						const uint32 dispatch_duration      = ds.GetScheduledDispatchDuration();
 | 
				
			||||||
	const int32 max_delay               = v->orders.list->GetScheduledDispatchDelay();
 | 
						const int32 max_delay               = ds.GetScheduledDispatchDelay();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Find next available slots */
 | 
						/* Find next available slots */
 | 
				
			||||||
	for (auto current_offset : v->orders.list->GetScheduledDispatch()) {
 | 
						for (auto current_offset : ds.GetScheduledDispatch()) {
 | 
				
			||||||
		if (current_offset >= dispatch_duration) continue;
 | 
							if (current_offset >= dispatch_duration) continue;
 | 
				
			||||||
		if (int32(current_offset) <= last_dispatched_offset) {
 | 
							if (int32(current_offset) <= last_dispatched_offset) {
 | 
				
			||||||
			current_offset += dispatch_duration * ((last_dispatched_offset + dispatch_duration - current_offset) / dispatch_duration);
 | 
								current_offset += dispatch_duration * ((last_dispatched_offset + dispatch_duration - current_offset) / dispatch_duration);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		DateTicksScaled current_departure = begin_time + current_offset;
 | 
							DateTicksScaled current_departure = begin_time + current_offset;
 | 
				
			||||||
		DateTicksScaled minimum = _scaled_date_ticks + wait_offset - max_delay;
 | 
							DateTicksScaled minimum = leave_time - max_delay;
 | 
				
			||||||
		if (current_departure < minimum) {
 | 
							if (current_departure < minimum) {
 | 
				
			||||||
			current_departure += dispatch_duration * ((minimum + dispatch_duration - current_departure - 1) / dispatch_duration);
 | 
								current_departure += dispatch_duration * ((minimum + dispatch_duration - current_departure - 1) / dispatch_duration);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -801,17 +841,20 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling)
 | 
				
			|||||||
	bool set_scheduled_dispatch = false;
 | 
						bool set_scheduled_dispatch = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Start scheduled dispatch at first opportunity */
 | 
						/* Start scheduled dispatch at first opportunity */
 | 
				
			||||||
	if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)) {
 | 
						if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) && v->cur_implicit_order_index != INVALID_VEH_ORDER_ID) {
 | 
				
			||||||
		if (IsVehicleAtFirstWaitingLocation(v) && travelling) {
 | 
							Order *real_implicit_order = v->GetOrder(v->cur_implicit_order_index);
 | 
				
			||||||
 | 
							if (real_implicit_order->IsScheduledDispatchOrder(true) && travelling) {
 | 
				
			||||||
 | 
								DispatchSchedule &ds = v->orders.list->GetDispatchScheduleByIndex(real_implicit_order->GetDispatchScheduleIndex());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			/* Update scheduled information */
 | 
								/* Update scheduled information */
 | 
				
			||||||
			v->orders.list->UpdateScheduledDispatch();
 | 
								ds.UpdateScheduledDispatch(v);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			const int wait_offset = real_current_order->GetTimetabledWait();
 | 
								const int wait_offset = real_current_order->GetTimetabledWait();
 | 
				
			||||||
			DateTicksScaled slot = GetScheduledDispatchTime(v, wait_offset);
 | 
								DateTicksScaled slot = GetScheduledDispatchTime(ds, _scaled_date_ticks + wait_offset);
 | 
				
			||||||
			if (slot > -1) {
 | 
								if (slot > -1) {
 | 
				
			||||||
				SetBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
 | 
									SetBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
 | 
				
			||||||
				v->lateness_counter = _scaled_date_ticks - slot + wait_offset;
 | 
									v->lateness_counter = _scaled_date_ticks - slot + wait_offset;
 | 
				
			||||||
				v->orders.list->SetScheduledDispatchLastDispatch(slot - v->orders.list->GetScheduledDispatchStartTick());
 | 
									ds.SetScheduledDispatchLastDispatch(slot - ds.GetScheduledDispatchStartTick());
 | 
				
			||||||
				set_scheduled_dispatch = true;
 | 
									set_scheduled_dispatch = true;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -824,9 +867,7 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling)
 | 
				
			|||||||
		/* If the lateness is set by scheduled dispatch above, do not reset */
 | 
							/* If the lateness is set by scheduled dispatch above, do not reset */
 | 
				
			||||||
		if (!HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)) v->lateness_counter = 0;
 | 
							if (!HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)) v->lateness_counter = 0;
 | 
				
			||||||
		if (HasBit(v->vehicle_flags, VF_TIMETABLE_SEPARATION)) UpdateSeparationOrder(v);
 | 
							if (HasBit(v->vehicle_flags, VF_TIMETABLE_SEPARATION)) UpdateSeparationOrder(v);
 | 
				
			||||||
		for (v = v->FirstShared(); v != nullptr; v = v->NextShared()) {
 | 
							SetTimetableWindowsDirty(v);
 | 
				
			||||||
			SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -926,8 +967,8 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling)
 | 
				
			|||||||
					/* Clear VF_TIMETABLE_STARTED but do not call ClearSeparation */
 | 
										/* Clear VF_TIMETABLE_STARTED but do not call ClearSeparation */
 | 
				
			||||||
					ClrBit(v2->vehicle_flags, VF_TIMETABLE_STARTED);
 | 
										ClrBit(v2->vehicle_flags, VF_TIMETABLE_STARTED);
 | 
				
			||||||
					v2->lateness_counter = 0;
 | 
										v2->lateness_counter = 0;
 | 
				
			||||||
					SetWindowDirty(WC_VEHICLE_TIMETABLE, v2->index);
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
									SetTimetableWindowsDirty(v);
 | 
				
			||||||
				return;
 | 
									return;
 | 
				
			||||||
			} else if (new_time >= (int32)timetabled / 2) {
 | 
								} else if (new_time >= (int32)timetabled / 2) {
 | 
				
			||||||
				/* Compute running average, with sign conversion to avoid negative overflow.
 | 
									/* Compute running average, with sign conversion to avoid negative overflow.
 | 
				
			||||||
@@ -994,9 +1035,7 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling)
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (v = v->FirstShared(); v != nullptr; v = v->NextShared()) {
 | 
						SetTimetableWindowsDirty(v);
 | 
				
			||||||
		SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void SetOrderFixedWaitTime(Vehicle *v, VehicleOrderID order_number, uint32 wait_time, bool wait_timetabled) {
 | 
					void SetOrderFixedWaitTime(Vehicle *v, VehicleOrderID order_number, uint32 wait_time, bool wait_timetabled) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,6 +25,7 @@
 | 
				
			|||||||
#include "viewport_func.h"
 | 
					#include "viewport_func.h"
 | 
				
			||||||
#include "schdispatch.h"
 | 
					#include "schdispatch.h"
 | 
				
			||||||
#include "vehiclelist.h"
 | 
					#include "vehiclelist.h"
 | 
				
			||||||
 | 
					#include "tracerestrict.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "widgets/timetable_widget.h"
 | 
					#include "widgets/timetable_widget.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -33,10 +34,19 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "safeguards.h"
 | 
					#include "safeguards.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum TimetableArrivalDepartureFlags {
 | 
				
			||||||
 | 
						TADF_ARRIVAL_PREDICTED,
 | 
				
			||||||
 | 
						TADF_DEPARTURE_PREDICTED,
 | 
				
			||||||
 | 
						TADF_ARRIVAL_NO_OFFSET,
 | 
				
			||||||
 | 
						TADF_DEPARTURE_NO_OFFSET,
 | 
				
			||||||
 | 
						TADF_REACHED,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Container for the arrival/departure dates of a vehicle */
 | 
					/** Container for the arrival/departure dates of a vehicle */
 | 
				
			||||||
struct TimetableArrivalDeparture {
 | 
					struct TimetableArrivalDeparture {
 | 
				
			||||||
	Ticks arrival;   ///< The arrival time
 | 
						Ticks arrival;   ///< The arrival time
 | 
				
			||||||
	Ticks departure; ///< The departure time
 | 
						Ticks departure; ///< The departure time
 | 
				
			||||||
 | 
						uint flags;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -121,29 +131,90 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID
 | 
				
			|||||||
	/* Pre-initialize with unknown time */
 | 
						/* Pre-initialize with unknown time */
 | 
				
			||||||
	for (int i = 0; i < v->GetNumOrders(); ++i) {
 | 
						for (int i = 0; i < v->GetNumOrders(); ++i) {
 | 
				
			||||||
		table[i].arrival = table[i].departure = INVALID_TICKS;
 | 
							table[i].arrival = table[i].departure = INVALID_TICKS;
 | 
				
			||||||
 | 
							table[i].flags = 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	VehicleOrderID scheduled_dispatch_order = INVALID_VEH_ORDER_ID;
 | 
						bool predicted = false;
 | 
				
			||||||
	if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)) scheduled_dispatch_order = v->GetFirstWaitingLocation(true);
 | 
						bool no_offset = false;
 | 
				
			||||||
 | 
						bool skip_travel = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Cyclically loop over all orders until we reach the current one again.
 | 
						/* Cyclically loop over all orders until we reach the current one again.
 | 
				
			||||||
	 * As we may start at the current order, do a post-checking loop */
 | 
						 * As we may start at the current order, do a post-checking loop */
 | 
				
			||||||
	do {
 | 
						do {
 | 
				
			||||||
 | 
							if (HasBit(table[i].flags, TADF_REACHED)) break;
 | 
				
			||||||
 | 
							SetBit(table[i].flags, TADF_REACHED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							bool skip = order->IsType(OT_IMPLICIT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (order->IsType(OT_CONDITIONAL)) {
 | 
				
			||||||
 | 
								bool jump = false;
 | 
				
			||||||
 | 
								switch (order->GetConditionVariable()) {
 | 
				
			||||||
 | 
									case OCV_UNCONDITIONALLY: {
 | 
				
			||||||
 | 
										jump = true;
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									case OCV_TIME_DATE: {
 | 
				
			||||||
 | 
										predicted = true;
 | 
				
			||||||
 | 
										DateTicksScaled time = _scaled_date_ticks + sum;
 | 
				
			||||||
 | 
										if (!no_offset) time -= v->lateness_counter;
 | 
				
			||||||
 | 
										int value = GetTraceRestrictTimeDateValueFromDate(static_cast<TraceRestrictTimeDateValueField>(order->GetConditionValue()), time);
 | 
				
			||||||
 | 
										jump = OrderConditionCompare(order->GetConditionComparator(), value, order->GetXData());
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									default:
 | 
				
			||||||
 | 
										return;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (jump) {
 | 
				
			||||||
 | 
									if (!order->IsWaitTimetabled()) return;
 | 
				
			||||||
 | 
									sum += order->GetTimetabledWait();
 | 
				
			||||||
 | 
									i = order->GetConditionSkipToOrder();
 | 
				
			||||||
 | 
									order = v->GetOrder(i);
 | 
				
			||||||
 | 
									skip_travel = true;
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									skip = true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/* Automatic orders don't influence the overall timetable;
 | 
							/* Automatic orders don't influence the overall timetable;
 | 
				
			||||||
		 * they just add some untimetabled entries, but the time till
 | 
							 * they just add some untimetabled entries, but the time till
 | 
				
			||||||
		 * the next non-implicit order can still be known. */
 | 
							 * the next non-implicit order can still be known. */
 | 
				
			||||||
		if (!order->IsType(OT_IMPLICIT)) {
 | 
							if (!skip) {
 | 
				
			||||||
			if (travelling || i != start) {
 | 
								if (travelling || i != start) {
 | 
				
			||||||
 | 
									if (!skip_travel) {
 | 
				
			||||||
					if (!CanDetermineTimeTaken(order, true)) return;
 | 
										if (!CanDetermineTimeTaken(order, true)) return;
 | 
				
			||||||
					sum += order->GetTimetabledTravel();
 | 
										sum += order->GetTimetabledTravel();
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
				table[i].arrival = sum;
 | 
									table[i].arrival = sum;
 | 
				
			||||||
 | 
									if (predicted) SetBit(table[i].flags, TADF_ARRIVAL_PREDICTED);
 | 
				
			||||||
 | 
									if (no_offset) SetBit(table[i].flags, TADF_ARRIVAL_NO_OFFSET);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if (i == scheduled_dispatch_order && !(i == start && !travelling)) return;
 | 
								if (order->IsScheduledDispatchOrder(true) && !(i == start && !travelling)) {
 | 
				
			||||||
 | 
									if (!no_offset) sum -= v->lateness_counter;
 | 
				
			||||||
 | 
									extern DateTicksScaled GetScheduledDispatchTime(const DispatchSchedule &ds, DateTicksScaled leave_time);
 | 
				
			||||||
 | 
									DispatchSchedule &ds = v->orders.list->GetDispatchScheduleByIndex(order->GetDispatchScheduleIndex());
 | 
				
			||||||
 | 
									DispatchSchedule predicted_ds;
 | 
				
			||||||
 | 
									predicted_ds.BorrowSchedule(ds);
 | 
				
			||||||
 | 
									predicted_ds.UpdateScheduledDispatchToDate(_scaled_date_ticks + sum);
 | 
				
			||||||
 | 
									DateTicksScaled slot = GetScheduledDispatchTime(predicted_ds, _scaled_date_ticks + sum + order->GetTimetabledWait());
 | 
				
			||||||
 | 
									predicted_ds.ReturnSchedule(ds);
 | 
				
			||||||
 | 
									if (slot <= -1) return;
 | 
				
			||||||
 | 
									sum = slot - _scaled_date_ticks;
 | 
				
			||||||
 | 
									predicted = true;
 | 
				
			||||||
 | 
									no_offset = true;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
				if (!CanDetermineTimeTaken(order, false)) return;
 | 
									if (!CanDetermineTimeTaken(order, false)) return;
 | 
				
			||||||
				sum += order->GetTimetabledWait();
 | 
									sum += order->GetTimetabledWait();
 | 
				
			||||||
			table[i].departure = sum;
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								table[i].departure = sum;
 | 
				
			||||||
 | 
								if (predicted) SetBit(table[i].flags, TADF_DEPARTURE_PREDICTED);
 | 
				
			||||||
 | 
								if (predicted) SetBit(table[i].flags, TADF_DEPARTURE_NO_OFFSET);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							skip_travel = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		++i;
 | 
							++i;
 | 
				
			||||||
		order = order->next;
 | 
							order = order->next;
 | 
				
			||||||
@@ -156,10 +227,12 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	/* When loading at a scheduled station we still have to treat the
 | 
						/* When loading at a scheduled station we still have to treat the
 | 
				
			||||||
	 * travelling part of the first order. */
 | 
						 * travelling part of the first order. */
 | 
				
			||||||
	if (!travelling) {
 | 
						if (!travelling && table[i].arrival == INVALID_TICKS) {
 | 
				
			||||||
		if (!CanDetermineTimeTaken(order, true)) return;
 | 
							if (!CanDetermineTimeTaken(order, true)) return;
 | 
				
			||||||
		sum += order->GetTimetabledTravel();
 | 
							sum += order->GetTimetabledTravel();
 | 
				
			||||||
		table[i].arrival = sum;
 | 
							table[i].arrival = sum;
 | 
				
			||||||
 | 
							if (predicted) SetBit(table[i].flags, TADF_ARRIVAL_PREDICTED);
 | 
				
			||||||
 | 
							if (no_offset) SetBit(table[i].flags, TADF_ARRIVAL_NO_OFFSET);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -240,20 +313,37 @@ void ProcessTimetableWarnings(const Vehicle *v, std::function<void(StringID, boo
 | 
				
			|||||||
	if (have_bad_full_load) handler(STR_TIMETABLE_WARNING_FULL_LOAD, true);
 | 
						if (have_bad_full_load) handler(STR_TIMETABLE_WARNING_FULL_LOAD, true);
 | 
				
			||||||
	if (have_conditional && HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE)) handler(STR_TIMETABLE_WARNING_AUTOFILL_CONDITIONAL, true);
 | 
						if (have_conditional && HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE)) handler(STR_TIMETABLE_WARNING_AUTOFILL_CONDITIONAL, true);
 | 
				
			||||||
	if (total_time && have_non_timetabled_conditional_branch) handler(STR_TIMETABLE_NON_TIMETABLED_BRANCH, false);
 | 
						if (total_time && have_non_timetabled_conditional_branch) handler(STR_TIMETABLE_NON_TIMETABLED_BRANCH, false);
 | 
				
			||||||
	if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)) {
 | 
						if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) && v->orders.list != nullptr) {
 | 
				
			||||||
		VehicleOrderID n = v->GetFirstWaitingLocation(false);
 | 
							auto sd_warning = [&](int schedule_index, StringID str) {
 | 
				
			||||||
		if (n == INVALID_VEH_ORDER_ID) {
 | 
								if (v->orders.list->GetScheduledDispatchScheduleCount() > 1) {
 | 
				
			||||||
			handler(STR_TIMETABLE_WARNING_NO_SCHEDULED_DISPATCH_ORDER, true);
 | 
									SetDParam(0, schedule_index + 1);
 | 
				
			||||||
		} else if (!v->GetOrder(n)->IsWaitTimetabled()) {
 | 
									SetDParam(1, str);
 | 
				
			||||||
			handler(STR_TIMETABLE_WARNING_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME, true);
 | 
									handler(STR_TIMETABLE_WARNING_SCHEDULE_ID, true);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									handler(str, true);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
							std::vector<bool> seen_sched_dispatch_orders(v->orders.list->GetScheduledDispatchScheduleCount());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (int n = 0; n < v->GetNumOrders(); n++) {
 | 
				
			||||||
 | 
								const Order *order = v->GetOrder(n);
 | 
				
			||||||
 | 
								int schedule_index = order->GetDispatchScheduleIndex();
 | 
				
			||||||
 | 
								if (schedule_index >= 0) {
 | 
				
			||||||
 | 
									seen_sched_dispatch_orders[schedule_index] = true;
 | 
				
			||||||
 | 
									if (!order->IsWaitTimetabled()) {
 | 
				
			||||||
 | 
										sd_warning(schedule_index, STR_TIMETABLE_WARNING_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for (uint i = 0; i < seen_sched_dispatch_orders.size(); i++) {
 | 
				
			||||||
 | 
								if (!seen_sched_dispatch_orders[i]) sd_warning(i, STR_TIMETABLE_WARNING_NO_SCHEDULED_DISPATCH_ORDER_ASSIGNED);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct TimetableWindow : Window {
 | 
					struct TimetableWindow : GeneralVehicleWindow {
 | 
				
			||||||
	int sel_index;
 | 
						int sel_index;
 | 
				
			||||||
	const Vehicle *vehicle; ///< Vehicle monitored by the window.
 | 
					 | 
				
			||||||
	bool show_expected;     ///< Whether we show expected arrival or scheduled
 | 
						bool show_expected;     ///< Whether we show expected arrival or scheduled
 | 
				
			||||||
	uint deparr_time_width; ///< The width of the departure/arrival time
 | 
						uint deparr_time_width; ///< The width of the departure/arrival time
 | 
				
			||||||
	uint deparr_abbr_width; ///< The width of the departure/arrival abbreviation
 | 
						uint deparr_abbr_width; ///< The width of the departure/arrival abbreviation
 | 
				
			||||||
@@ -264,10 +354,13 @@ struct TimetableWindow : Window {
 | 
				
			|||||||
	bool change_timetable_all; ///< Set wait time or speed for all timetable entries (ctrl-click) action
 | 
						bool change_timetable_all; ///< Set wait time or speed for all timetable entries (ctrl-click) action
 | 
				
			||||||
	int summary_warnings = 0;  ///< NUmber of summary warnings shown
 | 
						int summary_warnings = 0;  ///< NUmber of summary warnings shown
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						enum {
 | 
				
			||||||
 | 
							MAX_SUMMARY_WARNINGS = 10,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	TimetableWindow(WindowDesc *desc, WindowNumber window_number) :
 | 
						TimetableWindow(WindowDesc *desc, WindowNumber window_number) :
 | 
				
			||||||
			Window(desc),
 | 
								GeneralVehicleWindow(desc, Vehicle::Get(window_number)),
 | 
				
			||||||
			sel_index(-1),
 | 
								sel_index(-1),
 | 
				
			||||||
			vehicle(Vehicle::Get(window_number)),
 | 
					 | 
				
			||||||
			show_expected(true)
 | 
								show_expected(true)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		this->CreateNestedTree();
 | 
							this->CreateNestedTree();
 | 
				
			||||||
@@ -330,7 +423,7 @@ struct TimetableWindow : Window {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
			case WID_VT_SUMMARY_PANEL: {
 | 
								case WID_VT_SUMMARY_PANEL: {
 | 
				
			||||||
				Dimension d = GetSpriteSize(SPR_WARNING_SIGN);
 | 
									Dimension d = GetSpriteSize(SPR_WARNING_SIGN);
 | 
				
			||||||
				size->height = WD_FRAMERECT_TOP + 2 * FONT_HEIGHT_NORMAL + this->summary_warnings * std::max<int>(d.height, FONT_HEIGHT_NORMAL) + WD_FRAMERECT_BOTTOM;
 | 
									size->height = WD_FRAMERECT_TOP + 2 * FONT_HEIGHT_NORMAL + std::min<int>(MAX_SUMMARY_WARNINGS, this->summary_warnings) * std::max<int>(d.height, FONT_HEIGHT_NORMAL) + WD_FRAMERECT_BOTTOM;
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -437,6 +530,7 @@ struct TimetableWindow : Window {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		if (v->owner == _local_company) {
 | 
							if (v->owner == _local_company) {
 | 
				
			||||||
			bool disable = true;
 | 
								bool disable = true;
 | 
				
			||||||
 | 
								bool disable_time = true;
 | 
				
			||||||
			bool wait_lockable = false;
 | 
								bool wait_lockable = false;
 | 
				
			||||||
			bool wait_locked = false;
 | 
								bool wait_locked = false;
 | 
				
			||||||
			bool clearable_when_wait_locked = false;
 | 
								bool clearable_when_wait_locked = false;
 | 
				
			||||||
@@ -445,6 +539,7 @@ struct TimetableWindow : Window {
 | 
				
			|||||||
				if (selected % 2 == 1) {
 | 
									if (selected % 2 == 1) {
 | 
				
			||||||
					/* Travel time */
 | 
										/* Travel time */
 | 
				
			||||||
					disable = order != nullptr && (order->IsType(OT_CONDITIONAL) || order->IsType(OT_IMPLICIT));
 | 
										disable = order != nullptr && (order->IsType(OT_CONDITIONAL) || order->IsType(OT_IMPLICIT));
 | 
				
			||||||
 | 
										disable_time = disable;
 | 
				
			||||||
					wait_lockable = !disable;
 | 
										wait_lockable = !disable;
 | 
				
			||||||
					wait_locked = wait_lockable && order->IsTravelFixed();
 | 
										wait_locked = wait_lockable && order->IsTravelFixed();
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
@@ -452,24 +547,29 @@ struct TimetableWindow : Window {
 | 
				
			|||||||
					if (order != nullptr) {
 | 
										if (order != nullptr) {
 | 
				
			||||||
						if (order->IsType(OT_GOTO_WAYPOINT)) {
 | 
											if (order->IsType(OT_GOTO_WAYPOINT)) {
 | 
				
			||||||
							disable = false;
 | 
												disable = false;
 | 
				
			||||||
 | 
												disable_time = false;
 | 
				
			||||||
							clearable_when_wait_locked = true;
 | 
												clearable_when_wait_locked = true;
 | 
				
			||||||
						} else if (order->IsType(OT_CONDITIONAL)) {
 | 
											} else if (order->IsType(OT_CONDITIONAL)) {
 | 
				
			||||||
							disable = true;
 | 
												disable = true;
 | 
				
			||||||
 | 
												disable_time = false;
 | 
				
			||||||
 | 
												clearable_when_wait_locked = true;
 | 
				
			||||||
						} else {
 | 
											} else {
 | 
				
			||||||
							disable = (!(order->IsType(OT_GOTO_STATION) || (order->IsType(OT_GOTO_DEPOT) && !(order->GetDepotActionType() & ODATFB_HALT))) ||
 | 
												disable = (!(order->IsType(OT_GOTO_STATION) || (order->IsType(OT_GOTO_DEPOT) && !(order->GetDepotActionType() & ODATFB_HALT))) ||
 | 
				
			||||||
									(order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION));
 | 
														(order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION));
 | 
				
			||||||
 | 
												disable_time = disable;
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					} else {
 | 
										} else {
 | 
				
			||||||
						disable = true;
 | 
											disable = true;
 | 
				
			||||||
 | 
											disable_time = true;
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
					wait_lockable = !disable;
 | 
										wait_lockable = !disable_time;
 | 
				
			||||||
					wait_locked = wait_lockable && order->IsWaitFixed();
 | 
										wait_locked = wait_lockable && order->IsWaitFixed();
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			bool disable_speed = disable || selected % 2 != 1 || v->type == VEH_AIRCRAFT;
 | 
								bool disable_speed = disable || selected % 2 != 1 || v->type == VEH_AIRCRAFT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			this->SetWidgetDisabledState(WID_VT_CHANGE_TIME, disable || (HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE) && !wait_locked));
 | 
								this->SetWidgetDisabledState(WID_VT_CHANGE_TIME, disable_time || (HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE) && !wait_locked));
 | 
				
			||||||
			this->SetWidgetDisabledState(WID_VT_CLEAR_TIME, disable || (HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE) && !(wait_locked && clearable_when_wait_locked)));
 | 
								this->SetWidgetDisabledState(WID_VT_CLEAR_TIME, disable_time || (HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE) && !(wait_locked && clearable_when_wait_locked)));
 | 
				
			||||||
			this->SetWidgetDisabledState(WID_VT_CHANGE_SPEED, disable_speed);
 | 
								this->SetWidgetDisabledState(WID_VT_CHANGE_SPEED, disable_speed);
 | 
				
			||||||
			this->SetWidgetDisabledState(WID_VT_CLEAR_SPEED, disable_speed);
 | 
								this->SetWidgetDisabledState(WID_VT_CLEAR_SPEED, disable_speed);
 | 
				
			||||||
			this->SetWidgetDisabledState(WID_VT_SHARED_ORDER_LIST, !(v->IsOrderListShared() || _settings_client.gui.enable_single_veh_shared_order_gui));
 | 
								this->SetWidgetDisabledState(WID_VT_SHARED_ORDER_LIST, !(v->IsOrderListShared() || _settings_client.gui.enable_single_veh_shared_order_gui));
 | 
				
			||||||
@@ -483,6 +583,7 @@ struct TimetableWindow : Window {
 | 
				
			|||||||
			this->SetWidgetDisabledState(WID_VT_LOCK_ORDER_TIME, !wait_lockable);
 | 
								this->SetWidgetDisabledState(WID_VT_LOCK_ORDER_TIME, !wait_lockable);
 | 
				
			||||||
			this->SetWidgetLoweredState(WID_VT_LOCK_ORDER_TIME, wait_locked);
 | 
								this->SetWidgetLoweredState(WID_VT_LOCK_ORDER_TIME, wait_locked);
 | 
				
			||||||
			this->SetWidgetDisabledState(WID_VT_EXTRA, disable || (selected % 2 != 0));
 | 
								this->SetWidgetDisabledState(WID_VT_EXTRA, disable || (selected % 2 != 0));
 | 
				
			||||||
 | 
								this->SetWidgetDisabledState(WID_VT_ASSIGN_SCHEDULE, disable || (selected % 2 != 0) || !HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH));
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			this->DisableWidget(WID_VT_START_DATE);
 | 
								this->DisableWidget(WID_VT_START_DATE);
 | 
				
			||||||
			this->DisableWidget(WID_VT_CHANGE_TIME);
 | 
								this->DisableWidget(WID_VT_CHANGE_TIME);
 | 
				
			||||||
@@ -497,14 +598,17 @@ struct TimetableWindow : Window {
 | 
				
			|||||||
			this->DisableWidget(WID_VT_ADD_VEH_GROUP);
 | 
								this->DisableWidget(WID_VT_ADD_VEH_GROUP);
 | 
				
			||||||
			this->DisableWidget(WID_VT_LOCK_ORDER_TIME);
 | 
								this->DisableWidget(WID_VT_LOCK_ORDER_TIME);
 | 
				
			||||||
			this->DisableWidget(WID_VT_EXTRA);
 | 
								this->DisableWidget(WID_VT_EXTRA);
 | 
				
			||||||
 | 
								this->DisableWidget(WID_VT_ASSIGN_SCHEDULE);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this->SetWidgetLoweredState(WID_VT_AUTOFILL, HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE));
 | 
							this->SetWidgetLoweredState(WID_VT_AUTOFILL, HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE));
 | 
				
			||||||
		this->SetWidgetLoweredState(WID_VT_AUTOMATE, HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE));
 | 
							this->SetWidgetLoweredState(WID_VT_AUTOMATE, HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE));
 | 
				
			||||||
		this->SetWidgetLoweredState(WID_VT_AUTO_SEPARATION, HasBit(v->vehicle_flags, VF_TIMETABLE_SEPARATION));
 | 
							this->SetWidgetLoweredState(WID_VT_AUTO_SEPARATION, HasBit(v->vehicle_flags, VF_TIMETABLE_SEPARATION));
 | 
				
			||||||
		this->SetWidgetLoweredState(WID_VT_SCHEDULED_DISPATCH, HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH));
 | 
							this->SetWidgetLoweredState(WID_VT_SCHEDULED_DISPATCH, HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH));
 | 
				
			||||||
 | 
							this->SetWidgetLoweredState(WID_VT_SCHEDULED_DISPATCH, HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this->SetWidgetDisabledState(WID_VT_SCHEDULED_DISPATCH, v->orders.list == nullptr);
 | 
							this->SetWidgetDisabledState(WID_VT_SCHEDULED_DISPATCH, v->orders.list == nullptr);
 | 
				
			||||||
 | 
							this->GetWidget<NWidgetStacked>(WID_VT_START_DATE_SELECTION)->SetDisplayedPlane(HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) ? 1 : 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this->DrawWidgets();
 | 
							this->DrawWidgets();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -671,17 +775,17 @@ struct TimetableWindow : Window {
 | 
				
			|||||||
								SetDParam(0, _scaled_date_ticks + arr_dep[i / 2].arrival);
 | 
													SetDParam(0, _scaled_date_ticks + arr_dep[i / 2].arrival);
 | 
				
			||||||
								DrawString(time_left, time_right, y, STR_JUST_DATE_WALLCLOCK_TINY, TC_GREEN);
 | 
													DrawString(time_left, time_right, y, STR_JUST_DATE_WALLCLOCK_TINY, TC_GREEN);
 | 
				
			||||||
							} else {
 | 
												} else {
 | 
				
			||||||
								SetDParam(0, _scaled_date_ticks + arr_dep[i / 2].arrival + offset);
 | 
													SetDParam(0, _scaled_date_ticks + arr_dep[i / 2].arrival + (HasBit(arr_dep[i / 2].flags, TADF_ARRIVAL_NO_OFFSET) ? 0 : offset));
 | 
				
			||||||
								DrawString(time_left, time_right, y, STR_JUST_DATE_WALLCLOCK_TINY,
 | 
													DrawString(time_left, time_right, y, STR_JUST_DATE_WALLCLOCK_TINY,
 | 
				
			||||||
										show_late ? TC_RED : i == selected ? TC_WHITE : TC_BLACK);
 | 
															HasBit(arr_dep[i / 2].flags, TADF_ARRIVAL_PREDICTED) ? (TextColour)(TC_IS_PALETTE_COLOUR | TC_NO_SHADE | 4) : (show_late ? TC_RED : i == selected ? TC_WHITE : TC_BLACK));
 | 
				
			||||||
							}
 | 
												}
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					} else {
 | 
										} else {
 | 
				
			||||||
						if (arr_dep[i / 2].departure != INVALID_TICKS) {
 | 
											if (arr_dep[i / 2].departure != INVALID_TICKS) {
 | 
				
			||||||
							DrawString(abbr_left, abbr_right, y, STR_TIMETABLE_DEPARTURE_ABBREVIATION, i == selected ? TC_WHITE : TC_BLACK);
 | 
												DrawString(abbr_left, abbr_right, y, STR_TIMETABLE_DEPARTURE_ABBREVIATION, i == selected ? TC_WHITE : TC_BLACK);
 | 
				
			||||||
							SetDParam(0, _scaled_date_ticks + arr_dep[i/2].departure + offset);
 | 
												SetDParam(0, _scaled_date_ticks + arr_dep[i/2].departure + (HasBit(arr_dep[i / 2].flags, TADF_DEPARTURE_NO_OFFSET) ? 0 : offset));
 | 
				
			||||||
							DrawString(time_left, time_right, y, STR_JUST_DATE_WALLCLOCK_TINY,
 | 
												DrawString(time_left, time_right, y, STR_JUST_DATE_WALLCLOCK_TINY,
 | 
				
			||||||
									show_late ? TC_RED : i == selected ? TC_WHITE : TC_BLACK);
 | 
														HasBit(arr_dep[i / 2].flags, TADF_DEPARTURE_PREDICTED) ? (TextColour)(TC_IS_PALETTE_COLOUR | TC_NO_SHADE | 4) : (show_late ? TC_RED : i == selected ? TC_WHITE : TC_BLACK));
 | 
				
			||||||
						}
 | 
											}
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
					y += line_height;
 | 
										y += line_height;
 | 
				
			||||||
@@ -724,9 +828,7 @@ struct TimetableWindow : Window {
 | 
				
			|||||||
					const int warning_offset_y = (step_height - warning_dimensions.height) / 2;
 | 
										const int warning_offset_y = (step_height - warning_dimensions.height) / 2;
 | 
				
			||||||
					const bool rtl = _current_text_dir == TD_RTL;
 | 
										const bool rtl = _current_text_dir == TD_RTL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					int warning_count = 0;
 | 
										auto draw_warning = [&](StringID text, bool warning) {
 | 
				
			||||||
 | 
					 | 
				
			||||||
					ProcessTimetableWarnings(v, [&](StringID text, bool warning) {
 | 
					 | 
				
			||||||
						int left = r.left + WD_FRAMERECT_LEFT;
 | 
											int left = r.left + WD_FRAMERECT_LEFT;
 | 
				
			||||||
						int right = r.right - WD_FRAMERECT_RIGHT;
 | 
											int right = r.right - WD_FRAMERECT_RIGHT;
 | 
				
			||||||
						if (warning) {
 | 
											if (warning) {
 | 
				
			||||||
@@ -739,8 +841,19 @@ struct TimetableWindow : Window {
 | 
				
			|||||||
						}
 | 
											}
 | 
				
			||||||
						DrawString(left, right, y + text_offset_y, text);
 | 
											DrawString(left, right, y + text_offset_y, text);
 | 
				
			||||||
						y += step_height;
 | 
											y += step_height;
 | 
				
			||||||
 | 
										};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										int warning_count = 0;
 | 
				
			||||||
 | 
										int warning_limit = this->summary_warnings > MAX_SUMMARY_WARNINGS ? MAX_SUMMARY_WARNINGS - 1 : std::min<int>(MAX_SUMMARY_WARNINGS, this->summary_warnings);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										ProcessTimetableWarnings(v, [&](StringID text, bool warning) {
 | 
				
			||||||
 | 
											if (warning_count < warning_limit) draw_warning(text, warning);
 | 
				
			||||||
						warning_count++;
 | 
											warning_count++;
 | 
				
			||||||
					});
 | 
										});
 | 
				
			||||||
 | 
										if (warning_count > warning_limit) {
 | 
				
			||||||
 | 
											SetDParam(0, warning_count - warning_limit);
 | 
				
			||||||
 | 
											draw_warning(STR_TIMETABLE_WARNINGS_OMITTED, true);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					if (warning_count != this->summary_warnings) {
 | 
										if (warning_count != this->summary_warnings) {
 | 
				
			||||||
						TimetableWindow *mutable_this = const_cast<TimetableWindow *>(this);
 | 
											TimetableWindow *mutable_this = const_cast<TimetableWindow *>(this);
 | 
				
			||||||
@@ -944,6 +1057,22 @@ struct TimetableWindow : Window {
 | 
				
			|||||||
				ShowDropDownList(this, std::move(list), order != nullptr ? order->GetLeaveType() : -1, WID_VT_EXTRA);
 | 
									ShowDropDownList(this, std::move(list), order != nullptr ? order->GetLeaveType() : -1, WID_VT_EXTRA);
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case WID_VT_ASSIGN_SCHEDULE: {
 | 
				
			||||||
 | 
									VehicleOrderID real = (this->sel_index + 1) / 2;
 | 
				
			||||||
 | 
									if (real >= this->vehicle->GetNumOrders()) real = 0;
 | 
				
			||||||
 | 
									const Order *order = this->vehicle->GetOrder(real);
 | 
				
			||||||
 | 
									DropDownList list;
 | 
				
			||||||
 | 
									list.emplace_back(new DropDownListStringItem(STR_TIMETABLE_ASSIGN_SCHEDULE_NONE, -1, false));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									for (uint i = 0; i < v->orders.list->GetScheduledDispatchScheduleCount(); i++) {
 | 
				
			||||||
 | 
										DropDownListParamStringItem *item = new DropDownListParamStringItem(STR_TIMETABLE_ASSIGN_SCHEDULE_ID, i, false);
 | 
				
			||||||
 | 
										item->SetParam(0, i + 1);
 | 
				
			||||||
 | 
										list.emplace_back(item);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									ShowDropDownList(this, std::move(list), order->GetDispatchScheduleIndex(), WID_VT_ASSIGN_SCHEDULE);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this->SetDirty();
 | 
							this->SetDirty();
 | 
				
			||||||
@@ -952,9 +1081,13 @@ struct TimetableWindow : Window {
 | 
				
			|||||||
	void OnDropdownSelect(int widget, int index) override
 | 
						void OnDropdownSelect(int widget, int index) override
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		switch (widget) {
 | 
							switch (widget) {
 | 
				
			||||||
			case WID_VT_EXTRA: {
 | 
								case WID_VT_EXTRA:
 | 
				
			||||||
				ExecuteTimetableCommand(this->vehicle, false, this->sel_index, MTF_SET_LEAVE_TYPE, index, false);
 | 
									ExecuteTimetableCommand(this->vehicle, false, this->sel_index, MTF_SET_LEAVE_TYPE, index, false);
 | 
				
			||||||
			}
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								case WID_VT_ASSIGN_SCHEDULE:
 | 
				
			||||||
 | 
									ExecuteTimetableCommand(this->vehicle, false, this->sel_index, MTF_ASSIGN_SCHEDULE, index, false);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			default:
 | 
								default:
 | 
				
			||||||
				break;
 | 
									break;
 | 
				
			||||||
@@ -1068,7 +1201,10 @@ static const NWidgetPart _nested_timetable_widgets[] = {
 | 
				
			|||||||
	NWidget(NWID_HORIZONTAL),
 | 
						NWidget(NWID_HORIZONTAL),
 | 
				
			||||||
		NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
 | 
							NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
 | 
				
			||||||
			NWidget(NWID_VERTICAL, NC_EQUALSIZE),
 | 
								NWidget(NWID_VERTICAL, NC_EQUALSIZE),
 | 
				
			||||||
 | 
									NWidget(NWID_SELECTION, INVALID_COLOUR, WID_VT_START_DATE_SELECTION),
 | 
				
			||||||
					NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_START_DATE), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_STARTING_DATE, STR_TIMETABLE_STARTING_DATE_TOOLTIP),
 | 
										NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_START_DATE), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_STARTING_DATE, STR_TIMETABLE_STARTING_DATE_TOOLTIP),
 | 
				
			||||||
 | 
										NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_VT_ASSIGN_SCHEDULE), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_ASSIGN_SCHEDULE_DROP_DOWN, STR_TIMETABLE_ASSIGN_SCHEDULE_DROP_DOWN_TOOLTIP),
 | 
				
			||||||
 | 
									EndContainer(),
 | 
				
			||||||
				NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_CHANGE_TIME), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_CHANGE_TIME, STR_TIMETABLE_WAIT_TIME_TOOLTIP),
 | 
									NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_CHANGE_TIME), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_CHANGE_TIME, STR_TIMETABLE_WAIT_TIME_TOOLTIP),
 | 
				
			||||||
				NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_CLEAR_TIME), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_CLEAR_TIME, STR_TIMETABLE_CLEAR_TIME_TOOLTIP),
 | 
									NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_CLEAR_TIME), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_CLEAR_TIME, STR_TIMETABLE_CLEAR_TIME_TOOLTIP),
 | 
				
			||||||
			EndContainer(),
 | 
								EndContainer(),
 | 
				
			||||||
@@ -1119,3 +1255,13 @@ void ShowTimetableWindow(const Vehicle *v)
 | 
				
			|||||||
	DeleteWindowById(WC_VEHICLE_ORDERS, v->index, false);
 | 
						DeleteWindowById(WC_VEHICLE_ORDERS, v->index, false);
 | 
				
			||||||
	AllocateWindowDescFront<TimetableWindow>(&_timetable_desc, v->index);
 | 
						AllocateWindowDescFront<TimetableWindow>(&_timetable_desc, v->index);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SetTimetableWindowsDirty(const Vehicle *v, bool include_scheduled_dispatch)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						v = v->FirstShared();
 | 
				
			||||||
 | 
						for (Window *w : Window::IterateFromBack()) {
 | 
				
			||||||
 | 
							if (w->window_class == WC_VEHICLE_TIMETABLE || (include_scheduled_dispatch && w->window_class == WC_SCHDISPATCH_SLOTS)) {
 | 
				
			||||||
 | 
								if (static_cast<GeneralVehicleWindow *>(w)->vehicle->FirstShared() == v) w->SetDirty();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1759,6 +1759,37 @@ int GetTraceRestrictTimeDateValue(TraceRestrictTimeDateValueField type)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int GetTraceRestrictTimeDateValueFromDate(TraceRestrictTimeDateValueField type, DateTicksScaled scaled_date_ticks)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						Minutes minutes = (scaled_date_ticks / _settings_game.game_time.ticks_per_minute) + _settings_game.game_time.clock_offset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (type) {
 | 
				
			||||||
 | 
							case TRTDVF_MINUTE:
 | 
				
			||||||
 | 
								return MINUTES_MINUTE(minutes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case TRTDVF_HOUR:
 | 
				
			||||||
 | 
								return MINUTES_HOUR(minutes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case TRTDVF_HOUR_MINUTE:
 | 
				
			||||||
 | 
								return (MINUTES_HOUR(minutes) * 100) + MINUTES_MINUTE(minutes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case TRTDVF_DAY: {
 | 
				
			||||||
 | 
								YearMonthDay ymd;
 | 
				
			||||||
 | 
								ConvertDateToYMD(scaled_date_ticks / (DAY_TICKS * _settings_game.economy.day_length_factor), &ymd);
 | 
				
			||||||
 | 
								return ymd.day;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case TRTDVF_MONTH: {
 | 
				
			||||||
 | 
								YearMonthDay ymd;
 | 
				
			||||||
 | 
								ConvertDateToYMD(scaled_date_ticks / (DAY_TICKS * _settings_game.economy.day_length_factor), &ymd);
 | 
				
			||||||
 | 
								return ymd.month + 1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * This is called when a station, waypoint or depot is about to be deleted
 | 
					 * This is called when a station, waypoint or depot is about to be deleted
 | 
				
			||||||
 * Scan program pool and change any references to it to the invalid station ID, to avoid dangling references
 | 
					 * Scan program pool and change any references to it to the invalid station ID, to avoid dangling references
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -941,6 +941,7 @@ CommandCost TraceRestrictProgramMoveItemAt(std::vector<TraceRestrictItem> &items
 | 
				
			|||||||
void ShowTraceRestrictProgramWindow(TileIndex tile, Track track);
 | 
					void ShowTraceRestrictProgramWindow(TileIndex tile, Track track);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int GetTraceRestrictTimeDateValue(TraceRestrictTimeDateValueField type);
 | 
					int GetTraceRestrictTimeDateValue(TraceRestrictTimeDateValueField type);
 | 
				
			||||||
 | 
					int GetTraceRestrictTimeDateValueFromDate(TraceRestrictTimeDateValueField type, DateTicksScaled scaled_date_ticks);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void TraceRestrictRemoveDestinationID(TraceRestrictOrderCondAuxField type, uint16 index);
 | 
					void TraceRestrictRemoveDestinationID(TraceRestrictOrderCondAuxField type, uint16 index);
 | 
				
			||||||
void TraceRestrictRemoveGroupID(GroupID index);
 | 
					void TraceRestrictRemoveGroupID(GroupID index);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2139,6 +2139,7 @@ CommandCost CmdMoveRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, u
 | 
				
			|||||||
			DeleteWindowById(WC_VEHICLE_REFIT, src->index);
 | 
								DeleteWindowById(WC_VEHICLE_REFIT, src->index);
 | 
				
			||||||
			DeleteWindowById(WC_VEHICLE_DETAILS, src->index);
 | 
								DeleteWindowById(WC_VEHICLE_DETAILS, src->index);
 | 
				
			||||||
			DeleteWindowById(WC_VEHICLE_TIMETABLE, src->index);
 | 
								DeleteWindowById(WC_VEHICLE_TIMETABLE, src->index);
 | 
				
			||||||
 | 
								DeleteWindowById(WC_SCHDISPATCH_SLOTS, src->index);
 | 
				
			||||||
			DeleteNewGRFInspectWindow(GSF_TRAINS, src->index);
 | 
								DeleteNewGRFInspectWindow(GSF_TRAINS, src->index);
 | 
				
			||||||
			SetWindowDirty(WC_COMPANY, _current_company);
 | 
								SetWindowDirty(WC_COMPANY, _current_company);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,6 +37,8 @@ enum VehicleTimetableWidgets {
 | 
				
			|||||||
	WID_VT_SCHEDULED_DISPATCH,          ///< Scheduled Dispatch button.
 | 
						WID_VT_SCHEDULED_DISPATCH,          ///< Scheduled Dispatch button.
 | 
				
			||||||
	WID_VT_LOCK_ORDER_TIME,             ///< Lock order time button.
 | 
						WID_VT_LOCK_ORDER_TIME,             ///< Lock order time button.
 | 
				
			||||||
	WID_VT_EXTRA,                       ///< Extra drop down menu.
 | 
						WID_VT_EXTRA,                       ///< Extra drop down menu.
 | 
				
			||||||
 | 
						WID_VT_ASSIGN_SCHEDULE,             ///< Assign scheduled dispatch schedule.
 | 
				
			||||||
 | 
						WID_VT_START_DATE_SELECTION,        ///< #NWID_SELECTION widget for WID_VT_START_DATE and WID_VT_ASSIGN_SCHEDULE.
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* WIDGETS_TIMETABLE_WIDGET_H */
 | 
					#endif /* WIDGETS_TIMETABLE_WIDGET_H */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1102,4 +1102,10 @@ inline bool MayBeShown(const Window *w)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct GeneralVehicleWindow : public Window {
 | 
				
			||||||
 | 
						const Vehicle *vehicle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						GeneralVehicleWindow(WindowDesc *desc, const Vehicle *v) : Window(desc), vehicle(v) {}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* WINDOW_GUI_H */
 | 
					#endif /* WINDOW_GUI_H */
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user