Scheduled dispatch: Store vehicle last dispatch records

This commit is contained in:
Jonathan G Rennison 2024-08-12 00:06:12 +01:00
parent 0d863bdf0d
commit a2b6363584
16 changed files with 222 additions and 13 deletions

View File

@ -60,4 +60,6 @@ void BaseConsist::CopyConsistPropertiesFrom(const BaseConsist *src)
} else {
ClrBit(this->vehicle_flags, VF_SCHEDULED_DISPATCH);
}
this->dispatch_records = src->dispatch_records;
}

View File

@ -14,11 +14,26 @@
#include "date_type.h"
#include "timetable.h"
#include "core/tinystring_type.hpp"
#include "3rdparty/cpp-btree/btree_map.h"
struct LastDispatchRecord {
enum RecordFlags {
RF_FIRST_SLOT = 0, ///< Dispatch slot was first
RF_LAST_SLOT, ///< Dispatch slot was last
};
StateTicks dispatched;
uint32_t offset;
uint16_t slot_flags;
uint8_t record_flags;
};
/** Various front vehicle properties that are preserved when autoreplacing, using order-backup or switching front engines within a consist. */
struct BaseConsist {
TinyString name; ///< Name of vehicle
btree::btree_map<uint16_t, LastDispatchRecord> dispatch_records; ///< Records of last scheduled dispatches
/* Used for timetabling. */
uint32_t current_order_time; ///< How many ticks have passed since this order started.
int32_t lateness_counter; ///< How many ticks late (or early if negative) this vehicle is.

View File

@ -2237,8 +2237,22 @@ STR_SCHDISPATCH_SLOT_TOOLTIP_TAG :{}Tag {NUM}
STR_SCHDISPATCH_SLOT_TOOLTIP_TAG_NAMED :{}Tag {NUM}: {RAW_STRING}
STR_SCHDISPATCH_SUMMARY_NO_LAST_DEPARTURE :{BLACK}No previous departures.
STR_SCHDISPATCH_SUMMARY_VEHICLE_NO_LAST_DEPARTURE :{BLACK}This vehicle has not departed.
STR_SCHDISPATCH_SUMMARY_DEPARTURE_DETAILS : ({RAW_STRING})
STR_SCHDISPATCH_SUMMARY_DEPARTURE_DETAIL_SEPARATOR :, {STRING}
STR_SCHDISPATCH_SUMMARY_DEPARTURE_DETAIL_WAS_FIRST :first slot
STR_SCHDISPATCH_SUMMARY_DEPARTURE_DETAIL_WAS_LAST :last slot
STR_SCHDISPATCH_SUMMARY_DEPARTURE_DETAIL_TAG :tag {NUM}
STR_SCHDISPATCH_SUMMARY_DEPARTURE_DETAIL_TAG_NAMED :tag {NUM} ({RAW_STRING})
###length 2
STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_PAST :{BLACK}Last departure at {TT_TIME}.
STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_FUTURE :{BLACK}Last departure has not left yet, it will depart at {TT_TIME}.
###length 2
STR_SCHDISPATCH_SUMMARY_VEHICLE_DEPARTURE_PAST :{BLACK}This vehicle departed at {TT_TIME}{STRING1}.
STR_SCHDISPATCH_SUMMARY_VEHICLE_DEPARTURE_FUTURE :{BLACK}This vehicle has not left yet, it will depart at {TT_TIME}{STRING1}.
STR_SCHDISPATCH_SUMMARY_L1 :{BLACK}This schedule is estimated to require at least {COMMA} vehicle{P "" s}.
STR_SCHDISPATCH_SUMMARY_L2 :{BLACK}This schedule repeats every {STRING1}.
STR_SCHDISPATCH_SUMMARY_L3 :{BLACK}This schedule began at {TT_TIME}, and ends at {TT_TIME}.

View File

@ -2657,6 +2657,7 @@ CommandCost CmdCloneOrder(TileIndex tile, DoCommandFlag flags, uint32_t p1, uint
* We reset the order indices, if the new orders are different.
* (We mainly do this to keep the order indices valid and in range.) */
DeleteVehicleOrders(dst, false, ShouldResetOrderIndicesOnOrderCopy(src, dst));
dst->dispatch_records.clear();
dst->orders = src->orders;
@ -2745,6 +2746,7 @@ CommandCost CmdCloneOrder(TileIndex tile, DoCommandFlag flags, uint32_t p1, uint
* We only the order indices, if the new orders are different.
* (We mainly do this to keep the order indices valid and in range.) */
DeleteVehicleOrders(dst, true, ShouldResetOrderIndicesOnOrderCopy(src, dst));
dst->dispatch_records.clear();
order_dst = &first;
for (const Order *order : src->Orders()) {
@ -3219,7 +3221,7 @@ bool EvaluateDispatchSlotConditionalOrder(const Order *order, const Vehicle *v,
}
offset = last % sched.GetScheduledDispatchDuration();
} else {
StateTicks slot = GetScheduledDispatchTime(sched, state_ticks);
StateTicks slot = GetScheduledDispatchTime(sched, state_ticks).first;
if (slot == INVALID_STATE_TICKS) {
/* No next dispatch */
return OrderConditionCompare(order->GetConditionComparator(), 0, 0);

View File

@ -431,6 +431,19 @@ CommandCost CmdScheduledDispatchRemoveSchedule(TileIndex tile, DoCommandFlag fla
}
}
}
for (Vehicle *v2 = v->FirstShared(); v2 != nullptr; v2 = v2->NextShared()) {
if (v2->dispatch_records.empty()) continue;
btree::btree_map<uint16_t, LastDispatchRecord> new_records;
for (auto &iter : v2->dispatch_records) {
if (iter.first < schedule_index) {
new_records[iter.first] = std::move(iter.second);
} else if (iter.first > schedule_index) {
new_records[iter.first - 1] = std::move(iter.second);
}
}
v2->dispatch_records = std::move(new_records);
}
SchdispatchInvalidateWindows(v);
}
@ -682,6 +695,23 @@ CommandCost CmdScheduledDispatchSwapSchedules(TileIndex tile, DoCommandFlag flag
}
}
}
for (Vehicle *v2 = v->FirstShared(); v2 != nullptr; v2 = v2->NextShared()) {
if (v2->dispatch_records.empty()) continue;
auto iter_1 = v2->dispatch_records.find(static_cast<uint16_t>(schedule_index_1));
auto iter_2 = v2->dispatch_records.find(static_cast<uint16_t>(schedule_index_2));
if (iter_1 != v2->dispatch_records.end() && iter_2 != v2->dispatch_records.end()) {
std::swap(iter_1->second, iter_2->second);
} else if (iter_1 != v2->dispatch_records.end()) {
LastDispatchRecord r = std::move(iter_1->second);
v2->dispatch_records.erase(iter_1);
v2->dispatch_records[static_cast<uint16_t>(schedule_index_2)] = std::move(r);
} else if (iter_2 != v2->dispatch_records.end()) {
LastDispatchRecord r = std::move(iter_2->second);
v2->dispatch_records.erase(iter_2);
v2->dispatch_records[static_cast<uint16_t>(schedule_index_1)] = std::move(r);
}
}
SchdispatchInvalidateWindows(v);
SetTimetableWindowsDirty(v, STWDF_SCHEDULED_DISPATCH);
}

