Scheduled dispatch: Add per dispatch slot flags field

Add flag for slot re-use
pull/642/head
Jonathan G Rennison 4 months ago
parent dee611f5f3
commit 6c329871f1

@ -305,6 +305,7 @@ CommandProc CmdScheduledDispatchDuplicateSchedule;
CommandProc CmdScheduledDispatchAppendVehicleSchedules;
CommandProc CmdScheduledDispatchAdjust;
CommandProc CmdScheduledDispatchSwapSchedules;
CommandProcEx CmdScheduledDispatchSetSlotFlags;
CommandProc CmdAddPlan;
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(CmdScheduledDispatchAdjust, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SCHEDULED_DISPATCH_ADJUST
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(CmdAddPlanLine, CMD_NO_TEST, CMDT_OTHER_MANAGEMENT ), // CMD_ADD_PLAN_LINE

@ -521,6 +521,7 @@ enum Commands {
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_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_LINE,

@ -132,15 +132,19 @@ static bool VehicleSetNextDepartureTime(Ticks *previous_departure, Ticks *waitin
btree::btree_set<DateTicksScaled> &slot_cache = dept_schedule_last[&ds];
/* Find next available slots */
for (auto current_offset : ds.GetScheduledDispatch()) {
if (current_offset >= dispatch_duration) continue;
DateTicksScaled current_departure = begin_time + current_offset;
for (const DispatchSlot &slot : ds.GetScheduledDispatch()) {
if (slot.offset >= dispatch_duration) continue;
DateTicksScaled current_departure = begin_time + slot.offset;
while (current_departure <= earliest_departure) {
current_departure += dispatch_duration;
}
/* Make sure the slots has not already been used previously in this departure board calculation */
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;
}
@ -151,7 +155,9 @@ static bool VehicleSetNextDepartureTime(Ticks *previous_departure, Ticks *waitin
*waiting_time = (actual_departure - date_ticks_base).AsTicks() - *previous_departure - order->GetTravelTime();
*previous_departure = (actual_departure - date_ticks_base).AsTicks();
slot_cache.insert(actual_departure);
if (!ds.GetScheduledDispatchReuseSlots()) {
slot_cache.insert(actual_departure);
}
/* Return true means that vehicle lateness should be clear from this point onward */
return true;

@ -2054,7 +2054,7 @@ STR_TMPL_CANT_RENAME :{WHITE}Can't re
STR_SCHDISPATCH_CAPTION :{WHITE}{VEHICLE} (Scheduled Dispatch)
STR_SCHDISPATCH_ENABLED :{BLACK}Enable
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_EXTRA :{BLACK}{STRING} Ctrl+Click to add multiple departure slots at once.
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_DAY :{BLACK}Delay (days)
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_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_ADJUST_CAPTION_MINUTE :{BLACK}Adjustment to add (minutes)
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_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_REUSE_DEPARTURE_SLOTS :{BLACK}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 :Re-use departure slots
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_SCHEDULE_ID :{BLACK}Schedule {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_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_DATE_WALLCLOCK_TINY_FLAGGED :{DATE_WALLCLOCK_TINY}*
STR_SCHDISPATCH_SUMMARY_NO_LAST_DEPARTURE :{BLACK}No previous departures.
STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_PAST :{BLACK}Last departure at {DATE_WALLCLOCK_TINY}.

@ -716,11 +716,28 @@ template <typename T, typename F> T CargoMaskValueFilter(CargoTypes &cargo_mask,
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 {
private:
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
uint32_t scheduled_dispatch_duration = 0; ///< Scheduled dispatch duration
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
* @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 RemoveScheduledDispatch(uint32_t offset);
void AdjustScheduledDispatch(int32_t adjust);

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

@ -647,11 +647,61 @@ CommandCost CmdScheduledDispatchSwapSchedules(TileIndex tile, DoCommandFlag flag
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.
* @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);
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)
{
/* Maintain sorted list status */
auto insert_position = std::lower_bound(this->scheduled_dispatch.begin(), this->scheduled_dispatch.end(), offset);
if (insert_position != this->scheduled_dispatch.end() && *insert_position == 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 == offset) {
return;
}
this->scheduled_dispatch.insert(insert_position, offset);
this->scheduled_dispatch.insert(insert_position, { offset, 0 });
this->UpdateScheduledDispatch(nullptr);
}
@ -680,8 +730,8 @@ void DispatchSchedule::AddScheduledDispatch(uint32_t offset)
void DispatchSchedule::RemoveScheduledDispatch(uint32_t offset)
{
/* Maintain sorted list status */
auto erase_position = std::lower_bound(this->scheduled_dispatch.begin(), this->scheduled_dispatch.end(), offset);
if (erase_position == this->scheduled_dispatch.end() || *erase_position != 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 != offset) {
return;
}
this->scheduled_dispatch.erase(erase_position);
@ -693,11 +743,11 @@ void DispatchSchedule::RemoveScheduledDispatch(uint32_t offset)
*/
void DispatchSchedule::AdjustScheduledDispatch(int32_t adjust)
{
for (uint32_t &time : this->scheduled_dispatch) {
int32_t t = (int32_t)time + adjust;
for (DispatchSlot &slot : this->scheduled_dispatch) {
int32_t t = (int32_t)slot.offset + adjust;
if (t < 0) 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());
}

@ -60,6 +60,7 @@ enum SchdispatchWidgets {
WID_SCHDISPATCH_MANAGEMENT, ///< Management button
WID_SCHDISPATCH_ADJUST, ///< Adjust 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
* @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 (offsets.size() == 0) return -1;
if (slots.size() == 0) return -1;
/* Number of time required to ensure all vehicle are counted */
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 */
std::vector<std::pair<uint32_t, int>> indices;
for (int i = 0; i < required_loop; i++) {
for (uint32_t offset : offsets) {
if (offset >= schedule_duration) continue;
indices.push_back(std::make_pair(i * schedule_duration + offset, 1));
indices.push_back(std::make_pair(i * schedule_duration + offset + timetable_duration, -1));
for (const DispatchSlot &slot : slots) {
if (slot.offset >= schedule_duration) continue;
indices.push_back(std::make_pair(i * schedule_duration + slot.offset, 1));
indices.push_back(std::make_pair(i * schedule_duration + slot.offset + timetable_duration, -1));
}
}
if (indices.empty()) return -1;
@ -204,6 +205,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
int arrow_flag_height = 0;
bool remove_slot_mode = false;
uint32_t selected_slot = UINT32_MAX;
enum ManagementDropdown {
SCH_MD_RESET_LAST_DISPATCHED,
@ -214,6 +216,10 @@ struct SchdispatchWindow : GeneralVehicleWindow {
SCH_MD_REUSE_DEPARTURE_SLOTS,
};
enum SlotManagementDropdown {
SCH_SMD_REUSE_DEPARTURE_SLOT,
};
SchdispatchWindow(WindowDesc *desc, WindowNumber window_number) :
GeneralVehicleWindow(desc, Vehicle::Get(window_number))
@ -248,6 +254,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
} else {
this->schedule_index = -1;
}
this->selected_slot = UINT32_MAX;
}
}
@ -256,6 +263,21 @@ struct SchdispatchWindow : GeneralVehicleWindow {
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
{
switch (widget) {
@ -263,7 +285,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
uint min_height = 0;
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));
this->delete_flag_width = UnScaleGUI(spr->width);
@ -351,6 +373,11 @@ struct SchdispatchWindow : GeneralVehicleWindow {
this->SetWidgetDisabledState(WID_SCHDISPATCH_MANAGEMENT, disabled);
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);
remove_slot_widget->SetDisabled(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_DUPLICATE_SCHEDULE_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);
return true;
}
@ -436,7 +469,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
* @param right Right side 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;
@ -462,7 +495,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
}
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
@ -502,16 +535,26 @@ struct SchdispatchWindow : GeneralVehicleWindow {
DateTicksScaled slot = GetScheduledDispatchTime(ds, _scaled_date_ticks);
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 (uint i = 0; i < this->num_columns && num < maxval; i++, num++) {
/* Draw all departure time in the current row */
if (current_schedule != ds.GetScheduledDispatch().end()) {
int x = r.left + (rtl ? (this->num_columns - i - 1) : i) * this->resize.step_width;
DateTicksScaled draw_time = start_tick + *current_schedule;
bool last = ds.GetScheduledDispatchLastDispatch() == (int32_t)*current_schedule;
bool next = next_offset == (int32_t)*current_schedule;
DateTicksScaled draw_time = start_tick + current_schedule->offset;
bool last = last_dispatch == (int32_t)current_schedule->offset;
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),
y, draw_time >= end_tick ? TC_RED : TC_BLACK, last, next);
y, colour, last, next, flags != 0);
current_schedule++;
} else {
break;
@ -699,8 +742,8 @@ struct SchdispatchWindow : GeneralVehicleWindow {
y += GetCharacterHeight(FS_NORMAL);
uint32_t duration = ds.GetScheduledDispatchDuration();
for (uint32_t slot : ds.GetScheduledDispatch()) {
if (slot >= duration) {
for (const DispatchSlot &slot : ds.GetScheduledDispatch()) {
if (slot.offset >= duration) {
draw_warning(STR_SCHDISPATCH_SLOT_OUTSIDE_SCHEDULE);
break;
}
@ -743,11 +786,26 @@ struct SchdispatchWindow : GeneralVehicleWindow {
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) {
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
@ -842,20 +900,26 @@ struct SchdispatchWindow : GeneralVehicleWindow {
add_item(STR_SCHDISPATCH_REMOVE_SCHEDULE, SCH_MD_REMOVE_SCHEDULE);
add_item(STR_SCHDISPATCH_DUPLICATE_SCHEDULE, SCH_MD_DUPLICATE_SCHEDULE);
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);
break;
}
case WID_SCHDISPATCH_PREV:
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();
break;
case WID_SCHDISPATCH_NEXT:
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();
break;
@ -885,6 +949,18 @@ struct SchdispatchWindow : GeneralVehicleWindow {
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:
if (!this->IsScheduleSelected()) break;
if (this->schedule_index > 0) {
@ -963,6 +1039,23 @@ struct SchdispatchWindow : GeneralVehicleWindow {
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:
@ -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_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_DROPDOWN, COLOUR_GREY, WID_SCHDISPATCH_MANAGE_SLOT), SetDataTip(STR_SCHDISPATCH_MANAGE_SLOT, STR_NULL), SetFill(1, 1), SetResize(1, 0),
EndContainer(),
NWidget(WWT_PANEL, COLOUR_GREY, WID_SCHDISPATCH_SUMMARY_PANEL), SetMinimalSize(400, 22), SetResize(1, 0), EndContainer(),
NWidget(NWID_HORIZONTAL),

@ -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_CUSTOM_BRIDGE_HEADS, XSCF_NULL, 4, 4, "custom_bridge_heads", nullptr, nullptr, nullptr },
{ 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_MULTIPLE_DOCKS, XSCF_NULL, 2, 2, "multiple_docks", nullptr, nullptr, nullptr },
{ XSLFI_TIMETABLE_EXTRA, XSCF_NULL, 7, 7, "timetable_extra", nullptr, nullptr, "ORDX" },

@ -22,6 +22,7 @@ std::vector<OrderList *> _jokerpp_non_auto_separation;
static uint16_t _old_scheduled_dispatch_start_full_date_fract;
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;
@ -264,7 +265,7 @@ static void Ptrs_ORDR()
SaveLoadTable GetDispatchScheduleDescription()
{
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_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)),
@ -278,6 +279,16 @@ SaveLoadTable GetDispatchScheduleDescription()
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()
{
static const SaveLoad _orderlist_desc[] = {
@ -291,11 +302,13 @@ SaveLoadTable GetOrderListDescription()
static std::vector<SaveLoad> _filtered_ordl_desc;
static std::vector<SaveLoad> _filtered_ordl_sd_desc;
static std::vector<SaveLoad> _filtered_ordl_slot_desc;
static void SetupDescs_ORDL()
{
_filtered_ordl_desc = SlFilterObject(GetOrderListDescription());
_filtered_ordl_sd_desc = SlFilterObject(GetDispatchScheduleDescription());
_filtered_ordl_slot_desc = SlFilterObject(GetDispatchSlotDescription());
}
static void Save_ORDL()
@ -309,6 +322,11 @@ static void Save_ORDL()
SlWriteUint32(list->GetScheduledDispatchScheduleCount());
for (DispatchSchedule &ds : list->GetScheduledDispatchScheduleSet()) {
SlObjectSaveFiltered(&ds, _filtered_ordl_sd_desc);
SlWriteUint32((uint32_t)ds.GetScheduledDispatchMutable().size());
for (DispatchSlot &slot : ds.GetScheduledDispatchMutable()) {
SlObjectSaveFiltered(&slot, _filtered_ordl_slot_desc);
}
}
}, list);
}
@ -344,9 +362,23 @@ static void Load_ORDL()
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;
}
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()

@ -807,10 +807,14 @@ DateTicksScaled GetScheduledDispatchTime(const DispatchSchedule &ds, DateTicksSc
DateTicksScaled first_slot = -1;
/* 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 ((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;

Loading…
Cancel
Save