Allow timetable start dates to be set with full precision.

This avoids rounding of start dates when using minutes and
day length > 1.
This commit is contained in:
Jonathan G Rennison
2016-05-11 21:31:31 +01:00
parent f82673bed8
commit d8a083b666
10 changed files with 46 additions and 22 deletions

View File

@@ -35,6 +35,7 @@ void BaseConsist::CopyConsistPropertiesFrom(const BaseConsist *src)
this->current_order_time = src->current_order_time; this->current_order_time = src->current_order_time;
this->lateness_counter = src->lateness_counter; this->lateness_counter = src->lateness_counter;
this->timetable_start = src->timetable_start; this->timetable_start = src->timetable_start;
this->timetable_start_subticks = src->timetable_start_subticks;
this->service_interval = src->service_interval; this->service_interval = src->service_interval;

View File

@@ -24,6 +24,7 @@ struct BaseConsist {
uint32 current_order_time; ///< How many ticks have passed since this order started. 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. 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. 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 %. uint16 service_interval; ///< The interval for (automatic) servicing; either in days or %.

View File

@@ -148,7 +148,10 @@ struct SetDateWindow : Window {
ShowDateDropDown(widget); ShowDateDropDown(widget);
break; break;
case WID_SD_SET_DATE: 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; delete this;
break; break;
} }
@@ -178,9 +181,9 @@ struct SetMinutesWindow : SetDateWindow
Minutes minutes; Minutes minutes;
/** Constructor. */ /** 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), 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: case WID_SD_SET_DATE:
if (this->callback != NULL) { if (this->callback != NULL) {
this->callback(this, (((DateTicks)minutes - _settings_client.gui.clock_offset) * _settings_client.gui.ticks_per_minute) this->callback(this, ((DateTicks)minutes - _settings_client.gui.clock_offset) * _settings_client.gui.ticks_per_minute);
/ _settings_game.economy.day_length_factor);
} }
delete this; delete this;
break; break;
@@ -358,13 +360,15 @@ static WindowDesc _set_minutes_desc(
* @param max_year the maximum year (inclusive) to show in the year dropdown * @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 * @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); DeleteWindowByClass(WC_SET_DATE);
if (!_settings_client.gui.time_in_minutes) { 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 { } 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);
} }
} }

View File

@@ -20,8 +20,8 @@
* @param w the window that sends the callback * @param w the window that sends the callback
* @param date the date that has been chosen * @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 */ #endif /* DATE_GUI_H */

View File

