diff --git a/src/cheat_gui.cpp b/src/cheat_gui.cpp --- a/src/cheat_gui.cpp +++ b/src/cheat_gui.cpp @@ -116,7 +116,9 @@ static int32_t ClickChangeDateCheat(int3 /* Now it's safe to actually change the date. */ TimerGameCalendar::SetDate(new_calendar_date, TimerGameCalendar::date_fract); - TimerGameEconomy::SetDate(new_economy_date, TimerGameEconomy::date_fract); + + /* If not using wallclock units, we keep economy date in sync with calendar date and must change it also. */ + if (!TimerGameEconomy::UsingWallclockUnits()) TimerGameEconomy::SetDate(new_economy_date, TimerGameEconomy::date_fract); CalendarEnginesMonthlyLoop(); SetWindowDirty(WC_STATUS_BAR, 0); diff --git a/src/company_gui.cpp b/src/company_gui.cpp --- a/src/company_gui.cpp +++ b/src/company_gui.cpp @@ -138,7 +138,7 @@ static uint GetTotalCategoriesHeight() */ static uint GetMaxCategoriesWidth() { - uint max_width = 0; + uint max_width = GetStringBoundingBox(STR_FINANCES_YEAR_CAPTION).width; /* Loop through categories to check max widths. */ for (const ExpensesList &list : _expenses_list_types) { @@ -173,8 +173,10 @@ static void DrawCategory(const Rect &r, */ static void DrawCategories(const Rect &r) { - /* Start with an empty space in the year row, plus the blockspace under the year. */ - int y = r.top + GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_wide; + int y = r.top; + /* Draw description of 12-minute economic period. */ + DrawString(r.left, r.right, y, (STR_FINANCES_YEAR_CAPTION), TC_FROMSTRING, SA_LEFT, true); + y += GetCharacterHeight(FS_NORMAL) + WidgetDimensions::scaled.vsep_wide; for (const ExpensesList &list : _expenses_list_types) { /* Draw category title and advance y */ diff --git a/src/graph_gui.cpp b/src/graph_gui.cpp --- a/src/graph_gui.cpp +++ b/src/graph_gui.cpp @@ -170,6 +170,9 @@ protected: static const int GRAPH_ZERO_LINE_COLOUR = GREY_SCALE(8); static const int GRAPH_YEAR_LINE_COLOUR = GREY_SCALE(5); static const int GRAPH_NUM_MONTHS = 24; ///< Number of months displayed in the graph. + static const int PAYMENT_GRAPH_X_STEP_DAYS = 20; ///< X-axis step label for cargo payment rates "Days in transit". + static const int PAYMENT_GRAPH_X_STEP_SECONDS = 10; ///< X-axis step label for cargo payment rates "Seconds in transit". + static const int ECONOMY_QUARTER_MINUTES = 3; ///< Minutes per economic quarter. static const TextColour GRAPH_AXIS_LABEL_COLOUR = TC_BLACK; ///< colour of the graph axis label. @@ -336,8 +339,13 @@ protected: /* Don't draw the first line, as that's where the axis will be. */ x = r.left + x_sep; - for (int i = 0; i < this->num_vert_lines; i++) { - GfxFillRect(x, r.top, x, r.bottom, GRAPH_GRID_COLOUR); + int grid_colour = GRAPH_GRID_COLOUR; + for (int i = 1; i < this->num_vert_lines + 1; i++) { + /* If using wallclock units, we separate periods with a lighter line. */ + if (TimerGameEconomy::UsingWallclockUnits()) { + grid_colour = (i % 4 == 0) ? GRAPH_YEAR_LINE_COLOUR : GRAPH_GRID_COLOUR; + } + GfxFillRect(x, r.top, x, r.bottom, grid_colour); x += x_sep; } @@ -399,7 +407,7 @@ protected: x += x_sep; } } else { - /* Draw x-axis labels for graphs not based on quarterly performance (cargo payment rates). */ + /* Draw x-axis labels for graphs not based on quarterly performance (cargo payment rates, and all graphs when using wallclock units). */ x = r.left; y = r.bottom + ScaleGUITrad(2); uint16_t label = this->x_values_start; @@ -622,6 +630,12 @@ struct OperatingProfitGraphWindow : Base OperatingProfitGraphWindow(WindowDesc *desc, WindowNumber window_number) : BaseGraphWindow(desc, WID_CV_GRAPH, STR_JUST_CURRENCY_SHORT) { + this->num_on_x_axis = GRAPH_NUM_MONTHS; + this->num_vert_lines = GRAPH_NUM_MONTHS; + this->x_values_start = ECONOMY_QUARTER_MINUTES; + this->x_values_increment = ECONOMY_QUARTER_MINUTES; + this->draw_dates = !TimerGameEconomy::UsingWallclockUnits(); + this->InitializeWindow(window_number); } @@ -641,10 +655,12 @@ static constexpr NWidgetPart _nested_ope NWidget(WWT_STICKYBOX, COLOUR_BROWN), EndContainer(), NWidget(WWT_PANEL, COLOUR_BROWN, WID_CV_BACKGROUND), - NWidget(NWID_HORIZONTAL), + NWidget(NWID_VERTICAL), NWidget(WWT_EMPTY, COLOUR_BROWN, WID_CV_GRAPH), SetMinimalSize(576, 160), SetFill(1, 1), SetResize(1, 1), - NWidget(NWID_VERTICAL), - NWidget(NWID_SPACER), SetFill(0, 1), SetResize(0, 1), + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(12, 0), SetFill(1, 0), SetResize(1, 0), + NWidget(WWT_TEXT, COLOUR_BROWN, WID_CV_FOOTER), SetMinimalSize(0, 6), SetPadding(2, 0, 2, 0), SetDataTip(STR_GRAPH_LAST_72_MINUTES_TIME_LABEL, STR_NULL), + NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0), NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_CV_RESIZE), SetDataTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE), EndContainer(), EndContainer(), @@ -673,6 +689,12 @@ struct IncomeGraphWindow : BaseGraphWind IncomeGraphWindow(WindowDesc *desc, WindowNumber window_number) : BaseGraphWindow(desc, WID_CV_GRAPH, STR_JUST_CURRENCY_SHORT) { + this->num_on_x_axis = GRAPH_NUM_MONTHS; + this->num_vert_lines = GRAPH_NUM_MONTHS; + this->x_values_start = ECONOMY_QUARTER_MINUTES; + this->x_values_increment = ECONOMY_QUARTER_MINUTES; + this->draw_dates = !TimerGameEconomy::UsingWallclockUnits(); + this->InitializeWindow(window_number); } @@ -692,10 +714,12 @@ static constexpr NWidgetPart _nested_inc NWidget(WWT_STICKYBOX, COLOUR_BROWN), EndContainer(), NWidget(WWT_PANEL, COLOUR_BROWN, WID_CV_BACKGROUND), - NWidget(NWID_HORIZONTAL), + NWidget(NWID_VERTICAL), NWidget(WWT_EMPTY, COLOUR_BROWN, WID_CV_GRAPH), SetMinimalSize(576, 128), SetFill(1, 1), SetResize(1, 1), - NWidget(NWID_VERTICAL), - NWidget(NWID_SPACER), SetFill(0, 1), SetResize(0, 1), + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(12, 0), SetFill(1, 0), SetResize(1, 0), + NWidget(WWT_TEXT, COLOUR_BROWN, WID_CV_FOOTER), SetMinimalSize(0, 6), SetPadding(2, 0, 2, 0), SetDataTip(STR_GRAPH_LAST_72_MINUTES_TIME_LABEL, STR_NULL), + NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0), NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_CV_RESIZE), SetDataTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE), EndContainer(), EndContainer(), @@ -722,6 +746,12 @@ struct DeliveredCargoGraphWindow : BaseG DeliveredCargoGraphWindow(WindowDesc *desc, WindowNumber window_number) : BaseGraphWindow(desc, WID_CV_GRAPH, STR_JUST_COMMA) { + this->num_on_x_axis = GRAPH_NUM_MONTHS; + this->num_vert_lines = GRAPH_NUM_MONTHS; + this->x_values_start = ECONOMY_QUARTER_MINUTES; + this->x_values_increment = ECONOMY_QUARTER_MINUTES; + this->draw_dates = !TimerGameEconomy::UsingWallclockUnits(); + this->InitializeWindow(window_number); } @@ -741,10 +771,12 @@ static constexpr NWidgetPart _nested_del NWidget(WWT_STICKYBOX, COLOUR_BROWN), EndContainer(), NWidget(WWT_PANEL, COLOUR_BROWN, WID_CV_BACKGROUND), - NWidget(NWID_HORIZONTAL), + NWidget(NWID_VERTICAL), NWidget(WWT_EMPTY, COLOUR_BROWN, WID_CV_GRAPH), SetMinimalSize(576, 128), SetFill(1, 1), SetResize(1, 1), - NWidget(NWID_VERTICAL), - NWidget(NWID_SPACER), SetFill(0, 1), SetResize(0, 1), + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(12, 0), SetFill(1, 0), SetResize(1, 0), + NWidget(WWT_TEXT, COLOUR_BROWN, WID_CV_FOOTER), SetMinimalSize(0, 6), SetPadding(2, 0, 2, 0), SetDataTip(STR_GRAPH_LAST_72_MINUTES_TIME_LABEL, STR_NULL), + NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0), NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_CV_RESIZE), SetDataTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE), EndContainer(), EndContainer(), @@ -771,6 +803,12 @@ struct PerformanceHistoryGraphWindow : B PerformanceHistoryGraphWindow(WindowDesc *desc, WindowNumber window_number) : BaseGraphWindow(desc, WID_PHG_GRAPH, STR_JUST_COMMA) { + this->num_on_x_axis = GRAPH_NUM_MONTHS; + this->num_vert_lines = GRAPH_NUM_MONTHS; + this->x_values_start = ECONOMY_QUARTER_MINUTES; + this->x_values_increment = ECONOMY_QUARTER_MINUTES; + this->draw_dates = !TimerGameEconomy::UsingWallclockUnits(); + this->InitializeWindow(window_number); } @@ -797,10 +835,12 @@ static constexpr NWidgetPart _nested_per NWidget(WWT_STICKYBOX, COLOUR_BROWN), EndContainer(), NWidget(WWT_PANEL, COLOUR_BROWN, WID_PHG_BACKGROUND), - NWidget(NWID_HORIZONTAL), + NWidget(NWID_VERTICAL), NWidget(WWT_EMPTY, COLOUR_BROWN, WID_PHG_GRAPH), SetMinimalSize(576, 224), SetFill(1, 1), SetResize(1, 1), - NWidget(NWID_VERTICAL), - NWidget(NWID_SPACER), SetFill(0, 1), SetResize(0, 1), + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(12, 0), SetFill(1, 0), SetResize(1, 0), + NWidget(WWT_TEXT, COLOUR_BROWN, WID_PHG_FOOTER), SetMinimalSize(0, 6), SetPadding(2, 0, 2, 0), SetDataTip(STR_GRAPH_LAST_72_MINUTES_TIME_LABEL, STR_NULL), + NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0), NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_PHG_RESIZE), SetDataTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE), EndContainer(), EndContainer(), @@ -827,6 +867,12 @@ struct CompanyValueGraphWindow : BaseGra CompanyValueGraphWindow(WindowDesc *desc, WindowNumber window_number) : BaseGraphWindow(desc, WID_CV_GRAPH, STR_JUST_CURRENCY_SHORT) { + this->num_on_x_axis = GRAPH_NUM_MONTHS; + this->num_vert_lines = GRAPH_NUM_MONTHS; + this->x_values_start = ECONOMY_QUARTER_MINUTES; + this->x_values_increment = ECONOMY_QUARTER_MINUTES; + this->draw_dates = !TimerGameEconomy::UsingWallclockUnits(); + this->InitializeWindow(window_number); } @@ -846,10 +892,12 @@ static constexpr NWidgetPart _nested_com NWidget(WWT_STICKYBOX, COLOUR_BROWN), EndContainer(), NWidget(WWT_PANEL, COLOUR_BROWN, WID_CV_BACKGROUND), - NWidget(NWID_HORIZONTAL), + NWidget(NWID_VERTICAL), NWidget(WWT_EMPTY, COLOUR_BROWN, WID_CV_GRAPH), SetMinimalSize(576, 224), SetFill(1, 1), SetResize(1, 1), - NWidget(NWID_VERTICAL), - NWidget(NWID_SPACER), SetFill(0, 1), SetResize(0, 1), + NWidget(NWID_HORIZONTAL), + NWidget(NWID_SPACER), SetMinimalSize(12, 0), SetFill(1, 0), SetResize(1, 0), + NWidget(WWT_TEXT, COLOUR_BROWN, WID_CV_FOOTER), SetMinimalSize(0, 6), SetPadding(2, 0, 2, 0), SetDataTip(STR_GRAPH_LAST_72_MINUTES_TIME_LABEL, STR_NULL), + NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0), NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_CV_RESIZE), SetDataTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE), EndContainer(), EndContainer(), @@ -883,8 +931,9 @@ struct PaymentRatesGraphWindow : BaseGra this->num_on_x_axis = 20; this->num_vert_lines = 20; this->draw_dates = false; - this->x_values_start = 10; - this->x_values_increment = 10; + /* The x-axis is labeled in either seconds or days. A day is two seconds, so we adjust the label if needed. */ + this->x_values_start = (TimerGameEconomy::UsingWallclockUnits() ? PAYMENT_GRAPH_X_STEP_SECONDS : PAYMENT_GRAPH_X_STEP_DAYS); + this->x_values_increment = (TimerGameEconomy::UsingWallclockUnits() ? PAYMENT_GRAPH_X_STEP_SECONDS : PAYMENT_GRAPH_X_STEP_DAYS); this->CreateNestedTree(); this->vscroll = this->GetScrollbar(WID_CPR_MATRIX_SCROLLBAR); @@ -1085,7 +1134,7 @@ static constexpr NWidgetPart _nested_car EndContainer(), NWidget(NWID_HORIZONTAL), NWidget(NWID_SPACER), SetMinimalSize(12, 0), SetFill(1, 0), SetResize(1, 0), - NWidget(WWT_TEXT, COLOUR_BROWN, WID_CPR_FOOTER), SetMinimalSize(0, 6), SetPadding(2, 0, 2, 0), SetDataTip(STR_GRAPH_CARGO_PAYMENT_RATES_X_LABEL, STR_NULL), + NWidget(WWT_TEXT, COLOUR_BROWN, WID_CPR_FOOTER), SetMinimalSize(0, 6), SetPadding(2, 0, 2, 0), SetDataTip(STR_GRAPH_CARGO_PAYMENT_RATES_TIME_LABEL, STR_NULL), NWidget(NWID_SPACER), SetFill(1, 0), SetResize(1, 0), NWidget(WWT_RESIZEBOX, COLOUR_BROWN, WID_CPR_RESIZE), SetDataTip(RWV_HIDE_BEVEL, STR_TOOLTIP_RESIZE), EndContainer(), diff --git a/src/lang/english.txt b/src/lang/english.txt --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -214,7 +214,8 @@ STR_COLOUR_SECONDARY_SAME_AS_PRIMARY STR_UNITS_VELOCITY_IMPERIAL :{DECIMAL}{NBSP}mph STR_UNITS_VELOCITY_METRIC :{DECIMAL}{NBSP}km/h STR_UNITS_VELOCITY_SI :{DECIMAL}{NBSP}m/s -STR_UNITS_VELOCITY_GAMEUNITS :{DECIMAL}{NBSP}tiles/day +STR_UNITS_VELOCITY_GAMEUNITS_DAY :{DECIMAL}{NBSP}tiles/day +STR_UNITS_VELOCITY_GAMEUNITS_SEC :{DECIMAL}{NBSP}tiles/sec STR_UNITS_VELOCITY_KNOTS :{DECIMAL}{NBSP}knots STR_UNITS_POWER_IMPERIAL :{DECIMAL}{NBSP}hp @@ -255,10 +256,17 @@ STR_UNITS_HEIGHT_IMPERIAL STR_UNITS_HEIGHT_METRIC :{DECIMAL}{NBSP}m STR_UNITS_HEIGHT_SI :{DECIMAL}{NBSP}m +# Time units used in string control characters STR_UNITS_DAYS :{COMMA}{NBSP}day{P "" s} STR_UNITS_SECONDS :{COMMA}{NBSP}second{P "" s} STR_UNITS_TICKS :{COMMA}{NBSP}tick{P "" s} +STR_UNITS_MONTHS :{NUM}{NBSP}month{P "" s} +STR_UNITS_MINUTES :{NUM}{NBSP}minute{P "" s} + +STR_UNITS_YEARS :{NUM}{NBSP}year{P "" s} +STR_UNITS_PERIODS :{NUM}{NBSP}period{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 @@ -323,8 +331,8 @@ STR_SORT_BY_PRODUCTION STR_SORT_BY_TYPE :Type STR_SORT_BY_TRANSPORTED :Transported STR_SORT_BY_NUMBER :Number -STR_SORT_BY_PROFIT_LAST_YEAR :Profit last year -STR_SORT_BY_PROFIT_THIS_YEAR :Profit this year +STR_SORT_BY_PROFIT_LAST_YEAR :Profit last {TKM year period} +STR_SORT_BY_PROFIT_THIS_YEAR :Profit this {TKM year period} STR_SORT_BY_AGE :Age STR_SORT_BY_RELIABILITY :Reliability STR_SORT_BY_TOTAL_CAPACITY_PER_CARGOTYPE :Total capacity per cargo type @@ -351,10 +359,10 @@ STR_SORT_BY_RANGE STR_SORT_BY_POPULATION :Population STR_SORT_BY_RATING :Rating STR_SORT_BY_NUM_VEHICLES :Number of vehicles -STR_SORT_BY_TOTAL_PROFIT_LAST_YEAR :Total profit last year -STR_SORT_BY_TOTAL_PROFIT_THIS_YEAR :Total profit this year -STR_SORT_BY_AVERAGE_PROFIT_LAST_YEAR :Average profit last year -STR_SORT_BY_AVERAGE_PROFIT_THIS_YEAR :Average profit this year +STR_SORT_BY_TOTAL_PROFIT_LAST_YEAR :Total profit last {TKM year period} +STR_SORT_BY_TOTAL_PROFIT_THIS_YEAR :Total profit this {TKM year period} +STR_SORT_BY_AVERAGE_PROFIT_LAST_YEAR :Average profit last {TKM year period} +STR_SORT_BY_AVERAGE_PROFIT_THIS_YEAR :Average profit this {TKM year period} # Group by options for vehicle list STR_GROUP_BY_NONE :None @@ -615,8 +623,10 @@ STR_GRAPH_CARGO_DELIVERED_CAPTION STR_GRAPH_COMPANY_PERFORMANCE_RATINGS_CAPTION :{WHITE}Company performance ratings (maximum rating=1000) STR_GRAPH_COMPANY_VALUES_CAPTION :{WHITE}Company Value Graph +STR_GRAPH_LAST_72_MINUTES_TIME_LABEL :{TINY_FONT}{BLACK}{TKM "" "Last 72 minutes"} + STR_GRAPH_CARGO_PAYMENT_RATES_CAPTION :{WHITE}Cargo Payment Rates -STR_GRAPH_CARGO_PAYMENT_RATES_X_LABEL :{TINY_FONT}{BLACK}Days in transit +STR_GRAPH_CARGO_PAYMENT_RATES_TIME_LABEL :{TINY_FONT}{BLACK}{TKM Days Seconds} in transit STR_GRAPH_CARGO_PAYMENT_RATES_TITLE :{TINY_FONT}{BLACK}Payment for delivering 10 units (or 10,000 litres) of cargo a distance of 20 squares STR_GRAPH_CARGO_ENABLE_ALL :{TINY_FONT}{BLACK}Enable all STR_GRAPH_CARGO_DISABLE_ALL :{TINY_FONT}{BLACK}Disable all @@ -665,9 +675,9 @@ STR_PERFORMANCE_DETAIL_LOAN STR_PERFORMANCE_DETAIL_TOTAL :{BLACK}Total: ###length 10 -STR_PERFORMANCE_DETAIL_VEHICLES_TOOLTIP :{BLACK}Number of vehicles that turned a profit last year. This includes road vehicles, trains, ships and aircraft +STR_PERFORMANCE_DETAIL_VEHICLES_TOOLTIP :{BLACK}Number of vehicles that turned a profit last {TKM year period}. This includes road vehicles, trains, ships and aircraft STR_PERFORMANCE_DETAIL_STATIONS_TOOLTIP :{BLACK}Number of recently-serviced stations. Train stations, bus stops, airports and so on are counted separately even if they belong to the same station -STR_PERFORMANCE_DETAIL_MIN_PROFIT_TOOLTIP :{BLACK}The profit of the vehicle with the lowest income (only vehicles older than two years are considered) +STR_PERFORMANCE_DETAIL_MIN_PROFIT_TOOLTIP :{BLACK}The profit of the vehicle with the lowest income (only vehicles older than two {TKM years periods} are considered) STR_PERFORMANCE_DETAIL_MIN_INCOME_TOOLTIP :{BLACK}Amount of cash made in the quarter with the lowest profit of the last 12 quarters STR_PERFORMANCE_DETAIL_MAX_INCOME_TOOLTIP :{BLACK}Amount of cash made in the quarter with the highest profit of the last 12 quarters STR_PERFORMANCE_DETAIL_DELIVERED_TOOLTIP :{BLACK}Units of cargo delivered in the last four quarters @@ -899,7 +909,7 @@ STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD STR_NEWS_VEHICLE_IS_GETTING_VERY_OLD_AND :{WHITE}{VEHICLE} is getting very old and urgently needs replacing STR_NEWS_TRAIN_IS_STUCK :{WHITE}{VEHICLE} can't find a path to continue STR_NEWS_VEHICLE_IS_LOST :{WHITE}{VEHICLE} is lost -STR_NEWS_VEHICLE_IS_UNPROFITABLE :{WHITE}{VEHICLE}'s profit last year was {CURRENCY_LONG} +STR_NEWS_VEHICLE_IS_UNPROFITABLE :{WHITE}{VEHICLE}'s profit last {TKM year period} was {CURRENCY_LONG} STR_NEWS_AIRCRAFT_DEST_TOO_FAR :{WHITE}{VEHICLE} can't get to the next destination because it is out of range STR_NEWS_ORDER_REFIT_FAILED :{WHITE}{VEHICLE} stopped because an ordered refit failed @@ -916,16 +926,16 @@ STR_NEWS_STATION_NOW_ACCEPTS_CARGO_LIST STR_NEWS_OFFER_OF_SUBSIDY_EXPIRED :{BIG_FONT}{BLACK}Offer of subsidy expired:{}{}{STRING} from {STRING2} to {STRING2} will now not attract a subsidy STR_NEWS_SUBSIDY_WITHDRAWN_SERVICE :{BIG_FONT}{BLACK}Subsidy withdrawn:{}{}{STRING} service from {STRING2} to {STRING2} is no longer subsidised -STR_NEWS_SERVICE_SUBSIDY_OFFERED :{BIG_FONT}{BLACK}Service subsidy offered:{}{}First {STRING} from {STRING2} to {STRING2} will attract a {NUM} year subsidy from the local authority! +STR_NEWS_SERVICE_SUBSIDY_OFFERED :{BIG_FONT}{BLACK}Service subsidy offered:{}{}First {STRING} from {STRING2} to {STRING2} will attract a {UNITS_YEARS_OR_MINUTES} subsidy from the local authority! ###length 4 -STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} from {STRING2} to {STRING2} will pay 50% extra for the next {NUM} year{P "" s}! -STR_NEWS_SERVICE_SUBSIDY_AWARDED_DOUBLE :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} from {STRING2} to {STRING2} will pay double rates for the next {NUM} year{P "" s}! -STR_NEWS_SERVICE_SUBSIDY_AWARDED_TRIPLE :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} from {STRING2} to {STRING2} will pay triple rates for the next {NUM} year{P "" s}! -STR_NEWS_SERVICE_SUBSIDY_AWARDED_QUADRUPLE :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} from {STRING2} to {STRING2} will pay quadruple rates for the next {NUM} year{P "" s}! - -STR_NEWS_ROAD_REBUILDING :{BIG_FONT}{BLACK}Traffic chaos in {TOWN}!{}{}Road rebuilding programme funded by {RAW_STRING} brings 6 months of misery to motorists! +STR_NEWS_SERVICE_SUBSIDY_AWARDED_HALF :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} from {STRING2} to {STRING2} will pay 50% extra for the next {UNITS_YEARS_OR_MINUTES}! +STR_NEWS_SERVICE_SUBSIDY_AWARDED_DOUBLE :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} from {STRING2} to {STRING2} will pay double rates for the next {UNITS_YEARS_OR_MINUTES}! +STR_NEWS_SERVICE_SUBSIDY_AWARDED_TRIPLE :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} from {STRING2} to {STRING2} will pay triple rates for the next {UNITS_YEARS_OR_MINUTES}! +STR_NEWS_SERVICE_SUBSIDY_AWARDED_QUADRUPLE :{BIG_FONT}{BLACK}Service subsidy awarded to {RAW_STRING}!{}{}{STRING} from {STRING2} to {STRING2} will pay quadruple rates for the next {UNITS_YEARS_OR_MINUTES}! + +STR_NEWS_ROAD_REBUILDING :{BIG_FONT}{BLACK}Traffic chaos in {TOWN}!{}{}Road rebuilding programme funded by {RAW_STRING} brings 6 {TKM months minutes} of misery to motorists! STR_NEWS_EXCLUSIVE_RIGHTS_TITLE :{BIG_FONT}{BLACK}Transport monopoly! -STR_NEWS_EXCLUSIVE_RIGHTS_DESCRIPTION :{BIG_FONT}{BLACK}Local authority of {TOWN} signs contract with {RAW_STRING} for one year of exclusive transport rights! +STR_NEWS_EXCLUSIVE_RIGHTS_DESCRIPTION :{BIG_FONT}{BLACK}Local authority of {TOWN} signs contract with {RAW_STRING} for 12 {TKM months minutes} of exclusive transport rights! # Extra view window STR_EXTRA_VIEWPORT_TITLE :{WHITE}Viewport {COMMA} @@ -1286,9 +1296,9 @@ STR_CONFIG_SETTING_SUBSIDY_MULTIPLIER STR_CONFIG_SETTING_SUBSIDY_MULTIPLIER_HELPTEXT :Set how much is paid for subsidised connections STR_CONFIG_SETTING_SUBSIDY_DURATION :Subsidy duration: {STRING2} -STR_CONFIG_SETTING_SUBSIDY_DURATION_HELPTEXT :Set the number of years for which a subsidy is awarded - -STR_CONFIG_SETTING_SUBSIDY_DURATION_VALUE :{NUM} year{P "" s} +STR_CONFIG_SETTING_SUBSIDY_DURATION_HELPTEXT :Set the number of {TKM years periods} for which a subsidy is awarded + +STR_CONFIG_SETTING_SUBSIDY_DURATION_VALUE :{UNITS_YEARS_OR_PERIODS} ###setting-zero-is-special STR_CONFIG_SETTING_SUBSIDY_DURATION_DISABLED :No subsidies @@ -1296,7 +1306,7 @@ STR_CONFIG_SETTING_CONSTRUCTION_COSTS STR_CONFIG_SETTING_CONSTRUCTION_COSTS_HELPTEXT :Set level of construction and purchase costs STR_CONFIG_SETTING_RECESSIONS :Recessions: {STRING2} -STR_CONFIG_SETTING_RECESSIONS_HELPTEXT :If enabled, recessions may occur every few years. During a recession all production is significantly lower (it returns to previous level when the recession is over) +STR_CONFIG_SETTING_RECESSIONS_HELPTEXT :If enabled, recessions may occur periodically. During a recession all production is significantly lower (it returns to previous level when the recession is over) STR_CONFIG_SETTING_TRAIN_REVERSING :Disallow train reversing in stations: {STRING2} STR_CONFIG_SETTING_TRAIN_REVERSING_HELPTEXT :If enabled, trains will not reverse in non-terminus stations, even if there is a shorter path to their next destination when reversing @@ -1384,8 +1394,8 @@ STR_CONFIG_SETTING_SIGNALSIDE_LEFT STR_CONFIG_SETTING_SIGNALSIDE_DRIVING_SIDE :On the driving side STR_CONFIG_SETTING_SIGNALSIDE_RIGHT :On the right -STR_CONFIG_SETTING_SHOWFINANCES :Show finances window at the end of the year: {STRING2} -STR_CONFIG_SETTING_SHOWFINANCES_HELPTEXT :If enabled, the finances window pops up at the end of each year to allow easy inspection of the financial status of the company +STR_CONFIG_SETTING_SHOWFINANCES :Show finances window at the end of the {TKM year period}: {STRING2} +STR_CONFIG_SETTING_SHOWFINANCES_HELPTEXT :If enabled, the finances window pops up at the end of each {TKM year period} to allow easy inspection of the financial status of the company STR_CONFIG_SETTING_NONSTOP_BY_DEFAULT :New orders are 'non-stop' by default: {STRING2} STR_CONFIG_SETTING_NONSTOP_BY_DEFAULT_HELPTEXT :Normally, a vehicle will stop at every station it passes. By enabling this setting, it will drive through all station on the way to its final destination without stopping. Note, that this setting only defines a default value for new orders. Individual orders can be set explicitly to either behaviour nevertheless @@ -1406,10 +1416,10 @@ STR_CONFIG_SETTING_AUTOSCROLL_MAIN_VIEWP STR_CONFIG_SETTING_AUTOSCROLL_EVERY_VIEWPORT :Every viewport STR_CONFIG_SETTING_BRIBE :Allow bribing of the local authority: {STRING2} -STR_CONFIG_SETTING_BRIBE_HELPTEXT :Allow companies to try bribing the local town authority. If the bribe is noticed by an inspector, the company will not be able to act in the town for six months +STR_CONFIG_SETTING_BRIBE_HELPTEXT :Allow companies to try bribing the local town authority. If the bribe is noticed by an inspector, the company will not be able to act in the town for six {TKM months minutes} STR_CONFIG_SETTING_ALLOW_EXCLUSIVE :Allow buying exclusive transport rights: {STRING2} -STR_CONFIG_SETTING_ALLOW_EXCLUSIVE_HELPTEXT :If a company buys exclusive transport rights for a town, opponents' stations (passenger and cargo) won't receive any cargo for a whole year +STR_CONFIG_SETTING_ALLOW_EXCLUSIVE_HELPTEXT :If a company buys exclusive transport rights for a town, opponents' stations (passenger and cargo) won't receive any cargo for twelve {TKM months minutes} STR_CONFIG_SETTING_ALLOW_FUND_BUILDINGS :Allow funding buildings: {STRING2} STR_CONFIG_SETTING_ALLOW_FUND_BUILDINGS_HELPTEXT :Allow companies to give money to towns for funding new houses @@ -1466,11 +1476,17 @@ STR_CONFIG_SETTING_ORDER_REVIEW_EXDEPOT STR_CONFIG_SETTING_ORDER_REVIEW_ON :Of all vehicles STR_CONFIG_SETTING_WARN_INCOME_LESS :Warn if a vehicle's income is negative: {STRING2} -STR_CONFIG_SETTING_WARN_INCOME_LESS_HELPTEXT :When enabled, a news message gets sent when a vehicle has not made any profit within a calendar year +STR_CONFIG_SETTING_WARN_INCOME_LESS_HELPTEXT :When enabled, a news message gets sent when a vehicle has not made any profit within a {TKM year period} STR_CONFIG_SETTING_NEVER_EXPIRE_VEHICLES :Vehicles never expire: {STRING2} STR_CONFIG_SETTING_NEVER_EXPIRE_VEHICLES_HELPTEXT :When enabled, all vehicle models remain available forever after their introduction +STR_CONFIG_SETTING_TIMEKEEPING_UNITS :Timekeeping: {STRING2} +STR_CONFIG_SETTING_TIMEKEEPING_UNITS_HELPTEXT :Select the timekeeping units of the game. This cannot be changed later.{}{}Calendar-based is the classic OpenTTD experience, with a year consisting of 12 months, and each month having 28-31 days.{}{}In Wallclock-based time, vehicle movement, cargo production, and financials are instead based on one-minute increments, which is about as long as a 30 day month takes in Calendar-based mode. These are grouped into 12-minute periods, equivalent to a year in Calendar-based mode.{}{}In either mode there is always a classic calendar, which is used for introduction dates of vehicles, houses, and other infrastructure. +###length 2 +STR_CONFIG_SETTING_TIMEKEEPING_UNITS_CALENDAR :Calendar +STR_CONFIG_SETTING_TIMEKEEPING_UNITS_WALLCLOCK :Wallclock + STR_CONFIG_SETTING_AUTORENEW_VEHICLE :Autorenew vehicle when it gets old: {STRING2} STR_CONFIG_SETTING_AUTORENEW_VEHICLE_HELPTEXT :When enabled, a vehicle nearing its end of life gets automatically replaced when the renew conditions are fulfilled @@ -1723,8 +1739,8 @@ STR_CONFIG_SETTING_SOUND_TICKER_HELPTEXT STR_CONFIG_SETTING_SOUND_NEWS :Newspaper: {STRING2} STR_CONFIG_SETTING_SOUND_NEWS_HELPTEXT :Play sound upon display of newspapers -STR_CONFIG_SETTING_SOUND_NEW_YEAR :End of year: {STRING2} -STR_CONFIG_SETTING_SOUND_NEW_YEAR_HELPTEXT :Play sound at the end of a year summarising the company's performance during the year compared to the previous year +STR_CONFIG_SETTING_SOUND_NEW_YEAR :End of {TKM year period}: {STRING2} +STR_CONFIG_SETTING_SOUND_NEW_YEAR_HELPTEXT :Play sound at the end of a {TKM year period} summarising the company's performance during the {TKM year period} compared to the previous {TKM year period} STR_CONFIG_SETTING_SOUND_CONFIRM :Construction: {STRING2} STR_CONFIG_SETTING_SOUND_CONFIRM_HELPTEXT :Play sound on successful constructions or other actions @@ -1792,7 +1808,7 @@ STR_CONFIG_SETTING_SERVINT_AIRCRAFT STR_CONFIG_SETTING_SERVINT_AIRCRAFT_HELPTEXT :Set the default service interval for new aircraft, if no explicit service interval is set for the vehicle STR_CONFIG_SETTING_SERVINT_SHIPS :Default service interval for ships: {STRING2} STR_CONFIG_SETTING_SERVINT_SHIPS_HELPTEXT :Set the default service interval for new ships, if no explicit service interval is set for the vehicle -STR_CONFIG_SETTING_SERVINT_VALUE :{COMMA}{NBSP}day{P 0 "" s}/% +STR_CONFIG_SETTING_SERVINT_VALUE :{COMMA}{NBSP}Day{P 0 "" s}/Minute{P 0 "" s}/% ###setting-zero-is-special STR_CONFIG_SETTING_SERVINT_DISABLED :Disabled @@ -2026,7 +2042,7 @@ STR_CONFIG_SETTING_LOCALISATION_UNITS_VE STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_IMPERIAL :Imperial (mph) STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_METRIC :Metric (km/h) STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_SI :SI (m/s) -STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_GAMEUNITS :Game units (tiles/day) +STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_GAMEUNITS :Game units ({TKM "tiles/day" "tiles/sec"}) STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_KNOTS :Knots STR_CONFIG_SETTING_LOCALISATION_UNITS_POWER :Vehicle power units: {STRING2} @@ -2712,9 +2728,9 @@ STR_LINKGRAPH_LEGEND_SATURATED STR_LINKGRAPH_LEGEND_OVERLOADED :{TINY_FONT}{BLACK}overloaded # Linkgraph tooltip -STR_LINKGRAPH_STATS_TOOLTIP :{BLACK}{CARGO_LONG} to be transported per month from {STATION} to {STATION} ({COMMA}% of capacity){RAW_STRING} +STR_LINKGRAPH_STATS_TOOLTIP :{BLACK}{CARGO_LONG} to be transported per {TKM month minute} from {STATION} to {STATION} ({COMMA}% of capacity){RAW_STRING} STR_LINKGRAPH_STATS_TOOLTIP_RETURN_EXTENSION :{}{CARGO_LONG} to be transported back ({COMMA}% of capacity) -STR_LINKGRAPH_STATS_TOOLTIP_TIME_EXTENSION :{}Average travel time: {NUM}{NBSP}day{P "" s} +STR_LINKGRAPH_STATS_TOOLTIP_TIME_EXTENSION :{}Average travel time: {UNITS_DAYS_OR_SECONDS} # Base for station construction window(s) STR_STATION_BUILD_COVERAGE_AREA_TITLE :{BLACK}Coverage area highlight @@ -2724,7 +2740,7 @@ STR_STATION_BUILD_COVERAGE_AREA_OFF_TOOL STR_STATION_BUILD_COVERAGE_AREA_ON_TOOLTIP :{BLACK}Highlight coverage area of proposed site STR_STATION_BUILD_ACCEPTS_CARGO :{BLACK}Accepts: {GOLD}{CARGO_LIST} STR_STATION_BUILD_SUPPLIES_CARGO :{BLACK}Supplies: {GOLD}{CARGO_LIST} -STR_STATION_BUILD_INFRASTRUCTURE_COST :{BLACK}Maintenance cost: {GOLD}{CURRENCY_SHORT}/yr +STR_STATION_BUILD_INFRASTRUCTURE_COST :{BLACK}Maintenance cost: {GOLD}{CURRENCY_SHORT}/{TKM year period} # Join station window STR_JOIN_STATION_CAPTION :{WHITE}Join station @@ -3571,15 +3587,15 @@ STR_TOWN_POPULATION STR_TOWN_VIEW_TOWN_CAPTION :{WHITE}{TOWN} STR_TOWN_VIEW_CITY_CAPTION :{WHITE}{TOWN} (City) STR_TOWN_VIEW_POPULATION_HOUSES :{BLACK}Population: {ORANGE}{COMMA}{BLACK} Houses: {ORANGE}{COMMA} -STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX :{BLACK}{CARGO_LIST} last month: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} +STR_TOWN_VIEW_CARGO_LAST_MONTH_MAX :{BLACK}{CARGO_LIST} last {TKM month minute}: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH :{BLACK}Cargo needed for town growth: STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_GENERAL :{ORANGE}{STRING}{RED} required STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER :{ORANGE}{STRING}{BLACK} required in winter STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_DELIVERED_GENERAL :{ORANGE}{STRING}{GREEN} delivered STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED :{ORANGE}{CARGO_TINY} / {CARGO_LONG}{RED} (still required) STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_DELIVERED :{ORANGE}{CARGO_TINY} / {CARGO_LONG}{GREEN} (delivered) -STR_TOWN_VIEW_TOWN_GROWS_EVERY :{BLACK}Town grows every {ORANGE}{COMMA}{BLACK}{NBSP}day{P "" s} -STR_TOWN_VIEW_TOWN_GROWS_EVERY_FUNDED :{BLACK}Town grows every {ORANGE}{COMMA}{BLACK}{NBSP}day{P "" s} (funded) +STR_TOWN_VIEW_TOWN_GROWS_EVERY :{BLACK}Town grows every {ORANGE}{UNITS_DAYS_OR_SECONDS} +STR_TOWN_VIEW_TOWN_GROWS_EVERY_FUNDED :{BLACK}Town grows every {ORANGE}{UNITS_DAYS_OR_SECONDS} (funded) STR_TOWN_VIEW_TOWN_GROW_STOPPED :{BLACK}Town is {RED}not{BLACK} growing STR_TOWN_VIEW_NOISE_IN_TOWN :{BLACK}Noise limit in town: {ORANGE}{COMMA}{BLACK} max: {ORANGE}{COMMA} STR_TOWN_VIEW_CENTER_TOOLTIP :{BLACK}Centre the main view on town location. Ctrl+Click to open a new viewport on town location @@ -3619,10 +3635,10 @@ STR_LOCAL_AUTHORITY_ACTION_BRIBE STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING :{PUSH_COLOUR}{YELLOW}Initiate a small local advertising campaign, to attract more passengers and cargo to your transport services.{}Provides a temporary boost to station rating in a small radius around the town centre.{}{POP_COLOUR}Cost: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_MEDIUM_ADVERTISING :{PUSH_COLOUR}{YELLOW}Initiate a medium local advertising campaign, to attract more passengers and cargo to your transport services.{}Provides a temporary boost to station rating in a medium radius around the town centre.{}{POP_COLOUR}Cost: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_LARGE_ADVERTISING :{PUSH_COLOUR}{YELLOW}Initiate a large local advertising campaign, to attract more passengers and cargo to your transport services.{}Provides a temporary boost to station rating in a large radius around the town centre.{}{POP_COLOUR}Cost: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_ROAD_RECONSTRUCTION :{PUSH_COLOUR}{YELLOW}Fund the reconstruction of the urban road network.{}Causes considerable disruption to road traffic for up to 6 months.{}{POP_COLOUR}Cost: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_ROAD_RECONSTRUCTION :{PUSH_COLOUR}{YELLOW}Fund the reconstruction of the urban road network.{}Causes considerable disruption to road traffic for up to 6 {TKM months minutes}.{}{POP_COLOUR}Cost: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_STATUE_OF_COMPANY :{PUSH_COLOUR}{YELLOW}Build a statue in honour of your company.{}Provides a permanent boost to station rating in this town.{}{POP_COLOUR}Cost: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_NEW_BUILDINGS :{PUSH_COLOUR}{YELLOW}Fund the construction of new buildings in the town.{}Provides a temporary boost to town growth in this town.{}{POP_COLOUR}Cost: {CURRENCY_LONG} -STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_EXCLUSIVE_TRANSPORT :{PUSH_COLOUR}{YELLOW}Buy 1 year's exclusive transport rights in town.{}Town authority will not allow passengers and cargo to use your competitors' stations. A successful bribe from a competitor will cancel this contract.{}{POP_COLOUR}Cost: {CURRENCY_LONG} +STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_EXCLUSIVE_TRANSPORT :{PUSH_COLOUR}{YELLOW}Buy exclusive transport rights in town for 12 {TKM months minutes}.{}Town authority will not allow passengers and cargo to use your competitors' stations. A successful bribe from a competitor will cancel this contract.{}{POP_COLOUR}Cost: {CURRENCY_LONG} STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_BRIBE :{PUSH_COLOUR}{YELLOW}Bribe the local authority to increase your rating and abort a competitor's exclusive transport rights, at the risk of a severe penalty if caught.{}{POP_COLOUR}Cost: {CURRENCY_LONG} # Goal window @@ -3669,11 +3685,15 @@ STR_GOAL_QUESTION_BUTTON_CLOSE # Subsidies window STR_SUBSIDIES_CAPTION :{WHITE}Subsidies STR_SUBSIDIES_OFFERED_TITLE :{BLACK}Subsidies on offer for services taking: -STR_SUBSIDIES_OFFERED_FROM_TO :{ORANGE}{STRING} from {STRING2} to {STRING2}{YELLOW} (by {DATE_SHORT}) +STR_SUBSIDIES_OFFERED_FROM_TO :{ORANGE}{STRING} from {STRING2} to {STRING2}{YELLOW} ({STRING1}) STR_SUBSIDIES_NONE :{ORANGE}- None - STR_SUBSIDIES_SUBSIDISED_TITLE :{BLACK}Services already subsidised: -STR_SUBSIDIES_SUBSIDISED_FROM_TO :{ORANGE}{STRING} from {STRING2} to {STRING2}{YELLOW} ({COMPANY}{YELLOW}, until {DATE_SHORT}) +STR_SUBSIDIES_SUBSIDISED_FROM_TO :{ORANGE}{STRING} from {STRING2} to {STRING2}{YELLOW} ({COMPANY}{YELLOW}, {STRING1}) STR_SUBSIDIES_TOOLTIP_CLICK_ON_SERVICE_TO_CENTER :{BLACK}Click on service to centre main view on industry/town. Ctrl+Click to open a new viewport on industry/town location +STR_SUBSIDIES_OFFERED_EXPIRY_DATE :by {DATE_SHORT} +STR_SUBSIDIES_OFFERED_EXPIRY_TIME :within {UNITS_MONTHS_OR_MINUTES} +STR_SUBSIDIES_SUBSIDISED_EXPIRY_DATE :until {DATE_SHORT} +STR_SUBSIDIES_SUBSIDISED_EXPIRY_TIME :{UNITS_MONTHS_OR_MINUTES} remaining # Story book window STR_STORY_BOOK_CAPTION :{WHITE}{COMPANY} Story Book @@ -3713,7 +3733,7 @@ STR_STATION_VIEW_EXCLUSIVE_RIGHTS_COMPAN STR_STATION_VIEW_RATINGS_BUTTON :{BLACK}Ratings STR_STATION_VIEW_RATINGS_TOOLTIP :{BLACK}Show station ratings -STR_STATION_VIEW_SUPPLY_RATINGS_TITLE :{BLACK}Monthly supply and local rating: +STR_STATION_VIEW_SUPPLY_RATINGS_TITLE :{BLACK}Supply per {TKM month minute} and local rating: STR_STATION_VIEW_CARGO_SUPPLY_RATING :{WHITE}{STRING}: {YELLOW}{COMMA} / {STRING} ({COMMA}%) STR_STATION_VIEW_GROUP :{BLACK}Group by @@ -3774,6 +3794,7 @@ STR_EDIT_WAYPOINT_NAME # Finances window STR_FINANCES_CAPTION :{WHITE}{COMPANY} Finances {BLACK}{COMPANY_NUM} STR_FINANCES_YEAR :{WHITE}{NUM} +STR_FINANCES_YEAR_CAPTION :{WHITE}{TKM Year Period} ###length 3 STR_FINANCES_REVENUE_TITLE :{WHITE}Revenue @@ -3875,7 +3896,7 @@ STR_COMPANY_INFRASTRUCTURE_VIEW_CANALS STR_COMPANY_INFRASTRUCTURE_VIEW_STATION_SECT :{GOLD}Stations: STR_COMPANY_INFRASTRUCTURE_VIEW_STATIONS :{WHITE}Station tiles STR_COMPANY_INFRASTRUCTURE_VIEW_AIRPORTS :{WHITE}Airports -STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL :{WHITE}{CURRENCY_LONG}/yr +STR_COMPANY_INFRASTRUCTURE_VIEW_TOTAL :{WHITE}{CURRENCY_LONG}/{TKM year period} # Industry directory STR_INDUSTRY_DIRECTORY_CAPTION :{WHITE}Industries @@ -3894,7 +3915,7 @@ STR_INDUSTRY_DIRECTORY_FILTER_NONE # Industry view STR_INDUSTRY_VIEW_CAPTION :{WHITE}{INDUSTRY} -STR_INDUSTRY_VIEW_PRODUCTION_LAST_MONTH_TITLE :{BLACK}Production last month: +STR_INDUSTRY_VIEW_PRODUCTION_LAST_MONTH_TITLE :{BLACK}Production last {TKM month minute}: STR_INDUSTRY_VIEW_TRANSPORTED :{YELLOW}{CARGO_LONG}{RAW_STRING}{BLACK} ({COMMA}% transported) STR_INDUSTRY_VIEW_LOCATION_TOOLTIP :{BLACK}Centre the main view on industry location. Ctrl+Click to open a new viewport on industry location STR_INDUSTRY_VIEW_PRODUCTION_LEVEL :{BLACK}Production level: {YELLOW}{COMMA}% @@ -3935,7 +3956,7 @@ STR_VEHICLE_LIST_MANAGE_LIST_TOOLTIP STR_VEHICLE_LIST_REPLACE_VEHICLES :Replace vehicles STR_VEHICLE_LIST_SEND_FOR_SERVICING :Send for servicing STR_VEHICLE_LIST_CREATE_GROUP :Create group -STR_VEHICLE_LIST_PROFIT_THIS_YEAR_LAST_YEAR :{TINY_FONT}{BLACK}Profit this year: {CURRENCY_LONG} (last year: {CURRENCY_LONG}) +STR_VEHICLE_LIST_PROFIT_THIS_YEAR_LAST_YEAR :{TINY_FONT}{BLACK}Profit this {TKM year period}: {CURRENCY_LONG} (last {TKM year period}: {CURRENCY_LONG}) STR_VEHICLE_LIST_CARGO :[{CARGO_LIST}] STR_VEHICLE_LIST_NAME_AND_CARGO :{STRING1} {STRING1} @@ -3980,8 +4001,8 @@ STR_GROUP_REMOVE_ALL_VEHICLES STR_GROUP_RENAME_CAPTION :{BLACK}Rename a group -STR_GROUP_PROFIT_THIS_YEAR :Profit this year: -STR_GROUP_PROFIT_LAST_YEAR :Profit last year: +STR_GROUP_PROFIT_THIS_YEAR :Profit this {TKM year period}: +STR_GROUP_PROFIT_LAST_YEAR :Profit last {TKM year period}: STR_GROUP_OCCUPANCY :Current usage: STR_GROUP_OCCUPANCY_VALUE :{NUM}% @@ -4008,7 +4029,7 @@ STR_PURCHASE_INFO_SPEED_POWER STR_PURCHASE_INFO_SPEED :{BLACK}Speed: {GOLD}{VELOCITY} STR_PURCHASE_INFO_SPEED_OCEAN :{BLACK}Speed on ocean: {GOLD}{VELOCITY} STR_PURCHASE_INFO_SPEED_CANAL :{BLACK}Speed on canal/river: {GOLD}{VELOCITY} -STR_PURCHASE_INFO_RUNNINGCOST :{BLACK}Running Cost: {GOLD}{CURRENCY_LONG}/yr +STR_PURCHASE_INFO_RUNNINGCOST :{BLACK}Running Cost: {GOLD}{CURRENCY_LONG}/{TKM year period} STR_PURCHASE_INFO_CAPACITY :{BLACK}Capacity: {GOLD}{CARGO_LONG} {STRING} STR_PURCHASE_INFO_REFITTABLE :(refittable) STR_PURCHASE_INFO_DESIGNED_LIFE :{BLACK}Designed: {GOLD}{NUM}{BLACK} Life: {GOLD}{COMMA} year{P "" s} @@ -4201,13 +4222,13 @@ STR_ENGINE_PREVIEW_TRAM_VEHICLE STR_ENGINE_PREVIEW_AIRCRAFT :aircraft STR_ENGINE_PREVIEW_SHIP :ship -STR_ENGINE_PREVIEW_COST_WEIGHT_SPEED_POWER :{BLACK}Cost: {CURRENCY_LONG} Weight: {WEIGHT_SHORT}{}Speed: {VELOCITY} Power: {POWER}{}Running Cost: {CURRENCY_LONG}/yr{}Capacity: {CARGO_LONG} -STR_ENGINE_PREVIEW_COST_WEIGHT_SPEED_POWER_MAX_TE :{BLACK}Cost: {0:CURRENCY_LONG} Weight: {1:WEIGHT_SHORT}{}Speed: {2:VELOCITY} Power: {3:POWER} Max. T.E.: {6:FORCE}{}Running Cost: {4:CURRENCY_LONG}/yr{}Capacity: {5:CARGO_LONG} -STR_ENGINE_PREVIEW_COST_MAX_SPEED_CAP_RUNCOST :{BLACK}Cost: {CURRENCY_LONG} Max. Speed: {VELOCITY}{}Capacity: {CARGO_LONG}{}Running Cost: {CURRENCY_LONG}/yr -STR_ENGINE_PREVIEW_COST_MAX_SPEED_TYPE_CAP_CAP_RUNCOST :{BLACK}Cost: {CURRENCY_LONG} Max. Speed: {VELOCITY}{}Aircraft type: {STRING}{}Capacity: {CARGO_LONG}, {CARGO_LONG}{}Running Cost: {CURRENCY_LONG}/yr -STR_ENGINE_PREVIEW_COST_MAX_SPEED_TYPE_CAP_RUNCOST :{BLACK}Cost: {CURRENCY_LONG} Max. Speed: {VELOCITY}{}Aircraft type: {STRING}{}Capacity: {CARGO_LONG}{}Running Cost: {CURRENCY_LONG}/yr -STR_ENGINE_PREVIEW_COST_MAX_SPEED_TYPE_RANGE_CAP_CAP_RUNCOST :{BLACK}Cost: {CURRENCY_LONG} Max. Speed: {VELOCITY}{}Aircraft type: {STRING} Range: {COMMA} tiles{}Capacity: {CARGO_LONG}, {CARGO_LONG}{}Running Cost: {CURRENCY_LONG}/yr -STR_ENGINE_PREVIEW_COST_MAX_SPEED_TYPE_RANGE_CAP_RUNCOST :{BLACK}Cost: {CURRENCY_LONG} Max. Speed: {VELOCITY}{}Aircraft type: {STRING} Range: {COMMA} tiles{}Capacity: {CARGO_LONG}{}Running Cost: {CURRENCY_LONG}/yr +STR_ENGINE_PREVIEW_COST_WEIGHT_SPEED_POWER :{BLACK}Cost: {CURRENCY_LONG} Weight: {WEIGHT_SHORT}{}Speed: {VELOCITY} Power: {POWER}{}Running Cost: {CURRENCY_LONG}/{TKM year period}{}Capacity: {CARGO_LONG} +STR_ENGINE_PREVIEW_COST_WEIGHT_SPEED_POWER_MAX_TE :{BLACK}Cost: {0:CURRENCY_LONG} Weight: {1:WEIGHT_SHORT}{}Speed: {2:VELOCITY} Power: {3:POWER} Max. T.E.: {6:FORCE}{}Running Cost: {4:CURRENCY_LONG}/{TKM year period}{}Capacity: {5:CARGO_LONG} +STR_ENGINE_PREVIEW_COST_MAX_SPEED_CAP_RUNCOST :{BLACK}Cost: {CURRENCY_LONG} Max. Speed: {VELOCITY}{}Capacity: {CARGO_LONG}{}Running Cost: {CURRENCY_LONG}/{TKM year period} +STR_ENGINE_PREVIEW_COST_MAX_SPEED_TYPE_CAP_CAP_RUNCOST :{BLACK}Cost: {CURRENCY_LONG} Max. Speed: {VELOCITY}{}Aircraft type: {STRING}{}Capacity: {CARGO_LONG}, {CARGO_LONG}{}Running Cost: {CURRENCY_LONG}/{TKM year period} +STR_ENGINE_PREVIEW_COST_MAX_SPEED_TYPE_CAP_RUNCOST :{BLACK}Cost: {CURRENCY_LONG} Max. Speed: {VELOCITY}{}Aircraft type: {STRING}{}Capacity: {CARGO_LONG}{}Running Cost: {CURRENCY_LONG}/{TKM year period} +STR_ENGINE_PREVIEW_COST_MAX_SPEED_TYPE_RANGE_CAP_CAP_RUNCOST :{BLACK}Cost: {CURRENCY_LONG} Max. Speed: {VELOCITY}{}Aircraft type: {STRING} Range: {COMMA} tiles{}Capacity: {CARGO_LONG}, {CARGO_LONG}{}Running Cost: {CURRENCY_LONG}/{TKM year period} +STR_ENGINE_PREVIEW_COST_MAX_SPEED_TYPE_RANGE_CAP_RUNCOST :{BLACK}Cost: {CURRENCY_LONG} Max. Speed: {VELOCITY}{}Aircraft type: {STRING} Range: {COMMA} tiles{}Capacity: {CARGO_LONG}{}Running Cost: {CURRENCY_LONG}/{TKM year period} # Autoreplace window STR_REPLACE_VEHICLES_WHITE :{WHITE}Replace {STRING} - {STRING1} @@ -4350,9 +4371,9 @@ STR_VEHICLE_DETAILS_ROAD_VEHICLE_RENAME STR_VEHICLE_DETAILS_SHIP_RENAME :{BLACK}Name ship STR_VEHICLE_DETAILS_AIRCRAFT_RENAME :{BLACK}Name aircraft -STR_VEHICLE_INFO_AGE_RUNNING_COST_YR :{BLACK}Age: {LTBLUE}{STRING2}{BLACK} Running Cost: {LTBLUE}{CURRENCY_LONG}/yr STR_VEHICLE_INFO_AGE :{COMMA} year{P "" s} ({COMMA}) STR_VEHICLE_INFO_AGE_RED :{RED}{COMMA} year{P "" s} ({COMMA}) +STR_VEHICLE_INFO_AGE_RUNNING_COST_YR :{BLACK}Age: {LTBLUE}{STRING2}{BLACK} Running Cost: {LTBLUE}{CURRENCY_LONG}/{TKM year period} STR_VEHICLE_INFO_MAX_SPEED :{BLACK}Max. speed: {LTBLUE}{VELOCITY} STR_VEHICLE_INFO_MAX_SPEED_TYPE :{BLACK}Max. speed: {LTBLUE}{VELOCITY} {BLACK}Aircraft type: {LTBLUE}{STRING} @@ -4360,8 +4381,8 @@ STR_VEHICLE_INFO_MAX_SPEED_TYPE_RANGE STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED :{BLACK}Weight: {LTBLUE}{WEIGHT_SHORT} {BLACK}Power: {LTBLUE}{POWER}{BLACK} Max. speed: {LTBLUE}{VELOCITY} STR_VEHICLE_INFO_WEIGHT_POWER_MAX_SPEED_MAX_TE :{BLACK}Weight: {LTBLUE}{WEIGHT_SHORT} {BLACK}Power: {LTBLUE}{POWER}{BLACK} Max. speed: {LTBLUE}{VELOCITY} {BLACK}Max. T.E.: {LTBLUE}{FORCE} -STR_VEHICLE_INFO_PROFIT_THIS_YEAR_LAST_YEAR :{BLACK}Profit this year: {LTBLUE}{CURRENCY_LONG} (last year: {CURRENCY_LONG}) -STR_VEHICLE_INFO_PROFIT_THIS_YEAR_LAST_YEAR_MIN_PERFORMANCE :{BLACK}Profit this year: {LTBLUE}{CURRENCY_LONG} (last year: {CURRENCY_LONG}) {BLACK}Min. performance: {LTBLUE}{POWER_TO_WEIGHT} +STR_VEHICLE_INFO_PROFIT_THIS_YEAR_LAST_YEAR :{BLACK}Profit this {TKM year period}: {LTBLUE}{CURRENCY_LONG} (last {TKM year period}: {CURRENCY_LONG}) +STR_VEHICLE_INFO_PROFIT_THIS_YEAR_LAST_YEAR_MIN_PERFORMANCE :{BLACK}Profit this {TKM year period}: {LTBLUE}{CURRENCY_LONG} (last {TKM year period}: {CURRENCY_LONG}) {BLACK}Min. performance: {LTBLUE}{POWER_TO_WEIGHT} STR_VEHICLE_INFO_RELIABILITY_BREAKDOWNS :{BLACK}Reliability: {LTBLUE}{COMMA}% {BLACK}Breakdowns since last service: {LTBLUE}{COMMA} STR_VEHICLE_INFO_BUILT_VALUE :{LTBLUE}{ENGINE} {BLACK}Built: {LTBLUE}{NUM}{BLACK} Value: {LTBLUE}{CURRENCY_LONG} @@ -4372,14 +4393,17 @@ STR_VEHICLE_INFO_CAPACITY_CAPACITY STR_VEHICLE_INFO_FEEDER_CARGO_VALUE :{BLACK}Transfer Credits: {LTBLUE}{CURRENCY_LONG} -STR_VEHICLE_DETAILS_SERVICING_INTERVAL_DAYS :{BLACK}Servicing interval: {LTBLUE}{COMMA}{NBSP}days{BLACK} Last service: {LTBLUE}{DATE_LONG} -STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT :{BLACK}Servicing interval: {LTBLUE}{COMMA}%{BLACK} Last service: {LTBLUE}{DATE_LONG} -STR_VEHICLE_DETAILS_INCREASE_SERVICING_INTERVAL_TOOLTIP :{BLACK}Increase servicing interval by 10. Ctrl+Click to increase servicing interval by 5 -STR_VEHICLE_DETAILS_DECREASE_SERVICING_INTERVAL_TOOLTIP :{BLACK}Decrease servicing interval by 10. Ctrl+Click to decrease servicing interval by 5 +STR_VEHICLE_DETAILS_SERVICING_INTERVAL_DAYS :{BLACK}Servicing interval: {LTBLUE}{COMMA}{NBSP}days{BLACK} {STRING1} +STR_VEHICLE_DETAILS_SERVICING_INTERVAL_MINUTES :{BLACK}Servicing interval: {LTBLUE}{COMMA}{NBSP}minutes{BLACK} {STRING1} +STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT :{BLACK}Servicing interval: {LTBLUE}{COMMA}%{BLACK} {STRING1} +STR_VEHICLE_DETAILS_LAST_SERVICE_DATE :Last service: {LTBLUE}{DATE_LONG} +STR_VEHICLE_DETAILS_LAST_SERVICE_MINUTES_AGO :Last service: {LTBLUE}{NUM} minutes ago +STR_VEHICLE_DETAILS_INCREASE_SERVICING_INTERVAL_TOOLTIP :{BLACK}Increase servicing interval by {TKM 10 5}. Ctrl+Click to increase servicing interval by {TKM 5 1} +STR_VEHICLE_DETAILS_DECREASE_SERVICING_INTERVAL_TOOLTIP :{BLACK}Decrease servicing interval by {TKM 10 5}. Ctrl+Click to decrease servicing interval by {TKM 5 1} STR_SERVICE_INTERVAL_DROPDOWN_TOOLTIP :{BLACK}Change servicing interval type STR_VEHICLE_DETAILS_DEFAULT :Default -STR_VEHICLE_DETAILS_DAYS :Days +STR_VEHICLE_DETAILS_TIME :{TKM Days Minutes} STR_VEHICLE_DETAILS_PERCENT :Percentage ###length VEHICLE_TYPES diff --git a/src/misc.cpp b/src/misc.cpp --- a/src/misc.cpp +++ b/src/misc.cpp @@ -112,8 +112,14 @@ void InitializeGame(uint size_x, uint si if (reset_date) { TimerGameCalendar::Date new_date = TimerGameCalendar::ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1); TimerGameCalendar::SetDate(new_date, 0); - /* Keep the economy date synced with the calendar date. */ - TimerGameEconomy::SetDate(new_date.base(), 0); + + if (TimerGameEconomy::UsingWallclockUnits()) { + /* If using wallclock units, start at year 1. */ + TimerGameEconomy::SetDate(TimerGameEconomy::ConvertYMDToDate(1, 0, 1), 0); + } else { + /* Otherwise, we always keep the economy date synced with the calendar date. */ + TimerGameEconomy::SetDate(new_date.base(), 0); + } InitializeOldNames(); } diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -1845,13 +1845,21 @@ void DeleteVehicleOrders(Vehicle *v, boo /** * Clamp the service interval to the correct min/max. The actual min/max values - * depend on whether it's in percent or days. - * @param interval proposed service interval - * @return Clamped service interval + * depend on whether it's in days, minutes, or percent. + * @param interval The proposed service interval. + * @param ispercent Whether the interval is a percent. + * @return The service interval clamped to use the chosen units. */ uint16_t GetServiceIntervalClamped(int interval, bool ispercent) { - return ispercent ? Clamp(interval, MIN_SERVINT_PERCENT, MAX_SERVINT_PERCENT) : Clamp(interval, MIN_SERVINT_DAYS, MAX_SERVINT_DAYS); + /* Service intervals are in percents. */ + if (ispercent) return Clamp(interval, MIN_SERVINT_PERCENT, MAX_SERVINT_PERCENT); + + /* Service intervals are in minutes. */ + if (TimerGameEconomy::UsingWallclockUnits(_game_mode == GM_MENU)) return Clamp(interval, MIN_SERVINT_MINUTES, MAX_SERVINT_MINUTES); + + /* Service intervals are in days. */ + return Clamp(interval, MIN_SERVINT_DAYS, MAX_SERVINT_DAYS); } /** diff --git a/src/order_func.h b/src/order_func.h --- a/src/order_func.h +++ b/src/order_func.h @@ -33,6 +33,13 @@ static const uint DEF_SERVINT_DAYS_SHIPS static const uint MIN_SERVINT_DAYS = 30; static const uint MAX_SERVINT_DAYS = 800; +static const uint DEF_SERVINT_MINUTES_TRAINS = 5; +static const uint DEF_SERVINT_MINUTES_ROADVEH = 5; +static const uint DEF_SERVINT_MINUTES_AIRCRAFT = 3; +static const uint DEF_SERVINT_MINUTES_SHIPS = 12; +static const uint MIN_SERVINT_MINUTES = 1; +static const uint MAX_SERVINT_MINUTES = 30; + static const uint DEF_SERVINT_PERCENT = 50; static const uint MIN_SERVINT_PERCENT = 5; static const uint MAX_SERVINT_PERCENT = 90; diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -735,6 +735,9 @@ bool AfterLoadGame() * must be done before loading sprites as some newgrfs check it */ TimerGameCalendar::SetDate(TimerGameCalendar::date, TimerGameCalendar::date_fract); + /* Only new games can use wallclock units. */ + if (IsSavegameVersionBefore(SLV_ECONOMY_MODE_TIMEKEEPING_UNITS)) _settings_game.economy.timekeeping_units = TKU_CALENDAR; + /* Update economy year. If we don't have a separate economy date saved, follow the calendar date. */ if (IsSavegameVersionBefore(SLV_ECONOMY_DATE)) { TimerGameEconomy::SetDate(TimerGameCalendar::date.base(), TimerGameCalendar::date_fract); diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h --- a/src/saveload/saveload.h +++ b/src/saveload/saveload.h @@ -369,6 +369,7 @@ enum SaveLoadVersion : uint16_t { SLV_WATER_REGION_EVAL_SIMPLIFIED, ///< 325 PR#11750 Simplified Water Region evaluation. SLV_ECONOMY_DATE, ///< 326 PR#10700 Split calendar and economy timers and dates. + SLV_ECONOMY_MODE_TIMEKEEPING_UNITS, ///< 327 PR#11341 Mode to display economy measurements in wallclock units. SL_MAX_VERSION, ///< Highest possible saveload version }; diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -2212,6 +2212,7 @@ static SettingsContainer &GetSettingsTre { SettingsPage *time = environment->Add(new SettingsPage(STR_CONFIG_SETTING_ENVIRONMENT_TIME)); { + time->Add(new SettingEntry("economy.timekeeping_units")); time->Add(new SettingEntry("game_creation.ending_year")); time->Add(new SettingEntry("gui.pause_on_newgame")); time->Add(new SettingEntry("gui.fast_forward_speed_limit")); diff --git a/src/settings_table.cpp b/src/settings_table.cpp --- a/src/settings_table.cpp +++ b/src/settings_table.cpp @@ -130,7 +130,7 @@ static void UpdateConsists(int32_t) /** * Check and update if needed all vehicle service intervals. - * @param new_value Contains 0 if service intervals are in days, otherwise intervals use percents. + * @param new_value Contains 0 if service intervals are in time (days or real-world minutes), otherwise intervals use percents. */ static void UpdateAllServiceInterval(int32_t new_value) { @@ -150,6 +150,12 @@ static void UpdateAllServiceInterval(int vds->servint_roadveh = DEF_SERVINT_PERCENT; vds->servint_aircraft = DEF_SERVINT_PERCENT; vds->servint_ships = DEF_SERVINT_PERCENT; + } else if (TimerGameEconomy::UsingWallclockUnits(_game_mode == GM_MENU)) { + /* Service intervals are in minutes. */ + vds->servint_trains = DEF_SERVINT_MINUTES_TRAINS; + vds->servint_roadveh = DEF_SERVINT_MINUTES_ROADVEH; + vds->servint_aircraft = DEF_SERVINT_MINUTES_AIRCRAFT; + vds->servint_ships = DEF_SERVINT_MINUTES_SHIPS; } else { /* Service intervals are in days. */ vds->servint_trains = DEF_SERVINT_DAYS_TRAINS; @@ -483,4 +489,28 @@ static void UpdateClientConfigValues() } } +/** + * Callback for when the player changes the timekeeping units. + * @param Unused. + */ +static void ChangeTimekeepingUnits(int32_t) +{ + /* If service intervals are in time units (calendar days or real-world minutes), reset them to the correct defaults. */ + if (!_settings_client.company.vehicle.servint_ispercent) { + UpdateAllServiceInterval(0); + } + + InvalidateWindowClassesData(WC_GAME_OPTIONS, 0); +} + +/** + * Pre-callback check when trying to change the timetable mode. This is locked to Seconds when using wallclock units. + * @param Unused. + * @return True if we allow changing the timetable mode. + */ +static bool CanChangeTimetableMode(int32_t &) +{ + return !TimerGameEconomy::UsingWallclockUnits(); +} + /* End - Callback Functions */ diff --git a/src/settings_type.h b/src/settings_type.h --- a/src/settings_type.h +++ b/src/settings_type.h @@ -64,6 +64,12 @@ enum IndustryDensity { ID_END, ///< Number of industry density settings. }; +/** Possible values for the "timekeeping_units" setting. */ +enum TimekeepingUnits : uint8_t { + TKU_CALENDAR = 0, + TKU_WALLCLOCK, +}; + /** Possible values for "use_relay_service" setting. */ enum UseRelayService : uint8_t { URS_NEVER = 0, @@ -551,6 +557,7 @@ struct EconomySettings { uint16_t town_noise_population[4]; ///< population to base decision on noise evaluation (@see town_council_tolerance) bool allow_town_level_crossings; ///< towns are allowed to build level crossings bool infrastructure_maintenance; ///< enable monthly maintenance fee for owner infrastructure + TimekeepingUnits timekeeping_units; ///< time units to use for the game economy, either calendar or wallclock }; struct LinkGraphSettings { diff --git a/src/strgen/strgen_base.cpp b/src/strgen/strgen_base.cpp --- a/src/strgen/strgen_base.cpp +++ b/src/strgen/strgen_base.cpp @@ -377,6 +377,33 @@ void EmitPlural(Buffer *buffer, char *bu EmitWordList(buffer, words, nw); } +/** + * Handle the selection of timekeeping units based on the timekeeping setting. + * This uses the string control character {TKM [value if calendar] [value if wallclock]}, e.g. {TKM month minute}. + * @param buffer The output buffer + * @param buf The input buffer + * @param Unused + */ +void EmitTKM(Buffer* buffer, char* buf, int) +{ + /* The correct number of words is 2, but we'll check for more in case of typos. */ + std::vector words(3, nullptr); + + /* Parse each string. */ + uint nw = 0; + for (nw = 0; nw < 3; nw++) { + words[nw] = ParseWord(&buf); + if (words[nw] == nullptr) break; + } + + /* Warn about the wrong number of parameters. */ + if (nw != 2) { + StrgenFatal("%s: Invalid number of TKM options. Expecting %d, found %d.", _cur_ident, 2, nw); + } + + buffer->AppendUtf8(SCC_TIMEKEEPING_MODE_LIST); + EmitWordList(buffer, words, 2); +} void EmitGender(Buffer *buffer, char *buf, int) { diff --git a/src/strings.cpp b/src/strings.cpp --- a/src/strings.cpp +++ b/src/strings.cpp @@ -746,12 +746,21 @@ struct UnitsLong { }; /** Unit conversions for velocity. */ -static const Units _units_velocity[] = { - { { 1.0 }, STR_UNITS_VELOCITY_IMPERIAL, 0 }, - { { 1.609344 }, STR_UNITS_VELOCITY_METRIC, 0 }, - { { 0.44704 }, STR_UNITS_VELOCITY_SI, 0 }, - { { 0.578125 }, STR_UNITS_VELOCITY_GAMEUNITS, 1 }, - { { 0.868976 }, STR_UNITS_VELOCITY_KNOTS, 0 }, +static const Units _units_velocity_calendar[] = { + { { 1.0 }, STR_UNITS_VELOCITY_IMPERIAL, 0 }, + { { 1.609344 }, STR_UNITS_VELOCITY_METRIC, 0 }, + { { 0.44704 }, STR_UNITS_VELOCITY_SI, 0 }, + { { 0.578125 }, STR_UNITS_VELOCITY_GAMEUNITS_DAY, 1 }, + { { 0.868976 }, STR_UNITS_VELOCITY_KNOTS, 0 }, +}; + +/** Unit conversions for velocity. */ +static const Units _units_velocity_realtime[] = { + { { 1.0 }, STR_UNITS_VELOCITY_IMPERIAL, 0 }, + { { 1.609344 }, STR_UNITS_VELOCITY_METRIC, 0 }, + { { 0.44704 }, STR_UNITS_VELOCITY_SI, 0 }, + { { 0.289352 }, STR_UNITS_VELOCITY_GAMEUNITS_SEC, 1 }, + { { 0.868976 }, STR_UNITS_VELOCITY_KNOTS, 0 }, }; /** Unit conversions for power. */ @@ -802,16 +811,45 @@ static const Units _units_height[] = { { { 1.0 }, STR_UNITS_HEIGHT_SI, 0 }, }; +/** Unit conversions for time in calendar days or wallclock seconds */ +static const Units _units_time_days_or_seconds[] = { + { { 1 }, STR_UNITS_DAYS, 0 }, + { { 2 }, STR_UNITS_SECONDS, 0 }, +}; + +/** Unit conversions for time in calendar months or wallclock minutes */ +static const Units _units_time_months_or_minutes[] = { + { { 1 }, STR_UNITS_MONTHS, 0 }, + { { 1 }, STR_UNITS_MINUTES, 0 }, +}; + +/** Unit conversions for time in calendar years or economic periods */ +static const Units _units_time_years_or_periods[] = { + { { 1 }, STR_UNITS_YEARS, 0 }, + { { 1 }, STR_UNITS_PERIODS, 0 }, +}; + +/** Unit conversions for time in calendar years or wallclock minutes */ +static const Units _units_time_years_or_minutes[] = { + { { 1 }, STR_UNITS_YEARS, 0 }, + { { 12 }, STR_UNITS_MINUTES, 0 }, +}; + /** - * Get index for velocity conversion units for a vehicle type. + * Get the correct velocity units depending on the vehicle type and whether we're using real-time units. * @param type VehicleType to convert velocity for. - * @return Index within velocity conversion units for vehicle type. + * @return The Units for the proper vehicle and time mode. */ -static byte GetVelocityUnits(VehicleType type) +static const Units GetVelocityUnits(VehicleType type) { - if (type == VEH_SHIP || type == VEH_AIRCRAFT) return _settings_game.locale.units_velocity_nautical; + byte setting = (type == VEH_SHIP || type == VEH_AIRCRAFT) ? _settings_game.locale.units_velocity_nautical : _settings_game.locale.units_velocity; - return _settings_game.locale.units_velocity; + assert(setting < lengthof(_units_velocity_calendar)); + assert(setting < lengthof(_units_velocity_realtime)); + + if (TimerGameEconomy::UsingWallclockUnits()) return _units_velocity_realtime[setting]; + + return _units_velocity_calendar[setting]; } /** @@ -824,7 +862,7 @@ uint ConvertSpeedToDisplaySpeed(uint spe /* For historical reasons we don't want to mess with the * conversion for speed. So, don't round it and keep the * original conversion factors instead of the real ones. */ - return _units_velocity[GetVelocityUnits(type)].c.ToDisplay(speed, false); + return GetVelocityUnits(type).c.ToDisplay(speed, false); } /** @@ -834,7 +872,7 @@ uint ConvertSpeedToDisplaySpeed(uint spe */ uint ConvertDisplaySpeedToSpeed(uint speed, VehicleType type) { - return _units_velocity[GetVelocityUnits(type)].c.FromDisplay(speed); + return GetVelocityUnits(type).c.FromDisplay(speed); } /** @@ -844,7 +882,7 @@ uint ConvertDisplaySpeedToSpeed(uint spe */ uint ConvertKmhishSpeedToDisplaySpeed(uint speed, VehicleType type) { - return _units_velocity[GetVelocityUnits(type)].c.ToDisplay(speed * 10, false) / 16; + return GetVelocityUnits(type).c.ToDisplay(speed * 10, false) / 16; } /** @@ -854,7 +892,7 @@ uint ConvertKmhishSpeedToDisplaySpeed(ui */ uint ConvertDisplaySpeedToKmhishSpeed(uint speed, VehicleType type) { - return _units_velocity[GetVelocityUnits(type)].c.FromDisplay(speed * 16, true, 10); + return GetVelocityUnits(type).c.FromDisplay(speed * 16, true, 10); } /** @@ -1334,9 +1372,7 @@ static void FormatString(StringBuilder & int64_t arg = args.GetNextParameter(); // Unpack vehicle type from packed argument to get desired units. VehicleType vt = static_cast(GB(arg, 56, 8)); - byte units = GetVelocityUnits(vt); - assert(units < lengthof(_units_velocity)); - const auto &x = _units_velocity[units]; + const auto &x = GetVelocityUnits(vt); auto tmp_params = MakeParameters(ConvertKmhishSpeedToDisplaySpeed(GB(arg, 0, 56), vt), x.decimal_places); FormatString(builder, GetStringPtr(x.s), tmp_params); break; @@ -1374,6 +1410,43 @@ static void FormatString(StringBuilder & break; } + case SCC_UNITS_DAYS_OR_SECONDS: { // {UNITS_DAYS_OR_SECONDS} + uint8_t realtime = TimerGameEconomy::UsingWallclockUnits(_game_mode == GM_MENU); + const auto &x = _units_time_days_or_seconds[realtime]; + auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); + FormatString(builder, GetStringPtr(x.s), tmp_params); + break; + } + + case SCC_UNITS_MONTHS_OR_MINUTES: { // {UNITS_MONTHS_OR_MINUTES} + uint8_t realtime = TimerGameEconomy::UsingWallclockUnits(_game_mode == GM_MENU); + const auto &x = _units_time_months_or_minutes[realtime]; + auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); + FormatString(builder, GetStringPtr(x.s), tmp_params); + break; + } + + case SCC_UNITS_YEARS_OR_PERIODS: { // {UNITS_YEARS_OR_PERIODS} + uint8_t realtime = TimerGameEconomy::UsingWallclockUnits(_game_mode == GM_MENU); + const auto &x = _units_time_years_or_periods[realtime]; + auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); + FormatString(builder, GetStringPtr(x.s), tmp_params); + break; + } + + case SCC_UNITS_YEARS_OR_MINUTES: { // {UNITS_YEARS_OR_MINUTES} + uint8_t realtime = TimerGameEconomy::UsingWallclockUnits(_game_mode == GM_MENU); + const auto &x = _units_time_years_or_minutes[realtime]; + auto tmp_params = MakeParameters(x.c.ToDisplay(args.GetNextParameter()), x.decimal_places); + FormatString(builder, GetStringPtr(x.s), tmp_params); + break; + } + + case SCC_TIMEKEEPING_MODE_LIST: { // {TKM} + str = ParseStringChoice(str, (uint8_t)TimerGameEconomy::UsingWallclockUnits(_game_mode == GM_MENU), builder); + break; + } + case SCC_COMPANY_NAME: { // {COMPANY} const Company *c = Company::GetIfValid(args.GetNextParameter()); if (c == nullptr) break; diff --git a/src/subsidy_gui.cpp b/src/subsidy_gui.cpp --- a/src/subsidy_gui.cpp +++ b/src/subsidy_gui.cpp @@ -159,7 +159,15 @@ struct SubsidyListWindow : Window { if (IsInsideMM(pos, 0, cap)) { /* Displays the two offered towns */ SetupSubsidyDecodeParam(s, SubsidyDecodeParamType::Gui); - SetDParam(7, TimerGameEconomy::date - ymd.day + s->remaining * 32); + /* If using wallclock units, show minutes remaining. Otherwise show the date when the subsidy ends. */ + if (TimerGameEconomy::UsingWallclockUnits()) { + SetDParam(7, STR_SUBSIDIES_OFFERED_EXPIRY_TIME); + SetDParam(8, s->remaining + 1); // We get the rest of the current economy month for free, since the expiration is checked on each new month. + } else { + SetDParam(7, STR_SUBSIDIES_OFFERED_EXPIRY_DATE); + SetDParam(8, TimerGameEconomy::date - ymd.day + s->remaining * 32); + } + DrawString(tr.left, tr.right, tr.top + pos * GetCharacterHeight(FS_NORMAL), STR_SUBSIDIES_OFFERED_FROM_TO); } pos++; @@ -183,7 +191,15 @@ struct SubsidyListWindow : Window { if (IsInsideMM(pos, 0, cap)) { SetupSubsidyDecodeParam(s, SubsidyDecodeParamType::Gui); SetDParam(7, s->awarded); - SetDParam(8, TimerGameEconomy::date - ymd.day + s->remaining * 32); + /* If using wallclock units, show minutes remaining. Otherwise show the date when the subsidy ends. */ + if (TimerGameEconomy::UsingWallclockUnits()) { + SetDParam(8, STR_SUBSIDIES_SUBSIDISED_EXPIRY_TIME); + SetDParam(9, s->remaining); + } + else { + SetDParam(8, STR_SUBSIDIES_SUBSIDISED_EXPIRY_DATE); + SetDParam(9, TimerGameEconomy::date - ymd.day + s->remaining * 32); + } /* Displays the two connected stations */ DrawString(tr.left, tr.right, tr.top + pos * GetCharacterHeight(FS_NORMAL), STR_SUBSIDIES_SUBSIDISED_FROM_TO); diff --git a/src/table/control_codes.h b/src/table/control_codes.h --- a/src/table/control_codes.h +++ b/src/table/control_codes.h @@ -65,6 +65,12 @@ enum StringControlCode { SCC_VELOCITY, SCC_HEIGHT, + SCC_UNITS_DAYS_OR_SECONDS, + SCC_UNITS_MONTHS_OR_MINUTES, + SCC_UNITS_YEARS_OR_PERIODS, + SCC_UNITS_YEARS_OR_MINUTES, + SCC_TIMEKEEPING_MODE_LIST, + SCC_DATE_TINY, SCC_DATE_SHORT, SCC_DATE_LONG, diff --git a/src/table/settings/company_settings.ini b/src/table/settings/company_settings.ini --- a/src/table/settings/company_settings.ini +++ b/src/table/settings/company_settings.ini @@ -84,7 +84,7 @@ var = vehicle.servint_trains type = SLE_UINT16 flags = SF_PER_COMPANY | SF_GUI_0_IS_SPECIAL def = DEF_SERVINT_DAYS_TRAINS -min = MIN_SERVINT_PERCENT +min = MIN_SERVINT_MINUTES max = MAX_SERVINT_DAYS interval = 1 str = STR_CONFIG_SETTING_SERVINT_TRAINS @@ -98,7 +98,7 @@ var = vehicle.servint_roadveh type = SLE_UINT16 flags = SF_PER_COMPANY | SF_GUI_0_IS_SPECIAL def = DEF_SERVINT_DAYS_ROADVEH -min = MIN_SERVINT_PERCENT +min = MIN_SERVINT_MINUTES max = MAX_SERVINT_DAYS interval = 1 str = STR_CONFIG_SETTING_SERVINT_ROAD_VEHICLES @@ -112,7 +112,7 @@ var = vehicle.servint_ships type = SLE_UINT16 flags = SF_PER_COMPANY | SF_GUI_0_IS_SPECIAL def = DEF_SERVINT_DAYS_SHIPS -min = MIN_SERVINT_PERCENT +min = MIN_SERVINT_MINUTES max = MAX_SERVINT_DAYS interval = 1 str = STR_CONFIG_SETTING_SERVINT_SHIPS @@ -126,7 +126,7 @@ var = vehicle.servint_aircraft type = SLE_UINT16 flags = SF_PER_COMPANY | SF_GUI_0_IS_SPECIAL def = DEF_SERVINT_DAYS_AIRCRAFT -min = MIN_SERVINT_PERCENT +min = MIN_SERVINT_MINUTES max = MAX_SERVINT_DAYS interval = 1 str = STR_CONFIG_SETTING_SERVINT_AIRCRAFT diff --git a/src/table/settings/economy_settings.ini b/src/table/settings/economy_settings.ini --- a/src/table/settings/economy_settings.ini +++ b/src/table/settings/economy_settings.ini @@ -9,6 +9,7 @@ [pre-amble] static void TownFoundingChanged(int32_t new_value); +static void ChangeTimekeepingUnits(int32_t new_value); static const SettingVariant _economy_settings_table[] = { [post-amble] @@ -280,3 +281,16 @@ str = STR_CONFIG_SETTING_INFRASTRUC strhelp = STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE_HELPTEXT post_cb = [](auto) { InvalidateWindowClassesData(WC_COMPANY_INFRASTRUCTURE); } cat = SC_BASIC + +[SDT_VAR] +var = economy.timekeeping_units +type = SLE_UINT8 +flags = SF_GUI_DROPDOWN | SF_NEWGAME_ONLY | SF_SCENEDIT_TOO +def = TKU_CALENDAR +min = TKU_CALENDAR +max = TKU_WALLCLOCK +str = STR_CONFIG_SETTING_TIMEKEEPING_UNITS +strval = STR_CONFIG_SETTING_TIMEKEEPING_UNITS_CALENDAR +strhelp = STR_CONFIG_SETTING_TIMEKEEPING_UNITS_HELPTEXT +post_cb = ChangeTimekeepingUnits +cat = SC_BASIC diff --git a/src/table/settings/gui_settings.ini b/src/table/settings/gui_settings.ini --- a/src/table/settings/gui_settings.ini +++ b/src/table/settings/gui_settings.ini @@ -7,6 +7,7 @@ ; GUI settings as stored in the main configuration file ("openttd.cfg"). [pre-amble] +static bool CanChangeTimetableMode(int32_t &new_value); static void v_PositionMainToolbar(int32_t new_value); static void v_PositionStatusbar(int32_t new_value); static void RedrawSmallmap(int32_t new_value); @@ -430,6 +431,7 @@ max = 2 str = STR_CONFIG_SETTING_TIMETABLE_MODE strhelp = STR_CONFIG_SETTING_TIMETABLE_MODE_HELPTEXT strval = STR_CONFIG_SETTING_TIMETABLE_MODE_DAYS +pre_cb = CanChangeTimetableMode post_cb = [](auto) { InvalidateWindowClassesData(WC_VEHICLE_TIMETABLE, VIWD_MODIFY_ORDERS); } cat = SC_ADVANCED diff --git a/src/table/strgen_tables.h b/src/table/strgen_tables.h --- a/src/table/strgen_tables.h +++ b/src/table/strgen_tables.h @@ -30,6 +30,7 @@ struct CmdStruct { }; extern void EmitSingleChar(Buffer *buffer, char *buf, int value); +extern void EmitTKM(Buffer* buffer, char* buf, int value); extern void EmitPlural(Buffer *buffer, char *buf, int value); extern void EmitGender(Buffer *buffer, char *buf, int value); @@ -88,6 +89,13 @@ static const CmdStruct _cmd_structs[] = {"VELOCITY", EmitSingleChar, SCC_VELOCITY, 1, 0, C_NONE}, {"HEIGHT", EmitSingleChar, SCC_HEIGHT, 1, 0, C_NONE}, + {"UNITS_DAYS_OR_SECONDS", EmitSingleChar, SCC_UNITS_DAYS_OR_SECONDS, 1, 0, C_NONE}, + {"UNITS_MONTHS_OR_MINUTES", EmitSingleChar, SCC_UNITS_MONTHS_OR_MINUTES, 1, 0, C_NONE}, + {"UNITS_YEARS_OR_PERIODS", EmitSingleChar, SCC_UNITS_YEARS_OR_PERIODS, 1, 0, C_NONE}, + {"UNITS_YEARS_OR_MINUTES", EmitSingleChar, SCC_UNITS_YEARS_OR_MINUTES, 1, 0, C_NONE}, + + {"TKM", EmitTKM, 0, 0, -1, C_DONTCOUNT}, // Timekeeping mode string selection, e.g. "{TKM month minute}" + {"P", EmitPlural, 0, 0, -1, C_DONTCOUNT}, // plural specifier {"G", EmitGender, 0, 0, -1, C_DONTCOUNT}, // gender specifier diff --git a/src/timer/timer_game_calendar.cpp b/src/timer/timer_game_calendar.cpp --- a/src/timer/timer_game_calendar.cpp +++ b/src/timer/timer_game_calendar.cpp @@ -34,6 +34,30 @@ TimerGameCalendar::Date TimerGameCalenda TimerGameCalendar::DateFract TimerGameCalendar::date_fract = {}; /** + * Converts a Date to a Year, Month & Day. + * @param date the date to convert from + * @returns YearMonthDay representation of the Date. + */ +/* static */ TimerGameCalendar::YearMonthDay TimerGameCalendar::ConvertDateToYMD(TimerGameCalendar::Date date) +{ + /* This wrapper function only exists because economy time sometimes does things differently, when using wallclock units. */ + return CalendarConvertDateToYMD(date); +} + +/** + * Converts a tuple of Year, Month and Day to a Date. + * @param year is a number between 0..MAX_YEAR + * @param month is a number between 0..11 + * @param day is a number between 1..31 + * @returns The equivalent date. + */ +/* static */ TimerGameCalendar::Date TimerGameCalendar::ConvertYMDToDate(TimerGameCalendar::Year year, TimerGameCalendar::Month month, TimerGameCalendar::Day day) +{ + /* This wrapper function only exists because economy time sometimes does things differently, when using wallclock units. */ + return CalendarConvertYMDToDate(year, month, day); +} + +/** * Set the date. * @param date New date * @param fract The number of ticks that have passed on this date. diff --git a/src/timer/timer_game_calendar.h b/src/timer/timer_game_calendar.h --- a/src/timer/timer_game_calendar.h +++ b/src/timer/timer_game_calendar.h @@ -34,6 +34,8 @@ public: static Date date; ///< Current date in days (day counter). static DateFract date_fract; ///< Fractional part of the day. + static YearMonthDay ConvertDateToYMD(Date date); + static Date ConvertYMDToDate(Year year, Month month, Day day); static void SetDate(Date date, DateFract fract); }; diff --git a/src/timer/timer_game_common.cpp b/src/timer/timer_game_common.cpp --- a/src/timer/timer_game_common.cpp +++ b/src/timer/timer_game_common.cpp @@ -63,7 +63,7 @@ static constexpr uint16_t _accum_days_fo * @returns YearMonthDay representation of the Date. */ template -/* static */ typename TimerGame::YearMonthDay TimerGame::ConvertDateToYMD(Date date) +/* static */ typename TimerGame::YearMonthDay TimerGame::CalendarConvertDateToYMD(Date date) { /* Year determination in multiple steps to account for leap * years. First do the large steps, then the smaller ones. @@ -118,9 +118,10 @@ template * @param year is a number between 0..MAX_YEAR * @param month is a number between 0..11 * @param day is a number between 1..31 + * @returns The equivalent date. */ template -/* static */ typename TimerGame::Date TimerGame::ConvertYMDToDate(Year year, Month month, Day day) +/* static */ typename TimerGame::Date TimerGame::CalendarConvertYMDToDate(Year year, Month month, Day day) { /* Day-offset in a leap year */ int days = _accum_days_for_month[month] + day - 1; @@ -133,8 +134,8 @@ template /* Create instances of the two template variants that we have. * This is needed, as this templated functions are not in a header-file. */ -template TimerGame::YearMonthDay TimerGame::ConvertDateToYMD(Date date); -template TimerGame::YearMonthDay TimerGame::ConvertDateToYMD(Date date); +template TimerGame::YearMonthDay TimerGame::CalendarConvertDateToYMD(Date date); +template TimerGame::YearMonthDay TimerGame::CalendarConvertDateToYMD(Date date); -template TimerGame::Date TimerGame::ConvertYMDToDate(Year year, Month month, Day day); -template TimerGame::Date TimerGame::ConvertYMDToDate(Year year, Month month, Day day); +template TimerGame::Date TimerGame::CalendarConvertYMDToDate(Year year, Month month, Day day); +template TimerGame::Date TimerGame::CalendarConvertYMDToDate(Year year, Month month, Day day); diff --git a/src/timer/timer_game_common.h b/src/timer/timer_game_common.h --- a/src/timer/timer_game_common.h +++ b/src/timer/timer_game_common.h @@ -66,8 +66,8 @@ public: return year_as_int % 4 == 0 && (year_as_int % 100 != 0 || year_as_int % 400 == 0); } - static YearMonthDay ConvertDateToYMD(Date date); - static Date ConvertYMDToDate(Year year, Month month, Day day); + static YearMonthDay CalendarConvertDateToYMD(Date date); + static Date CalendarConvertYMDToDate(Year year, Month month, Day day); /** * Calculate the year of a given date. diff --git a/src/timer/timer_game_economy.cpp b/src/timer/timer_game_economy.cpp --- a/src/timer/timer_game_economy.cpp +++ b/src/timer/timer_game_economy.cpp @@ -39,6 +39,41 @@ TimerGameEconomy::Date TimerGameEconomy: TimerGameEconomy::DateFract TimerGameEconomy::date_fract = {}; /** + * Converts a Date to a Year, Month & Day. + * @param date the date to convert from + * @returns YearMonthDay representation of the Date. + */ +/* static */ TimerGameEconomy::YearMonthDay TimerGameEconomy::ConvertDateToYMD(TimerGameEconomy::Date date) +{ + /* If we're not using wallclock units, we keep the economy date in sync with the calendar. */ + if (!UsingWallclockUnits()) return CalendarConvertDateToYMD(date); + + /* If we're using wallclock units, economy months have 30 days and an economy year has 360 days. */ + TimerGameEconomy::YearMonthDay ymd; + ymd.year = TimerGameEconomy::date.base() / EconomyTime::DAYS_IN_ECONOMY_YEAR; + ymd.month = (TimerGameEconomy::date.base() % EconomyTime::DAYS_IN_ECONOMY_YEAR) / EconomyTime::DAYS_IN_ECONOMY_MONTH; + ymd.day = TimerGameEconomy::date.base() % EconomyTime::DAYS_IN_ECONOMY_MONTH; + return ymd; +} + +/** + * Converts a tuple of Year, Month and Day to a Date. + * @param year is a number between 0..MAX_YEAR + * @param month is a number between 0..11 + * @param day is a number between 1..31 + * @returns The equivalent date. + */ +/* static */ TimerGameEconomy::Date TimerGameEconomy::ConvertYMDToDate(TimerGameEconomy::Year year, TimerGameEconomy::Month month, TimerGameEconomy::Day day) +{ + /* If we're not using wallclock units, we keep the economy date in sync with the calendar. */ + if (!UsingWallclockUnits()) return CalendarConvertYMDToDate(year, month, day); + + /* If we're using wallclock units, economy months have 30 days and an economy year has 360 days. */ + const int total_months = (year.base() * EconomyTime::MONTHS_IN_YEAR) + month; + return (total_months * EconomyTime::DAYS_IN_ECONOMY_MONTH) + day - 1; // Day is 1-indexed but Date is 0-indexed, hence the - 1. +} + +/** * Set the date. * @param date The new date * @param fract The number of ticks that have passed on this date. @@ -54,6 +89,18 @@ TimerGameEconomy::DateFract TimerGameEco TimerGameEconomy::month = ymd.month; } +/** + * Check if we are using wallclock units. + * @param newgame Should we check the settings for a new game (since we are in the main menu)? + * @return True if the game is using wallclock units, or false if the game is using calendar units. + */ +/* static */ bool TimerGameEconomy::UsingWallclockUnits(bool newgame) +{ + if (newgame) return (_settings_newgame.economy.timekeeping_units == TKU_WALLCLOCK); + + return (_settings_game.economy.timekeeping_units == TKU_WALLCLOCK); +} + template<> void IntervalTimer::Elapsed(TimerGameEconomy::TElapsed trigger) { diff --git a/src/timer/timer_game_economy.h b/src/timer/timer_game_economy.h --- a/src/timer/timer_game_economy.h +++ b/src/timer/timer_game_economy.h @@ -37,12 +37,19 @@ public: static Date date; ///< Current date in days (day counter). static DateFract date_fract; ///< Fractional part of the day. + static YearMonthDay ConvertDateToYMD(Date date); + static Date ConvertYMDToDate(Year year, Month month, Day day); static void SetDate(Date date, DateFract fract); + static bool UsingWallclockUnits(bool newgame = false); }; /** * Storage class for Economy time constants. */ -class EconomyTime : public TimerGameConst {}; +class EconomyTime : public TimerGameConst { +public: + static constexpr int DAYS_IN_ECONOMY_YEAR = 360; ///< Days in an economy year, when in wallclock timekeeping mode. + static constexpr int DAYS_IN_ECONOMY_MONTH = 30; ///< Days in an economy month, when in wallclock timekeeping mode. +}; #endif /* TIMER_GAME_ECONOMY_H */ diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -215,6 +215,12 @@ struct TimetableWindow : Window { { this->CreateNestedTree(); this->vscroll = this->GetScrollbar(WID_VT_SCROLLBAR); + + /* When using wallclock units, we must ensure the client displays timetables in seconds. */ + if (TimerGameEconomy::UsingWallclockUnits()) { + _settings_client.gui.timetable_mode = TimetableMode::Seconds; + } + this->UpdateSelectionStates(); this->FinishInitNested(window_number); diff --git a/src/vehicle.cpp b/src/vehicle.cpp --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -195,10 +195,17 @@ bool Vehicle::NeedsServicing() const /* Are we ready for the next service cycle? */ const Company *c = Company::Get(this->owner); - if (this->ServiceIntervalIsPercent() ? - (this->reliability >= this->GetEngine()->reliability * (100 - this->GetServiceInterval()) / 100) : - (this->date_of_last_service + this->GetServiceInterval() >= TimerGameEconomy::date)) { - return false; + + /* Service intervals can be measured in different units, which we handle individually. */ + if (this->ServiceIntervalIsPercent()) { + /* Service interval is in percents. */ + if (this->reliability >= this->GetEngine()->reliability * (100 - this->GetServiceInterval()) / 100) return false; + } else if (TimerGameEconomy::UsingWallclockUnits()) { + /* Service interval is in minutes. */ + if (this->date_of_last_service + (this->GetServiceInterval() * EconomyTime::DAYS_IN_ECONOMY_MONTH) >= TimerGameEconomy::date) return false; + } else { + /* Service interval is in days. */ + if (this->date_of_last_service + this->GetServiceInterval() >= TimerGameEconomy::date) return false; } /* If we're servicing anyway, because we have not disabled servicing when diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -2299,7 +2299,7 @@ extern void DrawAircraftDetails(const Ai static StringID _service_interval_dropdown[] = { STR_VEHICLE_DETAILS_DEFAULT, - STR_VEHICLE_DETAILS_DAYS, + STR_VEHICLE_DETAILS_TIME, STR_VEHICLE_DETAILS_PERCENT, INVALID_STRING_ID, }; @@ -2431,7 +2431,17 @@ struct VehicleDetailsWindow : Window { case WID_VD_SERVICING_INTERVAL: SetDParamMaxValue(0, MAX_SERVINT_DAYS); // Roughly the maximum interval - SetDParamMaxValue(1, TimerGameEconomy::DateAtStartOfYear(EconomyTime::MAX_YEAR)); // Roughly the maximum year + + /* Do we show the last serviced value as a date or minutes since service? */ + if (TimerGameEconomy::UsingWallclockUnits()) { + SetDParam(1, STR_VEHICLE_DETAILS_LAST_SERVICE_MINUTES_AGO); + /*/ Vehicle was last serviced at year 0, and we're at max year */ + SetDParamMaxValue(2, EconomyTime::MONTHS_IN_YEAR * EconomyTime::MAX_YEAR.base()); + } else { + SetDParam(1, STR_VEHICLE_DETAILS_LAST_SERVICE_DATE); + /*/ Vehicle was last serviced at year 0, and we're at max year */ + SetDParamMaxValue(2, TimerGameEconomy::DateAtStartOfYear(EconomyTime::MAX_YEAR)); + } size->width = std::max( GetStringBoundingBox(STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT).width, GetStringBoundingBox(STR_VEHICLE_DETAILS_SERVICING_INTERVAL_DAYS).width @@ -2572,8 +2582,22 @@ struct VehicleDetailsWindow : Window { case WID_VD_SERVICING_INTERVAL: { /* Draw service interval text */ Rect tr = r.Shrink(WidgetDimensions::scaled.framerect); + SetDParam(0, v->GetServiceInterval()); - SetDParam(1, v->date_of_last_service); + + /* We're using wallclock units. Show minutes since last serviced. */ + if (TimerGameEconomy::UsingWallclockUnits()) { + int minutes_since_serviced = (TimerGameEconomy::date - v->date_of_last_service).base() / EconomyTime::DAYS_IN_ECONOMY_MONTH; + SetDParam(1, STR_VEHICLE_DETAILS_LAST_SERVICE_MINUTES_AGO); + SetDParam(2, minutes_since_serviced); + DrawString(tr.left, tr.right, CenterBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)), + v->ServiceIntervalIsPercent() ? STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT : STR_VEHICLE_DETAILS_SERVICING_INTERVAL_MINUTES); + break; + } + + /* We're using calendar dates. Show the date of last service. */ + SetDParam(1, STR_VEHICLE_DETAILS_LAST_SERVICE_DATE); + SetDParam(2, v->date_of_last_service); DrawString(tr.left, tr.right, CenterBounds(r.top, r.bottom, GetCharacterHeight(FS_NORMAL)), v->ServiceIntervalIsPercent() ? STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT : STR_VEHICLE_DETAILS_SERVICING_INTERVAL_DAYS); break; @@ -2597,7 +2621,7 @@ struct VehicleDetailsWindow : Window { WID_VD_DECREASE_SERVICING_INTERVAL); StringID str = v->ServiceIntervalIsCustom() ? - (v->ServiceIntervalIsPercent() ? STR_VEHICLE_DETAILS_PERCENT : STR_VEHICLE_DETAILS_DAYS) : + (v->ServiceIntervalIsPercent() ? STR_VEHICLE_DETAILS_PERCENT : STR_VEHICLE_DETAILS_TIME) : STR_VEHICLE_DETAILS_DEFAULT; this->GetWidget(WID_VD_SERVICE_INTERVAL_DROPDOWN)->widget_data = str; @@ -2609,7 +2633,7 @@ struct VehicleDetailsWindow : Window { switch (widget) { case WID_VD_INCREASE_SERVICING_INTERVAL: // increase int case WID_VD_DECREASE_SERVICING_INTERVAL: { // decrease int - int mod = _ctrl_pressed ? 5 : 10; + int mod = TimerGameEconomy::UsingWallclockUnits() ? (_ctrl_pressed ? 1 : 5) : (_ctrl_pressed ? 5 : 10); const Vehicle *v = Vehicle::Get(this->window_number); mod = (widget == WID_VD_DECREASE_SERVICING_INTERVAL) ? -mod : mod; diff --git a/src/widgets/graph_widget.h b/src/widgets/graph_widget.h --- a/src/widgets/graph_widget.h +++ b/src/widgets/graph_widget.h @@ -27,6 +27,7 @@ enum CompanyValueWidgets : WidgetID { WID_CV_BACKGROUND, ///< Background of the window. WID_CV_GRAPH, ///< Graph itself. WID_CV_RESIZE, ///< Resize button. + WID_CV_FOOTER, ///< Footer. }; /** Widget of the #PerformanceHistoryGraphWindow class. */ @@ -36,6 +37,7 @@ enum PerformanceHistoryGraphWidgets : Wi WID_PHG_BACKGROUND, ///< Background of the window. WID_PHG_GRAPH, ///< Graph itself. WID_PHG_RESIZE, ///< Resize button. + WID_PHG_FOOTER, ///< Footer. }; /** Widget of the #PaymentRatesGraphWindow class. */