Changeset - r25527:ad30d3893783
[Not reviewed]
master
0 5 0
rubidium42 - 3 years ago 2021-05-23 07:51:33
rubidium@openttd.org
Codechange: let SettingDesc extend SettingDescBase
5 files changed with 62 insertions and 63 deletions:
0 comments (0 inline, 0 general)
src/saveload/linkgraph_sl.cpp
Show inline comments
 
@@ -54,49 +54,49 @@ const SaveLoad *GetLinkGraphJobDesc()
 
	static const char *prefix = "linkgraph.";
 

	
 
	static const SaveLoad job_desc[] = {
 
		SLE_VAR(LinkGraphJob, join_date,        SLE_INT32),
 
		SLE_VAR(LinkGraphJob, link_graph.index, SLE_UINT16),
 
		SLE_END()
 
	};
 

	
 
	/* The member offset arithmetic below is only valid if the types in question
 
	 * are standard layout types. Otherwise, it would be undefined behaviour. */
 
	static_assert(std::is_standard_layout<LinkGraphSettings>::value, "LinkGraphSettings needs to be a standard layout type");
 

	
 
	/* We store the offset of each member of the #LinkGraphSettings in the
 
	 * extra data of the saveload struct. Use it together with the address
 
	 * of the settings struct inside the job to find the final memory address. */
 
	static SaveLoadAddrProc * const proc = [](void *b, size_t extra) -> void * { return const_cast<void *>(static_cast<const void *>(reinterpret_cast<const char *>(std::addressof(static_cast<LinkGraphJob *>(b)->settings)) + extra)); };
 

	
 
	/* Build the SaveLoad array on first call and don't touch it later on */
 
	if (saveloads.size() == 0) {
 
		size_t prefixlen = strlen(prefix);
 

	
 
		int setting = 0;
 
		const SettingDesc *desc = GetSettingDescription(setting);
 
		while (desc->save.cmd != SL_END) {
 
			if (desc->desc.name != nullptr && strncmp(desc->desc.name, prefix, prefixlen) == 0) {
 
			if (desc->name != nullptr && strncmp(desc->name, prefix, prefixlen) == 0) {
 
				SaveLoad sl = desc->save;
 
				sl.address_proc = proc;
 
				saveloads.push_back(sl);
 
			}
 
			desc = GetSettingDescription(++setting);
 
		}
 

	
 
		int i = 0;
 
		do {
 
			saveloads.push_back(job_desc[i++]);
 
		} while (saveloads[saveloads.size() - 1].cmd != SL_END);
 
	}
 

	
 
	return &saveloads[0];
 
}
 

	
 
/**
 
 * Get a SaveLoad array for the link graph schedule.
 
 * @return SaveLoad array for the link graph schedule.
 
 */
 
const SaveLoad *GetLinkGraphScheduleDesc()
 
{
 
	static const SaveLoad schedule_desc[] = {
 
		SLE_LST(LinkGraphSchedule, schedule, REF_LINK_GRAPH),
src/script/api/script_gamesettings.cpp
Show inline comments
 
/*
 
 * This file is part of OpenTTD.
 
 * 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.
 
 * 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.
 
 * 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/>.
 
 */
 

	
 
/** @file script_gamesettings.cpp Implementation of ScriptGameSettings. */
 

	
 
#include "../../stdafx.h"
 
#include "script_gamesettings.hpp"
 
#include "../../settings_internal.h"
 
#include "../../settings_type.h"
 
#include "../../command_type.h"
 

	
 
#include "../../safeguards.h"
 

	
 
/* static */ bool ScriptGameSettings::IsValid(const char *setting)
 
{
 
	const SettingDesc *sd = GetSettingFromName(setting);
 
	return sd != nullptr && sd->desc.cmd != SDT_STDSTRING;
 
	return sd != nullptr && sd->cmd != SDT_STDSTRING;
 
}
 

	
 
/* static */ int32 ScriptGameSettings::GetValue(const char *setting)
 
{
 
	if (!IsValid(setting)) return -1;
 

	
 
	const SettingDesc *sd = GetSettingFromName(setting);
 

	
 
	void *ptr = GetVariableAddress(&_settings_game, &sd->save);
 
	if (sd->desc.cmd == SDT_BOOLX) return *(bool*)ptr;
 
	if (sd->cmd == SDT_BOOLX) return *(bool*)ptr;
 

	
 
	return (int32)ReadValue(ptr, sd->save.conv);
 
}
 

	
 
/* static */ bool ScriptGameSettings::SetValue(const char *setting, int value)
 
{
 
	if (!IsValid(setting)) return false;
 

	
 
	const SettingDesc *sd = GetSettingFromName(setting);
 

	
 
	if ((sd->save.conv & SLF_NO_NETWORK_SYNC) != 0) return false;
 
	if (sd->desc.cmd != SDT_BOOLX && sd->desc.cmd != SDT_NUMX) return false;
 
	if (sd->cmd != SDT_BOOLX && sd->cmd != SDT_NUMX) return false;
 

	
 
	return ScriptObject::DoCommand(0, GetSettingIndex(sd), value, CMD_CHANGE_SETTING);
 
}
 

	
 
/* static */ bool ScriptGameSettings::IsDisabledVehicleType(ScriptVehicle::VehicleType vehicle_type)
 
{
 
	switch (vehicle_type) {
 
		case ScriptVehicle::VT_RAIL:  return _settings_game.ai.ai_disable_veh_train;
 
		case ScriptVehicle::VT_ROAD:  return _settings_game.ai.ai_disable_veh_roadveh;
 
		case ScriptVehicle::VT_WATER: return _settings_game.ai.ai_disable_veh_ship;
 
		case ScriptVehicle::VT_AIR:   return _settings_game.ai.ai_disable_veh_aircraft;
 
		default:                       return true;
 
	}
 
}
src/settings.cpp
Show inline comments
 
@@ -413,49 +413,49 @@ static const void *StringToVal(const Set
 
			msg.SetDParamStr(1, desc->name);
 
			_settings_error_list.push_back(msg);
 
			return desc->def;
 
		}
 

	
 
		case SDT_STDSTRING: return orig_str;
 
		case SDT_INTLIST: return str;
 
		default: break;
 
	}
 

	
 
	return nullptr;
 
}
 

	
 
/**
 
 * Set the value of a setting and if needed clamp the value to
 
 * the preset minimum and maximum.
 
 * @param ptr the variable itself
 
 * @param sd pointer to the 'information'-database of the variable
 
 * @param val signed long version of the new value
 
 * @pre SettingDesc is of type SDT_BOOLX, SDT_NUMX,
 
 * SDT_ONEOFMANY or SDT_MANYOFMANY. Other types are not supported as of now
 
 */
 
static void Write_ValidateSetting(void *ptr, const SettingDesc *sd, int32 val)
 