@@ -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)) { if (SlXvIsFeaturePresent(XSLFI_SPRINGPP, 1, 1)) {
/* /*

View File

@@ -52,7 +52,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_PROG_SIGS, XSCF_NULL, 1, 1, "programmable_signals", NULL, NULL, "SPRG" }, { 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_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_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_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_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 }, { XSLFI_IMPROVED_BREAKDOWNS, XSCF_NULL, 3, 3, "improved_breakdowns", NULL, NULL, NULL },

View File

@@ -266,6 +266,7 @@ const SaveLoad *GetOrderBackupDescription()
SLE_CONDVAR(OrderBackup, current_order_time, SLE_UINT32, 176, SL_MAX_VERSION), 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, lateness_counter, SLE_INT32, 176, SL_MAX_VERSION),
SLE_CONDVAR(OrderBackup, timetable_start, 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_FILE_U8 | SLE_VAR_U16, 176, 179),
SLE_CONDVAR(OrderBackup, vehicle_flags, SLE_UINT16, 180, SL_MAX_VERSION), SLE_CONDVAR(OrderBackup, vehicle_flags, SLE_UINT16, 180, SL_MAX_VERSION),
SLE_REF(OrderBackup, orders, REF_ORDER), SLE_REF(OrderBackup, orders, REF_ORDER),

View File

@@ -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.travel_time, SLE_UINT16, 67, SL_MAX_VERSION),
SLE_CONDVAR(Vehicle, current_order.max_speed, SLE_UINT16, 174, 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(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_ORDER, 0, 104),
SLE_CONDREF(Vehicle, orders, REF_ORDERLIST, 105, SL_MAX_VERSION), SLE_CONDREF(Vehicle, orders, REF_ORDERLIST, 105, SL_MAX_VERSION),

View File

@@ -315,6 +315,7 @@ static int CDECL VehicleTimetableSorter(Vehicle * const *ap, Vehicle * const *bp
* @param p1 Various bitstuffed elements * @param p1 Various bitstuffed elements
* - p1 = (bit 0-19) - Vehicle ID. * - p1 = (bit 0-19) - Vehicle ID.
* - p1 = (bit 20) - Set to 1 to set timetable start for all vehicles sharing this order * - 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 p2 The timetable start date.
* @param text Not used. * @param text Not used.
* @return The error or cost of the operation. * @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); bool timetable_all = HasBit(p1, 20);
Vehicle *v = Vehicle::GetIfValid(GB(p1, 0, 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; if (v == NULL || !v->IsPrimaryVehicle() || v->orders.list == NULL) return CMD_ERROR;
CommandCost ret = CheckOwnership(v->owner); 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; 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) { if (flags & DC_EXEC) {
SmallVector<Vehicle *, 8> vehs; SmallVector<Vehicle *, 8> vehs;
@@ -359,10 +362,12 @@ CommandCost CmdSetTimetableStart(TileIndex tile, DoCommandFlag flags, uint32 p1,
w->lateness_counter = 0; w->lateness_counter = 0;
ClrBit(w->vehicle_flags, VF_TIMETABLE_STARTED); ClrBit(w->vehicle_flags, VF_TIMETABLE_STARTED);
/* Do multiplication, then division to reduce rounding errors. */ /* Do multiplication, then division to reduce rounding errors. */
w->timetable_start = start_date + idx * total_duration / (num_vehs * _settings_game.economy.day_length_factor); DateTicksScaled tt_start = start_date_scaled + ((idx * total_duration) / num_vehs);
if (w->timetable_start < (((DateTicks)_date * DAY_TICKS) + _date_fract) && idx < 0) { if (tt_start < now && idx < 0) {
w->timetable_start += (total_duration / _settings_game.economy.day_length_factor); 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); 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); if (HasBit(p2, 1)) SetBit(v->vehicle_flags, VF_AUTOFILL_PRES_WAIT_TIME);
v->timetable_start = 0; v->timetable_start = 0;
v->timetable_start_subticks = 0;
v->lateness_counter = 0; v->lateness_counter = 0;
} else { } else {
ClrBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE); 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_AUTOFILL_PRES_WAIT_TIME);
ClrBit(v2->vehicle_flags, VF_TIMETABLE_STARTED); ClrBit(v2->vehicle_flags, VF_TIMETABLE_STARTED);
v2->timetable_start = 0; v2->timetable_start = 0;
v->timetable_start_subticks = 0;
v2->lateness_counter = 0; v2->lateness_counter = 0;
v2->current_loading_time = 0; v2->current_loading_time = 0;
v2->ClearSeparation(); v2->ClearSeparation();
@@ -470,6 +477,7 @@ CommandCost CmdAutomateTimetable(TileIndex index, DoCommandFlag flags, uint32 p1
/* Ctrl wasn't pressed, so clear all timetabled times. */ /* Ctrl wasn't pressed, so clear all timetabled times. */
SetBit(v2->vehicle_flags, VF_TIMETABLE_STARTED); SetBit(v2->vehicle_flags, VF_TIMETABLE_STARTED);
v2->timetable_start = 0; v2->timetable_start = 0;
v->timetable_start_subticks = 0;
v2->lateness_counter = 0; v2->lateness_counter = 0;
v2->current_loading_time = 0; v2->current_loading_time = 0;
OrderList *orders = v2->orders.list; OrderList *orders = v2->orders.list;
@@ -690,8 +698,9 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling)
just_started = !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED); just_started = !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED);
if (v->timetable_start != 0) { 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 = 0;
v->timetable_start_subticks = 0;
} }
SetBit(v->vehicle_flags, VF_TIMETABLE_STARTED); SetBit(v->vehicle_flags, VF_TIMETABLE_STARTED);

View File

@@ -152,9 +152,11 @@ static void FillTimetableArrivalDepartureTable(const Vehicle *v, VehicleOrderID
* @param p1 The p1 parameter to send to CmdSetTimetableStart * @param p1 The p1 parameter to send to CmdSetTimetableStart
* @param date the actually chosen date * @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 window the window related to the setting of the date
* @param date the actually chosen 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); ChangeTimetableStartIntl(w->window_number, date);
} }
@@ -524,7 +526,7 @@ struct TimetableWindow : Window {
/* We are running towards the first station so we can start the /* We are running towards the first station so we can start the
* timetable at the given time. */ * timetable at the given time. */
SetDParam(0, STR_JUST_DATE_WALLCLOCK_TINY); 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); 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)) { } else if (!HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED)) {
/* We aren't running on a timetable yet, so how can we be "on time" /* 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); ShowQueryString(str, STR_TIMETABLE_STARTING_DATE, 31, this, CS_NUMERAL, QSF_ACCEPT_UNCHANGED);
} else { } else {
ShowSetDateWindow(this, v->index | (v->orders.list->IsCompleteTimetable() && _ctrl_pressed ? 1U << 20 : 0), 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; break;
@@ -720,7 +722,6 @@ struct TimetableWindow : Window {
if (val < (CURRENT_MINUTE - 60)) val += 60 * 24; if (val < (CURRENT_MINUTE - 60)) val += 60 * 24;
val *= _settings_client.gui.ticks_per_minute; 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); ChangeTimetableStartIntl(v->index | (this->set_start_date_all ? 1 << 20 : 0), val);
} }
break; break;