View File

@ -526,7 +526,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
}
have_last = true;
}
StateTicks next_slot = GetScheduledDispatchTime(ds, _state_ticks);
StateTicks next_slot = GetScheduledDispatchTime(ds, _state_ticks).first;
if (next_slot != INVALID_STATE_TICKS && ((next_slot - ds.GetScheduledDispatchStartTick()).AsTicks() % ds.GetScheduledDispatchDuration() == slot->offset)) {
if (!have_last) _temp_special_strings[0] += '\n';
_temp_special_strings[0] += GetString(STR_SCHDISPATCH_SLOT_TOOLTIP_NEXT);
@ -638,7 +638,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
const StateTicks start_tick = ds.GetScheduledDispatchStartTick();
const StateTicks end_tick = ds.GetScheduledDispatchStartTick() + ds.GetScheduledDispatchDuration();
StateTicks slot = GetScheduledDispatchTime(ds, _state_ticks);
StateTicks slot = GetScheduledDispatchTime(ds, _state_ticks).first;
int32_t next_offset = (slot != INVALID_STATE_TICKS) ? (slot - ds.GetScheduledDispatchStartTick()).AsTicks() % ds.GetScheduledDispatchDuration() : INT32_MIN;
int32_t last_dispatch;
@ -806,8 +806,7 @@ struct SchdispatchWindow : GeneralVehicleWindow {
y += WidgetDimensions::scaled.vsep_wide;
if (ds.GetScheduledDispatchLastDispatch() != INVALID_SCHEDULED_DISPATCH_OFFSET) {
const StateTicks last_departure = ds.GetScheduledDispatchStartTick() + ds.GetScheduledDispatchLastDispatch();
auto show_last_departure = [&](const StateTicks last_departure, bool vehicle_mode, std::string details) {
StringID str;
if (_state_ticks < last_departure) {
str = STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_FUTURE;
@ -815,7 +814,16 @@ struct SchdispatchWindow : GeneralVehicleWindow {
} else {
str = STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_PAST;
}
if (vehicle_mode) str += (STR_SCHDISPATCH_SUMMARY_VEHICLE_DEPARTURE_PAST - STR_SCHDISPATCH_SUMMARY_LAST_DEPARTURE_PAST);
SetDParam(0, last_departure);
if (details.empty()) {
SetDParam(1, STR_EMPTY);
} else {
SetDParam(1, STR_SCHDISPATCH_SUMMARY_DEPARTURE_DETAILS);
SetDParamStr(2, std::move(details));
}
DrawString(ir.left, ir.right, y, str);
y += GetCharacterHeight(FS_NORMAL);
@ -836,12 +844,42 @@ struct SchdispatchWindow : GeneralVehicleWindow {
set_next_departure_update(_settings_time.FromTickMinutes(target + ((hours + 1) * 60) + 1));
}
}
};
auto record_iter = v->dispatch_records.find(static_cast<uint16_t>(this->schedule_index));
if (record_iter != v->dispatch_records.end()) {
const LastDispatchRecord &record = record_iter->second;
std::string details;
auto add_detail = [&](StringID str) {
auto tmp_params = MakeParameters(str);
GetStringWithArgs(StringBuilder(details), details.empty() ? STR_JUST_STRING : STR_SCHDISPATCH_SUMMARY_DEPARTURE_DETAIL_SEPARATOR, tmp_params);
};
if (HasBit(record.record_flags, LastDispatchRecord::RF_FIRST_SLOT)) add_detail(STR_SCHDISPATCH_SUMMARY_DEPARTURE_DETAIL_WAS_FIRST);
if (HasBit(record.record_flags, LastDispatchRecord::RF_LAST_SLOT)) add_detail(STR_SCHDISPATCH_SUMMARY_DEPARTURE_DETAIL_WAS_LAST);
for (uint8_t flag_bit = DispatchSlot::SDSF_FIRST_TAG; flag_bit <= DispatchSlot::SDSF_LAST_TAG; flag_bit++) {
if (HasBit(record.slot_flags, flag_bit)) {
std::string_view name = ds.GetSupplementaryName(SDSNT_DEPARTURE_TAG, flag_bit - DispatchSlot::SDSF_FIRST_TAG);
auto tmp_params = MakeParameters(1 + flag_bit - DispatchSlot::SDSF_FIRST_TAG, std::string{name});
_temp_special_strings[1] = GetStringWithArgs(name.empty() ? STR_SCHDISPATCH_SUMMARY_DEPARTURE_DETAIL_TAG : STR_SCHDISPATCH_SUMMARY_DEPARTURE_DETAIL_TAG_NAMED, tmp_params);
add_detail(SPECSTR_TEMP_START + 1);
}
}
show_last_departure(record.dispatched, true, std::move(details));
} else {
DrawString(ir.left, ir.right, y, STR_SCHDISPATCH_SUMMARY_VEHICLE_NO_LAST_DEPARTURE);
y += GetCharacterHeight(FS_NORMAL);
}
if (ds.GetScheduledDispatchLastDispatch() != INVALID_SCHEDULED_DISPATCH_OFFSET) {
show_last_departure(ds.GetScheduledDispatchStartTick() + ds.GetScheduledDispatchLastDispatch(), false, "");
} else {
DrawString(ir.left, ir.right, y, STR_SCHDISPATCH_SUMMARY_NO_LAST_DEPARTURE);
y += GetCharacterHeight(FS_NORMAL);
}
const StateTicks next_departure = GetScheduledDispatchTime(ds, _state_ticks);
const StateTicks next_departure = GetScheduledDispatchTime(ds, _state_ticks).first;
if (next_departure != INVALID_STATE_TICKS) {
set_next_departure_update(next_departure + ds.GetScheduledDispatchDelay());
SetDParam(0, next_departure);

View File

@ -54,5 +54,6 @@ add_files(
train_speed_adaptation.cpp
tunnel_sl.cpp
vehicle_sl.cpp
vehicle_sl.h
waypoint_sl.cpp
)

View File

@ -117,7 +117,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_STATION_CATCHMENT_INC, XSCF_NULL, 1, 1, "station_catchment_inc", nullptr, nullptr, nullptr },
{ XSLFI_CUSTOM_BRIDGE_HEADS, XSCF_NULL, 4, 4, "custom_bridge_heads", nullptr, nullptr, nullptr },
{ XSLFI_CHUNNEL, XSCF_NULL, 3, 3, "chunnel", nullptr, nullptr, "TUNN" },
{ XSLFI_SCHEDULED_DISPATCH, XSCF_NULL, 8, 8, "scheduled_dispatch", nullptr, nullptr, nullptr },
{ XSLFI_SCHEDULED_DISPATCH, XSCF_NULL, 9, 9, "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, 8, 8, "timetable_extra", nullptr, nullptr, nullptr },

View File

@ -13,6 +13,7 @@
#include "../network/network.h"
#include "saveload_internal.h"
#include "vehicle_sl.h"
#include "../safeguards.h"
@ -449,6 +450,12 @@ struct OrderBackupDispatchScheduleStructHandler final : public DispatchScheduleS
void Load(void *object) const override { this->LoadSchedules(static_cast<OrderBackup *>(object)->dispatch_schedules); }
};
struct OrderBackupDispatchRecordsStructHandlerBase final : public DispatchRecordsStructHandlerBase {
void Save(void *object) const override { this->SaveDispatchRecords(static_cast<OrderBackup *>(object)->dispatch_records); }
void Load(void *object) const override { this->LoadDispatchRecords(static_cast<OrderBackup *>(object)->dispatch_records); }
};
NamedSaveLoadTable GetOrderListDescription()
{
static const NamedSaveLoad _orderlist_desc[] = {
@ -488,6 +495,7 @@ NamedSaveLoadTable GetOrderBackupDescription()
NSL("", SLE_CONDNULL_X(18, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SCHEDULED_DISPATCH, 2, 2))),
NSLT_STRUCTLIST<OrderBackupDispatchScheduleStructHandler>("dispatch_schedule"),
NSLT_STRUCTLIST<OrderBackupDispatchRecordsStructHandlerBase>("dispatch_records"),
};
return _order_backup_desc;

View File

@ -29,6 +29,7 @@
#include "../core/format.hpp"
#include "saveload.h"
#include "vehicle_sl.h"
#include <map>
@ -939,6 +940,43 @@ struct TrainLookaheadStateStructHandler final : public TypedSaveLoadStructHandle
}
};
NamedSaveLoadTable DispatchRecordsStructHandlerBase::GetDescription() const
{
static const NamedSaveLoad _record_desc[] = {
NSL("id", SLE_VAR(RecordPair, first, SLE_UINT16)),
NSL("dispatched", SLE_VAR(RecordPair, second.dispatched, SLE_INT64)),
NSL("offset", SLE_VAR(RecordPair, second.offset, SLE_UINT32)),
NSL("slot_flags", SLE_VAR(RecordPair, second.slot_flags, SLE_UINT16)),
NSL("record_flags", SLE_VAR(RecordPair, second.record_flags, SLE_UINT8)),
};
return _record_desc;
}
void DispatchRecordsStructHandlerBase::SaveDispatchRecords(btree::btree_map<uint16_t, LastDispatchRecord> &records) const
{
SlSetStructListLength(records.size());
for (RecordPair &it : records) {
SlObjectSaveFiltered(&it, this->GetLoadDescription());
}
}
void DispatchRecordsStructHandlerBase::LoadDispatchRecords(btree::btree_map<uint16_t, LastDispatchRecord> &records) const
{
size_t count = SlGetStructListLength(UINT32_MAX);
for (size_t i = 0; i < count; i++) {
RecordPair it{};
SlObjectLoadFiltered(&it, this->GetLoadDescription());
records.insert(it);
}
}
struct VehicleDispatchRecordsStructHandlerBase final : public DispatchRecordsStructHandlerBase {
void Save(void *object) const override { this->SaveDispatchRecords(static_cast<Vehicle *>(object)->dispatch_records); }
void Load(void *object) const override { this->LoadDispatchRecords(static_cast<Vehicle *>(object)->dispatch_records); }
};
/**
* Make it possible to make the saveload tables "friends" of other classes.
* @param vt the vehicle type. Can be VEH_END for the common vehicle description data
@ -1119,6 +1157,7 @@ NamedSaveLoadTable GetVehicleDescription(VehicleType vt)
NSL("", SLE_CONDNULL_X(160, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_JOKERPP))),
NSLT_STRUCT<VehicleUnbunchStateStructHandler>("depot_unbunch_state"),
NSLT_STRUCTLIST<VehicleDispatchRecordsStructHandlerBase>("dispatch_records"),
};
static const NamedSaveLoad _train_desc[] = {

24
src/sl/vehicle_sl.h Normal file
View File

@ -0,0 +1,24 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file vehicle_sl.h Code handling saving and loading of vehicles. */
#ifndef SL_VEHICLE_SL_H
#define SL_VEHICLE_SL_H
#include "../base_consist.h"
#include "saveload.h"
struct DispatchRecordsStructHandlerBase : public SaveLoadStructHandler {
using RecordPair = std::pair<const uint16_t, LastDispatchRecord>;
NamedSaveLoadTable GetDescription() const override;
void SaveDispatchRecords(btree::btree_map<uint16_t, LastDispatchRecord> &records) const;
void LoadDispatchRecords(btree::btree_map<uint16_t, LastDispatchRecord> &records) const;
};
#endif /* SL_NEWGRF_SL_H */

View File

@ -41,6 +41,6 @@ struct TimetableProgress {
std::vector<TimetableProgress> PopulateSeparationState(const Vehicle *v_start);
struct DispatchSchedule;
StateTicks GetScheduledDispatchTime(const DispatchSchedule &ds, StateTicks leave_time);
std::pair<StateTicks, int> GetScheduledDispatchTime(const DispatchSchedule &ds, StateTicks leave_time);
#endif /* TIMETABLE_H */

View File

@ -808,9 +808,11 @@ void UpdateSeparationOrder(Vehicle *v_start)
* Get next scheduled dispatch time
* @param ds Dispatch schedule.
* @param leave_time Leave time.
* @return Dispatch time, or INVALID_STATE_TICKS
* @return Pair of:
* * Dispatch time, or INVALID_STATE_TICKS
* * Index of departure slot, or -1
*/
StateTicks GetScheduledDispatchTime(const DispatchSchedule &ds, StateTicks leave_time)
std::pair<StateTicks, int> GetScheduledDispatchTime(const DispatchSchedule &ds, StateTicks leave_time)
{
const uint32_t dispatch_duration = ds.GetScheduledDispatchDuration();
const int32_t max_delay = ds.GetScheduledDispatchDelay();
@ -828,9 +830,13 @@ StateTicks GetScheduledDispatchTime(const DispatchSchedule &ds, StateTicks leave
}
StateTicks first_slot = INVALID_STATE_TICKS;
int first_slot_index = -1;
/* Find next available slots */
int slot_idx = 0;
for (const DispatchSlot &slot : ds.GetScheduledDispatch()) {
int this_slot = slot_idx++;
auto current_offset = slot.offset;
if (current_offset >= dispatch_duration) continue;
@ -847,10 +853,25 @@ StateTicks GetScheduledDispatchTime(const DispatchSchedule &ds, StateTicks leave
if (first_slot == INVALID_STATE_TICKS || first_slot > current_departure) {
first_slot = current_departure;
first_slot_index = this_slot;
}
}
return first_slot;
return std::make_pair(first_slot, first_slot_index);
}
LastDispatchRecord MakeLastDispatchRecord(const DispatchSchedule &ds, StateTicks slot, int slot_index)
{
uint8_t record_flags = 0;
if (slot_index == 0) SetBit(record_flags, LastDispatchRecord::RF_FIRST_SLOT);
if (slot_index == (int)(ds.GetScheduledDispatch().size() - 1)) SetBit(record_flags, LastDispatchRecord::RF_LAST_SLOT);
const DispatchSlot &dispatch_slot = ds.GetScheduledDispatch()[slot_index];
return {
slot,
dispatch_slot.offset,
dispatch_slot.flags,
record_flags,
};
}
/**
@ -896,7 +917,11 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling)
ds.UpdateScheduledDispatch(v);
const int wait_offset = real_current_order->GetTimetabledWait();
StateTicks slot = GetScheduledDispatchTime(ds, _state_ticks + wait_offset);
StateTicks slot;
int slot_index;
std::tie(slot, slot_index) = GetScheduledDispatchTime(ds, _state_ticks + wait_offset);
if (slot != INVALID_STATE_TICKS) {
just_started = !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
SetBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
@ -904,6 +929,7 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling)
ds.SetScheduledDispatchLastDispatch((slot - ds.GetScheduledDispatchStartTick()).AsTicks());
SetTimetableWindowsDirty(v, STWDF_SCHEDULED_DISPATCH);
set_scheduled_dispatch = true;
v->dispatch_records[static_cast<uint16_t>(real_implicit_order->GetDispatchScheduleIndex())] = MakeLastDispatchRecord(ds, slot, slot_index);
}
}
}

View File

@ -194,7 +194,7 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID
DispatchSchedule predicted_ds;
predicted_ds.BorrowSchedule(ds);
predicted_ds.UpdateScheduledDispatchToDate(_state_ticks + sum);
StateTicks slot = GetScheduledDispatchTime(predicted_ds, _state_ticks + sum + order->GetTimetabledWait());
StateTicks slot = GetScheduledDispatchTime(predicted_ds, _state_ticks + sum + order->GetTimetabledWait()).first;
predicted_ds.ReturnSchedule(ds);
if (slot == INVALID_STATE_TICKS) return;
sum = (slot - _state_ticks).AsTicks();

View File

@ -2255,6 +2255,7 @@ CommandCost CmdMoveRailVehicle(TileIndex tile, DoCommandFlag flags, uint32_t p1,
/* Remove stuff not valid anymore for non-front engines. */
DeleteVehicleOrders(src);
src->ReleaseUnitNumber();
src->dispatch_records.clear();
if (!_settings_game.vehicle.non_leading_engines_keep_name) {
src->name.clear();
}

View File

@ -4995,6 +4995,9 @@ void AdjustVehicleStateTicksBase(StateTicksDelta delta)
if (v->unbunch_state->depot_unbunching_last_departure != INVALID_STATE_TICKS) v->unbunch_state->depot_unbunching_last_departure += delta;
if (v->unbunch_state->depot_unbunching_next_departure != INVALID_STATE_TICKS) v->unbunch_state->depot_unbunching_next_departure += delta;
}
for (auto &it : v->dispatch_records) {
it.second.dispatched += delta;
}
}
for (OrderList *order_list : OrderList::Iterate()) {
@ -5002,6 +5005,12 @@ void AdjustVehicleStateTicksBase(StateTicksDelta delta)
ds.SetScheduledDispatchStartTick(ds.GetScheduledDispatchStartTick() + delta);
}
}
for (OrderBackup *ob : OrderBackup::Iterate()) {
for (auto &it : ob->dispatch_records) {
it.second.dispatched += delta;
}
}
}
void ShiftVehicleDates(DateDelta interval)