2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
9 * @file settings_table.cpp The tables of all the settings as well as the implementation of most of their callbacks.
13 #include "settings_table.h"
15 #include "screenshot.h"
16 #include "network/network.h"
17 #include "network/network_func.h"
18 #include "network/core/config.h"
19 #include "pathfinder/pathfinder_type.h"
20 #include "pathfinder/aystar.h"
21 #include "linkgraph/linkgraphschedule.h"
24 #include "news_func.h"
25 #include "window_func.h"
26 #include "company_func.h"
27 #include "timer/timer_game_calendar.h"
28 #if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
29 #define HAS_TRUETYPE_FONT
30 #include "fontcache.h"
32 #include "textbuf_gui.h"
34 #include "elrail_func.h"
37 #include "video/video_driver.hpp"
38 #include "sound/sound_driver.hpp"
39 #include "music/music_driver.hpp"
40 #include "blitter/factory.hpp"
41 #include "base_media_base.h"
42 #include "ai/ai_config.hpp"
44 #include "game/game_config.hpp"
46 #include "smallmap_gui.h"
48 #include "roadveh_cmd.h"
49 #include "vehicle_func.h"
50 #include "viewport_func.h"
52 #include "station_func.h"
53 #include "station_base.h"
55 #include "table/strings.h"
56 #include "table/settings.h"
58 #include "safeguards.h"
60 SettingTable _company_settings
{ _company_settings_table
};
61 SettingTable _currency_settings
{ _currency_settings_table
};
62 SettingTable _difficulty_settings
{ _difficulty_settings_table
};
63 SettingTable _multimedia_settings
{ _multimedia_settings_table
};
64 SettingTable _economy_settings
{ _economy_settings_table
};
65 SettingTable _game_settings
{ _game_settings_table
};
66 SettingTable _gui_settings
{ _gui_settings_table
};
67 SettingTable _linkgraph_settings
{ _linkgraph_settings_table
};
68 SettingTable _locale_settings
{ _locale_settings_table
};
69 SettingTable _misc_settings
{ _misc_settings_table
};
70 SettingTable _network_private_settings
{ _network_private_settings_table
};
71 SettingTable _network_secrets_settings
{ _network_secrets_settings_table
};
72 SettingTable _network_settings
{ _network_settings_table
};
73 SettingTable _news_display_settings
{ _news_display_settings_table
};
74 SettingTable _old_gameopt_settings
{ _old_gameopt_settings_table
};
75 SettingTable _pathfinding_settings
{ _pathfinding_settings_table
};
76 SettingTable _script_settings
{ _script_settings_table
};
77 SettingTable _window_settings
{ _window_settings_table
};
78 SettingTable _world_settings
{ _world_settings_table
};
79 #if defined(_WIN32) && !defined(DEDICATED)
80 SettingTable _win32_settings
{ _win32_settings_table
};
84 /* Begin - Callback Functions for the various settings. */
86 /** Switch setting title depending on wallclock setting */
87 static StringID
SettingTitleWallclock(const IntSettingDesc
&sd
)
89 return TimerGameEconomy::UsingWallclockUnits(_game_mode
== GM_MENU
) ? sd
.str
+ 1 : sd
.str
;
92 /** Switch setting help depending on wallclock setting */
93 static StringID
SettingHelpWallclock(const IntSettingDesc
&sd
)
95 return TimerGameEconomy::UsingWallclockUnits(_game_mode
== GM_MENU
) ? sd
.str_help
+ 1 : sd
.str_help
;
98 /** Setting values for velocity unit localisation */
99 static void SettingsValueVelocityUnit(const IntSettingDesc
&, uint first_param
, int32_t value
)
103 case 0: val
= STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_IMPERIAL
; break;
104 case 1: val
= STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_METRIC
; break;
105 case 2: val
= STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_SI
; break;
106 case 3: val
= TimerGameEconomy::UsingWallclockUnits(_game_mode
== GM_MENU
) ? STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_GAMEUNITS_SECS
: STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_GAMEUNITS_DAYS
; break;
107 case 4: val
= STR_CONFIG_SETTING_LOCALISATION_UNITS_VELOCITY_KNOTS
; break;
108 default: NOT_REACHED();
110 SetDParam(first_param
, val
);
113 /** A negative value has another string (the one after "strval"). */
114 static void SettingsValueAbsolute(const IntSettingDesc
&sd
, uint first_param
, int32_t value
)
116 SetDParam(first_param
, sd
.str_val
+ ((value
>= 0) ? 1 : 0));
117 SetDParam(first_param
+ 1, abs(value
));
120 /** Service Interval Settings Default Value displays the correct units or as a percentage */
121 static void ServiceIntervalSettingsValueText(const IntSettingDesc
&sd
, uint first_param
, int32_t value
)
123 VehicleDefaultSettings
*vds
;
124 if (_game_mode
== GM_MENU
|| !Company::IsValidID(_current_company
)) {
125 vds
= &_settings_client
.company
.vehicle
;
127 vds
= &Company::Get(_current_company
)->settings
.vehicle
;
131 SetDParam(first_param
, sd
.str_val
+ 3);
132 } else if (vds
->servint_ispercent
) {
133 SetDParam(first_param
, sd
.str_val
+ 2);
134 } else if (TimerGameEconomy::UsingWallclockUnits(_game_mode
== GM_MENU
)) {
135 SetDParam(first_param
, sd
.str_val
+ 1);
137 SetDParam(first_param
, sd
.str_val
);
139 SetDParam(first_param
+ 1, value
);
142 /** Reposition the main toolbar as the setting changed. */
143 static void v_PositionMainToolbar(int32_t)
145 if (_game_mode
!= GM_MENU
) PositionMainToolbar(nullptr);
148 /** Reposition the statusbar as the setting changed. */
149 static void v_PositionStatusbar(int32_t)
151 if (_game_mode
!= GM_MENU
) {
152 PositionStatusbar(nullptr);
153 PositionNewsMessage(nullptr);
154 PositionNetworkChatWindow(nullptr);
159 * Redraw the smallmap after a colour scheme change.
161 static void RedrawSmallmap(int32_t)
165 SetWindowClassesDirty(WC_SMALLMAP
);
168 /** Redraw linkgraph links after a colour scheme change. */
169 static void UpdateLinkgraphColours(int32_t)
171 BuildLinkStatsLegend();
172 MarkWholeScreenDirty();
175 static void StationSpreadChanged(int32_t)
177 InvalidateWindowData(WC_SELECT_STATION
, 0);
178 InvalidateWindowData(WC_BUILD_STATION
, 0);
181 static void UpdateConsists(int32_t)
183 for (Train
*t
: Train::Iterate()) {
184 /* Update the consist of all trains so the maximum speed is set correctly. */
185 if (t
->IsFrontEngine() || t
->IsFreeWagon()) t
->ConsistChanged(CCF_TRACK
);
187 InvalidateWindowClassesData(WC_BUILD_VEHICLE
, 0);
191 * Check and update if needed all vehicle service intervals.
192 * @param new_value Contains 0 if service intervals are in time (days or real-world minutes), otherwise intervals use percents.
194 static void UpdateAllServiceInterval(int32_t new_value
)
196 bool update_vehicles
;
197 VehicleDefaultSettings
*vds
;
198 if (_game_mode
== GM_MENU
|| !Company::IsValidID(_current_company
)) {
199 vds
= &_settings_client
.company
.vehicle
;
200 update_vehicles
= false;
202 vds
= &Company::Get(_current_company
)->settings
.vehicle
;
203 update_vehicles
= true;
206 if (new_value
!= 0) {
207 /* Service intervals are in percents. */
208 vds
->servint_trains
= DEF_SERVINT_PERCENT
;
209 vds
->servint_roadveh
= DEF_SERVINT_PERCENT
;
210 vds
->servint_aircraft
= DEF_SERVINT_PERCENT
;
211 vds
->servint_ships
= DEF_SERVINT_PERCENT
;
212 } else if (TimerGameEconomy::UsingWallclockUnits(_game_mode
== GM_MENU
)) {
213 /* Service intervals are in minutes. */
214 vds
->servint_trains
= DEF_SERVINT_MINUTES_TRAINS
;
215 vds
->servint_roadveh
= DEF_SERVINT_MINUTES_ROADVEH
;
216 vds
->servint_aircraft
= DEF_SERVINT_MINUTES_AIRCRAFT
;
217 vds
->servint_ships
= DEF_SERVINT_MINUTES_SHIPS
;
219 /* Service intervals are in days. */
220 vds
->servint_trains
= DEF_SERVINT_DAYS_TRAINS
;
221 vds
->servint_roadveh
= DEF_SERVINT_DAYS_ROADVEH
;
222 vds
->servint_aircraft
= DEF_SERVINT_DAYS_AIRCRAFT
;
223 vds
->servint_ships
= DEF_SERVINT_DAYS_SHIPS
;
226 if (update_vehicles
) {
227 const Company
*c
= Company::Get(_current_company
);
228 for (Vehicle
*v
: Vehicle::Iterate()) {
229 if (v
->owner
== _current_company
&& v
->IsPrimaryVehicle() && !v
->ServiceIntervalIsCustom()) {
230 v
->SetServiceInterval(CompanyServiceInterval(c
, v
->type
));
231 v
->SetServiceIntervalIsPercent(new_value
!= 0);
236 SetWindowClassesDirty(WC_VEHICLE_DETAILS
);
239 static bool CanUpdateServiceInterval(VehicleType
, int32_t &new_value
)
241 VehicleDefaultSettings
*vds
;
242 if (_game_mode
== GM_MENU
|| !Company::IsValidID(_current_company
)) {
243 vds
= &_settings_client
.company
.vehicle
;
245 vds
= &Company::Get(_current_company
)->settings
.vehicle
;
248 /* Test if the interval is valid */
249 int32_t interval
= GetServiceIntervalClamped(new_value
, vds
->servint_ispercent
);
250 return interval
== new_value
;
253 static void UpdateServiceInterval(VehicleType type
, int32_t new_value
)
255 if (_game_mode
!= GM_MENU
&& Company::IsValidID(_current_company
)) {
256 for (Vehicle
*v
: Vehicle::Iterate()) {
257 if (v
->owner
== _current_company
&& v
->type
== type
&& v
->IsPrimaryVehicle() && !v
->ServiceIntervalIsCustom()) {
258 v
->SetServiceInterval(new_value
);
263 SetWindowClassesDirty(WC_VEHICLE_DETAILS
);
267 * Checks if the service intervals in the settings are specified as percentages and corrects the default value accordingly.
268 * @param new_value Contains the service interval's default value in days, or 50 (default in percentage).
270 static int32_t GetDefaultServiceInterval(VehicleType type
)
272 VehicleDefaultSettings
*vds
;
273 if (_game_mode
== GM_MENU
|| !Company::IsValidID(_current_company
)) {
274 vds
= &_settings_client
.company
.vehicle
;
276 vds
= &Company::Get(_current_company
)->settings
.vehicle
;
280 if (vds
->servint_ispercent
) {
281 new_value
= DEF_SERVINT_PERCENT
;
282 } else if (TimerGameEconomy::UsingWallclockUnits(_game_mode
== GM_MENU
)) {
284 case VEH_TRAIN
: new_value
= DEF_SERVINT_MINUTES_TRAINS
; break;
285 case VEH_ROAD
: new_value
= DEF_SERVINT_MINUTES_ROADVEH
; break;
286 case VEH_AIRCRAFT
: new_value
= DEF_SERVINT_MINUTES_AIRCRAFT
; break;
287 case VEH_SHIP
: new_value
= DEF_SERVINT_MINUTES_SHIPS
; break;
288 default: NOT_REACHED();
292 case VEH_TRAIN
: new_value
= DEF_SERVINT_DAYS_TRAINS
; break;
293 case VEH_ROAD
: new_value
= DEF_SERVINT_DAYS_ROADVEH
; break;
294 case VEH_AIRCRAFT
: new_value
= DEF_SERVINT_DAYS_AIRCRAFT
; break;
295 case VEH_SHIP
: new_value
= DEF_SERVINT_DAYS_SHIPS
; break;
296 default: NOT_REACHED();
303 static void TrainAccelerationModelChanged(int32_t)
305 for (Train
*t
: Train::Iterate()) {
306 if (t
->IsFrontEngine()) {
307 t
->tcache
.cached_max_curve_speed
= t
->GetCurveSpeedLimit();
308 t
->UpdateAcceleration();
312 /* These windows show acceleration values only when realistic acceleration is on. They must be redrawn after a setting change. */
313 SetWindowClassesDirty(WC_ENGINE_PREVIEW
);
314 InvalidateWindowClassesData(WC_BUILD_VEHICLE
, 0);
315 SetWindowClassesDirty(WC_VEHICLE_DETAILS
);
319 * This function updates the train acceleration cache after a steepness change.
321 static void TrainSlopeSteepnessChanged(int32_t)
323 for (Train
*t
: Train::Iterate()) {
324 if (t
->IsFrontEngine()) t
->CargoChanged();
329 * This function updates realistic acceleration caches when the setting "Road vehicle acceleration model" is set.
331 static void RoadVehAccelerationModelChanged(int32_t)
333 if (_settings_game
.vehicle
.roadveh_acceleration_model
!= AM_ORIGINAL
) {
334 for (RoadVehicle
*rv
: RoadVehicle::Iterate()) {
335 if (rv
->IsFrontEngine()) {
341 /* These windows show acceleration values only when realistic acceleration is on. They must be redrawn after a setting change. */
342 SetWindowClassesDirty(WC_ENGINE_PREVIEW
);
343 InvalidateWindowClassesData(WC_BUILD_VEHICLE
, 0);
344 SetWindowClassesDirty(WC_VEHICLE_DETAILS
);
348 * This function updates the road vehicle acceleration cache after a steepness change.
350 static void RoadVehSlopeSteepnessChanged(int32_t)
352 for (RoadVehicle
*rv
: RoadVehicle::Iterate()) {
353 if (rv
->IsFrontEngine()) rv
->CargoChanged();
357 static void TownFoundingChanged(int32_t)
359 if (_game_mode
!= GM_EDITOR
&& _settings_game
.economy
.found_town
== TF_FORBIDDEN
) {
360 CloseWindowById(WC_FOUND_TOWN
, 0);
362 InvalidateWindowData(WC_FOUND_TOWN
, 0);
366 static void ZoomMinMaxChanged(int32_t)
368 ConstrainAllViewportsZoom();
369 GfxClearSpriteCache();
370 InvalidateWindowClassesData(WC_SPRITE_ALIGNER
);
371 if (AdjustGUIZoom(false)) {
372 ReInitAllWindows(true);
376 static void SpriteZoomMinChanged(int32_t)
378 GfxClearSpriteCache();
379 /* Force all sprites to redraw at the new chosen zoom level */
380 MarkWholeScreenDirty();
384 * Update any possible saveload window and delete any newgrf dialogue as
385 * its widget parts might change. Reinit all windows as it allows access to the
386 * newgrf debug button.
388 static void InvalidateNewGRFChangeWindows(int32_t)
390 InvalidateWindowClassesData(WC_SAVELOAD
);
391 CloseWindowByClass(WC_GAME_OPTIONS
);
392 ReInitAllWindows(false);
395 static void InvalidateCompanyLiveryWindow(int32_t)
397 InvalidateWindowClassesData(WC_COMPANY_COLOUR
, -1);
398 ResetVehicleColourMap();
401 static void DifficultyNoiseChange(int32_t)
403 if (_game_mode
== GM_NORMAL
) {
404 UpdateAirportsNoise();
405 if (_settings_game
.economy
.station_noise_level
) {
406 InvalidateWindowClassesData(WC_TOWN_VIEW
, 0);
411 static void MaxNoAIsChange(int32_t)
413 if (GetGameSettings().difficulty
.max_no_competitors
!= 0 &&
414 AI::GetInfoList()->empty() &&
415 (!_networking
|| _network_server
)) {
416 ShowErrorMessage(STR_WARNING_NO_SUITABLE_AI
, INVALID_STRING_ID
, WL_CRITICAL
);
419 InvalidateWindowClassesData(WC_GAME_OPTIONS
, 0);
423 * Check whether the road side may be changed.
424 * @return true if the road side may be changed.
426 static bool CheckRoadSide(int32_t &)
428 return _game_mode
== GM_MENU
|| !RoadVehiclesAreBuilt();
432 * Conversion callback for _gameopt_settings_game.landscape
433 * It converts (or try) between old values and the new ones,
434 * without losing initial setting of the user
435 * @param value that was read from config file
436 * @return the "hopefully" converted value
438 static size_t ConvertLandscape(const char *value
)
440 /* try with the old values */
441 static std::vector
<std::string
> _old_landscape_values
{"normal", "hilly", "desert", "candy"};
442 return OneOfManySettingDesc::ParseSingleValue(value
, strlen(value
), _old_landscape_values
);
445 static bool CheckFreeformEdges(int32_t &new_value
)
447 if (_game_mode
== GM_MENU
) return true;
448 if (new_value
!= 0) {
449 for (Ship
*s
: Ship::Iterate()) {
450 /* Check if there is a ship on the northern border. */
451 if (TileX(s
->tile
) == 0 || TileY(s
->tile
) == 0) {
452 ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_EMPTY
, INVALID_STRING_ID
, WL_ERROR
);
456 for (const BaseStation
*st
: BaseStation::Iterate()) {
457 /* Check if there is a non-deleted buoy on the northern border. */
458 if (st
->IsInUse() && (TileX(st
->xy
) == 0 || TileY(st
->xy
) == 0)) {
459 ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_EMPTY
, INVALID_STRING_ID
, WL_ERROR
);
464 for (uint i
= 0; i
< Map::MaxX(); i
++) {
465 if (TileHeight(TileXY(i
, 1)) != 0) {
466 ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_WATER
, INVALID_STRING_ID
, WL_ERROR
);
470 for (uint i
= 1; i
< Map::MaxX(); i
++) {
471 if (!IsTileType(TileXY(i
, Map::MaxY() - 1), MP_WATER
) || TileHeight(TileXY(1, Map::MaxY())) != 0) {
472 ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_WATER
, INVALID_STRING_ID
, WL_ERROR
);
476 for (uint i
= 0; i
< Map::MaxY(); i
++) {
477 if (TileHeight(TileXY(1, i
)) != 0) {
478 ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_WATER
, INVALID_STRING_ID
, WL_ERROR
);
482 for (uint i
= 1; i
< Map::MaxY(); i
++) {
483 if (!IsTileType(TileXY(Map::MaxX() - 1, i
), MP_WATER
) || TileHeight(TileXY(Map::MaxX(), i
)) != 0) {
484 ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_WATER
, INVALID_STRING_ID
, WL_ERROR
);
492 static void UpdateFreeformEdges(int32_t new_value
)
494 if (_game_mode
== GM_MENU
) return;
496 if (new_value
!= 0) {
497 for (uint x
= 0; x
< Map::SizeX(); x
++) MakeVoid(TileXY(x
, 0));
498 for (uint y
= 0; y
< Map::SizeY(); y
++) MakeVoid(TileXY(0, y
));
500 /* Make tiles at the border water again. */
501 for (uint i
= 0; i
< Map::MaxX(); i
++) {
502 SetTileHeight(TileXY(i
, 0), 0);
503 SetTileType(TileXY(i
, 0), MP_WATER
);
505 for (uint i
= 0; i
< Map::MaxY(); i
++) {
506 SetTileHeight(TileXY(0, i
), 0);
507 SetTileType(TileXY(0, i
), MP_WATER
);
510 MarkWholeScreenDirty();
514 * Changing the setting "allow multiple NewGRF sets" is not allowed
515 * if there are vehicles.
517 static bool CheckDynamicEngines(int32_t &)
519 if (_game_mode
== GM_MENU
) return true;
521 if (!EngineOverrideManager::ResetToCurrentNewGRFConfig()) {
522 ShowErrorMessage(STR_CONFIG_SETTING_DYNAMIC_ENGINES_EXISTING_VEHICLES
, INVALID_STRING_ID
, WL_ERROR
);
529 static bool CheckMaxHeightLevel(int32_t &new_value
)
531 if (_game_mode
== GM_NORMAL
) return false;
532 if (_game_mode
!= GM_EDITOR
) return true;
534 /* Check if at least one mountain on the map is higher than the new value.
535 * If yes, disallow the change. */
536 for (TileIndex t
= 0; t
< Map::Size(); t
++) {
537 if ((int32_t)TileHeight(t
) > new_value
) {
538 ShowErrorMessage(STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN
, INVALID_STRING_ID
, WL_ERROR
);
539 /* Return old, unchanged value */
547 static void StationCatchmentChanged(int32_t)
549 Station::RecomputeCatchmentForAll();
550 MarkWholeScreenDirty();
553 static void MaxVehiclesChanged(int32_t)
555 InvalidateWindowClassesData(WC_BUILD_TOOLBAR
);
556 MarkWholeScreenDirty();
560 * Replace a passwords that are a literal asterisk with an empty string.
561 * @param newval The new string value for this password field.
562 * @return Always true.
564 static bool ReplaceAsteriskWithEmptyPassword(std::string
&newval
)
566 if (newval
.compare("*") == 0) newval
.clear();
570 /** Update the game info, and send it to the clients when we are running as a server. */
571 static void UpdateClientConfigValues()
573 NetworkServerUpdateGameInfo();
575 InvalidateWindowData(WC_CLIENT_LIST
, 0);
577 if (_network_server
) {
578 NetworkServerSendConfigUpdate();
583 * Callback for when the player changes the timekeeping units.
586 static void ChangeTimekeepingUnits(int32_t)
588 /* If service intervals are in time units (calendar days or real-world minutes), reset them to the correct defaults. */
589 if (!_settings_client
.company
.vehicle
.servint_ispercent
) {
590 UpdateAllServiceInterval(0);
593 /* If we are using calendar timekeeping, "minutes per year" must be default. */
594 if (!TimerGameEconomy::UsingWallclockUnits(true)) {
595 _settings_newgame
.economy
.minutes_per_calendar_year
= CalendarTime::DEF_MINUTES_PER_YEAR
;
598 InvalidateWindowClassesData(WC_GAME_OPTIONS
, 0);
600 /* It is possible to change these units in Scenario Editor. We must set the economy date appropriately. */
601 if (_game_mode
== GM_EDITOR
) {
602 TimerGameEconomy::Date new_economy_date
;
603 TimerGameEconomy::DateFract new_economy_date_fract
;
605 if (TimerGameEconomy::UsingWallclockUnits()) {
606 /* If the new mode is wallclock units, set the economy year back to 1. */
607 new_economy_date
= TimerGameEconomy::ConvertYMDToDate(1, 0, 1);
608 new_economy_date_fract
= 0;
610 /* If the new mode is calendar units, sync the economy year with the calendar year. */
611 new_economy_date
= TimerGameCalendar::date
.base();
612 new_economy_date_fract
= TimerGameCalendar::date_fract
;
615 /* If you open a savegame as a scenario, there may already be link graphs and/or vehicles. These use economy date. */
616 LinkGraphSchedule::instance
.ShiftDates(new_economy_date
- TimerGameEconomy::date
);
617 for (auto v
: Vehicle::Iterate()) v
->ShiftDates(new_economy_date
- TimerGameEconomy::date
);
619 /* Only change the date after changing cached values above. */
620 TimerGameEconomy::SetDate(new_economy_date
, new_economy_date_fract
);
625 * Callback after the player changes the minutes per year.
626 * @param new_value The intended new value of the setting, used for clamping.
628 static void ChangeMinutesPerYear(int32_t new_value
)
630 /* We don't allow setting Minutes Per Year below default, unless it's to 0 for frozen calendar time. */
631 if (new_value
< CalendarTime::DEF_MINUTES_PER_YEAR
) {
634 /* If the new value is 1, we're probably at 0 and trying to increase the value, so we should jump up to default. */
635 if (new_value
== 1) {
636 clamped
= CalendarTime::DEF_MINUTES_PER_YEAR
;
638 clamped
= CalendarTime::FROZEN_MINUTES_PER_YEAR
;
641 /* Override the setting with the clamped value. */
642 if (_game_mode
== GM_MENU
) {
643 _settings_newgame
.economy
.minutes_per_calendar_year
= clamped
;
645 _settings_game
.economy
.minutes_per_calendar_year
= clamped
;
649 /* If the setting value is not the default, force the game to use wallclock timekeeping units.
650 * 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.
652 if (_game_mode
== GM_MENU
&& (_settings_newgame
.economy
.minutes_per_calendar_year
!= CalendarTime::DEF_MINUTES_PER_YEAR
)) {
653 _settings_newgame
.economy
.timekeeping_units
= TKU_WALLCLOCK
;
654 InvalidateWindowClassesData(WC_GAME_OPTIONS
, 0);
659 * Pre-callback check when trying to change the timetable mode. This is locked to Seconds when using wallclock units.
661 * @return True if we allow changing the timetable mode.
663 static bool CanChangeTimetableMode(int32_t &)
665 return !TimerGameEconomy::UsingWallclockUnits();
668 /* End - Callback Functions */