From f0904157c50f35440bd33dd528220dcc9070a91d Mon Sep 17 00:00:00 2001 From: PatR Date: Mon, 1 Jun 2015 18:18:47 -0700 Subject: [PATCH] tty role selection when filter by options Honor things like OPTIONS:role=!tourist and NETHACKOPTIONS='race=!orc' when performing interactive role selection. I don't think it was completely correct when players let the program choose, but it must have been close enough because we haven't gotten any complaints. The post-3.4.3 interactive selection was ignoring options-base filtering entirely and did get complaints for the pre-beta. Role selection has a ton of code which bloats the program without doing anything useful for actual game play. It ought to be split off into a separate front end. --- include/extern.h | 10 +- include/winprocs.h | 5 +- include/wintype.h | 3 +- src/role.c | 217 ++++++++++++++++++++----------- win/tty/wintty.c | 313 +++++++++++++++++++++++++++++++++++---------- 5 files changed, 397 insertions(+), 151 deletions(-) diff --git a/include/extern.h b/include/extern.h index a4f355a09..67988dcbd 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1433050874 2015/05/31 05:41:14 $ $NHDT-Branch: master $:$NHDT-Revision: 1.499 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1433207912 2015/06/02 01:18:32 $ $NHDT-Branch: master $:$NHDT-Revision: 1.500 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1982,8 +1982,8 @@ E int FDECL(randgend, (int, int)); E int FDECL(randalign, (int, int)); E int FDECL(str2role, (const char *)); E int FDECL(str2race, (const char *)); -E int FDECL(str2gend, (char *)); -E int FDECL(str2align, (char *)); +E int FDECL(str2gend, (const char *)); +E int FDECL(str2align, (const char *)); E boolean FDECL(ok_role, (int, int, int, int)); E int FDECL(pick_role, (int, int, int, int)); E boolean FDECL(ok_race, (int, int, int, int)); @@ -1993,7 +1993,9 @@ E int FDECL(pick_gend, (int, int, int, int)); E boolean FDECL(ok_align, (int, int, int, int)); E int FDECL(pick_align, (int, int, int, int)); E void NDECL(rigid_role_checks); -E boolean FDECL(setrolefilter, (char *)); +E boolean FDECL(setrolefilter, (const char *)); +E boolean NDECL(gotrolefilter); +E void NDECL(clearrolefilter); E char *FDECL(build_plselection_prompt, (char *, int, int, int, int, int)); E char *FDECL(root_plselection_prompt, (char *, int, int, int, int, int)); E void NDECL(plnamesuffix); diff --git a/include/winprocs.h b/include/winprocs.h index 149285e9b..47b71c9ad 100644 --- a/include/winprocs.h +++ b/include/winprocs.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 winprocs.h $NHDT-Date: 1432512776 2015/05/25 00:12:56 $ $NHDT-Branch: master $:$NHDT-Revision: 1.34 $ */ +/* NetHack 3.6 winprocs.h $NHDT-Date: 1433207914 2015/06/02 01:18:34 $ $NHDT-Branch: master $:$NHDT-Revision: 1.35 $ */ /* Copyright (c) David Cohrs, 1992 */ /* NetHack may be freely redistributed. See license for details. */ @@ -289,7 +289,8 @@ struct wc_Opt { #define RS_RACE 2 #define RS_GENDER 3 #define RS_ALGNMNT 4 -#define RS_menu_arg(x) (ROLE_RANDOM - ((x) + 1)) /* 0..4 -> -3..-7 */ +#define RS_filter 5 +#define RS_menu_arg(x) (ROLE_RANDOM - ((x) + 1)) /* 0..5 -> -3..-8 */ /* Choose_windows() may be called multiple times; these constants tell the * init function whether the window system is coming or going. */ diff --git a/include/wintype.h b/include/wintype.h index fdd4ca814..960f65274 100644 --- a/include/wintype.h +++ b/include/wintype.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 wintype.h $NHDT-Date: 1432512782 2015/05/25 00:13:02 $ $NHDT-Branch: master $:$NHDT-Revision: 1.14 $ */ +/* NetHack 3.6 wintype.h $NHDT-Date: 1433207914 2015/06/02 01:18:34 $ $NHDT-Branch: master $:$NHDT-Revision: 1.15 $ */ /* Copyright (c) David Cohrs, 1991 */ /* NetHack may be freely redistributed. See license for details. */ @@ -22,6 +22,7 @@ typedef union any { long *a_lptr; unsigned long *a_ulptr; unsigned *a_uptr; + const char *a_string; /* add types as needed */ } anything; #define ANY_P union any /* avoid typedef in prototypes */ diff --git a/src/role.c b/src/role.c index 117a5f708..d593faabc 100644 --- a/src/role.c +++ b/src/role.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 role.c $NHDT-Date: 1432512766 2015/05/25 00:12:46 $ $NHDT-Branch: master $:$NHDT-Revision: 1.30 $ */ +/* NetHack 3.6 role.c $NHDT-Date: 1433207910 2015/06/02 01:18:30 $ $NHDT-Branch: master $:$NHDT-Revision: 1.31 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985-1999. */ /* NetHack may be freely redistributed. See license for details. */ @@ -614,49 +614,30 @@ const struct Role roles[] = { /* The player's role, created at runtime from initial * choices. This may be munged in role_init(). */ -struct Role urole = { { "Undefined", 0 }, - { { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 }, - { 0, 0 } }, - "L", - "N", - "C", - "Xxx", - "home", - "locate", - NON_PM, - NON_PM, - NON_PM, - NON_PM, - NON_PM, - NON_PM, - NON_PM, - NON_PM, - 0, - 0, - 0, - 0, - /* Str Int Wis Dex Con Cha */ - { 7, 7, 7, 7, 7, 7 }, - { 20, 15, 15, 20, 20, 10 }, - /* Init Lower Higher */ - { 10, 0, 0, 8, 1, 0 }, /* Hit points */ - { 2, 0, 0, 2, 0, 3 }, - 14, /* Energy */ - 0, - 10, - 0, - 0, - 4, - A_INT, - 0, - -3 }; +struct Role urole = { + { "Undefined", 0 }, + { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, + { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, + "L", "N", "C", + "Xxx", "home", "locate", + NON_PM, NON_PM, NON_PM, NON_PM, NON_PM, NON_PM, NON_PM, NON_PM, + 0, 0, 0, 0, + /* Str Int Wis Dex Con Cha */ + { 7, 7, 7, 7, 7, 7 }, + { 20, 15, 15, 20, 20, 10 }, + /* Init Lower Higher */ + { 10, 0, 0, 8, 1, 0 }, /* Hit points */ + { 2, 0, 0, 2, 0, 3 }, + 14, /* Energy */ + 0, + 10, + 0, + 0, + 4, + A_INT, + 0, + -3 +}; /* Table of all races */ const struct Race races[] = { @@ -816,6 +797,7 @@ static struct { short mask; } filter; +STATIC_DCL int NDECL(randrole_filtered); STATIC_DCL char *FDECL(promptsep, (char *, int)); STATIC_DCL int FDECL(role_gendercount, (int)); STATIC_DCL int FDECL(race_alignmentcount, (int)); @@ -836,6 +818,22 @@ randrole() return (rn2(SIZE(roles) - 1)); } +STATIC_OVL int +randrole_filtered() +{ + int i, n = 0, set[SIZE(roles)]; + + /* this doesn't rule out impossible combinations but attempts to + honor all the filter masks */ + for (i = 0; i < SIZE(roles); ++i) + if (ok_role(i, ROLE_NONE, ROLE_NONE, ROLE_NONE) + && ok_race(i, ROLE_RANDOM, ROLE_NONE, ROLE_NONE) + && ok_gend(i, ROLE_NONE, ROLE_RANDOM, ROLE_NONE) + && ok_align(i, ROLE_NONE, ROLE_NONE, ROLE_RANDOM)) + set[n++] = i; + return n ? set[rn2(n)] : randrole(); +} + int str2role(str) const char *str; @@ -973,7 +971,7 @@ int rolenum, racenum; int str2gend(str) -char *str; +const char *str; { int i, len; @@ -1039,7 +1037,7 @@ int rolenum, racenum; int str2align(str) -char *str; +const char *str; { int i, len; @@ -1074,6 +1072,8 @@ int rolenum, racenum, gendnum, alignnum; short allow; if (rolenum >= 0 && rolenum < SIZE(roles) - 1) { + if (filter.roles[rolenum]) + return FALSE; allow = roles[rolenum].allow; if (racenum >= 0 && racenum < SIZE(races) - 1 && !(allow & races[racenum].allow & ROLE_RACEMASK)) @@ -1084,11 +1084,12 @@ int rolenum, racenum, gendnum, alignnum; if (alignnum >= 0 && alignnum < ROLE_ALIGNS && !(allow & aligns[alignnum].allow & ROLE_ALIGNMASK)) return FALSE; - if (filter.roles[rolenum]) - return FALSE; return TRUE; } else { + /* random; check whether any selection is possible */ for (i = 0; i < SIZE(roles) - 1; i++) { + if (filter.roles[i]) + continue; allow = roles[i].allow; if (racenum >= 0 && racenum < SIZE(races) - 1 && !(allow & races[racenum].allow & ROLE_RACEMASK)) @@ -1113,24 +1114,21 @@ pick_role(racenum, gendnum, alignnum, pickhow) int racenum, gendnum, alignnum, pickhow; { int i; - int roles_ok = 0; + int roles_ok = 0, set[SIZE(roles)]; for (i = 0; i < SIZE(roles) - 1; i++) { - if (ok_role(i, racenum, gendnum, alignnum)) - roles_ok++; + if (ok_role(i, racenum, gendnum, alignnum) + && ok_race(i, (racenum >= 0) ? racenum : ROLE_RANDOM, + gendnum, alignnum) + && ok_gend(i, racenum, + (gendnum >= 0) ? gendnum : ROLE_RANDOM, alignnum) + && ok_race(i, racenum, + gendnum, (alignnum >= 0) ? alignnum : ROLE_RANDOM)) + set[roles_ok++] = i; } if (roles_ok == 0 || (roles_ok > 1 && pickhow == PICK_RIGID)) return ROLE_NONE; - roles_ok = rn2(roles_ok); - for (i = 0; i < SIZE(roles) - 1; i++) { - if (ok_role(i, racenum, gendnum, alignnum)) { - if (roles_ok == 0) - return i; - else - roles_ok--; - } - } - return ROLE_NONE; + return set[rn2(roles_ok)]; } /* is racenum compatible with any rolenum/gendnum/alignnum constraints? */ @@ -1142,6 +1140,8 @@ int rolenum, racenum, gendnum, alignnum; short allow; if (racenum >= 0 && racenum < SIZE(races) - 1) { + if (filter.mask & races[racenum].selfmask) + return FALSE; allow = races[racenum].allow; if (rolenum >= 0 && rolenum < SIZE(roles) - 1 && !(allow & roles[rolenum].allow & ROLE_RACEMASK)) @@ -1152,11 +1152,12 @@ int rolenum, racenum, gendnum, alignnum; if (alignnum >= 0 && alignnum < ROLE_ALIGNS && !(allow & aligns[alignnum].allow & ROLE_ALIGNMASK)) return FALSE; - if (filter.mask & races[racenum].selfmask) - return FALSE; return TRUE; } else { + /* random; check whether any selection is possible */ for (i = 0; i < SIZE(races) - 1; i++) { + if (filter.mask & races[i].selfmask) + continue; allow = races[i].allow; if (rolenum >= 0 && rolenum < SIZE(roles) - 1 && !(allow & roles[rolenum].allow & ROLE_RACEMASK)) @@ -1212,6 +1213,8 @@ int alignnum UNUSED; short allow; if (gendnum >= 0 && gendnum < ROLE_GENDERS) { + if (filter.mask & genders[gendnum].allow) + return FALSE; allow = genders[gendnum].allow; if (rolenum >= 0 && rolenum < SIZE(roles) - 1 && !(allow & roles[rolenum].allow & ROLE_GENDMASK)) @@ -1219,11 +1222,12 @@ int alignnum UNUSED; if (racenum >= 0 && racenum < SIZE(races) - 1 && !(allow & races[racenum].allow & ROLE_GENDMASK)) return FALSE; - if (filter.mask & genders[gendnum].allow) - return FALSE; return TRUE; } else { + /* random; check whether any selection is possible */ for (i = 0; i < ROLE_GENDERS; i++) { + if (filter.mask & genders[i].allow) + continue; allow = genders[i].allow; if (rolenum >= 0 && rolenum < SIZE(roles) - 1 && !(allow & roles[rolenum].allow & ROLE_GENDMASK)) @@ -1278,6 +1282,8 @@ int alignnum; short allow; if (alignnum >= 0 && alignnum < ROLE_ALIGNS) { + if (filter.mask & aligns[alignnum].allow) + return FALSE; allow = aligns[alignnum].allow; if (rolenum >= 0 && rolenum < SIZE(roles) - 1 && !(allow & roles[rolenum].allow & ROLE_ALIGNMASK)) @@ -1285,11 +1291,12 @@ int alignnum; if (racenum >= 0 && racenum < SIZE(races) - 1 && !(allow & races[racenum].allow & ROLE_ALIGNMASK)) return FALSE; - if (filter.mask & aligns[alignnum].allow) - return FALSE; return TRUE; } else { + /* random; check whether any selection is possible */ for (i = 0; i < ROLE_ALIGNS; i++) { + if (filter.mask & aligns[i].allow) + return FALSE; allow = aligns[i].allow; if (rolenum >= 0 && rolenum < SIZE(roles) - 1 && !(allow & roles[rolenum].allow & ROLE_ALIGNMASK)) @@ -1352,7 +1359,7 @@ rigid_role_checks() flags.initrole = pick_role(flags.initrace, flags.initgend, flags.initalign, PICK_RANDOM); if (flags.initrole < 0) - flags.initrole = randrole(); + flags.initrole = randrole_filtered(); } if (flags.initrole != ROLE_NONE) { if (flags.initrace == ROLE_NONE) @@ -1369,7 +1376,7 @@ rigid_role_checks() boolean setrolefilter(bufp) -char *bufp; +const char *bufp; { int i; boolean reslt = TRUE; @@ -1387,6 +1394,29 @@ char *bufp; return reslt; } +boolean +gotrolefilter() +{ + int i; + + if (filter.mask) + return TRUE; + for (i = 0; i < SIZE(roles); ++i) + if (filter.roles[i]) + return TRUE; + return FALSE; +} + +void +clearrolefilter() +{ + int i; + + for (i = 0; i < SIZE(roles); ++i) + filter.roles[i] = FALSE; + filter.mask = 0; +} + #define BP_ALIGN 0 #define BP_GEND 1 #define BP_RACE 2 @@ -1401,6 +1431,7 @@ char *buf; int num_post_attribs; { const char *conjuct = "and "; + if (num_post_attribs > 1 && post_attribs < num_post_attribs && post_attribs > 1) Strcat(buf, ","); @@ -1416,6 +1447,7 @@ role_gendercount(rolenum) int rolenum; { int gendcount = 0; + if (validrole(rolenum)) { if (roles[rolenum].allow & ROLE_MALE) ++gendcount; @@ -1432,6 +1464,7 @@ race_alignmentcount(racenum) int racenum; { int aligncount = 0; + if (racenum != ROLE_NONE && racenum != ROLE_RANDOM) { if (races[racenum].allow & ROLE_CHAOTIC) ++aligncount; @@ -1487,8 +1520,8 @@ int buflen, rolenum, racenum, gendnum, alignnum; if (alignnum != ROLE_RANDOM) alignnum = ROLE_NONE; /* if alignment not specified, but race is specified - and only one choice of alignment for that race then - don't include it in the later list */ + and only one choice of alignment for that race then + don't include it in the later list */ if ((((racenum != ROLE_NONE && racenum != ROLE_RANDOM) && ok_race(rolenum, racenum, gendnum, alignnum)) && (aligncount > 1)) @@ -1619,13 +1652,12 @@ int buflen, rolenum, racenum, gendnum, alignnum; Sprintf(buf, "%s", s_suffix(tmpbuf)); /* buf should now be: - * < your lawful female gnomish cavewoman's> || - * || + * + * || + * || * * Now append the post attributes to it */ - num_post_attribs = post_attribs; if (post_attribs) { if (pa[BP_RACE]) { @@ -1811,7 +1843,7 @@ winid where; anything any; char buf[BUFSZ]; const char *what = 0, *constrainer = 0, *forcedvalue = 0; - int f = 0, r, c, g, a, allowmask; + int f = 0, r, c, g, a, i, allowmask; r = flags.initrole; c = flags.initrace; @@ -1822,7 +1854,13 @@ winid where; case RS_ROLE: what = "role"; f = r; - /* nothing contrains role to a single choice */ + for (i = 0; i < SIZE(roles); ++i) + if (i != f && !filter.roles[i]) + break; + if (i == SIZE(roles)) { + constrainer = "filter"; + forcedvalue = "role"; + } break; case RS_RACE: what = "race"; @@ -1835,6 +1873,12 @@ winid where; if (c >= 0) { constrainer = "role"; forcedvalue = races[c].noun; + } else if (f >= 0 + && (allowmask & ~filter.mask) == races[f].selfmask) { + /* if there is only one race choice available due to user + options disallowing others, race menu entry is disabled */ + constrainer = "filter"; + forcedvalue = "race"; } } break; @@ -1851,6 +1895,12 @@ winid where; if (g >= 0) { constrainer = "role"; forcedvalue = genders[g].adj; + } else if (f >= 0 + && (allowmask & ~filter.mask) == genders[f].allow) { + /* if there is only one gender choice available due to user + options disallowing other, gender menu entry is disabled */ + constrainer = "filter"; + forcedvalue = "gender"; } } break; @@ -1880,6 +1930,13 @@ winid where; if (a >= 0) constrainer = "race"; } + if (f >= 0 && !constrainer + && (ROLE_ALIGNMASK & ~filter.mask) == aligns[f].allow) { + /* if there is only one alignment choice available due to user + options disallowing others, algn menu entry is disabled */ + constrainer = "filter"; + forcedvalue = "alignment"; + } if (a >= 0) forcedvalue = aligns[a].adj; break; @@ -1897,6 +1954,10 @@ winid where; Sprintf(buf, "Pick%s %s first", (f >= 0) ? " another" : "", what); add_menu(where, NO_GLYPH, &any, RS_menu_let[which], 0, ATR_NONE, buf, MENU_UNSELECTED); + } else if (which == RS_filter) { + any.a_int = RS_menu_arg(RS_filter); + add_menu(where, NO_GLYPH, &any, '~', 0, ATR_NONE, + "Reset role/race/&c filtering", MENU_UNSELECTED); } else if (which == ROLE_RANDOM) { any.a_int = ROLE_RANDOM; add_menu(where, NO_GLYPH, &any, '*', 0, ATR_NONE, "Random", @@ -1942,7 +2003,7 @@ role_init() /* Try the player letter second */ if ((flags.initrole = str2role(pl_character)) < 0) /* None specified; pick a random role */ - flags.initrole = randrole(); + flags.initrole = randrole_filtered(); } /* We now have a valid role index. Copy the role name back. */ diff --git a/win/tty/wintty.c b/win/tty/wintty.c index e3cebdfb9..9a24a424d 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 wintty.c $NHDT-Date: 1432536533 2015/05/25 06:48:53 $ $NHDT-Branch: master $:$NHDT-Revision: 1.94 $ */ +/* NetHack 3.6 wintty.c $NHDT-Date: 1433207911 2015/06/02 01:18:31 $ $NHDT-Branch: master $:$NHDT-Revision: 1.95 $ */ /* Copyright (c) David Cohrs, 1991 */ /* NetHack may be freely redistributed. See license for details. */ @@ -158,6 +158,11 @@ STATIC_DCL const char *FDECL(compress_str, (const char *)); STATIC_DCL void FDECL(tty_putsym, (winid, int, int, CHAR_P)); STATIC_DCL char *FDECL(copy_of, (const char *)); STATIC_DCL void FDECL(bail, (const char *)); /* __attribute__((noreturn)) */ +STATIC_DCL void FDECL(setup_rolemenu, (winid, BOOLEAN_P, int, int, int)); +STATIC_DCL void FDECL(setup_racemenu, (winid, BOOLEAN_P, int, int, int)); +STATIC_DCL void FDECL(setup_gendmenu, (winid, BOOLEAN_P, int, int, int)); +STATIC_DCL void FDECL(setup_algnmenu, (winid, BOOLEAN_P, int, int, int)); +STATIC_DCL boolean NDECL(reset_role_filtering); /* * A string containing all the default commands -- to add to a list @@ -327,7 +332,7 @@ tty_player_selection() { int i, k, n, choice, nextpick; boolean getconfirmation; - char pick4u = 'n', thisch, lastch = 0; + char pick4u = 'n'; char pbuf[QBUFSZ], plbuf[QBUFSZ]; winid win; anything any; @@ -351,8 +356,8 @@ tty_player_selection() if (ROLE == ROLE_NONE || RACE == ROLE_NONE || GEND == ROLE_NONE || ALGN == ROLE_NONE) { int echoline; - char *prompt = - build_plselection_prompt(pbuf, QBUFSZ, ROLE, RACE, GEND, ALGN); + char *prompt = build_plselection_prompt(pbuf, QBUFSZ, + ROLE, RACE, GEND, ALGN); /* this prompt string ends in "[ynaq]?": y - game picks role,&c then asks player to confirm; @@ -404,36 +409,13 @@ makepicks: } } else { /* Prompt for a role */ - char rolenamebuf[QBUFSZ]; - tty_clear_nhwindow(BASE_WINDOW); role_selection_prolog(RS_ROLE, BASE_WINDOW); win = create_nhwindow(NHW_MENU); start_menu(win); - any = zeroany; /* zero out all bits */ - for (i = 0; roles[i].name.m; i++) { - if (!ok_role(i, RACE, GEND, ALGN)) - continue; - any.a_int = i + 1; /* must be non-zero */ - thisch = lowc(roles[i].name.m[0]); - if (thisch == lastch) - thisch = highc(thisch); - Strcpy(rolenamebuf, roles[i].name.m); - if (roles[i].name.f) { - /* role has distinct name for female (C,P) */ - if (GEND == 1) { - /* female already chosen; replace male name */ - Strcpy(rolenamebuf, roles[i].name.f); - } else if (GEND < 0) { - /* not chosen yet; append slash+female name */ - Strcat(rolenamebuf, "/"); - Strcat(rolenamebuf, roles[i].name.f); - } - } - add_menu(win, NO_GLYPH, &any, thisch, 0, ATR_NONE, - an(rolenamebuf), MENU_UNSELECTED); - lastch = thisch; - } + /* populate the menu with role choices */ + setup_rolemenu(win, TRUE, RACE, GEND, ALGN); + /* add miscellaneous menu entries */ role_menu_extra(ROLE_RANDOM, win); any.a_int = 0; /* separator, not a choice */ add_menu(win, NO_GLYPH, &any, ' ', 0, ATR_NONE, "", @@ -441,6 +423,8 @@ makepicks: role_menu_extra(RS_RACE, win); role_menu_extra(RS_GENDER, win); role_menu_extra(RS_ALGNMNT, win); + if (gotrolefilter()) + role_menu_extra(RS_filter, win); role_menu_extra(ROLE_NONE, win); /* quit */ Strcpy(pbuf, "Pick a role or profession"); end_menu(win, pbuf); @@ -461,6 +445,10 @@ makepicks: } else if (choice == RS_menu_arg(RS_RACE)) { RACE = k = ROLE_NONE; nextpick = RS_RACE; + } else if (choice == RS_menu_arg(RS_filter)) { + ROLE = k = ROLE_NONE; + (void) reset_role_filtering(); + nextpick = RS_ROLE; } else if (choice == ROLE_RANDOM) { k = pick_role(RACE, GEND, ALGN, PICK_RANDOM); if (k < 0) @@ -509,14 +497,9 @@ makepicks: win = create_nhwindow(NHW_MENU); start_menu(win); any = zeroany; /* zero out all bits */ - for (i = 0; races[i].noun; i++) { - if (!ok_race(ROLE, i, GEND, ALGN)) - continue; - any.a_int = i + 1; /* must be non-zero */ - add_menu(win, NO_GLYPH, &any, races[i].noun[0], 0, - ATR_NONE, races[i].noun, - MENU_UNSELECTED); - } + /* populate the menu with role choices */ + setup_racemenu(win, TRUE, ROLE, GEND, ALGN); + /* add miscellaneous menu entries */ role_menu_extra(ROLE_RANDOM, win); any.a_int = 0; /* separator, not a choice */ add_menu(win, NO_GLYPH, &any, ' ', 0, ATR_NONE, "", @@ -524,12 +507,14 @@ makepicks: role_menu_extra(RS_ROLE, win); role_menu_extra(RS_GENDER, win); role_menu_extra(RS_ALGNMNT, win); + if (gotrolefilter()) + role_menu_extra(RS_filter, win); role_menu_extra(ROLE_NONE, win); /* quit */ Strcpy(pbuf, "Pick a race or species"); end_menu(win, pbuf); n = select_menu(win, PICK_ONE, &selected); - choice = - (n == 1) ? selected[0].item.a_int : ROLE_NONE; + choice = (n == 1) ? selected[0].item.a_int + : ROLE_NONE; if (selected) free((genericptr_t) selected), selected = 0; destroy_nhwindow(win); @@ -545,6 +530,12 @@ makepicks: } else if (choice == RS_menu_arg(RS_ROLE)) { ROLE = k = ROLE_NONE; nextpick = RS_ROLE; + } else if (choice == RS_menu_arg(RS_filter)) { + RACE = k = ROLE_NONE; + if (reset_role_filtering()) + nextpick = RS_ROLE; + else + nextpick = RS_RACE; } else if (choice == ROLE_RANDOM) { k = pick_race(ROLE, GEND, ALGN, PICK_RANDOM); if (k < 0) @@ -559,8 +550,8 @@ makepicks: } /* picking race */ if (nextpick == RS_GENDER) { - nextpick = - (ROLE < 0) ? RS_ROLE : (RACE < 0) ? RS_RACE : RS_ALGNMNT; + nextpick = (ROLE < 0) ? RS_ROLE : (RACE < 0) ? RS_RACE + : RS_ALGNMNT; /* Select a gender, if necessary; force compatibility with role/race, try for compatibility with pre-selected alignment. */ @@ -595,14 +586,9 @@ makepicks: win = create_nhwindow(NHW_MENU); start_menu(win); any = zeroany; /* zero out all bits */ - for (i = 0; i < ROLE_GENDERS; i++) { - if (!ok_gend(ROLE, RACE, i, ALGN)) - continue; - any.a_int = i + 1; /* non-zero */ - add_menu(win, NO_GLYPH, &any, genders[i].adj[0], - 0, ATR_NONE, genders[i].adj, - MENU_UNSELECTED); - } + /* populate the menu with gender choices */ + setup_gendmenu(win, TRUE, ROLE, RACE, ALGN); + /* add miscellaneous menu entries */ role_menu_extra(ROLE_RANDOM, win); any.a_int = 0; /* separator, not a choice */ add_menu(win, NO_GLYPH, &any, ' ', 0, ATR_NONE, "", @@ -610,12 +596,14 @@ makepicks: role_menu_extra(RS_ROLE, win); role_menu_extra(RS_RACE, win); role_menu_extra(RS_ALGNMNT, win); + if (gotrolefilter()) + role_menu_extra(RS_filter, win); role_menu_extra(ROLE_NONE, win); /* quit */ Strcpy(pbuf, "Pick a gender or sex"); end_menu(win, pbuf); n = select_menu(win, PICK_ONE, &selected); - choice = - (n == 1) ? selected[0].item.a_int : ROLE_NONE; + choice = (n == 1) ? selected[0].item.a_int + : ROLE_NONE; if (selected) free((genericptr_t) selected), selected = 0; destroy_nhwindow(win); @@ -631,6 +619,12 @@ makepicks: } else if (choice == RS_menu_arg(RS_ROLE)) { ROLE = k = ROLE_NONE; nextpick = RS_ROLE; + } else if (choice == RS_menu_arg(RS_filter)) { + GEND = k = ROLE_NONE; + if (reset_role_filtering()) + nextpick = RS_ROLE; + else + nextpick = RS_GENDER; } else if (choice == ROLE_RANDOM) { k = pick_gend(ROLE, RACE, ALGN, PICK_RANDOM); if (k < 0) @@ -645,8 +639,7 @@ makepicks: } /* picking gender */ if (nextpick == RS_ALGNMNT) { - nextpick = - (ROLE < 0) ? RS_ROLE : (RACE < 0) ? RS_RACE : RS_GENDER; + nextpick = (ROLE < 0) ? RS_ROLE : (RACE < 0) ? RS_RACE : RS_GENDER; /* Select an alignment, if necessary; force compatibility with role/race/gender. */ if (ALGN < 0 || !validalign(ROLE, RACE, ALGN)) { @@ -680,14 +673,7 @@ makepicks: win = create_nhwindow(NHW_MENU); start_menu(win); any = zeroany; /* zero out all bits */ - for (i = 0; i < ROLE_ALIGNS; i++) { - if (!ok_align(ROLE, RACE, GEND, i)) - continue; - any.a_int = i + 1; /* non-zero */ - add_menu(win, NO_GLYPH, &any, aligns[i].adj[0], 0, - ATR_NONE, aligns[i].adj, - MENU_UNSELECTED); - } + setup_algnmenu(win, TRUE, ROLE, RACE, GEND); role_menu_extra(ROLE_RANDOM, win); any.a_int = 0; /* separator, not a choice */ add_menu(win, NO_GLYPH, &any, ' ', 0, ATR_NONE, "", @@ -695,12 +681,14 @@ makepicks: role_menu_extra(RS_ROLE, win); role_menu_extra(RS_RACE, win); role_menu_extra(RS_GENDER, win); + if (gotrolefilter()) + role_menu_extra(RS_filter, win); role_menu_extra(ROLE_NONE, win); /* quit */ Strcpy(pbuf, "Pick an alignment or creed"); end_menu(win, pbuf); n = select_menu(win, PICK_ONE, &selected); - choice = - (n == 1) ? selected[0].item.a_int : ROLE_NONE; + choice = (n == 1) ? selected[0].item.a_int + : ROLE_NONE; if (selected) free((genericptr_t) selected), selected = 0; destroy_nhwindow(win); @@ -716,6 +704,12 @@ makepicks: } else if (choice == RS_menu_arg(RS_ROLE)) { ROLE = k = ROLE_NONE; nextpick = RS_ROLE; + } else if (choice == RS_menu_arg(RS_filter)) { + ALGN = k = ROLE_NONE; + if (reset_role_filtering()) + nextpick = RS_ROLE; + else + nextpick = RS_ALGNMNT; } else if (choice == ROLE_RANDOM) { k = pick_align(ROLE, RACE, GEND, PICK_RANDOM); if (k < 0) @@ -811,11 +805,11 @@ makepicks: /* plnamesuffix() can change any or all of ROLE, RACE, GEND, ALGN; we'll override that and honor only the name */ saveROLE = ROLE, saveRACE = RACE, saveGEND = GEND, - saveALGN = ALGN; + saveALGN = ALGN; *plname = '\0'; plnamesuffix(); /* calls askname() when plname[] is empty */ ROLE = saveROLE, RACE = saveRACE, GEND = saveGEND, - ALGN = saveALGN; + ALGN = saveALGN; } break; /* getconfirmation is still True */ case 2: /* 'n' */ @@ -846,11 +840,198 @@ give_up: return; } +STATIC_OVL boolean +reset_role_filtering() +{ + winid win; + anything any; + int i, n; + menu_item *selected = 0; + + win = create_nhwindow(NHW_MENU); + start_menu(win); + any = zeroany; + + /* no extra blank line preceding this entry; end_menu supplies one */ + add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, + "Unacceptable roles", MENU_UNSELECTED); + setup_rolemenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE); + + add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED); + add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, + "Unacceptable races", MENU_UNSELECTED); + setup_racemenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE); + + add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED); + add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, + "Unacceptable genders", MENU_UNSELECTED); + setup_gendmenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE); + + add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED); + add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, + "Uncceptable alignments", MENU_UNSELECTED); + setup_algnmenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE); + + end_menu(win, "Pick all that apply"); + n = select_menu(win, PICK_ANY, &selected); + + if (n > 0) { + clearrolefilter(); + for (i = 0; i < n; i++) + setrolefilter(selected[i].item.a_string); + + ROLE = RACE = GEND = ALGN = ROLE_NONE; + } + if (selected) + free((genericptr_t) selected), selected = 0; + destroy_nhwindow(win); + return (n > 0) ? TRUE : FALSE; +} + #undef ROLE #undef RACE #undef GEND #undef ALGN +/* add entries a-Archeologist, b-Barbarian, &c to menu being built in 'win' */ +STATIC_OVL void +setup_rolemenu(win, filtering, race, gend, algn) +winid win; +boolean filtering; /* True => exclude filtered roles; False => filter reset */ +int race, gend, algn; /* all ROLE_NONE for !filtering case */ +{ + anything any; + int i; + boolean role_ok; + char thisch, lastch = '\0', rolenamebuf[50]; + + any = zeroany; /* zero out all bits */ + for (i = 0; roles[i].name.m; i++) { + role_ok = ok_role(i, race, gend, algn); + if (filtering && !role_ok) + continue; + if (filtering) + any.a_int = i + 1; + else + any.a_string = roles[i].name.m; + thisch = lowc(*roles[i].name.m); + if (thisch == lastch) + thisch = highc(thisch); + Strcpy(rolenamebuf, roles[i].name.m); + if (roles[i].name.f) { + /* role has distinct name for female (C,P) */ + if (gend == 1) { + /* female already chosen; replace male name */ + Strcpy(rolenamebuf, roles[i].name.f); + } else if (gend < 0) { + /* not chosen yet; append slash+female name */ + Strcat(rolenamebuf, "/"); + Strcat(rolenamebuf, roles[i].name.f); + } + } + /* !filtering implies reset_role_filtering() where we want to + mark this role as preseleted if current filter excludes it */ + add_menu(win, NO_GLYPH, &any, thisch, 0, ATR_NONE, an(rolenamebuf), + (!filtering && !role_ok) ? MENU_SELECTED : MENU_UNSELECTED); + lastch = thisch; + } +} + +STATIC_OVL void +setup_racemenu(win, filtering, role, gend, algn) +winid win; +boolean filtering; +int role, gend, algn; +{ + anything any; + boolean race_ok; + int i; + char this_ch; + + any = zeroany; + for (i = 0; races[i].noun; i++) { + race_ok = ok_race(role, i, gend, algn); + if (filtering && !race_ok) + continue; + if (filtering) + any.a_int = i + 1; + else + any.a_string = races[i].noun; + this_ch = *races[i].noun; + /* filtering: picking race, so choose by first letter, with + capital letter as unseen accelerator; + !filtering: resetting filter rather than picking, choose by + capital letter since lowercase role letters will be present */ + add_menu(win, NO_GLYPH, &any, + filtering ? this_ch : highc(this_ch), + filtering ? highc(this_ch) : 0, + ATR_NONE, races[i].noun, + (!filtering && !race_ok) ? MENU_SELECTED : MENU_UNSELECTED); + } +} + +STATIC_DCL void +setup_gendmenu(win, filtering, role, race, algn) +winid win; +boolean filtering; +int role, race, algn; +{ + anything any; + boolean gend_ok; + int i; + char this_ch; + + any = zeroany; + for (i = 0; i < ROLE_GENDERS; i++) { + gend_ok = ok_gend(role, race, i, algn); + if (filtering && !gend_ok) + continue; + if (filtering) + any.a_int = i + 1; + else + any.a_string = genders[i].adj; + this_ch = *genders[i].adj; + /* (see setup_racemenu for explanation of selector letters + and setup_rolemenu for preselection) */ + add_menu(win, NO_GLYPH, &any, + filtering ? this_ch : highc(this_ch), + filtering ? highc(this_ch) : 0, + ATR_NONE, genders[i].adj, + (!filtering && !gend_ok) ? MENU_SELECTED : MENU_UNSELECTED); + } +} + +STATIC_DCL void +setup_algnmenu(win, filtering, role, race, gend) +winid win; +boolean filtering; +int role, race, gend; +{ + anything any; + boolean algn_ok; + int i; + char this_ch; + + any = zeroany; + for (i = 0; i < ROLE_ALIGNS; i++) { + algn_ok = ok_align(role, race, gend, i); + if (filtering && !algn_ok) + continue; + if (filtering) + any.a_int = i + 1; + else + any.a_string = aligns[i].adj; + this_ch = *aligns[i].adj; + /* (see setup_racemenu for explanation of selector letters + and setup_rolemenu for preselection) */ + add_menu(win, NO_GLYPH, &any, + filtering ? this_ch : highc(this_ch), + filtering ? highc(this_ch) : 0, + ATR_NONE, aligns[i].adj, + (!filtering && !algn_ok) ? MENU_SELECTED : MENU_UNSELECTED); + } +} + /* * plname is filled either by an option (-u Player or -uPlayer) or * explicitly (by being the wizard) or by askname. -- 2.50.1