Changeset - r28279:0fa1023cc51e
[Not reviewed]
master
0 7 0
Tyler Trahan - 14 months ago 2023-09-24 23:43:10
tyler@tylertrahan.com
Feature: Mode to display timetable in seconds
7 files changed with 171 insertions and 47 deletions:
0 comments (0 inline, 0 general)
src/lang/english.txt
Show inline comments
 
@@ -255,6 +255,10 @@ STR_UNITS_HEIGHT_IMPERIAL               
 
STR_UNITS_HEIGHT_METRIC                                         :{DECIMAL}{NBSP}m
 
STR_UNITS_HEIGHT_SI                                             :{DECIMAL}{NBSP}m
 

	
 
STR_UNITS_DAYS                                                  :{COMMA}{NBSP}day{P "" s}
 
STR_UNITS_SECONDS                                               :{COMMA}{NBSP}second{P "" s}
 
STR_UNITS_TICKS                                                 :{COMMA}{NBSP}tick{P "" s}
 

	
 
# Common window strings
 
STR_LIST_FILTER_TITLE                                           :{BLACK}Filter:
 
STR_LIST_FILTER_OSKTITLE                                        :{BLACK}Enter one or more keywords to filter the list for
 
@@ -1655,8 +1659,12 @@ STR_CONFIG_SETTING_ADVANCED_VEHICLE_LIST
 
STR_CONFIG_SETTING_LOADING_INDICATORS                           :Use loading indicators: {STRING2}
 
STR_CONFIG_SETTING_LOADING_INDICATORS_HELPTEXT                  :Select whether loading indicators are displayed above loading or unloading vehicles
 

	
 
STR_CONFIG_SETTING_TIMETABLE_IN_TICKS                           :Show timetable in ticks rather than days: {STRING2}
 
STR_CONFIG_SETTING_TIMETABLE_IN_TICKS_HELPTEXT                  :Show travel times in time tables in game ticks instead of days
 
STR_CONFIG_SETTING_TIMETABLE_MODE                               :Time units for timetables: {STRING2}
 
STR_CONFIG_SETTING_TIMETABLE_MODE_HELPTEXT                      :Select the time units used for vehicle timetables
 
###length 3
 
STR_CONFIG_SETTING_TIMETABLE_MODE_DAYS                          :Days
 
STR_CONFIG_SETTING_TIMETABLE_MODE_SECONDS                       :Seconds
 
STR_CONFIG_SETTING_TIMETABLE_MODE_TICKS                         :Ticks
 

	
 
STR_CONFIG_SETTING_TIMETABLE_SHOW_ARRIVAL_DEPARTURE             :Show arrival and departure in timetables: {STRING2}
 
STR_CONFIG_SETTING_TIMETABLE_SHOW_ARRIVAL_DEPARTURE_HELPTEXT    :Display anticipated arrival and departure times in timetables
 
@@ -4565,8 +4573,6 @@ STR_TIMETABLE_STAY_FOR_ESTIMATED        
 
STR_TIMETABLE_AND_TRAVEL_FOR_ESTIMATED                          :(travel for {STRING1}, not timetabled)
 
STR_TIMETABLE_STAY_FOR                                          :and stay for {STRING1}
 
STR_TIMETABLE_AND_TRAVEL_FOR                                    :and travel for {STRING1}
 
STR_TIMETABLE_DAYS                                              :{COMMA}{NBSP}day{P "" s}
 
STR_TIMETABLE_TICKS                                             :{COMMA}{NBSP}tick{P "" s}
 

	
 
STR_TIMETABLE_TOTAL_TIME                                        :{BLACK}This timetable will take {STRING1} to complete
 
STR_TIMETABLE_TOTAL_TIME_INCOMPLETE                             :{BLACK}This timetable will take at least {STRING1} to complete (not all timetabled)
 
@@ -4575,10 +4581,13 @@ STR_TIMETABLE_STATUS_ON_TIME            
 
STR_TIMETABLE_STATUS_LATE                                       :{BLACK}This vehicle is currently running {STRING1} late
 
