From a48e15dce04b4fb01a81a44bb4d0b3de22257013 Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Thu, 15 Feb 2024 19:33:01 +0000 Subject: [PATCH] Add wallclock time settings --- src/cheat_gui.cpp | 2 +- src/date.cpp | 36 +++++++++-- src/date_gui.cpp | 2 +- src/date_type.h | 37 +++++++++++ src/misc.cpp | 4 +- src/newgrf_industries.cpp | 1 + src/order_cmd.cpp | 16 +++-- src/order_func.h | 7 ++ src/saveload/afterload.cpp | 5 ++ src/settings.cpp | 85 +++++++++++++++++++++++++ src/settings_gui.cpp | 2 + src/settings_type.h | 10 ++- src/subsidy_gui.cpp | 20 +++++- src/table/settings/economy_settings.ini | 32 +++++++++- src/vehicle_gui.cpp | 62 ++++++++++++++---- 15 files changed, 294 insertions(+), 27 deletions(-) diff --git a/src/cheat_gui.cpp b/src/cheat_gui.cpp index dae6298804..e5af55b599 100644 --- a/src/cheat_gui.cpp +++ b/src/cheat_gui.cpp @@ -113,7 +113,7 @@ static int32_t ClickChangeDateCheat(int32_t p1, int32_t p2) /* Change the date. */ CalTime::Detail::SetDate(new_date, CalTime::CurDateFract()); - { + if (!EconTime::UsingWallclockUnits()) { EconTime::Date new_econ_date = new_date.base(); EconTime::DateFract new_econ_date_fract = CalTime::CurDateFract(); diff --git a/src/date.cpp b/src/date.cpp index f2621f149f..a09513e0c2 100644 --- a/src/date.cpp +++ b/src/date.cpp @@ -246,21 +246,37 @@ CalTime::Date CalTime::ConvertYMDToDate(CalTime::Year year, CalTime::Month month EconTime::YearMonthDay EconTime::ConvertDateToYMD(EconTime::Date date) { - /* Process the same as calendar time (for now) */ + if (EconTime::UsingWallclockUnits()) { + /* If we're using wallclock units, economy months have 30 days and an economy year has 360 days. */ + EconTime::YearMonthDay ymd; + ymd.year =date.base() / EconTime::DAYS_IN_ECONOMY_WALLCLOCK_YEAR; + ymd.month = (date.base() % EconTime::DAYS_IN_ECONOMY_WALLCLOCK_YEAR) / EconTime::DAYS_IN_ECONOMY_WALLCLOCK_MONTH; + ymd.day = date.base() % EconTime::DAYS_IN_ECONOMY_WALLCLOCK_MONTH; + return ymd; + } + + /* Process the same as calendar time */ CalTime::YearMonthDay ymd = CalTime::ConvertDateToYMD(date.base()); return { ymd.year.base(), ymd.month, ymd.day }; } EconTime::Date EconTime::ConvertYMDToDate(EconTime::Year year, EconTime::Month month, EconTime::Day day) { - /* Process the same as calendar time (for now) */ + if (EconTime::UsingWallclockUnits()) { + /* If we're using wallclock units, economy months have 30 days and an economy year has 360 days. */ + const int total_months = (year.base() * MONTHS_IN_YEAR) + month; + return (total_months * EconTime::DAYS_IN_ECONOMY_WALLCLOCK_MONTH) + day - 1; // Day is 1-indexed but Date is 0-indexed, hence the - 1. + } + + /* Process the same as calendar time */ return CalTime::ConvertYMDToDate(year.base(), month, day).base(); } bool EconTime::UsingWallclockUnits(bool newgame) { - /* Always return false (for now) */ - return false; + if (newgame) return (_settings_newgame.economy.timekeeping_units == TKU_WALLCLOCK); + + return (_settings_game.economy.timekeeping_units == TKU_WALLCLOCK); } /** Functions used by the IncreaseDate function */ @@ -397,6 +413,18 @@ static void OnNewEconomyDay() static void IncreaseCalendarDate() { + /* If calendar day progress is frozen, don't try to advance time. */ + if (_settings_game.economy.minutes_per_calendar_year == CalTime::FROZEN_MINUTES_PER_YEAR) return; + + /* If we are using a non-default calendar progression speed, we need to check the sub_date_fract before updating date_fract. */ + if (_settings_game.economy.minutes_per_calendar_year != CalTime::DEF_MINUTES_PER_YEAR) { + CalTime::Detail::now.sub_date_fract++; + + /* Check if we are ready to increment date_fract */ + if (CalTime::Detail::now.sub_date_fract < (DAY_TICKS * _settings_game.economy.minutes_per_calendar_year) / CalTime::DEF_MINUTES_PER_YEAR) return; + } + CalTime::Detail::now.sub_date_fract = 0; + CalTime::Detail::now.cal_date_fract++; if (CalTime::Detail::now.cal_date_fract < DAY_TICKS) return; CalTime::Detail::now.cal_date_fract = 0; diff --git a/src/date_gui.cpp b/src/date_gui.cpp index 4456878db7..f1c2e3b776 100644 --- a/src/date_gui.cpp +++ b/src/date_gui.cpp @@ -124,7 +124,7 @@ struct SetDateWindow : Window { break; case WID_SD_YEAR: - SetDParamMaxValue(0, this->max_year.base()); + SetDParamMaxValue(0, this->max_year); d = maxdim(d, GetStringBoundingBox(STR_JUST_INT)); break; } diff --git a/src/date_type.h b/src/date_type.h index 2e223ac7b5..a7986ef930 100644 --- a/src/date_type.h +++ b/src/date_type.h @@ -176,6 +176,10 @@ struct CalTime : public DateDetail::BaseTime { static State NewState(Year year); }; + static constexpr int DEF_MINUTES_PER_YEAR = 12; + static constexpr int FROZEN_MINUTES_PER_YEAR = 0; + static constexpr int MAX_MINUTES_PER_YEAR = 10080; // One week of real time. The actual max that doesn't overflow TimerGameCalendar::sub_date_fract is 10627, but this is neater. + static inline const YearMonthDay &CurYMD() { return Detail::now.cal_ymd; } static inline Year CurYear() { return Detail::now.cal_ymd.year; } static inline Month CurMonth() { return Detail::now.cal_ymd.month; } @@ -213,20 +217,42 @@ struct CalTime : public DateDetail::BaseTime { }; struct EconTime : public DateDetail::BaseTime { + using ParentBaseTime = DateDetail::BaseTime; + /* Use a state struct to make backup/restore/init simpler */ struct State { YearMonthDay econ_ymd; Date econ_date; DateFract econ_date_fract; }; + + static constexpr int DAYS_IN_ECONOMY_WALLCLOCK_YEAR = 360; ///< Days in an economy year, when in wallclock timekeeping mode. + static constexpr int DAYS_IN_ECONOMY_WALLCLOCK_MONTH = 30; ///< Days in an economy month, when in wallclock timekeeping mode. + /* Use a detail struct/namespace to more easily control writes */ struct Detail { static State now; static void SetDate(Date date, DateFract fract); static State NewState(Year year); + + /** + * Calculate the date of the first day of a given year. + * @param year the year to get the first day of. + * @return the date (when using wallclock 30-day months). + */ + static constexpr Date DateAtStartOfWallclockModeYear(Year year) + { + return DAYS_IN_ECONOMY_WALLCLOCK_YEAR * year.base(); + } }; + /** + * The offset in days from the '_date == 0' till + * 'ConvertYMDToDate(ORIGINAL_BASE_YEAR, 0, 1)', when using wallclock 30-day months + */ + static constexpr Date DAYS_TILL_ORIGINAL_BASE_YEAR_WALLCLOCK_MODE = DAYS_IN_ECONOMY_WALLCLOCK_YEAR * ORIGINAL_BASE_YEAR.base(); + static inline const YearMonthDay &CurYMD() { return Detail::now.econ_ymd; } static inline Year CurYear() { return Detail::now.econ_ymd.year; } static inline Month CurMonth() { return Detail::now.econ_ymd.month; } @@ -244,6 +270,17 @@ struct EconTime : public DateDetail::BaseTime { } static bool UsingWallclockUnits(bool newgame = false); + + /** + * Calculate the date of the first day of a given year. + * @param year the year to get the first day of. + * @return the date. + */ + static inline Date DateAtStartOfYear(Year year) + { + if (UsingWallclockUnits()) return Detail::DateAtStartOfWallclockModeYear(year); + return ParentBaseTime::Detail::DateAtStartOfCalendarYear(year); + } }; namespace DateDetail { diff --git a/src/misc.cpp b/src/misc.cpp index 1ec8b4f0fa..93efc983b1 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -158,7 +158,9 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin if (reset_date) { CalTime::Detail::SetDate(CalTime::ConvertYMDToDate(_settings_game.game_creation.starting_year, 0, 1), 0); - { + if (EconTime::UsingWallclockUnits()) { + EconTime::Detail::SetDate(EconTime::DAYS_TILL_ORIGINAL_BASE_YEAR_WALLCLOCK_MODE, 0); + } else { EconTime::Detail::SetDate(CalTime::CurDate().base(), 0); } InitializeOldNames(); diff --git a/src/newgrf_industries.cpp b/src/newgrf_industries.cpp index f3f48512ce..1fbd0c14d3 100644 --- a/src/newgrf_industries.cpp +++ b/src/newgrf_industries.cpp @@ -431,6 +431,7 @@ uint32_t IndustriesScopeResolver::GetCountAndDistanceOfClosestInstance(byte para case 0xB3: return this->industry->construction_type; // Construction type case 0xB4: { EconTime::Date *latest = std::max_element(this->industry->last_cargo_accepted_at, endof(this->industry->last_cargo_accepted_at)); + if (EconTime::UsingWallclockUnits()) return ClampTo((*latest) - EconTime::DAYS_TILL_ORIGINAL_BASE_YEAR_WALLCLOCK_MODE); return ClampTo((*latest) - EconTime::DAYS_TILL_ORIGINAL_BASE_YEAR); // Date last cargo accepted since 1920 (in days) } } diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index cda9220433..88914778a6 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -2962,13 +2962,21 @@ void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist, bool reset_order_indic /** * 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 (EconTime::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 index e8be38ff56..14c8c9181f 100644 --- a/src/order_func.h +++ b/src/order_func.h @@ -42,6 +42,13 @@ static const uint DEF_SERVINT_DAYS_SHIPS = 360; 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 index 6cf2b3ece8..343c5a80d4 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -873,6 +873,11 @@ bool AfterLoadGame() * must be done before loading sprites as some newgrfs check it */ CalTime::Detail::SetDate(CalTime::CurDate(), CalTime::CurDateFract()); + /* Only new games can use wallclock units. */ + if (SlXvIsFeatureMissing(XSLFI_VARIABLE_DAY_LENGTH, 5) && IsSavegameVersionBefore(SLV_ECONOMY_MODE_TIMEKEEPING_UNITS)) { + _settings_game.economy.timekeeping_units = TKU_CALENDAR; + } + if (SlXvIsFeaturePresent(XSLFI_VARIABLE_DAY_LENGTH, 5) || !IsSavegameVersionBefore(SLV_ECONOMY_DATE)) { EconTime::Detail::SetDate(EconTime::CurDate(), EconTime::CurDateFract()); } else { diff --git a/src/settings.cpp b/src/settings.cpp index 3b4f891c71..181f70e41b 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1079,6 +1079,12 @@ static void UpdateAllServiceInterval(int32_t new_value) vds->servint_roadveh = DEF_SERVINT_PERCENT; vds->servint_aircraft = DEF_SERVINT_PERCENT; vds->servint_ships = DEF_SERVINT_PERCENT; + } else if (EconTime::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; @@ -1127,6 +1133,85 @@ static void UpdateServiceInterval(VehicleType type, int32_t new_value) SetWindowClassesDirty(WC_VEHICLE_DETAILS); } +/** + * 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); + } + + /* If we are using calendar timekeeping, "minutes per year" must be default. */ + if (_game_mode == GM_MENU && !EconTime::UsingWallclockUnits(true)) { + _settings_newgame.economy.minutes_per_calendar_year = CalTime::DEF_MINUTES_PER_YEAR; + } + + InvalidateWindowClassesData(WC_GAME_OPTIONS, 0); + + /* It is possible to change these units in Scenario Editor. We must set the economy date appropriately. */ + if (_game_mode == GM_EDITOR) { + EconTime::Date new_economy_date; + EconTime::DateFract new_economy_date_fract; + + if (EconTime::UsingWallclockUnits()) { + /* If the new mode is wallclock units, set the economy year back to 1. */ + new_economy_date = EconTime::DAYS_TILL_ORIGINAL_BASE_YEAR_WALLCLOCK_MODE; + new_economy_date_fract = 0; + } else { + /* If the new mode is calendar units, sync the economy year with the calendar year. */ + new_economy_date = CalTime::CurDate().base(); + new_economy_date_fract = CalTime::CurDateFract(); + } + + /* If you open a savegame as a scenario, there may already be link graphs and/or vehicles. These use economy date. */ + LinkGraphSchedule::instance.ShiftDates(new_economy_date - EconTime::CurDate()); + ShiftVehicleDates(new_economy_date - EconTime::CurDate()); + + /* Only change the date after changing cached values above. */ + EconTime::Detail::SetDate(new_economy_date, new_economy_date_fract); + UpdateOrderUIOnDateChange(); + } + + UpdateEffectiveDayLengthFactor(); +} + +/** + * Callback after the player changes the minutes per year. + * @param new_value The intended new value of the setting, used for clamping. + */ +static void ChangeMinutesPerYear(int32_t new_value) +{ + /* We don't allow setting Minutes Per Year below default, unless it's to 0 for frozen calendar time. */ + if (new_value < CalTime::DEF_MINUTES_PER_YEAR) { + int clamped; + + /* If the new value is 1, we're probably at 0 and trying to increase the value, so we should jump up to default. */ + if (new_value == 1) { + clamped = CalTime::DEF_MINUTES_PER_YEAR; + } else { + clamped = CalTime::FROZEN_MINUTES_PER_YEAR; + } + + /* Override the setting with the clamped value. */ + if (_game_mode == GM_MENU) { + _settings_newgame.economy.minutes_per_calendar_year = clamped; + } else { + _settings_game.economy.minutes_per_calendar_year = clamped; + } + } + + /* If the setting value is not the default, force the game to use wallclock timekeeping units. + * This can only happen in the menu, since the pre_cb ensures this setting can only be changed there, or if we're already using wallclock units. + */ + if (_game_mode == GM_MENU && (_settings_newgame.economy.minutes_per_calendar_year != CalTime::DEF_MINUTES_PER_YEAR)) { + _settings_newgame.economy.timekeeping_units = TKU_WALLCLOCK; + InvalidateWindowClassesData(WC_GAME_OPTIONS, 0); + } +} + static void TrainAccelerationModelChanged(int32_t new_value) { for (Train *t : Train::Iterate()) { diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 23126a2d42..21022854ed 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -2432,6 +2432,8 @@ static SettingsContainer &GetSettingsTree() { SettingsPage *time = environment->Add(new SettingsPage(STR_CONFIG_SETTING_ENVIRONMENT_TIME)); { + time->Add(new SettingEntry("economy.timekeeping_units")); + time->Add(new SettingEntry("economy.minutes_per_calendar_year")); 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_type.h b/src/settings_type.h index efdb4f70c2..dbe4b99e08 100644 --- 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, @@ -763,6 +769,8 @@ struct EconomySettings { uint8_t town_max_road_slope; ///< maximum number of consecutive sloped road tiles which towns are allowed to build bool allow_town_bridges; ///< towns are allowed to build bridges 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 + uint16_t minutes_per_calendar_year; ///< minutes per calendar year. Special value 0 means that calendar time is frozen. uint16_t town_cargo_scale; ///< scale cargo production of towns by this percentage. uint16_t industry_cargo_scale; ///< scale cargo production of industries by this percentage. CargoScalingMode town_cargo_scale_mode; ///< scaling mode for town cargo. @@ -890,7 +898,7 @@ struct GameSettings { uint8_t EffectiveDayLengthFactor() const { - return this->economy.day_length_factor; + return this->economy.timekeeping_units == TKU_CALENDAR ? this->economy.day_length_factor : 1; } }; diff --git a/src/subsidy_gui.cpp b/src/subsidy_gui.cpp index 2aa89ef8a4..051effa11f 100644 --- a/src/subsidy_gui.cpp +++ b/src/subsidy_gui.cpp @@ -157,7 +157,15 @@ struct SubsidyListWindow : Window { if (IsInsideMM(pos, 0, cap)) { /* Displays the two offered towns */ SetupSubsidyDecodeParam(s, SubsidyDecodeParamType::Gui); - SetDParam(7, EconTime::CurDate() - EconTime::CurDay() + s->remaining * 32); + /* If using wallclock units, show minutes remaining. Otherwise show the date when the subsidy ends. */ + if (EconTime::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, EconTime::CurDate() - EconTime::CurDay() + s->remaining * 32); + } + DrawString(tr.left, tr.right, tr.top + pos * GetCharacterHeight(FS_NORMAL), STR_SUBSIDIES_OFFERED_FROM_TO); } pos++; @@ -181,7 +189,15 @@ struct SubsidyListWindow : Window { if (IsInsideMM(pos, 0, cap)) { SetupSubsidyDecodeParam(s, SubsidyDecodeParamType::Gui); SetDParam(7, s->awarded); - SetDParam(8, EconTime::CurDate() - EconTime::CurDay() + s->remaining * 32); + /* If using wallclock units, show minutes remaining. Otherwise show the date when the subsidy ends. */ + if (EconTime::UsingWallclockUnits()) { + SetDParam(8, STR_SUBSIDIES_SUBSIDISED_EXPIRY_TIME); + SetDParam(9, s->remaining); + } + else { + SetDParam(8, STR_SUBSIDIES_SUBSIDISED_EXPIRY_DATE); + SetDParam(9, EconTime::CurDate() - EconTime::CurDay() + 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/settings/economy_settings.ini b/src/table/settings/economy_settings.ini index 6370e11e0a..7b5228647c 100644 --- a/src/table/settings/economy_settings.ini +++ b/src/table/settings/economy_settings.ini @@ -9,6 +9,8 @@ [pre-amble] static void TownFoundingChanged(int32_t new_value); +static void ChangeTimekeepingUnits(int32_t new_value); +static void ChangeMinutesPerYear(int32_t new_value); static void InvalidateCompanyWindow(int32_t new_value); static void DayLengthChanged(int32_t new_value); static bool CheckSharingRail(int32_t &new_value); @@ -702,10 +704,37 @@ 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 + +[SDT_VAR] +var = economy.minutes_per_calendar_year +type = SLE_UINT16 +flags = SF_GUI_0_IS_SPECIAL +def = CalTime::DEF_MINUTES_PER_YEAR +min = CalTime::FROZEN_MINUTES_PER_YEAR +max = CalTime::MAX_MINUTES_PER_YEAR +interval = 1 +str = STR_CONFIG_SETTING_MINUTES_PER_YEAR +strhelp = STR_CONFIG_SETTING_MINUTES_PER_YEAR_HELPTEXT +strval = STR_CONFIG_SETTING_MINUTES_PER_YEAR_VALUE +pre_cb = [](auto) { return _game_mode == GM_MENU || _settings_game.economy.timekeeping_units == 1; } +post_cb = ChangeMinutesPerYear +cat = SC_BASIC + [SDT_VAR] var = economy.town_cargo_scale type = SLE_UINT16 -flags = SF_PATCH def = 100 min = 1 max = 5000 @@ -720,7 +749,6 @@ guiproc = TownCargoScaleGUI [SDT_VAR] var = economy.industry_cargo_scale type = SLE_UINT16 -flags = SF_PATCH def = 100 min = 5 max = 3000 diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index 9fcc488e8f..a532d43882 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -2819,13 +2819,20 @@ extern void DrawRoadVehDetails(const Vehicle *v, const Rect &r); extern void DrawShipDetails(const Vehicle *v, const Rect &r); extern void DrawAircraftDetails(const Aircraft *v, const Rect &r); -static StringID _service_interval_dropdown[] = { +static StringID _service_interval_dropdown_calendar[] = { STR_VEHICLE_DETAILS_DEFAULT, STR_VEHICLE_DETAILS_DAYS, STR_VEHICLE_DETAILS_PERCENT, INVALID_STRING_ID, }; +static StringID _service_interval_dropdown_wallclock[] = { + STR_VEHICLE_DETAILS_DEFAULT, + STR_VEHICLE_DETAILS_MINUTES, + STR_VEHICLE_DETAILS_PERCENT, + INVALID_STRING_ID, +}; + /** Class for managing the vehicle details window. */ struct VehicleDetailsWindow : Window { TrainDetailsWindowTabs tab; ///< For train vehicles: which tab is displayed. @@ -3024,9 +3031,10 @@ struct VehicleDetailsWindow : Window { case WID_VD_SERVICE_INTERVAL_DROPDOWN: { Dimension d{0, 0}; - StringID *strs = _service_interval_dropdown; - while (*strs != INVALID_STRING_ID) { - d = maxdim(d, GetStringBoundingBox(*strs++)); + for (const StringID *strs : {_service_interval_dropdown_calendar, _service_interval_dropdown_wallclock}) { + while (*strs != INVALID_STRING_ID) { + d = maxdim(d, GetStringBoundingBox(*strs++)); + } } d.width += padding.width; d.height += padding.height; @@ -3036,7 +3044,17 @@ struct VehicleDetailsWindow : Window { case WID_VD_SERVICING_INTERVAL: SetDParamMaxValue(0, MAX_SERVINT_DAYS); // Roughly the maximum interval - SetDParamMaxValue(1, CalTime::MAX_YEAR.base() * DAYS_IN_YEAR); // Roughly the maximum year + + /* Do we show the last serviced value as a date or minutes since service? */ + if (EconTime::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, MONTHS_IN_YEAR * EconTime::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, EconTime::DateAtStartOfYear(EconTime::MAX_YEAR)); + } size->width = std::max( GetStringBoundingBox(STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT).width, GetStringBoundingBox(STR_VEHICLE_DETAILS_SERVICING_INTERVAL_DAYS).width @@ -3264,8 +3282,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 (EconTime::UsingWallclockUnits()) { + int minutes_since_serviced = (EconTime::CurDate() - v->date_of_last_service).base() / EconTime::DAYS_IN_ECONOMY_WALLCLOCK_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; @@ -3288,9 +3320,10 @@ struct VehicleDetailsWindow : Window { WID_VD_INCREASE_SERVICING_INTERVAL, WID_VD_DECREASE_SERVICING_INTERVAL); - StringID str = v->ServiceIntervalIsCustom() ? - (v->ServiceIntervalIsPercent() ? STR_VEHICLE_DETAILS_PERCENT : STR_VEHICLE_DETAILS_DAYS) : - STR_VEHICLE_DETAILS_DEFAULT; + StringID str = + !v->ServiceIntervalIsCustom() ? STR_VEHICLE_DETAILS_DEFAULT : + v->ServiceIntervalIsPercent() ? STR_VEHICLE_DETAILS_PERCENT : + EconTime::UsingWallclockUnits() ? STR_VEHICLE_DETAILS_MINUTES : STR_VEHICLE_DETAILS_DAYS; this->GetWidget(WID_VD_SERVICE_INTERVAL_DROPDOWN)->widget_data = str; this->DrawWidgets(); @@ -3301,8 +3334,13 @@ 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; const Vehicle *v = Vehicle::Get(this->window_number); + int mod; + if (!v->ServiceIntervalIsPercent() && EconTime::UsingWallclockUnits()) { + mod = _ctrl_pressed ? 1 : 5; + } else { + mod = _ctrl_pressed ? 5 : 10; + } mod = (widget == WID_VD_DECREASE_SERVICING_INTERVAL) ? -mod : mod; mod = GetServiceIntervalClamped(mod + v->GetServiceInterval(), v->ServiceIntervalIsPercent()); @@ -3314,7 +3352,9 @@ struct VehicleDetailsWindow : Window { case WID_VD_SERVICE_INTERVAL_DROPDOWN: { const Vehicle *v = Vehicle::Get(this->window_number); - ShowDropDownMenu(this, _service_interval_dropdown, v->ServiceIntervalIsCustom() ? (v->ServiceIntervalIsPercent() ? 2 : 1) : 0, widget, 0, 0, 0, DDSF_LOST_FOCUS); + ShowDropDownMenu(this, + EconTime::UsingWallclockUnits() ? _service_interval_dropdown_wallclock : _service_interval_dropdown_calendar, + v->ServiceIntervalIsCustom() ? (v->ServiceIntervalIsPercent() ? 2 : 1) : 0, widget, 0, 0, 0, DDSF_LOST_FOCUS); break; }