From 91b3e9c1ab84e76288f31ae04dbc7e20d218b6a4 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Thu, 22 Feb 2024 20:16:01 +0000 Subject: [PATCH] Initial support for relative timetabling in wallclock mode --- src/date_func.h | 2 ++ src/date_type.h | 3 +++ src/lang/extra/english.txt | 2 ++ src/strings.cpp | 14 ++++++++++++- src/timer/timer_game_tick.h | 3 --- src/timetable_gui.cpp | 39 ++++++++++++++++++++++++++++--------- 6 files changed, 50 insertions(+), 13 deletions(-) diff --git a/src/date_func.h b/src/date_func.h index 44b71ff4ab..62df873152 100644 --- a/src/date_func.h +++ b/src/date_func.h @@ -75,6 +75,8 @@ inline Ticks TimetableDisplayUnitSize() { if (_settings_time.time_in_minutes) { return _settings_time.ticks_per_minute; + } else if (EconTime::UsingWallclockUnits()) { + return TICKS_PER_SECOND; } else { return DAY_TICKS * DayLengthFactor(); } diff --git a/src/date_type.h b/src/date_type.h index 8b4fd55f4c..4b1e1b9650 100644 --- a/src/date_type.h +++ b/src/date_type.h @@ -26,6 +26,9 @@ static const int MONTHS_IN_YEAR = 12; ///< months per year static const int SECONDS_PER_DAY = 2; ///< approximate seconds per day, not for precise calculations +/** Estimation of how many ticks fit in a single second. */ +static const int TICKS_PER_SECOND = 1000 / 27 /*MILLISECONDS_PER_TICK*/; + using Ticks = int32_t; ///< The type to store ticks in static constexpr Ticks INVALID_TICKS = -1; ///< Representation of an invalid number of ticks diff --git a/src/lang/extra/english.txt b/src/lang/extra/english.txt index e6536ce5f4..74e4d248df 100644 --- a/src/lang/extra/english.txt +++ b/src/lang/extra/english.txt @@ -82,6 +82,8 @@ STR_UNIT_NAME_VELOCITY_METRIC :km/h STR_UNIT_NAME_VELOCITY_SI :m/s STR_UNIT_NAME_VELOCITY_GAMEUNITS :tiles/day +STR_UNITS_SECONDS_SHORT :{COMMA}{NBSP}s + STR_BUTTON_RENAME :{BLACK}Rename STR_MEASURE_DIST_HEIGHTDIFF :{BLACK}Manhattan Distance: {NUM}{}Bird Fly Distance: {NUM}{}Distance from the nearest edge: {NUM}{}Height from sea level: {HEIGHT}{}Height difference: {HEIGHT} diff --git a/src/strings.cpp b/src/strings.cpp index d317282eb5..57c38f92b4 100644 --- a/src/strings.cpp +++ b/src/strings.cpp @@ -1571,7 +1571,14 @@ static void FormatString(StringBuilder builder, const char *str_arg, StringParam auto tmp_params = MakeParameters(args.GetNextParameter()); FormatString(builder, GetStringPtr(STR_UNITS_TICKS), tmp_params); } else { - StringID str = _settings_time.time_in_minutes ? STR_TIMETABLE_MINUTES : STR_UNITS_DAYS; + StringID str; + if (_settings_time.time_in_minutes) { + str = STR_TIMETABLE_MINUTES; + } else if (EconTime::UsingWallclockUnits()) { + str = STR_UNITS_SECONDS; + } else { + str = STR_UNITS_DAYS; + } const int64_t ticks = args.GetNextParameter(); const int64_t ratio = TimetableDisplayUnitSize(); const int64_t units = ticks / ratio; @@ -1598,6 +1605,11 @@ static void FormatString(StringBuilder builder, const char *str_arg, StringParam case SCC_TT_TIME: { // {TT_TIME} if (_settings_time.time_in_minutes) { FormatStateTicksHHMMString(builder, args.GetNextParameter(), next_substr_case_index); + } else if (EconTime::UsingWallclockUnits()) { + StateTicks tick = args.GetNextParameter(); + StateTicksDelta offset = tick - _state_ticks; + auto tmp_params = MakeParameters(offset / TICKS_PER_SECOND); + FormatString(builder, GetStringPtr(STR_UNITS_SECONDS_SHORT), tmp_params); } else { FormatTinyOrISODate(builder, StateTicksToCalendarDate(args.GetNextParameter()), STR_FORMAT_DATE_TINY); } diff --git a/src/timer/timer_game_tick.h b/src/timer/timer_game_tick.h index ffb3a52e26..2a8a8cc103 100644 --- a/src/timer/timer_game_tick.h +++ b/src/timer/timer_game_tick.h @@ -14,9 +14,6 @@ #include -/** Estimation of how many ticks fit in a single second. */ -static const uint TICKS_PER_SECOND = 1000 / 27 /*MILLISECONDS_PER_TICK*/; - /** * Timer that represents the game-ticks. It will pause when the game is paused. * diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp index 28afdc337f..271982a91e 100644 --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -403,7 +403,13 @@ struct TimetableWindow : GeneralVehicleWindow { { switch (widget) { case WID_VT_ARRIVAL_DEPARTURE_PANEL: - SetDParamMaxValue(0, _settings_time.time_in_minutes ? 0 : EconTime::MAX_YEAR.base() * DAYS_IN_YEAR); + if (_settings_time.time_in_minutes) { + SetDParam(0, 0); + } else if (EconTime::UsingWallclockUnits()) { + SetDParam(0, _state_ticks + (TICKS_PER_SECOND * 9999)); + } else { + SetDParam(0, EconTime::MAX_YEAR.base() * DAYS_IN_YEAR); + } this->deparr_time_width = GetStringBoundingBox(STR_JUST_TT_TIME).width + 4; this->deparr_abbr_width = std::max(GetStringBoundingBox(STR_TIMETABLE_ARRIVAL_ABBREVIATION).width, GetStringBoundingBox(STR_TIMETABLE_DEPARTURE_ABBREVIATION).width); size->width = this->deparr_abbr_width + WidgetDimensions::scaled.hsep_wide + this->deparr_time_width + padding.width; @@ -812,9 +818,14 @@ struct TimetableWindow : GeneralVehicleWindow { if (v->timetable_start != 0) { /* We are running towards the first station so we can start the * timetable at the given time. */ - SetDParam(0, STR_JUST_TT_TIME); - SetDParam(1, v->timetable_start); - DrawString(tr, STR_TIMETABLE_STATUS_START_AT_DATE); + if (EconTime::UsingWallclockUnits() && !_settings_time.time_in_minutes) { + SetDParam(0, (v->timetable_start - _state_ticks) / TICKS_PER_SECOND); + DrawString(tr, STR_TIMETABLE_STATUS_START_IN_SECONDS); + } else { + SetDParam(0, STR_JUST_TT_TIME); + SetDParam(1, v->timetable_start); + DrawString(tr, STR_TIMETABLE_STATUS_START_AT_DATE); + } } else if (!HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED)) { /* We aren't running on a timetable yet, so how can we be "on time" * when we aren't even "on service"/"on duty"? */ @@ -917,17 +928,22 @@ struct TimetableWindow : GeneralVehicleWindow { break; } - case WID_VT_START_DATE: // Change the date that the timetable starts. - if (_settings_time.time_in_minutes && _settings_client.gui.timetable_start_text_entry) { - this->set_start_date_all = v->orders->IsCompleteTimetable() && _ctrl_pressed; + case WID_VT_START_DATE: { // Change the date that the timetable starts. + bool set_all = _ctrl_pressed && v->orders->IsCompleteTimetable(); + if (EconTime::UsingWallclockUnits() && !_settings_time.time_in_minutes) { + this->set_start_date_all = set_all; + ShowQueryString(STR_EMPTY, STR_TIMETABLE_START_SECONDS_QUERY, 6, this, CS_NUMERAL, QSF_ACCEPT_UNCHANGED); + } else if (_settings_time.time_in_minutes && _settings_client.gui.timetable_start_text_entry) { + this->set_start_date_all = set_all; StringID str = STR_JUST_INT; SetDParam(0, _settings_time.NowInTickMinutes().ClockHHMM()); ShowQueryString(str, STR_TIMETABLE_START, 31, this, CS_NUMERAL, QSF_ACCEPT_UNCHANGED); } else { - ShowSetDateWindow(this, v->index | (_ctrl_pressed ? 1U << 20 : 0), + ShowSetDateWindow(this, v->index | (set_all ? 1U << 20 : 0), _state_ticks, EconTime::CurYear(), EconTime::CurYear() + 15, ChangeTimetableStartCallback); } break; + } case WID_VT_CHANGE_TIME: { // "Wait For" button. int selected = this->sel_index; @@ -1131,7 +1147,12 @@ struct TimetableWindow : GeneralVehicleWindow { if (StrEmpty(str)) break; char *end; int32_t val = std::strtol(str, &end, 10); - if (val >= 0 && end && *end == 0) { + if (!(end != nullptr && *end == 0)) break; + if (EconTime::UsingWallclockUnits() && !_settings_time.time_in_minutes) { + ChangeTimetableStartIntl(v->index | (this->set_start_date_all ? 1 << 20 : 0), _state_ticks + (val * TICKS_PER_SECOND)); + break; + } + if (val >= 0) { uint minutes = (val % 100) % 60; uint hours = (val / 100) % 24; const TickMinutes now = _settings_time.NowInTickMinutes();