changeset 9359:c3a0ec3104c9 draft

(svn r13256) -Codechange: merge the OPTS and PATS chuncks. -Codechange: split the diff_custom variable. -Feature: allow changing some of the diff_custom variables via the console in network games.
author rubidium <rubidium@openttd.org>
date Mon, 26 May 2008 00:31:36 +0000
parents 12f4585b2124
children 71535b4ff2ee
files src/command.cpp src/command_type.h src/economy.cpp src/genworld_gui.cpp src/misc.cpp src/misc_cmd.cpp src/oldloader.cpp src/saveload.cpp src/settings.cpp src/settings_gui.cpp src/settings_internal.h src/settings_type.h
diffstat 12 files changed, 260 insertions(+), 266 deletions(-) [+]
line wrap: on
line diff
--- a/src/command.cpp
+++ b/src/command.cpp
@@ -152,7 +152,6 @@
 
 DEF_COMMAND(CmdSetRoadDriveSide);
 
-DEF_COMMAND(CmdChangeDifficultyLevel);
 DEF_COMMAND(CmdChangePatchSetting);
 
 DEF_COMMAND(CmdStartStopShip);
@@ -304,7 +303,6 @@
 	{CmdDoTownAction,                        0}, /* CMD_DO_TOWN_ACTION */
 
 	{CmdSetRoadDriveSide,           CMD_SERVER}, /* CMD_SET_ROAD_DRIVE_SIDE */
-	{CmdChangeDifficultyLevel,      CMD_SERVER}, /* CMD_CHANGE_DIFFICULTY_LEVEL */
 
 	{CmdStartStopShip,                       0}, /* CMD_START_STOP_SHIP */
 	{CmdSellShip,                            0}, /* CMD_SELL_SHIP */
--- a/src/command_type.h
+++ b/src/command_type.h
@@ -233,8 +233,6 @@
 
 	CMD_SET_ROAD_DRIVE_SIDE,          ///< set the side where the road vehicles drive
 
-	CMD_CHANGE_DIFFICULTY_LEVEL,      ///< change the difficult of a game (each setting for it own)
-
 	CMD_START_STOP_SHIP,              ///< start/stop a ship
 	CMD_SELL_SHIP,                    ///< sell a ship
 	CMD_BUILD_SHIP,                   ///< build a new ship
--- a/src/economy.cpp
+++ b/src/economy.cpp
@@ -832,7 +832,7 @@
 	_economy.interest_rate = _settings.difficulty.initial_interest;
 	_economy.infl_amount = _settings.difficulty.initial_interest;
 	_economy.infl_amount_pr = max(0, _settings.difficulty.initial_interest - 1);
-	_economy.max_loan_unround = _economy.max_loan = _settings.difficulty.max_loan * 1000;
+	_economy.max_loan_unround = _economy.max_loan = _settings.difficulty.max_loan;
 	_economy.fluct = GB(Random(), 0, 8) + 168;
 }
 
--- a/src/genworld_gui.cpp
+++ b/src/genworld_gui.cpp
@@ -48,7 +48,7 @@
 
 static inline void SetNewLandscapeType(byte landscape)
 {
-	_settings.game_creation.landscape = landscape;
+	_settings_newgame.game_creation.landscape = landscape;
 	InvalidateWindowClasses(WC_SELECT_GAME);
 	InvalidateWindowClasses(WC_GENERATE_LANDSCAPE);
 }
@@ -253,7 +253,7 @@
 
 	GenerateLandscapeWindow(const WindowDesc *desc, WindowNumber number = 0) : QueryStringBaseWindow(desc, number)
 	{
-		this->LowerWidget(_settings.game_creation.landscape + GLAND_TEMPERATE);
+		this->LowerWidget(_settings_newgame.game_creation.landscape + GLAND_TEMPERATE);
 
 		snprintf(this->edit_str_buf, sizeof(this->edit_str_buf), "%u", _settings_newgame.game_creation.generation_seed);
 		InitializeTextBuffer(&this->text, this->edit_str_buf, lengthof(this->edit_str_buf), 120);
@@ -272,7 +272,7 @@
 			this->SetWidgetDisabledState(GLAND_SMOOTHNESS_PULLDOWN, _settings_newgame.game_creation.land_generator == 0);
 		}
 		/* Disable snowline if not hilly */
-		this->SetWidgetDisabledState(GLAND_SNOW_LEVEL_TEXT, _settings.game_creation.landscape != LT_ARCTIC);
+		this->SetWidgetDisabledState(GLAND_SNOW_LEVEL_TEXT, _settings_newgame.game_creation.landscape != LT_ARCTIC);
 		/* Disable town, industry and trees in SE */
 		this->SetWidgetDisabledState(GLAND_TOWN_PULLDOWN,     _game_mode == GM_EDITOR);
 		this->SetWidgetDisabledState(GLAND_INDUSTRY_PULLDOWN, _game_mode == GM_EDITOR);
@@ -280,13 +280,13 @@
 
 		this->SetWidgetDisabledState(GLAND_START_DATE_DOWN, _settings_newgame.game_creation.starting_year <= MIN_YEAR);
 		this->SetWidgetDisabledState(GLAND_START_DATE_UP,   _settings_newgame.game_creation.starting_year >= MAX_YEAR);
-		this->SetWidgetDisabledState(GLAND_SNOW_LEVEL_DOWN, _settings_newgame.game_creation.snow_line_height <= 2 || _settings.game_creation.landscape != LT_ARCTIC);
-		this->SetWidgetDisabledState(GLAND_SNOW_LEVEL_UP,   _settings_newgame.game_creation.snow_line_height >= MAX_SNOWLINE_HEIGHT || _settings.game_creation.landscape != LT_ARCTIC);
+		this->SetWidgetDisabledState(GLAND_SNOW_LEVEL_DOWN, _settings_newgame.game_creation.snow_line_height <= 2 || _settings_newgame.game_creation.landscape != LT_ARCTIC);
+		this->SetWidgetDisabledState(GLAND_SNOW_LEVEL_UP,   _settings_newgame.game_creation.snow_line_height >= MAX_SNOWLINE_HEIGHT || _settings_newgame.game_creation.landscape != LT_ARCTIC);
 
