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"
22 #include "news_func.h"
23 #include "window_func.h"
24 #include "company_func.h"
25 #if defined(WITH_FREETYPE) || defined(_WIN32) || defined(WITH_COCOA)
26 #define HAS_TRUETYPE_FONT
27 #include "fontcache.h"
29 #include "textbuf_gui.h"
31 #include "elrail_func.h"
34 #include "video/video_driver.hpp"
35 #include "sound/sound_driver.hpp"
36 #include "music/music_driver.hpp"
37 #include "blitter/factory.hpp"
38 #include "base_media_base.h"
39 #include "ai/ai_config.hpp"
41 #include "game/game_config.hpp"
43 #include "smallmap_gui.h"
45 #include "vehicle_func.h"
48 #include "table/strings.h"
49 #include "table/settings.h"
51 #include "safeguards.h"
53 SettingTable _company_settings
{ _company_settings_table
};
54 SettingTable _currency_settings
{ _currency_settings_table
};
55 SettingTable _difficulty_settings
{ _difficulty_settings_table
};
56 SettingTable _multimedia_settings
{ _multimedia_settings_table
};
57 SettingTable _economy_settings
{ _economy_settings_table
};
58 SettingTable _game_settings
{ _game_settings_table
};
59 SettingTable _gui_settings
{ _gui_settings_table
};
60 SettingTable _linkgraph_settings
{ _linkgraph_settings_table
};
61 SettingTable _locale_settings
{ _locale_settings_table
};
62 SettingTable _misc_settings
{ _misc_settings_table
};
63 SettingTable _network_private_settings
{ _network_private_settings_table
};
64 SettingTable _network_secrets_settings
{ _network_secrets_settings_table
};
65 SettingTable _network_settings
{ _network_settings_table
};
66 SettingTable _news_display_settings
{ _news_display_settings_table
};
67 SettingTable _old_gameopt_settings
{ _old_gameopt_settings_table
};
68 SettingTable _pathfinding_settings
{ _pathfinding_settings_table
};
69 SettingTable _script_settings
{ _script_settings_table
};
70 SettingTable _window_settings
{ _window_settings_table
};
71 SettingTable _world_settings
{ _world_settings_table
};
72 #if defined(_WIN32) && !defined(DEDICATED)
73 SettingTable _win32_settings
{ _win32_settings_table
};
77 /* Begin - Callback Functions for the various settings. */
79 /** Reposition the main toolbar as the setting changed. */
80 static void v_PositionMainToolbar(int32 new_value
)
82 if (_game_mode
!= GM_MENU
) PositionMainToolbar(nullptr);
85 /** Reposition the statusbar as the setting changed. */
86 static void v_PositionStatusbar(int32 new_value
)
88 if (_game_mode
!= GM_MENU
) {
89 PositionStatusbar(nullptr);
90 PositionNewsMessage(nullptr);
91 PositionNetworkChatWindow(nullptr);
96 * Redraw the smallmap after a colour scheme change.
97 * @param p1 Callback parameter.
99 static void RedrawSmallmap(int32 new_value
)
103 SetWindowClassesDirty(WC_SMALLMAP
);
106 /** Redraw linkgraph links after a colour scheme change. */
107 static void UpdateLinkgraphColours(int32 new_value
)
109 BuildLinkStatsLegend();
110 MarkWholeScreenDirty();
113 static void StationSpreadChanged(int32 p1
)
115 InvalidateWindowData(WC_SELECT_STATION
, 0);
116 InvalidateWindowData(WC_BUILD_STATION
, 0);
119 static void UpdateConsists(int32 new_value
)
121 for (Train
*t
: Train::Iterate()) {
122 /* Update the consist of all trains so the maximum speed is set correctly. */
123 if (t
->IsFrontEngine() || t
->IsFreeWagon()) t
->ConsistChanged(CCF_TRACK
);
125 InvalidateWindowClassesData(WC_BUILD_VEHICLE
, 0);
128 /* Check service intervals of vehicles, newvalue is value of % or day based servicing */
129 static void UpdateAllServiceInterval(int32 new_value
)
131 bool update_vehicles
;
132 VehicleDefaultSettings
*vds
;
133 if (_game_mode
== GM_MENU
|| !Company::IsValidID(_current_company
)) {
134 vds
= &_settings_client
.company
.vehicle
;
135 update_vehicles
= false;
137 vds
= &Company::Get(_current_company
)->settings
.vehicle
;
138 update_vehicles
= true;
141 if (new_value
!= 0) {
142 vds
->servint_trains
= 50;
143 vds
->servint_roadveh
= 50;
144 vds
->servint_aircraft
= 50;
145 vds
->servint_ships
= 50;
147 vds
->servint_trains
= 150;
148 vds
->servint_roadveh
= 150;
149 vds
->servint_aircraft
= 100;
150 vds
->servint_ships
= 360;
153 if (update_vehicles
) {
154 const Company
*c
= Company::Get(_current_company
);
155 for (Vehicle
*v
: Vehicle::Iterate()) {
156 if (v
->owner
== _current_company
&& v
->IsPrimaryVehicle() && !v
->ServiceIntervalIsCustom()) {
157 v
->SetServiceInterval(CompanyServiceInterval(c
, v
->type
));
158 v
->SetServiceIntervalIsPercent(new_value
!= 0);
163 SetWindowClassesDirty(WC_VEHICLE_DETAILS
);
166 static bool CanUpdateServiceInterval(VehicleType type
, int32
&new_value
)
168 VehicleDefaultSettings
*vds
;
169 if (_game_mode
== GM_MENU
|| !Company::IsValidID(_current_company
)) {
170 vds
= &_settings_client
.company
.vehicle
;
172 vds
= &Company::Get(_current_company
)->settings
.vehicle
;
175 /* Test if the interval is valid */
176 int32 interval
= GetServiceIntervalClamped(new_value
, vds
->servint_ispercent
);
177 return interval
== new_value
;
180 static void UpdateServiceInterval(VehicleType type
, int32 new_value
)
182 if (_game_mode
!= GM_MENU
&& Company::IsValidID(_current_company
)) {
183 for (Vehicle
*v
: Vehicle::Iterate()) {
184 if (v
->owner
== _current_company
&& v
->type
== type
&& v
->IsPrimaryVehicle() && !v
->ServiceIntervalIsCustom()) {
185 v
->SetServiceInterval(new_value
);
190 SetWindowClassesDirty(WC_VEHICLE_DETAILS
);
193 static void TrainAccelerationModelChanged(int32 new_value
)
195 for (Train
*t
: Train::Iterate()) {
196 if (t
->IsFrontEngine()) {
197 t
->tcache
.cached_max_curve_speed
= t
->GetCurveSpeedLimit();
198 t
->UpdateAcceleration();
202 /* These windows show acceleration values only when realistic acceleration is on. They must be redrawn after a setting change. */
203 SetWindowClassesDirty(WC_ENGINE_PREVIEW
);
204 InvalidateWindowClassesData(WC_BUILD_VEHICLE
, 0);
205 SetWindowClassesDirty(WC_VEHICLE_DETAILS
);
209 * This function updates the train acceleration cache after a steepness change.
210 * @param new_value Unused new value of setting.
212 static void TrainSlopeSteepnessChanged(int32 new_value
)
214 for (Train
*t
: Train::Iterate()) {
215 if (t
->IsFrontEngine()) t
->CargoChanged();
220 * This function updates realistic acceleration caches when the setting "Road vehicle acceleration model" is set.
221 * @param new_value Unused new value of setting.
223 static void RoadVehAccelerationModelChanged(int32 new_value
)
225 if (_settings_game
.vehicle
.roadveh_acceleration_model
!= AM_ORIGINAL
) {
226 for (RoadVehicle
*rv
: RoadVehicle::Iterate()) {
227 if (rv
->IsFrontEngine()) {
233 /* These windows show acceleration values only when realistic acceleration is on. They must be redrawn after a setting change. */
234 SetWindowClassesDirty(WC_ENGINE_PREVIEW
);
235 InvalidateWindowClassesData(WC_BUILD_VEHICLE
, 0);
236 SetWindowClassesDirty(WC_VEHICLE_DETAILS
);
240 * This function updates the road vehicle acceleration cache after a steepness change.
241 * @param new_value Unused new value of setting.
243 static void RoadVehSlopeSteepnessChanged(int32 new_value
)
245 for (RoadVehicle
*rv
: RoadVehicle::Iterate()) {
246 if (rv
->IsFrontEngine()) rv
->CargoChanged();
250 static void TownFoundingChanged(int32 new_value
)
252 if (_game_mode
!= GM_EDITOR
&& _settings_game
.economy
.found_town
== TF_FORBIDDEN
) {
253 CloseWindowById(WC_FOUND_TOWN
, 0);
255 InvalidateWindowData(WC_FOUND_TOWN
, 0);
259 static void ZoomMinMaxChanged(int32 new_value
)
261 extern void ConstrainAllViewportsZoom();
262 ConstrainAllViewportsZoom();
263 GfxClearSpriteCache();
264 if (_settings_client
.gui
.zoom_min
> _gui_zoom
) {
265 /* Restrict GUI zoom if it is no longer available. */
266 _gui_zoom
= _settings_client
.gui
.zoom_min
;
268 LoadStringWidthTable();
272 static void SpriteZoomMinChanged(int32 new_value
)
274 GfxClearSpriteCache();
275 /* Force all sprites to redraw at the new chosen zoom level */
276 MarkWholeScreenDirty();
280 * Update any possible saveload window and delete any newgrf dialogue as
281 * its widget parts might change. Reinit all windows as it allows access to the
282 * newgrf debug button.
283 * @param new_value unused.
285 static void InvalidateNewGRFChangeWindows(int32 new_value
)
287 InvalidateWindowClassesData(WC_SAVELOAD
);
288 CloseWindowByClass(WC_GAME_OPTIONS
);
289 ReInitAllWindows(false);
292 static void InvalidateCompanyLiveryWindow(int32 new_value
)
294 InvalidateWindowClassesData(WC_COMPANY_COLOUR
, -1);
295 ResetVehicleColourMap();
298 static void DifficultyNoiseChange(int32 new_value
)
300 if (_game_mode
== GM_NORMAL
) {
301 UpdateAirportsNoise();
302 if (_settings_game
.economy
.station_noise_level
) {
303 InvalidateWindowClassesData(WC_TOWN_VIEW
, 0);
308 static void MaxNoAIsChange(int32 new_value
)
310 if (GetGameSettings().difficulty
.max_no_competitors
!= 0 &&
311 AI::GetInfoList()->size() == 0 &&
312 (!_networking
|| _network_server
)) {
313 ShowErrorMessage(STR_WARNING_NO_SUITABLE_AI
, INVALID_STRING_ID
, WL_CRITICAL
);
316 InvalidateWindowClassesData(WC_GAME_OPTIONS
, 0);
320 * Check whether the road side may be changed.
321 * @param new_value unused
322 * @return true if the road side may be changed.
324 static bool CheckRoadSide(int32
&new_value
)
326 extern bool RoadVehiclesAreBuilt();
327 return _game_mode
== GM_MENU
|| !RoadVehiclesAreBuilt();
331 * Conversion callback for _gameopt_settings_game.landscape
332 * It converts (or try) between old values and the new ones,
333 * without losing initial setting of the user
334 * @param value that was read from config file
335 * @return the "hopefully" converted value
337 static size_t ConvertLandscape(const char *value
)
339 /* try with the old values */
340 static std::vector
<std::string
> _old_landscape_values
{"normal", "hilly", "desert", "candy"};
341 return OneOfManySettingDesc::ParseSingleValue(value
, strlen(value
), _old_landscape_values
);
344 static bool CheckFreeformEdges(int32
&new_value
)
346 if (_game_mode
== GM_MENU
) return true;
347 if (new_value
!= 0) {
348 for (Ship
*s
: Ship::Iterate()) {
349 /* Check if there is a ship on the northern border. */
350 if (TileX(s
->tile
) == 0 || TileY(s
->tile
) == 0) {
351 ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_EMPTY
, INVALID_STRING_ID
, WL_ERROR
);
355 for (const BaseStation
*st
: BaseStation::Iterate()) {
356 /* Check if there is a non-deleted buoy on the northern border. */
357 if (st
->IsInUse() && (TileX(st
->xy
) == 0 || TileY(st
->xy
) == 0)) {
358 ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_EMPTY
, INVALID_STRING_ID
, WL_ERROR
);
363 for (uint i
= 0; i
< MapMaxX(); i
++) {
364 if (TileHeight(TileXY(i
, 1)) != 0) {
365 ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_WATER
, INVALID_STRING_ID
, WL_ERROR
);
369 for (uint i
= 1; i
< MapMaxX(); i
++) {
370 if (!IsTileType(TileXY(i
, MapMaxY() - 1), MP_WATER
) || TileHeight(TileXY(1, MapMaxY())) != 0) {
371 ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_WATER
, INVALID_STRING_ID
, WL_ERROR
);
375 for (uint i
= 0; i
< MapMaxY(); i
++) {
376 if (TileHeight(TileXY(1, i
)) != 0) {
377 ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_WATER
, INVALID_STRING_ID
, WL_ERROR
);
381 for (uint i
= 1; i
< MapMaxY(); i
++) {
382 if (!IsTileType(TileXY(MapMaxX() - 1, i
), MP_WATER
) || TileHeight(TileXY(MapMaxX(), i
)) != 0) {
383 ShowErrorMessage(STR_CONFIG_SETTING_EDGES_NOT_WATER
, INVALID_STRING_ID
, WL_ERROR
);
391 static void UpdateFreeformEdges(int32 new_value
)
393 if (_game_mode
== GM_MENU
) return;
395 if (new_value
!= 0) {
396 for (uint x
= 0; x
< MapSizeX(); x
++) MakeVoid(TileXY(x
, 0));
397 for (uint y
= 0; y
< MapSizeY(); y
++) MakeVoid(TileXY(0, y
));
399 /* Make tiles at the border water again. */
400 for (uint i
= 0; i
< MapMaxX(); i
++) {
401 SetTileHeight(TileXY(i
, 0), 0);
402 SetTileType(TileXY(i
, 0), MP_WATER
);
404 for (uint i
= 0; i
< MapMaxY(); i
++) {
405 SetTileHeight(TileXY(0, i
), 0);
406 SetTileType(TileXY(0, i
), MP_WATER
);
409 MarkWholeScreenDirty();
413 * Changing the setting "allow multiple NewGRF sets" is not allowed
414 * if there are vehicles.
416 static bool CheckDynamicEngines(int32
&new_value
)
418 if (_game_mode
== GM_MENU
) return true;
420 if (!EngineOverrideManager::ResetToCurrentNewGRFConfig()) {
421 ShowErrorMessage(STR_CONFIG_SETTING_DYNAMIC_ENGINES_EXISTING_VEHICLES
, INVALID_STRING_ID
, WL_ERROR
);
428 static bool CheckMaxHeightLevel(int32
&new_value
)
430 if (_game_mode
== GM_NORMAL
) return false;
431 if (_game_mode
!= GM_EDITOR
) return true;
433 /* Check if at least one mountain on the map is higher than the new value.
434 * If yes, disallow the change. */
435 for (TileIndex t
= 0; t
< MapSize(); t
++) {
436 if ((int32
)TileHeight(t
) > new_value
) {
437 ShowErrorMessage(STR_CONFIG_SETTING_TOO_HIGH_MOUNTAIN
, INVALID_STRING_ID
, WL_ERROR
);
438 /* Return old, unchanged value */
446 static void StationCatchmentChanged(int32 new_value
)
448 Station::RecomputeCatchmentForAll();
449 MarkWholeScreenDirty();
452 static void MaxVehiclesChanged(int32 new_value
)
454 InvalidateWindowClassesData(WC_BUILD_TOOLBAR
);
455 MarkWholeScreenDirty();
458 static void InvalidateShipPathCache(int32 new_value
)
460 for (Ship
*s
: Ship::Iterate()) {
466 * Replace a passwords that are a literal asterisk with an empty string.
467 * @param newval The new string value for this password field.
468 * @return Always true.
470 static bool ReplaceAsteriskWithEmptyPassword(std::string
&newval
)
472 if (newval
.compare("*") == 0) newval
.clear();
476 /** Update the game info, and send it to the clients when we are running as a server. */
477 static void UpdateClientConfigValues()
479 NetworkServerUpdateGameInfo();
480 if (_network_server
) NetworkServerSendConfigUpdate();
483 /* End - Callback Functions */