{
 
	const SettingDescBase *sdb = &sd->desc;
 
	const SettingDescBase *sdb = sd;
 

	
 
	if (sdb->cmd != SDT_BOOLX &&
 
			sdb->cmd != SDT_NUMX &&
 
			sdb->cmd != SDT_ONEOFMANY &&
 
			sdb->cmd != SDT_MANYOFMANY) {
 
		return;
 
	}
 

	
 
	/* We cannot know the maximum value of a bitset variable, so just have faith */
 
	if (sdb->cmd != SDT_MANYOFMANY) {
 
		/* We need to take special care of the uint32 type as we receive from the function
 
		 * a signed integer. While here also bail out on 64-bit settings as those are not
 
		 * supported. Unsigned 8 and 16-bit variables are safe since they fit into a signed
 
		 * 32-bit variable
 
		 * TODO: Support 64-bit settings/variables */
 
		switch (GetVarMemType(sd->save.conv)) {
 
			case SLE_VAR_NULL: return;
 
			case SLE_VAR_BL:
 
			case SLE_VAR_I8:
 
			case SLE_VAR_U8:
 
			case SLE_VAR_I16:
 
			case SLE_VAR_U16:
 
			case SLE_VAR_I32: {
 
				/* Override the minimum value. No value below sdb->min, except special value 0 */
 
@@ -487,164 +487,164 @@ static void Write_ValidateSetting(void *
 
			}
 
			case SLE_VAR_I64:
 
			case SLE_VAR_U64:
 
			default: NOT_REACHED();
 
		}
 
	}
 

	
 
	WriteValue(ptr, sd->save.conv, (int64)val);
 
}
 

	
 
/**
 
 * Set the string value of a setting.
 
 * @param ptr Pointer to the std::string.
 
 * @param sd  Pointer to the information for the conversions and limitations to apply.
 
 * @param p   The string to save.
 
 */
 
static void Write_ValidateStdString(void *ptr, const SettingDesc *sd, const char *p)
 
{
 
	std::string *dst = reinterpret_cast<std::string *>(ptr);
 

	
 
	switch (GetVarMemType(sd->save.conv)) {
 
		case SLE_VAR_STR:
 
		case SLE_VAR_STRQ:
 
			if (p != nullptr) {
 
				if (sd->desc.max != 0 && strlen(p) >= sd->desc.max) {
 
				if (sd->max != 0 && strlen(p) >= sd->max) {
 
					/* In case a maximum length is imposed by the setting, the length
 
					 * includes the '\0' termination for network transfer purposes.
 
					 * Also ensure the string is valid after chopping of some bytes. */
 
					std::string str(p, sd->desc.max - 1);
 
					std::string str(p, sd->max - 1);
 
					dst->assign(str_validate(str, SVS_NONE));
 
				} else {
 
					dst->assign(p);
 
				}
 
			} else {
 
				dst->clear();
 
			}
 
			break;
 

	
 
		default: NOT_REACHED();
 
	}
 
}
 

	
 
/**
 
 * Load values from a group of an IniFile structure into the internal representation
 
 * @param ini pointer to IniFile structure that holds administrative information
 
 * @param sd pointer to SettingDesc structure whose internally pointed variables will
 
 *        be given values
 
 * @param grpname the group of the IniFile to search in for the new values
 
 * @param object pointer to the object been loaded
 
 * @param only_startup load only the startup settings set
 
 */
 
static void IniLoadSettings(IniFile *ini, const SettingDesc *sd, const char *grpname, void *object, bool only_startup)
 
{
 
	IniGroup *group;
 
	IniGroup *group_def = ini->GetGroup(grpname);
 

	
 
	for (; sd->save.cmd != SL_END; sd++) {
 
		const SettingDescBase *sdb = &sd->desc;
 
		const SettingDescBase *sdb = sd;
 
		const SaveLoad        *sld = &sd->save;
 

	
 
		if (!SlIsObjectCurrentlyValid(sld->version_from, sld->version_to)) continue;
 
		if (sd->desc.startup != only_startup) continue;
 
		if (sd->startup != only_startup) continue;
 

	
 
		/* For settings.xx.yy load the settings from [xx] yy = ? */
 
		std::string s{ sdb->name };
 
		auto sc = s.find('.');
 
		if (sc != std::string::npos) {
 
			group = ini->GetGroup(s.substr(0, sc));
 
			s = s.substr(sc + 1);
 
		} else {
 
			group = group_def;
 
		}
 

	
 
		IniItem *item = group->GetItem(s, false);
 
		if (item == nullptr && group != group_def) {
 
			/* For settings.xx.yy load the settings from [settings] yy = ? in case the previous
 
			 * did not exist (e.g. loading old config files with a [settings] section */
 
			item = group_def->GetItem(s, false);
 
		}
 
		if (item == nullptr) {
 
			/* For settings.xx.zz.yy load the settings from [zz] yy = ? in case the previous
 
			 * did not exist (e.g. loading old config files with a [yapf] section */
 
			sc = s.find('.');
 
			if (sc != std::string::npos) item = ini->GetGroup(s.substr(0, sc))->GetItem(s.substr(sc + 1), false);
 
		}
 

	
 
		const void *p = (item == nullptr) ? sdb->def : StringToVal(sdb, item->value.has_value() ? item->value->c_str() : nullptr);
 
		void *ptr = GetVariableAddress(object, sld);
 

	
 
		switch (sdb->cmd) {
 
			case SDT_BOOLX: // All four are various types of (integer) numbers
 
			case SDT_NUMX:
 
			case SDT_ONEOFMANY:
 
			case SDT_MANYOFMANY:
 
				Write_ValidateSetting(ptr, sd, (int32)(size_t)p);
 
				break;
 

	
 
			case SDT_STDSTRING:
 
				Write_ValidateStdString(ptr, sd, (const char *)p);
 
				break;
 

	
 
			case SDT_INTLIST: {
 
				if (!LoadIntList((const char*)p, ptr, sld->length, GetVarMemType(sld->conv))) {
 
					ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_ARRAY);
 
					msg.SetDParamStr(0, sdb->name);
 
					_settings_error_list.push_back(msg);
 

	
 
					/* Use default */
 
					LoadIntList((const char*)sdb->def, ptr, sld->length, GetVarMemType(sld->conv));
 
				} else if (sd->desc.proc_cnvt != nullptr) {
 
					sd->desc.proc_cnvt((const char*)p);
 
				} else if (sd->proc_cnvt != nullptr) {
 
					sd->proc_cnvt((const char*)p);
 
				}
 
				break;
 
			}
 
			default: NOT_REACHED();
 
		}
 
	}
 
}
 

	
 
/**
 
 * Save the values of settings to the inifile.
 
 * @param ini pointer to IniFile structure
 
 * @param sd read-only SettingDesc structure which contains the unmodified,
 
 *        loaded values of the configuration file and various information about it
 
 * @param grpname holds the name of the group (eg. [network]) where these will be saved
 
 * @param object pointer to the object been saved
 
 * The function works as follows: for each item in the SettingDesc structure we
 
 * have a look if the value has changed since we started the game (the original
 
 * values are reloaded when saving). If settings indeed have changed, we get
 
 * these and save them.
 
 */
 
static void IniSaveSettings(IniFile *ini, const SettingDesc *sd, const char *grpname, void *object, bool)
 
{
 
	IniGroup *group_def = nullptr, *group;
 
	IniItem *item;
 
	char buf[512];
 
	void *ptr;
 

	
 
	for (; sd->save.cmd != SL_END; sd++) {
 
		const SettingDescBase *sdb = &sd->desc;
 
		const SettingDescBase *sdb = sd;
 
		const SaveLoad        *sld = &sd->save;
 

	
 
		/* If the setting is not saved to the configuration
 
		 * file, just continue with the next setting */
 
		if (!SlIsObjectCurrentlyValid(sld->version_from, sld->version_to)) continue;
 
		if (sld->conv & SLF_NOT_IN_CONFIG) continue;
 

	
 
		/* XXX - wtf is this?? (group override?) */
 
		std::string s{ sdb->name };
 
		auto sc = s.find('.');
 
		if (sc != std::string::npos) {
 
			group = ini->GetGroup(s.substr(0, sc));
 
			s = s.substr(sc + 1);
 
		} else {
 
			if (group_def == nullptr) group_def = ini->GetGroup(grpname);
 
			group = group_def;
 
		}
 

	
 
		item = group->GetItem(s, true);
 
		ptr = GetVariableAddress(object, sld);
 

	
 
		if (item->value.has_value()) {
 
			/* check if the value is the same as the old value */
 
			const void *p = StringToVal(sdb, item->value->c_str());
 
@@ -781,65 +781,65 @@ static void IniSaveSettingList(IniFile *
 
 */
 
void IniLoadWindowSettings(IniFile *ini, const char *grpname, void *desc)
 
{
 
	IniLoadSettings(ini, _window_settings, grpname, desc, false);
 
}
 

	
 
/**
 
 * Save a WindowDesc to config.
 
 * @param ini IniFile handle to the ini file where the destination data is saved
 
 * @param grpname character string identifying the section-header of the ini file
 
 * @param desc Source WindowDesc
 
 */
 
void IniSaveWindowSettings(IniFile *ini, const char *grpname, void *desc)
 
{
 
	IniSaveSettings(ini, _window_settings, grpname, desc, false);
 
}
 

	
 
/**
 
 * Check whether the setting is editable in the current gamemode.
 
 * @param do_command true if this is about checking a command from the server.
 
 * @return true if editable.
 
 */
 
bool SettingDesc::IsEditable(bool do_command) const
 
{
 
	if (!do_command && !(this->save.conv & SLF_NO_NETWORK_SYNC) && _networking && !_network_server && !(this->desc.flags & SGF_PER_COMPANY)) return false;
 
	if ((this->desc.flags & SGF_NETWORK_ONLY) && !_networking && _game_mode != GM_MENU) return false;
 
	if ((this->desc.flags & SGF_NO_NETWORK) && _networking) return false;
 
	if ((this->desc.flags & SGF_NEWGAME_ONLY) &&
 
	if (!do_command && !(this->save.conv & SLF_NO_NETWORK_SYNC) && _networking && !_network_server && !(this->flags & SGF_PER_COMPANY)) return false;
 
	if ((this->flags & SGF_NETWORK_ONLY) && !_networking && _game_mode != GM_MENU) return false;
 
	if ((this->flags & SGF_NO_NETWORK) && _networking) return false;
 
	if ((this->flags & SGF_NEWGAME_ONLY) &&
 
			(_game_mode == GM_NORMAL ||
 
			(_game_mode == GM_EDITOR && !(this->desc.flags & SGF_SCENEDIT_TOO)))) return false;
 
	if ((this->desc.flags & SGF_SCENEDIT_ONLY) && _game_mode != GM_EDITOR) return false;
 
			(_game_mode == GM_EDITOR && !(this->flags & SGF_SCENEDIT_TOO)))) return false;
 
	if ((this->flags & SGF_SCENEDIT_ONLY) && _game_mode != GM_EDITOR) return false;
 
	return true;
 
}
 

	
 
/**
 
 * Return the type of the setting.
 
 * @return type of setting
 
 */
 
SettingType SettingDesc::GetType() const
 
{
 
	if (this->desc.flags & SGF_PER_COMPANY) return ST_COMPANY;
 
	if (this->flags & SGF_PER_COMPANY) return ST_COMPANY;
 
	return (this->save.conv & SLF_NOT_IN_SAVE) ? ST_CLIENT : ST_GAME;
 
}
 

	
 
/* Begin - Callback Functions for the various settings. */
 

	
 
/** Reposition the main toolbar as the setting changed. */
 
static bool v_PositionMainToolbar(int32 p1)
 
{
 
	if (_game_mode != GM_MENU) PositionMainToolbar(nullptr);
 
	return true;
 
}
 

	
 
/** Reposition the statusbar as the setting changed. */
 
static bool v_PositionStatusbar(int32 p1)
 
{
 
	if (_game_mode != GM_MENU) {
 
		PositionStatusbar(nullptr);
 
		PositionNewsMessage(nullptr);
 
		PositionNetworkChatWindow(nullptr);
 
	}
 
	return true;
 
}
 

	
 
static bool PopulationInLabelActive(int32 p1)
 
@@ -1871,366 +1871,366 @@ const SettingDesc *GetSettingDescription
 
 * @param text unused
 
 * @return the cost of this operation or an error
 
 * @see _settings
 
 */
 
CommandCost CmdChangeSetting(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 
{
 
	const SettingDesc *sd = GetSettingDescription(p1);
 

	
 
	if (sd == nullptr) return CMD_ERROR;
 
	if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) return CMD_ERROR;
 

	
 
	if (!sd->IsEditable(true)) return CMD_ERROR;
 

	
 
	if (flags & DC_EXEC) {
 
		void *var = GetVariableAddress(&GetGameSettings(), &sd->save);
 

	
 
		int32 oldval = (int32)ReadValue(var, sd->save.conv);
 
		int32 newval = (int32)p2;
 

	
 
		Write_ValidateSetting(var, sd, newval);
 
		newval = (int32)ReadValue(var, sd->save.conv);
 

	
 
		if (oldval == newval) return CommandCost();
 

	
 
		if (sd->desc.proc != nullptr && !sd->desc.proc(newval)) {
 
		if (sd->proc != nullptr && !sd->proc(newval)) {
 
			WriteValue(var, sd->save.conv, (int64)oldval);
 
			return CommandCost();
 
		}
 

	
 
		if (sd->desc.flags & SGF_NO_NETWORK) {
 
		if (sd->flags & SGF_NO_NETWORK) {
 
			GamelogStartAction(GLAT_SETTING);
 
			GamelogSetting(sd->desc.name, oldval, newval);
 
			GamelogSetting(sd->name, oldval, newval);
 
			GamelogStopAction();
 
		}
 

	
 
		SetWindowClassesDirty(WC_GAME_OPTIONS);
 

	
 
		if (_save_config) SaveToConfig();
 
	}
 

	
 
	return CommandCost();
 
}
 

	
 
/**
 
 * Change one of the per-company settings.
 
 * @param tile unused
 
 * @param flags operation to perform
 
 * @param p1 the index of the setting in the _company_settings array which identifies it
 
 * @param p2 the new value for the setting
 
 * The new value is properly clamped to its minimum/maximum when setting
 
 * @param text unused
 
 * @return the cost of this operation or an error
 
 */
 
CommandCost CmdChangeCompanySetting(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 
{
 
	if (p1 >= lengthof(_company_settings)) return CMD_ERROR;
 
	const SettingDesc *sd = &_company_settings[p1];
 

	
 
	if (flags & DC_EXEC) {
 
		void *var = GetVariableAddress(&Company::Get(_current_company)->settings, &sd->save);
 

	
 
		int32 oldval = (int32)ReadValue(var, sd->save.conv);
 
		int32 newval = (int32)p2;
 

	
 
		Write_ValidateSetting(var, sd, newval);
 
		newval = (int32)ReadValue(var, sd->save.conv);
 

	
 
		if (oldval == newval) return CommandCost();
 

	
 
		if (sd->desc.proc != nullptr && !sd->desc.proc(newval)) {
 
		if (sd->proc != nullptr && !sd->proc(newval)) {
 
			WriteValue(var, sd->save.conv, (int64)oldval);
 
			return CommandCost();
 
		}
 

	
 
		SetWindowClassesDirty(WC_GAME_OPTIONS);
 
	}
 

	
 
	return CommandCost();
 
}
 

	
 
/**
 
 * Get the index of the setting with this description.
 
 * @param sd the setting to get the index for.
 
 * @return the index of the setting to be used for CMD_CHANGE_SETTING.
 
 */
 
uint GetSettingIndex(const SettingDesc *sd)
 
{
 
	assert((sd->desc.flags & SGF_PER_COMPANY) == 0);
 
	assert((sd->flags & SGF_PER_COMPANY) == 0);
 
	return sd - _settings;
 
}
 

	
 
/**
 
 * Top function to save the new value of an element of the Settings struct
 
 * @param index offset in the SettingDesc array of the Settings struct which
 
 * identifies the setting member we want to change
 
 * @param value new value of the setting
 
 * @param force_newgame force the newgame settings
 
 */
 
bool SetSettingValue(const SettingDesc *sd, int32 value, bool force_newgame)
 
{
 
	if ((sd->desc.flags & SGF_PER_COMPANY) != 0) {
 
	if ((sd->flags & SGF_PER_COMPANY) != 0) {
 
		if (Company::IsValidID(_local_company) && _game_mode != GM_MENU) {
 
			return DoCommandP(0, sd - _company_settings, value, CMD_CHANGE_COMPANY_SETTING);
 
		}
 

	
 
		void *var = GetVariableAddress(&_settings_client.company, &sd->save);
 
		Write_ValidateSetting(var, sd, value);
 
		if (sd->desc.proc != nullptr) sd->desc.proc((int32)ReadValue(var, sd->save.conv));
 
		if (sd->proc != nullptr) sd->proc((int32)ReadValue(var, sd->save.conv));
 
		return true;
 
	}
 

	
 
	/* If an item is company-based, we do not send it over the network
 
	 * (if any) to change. Also *hack*hack* we update the _newgame version
 
	 * of settings because changing a company-based setting in a game also
 
	 * changes its defaults. At least that is the convention we have chosen */
 
	if (sd->save.conv & SLF_NO_NETWORK_SYNC) {
 
		void *var = GetVariableAddress(&GetGameSettings(), &sd->save);
 
		Write_ValidateSetting(var, sd, value);
 

	
 
		if (_game_mode != GM_MENU) {
 
			void *var2 = GetVariableAddress(&_settings_newgame, &sd->save);
 
			Write_ValidateSetting(var2, sd, value);
 
		}
 
		if (sd->desc.proc != nullptr) sd->desc.proc((int32)ReadValue(var, sd->save.conv));
 
		if (sd->proc != nullptr) sd->proc((int32)ReadValue(var, sd->save.conv));
 

	
 
		SetWindowClassesDirty(WC_GAME_OPTIONS);
 

	
 
		if (_save_config) SaveToConfig();
 
		return true;
 
	}
 

	
 
	if (force_newgame) {
 
		void *var2 = GetVariableAddress(&_settings_newgame, &sd->save);
 
		Write_ValidateSetting(var2, sd, value);
 

	
 
		if (_save_config) SaveToConfig();
 
		return true;
 
	}
 

	
 
	/* send non-company-based settings over the network */
 
	if (!_networking || (_networking && _network_server)) {
 
		return DoCommandP(0, GetSettingIndex(sd), value, CMD_CHANGE_SETTING);
 
	}
 
	return false;
 
}
 

	
 
/**
 
 * Set the company settings for a new company to their default values.
 
 */
 
void SetDefaultCompanySettings(CompanyID cid)
 
{
 
	Company *c = Company::Get(cid);
 
	const SettingDesc *sd;
 
	for (sd = _company_settings; sd->save.cmd != SL_END; sd++) {
 
		void *var = GetVariableAddress(&c->settings, &sd->save);
 
		Write_ValidateSetting(var, sd, (int32)(size_t)sd->desc.def);
 
		Write_ValidateSetting(var, sd, (int32)(size_t)sd->def);
 
	}
 
}
 

	
 
/**
 
 * Sync all company settings in a multiplayer game.
 
 */
 
void SyncCompanySettings()
 
{
 
	const SettingDesc *sd;
 
	uint i = 0;
 
	for (sd = _company_settings; sd->save.cmd != SL_END; sd++, i++) {
 
		const void *old_var = GetVariableAddress(&Company::Get(_current_company)->settings, &sd->save);
 
		const void *new_var = GetVariableAddress(&_settings_client.company, &sd->save);
 
		uint32 old_value = (uint32)ReadValue(old_var, sd->save.conv);
 
		uint32 new_value = (uint32)ReadValue(new_var, sd->save.conv);
 
		if (old_value != new_value) NetworkSendCommand(0, i, new_value, CMD_CHANGE_COMPANY_SETTING, nullptr, nullptr, _local_company);
 
	}
 
}
 

	
 
/**
 
 * Get the index in the _company_settings array of a setting
 
 * @param name The name of the setting
 
 * @return The index in the _company_settings array
 
 */
 
uint GetCompanySettingIndex(const char *name)
 
{
 
	const SettingDesc *sd = GetSettingFromName(name);
 
	assert(sd != nullptr && (sd->desc.flags & SGF_PER_COMPANY) != 0);
 
	assert(sd != nullptr && (sd->flags & SGF_PER_COMPANY) != 0);
 
	return sd - _company_settings;
 
}
 

	
 
/**
 
 * Set a setting value with a string.
 
 * @param sd the setting to change.
 
 * @param value the value to write
 
 * @param force_newgame force the newgame settings
 
 * @note Strings WILL NOT be synced over the network
 
 */
 
bool SetSettingValue(const SettingDesc *sd, const char *value, bool force_newgame)
 
{
 
	assert(sd->save.conv & SLF_NO_NETWORK_SYNC);
 

	
 
	if (GetVarMemType(sd->save.conv) == SLE_VAR_STRQ && strcmp(value, "(null)") == 0) {
 
		value = nullptr;
 
	}
 

	
 
	void *ptr = GetVariableAddress((_game_mode == GM_MENU || force_newgame) ? &_settings_newgame : &_settings_game, &sd->save);
 
	Write_ValidateStdString(ptr, sd, value);
 
	if (sd->desc.proc != nullptr) sd->desc.proc(0);
 
	if (sd->proc != nullptr) sd->proc(0);
 

	
 
	if (_save_config) SaveToConfig();
 
	return true;
 
}
 

	
 
/**
 
 * Given a name of setting, return a setting description of it.
 
 * @param name  Name of the setting to return a setting description of
 
 * @param i     Pointer to an integer that will contain the index of the setting after the call, if it is successful.
 
 * @return Pointer to the setting description of setting \a name if it can be found,
 
 *         \c nullptr indicates failure to obtain the description
 
 */
 
const SettingDesc *GetSettingFromName(const char *name)
 
{
 
	/* First check all full names */
 
	for (const SettingDesc *sd = _settings; sd->save.cmd != SL_END; sd++) {
 
		if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
 
		if (strcmp(sd->desc.name, name) == 0) return sd;
 
		if (strcmp(sd->name, name) == 0) return sd;
 
	}
 

	
 
	/* Then check the shortcut variant of the name. */
 
	for (const SettingDesc *sd = _settings; sd->save.cmd != SL_END; sd++) {
 
		if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
 
		const char *short_name = strchr(sd->desc.name, '.');
 
		const char *short_name = strchr(sd->name, '.');
 
		if (short_name != nullptr) {
 
			short_name++;
 
			if (strcmp(short_name, name) == 0) return sd;
 
		}
 
	}
 

	
 
	if (strncmp(name, "company.", 8) == 0) name += 8;
 
	/* And finally the company-based settings */
 
	for (const SettingDesc *sd = _company_settings; sd->save.cmd != SL_END; sd++) {
 
		if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
 
		if (strcmp(sd->desc.name, name) == 0) return sd;
 
		if (strcmp(sd->name, name) == 0) return sd;
 
	}
 

	
 
	return nullptr;
 
}
 

	
 
/* Those 2 functions need to be here, else we have to make some stuff non-static
 
 * and besides, it is also better to keep stuff like this at the same place */
 
void IConsoleSetSetting(const char *name, const char *value, bool force_newgame)
 
{
 
	const SettingDesc *sd = GetSettingFromName(name);
 

	
 
	if (sd == nullptr) {
 
		IConsolePrintF(CC_WARNING, "'%s' is an unknown setting.", name);
 
		return;
 
	}
 

	
 
	bool success;
 
	if (sd->desc.cmd == SDT_STDSTRING) {
 
	if (sd->cmd == SDT_STDSTRING) {
 
		success = SetSettingValue(sd, value, force_newgame);
 
	} else {
 
		uint32 val;
 
		extern bool GetArgumentInteger(uint32 *value, const char *arg);
 
		success = GetArgumentInteger(&val, value);
 
		if (!success) {
 
			IConsolePrintF(CC_ERROR, "'%s' is not an integer.", value);
 
			return;
 
		}
 

	
 
		success = SetSettingValue(sd, val, force_newgame);
 
	}
 

	
 
	if (!success) {
 
		if (_network_server) {
 
			IConsoleError("This command/variable is not available during network games.");
 
		} else {
 
			IConsoleError("This command/variable is only available to a network server.");
 
		}
 
	}
 
}
 

	
 
void IConsoleSetSetting(const char *name, int value)
 
{
 
	const SettingDesc *sd = GetSettingFromName(name);
 
	assert(sd != nullptr);
 
	SetSettingValue(sd, value);
 
}
 

	
 
/**
 
 * Output value of a specific setting to the console
 
 * @param name  Name of the setting to output its value
 
 * @param force_newgame force the newgame settings
 
 */
 
void IConsoleGetSetting(const char *name, bool force_newgame)
 
{
 
	char value[20];
 
	const SettingDesc *sd = GetSettingFromName(name);
 
	const void *ptr;
 

	
 
	if (sd == nullptr) {
 
		IConsolePrintF(CC_WARNING, "'%s' is an unknown setting.", name);
 
		return;
 
	}
 

	
 
	ptr = GetVariableAddress((_game_mode == GM_MENU || force_newgame) ? &_settings_newgame : &_settings_game, &sd->save);
 

	
 
	if (sd->desc.cmd == SDT_STDSTRING) {
 
	if (sd->cmd == SDT_STDSTRING) {
 
		IConsolePrintF(CC_WARNING, "Current value for '%s' is: '%s'", name, reinterpret_cast<const std::string *>(ptr)->c_str());
 
	} else {
 
		if (sd->desc.cmd == SDT_BOOLX) {
 
		if (sd->cmd == SDT_BOOLX) {
 
			seprintf(value, lastof(value), (*(const bool*)ptr != 0) ? "on" : "off");
 
		} else {
 
			seprintf(value, lastof(value), sd->desc.min < 0 ? "%d" : "%u", (int32)ReadValue(ptr, sd->save.conv));
 
			seprintf(value, lastof(value), sd->min < 0 ? "%d" : "%u", (int32)ReadValue(ptr, sd->save.conv));
 
		}
 

	
 
		IConsolePrintF(CC_WARNING, "Current value for '%s' is: '%s' (min: %s%d, max: %u)",
 
			name, value, (sd->desc.flags & SGF_0ISDISABLED) ? "(0) " : "", sd->desc.min, sd->desc.max);
 
			name, value, (sd->flags & SGF_0ISDISABLED) ? "(0) " : "", sd->min, sd->max);
 
	}
 
}
 

	
 
/**
 
 * List all settings and their value to the console
 
 *
 
 * @param prefilter  If not \c nullptr, only list settings with names that begin with \a prefilter prefix
 
 */
 
void IConsoleListSettings(const char *prefilter)
 
{
 
	IConsolePrintF(CC_WARNING, "All settings with their current value:");
 

	
 
	for (const SettingDesc *sd = _settings; sd->save.cmd != SL_END; sd++) {
 
		if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
 
		if (prefilter != nullptr && strstr(sd->desc.name, prefilter) == nullptr) continue;
 
		if (prefilter != nullptr && strstr(sd->name, prefilter) == nullptr) continue;
 
		char value[80];
 
		const void *ptr = GetVariableAddress(&GetGameSettings(), &sd->save);
 

	
 
		if (sd->desc.cmd == SDT_BOOLX) {
 
		if (sd->cmd == SDT_BOOLX) {
 
			seprintf(value, lastof(value), (*(const bool *)ptr != 0) ? "on" : "off");
 
		} else if (sd->desc.cmd == SDT_STDSTRING) {
 
		} else if (sd->cmd == SDT_STDSTRING) {
 
			seprintf(value, lastof(value), "%s", reinterpret_cast<const std::string *>(ptr)->c_str());
 
		} else {
 
			seprintf(value, lastof(value), sd->desc.min < 0 ? "%d" : "%u", (int32)ReadValue(ptr, sd->save.conv));
 
			seprintf(value, lastof(value), sd->min < 0 ? "%d" : "%u", (int32)ReadValue(ptr, sd->save.conv));
 
		}
 
		IConsolePrintF(CC_DEFAULT, "%s = %s", sd->desc.name, value);
 
		IConsolePrintF(CC_DEFAULT, "%s = %s", sd->name, value);
 
	}
 

	
 
	IConsolePrintF(CC_WARNING, "Use 'setting' command to change a value");
 
}
 

	
 
/**
 
 * Save and load handler for settings
 
 * @param osd SettingDesc struct containing all information
 
 * @param object can be either nullptr in which case we load global variables or
 
 * a pointer to a struct which is getting saved
 
 */
 
static void LoadSettings(const SettingDesc *osd, void *object)
 
{
 
	for (; osd->save.cmd != SL_END; osd++) {
 
		const SaveLoad *sld = &osd->save;
 
		void *ptr = GetVariableAddress(object, sld);
 

	
 
		if (!SlObjectMember(ptr, sld)) continue;
 
		if (IsNumericType(sld->conv)) Write_ValidateSetting(ptr, osd, ReadValue(ptr, sld->conv));
 
	}
 
}
 

	
 
/**
 
 * Save and load handler for settings
src/settings_gui.cpp
Show inline comments
 
@@ -822,49 +822,49 @@ protected:
 
	virtual void DrawSetting(GameSettings *settings_ptr, int left, int right, int y, bool highlight) const = 0;
 
};
 

	
 
/** Standard setting */
 
struct SettingEntry : BaseSettingEntry {
 
	const char *name;           ///< Name of the setting
 
	const SettingDesc *setting; ///< Setting description of the setting
 

	
 
	SettingEntry(const char *name);
 

	
 
	virtual void Init(byte level = 0);
 
	virtual void ResetAll();
 
	virtual uint Length() const;
 
	virtual uint GetMaxHelpHeight(int maxw);
 
	virtual bool UpdateFilterState(SettingFilter &filter, bool force_visible);
 

	
 
	void SetButtons(byte new_val);
 

	
 
	/**
 
	 * Get the help text of a single setting.
 
	 * @return The requested help text.
 
	 */
 
	inline StringID GetHelpText() const
 
	{
 
		return this->setting->desc.str_help;
 
		return this->setting->str_help;
 
	}
 

	
 
	void SetValueDParams(uint first_param, int32 value) const;
 

	
 
protected:
 
	virtual void DrawSetting(GameSettings *settings_ptr, int left, int right, int y, bool highlight) const;
 

	
 
private:
 
	bool IsVisibleByRestrictionMode(RestrictionMode mode) const;
 
};
 

	
 
/** Containers for BaseSettingEntry */
 
struct SettingsContainer {
 
	typedef std::vector<BaseSettingEntry*> EntryVector;
 
	EntryVector entries; ///< Settings on this page
 

	
 
	template<typename T>
 
	T *Add(T *item)
 
	{
 
		this->entries.push_back(item);
 
		return item;
 
	}
 

	
 
	void Init(byte level = 0);
 
@@ -1015,211 +1015,211 @@ uint BaseSettingEntry::Draw(GameSettings
 
/**
 
 * Constructor for a single setting in the 'advanced settings' window
 
 * @param name Name of the setting in the setting table
 
 */
 
SettingEntry::SettingEntry(const char *name)
 
{
 
	this->name = name;
 
	this->setting = nullptr;
 
}
 

	
 
/**
 
 * Initialization of a setting entry
 
 * @param level      Page nesting level of this entry
 
 */
 
void SettingEntry::Init(byte level)
 
{
 
	BaseSettingEntry::Init(level);
 
	this->setting = GetSettingFromName(this->name);
 
	assert(this->setting != nullptr);
 
}
 

	
 
/* Sets the given setting entry to its default value */
 
void SettingEntry::ResetAll()
 
{
 
	int32 default_value = ReadValue(&this->setting->desc.def, this->setting->save.conv);
 
	int32 default_value = ReadValue(&this->setting->def, this->setting->save.conv);
 
	SetSettingValue(this->setting, default_value);
 
}
 

	
 
/**
 
 * Set the button-depressed flags (#SEF_LEFT_DEPRESSED and #SEF_RIGHT_DEPRESSED) to a specified value
 
 * @param new_val New value for the button flags
 
 * @see SettingEntryFlags
 
 */
 
void SettingEntry::SetButtons(byte new_val)
 
{
 
	assert((new_val & ~SEF_BUTTONS_MASK) == 0); // Should not touch any flags outside the buttons
 
	this->flags = (this->flags & ~SEF_BUTTONS_MASK) | new_val;
 
}
 

	
 
/** Return number of rows needed to display the (filtered) entry */
 
uint SettingEntry::Length() const
 
{
 
	return this->IsFiltered() ? 0 : 1;
 
}
 

	
 
/**
 
 * Get the biggest height of the help text(s), if the width is at least \a maxw. Help text gets wrapped if needed.
 
 * @param maxw Maximal width of a line help text.
 
 * @return Biggest height needed to display any help text of this node (and its descendants).
 
 */
 
uint SettingEntry::GetMaxHelpHeight(int maxw)
 
{
 
	return GetStringHeight(this->GetHelpText(), maxw);
 
}
 

	
 
/**
 
 * Checks whether an entry shall be made visible based on the restriction mode.
 
 * @param mode The current status of the restriction drop down box.
 
 * @return true if the entry shall be visible.
 
 */
 
bool SettingEntry::IsVisibleByRestrictionMode(RestrictionMode mode) const
 
{
 
	/* There shall not be any restriction, i.e. all settings shall be visible. */
 
	if (mode == RM_ALL) return true;
 

	
 
	GameSettings *settings_ptr = &GetGameSettings();
 
	const SettingDesc *sd = this->setting;
 

	
 
	if (mode == RM_BASIC) return (this->setting->desc.cat & SC_BASIC_LIST) != 0;
 
	if (mode == RM_ADVANCED) return (this->setting->desc.cat & SC_ADVANCED_LIST) != 0;
 
	if (mode == RM_BASIC) return (this->setting->cat & SC_BASIC_LIST) != 0;
 
	if (mode == RM_ADVANCED) return (this->setting->cat & SC_ADVANCED_LIST) != 0;
 

	
 
	/* Read the current value. */
 
	const void *var = ResolveVariableAddress(settings_ptr, sd);
 
	int64 current_value = ReadValue(var, sd->save.conv);
 

	
 
	int64 filter_value;
 

	
 
	if (mode == RM_CHANGED_AGAINST_DEFAULT) {
 
		/* This entry shall only be visible, if the value deviates from its default value. */
 

	
 
		/* Read the default value. */
 
		filter_value = ReadValue(&sd->desc.def, sd->save.conv);
 
		filter_value = ReadValue(&sd->def, sd->save.conv);
 
	} else {
 
		assert(mode == RM_CHANGED_AGAINST_NEW);
 
		/* This entry shall only be visible, if the value deviates from
 
		 * its value is used when starting a new game. */
 

	
 
		/* Make sure we're not comparing the new game settings against itself. */
 
		assert(settings_ptr != &_settings_newgame);
 

	
 
		/* Read the new game's value. */
 
		var = ResolveVariableAddress(&_settings_newgame, sd);
 
		filter_value = ReadValue(var, sd->save.conv);
 
	}
 

	
 
	return current_value != filter_value;
 
}
 

	
 
/**
 
 * Update the filter state.
 
 * @param filter Filter
 
 * @param force_visible Whether to force all items visible, no matter what (due to filter text; not affected by restriction drop down box).
 
 * @return true if item remains visible
 
 */
 
bool SettingEntry::UpdateFilterState(SettingFilter &filter, bool force_visible)
 
{
 
	CLRBITS(this->flags, SEF_FILTERED);
 

	
 
	bool visible = true;
 

	
 
	const SettingDesc *sd = this->setting;
 
	if (!force_visible && !filter.string.IsEmpty()) {
 
		/* Process the search text filter for this item. */
 
		filter.string.ResetState();
 

	
 
		const SettingDescBase *sdb = &sd->desc;
 
		const SettingDescBase *sdb = sd;
 

	
 
		SetDParam(0, STR_EMPTY);
 
		filter.string.AddLine(sdb->str);
 
		filter.string.AddLine(this->GetHelpText());
 

	
 
		visible = filter.string.GetState();
 
	}
 

	
 
	if (visible) {
 
		if (filter.type != ST_ALL && sd->GetType() != filter.type) {
 
			filter.type_hides = true;
 
			visible = false;
 
		}
 
		if (!this->IsVisibleByRestrictionMode(filter.mode)) {
 
			while (filter.min_cat < RM_ALL && (filter.min_cat == filter.mode || !this->IsVisibleByRestrictionMode(filter.min_cat))) filter.min_cat++;
 
			visible = false;
 
		}
 
	}
 

	
 
	if (!visible) SETBITS(this->flags, SEF_FILTERED);
 
	return visible;
 
}
 

	
 
static const void *ResolveVariableAddress(const GameSettings *settings_ptr, const SettingDesc *sd)
 
{
 
	if ((sd->desc.flags & SGF_PER_COMPANY) != 0) {
 
	if ((sd->flags & SGF_PER_COMPANY) != 0) {
 
		if (Company::IsValidID(_local_company) && _game_mode != GM_MENU) {
 
			return GetVariableAddress(&Company::Get(_local_company)->settings, &sd->save);
 
		} else {
 
			return GetVariableAddress(&_settings_client.company, &sd->save);
 
		}
 
	} else {
 
		return GetVariableAddress(settings_ptr, &sd->save);
 
	}
 
}
 

	
 
/**
 
 * Set the DParams for drawing the value of a setting.
 
 * @param first_param First DParam to use
 
 * @param value Setting value to set params for.
 
 */
 
void SettingEntry::SetValueDParams(uint first_param, int32 value) const
 
{
 
	const SettingDescBase *sdb = &this->setting->desc;
 
	const SettingDescBase *sdb = this->setting;
 
	if (sdb->cmd == SDT_BOOLX) {
 
		SetDParam(first_param++, value != 0 ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF);
 
	} else {
 
		if ((sdb->flags & SGF_MULTISTRING) != 0) {
 
			SetDParam(first_param++, sdb->str_val - sdb->min + value);
 
		} else if ((sdb->flags & SGF_DISPLAY_ABS) != 0) {
 
			SetDParam(first_param++, sdb->str_val + ((value >= 0) ? 1 : 0));
 
			value = abs(value);
 
		} else {
 
			SetDParam(first_param++, sdb->str_val + ((value == 0 && (sdb->flags & SGF_0ISDISABLED) != 0) ? 1 : 0));
 
		}
 
		SetDParam(first_param++, value);
 
	}
 
}
 

	
 
/**
 
 * Function to draw setting value (button + text + current value)
 
 * @param settings_ptr Pointer to current values of all settings
 
 * @param left         Left-most position in window/panel to start drawing
 
 * @param right        Right-most position in window/panel to draw
 
 * @param y            Upper-most position in window/panel to start drawing
 
 * @param highlight    Highlight entry.
 
 */
 
void SettingEntry::DrawSetting(GameSettings *settings_ptr, int left, int right, int y, bool highlight) const
 
{
 
	const SettingDesc *sd = this->setting;
 
	const SettingDescBase *sdb = &sd->desc;
 
	const SettingDescBase *sdb = sd;
 
	const void *var = ResolveVariableAddress(settings_ptr, sd);
 
	int state = this->flags & SEF_BUTTONS_MASK;
 

	
 
	bool rtl = _current_text_dir == TD_RTL;
 
	uint buttons_left = rtl ? right + 1 - SETTING_BUTTON_WIDTH : left;
 
	uint text_left  = left + (rtl ? 0 : SETTING_BUTTON_WIDTH + 5);
 
	uint text_right = right - (rtl ? SETTING_BUTTON_WIDTH + 5 : 0);
 
	uint button_y = y + (SETTING_HEIGHT - SETTING_BUTTON_HEIGHT) / 2;
 

	
 
	/* We do not allow changes of some items when we are a client in a networkgame */
 
	bool editable = sd->IsEditable();
 

	
 
	SetDParam(0, highlight ? STR_ORANGE_STRING1_WHITE : STR_ORANGE_STRING1_LTBLUE);
 
	int32 value = (int32)ReadValue(var, sd->save.conv);
 
	if (sdb->cmd == SDT_BOOLX) {
 
		/* Draw checkbox for boolean-value either on/off */
 
		DrawBoolButton(buttons_left, button_y, value != 0, editable);
 
	} else if ((sdb->flags & SGF_MULTISTRING) != 0) {
 
		/* Draw [v] button for settings of an enum-type */
 
		DrawDropDownButton(buttons_left, button_y, COLOUR_YELLOW, state != 0, editable);
 
	} else {
 
		/* Draw [<][>] boxes for settings of an integer-type */
 
		DrawArrowButtons(buttons_left, button_y, COLOUR_YELLOW, state,
 
				editable && value != (sdb->flags & SGF_0ISDISABLED ? 0 : sdb->min), editable && (uint32)value != sdb->max);
 
@@ -2070,49 +2070,49 @@ struct GameSettingsWindow : Window {
 
		switch (widget) {
 
			case WID_GS_OPTIONSPANEL: {
 
				int top_pos = r.top + SETTINGTREE_TOP_OFFSET + 1 + this->warn_lines * SETTING_HEIGHT;
 
				uint last_row = this->vscroll->GetPosition() + this->vscroll->GetCapacity() - this->warn_lines;
 
				int next_row = GetSettingsTree().Draw(settings_ptr, r.left + SETTINGTREE_LEFT_OFFSET, r.right - SETTINGTREE_RIGHT_OFFSET, top_pos,
 
						this->vscroll->GetPosition(), last_row, this->last_clicked);
 
				if (next_row == 0) DrawString(r.left + SETTINGTREE_LEFT_OFFSET, r.right - SETTINGTREE_RIGHT_OFFSET, top_pos, STR_CONFIG_SETTINGS_NONE);
 
				break;
 
			}
 

	
 
			case WID_GS_HELP_TEXT:
 
				if (this->last_clicked != nullptr) {
 
					const SettingDesc *sd = this->last_clicked->setting;
 

	
 
					int y = r.top;
 
					switch (sd->GetType()) {
 
						case ST_COMPANY: SetDParam(0, _game_mode == GM_MENU ? STR_CONFIG_SETTING_TYPE_COMPANY_MENU : STR_CONFIG_SETTING_TYPE_COMPANY_INGAME); break;
 
						case ST_CLIENT:  SetDParam(0, STR_CONFIG_SETTING_TYPE_CLIENT); break;
 
						case ST_GAME:    SetDParam(0, _game_mode == GM_MENU ? STR_CONFIG_SETTING_TYPE_GAME_MENU : STR_CONFIG_SETTING_TYPE_GAME_INGAME); break;
 
						default: NOT_REACHED();
 
					}
 
					DrawString(r.left, r.right, y, STR_CONFIG_SETTING_TYPE);
 
					y += FONT_HEIGHT_NORMAL;
 

	
 
					int32 default_value = ReadValue(&sd->desc.def, sd->save.conv);
 
					int32 default_value = ReadValue(&sd->def, sd->save.conv);
 
					this->last_clicked->SetValueDParams(0, default_value);
 
					DrawString(r.left, r.right, y, STR_CONFIG_SETTING_DEFAULT_VALUE);
 
					y += FONT_HEIGHT_NORMAL + WD_PAR_VSEP_NORMAL;
 

	
 
					DrawStringMultiLine(r.left, r.right, y, r.bottom, this->last_clicked->GetHelpText(), TC_WHITE);
 
				}
 
				break;
 

	
 
			default:
 
				break;
 
		}
 
	}
 

	
 
	/**
 
	 * Set the entry that should have its help text displayed, and mark the window dirty so it gets repainted.
 
	 * @param pe Setting to display help text of, use \c nullptr to stop displaying help of the currently displayed setting.
 
	 */
 
	void SetDisplayedHelpText(SettingEntry *pe)
 
	{
 
		if (this->last_clicked != pe) this->SetDirty();
 
		this->last_clicked = pe;
 
	}
 

	
 
	void OnClick(Point pt, int widget, int click_count) override
 
@@ -2174,88 +2174,88 @@ struct GameSettingsWindow : Window {
 
		if (clicked_page != nullptr) {
 
			this->SetDisplayedHelpText(nullptr);
 
			clicked_page->folded = !clicked_page->folded; // Flip 'folded'-ness of the sub-page
 

	
 
			this->manually_changed_folding = true;
 

	
 
			this->InvalidateData();
 
			return;
 
		}
 

	
 
		SettingEntry *pe = dynamic_cast<SettingEntry*>(clicked_entry);
 
		assert(pe != nullptr);
 
		const SettingDesc *sd = pe->setting;
 

	
 
		/* return if action is only active in network, or only settable by server */
 
		if (!sd->IsEditable()) {
 
			this->SetDisplayedHelpText(pe);
 
			return;
 
		}
 

	
 
		const void *var = ResolveVariableAddress(settings_ptr, sd);
 
		int32 value = (int32)ReadValue(var, sd->save.conv);
 

	
 
		/* clicked on the icon on the left side. Either scroller, bool on/off or dropdown */
 
		if (x < SETTING_BUTTON_WIDTH && (sd->desc.flags & SGF_MULTISTRING)) {
 
			const SettingDescBase *sdb = &sd->desc;
 
		if (x < SETTING_BUTTON_WIDTH && (sd->flags & SGF_MULTISTRING)) {
 
			const SettingDescBase *sdb = sd;
 
			this->SetDisplayedHelpText(pe);
 

	
 
			if (this->valuedropdown_entry == pe) {
 
				/* unclick the dropdown */
 
				HideDropDownMenu(this);
 
				this->closing_dropdown = false;
 
				this->valuedropdown_entry->SetButtons(0);
 
				this->valuedropdown_entry = nullptr;
 
			} else {
 
				if (this->valuedropdown_entry != nullptr) this->valuedropdown_entry->SetButtons(0);
 
				this->closing_dropdown = false;
 

	
 
				const NWidgetBase *wid = this->GetWidget<NWidgetBase>(WID_GS_OPTIONSPANEL);
 
				int rel_y = (pt.y - (int)wid->pos_y - SETTINGTREE_TOP_OFFSET) % wid->resize_y;
 

	
 
				Rect wi_rect;
 
				wi_rect.left = pt.x - (_current_text_dir == TD_RTL ? SETTING_BUTTON_WIDTH - 1 - x : x);
 
				wi_rect.right = wi_rect.left + SETTING_BUTTON_WIDTH - 1;
 
				wi_rect.top = pt.y - rel_y + (SETTING_HEIGHT - SETTING_BUTTON_HEIGHT) / 2;
 
				wi_rect.bottom = wi_rect.top + SETTING_BUTTON_HEIGHT - 1;
 

	
 
				/* For dropdowns we also have to check the y position thoroughly, the mouse may not above the just opening dropdown */
 
				if (pt.y >= wi_rect.top && pt.y <= wi_rect.bottom) {
 
					this->valuedropdown_entry = pe;
 
					this->valuedropdown_entry->SetButtons(SEF_LEFT_DEPRESSED);
 

	
 
					DropDownList list;
 
					for (int i = sdb->min; i <= (int)sdb->max; i++) {
 
						list.emplace_back(new DropDownListStringItem(sdb->str_val + i - sdb->min, i, false));
 
					}
 

	
 
					ShowDropDownListAt(this, std::move(list), value, -1, wi_rect, COLOUR_ORANGE, true);
 
				}
 
			}
 
			this->SetDirty();
 
		} else if (x < SETTING_BUTTON_WIDTH) {
 
			this->SetDisplayedHelpText(pe);
 
			const SettingDescBase *sdb = &sd->desc;
 
			const SettingDescBase *sdb = sd;
 
			int32 oldvalue = value;
 

	
 
			switch (sdb->cmd) {
 
				case SDT_BOOLX: value ^= 1; break;
 
				case SDT_ONEOFMANY:
 
				case SDT_NUMX: {
 
					/* Add a dynamic step-size to the scroller. In a maximum of
 
					 * 50-steps you should be able to get from min to max,
 
					 * unless specified otherwise in the 'interval' variable
 
					 * of the current setting. */
 
					uint32 step = (sdb->interval == 0) ? ((sdb->max - sdb->min) / 50) : sdb->interval;
 
					if (step == 0) step = 1;
 

	
 
					/* don't allow too fast scrolling */
 
					if ((this->flags & WF_TIMEOUT) && this->timeout_timer > 1) {
 
						_left_button_clicked = false;
 
						return;
 
					}
 

	
 
					/* Increase or decrease the value and clamp it to extremes */
 
					if (x >= SETTING_BUTTON_WIDTH / 2) {
 
						value += step;
 
						if (sdb->min < 0) {
 
							assert((int32)sdb->max >= 0);
 
@@ -2270,126 +2270,126 @@ struct GameSettingsWindow : Window {
 
					}
 

	
 
					/* Set up scroller timeout for numeric values */
 
					if (value != oldvalue) {
 
						if (this->clicked_entry != nullptr) { // Release previous buttons if any
 
							this->clicked_entry->SetButtons(0);
 
						}
 
						this->clicked_entry = pe;
 
						this->clicked_entry->SetButtons((x >= SETTING_BUTTON_WIDTH / 2) != (_current_text_dir == TD_RTL) ? SEF_RIGHT_DEPRESSED : SEF_LEFT_DEPRESSED);
 
						this->SetTimeout();
 
						_left_button_clicked = false;
 
					}
 
					break;
 
				}
 

	
 
				default: NOT_REACHED();
 
			}
 

	
 
			if (value != oldvalue) {
 
				SetSettingValue(sd, value);
 
				this->SetDirty();
 
			}
 
		} else {
 
			/* Only open editbox if clicked for the second time, and only for types where it is sensible for. */
 
			if (this->last_clicked == pe && sd->desc.cmd != SDT_BOOLX && !(sd->desc.flags & SGF_MULTISTRING)) {
 
			if (this->last_clicked == pe && sd->cmd != SDT_BOOLX && !(sd->flags & SGF_MULTISTRING)) {
 
				int64 value64 = value;
 
				/* Show the correct currency-translated value */
 
				if (sd->desc.flags & SGF_CURRENCY) value64 *= _currency->rate;
 
				if (sd->flags & SGF_CURRENCY) value64 *= _currency->rate;
 

	
 
				this->valuewindow_entry = pe;
 
				SetDParam(0, value64);
 
				/* Limit string length to 14 so that MAX_INT32 * max currency rate doesn't exceed MAX_INT64. */
 
				ShowQueryString(STR_JUST_INT, STR_CONFIG_SETTING_QUERY_CAPTION, 15, this, CS_NUMERAL, QSF_ENABLE_DEFAULT);
 
			}
 
			this->SetDisplayedHelpText(pe);
 
		}
 
	}
 

	
 
	void OnTimeout() override
 
	{
 
		if (this->clicked_entry != nullptr) { // On timeout, release any depressed buttons
 
			this->clicked_entry->SetButtons(0);
 
			this->clicked_entry = nullptr;
 
			this->SetDirty();
 
		}
 
	}
 

	
 
	void OnQueryTextFinished(char *str) override
 
	{
 
		/* The user pressed cancel */
 
		if (str == nullptr) return;
 

	
 
		assert(this->valuewindow_entry != nullptr);
 
		const SettingDesc *sd = this->valuewindow_entry->setting;
 

	
 
		int32 value;
 
		if (!StrEmpty(str)) {
 
			long long llvalue = atoll(str);
 

	
 
			/* Save the correct currency-translated value */
 
			if (sd->desc.flags & SGF_CURRENCY) llvalue /= _currency->rate;
 
			if (sd->flags & SGF_CURRENCY) llvalue /= _currency->rate;
 

	
 
			value = (int32)ClampToI32(llvalue);
 
		} else {
 
			value = (int32)(size_t)sd->desc.def;
 
			value = (int32)(size_t)sd->def;
 
		}
 

	
 
		SetSettingValue(this->valuewindow_entry->setting, value);
 
		this->SetDirty();
 
	}
 

	
 
	void OnDropdownSelect(int widget, int index) override
 
	{
 
		switch (widget) {
 
			case WID_GS_RESTRICT_DROPDOWN:
 
				this->filter.mode = (RestrictionMode)index;
 
				if (this->filter.mode == RM_CHANGED_AGAINST_DEFAULT ||
 
						this->filter.mode == RM_CHANGED_AGAINST_NEW) {
 

	
 
					if (!this->manually_changed_folding) {
 
						/* Expand all when selecting 'changes'. Update the filter state first, in case it becomes less restrictive in some cases. */
 
						GetSettingsTree().UpdateFilterState(this->filter, false);
 
						GetSettingsTree().UnFoldAll();
 
					}
 
				} else {
 
					/* Non-'changes' filter. Save as default. */
 
					_settings_client.gui.settings_restriction_mode = this->filter.mode;
 
				}
 
				this->InvalidateData();
 
				break;
 

	
 
			case WID_GS_TYPE_DROPDOWN:
 
				this->filter.type = (SettingType)index;
 
				this->InvalidateData();
 
				break;
 

	
 
			default:
 
				if (widget < 0) {
 
					/* Deal with drop down boxes on the panel. */
 
					assert(this->valuedropdown_entry != nullptr);
 
					const SettingDesc *sd = this->valuedropdown_entry->setting;
 
					assert(sd->desc.flags & SGF_MULTISTRING);
 
					assert(sd->flags & SGF_MULTISTRING);
 

	
 
					SetSettingValue(sd, index);
 
					this->SetDirty();
 
				}
 
				break;
 
		}
 
	}
 

	
 
	void OnDropdownClose(Point pt, int widget, int index, bool instant_close) override
 
	{
 
		if (widget >= 0) {
 
			/* Normally the default implementation of OnDropdownClose() takes care of
 
			 * a few things. We want that behaviour here too, but only for
 
			 * "normal" dropdown boxes. The special dropdown boxes added for every
 
			 * setting that needs one can't have this call. */
 
			Window::OnDropdownClose(pt, widget, index, instant_close);
 
		} else {
 
			/* We cannot raise the dropdown button just yet. OnClick needs some hint, whether
 
			 * the same dropdown button was clicked again, and then not open the dropdown again.
 
			 * So, we only remember that it was closed, and process it on the next OnPaint, which is
 
			 * after OnClick. */
 
			assert(this->valuedropdown_entry != nullptr);
 
			this->closing_dropdown = true;
 
			this->SetDirty();
src/settings_internal.h
Show inline comments
 
@@ -79,38 +79,37 @@ enum SettingType {
 
};
 

	
 
typedef bool OnChange(int32 var);           ///< callback prototype on data modification
 
typedef size_t OnConvert(const char *value); ///< callback prototype for conversion error
 

	
 
/** Properties of config file settings. */
 
struct SettingDescBase {
 
	const char *name;       ///< name of the setting. Used in configuration file and for console
 
	const void *def;        ///< default value given when none is present
 
	SettingDescType cmd;    ///< various flags for the variable
 
	SettingGuiFlag flags;   ///< handles how a setting would show up in the GUI (text/currency, etc.)
 
	int32 min;              ///< minimum values
 
	uint32 max;             ///< maximum values
 
	int32 interval;         ///< the interval to use between settings in the 'settings' window. If interval is '0' the interval is dynamically determined
 
	const char *many;       ///< ONE/MANY_OF_MANY: string of possible values for this type
 
	StringID str;           ///< (translated) string with descriptive text; gui and console
 
	StringID str_help;      ///< (Translated) string with help text; gui only.
 
	StringID str_val;       ///< (Translated) first string describing the value.
 
	OnChange *proc;         ///< callback procedure for when the value is changed
 
	OnConvert *proc_cnvt;   ///< callback procedure when loading value mechanism fails
 
	SettingCategory cat;    ///< assigned categories of the setting
 
	bool startup;           ///< setting has to be loaded directly at startup?
 
};
 

	
 
struct SettingDesc {
 
	SettingDescBase desc;   ///< Settings structure (going to configuration file)
 
struct SettingDesc : SettingDescBase {
 
	SaveLoad save;          ///< Internal structure (going to savegame, parts to config)
 

	
 
	bool IsEditable(bool do_command = false) const;
 
	SettingType GetType() const;
 
};
 

	
 
const SettingDesc *GetSettingFromName(const char *name);
 
bool SetSettingValue(const SettingDesc *sd, int32 value, bool force_newgame = false);
 
bool SetSettingValue(const SettingDesc *sd, const char *value, bool force_newgame = false);
 
uint GetSettingIndex(const SettingDesc *sd);
 

	
 
#endif /* SETTINGS_INTERNAL_H */
0 comments (0 inline, 0 general)