From ff3473fe6a365322f685311454e4dd3b99d2a154 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Thu, 13 Jan 2022 19:46:43 +0000 Subject: [PATCH 01/11] Add support for multiple scheduled dispatch schedules per order list --- src/command.cpp | 6 +- src/command_func.h | 3 + src/command_type.h | 2 + src/departures.cpp | 97 ++++++----- src/lang/english.txt | 31 +++- src/lang/galician.txt | 5 +- src/lang/german.txt | 5 +- src/lang/korean.txt | 7 +- src/order_backup.cpp | 13 +- src/order_backup.h | 9 +- src/order_base.h | 215 +++++++++++++----------- src/order_cmd.cpp | 18 +- src/order_gui.cpp | 4 +- src/order_type.h | 1 + src/saveload/afterload.cpp | 16 ++ src/saveload/extended_ver_sl.cpp | 2 +- src/saveload/order_sl.cpp | 62 +++++-- src/saveload/saveload.h | 8 + src/schdispatch_cmd.cpp | 195 ++++++++++++++-------- src/schdispatch_gui.cpp | 276 +++++++++++++++++++++++-------- src/timetable_cmd.cpp | 77 +++++++-- src/timetable_gui.cpp | 68 ++++++-- src/widgets/timetable_widget.h | 2 + 23 files changed, 755 insertions(+), 367 deletions(-) diff --git a/src/command.cpp b/src/command.cpp index d88d3c1384..edaffc2f45 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -269,10 +269,12 @@ CommandProc CmdScheduledDispatch; CommandProcEx CmdScheduledDispatchAdd; CommandProc CmdScheduledDispatchRemove; CommandProc CmdScheduledDispatchSetDuration; -CommandProc CmdScheduledDispatchSetStartDate; +CommandProcEx CmdScheduledDispatchSetStartDate; CommandProc CmdScheduledDispatchSetDelay; CommandProc CmdScheduledDispatchResetLastDispatch; CommandProc CmdScheduledDispatchClear; +CommandProcEx CmdScheduledDispatchAddNewSchedule; +CommandProc CmdScheduledDispatchRemoveSchedule; CommandProc CmdAddPlan; CommandProcEx CmdAddPlanLine; @@ -509,6 +511,8 @@ static const Command _command_proc_table[] = { DEF_CMD(CmdScheduledDispatchSetDelay, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SCHEDULED_DISPATCH_SET_DELAY 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 + DEF_CMD(CmdScheduledDispatchRemoveSchedule, 0, CMDT_ROUTE_MANAGEMENT ), // CMD_SCHEDULED_DISPATCH_REMOVE_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_func.h b/src/command_func.h index 82de9be2ed..62756b89e1 100644 --- a/src/command_func.h +++ b/src/command_func.h @@ -154,4 +154,7 @@ CommandCallback CcDeleteVirtualTrain; CommandCallback CcAddVirtualEngine; CommandCallback CcMoveNewVirtualEngine; +/* schdispatch_gui.cpp */ +CommandCallback CcAddNewSchDispatchSchedule; + #endif /* COMMAND_FUNC_H */ diff --git a/src/command_type.h b/src/command_type.h index 2528ebfed9..2ef016c499 100644 --- a/src/command_type.h +++ b/src/command_type.h @@ -450,6 +450,8 @@ enum Commands { CMD_SCHEDULED_DISPATCH_SET_DELAY, ///< scheduled dispatch set maximum allow delay 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 + CMD_SCHEDULED_DISPATCH_REMOVE_SCHEDULE, ///< scheduled dispatch remove schedule CMD_ADD_PLAN, CMD_ADD_PLAN_LINE, diff --git a/src/departures.cpp b/src/departures.cpp index b72d414a6d..d3af2e2002 100644 --- a/src/departures.cpp +++ b/src/departures.cpp @@ -88,63 +88,62 @@ static uint8 GetDepartureConditionalOrderMode(const Order *order) static inline bool VehicleSetNextDepartureTime(DateTicks *previous_departure, uint *waiting_time, const DateTicksScaled date_only_scaled, const Vehicle *v, const Order *order, bool arrived_at_timing_point, schdispatch_cache_t &dept_schedule_last) { if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)) { - /* Loop over all order to find the first waiting order */ - for (int j = 0; j < v->orders.list->GetNumOrders(); ++j) { - Order* iterating_order = v->orders.list->GetOrderAt(j); + auto is_current_implicit_order = [&v](const Order *o) -> bool { + if (v->cur_implicit_order_index >= v->orders.list->GetNumOrders()) return false; + return v->orders.list->GetOrderAt(v->cur_implicit_order_index) == o; + }; - if (iterating_order->IsWaitTimetabled() && !iterating_order->IsType(OT_IMPLICIT)) { - /* This condition means that we want departure time for the first order */ - /* but not if the vehicle has arrived at the first order because the timetable is already shifted */ - if (iterating_order == order && !(arrived_at_timing_point && v->cur_implicit_order_index == j)) { - DateTicksScaled actual_departure = -1; - const DateTicksScaled begin_time = v->orders.list->GetScheduledDispatchStartTick(); - const uint32 dispatch_duration = v->orders.list->GetScheduledDispatchDuration(); - const int32 max_delay = v->orders.list->GetScheduledDispatchDelay(); + /* This condition means that we want departure time for the dispatch order */ + /* but not if the vehicle has arrived at the dispatch order because the timetable is already shifted */ + if (order->IsScheduledDispatchOrder(true) && !(arrived_at_timing_point && is_current_implicit_order(order))) { + const DispatchSchedule &ds = v->orders.list->GetDispatchScheduleByIndex(order->GetDispatchScheduleIndex()); - /* Earliest possible departure according to schedue */ - DateTicksScaled earliest_departure = begin_time + v->orders.list->GetScheduledDispatchLastDispatch(); + DateTicksScaled actual_departure = -1; + const DateTicksScaled begin_time = ds.GetScheduledDispatchStartTick(); + const uint32 dispatch_duration = ds.GetScheduledDispatchDuration(); + const int32 max_delay = ds.GetScheduledDispatchDelay(); - /* Earliest possible departure according to vehicle current timetable */ - if (earliest_departure + max_delay < date_only_scaled + *previous_departure + order->GetTravelTime()) { - earliest_departure = date_only_scaled + *previous_departure + order->GetTravelTime() - max_delay - 1; - /* -1 because this number is actually a moment before actual departure */ - } + /* Earliest possible departure according to schedue */ + DateTicksScaled earliest_departure = begin_time + ds.GetScheduledDispatchLastDispatch(); - /* Find next available slots */ - for (auto current_offset : v->orders.list->GetScheduledDispatch()) { - if (current_offset >= dispatch_duration) continue; - DateTicksScaled current_departure = begin_time + current_offset; - while (current_departure <= earliest_departure) { - current_departure += dispatch_duration; - } + /* Earliest possible departure according to vehicle current timetable */ + if (earliest_departure + max_delay < date_only_scaled + *previous_departure + order->GetTravelTime()) { + earliest_departure = date_only_scaled + *previous_departure + order->GetTravelTime() - max_delay - 1; + /* -1 because this number is actually a moment before actual departure */ + } - /* Make sure the slots has not already been used previously in this departure board calculation */ - while (dept_schedule_last[v->orders.list->index].count(current_departure) > 0) { - current_departure += dispatch_duration; - } - - if (actual_departure == -1 || actual_departure > current_departure) { - actual_departure = current_departure; - } - } - - *waiting_time = order->GetWaitTime() + actual_departure - date_only_scaled - *previous_departure - order->GetTravelTime(); - *previous_departure = actual_departure - date_only_scaled + order->GetWaitTime(); - dept_schedule_last[v->orders.list->index].insert(actual_departure); - - /* Return true means that vehicle lateness should be clear from this point onward */ - return true; + /* Find next available slots */ + for (auto current_offset : ds.GetScheduledDispatch()) { + if (current_offset >= dispatch_duration) continue; + DateTicksScaled current_departure = begin_time + current_offset; + while (current_departure <= earliest_departure) { + current_departure += dispatch_duration; } - /* This is special case for proper calculation of arrival time. */ - if (arrived_at_timing_point && v->cur_implicit_order_index == j) { - *previous_departure += order->GetTravelTime() + order->GetWaitTime(); - *waiting_time = -v->lateness_counter + order->GetWaitTime(); - return false; + /* Make sure the slots has not already been used previously in this departure board calculation */ + while (dept_schedule_last[v->orders.list->index].count(current_departure) > 0) { + current_departure += dispatch_duration; } - break; - } /* if it is first waiting order */ - } /* for in order list */ + + if (actual_departure == -1 || actual_departure > current_departure) { + actual_departure = current_departure; + } + } + + *waiting_time = order->GetWaitTime() + actual_departure - date_only_scaled - *previous_departure - order->GetTravelTime(); + *previous_departure = actual_departure - date_only_scaled + order->GetWaitTime(); + dept_schedule_last[v->orders.list->index].insert(actual_departure); + + /* Return true means that vehicle lateness should be clear from this point onward */ + return true; + } + + /* This is special case for proper calculation of arrival time. */ + if (arrived_at_timing_point && v->cur_implicit_order_index < v->orders.list->GetNumOrders() && v->orders.list->GetOrderAt(v->cur_implicit_order_index)->IsScheduledDispatchOrder(true)) { + *previous_departure += order->GetTravelTime() + order->GetWaitTime(); + *waiting_time = -v->lateness_counter + order->GetWaitTime(); + return false; + } } /* if vehicle is on scheduled dispatch */ /* Not using schedule for this departure time */ diff --git a/src/lang/english.txt b/src/lang/english.txt index c63ac9446f..28e7b0f967 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5701,8 +5701,9 @@ STR_TIMETABLE_AUTO_SEPARATION_TOOLTIP :{BLACK}Automati STR_TIMETABLE_SCHEDULED_DISPATCH :{BLACK}Scheduled Dispatch STR_TIMETABLE_SCHEDULED_DISPATCH_TOOLTIP :{BLACK}Open scheduled dispatch windows for automatic setting of timetable start time -STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER :[scheduled dispatch] -STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME :{PUSH_COLOUR}{RED}[scheduled dispatch - no wait time timetabled]{POP_COLOUR} +STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER :[{STRING1}scheduled dispatch] +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_EXPECTED :{BLACK}Expected STR_TIMETABLE_SCHEDULED :{BLACK}Scheduled @@ -5721,6 +5722,11 @@ STR_TIMETABLE_LEAVE_EARLY_FULL_ALL :Leave early if STR_TIMETABLE_EXTRA_DROP_DOWN :{BLACK}Extra STR_TIMETABLE_EXTRA_DROP_DOWN_TOOLTIP :{BLACK}Extra Options{}{}Leave as timetabled: Vehicles wait until their timetabled departure time before leaving (default).{}Leave Early: Vehicles leave as soon as loading/unloading is done, possibly before the timetabled departure time.{}Leave Early if Any Full: As above, if any cargo is fully loaded.{}Leave Early if All Full: As above, if all cargoes are fully loaded. +STR_TIMETABLE_ASSIGN_SCHEDULE_DROP_DOWN :{BLACK}Assign Schedule +STR_TIMETABLE_ASSIGN_SCHEDULE_DROP_DOWN_TOOLTIP :{BLACK}Assign or unassign a scheduled dispatch schedule to the selected order +STR_TIMETABLE_ASSIGN_SCHEDULE_NONE :No Schedule +STR_TIMETABLE_ASSIGN_SCHEDULE_ID :Schedule {NUM} + STR_TIMETABLE_ARRIVAL_ABBREVIATION :A: STR_TIMETABLE_DEPARTURE_ABBREVIATION :D: @@ -5730,10 +5736,11 @@ STR_TIMETABLE_AUTOSEP_SINGLE_VEH :{BLACK}This tim STR_TIMETABLE_WARNING_AUTOSEP_CONDITIONAL :{BLACK}Cannot auto separate timetable: conditional order(s) present STR_TIMETABLE_WARNING_AUTOSEP_MISSING_TIMINGS :{BLACK}Cannot auto separate timetable: wait or travel time(s) missing STR_TIMETABLE_WARNING_FULL_LOAD :{BLACK}Timetabling full-load orders is not recommended -STR_TIMETABLE_WARNING_AUTOFILL_CONDITIONAL :{BLACK}Autofill will only update taken branch of conditional orders. -STR_TIMETABLE_NON_TIMETABLED_BRANCH :{BLACK}Not all conditional order branch-taken travel times are timetabled. -STR_TIMETABLE_WARNING_NO_SCHEDULED_DISPATCH_ORDER :{BLACK}No order is suitable for use with scheduled dispatch. -STR_TIMETABLE_WARNING_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME :{BLACK}The scheduled dispatch order should have a timetabled wait time. +STR_TIMETABLE_WARNING_AUTOFILL_CONDITIONAL :{BLACK}Autofill will only update taken branch of conditional orders +STR_TIMETABLE_NON_TIMETABLED_BRANCH :{BLACK}Not all conditional order branch-taken travel times are timetabled +STR_TIMETABLE_WARNING_NO_SCHEDULED_DISPATCH_ORDER_ASSIGNED :{BLACK}The dispatch schedule is not assigned to an order +STR_TIMETABLE_WARNING_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME :{BLACK}The scheduled dispatch order should have a timetabled wait time +STR_TIMETABLE_WARNING_SCHEDULE_ID :{BLACK}Schedule {NUM}: {STRING} # Date window (for timetable) STR_DATE_CAPTION :{WHITE}Set date @@ -7115,7 +7122,17 @@ STR_SCHDISPATCH_RESET_LAST_DISPATCH_TOOLTIP :{BLACK}Reset wh STR_SCHDISPATCH_CLEAR :{BLACK}Clear Departure Slots STR_SCHDISPATCH_CLEAR_TOOLTIP :{BLACK}Clear all departure slots from the schedule. STR_SCHDISPATCH_MANAGE :{BLACK}Manage Schedule -STR_SCHDISPATCH_MANAGE_TOOLTIP :{STRING}{}{}{STRING} +STR_SCHDISPATCH_MANAGE_TOOLTIP :{STRING}{}{}{STRING}{}{}{STRING} +STR_SCHDISPATCH_PREV_SCHEDULE :{BLACK}Previous +STR_SCHDISPATCH_PREV_SCHEDULE_TOOLTIP :{BLACK}Go to previous dispatch schedule +STR_SCHDISPATCH_NEXT_SCHEDULE :{BLACK}Next +STR_SCHDISPATCH_NEXT_SCHEDULE_TOOLTIP :{BLACK}Go to next dispatch schedule +STR_SCHDISPATCH_ADD_SCHEDULE :{BLACK}Add +STR_SCHDISPATCH_ADD_SCHEDULE_TOOLTIP :{BLACK}Add a new dispatch schedule +STR_SCHDISPATCH_REMOVE_SCHEDULE :{BLACK}Remove Current Schedule +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_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/lang/galician.txt b/src/lang/galician.txt index 648382c2e6..84c29c60de 100644 --- a/src/lang/galician.txt +++ b/src/lang/galician.txt @@ -6681,8 +6681,8 @@ STR_TIMETABLE_AUTO_SEPARATION :{BLACK}Auto Sep STR_TIMETABLE_AUTO_SEPARATION_TOOLTIP :{BLACK}Automáticamente axusta a hora de comezo dos horarios para asegurar a separación dos vehículos STR_TIMETABLE_SCHEDULED_DISPATCH :{BLACK}Saída programada STR_TIMETABLE_SCHEDULED_DISPATCH_TOOLTIP :{BLACK}Abrir fiestras de saídas programadas para configurar automáticamente a hora de inicio dos horarios -STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER :[saída programada] -STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME :{PUSH_COLOUR}{RED}[saída programadda - sen tempo de espera no horario]{POP_COLOUR} +STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER :[{STRING}saída programada] +STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME :{PUSH_COLOUR}{RED}[{STRING}saída programadda - sen tempo de espera no horario]{POP_COLOUR} STR_TIMETABLE_LOCK_ORDER_TIME_TOOLTIP :{BLACK}Bloquear/desbloquear a cantidade de tempo para a orde resaltada (Ctrl+Click fixa o estado de bloqueo para todas as ordes).{}Cando estea bloqueado, a hora non se modificará mediante o autocompletado ou automatización do horario. STR_TIMETABLE_LEAVE_EARLY_ORDER :[saír cedo] STR_TIMETABLE_LEAVE_EARLY_ORDER_FULL_ANY :[saír cedo se calquera cargamento está cheo] @@ -6701,7 +6701,6 @@ STR_TIMETABLE_WARNING_AUTOSEP_MISSING_TIMINGS :{BLACK}Non se p STR_TIMETABLE_WARNING_FULL_LOAD :{BLACK}Non se recomenda a programación de ordes a carga completa STR_TIMETABLE_WARNING_AUTOFILL_CONDITIONAL :{BLACK}O enchemento automático só actualizará as ordes condicionais da rama escollida STR_TIMETABLE_NON_TIMETABLED_BRANCH :{BLACK}Non todas as ordes da rama condicional escollida están programadas. -STR_TIMETABLE_WARNING_NO_SCHEDULED_DISPATCH_ORDER :{BLACK}Non hai unha orde axeitada para usar coa saída programada. STR_TIMETABLE_WARNING_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME :{BLACK}A orde de saída programada debería ter un temepo de espera programado. STR_DATE_MINUTES_MINUTE_TOOLTIP :{BLACK}Seleccionar minuto diff --git a/src/lang/german.txt b/src/lang/german.txt index ca9283741e..f5da7f01bd 100644 --- a/src/lang/german.txt +++ b/src/lang/german.txt @@ -5479,8 +5479,8 @@ STR_TIMETABLE_AUTO_SEPARATION_TOOLTIP :{BLACK}Passt au STR_TIMETABLE_SCHEDULED_DISPATCH :{BLACK}Geplante Beförderung STR_TIMETABLE_SCHEDULED_DISPATCH_TOOLTIP :{BLACK}Öffnet das Fenster für die geplante Beförderung, um den Beginn des Fahrplans automatisch zu setzen -STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER :[Geplante Beförderung] -STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME :{PUSH_COLOUR}{RED}[Geplante Beförderung - keine Wartezeit geplant]{POP_COLOUR} +STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER :[{STRING}Geplante Beförderung] +STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME :{PUSH_COLOUR}{RED}[{STRING}Geplante Beförderung - keine Wartezeit geplant]{POP_COLOUR} STR_TIMETABLE_EXPECTED :{BLACK}Voraussichtlich STR_TIMETABLE_SCHEDULED :{BLACK}Fahrplanmäßig @@ -5510,7 +5510,6 @@ STR_TIMETABLE_WARNING_AUTOSEP_MISSING_TIMINGS :{BLACK}Kann Fah STR_TIMETABLE_WARNING_FULL_LOAD :{BLACK}Volllade-Aufträge zu planen ist nicht empfohlen STR_TIMETABLE_WARNING_AUTOFILL_CONDITIONAL :{BLACK}Es werden nur bedingte Sprünge erfasst, die genommen wurden STR_TIMETABLE_NON_TIMETABLED_BRANCH :{BLACK}Nicht alle Reisezeiten der bedingten Sprünge sind geplant -STR_TIMETABLE_WARNING_NO_SCHEDULED_DISPATCH_ORDER :{BLACK}Kein Auftrag ist geeignet für die Nutzung der geplanten Beförderung STR_TIMETABLE_WARNING_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME :{BLACK}Der geplant beförderte Auftrag sollte eine geplante Wartezeit haben # Date window (for timetable) diff --git a/src/lang/korean.txt b/src/lang/korean.txt index 95b7dcbdef..8c3d3fdd88 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -5697,8 +5697,8 @@ STR_TIMETABLE_AUTO_SEPARATION_TOOLTIP :{BLACK}시간 STR_TIMETABLE_SCHEDULED_DISPATCH :{BLACK}배차 일정 STR_TIMETABLE_SCHEDULED_DISPATCH_TOOLTIP :{BLACK}시간표 시작 시간 자동 설정을 위한 '배차 일정' 창을 엽니다. -STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER :[배차 기준] -STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME :{PUSH_COLOUR}{RED}[배차 기준 - 대기 시간표가 작성되지 않음]{POP_COLOUR} +STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER :[배{STRING}차 기준] +STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME :{PUSH_COLOUR}{RED}[{STRING}배차 기준 - 대기 시간표가 작성되지 않음]{POP_COLOUR} STR_TIMETABLE_EXPECTED :{BLACK}예정일 기준 STR_TIMETABLE_SCHEDULED :{BLACK}예정 소요시간 기준 @@ -5728,7 +5728,6 @@ STR_TIMETABLE_WARNING_AUTOSEP_MISSING_TIMINGS :{BLACK}자동 STR_TIMETABLE_WARNING_FULL_LOAD :{BLACK}'가득 싣기'가 설정되어 있는 경로에 시간표를 지정하는 것은 권장하지 않습니다. STR_TIMETABLE_WARNING_AUTOFILL_CONDITIONAL :{BLACK}자동 시간 설정은 조건부 경로 중 한 경로만 채울 것입니다. STR_TIMETABLE_NON_TIMETABLED_BRANCH :{BLACK}하나 이상의 조건부 경로 운행 시간이 채워지지 않았습니다. -STR_TIMETABLE_WARNING_NO_SCHEDULED_DISPATCH_ORDER :{BLACK}배차 일정을 사용하기에 적합한 경로가 존재하지 않습니다. STR_TIMETABLE_WARNING_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME :{BLACK}배차 기준은 대기 시간이 작성되어 있어야 합니다. # Date window (for timetable) @@ -7111,7 +7110,7 @@ STR_SCHDISPATCH_RESET_LAST_DISPATCH_TOOLTIP :{BLACK}최근 STR_SCHDISPATCH_CLEAR :{BLACK}출발 슬롯 초기화 STR_SCHDISPATCH_CLEAR_TOOLTIP :{BLACK}일정에서 모든 출발 슬롯을 제거합니다. STR_SCHDISPATCH_MANAGE :{BLACK}일정 관리 -STR_SCHDISPATCH_MANAGE_TOOLTIP :{STRING}{}{}{STRING} +STR_SCHDISPATCH_MANAGE_TOOLTIP :{STRING}{}{}{STRING}{}{}{STRING} STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_PAST :{BLACK}최근 출발: {DATE_WALLCLOCK_TINY} STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_FUTURE :{BLACK}최근 배차된 열차가 아직 출발하지 않았습니다. {DATE_WALLCLOCK_TINY}에 출발할 예정입니다. diff --git a/src/order_backup.cpp b/src/order_backup.cpp index e12a407edb..de0dfd07a3 100644 --- a/src/order_backup.cpp +++ b/src/order_backup.cpp @@ -69,11 +69,7 @@ OrderBackup::OrderBackup(const Vehicle *v, uint32 user) if (v->orders.list != nullptr && HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)) { SetBit(this->vehicle_flags, VF_SCHEDULED_DISPATCH); - this->scheduled_dispatch = v->orders.list->GetScheduledDispatch(); - this->scheduled_dispatch_duration = v->orders.list->GetScheduledDispatchDuration(); - this->scheduled_dispatch_start_date = v->orders.list->GetScheduledDispatchStartDatePart(); - this->scheduled_dispatch_start_full_date_fract = v->orders.list->GetScheduledDispatchStartDateFractPart(); - this->scheduled_dispatch_max_delay = v->orders.list->GetScheduledDispatchDelay(); + this->dispatch_schedules = v->orders.list->GetScheduledDispatchScheduleSet(); } } @@ -95,12 +91,7 @@ void OrderBackup::DoRestore(Vehicle *v) if (HasBit(this->vehicle_flags, VF_SCHEDULED_DISPATCH)) { SetBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH); - v->orders.list->SetScheduledDispatchDuration(this->scheduled_dispatch_duration); - v->orders.list->SetScheduledDispatchDelay(this->scheduled_dispatch_max_delay); - v->orders.list->SetScheduledDispatchStartDate(this->scheduled_dispatch_start_date, - this->scheduled_dispatch_start_full_date_fract); - v->orders.list->SetScheduledDispatchLastDispatch(0); - v->orders.list->SetScheduledDispatch(std::move(this->scheduled_dispatch)); + v->orders.list->GetScheduledDispatchScheduleSet() = std::move(this->dispatch_schedules); } /* Make sure buoys/oil rigs are updated in the station list. */ diff --git a/src/order_backup.h b/src/order_backup.h index 4c7ffa5b4b..7a1cab0c33 100644 --- a/src/order_backup.h +++ b/src/order_backup.h @@ -15,6 +15,7 @@ #include "tile_type.h" #include "vehicle_type.h" #include "base_consist.h" +#include "order_base.h" #include "saveload/saveload_common.h" /** Unique identifier for an order backup. */ @@ -43,6 +44,7 @@ private: friend SaveLoadTable GetOrderBackupDescription(); ///< Saving and loading of order backups. friend upstream_sl::SaveLoadTable upstream_sl::GetOrderBackupDescription(); ///< Saving and loading of order backups. friend void Load_BKOR(); ///< Creating empty orders upon savegame loading. + friend void Save_BKOR(); ///< Saving orders upon savegame saving. friend upstream_sl::BKORChunkHandler; uint32 user; ///< The user that requested the backup. TileIndex tile; ///< Tile of the depot where the order was changed. @@ -51,12 +53,7 @@ private: const Vehicle *clone; ///< Vehicle this vehicle was a clone of. Order *orders; ///< The actual orders if the vehicle was not a clone. - std::vector scheduled_dispatch; ///< Scheduled dispatch time - uint32 scheduled_dispatch_duration; ///< Scheduled dispatch duration - Date scheduled_dispatch_start_date; ///< Scheduled dispatch start date - uint16 scheduled_dispatch_start_full_date_fract;///< Scheduled dispatch start full date fraction; - /// this count to (DAY_TICK * _settings_game.economy.day_length_factor) - int32 scheduled_dispatch_max_delay; ///< Maximum allowed delay + std::vector dispatch_schedules; ///< Scheduled dispatch schedules static uint update_counter; diff --git a/src/order_base.h b/src/order_base.h index 401eeeee9f..20ddd2cc8d 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -53,6 +53,7 @@ void ClearOrderDestinationRefcountMap(); struct OrderExtraInfo { uint8 cargo_type_flags[NUM_CARGO] = {}; ///< Load/unload types for each cargo type. uint32 xdata = 0; ///< Extra arbitrary data + uint16 dispatch_index = 0; ///< Scheduled dispatch index + 1 uint8 xflags = 0; ///< Extra flags }; @@ -526,6 +527,24 @@ public: return true; } + inline int GetDispatchScheduleIndex() const + { + return this->extra != nullptr ? (int)this->extra->dispatch_index - 1 : -1; + } + + inline void SetDispatchScheduleIndex(int schedule_index) + { + if (schedule_index != this->GetDispatchScheduleIndex()) { + this->CheckExtraInfoAlloced(); + this->extra->dispatch_index = (uint16)(schedule_index + 1); + } + } + + inline bool IsScheduledDispatchOrder(bool require_wait_timetabled) const + { + return this->extra != nullptr && this->extra->dispatch_index > 0 && (!require_wait_timetabled || this->IsWaitTimetabled()); + } + void AssignOrder(const Order &other); bool Equals(const Order &other) const; @@ -587,6 +606,103 @@ template T CargoMaskValueFilter(CargoTypes &cargo_mask, return value; } +struct DispatchSchedule { +private: + friend SaveLoadTable GetDispatchScheduleDescription(); ///< Saving and loading of dispatch schedules + + std::vector scheduled_dispatch; ///< Scheduled dispatch time + uint32 scheduled_dispatch_duration = 0; ///< Scheduled dispatch duration + Date scheduled_dispatch_start_date = -1; ///< Scheduled dispatch start date + uint16 scheduled_dispatch_start_full_date_fract = 0; ///< Scheduled dispatch start full date fraction; + /// this counts to (DAY_TICK * _settings_game.economy.day_length_factor) + int32 scheduled_dispatch_last_dispatch = 0; ///< Last vehicle dispatched offset + int32 scheduled_dispatch_max_delay = 0; ///< Maximum allowed delay + +public: + /** + * Get the vector of all scheduled dispatch slot + * @return first scheduled dispatch + */ + inline const std::vector &GetScheduledDispatch() const { return this->scheduled_dispatch; } + + void SetScheduledDispatch(std::vector dispatch_list); + void AddScheduledDispatch(uint32 offset); + void RemoveScheduledDispatch(uint32 offset); + void ClearScheduledDispatch() { this->scheduled_dispatch.clear(); } + void UpdateScheduledDispatch(); + + /** + * Set the scheduled dispatch duration, in scaled tick + * @param duration New duration + */ + inline void SetScheduledDispatchDuration(uint32 duration) { this->scheduled_dispatch_duration = duration; } + + /** + * Get the scheduled dispatch duration, in scaled tick + * @return scheduled dispatch duration + */ + inline uint32 GetScheduledDispatchDuration() const { return this->scheduled_dispatch_duration; } + + /** + * Set the scheduled dispatch start + * @param start New start date + * @param fract New start full date fraction, see \c CmdScheduledDispatchSetStartDate + */ + inline void SetScheduledDispatchStartDate(Date start_date, uint16 start_full_date_fract) + { + this->scheduled_dispatch_start_date = start_date; + this->scheduled_dispatch_start_full_date_fract = start_full_date_fract; + } + + /** + * Get the scheduled dispatch start date part + * @return scheduled dispatch start date part + */ + inline Date GetScheduledDispatchStartDatePart() const { return this->scheduled_dispatch_start_date; } + + /** + * Get the scheduled dispatch start date fract part + * @return scheduled dispatch start date fract part + */ + inline uint16 GetScheduledDispatchStartDateFractPart() const { return this->scheduled_dispatch_start_full_date_fract; } + + /** + * Get the scheduled dispatch start date, in absolute scaled tick + * @return scheduled dispatch start date + */ + inline DateTicksScaled GetScheduledDispatchStartTick() const { return SchdispatchConvertToScaledTick(this->scheduled_dispatch_start_date, this->scheduled_dispatch_start_full_date_fract); } + + /** + * Whether the scheduled dispatch setting is valid + * @return scheduled dispatch start date fraction + */ + inline bool IsScheduledDispatchValid() const { return this->scheduled_dispatch_start_date >= 0 && this->scheduled_dispatch_duration > 0; } + + /** + * Set the scheduled dispatch last dispatch offset, in scaled tick + * @param duration New last dispatch offset + */ + inline void SetScheduledDispatchLastDispatch(int32 offset) { this->scheduled_dispatch_last_dispatch = offset; } + + /** + * Get the scheduled dispatch last dispatch offset, in scaled tick + * @return scheduled dispatch last dispatch + */ + inline int32 GetScheduledDispatchLastDispatch() const { return this->scheduled_dispatch_last_dispatch; } + + /** + * Set the scheduled dispatch maximum allowed delay, in scaled tick + * @param delay New maximum allow delay + */ + inline void SetScheduledDispatchDelay(int32 delay) { this->scheduled_dispatch_max_delay = delay; } + + /** + * Get the scheduled dispatch maximum alowed delay, in scaled tick + * @return scheduled dispatch last dispatch + */ + inline int32 GetScheduledDispatchDelay() const { return this->scheduled_dispatch_max_delay; } +}; + /** * Shared order list linking together the linked list of orders and the list * of vehicles sharing this order list. @@ -611,21 +727,13 @@ private: Ticks timetable_duration; ///< NOSAVE: Total timetabled duration of the order list. Ticks total_duration; ///< NOSAVE: Total (timetabled or not) duration of the order list. - std::vector scheduled_dispatch; ///< Scheduled dispatch time - uint32 scheduled_dispatch_duration; ///< Scheduled dispatch duration - Date scheduled_dispatch_start_date; ///< Scheduled dispatch start date - uint16 scheduled_dispatch_start_full_date_fract;///< Scheduled dispatch start full date fraction; - /// this count to (DAY_TICK * _settings_game.economy.day_length_factor) - int32 scheduled_dispatch_last_dispatch; ///< Last vehicle dispatched offset - int32 scheduled_dispatch_max_delay; ///< Maximum allowed delay + std::vector dispatch_schedules; ///< Scheduled dispatch schedules public: /** Default constructor producing an invalid order list. */ OrderList(VehicleOrderID num_orders = INVALID_VEH_ORDER_ID) : first(nullptr), num_manual_orders(0), num_vehicles(0), first_shared(nullptr), - timetable_duration(0), total_duration(0), scheduled_dispatch_duration(0), - scheduled_dispatch_start_date(-1), scheduled_dispatch_start_full_date_fract(0), - scheduled_dispatch_last_dispatch(0), scheduled_dispatch_max_delay(0) { } + timetable_duration(0), total_duration(0) { } /** * Create an order list with the given order chain for the given vehicle. @@ -752,90 +860,13 @@ public: void DebugCheckSanity() const; bool CheckOrderListIndexing() const; - /** - * Get the vector of all scheduled dispatch slot - * @return first scheduled dispatch - */ - inline const std::vector &GetScheduledDispatch() const { return this->scheduled_dispatch; } + inline std::vector &GetScheduledDispatchScheduleSet() { return this->dispatch_schedules; } + inline const std::vector &GetScheduledDispatchScheduleSet() const { return this->dispatch_schedules; } - void SetScheduledDispatch(std::vector dispatch_list); - void AddScheduledDispatch(uint32 offset); - void RemoveScheduledDispatch(uint32 offset); - void ClearScheduledDispatch() { this->scheduled_dispatch.clear(); } - void UpdateScheduledDispatch(); - void ResetScheduledDispatch(); - - /** - * Set the scheduled dispatch duration, in scaled tick - * @param duration New duration - */ - inline void SetScheduledDispatchDuration(uint32 duration) { this->scheduled_dispatch_duration = duration; } - - /** - * Get the scheduled dispatch duration, in scaled tick - * @return scheduled dispatch duration - */ - inline uint32 GetScheduledDispatchDuration() const { return this->scheduled_dispatch_duration; } - - /** - * Set the scheduled dispatch start - * @param start New start date - * @param fract New start full date fraction, see \c CmdScheduledDispatchSetStartDate - */ - inline void SetScheduledDispatchStartDate(Date start_date, uint16 start_full_date_fract) - { - this->scheduled_dispatch_start_date = start_date; - this->scheduled_dispatch_start_full_date_fract = start_full_date_fract; - } - - /** - * Get the scheduled dispatch start date part - * @return scheduled dispatch start date part - */ - inline Date GetScheduledDispatchStartDatePart() const { return this->scheduled_dispatch_start_date; } - - /** - * Get the scheduled dispatch start date fract part - * @return scheduled dispatch start date fract part - */ - inline uint16 GetScheduledDispatchStartDateFractPart() const { return this->scheduled_dispatch_start_full_date_fract; } - - /** - * Get the scheduled dispatch start date, in absolute scaled tick - * @return scheduled dispatch start date - */ - inline DateTicksScaled GetScheduledDispatchStartTick() const { return SchdispatchConvertToScaledTick(this->scheduled_dispatch_start_date, this->scheduled_dispatch_start_full_date_fract); } - - /** - * Whether the scheduled dispatch setting is valid - * @return scheduled dispatch start date fraction - */ - inline bool IsScheduledDispatchValid() const { return this->scheduled_dispatch_start_date >= 0 && this->scheduled_dispatch_duration > 0; } - - /** - * Set the scheduled dispatch last dispatch offset, in scaled tick - * @param duration New last dispatch offset - */ - inline void SetScheduledDispatchLastDispatch(int32 offset) { this->scheduled_dispatch_last_dispatch = offset; } - - /** - * Get the scheduled dispatch last dispatch offset, in scaled tick - * @return scheduled dispatch last dispatch - */ - inline int32 GetScheduledDispatchLastDispatch() const { return this->scheduled_dispatch_last_dispatch; } - - /** - * Set the scheduled dispatch maximum allowed delay, in scaled tick - * @param delay New maximum allow delay - */ - inline void SetScheduledDispatchDelay(int32 delay) { this->scheduled_dispatch_max_delay = delay; } - - /** - * Get the scheduled dispatch maximum alowed delay, in scaled tick - * @return scheduled dispatch last dispatch - */ - inline int32 GetScheduledDispatchDelay() const { return this->scheduled_dispatch_max_delay; } + inline uint GetScheduledDispatchScheduleCount() const { return (uint)this->dispatch_schedules.size(); } + inline DispatchSchedule &GetDispatchScheduleByIndex(uint index) { return this->dispatch_schedules[index]; } + inline const DispatchSchedule &GetDispatchScheduleByIndex(uint index) const { return this->dispatch_schedules[index]; } }; void ShiftOrderDates(int interval); diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 77e0e6ba36..2f69db1d92 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -370,7 +370,7 @@ void Order::AssignOrder(const Order &other) this->travel_time = other.travel_time; this->max_speed = other.max_speed; - if (other.extra != nullptr && (this->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD || this->GetLoadType() == OLFB_CARGO_TYPE_LOAD || other.extra->xdata != 0 || other.extra->xflags != 0)) { + if (other.extra != nullptr && (this->GetUnloadType() == OUFB_CARGO_TYPE_UNLOAD || this->GetLoadType() == OLFB_CARGO_TYPE_LOAD || other.extra->xdata != 0 || other.extra->xflags != 0 || other.extra->dispatch_index != 0)) { this->AllocExtraInfo(); *(this->extra) = *(other.extra); } else { @@ -1894,6 +1894,9 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 if (order->IsWaitTimetabled() || order->GetWaitTime() > 0) { DoCommandEx(tile, v->index | (MTF_WAIT_TIME << 28) | (1 << 31), 0, p3, flags, CMD_CHANGE_TIMETABLE); } + if (order->IsScheduledDispatchOrder(false)) { + DoCommandEx(tile, v->index | (MTF_ASSIGN_SCHEDULE << 28), -1, p3, flags, CMD_CHANGE_TIMETABLE); + } } break; @@ -2390,12 +2393,7 @@ CommandCost CmdCloneOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 /* Copy over scheduled dispatch data */ assert(dst->orders.list != nullptr); if (src->orders.list != nullptr) { - dst->orders.list->SetScheduledDispatchDuration(src->orders.list->GetScheduledDispatchDuration()); - dst->orders.list->SetScheduledDispatchDelay(src->orders.list->GetScheduledDispatchDelay()); - dst->orders.list->SetScheduledDispatchStartDate(src->orders.list->GetScheduledDispatchStartDatePart(), - src->orders.list->GetScheduledDispatchStartDateFractPart()); - dst->orders.list->SetScheduledDispatchLastDispatch(0); - dst->orders.list->SetScheduledDispatch(src->orders.list->GetScheduledDispatch()); + dst->orders.list->GetScheduledDispatchScheduleSet() = src->orders.list->GetScheduledDispatchScheduleSet(); } /* Set automation bit if target has it. */ @@ -3223,8 +3221,10 @@ CommandCost CmdMassChangeOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, u void ShiftOrderDates(int interval) { for (OrderList *orderlist : OrderList::Iterate()) { - if (orderlist->GetScheduledDispatchStartDatePart() >= 0) { - orderlist->SetScheduledDispatchStartDate(orderlist->GetScheduledDispatchStartDatePart() + interval, orderlist->GetScheduledDispatchStartDateFractPart()); + for (DispatchSchedule &ds : orderlist->GetScheduledDispatchScheduleSet()) { + if (ds.GetScheduledDispatchStartDatePart() >= 0) { + ds.SetScheduledDispatchStartDate(ds.GetScheduledDispatchStartDatePart() + interval, ds.GetScheduledDispatchStartDateFractPart()); + } } } diff --git a/src/order_gui.cpp b/src/order_gui.cpp index 56ae2c7e00..53dd47da1e 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -1057,8 +1057,10 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int if (timetable && timetable_wait_time_valid && order->GetLeaveType() != OLT_NORMAL && edge != 0) { edge = DrawString(rtl ? left : edge + 3, rtl ? edge - 3 : right, y, STR_TIMETABLE_LEAVE_EARLY_ORDER + order->GetLeaveType() - OLT_LEAVE_EARLY, colour); } - if (timetable && HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) && v->GetFirstWaitingLocation(false) == order_index && edge != 0) { + 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.list->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); } diff --git a/src/order_type.h b/src/order_type.h index 7e44c85e58..399d6ab641 100644 --- a/src/order_type.h +++ b/src/order_type.h @@ -238,6 +238,7 @@ enum ModifyTimetableFlags { MTF_SET_WAIT_FIXED,///< Set wait time fixed flag state. MTF_SET_TRAVEL_FIXED,///< Set travel time fixed flag state. MTF_SET_LEAVE_TYPE,///< Passes an OrderLeaveType. + MTF_ASSIGN_SCHEDULE, ///< Assign a dispatch schedule. MTF_END }; template <> struct EnumPropsT : MakeEnumPropsT {}; diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 056330e499..f065181a58 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -3999,6 +3999,22 @@ bool AfterLoadGame() _settings_game.vehicle.through_load_speed_limit = 15; } + if (SlXvIsFeaturePresent(XSLFI_SCHEDULED_DISPATCH, 1, 2)) { + for (OrderList *order_list : OrderList::Iterate()) { + if (order_list->GetScheduledDispatchScheduleCount() == 1) { + const DispatchSchedule &ds = order_list->GetDispatchScheduleByIndex(0); + if (!ds.IsScheduledDispatchValid() && ds.GetScheduledDispatch().empty()) { + order_list->GetScheduledDispatchScheduleSet().clear(); + } else { + VehicleOrderID idx = order_list->GetFirstSharedVehicle()->GetFirstWaitingLocation(false); + if (idx != INVALID_VEH_ORDER_ID) { + order_list->GetOrderAt(idx)->SetDispatchScheduleIndex(0); + } + } + } + } + } + InitializeRoadGUI(); /* This needs to be done after conversion. */ diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 3495637b40..c8cc9b31b9 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -109,7 +109,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, 2, 2, "scheduled_dispatch", nullptr, nullptr, nullptr }, + { XSLFI_SCHEDULED_DISPATCH, XSCF_NULL, 3, 3, "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 e582a72e93..e4846214cf 100644 --- a/src/saveload/order_sl.cpp +++ b/src/saveload/order_sl.cpp @@ -217,6 +217,7 @@ const SaveLoadTable GetOrderExtraInfoDescription() SLE_CONDARR_X(OrderExtraInfo, cargo_type_flags, SLE_UINT8, NUM_CARGO, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_CARGO_TYPE_ORDERS, 3)), SLE_CONDVAR_X(OrderExtraInfo, xflags, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA)), SLE_CONDVAR_X(OrderExtraInfo, xdata, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_ORDER_EXTRA_DATA)), + SLE_CONDVAR_X(OrderExtraInfo, dispatch_index, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 3)), }; return _order_extra_info_desc; @@ -255,16 +256,24 @@ static void Ptrs_ORDR() } } +SaveLoadTable GetDispatchScheduleDescription() +{ + static const SaveLoad _order_extra_info_desc[] = { + SLE_VARVEC(DispatchSchedule, scheduled_dispatch, SLE_UINT32), + SLE_VAR(DispatchSchedule, scheduled_dispatch_duration, SLE_UINT32), + SLE_VAR(DispatchSchedule, scheduled_dispatch_start_date, SLE_INT32), + 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), + }; + + return _order_extra_info_desc; +} + SaveLoadTable GetOrderListDescription() { static const SaveLoad _orderlist_desc[] = { SLE_REF(OrderList, first, REF_ORDER), - SLE_CONDVARVEC_X(OrderList, scheduled_dispatch, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH)), - SLE_CONDVAR_X(OrderList, scheduled_dispatch_duration, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH)), - SLE_CONDVAR_X(OrderList, scheduled_dispatch_start_date, SLE_INT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH)), - SLE_CONDVAR_X(OrderList, scheduled_dispatch_start_full_date_fract, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH)), - SLE_CONDVAR_X(OrderList, scheduled_dispatch_last_dispatch, SLE_INT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH)), - SLE_CONDVAR_X(OrderList, scheduled_dispatch_max_delay, SLE_INT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH)), SLEG_CONDVAR_X(_jokerpp_separation_mode, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)), SLE_CONDNULL_X(21, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP)), }; @@ -276,7 +285,14 @@ static void Save_ORDL() { for (OrderList *list : OrderList::Iterate()) { SlSetArrayIndex(list->index); - SlObject(list, GetOrderListDescription()); + SlAutolength([](void *data) { + OrderList *list = static_cast(data); + SlObject(list, GetOrderListDescription()); + SlWriteUint32(list->GetScheduledDispatchScheduleCount()); + for (DispatchSchedule &ds : list->GetScheduledDispatchScheduleSet()) { + SlObject(&ds, GetDispatchScheduleDescription()); + } + }, list); } } @@ -297,8 +313,14 @@ static void Load_ORDL() _jokerpp_non_auto_separation.push_back(list); } } + if (SlXvIsFeaturePresent(XSLFI_SCHEDULED_DISPATCH)) { + uint count = SlXvIsFeaturePresent(XSLFI_SCHEDULED_DISPATCH, 3) ? SlReadUint32() : 1; + list->GetScheduledDispatchScheduleSet().resize(count); + for (DispatchSchedule &ds : list->GetScheduledDispatchScheduleSet()) { + SlObject(&ds, GetDispatchScheduleDescription()); + } + } } - } void Ptrs_ORDL() @@ -330,17 +352,13 @@ SaveLoadTable GetOrderBackupDescription() SLE_CONDVAR(OrderBackup, vehicle_flags, SLE_FILE_U8 | SLE_VAR_U16, SLV_176, SLV_180), SLE_CONDVAR(OrderBackup, vehicle_flags, SLE_UINT16, SLV_180, SL_MAX_VERSION), SLE_REF(OrderBackup, orders, REF_ORDER), - SLE_CONDVARVEC_X(OrderBackup, scheduled_dispatch, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 2)), - SLE_CONDVAR_X(OrderBackup, scheduled_dispatch_duration, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 2)), - SLE_CONDVAR_X(OrderBackup, scheduled_dispatch_start_date, SLE_INT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 2)), - SLE_CONDVAR_X(OrderBackup, scheduled_dispatch_start_full_date_fract, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 2)), - SLE_CONDVAR_X(OrderBackup, scheduled_dispatch_max_delay, SLE_INT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 2)), + SLE_CONDNULL_X(18, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 2, 2)), }; return _order_backup_desc; } -static void Save_BKOR() +void Save_BKOR() { /* We only save this when we're a network server * as we want this information on our clients. For @@ -349,7 +367,14 @@ static void Save_BKOR() for (OrderBackup *ob : OrderBackup::Iterate()) { SlSetArrayIndex(ob->index); - SlObject(ob, GetOrderBackupDescription()); + SlAutolength([](void *data) { + OrderBackup *ob = static_cast(data); + SlObject(ob, GetOrderBackupDescription()); + SlWriteUint32((uint)ob->dispatch_schedules.size()); + for (DispatchSchedule &ds : ob->dispatch_schedules) { + SlObject(&ds, GetDispatchScheduleDescription()); + } + }, ob); } } @@ -361,6 +386,13 @@ void Load_BKOR() /* set num_orders to 0 so it's a valid OrderList */ OrderBackup *ob = new (index) OrderBackup(); SlObject(ob, GetOrderBackupDescription()); + if (SlXvIsFeaturePresent(XSLFI_SCHEDULED_DISPATCH, 3)) { + uint count = SlReadUint32(); + ob->dispatch_schedules.resize(count); + for (DispatchSchedule &ds : ob->dispatch_schedules) { + SlObject(&ds, GetDispatchScheduleDescription()); + } + } } } diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index fb8c0a637e..c96d2ff7a6 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -327,6 +327,14 @@ DECLARE_ENUM_AS_BIT_SET(SaveLoadChunkExtHeaderFlags) */ #define SLE_VEC(base, variable, type) SLE_CONDVEC(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) +/** + * Storage of a variable vector in every savegame version. + * @param base Name of the class or struct containing the list. + * @param variable Name of the variable in the class or struct referenced by \a base. + * @param type Storage of the data in memory and in the savegame. + */ +#define SLE_VARVEC(base, variable, type) SLE_CONDVARVEC(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION) + /** * Empty space in every savegame version. * @param length Length of the empty space. diff --git a/src/schdispatch_cmd.cpp b/src/schdispatch_cmd.cpp index a6144a9dff..fc10427c6a 100644 --- a/src/schdispatch_cmd.cpp +++ b/src/schdispatch_cmd.cpp @@ -82,6 +82,7 @@ CommandCost CmdScheduledDispatch(TileIndex tile, DoCommandFlag flags, uint32 p1, CommandCost CmdScheduledDispatchAdd(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, uint64 p3, const char *text, uint32 binary_length) { VehicleID veh = GB(p1, 0, 20); + uint schedule_index = GB(p1, 20, 12); uint32 offset = GB(p3, 0, 32); uint32 extra_slots = GB(p3, 32, 16); @@ -93,14 +94,17 @@ CommandCost CmdScheduledDispatchAdd(TileIndex tile, DoCommandFlag flags, uint32 if (v->orders.list == nullptr) return CMD_ERROR; + if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) 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); + DispatchSchedule &ds = v->orders.list->GetDispatchScheduleByIndex(schedule_index); + ds.AddScheduledDispatch(p2); for (uint i = 0; i < extra_slots; i++) { p2 += offset; - v->orders.list->AddScheduledDispatch(p2); + ds.AddScheduledDispatch(p2); } SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index); } @@ -120,6 +124,7 @@ CommandCost CmdScheduledDispatchAdd(TileIndex tile, DoCommandFlag flags, uint32 CommandCost CmdScheduledDispatchRemove(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; @@ -129,8 +134,10 @@ CommandCost CmdScheduledDispatchRemove(TileIndex tile, DoCommandFlag flags, uint if (v->orders.list == nullptr) return CMD_ERROR; + if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR; + if (flags & DC_EXEC) { - v->orders.list->RemoveScheduledDispatch(p2); + v->orders.list->GetDispatchScheduleByIndex(schedule_index).RemoveScheduledDispatch(p2); SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index); } @@ -150,6 +157,7 @@ CommandCost CmdScheduledDispatchRemove(TileIndex tile, DoCommandFlag flags, uint CommandCost CmdScheduledDispatchSetDuration(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; @@ -159,9 +167,12 @@ CommandCost CmdScheduledDispatchSetDuration(TileIndex tile, DoCommandFlag flags, if (v->orders.list == nullptr) return CMD_ERROR; + if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR; + if (flags & DC_EXEC) { - v->orders.list->SetScheduledDispatchDuration(p2); - v->orders.list->UpdateScheduledDispatch(); + DispatchSchedule &ds = v->orders.list->GetDispatchScheduleByIndex(schedule_index); + ds.SetScheduledDispatchDuration(p2); + ds.UpdateScheduledDispatch(); SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index); } @@ -173,23 +184,22 @@ CommandCost CmdScheduledDispatchSetDuration(TileIndex tile, DoCommandFlag flags, * * The parameter is quite tricky. The default maximum of daylength factor is 125, * and with DAY_TICKS of 74 the result (maximum scaled tick per day) fits in 14 bit. - * Vehicle index in p1 takes 20 bit, so we have 12 bit here. The MSB of the fraction is stored here. - * The 2-bit LSB is stored in MSB of p2, which is start date. The default date is stored in int32, - * which only have topmost bit available. However, if the date reached 31 bits, that means it is over 1,000,000 years, - * so I think it is safe to steal another bit here. * * See also the static_assert at the top of the file. * * @param tile Not used. * @param flags Operation to perform. - * @param p1 MSB of Start Full Date Fraction || Vehicle index - * @param p2 LSB of Start Full Date Fraction || Date to add. + * @param p1 Vehicle index + * @param p2 Date to add. + * @param p3 various bitstuffed elements + * - p3 = (bit 0 - 15) - Full date fraction * @param text unused * @return the cost of this operation or an error */ -CommandCost CmdScheduledDispatchSetStartDate(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +CommandCost CmdScheduledDispatchSetStartDate(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, uint64 p3, const char *text, uint32 binary_length) { 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; @@ -199,12 +209,15 @@ CommandCost CmdScheduledDispatchSetStartDate(TileIndex tile, DoCommandFlag flags if (v->orders.list == nullptr) return CMD_ERROR; - int32 date = (int32) GB(p2, 0, 30); - uint16 full_date_fract = (GB(p1, 20, 12) << 2) + GB(p2, 30, 2); + if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR; + + int32 date = (int32)p2; + uint16 full_date_fract = GB(p3, 0, 16); if (flags & DC_EXEC) { - v->orders.list->SetScheduledDispatchStartDate(date, full_date_fract); - v->orders.list->UpdateScheduledDispatch(); + DispatchSchedule &ds = v->orders.list->GetDispatchScheduleByIndex(schedule_index); + ds.SetScheduledDispatchStartDate(date, full_date_fract); + ds.UpdateScheduledDispatch(); SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index); } @@ -224,6 +237,7 @@ CommandCost CmdScheduledDispatchSetStartDate(TileIndex tile, DoCommandFlag flags CommandCost CmdScheduledDispatchSetDelay(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; @@ -233,8 +247,10 @@ CommandCost CmdScheduledDispatchSetDelay(TileIndex tile, DoCommandFlag flags, ui if (v->orders.list == nullptr) return CMD_ERROR; + if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR; + if (flags & DC_EXEC) { - v->orders.list->SetScheduledDispatchDelay(p2); + v->orders.list->GetDispatchScheduleByIndex(schedule_index).SetScheduledDispatchDelay(p2); SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index); } @@ -259,6 +275,7 @@ CommandCost CmdScheduledDispatchSetDelay(TileIndex tile, DoCommandFlag flags, ui CommandCost CmdScheduledDispatchResetLastDispatch(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; @@ -268,8 +285,10 @@ CommandCost CmdScheduledDispatchResetLastDispatch(TileIndex tile, DoCommandFlag if (v->orders.list == nullptr) return CMD_ERROR; + if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR; + if (flags & DC_EXEC) { - v->orders.list->SetScheduledDispatchLastDispatch(0); + v->orders.list->GetDispatchScheduleByIndex(schedule_index).SetScheduledDispatchLastDispatch(0); SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index); } @@ -289,6 +308,7 @@ CommandCost CmdScheduledDispatchResetLastDispatch(TileIndex tile, DoCommandFlag CommandCost CmdScheduledDispatchClear(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; @@ -298,19 +318,106 @@ CommandCost CmdScheduledDispatchClear(TileIndex tile, DoCommandFlag flags, uint3 if (v->orders.list == nullptr) return CMD_ERROR; + if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR; + if (flags & DC_EXEC) { - v->orders.list->ClearScheduledDispatch(); + v->orders.list->GetDispatchScheduleByIndex(schedule_index).ClearScheduledDispatch(); SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index); } return CommandCost(); } +/** + * Add a new scheduled dispatch schedule + * + * @param tile Not used. + * @param flags Operation to perform. + * @param p1 Vehicle index + * @param p2 Duration, in scaled tick + * @param p3 various bitstuffed elements + * - p3 = (bit 0 - 31) - Start date + * - p3 = (bit 32 - 47) - Full date fraction + * @param text unused + * @return the cost of this operation or an error + */ +CommandCost CmdScheduledDispatchAddNewSchedule(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, uint64 p3, const char *text, uint32 binary_length) +{ + VehicleID veh = GB(p1, 0, 20); + + 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.list == nullptr) return CMD_ERROR; + if (v->orders.list->GetScheduledDispatchScheduleCount() >= 4096) return CMD_ERROR; + + int32 date = GB(p3, 0, 32); + uint16 full_date_fract = GB(p3, 32, 16); + + if (flags & DC_EXEC) { + v->orders.list->GetScheduledDispatchScheduleSet().emplace_back(); + DispatchSchedule &ds = v->orders.list->GetScheduledDispatchScheduleSet().back(); + ds.SetScheduledDispatchDuration(p2); + ds.SetScheduledDispatchStartDate(date, full_date_fract); + ds.UpdateScheduledDispatch(); + SetWindowClassesDirty(WC_VEHICLE_TIMETABLE); + SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index); + } + + return CommandCost(); +} + +/** + * Remove scheduled dispatch schedule + * + * @param tile Not used. + * @param flags Operation to perform. + * @param p1 Vehicle index + * @param p2 Not used + * @param text unused + * @return the cost of this operation or an error + */ +CommandCost CmdScheduledDispatchRemoveSchedule(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.list == nullptr) return CMD_ERROR; + + if (schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR; + + if (flags & DC_EXEC) { + std::vector &scheds = v->orders.list->GetScheduledDispatchScheduleSet(); + scheds.erase(scheds.begin() + schedule_index); + for (Order *o = v->GetFirstOrder(); o != nullptr; o = o->next) { + int idx = o->GetDispatchScheduleIndex(); + if (idx == (int)schedule_index) { + o->SetDispatchScheduleIndex(-1); + } else if (idx > (int)schedule_index) { + o->SetDispatchScheduleIndex(idx - 1); + } + } + SetWindowClassesDirty(WC_VEHICLE_TIMETABLE); + InvalidateWindowClassesData(WC_SCHDISPATCH_SLOTS, VIWD_MODIFY_ORDERS); + } + + return CommandCost(); +} + /** * Set scheduled dispatch slot list. * @param dispatch_list The offset time list, must be correctly sorted. */ -void OrderList::SetScheduledDispatch(std::vector dispatch_list) +void DispatchSchedule::SetScheduledDispatch(std::vector dispatch_list) { this->scheduled_dispatch = std::move(dispatch_list); assert(std::is_sorted(this->scheduled_dispatch.begin(), this->scheduled_dispatch.end())); @@ -321,7 +428,7 @@ void OrderList::SetScheduledDispatch(std::vector dispatch_list) * Add new scheduled dispatch slot at offsets time. * @param offset The offset time to add. */ -void OrderList::AddScheduledDispatch(uint32 offset) +void DispatchSchedule::AddScheduledDispatch(uint32 offset) { /* Maintain sorted list status */ auto insert_position = std::lower_bound(this->scheduled_dispatch.begin(), this->scheduled_dispatch.end(), offset); @@ -336,7 +443,7 @@ void OrderList::AddScheduledDispatch(uint32 offset) * Remove scheduled dispatch slot at offsets time. * @param offset The offset time to remove. */ -void OrderList::RemoveScheduledDispatch(uint32 offset) +void DispatchSchedule::RemoveScheduledDispatch(uint32 offset) { /* Maintain sorted list status */ auto erase_position = std::lower_bound(this->scheduled_dispatch.begin(), this->scheduled_dispatch.end(), offset); @@ -349,7 +456,7 @@ void OrderList::RemoveScheduledDispatch(uint32 offset) /** * Update the scheduled dispatch start time to be the most recent possible. */ -void OrderList::UpdateScheduledDispatch() +void DispatchSchedule::UpdateScheduledDispatch() { bool update_windows = false; if (this->GetScheduledDispatchStartTick() == 0) { @@ -388,45 +495,3 @@ void OrderList::UpdateScheduledDispatch() } if (update_windows) InvalidateWindowClassesData(WC_SCHDISPATCH_SLOTS, VIWD_MODIFY_ORDERS); } - -/** - * Reset the scheduled dispatch schedule. - * - * This only occurs during initialization of the scheduled dispatch for each shared order. Basically we set - * proper default value for start time and duration - */ -void OrderList::ResetScheduledDispatch() -{ - uint32 windex = this->first_shared->index; - - Date start_date; - uint16 start_full_date_fract; - uint32 duration; - - if (_settings_time.time_in_minutes) { - /* Set to 00:00 of today, and 1 day */ - - DateTicksScaled val; - val = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), 0, 0); - val -= _settings_time.clock_offset; - val *= _settings_time.ticks_per_minute; - SchdispatchConvertToFullDateFract(val, &start_date, &start_full_date_fract); - - duration = 24 * 60 * _settings_time.ticks_per_minute; - } else { - /* Set Jan 1st and 365 day */ - start_date = DAYS_TILL(_cur_year); - start_full_date_fract = 0; - duration = 365*DAY_TICKS; - } - - DoCommandP(0, windex, duration, CMD_SCHEDULED_DISPATCH_SET_DURATION | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); - - uint32 p1 = 0, p2 = 0; - SB(p1, 0, 20, windex); - SB(p1, 20, 12, GB(start_full_date_fract, 2, 12)); - SB(p2, 0, 30, start_date); - SB(p2, 30, 2, GB(start_full_date_fract, 0, 2)); - - DoCommandP(0, p1, p2, CMD_SCHEDULED_DISPATCH_SET_START_DATE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); -} diff --git a/src/schdispatch_gui.cpp b/src/schdispatch_gui.cpp index 5a615a1c49..d0ba9cfa01 100644 --- a/src/schdispatch_gui.cpp +++ b/src/schdispatch_gui.cpp @@ -43,6 +43,11 @@ enum SchdispatchWidgets { WID_SCHDISPATCH_SUMMARY_PANEL, ///< Summary panel WID_SCHDISPATCH_ENABLED, ///< Enable button. + WID_SCHDISPATCH_HEADER, ///< Header text. + WID_SCHDISPATCH_PREV, ///< Previous schedule. + WID_SCHDISPATCH_NEXT, ///< Next schedule. + WID_SCHDISPATCH_ADD_SCHEDULE, ///< Add schedule. + WID_SCHDISPATCH_ADD, ///< Add Departure Time button WID_SCHDISPATCH_SET_DURATION, ///< Duration button WID_SCHDISPATCH_SET_START_DATE, ///< Start Date button @@ -52,22 +57,16 @@ enum SchdispatchWidgets { /** * Callback for when a time has been chosen to start the schedule - * @param windex The windows index + * @param p1 The p1 parameter to send to CmdScheduledDispatchSetStartDate * @param date the actually chosen date */ -static void SetScheduleStartDateIntl(uint32 windex, DateTicksScaled date) +static void SetScheduleStartDateIntl(uint32 p1, DateTicksScaled date) { Date start_date; uint16 start_full_date_fract; SchdispatchConvertToFullDateFract(date, &start_date, &start_full_date_fract); - uint32 p1 = 0, p2 = 0; - SB(p1, 0, 20, windex); - SB(p1, 20, 12, GB(start_full_date_fract, 2, 12)); - SB(p2, 0, 30, start_date); - SB(p2, 30, 2, GB(start_full_date_fract, 0, 2)); - - DoCommandP(0, p1, p2, CMD_SCHEDULED_DISPATCH_SET_START_DATE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); + DoCommandPEx(0, p1, start_date, start_full_date_fract, CMD_SCHEDULED_DISPATCH_SET_START_DATE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE), nullptr, nullptr, 0); } /** @@ -88,12 +87,15 @@ static void SetScheduleStartDateCallback(const Window *w, DateTicksScaled date) static void ScheduleAddIntl(uint32 p1, DateTicksScaled date, uint extra_slots, uint offset) { VehicleID veh = GB(p1, 0, 20); + uint schedule_index = GB(p1, 20, 12); Vehicle *v = Vehicle::GetIfValid(veh); - if (v == nullptr || !v->IsPrimaryVehicle()) return; + if (v == nullptr || !v->IsPrimaryVehicle() || schedule_index >= v->orders.list->GetScheduledDispatchScheduleCount()) return; + + const DispatchSchedule &ds = v->orders.list->GetDispatchScheduleByIndex(schedule_index); /* Make sure the time is the closest future to the timetable start */ - DateTicksScaled start_tick = v->orders.list->GetScheduledDispatchStartTick(); - uint32 duration = v->orders.list->GetScheduledDispatchDuration(); + DateTicksScaled start_tick = ds.GetScheduledDispatchStartTick(); + uint32 duration = ds.GetScheduledDispatchDuration(); while (date > start_tick) date -= duration; while (date < start_tick) date += duration; @@ -104,7 +106,7 @@ static void ScheduleAddIntl(uint32 p1, DateTicksScaled date, uint extra_slots, u extra_slots = std::min(extra_slots, UINT16_MAX); } - 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); + DoCommandPEx(0, p1, (uint32)(date - start_tick), (((uint64)extra_slots) << 32) | offset, CMD_SCHEDULED_DISPATCH_ADD | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE), nullptr, nullptr, 0); } /** @@ -152,8 +154,39 @@ static int CalculateMaxRequiredVehicle(Ticks timetable_duration, uint32 schedule return vehicle_count; } +static void AddNewScheduledDispatchSchedule(VehicleID vindex) +{ + Date start_date; + uint16 start_full_date_fract; + uint32 duration; + + if (_settings_time.time_in_minutes) { + /* Set to 00:00 of today, and 1 day */ + + DateTicksScaled val; + val = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), 0, 0); + val -= _settings_time.clock_offset; + val *= _settings_time.ticks_per_minute; + SchdispatchConvertToFullDateFract(val, &start_date, &start_full_date_fract); + + duration = 24 * 60 * _settings_time.ticks_per_minute; + } else { + /* Set Jan 1st and 365 day */ + start_date = DAYS_TILL(_cur_year); + start_full_date_fract = 0; + duration = 365 * DAY_TICKS; + } + + uint64 p3 = 0; + SB(p3, 0, 32, start_date); + SB(p3, 32, 16, start_full_date_fract); + + DoCommandPEx(0, vindex, duration, p3, CMD_SCHEDULED_DISPATCH_ADD_NEW_SCHEDULE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE), CcAddNewSchDispatchSchedule, nullptr, 0); +} + struct SchdispatchWindow : Window { const Vehicle *vehicle; ///< Vehicle monitored by the window. + int schedule_index; int clicked_widget; ///< The widget that was clicked (used to determine what to do in OnQueryTextFinished) Scrollbar *vscroll; ///< Verticle scrollbar uint num_columns; ///< Number of columns. @@ -171,6 +204,8 @@ struct SchdispatchWindow : Window { this->FinishInitNested(window_number); this->owner = this->vehicle->owner; + this->schedule_index = -1; + this->AutoSelectSchedule(); } ~SchdispatchWindow() @@ -188,8 +223,30 @@ struct SchdispatchWindow : Window { enum ManagementDropdown { SCH_MD_RESET_LAST_DISPATCHED, SCH_MD_CLEAR_SCHEDULE, + SCH_MD_REMOVE_SCHEDULE, }; + bool IsScheduleSelected() const + { + return this->vehicle->orders.list != nullptr && this->schedule_index >= 0 && (uint)this->schedule_index < this->vehicle->orders.list->GetScheduledDispatchScheduleCount(); + } + + void AutoSelectSchedule() + { + if (!this->IsScheduleSelected()) { + if (this->vehicle->orders.list != nullptr && this->vehicle->orders.list->GetScheduledDispatchScheduleCount() > 0) { + this->schedule_index = Clamp(this->schedule_index, 0, this->vehicle->orders.list->GetScheduledDispatchScheduleCount() - 1); + } else { + this->schedule_index = -1; + } + } + } + + const DispatchSchedule &GetSelectedSchedule() const + { + return this->vehicle->orders.list->GetDispatchScheduleByIndex(this->schedule_index); + } + virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override { switch (widget) { @@ -232,8 +289,8 @@ struct SchdispatchWindow : Window { void CountItem() { this->item_count = 0; - if (this->vehicle->orders.list != nullptr) { - this->item_count = (uint)this->vehicle->orders.list->GetScheduledDispatch().size(); + if (this->IsScheduleSelected()) { + this->item_count = (uint)this->GetSelectedSchedule().GetScheduledDispatch().size(); } } @@ -247,6 +304,7 @@ struct SchdispatchWindow : Window { switch (data) { case VIWD_MODIFY_ORDERS: if (!gui_scope) break; + this->AutoSelectSchedule(); this->ReInit(); break; } @@ -257,9 +315,15 @@ struct SchdispatchWindow : Window { const Vehicle *v = this->vehicle; CountItem(); - this->SetWidgetDisabledState(WID_SCHDISPATCH_ENABLED, (v->owner != _local_company) || HasBit(v->vehicle_flags, VF_TIMETABLE_SEPARATION)); + bool unusable = (v->owner != _local_company) || (v->orders.list == nullptr); - bool disabled = (v->owner != _local_company) || !HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) || (v->orders.list == nullptr); + this->SetWidgetDisabledState(WID_SCHDISPATCH_ENABLED, unusable || HasBit(v->vehicle_flags, VF_TIMETABLE_SEPARATION)); + + this->SetWidgetDisabledState(WID_SCHDISPATCH_PREV, v->orders.list == nullptr || this->schedule_index <= 0); + this->SetWidgetDisabledState(WID_SCHDISPATCH_NEXT, v->orders.list == nullptr || this->schedule_index >= (int)(v->orders.list->GetScheduledDispatchScheduleCount() - 1)); + this->SetWidgetDisabledState(WID_SCHDISPATCH_ADD_SCHEDULE, unusable || v->orders.list->GetScheduledDispatchScheduleCount() >= 4096); + + bool disabled = unusable || !HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) || !this->IsScheduleSelected(); this->SetWidgetDisabledState(WID_SCHDISPATCH_ADD, disabled); this->SetWidgetDisabledState(WID_SCHDISPATCH_SET_DURATION, disabled); this->SetWidgetDisabledState(WID_SCHDISPATCH_SET_START_DATE, disabled); @@ -275,7 +339,19 @@ struct SchdispatchWindow : Window { virtual void SetStringParameters(int widget) const override { switch (widget) { - case WID_SCHDISPATCH_CAPTION: SetDParam(0, this->vehicle->index); break; + case WID_SCHDISPATCH_CAPTION: + SetDParam(0, this->vehicle->index); + break; + + case WID_SCHDISPATCH_HEADER: + if (this->IsScheduleSelected()) { + SetDParam(0, STR_SCHDISPATCH_SCHEDULE_ID); + SetDParam(1, this->schedule_index + 1); + SetDParam(2, this->vehicle->orders.list->GetScheduledDispatchScheduleCount()); + } else { + SetDParam(0, STR_SCHDISPATCH_NO_SCHEDULES); + } + break; } } @@ -293,10 +369,11 @@ struct SchdispatchWindow : Window { } case WID_SCHDISPATCH_MANAGEMENT: { - uint64 params[2]; + uint64 params[3]; params[0] = STR_SCHDISPATCH_RESET_LAST_DISPATCH_TOOLTIP; params[1] = STR_SCHDISPATCH_CLEAR_TOOLTIP; - GuiShowTooltips(this, STR_SCHDISPATCH_MANAGE_TOOLTIP, 2, params, close_cond); + params[2] = STR_SCHDISPATCH_REMOVE_SCHEDULE_TOOLTIP; + GuiShowTooltips(this, STR_SCHDISPATCH_MANAGE_TOOLTIP, 3, params, close_cond); return true; } @@ -332,9 +409,9 @@ struct SchdispatchWindow : Window { virtual void OnGameTick() override { - const Vehicle *v = this->vehicle; - if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) && v->orders.list != nullptr) { - if (((v->orders.list->GetScheduledDispatchStartTick() + v->orders.list->GetScheduledDispatchLastDispatch()) > _scaled_date_ticks) != this->last_departure_future) { + if (HasBit(this->vehicle->vehicle_flags, VF_SCHEDULED_DISPATCH) && this->IsScheduleSelected()) { + const DispatchSchedule &ds = this->GetSelectedSchedule(); + if (((ds.GetScheduledDispatchStartTick() + ds.GetScheduledDispatchLastDispatch()) > _scaled_date_ticks) != this->last_departure_future) { SetWidgetDirty(WID_SCHDISPATCH_SUMMARY_PANEL); } } @@ -347,7 +424,7 @@ struct SchdispatchWindow : Window { switch (widget) { case WID_SCHDISPATCH_MATRIX: { /* If order is not initialized, don't draw */ - if (v->orders.list == nullptr) break; + if (!this->IsScheduleSelected()) break; bool rtl = _current_text_dir == TD_RTL; @@ -355,19 +432,21 @@ struct SchdispatchWindow : Window { const NWidgetCore *wid = this->GetWidget(WID_SCHDISPATCH_MATRIX); const uint16 rows_in_display = wid->current_y / wid->resize_y; + const DispatchSchedule &ds = this->GetSelectedSchedule(); + uint num = this->vscroll->GetPosition() * this->num_columns; - if (num >= v->orders.list->GetScheduledDispatch().size()) break; + if (num >= ds.GetScheduledDispatch().size()) break; const uint maxval = std::min(this->item_count, num + (rows_in_display * this->num_columns)); - auto current_schedule = v->orders.list->GetScheduledDispatch().begin() + num; - const DateTicksScaled start_tick = v->orders.list->GetScheduledDispatchStartTick(); - const DateTicksScaled end_tick = v->orders.list->GetScheduledDispatchStartTick() + v->orders.list->GetScheduledDispatchDuration(); + auto current_schedule = ds.GetScheduledDispatch().begin() + num; + const DateTicksScaled start_tick = ds.GetScheduledDispatchStartTick(); + const DateTicksScaled end_tick = ds.GetScheduledDispatchStartTick() + ds.GetScheduledDispatchDuration(); for (int y = r.top + 1; num < maxval; y += this->resize.step_height) { /* Draw the rows */ for (byte i = 0; i < this->num_columns && num < maxval; i++, num++) { /* Draw all departure time in the current row */ - if (current_schedule != v->orders.list->GetScheduledDispatch().end()) { + 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; this->DrawScheduledTime(draw_time, x, x + this->resize.step_width - 1, y, draw_time >= end_tick ? TC_RED : TC_BLACK); @@ -381,15 +460,14 @@ struct SchdispatchWindow : Window { } case WID_SCHDISPATCH_SUMMARY_PANEL: { - int y = r.top + WD_FRAMERECT_TOP; - if (!HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) || v->orders.list == nullptr) { + if (!HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) || !this->IsScheduleSelected()) { y += FONT_HEIGHT_NORMAL; DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SCHDISPATCH_SUMMARY_NOT_ENABLED); } else { - - const DateTicksScaled last_departure = v->orders.list->GetScheduledDispatchStartTick() + v->orders.list->GetScheduledDispatchLastDispatch(); + const DispatchSchedule &ds = this->GetSelectedSchedule(); + const DateTicksScaled last_departure = ds.GetScheduledDispatchStartTick() + ds.GetScheduledDispatchLastDispatch(); SetDParam(0, last_departure); const_cast(this)->last_departure_future = (last_departure > _scaled_date_ticks); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, @@ -404,7 +482,7 @@ struct SchdispatchWindow : Window { } } if (!have_conditional) { - const int required_vehicle = CalculateMaxRequiredVehicle(v->orders.list->GetTimetableTotalDuration(), v->orders.list->GetScheduledDispatchDuration(), v->orders.list->GetScheduledDispatch()); + const int required_vehicle = CalculateMaxRequiredVehicle(v->orders.list->GetTimetableTotalDuration(), ds.GetScheduledDispatchDuration(), ds.GetScheduledDispatch()); if (required_vehicle > 0) { SetDParam(0, required_vehicle); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SCHDISPATCH_SUMMARY_L1); @@ -412,16 +490,16 @@ struct SchdispatchWindow : Window { } y += FONT_HEIGHT_NORMAL; - SetTimetableParams(0, v->orders.list->GetScheduledDispatchDuration(), true); + SetTimetableParams(0, ds.GetScheduledDispatchDuration(), true); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SCHDISPATCH_SUMMARY_L2); y += FONT_HEIGHT_NORMAL; - SetDParam(0, v->orders.list->GetScheduledDispatchStartTick()); - SetDParam(1, v->orders.list->GetScheduledDispatchStartTick() + v->orders.list->GetScheduledDispatchDuration()); + SetDParam(0, ds.GetScheduledDispatchStartTick()); + SetDParam(1, ds.GetScheduledDispatchStartTick() + ds.GetScheduledDispatchDuration()); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SCHDISPATCH_SUMMARY_L3); y += FONT_HEIGHT_NORMAL; - SetTimetableParams(0, v->orders.list->GetScheduledDispatchDelay()); + SetTimetableParams(0, ds.GetScheduledDispatchDelay()); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SCHDISPATCH_SUMMARY_L4); y += FONT_HEIGHT_NORMAL; @@ -443,8 +521,8 @@ struct SchdispatchWindow : Window { warnings++; }; - uint32 duration = v->orders.list->GetScheduledDispatchDuration(); - for (uint32 slot : v->orders.list->GetScheduledDispatch()) { + uint32 duration = ds.GetScheduledDispatchDuration(); + for (uint32 slot : ds.GetScheduledDispatch()) { if (slot >= duration) { draw_warning(STR_SCHDISPATCH_SLOT_OUTSIDE_SCHEDULE); break; @@ -470,6 +548,8 @@ struct SchdispatchWindow : Window { */ void TimeClick(int x, int y) { + if (!this->IsScheduleSelected()) return; + const NWidgetCore *matrix_widget = this->GetWidget(WID_SCHDISPATCH_MATRIX); /* In case of RTL the widgets are swapped as a whole */ if (_current_text_dir == TD_RTL) x = matrix_widget->current_x - x; @@ -484,10 +564,12 @@ struct SchdispatchWindow : Window { uint pos = ((row + this->vscroll->GetPosition()) * this->num_columns) + xt; - if (pos >= this->item_count) return; + const DispatchSchedule &ds = this->GetSelectedSchedule(); + + if (pos >= this->item_count || pos >= ds.GetScheduledDispatch().size()) return; if (xm <= this->header_width) { - DoCommandP(0, this->vehicle->index, this->vehicle->orders.list->GetScheduledDispatch()[pos], CMD_SCHEDULED_DISPATCH_REMOVE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); + DoCommandP(0, this->vehicle->index | (this->schedule_index << 20), ds.GetScheduledDispatch()[pos], CMD_SCHEDULED_DISPATCH_REMOVE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); } } @@ -522,30 +604,35 @@ struct SchdispatchWindow : Window { uint32 p2 = 0; if (!HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)) SetBit(p2, 0); - if (!v->orders.list->IsScheduledDispatchValid()) v->orders.list->ResetScheduledDispatch(); DoCommandP(0, v->index, p2, CMD_SCHEDULED_DISPATCH | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); + if (HasBit(p2, 0) && this->vehicle->orders.list != nullptr && this->vehicle->orders.list->GetScheduledDispatchScheduleCount() == 0) { + AddNewScheduledDispatchSchedule(v->index); + } break; } case WID_SCHDISPATCH_ADD: { + if (!this->IsScheduleSelected()) break; 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); + ShowSetDateWindow(this, v->index | (this->schedule_index << 20), _scaled_date_ticks, _cur_year, _cur_year + 15, ScheduleAddCallback, STR_SCHDISPATCH_ADD, STR_SCHDISPATCH_ADD_TOOLTIP); } break; } case WID_SCHDISPATCH_SET_DURATION: { - SetDParam(0, ProcessDurationForQueryString(v->orders.list->GetScheduledDispatchDuration())); + if (!this->IsScheduleSelected()) break; + SetDParam(0, ProcessDurationForQueryString(this->GetSelectedSchedule().GetScheduledDispatchDuration())); ShowQueryString(STR_JUST_INT, STR_SCHDISPATCH_DURATION_CAPTION_MINUTE + this->GetQueryStringCaptionOffset(), 31, this, CS_NUMERAL, QSF_NONE); break; } case WID_SCHDISPATCH_SET_START_DATE: { + if (!this->IsScheduleSelected()) break; if (_settings_time.time_in_minutes && _settings_client.gui.timetable_start_text_entry) { uint64 time = _scaled_date_ticks; time /= _settings_time.ticks_per_minute; @@ -555,24 +642,43 @@ struct SchdispatchWindow : Window { SetDParam(0, time); ShowQueryString(STR_JUST_INT, STR_SCHDISPATCH_START_CAPTION_MINUTE, 31, this, CS_NUMERAL, QSF_ACCEPT_UNCHANGED); } else { - ShowSetDateWindow(this, v->index, _scaled_date_ticks, _cur_year, _cur_year + 15, SetScheduleStartDateCallback, STR_SCHDISPATCH_SET_START, STR_SCHDISPATCH_START_TOOLTIP); + ShowSetDateWindow(this, v->index | (this->schedule_index << 20), _scaled_date_ticks, _cur_year, _cur_year + 15, SetScheduleStartDateCallback, STR_SCHDISPATCH_SET_START, STR_SCHDISPATCH_START_TOOLTIP); } break; } case WID_SCHDISPATCH_SET_DELAY: { - SetDParam(0, ProcessDurationForQueryString(v->orders.list->GetScheduledDispatchDelay())); + if (!this->IsScheduleSelected()) break; + SetDParam(0, ProcessDurationForQueryString(this->GetSelectedSchedule().GetScheduledDispatchDelay())); ShowQueryString(STR_JUST_INT, STR_SCHDISPATCH_DELAY_CAPTION_MINUTE + this->GetQueryStringCaptionOffset(), 31, this, CS_NUMERAL, QSF_NONE); break; } case WID_SCHDISPATCH_MANAGEMENT: { + if (!this->IsScheduleSelected()) break; DropDownList list; list.emplace_back(new DropDownListStringItem(STR_SCHDISPATCH_RESET_LAST_DISPATCH, SCH_MD_RESET_LAST_DISPATCHED, false)); list.emplace_back(new DropDownListStringItem(STR_SCHDISPATCH_CLEAR, SCH_MD_CLEAR_SCHEDULE, false)); + list.emplace_back(new DropDownListStringItem(STR_SCHDISPATCH_REMOVE_SCHEDULE, SCH_MD_REMOVE_SCHEDULE, 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--; + this->ReInit(); + break; + + case WID_SCHDISPATCH_NEXT: + if (!this->IsScheduleSelected()) break; + if (this->schedule_index < (int)(this->vehicle->orders.list->GetScheduledDispatchScheduleCount() - 1)) this->schedule_index++; + this->ReInit(); + break; + + case WID_SCHDISPATCH_ADD_SCHEDULE: + AddNewScheduledDispatchSchedule(this->vehicle->index); + break; } this->SetDirty(); @@ -582,13 +688,18 @@ struct SchdispatchWindow : Window { { switch (widget) { case WID_SCHDISPATCH_MANAGEMENT: { + if (!this->IsScheduleSelected()) break; switch((ManagementDropdown)index) { case SCH_MD_RESET_LAST_DISPATCHED: - DoCommandP(0, this->vehicle->index, 0, CMD_SCHEDULED_DISPATCH_RESET_LAST_DISPATCH | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); + DoCommandP(0, this->vehicle->index | (this->schedule_index << 20), 0, CMD_SCHEDULED_DISPATCH_RESET_LAST_DISPATCH | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); break; case SCH_MD_CLEAR_SCHEDULE: - DoCommandP(0, this->vehicle->index, 0, CMD_SCHEDULED_DISPATCH_CLEAR | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); + DoCommandP(0, this->vehicle->index | (this->schedule_index << 20), 0, CMD_SCHEDULED_DISPATCH_CLEAR | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); + break; + + case SCH_MD_REMOVE_SCHEDULE: + DoCommandP(0, this->vehicle->index | (this->schedule_index << 20), 0, CMD_SCHEDULED_DISPATCH_REMOVE_SCHEDULE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); break; } } @@ -607,6 +718,7 @@ struct SchdispatchWindow : Window { default: NOT_REACHED(); case WID_SCHDISPATCH_ADD: { + if (!this->IsScheduleSelected()) break; char *end; int32 val = StrEmpty(str) ? -1 : strtoul(str, &end, 10); @@ -616,12 +728,13 @@ 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, 0, 0); + ScheduleAddIntl(v->index | (this->schedule_index << 20), slot, 0, 0); } break; } case WID_SCHDISPATCH_SET_START_DATE: { + if (!this->IsScheduleSelected()) break; char *end; int32 val = StrEmpty(str) ? -1 : strtoul(str, &end, 10); @@ -631,30 +744,32 @@ struct SchdispatchWindow : Window { DateTicksScaled start = MINUTES_DATE(MINUTES_DAY(CURRENT_MINUTE), hours, minutes); start -= _settings_time.clock_offset; start *= _settings_time.ticks_per_minute; - SetScheduleStartDateIntl(v->index, start); + SetScheduleStartDateIntl(v->index | (this->schedule_index << 20), start); } break; } case WID_SCHDISPATCH_SET_DURATION: { + if (!this->IsScheduleSelected()) break; int32 val = StrEmpty(str) ? 0 : strtoul(str, nullptr, 10); if (val > 0) { if (!_settings_client.gui.timetable_in_ticks) val *= DATE_UNIT_SIZE; - DoCommandP(0, v->index, val, CMD_SCHEDULED_DISPATCH_SET_DURATION | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); + DoCommandP(0, v->index | (this->schedule_index << 20), val, CMD_SCHEDULED_DISPATCH_SET_DURATION | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); } break; } case WID_SCHDISPATCH_SET_DELAY: { + if (!this->IsScheduleSelected()) break; char *end; int32 val = StrEmpty(str) ? -1 : strtoul(str, &end, 10); if (val >= 0 && end && *end == 0) { if (!_settings_client.gui.timetable_in_ticks) val *= DATE_UNIT_SIZE; - DoCommandP(0, v->index, val, CMD_SCHEDULED_DISPATCH_SET_DELAY | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); + DoCommandP(0, v->index | (this->schedule_index << 20), val, CMD_SCHEDULED_DISPATCH_SET_DELAY | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); } break; } @@ -685,15 +800,25 @@ struct SchdispatchWindow : Window { void AddMultipleDepartureSlots(uint start, uint step, uint end) { - if (end < start || step == 0) return; + if (end < start || step == 0 || !this->IsScheduleSelected()) 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); + ScheduleAddIntl(this->vehicle->index | (this->schedule_index << 20), slot, (end - start) / step, step * _settings_time.ticks_per_minute); } }; +void CcAddNewSchDispatchSchedule(const CommandCost &result, TileIndex tile, uint32 p1, uint32 p2, uint64 p3, uint32 cmd) +{ + SchdispatchWindow *w = dynamic_cast(FindWindowById(WC_SCHDISPATCH_SLOTS, p1)); + if (w != nullptr) { + w->schedule_index = INT_MAX; + w->AutoSelectSchedule(); + w->ReInit(); + } +} + static const NWidgetPart _nested_schdispatch_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_GREY), @@ -702,25 +827,36 @@ static const NWidgetPart _nested_schdispatch_widgets[] = { NWidget(WWT_DEFSIZEBOX, COLOUR_GREY), NWidget(WWT_STICKYBOX, COLOUR_GREY), EndContainer(), - NWidget(NWID_HORIZONTAL), - NWidget(WWT_MATRIX, COLOUR_GREY, WID_SCHDISPATCH_MATRIX), SetResize(1, 1), SetScrollbar(WID_SCHDISPATCH_V_SCROLL), - NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_SCHDISPATCH_V_SCROLL), - EndContainer(), - NWidget(WWT_PANEL, COLOUR_GREY, WID_SCHDISPATCH_SUMMARY_PANEL), SetMinimalSize(400, 22), SetResize(1, 0), EndContainer(), - NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), - NWidget(NWID_VERTICAL, NC_EQUALSIZE), + 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_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_ADD), SetDataTip(STR_SCHDISPATCH_ADD, STR_SCHDISPATCH_ADD_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(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), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_ADD_SCHEDULE), SetDataTip(STR_SCHDISPATCH_ADD_SCHEDULE, STR_SCHDISPATCH_ADD_SCHEDULE_TOOLTIP), SetFill(1, 1), SetResize(1, 0), + EndContainer(), EndContainer(), + NWidget(NWID_HORIZONTAL), + NWidget(WWT_MATRIX, COLOUR_GREY, WID_SCHDISPATCH_MATRIX), SetResize(1, 1), SetScrollbar(WID_SCHDISPATCH_V_SCROLL), + NWidget(NWID_VSCROLLBAR, COLOUR_GREY, WID_SCHDISPATCH_V_SCROLL), + EndContainer(), + NWidget(WWT_PANEL, COLOUR_GREY, WID_SCHDISPATCH_SUMMARY_PANEL), SetMinimalSize(400, 22), SetResize(1, 0), EndContainer(), + NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), NWidget(NWID_VERTICAL, NC_EQUALSIZE), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_SET_DURATION), SetDataTip(STR_SCHDISPATCH_DURATION, STR_SCHDISPATCH_DURATION_TOOLTIP), SetFill(1, 1), SetResize(1, 0), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_SET_START_DATE), SetDataTip(STR_SCHDISPATCH_START, STR_SCHDISPATCH_START_TOOLTIP), SetFill(1, 1), SetResize(1, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_ADD), SetDataTip(STR_SCHDISPATCH_ADD, STR_SCHDISPATCH_ADD_TOOLTIP), SetFill(1, 1), SetResize(1, 0), + NWidget(NWID_SPACER), SetFill(1, 1), + EndContainer(), + NWidget(NWID_VERTICAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_SET_DURATION), SetDataTip(STR_SCHDISPATCH_DURATION, STR_SCHDISPATCH_DURATION_TOOLTIP), SetFill(1, 1), SetResize(1, 0), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_SET_START_DATE), SetDataTip(STR_SCHDISPATCH_START, STR_SCHDISPATCH_START_TOOLTIP), SetFill(1, 1), SetResize(1, 0), + EndContainer(), + NWidget(NWID_VERTICAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_SET_DELAY), SetDataTip(STR_SCHDISPATCH_DELAY, STR_SCHDISPATCH_DELAY_TOOLTIP), SetFill(1, 1), SetResize(1, 0), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_SCHDISPATCH_MANAGEMENT), SetDataTip(STR_SCHDISPATCH_MANAGE, STR_SCHDISPATCH_MANAGE_TOOLTIP), SetFill(1, 1), SetResize(1, 0), + EndContainer(), + NWidget(WWT_RESIZEBOX, COLOUR_GREY), EndContainer(), - NWidget(NWID_VERTICAL, NC_EQUALSIZE), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_SCHDISPATCH_SET_DELAY), SetDataTip(STR_SCHDISPATCH_DELAY, STR_SCHDISPATCH_DELAY_TOOLTIP), SetFill(1, 1), SetResize(1, 0), - NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_SCHDISPATCH_MANAGEMENT), SetDataTip(STR_SCHDISPATCH_MANAGE, STR_SCHDISPATCH_MANAGE_TOOLTIP), SetFill(1, 1), SetResize(1, 0), - EndContainer(), - NWidget(WWT_RESIZEBOX, COLOUR_GREY), EndContainer(), }; diff --git a/src/timetable_cmd.cpp b/src/timetable_cmd.cpp index a53971bdfc..378dc98b9c 100644 --- a/src/timetable_cmd.cpp +++ b/src/timetable_cmd.cpp @@ -48,7 +48,7 @@ static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint32 val, } order->SetWaitTime(val); order->SetWaitTimetabled(timetabled); - if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) && timetabled && order->IsWaitTimetabled() && v->GetFirstWaitingLocation(true) == order_number) { + if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) && timetabled && order->IsScheduledDispatchOrder(true)) { for (Vehicle *u = v->FirstShared(); u != nullptr; u = u->NextShared()) { if (u->cur_implicit_order_index == order_number && (u->last_station_visited == order->GetDestination())) { u->lateness_counter += timetable_delta; @@ -84,6 +84,18 @@ static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint32 val, order->SetLeaveType((OrderLeaveType)val); break; + case MTF_ASSIGN_SCHEDULE: + if ((int)val >= 0) { + for (int n = 0; n < v->GetNumOrders(); n++) { + Order *o = v->GetOrder(n); + if (o->GetDispatchScheduleIndex() == (int)val) { + o->SetDispatchScheduleIndex(-1); + } + } + } + order->SetDispatchScheduleIndex((int)val); + break; + default: NOT_REACHED(); } @@ -119,6 +131,10 @@ static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint32 val, v->current_order.SetLeaveType((OrderLeaveType)val); break; + case MTF_ASSIGN_SCHEDULE: + v->current_order.SetDispatchScheduleIndex((int)val); + break; + default: NOT_REACHED(); } @@ -170,6 +186,7 @@ CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, u bool wait_fixed = order->IsWaitFixed(); bool travel_fixed = order->IsTravelFixed(); OrderLeaveType leave_type = order->GetLeaveType(); + int dispatch_index = order->GetDispatchScheduleIndex(); switch (mtf) { case MTF_WAIT_TIME: wait_time = p2; @@ -199,6 +216,11 @@ CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, u if (leave_type >= OLT_END) return CMD_ERROR; break; + case MTF_ASSIGN_SCHEDULE: + dispatch_index = (int)p2; + if (dispatch_index < -1 || dispatch_index >= (int)v->orders.list->GetScheduledDispatchScheduleCount()) return CMD_ERROR; + break; + default: NOT_REACHED(); } @@ -223,6 +245,23 @@ CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, u } } + if (dispatch_index != order->GetDispatchScheduleIndex()) { + switch (order->GetType()) { + case OT_GOTO_STATION: + if (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) { + if (mtf == MTF_ASSIGN_SCHEDULE && dispatch_index == -1) break; + return_cmd_error(STR_ERROR_TIMETABLE_NOT_STOPPING_HERE); + } + break; + + case OT_GOTO_DEPOT: + case OT_GOTO_WAYPOINT: + break; + + default: return_cmd_error(STR_ERROR_TIMETABLE_ONLY_WAIT_AT_STATIONS); + } + } + if (travel_time != order->GetTravelTime() && order->IsType(OT_CONDITIONAL)) return CMD_ERROR; if (max_speed != order->GetMaxSpeed() && (order->IsType(OT_CONDITIONAL) || v->type == VEH_AIRCRAFT)) return CMD_ERROR; if (wait_fixed != order->IsWaitFixed() && order->IsType(OT_CONDITIONAL)) return CMD_ERROR; @@ -268,6 +307,12 @@ CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, u } break; + case MTF_ASSIGN_SCHEDULE: + if (dispatch_index != order->GetDispatchScheduleIndex()) { + ChangeTimetable(v, order_number, dispatch_index, MTF_ASSIGN_SCHEDULE, true); + } + break; + default: break; } @@ -733,21 +778,16 @@ void UpdateSeparationOrder(Vehicle *v_start) } } -static bool IsVehicleAtFirstWaitingLocation(const Vehicle *v) -{ - return (v->cur_implicit_order_index == v->GetFirstWaitingLocation(true)); -} - -static DateTicksScaled GetScheduledDispatchTime(Vehicle *v, int wait_offset) +static DateTicksScaled GetScheduledDispatchTime(const DispatchSchedule &ds, int wait_offset) { DateTicksScaled first_slot = -1; - const DateTicksScaled begin_time = v->orders.list->GetScheduledDispatchStartTick(); - const int32 last_dispatched_offset = v->orders.list->GetScheduledDispatchLastDispatch(); - const uint32 dispatch_duration = v->orders.list->GetScheduledDispatchDuration(); - const int32 max_delay = v->orders.list->GetScheduledDispatchDelay(); + const DateTicksScaled begin_time = ds.GetScheduledDispatchStartTick(); + const int32 last_dispatched_offset = ds.GetScheduledDispatchLastDispatch(); + const uint32 dispatch_duration = ds.GetScheduledDispatchDuration(); + const int32 max_delay = ds.GetScheduledDispatchDelay(); /* Find next available slots */ - for (auto current_offset : v->orders.list->GetScheduledDispatch()) { + for (auto current_offset : ds.GetScheduledDispatch()) { if (current_offset >= dispatch_duration) continue; if (int32(current_offset) <= last_dispatched_offset) { current_offset += dispatch_duration * ((last_dispatched_offset + dispatch_duration - current_offset) / dispatch_duration); @@ -801,17 +841,20 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling) bool set_scheduled_dispatch = false; /* Start scheduled dispatch at first opportunity */ - if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)) { - if (IsVehicleAtFirstWaitingLocation(v) && travelling) { + if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) && v->cur_implicit_order_index != INVALID_VEH_ORDER_ID) { + Order *real_implicit_order = v->GetOrder(v->cur_implicit_order_index); + if (real_implicit_order->IsScheduledDispatchOrder(true) && travelling) { + DispatchSchedule &ds = v->orders.list->GetDispatchScheduleByIndex(real_implicit_order->GetDispatchScheduleIndex()); + /* Update scheduled information */ - v->orders.list->UpdateScheduledDispatch(); + ds.UpdateScheduledDispatch(); const int wait_offset = real_current_order->GetTimetabledWait(); - DateTicksScaled slot = GetScheduledDispatchTime(v, wait_offset); + DateTicksScaled slot = GetScheduledDispatchTime(ds, wait_offset); if (slot > -1) { SetBit(v->vehicle_flags, VF_TIMETABLE_STARTED); v->lateness_counter = _scaled_date_ticks - slot + wait_offset; - v->orders.list->SetScheduledDispatchLastDispatch(slot - v->orders.list->GetScheduledDispatchStartTick()); + ds.SetScheduledDispatchLastDispatch(slot - ds.GetScheduledDispatchStartTick()); set_scheduled_dispatch = true; } } diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp index ad172be05f..290e74cb41 100644 --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -123,9 +123,6 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID table[i].arrival = table[i].departure = INVALID_TICKS; } - VehicleOrderID scheduled_dispatch_order = INVALID_VEH_ORDER_ID; - if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)) scheduled_dispatch_order = v->GetFirstWaitingLocation(true); - /* Cyclically loop over all orders until we reach the current one again. * As we may start at the current order, do a post-checking loop */ do { @@ -139,7 +136,7 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID table[i].arrival = sum; } - if (i == scheduled_dispatch_order && !(i == start && !travelling)) return; + if (order->IsScheduledDispatchOrder(true) && !(i == start && !travelling)) return; if (!CanDetermineTimeTaken(order, false)) return; sum += order->GetTimetabledWait(); table[i].departure = sum; @@ -240,12 +237,30 @@ void ProcessTimetableWarnings(const Vehicle *v, std::functionvehicle_flags, VF_AUTOFILL_TIMETABLE)) handler(STR_TIMETABLE_WARNING_AUTOFILL_CONDITIONAL, true); if (total_time && have_non_timetabled_conditional_branch) handler(STR_TIMETABLE_NON_TIMETABLED_BRANCH, false); - if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)) { - VehicleOrderID n = v->GetFirstWaitingLocation(false); - if (n == INVALID_VEH_ORDER_ID) { - handler(STR_TIMETABLE_WARNING_NO_SCHEDULED_DISPATCH_ORDER, true); - } else if (!v->GetOrder(n)->IsWaitTimetabled()) { - handler(STR_TIMETABLE_WARNING_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME, true); + if (HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) && v->orders.list != nullptr) { + auto sd_warning = [&](int schedule_index, StringID str) { + if (v->orders.list->GetScheduledDispatchScheduleCount() > 1) { + SetDParam(0, schedule_index + 1); + SetDParam(1, str); + handler(STR_TIMETABLE_WARNING_SCHEDULE_ID, true); + } else { + handler(str, true); + } + }; + std::vector seen_sched_dispatch_orders(v->orders.list->GetScheduledDispatchScheduleCount()); + + for (int n = 0; n < v->GetNumOrders(); n++) { + const Order *order = v->GetOrder(n); + int schedule_index = order->GetDispatchScheduleIndex(); + if (schedule_index >= 0) { + seen_sched_dispatch_orders[schedule_index] = true; + if (!order->IsWaitTimetabled()) { + sd_warning(schedule_index, STR_TIMETABLE_WARNING_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME); + } + } + } + for (uint i = 0; i < seen_sched_dispatch_orders.size(); i++) { + if (!seen_sched_dispatch_orders[i]) sd_warning(i, STR_TIMETABLE_WARNING_NO_SCHEDULED_DISPATCH_ORDER_ASSIGNED); } } } @@ -483,6 +498,7 @@ struct TimetableWindow : Window { this->SetWidgetDisabledState(WID_VT_LOCK_ORDER_TIME, !wait_lockable); this->SetWidgetLoweredState(WID_VT_LOCK_ORDER_TIME, wait_locked); this->SetWidgetDisabledState(WID_VT_EXTRA, disable || (selected % 2 != 0)); + this->SetWidgetDisabledState(WID_VT_ASSIGN_SCHEDULE, disable || (selected % 2 != 0) || !HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)); } else { this->DisableWidget(WID_VT_START_DATE); this->DisableWidget(WID_VT_CHANGE_TIME); @@ -497,14 +513,17 @@ struct TimetableWindow : Window { this->DisableWidget(WID_VT_ADD_VEH_GROUP); this->DisableWidget(WID_VT_LOCK_ORDER_TIME); this->DisableWidget(WID_VT_EXTRA); + this->DisableWidget(WID_VT_ASSIGN_SCHEDULE); } this->SetWidgetLoweredState(WID_VT_AUTOFILL, HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE)); this->SetWidgetLoweredState(WID_VT_AUTOMATE, HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE)); this->SetWidgetLoweredState(WID_VT_AUTO_SEPARATION, HasBit(v->vehicle_flags, VF_TIMETABLE_SEPARATION)); this->SetWidgetLoweredState(WID_VT_SCHEDULED_DISPATCH, HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)); + this->SetWidgetLoweredState(WID_VT_SCHEDULED_DISPATCH, HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)); this->SetWidgetDisabledState(WID_VT_SCHEDULED_DISPATCH, v->orders.list == nullptr); + this->GetWidget(WID_VT_START_DATE_SELECTION)->SetDisplayedPlane(HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH) ? 1 : 0); this->DrawWidgets(); } @@ -944,6 +963,22 @@ struct TimetableWindow : Window { ShowDropDownList(this, std::move(list), order != nullptr ? order->GetLeaveType() : -1, WID_VT_EXTRA); break; } + + case WID_VT_ASSIGN_SCHEDULE: { + VehicleOrderID real = (this->sel_index + 1) / 2; + if (real >= this->vehicle->GetNumOrders()) real = 0; + const Order *order = this->vehicle->GetOrder(real); + DropDownList list; + list.emplace_back(new DropDownListStringItem(STR_TIMETABLE_ASSIGN_SCHEDULE_NONE, -1, false)); + + for (uint i = 0; i < v->orders.list->GetScheduledDispatchScheduleCount(); i++) { + DropDownListParamStringItem *item = new DropDownListParamStringItem(STR_TIMETABLE_ASSIGN_SCHEDULE_ID, i, false); + item->SetParam(0, i + 1); + list.emplace_back(item); + } + ShowDropDownList(this, std::move(list), order->GetDispatchScheduleIndex(), WID_VT_ASSIGN_SCHEDULE); + break; + } } this->SetDirty(); @@ -952,9 +987,13 @@ struct TimetableWindow : Window { void OnDropdownSelect(int widget, int index) override { switch (widget) { - case WID_VT_EXTRA: { + case WID_VT_EXTRA: ExecuteTimetableCommand(this->vehicle, false, this->sel_index, MTF_SET_LEAVE_TYPE, index, false); - } + break; + + case WID_VT_ASSIGN_SCHEDULE: + ExecuteTimetableCommand(this->vehicle, false, this->sel_index, MTF_ASSIGN_SCHEDULE, index, false); + break; default: break; @@ -1068,7 +1107,10 @@ static const NWidgetPart _nested_timetable_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL, NC_EQUALSIZE), NWidget(NWID_VERTICAL, NC_EQUALSIZE), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_START_DATE), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_STARTING_DATE, STR_TIMETABLE_STARTING_DATE_TOOLTIP), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_VT_START_DATE_SELECTION), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_START_DATE), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_STARTING_DATE, STR_TIMETABLE_STARTING_DATE_TOOLTIP), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_VT_ASSIGN_SCHEDULE), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_ASSIGN_SCHEDULE_DROP_DOWN, STR_TIMETABLE_ASSIGN_SCHEDULE_DROP_DOWN_TOOLTIP), + EndContainer(), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_CHANGE_TIME), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_CHANGE_TIME, STR_TIMETABLE_WAIT_TIME_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_CLEAR_TIME), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_CLEAR_TIME, STR_TIMETABLE_CLEAR_TIME_TOOLTIP), EndContainer(), diff --git a/src/widgets/timetable_widget.h b/src/widgets/timetable_widget.h index 5bf05e4e96..8dcecaa080 100644 --- a/src/widgets/timetable_widget.h +++ b/src/widgets/timetable_widget.h @@ -37,6 +37,8 @@ enum VehicleTimetableWidgets { WID_VT_SCHEDULED_DISPATCH, ///< Scheduled Dispatch button. WID_VT_LOCK_ORDER_TIME, ///< Lock order time button. WID_VT_EXTRA, ///< Extra drop down menu. + WID_VT_ASSIGN_SCHEDULE, ///< Assign scheduled dispatch schedule. + WID_VT_START_DATE_SELECTION, ///< #NWID_SELECTION widget for WID_VT_START_DATE and WID_VT_ASSIGN_SCHEDULE. }; #endif /* WIDGETS_TIMETABLE_WIDGET_H */ From 4f7f8c5b63fa145f487abf3a9fb6ced2a21c70e9 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Thu, 13 Jan 2022 23:04:38 +0000 Subject: [PATCH 02/11] Show which order schedule assigned to in scheduled dispatch window --- src/lang/english.txt | 3 ++ src/schdispatch_gui.cpp | 99 ++++++++++++++++++++++++++++++----------- 2 files changed, 75 insertions(+), 27 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 28e7b0f967..6eb63a64a5 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -7144,6 +7144,9 @@ 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_NOT_ASSIGNED_TO_ORDER :{BLACK}This dispatch schedule is not assigned to an order. +STR_SCHDISPATCH_ASSIGNED_TO_ORDER :{BLACK}This dispatch schedule is assigned to order {NUM}: {STRING1}. + STR_SCHDISPATCH_ADD_DEPARTURE_SLOTS_START :{BLACK}Start: STR_SCHDISPATCH_ADD_DEPARTURE_SLOTS_STEP :{BLACK}Period: STR_SCHDISPATCH_ADD_DEPARTURE_SLOTS_END :{BLACK}End: diff --git a/src/schdispatch_gui.cpp b/src/schdispatch_gui.cpp index d0ba9cfa01..74575c6352 100644 --- a/src/schdispatch_gui.cpp +++ b/src/schdispatch_gui.cpp @@ -194,6 +194,7 @@ struct SchdispatchWindow : Window { uint item_count = 0; ///< Number of scheduled item bool last_departure_future; ///< True if last departure is currently displayed in the future uint warning_count = 0; + bool no_order_warning_pad = false; SchdispatchWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc), @@ -274,7 +275,12 @@ struct SchdispatchWindow : Window { } case WID_SCHDISPATCH_SUMMARY_PANEL: - size->height = WD_FRAMERECT_TOP + 5 * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM; + size->height = WD_FRAMERECT_TOP + 6 * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM; + uint warning_count = this->warning_count; + if (this->no_order_warning_pad) { + warning_count++; + size->height -= FONT_HEIGHT_NORMAL; + } if (warning_count > 0) { const Dimension warning_dimensions = GetSpriteSize(SPR_WARNING_SIGN); size->height += warning_count * std::max(warning_dimensions.height, FONT_HEIGHT_NORMAL); @@ -467,6 +473,67 @@ struct SchdispatchWindow : Window { DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SCHDISPATCH_SUMMARY_NOT_ENABLED); } else { const DispatchSchedule &ds = this->GetSelectedSchedule(); + + auto draw_warning = [&](StringID text) { + const Dimension warning_dimensions = GetSpriteSize(SPR_WARNING_SIGN); + int step_height = std::max(warning_dimensions.height, FONT_HEIGHT_NORMAL); + int left = r.left + WD_FRAMERECT_LEFT; + int right = r.right - WD_FRAMERECT_RIGHT; + const bool rtl = (_current_text_dir == TD_RTL); + DrawSprite(SPR_WARNING_SIGN, 0, rtl ? right - warning_dimensions.width - 5 : left + 5, y + (step_height - warning_dimensions.height) / 2); + if (rtl) { + right -= (warning_dimensions.width + 10); + } else { + left += (warning_dimensions.width + 10); + } + DrawString(left, right, y + (step_height - FONT_HEIGHT_NORMAL) / 2, text); + y += step_height; + }; + + bool have_conditional = false; + int schedule_order_index = -1; + for (int n = 0; n < v->GetNumOrders(); n++) { + const Order *order = v->GetOrder(n); + if (order->IsType(OT_CONDITIONAL)) { + have_conditional = true; + } + if (order->GetDispatchScheduleIndex() == this->schedule_index) { + schedule_order_index = n; + } + } + bool no_order_warning_pad = false; + if (schedule_order_index < 0) { + draw_warning(STR_SCHDISPATCH_NOT_ASSIGNED_TO_ORDER); + no_order_warning_pad = true; + } else { + const Order *order = v->GetOrder(schedule_order_index); + SetDParam(0, schedule_order_index + 1); + + switch (order->GetType()) { + case OT_GOTO_STATION: + SetDParam(1, STR_STATION_NAME); + SetDParam(2, order->GetDestination()); + break; + + case OT_GOTO_WAYPOINT: + SetDParam(1, STR_WAYPOINT_NAME); + SetDParam(2, order->GetDestination()); + break; + + case OT_GOTO_DEPOT: + SetDParam(1, STR_DEPOT_NAME); + SetDParam(2, order->GetDestination()); + break; + + default: + SetDParam(1, STR_INVALID_ORDER); + break; + } + + DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SCHDISPATCH_ASSIGNED_TO_ORDER); + y += FONT_HEIGHT_NORMAL; + } + const DateTicksScaled last_departure = ds.GetScheduledDispatchStartTick() + ds.GetScheduledDispatchLastDispatch(); SetDParam(0, last_departure); const_cast(this)->last_departure_future = (last_departure > _scaled_date_ticks); @@ -474,13 +541,6 @@ struct SchdispatchWindow : Window { this->last_departure_future ? STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_FUTURE : STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_PAST); y += FONT_HEIGHT_NORMAL; - bool have_conditional = false; - for (int n = 0; n < v->GetNumOrders(); n++) { - const Order *order = v->GetOrder(n); - if (order->IsType(OT_CONDITIONAL)) { - have_conditional = true; - } - } if (!have_conditional) { const int required_vehicle = CalculateMaxRequiredVehicle(v->orders.list->GetTimetableTotalDuration(), ds.GetScheduledDispatchDuration(), ds.GetScheduledDispatch()); if (required_vehicle > 0) { @@ -503,35 +563,20 @@ struct SchdispatchWindow : Window { DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SCHDISPATCH_SUMMARY_L4); y += FONT_HEIGHT_NORMAL; - uint warnings = 0; - auto draw_warning = [&](StringID text) { - const Dimension warning_dimensions = GetSpriteSize(SPR_WARNING_SIGN); - int step_height = std::max(warning_dimensions.height, FONT_HEIGHT_NORMAL); - int left = r.left + WD_FRAMERECT_LEFT; - int right = r.right - WD_FRAMERECT_RIGHT; - const bool rtl = (_current_text_dir == TD_RTL); - DrawSprite(SPR_WARNING_SIGN, 0, rtl ? right - warning_dimensions.width - 5 : left + 5, y + (step_height - warning_dimensions.height) / 2); - if (rtl) { - right -= (warning_dimensions.width + 10); - } else { - left += (warning_dimensions.width + 10); - } - DrawString(left, right, y + (step_height - FONT_HEIGHT_NORMAL) / 2, text); - y += step_height; - warnings++; - }; - uint32 duration = ds.GetScheduledDispatchDuration(); + uint warnings = 0; for (uint32 slot : ds.GetScheduledDispatch()) { if (slot >= duration) { draw_warning(STR_SCHDISPATCH_SLOT_OUTSIDE_SCHEDULE); + warnings++; break; } } - if (warnings != this->warning_count) { + if (warnings != this->warning_count || no_order_warning_pad != this->no_order_warning_pad) { SchdispatchWindow *mutable_this = const_cast(this); mutable_this->warning_count = warnings; + mutable_this->no_order_warning_pad = no_order_warning_pad; mutable_this->ReInit(); } } From 65cb4ccedf25386608dbf6041beae9ee4f27036d Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 14 Jan 2022 18:11:59 +0000 Subject: [PATCH 03/11] Limit number of warnings whon in timetable window --- src/lang/english.txt | 2 ++ src/timetable_gui.cpp | 21 +++++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 6eb63a64a5..2c7e6e82e8 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5742,6 +5742,8 @@ STR_TIMETABLE_WARNING_NO_SCHEDULED_DISPATCH_ORDER_ASSIGNED :{BLACK}The disp STR_TIMETABLE_WARNING_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME :{BLACK}The scheduled dispatch order should have a timetabled wait time STR_TIMETABLE_WARNING_SCHEDULE_ID :{BLACK}Schedule {NUM}: {STRING} +STR_TIMETABLE_WARNINGS_OMITTED :{BLACK}{NUM} further warnings omitted... + # Date window (for timetable) STR_DATE_CAPTION :{WHITE}Set date STR_DATE_SET_DATE :{BLACK}Set date diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp index 290e74cb41..c7de060a91 100644 --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -279,6 +279,10 @@ struct TimetableWindow : Window { bool change_timetable_all; ///< Set wait time or speed for all timetable entries (ctrl-click) action int summary_warnings = 0; ///< NUmber of summary warnings shown + enum { + MAX_SUMMARY_WARNINGS = 10, + }; + TimetableWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc), sel_index(-1), @@ -345,7 +349,7 @@ struct TimetableWindow : Window { case WID_VT_SUMMARY_PANEL: { Dimension d = GetSpriteSize(SPR_WARNING_SIGN); - size->height = WD_FRAMERECT_TOP + 2 * FONT_HEIGHT_NORMAL + this->summary_warnings * std::max(d.height, FONT_HEIGHT_NORMAL) + WD_FRAMERECT_BOTTOM; + size->height = WD_FRAMERECT_TOP + 2 * FONT_HEIGHT_NORMAL + std::min(MAX_SUMMARY_WARNINGS, this->summary_warnings) * std::max(d.height, FONT_HEIGHT_NORMAL) + WD_FRAMERECT_BOTTOM; break; } } @@ -743,9 +747,7 @@ struct TimetableWindow : Window { const int warning_offset_y = (step_height - warning_dimensions.height) / 2; const bool rtl = _current_text_dir == TD_RTL; - int warning_count = 0; - - ProcessTimetableWarnings(v, [&](StringID text, bool warning) { + auto draw_warning = [&](StringID text, bool warning) { int left = r.left + WD_FRAMERECT_LEFT; int right = r.right - WD_FRAMERECT_RIGHT; if (warning) { @@ -758,8 +760,19 @@ struct TimetableWindow : Window { } DrawString(left, right, y + text_offset_y, text); y += step_height; + }; + + int warning_count = 0; + int warning_limit = this->summary_warnings > MAX_SUMMARY_WARNINGS ? MAX_SUMMARY_WARNINGS - 1 : std::min(MAX_SUMMARY_WARNINGS, this->summary_warnings); + + ProcessTimetableWarnings(v, [&](StringID text, bool warning) { + if (warning_count < warning_limit) draw_warning(text, warning); warning_count++; }); + if (warning_count > warning_limit) { + SetDParam(0, warning_count - warning_limit); + draw_warning(STR_TIMETABLE_WARNINGS_OMITTED, true); + } if (warning_count != this->summary_warnings) { TimetableWindow *mutable_this = const_cast(this); From 8754e1c5d07de6da616f5187bcde635db5929c95 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 14 Jan 2022 22:49:49 +0000 Subject: [PATCH 04/11] Show scheduled dispatch marker in yellow in timetable window --- src/lang/english.txt | 2 +- src/lang/galician.txt | 2 +- src/lang/german.txt | 2 +- src/lang/korean.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 2c7e6e82e8..a2e3669dfa 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5701,7 +5701,7 @@ STR_TIMETABLE_AUTO_SEPARATION_TOOLTIP :{BLACK}Automati STR_TIMETABLE_SCHEDULED_DISPATCH :{BLACK}Scheduled Dispatch STR_TIMETABLE_SCHEDULED_DISPATCH_TOOLTIP :{BLACK}Open scheduled dispatch windows for automatic setting of timetable start time -STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER :[{STRING1}scheduled dispatch] +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} diff --git a/src/lang/galician.txt b/src/lang/galician.txt index 84c29c60de..8e9ba49ecd 100644 --- a/src/lang/galician.txt +++ b/src/lang/galician.txt @@ -6681,7 +6681,7 @@ STR_TIMETABLE_AUTO_SEPARATION :{BLACK}Auto Sep STR_TIMETABLE_AUTO_SEPARATION_TOOLTIP :{BLACK}Automáticamente axusta a hora de comezo dos horarios para asegurar a separación dos vehículos STR_TIMETABLE_SCHEDULED_DISPATCH :{BLACK}Saída programada STR_TIMETABLE_SCHEDULED_DISPATCH_TOOLTIP :{BLACK}Abrir fiestras de saídas programadas para configurar automáticamente a hora de inicio dos horarios -STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER :[{STRING}saída programada] +STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER :{PUSH_COLOUR}{YELLOW}[{STRING}saída programada]{POP_COLOUR} STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME :{PUSH_COLOUR}{RED}[{STRING}saída programadda - sen tempo de espera no horario]{POP_COLOUR} STR_TIMETABLE_LOCK_ORDER_TIME_TOOLTIP :{BLACK}Bloquear/desbloquear a cantidade de tempo para a orde resaltada (Ctrl+Click fixa o estado de bloqueo para todas as ordes).{}Cando estea bloqueado, a hora non se modificará mediante o autocompletado ou automatización do horario. STR_TIMETABLE_LEAVE_EARLY_ORDER :[saír cedo] diff --git a/src/lang/german.txt b/src/lang/german.txt index f5da7f01bd..8df2660c23 100644 --- a/src/lang/german.txt +++ b/src/lang/german.txt @@ -5479,7 +5479,7 @@ STR_TIMETABLE_AUTO_SEPARATION_TOOLTIP :{BLACK}Passt au STR_TIMETABLE_SCHEDULED_DISPATCH :{BLACK}Geplante Beförderung STR_TIMETABLE_SCHEDULED_DISPATCH_TOOLTIP :{BLACK}Öffnet das Fenster für die geplante Beförderung, um den Beginn des Fahrplans automatisch zu setzen -STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER :[{STRING}Geplante Beförderung] +STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER :{PUSH_COLOUR}{YELLOW}[{STRING}Geplante Beförderung]{POP_COLOUR} STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME :{PUSH_COLOUR}{RED}[{STRING}Geplante Beförderung - keine Wartezeit geplant]{POP_COLOUR} STR_TIMETABLE_EXPECTED :{BLACK}Voraussichtlich diff --git a/src/lang/korean.txt b/src/lang/korean.txt index 8c3d3fdd88..2d58ceafd2 100644 --- a/src/lang/korean.txt +++ b/src/lang/korean.txt @@ -5697,7 +5697,7 @@ STR_TIMETABLE_AUTO_SEPARATION_TOOLTIP :{BLACK}시간 STR_TIMETABLE_SCHEDULED_DISPATCH :{BLACK}배차 일정 STR_TIMETABLE_SCHEDULED_DISPATCH_TOOLTIP :{BLACK}시간표 시작 시간 자동 설정을 위한 '배차 일정' 창을 엽니다. -STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER :[배{STRING}차 기준] +STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER :{PUSH_COLOUR}{YELLOW}[배{STRING}차 기준]{POP_COLOUR} STR_TIMETABLE_SCHEDULED_DISPATCH_ORDER_NO_WAIT_TIME :{PUSH_COLOUR}{RED}[{STRING}배차 기준 - 대기 시간표가 작성되지 않음]{POP_COLOUR} STR_TIMETABLE_EXPECTED :{BLACK}예정일 기준 From 21ef4902c6fdf33d31728aa356cbeea8e9543a86 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 14 Jan 2022 22:53:55 +0000 Subject: [PATCH 05/11] Show predicted scheduled dispatch arr/dep times in timetable window --- src/order_base.h | 21 +++++++++++++++++++++ src/schdispatch_cmd.cpp | 23 +++++++++++++++-------- src/timetable_cmd.cpp | 6 +++--- src/timetable_gui.cpp | 37 ++++++++++++++++++++++++++++++------- 4 files changed, 69 insertions(+), 18 deletions(-) diff --git a/src/order_base.h b/src/order_base.h index 20ddd2cc8d..5bc5aa4bd4 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -618,6 +618,15 @@ private: int32 scheduled_dispatch_last_dispatch = 0; ///< Last vehicle dispatched offset int32 scheduled_dispatch_max_delay = 0; ///< Maximum allowed delay + inline void CopyBasicFields(const DispatchSchedule &other) + { + this->scheduled_dispatch_duration = other.scheduled_dispatch_duration; + this->scheduled_dispatch_start_date = other.scheduled_dispatch_start_date; + this->scheduled_dispatch_start_full_date_fract = other.scheduled_dispatch_start_full_date_fract; + this->scheduled_dispatch_last_dispatch = other.scheduled_dispatch_last_dispatch; + this->scheduled_dispatch_max_delay = other.scheduled_dispatch_max_delay; + } + public: /** * Get the vector of all scheduled dispatch slot @@ -629,6 +638,7 @@ public: void AddScheduledDispatch(uint32 offset); void RemoveScheduledDispatch(uint32 offset); void ClearScheduledDispatch() { this->scheduled_dispatch.clear(); } + bool UpdateScheduledDispatchToDate(DateTicksScaled now); void UpdateScheduledDispatch(); /** @@ -701,6 +711,17 @@ public: * @return scheduled dispatch last dispatch */ inline int32 GetScheduledDispatchDelay() const { return this->scheduled_dispatch_max_delay; } + + inline void BorrowSchedule(DispatchSchedule &other) + { + this->CopyBasicFields(other); + this->scheduled_dispatch = std::move(other.scheduled_dispatch); + } + + inline void ReturnSchedule(DispatchSchedule &other) + { + other.scheduled_dispatch = std::move(this->scheduled_dispatch); + } }; /** diff --git a/src/schdispatch_cmd.cpp b/src/schdispatch_cmd.cpp index fc10427c6a..e58143c5d6 100644 --- a/src/schdispatch_cmd.cpp +++ b/src/schdispatch_cmd.cpp @@ -453,14 +453,11 @@ void DispatchSchedule::RemoveScheduledDispatch(uint32 offset) this->scheduled_dispatch.erase(erase_position); } -/** - * Update the scheduled dispatch start time to be the most recent possible. - */ -void DispatchSchedule::UpdateScheduledDispatch() +bool DispatchSchedule::UpdateScheduledDispatchToDate(DateTicksScaled now) { bool update_windows = false; if (this->GetScheduledDispatchStartTick() == 0) { - int64 start = _scaled_date_ticks - (_scaled_date_ticks % this->GetScheduledDispatchDuration()); + int64 start = now - (now % this->GetScheduledDispatchDuration()); SchdispatchConvertToFullDateFract( start, &this->scheduled_dispatch_start_date, &this->scheduled_dispatch_start_full_date_fract); @@ -474,7 +471,7 @@ void DispatchSchedule::UpdateScheduledDispatch() } } /* Most of the time this loop does not runs. It makes sure start date in in past */ - while (this->GetScheduledDispatchStartTick() > _scaled_date_ticks) { + while (this->GetScheduledDispatchStartTick() > now) { OverflowSafeInt32 last_dispatch = this->scheduled_dispatch_last_dispatch; last_dispatch += this->GetScheduledDispatchDuration(); this->scheduled_dispatch_last_dispatch = last_dispatch; @@ -484,7 +481,7 @@ void DispatchSchedule::UpdateScheduledDispatch() update_windows = true; } /* Most of the time this loop runs once. It makes sure the start date is as close to current time as possible. */ - while (this->GetScheduledDispatchStartTick() + this->GetScheduledDispatchDuration() <= _scaled_date_ticks) { + while (this->GetScheduledDispatchStartTick() + this->GetScheduledDispatchDuration() <= now) { OverflowSafeInt32 last_dispatch = this->scheduled_dispatch_last_dispatch; last_dispatch -= this->GetScheduledDispatchDuration(); this->scheduled_dispatch_last_dispatch = last_dispatch; @@ -493,5 +490,15 @@ void DispatchSchedule::UpdateScheduledDispatch() &this->scheduled_dispatch_start_date, &this->scheduled_dispatch_start_full_date_fract); update_windows = true; } - if (update_windows) InvalidateWindowClassesData(WC_SCHDISPATCH_SLOTS, VIWD_MODIFY_ORDERS); + return update_windows; +} + +/** + * Update the scheduled dispatch start time to be the most recent possible. + */ +void DispatchSchedule::UpdateScheduledDispatch() +{ + if (this->UpdateScheduledDispatchToDate(_scaled_date_ticks)) { + InvalidateWindowClassesData(WC_SCHDISPATCH_SLOTS, VIWD_MODIFY_ORDERS); + } } diff --git a/src/timetable_cmd.cpp b/src/timetable_cmd.cpp index 378dc98b9c..3cde0dec16 100644 --- a/src/timetable_cmd.cpp +++ b/src/timetable_cmd.cpp @@ -778,7 +778,7 @@ void UpdateSeparationOrder(Vehicle *v_start) } } -static DateTicksScaled GetScheduledDispatchTime(const DispatchSchedule &ds, int wait_offset) +DateTicksScaled GetScheduledDispatchTime(const DispatchSchedule &ds, DateTicksScaled leave_time) { DateTicksScaled first_slot = -1; const DateTicksScaled begin_time = ds.GetScheduledDispatchStartTick(); @@ -794,7 +794,7 @@ static DateTicksScaled GetScheduledDispatchTime(const DispatchSchedule &ds, int } DateTicksScaled current_departure = begin_time + current_offset; - DateTicksScaled minimum = _scaled_date_ticks + wait_offset - max_delay; + DateTicksScaled minimum = leave_time - max_delay; if (current_departure < minimum) { current_departure += dispatch_duration * ((minimum + dispatch_duration - current_departure - 1) / dispatch_duration); } @@ -850,7 +850,7 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling) ds.UpdateScheduledDispatch(); const int wait_offset = real_current_order->GetTimetabledWait(); - DateTicksScaled slot = GetScheduledDispatchTime(ds, wait_offset); + DateTicksScaled slot = GetScheduledDispatchTime(ds, _scaled_date_ticks + wait_offset); if (slot > -1) { SetBit(v->vehicle_flags, VF_TIMETABLE_STARTED); v->lateness_counter = _scaled_date_ticks - slot + wait_offset; diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp index c7de060a91..b05c4fe253 100644 --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -33,10 +33,16 @@ #include "safeguards.h" +enum TimetableArrivalDepartureFlags { + TADF_ARRIVAL_PREDICTED, + TADF_DEPARTURE_PREDICTED, +}; + /** Container for the arrival/departure dates of a vehicle */ struct TimetableArrivalDeparture { Ticks arrival; ///< The arrival time Ticks departure; ///< The departure time + uint flags; }; /** @@ -121,8 +127,11 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID /* Pre-initialize with unknown time */ for (int i = 0; i < v->GetNumOrders(); ++i) { table[i].arrival = table[i].departure = INVALID_TICKS; + table[i].flags = 0; } + bool predicted = false; + /* Cyclically loop over all orders until we reach the current one again. * As we may start at the current order, do a post-checking loop */ do { @@ -134,12 +143,26 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID if (!CanDetermineTimeTaken(order, true)) return; sum += order->GetTimetabledTravel(); table[i].arrival = sum; + if (predicted) SetBit(table[i].flags, TADF_ARRIVAL_PREDICTED); } - if (order->IsScheduledDispatchOrder(true) && !(i == start && !travelling)) return; - if (!CanDetermineTimeTaken(order, false)) return; - sum += order->GetTimetabledWait(); + if (order->IsScheduledDispatchOrder(true) && !(i == start && !travelling)) { + extern DateTicksScaled GetScheduledDispatchTime(const DispatchSchedule &ds, DateTicksScaled leave_time); + DispatchSchedule &ds = v->orders.list->GetDispatchScheduleByIndex(order->GetDispatchScheduleIndex()); + DispatchSchedule predicted_ds; + predicted_ds.BorrowSchedule(ds); + predicted_ds.UpdateScheduledDispatchToDate(_scaled_date_ticks + sum); + DateTicksScaled slot = GetScheduledDispatchTime(predicted_ds, _scaled_date_ticks + sum + order->GetTimetabledWait()); + predicted_ds.ReturnSchedule(ds); + if (slot <= -1) return; + sum = slot - _scaled_date_ticks; + predicted = true; + } else { + if (!CanDetermineTimeTaken(order, false)) return; + sum += order->GetTimetabledWait(); + } table[i].departure = sum; + if (predicted) SetBit(table[i].flags, TADF_DEPARTURE_PREDICTED); } ++i; @@ -694,17 +717,17 @@ struct TimetableWindow : Window { SetDParam(0, _scaled_date_ticks + arr_dep[i / 2].arrival); DrawString(time_left, time_right, y, STR_JUST_DATE_WALLCLOCK_TINY, TC_GREEN); } else { - SetDParam(0, _scaled_date_ticks + arr_dep[i / 2].arrival + offset); + SetDParam(0, _scaled_date_ticks + arr_dep[i / 2].arrival + (HasBit(arr_dep[i / 2].flags, TADF_ARRIVAL_PREDICTED) ? 0 : offset)); DrawString(time_left, time_right, y, STR_JUST_DATE_WALLCLOCK_TINY, - show_late ? TC_RED : i == selected ? TC_WHITE : TC_BLACK); + HasBit(arr_dep[i / 2].flags, TADF_ARRIVAL_PREDICTED) ? (TextColour)(TC_IS_PALETTE_COLOUR | TC_NO_SHADE | 4) : (show_late ? TC_RED : i == selected ? TC_WHITE : TC_BLACK)); } } } else { if (arr_dep[i / 2].departure != INVALID_TICKS) { DrawString(abbr_left, abbr_right, y, STR_TIMETABLE_DEPARTURE_ABBREVIATION, i == selected ? TC_WHITE : TC_BLACK); - SetDParam(0, _scaled_date_ticks + arr_dep[i/2].departure + offset); + SetDParam(0, _scaled_date_ticks + arr_dep[i/2].departure + (HasBit(arr_dep[i / 2].flags, TADF_DEPARTURE_PREDICTED) ? 0 : offset)); DrawString(time_left, time_right, y, STR_JUST_DATE_WALLCLOCK_TINY, - show_late ? TC_RED : i == selected ? TC_WHITE : TC_BLACK); + HasBit(arr_dep[i / 2].flags, TADF_DEPARTURE_PREDICTED) ? (TextColour)(TC_IS_PALETTE_COLOUR | TC_NO_SHADE | 4) : (show_late ? TC_RED : i == selected ? TC_WHITE : TC_BLACK)); } } y += line_height; From fe522f193b5260c32d5ef181a403377230aa4dee Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Fri, 14 Jan 2022 23:24:41 +0000 Subject: [PATCH 06/11] Show confirmation window before clearing/removing dispatch schedule --- src/lang/english.txt | 6 ++++++ src/schdispatch_gui.cpp | 28 ++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index a2e3669dfa..4056c69c22 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -7153,6 +7153,12 @@ 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_SCHDISPATCH_QUERY_CLEAR_SCHEDULE_CAPTION :{WHITE}Clear Departure Slots +STR_SCHDISPATCH_QUERY_CLEAR_SCHEDULE_TEXT :{WHITE}Are you sure you want to clear all {NUM} departure slot{P "" s} from this schedule? + +STR_SCHDISPATCH_QUERY_REMOVE_SCHEDULE_CAPTION :{WHITE}Delete Schedule +STR_SCHDISPATCH_QUERY_REMOVE_SCHEDULE_TEXT :{WHITE}Are you sure you want to delete this schedule (containing {NUM} departure slot{P "" s})? + STR_ERROR_SCHDISPATCH_TRIED_TO_ADD_TOO_MANY_SLOTS :{WHITE}Tried to add too many departure slots at once # Modifier key toggle window diff --git a/src/schdispatch_gui.cpp b/src/schdispatch_gui.cpp index 74575c6352..b43cd2af10 100644 --- a/src/schdispatch_gui.cpp +++ b/src/schdispatch_gui.cpp @@ -729,6 +729,26 @@ struct SchdispatchWindow : Window { this->SetDirty(); } + static void ClearScheduleCallback(Window *win, bool confirmed) + { + if (confirmed) { + SchdispatchWindow *w = (SchdispatchWindow*)win; + if (w->IsScheduleSelected()) { + DoCommandP(0, w->vehicle->index | (w->schedule_index << 20), 0, CMD_SCHEDULED_DISPATCH_CLEAR | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); + } + } + } + + static void RemoveScheduleCallback(Window *win, bool confirmed) + { + if (confirmed) { + SchdispatchWindow *w = (SchdispatchWindow*)win; + if (w->IsScheduleSelected()) { + DoCommandP(0, w->vehicle->index | (w->schedule_index << 20), 0, CMD_SCHEDULED_DISPATCH_REMOVE_SCHEDULE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); + } + } + } + void OnDropdownSelect(int widget, int index) override { switch (widget) { @@ -740,11 +760,15 @@ struct SchdispatchWindow : Window { break; case SCH_MD_CLEAR_SCHEDULE: - DoCommandP(0, this->vehicle->index | (this->schedule_index << 20), 0, CMD_SCHEDULED_DISPATCH_CLEAR | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); + if (this->GetSelectedSchedule().GetScheduledDispatch().empty()) return; + SetDParam(0, (uint)this->GetSelectedSchedule().GetScheduledDispatch().size()); + ShowQuery(STR_SCHDISPATCH_QUERY_CLEAR_SCHEDULE_CAPTION, STR_SCHDISPATCH_QUERY_CLEAR_SCHEDULE_TEXT, this, ClearScheduleCallback); + break; case SCH_MD_REMOVE_SCHEDULE: - DoCommandP(0, this->vehicle->index | (this->schedule_index << 20), 0, CMD_SCHEDULED_DISPATCH_REMOVE_SCHEDULE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); + SetDParam(0, (uint)this->GetSelectedSchedule().GetScheduledDispatch().size()); + ShowQuery(STR_SCHDISPATCH_QUERY_REMOVE_SCHEDULE_CAPTION, STR_SCHDISPATCH_QUERY_REMOVE_SCHEDULE_TEXT, this, RemoveScheduleCallback); break; } } From 543433218ca93b871f7463fd3a9a1c7471c9e91d Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 15 Jan 2022 00:21:31 +0000 Subject: [PATCH 07/11] Ensure that timetable and scheduled dispatch windows redrawn as required Reduce inefficient window and shared order list iteration --- src/order_base.h | 2 +- src/order_cmd.cpp | 1 + src/schdispatch.h | 1 + src/schdispatch_cmd.cpp | 39 ++++++++++++++++++--------------------- src/schdispatch_gui.cpp | 19 +++++++++++++++---- src/timetable.h | 1 + src/timetable_cmd.cpp | 26 +++++++++++--------------- src/timetable_gui.cpp | 16 ++++++++++++---- src/train_cmd.cpp | 1 + src/window_gui.h | 6 ++++++ 10 files changed, 67 insertions(+), 45 deletions(-) diff --git a/src/order_base.h b/src/order_base.h index 5bc5aa4bd4..0b13c3e240 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -639,7 +639,7 @@ public: void RemoveScheduledDispatch(uint32 offset); void ClearScheduledDispatch() { this->scheduled_dispatch.clear(); } bool UpdateScheduledDispatchToDate(DateTicksScaled now); - void UpdateScheduledDispatch(); + void UpdateScheduledDispatch(const Vehicle *v); /** * Set the scheduled dispatch duration, in scaled tick diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 2f69db1d92..ea864421d9 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -338,6 +338,7 @@ Order::Order(uint64 packed) void InvalidateVehicleOrder(const Vehicle *v, int data) { SetWindowDirty(WC_VEHICLE_VIEW, v->index); + SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index); if (data != 0) { /* Calls SetDirty() too */ diff --git a/src/schdispatch.h b/src/schdispatch.h index a8315675b5..dd35e2c2d7 100644 --- a/src/schdispatch.h +++ b/src/schdispatch.h @@ -15,6 +15,7 @@ #include "settings_type.h" void ShowSchdispatchWindow(const Vehicle *v); +void SchdispatchInvalidateWindows(const Vehicle *v); /** * Convert date and full date fraction to DateTicksScaled diff --git a/src/schdispatch_cmd.cpp b/src/schdispatch_cmd.cpp index e58143c5d6..1d3c9c41ac 100644 --- a/src/schdispatch_cmd.cpp +++ b/src/schdispatch_cmd.cpp @@ -59,9 +59,8 @@ CommandCost CmdScheduledDispatch(TileIndex tile, DoCommandFlag flags, uint32 p1, } else { ClrBit(v2->vehicle_flags, VF_SCHEDULED_DISPATCH); } - SetWindowDirty(WC_VEHICLE_TIMETABLE, v2->index); - SetWindowDirty(WC_SCHDISPATCH_SLOTS, v2->index); } + SetTimetableWindowsDirty(v, true); } return CommandCost(); @@ -106,7 +105,7 @@ CommandCost CmdScheduledDispatchAdd(TileIndex tile, DoCommandFlag flags, uint32 p2 += offset; ds.AddScheduledDispatch(p2); } - SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index); + SetTimetableWindowsDirty(v, true); } return CommandCost(); @@ -138,7 +137,7 @@ CommandCost CmdScheduledDispatchRemove(TileIndex tile, DoCommandFlag flags, uint if (flags & DC_EXEC) { v->orders.list->GetDispatchScheduleByIndex(schedule_index).RemoveScheduledDispatch(p2); - SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index); + SetTimetableWindowsDirty(v, true); } return CommandCost(); @@ -172,8 +171,8 @@ CommandCost CmdScheduledDispatchSetDuration(TileIndex tile, DoCommandFlag flags, if (flags & DC_EXEC) { DispatchSchedule &ds = v->orders.list->GetDispatchScheduleByIndex(schedule_index); ds.SetScheduledDispatchDuration(p2); - ds.UpdateScheduledDispatch(); - SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index); + ds.UpdateScheduledDispatch(nullptr); + SetTimetableWindowsDirty(v, true); } return CommandCost(); @@ -217,8 +216,8 @@ CommandCost CmdScheduledDispatchSetStartDate(TileIndex tile, DoCommandFlag flags if (flags & DC_EXEC) { DispatchSchedule &ds = v->orders.list->GetDispatchScheduleByIndex(schedule_index); ds.SetScheduledDispatchStartDate(date, full_date_fract); - ds.UpdateScheduledDispatch(); - SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index); + ds.UpdateScheduledDispatch(nullptr); + SetTimetableWindowsDirty(v, true); } return CommandCost(); @@ -251,7 +250,7 @@ CommandCost CmdScheduledDispatchSetDelay(TileIndex tile, DoCommandFlag flags, ui if (flags & DC_EXEC) { v->orders.list->GetDispatchScheduleByIndex(schedule_index).SetScheduledDispatchDelay(p2); - SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index); + SetTimetableWindowsDirty(v, true); } return CommandCost(); @@ -289,7 +288,7 @@ CommandCost CmdScheduledDispatchResetLastDispatch(TileIndex tile, DoCommandFlag if (flags & DC_EXEC) { v->orders.list->GetDispatchScheduleByIndex(schedule_index).SetScheduledDispatchLastDispatch(0); - SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index); + SetTimetableWindowsDirty(v, true); } return CommandCost(); @@ -322,7 +321,7 @@ CommandCost CmdScheduledDispatchClear(TileIndex tile, DoCommandFlag flags, uint3 if (flags & DC_EXEC) { v->orders.list->GetDispatchScheduleByIndex(schedule_index).ClearScheduledDispatch(); - SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index); + SetTimetableWindowsDirty(v, true); } return CommandCost(); @@ -362,9 +361,8 @@ CommandCost CmdScheduledDispatchAddNewSchedule(TileIndex tile, DoCommandFlag fla DispatchSchedule &ds = v->orders.list->GetScheduledDispatchScheduleSet().back(); ds.SetScheduledDispatchDuration(p2); ds.SetScheduledDispatchStartDate(date, full_date_fract); - ds.UpdateScheduledDispatch(); - SetWindowClassesDirty(WC_VEHICLE_TIMETABLE); - SetWindowDirty(WC_SCHDISPATCH_SLOTS, v->index); + ds.UpdateScheduledDispatch(nullptr); + SetTimetableWindowsDirty(v, true); } return CommandCost(); @@ -406,8 +404,7 @@ CommandCost CmdScheduledDispatchRemoveSchedule(TileIndex tile, DoCommandFlag fla o->SetDispatchScheduleIndex(idx - 1); } } - SetWindowClassesDirty(WC_VEHICLE_TIMETABLE); - InvalidateWindowClassesData(WC_SCHDISPATCH_SLOTS, VIWD_MODIFY_ORDERS); + SchdispatchInvalidateWindows(v); } return CommandCost(); @@ -421,7 +418,7 @@ void DispatchSchedule::SetScheduledDispatch(std::vector dispatch_list) { this->scheduled_dispatch = std::move(dispatch_list); assert(std::is_sorted(this->scheduled_dispatch.begin(), this->scheduled_dispatch.end())); - if (this->IsScheduledDispatchValid()) this->UpdateScheduledDispatch(); + if (this->IsScheduledDispatchValid()) this->UpdateScheduledDispatch(nullptr); } /** @@ -436,7 +433,7 @@ void DispatchSchedule::AddScheduledDispatch(uint32 offset) return; } this->scheduled_dispatch.insert(insert_position, offset); - this->UpdateScheduledDispatch(); + this->UpdateScheduledDispatch(nullptr); } /** @@ -496,9 +493,9 @@ bool DispatchSchedule::UpdateScheduledDispatchToDate(DateTicksScaled now) /** * Update the scheduled dispatch start time to be the most recent possible. */ -void DispatchSchedule::UpdateScheduledDispatch() +void DispatchSchedule::UpdateScheduledDispatch(const Vehicle *v) { - if (this->UpdateScheduledDispatchToDate(_scaled_date_ticks)) { - InvalidateWindowClassesData(WC_SCHDISPATCH_SLOTS, VIWD_MODIFY_ORDERS); + if (this->UpdateScheduledDispatchToDate(_scaled_date_ticks) && v != nullptr) { + SetTimetableWindowsDirty(v, true); } } diff --git a/src/schdispatch_gui.cpp b/src/schdispatch_gui.cpp index b43cd2af10..17436a1b7a 100644 --- a/src/schdispatch_gui.cpp +++ b/src/schdispatch_gui.cpp @@ -184,8 +184,7 @@ static void AddNewScheduledDispatchSchedule(VehicleID vindex) DoCommandPEx(0, vindex, duration, p3, CMD_SCHEDULED_DISPATCH_ADD_NEW_SCHEDULE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE), CcAddNewSchDispatchSchedule, nullptr, 0); } -struct SchdispatchWindow : Window { - const Vehicle *vehicle; ///< Vehicle monitored by the window. +struct SchdispatchWindow : GeneralVehicleWindow { int schedule_index; int clicked_widget; ///< The widget that was clicked (used to determine what to do in OnQueryTextFinished) Scrollbar *vscroll; ///< Verticle scrollbar @@ -197,8 +196,7 @@ struct SchdispatchWindow : Window { bool no_order_warning_pad = false; SchdispatchWindow(WindowDesc *desc, WindowNumber window_number) : - Window(desc), - vehicle(Vehicle::Get(window_number)) + GeneralVehicleWindow(desc, Vehicle::Get(window_number)) { this->CreateNestedTree(); this->vscroll = this->GetScrollbar(WID_SCHDISPATCH_V_SCROLL); @@ -1151,3 +1149,16 @@ void ShowScheduledDispatchAddSlotsWindow(SchdispatchWindow *parent, int window_n new ScheduledDispatchAddSlotsWindow(&_scheduled_dispatch_add_desc, window_number, parent); } + +void SchdispatchInvalidateWindows(const Vehicle *v) +{ + v = v->FirstShared(); + for (Window *w : Window::IterateFromBack()) { + if (w->window_class == WC_VEHICLE_TIMETABLE) { + if (static_cast(w)->vehicle->FirstShared() == v) w->SetDirty(); + } + if (w->window_class == WC_SCHDISPATCH_SLOTS) { + if (static_cast(w)->vehicle->FirstShared() == v) w->InvalidateData(VIWD_MODIFY_ORDERS, false); + } + } +} diff --git a/src/timetable.h b/src/timetable.h index 891b2573fd..8ed9f7132b 100644 --- a/src/timetable.h +++ b/src/timetable.h @@ -18,6 +18,7 @@ void ShowTimetableWindow(const Vehicle *v); void UpdateVehicleTimetable(Vehicle *v, bool travelling); void SetTimetableParams(int first_param, Ticks ticks, bool long_mode = false); +void SetTimetableWindowsDirty(const Vehicle *v, bool include_scheduled_dispatch = false); struct TimetableProgress { VehicleID id; diff --git a/src/timetable_cmd.cpp b/src/timetable_cmd.cpp index 3cde0dec16..6eb39b2f5a 100644 --- a/src/timetable_cmd.cpp +++ b/src/timetable_cmd.cpp @@ -102,6 +102,8 @@ static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint32 val, v->orders.list->UpdateTotalDuration(total_delta); v->orders.list->UpdateTimetableDuration(timetable_delta); + SetTimetableWindowsDirty(v, mtf == MTF_ASSIGN_SCHEDULE); + for (v = v->FirstShared(); v != nullptr; v = v->NextShared()) { if (v->cur_real_order_index == order_number && v->current_order.Equals(*order)) { switch (mtf) { @@ -139,7 +141,6 @@ static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint32 val, NOT_REACHED(); } } - SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index); } } @@ -469,8 +470,10 @@ CommandCost CmdSetTimetableStart(TileIndex tile, DoCommandFlag flags, uint32 p1, for (Vehicle *w = v->orders.list->GetFirstSharedVehicle(); w != nullptr; w = w->NextShared()) { vehs.push_back(w); } + SetTimetableWindowsDirty(v); } else { vehs.push_back(v); + SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index); } int total_duration = v->orders.list->GetTimetableTotalDuration(); @@ -483,7 +486,6 @@ CommandCost CmdSetTimetableStart(TileIndex tile, DoCommandFlag flags, uint32 p1, int idx = vehs.begin() - std::find(vehs.begin(), vehs.end(), v); for (Vehicle *w : vehs) { - w->lateness_counter = 0; ClrBit(w->vehicle_flags, VF_TIMETABLE_STARTED); /* Do multiplication, then division to reduce rounding errors. */ @@ -493,7 +495,6 @@ CommandCost CmdSetTimetableStart(TileIndex tile, DoCommandFlag flags, uint32 p1, } w->timetable_start = tt_start / _settings_game.economy.day_length_factor; w->timetable_start_subticks = tt_start % _settings_game.economy.day_length_factor; - SetWindowDirty(WC_VEHICLE_TIMETABLE, w->index); ++idx; } @@ -551,8 +552,8 @@ CommandCost CmdAutofillTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, ClrBit(v2->vehicle_flags, VF_AUTOFILL_TIMETABLE); ClrBit(v2->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME); } - SetWindowDirty(WC_VEHICLE_TIMETABLE, v2->index); } + SetTimetableWindowsDirty(v); } return CommandCost(); @@ -608,8 +609,8 @@ CommandCost CmdAutomateTimetable(TileIndex index, DoCommandFlag flags, uint32 p1 v2->current_loading_time = 0; } } - SetWindowDirty(WC_VEHICLE_TIMETABLE, v2->index); } + SetTimetableWindowsDirty(v); if (!HasBit(p2, 0) && !HasBit(p2, 1)) { OrderList *orders = v->orders.list; if (orders != nullptr) { @@ -652,9 +653,8 @@ CommandCost CmdTimetableSeparation(TileIndex tile, DoCommandFlag flags, uint32 p ClrBit(v2->vehicle_flags, VF_TIMETABLE_SEPARATION); } v2->ClearSeparation(); - SetWindowDirty(WC_VEHICLE_TIMETABLE, v2->index); - SetWindowDirty(WC_SCHDISPATCH_SLOTS, v2->index); } + SetTimetableWindowsDirty(v, true); } return CommandCost(); @@ -847,7 +847,7 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling) DispatchSchedule &ds = v->orders.list->GetDispatchScheduleByIndex(real_implicit_order->GetDispatchScheduleIndex()); /* Update scheduled information */ - ds.UpdateScheduledDispatch(); + ds.UpdateScheduledDispatch(v); const int wait_offset = real_current_order->GetTimetabledWait(); DateTicksScaled slot = GetScheduledDispatchTime(ds, _scaled_date_ticks + wait_offset); @@ -867,9 +867,7 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling) /* If the lateness is set by scheduled dispatch above, do not reset */ if (!HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)) v->lateness_counter = 0; if (HasBit(v->vehicle_flags, VF_TIMETABLE_SEPARATION)) UpdateSeparationOrder(v); - for (v = v->FirstShared(); v != nullptr; v = v->NextShared()) { - SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index); - } + SetTimetableWindowsDirty(v); return; } @@ -969,8 +967,8 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling) /* Clear VF_TIMETABLE_STARTED but do not call ClearSeparation */ ClrBit(v2->vehicle_flags, VF_TIMETABLE_STARTED); v2->lateness_counter = 0; - SetWindowDirty(WC_VEHICLE_TIMETABLE, v2->index); } + SetTimetableWindowsDirty(v); return; } else if (new_time >= (int32)timetabled / 2) { /* Compute running average, with sign conversion to avoid negative overflow. @@ -1037,9 +1035,7 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling) } } - for (v = v->FirstShared(); v != nullptr; v = v->NextShared()) { - SetWindowDirty(WC_VEHICLE_TIMETABLE, v->index); - } + SetTimetableWindowsDirty(v); } void SetOrderFixedWaitTime(Vehicle *v, VehicleOrderID order_number, uint32 wait_time, bool wait_timetabled) { diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp index b05c4fe253..b417b70049 100644 --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -289,9 +289,8 @@ void ProcessTimetableWarnings(const Vehicle *v, std::functionCreateNestedTree(); @@ -1197,3 +1195,13 @@ void ShowTimetableWindow(const Vehicle *v) DeleteWindowById(WC_VEHICLE_ORDERS, v->index, false); AllocateWindowDescFront(&_timetable_desc, v->index); } + +void SetTimetableWindowsDirty(const Vehicle *v, bool include_scheduled_dispatch) +{ + v = v->FirstShared(); + for (Window *w : Window::IterateFromBack()) { + if (w->window_class == WC_VEHICLE_TIMETABLE || (include_scheduled_dispatch && w->window_class == WC_SCHDISPATCH_SLOTS)) { + if (static_cast(w)->vehicle->FirstShared() == v) w->SetDirty(); + } + } +} diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 0cf1b41b9f..55317bf2b7 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -2139,6 +2139,7 @@ CommandCost CmdMoveRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, u DeleteWindowById(WC_VEHICLE_REFIT, src->index); DeleteWindowById(WC_VEHICLE_DETAILS, src->index); DeleteWindowById(WC_VEHICLE_TIMETABLE, src->index); + DeleteWindowById(WC_SCHDISPATCH_SLOTS, src->index); DeleteNewGRFInspectWindow(GSF_TRAINS, src->index); SetWindowDirty(WC_COMPANY, _current_company); diff --git a/src/window_gui.h b/src/window_gui.h index 8b55f06e60..990938d42f 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -1102,4 +1102,10 @@ inline bool MayBeShown(const Window *w) } } +struct GeneralVehicleWindow : public Window { + const Vehicle *vehicle; + + GeneralVehicleWindow(WindowDesc *desc, const Vehicle *v) : Window(desc), vehicle(v) {} +}; + #endif /* WINDOW_GUI_H */ From 1ac632630661707c21f6ec28ac3a90a3ea7d9da3 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 15 Jan 2022 12:25:09 +0000 Subject: [PATCH 08/11] Departure boards: Fix departure slot cache with multiple schedules --- src/departures.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/departures.cpp b/src/departures.cpp index d3af2e2002..2a515b01b1 100644 --- a/src/departures.cpp +++ b/src/departures.cpp @@ -33,6 +33,7 @@ #include "cargo_type.h" #include "departures_func.h" #include "departures_type.h" +#include "3rdparty/cpp-btree/btree_set.h" #include #include @@ -40,7 +41,7 @@ #include /* A cache of used departure time for scheduled dispatch in departure time calculation */ -typedef std::map> schdispatch_cache_t; +typedef std::map> schdispatch_cache_t; /** A scheduled order. */ typedef struct OrderDate @@ -112,6 +113,8 @@ static inline bool VehicleSetNextDepartureTime(DateTicks *previous_departure, ui /* -1 because this number is actually a moment before actual departure */ } + btree::btree_set &slot_cache = dept_schedule_last[&ds]; + /* Find next available slots */ for (auto current_offset : ds.GetScheduledDispatch()) { if (current_offset >= dispatch_duration) continue; @@ -121,7 +124,7 @@ static inline bool VehicleSetNextDepartureTime(DateTicks *previous_departure, ui } /* Make sure the slots has not already been used previously in this departure board calculation */ - while (dept_schedule_last[v->orders.list->index].count(current_departure) > 0) { + while (slot_cache.count(current_departure) > 0) { current_departure += dispatch_duration; } @@ -132,7 +135,7 @@ static inline bool VehicleSetNextDepartureTime(DateTicks *previous_departure, ui *waiting_time = order->GetWaitTime() + actual_departure - date_only_scaled - *previous_departure - order->GetTravelTime(); *previous_departure = actual_departure - date_only_scaled + order->GetWaitTime(); - dept_schedule_last[v->orders.list->index].insert(actual_departure); + slot_cache.insert(actual_departure); /* Return true means that vehicle lateness should be clear from this point onward */ return true; From 91ed26415e619303f9e8e1d21e30175906ef7b46 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 15 Jan 2022 15:33:34 +0000 Subject: [PATCH 09/11] Fix manual setting of conditional order jump taken travel times --- src/order_base.h | 2 +- src/order_gui.cpp | 4 ++-- src/timetable_cmd.cpp | 2 +- src/timetable_gui.cpp | 13 ++++++++++--- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/order_base.h b/src/order_base.h index 0b13c3e240..1a6ff77a88 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -477,7 +477,7 @@ public: /** Set if the wait time is fixed */ inline void SetWaitFixed(bool fixed) { - if (!this->IsType(OT_CONDITIONAL) && fixed != this->IsWaitFixed()) SB(this->GetXFlagsRef(), 1, 1, fixed ? 1 : 0); + if (fixed != this->IsWaitFixed()) SB(this->GetXFlagsRef(), 1, 1, fixed ? 1 : 0); } /** Does this order have a fixed travel time? */ diff --git a/src/order_gui.cpp b/src/order_gui.cpp index 53dd47da1e..117582273a 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -1029,7 +1029,7 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int } } - if (timetable && order->GetWaitTime() > 0) { + if (timetable && (order->IsWaitTimetabled() || order->GetWaitTime() > 0)) { SetDParam(7, order->IsWaitTimetabled() ? STR_TIMETABLE_AND_TRAVEL_FOR : STR_TIMETABLE_AND_TRAVEL_FOR_ESTIMATED); SetTimetableParams(8, order->GetWaitTime()); } else { @@ -1064,7 +1064,7 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int edge = DrawString(rtl ? left : edge + 3, rtl ? edge - 3 : right, y, str, colour); } - if (timetable && timetable_wait_time_valid && order->IsWaitFixed() && edge != 0) { + if (timetable && (timetable_wait_time_valid || order->IsType(OT_CONDITIONAL)) && order->IsWaitFixed() && edge != 0) { Dimension lock_d = GetSpriteSize(SPR_LOCK); DrawPixelInfo tmp_dpi; if (FillDrawPixelInfo(&tmp_dpi, rtl ? left : middle, y, rtl ? middle - left : right - middle, lock_d.height)) { diff --git a/src/timetable_cmd.cpp b/src/timetable_cmd.cpp index 6eb39b2f5a..b2910d87de 100644 --- a/src/timetable_cmd.cpp +++ b/src/timetable_cmd.cpp @@ -264,8 +264,8 @@ CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, u } if (travel_time != order->GetTravelTime() && order->IsType(OT_CONDITIONAL)) return CMD_ERROR; + if (travel_fixed != order->IsTravelFixed() && order->IsType(OT_CONDITIONAL)) return CMD_ERROR; if (max_speed != order->GetMaxSpeed() && (order->IsType(OT_CONDITIONAL) || v->type == VEH_AIRCRAFT)) return CMD_ERROR; - if (wait_fixed != order->IsWaitFixed() && order->IsType(OT_CONDITIONAL)) return CMD_ERROR; if (leave_type != order->GetLeaveType() && order->IsType(OT_CONDITIONAL)) return CMD_ERROR; if (flags & DC_EXEC) { diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp index b417b70049..89bed2905c 100644 --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -477,6 +477,7 @@ struct TimetableWindow : GeneralVehicleWindow { if (v->owner == _local_company) { bool disable = true; + bool disable_time = true; bool wait_lockable = false; bool wait_locked = false; bool clearable_when_wait_locked = false; @@ -485,6 +486,7 @@ struct TimetableWindow : GeneralVehicleWindow { if (selected % 2 == 1) { /* Travel time */ disable = order != nullptr && (order->IsType(OT_CONDITIONAL) || order->IsType(OT_IMPLICIT)); + disable_time = disable; wait_lockable = !disable; wait_locked = wait_lockable && order->IsTravelFixed(); } else { @@ -492,24 +494,29 @@ struct TimetableWindow : GeneralVehicleWindow { if (order != nullptr) { if (order->IsType(OT_GOTO_WAYPOINT)) { disable = false; + disable_time = false; clearable_when_wait_locked = true; } else if (order->IsType(OT_CONDITIONAL)) { disable = true; + disable_time = false; + clearable_when_wait_locked = true; } else { disable = (!(order->IsType(OT_GOTO_STATION) || (order->IsType(OT_GOTO_DEPOT) && !(order->GetDepotActionType() & ODATFB_HALT))) || (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION)); + disable_time = disable; } } else { disable = true; + disable_time = true; } - wait_lockable = !disable; + wait_lockable = !disable_time; wait_locked = wait_lockable && order->IsWaitFixed(); } } bool disable_speed = disable || selected % 2 != 1 || v->type == VEH_AIRCRAFT; - this->SetWidgetDisabledState(WID_VT_CHANGE_TIME, disable || (HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE) && !wait_locked)); - this->SetWidgetDisabledState(WID_VT_CLEAR_TIME, disable || (HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE) && !(wait_locked && clearable_when_wait_locked))); + this->SetWidgetDisabledState(WID_VT_CHANGE_TIME, disable_time || (HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE) && !wait_locked)); + this->SetWidgetDisabledState(WID_VT_CLEAR_TIME, disable_time || (HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE) && !(wait_locked && clearable_when_wait_locked))); this->SetWidgetDisabledState(WID_VT_CHANGE_SPEED, disable_speed); this->SetWidgetDisabledState(WID_VT_CLEAR_SPEED, disable_speed); this->SetWidgetDisabledState(WID_VT_SHARED_ORDER_LIST, !(v->IsOrderListShared() || _settings_client.gui.enable_single_veh_shared_order_gui)); From b23d394f0b5164abdf8607c4fdd30e3eca498a7b Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 15 Jan 2022 15:57:09 +0000 Subject: [PATCH 10/11] Handle predictable conditional orders in timetable window --- src/order_cmd.cpp | 2 +- src/order_func.h | 1 + src/timetable_gui.cpp | 65 +++++++++++++++++++++++++++++++++++++++---- src/tracerestrict.cpp | 31 +++++++++++++++++++++ src/tracerestrict.h | 1 + 5 files changed, 93 insertions(+), 7 deletions(-) diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index ea864421d9..c64b0bf4be 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -2700,7 +2700,7 @@ static bool CheckForValidOrders(const Vehicle *v) /** * Compare the variable and value based on the given comparator. */ -static bool OrderConditionCompare(OrderConditionComparator occ, int variable, int value) +bool OrderConditionCompare(OrderConditionComparator occ, int variable, int value) { switch (occ) { case OCC_EQUALS: return variable == value; diff --git a/src/order_func.h b/src/order_func.h index 88c3a3a2dc..da99626cdd 100644 --- a/src/order_func.h +++ b/src/order_func.h @@ -32,5 +32,6 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int #define MAX_SERVINT_DAYS 800 uint16 GetServiceIntervalClamped(uint interval, bool ispercent); +bool OrderConditionCompare(OrderConditionComparator occ, int variable, int value); #endif /* ORDER_FUNC_H */ diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp index 89bed2905c..554956c530 100644 --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -25,6 +25,7 @@ #include "viewport_func.h" #include "schdispatch.h" #include "vehiclelist.h" +#include "tracerestrict.h" #include "widgets/timetable_widget.h" @@ -36,6 +37,9 @@ enum TimetableArrivalDepartureFlags { TADF_ARRIVAL_PREDICTED, TADF_DEPARTURE_PREDICTED, + TADF_ARRIVAL_NO_OFFSET, + TADF_DEPARTURE_NO_OFFSET, + TADF_REACHED, }; /** Container for the arrival/departure dates of a vehicle */ @@ -131,22 +135,65 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID } bool predicted = false; + bool no_offset = false; + bool skip_travel = false; /* Cyclically loop over all orders until we reach the current one again. * As we may start at the current order, do a post-checking loop */ do { + if (HasBit(table[i].flags, TADF_REACHED)) break; + SetBit(table[i].flags, TADF_REACHED); + + bool skip = order->IsType(OT_IMPLICIT); + + if (order->IsType(OT_CONDITIONAL)) { + bool jump = false; + switch (order->GetConditionVariable()) { + case OCV_UNCONDITIONALLY: { + jump = true; + break; + } + + case OCV_TIME_DATE: { + predicted = true; + DateTicksScaled time = _scaled_date_ticks + sum; + if (!no_offset) time -= v->lateness_counter; + int value = GetTraceRestrictTimeDateValueFromDate(static_cast(order->GetConditionValue()), time); + jump = OrderConditionCompare(order->GetConditionComparator(), value, order->GetXData()); + break; + } + + default: + return; + } + if (jump) { + if (!order->IsWaitTimetabled()) return; + sum += order->GetTimetabledWait(); + i = order->GetConditionSkipToOrder(); + order = v->GetOrder(i); + skip_travel = true; + continue; + } else { + skip = true; + } + } + /* Automatic orders don't influence the overall timetable; * they just add some untimetabled entries, but the time till * the next non-implicit order can still be known. */ - if (!order->IsType(OT_IMPLICIT)) { + if (!skip) { if (travelling || i != start) { - if (!CanDetermineTimeTaken(order, true)) return; - sum += order->GetTimetabledTravel(); + if (!skip_travel) { + if (!CanDetermineTimeTaken(order, true)) return; + sum += order->GetTimetabledTravel(); + } table[i].arrival = sum; if (predicted) SetBit(table[i].flags, TADF_ARRIVAL_PREDICTED); + if (no_offset) SetBit(table[i].flags, TADF_ARRIVAL_NO_OFFSET); } if (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.list->GetDispatchScheduleByIndex(order->GetDispatchScheduleIndex()); DispatchSchedule predicted_ds; @@ -157,14 +204,18 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID if (slot <= -1) return; sum = slot - _scaled_date_ticks; predicted = true; + no_offset = true; } else { if (!CanDetermineTimeTaken(order, false)) return; sum += order->GetTimetabledWait(); } table[i].departure = sum; if (predicted) SetBit(table[i].flags, TADF_DEPARTURE_PREDICTED); + if (predicted) SetBit(table[i].flags, TADF_DEPARTURE_NO_OFFSET); } + skip_travel = false; + ++i; order = order->next; if (i >= v->GetNumOrders()) { @@ -176,10 +227,12 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID /* When loading at a scheduled station we still have to treat the * travelling part of the first order. */ - if (!travelling) { + if (!travelling && table[i].arrival == INVALID_TICKS) { if (!CanDetermineTimeTaken(order, true)) return; sum += order->GetTimetabledTravel(); table[i].arrival = sum; + if (predicted) SetBit(table[i].flags, TADF_ARRIVAL_PREDICTED); + if (no_offset) SetBit(table[i].flags, TADF_ARRIVAL_NO_OFFSET); } } @@ -722,7 +775,7 @@ struct TimetableWindow : GeneralVehicleWindow { SetDParam(0, _scaled_date_ticks + arr_dep[i / 2].arrival); DrawString(time_left, time_right, y, STR_JUST_DATE_WALLCLOCK_TINY, TC_GREEN); } else { - SetDParam(0, _scaled_date_ticks + arr_dep[i / 2].arrival + (HasBit(arr_dep[i / 2].flags, TADF_ARRIVAL_PREDICTED) ? 0 : offset)); + SetDParam(0, _scaled_date_ticks + arr_dep[i / 2].arrival + (HasBit(arr_dep[i / 2].flags, TADF_ARRIVAL_NO_OFFSET) ? 0 : offset)); DrawString(time_left, time_right, y, STR_JUST_DATE_WALLCLOCK_TINY, HasBit(arr_dep[i / 2].flags, TADF_ARRIVAL_PREDICTED) ? (TextColour)(TC_IS_PALETTE_COLOUR | TC_NO_SHADE | 4) : (show_late ? TC_RED : i == selected ? TC_WHITE : TC_BLACK)); } @@ -730,7 +783,7 @@ struct TimetableWindow : GeneralVehicleWindow { } else { if (arr_dep[i / 2].departure != INVALID_TICKS) { DrawString(abbr_left, abbr_right, y, STR_TIMETABLE_DEPARTURE_ABBREVIATION, i == selected ? TC_WHITE : TC_BLACK); - SetDParam(0, _scaled_date_ticks + arr_dep[i/2].departure + (HasBit(arr_dep[i / 2].flags, TADF_DEPARTURE_PREDICTED) ? 0 : offset)); + SetDParam(0, _scaled_date_ticks + arr_dep[i/2].departure + (HasBit(arr_dep[i / 2].flags, TADF_DEPARTURE_NO_OFFSET) ? 0 : offset)); DrawString(time_left, time_right, y, STR_JUST_DATE_WALLCLOCK_TINY, HasBit(arr_dep[i / 2].flags, TADF_DEPARTURE_PREDICTED) ? (TextColour)(TC_IS_PALETTE_COLOUR | TC_NO_SHADE | 4) : (show_late ? TC_RED : i == selected ? TC_WHITE : TC_BLACK)); } diff --git a/src/tracerestrict.cpp b/src/tracerestrict.cpp index 843112d12c..2e6fbb9955 100644 --- a/src/tracerestrict.cpp +++ b/src/tracerestrict.cpp @@ -1759,6 +1759,37 @@ int GetTraceRestrictTimeDateValue(TraceRestrictTimeDateValueField type) } } +int GetTraceRestrictTimeDateValueFromDate(TraceRestrictTimeDateValueField type, DateTicksScaled scaled_date_ticks) +{ + Minutes minutes = (scaled_date_ticks / _settings_game.game_time.ticks_per_minute) + _settings_game.game_time.clock_offset; + + switch (type) { + case TRTDVF_MINUTE: + return MINUTES_MINUTE(minutes); + + case TRTDVF_HOUR: + return MINUTES_HOUR(minutes); + + case TRTDVF_HOUR_MINUTE: + return (MINUTES_HOUR(minutes) * 100) + MINUTES_MINUTE(minutes); + + case TRTDVF_DAY: { + YearMonthDay ymd; + ConvertDateToYMD(scaled_date_ticks / (DAY_TICKS * _settings_game.economy.day_length_factor), &ymd); + return ymd.day; + } + + case TRTDVF_MONTH: { + YearMonthDay ymd; + ConvertDateToYMD(scaled_date_ticks / (DAY_TICKS * _settings_game.economy.day_length_factor), &ymd); + return ymd.month + 1; + } + + default: + return 0; + } +} + /** * This is called when a station, waypoint or depot is about to be deleted * Scan program pool and change any references to it to the invalid station ID, to avoid dangling references diff --git a/src/tracerestrict.h b/src/tracerestrict.h index 94c3682176..94b01220e3 100644 --- a/src/tracerestrict.h +++ b/src/tracerestrict.h @@ -941,6 +941,7 @@ CommandCost TraceRestrictProgramMoveItemAt(std::vector &items void ShowTraceRestrictProgramWindow(TileIndex tile, Track track); int GetTraceRestrictTimeDateValue(TraceRestrictTimeDateValueField type); +int GetTraceRestrictTimeDateValueFromDate(TraceRestrictTimeDateValueField type, DateTicksScaled scaled_date_ticks); void TraceRestrictRemoveDestinationID(TraceRestrictOrderCondAuxField type, uint16 index); void TraceRestrictRemoveGroupID(GroupID index); From c40f4e0abc8d56e44d6011e7def5ebb10b2f8a3d Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Sat, 15 Jan 2022 15:57:33 +0000 Subject: [PATCH 11/11] Handle predictable conditional orders in departure boards --- src/departures.cpp | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/departures.cpp b/src/departures.cpp index 2a515b01b1..cb5ce47bce 100644 --- a/src/departures.cpp +++ b/src/departures.cpp @@ -33,6 +33,7 @@ #include "cargo_type.h" #include "departures_func.h" #include "departures_type.h" +#include "tracerestrict.h" #include "3rdparty/cpp-btree/btree_set.h" #include @@ -80,9 +81,13 @@ static bool IsArrival(const Order *order, StationID station) { !(order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION)); } -static uint8 GetDepartureConditionalOrderMode(const Order *order) +static uint8 GetDepartureConditionalOrderMode(const Order *order, DateTicksScaled eval_date) { if (order->GetConditionVariable() == OCV_UNCONDITIONALLY) return 1; + if (order->GetConditionVariable() == OCV_TIME_DATE) { + int value = GetTraceRestrictTimeDateValueFromDate(static_cast(order->GetConditionValue()), eval_date); + return OrderConditionCompare(order->GetConditionComparator(), value, order->GetXData()) ? 1 : 2; + } return _settings_client.gui.departure_conditionals; } @@ -299,14 +304,9 @@ DepartureList* MakeDepartureList(StationID station, const std::vectorlateness_counter > max_date) { - break; - } - /* If the order is a conditional branch, handle it. */ if (order->IsType(OT_CONDITIONAL)) { - switch(GetDepartureConditionalOrderMode(order)) { + switch(GetDepartureConditionalOrderMode(order, start_date + date_only_scaled)) { case 0: { /* Give up */ break; @@ -330,12 +330,18 @@ DepartureList* MakeDepartureList(StationID station, const std::vectorGetWaitTime(); /* Added previously in VehicleSetNextDepartureTime */ order = (order->next == nullptr) ? v->GetFirstOrder() : order->next; continue; } } } + /* If the scheduled departure date is too far in the future, stop. */ + if (start_date - v->lateness_counter > max_date) { + break; + } + /* Skip it if it's an automatic order. */ if (order->IsType(OT_IMPLICIT)) { order = (order->next == nullptr) ? v->GetFirstOrder() : order->next; @@ -466,17 +472,23 @@ DepartureList* MakeDepartureList(StationID station, const std::vectorIsType(OT_CONDITIONAL)) { - switch(GetDepartureConditionalOrderMode(order)) { + switch (GetDepartureConditionalOrderMode(order, c.scheduled_date != 0 ? c.scheduled_date : _scaled_date_ticks)) { case 0: { /* Give up */ break; } case 1: { /* Take the branch */ + if (c.scheduled_date != 0 && (order->GetWaitTime() != 0 || order->IsWaitTimetabled())) { + c.scheduled_date += order->GetWaitTime(); + } else { + c.scheduled_date = 0; + } order = least_order->v->GetOrder(order->GetConditionSkipToOrder()); if (order == nullptr) { break; } + if (c.scheduled_date != 0) c.scheduled_date -= order->GetTravelTime(); continue; } case 2: { @@ -522,7 +534,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vectorGetType() != OT_IMPLICIT) || order->GetNonStopType() == ONSF_NO_STOP_AT_ANY_STATION || order->GetNonStopType() == ONSF_NO_STOP_AT_DESTINATION_STATION) { - c.scheduled_date += order->GetWaitTime(); + if (c.scheduled_date != 0) c.scheduled_date += order->GetWaitTime(); order = (order->next == nullptr) ? least_order->v->GetFirstOrder() : order->next; continue; } @@ -554,7 +566,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vectorGetWaitTime(); + if (c.scheduled_date != 0) c.scheduled_date += order->GetWaitTime(); /* Get the next order, which may be the vehicle's first order. */ order = (order->next == nullptr) ? least_order->v->GetFirstOrder() : order->next; @@ -704,7 +716,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vectorv->GetNumOrders(); i > 0; --i) { /* If the order is a conditional branch, handle it. */ if (order->IsType(OT_CONDITIONAL)) { - switch(GetDepartureConditionalOrderMode(order)) { + switch(GetDepartureConditionalOrderMode(order, least_order->expected_date)) { case 0: { /* Give up */ break; @@ -716,6 +728,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vectorexpected_date -= order->GetTravelTime(); /* Added in next VehicleSetNextDepartureTime */ if (VehicleSetNextDepartureTime(&least_order->expected_date, &least_order->scheduled_waiting_time, date_only_scaled, least_order->v, order, false, schdispatch_last_planned_dispatch)) { least_order->lateness = 0; } @@ -724,6 +737,7 @@ DepartureList* MakeDepartureList(StationID station, const std::vectorexpected_date -= order->GetWaitTime(); /* Added previously in VehicleSetNextDepartureTime */ order = (order->next == nullptr) ? least_order->v->GetFirstOrder() : order->next; if (VehicleSetNextDepartureTime(&least_order->expected_date, &least_order->scheduled_waiting_time, date_only_scaled, least_order->v, order, false, schdispatch_last_planned_dispatch)) { least_order->lateness = 0;