From 5b7689a0aafe695a7a3967bf6325c2cb87396ef9 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sun, 21 Jan 2024 18:09:24 +0000 Subject: [PATCH] Scheduled dispatch: Add flag to re-use all dispatch slots --- src/command.cpp | 2 ++ src/command_type.h | 1 + src/lang/extra/english.txt | 4 +++ src/order_base.h | 21 ++++++++++++++ src/order_cmd.cpp | 1 - src/schdispatch_cmd.cpp | 33 +++++++++++++++++++++ src/schdispatch_gui.cpp | 59 +++++++++++++++++++++++++++----------- src/sl/extended_ver_sl.cpp | 2 +- src/sl/order_sl.cpp | 3 +- src/timetable.h | 3 ++ src/timetable_cmd.cpp | 8 +++--- src/timetable_gui.cpp | 1 - 12 files changed, 114 insertions(+), 24 deletions(-) diff --git a/src/command.cpp b/src/command.cpp index d702dbd705..9034153ea1 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -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 diff --git a/src/command_type.h b/src/command_type.h index fba864b529..34c5cd977a 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -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 diff --git a/src/lang/extra/english.txt b/src/lang/extra/english.txt index a9d45b86f5..e1fbbe8c99 100644 --- a/src/lang/extra/english.txt +++ b/src/lang/extra/english.txt @@ -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. diff --git a/src/order_base.h b/src/order_base.h index f6bb3ece3f..c449f0df77 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -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 diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 92e889cf5b..88e15a0c54 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -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(); } diff --git a/src/schdispatch_cmd.cpp b/src/schdispatch_cmd.cpp index 40bb201978..d41a4dbda6 100644 --- a/src/schdispatch_cmd.cpp +++ b/src/schdispatch_cmd.cpp @@ -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 * diff --git a/src/schdispatch_gui.cpp b/src/schdispatch_gui.cpp index 7e30c331dd..257262caf5 100644 --- a/src/schdispatch_gui.cpp +++ b/src/schdispatch_gui.cpp @@ -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(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(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(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 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(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; + } } } diff --git a/src/sl/extended_ver_sl.cpp b/src/sl/extended_ver_sl.cpp index 4010ab22c1..44aa3639c1 100644 --- a/src/sl/extended_ver_sl.cpp +++ b/src/sl/extended_ver_sl.cpp @@ -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" }, diff --git a/src/sl/order_sl.cpp b/src/sl/order_sl.cpp index 45756e7e47..c50795fcdd 100644 --- a/src/sl/order_sl.cpp +++ b/src/sl/order_sl.cpp @@ -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; diff --git a/src/timetable.h b/src/timetable.h index 7cb4ab55b2..3d8cd45ff8 100644 --- a/src/timetable.h +++ b/src/timetable.h @@ -40,4 +40,7 @@ struct TimetableProgress { std::vector PopulateSeparationState(const Vehicle *v_start); +struct DispatchSchedule; +DateTicksScaled GetScheduledDispatchTime(const DispatchSchedule &ds, DateTicksScaled leave_time); + #endif /* TIMETABLE_H */ diff --git a/src/timetable_cmd.cpp b/src/timetable_cmd.cpp index 726f63a7d6..4eef2d3c35 100644 --- a/src/timetable_cmd.cpp +++ b/src/timetable_cmd.cpp @@ -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); } diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp index 2141d52b3a..4f5fcf3c0a 100644 --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -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);