diff --git a/src/base_consist.h b/src/base_consist.h index f8a68faaa6..b89cddab55 100644 --- a/src/base_consist.h +++ b/src/base_consist.h @@ -11,7 +11,7 @@ #define BASE_CONSIST_H #include "order_type.h" -#include "timer/timer_game_calendar.h" +#include "timer/timer_game_tick.h" /** Various front vehicle properties that are preserved when autoreplacing, using order-backup or switching front engines within a consist. */ struct BaseConsist { @@ -20,7 +20,7 @@ struct BaseConsist { /* 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. - TimerGameCalendar::Date timetable_start; ///< When the vehicle is supposed to start the timetable. + TimerGameTick::TickCounter timetable_start; ///< At what tick of TimerGameTick::counter the vehicle should start its timetable. uint16_t service_interval; ///< The interval for (automatic) servicing; either in days or %. diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h index bde63020eb..5d993b928d 100644 --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -363,6 +363,7 @@ enum SaveLoadVersion : uint16_t { SLV_CARGO_TRAVELLED, ///< 319 PR#11283 CargoPacket now tracks how far it travelled inside a vehicle. SLV_STATION_RATING_CHEAT, ///< 320 PR#11346 Add cheat to fix station ratings at 100%. + SLV_TIMETABLE_START_TICKS, ///< 321 PR#11468 Convert timetable start from a date to ticks. SL_MAX_VERSION, ///< Highest possible saveload version }; diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp index 4ef24cf228..e0b7073ce6 100644 --- a/src/saveload/vehicle_sl.cpp +++ b/src/saveload/vehicle_sl.cpp @@ -17,6 +17,7 @@ #include "../roadveh.h" #include "../ship.h" #include "../aircraft.h" +#include "../timetable.h" #include "../station_base.h" #include "../effectvehicle_base.h" #include "../company_base.h" @@ -373,6 +374,16 @@ void AfterLoadVehicles(bool part_of_load) s->rotation_y_pos = s->y_pos; } } + + if (IsSavegameVersionBefore(SLV_TIMETABLE_START_TICKS)) { + /* Convert timetable start from a date to an absolute tick in TimerGameTick::counter. */ + for (Vehicle *v : Vehicle::Iterate()) { + /* If the start date is 0, the vehicle is not waiting to start and can be ignored. */ + if (v->timetable_start == 0) continue; + + v->timetable_start = GetStartTickFromDate(v->timetable_start); + } + } } CheckValidVehicles(); @@ -663,7 +674,8 @@ public: SLE_CONDVAR(Vehicle, current_order.wait_time, SLE_UINT16, SLV_67, SL_MAX_VERSION), SLE_CONDVAR(Vehicle, current_order.travel_time, SLE_UINT16, 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_U64, SLV_129, SLV_TIMETABLE_START_TICKS), + SLE_CONDVAR(Vehicle, timetable_start, SLE_UINT64, SLV_TIMETABLE_START_TICKS, 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/timetable.h b/src/timetable.h index 597d391da7..8aacf98483 100644 --- a/src/timetable.h +++ b/src/timetable.h @@ -16,6 +16,9 @@ static const TimerGameCalendar::Year MAX_TIMETABLE_START_YEARS = 15; ///< The maximum start date offset, in years. +TimerGameTick::TickCounter GetStartTickFromDate(TimerGameCalendar::Date start_date); +TimerGameCalendar::Date GetDateFromStartTick(TimerGameTick::TickCounter start_tick); + void ShowTimetableWindow(const Vehicle *v); void UpdateVehicleTimetable(Vehicle *v, bool travelling); void SetTimetableParams(int param1, int param2, TimerGameTick::Ticks ticks); diff --git a/src/timetable_cmd.cpp b/src/timetable_cmd.cpp index c1911bd49e..e36ea4a144 100644 --- a/src/timetable_cmd.cpp +++ b/src/timetable_cmd.cpp @@ -21,6 +21,40 @@ #include "safeguards.h" +/** + * Get the TimerGameTick::TickCounter tick of a given date. + * @param start_date The date when the timetable starts. + * @return The first tick of this date. + */ +TimerGameTick::TickCounter GetStartTickFromDate(TimerGameCalendar::Date start_date) +{ + /* Calculate the offset in ticks from the current date. */ + TimerGameTick::Ticks tick_offset = (start_date - TimerGameCalendar::date).base() * Ticks::DAY_TICKS; + + /* Compensate for the current date_fract. */ + tick_offset -= TimerGameCalendar::date_fract; + + /* Return the current tick plus the offset. */ + return TimerGameTick::counter + tick_offset; +} + +/** + * Get a date from a given start tick of timetable. + * @param start_tick The TimerGameTick::TickCounter when the timetable starts. + * @return The date when we reach this tick. + */ +TimerGameCalendar::Date GetDateFromStartTick(TimerGameTick::TickCounter start_tick) +{ + /* Calculate the offset in ticks from the current counter tick. */ + TimerGameTick::Ticks tick_offset = start_tick - TimerGameTick::counter; + + /* Compensate for the current date_fract. */ + tick_offset += TimerGameCalendar::date_fract; + + /* Return the current date plus the offset in days. */ + return TimerGameCalendar::date + (tick_offset / Ticks::DAY_TICKS); +} + /** * Change/update a particular timetable entry. * @param v The vehicle to change the timetable of. @@ -300,10 +334,10 @@ static bool VehicleTimetableSorter(Vehicle * const &a, Vehicle * const &b) * @param flags Operation to perform. * @param veh_id Vehicle ID. * @param timetable_all Set to set timetable start for all vehicles sharing this order - * @param start_date The timetable start date. + * @param start_tick The TimerGameTick::counter tick when the timetable starts. * @return The error or cost of the operation. */ -CommandCost CmdSetTimetableStart(DoCommandFlag flags, VehicleID veh_id, bool timetable_all, TimerGameCalendar::Date start_date) +CommandCost CmdSetTimetableStart(DoCommandFlag flags, VehicleID veh_id, bool timetable_all, TimerGameTick::TickCounter start_tick) { Vehicle *v = Vehicle::GetIfValid(veh_id); if (v == nullptr || !v->IsPrimaryVehicle() || v->orders == nullptr) return CMD_ERROR; @@ -313,6 +347,8 @@ CommandCost CmdSetTimetableStart(DoCommandFlag flags, VehicleID veh_id, bool tim TimerGameTick::Ticks total_duration = v->orders->GetTimetableTotalDuration(); + TimerGameCalendar::Date start_date = GetDateFromStartTick(start_tick); + /* Don't let a timetable start at an invalid date. */ if (start_date < 0 || start_date > CalendarTime::MAX_DATE) return CMD_ERROR; @@ -351,7 +387,7 @@ CommandCost CmdSetTimetableStart(DoCommandFlag flags, VehicleID veh_id, bool tim w->lateness_counter = 0; ClrBit(w->vehicle_flags, VF_TIMETABLE_STARTED); /* Do multiplication, then division to reduce rounding errors. */ - w->timetable_start = start_date + idx * total_duration / num_vehs / Ticks::DAY_TICKS; + w->timetable_start = start_tick + (idx * total_duration / num_vehs); SetWindowDirty(WC_VEHICLE_TIMETABLE, w->index); ++idx; } @@ -444,7 +480,7 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling) just_started = !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED); if (v->timetable_start != 0) { - v->lateness_counter = (TimerGameCalendar::date - v->timetable_start).base() * Ticks::DAY_TICKS + TimerGameCalendar::date_fract; + v->lateness_counter = TimerGameTick::counter - v->timetable_start; v->timetable_start = 0; } diff --git a/src/timetable_cmd.h b/src/timetable_cmd.h index 433f191196..43e76fa985 100644 --- a/src/timetable_cmd.h +++ b/src/timetable_cmd.h @@ -11,13 +11,13 @@ #define TIMETABLE_CMD_H #include "command_type.h" -#include "timer/timer_game_calendar.h" +#include "timer/timer_game_tick.h" CommandCost CmdChangeTimetable(DoCommandFlag flags, VehicleID veh, VehicleOrderID order_number, ModifyTimetableFlags mtf, uint16_t data); CommandCost CmdBulkChangeTimetable(DoCommandFlag flags, VehicleID veh, ModifyTimetableFlags mtf, uint16_t data); CommandCost CmdSetVehicleOnTime(DoCommandFlag flags, VehicleID veh, bool apply_to_group); CommandCost CmdAutofillTimetable(DoCommandFlag flags, VehicleID veh, bool autofill, bool preserve_wait_time); -CommandCost CmdSetTimetableStart(DoCommandFlag flags, VehicleID veh_id, bool timetable_all, TimerGameCalendar::Date start_date); +CommandCost CmdSetTimetableStart(DoCommandFlag flags, VehicleID veh_id, bool timetable_all, TimerGameTick::TickCounter start_tick); DEF_CMD_TRAIT(CMD_CHANGE_TIMETABLE, CmdChangeTimetable, 0, CMDT_ROUTE_MANAGEMENT) DEF_CMD_TRAIT(CMD_BULK_CHANGE_TIMETABLE, CmdBulkChangeTimetable, 0, CMDT_ROUTE_MANAGEMENT) diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp index c38b91f434..c2ac3ca415 100644 --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -145,7 +145,7 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID */ static void ChangeTimetableStartCallback(const Window *w, TimerGameCalendar::Date date, void *data) { - Command::Post(STR_ERROR_CAN_T_TIMETABLE_VEHICLE, (VehicleID)w->window_number, reinterpret_cast(data) != 0, date); + Command::Post(STR_ERROR_CAN_T_TIMETABLE_VEHICLE, (VehicleID)w->window_number, reinterpret_cast(data) != 0, GetStartTickFromDate(date)); } @@ -494,7 +494,7 @@ struct TimetableWindow : Window { /* We are running towards the first station so we can start the * timetable at the given time. */ SetDParam(0, STR_JUST_DATE_TINY); - SetDParam(1, v->timetable_start); + SetDParam(1, GetDateFromStartTick(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"