Scheduled dispatch: Add flag to re-use all dispatch slots

pull/642/head
Jonathan G Rennison 4 months ago
parent 423877374b
commit 5b7689a0aa

@ -295,6 +295,7 @@ CommandProc CmdScheduledDispatchRemove;
CommandProc CmdScheduledDispatchSetDuration;
CommandProcEx CmdScheduledDispatchSetStartDate;
CommandProc CmdScheduledDispatchSetDelay;
CommandProc CmdScheduledDispatchSetReuseSlots;
CommandProc CmdScheduledDispatchResetLastDispatch;
CommandProc CmdScheduledDispatchClear;
CommandProcEx CmdScheduledDispatchAddNewSchedule;
@ -558,6 +559,7 @@ static const Command _command_proc_table[] = {
DEF_CMD(CmdScheduledDispatchSetDuration, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SCHEDULED_DISPATCH_SET_DURATION
DEF_CMD(CmdScheduledDispatchSetStartDate, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SCHEDULED_DISPATCH_SET_START_DATE
DEF_CMD(CmdScheduledDispatchSetDelay, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SCHEDULED_DISPATCH_SET_DELAY
DEF_CMD(CmdScheduledDispatchSetReuseSlots, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SCHEDULED_DISPATCH_SET_REUSE_SLOTS
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(CmdScheduledDispatchAddNewSchedule, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SCHEDULED_DISPATCH_ADD_NEW_SCHEDULE

@ -512,6 +512,7 @@ enum Commands {
CMD_SCHEDULED_DISPATCH_SET_DURATION, ///< scheduled dispatch set schedule duration
CMD_SCHEDULED_DISPATCH_SET_START_DATE, ///< scheduled dispatch set start date
CMD_SCHEDULED_DISPATCH_SET_DELAY, ///< scheduled dispatch set maximum allow delay
CMD_SCHEDULED_DISPATCH_SET_REUSE_SLOTS, ///< scheduled dispatch set whether to re-use dispatch slots
CMD_SCHEDULED_DISPATCH_RESET_LAST_DISPATCH, ///< scheduled dispatch reset last dispatch date
CMD_SCHEDULED_DISPATCH_CLEAR, ///< scheduled dispatch clear schedule
CMD_SCHEDULED_DISPATCH_ADD_NEW_SCHEDULE, ///< scheduled dispatch add new schedule

@ -2095,6 +2095,8 @@ 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_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})
@ -2110,6 +2112,8 @@ STR_SCHDISPATCH_SUMMARY_L2 :{BLACK}This sch
STR_SCHDISPATCH_SUMMARY_L3 :{BLACK}This schedule began at {DATE_WALLCLOCK_TINY}, and ends at {DATE_WALLCLOCK_TINY}.
STR_SCHDISPATCH_SUMMARY_L4 :{BLACK}Maximum delay of {STRING1} is allowed before the slot is skipped.
STR_SCHDISPATCH_SUMMARY_NOT_ENABLED :{BLACK}This schedule is not active.
STR_SCHDISPATCH_SUMMARY_REUSE_SLOTS_ENABLED :{BLACK}All departure slots may be used more than once.
STR_SCHDISPATCH_SUMMARY_NEXT_AVAILABLE_DEPARTURE :{BLACK}Next available departure at {DATE_WALLCLOCK_TINY}.
STR_SCHDISPATCH_SLOT_OUTSIDE_SCHEDULE :{BLACK}One or more departure slots are outside the schedule duration.

@ -725,6 +725,7 @@ private:
/// this counts to (DAY_TICK * _settings_game.economy.day_length_factor)
int32_t scheduled_dispatch_last_dispatch = 0; ///< Last vehicle dispatched offset
int32_t scheduled_dispatch_max_delay = 0; ///< Maximum allowed delay
uint8_t scheduled_dispatch_flags = 0; ///< Flags
std::string name; ///< Name of dispatch schedule
@ -734,8 +735,16 @@ private:
this->scheduled_dispatch_start_tick = other.scheduled_dispatch_start_tick;
this->scheduled_dispatch_last_dispatch = other.scheduled_dispatch_last_dispatch;
this->scheduled_dispatch_max_delay = other.scheduled_dispatch_max_delay;
this->scheduled_dispatch_flags = other.scheduled_dispatch_flags;
}
/**
* Flag bit numbers for scheduled_dispatch_flags
*/
enum ScheduledDispatchFlags {
SDF_REUSE_SLOTS = 0, ///< Allow each dispatch slot to be used more than once
};
public:
/**
* Get the vector of all scheduled dispatch slot
@ -802,6 +811,18 @@ public:
*/
inline void SetScheduledDispatchDelay(int32_t delay) { this->scheduled_dispatch_max_delay = delay; }
/**
* Get whether to re-use dispatch slots
* @return whether dispatch slots are re-used
*/
inline bool GetScheduledDispatchReuseSlots() const { return HasBit(this->scheduled_dispatch_flags, SDF_REUSE_SLOTS); }
/**
* Set whether to re-use dispatch slots
* @param delay New maximum allow delay
*/
inline void SetScheduledDispatchReuseSlots(bool reuse_slots) { SB(this->scheduled_dispatch_flags, SDF_REUSE_SLOTS, 1, reuse_slots ? 1 : 0); }
/**
* Get the scheduled dispatch maximum alowed delay, in scaled tick
* @return scheduled dispatch last dispatch

@ -3058,7 +3058,6 @@ bool EvaluateDispatchSlotConditionalOrder(const Order *order, const Vehicle *v,
}
offset = last % sched.GetScheduledDispatchDuration();
} else {
extern DateTicksScaled GetScheduledDispatchTime(const DispatchSchedule &ds, DateTicksScaled leave_time);
DateTicksScaled slot = GetScheduledDispatchTime(sched, _scaled_date_ticks);
offset = (slot - sched.GetScheduledDispatchStartTick()).base() % sched.GetScheduledDispatchDuration();
}

@ -243,6 +243,39 @@ CommandCost CmdScheduledDispatchSetDelay(TileIndex tile, DoCommandFlag flags, ui
return CommandCost();
}
/**
* Set scheduled dispatch maximum allow delay
*
* @param tile Not used.
* @param flags Operation to perform.
* @param p1 Vehicle index
* @param p2 Whether to re-use slots
* @param text unused
* @return the cost of this operation or an error
*/
CommandCost CmdScheduledDispatchSetReuseSlots(TileIndex tile, DoCommandFlag flags, uint32_t p1, uint32_t 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 == nullptr) return CMD_ERROR;
if (schedule_index >= v->orders->GetScheduledDispatchScheduleCount()) return CMD_ERROR;
if (flags & DC_EXEC) {
v->orders->GetDispatchScheduleByIndex(schedule_index).SetScheduledDispatchReuseSlots(p2 != 0);
SetTimetableWindowsDirty(v, STWDF_SCHEDULED_DISPATCH);
}
return CommandCost();
}
/**
* Reset scheduled dispatch last dispatch vehicle time
*

@ -226,6 +226,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
SCH_MD_REMOVE_SCHEDULE,
SCH_MD_DUPLICATE_SCHEDULE,
SCH_MD_APPEND_VEHICLE_SCHEDULES,
SCH_MD_REUSE_DEPARTURE_SLOTS,
};
bool IsScheduleSelected() const
@ -276,7 +277,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
}
case WID_SCHDISPATCH_SUMMARY_PANEL:
size->height = 6 * GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.framerect.Vertical();
size->height = 7 * GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.framerect.Vertical();
uint warning_count = this->warning_count;
if (this->no_order_warning_pad) {
size->height -= GetCharacterHeight(FS_NORMAL);
@ -395,6 +396,7 @@ 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);
GuiShowTooltips(this, SPECSTR_TEMP_START, close_cond);
return true;
}
@ -484,6 +486,10 @@ struct SchdispatchWindow : GeneralVehicleWindow {
Rect ir = r.Shrink(WidgetDimensions::scaled.framerect);
int y = ir.top;
auto set_next_departure_update = [&](DateTicksScaled time) {
if (time < this->next_departure_update) const_cast<SchdispatchWindow*>(this)->next_departure_update = time;
};
if (!HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) || !this->IsScheduleSelected()) {
y += GetCharacterHeight(FS_NORMAL);
DrawString(ir.left, ir.right, y, STR_SCHDISPATCH_SUMMARY_NOT_ENABLED);
@ -508,6 +514,22 @@ struct SchdispatchWindow : GeneralVehicleWindow {
warnings++;
};
auto departure_time_warnings = [&](DateTicksScaled time) {
if (_settings_time.time_in_minutes && time > (_scaled_date_ticks + (1350 * (uint)_settings_time.ticks_per_minute))) {
/* If the departure slot is more than 23 hours ahead of now, show a warning */
const TickMinutes now = _settings_time.NowInTickMinutes();
const TickMinutes target = _settings_time.ToTickMinutes(time);
const TickMinutes delta = target - now;
if (delta >= (23 * 60)) {
const uint hours = delta.base() / 60;
SetDParam(0, hours);
draw_warning(STR_SCHDISPATCH_MORE_THAN_N_HOURS_IN_FUTURE);
set_next_departure_update(_settings_time.FromTickMinutes(target - (hours * 60) + 1));
}
}
};
bool have_conditional = false;
int schedule_order_index = -1;
for (int n = 0; n < v->GetNumOrders(); n++) {
@ -565,7 +587,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
StringID str;
if (_scaled_date_ticks < last_departure) {
str = STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_FUTURE;
const_cast<SchdispatchWindow*>(this)->next_departure_update = last_departure;
set_next_departure_update(last_departure);
} else {
str = STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_PAST;
}
@ -573,21 +595,19 @@ struct SchdispatchWindow : GeneralVehicleWindow {
DrawString(ir.left, ir.right, y, str);
y += GetCharacterHeight(FS_NORMAL);
if (_settings_time.time_in_minutes && last_departure > (_scaled_date_ticks + (1350 * (uint)_settings_time.ticks_per_minute))) {
/* If the departure slot is more than 23 hours ahead of now, show a warning */
const TickMinutes now = _settings_time.NowInTickMinutes();
const TickMinutes target = _settings_time.ToTickMinutes(last_departure);
const TickMinutes delta = target - now;
if (delta >= (23 * 60)) {
const uint hours = delta.base() / 60;
SetDParam(0, hours);
draw_warning(STR_SCHDISPATCH_MORE_THAN_N_HOURS_IN_FUTURE);
const_cast<SchdispatchWindow*>(this)->next_departure_update = _settings_time.FromTickMinutes(target - (hours * 60) + 1);
}
}
departure_time_warnings(last_departure);
if (!have_conditional) {
const DateTicksScaled next_departure = GetScheduledDispatchTime(ds, _scaled_date_ticks);
set_next_departure_update(next_departure + ds.GetScheduledDispatchDelay());
SetDParam(0, next_departure);
DrawString(ir.left, ir.right, y, STR_SCHDISPATCH_SUMMARY_NEXT_AVAILABLE_DEPARTURE);
y += GetCharacterHeight(FS_NORMAL);
departure_time_warnings(next_departure);
if (ds.GetScheduledDispatchReuseSlots()) {
DrawString(ir.left, ir.right, y, STR_SCHDISPATCH_SUMMARY_REUSE_SLOTS_ENABLED);
} else if (!have_conditional) {
const int required_vehicle = CalculateMaxRequiredVehicle(v->orders->GetTimetableTotalDuration(), ds.GetScheduledDispatchDuration(), ds.GetScheduledDispatch());
if (required_vehicle > 0) {
SetDParam(0, required_vehicle);
@ -742,6 +762,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
case WID_SCHDISPATCH_MANAGEMENT: {
if (!this->IsScheduleSelected()) break;
const DispatchSchedule &schedule = this->GetSelectedSchedule();
DropDownList list;
auto add_item = [&](StringID string, int result) {
std::unique_ptr<DropDownListStringItem> item(new DropDownListStringItem(string, result, false));
@ -753,6 +774,7 @@ 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));
ShowDropDownList(this, std::move(list), -1, WID_SCHDISPATCH_MANAGEMENT);
break;
}
@ -860,6 +882,11 @@ struct SchdispatchWindow : GeneralVehicleWindow {
SetObjectToPlaceWnd(clone_icons[this->vehicle->type], PAL_NONE, HT_VEHICLE, this);
break;
}
case SCH_MD_REUSE_DEPARTURE_SLOTS: {
DoCommandP(0, this->vehicle->index | (this->schedule_index << 20), this->GetSelectedSchedule().GetScheduledDispatchReuseSlots() ? 0 : 1, CMD_SCHEDULED_DISPATCH_SET_REUSE_SLOTS | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE));
break;
}
}
}

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

@ -271,7 +271,8 @@ SaveLoadTable GetDispatchScheduleDescription()
SLE_CONDVAR_X(DispatchSchedule, scheduled_dispatch_start_tick, SLE_INT64, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 5)),
SLE_VAR(DispatchSchedule, scheduled_dispatch_last_dispatch, SLE_INT32),
SLE_VAR(DispatchSchedule, scheduled_dispatch_max_delay, SLE_INT32),
SLE_CONDSSTR_X(DispatchSchedule, name, 0, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 4)),
SLE_CONDSSTR_X(DispatchSchedule, name, 0, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 4)),
SLE_CONDVAR_X(DispatchSchedule, scheduled_dispatch_flags, SLE_FILE_U32 | SLE_VAR_U8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 6)),
};
return _order_extra_info_desc;

