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;