generic feature_toggle
This adds a generic feature_toggle mechanism to the game. Code that wants to offer two different ways of doing something can add an entry to feature_toggles[] (in decl.c), and create a preprocessor macro for its array index in decl.h. Then the code can test it using if (feature_toggle(FEATURE_NAME)) ..do_this.. else ..do_that.. The player can toggle the alternate code path on using OPTIONS=feature_toggle:feature_name_1 feature_name_2 ... This seems better than creating brand new options for controlling features (ala prayconfirm, which could switch to this single option feature_toggle mechanism as well) My first use of it is to allow toggling of the selectors on the loot menu, which I'm hesitant to just change back because now people are actively using the new selectors and the complaints would be really loud if the interface were to just switch back after they adjusted. The default behaviour is the new behaviour "iob", but with an OPTIONS=feature_toggle:loot_menu_selectors in your config file, it will revert to using "abc" as it did in 3.3.1. I'll add a Guidebook page of "features/behaviour that can be toggled" later. The toggles can only be done in defaults.nh, and are not saved with the game.
This commit is contained in:
@@ -370,6 +370,28 @@ E char *fqn_prefix[PREFIX_COUNT];
|
||||
E char *fqn_prefix_names[PREFIX_COUNT];
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Feature toggles
|
||||
*
|
||||
* The following provide a way to offer two different
|
||||
* behaviours for a game interface feature, and allow
|
||||
* the player to toggle the alternate behaviour on.
|
||||
* The feature_name fields stored in the feature_toggles[]
|
||||
* array can be specified in an OPTIONS=feature_toggle:value1 value2
|
||||
* statement in the config file.
|
||||
*/
|
||||
|
||||
E unsigned long toggled_features;
|
||||
|
||||
E struct features_that_toggle {
|
||||
char *feature_name;
|
||||
unsigned long feature_bit;
|
||||
} feature_toggles[];
|
||||
|
||||
#define TOGGLE_INVALID 0
|
||||
#define TOGGLE_LOOT_MENU_SELECTORS 1 /* index in feature_toogle_list[] */
|
||||
#define LAST_FEATURE_TOGGLE 1
|
||||
|
||||
#undef E
|
||||
|
||||
#endif /* DECL_H */
|
||||
|
||||
@@ -1353,6 +1353,7 @@ E char *FDECL(nh_getenv, (const char *));
|
||||
E void FDECL(set_duplicate_opt_detection, (int));
|
||||
E void FDECL(set_wc_option_mod_status, (unsigned long, int));
|
||||
E void FDECL(set_option_mod_status, (char *, int));
|
||||
E boolean FDECL(feature_toggle, (int));
|
||||
|
||||
/* ### pager.c ### */
|
||||
|
||||
|
||||
15
src/decl.c
15
src/decl.c
@@ -268,6 +268,21 @@ char *fqn_prefix_names[PREFIX_COUNT] = { "hackdir", "leveldir", "savedir",
|
||||
"bonesdir", "datadir", "scoredir",
|
||||
"lockdir", "configdir" };
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The following provide a way to offer two different
|
||||
* behaviours for a game interface feature, and allow
|
||||
* the player to toggle the alternate behaviour on.
|
||||
* The strings in the feature_toggles[] array can be
|
||||
* specified in an OPTIONS=feature_toggle:value1 value2
|
||||
* statement in the config file.
|
||||
*/
|
||||
unsigned long toggled_features = 0L;
|
||||
|
||||
struct features_that_toggle feature_toggles[] = {
|
||||
{(char *)0, 0x00000000L}, /* 0 = TOGGLE_INVALID */
|
||||
{"loot_menu_selectors", 0x00000001L}, /* 1 = TOGGLE_LOOT_MENU_SELECTORS */
|
||||
};
|
||||
|
||||
/* dummy routine used to force linkage */
|
||||
void
|
||||
|
||||
@@ -219,6 +219,7 @@ static struct Comp_Opt
|
||||
MAXDCHARS+1, SET_IN_FILE },
|
||||
{ "effects", "the symbols to use in drawing special effects",
|
||||
MAXECHARS+1, SET_IN_FILE },
|
||||
{ "feature_toggle", "alternate feature behaviour", 79, SET_IN_FILE },
|
||||
{ "font_map", "the font to use in the map window", 40, DISP_IN_GAME }, /*WC*/
|
||||
{ "font_menu", "the font to use in menus", 40, DISP_IN_GAME }, /*WC*/
|
||||
{ "font_message", "the font to use in the message window",
|
||||
@@ -1041,6 +1042,43 @@ boolean tinitial, tfrom_file;
|
||||
return;
|
||||
}
|
||||
|
||||
fullname = "feature_toggle";
|
||||
if (match_optname(opts, fullname, 11, TRUE)) {
|
||||
char buf[BUFSZ];
|
||||
char *feature;
|
||||
boolean matched = FALSE;
|
||||
if (!(op = string_for_opt(opts, FALSE)))
|
||||
return;
|
||||
if (!negated) {
|
||||
boolean has_badfield = FALSE;
|
||||
char badfields[BUFSZ];
|
||||
buf[BUFSZ-1] = '\0';
|
||||
|
||||
Strcpy(badfields, "feature_toggle:");
|
||||
(void)strncpy(buf, op, BUFSZ - 1);
|
||||
(void)mungspaces(buf);
|
||||
feature = strtok(buf, " \n");
|
||||
while(feature && *feature && *feature != ' ') {
|
||||
matched = FALSE;
|
||||
for (num = 1; num <= LAST_FEATURE_TOGGLE; num++) {
|
||||
if (!strcmpi(feature, feature_toggles[num].feature_name)) {
|
||||
toggled_features |= feature_toggles[num].feature_bit;
|
||||
matched = TRUE;
|
||||
}
|
||||
}
|
||||
if (!matched) {
|
||||
if (has_badfield) Strcat(badfields, " ");
|
||||
Strcat(badfields, feature);
|
||||
has_badfield = TRUE;
|
||||
}
|
||||
feature = strtok((char *)0, " \n");
|
||||
}
|
||||
if (has_badfield) badoption(badfields);
|
||||
} else
|
||||
bad_negation(fullname, FALSE);
|
||||
return;
|
||||
}
|
||||
|
||||
fullname = "horsename";
|
||||
if (match_optname(opts, fullname, 5, TRUE)) {
|
||||
if (negated) bad_negation(fullname, FALSE);
|
||||
@@ -3249,6 +3287,16 @@ char *op;
|
||||
return 1;
|
||||
}
|
||||
|
||||
boolean
|
||||
feature_toggle(ftidx)
|
||||
int ftidx;
|
||||
{
|
||||
if (ftidx > 0 && ftidx <= LAST_FEATURE_TOGGLE) {
|
||||
if (toggled_features & feature_toggles[ftidx].feature_bit)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
#endif /* OPTION_LISTS_ONLY */
|
||||
|
||||
/*options.c*/
|
||||
|
||||
@@ -2197,18 +2197,21 @@ struct obj *obj;
|
||||
menu_item *pick_list;
|
||||
char buf[BUFSZ];
|
||||
int n;
|
||||
char oldmenu[3] = {'a', 'b', 'c'}, newmenu[3] = {'o', 'i', 'b'};
|
||||
char *menuselector = newmenu;
|
||||
|
||||
if (feature_toggle(TOGGLE_LOOT_MENU_SELECTORS)) menuselector = oldmenu;
|
||||
any.a_void = 0;
|
||||
win = create_nhwindow(NHW_MENU);
|
||||
start_menu(win);
|
||||
any.a_int = 1;
|
||||
Sprintf(buf,"Take %s out of %s", something, the(xname(obj)));
|
||||
add_menu(win, NO_GLYPH, &any, 'o', 0, ATR_NONE, buf, MENU_UNSELECTED);
|
||||
add_menu(win, NO_GLYPH, &any, *menuselector++, 0, ATR_NONE, buf, MENU_UNSELECTED);
|
||||
any.a_int = 2;
|
||||
Sprintf(buf,"Put %s into %s", something, the(xname(obj)));
|
||||
add_menu(win, NO_GLYPH, &any, 'i', 0, ATR_NONE, buf, MENU_UNSELECTED);
|
||||
add_menu(win, NO_GLYPH, &any, *menuselector++, 0, ATR_NONE, buf, MENU_UNSELECTED);
|
||||
any.a_int = 3;
|
||||
add_menu(win, NO_GLYPH, &any, 'b', 0, ATR_NONE,
|
||||
add_menu(win, NO_GLYPH, &any, *menuselector, 0, ATR_NONE,
|
||||
"Both of the above", MENU_UNSELECTED);
|
||||
end_menu(win, prompt);
|
||||
n = select_menu(win, PICK_ONE, &pick_list);
|
||||
|
||||
Reference in New Issue
Block a user