diff --git a/src/command.cpp b/src/command.cpp index 58998e35ee..b4355e826f 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -294,6 +294,7 @@ CommandProc CmdScheduledDispatchResetLastDispatch; CommandProc CmdScheduledDispatchClear; CommandProcEx CmdScheduledDispatchAddNewSchedule; CommandProc CmdScheduledDispatchRemoveSchedule; +CommandProc CmdScheduledDispatchRenameSchedule; CommandProc CmdAddPlan; CommandProcEx CmdAddPlanLine; @@ -549,6 +550,7 @@ static const Command _command_proc_table[] = { DEF_CMD(CmdScheduledDispatchClear, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SCHEDULED_DISPATCH_CLEAR DEF_CMD(CmdScheduledDispatchAddNewSchedule, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SCHEDULED_DISPATCH_ADD_NEW_SCHEDULE DEF_CMD(CmdScheduledDispatchRemoveSchedule, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SCHEDULED_DISPATCH_REMOVE_SCHEDULE + DEF_CMD(CmdScheduledDispatchRenameSchedule, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SCHEDULED_DISPATCH_RENAME_SCHEDULE 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 diff --git a/src/command_type.h b/src/command_type.h index c82cf9cf8a..4badfd358f 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -516,6 +516,7 @@ enum Commands { CMD_SCHEDULED_DISPATCH_CLEAR, ///< scheduled dispatch clear schedule CMD_SCHEDULED_DISPATCH_ADD_NEW_SCHEDULE, ///< scheduled dispatch add new schedule CMD_SCHEDULED_DISPATCH_REMOVE_SCHEDULE, ///< scheduled dispatch remove schedule + CMD_SCHEDULED_DISPATCH_RENAME_SCHEDULE, ///< scheduled dispatch rename schedule CMD_ADD_PLAN, CMD_ADD_PLAN_LINE, diff --git a/src/lang/extra/english.txt b/src/lang/extra/english.txt index fd738d29f7..fc80d530e4 100644 --- a/src/lang/extra/english.txt +++ b/src/lang/extra/english.txt @@ -1632,6 +1632,7 @@ STR_TIMETABLE_SCHEDULED_DISPATCH_TOOLTIP :{BLACK}Open sch STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER :{PUSH_COLOUR}{YELLOW}[{STRING1}scheduled dispatch]{POP_COLOUR} STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME :{PUSH_COLOUR}{RED}[{STRING1}scheduled dispatch - no wait time timetabled]{POP_COLOUR} STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_SCHEDULE_INDEX :{NUM}{NBSP}-{NBSP} +STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NAMED_SCHEDULE :{RAW_STRING}{NBSP}-{NBSP} STR_TIMETABLE_LOCK_ORDER_TIME_TOOLTIP :{BLACK}Lock/unlock the amount of time for the highlighted order (Ctrl+Click set lock state for all orders).{}When locked the time will not be changed by timetable autofill or automate. @@ -1799,6 +1800,7 @@ STR_JUST_TIME_HHMM :{TIME_HHMM} STR_JUST_STRING1 :{STRING1} STR_JUST_STRING2 :{STRING2} +STR_JUST_STRING3 :{STRING3} STR_JUST_VELOCITY :{VELOCITY} @@ -2005,6 +2007,10 @@ STR_SCHDISPATCH_REMOVE_SCHEDULE :{BLACK}Remove C STR_SCHDISPATCH_REMOVE_SCHEDULE_TOOLTIP :{BLACK}Remove this dispatch schedule entirely. 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}) +STR_SCHDISPATCH_RENAME_SCHEDULE_TOOLTIP :{BLACK}Name schedule +STR_SCHDISPATCH_RENAME_SCHEDULE_CAPTION :{WHITE}Name schedule +STR_ERROR_CAN_T_RENAME_SCHEDULE :{WHITE}Can't name schedule... STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_PAST :{BLACK}Last departure at {DATE_WALLCLOCK_TINY}. STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_FUTURE :{BLACK}Last departure has not left yet, it will depart at {DATE_WALLCLOCK_TINY}. diff --git a/src/order_base.h b/src/order_base.h index f633bfa8d3..1f675ada22 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -697,6 +697,8 @@ private: int32 scheduled_dispatch_last_dispatch = 0; ///< Last vehicle dispatched offset int32 scheduled_dispatch_max_delay = 0; ///< Maximum allowed delay + std::string name; ///< Name of dispatch schedule + inline void CopyBasicFields(const DispatchSchedule &other) { this->scheduled_dispatch_duration = other.scheduled_dispatch_duration; @@ -801,6 +803,9 @@ public: { other.scheduled_dispatch = std::move(this->scheduled_dispatch); } + + inline std::string &ScheduleName() { return this->name; } + inline const std::string &ScheduleName() const { return this->name; } }; /** diff --git a/src/order_gui.cpp b/src/order_gui.cpp index aa7720a495..4fb965592e 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -1075,11 +1075,16 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int } else if (ocv == OCV_DISPATCH_SLOT) { SetDParam(0, STR_ORDER_CONDITIONAL_DISPATCH_SLOT_DISPLAY); if (GB(order->GetXData(), 0, 16) != UINT16_MAX) { - char buf[256]; - int64 args_array[] = { GB(order->GetXData(), 0, 16) + 1 }; - StringParameters tmp_params(args_array); - char *end = GetStringWithArgs(buf, STR_TIMETABLE_ASSIGN_SCHEDULE_ID, &tmp_params, lastof(buf)); - _temp_special_strings[0].assign(buf, end); + const DispatchSchedule &ds = v->orders->GetDispatchScheduleByIndex(GB(order->GetXData(), 0, 16)); + if (ds.ScheduleName().empty()) { + char buf[256]; + int64 args_array[] = { GB(order->GetXData(), 0, 16) + 1 }; + StringParameters tmp_params(args_array); + char *end = GetStringWithArgs(buf, STR_TIMETABLE_ASSIGN_SCHEDULE_ID, &tmp_params, lastof(buf)); + _temp_special_strings[0].assign(buf, end); + } else { + _temp_special_strings[0] = ds.ScheduleName(); + } SetDParam(2, SPECSTR_TEMP_START); } else { SetDParam(2, STR_TIMETABLE_ASSIGN_SCHEDULE_NONE); @@ -1199,8 +1204,14 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int } if (timetable && HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) && order->IsScheduledDispatchOrder(false) && edge != 0) { StringID str = order->IsWaitTimetabled() ? STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER : STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME; - SetDParam(0, v->orders->GetScheduledDispatchScheduleCount() > 1 ? STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_SCHEDULE_INDEX : STR_EMPTY); - SetDParam(1, order->GetDispatchScheduleIndex() + 1); + const DispatchSchedule &ds = v->orders->GetDispatchScheduleByIndex(order->GetDispatchScheduleIndex()); + if (!ds.ScheduleName().empty()) { + SetDParam(0, STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NAMED_SCHEDULE); + SetDParamStr(1, ds.ScheduleName().c_str()); + } else { + SetDParam(0, v->orders->GetScheduledDispatchScheduleCount() > 1 ? STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_SCHEDULE_INDEX : STR_EMPTY); + SetDParam(1, order->GetDispatchScheduleIndex() + 1); + } edge = DrawString(rtl ? left : edge + 3, rtl ? edge - 3 : right, y, str, colour); } @@ -2489,8 +2500,14 @@ public: const Order *order = this->vehicle->GetOrder(sel); if (order != nullptr && order->IsType(OT_CONDITIONAL) && GB(order->GetXData(), 0, 16) != UINT16_MAX) { - SetDParam(0, STR_TIMETABLE_ASSIGN_SCHEDULE_ID); - SetDParam(1, GB(order->GetXData(), 0, 16) + 1); + const DispatchSchedule &ds = this->vehicle->orders->GetDispatchScheduleByIndex(GB(order->GetXData(), 0, 16)); + if (ds.ScheduleName().empty()) { + SetDParam(0, STR_TIMETABLE_ASSIGN_SCHEDULE_ID); + SetDParam(1, GB(order->GetXData(), 0, 16) + 1); + } else { + SetDParam(0, STR_JUST_RAW_STRING); + SetDParamStr(1, ds.ScheduleName().c_str()); + } } else { SetDParam(0, STR_TIMETABLE_ASSIGN_SCHEDULE_NONE); } @@ -2811,9 +2828,15 @@ public: uint count = this->vehicle->orders->GetScheduledDispatchScheduleCount(); DropDownList list; for (uint i = 0; i < count; ++i) { - DropDownListParamStringItem *item = new DropDownListParamStringItem(STR_TIMETABLE_ASSIGN_SCHEDULE_ID, i, false); - item->SetParam(0, i + 1); - list.emplace_back(item); + const DispatchSchedule &ds = this->vehicle->orders->GetDispatchScheduleByIndex(i); + if (ds.ScheduleName().empty()) { + DropDownListParamStringItem *item = new DropDownListParamStringItem(STR_TIMETABLE_ASSIGN_SCHEDULE_ID, i, false); + item->SetParam(0, i + 1); + list.emplace_back(item); + } else { + DropDownListCharStringItem *item = new DropDownListCharStringItem(ds.ScheduleName(), i, false); + list.emplace_back(item); + } } if (!list.empty()) ShowDropDownList(this, std::move(list), selected, WID_O_COND_SCHED_SELECT, 0); break; diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index de15f134b7..ed8ba01540 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -115,7 +115,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, 3, 3, "scheduled_dispatch", nullptr, nullptr, nullptr }, + { XSLFI_SCHEDULED_DISPATCH, XSCF_NULL, 4, 4, "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" }, diff --git a/src/saveload/order_sl.cpp b/src/saveload/order_sl.cpp index 5caefb12ae..721a5a0d30 100644 --- a/src/saveload/order_sl.cpp +++ b/src/saveload/order_sl.cpp @@ -266,6 +266,7 @@ SaveLoadTable GetDispatchScheduleDescription() SLE_VAR(DispatchSchedule, scheduled_dispatch_start_full_date_fract, SLE_UINT16), 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)), }; return _order_extra_info_desc; diff --git a/src/schdispatch_cmd.cpp b/src/schdispatch_cmd.cpp index 51716a2626..210497bb12 100644 --- a/src/schdispatch_cmd.cpp +++ b/src/schdispatch_cmd.cpp @@ -411,6 +411,49 @@ CommandCost CmdScheduledDispatchRemoveSchedule(TileIndex tile, DoCommandFlag fla return CommandCost(); } +/** + * Rename scheduled dispatch schedule + * + * @param tile Not used. + * @param flags Operation to perform. + * @param p1 Vehicle index + * @param p2 Not used + * @param text name + * @return the cost of this operation or an error + */ +CommandCost CmdScheduledDispatchRenameSchedule(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 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; + + bool reset = StrEmpty(text); + + if (!reset) { + if (Utf8StringLength(text) >= MAX_LENGTH_VEHICLE_NAME_CHARS) return CMD_ERROR; + } + + if (flags & DC_EXEC) { + if (reset) { + v->orders->GetDispatchScheduleByIndex(schedule_index).ScheduleName().clear(); + } else { + v->orders->GetDispatchScheduleByIndex(schedule_index).ScheduleName() = text; + } + SetTimetableWindowsDirty(v, true); + } + + return CommandCost(); +} + /** * Set scheduled dispatch slot list. * @param dispatch_list The offset time list, must be correctly sorted. diff --git a/src/schdispatch_gui.cpp b/src/schdispatch_gui.cpp index 0d243c8ded..83302745fb 100644 --- a/src/schdispatch_gui.cpp +++ b/src/schdispatch_gui.cpp @@ -38,6 +38,7 @@ enum SchdispatchWidgets { WID_SCHDISPATCH_CAPTION, ///< Caption of window. + WID_SCHDISPATCH_RENAME, ///< Rename button. WID_SCHDISPATCH_MATRIX, ///< Matrix of vehicles. WID_SCHDISPATCH_V_SCROLL, ///< Vertical scrollbar. WID_SCHDISPATCH_SUMMARY_PANEL, ///< Summary panel @@ -323,6 +324,7 @@ struct SchdispatchWindow : GeneralVehicleWindow { this->SetWidgetDisabledState(WID_SCHDISPATCH_ENABLED, unusable || HasBit(v->vehicle_flags, VF_TIMETABLE_SEPARATION)); + this->SetWidgetDisabledState(WID_SCHDISPATCH_RENAME, unusable || v->orders->GetScheduledDispatchScheduleCount() == 0); this->SetWidgetDisabledState(WID_SCHDISPATCH_PREV, v->orders == nullptr || this->schedule_index <= 0); this->SetWidgetDisabledState(WID_SCHDISPATCH_NEXT, v->orders == nullptr || this->schedule_index >= (int)(v->orders->GetScheduledDispatchScheduleCount() - 1)); this->SetWidgetDisabledState(WID_SCHDISPATCH_ADD_SCHEDULE, unusable || v->orders->GetScheduledDispatchScheduleCount() >= 4096); @@ -349,9 +351,17 @@ struct SchdispatchWindow : GeneralVehicleWindow { case WID_SCHDISPATCH_HEADER: if (this->IsScheduleSelected()) { - SetDParam(0, STR_SCHDISPATCH_SCHEDULE_ID); - SetDParam(1, this->schedule_index + 1); - SetDParam(2, this->vehicle->orders->GetScheduledDispatchScheduleCount()); + const DispatchSchedule &ds = this->GetSelectedSchedule(); + if (ds.ScheduleName().empty()) { + SetDParam(0, STR_SCHDISPATCH_SCHEDULE_ID); + SetDParam(1, this->schedule_index + 1); + SetDParam(2, this->vehicle->orders->GetScheduledDispatchScheduleCount()); + } else { + SetDParam(0, STR_SCHDISPATCH_NAMED_SCHEDULE_ID); + SetDParamStr(1, ds.ScheduleName().c_str()); + SetDParam(2, this->schedule_index + 1); + SetDParam(3, this->vehicle->orders->GetScheduledDispatchScheduleCount()); + } } else { SetDParam(0, STR_SCHDISPATCH_NO_SCHEDULES); } @@ -737,6 +747,13 @@ struct SchdispatchWindow : GeneralVehicleWindow { case WID_SCHDISPATCH_ADD_SCHEDULE: AddNewScheduledDispatchSchedule(this->vehicle->index); break; + + case WID_SCHDISPATCH_RENAME: + if (!this->IsScheduleSelected()) break; + SetDParamStr(0, this->GetSelectedSchedule().ScheduleName().c_str()); + ShowQueryString(STR_JUST_RAW_STRING, STR_SCHDISPATCH_RENAME_SCHEDULE_CAPTION, + MAX_LENGTH_VEHICLE_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS); + break; } this->SetDirty(); @@ -861,6 +878,13 @@ struct SchdispatchWindow : GeneralVehicleWindow { } break; } + + case WID_SCHDISPATCH_RENAME: { + if (str == nullptr) return; + + DoCommandP(0, v->index | (this->schedule_index << 20), 0, CMD_SCHEDULED_DISPATCH_RENAME_SCHEDULE | CMD_MSG(STR_ERROR_CAN_T_RENAME_SCHEDULE), nullptr, str); + break; + } } this->SetDirty(); @@ -919,6 +943,7 @@ void CcAddNewSchDispatchSchedule(const CommandCost &result, TileIndex tile, uint static const NWidgetPart _nested_schdispatch_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_GREY), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_SCHDISPATCH_RENAME), SetMinimalSize(12, 14), SetDataTip(SPR_RENAME, STR_SCHDISPATCH_RENAME_SCHEDULE_TOOLTIP), NWidget(WWT_CAPTION, COLOUR_GREY, WID_SCHDISPATCH_CAPTION), SetDataTip(STR_SCHDISPATCH_CAPTION, STR_NULL), NWidget(WWT_SHADEBOX, COLOUR_GREY), NWidget(WWT_DEFSIZEBOX, COLOUR_GREY), @@ -927,7 +952,7 @@ static const NWidgetPart _nested_schdispatch_widgets[] = { NWidget(WWT_PANEL, COLOUR_GREY), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_ENABLED), SetDataTip(STR_SCHDISPATCH_ENABLED, STR_SCHDISPATCH_ENABLED_TOOLTIP), SetFill(1, 1), SetResize(1, 0), - NWidget(WWT_TEXT, COLOUR_GREY, WID_SCHDISPATCH_HEADER), SetAlignment(SA_CENTER), SetDataTip(STR_JUST_STRING2, STR_NULL), SetFill(1, 1), SetResize(1, 0), + NWidget(WWT_TEXT, COLOUR_GREY, WID_SCHDISPATCH_HEADER), SetAlignment(SA_CENTER), SetDataTip(STR_JUST_STRING3, STR_NULL), SetFill(1, 1), SetResize(1, 0), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_PREV), SetDataTip(STR_SCHDISPATCH_PREV_SCHEDULE, STR_SCHDISPATCH_PREV_SCHEDULE_TOOLTIP), SetFill(1, 1), SetResize(1, 0), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_NEXT), SetDataTip(STR_SCHDISPATCH_NEXT_SCHEDULE, STR_SCHDISPATCH_NEXT_SCHEDULE_TOOLTIP), SetFill(1, 1), SetResize(1, 0), diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp index e9b73df834..1fb1bcfb8a 100644 --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -1071,9 +1071,15 @@ struct TimetableWindow : GeneralVehicleWindow { list.emplace_back(new DropDownListStringItem(STR_TIMETABLE_ASSIGN_SCHEDULE_NONE, -1, false)); for (uint i = 0; i < v->orders->GetScheduledDispatchScheduleCount(); i++) { - DropDownListParamStringItem *item = new DropDownListParamStringItem(STR_TIMETABLE_ASSIGN_SCHEDULE_ID, i, false); - item->SetParam(0, i + 1); - list.emplace_back(item); + const DispatchSchedule &ds = this->vehicle->orders->GetDispatchScheduleByIndex(i); + if (ds.ScheduleName().empty()) { + DropDownListParamStringItem *item = new DropDownListParamStringItem(STR_TIMETABLE_ASSIGN_SCHEDULE_ID, i, false); + item->SetParam(0, i + 1); + list.emplace_back(item); + } else { + DropDownListCharStringItem *item = new DropDownListCharStringItem(ds.ScheduleName(), i, false); + list.emplace_back(item); + } } ShowDropDownList(this, std::move(list), order->GetDispatchScheduleIndex(), WID_VT_ASSIGN_SCHEDULE); break;