diff --git a/src/base_consist.cpp b/src/base_consist.cpp index 720f770c34..e2bec33111 100644 --- a/src/base_consist.cpp +++ b/src/base_consist.cpp @@ -28,7 +28,6 @@ void BaseConsist::CopyConsistPropertiesFrom(const BaseConsist *src) this->current_order_time = src->current_order_time; this->lateness_counter = src->lateness_counter; this->timetable_start = src->timetable_start; - this->timetable_start_subticks = src->timetable_start_subticks; this->service_interval = src->service_interval; diff --git a/src/base_consist.h b/src/base_consist.h index 29691cdc03..f769434ca5 100644 --- a/src/base_consist.h +++ b/src/base_consist.h @@ -22,8 +22,7 @@ struct BaseConsist { /* Used for timetabling. */ uint32 current_order_time; ///< How many ticks have passed since this order started. int32 lateness_counter; ///< How many ticks late (or early if negative) this vehicle is. - DateTicks timetable_start; ///< When the vehicle is supposed to start the timetable. - uint16 timetable_start_subticks; ///< When the vehicle is supposed to start the timetable: sub-ticks. + DateTicksScaled timetable_start; ///< When the vehicle is supposed to start the timetable. uint16 service_interval; ///< The interval for (automatic) servicing; either in days or %. diff --git a/src/command.cpp b/src/command.cpp index 2d906a94bd..af6cf5cd6c 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -263,7 +263,7 @@ CommandProc CmdSetVehicleOnTime; CommandProc CmdAutofillTimetable; CommandProc CmdAutomateTimetable; CommandProc CmdTimetableSeparation; -CommandProc CmdSetTimetableStart; +CommandProcEx CmdSetTimetableStart; CommandProc CmdOpenCloseAirport; diff --git a/src/date_func.h b/src/date_func.h index 874ba672f3..924f74e38c 100644 --- a/src/date_func.h +++ b/src/date_func.h @@ -70,12 +70,6 @@ static inline DateTicksScaled DateTicksToScaledDateTicks(DateTicks date_ticks) return ((int64)date_ticks * _settings_game.economy.day_length_factor) + _scaled_date_ticks_offset; } -static inline std::pair ScaledDateTicksToDateTicksAndSubTicks(DateTicksScaled ticks) -{ - ticks -= _scaled_date_ticks_offset; - return std::make_pair(ticks / _settings_game.economy.day_length_factor, ticks % _settings_game.economy.day_length_factor); -} - static inline std::pair ScaledDateTicksToDateAndFullSubTicks(DateTicksScaled ticks) { ticks -= _scaled_date_ticks_offset; diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index ce93899e51..1430904916 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -3592,18 +3592,24 @@ bool AfterLoadGame() } } - if (SlXvIsFeatureMissing(XSLFI_TIMETABLES_START_TICKS)) { - // savegame timetable start is in days, but we want it in ticks, fix it up + if (!SlXvIsFeaturePresent(XSLFI_TIMETABLES_START_TICKS, 3)) { + extern btree::btree_map _old_timetable_start_subticks_map; + for (Vehicle *v : Vehicle::Iterate()) { - if (v->timetable_start != 0) { + if (v->timetable_start == 0) continue; + + if (SlXvIsFeatureMissing(XSLFI_TIMETABLES_START_TICKS)) { v->timetable_start *= DAY_TICKS; } + + v->timetable_start = DateTicksToScaledDateTicks(v->timetable_start); + + if (SlXvIsFeaturePresent(XSLFI_TIMETABLES_START_TICKS, 2, 2)) { + v->timetable_start += _old_timetable_start_subticks_map[v->index]; + } } - } - if (!SlXvIsFeaturePresent(XSLFI_TIMETABLES_START_TICKS, 2)) { - for (Vehicle *v : Vehicle::Iterate()) { - v->timetable_start_subticks = 0; - } + + _old_timetable_start_subticks_map.clear(); } if (SlXvIsFeaturePresent(XSLFI_SPRINGPP, 1, 1)) { diff --git a/src/saveload/order_sl.cpp b/src/saveload/order_sl.cpp index d32657bf11..5ccc266f61 100644 --- a/src/saveload/order_sl.cpp +++ b/src/saveload/order_sl.cpp @@ -204,7 +204,7 @@ SaveLoadTable GetOrderBackupDescription() SLE_CONDVAR(OrderBackup, cur_implicit_order_index, SLE_FILE_U8 | SLE_VAR_U16, SLV_176, SL_MAX_VERSION), SLE_CONDVAR(OrderBackup, current_order_time, SLE_UINT32, SLV_176, SL_MAX_VERSION), SLE_CONDVAR(OrderBackup, lateness_counter, SLE_INT32, SLV_176, SL_MAX_VERSION), - SLE_CONDVAR(OrderBackup, timetable_start, SLE_INT32, SLV_176, SL_MAX_VERSION), + SLE_CONDVAR(OrderBackup, timetable_start, SLE_FILE_I32 | SLE_VAR_I64, SLV_176, SL_MAX_VERSION), SLE_CONDVAR(OrderBackup, vehicle_flags, SLE_FILE_U8 | SLE_VAR_U32, SLV_176, SLV_180), SLE_CONDVAR(OrderBackup, vehicle_flags, SLE_FILE_U16 | SLE_VAR_U32, SLV_180, SL_MAX_VERSION), SLE_REF(OrderBackup, orders, REF_ORDER), diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp index a462032512..16ba859412 100644 --- a/src/saveload/vehicle_sl.cpp +++ b/src/saveload/vehicle_sl.cpp @@ -130,7 +130,7 @@ public: SLE_CONDVAR(Vehicle, current_order.wait_time, SLE_FILE_U16 | SLE_VAR_U32, SLV_67, SL_MAX_VERSION), SLE_CONDVAR(Vehicle, current_order.travel_time, SLE_FILE_U16 | SLE_VAR_U32, SLV_67, SL_MAX_VERSION), SLE_CONDVAR(Vehicle, current_order.max_speed, SLE_UINT16, SLV_174, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, timetable_start, SLE_INT32, SLV_129, SL_MAX_VERSION), + SLE_CONDVAR(Vehicle, timetable_start, SLE_FILE_I32 | SLE_VAR_I64, SLV_129, SL_MAX_VERSION), SLE_CONDREF(Vehicle, orders, REF_ORDER, SL_MIN_VERSION, SLV_105), SLE_CONDREF(Vehicle, orders, REF_ORDERLIST, SLV_105, SL_MAX_VERSION), diff --git a/src/sl/extended_ver_sl.cpp b/src/sl/extended_ver_sl.cpp index 8071493939..d66831036e 100644 --- a/src/sl/extended_ver_sl.cpp +++ b/src/sl/extended_ver_sl.cpp @@ -93,7 +93,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { { XSLFI_ADJACENT_CROSSINGS, XSCF_NULL, 1, 1, "adjacent_crossings", nullptr, nullptr, nullptr }, { XSLFI_SAFER_CROSSINGS, XSCF_NULL, 1, 1, "safer_crossings", nullptr, nullptr, nullptr }, { XSLFI_DEPARTURE_BOARDS, XSCF_IGNORABLE_UNKNOWN, 1, 1, "departure_boards", nullptr, nullptr, nullptr }, - { XSLFI_TIMETABLES_START_TICKS, XSCF_NULL, 2, 2, "timetable_start_ticks", nullptr, nullptr, nullptr }, + { XSLFI_TIMETABLES_START_TICKS, XSCF_NULL, 3, 3, "timetable_start_ticks", nullptr, nullptr, nullptr }, { XSLFI_TOWN_CARGO_ADJ, XSCF_IGNORABLE_UNKNOWN, 2, 2, "town_cargo_adj", nullptr, nullptr, nullptr }, { XSLFI_SIG_TUNNEL_BRIDGE, XSCF_NULL, 10, 10, "signal_tunnel_bridge", nullptr, nullptr, "XBSS" }, { XSLFI_IMPROVED_BREAKDOWNS, XSCF_NULL, 8, 8, "improved_breakdowns", nullptr, nullptr, nullptr }, diff --git a/src/sl/extended_ver_sl.h b/src/sl/extended_ver_sl.h index 1f6b17c874..d21334940a 100644 --- a/src/sl/extended_ver_sl.h +++ b/src/sl/extended_ver_sl.h @@ -42,7 +42,7 @@ enum SlXvFeatureIndex { XSLFI_ADJACENT_CROSSINGS, ///< Adjacent level crossings closure patch XSLFI_SAFER_CROSSINGS, ///< Safer level crossings XSLFI_DEPARTURE_BOARDS, ///< Departure boards patch, in ticks mode - XSLFI_TIMETABLES_START_TICKS, ///< Timetable start time is in ticks, instead of days (from departure boards patch) + XSLFI_TIMETABLES_START_TICKS, ///< Timetable start time format: 1: is in ticks, instead of days, 2: also has subticks, 3: uses DateTicksScaled XSLFI_TOWN_CARGO_ADJ, ///< Town cargo adjustment patch XSLFI_SIG_TUNNEL_BRIDGE, ///< Signals on tunnels and bridges XSLFI_IMPROVED_BREAKDOWNS, ///< Improved breakdowns patch diff --git a/src/sl/order_sl.cpp b/src/sl/order_sl.cpp index 0cd843929e..eef12f6cb8 100644 --- a/src/sl/order_sl.cpp +++ b/src/sl/order_sl.cpp @@ -350,8 +350,9 @@ SaveLoadTable GetOrderBackupDescription() SLE_CONDVAR_X(OrderBackup, cur_timetable_order_index, SLE_VEHORDERID, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA)), SLE_CONDVAR(OrderBackup, current_order_time, SLE_UINT32, SLV_176, SL_MAX_VERSION), SLE_CONDVAR(OrderBackup, lateness_counter, SLE_INT32, SLV_176, SL_MAX_VERSION), - SLE_CONDVAR(OrderBackup, timetable_start, SLE_INT32, SLV_176, SL_MAX_VERSION), - SLE_CONDVAR_X(OrderBackup,timetable_start_subticks, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLES_START_TICKS, 2)), + SLE_CONDVAR_X(OrderBackup, timetable_start, SLE_FILE_I32 | SLE_VAR_I64, SLV_176, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLES_START_TICKS, 0, 2)), + SLE_CONDVAR_X(OrderBackup, timetable_start, SLE_INT64, SLV_176, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLES_START_TICKS, 3)), + SLE_CONDNULL_X(2, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLES_START_TICKS, 2, 2)), SLE_CONDVAR(OrderBackup, vehicle_flags, SLE_FILE_U8 | SLE_VAR_U32, SLV_176, SLV_180), SLE_CONDVAR_X(OrderBackup, vehicle_flags, SLE_FILE_U16 | SLE_VAR_U32, SLV_180, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VEHICLE_FLAGS_EXTRA, 0, 0)), SLE_CONDVAR_X(OrderBackup, vehicle_flags, SLE_UINT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_VEHICLE_FLAGS_EXTRA, 1)), diff --git a/src/sl/vehicle_sl.cpp b/src/sl/vehicle_sl.cpp index ba1c0c7136..287fd25d03 100644 --- a/src/sl/vehicle_sl.cpp +++ b/src/sl/vehicle_sl.cpp @@ -648,6 +648,9 @@ static std::vector _path_tile; static uint32 _path_layout_ctr; static uint32 _old_ahead_separation; +static uint16 _old_timetable_start_subticks; + +btree::btree_map _old_timetable_start_subticks_map; /** * Make it possible to make the saveload tables "friends" of other classes. @@ -746,9 +749,10 @@ SaveLoadTable GetVehicleDescription(VehicleType vt) SLE_CONDVAR_X(Vehicle, current_order.wait_time, SLE_UINT32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 6)), SLE_CONDVAR_X(Vehicle, current_order.travel_time, SLE_FILE_U16 | SLE_VAR_U32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 0, 5)), SLE_CONDVAR_X(Vehicle, current_order.travel_time, SLE_UINT32, SLV_67, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLE_EXTRA, 6)), - SLE_CONDVAR(Vehicle, current_order.max_speed, SLE_UINT16, SLV_174, SL_MAX_VERSION), - SLE_CONDVAR(Vehicle, timetable_start, SLE_INT32, SLV_129, SL_MAX_VERSION), - SLE_CONDVAR_X(Vehicle, timetable_start_subticks, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLES_START_TICKS, 2)), + SLE_CONDVAR(Vehicle, current_order.max_speed, SLE_UINT16, SLV_174, SL_MAX_VERSION), + SLE_CONDVAR_X(Vehicle, timetable_start, SLE_FILE_I32 | SLE_VAR_I64, SLV_129, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLES_START_TICKS, 0, 2)), + SLE_CONDVAR_X(Vehicle, timetable_start, SLE_INT64, SLV_129, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLES_START_TICKS, 3)), + SLEG_CONDVAR_X(_old_timetable_start_subticks, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLES_START_TICKS, 2, 2)), SLE_CONDREF(Vehicle, orders, REF_ORDER, SL_MIN_VERSION, SLV_105), SLE_CONDREF(Vehicle, orders, REF_ORDERLIST, SLV_105, SL_MAX_VERSION), @@ -1076,6 +1080,9 @@ void Load_VEHS() _path_tile.clear(); _path_layout_ctr = 0; + _old_timetable_start_subticks = 0; + _old_timetable_start_subticks_map.clear(); + while ((index = SlIterateArray()) != -1) { Vehicle *v; VehicleType vtype = (VehicleType)SlReadByte(); @@ -1125,6 +1132,10 @@ void Load_VEHS() SB(v->vehicle_flags, VF_SEPARATION_ACTIVE, 1, _old_ahead_separation ? 1 : 0); } + if (SlXvIsFeaturePresent(XSLFI_TIMETABLES_START_TICKS, 2, 2) && v->timetable_start != 0 && _old_timetable_start_subticks != 0) { + _old_timetable_start_subticks_map[v->index] = _old_timetable_start_subticks; + } + if (vtype == VEH_ROAD && !_path_td.empty() && _path_td.size() <= RV_PATH_CACHE_SEGMENTS && _path_td.size() == _path_tile.size()) { RoadVehicle *rv = RoadVehicle::From(v); rv->cached_path.reset(new RoadVehPathCache()); diff --git a/src/timetable_cmd.cpp b/src/timetable_cmd.cpp index 875c60220f..22f7db42c6 100644 --- a/src/timetable_cmd.cpp +++ b/src/timetable_cmd.cpp @@ -473,25 +473,27 @@ static bool VehicleTimetableSorter(Vehicle * const &a, Vehicle * const &b) * @param p1 Various bitstuffed elements * - p1 = (bit 0-19) - Vehicle ID. * - p1 = (bit 20) - Set to 1 to set timetable start for all vehicles sharing this order - * - p1 = (bit 21-31)- Timetable start date: sub-ticks - * @param p2 The timetable start date. + * @param p3 The timetable start ticks. * @param text Not used. * @return The error or cost of the operation. */ -CommandCost CmdSetTimetableStart(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) +CommandCost CmdSetTimetableStart(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, uint64 p3, const char *text, const CommandAuxiliaryBase *aux_data) { bool timetable_all = HasBit(p1, 20); Vehicle *v = Vehicle::GetIfValid(GB(p1, 0, 20)); - uint16 sub_ticks = GB(p1, 21, 11); if (v == nullptr || !v->IsPrimaryVehicle() || v->orders == nullptr) return CMD_ERROR; CommandCost ret = CheckOwnership(v->owner); if (ret.Failed()) return ret; - if (timetable_all && !v->orders->IsCompleteTimetable()) return CommandCost(STR_ERROR_TIMETABLE_INCOMPLETE); + DateTicksScaled start_date_scaled = (DateTicksScaled)p3; + + /* Don't let a timetable start more than 15 unscaled years into the future... */ + if (start_date_scaled - _scaled_date_ticks > 15 * DAY_TICKS * DAYS_IN_LEAP_YEAR) return CMD_ERROR; + /* ...or 1 unscaled year in the past. */ + if (_scaled_date_ticks - start_date_scaled > DAY_TICKS * DAYS_IN_LEAP_YEAR) return CMD_ERROR; - const DateTicksScaled now = _scaled_date_ticks; - DateTicksScaled start_date_scaled = DateTicksToScaledDateTicks(_date * DAY_TICKS + _date_fract + (int32)p2) + sub_ticks; + if (timetable_all && !v->orders->IsCompleteTimetable()) return CommandCost(STR_ERROR_TIMETABLE_INCOMPLETE); if (flags & DC_EXEC) { std::vector vehs; @@ -519,11 +521,7 @@ CommandCost CmdSetTimetableStart(TileIndex tile, DoCommandFlag flags, uint32 p1, w->lateness_counter = 0; ClrBit(w->vehicle_flags, VF_TIMETABLE_STARTED); /* Do multiplication, then division to reduce rounding errors. */ - DateTicksScaled tt_start = start_date_scaled + ((idx * total_duration) / num_vehs); - if (tt_start < now && idx < 0) { - tt_start += total_duration; - } - std::tie(w->timetable_start, w->timetable_start_subticks) = ScaledDateTicksToDateTicksAndSubTicks(tt_start); + w->timetable_start = start_date_scaled + ((idx * total_duration) / num_vehs); ++idx; } @@ -568,7 +566,6 @@ CommandCost CmdAutofillTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, if (HasBit(p2, 1)) SetBit(v->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME); v->timetable_start = 0; - v->timetable_start_subticks = 0; v->lateness_counter = 0; } else { ClrBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE); @@ -618,7 +615,6 @@ CommandCost CmdAutomateTimetable(TileIndex index, DoCommandFlag flags, uint32 p1 ClrBit(v2->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME); ClrBit(v2->vehicle_flags, VF_TIMETABLE_STARTED); v2->timetable_start = 0; - v2->timetable_start_subticks = 0; v2->lateness_counter = 0; v2->current_loading_time = 0; v2->ClearSeparation(); @@ -894,9 +890,8 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling) if (!set_scheduled_dispatch) just_started = !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED); if (v->timetable_start != 0) { - v->lateness_counter = _scaled_date_ticks - (DateTicksToScaledDateTicks(v->timetable_start) + v->timetable_start_subticks); + v->lateness_counter = (int32)(_scaled_date_ticks - v->timetable_start); v->timetable_start = 0; - v->timetable_start_subticks = 0; } SetBit(v->vehicle_flags, VF_TIMETABLE_STARTED); diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp index 87c3694d56..0b43c45b52 100644 --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -239,10 +239,7 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID */ static void ChangeTimetableStartIntl(uint32 p1, DateTicksScaled date) { - DateTicks date_part; - uint16 sub_ticks; - std::tie(date_part, sub_ticks) = ScaledDateTicksToDateTicksAndSubTicks(date); - DoCommandP(0, p1 | (sub_ticks << 21), (Ticks)(date_part - (((DateTicks)_date * DAY_TICKS) + _date_fract)), CMD_SET_TIMETABLE_START | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); + DoCommandPEx(0, p1, 0, (uint64)date, CMD_SET_TIMETABLE_START | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE), nullptr, nullptr, 0); } /** @@ -801,7 +798,7 @@ struct TimetableWindow : GeneralVehicleWindow { /* We are running towards the first station so we can start the * timetable at the given time. */ SetDParam(0, STR_JUST_DATE_WALLCLOCK_TINY); - SetDParam(1, DateTicksToScaledDateTicks(v->timetable_start) + v->timetable_start_subticks); + SetDParam(1, v->timetable_start); DrawString(tr, STR_TIMETABLE_STATUS_START_AT); } else if (!HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED)) { /* We aren't running on a timetable yet, so how can we be "on time" diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 66e728330f..04c7deeea1 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -4652,7 +4652,7 @@ void DumpVehicleStats(char *buffer, const char *last) void AdjustVehicleScaledTickBase(int64 delta) { for (Vehicle *v : Vehicle::Iterate()) { - v->last_loading_tick += delta; + if (v->timetable_start != 0) v->timetable_start += delta; } } @@ -4669,14 +4669,6 @@ extern void VehicleDayLengthChanged(DateTicksScaled old_scaled_date_ticks, DateT { if (_settings_game.economy.day_length_factor == old_day_length_factor || !_settings_game.game_time.time_in_minutes) return; - for (Vehicle *v : Vehicle::Iterate()) { - if (v->timetable_start != 0) { - DateTicksScaled tt_start = ((int64)v->timetable_start * old_day_length_factor) + v->timetable_start_subticks + old_scaled_date_ticks_offset; - tt_start += (_scaled_date_ticks - old_scaled_date_ticks); - std::tie(v->timetable_start, v->timetable_start_subticks) = ScaledDateTicksToDateTicksAndSubTicks(tt_start); - } - } - for (OrderList *orderlist : OrderList::Iterate()) { for (DispatchSchedule &ds : orderlist->GetScheduledDispatchScheduleSet()) { if (ds.GetScheduledDispatchStartDatePart() >= 0) {