-		this->SetWidgetLoweredState(GLAND_TEMPERATE, _settings.game_creation.landscape == LT_TEMPERATE);
-		this->SetWidgetLoweredState(GLAND_ARCTIC,    _settings.game_creation.landscape == LT_ARCTIC);
-		this->SetWidgetLoweredState(GLAND_TROPICAL,  _settings.game_creation.landscape == LT_TROPIC);
-		this->SetWidgetLoweredState(GLAND_TOYLAND,   _settings.game_creation.landscape == LT_TOYLAND);
+		this->SetWidgetLoweredState(GLAND_TEMPERATE, _settings_newgame.game_creation.landscape == LT_TEMPERATE);
+		this->SetWidgetLoweredState(GLAND_ARCTIC,    _settings_newgame.game_creation.landscape == LT_ARCTIC);
+		this->SetWidgetLoweredState(GLAND_TROPICAL,  _settings_newgame.game_creation.landscape == LT_TROPIC);
+		this->SetWidgetLoweredState(GLAND_TOYLAND,   _settings_newgame.game_creation.landscape == LT_TOYLAND);
 
 		if (_game_mode == GM_EDITOR) {
 			this->widget[GLAND_TOWN_PULLDOWN].data     = STR_6836_OFF;
@@ -345,7 +345,7 @@
 			case GLAND_ARCTIC:
 			case GLAND_TROPICAL:
 			case GLAND_TOYLAND:
-				this->RaiseWidget(_settings.game_creation.landscape + GLAND_TEMPERATE);
+				this->RaiseWidget(_settings_newgame.game_creation.landscape + GLAND_TEMPERATE);
 				SetNewLandscapeType(widget - GLAND_TEMPERATE);
 				break;
 
@@ -492,13 +492,13 @@
 			case GLAND_TOWN_PULLDOWN:
 				_settings_newgame.difficulty.number_towns = index;
 				if (_settings_newgame.difficulty.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
-				DoCommandP(0, 2, _settings_newgame.difficulty.number_towns, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
+				IConsoleSetPatchSetting("difficulty.number_towns", _settings_newgame.difficulty.number_towns);
 				break;
 
 			case GLAND_INDUSTRY_PULLDOWN:
 				_settings_newgame.difficulty.number_industries = index;
 				if (_settings_newgame.difficulty.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
-				DoCommandP(0, 3, _settings_newgame.difficulty.number_industries, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
+				IConsoleSetPatchSetting("difficulty.number_industries", _settings_newgame.difficulty.number_industries);
 				break;
 
 			case GLAND_LANDSCAPE_PULLDOWN:
@@ -513,13 +513,13 @@
 			case GLAND_TERRAIN_PULLDOWN:
 				_settings_newgame.difficulty.terrain_type = index;
 				if (_settings_newgame.difficulty.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
-				DoCommandP(0, 12, _settings_newgame.difficulty.terrain_type, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
+				IConsoleSetPatchSetting("difficulty.terrain_type", _settings_newgame.difficulty.terrain_type);
 				break;
 
 			case GLAND_WATER_PULLDOWN:
 				_settings_newgame.difficulty.quantity_sea_lakes = index;
 				if (_settings_newgame.difficulty.diff_level != 3) ShowErrorMessage(INVALID_STRING_ID, STR_DIFFICULTY_TO_CUSTOM, 0, 0);
-				DoCommandP(0, 13, _settings_newgame.difficulty.quantity_sea_lakes, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
+				IConsoleSetPatchSetting("difficulty.quantity_sea_lakes", _settings_newgame.difficulty.quantity_sea_lakes);
 				break;
 		}
 		this->SetDirty();
@@ -642,7 +642,7 @@
 
 	CreateScenarioWindow(const WindowDesc *desc, WindowNumber window_number) : Window(desc, window_number)
 	{
-		this->LowerWidget(_settings.game_creation.landscape + CSCEN_TEMPERATE);
+		this->LowerWidget(_settings_newgame.game_creation.landscape + CSCEN_TEMPERATE);
 		this->FindWindowPlacementAndResize(desc);
 	}
 
@@ -653,10 +653,10 @@
 		this->SetWidgetDisabledState(CSCEN_FLAT_LAND_HEIGHT_DOWN, _settings_newgame.game_creation.se_flat_world_height <= 0);
 		this->SetWidgetDisabledState(CSCEN_FLAT_LAND_HEIGHT_UP,   _settings_newgame.game_creation.se_flat_world_height >= MAX_TILE_HEIGHT);
 
-		this->SetWidgetLoweredState(CSCEN_TEMPERATE, _settings.game_creation.landscape == LT_TEMPERATE);
-		this->SetWidgetLoweredState(CSCEN_ARCTIC,    _settings.game_creation.landscape == LT_ARCTIC);
-		this->SetWidgetLoweredState(CSCEN_TROPICAL,  _settings.game_creation.landscape == LT_TROPIC);
-		this->SetWidgetLoweredState(CSCEN_TOYLAND,   _settings.game_creation.landscape == LT_TOYLAND);
+		this->SetWidgetLoweredState(CSCEN_TEMPERATE, _settings_newgame.game_creation.landscape == LT_TEMPERATE);
+		this->SetWidgetLoweredState(CSCEN_ARCTIC,    _settings_newgame.game_creation.landscape == LT_ARCTIC);
+		this->SetWidgetLoweredState(CSCEN_TROPICAL,  _settings_newgame.game_creation.landscape == LT_TROPIC);
+		this->SetWidgetLoweredState(CSCEN_TOYLAND,   _settings_newgame.game_creation.landscape == LT_TOYLAND);
 
 		/* Set parameters for widget text that requires them */
 		SetDParam(0, ConvertYMDToDate(_settings_newgame.game_creation.starting_year, 0, 1)); // CSCEN_START_DATE_TEXT
@@ -674,7 +674,7 @@
 			case CSCEN_ARCTIC:
 			case CSCEN_TROPICAL:
 			case CSCEN_TOYLAND:
-				this->RaiseWidget(_settings.game_creation.landscape + CSCEN_TEMPERATE);
+				this->RaiseWidget(_settings_newgame.game_creation.landscape + CSCEN_TEMPERATE);
 				SetNewLandscapeType(widget - CSCEN_TEMPERATE);
 				break;
 
--- a/src/misc.cpp
+++ b/src/misc.cpp
@@ -67,6 +67,7 @@
 	_realtime_tick = 0;
 	_date_fract = 0;
 	_cur_tileloop_tile = 0;
+	_settings = _settings_newgame;
 
 	if ((mode & IG_DATE_RESET) == IG_DATE_RESET) {
 		SetDate(ConvertYMDToDate(_settings.game_creation.starting_year, 0, 1));
--- a/src/misc_cmd.cpp
+++ b/src/misc_cmd.cpp
@@ -22,7 +22,6 @@
 #include "player_base.h"
 #include "player_gui.h"
 #include "settings_type.h"
-#include "station_func.h"
 
 #include "table/strings.h"
 
@@ -380,46 +379,3 @@
 	/* Subtract money from local-player */
 	return amount;
 }
-
-/** Change difficulty level/settings (server-only).
- * We cannot really check for valid values of p2 (too much work mostly); stored
- * in file 'settings_gui.c' _game_setting_info[]; we'll just trust the server it knows
- * what to do and does this correctly
- * @param tile unused
- * @param flags operation to perform
- * @param p1 the difficulty setting being changed. If it is -1, the difficulty level
- *           itself is changed. The new value is inside p2
- * @param p2 new value for a difficulty setting or difficulty level
- */
-CommandCost CmdChangeDifficultyLevel(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
-{
-	if (p1 != (uint32)-1L && ((int32)p1 >= GAME_DIFFICULTY_NUM || (int32)p1 < 0)) return CMD_ERROR;
-
-	DifficultySettings *opt_ptr = (_game_mode == GM_MENU) ? &_settings_newgame.difficulty : &_settings.difficulty;
-
-	if (flags & DC_EXEC) {
-		if (p1 != (uint32)-1L) {
-			((GDType*)opt_ptr)[p1] = p2;
-			opt_ptr->diff_level = 3; // custom difficulty level
-		} else {
-			opt_ptr->diff_level = p2;
-		}
-
-		/* Since the tolerance of the town council has a direct impact on the noise generation/tolerance,
-		 * launch a re-evaluation of the actual values only when setting has changed */
-		if (p2 == GAME_DIFFICULTY_TOWNCOUNCIL_TOLERANCE && _game_mode == GM_NORMAL) {
-			UpdateAirportsNoise();
-			if (_settings.economy.station_noise_level) {
-				InvalidateWindowClassesData(WC_TOWN_VIEW, 0);
-			}
-		}
-
-		/* If we are a network-client, update the difficult setting (if it is open).
-		 * Use this instead of just dirtying the window because we need to load in
-		 * the new difficulty settings */
-		if (_networking && !_network_server && FindWindowById(WC_GAME_OPTIONS, 0) != NULL) {
-			ShowGameDifficulty();
-		}
-	}
-	return CommandCost();
-}
--- a/src/oldloader.cpp
+++ b/src/oldloader.cpp
@@ -1374,7 +1374,7 @@
 	OCL_SVAR( OC_FILE_U16 |  OC_VAR_U8, DifficultySettings, competitor_start_time ),
 	OCL_SVAR( OC_FILE_U16 |  OC_VAR_U8, DifficultySettings, number_towns ),
 	OCL_SVAR( OC_FILE_U16 |  OC_VAR_U8, DifficultySettings, number_industries ),
-	OCL_SVAR( OC_FILE_U16 | OC_VAR_U16, DifficultySettings, max_loan ),
+	OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, DifficultySettings, max_loan ),
 	OCL_SVAR( OC_FILE_U16 |  OC_VAR_U8, DifficultySettings, initial_interest ),
 	OCL_SVAR( OC_FILE_U16 |  OC_VAR_U8, DifficultySettings, vehicle_costs ),
 	OCL_SVAR( OC_FILE_U16 |  OC_VAR_U8, DifficultySettings, competitor_speed ),
@@ -1392,7 +1392,9 @@
 
 static inline bool LoadOldGameDifficulty(LoadgameState *ls, int num)
 {
-	return LoadChunk(ls, &_settings.difficulty, game_difficulty_chunk);
+	bool ret = LoadChunk(ls, &_settings.difficulty, game_difficulty_chunk);
+	_settings.difficulty.max_loan *= 1000;
+	return ret;
 }
 
 
--- a/src/saveload.cpp
+++ b/src/saveload.cpp
@@ -35,7 +35,7 @@
 
 #include "table/strings.h"
 
-extern const uint16 SAVEGAME_VERSION = 96;
+extern const uint16 SAVEGAME_VERSION = 97;
 
 SavegameType _savegame_type; ///< type of savegame we are loading
 
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -60,6 +60,7 @@
 #include "sound/sound_driver.hpp"
 #include "music/music_driver.hpp"
 #include "blitter/factory.hpp"
+#include "station_func.h"
 
 #include "table/strings.h"
 
@@ -764,7 +765,7 @@
 		}
 
 		p = (item == NULL) ? sdb->def : string_to_val(sdb, item->value);
-		ptr = GetVariableAddress(object, sld);
+		ptr = GetVariableAddress(sld->global ? NULL : object, sld);
 
 		switch (sdb->cmd) {
 		case SDT_BOOLX: /* All four are various types of (integer) numbers */
@@ -1133,6 +1134,7 @@
 #define NO SGF_NETWORK_ONLY
 #define CR SGF_CURRENCY
 #define NN SGF_NO_NETWORK
+#define NG SGF_NEWGAME_ONLY
 
 /* Begin - Callback Functions for the various settings */
 /* virtual PositionMainToolbar function, calls the right one.*/
@@ -1274,6 +1276,92 @@
 	return 0;
 }
 
+/*
+ * A: competitors
+ * B: start time in months / 3
+ * C: town count (3 = high, 0 = very low)
+ * D: industry count (4 = high, 0 = none)
+ * E: inital loan (in GBP)
+ * F: interest rate
+ * G: running costs (0 = low, 2 = high)
+ * H: construction speed of competitors (0 = very slow, 4 = very fast)
+ * I: intelligence (0-2)
+ * J: breakdowns (0 = off, 2 = normal)
+ * K: subsidy multiplier (0 = 1.5, 3 = 4.0)
+ * L: construction cost (0-2)
+ * M: terrain type (0 = very flat, 3 = mountainous)
+ * N: amount of water (0 = very low, 3 = high)
+ * O: economy (0 = steady, 1 = fluctuating)
+ * P: Train reversing (0 = end of line + stations, 1 = end of line)
+ * Q: disasters
+ * R: area restructuring (0 = permissive, 2 = hostile)
+ * S: the difficulty level
+ */
+static const DifficultySettings _default_game_diff[3] = { /*
+	 A, B, C, D,      E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S*/
+	{2, 2, 2, 4, 300000, 2, 0, 2, 0, 1, 2, 0, 1, 0, 0, 0, 0, 0, 0}, ///< easy
+	{4, 1, 2, 3, 150000, 3, 1, 3, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1}, ///< medium
+	{7, 0, 3, 3, 100000, 4, 1, 3, 2, 2, 0, 2, 3, 2, 1, 1, 1, 2, 2}, ///< hard
+};
+
+void SetDifficultyLevel(int mode, DifficultySettings *gm_opt)
+{
+	assert(mode <= 3);
+
+	if (mode != 3) {
+		*gm_opt = _default_game_diff[mode];
+	} else {
+		gm_opt->diff_level = 3;
+	}
+}
+
+/**
+ * Checks the difficulty levels read from the configuration and
+ * forces them to be correct when invalid.
+ */
+void CheckDifficultyLevels()
+{
+	if (_settings_newgame.difficulty.diff_level != 3) {
+		SetDifficultyLevel(_settings_newgame.difficulty.diff_level, &_settings_newgame.difficulty);
+	}
+}
+
+static int32 DifficultyReset(int32 level)
+{
+	SetDifficultyLevel(level, (_game_mode == GM_MENU) ? &_settings_newgame.difficulty : &_settings.difficulty);
+	return 0;
+}
+
+static int32 DifficultyChange(int32)
+{
+	if (_game_mode == GM_MENU) {
+		_settings_newgame.difficulty.diff_level = 3;
+	} else {
+		_settings.difficulty.diff_level = 3;
+	}
+
+	/* If we are a network-client, update the difficult setting (if it is open).
+	 * Use this instead of just dirtying the window because we need to load in
+	 * the new difficulty settings */
+	if (_networking && FindWindowById(WC_GAME_OPTIONS, 0) != NULL) {
+		ShowGameDifficulty();
+	}
+
+	return 0;
+}
+
+static int32 DifficultyNoiseChange(int32 i)
+{
+	if (_game_mode == GM_NORMAL) {
+		UpdateAirportsNoise();
+		if (_settings.economy.station_noise_level) {
+			InvalidateWindowClassesData(WC_TOWN_VIEW, 0);
+		}
+	}
+
+	return DifficultyChange(i);
+}
+
 /**
  * Check for right TownLayout usage in editor mode.
  * The No Road mode is not desirable since towns have to be
@@ -1417,6 +1505,9 @@
 };
 #endif /* ENABLE_NETWORK */
 
+static const uint GAME_DIFFICULTY_NUM = 18;
+uint16 _old_diff_custom[GAME_DIFFICULTY_NUM];
+
 static const SettingDesc _gameopt_settings[] = {
 	/* In version 4 a new difficulty setting has been added to the difficulty settings,
 	 * town attitude towards demolishing. Needs special handling because some dimwit thought
@@ -1427,18 +1518,19 @@
 	 * and why not byte for example?
 	 * 'SLE_FILE_I16 | SLE_VAR_U16' in "diff_custom" is needed to get around SlArray() hack
 	 * for savegames version 0 - though it is an array, it has to go through the byteswap process */
-	SDT_GENERAL("diff_custom", SDT_INTLIST, SL_ARR, SLE_FILE_I16 | SLE_VAR_U16, 0, 0, Settings, difficulty, 17, 0, 0, 0, 0, NULL, STR_NULL, NULL, NULL, 0, 3),
-	SDT_GENERAL("diff_custom", SDT_INTLIST, SL_ARR, SLE_UINT16, 0, 0, Settings, difficulty, 18, 0, 0, 0, 0, NULL, STR_NULL, NULL, NULL, 4, SL_MAX_VERSION),
-	    SDT_VAR(Settings, difficulty.diff_level, SLE_UINT8, 0, 0, 0, 0,  3, 0, STR_NULL, NULL),
-	  SDT_OMANY(Settings, gui.currency,  SLE_UINT8, N, 0, 0, CUSTOM_CURRENCY_ID, "GBP|USD|EUR|YEN|ATS|BEF|CHF|CZK|DEM|DKK|ESP|FIM|FRF|GRD|HUF|ISK|ITL|NLG|NOK|PLN|ROL|RUR|SIT|SEK|YTL|SKK|BRR|custom", STR_NULL, NULL, NULL),
-	  SDT_OMANY(Settings, gui.units,     SLE_UINT8, N, 0, 1,     2, "imperial|metric|si", STR_NULL, NULL, NULL),
+	 SDTG_GENERAL("diff_custom", SDT_INTLIST, SL_ARR, SLE_FILE_I16 | SLE_VAR_U16,    C, 0, _old_diff_custom, 17, 0, 0, 0, 0, NULL, STR_NULL, NULL, 0,  3),
+	 SDTG_GENERAL("diff_custom", SDT_INTLIST, SL_ARR, SLE_UINT16,                    C, 0, _old_diff_custom, 18, 0, 0, 0, 0, NULL, STR_NULL, NULL, 4, 96),
+
+	      SDT_VAR(Settings, difficulty.diff_level,    SLE_UINT8,                     0, 0, 0, 0,  3, 0, STR_NULL, NULL),
+	    SDT_OMANY(Settings, gui.currency,             SLE_UINT8,                     N, 0, 0, CUSTOM_CURRENCY_ID, "GBP|USD|EUR|YEN|ATS|BEF|CHF|CZK|DEM|DKK|ESP|FIM|FRF|GRD|HUF|ISK|ITL|NLG|NOK|PLN|ROL|RUR|SIT|SEK|YTL|SKK|BRR|custom", STR_NULL, NULL, NULL),
+	    SDT_OMANY(Settings, gui.units,                SLE_UINT8,                     N, 0, 1, 2, "imperial|metric|si", STR_NULL, NULL, NULL),
 	/* There are only 21 predefined town_name values (0-20), but you can have more with newgrf action F so allow these bigger values (21-255). Invalid values will fallback to english on use and (undefined string) in GUI. */
-	  SDT_OMANY(Settings, game_creation.town_name, SLE_UINT8, 0, 0, 0,   255, "english|french|german|american|latin|silly|swedish|dutch|finnish|polish|slovakish|norwegian|hungarian|austrian|romanian|czech|swiss|danish|turkish|italian|catalan", STR_NULL, NULL, NULL),
-	  SDT_OMANY(Settings, game_creation.landscape, SLE_UINT8, 0, 0, 0,     3, "temperate|arctic|tropic|toyland", STR_NULL, NULL, ConvertLandscape),
-	    SDT_VAR(Settings, game_creation.snow_line, SLE_UINT8, 0, 0, 7 * TILE_HEIGHT, 2 * TILE_HEIGHT, 13 * TILE_HEIGHT, 0, STR_NULL, NULL),
-	SDT_CONDOMANY(Settings,gui.autosave, SLE_UINT8, 0, 22,             N, 0, 0, 0, "", STR_NULL, NULL, NULL),
-	SDT_CONDOMANY(Settings,gui.autosave, SLE_UINT8,23, SL_MAX_VERSION, S, 0, 1, 4, "off|monthly|quarterly|half year|yearly", STR_NULL, NULL, NULL),
-	  SDT_OMANY(Settings, vehicle.road_side, SLE_UINT8, 0, 0, 1,   1, "left|right", STR_NULL, NULL, NULL),
+	    SDT_OMANY(Settings, game_creation.town_name,  SLE_UINT8,                     0, 0, 0, 255, "english|french|german|american|latin|silly|swedish|dutch|finnish|polish|slovakish|norwegian|hungarian|austrian|romanian|czech|swiss|danish|turkish|italian|catalan", STR_NULL, NULL, NULL),
+	    SDT_OMANY(Settings, game_creation.landscape,  SLE_UINT8,                     0, 0, 0, 3, "temperate|arctic|tropic|toyland", STR_NULL, NULL, ConvertLandscape),
+	      SDT_VAR(Settings, game_creation.snow_line,  SLE_UINT8,                     0, 0, 7 * TILE_HEIGHT, 2 * TILE_HEIGHT, 13 * TILE_HEIGHT, 0, STR_NULL, NULL),
+	SDT_CONDOMANY(Settings, gui.autosave,             SLE_UINT8,  0, 22,             N, 0, 0, 0, "", STR_NULL, NULL, NULL),
+	SDT_CONDOMANY(Settings, gui.autosave,             SLE_UINT8, 23, SL_MAX_VERSION, S, 0, 1, 4, "off|monthly|quarterly|half year|yearly", STR_NULL, NULL, NULL),
+	    SDT_OMANY(Settings, vehicle.road_side,        SLE_UINT8,                     0, 0, 1, 1, "left|right", STR_NULL, NULL, NULL),
 	    SDT_END()
 };
 
@@ -1454,6 +1546,32 @@
 const SettingDesc _patch_settings[] = {
 	/***************************************************************************/
 	/* Saved patch variables. */
+	/* Do not ADD or REMOVE something in this "difficulty.XXX" table or before it. It breaks savegame compatability. */
+	 SDT_CONDVAR(Settings, difficulty.max_no_competitors,        SLE_UINT8, 97, SL_MAX_VERSION, 0, 0,     2,     0,      7,  1, STR_NULL,                                  DifficultyChange),
+	 SDT_CONDVAR(Settings, difficulty.competitor_start_time,     SLE_UINT8, 97, SL_MAX_VERSION, 0,NG,     2,     0,      3,  1, STR_6830_IMMEDIATE,                        DifficultyChange),
+	 SDT_CONDVAR(Settings, difficulty.number_towns,              SLE_UINT8, 97, SL_MAX_VERSION, 0,NG,     2,     0,      3,  1, STR_NUM_VERY_LOW,                          DifficultyChange),
+	 SDT_CONDVAR(Settings, difficulty.number_industries,         SLE_UINT8, 97, SL_MAX_VERSION, 0,NG,     4,     0,      4,  1, STR_NONE,                                  DifficultyChange),
+	 SDT_CONDVAR(Settings, difficulty.max_loan,                 SLE_UINT32, 97, SL_MAX_VERSION, 0,NG|CR,300000,100000,500000,50000,STR_NULL,                               DifficultyChange),
+	 SDT_CONDVAR(Settings, difficulty.initial_interest,          SLE_UINT8, 97, SL_MAX_VERSION, 0,NG,     2,     2,      4,  1, STR_NULL,                                  DifficultyChange),
+	 SDT_CONDVAR(Settings, difficulty.vehicle_costs,             SLE_UINT8, 97, SL_MAX_VERSION, 0, 0,     0,     0,      2,  1, STR_6820_LOW,                              DifficultyChange),
+	 SDT_CONDVAR(Settings, difficulty.competitor_speed,          SLE_UINT8, 97, SL_MAX_VERSION, 0, 0,     2,     0,      4,  1, STR_681B_VERY_SLOW,                        DifficultyChange),
+	 SDT_CONDVAR(Settings, difficulty.competitor_intelligence,   SLE_UINT8, 97, SL_MAX_VERSION, 0, 0,     0,     0,      2,  1, STR_6820_LOW,                              DifficultyChange),
+	 SDT_CONDVAR(Settings, difficulty.vehicle_breakdowns,        SLE_UINT8, 97, SL_MAX_VERSION, 0, 0,     1,     0,      2,  1, STR_6823_NONE,                             DifficultyChange),
+	 SDT_CONDVAR(Settings, difficulty.subsidy_multiplier,        SLE_UINT8, 97, SL_MAX_VERSION, 0, 0,     2,     0,      3,  1, STR_6826_X1_5,                             DifficultyChange),
+	 SDT_CONDVAR(Settings, difficulty.construction_cost,         SLE_UINT8, 97, SL_MAX_VERSION, 0,NG,     0,     0,      2,  1, STR_6820_LOW,                              DifficultyChange),
+	 SDT_CONDVAR(Settings, difficulty.terrain_type,              SLE_UINT8, 97, SL_MAX_VERSION, 0,NG,     1,     0,      3,  1, STR_682A_VERY_FLAT,                        DifficultyChange),
+	 SDT_CONDVAR(Settings, difficulty.quantity_sea_lakes,        SLE_UINT8, 97, SL_MAX_VERSION, 0,NG,     0,     0,      3,  1, STR_VERY_LOW,                              DifficultyChange),
+	 SDT_CONDVAR(Settings, difficulty.economy,                   SLE_UINT8, 97, SL_MAX_VERSION, 0, 0,     0,     0,      1,  1, STR_682E_STEADY,                           DifficultyChange),
+	 SDT_CONDVAR(Settings, difficulty.line_reverse_mode,         SLE_UINT8, 97, SL_MAX_VERSION, 0, 0,     0,     0,      1,  1, STR_6834_AT_END_OF_LINE_AND_AT_STATIONS,   DifficultyChange),
+	 SDT_CONDVAR(Settings, difficulty.disasters,                 SLE_UINT8, 97, SL_MAX_VERSION, 0, 0,     0,     0,      1,  1, STR_6836_OFF,                              DifficultyChange),
+	 SDT_CONDVAR(Settings, difficulty.town_council_tolerance,    SLE_UINT8, 97, SL_MAX_VERSION, 0, 0,     0,     0,      2,  1, STR_PERMISSIVE,                            DifficultyNoiseChange),
+	 SDT_CONDVAR(Settings, difficulty.diff_level,                SLE_UINT8, 97, SL_MAX_VERSION, 0,NG,     0,     0,      3,  0, STR_NULL,                                  DifficultyReset),
+
+	/* There are only 21 predefined town_name values (0-20), but you can have more with newgrf action F so allow these bigger values (21-255). Invalid values will fallback to english on use and (undefined string) in GUI. */
+ SDT_CONDOMANY(Settings, game_creation.town_name,              SLE_UINT8, 97, SL_MAX_VERSION, 0,NN, 0, 255, "english|french|german|american|latin|silly|swedish|dutch|finnish|polish|slovakish|norwegian|hungarian|austrian|romanian|czech|swiss|danish|turkish|italian|catalan", STR_NULL, NULL, NULL),
+ SDT_CONDOMANY(Settings, game_creation.landscape,              SLE_UINT8, 97, SL_MAX_VERSION, 0,NN, 0,   3, "temperate|arctic|tropic|toyland", STR_NULL, NULL, ConvertLandscape),
+	 SDT_CONDVAR(Settings, game_creation.snow_line,              SLE_UINT8, 97, SL_MAX_VERSION, 0,NN, 7 * TILE_HEIGHT, 2 * TILE_HEIGHT, 13 * TILE_HEIGHT, 0, STR_NULL, NULL),
+ SDT_CONDOMANY(Settings, vehicle.road_side,                    SLE_UINT8, 97, SL_MAX_VERSION, 0,NN, 1,   1, "left|right", STR_NULL, NULL, NULL),
 
 	    SDT_BOOL(Settings, construction.build_on_slopes,                                        0,NN,  true,                    STR_CONFIG_PATCHES_BUILDONSLOPES,          NULL),
 	SDT_CONDBOOL(Settings, construction.autoslope,                          75, SL_MAX_VERSION, 0, 0,  true,                    STR_CONFIG_PATCHES_AUTOSLOPE,              NULL),
@@ -1593,8 +1711,12 @@
 	     SDT_VAR(Settings, game_creation.heightmap_rotation,              SLE_UINT8,                     S,MS,     0,                     0,       1, 0, STR_CONFIG_PATCHES_HEIGHTMAP_ROTATION,    NULL),
 	     SDT_VAR(Settings, game_creation.se_flat_world_height,            SLE_UINT8,                     S, 0,     0,                     0,      15, 0, STR_CONFIG_PATCHES_SE_FLAT_WORLD_HEIGHT,  NULL),
 
+ SDT_CONDOMANY(Settings, gui.currency,                                  SLE_UINT8, 97, SL_MAX_VERSION, N, 0, 0, CUSTOM_CURRENCY_ID, "GBP|USD|EUR|YEN|ATS|BEF|CHF|CZK|DEM|DKK|ESP|FIM|FRF|GRD|HUF|ISK|ITL|NLG|NOK|PLN|ROL|RUR|SIT|SEK|YTL|SKK|BRR|custom", STR_NULL, NULL, NULL),
+ SDT_CONDOMANY(Settings, gui.units,                                     SLE_UINT8, 97, SL_MAX_VERSION, N, 0, 1, 2, "imperial|metric|si", STR_NULL, NULL, NULL),
+
 	/***************************************************************************/
 	/* Unsaved patch variables. */
+ SDT_OMANY(Settings, gui.autosave,               SLE_UINT8, S, 0, 1, 4, "off|monthly|quarterly|half year|yearly", STR_NULL, NULL,               NULL),
 	SDT_BOOL(Settings, gui.vehicle_speed,                     S, 0,  true,                        STR_CONFIG_PATCHES_VEHICLESPEED,                NULL),
 	SDT_BOOL(Settings, gui.status_long_date,                  S, 0,  true,                        STR_CONFIG_PATCHES_LONGDATE,                    NULL),
 	SDT_BOOL(Settings, gui.show_finances,                     S, 0,  true,                        STR_CONFIG_PATCHES_SHOWFINANCES,                NULL),
@@ -1671,6 +1793,31 @@
 #undef NO
 #undef CR
 
+static void PrepareOldDiffCustom()
+{
+	memset(_old_diff_custom, 0, sizeof(_old_diff_custom));
+}
+
+static void HandleOldDiffCustom()
+{
+	uint options_to_load = GAME_DIFFICULTY_NUM - (CheckSavegameVersion(4) ? 1 : 0);
+
+	/* If we did read to old_diff_custom, then at least one value must be non 0. */
+	bool old_diff_custom_used = false;
+	for (uint i = 0; i < options_to_load && !old_diff_custom_used; i++) {
+		old_diff_custom_used = (_old_diff_custom[i] != 0);
+	}
+
+	if (!old_diff_custom_used) return;
+
+	for (uint i = 0; i < options_to_load; i++) {
+		const SettingDesc *sd = &_patch_settings[i];
+		void *var = GetVariableAddress((_game_mode == GM_MENU) ? &_settings_newgame : &_settings, &sd->save);
+		Write_ValidateSetting(var, sd, (int32)((i == 4 ? 1000 : 1) * _old_diff_custom[i]));
+	}
+}
+
+
 static void NewsDisplayLoadConfig(IniFile *ini, const char *grpname)
 {
 	IniGroup *group = ini_getgroup(ini, grpname);
@@ -1836,7 +1983,10 @@
 	proc(ini, (const SettingDesc*)_win32_settings,   "win32", NULL);
 #endif /* WIN32 */
 
+	PrepareOldDiffCustom();
 	proc(ini, _gameopt_settings, "gameopt",  &_settings_newgame);
+	HandleOldDiffCustom();
+
 	proc(ini, _patch_settings,   "patches",  &_settings_newgame);
 	proc(ini, _currency_settings,"currency", &_custom_currency);
 
@@ -1847,8 +1997,6 @@
 #endif /* ENABLE_NETWORK */
 }
 
-extern void CheckDifficultyLevels();
-
 /** Load the values from the configuration files */
 void LoadFromConfig()
 {
@@ -1870,6 +2018,7 @@
 	/* Remove some obsolete groups. These have all been loaded into other groups. */
 	ini_removegroup(ini, "patches");
 	ini_removegroup(ini, "yapf");
+	ini_removegroup(ini, "gameopt");
 
 	HandleSettingDescs(ini, ini_save_settings, ini_save_setting_list);
 	GRFSaveConfig(ini, "newgrf", _grfconfig_newgame);
@@ -1903,6 +2052,7 @@
 
 	if ((sd->desc.flags & SGF_NETWORK_ONLY) && !_networking) return CMD_ERROR;
 	if ((sd->desc.flags & SGF_NO_NETWORK) && _networking) return CMD_ERROR;
+	if ((sd->desc.flags & SGF_NEWGAME_ONLY) && _game_mode != GM_MENU) return CMD_ERROR;
 
 	if (flags & DC_EXEC) {
 		Settings *s = (_game_mode == GM_MENU) ? &_settings_newgame : &_settings;
@@ -2034,7 +2184,7 @@
 {
 	for (; osd->save.cmd != SL_END; osd++) {
 		const SaveLoad *sld = &osd->save;
-		void *ptr = GetVariableAddress(object, sld);
+		void *ptr = GetVariableAddress(sld->global ? NULL : object, sld);
 
 		if (!SlObjectMember(ptr, sld)) continue;
 	}
@@ -2084,12 +2234,9 @@
 	/* Copy over default setting since some might not get loaded in
 	 * a networking environment. This ensures for example that the local
 	 * autosave-frequency stays when joining a network-server */
+	PrepareOldDiffCustom();
 	LoadSettings(_gameopt_settings, &_settings);
-}
-
-static void Save_OPTS()
-{
-	SaveSettings(_gameopt_settings, &_settings);
+	HandleOldDiffCustom();
 }
 
 static void Load_PATS()
@@ -2124,7 +2271,7 @@
 }
 
 extern const ChunkHandler _setting_chunk_handlers[] = {
-	{ 'OPTS', Save_OPTS, Load_OPTS, CH_RIFF},
+	{ 'OPTS', NULL,      Load_OPTS, CH_RIFF},
 	{ 'PATS', Save_PATS, Load_PATS, CH_RIFF | CH_LAST},
 };
 
--- a/src/settings_gui.cpp
+++ b/src/settings_gui.cpp
@@ -289,7 +289,7 @@
 				break;
 
 			case GAMEOPT_AUTOSAVE_BTN: // Autosave options
-				_settings.gui.autosave = _settings.gui.autosave = index;
+				_settings.gui.autosave = _settings_newgame.gui.autosave = index;
 				this->SetDirty();
 				break;
 
@@ -361,90 +361,6 @@
 	new GameOptionsWindow(&_game_options_desc);
 }
 
-struct GameSettingData {
-	int16 min;
-	int16 max;
-	int16 step;
-	StringID str;
-};
-
-static const GameSettingData _game_setting_info[] = {
-	{  0,   7,  1, STR_NULL},
-	{  0,   3,  1, STR_6830_IMMEDIATE},
-	{  0,   3,  1, STR_NUM_VERY_LOW},
-	{  0,   4,  1, STR_NONE},
-	{100, 500, 50, STR_NULL},
-	{  2,   4,  1, STR_NULL},
-	{  0,   2,  1, STR_6820_LOW},
-	{  0,   4,  1, STR_681B_VERY_SLOW},
-	{  0,   2,  1, STR_6820_LOW},
-	{  0,   2,  1, STR_6823_NONE},
-	{  0,   3,  1, STR_6826_X1_5},
-	{  0,   2,  1, STR_6820_LOW},
-	{  0,   3,  1, STR_682A_VERY_FLAT},
-	{  0,   3,  1, STR_VERY_LOW},
-	{  0,   1,  1, STR_682E_STEADY},
-	{  0,   1,  1, STR_6834_AT_END_OF_LINE_AND_AT_STATIONS},
-	{  0,   1,  1, STR_6836_OFF},
-	{  0,   2,  1, STR_PERMISSIVE},
-};
-
-/*
- * A: competitors
- * B: start time in months / 3
- * C: town count (3 = high, 0 = very low)
- * D: industry count (4 = high, 0 = none)
- * E: inital loan / 1000 (in GBP)
- * F: interest rate
- * G: running costs (0 = low, 2 = high)
- * H: construction speed of competitors (0 = very slow, 4 = very fast)
- * I: intelligence (0-2)
- * J: breakdowns (0 = off, 2 = normal)
- * K: subsidy multiplier (0 = 1.5, 3 = 4.0)
- * L: construction cost (0-2)
- * M: terrain type (0 = very flat, 3 = mountainous)
- * N: amount of water (0 = very low, 3 = high)
- * O: economy (0 = steady, 1 = fluctuating)
- * P: Train reversing (0 = end of line + stations, 1 = end of line)
- * Q: disasters
- * R: area restructuring (0 = permissive, 2 = hostile)
- */
-static const GDType _default_game_diff[3][GAME_DIFFICULTY_NUM] = { /*
-	 A, B, C, D,   E, F, G, H, I, J, K, L, M, N, O, P, Q, R*/
-	{2, 2, 2, 4, 300, 2, 0, 2, 0, 1, 2, 0, 1, 0, 0, 0, 0, 0}, ///< easy
-	{4, 1, 2, 3, 150, 3, 1, 3, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1}, ///< medium
-	{7, 0, 3, 3, 100, 4, 1, 3, 2, 2, 0, 2, 3, 2, 1, 1, 1, 2}, ///< hard
-};
-
-void SetDifficultyLevel(int mode, DifficultySettings *gm_opt)
-{
-	int i;
-	assert(mode <= 3);
-
-	gm_opt->diff_level = mode;
-	if (mode != 3) { // not custom
-		for (i = 0; i != GAME_DIFFICULTY_NUM; i++)
-			((GDType*)gm_opt)[i] = _default_game_diff[mode][i];
-	}
-}
-
-/**
- * Checks the difficulty levels read from the configuration and
- * forces them to be correct when invalid.
- */
-void CheckDifficultyLevels()
-{
-	if (_settings_newgame.difficulty.diff_level != 3) {
-		SetDifficultyLevel(_settings_newgame.difficulty.diff_level, &_settings_newgame.difficulty);
-	} else {
-		for (uint i = 0; i < GAME_DIFFICULTY_NUM; i++) {
-			GDType *diff = ((GDType*)&_settings_newgame.difficulty) + i;
-			*diff = Clamp(*diff, _game_setting_info[i].min, _game_setting_info[i].max);
-			*diff -= *diff % _game_setting_info[i].step;
-		}
-	}
-}
-
 extern void StartupEconomy();
 
 /* Widget definition for the game difficulty settings window */
@@ -472,20 +388,21 @@
 	_game_difficulty_widgets,
 };
 
+void SetDifficultyLevel(int mode, DifficultySettings *gm_opt);
+
 struct GameDifficultyWindow : public Window {
 private:
+	static const uint GAME_DIFFICULTY_NUM = 18;
 	bool clicked_increase;
 	uint8 clicked_button;
 	uint8 timeout;
 
 	/* Temporary holding place of values in the difficulty window until 'Save' is clicked */
-	DifficultySettings opt_mod_temp;
+	Settings opt_mod_temp;
 
 	enum {
 		GAMEDIFF_WND_TOP_OFFSET = 45,
 		GAMEDIFF_WND_ROWSIZE    = 9,
-		// 0x383E = (1 << 13) | (1 << 12) | (1 << 11) | (1 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (1 << 1)
-		DIFF_INGAME_DISABLED_BUTTONS = 0x383E,
 		NO_SETTINGS_BUTTON = 0xFF,
 	};
 
@@ -510,7 +427,7 @@
 	{
 		/* Copy current settings (ingame or in intro) to temporary holding place
 		 * change that when setting stuff, copy back on clicking 'OK' */
-		this->opt_mod_temp = (_game_mode == GM_MENU) ? _settings_newgame.difficulty : _settings.difficulty;
+		this->opt_mod_temp = (_game_mode == GM_MENU) ? _settings_newgame : _settings;
 		this->clicked_increase = false;
 		this->clicked_button = NO_SETTINGS_BUTTON;
 		this->timeout = 0;
@@ -527,7 +444,7 @@
 			WIDGET_LIST_END);
 		this->SetWidgetDisabledState(GDW_HIGHSCORE, _game_mode == GM_EDITOR || _networking); // highscore chart in multiplayer
 		this->SetWidgetDisabledState(GDW_ACCEPT, _networking && !_network_server); // Save-button in multiplayer (and if client)
-		this->LowerWidget(GDW_LVL_EASY + this->opt_mod_temp.diff_level);
+		this->LowerWidget(GDW_LVL_EASY + this->opt_mod_temp.difficulty.diff_level);
 		this->FindWindowPlacementAndResize(&_game_difficulty_desc);
 	}
 
@@ -535,29 +452,20 @@
 	{
 		this->DrawWidgets();
 
-		/* XXX - Disabled buttons in normal gameplay or during muliplayer as non server.
-		 *       Bitshifted for each button to see if that bit is set. If it is set, the
-		 *       button is disabled */
-		uint32 disabled = 0;
-		if (_networking && !_network_server) {
-			disabled = MAX_UVALUE(uint32); // Disable all
-		} else if (_game_mode == GM_NORMAL) {
-			disabled = DIFF_INGAME_DISABLED_BUTTONS;
-		}
-
-		int value;
+		uint i;
+		const SettingDesc *sd = GetPatchFromName("difficulty.max_no_competitors", &i);
 		int y = GAMEDIFF_WND_TOP_OFFSET;
-		for (uint i = 0; i != GAME_DIFFICULTY_NUM; i++) {
-			const GameSettingData *gsd = &_game_setting_info[i];
-			value = ((GDType*)&this->opt_mod_temp)[i];
+		for (i = 0; i < GAME_DIFFICULTY_NUM; i++, sd++) {
+			const SettingDescBase *sdb = &sd->desc;
+			int32 value = (int32)ReadValue(GetVariableAddress(&this->opt_mod_temp, &sd->save), sd->save.conv);
+			bool editable = (_game_mode == GM_MENU || (sdb->flags & SGF_NEWGAME_ONLY) == 0);
 
 			DrawArrowButtons(5, y, 3,
 					(this->clicked_button == i) ? 1 + !!this->clicked_increase : 0,
-					!(HasBit(disabled, i) || gsd->min == value),
-					!(HasBit(disabled, i) || gsd->max == value));
+					editable && sdb->min != value,
+					editable && sdb->max != value);
 
-			value += _game_setting_info[i].str;
-			if (i == 4) value *= 1000; // XXX - handle currency option
+			value += sdb->str;
 			SetDParam(0, value);
 			DrawString(30, y, STR_6805_MAXIMUM_NO_COMPETITORS + i, TC_FROMSTRING);
 
@@ -582,30 +490,33 @@
 				const uint8 btn = y / (GAMEDIFF_WND_ROWSIZE + 2);
 				if (btn >= GAME_DIFFICULTY_NUM || y % (GAMEDIFF_WND_ROWSIZE + 2) >= 9) return;
 
+				uint i;
+				const SettingDesc *sd = GetPatchFromName("difficulty.max_no_competitors", &i) + btn;
+				const SettingDescBase *sdb = &sd->desc;
+
 				/* Clicked disabled button? */
-				if (_game_mode == GM_NORMAL && HasBit((int)DIFF_INGAME_DISABLED_BUTTONS, btn)) return;
+				bool editable = (_game_mode == GM_MENU || (sdb->flags & SGF_NEWGAME_ONLY) == 0);
+				if (!editable) return;
 
 				this->timeout = 5;
+				int32 val = (int32)ReadValue(GetVariableAddress(&this->opt_mod_temp, &sd->save), sd->save.conv);
 
-				int16 val = ((GDType*)&this->opt_mod_temp)[btn];
-
-				const GameSettingData *info = &_game_setting_info[btn]; // get information about the difficulty setting
 				if (x >= 10) {
 					/* Increase button clicked */
-					val = min(val + info->step, info->max);
+					val = min(val + sdb->interval, sdb->max);
 					this->clicked_increase = true;
 				} else {
 					/* Decrease button clicked */
-					val -= info->step;
-					val = max(val,  info->min);
+					val -= sdb->interval;
+					val = max(val, sdb->min);
 					this->clicked_increase = false;
 				}
 				this->clicked_button = btn;
 
 				/* save value in temporary variable */
-				((GDType*)&this->opt_mod_temp)[btn] = val;
-				this->RaiseWidget(GDW_LVL_EASY + this->opt_mod_temp.diff_level);
-				SetDifficultyLevel(3, &this->opt_mod_temp); // set difficulty level to custom
+				WriteValue(GetVariableAddress(&this->opt_mod_temp, &sd->save), sd->save.conv, val);
+				this->RaiseWidget(GDW_LVL_EASY + this->opt_mod_temp.difficulty.diff_level);
+				SetDifficultyLevel(3, &this->opt_mod_temp.difficulty); // set difficulty level to custom
 				this->LowerWidget(GDW_LVL_CUSTOM);
 				this->SetDirty();
 			} break;
@@ -615,27 +526,32 @@
 			case GDW_LVL_HARD:
 			case GDW_LVL_CUSTOM:
 				/* temporarily change difficulty level */
-				this->RaiseWidget(GDW_LVL_EASY + this->opt_mod_temp.diff_level);
-				SetDifficultyLevel(widget - GDW_LVL_EASY, &this->opt_mod_temp);
-				this->LowerWidget(GDW_LVL_EASY + this->opt_mod_temp.diff_level);
+				this->RaiseWidget(GDW_LVL_EASY + this->opt_mod_temp.difficulty.diff_level);
+				SetDifficultyLevel(widget - GDW_LVL_EASY, &this->opt_mod_temp.difficulty);
+				this->LowerWidget(GDW_LVL_EASY + this->opt_mod_temp.difficulty.diff_level);
 				this->SetDirty();
 				break;
 
 			case GDW_HIGHSCORE: // Highscore Table
-				ShowHighscoreTable(this->opt_mod_temp.diff_level, -1);
+				ShowHighscoreTable(this->opt_mod_temp.difficulty.diff_level, -1);
 				break;
 
 			case GDW_ACCEPT: { // Save button - save changes
-				GDType btn, val;
 				Settings *opt_ptr = (_game_mode == GM_MENU) ? &_settings_newgame : &_settings;
-				for (btn = 0; btn != GAME_DIFFICULTY_NUM; btn++) {
-					val = ((GDType*)&this->opt_mod_temp)[btn];
+
+				uint i;
+				const SettingDesc *sd = GetPatchFromName("difficulty.max_no_competitors", &i);
+				for (uint btn = 0; btn != GAME_DIFFICULTY_NUM; btn++, sd++) {
+					int32 new_val = (int32)ReadValue(GetVariableAddress(&this->opt_mod_temp, &sd->save), sd->save.conv);
+					int32 cur_val = (int32)ReadValue(GetVariableAddress(opt_ptr, &sd->save), sd->save.conv);
 					/* if setting has changed, change it */
-					if (val != ((GDType*)&opt_ptr->difficulty)[btn]) {
-						DoCommandP(0, btn, val, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
+					if (new_val != cur_val) {
+						DoCommandP(0, i + btn, new_val, NULL, CMD_CHANGE_PATCH_SETTING);
 					}
 				}
-				DoCommandP(0, UINT_MAX, this->opt_mod_temp.diff_level, NULL, CMD_CHANGE_DIFFICULTY_LEVEL);
+
+				GetPatchFromName("difficulty.diff_level", &i);
+				DoCommandP(0, i, this->opt_mod_temp.difficulty.diff_level, NULL, CMD_CHANGE_PATCH_SETTING);
 				delete this;
 				/* If we are in the editor, we should reload the economy.
 				 * This way when you load a game, the max loan and interest rate
--- a/src/settings_internal.h
+++ b/src/settings_internal.h
@@ -39,8 +39,8 @@
 	SGF_NETWORK_ONLY = 1 << 3, ///< this setting only applies to network games
 	SGF_CURRENCY     = 1 << 4, ///< the number represents money, so when reading value multiply by exchange rate
 	SGF_NO_NETWORK   = 1 << 5, ///< this setting does not apply to network games; it may not be changed during the game
-	SGF_END          = 1 << 6,
-	/* 3 more possible flags */
+	SGF_NEWGAME_ONLY = 1 << 6, ///< this setting cannot be changed in inside a game
+	SGF_END          = 1 << 7,
 };
 
 DECLARE_ENUM_AS_BIT_SET(SettingGuiFlagLong);
--- a/src/settings_type.h
+++ b/src/settings_type.h
@@ -9,51 +9,27 @@
 #include "town_type.h"
 #include "transport_type.h"
 
-enum {
-	GAME_DIFFICULTY_AI_NUMBER,
-	GAME_DIFFICULTY_AI_STARTTIME,
-	GAME_DIFFICULTY_TOWN_NUMBER,
-	GAME_DIFFICULTY_INDUSTRIE_NUMBER,
-	GAME_DIFFICULTY_MAX_LOAN,
-	GAME_DIFFICULTY_INITIAL_INTEREST,
-	GAME_DIFFICULTY_VEHICLE_COST,
-	GAME_DIFFICULTY_AI_SPEED,
-	GAME_DIFFICULTY_AI_INTELLIGENCE,       ///< no longer in use
-	GAME_DIFFICULTY_VEHICLES_BREAKDOWN,
-	GAME_DIFFICULTY_SUBSIDY_MULTIPLIER,
-	GAME_DIFFICULTY_CONSTRUCTION_COST,
-	GAME_DIFFICULTY_TYPE_TERRAIN,
-	GAME_DIFFICULTY_SEALAKE_NUMBER,
-	GAME_DIFFICULTY_ECONOMY,
-	GAME_DIFFICULTY_LINE_REVERSEMODE,
-	GAME_DIFFICULTY_DISASTERS,
-	GAME_DIFFICULTY_TOWNCOUNCIL_TOLERANCE, ///< minimum required town ratings to be allowed to demolish stuff
-	GAME_DIFFICULTY_NUM,
-};
-
-/** Specific type for Game Difficulty to ease changing the type */
-typedef uint16 GDType;
 /** Settings related to the difficulty of the game */
 struct DifficultySettings {
-	GDType max_no_competitors;               ///< the number of competitors (AIs)
-	GDType competitor_start_time;            ///< how long to wait for the first competitors (AIs)
-	GDType number_towns;                     ///< the amount of towns
-	GDType number_industries;                ///< the amount of industries
-	GDType max_loan;                         ///< the maximum initial loan
-	GDType initial_interest;                 ///< amount of interest (to pay over the loan)
-	GDType vehicle_costs;                    ///< amount of money spent on vehicle running cost
-	GDType competitor_speed;                 ///< the speed at which the AI builds
-	GDType competitor_intelligence;          ///< the competior's (AI) intelligence
-	GDType vehicle_breakdowns;               ///< likelihood of vehicles breaking down
-	GDType subsidy_multiplier;               ///< amount of subsidy
-	GDType construction_cost;                ///< how expensive is building
-	GDType terrain_type;                     ///< the mountainousness of the landscape
-	GDType quantity_sea_lakes;               ///< the amount of seas/lakes
-	GDType economy;                          ///< how volatile is the economy
-	GDType line_reverse_mode;                ///< reversing at stations or not
-	GDType disasters;                        ///< are disasters enabled
-	GDType town_council_tolerance;           ///< minimum required town ratings to be allowed to demolish stuff
-	GDType diff_level;                       ///< the difficulty level
+	byte   max_no_competitors;               ///< the number of competitors (AIs)
+	byte   competitor_start_time;            ///< how long to wait for the first competitors (AIs)
+	byte   number_towns;                     ///< the amount of towns
+	byte   number_industries;                ///< the amount of industries
+	uint32 max_loan;                         ///< the maximum initial loan
+	byte   initial_interest;                 ///< amount of interest (to pay over the loan)
+	byte   vehicle_costs;                    ///< amount of money spent on vehicle running cost
+	byte   competitor_speed;                 ///< the speed at which the AI builds
+	byte   competitor_intelligence;          ///< the competior's (AI) intelligence
+	byte   vehicle_breakdowns;               ///< likelihood of vehicles breaking down
+	byte   subsidy_multiplier;               ///< amount of subsidy
+	byte   construction_cost;                ///< how expensive is building
+	byte   terrain_type;                     ///< the mountainousness of the landscape
+	byte   quantity_sea_lakes;               ///< the amount of seas/lakes
+	byte   economy;                          ///< how volatile is the economy
+	byte   line_reverse_mode;                ///< reversing at stations or not
+	byte   disasters;                        ///< are disasters enabled
+	byte   town_council_tolerance;           ///< minimum required town ratings to be allowed to demolish stuff
+	byte   diff_level;                       ///< the difficulty level
 };
 
 /** Settings related to the GUI and other stuff that is not saved in the savegame. */