@ -40,4 +40,7 @@ struct TimetableProgress {
std::vector<TimetableProgress> PopulateSeparationState(const Vehicle *v_start);
struct DispatchSchedule;
DateTicksScaled GetScheduledDispatchTime(const DispatchSchedule &ds, DateTicksScaled leave_time);
#endif /* TIMETABLE_H */

@ -789,11 +789,12 @@ void UpdateSeparationOrder(Vehicle *v_start)
DateTicksScaled GetScheduledDispatchTime(const DispatchSchedule &ds, DateTicksScaled leave_time)
{
DateTicksScaled first_slot = -1;
const DateTicksScaled begin_time = ds.GetScheduledDispatchStartTick();
const int32_t last_dispatched_offset = ds.GetScheduledDispatchLastDispatch();
DateTicksScaled first_slot = -1;
const DateTicksScaled begin_time = ds.GetScheduledDispatchStartTick() - (ds.GetScheduledDispatchReuseSlots() ? ds.GetScheduledDispatchDuration() : 0);
const int32_t last_dispatched_offset = ds.GetScheduledDispatchReuseSlots() ? -1 : ds.GetScheduledDispatchLastDispatch();
const uint32_t dispatch_duration = ds.GetScheduledDispatchDuration();
const int32_t max_delay = ds.GetScheduledDispatchDelay();
const DateTicksScaled minimum = leave_time - max_delay;
/* Find next available slots */
for (auto current_offset : ds.GetScheduledDispatch()) {
@ -803,7 +804,6 @@ DateTicksScaled GetScheduledDispatchTime(const DispatchSchedule &ds, DateTicksSc
}
DateTicksScaled current_departure = begin_time + current_offset;
DateTicksScaled minimum = leave_time - max_delay;
if (current_departure < minimum) {
current_departure += dispatch_duration * ((minimum + dispatch_duration - current_departure - 1) / dispatch_duration);
}

@ -190,7 +190,6 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID
if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) && 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->GetDispatchScheduleByIndex(order->GetDispatchScheduleIndex());
DispatchSchedule predicted_ds;
predicted_ds.BorrowSchedule(ds);

Loading…
Cancel
Save