STR_TIMETABLE_STATUS_EARLY                                      :{BLACK}This vehicle is currently running {STRING1} early
 
STR_TIMETABLE_STATUS_NOT_STARTED                                :{BLACK}This timetable has not yet started
 
STR_TIMETABLE_STATUS_START_AT                                   :{BLACK}This timetable will start at {STRING1}
 

	
 
STR_TIMETABLE_STARTING_DATE                                     :{BLACK}Start date
 
STR_TIMETABLE_STARTING_DATE_TOOLTIP                             :{BLACK}Select a date as starting point of this timetable. Ctrl+Click distributes all vehicles sharing this order evenly from the given date based on their relative order, if the order is completely timetabled
 
STR_TIMETABLE_STATUS_START_AT_DATE                              :{BLACK}This timetable will start at {STRING1}
 
STR_TIMETABLE_STATUS_START_IN_SECONDS                           :{BLACK}This timetable will start in {COMMA} seconds
 

	
 
STR_TIMETABLE_START                                             :{BLACK}Start Timetable
 
STR_TIMETABLE_START_TOOLTIP                                     :{BLACK}Select when this timetable starts. Ctrl+Click evenly distributes the start of all vehicles sharing this order based on their relative order, if the order is completely timetabled
 

	
 
STR_TIMETABLE_START_SECONDS_QUERY                               :Seconds until timetable starts
 

	
 
STR_TIMETABLE_CHANGE_TIME                                       :{BLACK}Change Time
 
STR_TIMETABLE_WAIT_TIME_TOOLTIP                                 :{BLACK}Change the amount of time that the highlighted order should take. Ctrl+Click sets the time for all orders
 
@@ -4602,8 +4611,10 @@ STR_TIMETABLE_EXPECTED                  
 
STR_TIMETABLE_SCHEDULED                                         :{BLACK}Scheduled
 
STR_TIMETABLE_EXPECTED_TOOLTIP                                  :{BLACK}Switch between expected and scheduled
 

	
 
STR_TIMETABLE_ARRIVAL                                           :A: {COLOUR}{DATE_TINY}
 
STR_TIMETABLE_DEPARTURE                                         :D: {COLOUR}{DATE_TINY}
 
STR_TIMETABLE_ARRIVAL_DATE                                      :A: {COLOUR}{DATE_TINY}
 
STR_TIMETABLE_DEPARTURE_DATE                                    :D: {COLOUR}{DATE_TINY}
 
STR_TIMETABLE_ARRIVAL_SECONDS_IN_FUTURE                         :A: {COLOUR}{COMMA} sec
 
STR_TIMETABLE_DEPARTURE_SECONDS_IN_FUTURE                       :D: {COLOUR}{COMMA} sec
 

	
 

	
 
# Date window (for timetable)
src/settings_gui.cpp
Show inline comments
 
@@ -1803,7 +1803,7 @@ static SettingsContainer &GetSettingsTre
 
			interface->Add(new SettingEntry("gui.statusbar_pos"));
 
			interface->Add(new SettingEntry("gui.prefer_teamchat"));
 
			interface->Add(new SettingEntry("gui.advanced_vehicle_list"));
 
			interface->Add(new SettingEntry("gui.timetable_in_ticks"));
 
			interface->Add(new SettingEntry("gui.timetable_mode"));
 
			interface->Add(new SettingEntry("gui.timetable_arrival_departure"));
 
			interface->Add(new SettingEntry("gui.show_newgrf_name"));
 
			interface->Add(new SettingEntry("gui.show_cargo_in_vehicle_lists"));
src/settings_type.h
Show inline comments
 
@@ -22,6 +22,7 @@
 
#include "openttd.h"
 
#include "rail_gui.h"
 
#include "signal_type.h"
 
#include "timetable.h"
 

	
 
/* Used to validate sizes of "max" value in settings. */
 
const size_t MAX_SLE_UINT8 = UINT8_MAX;
 
