Scheduled dispatch: Add per dispatch slot flags field
Add flag for slot re-use
This commit is contained in:
@@ -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();
|
||||
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;
|
||||
|
Reference in New Issue
Block a user