From 0aeafeaf3a5d999fb54a04abadd478024cfdd363 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 28 Nov 2021 02:25:42 +0000 Subject: [PATCH] Allow adding multiple scheduled dispatch departure slots at once --- src/command.cpp | 2 +- src/lang/english.txt | 7 ++ src/schdispatch_cmd.cpp | 14 ++- src/schdispatch_gui.cpp | 263 ++++++++++++++++++++++++++++++++++++++-- 4 files changed, 277 insertions(+), 9 deletions(-) diff --git a/src/command.cpp b/src/command.cpp index 90f5d4ac5f..fc56c45f48 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -265,7 +265,7 @@ CommandProc CmdRemoveSignalInstruction; CommandProc CmdSignalProgramMgmt; CommandProc CmdScheduledDispatch; -CommandProc CmdScheduledDispatchAdd; +CommandProcEx CmdScheduledDispatchAdd; CommandProc CmdScheduledDispatchRemove; CommandProc CmdScheduledDispatchSetDuration; CommandProc CmdScheduledDispatchSetStartDate; diff --git a/src/lang/english.txt b/src/lang/english.txt index 2bc0d074bd..2bbf28fdb4 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -7063,6 +7063,7 @@ 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_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 STR_SCHDISPATCH_DURATION :{BLACK}Duration STR_SCHDISPATCH_DURATION_TOOLTIP :{BLACK}Set duration of this schedule. @@ -7091,6 +7092,12 @@ STR_SCHDISPATCH_SUMMARY_NOT_ENABLED :{BLACK}This sch STR_SCHDISPATCH_SLOT_OUTSIDE_SCHEDULE :{BLACK}One or more departure slots are outside the schedule duration. +STR_SCHDISPATCH_ADD_DEPARTURE_SLOTS_START :{BLACK}Start: +STR_SCHDISPATCH_ADD_DEPARTURE_SLOTS_STEP :{BLACK}Period: +STR_SCHDISPATCH_ADD_DEPARTURE_SLOTS_END :{BLACK}End: + +STR_ERROR_SCHDISPATCH_TRIED_TO_ADD_TOO_MANY_SLOTS :{WHITE}Tried to add too many departure slots at once + # Modifier key toggle window STR_MODIFIER_KEY_TOGGLE_CAPTION :{WHITE}Modifier keys STR_SHIFT_KEY_NAME :{BLACK}Shift diff --git a/src/schdispatch_cmd.cpp b/src/schdispatch_cmd.cpp index ffd3825c4d..1ad8d7ae2c 100644 --- a/src/schdispatch_cmd.cpp +++ b/src/schdispatch_cmd.cpp @@ -73,12 +73,17 @@ CommandCost CmdScheduledDispatch(TileIndex tile, DoCommandFlag flags, uint32 p1, * @param flags Operation to perform. * @param p1 Vehicle index. * @param p2 Offset time to add. + * @param p3 various bitstuffed elements + * - p3 = (bit 0 - 31) - the offset for additional slots + * - p3 = (bit 32 - 47) - the number of additional slots to add * @param text unused * @return the cost of this operation or an error */ -CommandCost CmdScheduledDispatchAdd(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +CommandCost CmdScheduledDispatchAdd(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, uint64 p3, const char *text, uint32 binary_length) { VehicleID veh = GB(p1, 0, 20); + uint32 offset = GB(p3, 0, 32); + uint32 extra_slots = GB(p3, 32, 16); Vehicle *v = Vehicle::GetIfValid(veh); if (v == nullptr || !v->IsPrimaryVehicle()) return CMD_ERROR; @@ -88,8 +93,15 @@ CommandCost CmdScheduledDispatchAdd(TileIndex tile, DoCommandFlag flags, uint32 if (v->orders.list == nullptr) return CMD_ERROR; + if (extra_slots > 512) return_cmd_error(STR_ERROR_SCHDISPATCH_TRIED_TO_ADD_TOO_MANY_SLOTS); + if (extra_slots > 0 && offset == 0) return CMD_ERROR; + if (flags & DC_EXEC) { v->orders.list->AddScheduledDispatch(p2); + for (uint i = 0; i < extra_slots; i++) { + p2 += offset; + v->orders.list->AddScheduledDispatch(p2); + } SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index); } diff --git a/src/schdispatch_gui.cpp b/src/schdispatch_gui.cpp index ad23a3eb9e..30d6040dce 100644 --- a/src/schdispatch_gui.cpp +++ b/src/schdispatch_gui.cpp @@ -25,6 +25,7 @@ #include "settings_type.h" #include "viewport_func.h" #include "zoom_func.h" +#include "core/geometry_func.hpp" #include #include @@ -84,7 +85,7 @@ static void SetScheduleStartDateCallback(const Window *w, DateTicksScaled date) * @param p1 The p1 parameter to send to CmdScheduledDispatchAdd * @param date the actually chosen date */ -static void ScheduleAddIntl(uint32 p1, DateTicksScaled date) +static void ScheduleAddIntl(uint32 p1, DateTicksScaled date, uint extra_slots, uint offset) { VehicleID veh = GB(p1, 0, 20); Vehicle *v = Vehicle::GetIfValid(veh); @@ -92,10 +93,18 @@ static void ScheduleAddIntl(uint32 p1, DateTicksScaled date) /* Make sure the time is the closest future to the timetable start */ DateTicksScaled start_tick = v->orders.list->GetScheduledDispatchStartTick(); - while (date > start_tick) date -= v->orders.list->GetScheduledDispatchDuration(); - while (date < start_tick) date += v->orders.list->GetScheduledDispatchDuration(); + uint32 duration = v->orders.list->GetScheduledDispatchDuration(); + while (date > start_tick) date -= duration; + while (date < start_tick) date += duration; + + if (extra_slots > 0 && offset > 0) { + DateTicksScaled end_tick = start_tick + duration; + DateTicksScaled max_extra_slots = (end_tick - 1 - date) / offset; + if (max_extra_slots < extra_slots) extra_slots = static_cast(std::max(0, max_extra_slots)); + extra_slots = std::min(extra_slots, UINT16_MAX); + } - DoCommandP(0, v->index, (uint32)(date - start_tick), CMD_SCHEDULED_DISPATCH_ADD | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); + DoCommandPEx(0, v->index, (uint32)(date - start_tick), (((uint64)extra_slots) << 32) | offset, CMD_SCHEDULED_DISPATCH_ADD | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE), nullptr, nullptr, 0); } /** @@ -105,7 +114,7 @@ static void ScheduleAddIntl(uint32 p1, DateTicksScaled date) */ static void ScheduleAddCallback(const Window *w, DateTicksScaled date) { - ScheduleAddIntl(w->window_number, date); + ScheduleAddIntl(w->window_number, date, 0, 0); } /** @@ -265,6 +274,26 @@ struct SchdispatchWindow : Window { } } + virtual bool OnTooltip(Point pt, int widget, TooltipCloseCondition close_cond) override + { + switch (widget) { + case WID_SCHDISPATCH_ADD: { + if (_settings_time.time_in_minutes) { + uint64 params[1]; + params[0] = STR_SCHDISPATCH_ADD_TOOLTIP; + GuiShowTooltips(this, STR_SCHDISPATCH_ADD_TOOLTIP_EXTRA, 1, params, close_cond); + return true; + } + break; + } + + default: + break; + } + + return false; + } + /** * Draw a time in the box with the top left corner at x,y. * @param time Time to draw. @@ -486,7 +515,10 @@ struct SchdispatchWindow : Window { } case WID_SCHDISPATCH_ADD: { - if (_settings_time.time_in_minutes && _settings_client.gui.timetable_start_text_entry) { + if (_settings_time.time_in_minutes && _ctrl_pressed) { + void ShowScheduledDispatchAddSlotsWindow(SchdispatchWindow *parent, int window_number); + ShowScheduledDispatchAddSlotsWindow(this, v->index); + } else if (_settings_time.time_in_minutes && _settings_client.gui.timetable_start_text_entry) { ShowQueryString(STR_EMPTY, STR_SCHDISPATCH_ADD_CAPTION, 31, this, CS_NUMERAL, QSF_NONE); } else { ShowSetDateWindow(this, v->index, _scaled_date_ticks, _cur_year, _cur_year + 15, ScheduleAddCallback, STR_SCHDISPATCH_ADD, STR_SCHDISPATCH_ADD_TOOLTIP); @@ -548,7 +580,7 @@ struct SchdispatchWindow : Window { DateTicksScaled slot = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), hours, minutes); slot -= _settings_time.clock_offset; slot *= _settings_time.ticks_per_minute; - ScheduleAddIntl(v->index, slot); + ScheduleAddIntl(v->index, slot, 0, 0); } break; } @@ -614,6 +646,16 @@ struct SchdispatchWindow : Window { { return this->vehicle; } + + void AddMultipleDepartureSlots(uint start, uint step, uint end) + { + if (end < start || step == 0) return; + + DateTicksScaled slot = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), 0, start); + slot -= _settings_time.clock_offset; + slot *= _settings_time.ticks_per_minute; + ScheduleAddIntl(this->vehicle->index, slot, (end - start) / step, step * _settings_time.ticks_per_minute); + } }; static const NWidgetPart _nested_schdispatch_widgets[] = { @@ -661,3 +703,210 @@ void ShowSchdispatchWindow(const Vehicle *v) { AllocateWindowDescFront(&_schdispatch_desc, v->index); } + +enum ScheduledDispatchAddSlotsWindowWidgets { + WID_SCHDISPATCH_ADD_SLOT_START_HOUR, + WID_SCHDISPATCH_ADD_SLOT_START_MINUTE, + WID_SCHDISPATCH_ADD_SLOT_STEP_HOUR, + WID_SCHDISPATCH_ADD_SLOT_STEP_MINUTE, + WID_SCHDISPATCH_ADD_SLOT_END_HOUR, + WID_SCHDISPATCH_ADD_SLOT_END_MINUTE, + WID_SCHDISPATCH_ADD_SLOT_ADD_BUTTON, + WID_SCHDISPATCH_ADD_SLOT_START_TEXT, + WID_SCHDISPATCH_ADD_SLOT_STEP_TEXT, + WID_SCHDISPATCH_ADD_SLOT_END_TEXT, +}; + +struct ScheduledDispatchAddSlotsWindow : Window { + uint start; + uint step; + uint end; + + ScheduledDispatchAddSlotsWindow(WindowDesc *desc, WindowNumber window_number, SchdispatchWindow *parent) : + Window(desc) + { + this->start = (_scaled_date_ticks / _settings_time.ticks_per_minute) % (60 * 24); + this->step = 30; + this->end = this->start + 60; + this->parent = parent; + this->CreateNestedTree(); + this->FinishInitNested(window_number); + } + + Point OnInitialPosition(int16 sm_width, int16 sm_height, int window_number) override + { + Point pt = { this->parent->left + this->parent->width / 2 - sm_width / 2, this->parent->top + this->parent->height / 2 - sm_height / 2 }; + return pt; + } + + virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override + { + Dimension d = {0, 0}; + switch (widget) { + default: return; + + case WID_SCHDISPATCH_ADD_SLOT_START_TEXT: + case WID_SCHDISPATCH_ADD_SLOT_STEP_TEXT: + case WID_SCHDISPATCH_ADD_SLOT_END_TEXT: + d = maxdim(d, GetStringBoundingBox(STR_SCHDISPATCH_ADD_DEPARTURE_SLOTS_START)); + d = maxdim(d, GetStringBoundingBox(STR_SCHDISPATCH_ADD_DEPARTURE_SLOTS_STEP)); + d = maxdim(d, GetStringBoundingBox(STR_SCHDISPATCH_ADD_DEPARTURE_SLOTS_END)); + break; + + case WID_SCHDISPATCH_ADD_SLOT_START_HOUR: + case WID_SCHDISPATCH_ADD_SLOT_STEP_HOUR: + case WID_SCHDISPATCH_ADD_SLOT_END_HOUR: + for (uint i = 0; i < 24; i++) { + SetDParam(0, i); + d = maxdim(d, GetStringBoundingBox(STR_JUST_INT)); + } + break; + + case WID_SCHDISPATCH_ADD_SLOT_START_MINUTE: + case WID_SCHDISPATCH_ADD_SLOT_STEP_MINUTE: + case WID_SCHDISPATCH_ADD_SLOT_END_MINUTE: + for (uint i = 0; i < 60; i++) { + SetDParam(0, i); + d = maxdim(d, GetStringBoundingBox(STR_JUST_INT)); + } + break; + } + + d.width += padding.width; + d.height += padding.height; + *size = d; + } + + virtual void SetStringParameters(int widget) const override + { + switch (widget) { + case WID_SCHDISPATCH_ADD_SLOT_START_HOUR: SetDParam(0, MINUTES_HOUR(start)); break; + case WID_SCHDISPATCH_ADD_SLOT_START_MINUTE: SetDParam(0, MINUTES_MINUTE(start)); break; + case WID_SCHDISPATCH_ADD_SLOT_STEP_HOUR: SetDParam(0, MINUTES_HOUR(step)); break; + case WID_SCHDISPATCH_ADD_SLOT_STEP_MINUTE: SetDParam(0, MINUTES_MINUTE(step)); break; + case WID_SCHDISPATCH_ADD_SLOT_END_HOUR: SetDParam(0, MINUTES_HOUR(end)); break; + case WID_SCHDISPATCH_ADD_SLOT_END_MINUTE: SetDParam(0, MINUTES_MINUTE(end)); break; + } + } + + virtual void OnClick(Point pt, int widget, int click_count) override + { + auto handle_hours_dropdown = [&](uint current) { + DropDownList list; + for (uint i = 0; i < 24; i++) { + DropDownListParamStringItem *item = new DropDownListParamStringItem(STR_JUST_INT, i, false); + item->SetParam(0, i); + list.emplace_back(item); + } + ShowDropDownList(this, std::move(list), MINUTES_HOUR(current), widget); + }; + + auto handle_minutes_dropdown = [&](uint current) { + DropDownList list; + for (uint i = 0; i < 60; i++) { + DropDownListParamStringItem *item = new DropDownListParamStringItem(STR_JUST_INT, i, false); + item->SetParam(0, i); + list.emplace_back(item); + } + ShowDropDownList(this, std::move(list), MINUTES_MINUTE(current), widget); + }; + + switch (widget) { + case WID_SCHDISPATCH_ADD_SLOT_START_HOUR: + handle_hours_dropdown(this->start); + break; + case WID_SCHDISPATCH_ADD_SLOT_START_MINUTE: + handle_minutes_dropdown(this->start); + break; + case WID_SCHDISPATCH_ADD_SLOT_STEP_HOUR: + handle_hours_dropdown(this->step); + break; + case WID_SCHDISPATCH_ADD_SLOT_STEP_MINUTE: + handle_minutes_dropdown(this->step); + break; + case WID_SCHDISPATCH_ADD_SLOT_END_HOUR: + handle_hours_dropdown(this->end); + break; + case WID_SCHDISPATCH_ADD_SLOT_END_MINUTE: + handle_minutes_dropdown(this->end); + break; + + case WID_SCHDISPATCH_ADD_SLOT_ADD_BUTTON: + static_cast(this->parent)->AddMultipleDepartureSlots(this->start, this->step, this->end); + delete this; + break; + } + } + + virtual void OnDropdownSelect(int widget, int index) override + { + switch (widget) { + case WID_SCHDISPATCH_ADD_SLOT_START_HOUR: + this->start = MINUTES_DATE(0, index, MINUTES_MINUTE(this->start)); + break; + case WID_SCHDISPATCH_ADD_SLOT_START_MINUTE: + this->start = MINUTES_DATE(0, MINUTES_HOUR(this->start), index); + break; + case WID_SCHDISPATCH_ADD_SLOT_STEP_HOUR: + this->step = MINUTES_DATE(0, index, MINUTES_MINUTE(this->step)); + break; + case WID_SCHDISPATCH_ADD_SLOT_STEP_MINUTE: + this->step = MINUTES_DATE(0, MINUTES_HOUR(this->step), index); + break; + case WID_SCHDISPATCH_ADD_SLOT_END_HOUR: + this->end = MINUTES_DATE(0, index, MINUTES_MINUTE(this->end)); + break; + case WID_SCHDISPATCH_ADD_SLOT_END_MINUTE: + this->end = MINUTES_DATE(0, MINUTES_HOUR(this->end), index); + break; + } + + + this->SetWidgetDirty(widget); + } +}; + +static const NWidgetPart _nested_scheduled_dispatch_add_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_BROWN), + NWidget(WWT_CAPTION, COLOUR_BROWN), SetDataTip(STR_TIME_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + EndContainer(), + NWidget(WWT_PANEL, COLOUR_BROWN), + NWidget(NWID_VERTICAL), SetPIP(6, 6, 6), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(6, 6, 6), + NWidget(WWT_TEXT, COLOUR_BROWN, WID_SCHDISPATCH_ADD_SLOT_START_TEXT), SetDataTip(STR_SCHDISPATCH_ADD_DEPARTURE_SLOTS_START, STR_NULL), + NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_SCHDISPATCH_ADD_SLOT_START_HOUR), SetFill(1, 0), SetDataTip(STR_JUST_INT, STR_DATE_MINUTES_HOUR_TOOLTIP), + NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_SCHDISPATCH_ADD_SLOT_START_MINUTE), SetFill(1, 0), SetDataTip(STR_JUST_INT, STR_DATE_MINUTES_MINUTE_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(6, 6, 6), + NWidget(WWT_TEXT, COLOUR_BROWN, WID_SCHDISPATCH_ADD_SLOT_STEP_TEXT), SetDataTip(STR_SCHDISPATCH_ADD_DEPARTURE_SLOTS_STEP, STR_NULL), + NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_SCHDISPATCH_ADD_SLOT_STEP_HOUR), SetFill(1, 0), SetDataTip(STR_JUST_INT, STR_DATE_MINUTES_HOUR_TOOLTIP), + NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_SCHDISPATCH_ADD_SLOT_STEP_MINUTE), SetFill(1, 0), SetDataTip(STR_JUST_INT, STR_DATE_MINUTES_MINUTE_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), SetPIP(6, 6, 6), + NWidget(WWT_TEXT, COLOUR_BROWN, WID_SCHDISPATCH_ADD_SLOT_END_TEXT), SetDataTip(STR_SCHDISPATCH_ADD_DEPARTURE_SLOTS_END, STR_NULL), + NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_SCHDISPATCH_ADD_SLOT_END_HOUR), SetFill(1, 0), SetDataTip(STR_JUST_INT, STR_DATE_MINUTES_HOUR_TOOLTIP), + NWidget(WWT_DROPDOWN, COLOUR_ORANGE, WID_SCHDISPATCH_ADD_SLOT_END_MINUTE), SetFill(1, 0), SetDataTip(STR_JUST_INT, STR_DATE_MINUTES_MINUTE_TOOLTIP), + EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetFill(1, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_BROWN, WID_SCHDISPATCH_ADD_SLOT_ADD_BUTTON), SetMinimalSize(100, 12), SetDataTip(STR_SCHDISPATCH_ADD, STR_SCHDISPATCH_ADD_TOOLTIP), + NWidget(NWID_SPACER), SetFill(1, 0), + EndContainer(), + EndContainer(), + EndContainer() +}; + +static WindowDesc _scheduled_dispatch_add_desc( + WDP_CENTER, nullptr, 0, 0, + WC_SET_DATE, WC_NONE, + 0, + _nested_scheduled_dispatch_add_widgets, lengthof(_nested_scheduled_dispatch_add_widgets) +); + +void ShowScheduledDispatchAddSlotsWindow(SchdispatchWindow *parent, int window_number) +{ + DeleteWindowByClass(WC_SET_DATE); + + new ScheduledDispatchAddSlotsWindow(&_scheduled_dispatch_add_desc, window_number, parent); +}