diff --git a/src/settings.cpp b/src/settings.cpp index 6851764160..c025b7d054 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -419,6 +419,16 @@ static const void *StringToVal(const SettingDescBase *desc, const char *orig_str return nullptr; } +static bool ValidateEnumSetting(const SettingDescBase *sdb, int32 val) +{ + for (const SettingDescEnumEntry *enumlist = sdb->enumlist; enumlist != nullptr && enumlist->str != STR_NULL; enumlist++) { + if (enumlist->val == val) { + return true; + } + } + return false; +} + /** * Set the value of a setting and if needed clamp the value to * the preset minimum and maximum. @@ -456,7 +466,9 @@ static void Write_ValidateSetting(void *ptr, const SettingDesc *sd, int32 val) case SLE_VAR_I32: { /* Override the minimum value. No value below sdb->min, except special value 0 */ if (!(sdb->flags & SGF_0ISDISABLED) || val != 0) { - if (!(sdb->flags & SGF_MULTISTRING)) { + if (sdb->flags & SGF_ENUM) { + if (!ValidateEnumSetting(sdb, val)) val = (int32)(size_t)sdb->def; + } else if (!(sdb->flags & SGF_MULTISTRING)) { /* Clamp value-type setting to its valid range */ val = Clamp(val, sdb->min, sdb->max); } else if (val < sdb->min || val > (int32)sdb->max) { @@ -470,7 +482,9 @@ static void Write_ValidateSetting(void *ptr, const SettingDesc *sd, int32 val) /* Override the minimum value. No value below sdb->min, except special value 0 */ uint32 uval = (uint32)val; if (!(sdb->flags & SGF_0ISDISABLED) || uval != 0) { - if (!(sdb->flags & SGF_MULTISTRING)) { + if (sdb->flags & SGF_ENUM) { + if (!ValidateEnumSetting(sdb, val)) uval = (uint32)(size_t)sdb->def; + } else if (!(sdb->flags & SGF_MULTISTRING)) { /* Clamp value-type setting to its valid range */ uval = ClampU(uval, sdb->min, sdb->max); } else if (uval < (uint)sdb->min || uval > sdb->max) { diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index e21ef5445c..f1ca4078b9 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -1111,7 +1111,16 @@ void SettingEntry::SetValueDParams(uint first_param, int32 value) const 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) { + if ((sdb->flags & SGF_ENUM) != 0) { + StringID str = STR_UNDEFINED; + for (const SettingDescEnumEntry *enumlist = sdb->enumlist; enumlist != nullptr && enumlist->str != STR_NULL; enumlist++) { + if (enumlist->val == value) { + str = enumlist->str; + break; + } + } + SetDParam(first_param++, str); + } 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)); @@ -1152,7 +1161,7 @@ void SettingEntry::DrawSetting(GameSettings *settings_ptr, int left, int right, 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) { + } else if ((sdb->flags & (SGF_MULTISTRING | SGF_ENUM)) != 0) { /* Draw [v] button for settings of an enum-type */ DrawDropDownButton(buttons_left, button_y, COLOUR_YELLOW, state != 0, editable); } else { @@ -2209,7 +2218,7 @@ struct GameSettingsWindow : Window { 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)) { + if (x < SETTING_BUTTON_WIDTH && (sd->desc.flags & (SGF_MULTISTRING | SGF_ENUM))) { const SettingDescBase *sdb = &sd->desc; this->SetDisplayedHelpText(pe); @@ -2238,10 +2247,16 @@ struct GameSettingsWindow : Window { this->valuedropdown_entry->SetButtons(SEF_LEFT_DEPRESSED); DropDownList list; - for (int i = sdb->min; i <= (int)sdb->max; i++) { - int val = sd->orderproc ? sd->orderproc(i - sdb->min) : i; - assert_msg(val >= sdb->min && val <= (int)sdb->max, "min: %d, max: %d, val: %d", sdb->min, sdb->max, val); - list.emplace_back(new DropDownListStringItem(sdb->str_val + val - sdb->min, val, false)); + if (sd->desc.flags & SGF_MULTISTRING) { + for (int i = sdb->min; i <= (int)sdb->max; i++) { + int val = sd->orderproc ? sd->orderproc(i - sdb->min) : i; + assert_msg(val >= sdb->min && val <= (int)sdb->max, "min: %d, max: %d, val: %d", sdb->min, sdb->max, val); + list.emplace_back(new DropDownListStringItem(sdb->str_val + val - sdb->min, val, false)); + } + } else if ((sd->desc.flags & SGF_ENUM)) { + for (const SettingDescEnumEntry *enumlist = sd->desc.enumlist; enumlist != nullptr && enumlist->str != STR_NULL; enumlist++) { + list.emplace_back(new DropDownListStringItem(enumlist->str, enumlist->val, false)); + } } ShowDropDownListAt(this, std::move(list), value, -1, wi_rect, COLOUR_ORANGE, true); @@ -2311,7 +2326,7 @@ struct GameSettingsWindow : Window { } } 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->desc.cmd != SDT_BOOLX && !(sd->desc.flags & (SGF_MULTISTRING | SGF_ENUM))) { /* Show the correct currency-translated value */ if (sd->desc.flags & SGF_CURRENCY) value *= _currency->rate; @@ -2397,7 +2412,7 @@ struct GameSettingsWindow : Window { /* 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->desc.flags & (SGF_MULTISTRING | SGF_ENUM)); if ((sd->desc.flags & SGF_PER_COMPANY) != 0) { SetCompanySetting(this->valuedropdown_entry->index, index); diff --git a/src/settings_internal.h b/src/settings_internal.h index b514811d4f..3acfe8c7fb 100644 --- a/src/settings_internal.h +++ b/src/settings_internal.h @@ -49,6 +49,7 @@ enum SettingGuiFlagLong { SGF_SCENEDIT_TOO = 1 << 7, ///< this setting can be changed in the scenario editor (only makes sense when SGF_NEWGAME_ONLY is set) SGF_PER_COMPANY = 1 << 8, ///< this setting can be different for each company (saved in company struct) SGF_DECIMAL1 = 1 << 9, ///< display a decimal representation of the setting value divided by 10 + SGF_ENUM = 1 << 10,///< the setting can take one of the values given by an array of struct SettingDescEnumEntry }; DECLARE_ENUM_AS_BIT_SET(SettingGuiFlagLong) typedef SimpleTinyEnumT SettingGuiFlag; @@ -92,6 +93,12 @@ typedef bool OnChange(int32 var); ///< callback prototype on data modi typedef size_t OnConvert(const char *value); ///< callback prototype for conversion error typedef int OnGuiOrder(uint nth); ///< callback prototype for GUI ordering +/** The last entry in an array of struct SettingDescEnumEntry must use STR_NULL. */ +struct SettingDescEnumEntry { + int32 val; + StringID str; +}; + /** Properties of config file settings. */ struct SettingDescBase { const char *name; ///< name of the setting. Used in configuration file and for console @@ -108,6 +115,7 @@ struct SettingDescBase { 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 + const SettingDescEnumEntry *enumlist; ///< For SGF_ENUM. The last entry must use STR_NULL }; struct SettingDesc { diff --git a/src/table/settings.h.preamble b/src/table/settings.h.preamble index ea21ce0f48..f829c069fd 100644 --- a/src/table/settings.h.preamble +++ b/src/table/settings.h.preamble @@ -56,19 +56,22 @@ static size_t ConvertLandscape(const char *value); * on the appropriate macro. */ -#define NSD_GENERAL(name, def, cmd, guiflags, min, max, interval, many, str, strhelp, strval, proc, load, cat)\ - {name, (const void*)(size_t)(def), {(byte)cmd}, {(uint16)guiflags}, min, max, interval, many, str, strhelp, strval, proc, load, cat} +#define NSD_GENERAL(name, def, cmd, guiflags, min, max, interval, many, str, strhelp, strval, proc, load, cat, enumlist)\ + {name, (const void*)(size_t)(def), {(byte)cmd}, {(uint16)guiflags}, min, max, interval, many, str, strhelp, strval, proc, load, cat, enumlist} /* Macros for various objects to go in the configuration file. * This section is for global variables */ -#define SDTG_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, var, length, def, min, max, interval, full, str, strhelp, strval, proc, from, to, cat, extver, patxname, orderproc)\ - {NSD_GENERAL(name, def, sdt_cmd, guiflags, min, max, interval, full, str, strhelp, strval, proc, nullptr, cat), SLEG_GENERAL_X(sle_cmd, var, type | flags, length, from, to, extver), patxname, nullptr, orderproc} +#define SDTG_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, var, length, def, min, max, interval, full, str, strhelp, strval, proc, from, to, cat, extver, patxname, orderproc, enumlist)\ + {NSD_GENERAL(name, def, sdt_cmd, guiflags, min, max, interval, full, str, strhelp, strval, proc, nullptr, cat, enumlist), SLEG_GENERAL_X(sle_cmd, var, type | flags, length, from, to, extver), patxname, nullptr, orderproc} #define SDTG_GENERAL(name, sdt_cmd, sle_cmd, type, flags, guiflags, var, length, def, min, max, interval, full, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ - SDTG_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, var, length, def, min, max, interval, full, str, strhelp, strval, proc, from, to, cat, extver, patxname, nullptr) + SDTG_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, var, length, def, min, max, interval, full, str, strhelp, strval, proc, from, to, cat, extver, patxname, nullptr, nullptr) #define SDTG_VAR(name, type, flags, guiflags, var, def, min, max, interval, str, strhelp, strval, proc, from, to, cat, extver, patxname, orderproc)\ - SDTG_GENERAL2(name, SDT_NUMX, SL_VAR, type, flags, guiflags, var, 0, def, min, max, interval, nullptr, str, strhelp, strval, proc, from, to, cat, extver, patxname, orderproc) + SDTG_GENERAL2(name, SDT_NUMX, SL_VAR, type, flags, guiflags, var, 0, def, min, max, interval, nullptr, str, strhelp, strval, proc, from, to, cat, extver, patxname, orderproc, nullptr) + +#define SDTG_ENUM(name, type, flags, guiflags, var, def, str, strhelp, proc, from, to, cat, extver, patxname, enumlist)\ + SDTG_GENERAL2(name, SDT_NUMX, SL_VAR, type, flags, guiflags | SGF_ENUM, var, 0, def, 0, 0, 0, nullptr, str, strhelp, STR_NULL, proc, nullptr, from, to, cat, extver, patxname, nullptr, enumlist) #define SDTG_BOOL(name, flags, guiflags, var, def, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ SDTG_GENERAL(name, SDT_BOOLX, SL_VAR, SLE_BOOL, flags, guiflags, var, 0, def, 0, 1, 0, nullptr, str, strhelp, strval, proc, from, to, cat, extver, patxname) @@ -86,20 +89,23 @@ static size_t ConvertLandscape(const char *value); SDTG_GENERAL(name, SDT_MANYOFMANY, SL_VAR, type, flags, guiflags, var, 0, def, 0, 0, 0, full, str, strhelp, strval, proc, from, to, cat, extver, patxname) #define SDTG_NULL(length, from, to, extver)\ - {{"", nullptr, {0}, {0}, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE}, SLEG_NULL_X(length, from, to, extver), nullptr, nullptr, nullptr} + {{"", nullptr, {0}, {0}, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE, nullptr}, SLEG_NULL_X(length, from, to, extver), nullptr, nullptr, nullptr} -#define SDTG_END() {{nullptr, nullptr, {0}, {0}, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE}, SLEG_END(), nullptr, nullptr, nullptr} +#define SDTG_END() {{nullptr, nullptr, {0}, {0}, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE, nullptr}, SLEG_END(), nullptr, nullptr, nullptr} /* Macros for various objects to go in the configuration file. * This section is for structures where their various members are saved */ -#define SDT_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, base, var, length, def, min, max, interval, full, str, strhelp, strval, proc, load, from, to, cat, extver, patxname, orderproc)\ - {NSD_GENERAL(name, def, sdt_cmd, guiflags, min, max, interval, full, str, strhelp, strval, proc, load, cat), SLE_GENERAL_X(sle_cmd, base, var, type | flags, length, from, to, extver), patxname, nullptr, orderproc} +#define SDT_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, base, var, length, def, min, max, interval, full, str, strhelp, strval, proc, load, from, to, cat, extver, patxname, orderproc, enumlist)\ + {NSD_GENERAL(name, def, sdt_cmd, guiflags, min, max, interval, full, str, strhelp, strval, proc, load, cat, enumlist), SLE_GENERAL_X(sle_cmd, base, var, type | flags, length, from, to, extver), patxname, nullptr, orderproc} #define SDT_GENERAL(name, sdt_cmd, sle_cmd, type, flags, guiflags, base, var, length, def, min, max, interval, full, str, strhelp, strval, proc, load, from, to, cat, extver, patxname)\ - SDT_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, base, var, length, def, min, max, interval, full, str, strhelp, strval, proc, load, from, to, cat, extver, patxname, nullptr) + SDT_GENERAL2(name, sdt_cmd, sle_cmd, type, flags, guiflags, base, var, length, def, min, max, interval, full, str, strhelp, strval, proc, load, from, to, cat, extver, patxname, nullptr, nullptr) #define SDT_VAR(base, var, type, flags, guiflags, def, min, max, interval, str, strhelp, strval, proc, from, to, cat, extver, patxname, orderproc)\ - SDT_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags, base, var, 1, def, min, max, interval, nullptr, str, strhelp, strval, proc, nullptr, from, to, cat, extver, patxname, orderproc) + SDT_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags, base, var, 1, def, min, max, interval, nullptr, str, strhelp, strval, proc, nullptr, from, to, cat, extver, patxname, orderproc, nullptr) + +#define SDT_ENUM(base, var, type, flags, guiflags, def, str, strhelp, proc, from, to, cat, extver, patxname, enumlist)\ + SDT_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags | SGF_ENUM, base, var, 1, def, 0, 0, 0, nullptr, str, strhelp, STR_NULL, proc, nullptr, from, to, cat, extver, patxname, nullptr, enumlist) #define SDT_BOOL(base, var, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ SDT_GENERAL(#var, SDT_BOOLX, SL_VAR, SLE_BOOL, flags, guiflags, base, var, 1, def, 0, 1, 0, nullptr, str, strhelp, strval, proc, nullptr, from, to, cat, extver, patxname) @@ -120,11 +126,14 @@ static size_t ConvertLandscape(const char *value); SDT_GENERAL(#var, SDT_MANYOFMANY, SL_VAR, type, flags, guiflags, base, var, 1, def, 0, 0, 0, full, str, strhelp, strval, proc, nullptr, from, to, cat, extver, patxname) #define SDT_NULL(length, from, to, extver)\ - {{"", nullptr, {0}, {0}, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE}, SLE_CONDNULL_X(length, from, to, extver), nullptr, nullptr, nullptr} + {{"", nullptr, {0}, {0}, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE, nullptr}, SLE_CONDNULL_X(length, from, to, extver), nullptr, nullptr, nullptr} #define SDTC_VAR(var, type, flags, guiflags, def, min, max, interval, str, strhelp, strval, proc, from, to, cat, extver, patxname, orderproc)\ - SDTG_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags, _settings_client.var, 1, def, min, max, interval, nullptr, str, strhelp, strval, proc, from, to, cat, extver, patxname, orderproc) + SDTG_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags, _settings_client.var, 1, def, min, max, interval, nullptr, str, strhelp, strval, proc, from, to, cat, extver, patxname, orderproc, nullptr) + +#define SDTC_ENUM(var, type, flags, guiflags, def, str, strhelp, proc, from, to, cat, extver, patxname, enumlist)\ + SDTG_GENERAL2(#var, SDT_NUMX, SL_VAR, type, flags, guiflags | SGF_ENUM, _settings_client.var, 1, def, 0, 0, 0, nullptr, str, strhelp, STR_NULL, proc, nullptr, from, to, cat, extver, patxname, nullptr, enumlist) #define SDTC_BOOL(var, flags, guiflags, def, str, strhelp, strval, proc, from, to, cat, extver, patxname)\ SDTG_GENERAL(#var, SDT_BOOLX, SL_VAR, SLE_BOOL, flags, guiflags, _settings_client.var, 1, def, 0, 1, 0, nullptr, str, strhelp, strval, proc, from, to, cat, extver, patxname) @@ -139,7 +148,7 @@ static size_t ConvertLandscape(const char *value); SDTG_GENERAL(#var, SDT_ONEOFMANY, SL_VAR, type, flags, guiflags, _settings_client.var, 1, def, 0, max, 0, full, str, strhelp, strval, proc, from, to, cat, extver, patxname) #define SDT_XREF(from, to, extver, xref)\ - {{"", nullptr, {0}, {0}, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE}, SLE_CONDNULL_X(0, from, to, extver), nullptr, xref, nullptr} + {{"", nullptr, {0}, {0}, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE, nullptr}, SLE_CONDNULL_X(0, from, to, extver), nullptr, xref, nullptr} -#define SDT_END() {{nullptr, nullptr, {0}, {0}, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE}, SLE_END(), nullptr, nullptr, nullptr} +#define SDT_END() {{nullptr, nullptr, {0}, {0}, 0, 0, 0, nullptr, STR_NULL, STR_NULL, STR_NULL, nullptr, nullptr, SC_NONE, nullptr}, SLE_END(), nullptr, nullptr, nullptr} diff --git a/src/table/settings.ini b/src/table/settings.ini index 56e1a0b2d1..0a8123dbd9 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -81,6 +81,7 @@ const SettingDesc _settings[] = { [templates] SDTG_BOOL = SDTG_BOOL($name, $flags, $guiflags, $var, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, $patxname), SDTG_VAR = SDTG_VAR($name, $type, $flags, $guiflags, $var, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, $patxname, $orderproc), +SDTG_ENUM = SDTG_ENUM($name, $type, $flags, $guiflags, $var, $def, $str, $strhelp, $proc, $from, $to, $cat, $extver, $patxname, $enumlist), SDTG_OMANY = SDTG_OMANY($name, $type, $flags, $guiflags, $var, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, $patxname), SDTC_BOOL = SDTC_BOOL( $var, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, $patxname), SDTC_LIST = SDTC_LIST( $var, $type, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, $patxname), @@ -91,6 +92,7 @@ SDT_BOOL = SDT_BOOL($base, $var, $flags, $guiflags, $def, SDT_OMANY = SDT_OMANY($base, $var, $type, $flags, $guiflags, $def, $max, $full, $str, $strhelp, $strval, $proc, $from, $to, $load, $cat, $extver, $patxname), SDT_STR = SDT_STR($base, $var, $type, $flags, $guiflags, $def, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, $patxname), SDT_VAR = SDT_VAR($base, $var, $type, $flags, $guiflags, $def, $min, $max, $interval, $str, $strhelp, $strval, $proc, $from, $to, $cat, $extver, $patxname, $orderproc), +SDT_ENUM = SDT_ENUM($base, $var, $type, $flags, $guiflags, $def, $str, $strhelp, $proc, $from, $to, $cat, $extver, $patxname, $enumlist), SDT_NULL = SDT_NULL($length, $from, $to, $extver), SDT_XREF = SDT_XREF( $from, $to, $extver, $xref), SDT_END = SDT_END() @@ -111,6 +113,7 @@ extver = SlXvFeatureTest() patxname = nullptr xref = orderproc = nullptr +enumlist =