Scheduled dispatch: Add per dispatch slot flags field

Add flag for slot re-use
This commit is contained in:
Jonathan G Rennison
2024-01-28 01:48:54 +00:00
parent dee611f5f3
commit 6c329871f1
11 changed files with 260 additions and 49 deletions

View File

@@ -305,6 +305,7 @@ CommandProc CmdScheduledDispatchDuplicateSchedule;
CommandProc CmdScheduledDispatchAppendVehicleSchedules; CommandProc CmdScheduledDispatchAppendVehicleSchedules;
CommandProc CmdScheduledDispatchAdjust; CommandProc CmdScheduledDispatchAdjust;
CommandProc CmdScheduledDispatchSwapSchedules; CommandProc CmdScheduledDispatchSwapSchedules;
CommandProcEx CmdScheduledDispatchSetSlotFlags;
CommandProc CmdAddPlan; CommandProc CmdAddPlan;
CommandProcEx CmdAddPlanLine; CommandProcEx CmdAddPlanLine;
@@ -569,6 +570,7 @@ static const Command _command_proc_table[] = {
DEF_CMD(CmdScheduledDispatchAppendVehicleSchedules, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SCHEDULED_DISPATCH_APPEND_VEHICLE_SCHEDULE DEF_CMD(CmdScheduledDispatchAppendVehicleSchedules, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SCHEDULED_DISPATCH_APPEND_VEHICLE_SCHEDULE
DEF_CMD(CmdScheduledDispatchAdjust, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SCHEDULED_DISPATCH_ADJUST DEF_CMD(CmdScheduledDispatchAdjust, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SCHEDULED_DISPATCH_ADJUST
DEF_CMD(CmdScheduledDispatchSwapSchedules, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SCHEDULED_DISPATCH_SWAP_SCHEDULES DEF_CMD(CmdScheduledDispatchSwapSchedules, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SCHEDULED_DISPATCH_SWAP_SCHEDULES
DEF_CMD(CmdScheduledDispatchSetSlotFlags, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SCHEDULED_DISPATCH_SET_SLOT_FLAGS
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

View File

@@ -521,6 +521,7 @@ enum Commands {
CMD_SCHEDULED_DISPATCH_APPEND_VEHICLE_SCHEDULE, ///< scheduled dispatch append schedules from another vehicle CMD_SCHEDULED_DISPATCH_APPEND_VEHICLE_SCHEDULE, ///< scheduled dispatch append schedules from another vehicle
CMD_SCHEDULED_DISPATCH_ADJUST, ///< scheduled dispatch adjust time offsets in schedule CMD_SCHEDULED_DISPATCH_ADJUST, ///< scheduled dispatch adjust time offsets in schedule
CMD_SCHEDULED_DISPATCH_SWAP_SCHEDULES, ///< scheduled dispatch swap schedules in order CMD_SCHEDULED_DISPATCH_SWAP_SCHEDULES, ///< scheduled dispatch swap schedules in order
CMD_SCHEDULED_DISPATCH_SET_SLOT_FLAGS, ///< scheduled dispatch set flags of dispatch slot
CMD_ADD_PLAN, CMD_ADD_PLAN,
CMD_ADD_PLAN_LINE, CMD_ADD_PLAN_LINE,

View File

@@ -132,15 +132,19 @@ static bool VehicleSetNextDepartureTime(Ticks *previous_departure, Ticks *waitin
btree::btree_set<DateTicksScaled> &slot_cache = dept_schedule_last[&ds]; btree::btree_set<DateTicksScaled> &slot_cache = dept_schedule_last[&ds];
/* Find next available slots */ /* Find next available slots */
for (auto current_offset : ds.GetScheduledDispatch()) { for (const DispatchSlot &slot : ds.GetScheduledDispatch()) {
if (current_offset >= dispatch_duration) continue; if (slot.offset >= dispatch_duration) continue;
DateTicksScaled current_departure = begin_time + current_offset; DateTicksScaled current_departure = begin_time + slot.offset;
while (current_departure <= earliest_departure) { while (current_departure <= earliest_departure) {
current_departure += dispatch_duration; current_departure += dispatch_duration;
} }
/* 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 (slot_cache.count(current_departure) > 0) { while (slot_cache.count(current_departure) > 0) {
if (HasBit(slot.flags, DispatchSlot::SDSF_REUSE_SLOT)) {
/* Allow re-use of this slot if it's the last seen */
if (*slot_cache.rbegin() == current_departure) break;
}
current_departure += dispatch_duration; current_departure += dispatch_duration;
} }
@@ -151,7 +155,9 @@ static bool VehicleSetNextDepartureTime(Ticks *previous_departure, Ticks *waitin
*waiting_time = (actual_departure - date_ticks_base).AsTicks() - *previous_departure - order->GetTravelTime(); *waiting_time = (actual_departure - date_ticks_base).AsTicks() - *previous_departure - order->GetTravelTime();
*previous_departure = (actual_departure - date_ticks_base).AsTicks(); *previous_departure = (actual_departure - date_ticks_base).AsTicks();
if (!ds.GetScheduledDispatchReuseSlots()) {
slot_cache.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;

View File

@@ -2054,7 +2054,7 @@ STR_TMPL_CANT_RENAME :{WHITE}Can't re
STR_SCHDISPATCH_CAPTION :{WHITE}{VEHICLE} (Scheduled Dispatch) STR_SCHDISPATCH_CAPTION :{WHITE}{VEHICLE} (Scheduled Dispatch)
STR_SCHDISPATCH_ENABLED :{BLACK}Enable STR_SCHDISPATCH_ENABLED :{BLACK}Enable
STR_SCHDISPATCH_ENABLED_TOOLTIP :{BLACK}Enable scheduled dispatching for this order. Requires automatic separation to be off. STR_SCHDISPATCH_ENABLED_TOOLTIP :{BLACK}Enable scheduled dispatching for this order. Requires automatic separation to be off.
STR_SCHDISPATCH_ADD :{BLACK}Add Departure Slot STR_SCHDISPATCH_ADD :{BLACK}Add Slot
STR_SCHDISPATCH_ADD_TOOLTIP :{BLACK}Add new departure slot for this schedule. STR_SCHDISPATCH_ADD_TOOLTIP :{BLACK}Add new departure slot for this schedule.
STR_SCHDISPATCH_ADD_TOOLTIP_EXTRA :{BLACK}{STRING} Ctrl+Click to add multiple departure slots at once. STR_SCHDISPATCH_ADD_TOOLTIP_EXTRA :{BLACK}{STRING} Ctrl+Click to add multiple departure slots at once.
STR_SCHDISPATCH_ADD_CAPTION :{BLACK}Departure slot STR_SCHDISPATCH_ADD_CAPTION :{BLACK}Departure slot
@@ -2074,9 +2074,9 @@ STR_SCHDISPATCH_DELAY_TOOLTIP :{BLACK}Set maxi
STR_SCHDISPATCH_DELAY_CAPTION_MINUTE :{BLACK}Delay (minutes) STR_SCHDISPATCH_DELAY_CAPTION_MINUTE :{BLACK}Delay (minutes)
STR_SCHDISPATCH_DELAY_CAPTION_DAY :{BLACK}Delay (days) STR_SCHDISPATCH_DELAY_CAPTION_DAY :{BLACK}Delay (days)
STR_SCHDISPATCH_DELAY_CAPTION_TICKS :{BLACK}Delay (ticks) STR_SCHDISPATCH_DELAY_CAPTION_TICKS :{BLACK}Delay (ticks)
STR_SCHDISPATCH_ADJUST :{BLACK}Adjust Departure Slots STR_SCHDISPATCH_ADJUST :{BLACK}Adjust Slots
STR_SCHDISPATCH_ADJUST_TOOLTIP :{BLACK}Adjust all departure slots in this schedule. STR_SCHDISPATCH_ADJUST_TOOLTIP :{BLACK}Adjust all departure slots in this schedule.
STR_SCHDISPATCH_REMOVE :{BLACK}Remove Departure Slots STR_SCHDISPATCH_REMOVE :{BLACK}Remove Slots
STR_SCHDISPATCH_REMOVE_TOOLTIP :{BLACK}Enable removing departure slots from this schedule. STR_SCHDISPATCH_REMOVE_TOOLTIP :{BLACK}Enable removing departure slots from this schedule.
STR_SCHDISPATCH_ADJUST_CAPTION_MINUTE :{BLACK}Adjustment to add (minutes) STR_SCHDISPATCH_ADJUST_CAPTION_MINUTE :{BLACK}Adjustment to add (minutes)
STR_SCHDISPATCH_ADJUST_CAPTION_DAY :{BLACK}Adjustment to add (days) STR_SCHDISPATCH_ADJUST_CAPTION_DAY :{BLACK}Adjustment to add (days)
@@ -2099,8 +2099,11 @@ STR_SCHDISPATCH_DUPLICATE_SCHEDULE :{BLACK}Duplicat
STR_SCHDISPATCH_DUPLICATE_SCHEDULE_TOOLTIP :{BLACK}Create a copy of this dispatch schedule. STR_SCHDISPATCH_DUPLICATE_SCHEDULE_TOOLTIP :{BLACK}Create a copy of this dispatch schedule.
STR_SCHDISPATCH_APPEND_VEHICLE_SCHEDULES :{BLACK}Append Schedules From Vehicle STR_SCHDISPATCH_APPEND_VEHICLE_SCHEDULES :{BLACK}Append Schedules From Vehicle
STR_SCHDISPATCH_APPEND_VEHICLE_SCHEDULES_TOOLTIP :{BLACK}Append the dispatch schedules from another vehicle. STR_SCHDISPATCH_APPEND_VEHICLE_SCHEDULES_TOOLTIP :{BLACK}Append the dispatch schedules from another vehicle.
STR_SCHDISPATCH_APPEND_REUSE_DEPARTURE_SLOTS :{BLACK}Re-use departure slots STR_SCHDISPATCH_REUSE_DEPARTURE_SLOTS :Re-use departure slots
STR_SCHDISPATCH_APPEND_REUSE_DEPARTURE_SLOTS_TOOLTIP :{BLACK}Set whether departure slots may be used more than once. STR_SCHDISPATCH_REUSE_DEPARTURE_SLOTS_TOOLTIP :{BLACK}Set whether departure slots may be used more than once.
STR_SCHDISPATCH_MANAGE_SLOT :{BLACK}Manage Slot
STR_SCHDISPATCH_REUSE_THIS_DEPARTURE_SLOT :Re-use departure slot
STR_SCHDISPATCH_REUSE_THIS_DEPARTURE_SLOT_TOOLTIP :{BLACK}Set whether the selected departure slot may be used more than once.
STR_SCHDISPATCH_NO_SCHEDULES :{BLACK}No Schedules STR_SCHDISPATCH_NO_SCHEDULES :{BLACK}No Schedules
STR_SCHDISPATCH_SCHEDULE_ID :{BLACK}Schedule {NUM} of {NUM} STR_SCHDISPATCH_SCHEDULE_ID :{BLACK}Schedule {NUM} of {NUM}
STR_SCHDISPATCH_NAMED_SCHEDULE_ID :{BLACK}{RAW_STRING} ({NUM} of {NUM}) STR_SCHDISPATCH_NAMED_SCHEDULE_ID :{BLACK}{RAW_STRING} ({NUM} of {NUM})
@@ -2108,6 +2111,7 @@ STR_SCHDISPATCH_RENAME_SCHEDULE_TOOLTIP :{BLACK}Name sch
STR_SCHDISPATCH_RENAME_SCHEDULE_CAPTION :{WHITE}Name schedule STR_SCHDISPATCH_RENAME_SCHEDULE_CAPTION :{WHITE}Name schedule
STR_ERROR_CAN_T_RENAME_SCHEDULE :{WHITE}Can't name schedule... STR_ERROR_CAN_T_RENAME_SCHEDULE :{WHITE}Can't name schedule...
STR_SCHDISPATCH_MOVE_SCHEDULE :{BLACK}Change position of current schedule in schedule list STR_SCHDISPATCH_MOVE_SCHEDULE :{BLACK}Change position of current schedule in schedule list
STR_SCHDISPATCH_DATE_WALLCLOCK_TINY_FLAGGED :{DATE_WALLCLOCK_TINY}*
STR_SCHDISPATCH_SUMMARY_NO_LAST_DEPARTURE :{BLACK}No previous departures. STR_SCHDISPATCH_SUMMARY_NO_LAST_DEPARTURE :{BLACK}No previous departures.
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}.

View File

@@ -716,11 +716,28 @@ template <typename T, typename F> T CargoMaskValueFilter(CargoTypes &cargo_mask,
return value; return value;
} }
struct DispatchSlot {
uint32_t offset;
uint16_t flags;
bool operator<(const DispatchSlot &other) const
{
return this->offset < other.offset;
}
/**
* Flag bit numbers for scheduled_dispatch_flags
*/
enum ScheduledDispatchSlotFlags {
SDSF_REUSE_SLOT = 0, ///< Allow this slot to be used more than once
};
};
struct DispatchSchedule { struct DispatchSchedule {
private: private:
friend SaveLoadTable GetDispatchScheduleDescription(); ///< Saving and loading of dispatch schedules friend SaveLoadTable GetDispatchScheduleDescription(); ///< Saving and loading of dispatch schedules
std::vector<uint32_t> scheduled_dispatch; ///< Scheduled dispatch time std::vector<DispatchSlot> scheduled_dispatch; ///< Scheduled dispatch slots
DateTicksScaled scheduled_dispatch_start_tick = -1; ///< Scheduled dispatch start tick DateTicksScaled scheduled_dispatch_start_tick = -1; ///< Scheduled dispatch start tick
uint32_t scheduled_dispatch_duration = 0; ///< Scheduled dispatch duration uint32_t scheduled_dispatch_duration = 0; ///< Scheduled dispatch duration
int32_t scheduled_dispatch_last_dispatch = INVALID_SCHEDULED_DISPATCH_OFFSET; ///< Last vehicle dispatched offset int32_t scheduled_dispatch_last_dispatch = INVALID_SCHEDULED_DISPATCH_OFFSET; ///< Last vehicle dispatched offset
@@ -750,9 +767,10 @@ public:
* Get the vector of all scheduled dispatch slot * Get the vector of all scheduled dispatch slot
* @return first scheduled dispatch * @return first scheduled dispatch
*/ */
inline const std::vector<uint32_t> &GetScheduledDispatch() const { return this->scheduled_dispatch; } inline const std::vector<DispatchSlot> &GetScheduledDispatch() const { return this->scheduled_dispatch; }
inline std::vector<DispatchSlot> &GetScheduledDispatchMutable() { return this->scheduled_dispatch; }
void SetScheduledDispatch(std::vector<uint32_t> dispatch_list); void SetScheduledDispatch(std::vector<DispatchSlot> dispatch_list);
void AddScheduledDispatch(uint32_t offset); void AddScheduledDispatch(uint32_t offset);
void RemoveScheduledDispatch(uint32_t offset); void RemoveScheduledDispatch(uint32_t offset);
void AdjustScheduledDispatch(int32_t adjust); void AdjustScheduledDispatch(int32_t adjust);

View File

@@ -3068,9 +3068,9 @@ bool EvaluateDispatchSlotConditionalOrder(const Order *order, const Vehicle *v,
bool value; bool value;
if (order->GetConditionValue() & 1) { if (order->GetConditionValue() & 1) {
value = (offset == (int)sched.GetScheduledDispatch().back()); value = (offset == (int32_t)sched.GetScheduledDispatch().back().offset);
} else { } else {
value = (offset == (int)sched.GetScheduledDispatch().front()); value = (offset == (int32_t)sched.GetScheduledDispatch().front().offset);
} }
return OrderConditionCompare(order->GetConditionComparator(), value ? 1 : 0, 0); return OrderConditionCompare(order->GetConditionComparator(), value ? 1 : 0, 0);

View File

@@ -647,11 +647,61 @@ CommandCost CmdScheduledDispatchSwapSchedules(TileIndex tile, DoCommandFlag flag
return CommandCost(); return CommandCost();
} }
/**
* Add scheduled dispatch time offset
* @param tile Not used.
* @param flags Operation to perform.
* @param p1 Vehicle index.
* @param p2 Slot offset.
* @param p3 various bitstuffed elements
* - p3 = (bit 0 - 15) - flag values
* - p3 = (bit 16 - 31) - flag mask
* @param text unused
* @return the cost of this operation or an error
*/
CommandCost CmdScheduledDispatchSetSlotFlags(TileIndex tile, DoCommandFlag flags, uint32_t p1, uint32_t p2, uint64_t p3, const char *text, const CommandAuxiliaryBase *aux_data)
{
VehicleID veh = GB(p1, 0, 20);
uint schedule_index = GB(p1, 20, 12);
uint32_t offset = p2;
uint16_t values = (uint16_t)GB(p3, 0, 16);
uint16_t mask = (uint16_t)GB(p3, 16, 16);
const uint16_t permitted_mask = (1 << DispatchSlot::SDSF_REUSE_SLOT);
if ((mask & permitted_mask) != mask) return CMD_ERROR;
if ((values & (~mask)) != 0) return CMD_ERROR;
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 == nullptr) return CMD_ERROR;
if (schedule_index >= v->orders->GetScheduledDispatchScheduleCount()) return CMD_ERROR;
DispatchSchedule &ds = v->orders->GetDispatchScheduleByIndex(schedule_index);
for (DispatchSlot &slot : ds.GetScheduledDispatchMutable()) {
if (slot.offset == offset) {
if (flags & DC_EXEC) {
slot.flags &= ~mask;
slot.flags |= values;
SchdispatchInvalidateWindows(v);
SetTimetableWindowsDirty(v, STWDF_SCHEDULED_DISPATCH);
}
return CommandCost();
}
}
return CMD_ERROR;
}
/** /**
* 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 DispatchSchedule::SetScheduledDispatch(std::vector<uint32_t> dispatch_list) void DispatchSchedule::SetScheduledDispatch(std::vector<DispatchSlot> 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()));
@@ -665,11 +715,11 @@ void DispatchSchedule::SetScheduledDispatch(std::vector<uint32_t> dispatch_list)
void DispatchSchedule::AddScheduledDispatch(uint32_t offset) void DispatchSchedule::AddScheduledDispatch(uint32_t 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(), DispatchSlot{ offset, 0 });
if (insert_position != this->scheduled_dispatch.end() && *insert_position == offset) { if (insert_position != this->scheduled_dispatch.end() && insert_position->offset == offset) {
return; return;
} }
this->scheduled_dispatch.insert(insert_position, offset); this->scheduled_dispatch.insert(insert_position, { offset, 0 });
this->UpdateScheduledDispatch(nullptr); this->UpdateScheduledDispatch(nullptr);
} }
@@ -680,8 +730,8 @@ void DispatchSchedule::AddScheduledDispatch(uint32_t offset)
void DispatchSchedule::RemoveScheduledDispatch(uint32_t offset) void DispatchSchedule::RemoveScheduledDispatch(uint32_t 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(), DispatchSlot{ offset, 0 });
if (erase_position == this->scheduled_dispatch.end() || *erase_position != offset) { if (erase_position == this->scheduled_dispatch.end() || erase_position->offset != offset) {
return; return;
} }
this->scheduled_dispatch.erase(erase_position); this->scheduled_dispatch.erase(erase_position);
@@ -693,11 +743,11 @@ void DispatchSchedule::RemoveScheduledDispatch(uint32_t offset)
*/ */
void DispatchSchedule::AdjustScheduledDispatch(int32_t adjust) void DispatchSchedule::AdjustScheduledDispatch(int32_t adjust)
{ {
for (uint32_t &time : this->scheduled_dispatch) { for (DispatchSlot &slot : this->scheduled_dispatch) {
int32_t t = (int32_t)time + adjust; int32_t t = (int32_t)slot.offset + adjust;
if (t < 0) t += GetScheduledDispatchDuration(); if (t < 0) t += GetScheduledDispatchDuration();
if (t >= (int32_t)GetScheduledDispatchDuration()) t -= (int32_t)GetScheduledDispatchDuration(); if (t >= (int32_t)GetScheduledDispatchDuration()) t -= (int32_t)GetScheduledDispatchDuration();
time = (uint32_t)t; slot.offset = (uint32_t)t;
} }
std::sort(this->scheduled_dispatch.begin(), this->scheduled_dispatch.end()); std::sort(this->scheduled_dispatch.begin(), this->scheduled_dispatch.end());
} }

View File

@@ -60,6 +60,7 @@ enum SchdispatchWidgets {
WID_SCHDISPATCH_MANAGEMENT, ///< Management button WID_SCHDISPATCH_MANAGEMENT, ///< Management button
WID_SCHDISPATCH_ADJUST, ///< Adjust departure times WID_SCHDISPATCH_ADJUST, ///< Adjust departure times
WID_SCHDISPATCH_REMOVE, ///< Remove departure times WID_SCHDISPATCH_REMOVE, ///< Remove departure times
WID_SCHDISPATCH_MANAGE_SLOT, ///< Manage slot button
}; };
/** /**
@@ -129,10 +130,10 @@ static void ScheduleAddCallback(const Window *w, DateTicksScaled date)
* @param offsets list of all dispatch offsets in the schedule * @param offsets list of all dispatch offsets in the schedule
* @return maxinum number of vehicle required * @return maxinum number of vehicle required
*/ */
static int CalculateMaxRequiredVehicle(Ticks timetable_duration, uint32_t schedule_duration, std::vector<uint32_t> offsets) static int CalculateMaxRequiredVehicle(Ticks timetable_duration, uint32_t schedule_duration, const std::vector<DispatchSlot> &slots)
{ {
if (timetable_duration == INVALID_TICKS) return -1; if (timetable_duration == INVALID_TICKS) return -1;
if (offsets.size() == 0) return -1; if (slots.size() == 0) return -1;
/* Number of time required to ensure all vehicle are counted */ /* Number of time required to ensure all vehicle are counted */
int required_loop = CeilDiv(timetable_duration, schedule_duration) + 1; int required_loop = CeilDiv(timetable_duration, schedule_duration) + 1;
@@ -140,10 +141,10 @@ static int CalculateMaxRequiredVehicle(Ticks timetable_duration, uint32_t schedu
/* Create indice array to count maximum overlapping range */ /* Create indice array to count maximum overlapping range */
std::vector<std::pair<uint32_t, int>> indices; std::vector<std::pair<uint32_t, int>> indices;
for (int i = 0; i < required_loop; i++) { for (int i = 0; i < required_loop; i++) {
for (uint32_t offset : offsets) { for (const DispatchSlot &slot : slots) {
if (offset >= schedule_duration) continue; if (slot.offset >= schedule_duration) continue;
indices.push_back(std::make_pair(i * schedule_duration + offset, 1)); indices.push_back(std::make_pair(i * schedule_duration + slot.offset, 1));
indices.push_back(std::make_pair(i * schedule_duration + offset + timetable_duration, -1)); indices.push_back(std::make_pair(i * schedule_duration + slot.offset + timetable_duration, -1));
} }
} }
if (indices.empty()) return -1; if (indices.empty()) return -1;
@@ -204,6 +205,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
int arrow_flag_height = 0; int arrow_flag_height = 0;
bool remove_slot_mode = false; bool remove_slot_mode = false;
uint32_t selected_slot = UINT32_MAX;
enum ManagementDropdown { enum ManagementDropdown {
SCH_MD_RESET_LAST_DISPATCHED, SCH_MD_RESET_LAST_DISPATCHED,
@@ -214,6 +216,10 @@ struct SchdispatchWindow : GeneralVehicleWindow {
SCH_MD_REUSE_DEPARTURE_SLOTS, SCH_MD_REUSE_DEPARTURE_SLOTS,
}; };
enum SlotManagementDropdown {
SCH_SMD_REUSE_DEPARTURE_SLOT,
};
SchdispatchWindow(WindowDesc *desc, WindowNumber window_number) : SchdispatchWindow(WindowDesc *desc, WindowNumber window_number) :
GeneralVehicleWindow(desc, Vehicle::Get(window_number)) GeneralVehicleWindow(desc, Vehicle::Get(window_number))
@@ -248,6 +254,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
} else { } else {
this->schedule_index = -1; this->schedule_index = -1;
} }
this->selected_slot = UINT32_MAX;
} }
} }
@@ -256,6 +263,21 @@ struct SchdispatchWindow : GeneralVehicleWindow {
return this->vehicle->orders->GetDispatchScheduleByIndex(this->schedule_index); return this->vehicle->orders->GetDispatchScheduleByIndex(this->schedule_index);
} }
const DispatchSlot *GetSelectedDispatchSlot() const
{
if (!this->IsScheduleSelected()) return nullptr;
const DispatchSchedule &ds = this->GetSelectedSchedule();
if (this->selected_slot != UINT32_MAX) {
for (const DispatchSlot &slot : ds.GetScheduledDispatch()) {
if (slot.offset == this->selected_slot) {
return &slot;
}
}
}
return nullptr;
}
virtual void UpdateWidgetSize(WidgetID widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override virtual void UpdateWidgetSize(WidgetID widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override
{ {
switch (widget) { switch (widget) {
@@ -263,7 +285,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
uint min_height = 0; uint min_height = 0;
SetDParamMaxValue(0, _settings_time.time_in_minutes ? 0 : MAX_YEAR * DAYS_IN_YEAR); SetDParamMaxValue(0, _settings_time.time_in_minutes ? 0 : MAX_YEAR * DAYS_IN_YEAR);
Dimension unumber = GetStringBoundingBox(STR_JUST_DATE_WALLCLOCK_TINY); Dimension unumber = GetStringBoundingBox(STR_SCHDISPATCH_DATE_WALLCLOCK_TINY_FLAGGED);
const Sprite *spr = GetSprite(SPR_FLAG_VEH_STOPPED, SpriteType::Normal, ZoomMask(ZOOM_LVL_GUI)); const Sprite *spr = GetSprite(SPR_FLAG_VEH_STOPPED, SpriteType::Normal, ZoomMask(ZOOM_LVL_GUI));
this->delete_flag_width = UnScaleGUI(spr->width); this->delete_flag_width = UnScaleGUI(spr->width);
@@ -351,6 +373,11 @@ struct SchdispatchWindow : GeneralVehicleWindow {
this->SetWidgetDisabledState(WID_SCHDISPATCH_MANAGEMENT, disabled); this->SetWidgetDisabledState(WID_SCHDISPATCH_MANAGEMENT, disabled);
this->SetWidgetDisabledState(WID_SCHDISPATCH_ADJUST, no_editable_slots); this->SetWidgetDisabledState(WID_SCHDISPATCH_ADJUST, no_editable_slots);
if (no_editable_slots || this->GetSelectedDispatchSlot() == nullptr) {
this->selected_slot = UINT32_MAX;
}
this->SetWidgetDisabledState(WID_SCHDISPATCH_MANAGE_SLOT, this->selected_slot == UINT32_MAX);
NWidgetCore *remove_slot_widget = this->GetWidget<NWidgetCore>(WID_SCHDISPATCH_REMOVE); NWidgetCore *remove_slot_widget = this->GetWidget<NWidgetCore>(WID_SCHDISPATCH_REMOVE);
remove_slot_widget->SetDisabled(no_editable_slots); remove_slot_widget->SetDisabled(no_editable_slots);
if (no_editable_slots) { if (no_editable_slots) {
@@ -417,7 +444,13 @@ struct SchdispatchWindow : GeneralVehicleWindow {
add_suffix(STR_SCHDISPATCH_REMOVE_SCHEDULE_TOOLTIP); add_suffix(STR_SCHDISPATCH_REMOVE_SCHEDULE_TOOLTIP);
add_suffix(STR_SCHDISPATCH_DUPLICATE_SCHEDULE_TOOLTIP); add_suffix(STR_SCHDISPATCH_DUPLICATE_SCHEDULE_TOOLTIP);
add_suffix(STR_SCHDISPATCH_APPEND_VEHICLE_SCHEDULES_TOOLTIP); add_suffix(STR_SCHDISPATCH_APPEND_VEHICLE_SCHEDULES_TOOLTIP);
add_suffix(STR_SCHDISPATCH_APPEND_REUSE_DEPARTURE_SLOTS_TOOLTIP); add_suffix(STR_SCHDISPATCH_REUSE_DEPARTURE_SLOTS_TOOLTIP);
GuiShowTooltips(this, SPECSTR_TEMP_START, close_cond);
return true;
}
case WID_SCHDISPATCH_MANAGE_SLOT: {
_temp_special_strings[0] = GetString(STR_SCHDISPATCH_REUSE_THIS_DEPARTURE_SLOT_TOOLTIP);
GuiShowTooltips(this, SPECSTR_TEMP_START, close_cond); GuiShowTooltips(this, SPECSTR_TEMP_START, close_cond);
return true; return true;
} }
@@ -436,7 +469,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
* @param right Right side of the box to draw in. * @param right Right side of the box to draw in.
* @param y Top of the box to draw in. * @param y Top of the box to draw in.
*/ */
void DrawScheduledTime(const DateTicksScaled time, int left, int right, int y, TextColour colour, bool last, bool next) const void DrawScheduledTime(const DateTicksScaled time, int left, int right, int y, TextColour colour, bool last, bool next, bool flagged) const
{ {
bool rtl = _current_text_dir == TD_RTL; bool rtl = _current_text_dir == TD_RTL;
@@ -462,7 +495,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
} }
SetDParam(0, time); SetDParam(0, time);
DrawString(text_left, text_right, y + 2, STR_JUST_DATE_WALLCLOCK_TINY, colour); DrawString(text_left, text_right, y + 2, flagged ? STR_SCHDISPATCH_DATE_WALLCLOCK_TINY_FLAGGED : STR_JUST_DATE_WALLCLOCK_TINY, colour, SA_HOR_CENTER);
} }
virtual void OnGameTick() override virtual void OnGameTick() override
@@ -502,16 +535,26 @@ struct SchdispatchWindow : GeneralVehicleWindow {
DateTicksScaled slot = GetScheduledDispatchTime(ds, _scaled_date_ticks); DateTicksScaled slot = GetScheduledDispatchTime(ds, _scaled_date_ticks);
int32_t next_offset = (slot - ds.GetScheduledDispatchStartTick()).AsTicks() % ds.GetScheduledDispatchDuration(); int32_t next_offset = (slot - ds.GetScheduledDispatchStartTick()).AsTicks() % ds.GetScheduledDispatchDuration();
int32_t last_dispatch = ds.GetScheduledDispatchLastDispatch() % 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 (uint i = 0; i < this->num_columns && num < maxval; i++, num++) { for (uint 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 != ds.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->offset;
bool last = ds.GetScheduledDispatchLastDispatch() == (int32_t)*current_schedule; bool last = last_dispatch == (int32_t)current_schedule->offset;
bool next = next_offset == (int32_t)*current_schedule; bool next = next_offset == (int32_t)current_schedule->offset;
TextColour colour;
if (this->selected_slot == current_schedule->offset) {
colour = TC_WHITE;
} else {
colour = draw_time >= end_tick ? TC_RED : TC_BLACK;
}
auto flags = current_schedule->flags;
if (ds.GetScheduledDispatchReuseSlots()) ClrBit(flags, DispatchSlot::SDSF_REUSE_SLOT);
this->DrawScheduledTime(draw_time, x + WidgetDimensions::scaled.framerect.left, x + this->resize.step_width - 1 - (2 * WidgetDimensions::scaled.framerect.left), this->DrawScheduledTime(draw_time, x + WidgetDimensions::scaled.framerect.left, x + this->resize.step_width - 1 - (2 * WidgetDimensions::scaled.framerect.left),
y, draw_time >= end_tick ? TC_RED : TC_BLACK, last, next); y, colour, last, next, flags != 0);
current_schedule++; current_schedule++;
} else { } else {
break; break;
@@ -699,8 +742,8 @@ struct SchdispatchWindow : GeneralVehicleWindow {
y += GetCharacterHeight(FS_NORMAL); y += GetCharacterHeight(FS_NORMAL);
uint32_t duration = ds.GetScheduledDispatchDuration(); uint32_t duration = ds.GetScheduledDispatchDuration();
for (uint32_t slot : ds.GetScheduledDispatch()) { for (const DispatchSlot &slot : ds.GetScheduledDispatch()) {
if (slot >= duration) { if (slot.offset >= duration) {
draw_warning(STR_SCHDISPATCH_SLOT_OUTSIDE_SCHEDULE); draw_warning(STR_SCHDISPATCH_SLOT_OUTSIDE_SCHEDULE);
break; break;
} }
@@ -743,11 +786,26 @@ struct SchdispatchWindow : GeneralVehicleWindow {
const DispatchSchedule &ds = this->GetSelectedSchedule(); const DispatchSchedule &ds = this->GetSelectedSchedule();
if (pos >= this->item_count || pos >= ds.GetScheduledDispatch().size()) return; if (pos >= this->item_count || pos >= ds.GetScheduledDispatch().size()) {
if (this->selected_slot != UINT32_MAX) {
this->selected_slot = UINT32_MAX;
this->SetWidgetDirty(WID_SCHDISPATCH_MATRIX);
}
return;
}
uint32_t offset = ds.GetScheduledDispatch()[pos].offset;
if (xm <= this->header_width && this->remove_slot_mode) { if (xm <= this->header_width && this->remove_slot_mode) {
DoCommandP(0, this->vehicle->index | (this->schedule_index << 20), ds.GetScheduledDispatch()[pos], CMD_SCHEDULED_DISPATCH_REMOVE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); DoCommandP(0, this->vehicle->index | (this->schedule_index << 20), offset, CMD_SCHEDULED_DISPATCH_REMOVE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
} }
if (this->selected_slot == offset) {
this->selected_slot = UINT32_MAX;
} else {
this->selected_slot = offset;
}
this->SetWidgetDirty(WID_SCHDISPATCH_MATRIX);
} }
int32_t ProcessDurationForQueryString(int32_t duration) const int32_t ProcessDurationForQueryString(int32_t duration) const
@@ -842,20 +900,26 @@ struct SchdispatchWindow : GeneralVehicleWindow {
add_item(STR_SCHDISPATCH_REMOVE_SCHEDULE, SCH_MD_REMOVE_SCHEDULE); add_item(STR_SCHDISPATCH_REMOVE_SCHEDULE, SCH_MD_REMOVE_SCHEDULE);
add_item(STR_SCHDISPATCH_DUPLICATE_SCHEDULE, SCH_MD_DUPLICATE_SCHEDULE); add_item(STR_SCHDISPATCH_DUPLICATE_SCHEDULE, SCH_MD_DUPLICATE_SCHEDULE);
add_item(STR_SCHDISPATCH_APPEND_VEHICLE_SCHEDULES, SCH_MD_APPEND_VEHICLE_SCHEDULES); add_item(STR_SCHDISPATCH_APPEND_VEHICLE_SCHEDULES, SCH_MD_APPEND_VEHICLE_SCHEDULES);
list.push_back(std::make_unique<DropDownListCheckedItem>(schedule.GetScheduledDispatchReuseSlots(), STR_SCHDISPATCH_APPEND_REUSE_DEPARTURE_SLOTS, SCH_MD_REUSE_DEPARTURE_SLOTS, false)); list.push_back(std::make_unique<DropDownListCheckedItem>(schedule.GetScheduledDispatchReuseSlots(), STR_SCHDISPATCH_REUSE_DEPARTURE_SLOTS, SCH_MD_REUSE_DEPARTURE_SLOTS, 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: case WID_SCHDISPATCH_PREV:
if (!this->IsScheduleSelected()) break; if (!this->IsScheduleSelected()) break;
if (this->schedule_index > 0) this->schedule_index--; if (this->schedule_index > 0) {
this->schedule_index--;
this->selected_slot = UINT32_MAX;
}
this->ReInit(); this->ReInit();
break; break;
case WID_SCHDISPATCH_NEXT: case WID_SCHDISPATCH_NEXT:
if (!this->IsScheduleSelected()) break; if (!this->IsScheduleSelected()) break;
if (this->schedule_index < (int)(this->vehicle->orders->GetScheduledDispatchScheduleCount() - 1)) this->schedule_index++; if (this->schedule_index < (int)(this->vehicle->orders->GetScheduledDispatchScheduleCount() - 1)) {
this->schedule_index++;
this->selected_slot = UINT32_MAX;
}
this->ReInit(); this->ReInit();
break; break;
@@ -885,6 +949,18 @@ struct SchdispatchWindow : GeneralVehicleWindow {
break; break;
} }
case WID_SCHDISPATCH_MANAGE_SLOT: {
const DispatchSlot *selected_slot = this->GetSelectedDispatchSlot();
if (selected_slot == nullptr) break;
const DispatchSchedule &schedule = this->GetSelectedSchedule();
DropDownList list;
list.push_back(std::make_unique<DropDownListCheckedItem>(HasBit(selected_slot->flags, DispatchSlot::SDSF_REUSE_SLOT),
STR_SCHDISPATCH_REUSE_THIS_DEPARTURE_SLOT, SCH_SMD_REUSE_DEPARTURE_SLOT, schedule.GetScheduledDispatchReuseSlots()));
ShowDropDownList(this, std::move(list), -1, WID_SCHDISPATCH_MANAGE_SLOT);
break;
}
case WID_SCHDISPATCH_MOVE_LEFT: case WID_SCHDISPATCH_MOVE_LEFT:
if (!this->IsScheduleSelected()) break; if (!this->IsScheduleSelected()) break;
if (this->schedule_index > 0) { if (this->schedule_index > 0) {
@@ -963,6 +1039,23 @@ struct SchdispatchWindow : GeneralVehicleWindow {
break; break;
} }
} }
break;
}
case WID_SCHDISPATCH_MANAGE_SLOT: {
const DispatchSlot *selected_slot = this->GetSelectedDispatchSlot();
if (selected_slot == nullptr) break;
switch((SlotManagementDropdown)index) {
case SCH_SMD_REUSE_DEPARTURE_SLOT: {
uint64_t p3 = 0;
SetBit(p3, SCH_SMD_REUSE_DEPARTURE_SLOT + 16);
if (!HasBit(selected_slot->flags, SCH_SMD_REUSE_DEPARTURE_SLOT)) SetBit(p3, SCH_SMD_REUSE_DEPARTURE_SLOT);
DoCommandPEx(0, this->vehicle->index | (this->schedule_index << 20), this->selected_slot, p3, CMD_SCHEDULED_DISPATCH_SET_SLOT_FLAGS | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE), nullptr, nullptr, 0);
break;
}
}
break;
} }
default: default:
@@ -1145,6 +1238,7 @@ static constexpr NWidgetPart _nested_schdispatch_widgets[] = {
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(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_ADJUST), SetDataTip(STR_SCHDISPATCH_ADJUST, STR_SCHDISPATCH_ADJUST_TOOLTIP), SetFill(1, 1), SetResize(1, 0), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_ADJUST), SetDataTip(STR_SCHDISPATCH_ADJUST, STR_SCHDISPATCH_ADJUST_TOOLTIP), SetFill(1, 1), SetResize(1, 0),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SCHDISPATCH_REMOVE), SetDataTip(STR_SCHDISPATCH_REMOVE, STR_SCHDISPATCH_REMOVE_TOOLTIP), SetFill(1, 1), SetResize(1, 0), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_SCHDISPATCH_REMOVE), SetDataTip(STR_SCHDISPATCH_REMOVE, STR_SCHDISPATCH_REMOVE_TOOLTIP), SetFill(1, 1), SetResize(1, 0),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_SCHDISPATCH_MANAGE_SLOT), SetDataTip(STR_SCHDISPATCH_MANAGE_SLOT, STR_NULL), SetFill(1, 1), SetResize(1, 0),
EndContainer(), EndContainer(),
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), NWidget(NWID_HORIZONTAL),

View File

@@ -117,7 +117,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, 6, 6, "scheduled_dispatch", nullptr, nullptr, nullptr }, { XSLFI_SCHEDULED_DISPATCH, XSCF_NULL, 7, 7, "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" },

View File

@@ -22,6 +22,7 @@ std::vector<OrderList *> _jokerpp_non_auto_separation;
static uint16_t _old_scheduled_dispatch_start_full_date_fract; static uint16_t _old_scheduled_dispatch_start_full_date_fract;
btree::btree_map<DispatchSchedule *, uint16_t> _old_scheduled_dispatch_start_full_date_fract_map; btree::btree_map<DispatchSchedule *, uint16_t> _old_scheduled_dispatch_start_full_date_fract_map;
static std::vector<uint32_t> _old_scheduled_dispatch_slots;
/** /**
* Converts this order from an old savegame's version; * Converts this order from an old savegame's version;
@@ -264,7 +265,7 @@ static void Ptrs_ORDR()
SaveLoadTable GetDispatchScheduleDescription() SaveLoadTable GetDispatchScheduleDescription()
{ {
static const SaveLoad _dispatch_scheduled_info_desc[] = { static const SaveLoad _dispatch_scheduled_info_desc[] = {
SLE_VARVEC(DispatchSchedule, scheduled_dispatch, SLE_UINT32), SLEG_CONDVARVEC_X(_old_scheduled_dispatch_slots, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 1, 6)),
SLE_VAR(DispatchSchedule, scheduled_dispatch_duration, SLE_UINT32), SLE_VAR(DispatchSchedule, scheduled_dispatch_duration, SLE_UINT32),
SLE_CONDVAR_X(DispatchSchedule, scheduled_dispatch_start_tick, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 1, 4)), SLE_CONDVAR_X(DispatchSchedule, scheduled_dispatch_start_tick, SLE_FILE_I32 | SLE_VAR_I64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 1, 4)),
SLEG_CONDVAR_X(_old_scheduled_dispatch_start_full_date_fract, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 1, 4)), SLEG_CONDVAR_X(_old_scheduled_dispatch_start_full_date_fract, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 1, 4)),
@@ -278,6 +279,16 @@ SaveLoadTable GetDispatchScheduleDescription()
return _dispatch_scheduled_info_desc; return _dispatch_scheduled_info_desc;
} }
SaveLoadTable GetDispatchSlotDescription()
{
static const SaveLoad _dispatch_slot_info_desc[] = {
SLE_VAR(DispatchSlot, offset, SLE_UINT32),
SLE_VAR(DispatchSlot, flags, SLE_UINT16),
};
return _dispatch_slot_info_desc;
}
SaveLoadTable GetOrderListDescription() SaveLoadTable GetOrderListDescription()
{ {
static const SaveLoad _orderlist_desc[] = { static const SaveLoad _orderlist_desc[] = {
@@ -291,11 +302,13 @@ SaveLoadTable GetOrderListDescription()
static std::vector<SaveLoad> _filtered_ordl_desc; static std::vector<SaveLoad> _filtered_ordl_desc;
static std::vector<SaveLoad> _filtered_ordl_sd_desc; static std::vector<SaveLoad> _filtered_ordl_sd_desc;
static std::vector<SaveLoad> _filtered_ordl_slot_desc;
static void SetupDescs_ORDL() static void SetupDescs_ORDL()
{ {
_filtered_ordl_desc = SlFilterObject(GetOrderListDescription()); _filtered_ordl_desc = SlFilterObject(GetOrderListDescription());
_filtered_ordl_sd_desc = SlFilterObject(GetDispatchScheduleDescription()); _filtered_ordl_sd_desc = SlFilterObject(GetDispatchScheduleDescription());
_filtered_ordl_slot_desc = SlFilterObject(GetDispatchSlotDescription());
} }
static void Save_ORDL() static void Save_ORDL()
@@ -309,6 +322,11 @@ static void Save_ORDL()
SlWriteUint32(list->GetScheduledDispatchScheduleCount()); SlWriteUint32(list->GetScheduledDispatchScheduleCount());
for (DispatchSchedule &ds : list->GetScheduledDispatchScheduleSet()) { for (DispatchSchedule &ds : list->GetScheduledDispatchScheduleSet()) {
SlObjectSaveFiltered(&ds, _filtered_ordl_sd_desc); SlObjectSaveFiltered(&ds, _filtered_ordl_sd_desc);
SlWriteUint32((uint32_t)ds.GetScheduledDispatchMutable().size());
for (DispatchSlot &slot : ds.GetScheduledDispatchMutable()) {
SlObjectSaveFiltered(&slot, _filtered_ordl_slot_desc);
}
} }
}, list); }, list);
} }
@@ -344,9 +362,23 @@ static void Load_ORDL()
if (SlXvIsFeaturePresent(XSLFI_SCHEDULED_DISPATCH, 1, 4) && _old_scheduled_dispatch_start_full_date_fract != 0) { if (SlXvIsFeaturePresent(XSLFI_SCHEDULED_DISPATCH, 1, 4) && _old_scheduled_dispatch_start_full_date_fract != 0) {
_old_scheduled_dispatch_start_full_date_fract_map[&ds] = _old_scheduled_dispatch_start_full_date_fract; _old_scheduled_dispatch_start_full_date_fract_map[&ds] = _old_scheduled_dispatch_start_full_date_fract;
} }
if (SlXvIsFeaturePresent(XSLFI_SCHEDULED_DISPATCH, 1, 6)) {
ds.GetScheduledDispatchMutable().reserve(_old_scheduled_dispatch_slots.size());
for (uint32_t slot : _old_scheduled_dispatch_slots) {
ds.GetScheduledDispatchMutable().push_back({ slot, 0 });
}
} else {
ds.GetScheduledDispatchMutable().resize(SlReadUint32());
for (DispatchSlot &slot : ds.GetScheduledDispatchMutable()) {
SlObjectLoadFiltered(&slot, _filtered_ordl_slot_desc);
} }
} }
} }
}
}
_old_scheduled_dispatch_slots.clear();
} }
void Ptrs_ORDL() void Ptrs_ORDL()

View File

@@ -807,10 +807,14 @@ DateTicksScaled GetScheduledDispatchTime(const DispatchSchedule &ds, DateTicksSc
DateTicksScaled first_slot = -1; DateTicksScaled first_slot = -1;
/* Find next available slots */ /* Find next available slots */
for (auto current_offset : ds.GetScheduledDispatch()) { for (const DispatchSlot &slot : ds.GetScheduledDispatch()) {
auto current_offset = slot.offset;
if (current_offset >= dispatch_duration) continue; if (current_offset >= dispatch_duration) continue;
if ((int32_t)current_offset <= last_dispatched_offset) {
current_offset += dispatch_duration * ((last_dispatched_offset + dispatch_duration - current_offset) / dispatch_duration); int32_t threshold = last_dispatched_offset;
if (HasBit(slot.flags, DispatchSlot::SDSF_REUSE_SLOT)) threshold--;
if ((int32_t)current_offset <= threshold) {
current_offset += dispatch_duration * ((threshold + dispatch_duration - current_offset) / dispatch_duration);
} }
DateTicksScaled current_departure = begin_time + current_offset; DateTicksScaled current_departure = begin_time + current_offset;