@@ -167,7 +168,7 @@ struct GUISettings {
 
	SignalCycleSettings cycle_signal_types;  ///< Which signal types to cycle with the build signal tool.
 
	SignalType default_signal_type;          ///< The default signal type, which is set automatically by the last signal used. Not available in Settings.
 
	TimerGameCalendar::Year coloured_news_year; ///< when does newspaper become coloured?
 
	bool   timetable_in_ticks;               ///< whether to show the timetable in ticks rather than days
 
	TimetableMode timetable_mode;            ///< Time units for timetables: days, seconds, or ticks
 
	bool   quick_goto;                       ///< Allow quick access to 'goto button' in vehicle orders window
 
	bool   auto_euro;                        ///< automatically switch to euro in 2002
 
	byte   drag_signals_density;             ///< many signals density
src/table/settings/gui_settings.ini
Show inline comments
 
@@ -420,14 +420,18 @@ str      = STR_CONFIG_SETTING_ADVANCED_V
 
strhelp  = STR_CONFIG_SETTING_ADVANCED_VEHICLE_LISTS_HELPTEXT
 
strval   = STR_CONFIG_SETTING_COMPANIES_OFF
 

	
 
[SDTC_BOOL]
 
var      = gui.timetable_in_ticks
 
flags    = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC
 
def      = false
 
str      = STR_CONFIG_SETTING_TIMETABLE_IN_TICKS
 
strhelp  = STR_CONFIG_SETTING_TIMETABLE_IN_TICKS_HELPTEXT
 
[SDTC_VAR]
 
var      = gui.timetable_mode
 
type     = SLE_UINT8
 
flags    = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC | SF_GUI_DROPDOWN
 
def      = 0
 
min      = 0
 
max      = 2
 
str      = STR_CONFIG_SETTING_TIMETABLE_MODE
 
strhelp  = STR_CONFIG_SETTING_TIMETABLE_MODE_HELPTEXT
 
strval   = STR_CONFIG_SETTING_TIMETABLE_MODE_DAYS
 
post_cb  = [](auto) { InvalidateWindowClassesData(WC_VEHICLE_TIMETABLE, VIWD_MODIFY_ORDERS); }
 
cat      = SC_EXPERT
 
cat      = SC_ADVANCED
 

	
 
[SDTC_BOOL]
 
var      = gui.timetable_arrival_departure
src/timetable.h
Show inline comments
 
@@ -16,6 +16,12 @@
 

	
 
static const TimerGameCalendar::Year MAX_TIMETABLE_START_YEARS = 15; ///< The maximum start date offset, in years.
 

	
 
enum class TimetableMode : uint8_t {
 
	Days,
 
	Seconds,
 
	Ticks,
 
};
 

	
 
TimerGameTick::TickCounter GetStartTickFromDate(TimerGameCalendar::Date start_date);
 
TimerGameCalendar::Date GetDateFromStartTick(TimerGameTick::TickCounter start_tick);
 

	
src/timetable_cmd.cpp
Show inline comments
 
@@ -505,16 +505,15 @@ void UpdateVehicleTimetable(Vehicle *v, 
 
	/* Before modifying waiting times, check whether we want to preserve bigger ones. */
 
	if (!real_current_order->IsType(OT_CONDITIONAL) &&
 
			(travelling || time_taken > real_current_order->GetWaitTime() || remeasure_wait_time)) {
 
		/* Round the time taken up to the nearest day, as this will avoid
 
		 * confusion for people who are timetabling in days, and can be
 
		 * adjusted later by people who aren't.
 
		/* Round up to the smallest unit of time commonly shown in the GUI (seconds) to avoid confusion.
 
		 * Players timetabling in Ticks can adjust later.
 
		 * For trains/aircraft multiple movement cycles are done in one
 
		 * tick. This makes it possible to leave the station and process
 
		 * e.g. a depot order in the same tick, causing it to not fill
 
		 * the timetable entry like is done for road vehicles/ships.
 
		 * Thus always make sure at least one tick is used between the
 
		 * processing of different orders when filling the timetable. */
 
		uint time_to_set = CeilDiv(std::max(time_taken, 1), Ticks::DAY_TICKS) * Ticks::DAY_TICKS;
 
		uint time_to_set = CeilDiv(std::max(time_taken, 1), Ticks::TICKS_PER_SECOND) * Ticks::TICKS_PER_SECOND;
 

	
 
		if (travelling && (autofilling || !real_current_order->IsTravelTimetabled())) {
 
			ChangeTimetable(v, v->cur_real_order_index, time_to_set, MTF_TRAVEL_TIME, autofilling);
src/timetable_gui.cpp
Show inline comments
 
@@ -18,8 +18,10 @@
 
#include "string_func.h"
 
#include "gfx_func.h"
 
#include "company_func.h"
 
#include "timer/timer.h"
 
#include "timer/timer_game_tick.h"
 
#include "timer/timer_game_calendar.h"
 
#include "timer/timer_window.h"
 
#include "date_gui.h"
 
#include "vehicle_gui.h"
 
#include "settings_type.h"
 
@@ -47,12 +49,59 @@ struct TimetableArrivalDeparture {
 
 */
 
void SetTimetableParams(int param1, int param2, TimerGameTick::Ticks ticks)
 
{
 
	if (_settings_client.gui.timetable_in_ticks) {
 
		SetDParam(param1, STR_TIMETABLE_TICKS);
 
		SetDParam(param2, ticks);
 
	} else {
 
		SetDParam(param1, STR_TIMETABLE_DAYS);
 
		SetDParam(param2, ticks / Ticks::DAY_TICKS);
 
	switch (_settings_client.gui.timetable_mode) {
 
		case TimetableMode::Days:
 
			SetDParam(param1, STR_UNITS_DAYS);
 
			SetDParam(param2, ticks / Ticks::DAY_TICKS);
 
			break;
 
		case TimetableMode::Seconds:
 
			SetDParam(param1, STR_UNITS_SECONDS);
 
			SetDParam(param2, ticks / Ticks::TICKS_PER_SECOND);
 
			break;
 
		case TimetableMode::Ticks:
 
			SetDParam(param1, STR_UNITS_TICKS);
 
			SetDParam(param2, ticks);
 
			break;
 
		default:
 
			NOT_REACHED();
 
	}
 
}
 

	
 
/**
 
 * Get the number of ticks in the current timetable display unit.
 
 * @return The number of ticks per day, second, or tick, to match the timetable display.
 
 */
 
static inline TimerGameTick::Ticks TicksPerTimetableUnit()
 
{
 
	switch (_settings_client.gui.timetable_mode) {
 
		case TimetableMode::Days:
 
			return Ticks::DAY_TICKS;
 
		case TimetableMode::Seconds:
 
			return Ticks::TICKS_PER_SECOND;
 
		case TimetableMode::Ticks:
 
			return 1;
 
		default:
 
			NOT_REACHED();
 
	}
 
}
 

	
 
/**
 
 * Determine if a vehicle should be shown as late, depending on the timetable display setting.
 
 * @param v The vehicle in question.
 
 * @param round_to_day When using ticks, if we should round up to the nearest day.
 
 * @return True if the vehicle is later than the threshold.
 
 */
 
bool VehicleIsAboveLatenessThreshold(const Vehicle *v, bool round_to_day)
 
{
 
	switch (_settings_client.gui.timetable_mode) {
 
		case TimetableMode::Days:
 
			return v->lateness_counter > Ticks::DAY_TICKS;
 
		case TimetableMode::Seconds:
 
			return v->lateness_counter > Ticks::TICKS_PER_SECOND;
 
		case TimetableMode::Ticks:
 
			return v->lateness_counter > (round_to_day ? Ticks::DAY_TICKS : 0);
 
		default:
 
			NOT_REACHED();
 
	}
 
}
 

	
 
@@ -183,7 +232,10 @@ struct TimetableWindow : Window {
 
		assert(HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED));
 

	
 
		bool travelling = (!v->current_order.IsType(OT_LOADING) || v->current_order.GetNonStopType() == ONSF_STOP_EVERYWHERE);
 
		TimerGameTick::Ticks start_time = TimerGameCalendar::date_fract - v->current_order_time;
 
		TimerGameTick::Ticks start_time = -v->current_order_time;
 

	
 
		/* If arrival and departure times are in days, compensate for the current date_fract. */
 
		if (_settings_client.gui.timetable_mode != TimetableMode::Seconds) start_time += TimerGameCalendar::date_fract;
 

	
 
		FillTimetableArrivalDepartureTable(v, v->cur_real_order_index % v->GetNumOrders(), travelling, table, start_time);
 

	
 
@@ -194,8 +246,15 @@ struct TimetableWindow : Window {
 
	{
 
		switch (widget) {
 
			case WID_VT_ARRIVAL_DEPARTURE_PANEL:
 
				SetDParamMaxValue(1, TimerGameCalendar::DateAtStartOfYear(CalendarTime::MAX_YEAR), 0, FS_SMALL);
 
				size->width = std::max(GetStringBoundingBox(STR_TIMETABLE_ARRIVAL).width, GetStringBoundingBox(STR_TIMETABLE_DEPARTURE).width) + WidgetDimensions::scaled.hsep_wide + padding.width;
 
				/* We handle this differently depending on the timetable mode. */
 
				if (_settings_client.gui.timetable_mode == TimetableMode::Seconds) {
 
					/* A five-digit number would fit a timetable lasting 2.7 real-world hours, which should be plenty. */
 
					SetDParamMaxDigits(1, 4, FS_SMALL);
 
					size->width = std::max(GetStringBoundingBox(STR_TIMETABLE_ARRIVAL_SECONDS_IN_FUTURE).width, GetStringBoundingBox(STR_TIMETABLE_DEPARTURE_SECONDS_IN_FUTURE).width) + WidgetDimensions::scaled.hsep_wide + padding.width;
 
				} else {
 
					SetDParamMaxValue(1, TimerGameCalendar::DateAtStartOfYear(CalendarTime::MAX_YEAR), 0, FS_SMALL);
 
					size->width = std::max(GetStringBoundingBox(STR_TIMETABLE_ARRIVAL_DATE).width, GetStringBoundingBox(STR_TIMETABLE_DEPARTURE_DATE).width) + WidgetDimensions::scaled.hsep_wide + padding.width;
 
				}
 
				FALLTHROUGH;
 

	
 
			case WID_VT_ARRIVAL_DEPARTURE_SELECTION:
 
@@ -436,7 +495,7 @@ struct TimetableWindow : Window {
 
		int selected = this->sel_index;
 

	
 
		Rect tr = r.Shrink(WidgetDimensions::scaled.framerect);
 
		bool show_late = this->show_expected && v->lateness_counter > Ticks::DAY_TICKS;
 
		bool show_late = this->show_expected && VehicleIsAboveLatenessThreshold(v, true);
 
		TimerGameTick::Ticks offset = show_late ? 0 : -v->lateness_counter;
 

	
 
		for (int i = this->vscroll->GetPosition(); i / 2 < v->GetNumOrders(); ++i) { // note: i is also incremented in the loop
 
@@ -460,14 +519,28 @@ struct TimetableWindow : Window {
 
					}
 

	
 
					/* Now actually draw the arrival time. */
 
					SetDParam(1, TimerGameCalendar::date + (arr_dep[i / 2].arrival + this_offset) / Ticks::DAY_TICKS);
 
					DrawString(tr.left, tr.right, tr.top, STR_TIMETABLE_ARRIVAL, i == selected ? TC_WHITE : TC_BLACK);
 
					if (_settings_client.gui.timetable_mode == TimetableMode::Seconds) {
 
						/* Display seconds from now. */
 
						SetDParam(1, ((arr_dep[i / 2].arrival + offset) / Ticks::TICKS_PER_SECOND));
 
						DrawString(tr.left, tr.right, tr.top, STR_TIMETABLE_ARRIVAL_SECONDS_IN_FUTURE, i == selected ? TC_WHITE : TC_BLACK);
 
					} else {
 
						/* Show a date. */
 
						SetDParam(1, TimerGameCalendar::date + (arr_dep[i / 2].arrival + this_offset) / Ticks::DAY_TICKS);
 
						DrawString(tr.left, tr.right, tr.top, STR_TIMETABLE_ARRIVAL_DATE, i == selected ? TC_WHITE : TC_BLACK);
 
					}
 
				}
 
			} else {
 
				/* Draw a departure time. */
 
				if (arr_dep[i / 2].departure != Ticks::INVALID_TICKS) {
 
					SetDParam(1, TimerGameCalendar::date + (arr_dep[i / 2].departure + offset) / Ticks::DAY_TICKS);
 
					DrawString(tr.left, tr.right, tr.top, STR_TIMETABLE_DEPARTURE, i == selected ? TC_WHITE : TC_BLACK);
 
					if (_settings_client.gui.timetable_mode == TimetableMode::Seconds) {
 
						/* Display seconds from now. */
 
						SetDParam(1, ((arr_dep[i / 2].departure + offset) / Ticks::TICKS_PER_SECOND));
 
						DrawString(tr.left, tr.right, tr.top, STR_TIMETABLE_DEPARTURE_SECONDS_IN_FUTURE, i == selected ? TC_WHITE : TC_BLACK);
 
					} else {
 
						/* Show a date. */
 
						SetDParam(1, TimerGameCalendar::date + (arr_dep[i / 2].departure + offset) / Ticks::DAY_TICKS);
 
						DrawString(tr.left, tr.right, tr.top, STR_TIMETABLE_DEPARTURE_DATE, i == selected ? TC_WHITE : TC_BLACK);
 
					}
 
				}
 
			}
 
			tr.top += GetCharacterHeight(FS_NORMAL);
 
@@ -490,19 +563,28 @@ struct TimetableWindow : Window {
 
		}
 
		tr.top += GetCharacterHeight(FS_NORMAL);
 

	
 
		/* Draw the lateness display, or indicate that the timetable has not started yet. */
 
		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_DATE_TINY);
 
			SetDParam(1, GetDateFromStartTick(v->timetable_start));
 
			DrawString(tr, STR_TIMETABLE_STATUS_START_AT);
 
			if (_settings_client.gui.timetable_mode == TimetableMode::Seconds) {
 
				/* Real time units use seconds relative to now. */
 
				SetDParam(0, (static_cast<TimerGameTick::Ticks>(v->timetable_start - TimerGameTick::counter) / Ticks::TICKS_PER_SECOND));
 
				DrawString(tr, STR_TIMETABLE_STATUS_START_IN_SECONDS);
 
			} else {
 
				/* Calendar units use dates. */
 
				SetDParam(0, STR_JUST_DATE_TINY);
 
				SetDParam(1, GetDateFromStartTick(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"? */
 
			/* We aren't running on a timetable yet. */
 
			DrawString(tr, STR_TIMETABLE_STATUS_NOT_STARTED);
 
		} else if (v->lateness_counter == 0 || (!_settings_client.gui.timetable_in_ticks && v->lateness_counter / Ticks::DAY_TICKS == 0)) {
 
		} else if (!VehicleIsAboveLatenessThreshold(v, false)) {
 
			/* We are on time. */
 
			DrawString(tr, STR_TIMETABLE_STATUS_ON_TIME);
 
		} else {
 
			/* We are late. */
 
			SetTimetableParams(0, 1, abs(v->lateness_counter));
 
			DrawString(tr, v->lateness_counter < 0 ? STR_TIMETABLE_STATUS_EARLY : STR_TIMETABLE_STATUS_LATE);
 
		}
 
@@ -556,7 +638,13 @@ struct TimetableWindow : Window {
 
			}
 

	
 
			case WID_VT_START_DATE: // Change the date that the timetable starts.
 
				ShowSetDateWindow(this, v->index, TimerGameCalendar::date, TimerGameCalendar::year, TimerGameCalendar::year + MAX_TIMETABLE_START_YEARS, ChangeTimetableStartCallback, reinterpret_cast<void *>(static_cast<uintptr_t>(_ctrl_pressed)));
 
				if (_settings_client.gui.timetable_mode == TimetableMode::Seconds) {
 
					this->query_widget = WID_VT_START_DATE;
 
					this->change_timetable_all = _ctrl_pressed;
 
					ShowQueryString(STR_EMPTY, STR_TIMETABLE_START_SECONDS_QUERY, 6, this, CS_NUMERAL, QSF_ACCEPT_UNCHANGED);
 
				} else {
 
					ShowSetDateWindow(this, v->index, TimerGameCalendar::date, TimerGameCalendar::year, TimerGameCalendar::year + MAX_TIMETABLE_START_YEARS, ChangeTimetableStartCallback, reinterpret_cast<void*>(static_cast<uintptr_t>(_ctrl_pressed)));
 
				}
 
				break;
 

	
 
			case WID_VT_CHANGE_TIME: { // "Wait For" button.
 
@@ -571,7 +659,7 @@ struct TimetableWindow : Window {
 

	
 
				if (order != nullptr) {
 
					uint time = (selected % 2 != 0) ? order->GetTravelTime() : order->GetWaitTime();
 
					if (!_settings_client.gui.timetable_in_ticks) time /= Ticks::DAY_TICKS;
 
					time /= TicksPerTimetableUnit();
 

	
 
					if (time != 0) {
 
						SetDParam(0, time);
 
@@ -667,7 +755,7 @@ struct TimetableWindow : Window {
 
			}
 

	
 
			case WID_VT_CHANGE_TIME:
 
				if (!_settings_client.gui.timetable_in_ticks) val *= Ticks::DAY_TICKS;
 
				val *= TicksPerTimetableUnit();
 

	
 
				if (this->change_timetable_all) {
 
					Command<CMD_BULK_CHANGE_TIMETABLE>::Post(STR_ERROR_CAN_T_TIMETABLE_VEHICLE, v->index, mtf, ClampTo<uint16_t>(val));
 
@@ -676,6 +764,12 @@ struct TimetableWindow : Window {
 
				}
 
				break;
 

	
 
			case WID_VT_START_DATE: {
 
				TimerGameTick::TickCounter start_tick = TimerGameTick::counter + (val * Ticks::TICKS_PER_SECOND);
 
				Command<CMD_SET_TIMETABLE_START>::Post(STR_ERROR_CAN_T_TIMETABLE_VEHICLE, v->index, this->change_timetable_all, start_tick);
 
				break;
 
			}
 

	
 
			default:
 
				NOT_REACHED();
 
		}
 
@@ -695,6 +789,15 @@ struct TimetableWindow : Window {
 
		this->GetWidget<NWidgetStacked>(WID_VT_ARRIVAL_DEPARTURE_SELECTION)->SetDisplayedPlane(_settings_client.gui.timetable_arrival_departure ? 0 : SZSP_NONE);
 
		this->GetWidget<NWidgetStacked>(WID_VT_EXPECTED_SELECTION)->SetDisplayedPlane(_settings_client.gui.timetable_arrival_departure ? 0 : 1);
 
	}
 

	
 
	/**
 
	 * In real-time mode, the timetable GUI shows relative times and needs to be redrawn every second.
 
	 */
 
	IntervalTimer<TimerGameTick> redraw_interval = {Ticks::TICKS_PER_SECOND, [this](auto) {
 
		if (_settings_client.gui.timetable_mode == TimetableMode::Seconds) {
 
			this->SetDirty();
 
		}
 
	}};
 
};
 

	
 
static const NWidgetPart _nested_timetable_widgets[] = {
 
@@ -725,7 +828,7 @@ static const NWidgetPart _nested_timetab
 
				NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_CLEAR_SPEED), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_CLEAR_SPEED, STR_TIMETABLE_CLEAR_SPEED_TOOLTIP),
 
			EndContainer(),
 
			NWidget(NWID_VERTICAL, NC_EQUALSIZE),
 
				NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_START_DATE), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_STARTING_DATE, STR_TIMETABLE_STARTING_DATE_TOOLTIP),
 
				NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_START_DATE), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_START, STR_TIMETABLE_START_TOOLTIP),
 
				NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_RESET_LATENESS), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_RESET_LATENESS, STR_TIMETABLE_RESET_LATENESS_TOOLTIP),
 
			EndContainer(),
 
			NWidget(NWID_VERTICAL, NC_EQUALSIZE),
0 comments (0 inline, 0 general)