diff --git a/src/departures.cpp b/src/departures.cpp index f4bd6fac59..9e242c7b60 100644 --- a/src/departures.cpp +++ b/src/departures.cpp @@ -39,10 +39,17 @@ #include #include +static constexpr Ticks INVALID_DEPARTURE_TICKS = INT32_MIN; + /* A cache of used departure time for scheduled dispatch in departure time calculation */ using ScheduledDispatchCache = btree::btree_map>; using ScheduledDispatchVehicleRecords = btree::btree_map, LastDispatchRecord>; +struct ArrivalHistoryEntry { + const Order *order; + Ticks offset; +}; + /** A scheduled order. */ struct OrderDate { const Order *order; ///< The order @@ -54,7 +61,7 @@ struct OrderDate { bool arrivals_complete; ///< arrival history is complete Ticks scheduled_waiting_time; ///< Scheduled waiting time if scheduled dispatch is used ScheduledDispatchVehicleRecords dispatch_records; ///< Dispatch records for this vehicle - std::vector arrival_history; + std::vector arrival_history; inline Ticks EffectiveWaitingTime() const { @@ -420,7 +427,7 @@ static void GetDepartureCandidateOrderDatesFromVehicle(std::vector &n if (!IsVehicleUsableForDepartures(v, calling_settings)) return; ScheduledDispatchVehicleRecords dispatch_records; - std::vector arrival_history; + std::vector arrival_history; const StateTicks state_ticks_base = _state_ticks; @@ -551,7 +558,7 @@ static void GetDepartureCandidateOrderDatesFromVehicle(std::vector &n break; } else { if (type == D_ARRIVAL) { - arrival_history.push_back(order); + arrival_history.push_back({ order, start_ticks }); } /* Go to the next order in the list. */ @@ -667,13 +674,23 @@ bool DepartureViaTerminusState::HandleCallingPoint(Departure *d, const Order *or /** * Process arrival history, returns true if a valid arrival was found. + * @param d Departure. + * @param arrival_history Arrival history up to but not including the source order, offset field has arbitrary base, and refers to order departure time. + * @param arrival_tick Arrival time at the source order, in the same arbitrary base as arrival_history. + * @param source Source order detector. + * @param calling_settings Calling at settings. + * @return true is an arrival was found. */ -static bool ProcessArrivalHistory(Departure *d, std::span arrival_history, DepartureOrderDestinationDetector source, DepartureCallingSettings calling_settings) +static bool ProcessArrivalHistory(Departure *d, std::span arrival_history, Ticks arrival_tick, DepartureOrderDestinationDetector source, DepartureCallingSettings calling_settings) { + /* Not that d->scheduled_tick is an arrival time, not a departure time as in arrival_history. + * arrival_offset is thus usable to transform either arrival or departure times in the arrival_history timebase to StateTicks. */ + const StateTicks arrival_offset = d->scheduled_tick - arrival_tick; + std::vector> possible_origins; for (uint i = 0; i < (uint)arrival_history.size(); i++) { - const Order *o = arrival_history[i]; + const Order *o = arrival_history[i].order; if (IsStationIDCallingPointOrder(o)) { if (source.StationMatches(o->GetDestination())) { @@ -699,7 +716,7 @@ static bool ProcessArrivalHistory(Departure *d, std::span arrival } } - const Order *origin = nullptr; + ArrivalHistoryEntry origin = { nullptr, 0 }; uint origin_arrival_history_index = 0; for (const auto &item : possible_origins) { if (item.first != INVALID_STATION) { @@ -709,7 +726,7 @@ static bool ProcessArrivalHistory(Departure *d, std::span arrival } } possible_origins.clear(); - if (origin != nullptr) { + if (origin.order != nullptr) { bool check_no_load_mode = false; if (calling_settings.ShowAllStops() && d->show_as == DSA_NORMAL) { check_no_load_mode = true; @@ -721,21 +738,29 @@ static bool ProcessArrivalHistory(Departure *d, std::span arrival check_no_load_mode = false; } }; - check_order(origin); + check_order(origin.order); + + auto make_call_at = [&](const ArrivalHistoryEntry &entry) -> CallAt { + if (entry.offset == INVALID_DEPARTURE_TICKS) { + return CallAt((StationID)entry.order->GetDestination()); + } else { + return CallAt((StationID)entry.order->GetDestination(), entry.offset + arrival_offset); + } + }; for (uint i = origin_arrival_history_index + 1; i < (uint)arrival_history.size(); i++) { - const Order *o = arrival_history[i]; + const Order *o = arrival_history[i].order; if (IsStationIDCallingPointOrder(o)) { check_order(o); if (o->IsType(OT_GOTO_STATION) && (o->GetLoadType() != OLFB_NO_LOAD || calling_settings.ShowAllStops())) { - d->calling_at.push_back(CallAt((StationID)o->GetDestination())); + d->calling_at.push_back(make_call_at(arrival_history[i])); } else if (o->IsType(OT_GOTO_WAYPOINT) && calling_settings.ShowAllStops()) { - d->calling_at.push_back(CallAt((StationID)o->GetDestination())); + d->calling_at.push_back(make_call_at(arrival_history[i])); } } } - d->terminus = CallAt((StationID)origin->GetDestination()); + d->terminus = make_call_at(origin); return true; } @@ -948,12 +973,12 @@ static DepartureList MakeDepartureListLiveMode(DepartureOrderDestinationDetector /* Project back the arrival history if the vehicle is already part way along the route, this stops at conditional jumps or jump targets */ if (!lod.arrivals_complete) { - const Order *existing_history_start = lod.arrival_history.empty() ? lod.order : lod.arrival_history.front(); + ArrivalHistoryEntry existing_history_start = lod.arrival_history.empty() ? ArrivalHistoryEntry{ lod.order, lod.expected_tick } : lod.arrival_history.front(); OrderID existing_history_start_idx = 0; OrderID arrival_idx = 0; for (OrderID i = 0; i < lod.v->GetNumOrders(); i++) { const Order *o = lod.v->GetOrder(i); - if (o == existing_history_start) existing_history_start_idx = i; + if (o == existing_history_start.order) existing_history_start_idx = i; if (o == lod.order) arrival_idx = i; } @@ -979,15 +1004,46 @@ static DepartureList MakeDepartureListLiveMode(DepartureOrderDestinationDetector } } - std::vector new_history; - for (const Order *o = lod.v->GetOrder(predict_history_starting_from); o != existing_history_start; o = lod.v->orders->GetNext(o)) { - if (o->GetType() == OT_GOTO_STATION || o->GetType() == OT_IMPLICIT || (o->IsType(OT_GOTO_WAYPOINT) && o->IsWaitTimetabled())) new_history.push_back(o); + std::vector new_history; + Ticks cumul = 0; + for (const Order *o = lod.v->GetOrder(predict_history_starting_from); o != existing_history_start.order; o = lod.v->orders->GetNext(o)) { + if ((o->GetTravelTime() == 0 && !o->IsTravelTimetabled()) || o->IsScheduledDispatchOrder(true)) { + if (!new_history.empty()) new_history.back().offset = INVALID_DEPARTURE_TICKS; // Signal to not use times for orders before this in the history + } + + cumul += o->GetTravelTime() + o->GetWaitTime(); + + if (o->GetType() == OT_GOTO_STATION || o->GetType() == OT_IMPLICIT || (o->IsType(OT_GOTO_WAYPOINT) && o->IsWaitTimetabled())) { + new_history.push_back({ o, cumul }); + } } + cumul += existing_history_start.order->GetTravelTime(); + if (existing_history_start.order == lod.order) { + cumul += d->EffectiveWaitingTime(); + } else { + cumul += existing_history_start.order->GetWaitTime(); + } + + /* Iterate in reverse order, to fill in times properly */ + size_t idx = new_history.size(); + while (idx > 0) { + ArrivalHistoryEntry &entry = new_history[idx - 1]; + if (entry.offset == INVALID_DEPARTURE_TICKS) break; + + entry.offset = existing_history_start.offset - (cumul - entry.offset); + + idx--; + } + while (idx > 0) { + new_history[idx - 1].offset = INVALID_DEPARTURE_TICKS; + idx--; + } + new_history.insert(new_history.end(), lod.arrival_history.begin(), lod.arrival_history.end()); lod.arrival_history = std::move(new_history); } - if (ProcessArrivalHistory(d, lod.arrival_history, source, calling_settings)) { + if (ProcessArrivalHistory(d, lod.arrival_history, lod.expected_tick - d->EffectiveWaitingTime(), source, calling_settings)) { bool duplicate = false; if (_settings_client.gui.departure_merge_identical) { @@ -1083,7 +1139,7 @@ static DepartureList MakeDepartureListLiveMode(DepartureOrderDestinationDetector break; } else { if (type == D_ARRIVAL) { - lod.arrival_history.push_back(order); + lod.arrival_history.push_back({ order, lod.expected_tick }); } } @@ -1144,7 +1200,7 @@ struct DepartureListScheduleModeSlotEvaluator { const DepartureOrderDestinationDetector &source; DepartureType type; DepartureCallingSettings calling_settings; - std::vector &arrival_history; + std::vector &arrival_history; StateTicks slot{}; uint slot_index{}; @@ -1283,7 +1339,7 @@ void DepartureListScheduleModeSlotEvaluator::EvaluateFromSourceOrder(const Order } else { /* Computing arrivals: */ - if (ProcessArrivalHistory(&d, this->arrival_history, this->source, this->calling_settings)) { + if (ProcessArrivalHistory(&d, this->arrival_history, (departure_tick - this->slot).AsTicks(), this->source, this->calling_settings)) { this->result.push_back(std::make_unique(std::move(d))); } } @@ -1307,7 +1363,7 @@ void DepartureListScheduleModeSlotEvaluator::EvaluateSlotIndex(uint slot_index) return; } if (type == D_ARRIVAL) { - this->arrival_history.push_back(this->start_order); + this->arrival_history.push_back({ this->start_order, (departure_tick - this->slot).AsTicks() }); } const Order *order = this->v->orders->GetNext(this->start_order); @@ -1373,7 +1429,7 @@ void DepartureListScheduleModeSlotEvaluator::EvaluateSlotIndex(uint slot_index) } if (type == D_ARRIVAL) { - this->arrival_history.push_back(order); + this->arrival_history.push_back({ order, (departure_tick - this->slot).AsTicks() }); } order = this->v->orders->GetNext(order); @@ -1434,7 +1490,7 @@ static DepartureList MakeDepartureListScheduleMode(DepartureOrderDestinationDete const Ticks tick_duration = (end_tick - start_tick).AsTicks(); std::vector> result; - std::vector arrival_history; + std::vector arrival_history; for (const Vehicle *veh : vehicles) { if (!HasBit(veh->vehicle_flags, VF_SCHEDULED_DISPATCH)) continue; diff --git a/src/departures_gui.cpp b/src/departures_gui.cpp index ac36b42e0c..9efd17b89c 100644 --- a/src/departures_gui.cpp +++ b/src/departures_gui.cpp @@ -582,8 +582,6 @@ public: if (this->source_type == DST_STATION) { _settings_client.gui.departure_default_mode = this->mode; } - this->SetWidgetDisabledState(WID_DB_SHOW_TIMES, this->mode == DM_ARRIVALS || !_settings_time.time_in_minutes); - this->SetWidgetDirty(WID_DB_SHOW_TIMES); this->SetWidgetDirty(widget); break; } @@ -716,7 +714,7 @@ public: this->show_arrival_times = false; this->RaiseWidget(WID_DB_SHOW_TIMES); } - this->SetWidgetDisabledState(WID_DB_SHOW_TIMES, this->mode == DM_ARRIVALS || !_settings_time.time_in_minutes); + this->SetWidgetDisabledState(WID_DB_SHOW_TIMES, !_settings_time.time_in_minutes); this->SetupValues(); this->ReInit(); if (_pause_mode != PM_UNPAUSED) this->OnGameTick(); @@ -810,7 +808,7 @@ uint DeparturesWindow::GetMinWidth() const /* Time */ result = (this->mode == DM_COMBINED) ? cached_date_combined_width : cached_date_width; - if (this->show_arrival_times && _settings_time.time_in_minutes && this->mode != DM_ARRIVALS) { + if (this->show_arrival_times && _settings_time.time_in_minutes) { result += PadWidth(cached_date_width); } @@ -883,7 +881,7 @@ void DeparturesWindow::DrawDeparturesListItems(const Rect &r) const int time_width = (this->mode == DM_COMBINED) ? cached_date_combined_width : cached_date_width; int arrival_time_width = 0; - if (this->show_arrival_times && _settings_time.time_in_minutes && this->mode != DM_ARRIVALS) { + if (this->show_arrival_times && _settings_time.time_in_minutes) { arrival_time_width = cached_date_width; }