diff --git a/src/base_consist.cpp b/src/base_consist.cpp index 200512786c..69ad9c9d3e 100644 --- a/src/base_consist.cpp +++ b/src/base_consist.cpp @@ -35,6 +35,7 @@ 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 5376c3bf4d..6877539d9a 100644 --- a/src/base_consist.h +++ b/src/base_consist.h @@ -24,6 +24,7 @@ struct BaseConsist { 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. uint16 service_interval; ///< The interval for (automatic) servicing; either in days or %. diff --git a/src/date_gui.cpp b/src/date_gui.cpp index 330631bcae..f53b102537 100644 --- a/src/date_gui.cpp +++ b/src/date_gui.cpp @@ -148,7 +148,10 @@ struct SetDateWindow : Window { ShowDateDropDown(widget); break; case WID_SD_SET_DATE: - if (this->callback != NULL) this->callback(this, ConvertYMDToDate(this->date.year, this->date.month, this->date.day) * DAY_TICKS); + if (this->callback != NULL) { + this->callback(this, ConvertYMDToDate(this->date.year, this->date.month, this->date.day) + * DAY_TICKS * _settings_game.economy.day_length_factor); + } delete this; break; } @@ -178,9 +181,9 @@ struct SetMinutesWindow : SetDateWindow Minutes minutes; /** Constructor. */ - SetMinutesWindow(WindowDesc *desc, WindowNumber window_number, Window *parent, DateTicks initial_date, Year min_year, Year max_year, SetDateCallback *callback) : + SetMinutesWindow(WindowDesc *desc, WindowNumber window_number, Window *parent, DateTicksScaled initial_date, Year min_year, Year max_year, SetDateCallback *callback) : SetDateWindow(desc, window_number, parent, initial_date, min_year, max_year, callback), - minutes(initial_date * _settings_game.economy.day_length_factor / _settings_client.gui.ticks_per_minute) + minutes(initial_date / _settings_client.gui.ticks_per_minute) { } @@ -264,8 +267,7 @@ struct SetMinutesWindow : SetDateWindow case WID_SD_SET_DATE: if (this->callback != NULL) { - this->callback(this, (((DateTicks)minutes - _settings_client.gui.clock_offset) * _settings_client.gui.ticks_per_minute) - / _settings_game.economy.day_length_factor); + this->callback(this, ((DateTicks)minutes - _settings_client.gui.clock_offset) * _settings_client.gui.ticks_per_minute); } delete this; break; @@ -358,13 +360,15 @@ static WindowDesc _set_minutes_desc( * @param max_year the maximum year (inclusive) to show in the year dropdown * @param callback the callback to call once a date has been selected */ -void ShowSetDateWindow(Window *parent, int window_number, DateTicks initial_date, Year min_year, Year max_year, SetDateCallback *callback) +void ShowSetDateWindow(Window *parent, int window_number, DateTicksScaled initial_date, Year min_year, Year max_year, SetDateCallback *callback) { DeleteWindowByClass(WC_SET_DATE); if (!_settings_client.gui.time_in_minutes) { - new SetDateWindow(&_set_date_desc, window_number, parent, initial_date / DAY_TICKS, min_year, max_year, callback); + new SetDateWindow(&_set_date_desc, window_number, parent, initial_date / (DAY_TICKS * _settings_game.economy.day_length_factor), min_year, max_year, callback); } else { - new SetMinutesWindow(&_set_minutes_desc, window_number, parent, initial_date + (_settings_client.gui.clock_offset * _settings_client.gui.ticks_per_minute), min_year, max_year, callback); + new SetMinutesWindow(&_set_minutes_desc, window_number, parent, + initial_date + (_settings_game.economy.day_length_factor * (_settings_client.gui.clock_offset * _settings_client.gui.ticks_per_minute)), + min_year, max_year, callback); } } diff --git a/src/date_gui.h b/src/date_gui.h index 0c8dcba865..91ea684a58 100644 --- a/src/date_gui.h +++ b/src/date_gui.h @@ -20,8 +20,8 @@ * @param w the window that sends the callback * @param date the date that has been chosen */ -typedef void SetDateCallback(const Window *w, DateTicks date); +typedef void SetDateCallback(const Window *w, DateTicksScaled date); -void ShowSetDateWindow(Window *parent, int window_number, DateTicks initial_date, Year min_year, Year max_year, SetDateCallback *callback); +void ShowSetDateWindow(Window *parent, int window_number, DateTicksScaled initial_date, Year min_year, Year max_year, SetDateCallback *callback); #endif /* DATE_GUI_H */ diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 3bda6c2528..676111eb76 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -3113,6 +3113,12 @@ bool AfterLoadGame() } } } + if (!SlXvIsFeaturePresent(XSLFI_TIMETABLES_START_TICKS, 2)) { + Vehicle *v; + FOR_ALL_VEHICLES(v) { + v->timetable_start_subticks = 0; + } + } if (SlXvIsFeaturePresent(XSLFI_SPRINGPP, 1, 1)) { /* diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 577c132275..2346ebb767 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -52,7 +52,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { { XSLFI_PROG_SIGS, XSCF_NULL, 1, 1, "programmable_signals", NULL, NULL, "SPRG" }, { XSLFI_ADJACENT_CROSSINGS, XSCF_NULL, 1, 1, "adjacent_crossings", NULL, NULL, NULL }, { XSLFI_DEPARTURE_BOARDS, XSCF_IGNORABLE_UNKNOWN, 1, 1, "departure_boards", NULL, NULL, NULL }, - { XSLFI_TIMETABLES_START_TICKS, XSCF_NULL, 1, 1, "timetable_start_ticks", NULL, NULL, NULL }, + { XSLFI_TIMETABLES_START_TICKS, XSCF_NULL, 2, 2, "timetable_start_ticks", NULL, NULL, NULL }, { XSLFI_TOWN_CARGO_ADJ, XSCF_IGNORABLE_UNKNOWN, 1, 1, "town_cargo_adj", NULL, NULL, NULL }, { XSLFI_SIG_TUNNEL_BRIDGE, XSCF_NULL, 2, 2, "signal_tunnel_bridge", NULL, NULL, NULL }, { XSLFI_IMPROVED_BREAKDOWNS, XSCF_NULL, 3, 3, "improved_breakdowns", NULL, NULL, NULL }, diff --git a/src/saveload/order_sl.cpp b/src/saveload/order_sl.cpp index f45346b183..08f9289697 100644 --- a/src/saveload/order_sl.cpp +++ b/src/saveload/order_sl.cpp @@ -266,6 +266,7 @@ const SaveLoad *GetOrderBackupDescription() SLE_CONDVAR(OrderBackup, current_order_time, SLE_UINT32, 176, SL_MAX_VERSION), SLE_CONDVAR(OrderBackup, lateness_counter, SLE_INT32, 176, SL_MAX_VERSION), SLE_CONDVAR(OrderBackup, timetable_start, SLE_INT32, 176, SL_MAX_VERSION), + SLE_CONDVAR_X(OrderBackup,timetable_start_subticks, SLE_UINT16, 0, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLES_START_TICKS, 2)), SLE_CONDVAR(OrderBackup, vehicle_flags, SLE_FILE_U8 | SLE_VAR_U16, 176, 179), SLE_CONDVAR(OrderBackup, vehicle_flags, SLE_UINT16, 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 77043ee8b0..20eb0cc59c 100644 --- a/src/saveload/vehicle_sl.cpp +++ b/src/saveload/vehicle_sl.cpp @@ -673,6 +673,7 @@ const SaveLoad *GetVehicleDescription(VehicleType vt) SLE_CONDVAR(Vehicle, current_order.travel_time, SLE_UINT16, 67, SL_MAX_VERSION), SLE_CONDVAR(Vehicle, current_order.max_speed, SLE_UINT16, 174, SL_MAX_VERSION), SLE_CONDVAR(Vehicle, timetable_start, SLE_INT32, 129, SL_MAX_VERSION), + SLE_CONDVAR_X(Vehicle, timetable_start_subticks, SLE_UINT16, 0, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_TIMETABLES_START_TICKS, 2)), SLE_CONDREF(Vehicle, orders, REF_ORDER, 0, 104), SLE_CONDREF(Vehicle, orders, REF_ORDERLIST, 105, SL_MAX_VERSION), diff --git a/src/timetable_cmd.cpp b/src/timetable_cmd.cpp index 9eee2df554..980f14181e 100644 --- a/src/timetable_cmd.cpp +++ b/src/timetable_cmd.cpp @@ -315,6 +315,7 @@ static int CDECL VehicleTimetableSorter(Vehicle * const *ap, Vehicle * const *bp * @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 text Not used. * @return The error or cost of the operation. @@ -323,6 +324,7 @@ CommandCost CmdSetTimetableStart(TileIndex tile, DoCommandFlag flags, uint32 p1, { bool timetable_all = HasBit(p1, 20); Vehicle *v = Vehicle::GetIfValid(GB(p1, 0, 20)); + uint16 sub_ticks = GB(p1, 21, 11); if (v == NULL || !v->IsPrimaryVehicle() || v->orders.list == NULL) return CMD_ERROR; CommandCost ret = CheckOwnership(v->owner); @@ -330,7 +332,8 @@ CommandCost CmdSetTimetableStart(TileIndex tile, DoCommandFlag flags, uint32 p1, if (timetable_all && !v->orders.list->IsCompleteTimetable()) return CMD_ERROR; - DateTicks start_date = ((DateTicks)_date * DAY_TICKS) + _date_fract + (DateTicks)(int32)p2; + const DateTicksScaled now = CURRENT_SCALED_TICKS; + DateTicksScaled start_date_scaled = (_settings_game.economy.day_length_factor * (((DateTicks)_date * DAY_TICKS) + _date_fract + (DateTicks)(int32)p2)) + sub_ticks; if (flags & DC_EXEC) { SmallVector vehs; @@ -359,10 +362,12 @@ 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. */ - w->timetable_start = start_date + idx * total_duration / (num_vehs * _settings_game.economy.day_length_factor); - if (w->timetable_start < (((DateTicks)_date * DAY_TICKS) + _date_fract) && idx < 0) { - w->timetable_start += (total_duration / _settings_game.economy.day_length_factor); + DateTicksScaled tt_start = start_date_scaled + ((idx * total_duration) / num_vehs); + if (tt_start < now && idx < 0) { + tt_start += total_duration; } + w->timetable_start = tt_start / _settings_game.economy.day_length_factor; + w->timetable_start_subticks = tt_start % _settings_game.economy.day_length_factor; SetWindowDirty(WC_VEHICLE_TIMETABLE, w->index); } @@ -407,6 +412,7 @@ 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); @@ -457,6 +463,7 @@ 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; + v->timetable_start_subticks = 0; v2->lateness_counter = 0; v2->current_loading_time = 0; v2->ClearSeparation(); @@ -470,6 +477,7 @@ CommandCost CmdAutomateTimetable(TileIndex index, DoCommandFlag flags, uint32 p1 /* Ctrl wasn't pressed, so clear all timetabled times. */ SetBit(v2->vehicle_flags, VF_TIMETABLE_STARTED); v2->timetable_start = 0; + v->timetable_start_subticks = 0; v2->lateness_counter = 0; v2->current_loading_time = 0; OrderList *orders = v2->orders.list; @@ -690,8 +698,9 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling) just_started = !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED); if (v->timetable_start != 0) { - v->lateness_counter = ((_date * DAY_TICKS) + _date_fract - v->timetable_start) * _settings_game.economy.day_length_factor + _tick_skip_counter; + v->lateness_counter = CURRENT_SCALED_TICKS - ((_settings_game.economy.day_length_factor * v->timetable_start) + v->timetable_start_subticks); 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 1d688bfbee..dd6fe3355c 100644 --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -152,9 +152,11 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID * @param p1 The p1 parameter to send to CmdSetTimetableStart * @param date the actually chosen date */ -static void ChangeTimetableStartIntl(uint32 p1, DateTicks date) +static void ChangeTimetableStartIntl(uint32 p1, DateTicksScaled date) { - DoCommandP(0, p1, (Ticks)(date - (((DateTicks)_date * DAY_TICKS) + _date_fract)), CMD_SET_TIMETABLE_START | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); + DateTicks date_part = date / _settings_game.economy.day_length_factor; + uint32 sub_ticks = date % _settings_game.economy.day_length_factor; + 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)); } /** @@ -162,7 +164,7 @@ static void ChangeTimetableStartIntl(uint32 p1, DateTicks date) * @param window the window related to the setting of the date * @param date the actually chosen date */ -static void ChangeTimetableStartCallback(const Window *w, DateTicks date) +static void ChangeTimetableStartCallback(const Window *w, DateTicksScaled date) { ChangeTimetableStartIntl(w->window_number, date); } @@ -524,7 +526,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_WALLCLOCK_TINY); - SetDParam(1, v->timetable_start * _settings_game.economy.day_length_factor); + SetDParam(1, (v->timetable_start * _settings_game.economy.day_length_factor) + v->timetable_start_subticks); DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, 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" @@ -583,7 +585,7 @@ struct TimetableWindow : Window { ShowQueryString(str, STR_TIMETABLE_STARTING_DATE, 31, this, CS_NUMERAL, QSF_ACCEPT_UNCHANGED); } else { ShowSetDateWindow(this, v->index | (v->orders.list->IsCompleteTimetable() && _ctrl_pressed ? 1U << 20 : 0), - ((DateTicks)_date * DAY_TICKS) + _date_fract, _cur_year, _cur_year + 15, ChangeTimetableStartCallback); + CURRENT_SCALED_TICKS, _cur_year, _cur_year + 15, ChangeTimetableStartCallback); } break; @@ -720,7 +722,6 @@ struct TimetableWindow : Window { if (val < (CURRENT_MINUTE - 60)) val += 60 * 24; val *= _settings_client.gui.ticks_per_minute; - val /= _settings_game.economy.day_length_factor; ChangeTimetableStartIntl(v->index | (this->set_start_date_all ? 1 << 20 : 0), val); } break;