diff --git a/include/extern.h b/include/extern.h index 8a88f5abc..3e446f7a6 100644 --- a/include/extern.h +++ b/include/extern.h @@ -2417,6 +2417,8 @@ extern void role_init(void); extern const char *Hello(struct monst *); extern const char *Goodbye(void); extern const struct Race *character_race(short); +extern void genl_player_selection(void); +extern int genl_player_setup(void); /* ### rumors.c ### */ diff --git a/include/wincurs.h b/include/wincurs.h index 363fa4d77..88b4da29a 100644 --- a/include/wincurs.h +++ b/include/wincurs.h @@ -154,7 +154,7 @@ extern int curses_read_char(void); extern void curses_toggle_color_attr(WINDOW *win, int color, int attr, int onoff); extern void curses_menu_color_attr(WINDOW *, int, int, int); -extern void curses_bail(const char *mesg); +ATTRNORETURN extern void curses_bail(const char *mesg) NORETURN; extern winid curses_get_wid(int type); extern char *curses_copy_of(const char *s); extern int curses_num_lines(const char *str, int width); diff --git a/src/role.c b/src/role.c index e1d6a183e..188125e3c 100644 --- a/src/role.c +++ b/src/role.c @@ -2095,4 +2095,846 @@ character_race(short pmindex) return (const struct Race *) NULL; } +/*--------------------------------------------------------------------------*/ + +/* potential interface routine */ +void +genl_player_selection(void) +{ + if (genl_player_setup()) + return; + + /* player cancelled role/race/&c selection, so quit */ + nh_terminate(EXIT_SUCCESS); + /*NOTREACHED*/ +} + +#if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS) +/* ['#else' far below] */ + +static boolean reset_role_filtering(void); +static winid plsel_startmenu(void); +static void setup_rolemenu(winid, boolean, int, int, int); +static void setup_racemenu(winid, boolean, int, int, int); +static void setup_gendmenu(winid, boolean, int, int, int); +static void setup_algnmenu(winid, boolean, int, int, int); + +/* try to reduce clutter in the code below... */ +#define ROLE flags.initrole +#define RACE flags.initrace +#define GEND flags.initgend +#define ALGN flags.initalign + +/* guts of tty's player_selection() */ +int +genl_player_setup(void) +{ + char pbuf[QBUFSZ]; + anything any; + int i, k, n, choice, nextpick; + boolean getconfirmation, picksomething; + winid win = WIN_ERR; + menu_item *selected = 0; + int clr = 0; + char pick4u = 'n'; + + /* Used to avoid "Is this ok?" if player has already specified all + * four facets of role. + * Note that rigid_role_checks might force any unspecified facets to + * have a specific value, but that will still require confirmation; + * player can specify the forced ones if avoiding that is demanded. + */ + picksomething = (ROLE == ROLE_NONE || RACE == ROLE_NONE + || GEND == ROLE_NONE || ALGN == ROLE_NONE); + /* Used for '-@'; + * choose randomly without asking for all unspecified facets. + */ + if (flags.randomall && picksomething) { + if (ROLE == ROLE_NONE) + ROLE = ROLE_RANDOM; + if (RACE == ROLE_NONE) + RACE = ROLE_RANDOM; + if (GEND == ROLE_NONE) + GEND = ROLE_RANDOM; + if (ALGN == ROLE_NONE) + ALGN = ROLE_RANDOM; + } + + /* prevent unnecessary prompting if role forces race (samurai) or gender + (valkyrie) or alignment (rogue), or race forces alignment (orc), &c */ + rigid_role_checks(); + + if (ROLE == ROLE_NONE || RACE == ROLE_NONE + || GEND == ROLE_NONE || ALGN == ROLE_NONE) { + char *prompt = build_plselection_prompt(pbuf, QBUFSZ, + ROLE, RACE, GEND, ALGN); + /* prompt[] contains "Shall I pick ... for you? [ynaq] " + y - game picks role,&c then asks player to confirm; + n - player manually chooses via menu selections; + a - like 'y', but skips confirmation and starts game; + q - quit + */ +#if 1 + trimspaces(prompt); /* 'prompt' is constructed with trailing space */ + /* accept any character and do validation ourselves so that we can + shorten prompt; it will be "Shall I pick ... for you? [ynaq] " + with final space appended by yn_function() [for tty at least] */ + do { + pick4u = yn_function(prompt, (char *) 0, '\0', FALSE); + pick4u = lowc(pick4u); + if (pick4u == '\033' || pick4u == 'q') /* handle [q] */ + return 0; + if (pick4u == ' ' || pick4u == '\n') /* default is 'y' */ + pick4u = 'y'; + else if (pick4u == '@') /* similar to '-@' on command line */ + pick4u = 'a'; + /* TODO? handle response of '?' */ + } while (pick4u != 'y' && pick4u != 'a' && pick4u != 'n'); /* [yna] */ + +#else /* slightly simpler but more likely to end up being wrapped */ + + char *p; + /* strip choices off prompt string; yn_function() will show them */ + if ((p = strchr(prompt, '[')) != 0) + *p = '\0'; + trimspaces(prompt); /* remove trailing space */ + /* prompt becomes "Shall I pick ... for you? [ynaq] (y) " + with " [ynaq] (y) " appended by yn_function() which also changes + user's and to 'y', to 'q' */ + pick4u = yn_function(prompt, ynaqchars, 'y', FALSE); + if (pick4u != 'y' && pick4u != 'a' && pick4u != 'n') + return 0; /* bail */ +#endif + } + + makepicks: + nextpick = RS_ROLE; + do { + if (nextpick == RS_ROLE) { + nextpick = RS_RACE; + /* Select a role, if necessary; + we'll try to be compatible with pre-selected + race/gender/alignment, but may not succeed. */ + if (ROLE < 0) { + /* process the choice */ + if (pick4u == 'y' || pick4u == 'a' || ROLE == ROLE_RANDOM) { + /* pick a random role */ + k = pick_role(RACE, GEND, ALGN, PICK_RANDOM); + if (k < 0) { + pline("Incompatible role!"); + k = randrole(FALSE); + } + } else { + /* prompt for a role */ + win = plsel_startmenu(); + /* populate the menu with role choices */ + setup_rolemenu(win, TRUE, RACE, GEND, ALGN); + /* add miscellaneous menu entries */ + role_menu_extra(ROLE_RANDOM, win, TRUE); + any = cg.zeroany; /* separator, not a choice */ + add_menu(win, &nul_glyphinfo, &any, 0, 0, + ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); + role_menu_extra(RS_RACE, win, FALSE); + role_menu_extra(RS_GENDER, win, FALSE); + role_menu_extra(RS_ALGNMNT, win, FALSE); + role_menu_extra(RS_filter, win, FALSE); + role_menu_extra(ROLE_NONE, win, FALSE); /* quit */ + Strcpy(pbuf, "Pick a role or profession"); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + /* + * PICK_ONE with preselected choice behaves strangely: + * n == -1 -- , so use quit choice; + * n == 0 -- explicitly chose preselected entry, + * toggling it off, so use it; + * n == 1 -- implicitly chose preselected entry + * with or ; + * n == 2 -- explicitly chose a different entry, so + * both it and preselected one are in list. + */ + if (n > 0) { + choice = selected[0].item.a_int; + if (n > 1 && choice == ROLE_RANDOM) + choice = selected[1].item.a_int; + } else + choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE; + if (selected) + free((genericptr_t) selected), selected = 0; + destroy_nhwindow(win), win = WIN_ERR; + + if (choice == ROLE_NONE) { + return 0; /* selected quit */ + } else if (choice == RS_menu_arg(RS_ALGNMNT)) { + ALGN = k = ROLE_NONE; + nextpick = RS_ALGNMNT; + } else if (choice == RS_menu_arg(RS_GENDER)) { + GEND = k = ROLE_NONE; + nextpick = RS_GENDER; + } 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) + k = randrole(FALSE); + } else { + k = choice - 1; + } + } + ROLE = k; + } /* needed role */ + } /* picking role */ + + if (nextpick == RS_RACE) { + nextpick = (ROLE < 0) ? RS_ROLE : RS_GENDER; + /* Select a race, if necessary; + force compatibility with role, try for compatibility + with pre-selected gender/alignment. */ + if (RACE < 0 || !validrace(ROLE, RACE)) { + /* no race yet, or pre-selected race not valid */ + if (pick4u == 'y' || pick4u == 'a' || RACE == ROLE_RANDOM) { + k = pick_race(ROLE, GEND, ALGN, PICK_RANDOM); + if (k < 0) { + pline("Incompatible race!"); + k = randrace(ROLE); + } + } else { /* pick4u == 'n' */ + /* Count the number of valid races */ + n = 0; /* number valid */ + k = 0; /* valid race */ + for (i = 0; races[i].noun; i++) + if (ok_race(ROLE, i, GEND, ALGN)) { + n++; + k = i; + } + if (n == 0) { + for (i = 0; races[i].noun; i++) + if (validrace(ROLE, i)) { + n++; + k = i; + } + } + /* Permit the user to pick, if there is more than one */ + if (n > 1) { + win = plsel_startmenu(); + any = cg.zeroany; /* zero out all bits */ + /* populate the menu with role choices */ + setup_racemenu(win, TRUE, ROLE, GEND, ALGN); + /* add miscellaneous menu entries */ + role_menu_extra(ROLE_RANDOM, win, TRUE); + any.a_int = 0; /* separator, not a choice */ + add_menu(win, &nul_glyphinfo, &any, 0, 0, + ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); + role_menu_extra(RS_ROLE, win, FALSE); + role_menu_extra(RS_GENDER, win, FALSE); + role_menu_extra(RS_ALGNMNT, win, FALSE); + role_menu_extra(RS_filter, win, FALSE); + role_menu_extra(ROLE_NONE, win, FALSE); /* quit */ + Strcpy(pbuf, "Pick a race or species"); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + if (n > 0) { + choice = selected[0].item.a_int; + if (n > 1 && choice == ROLE_RANDOM) + choice = selected[1].item.a_int; + } else + choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE; + if (selected) + free((genericptr_t) selected), selected = 0; + destroy_nhwindow(win), win = WIN_ERR; + + if (choice == ROLE_NONE) { + return 0; /* selected quit */ + } else if (choice == RS_menu_arg(RS_ALGNMNT)) { + ALGN = k = ROLE_NONE; + nextpick = RS_ALGNMNT; + } else if (choice == RS_menu_arg(RS_GENDER)) { + GEND = k = ROLE_NONE; + nextpick = RS_GENDER; + } 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) + k = randrace(ROLE); + } else { + k = choice - 1; + } + } + } + RACE = k; + } /* needed race */ + } /* picking race */ + + if (nextpick == RS_GENDER) { + 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. */ + if (GEND < 0 || !validgend(ROLE, RACE, GEND)) { + /* no gender yet, or pre-selected gender not valid */ + if (pick4u == 'y' || pick4u == 'a' || GEND == ROLE_RANDOM) { + k = pick_gend(ROLE, RACE, ALGN, PICK_RANDOM); + if (k < 0) { + pline("Incompatible gender!"); + k = randgend(ROLE, RACE); + } + } else { /* pick4u == 'n' */ + /* Count the number of valid genders */ + n = 0; /* number valid */ + k = 0; /* valid gender */ + for (i = 0; i < ROLE_GENDERS; i++) + if (ok_gend(ROLE, RACE, i, ALGN)) { + n++; + k = i; + } + if (n == 0) { + for (i = 0; i < ROLE_GENDERS; i++) + if (validgend(ROLE, RACE, i)) { + n++; + k = i; + } + } + /* Permit the user to pick, if there is more than one */ + if (n > 1) { + win = plsel_startmenu(); + any = cg.zeroany; /* zero out all bits */ + /* populate the menu with gender choices */ + setup_gendmenu(win, TRUE, ROLE, RACE, ALGN); + /* add miscellaneous menu entries */ + role_menu_extra(ROLE_RANDOM, win, TRUE); + any.a_int = 0; /* separator, not a choice */ + add_menu(win, &nul_glyphinfo, &any, 0, 0, + ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); + role_menu_extra(RS_ROLE, win, FALSE); + role_menu_extra(RS_RACE, win, FALSE); + role_menu_extra(RS_ALGNMNT, win, FALSE); + role_menu_extra(RS_filter, win, FALSE); + role_menu_extra(ROLE_NONE, win, FALSE); /* quit */ + Strcpy(pbuf, "Pick a gender or sex"); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + if (n > 0) { + choice = selected[0].item.a_int; + if (n > 1 && choice == ROLE_RANDOM) + choice = selected[1].item.a_int; + } else + choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE; + if (selected) + free((genericptr_t) selected), selected = 0; + destroy_nhwindow(win), win = WIN_ERR; + + if (choice == ROLE_NONE) { + return 0; /* selected quit */ + } else if (choice == RS_menu_arg(RS_ALGNMNT)) { + ALGN = k = ROLE_NONE; + nextpick = RS_ALGNMNT; + } else if (choice == RS_menu_arg(RS_RACE)) { + RACE = k = ROLE_NONE; + nextpick = RS_RACE; + } 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) + k = randgend(ROLE, RACE); + } else { + k = choice - 1; + } + } + } + GEND = k; + } /* needed gender */ + } /* picking gender */ + + if (nextpick == RS_ALGNMNT) { + 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)) { + /* no alignment yet, or pre-selected alignment not valid */ + if (pick4u == 'y' || pick4u == 'a' || ALGN == ROLE_RANDOM) { + k = pick_align(ROLE, RACE, GEND, PICK_RANDOM); + if (k < 0) { + pline("Incompatible alignment!"); + k = randalign(ROLE, RACE); + } + } else { /* pick4u == 'n' */ + /* Count the number of valid alignments */ + n = 0; /* number valid */ + k = 0; /* valid alignment */ + for (i = 0; i < ROLE_ALIGNS; i++) + if (ok_align(ROLE, RACE, GEND, i)) { + n++; + k = i; + } + if (n == 0) { + for (i = 0; i < ROLE_ALIGNS; i++) + if (validalign(ROLE, RACE, i)) { + n++; + k = i; + } + } + /* Permit the user to pick, if there is more than one */ + if (n > 1) { + win = plsel_startmenu(); + any = cg.zeroany; /* zero out all bits */ + setup_algnmenu(win, TRUE, ROLE, RACE, GEND); + role_menu_extra(ROLE_RANDOM, win, TRUE); + any.a_int = 0; /* separator, not a choice */ + add_menu(win, &nul_glyphinfo, &any, 0, 0, + ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); + role_menu_extra(RS_ROLE, win, FALSE); + role_menu_extra(RS_RACE, win, FALSE); + role_menu_extra(RS_GENDER, win, FALSE); + role_menu_extra(RS_filter, win, FALSE); + role_menu_extra(ROLE_NONE, win, FALSE); /* quit */ + Strcpy(pbuf, "Pick an alignment or creed"); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + if (n > 0) { + choice = selected[0].item.a_int; + if (n > 1 && choice == ROLE_RANDOM) + choice = selected[1].item.a_int; + } else + choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE; + if (selected) + free((genericptr_t) selected), selected = 0; + destroy_nhwindow(win), win = WIN_ERR; + + if (choice == ROLE_NONE) { + return 0; /* selected quit */ + } else if (choice == RS_menu_arg(RS_GENDER)) { + GEND = k = ROLE_NONE; + nextpick = RS_GENDER; + } else if (choice == RS_menu_arg(RS_RACE)) { + RACE = k = ROLE_NONE; + nextpick = RS_RACE; + } 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) + k = randalign(ROLE, RACE); + } else { + k = choice - 1; + } + } + } + ALGN = k; + } /* needed alignment */ + } /* picking alignment */ + + } while (ROLE < 0 || RACE < 0 || GEND < 0 || ALGN < 0); + + /* + * Role, race, &c have now been determined; + * ask for confirmation and maybe go back to choose all over again. + * + * Uses ynaq for familiarity, although 'a' is usually a + * superset of 'y' but here is an alternate form of 'n'. + * Menu layout: + * title: Is this ok? [ynaq] + * blank: + * text: $name, $alignment $gender $race $role + * blank: + * menu: y + yes; play + * n - no; pick again + * maybe: a - no; rename hero + * q - quit + * (end) + */ + getconfirmation = (picksomething && pick4u != 'a' && !flags.randomall); + while (getconfirmation) { + win = plsel_startmenu(); + any = cg.zeroany; /* zero out all bits */ +#if 0 + start_menu(win, MENU_BEHAVE_STANDARD); + any.a_int = 0; + char plbuf[QBUFSZ]; + if (!roles[ROLE].name.f + && ((roles[ROLE].allow & ROLE_GENDMASK) + == (ROLE_MALE | ROLE_FEMALE))) + Sprintf(plbuf, " %s", genders[GEND].adj); + else + *plbuf = '\0'; /* omit redundant gender */ + Snprintf(pbuf, sizeof pbuf, "%s, %s%s %s %s", gp.plname, + aligns[ALGN].adj, plbuf, races[RACE].adj, + (GEND == 1 && roles[ROLE].name.f) ? roles[ROLE].name.f + : roles[ROLE].name.m); + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, pbuf, + MENU_ITEMFLAGS_NONE); + /* blank separator */ + any.a_int = 0; + add_menu(win, &nul_glyphinfo, &any, 0, 0, + ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); +#endif + /* [ynaq] menu choices */ + any.a_int = 1; + add_menu(win, &nul_glyphinfo, &any, 'y', 0, + ATR_NONE, clr, "Yes; start game", MENU_ITEMFLAGS_SELECTED); + any.a_int = 2; + add_menu(win, &nul_glyphinfo, &any, 'n', 0, + ATR_NONE, clr, "No; choose role again", MENU_ITEMFLAGS_NONE); + if (iflags.renameallowed) { + any.a_int = 3; + add_menu(win, &nul_glyphinfo, &any, 'a', 0, ATR_NONE, + clr, "Not yet; choose another name", MENU_ITEMFLAGS_NONE); + } + any.a_int = -1; + add_menu(win, &nul_glyphinfo, &any, 'q', 0, + ATR_NONE, clr, "Quit", MENU_ITEMFLAGS_NONE); + Sprintf(pbuf, "Is this ok? [yn%sq]", iflags.renameallowed ? "a" : ""); + end_menu(win, pbuf); + n = select_menu(win, PICK_ONE, &selected); + /* [pick-one menus with a preselected entry behave oddly...] */ + choice = (n > 0) ? selected[n - 1].item.a_int : (n == 0) ? 1 : -1; + if (selected) + free((genericptr_t) selected), selected = 0; + destroy_nhwindow(win); + + switch (choice) { + default: /* 'q' or ESC */ + return 0; /* quit */ + break; + case 3: { /* 'a' */ + /* + * TODO: what, if anything, should be done if the name is + * changed to or from "wizard" after port-specific startup + * code has set flags.debug based on the original name? + */ + int saveROLE, saveRACE, saveGEND, saveALGN; + + iflags.renameinprogress = TRUE; + /* 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; + *gp.plname = '\0'; + plnamesuffix(); /* calls askname() when gp.plname[] is empty */ + ROLE = saveROLE, RACE = saveRACE, GEND = saveGEND, ALGN = saveALGN; + break; /* getconfirmation is still True */ + } + case 2: /* 'n' */ + /* start fresh, but bypass "shall I pick everything for you?" + step; any partial role selection via config file, command + line, or name suffix is discarded this time */ + pick4u = 'n'; + ROLE = RACE = GEND = ALGN = ROLE_NONE; + goto makepicks; + break; + case 1: /* 'y' or Space or Return/Enter */ + /* success; drop out through end of function */ + getconfirmation = FALSE; + break; + } + } + + /* Success! */ + return 1; +} + +static boolean +reset_role_filtering(void) +{ + winid win; + anything any; + int i, n, clr = 0; + char filterprompt[QBUFSZ]; + menu_item *selected = 0; + + win = create_nhwindow(NHW_MENU); + start_menu(win, MENU_BEHAVE_STANDARD); + any = cg.zeroany; + + /* no extra blank line preceding this entry; end_menu supplies one */ + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, + "Unacceptable roles", MENU_ITEMFLAGS_NONE); + setup_rolemenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE); + + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, + clr, "", MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, + clr, "Unacceptable races", MENU_ITEMFLAGS_NONE); + setup_racemenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE); + + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, + clr, "", MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, + clr, "Unacceptable genders", MENU_ITEMFLAGS_NONE); + setup_gendmenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE); + + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, + clr, "", MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, + clr, "Unacceptable alignments", MENU_ITEMFLAGS_NONE); + setup_algnmenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE); + + Sprintf(filterprompt, "Pick all that apply%s", + gotrolefilter() ? " and/or unpick any that no longer apply" : ""); + end_menu(win, filterprompt); + n = select_menu(win, PICK_ANY, &selected); + + if (n >= 0) { /* n==0: clear current filters and don't set new ones */ + 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; +} + +/* start a menu; show role aspects specified so far as a header line */ +static winid +plsel_startmenu(void) +{ + char qbuf[QBUFSZ]; + winid win; + anything any; + const char *rolename; + int clr = 0; + + /* whatever aspect was just chosen might force others (Orc => chaotic, + Samurai => Human+lawful, Valkyrie => female) */ + rigid_role_checks(); + + rolename = (ROLE < 0) ? "" + : (GEND == 1 && roles[ROLE].name.f) ? roles[ROLE].name.f + : roles[ROLE].name.m; + if (!*gp.plname || ROLE < 0 || RACE < 0 || GEND < 0 || ALGN < 0) { + /* " " */ + Sprintf(qbuf, "%.20s %.20s %.20s %.20s", + rolename, + (RACE < 0) ? "" : races[RACE].noun, + (GEND < 0) ? "" : genders[GEND].adj, + (ALGN < 0) ? "" : aligns[ALGN].adj); + } else { + /* " the " */ + Sprintf(qbuf, "%.20s the %.20s %.20s %.20s %.20s", + gp.plname, + aligns[ALGN].adj, + genders[GEND].adj, + races[RACE].adj, + rolename); + } + + win = create_nhwindow(NHW_MENU); + if (win == WIN_ERR) + panic("could not create role selection window"); + start_menu(win, MENU_BEHAVE_STANDARD); + + any = cg.zeroany; + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, + qbuf, MENU_ITEMFLAGS_NONE); + add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, + "", MENU_ITEMFLAGS_NONE); + return win; +} + +#undef ROLE +#undef RACE +#undef GEND +#undef ALGN + +/* add entries a-Archeologist, b-Barbarian, &c to menu being built in 'win' */ +static void +setup_rolemenu( + winid win, + boolean filtering, /* True => exclude filtered roles; + * False => filter reset */ + int race, int gend, int algn) /* all ROLE_NONE for !filtering case */ +{ + anything any; + int i; + boolean role_ok; + char thisch, lastch = '\0', rolenamebuf[50]; + int clr = 0; + + any = cg.zeroany; /* zero out all bits */ + for (i = 0; roles[i].name.m; i++) { + /* role can be constrained by any of race, gender, or alignment */ + role_ok = (ok_role(i, race, gend, algn) + && ok_race(i, race, gend, algn) + && ok_gend(i, race, gend, algn) + && ok_align(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, &nul_glyphinfo, &any, thisch, 0, + ATR_NONE, clr, an(rolenamebuf), + (!filtering && !role_ok) + ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE); + lastch = thisch; + } +} + +static void +setup_racemenu( + winid win, + boolean filtering, + int role, int gend, int algn) +{ + anything any; + boolean race_ok; + int i; + char this_ch; + int clr = 0; + + any = cg.zeroany; + for (i = 0; races[i].noun; i++) { + /* no ok_gend(); race isn't constrained by gender */ + race_ok = (ok_race(role, i, gend, algn) + && ok_role(role, i, gend, algn) + && ok_align(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, &nul_glyphinfo, &any, + filtering ? this_ch : highc(this_ch), + filtering ? highc(this_ch) : 0, + ATR_NONE, clr, races[i].noun, + (!filtering && !race_ok) + ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE); + } +} + +static void +setup_gendmenu( + winid win, + boolean filtering, + int role, int race, int algn) +{ + anything any; + boolean gend_ok; + int i; + char this_ch; + int clr = 0; + + any = cg.zeroany; + for (i = 0; i < ROLE_GENDERS; i++) { + /* no ok_align(); gender isn't constrained by alignment */ + gend_ok = (ok_gend(role, race, i, algn) + && ok_role(role, race, i, algn) + && ok_race(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, &nul_glyphinfo, &any, + filtering ? this_ch : highc(this_ch), + filtering ? highc(this_ch) : 0, + ATR_NONE, clr, genders[i].adj, + (!filtering && !gend_ok) + ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE); + } +} + +static void +setup_algnmenu( + winid win, + boolean filtering, + int role, int race, int gend) +{ + anything any; + boolean algn_ok; + int i; + char this_ch; + int clr = 0; + + any = cg.zeroany; + for (i = 0; i < ROLE_ALIGNS; i++) { + /* no ok_gend(); alignment isn't constrained by gender */ + algn_ok = (ok_align(role, race, gend, i) + && ok_role(role, race, gend, i) + && ok_race(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, &nul_glyphinfo, &any, + filtering ? this_ch : highc(this_ch), + filtering ? highc(this_ch) : 0, + ATR_NONE, clr, aligns[i].adj, + (!filtering && !algn_ok) + ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE); + } +} + +#else /* !TTY_GRAPHICS */ + +int +genl_player_setup(void) +{ + return 0; +} + +#endif /* ?TTY_GRAPHICS */ + /* role.c */ diff --git a/win/curses/cursinit.c b/win/curses/cursinit.c index b4a4351b0..26f0b9ecf 100644 --- a/win/curses/cursinit.c +++ b/win/curses/cursinit.c @@ -365,6 +365,8 @@ curses_init_nhcolors(void) #endif } +#if 0 /* curses_choose_character + curses_character_dialog no longer used */ + /* Allow player to pick character's role, race, gender, and alignment. Borrowed from the Gnome window port. */ void @@ -675,6 +677,7 @@ curses_choose_character(void) flags.initalign = sel; } } + return; } /* Prompt user for character race, role, alignment, or gender */ @@ -731,6 +734,8 @@ curses_character_dialog(const char **choices, const char *prompt) return ret; } +#endif /* 0 */ + /* Initialize and display options appropriately */ void curses_init_options(void) diff --git a/win/curses/cursmain.c b/win/curses/cursmain.c index 3fb243bc8..ab018c711 100644 --- a/win/curses/cursmain.c +++ b/win/curses/cursmain.c @@ -270,14 +270,21 @@ curses_init_nhwindows(int *argcp UNUSED, curses_display_splash_window(); } -/* Do a window-port specific player type selection. If player_selection() - offers a Quit option, it is its responsibility to clean up and terminate - the process. You need to fill in pl_character[0]. -*/ +/* Use the general role/race/&c selection originally implemented for tty. */ void curses_player_selection(void) { +#if 1 + if (genl_player_setup()) + return; /* success */ + + /* quit/cancel */ + curses_bail((const char *) NULL); + /*NOTREACHED*/ +#else + /* still present cursinit.c but no longer used */ curses_choose_character(); +#endif } diff --git a/win/tty/wintty.c b/win/tty/wintty.c index b54fee8bb..fb0846cf6 100644 --- a/win/tty/wintty.c +++ b/win/tty/wintty.c @@ -229,12 +229,9 @@ static void process_menu_window(winid, struct WinDesc *); static void process_text_window(winid, struct WinDesc *); static tty_menu_item *reverse(tty_menu_item *); static const char *compress_str(const char *); +#ifndef STATUS_HILITES static void tty_putsym(winid, int, int, char); -static void setup_rolemenu(winid, boolean, int, int, int); -static void setup_racemenu(winid, boolean, int, int, int); -static void setup_gendmenu(winid, boolean, int, int, int); -static void setup_algnmenu(winid, boolean, int, int, int); -static boolean reset_role_filtering(void); +#endif #ifdef STATUS_HILITES #define MAX_STATUS_ROWS 3 static boolean check_fields(boolean forcefields, int sz[MAX_STATUS_ROWS]); @@ -575,772 +572,14 @@ tty_preference_update(const char *pref) return; } -/* try to reduce clutter in the code below... */ -#define ROLE flags.initrole -#define RACE flags.initrace -#define GEND flags.initgend -#define ALGN flags.initalign - void tty_player_selection(void) { - int i, k, n, choice, nextpick; - boolean getconfirmation, picksomething; - char pick4u = 'n'; - char pbuf[QBUFSZ], plbuf[QBUFSZ]; - winid win; - anything any; - menu_item *selected = 0; - int clr = 0; + if (genl_player_setup()) + return; - /* Used to avoid "Is this ok?" if player has already specified all - * four facets of role. - * Note that rigid_role_checks might force any unspecified facets to - * have a specific value, but that will still require confirmation; - * player can specify the forced ones if avoiding that is demanded. - */ - picksomething = (ROLE == ROLE_NONE || RACE == ROLE_NONE - || GEND == ROLE_NONE || ALGN == ROLE_NONE); - /* Used for '-@'; - * choose randomly without asking for all unspecified facets. - */ - if (flags.randomall && picksomething) { - if (ROLE == ROLE_NONE) - ROLE = ROLE_RANDOM; - if (RACE == ROLE_NONE) - RACE = ROLE_RANDOM; - if (GEND == ROLE_NONE) - GEND = ROLE_RANDOM; - if (ALGN == ROLE_NONE) - ALGN = ROLE_RANDOM; - } - - /* prevent unnecessary prompting if role forces race (samurai) or gender - (valkyrie) or alignment (rogue), or race forces alignment (orc), &c */ - rigid_role_checks(); - - /* Should we randomly pick for the player? */ - 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); - - /* this prompt string ends in "[ynaq]?": - y - game picks role,&c then asks player to confirm; - n - player manually chooses via menu selections; - a - like 'y', but skips confirmation and starts game; - q - quit - */ - tty_putstr(BASE_WINDOW, 0, ""); - echoline = wins[BASE_WINDOW]->cury; - tty_putstr(BASE_WINDOW, 0, prompt); - do { - pick4u = lowc(readchar()); - if (strchr(quitchars, pick4u)) - pick4u = 'y'; - } while (!strchr(ynaqchars, pick4u)); - if ((int) strlen(prompt) + 1 < CO) { - /* Echo choice and move back down line */ - tty_putsym(BASE_WINDOW, (int) strlen(prompt) + 1, echoline, - pick4u); - tty_putstr(BASE_WINDOW, 0, ""); - } else - /* Otherwise it's hard to tell where to echo, and things are - * wrapping a bit messily anyway, so (try to) make sure the next - * question shows up well and doesn't get wrapped at the - * bottom of the window. - */ - tty_clear_nhwindow(BASE_WINDOW); - - if (pick4u != 'y' && pick4u != 'a' && pick4u != 'n') - goto give_up; - } - - makepicks: - nextpick = RS_ROLE; - do { - if (nextpick == RS_ROLE) { - nextpick = RS_RACE; - /* Select a role, if necessary; - we'll try to be compatible with pre-selected - race/gender/alignment, but may not succeed. */ - if (ROLE < 0) { - /* Process the choice */ - if (pick4u == 'y' || pick4u == 'a' || ROLE == ROLE_RANDOM) { - /* Pick a random role */ - k = pick_role(RACE, GEND, ALGN, PICK_RANDOM); - if (k < 0) { - tty_putstr(BASE_WINDOW, 0, "Incompatible role!"); - k = randrole(FALSE); - } - } else { - /* Prompt for a role */ - tty_clear_nhwindow(BASE_WINDOW); - role_selection_prolog(RS_ROLE, BASE_WINDOW); - win = create_nhwindow(NHW_MENU); - start_menu(win, MENU_BEHAVE_STANDARD); - /* populate the menu with role choices */ - setup_rolemenu(win, TRUE, RACE, GEND, ALGN); - /* add miscellaneous menu entries */ - role_menu_extra(ROLE_RANDOM, win, TRUE); - any = cg.zeroany; /* separator, not a choice */ - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); - role_menu_extra(RS_RACE, win, FALSE); - role_menu_extra(RS_GENDER, win, FALSE); - role_menu_extra(RS_ALGNMNT, win, FALSE); - role_menu_extra(RS_filter, win, FALSE); - role_menu_extra(ROLE_NONE, win, FALSE); /* quit */ - Strcpy(pbuf, "Pick a role or profession"); - end_menu(win, pbuf); - n = select_menu(win, PICK_ONE, &selected); - /* - * PICK_ONE with preselected choice behaves strangely: - * n == -1 -- , so use quit choice; - * n == 0 -- explicitly chose preselected entry, - * toggling it off, so use it; - * n == 1 -- implicitly chose preselected entry - * with or ; - * n == 2 -- explicitly chose a different entry, so - * both it and preselected one are in list. - */ - if (n > 0) { - choice = selected[0].item.a_int; - if (n > 1 && choice == ROLE_RANDOM) - choice = selected[1].item.a_int; - } else - choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE; - if (selected) - free((genericptr_t) selected), selected = 0; - destroy_nhwindow(win); - - if (choice == ROLE_NONE) { - goto give_up; /* Selected quit */ - } else if (choice == RS_menu_arg(RS_ALGNMNT)) { - ALGN = k = ROLE_NONE; - nextpick = RS_ALGNMNT; - } else if (choice == RS_menu_arg(RS_GENDER)) { - GEND = k = ROLE_NONE; - nextpick = RS_GENDER; - } 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) - k = randrole(FALSE); - } else { - k = choice - 1; - } - } - ROLE = k; - } /* needed role */ - } /* picking role */ - - if (nextpick == RS_RACE) { - nextpick = (ROLE < 0) ? RS_ROLE : RS_GENDER; - /* Select a race, if necessary; - force compatibility with role, try for compatibility - with pre-selected gender/alignment. */ - if (RACE < 0 || !validrace(ROLE, RACE)) { - /* no race yet, or pre-selected race not valid */ - if (pick4u == 'y' || pick4u == 'a' || RACE == ROLE_RANDOM) { - k = pick_race(ROLE, GEND, ALGN, PICK_RANDOM); - if (k < 0) { - tty_putstr(BASE_WINDOW, 0, "Incompatible race!"); - k = randrace(ROLE); - } - } else { /* pick4u == 'n' */ - /* Count the number of valid races */ - n = 0; /* number valid */ - k = 0; /* valid race */ - for (i = 0; races[i].noun; i++) - if (ok_race(ROLE, i, GEND, ALGN)) { - n++; - k = i; - } - if (n == 0) { - for (i = 0; races[i].noun; i++) - if (validrace(ROLE, i)) { - n++; - k = i; - } - } - /* Permit the user to pick, if there is more than one */ - if (n > 1) { - tty_clear_nhwindow(BASE_WINDOW); - role_selection_prolog(RS_RACE, BASE_WINDOW); - win = create_nhwindow(NHW_MENU); - start_menu(win, MENU_BEHAVE_STANDARD); - any = cg.zeroany; /* zero out all bits */ - /* populate the menu with role choices */ - setup_racemenu(win, TRUE, ROLE, GEND, ALGN); - /* add miscellaneous menu entries */ - role_menu_extra(ROLE_RANDOM, win, TRUE); - any.a_int = 0; /* separator, not a choice */ - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); - role_menu_extra(RS_ROLE, win, FALSE); - role_menu_extra(RS_GENDER, win, FALSE); - role_menu_extra(RS_ALGNMNT, win, FALSE); - role_menu_extra(RS_filter, win, FALSE); - role_menu_extra(ROLE_NONE, win, FALSE); /* quit */ - Strcpy(pbuf, "Pick a race or species"); - end_menu(win, pbuf); - n = select_menu(win, PICK_ONE, &selected); - if (n > 0) { - choice = selected[0].item.a_int; - if (n > 1 && choice == ROLE_RANDOM) - choice = selected[1].item.a_int; - } else - choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE; - if (selected) - free((genericptr_t) selected), selected = 0; - destroy_nhwindow(win); - - if (choice == ROLE_NONE) { - goto give_up; /* Selected quit */ - } else if (choice == RS_menu_arg(RS_ALGNMNT)) { - ALGN = k = ROLE_NONE; - nextpick = RS_ALGNMNT; - } else if (choice == RS_menu_arg(RS_GENDER)) { - GEND = k = ROLE_NONE; - nextpick = RS_GENDER; - } 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) - k = randrace(ROLE); - } else { - k = choice - 1; - } - } - } - RACE = k; - } /* needed race */ - } /* picking race */ - - if (nextpick == RS_GENDER) { - 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. */ - if (GEND < 0 || !validgend(ROLE, RACE, GEND)) { - /* no gender yet, or pre-selected gender not valid */ - if (pick4u == 'y' || pick4u == 'a' || GEND == ROLE_RANDOM) { - k = pick_gend(ROLE, RACE, ALGN, PICK_RANDOM); - if (k < 0) { - tty_putstr(BASE_WINDOW, 0, "Incompatible gender!"); - k = randgend(ROLE, RACE); - } - } else { /* pick4u == 'n' */ - /* Count the number of valid genders */ - n = 0; /* number valid */ - k = 0; /* valid gender */ - for (i = 0; i < ROLE_GENDERS; i++) - if (ok_gend(ROLE, RACE, i, ALGN)) { - n++; - k = i; - } - if (n == 0) { - for (i = 0; i < ROLE_GENDERS; i++) - if (validgend(ROLE, RACE, i)) { - n++; - k = i; - } - } - /* Permit the user to pick, if there is more than one */ - if (n > 1) { - tty_clear_nhwindow(BASE_WINDOW); - role_selection_prolog(RS_GENDER, BASE_WINDOW); - win = create_nhwindow(NHW_MENU); - start_menu(win, MENU_BEHAVE_STANDARD); - any = cg.zeroany; /* zero out all bits */ - /* populate the menu with gender choices */ - setup_gendmenu(win, TRUE, ROLE, RACE, ALGN); - /* add miscellaneous menu entries */ - role_menu_extra(ROLE_RANDOM, win, TRUE); - any.a_int = 0; /* separator, not a choice */ - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); - role_menu_extra(RS_ROLE, win, FALSE); - role_menu_extra(RS_RACE, win, FALSE); - role_menu_extra(RS_ALGNMNT, win, FALSE); - role_menu_extra(RS_filter, win, FALSE); - role_menu_extra(ROLE_NONE, win, FALSE); /* quit */ - Strcpy(pbuf, "Pick a gender or sex"); - end_menu(win, pbuf); - n = select_menu(win, PICK_ONE, &selected); - if (n > 0) { - choice = selected[0].item.a_int; - if (n > 1 && choice == ROLE_RANDOM) - choice = selected[1].item.a_int; - } else - choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE; - if (selected) - free((genericptr_t) selected), selected = 0; - destroy_nhwindow(win); - - if (choice == ROLE_NONE) { - goto give_up; /* Selected quit */ - } else if (choice == RS_menu_arg(RS_ALGNMNT)) { - ALGN = k = ROLE_NONE; - nextpick = RS_ALGNMNT; - } else if (choice == RS_menu_arg(RS_RACE)) { - RACE = k = ROLE_NONE; - nextpick = RS_RACE; - } 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) - k = randgend(ROLE, RACE); - } else { - k = choice - 1; - } - } - } - GEND = k; - } /* needed gender */ - } /* picking gender */ - - if (nextpick == RS_ALGNMNT) { - 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)) { - /* no alignment yet, or pre-selected alignment not valid */ - if (pick4u == 'y' || pick4u == 'a' || ALGN == ROLE_RANDOM) { - k = pick_align(ROLE, RACE, GEND, PICK_RANDOM); - if (k < 0) { - tty_putstr(BASE_WINDOW, 0, "Incompatible alignment!"); - k = randalign(ROLE, RACE); - } - } else { /* pick4u == 'n' */ - /* Count the number of valid alignments */ - n = 0; /* number valid */ - k = 0; /* valid alignment */ - for (i = 0; i < ROLE_ALIGNS; i++) - if (ok_align(ROLE, RACE, GEND, i)) { - n++; - k = i; - } - if (n == 0) { - for (i = 0; i < ROLE_ALIGNS; i++) - if (validalign(ROLE, RACE, i)) { - n++; - k = i; - } - } - /* Permit the user to pick, if there is more than one */ - if (n > 1) { - tty_clear_nhwindow(BASE_WINDOW); - role_selection_prolog(RS_ALGNMNT, BASE_WINDOW); - win = create_nhwindow(NHW_MENU); - start_menu(win, MENU_BEHAVE_STANDARD); - any = cg.zeroany; /* zero out all bits */ - setup_algnmenu(win, TRUE, ROLE, RACE, GEND); - role_menu_extra(ROLE_RANDOM, win, TRUE); - any.a_int = 0; /* separator, not a choice */ - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); - role_menu_extra(RS_ROLE, win, FALSE); - role_menu_extra(RS_RACE, win, FALSE); - role_menu_extra(RS_GENDER, win, FALSE); - role_menu_extra(RS_filter, win, FALSE); - role_menu_extra(ROLE_NONE, win, FALSE); /* quit */ - Strcpy(pbuf, "Pick an alignment or creed"); - end_menu(win, pbuf); - n = select_menu(win, PICK_ONE, &selected); - if (n > 0) { - choice = selected[0].item.a_int; - if (n > 1 && choice == ROLE_RANDOM) - choice = selected[1].item.a_int; - } else - choice = (n == 0) ? ROLE_RANDOM : ROLE_NONE; - if (selected) - free((genericptr_t) selected), selected = 0; - destroy_nhwindow(win); - - if (choice == ROLE_NONE) { - goto give_up; /* Selected quit */ - } else if (choice == RS_menu_arg(RS_GENDER)) { - GEND = k = ROLE_NONE; - nextpick = RS_GENDER; - } else if (choice == RS_menu_arg(RS_RACE)) { - RACE = k = ROLE_NONE; - nextpick = RS_RACE; - } 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) - k = randalign(ROLE, RACE); - } else { - k = choice - 1; - } - } - } - ALGN = k; - } /* needed alignment */ - } /* picking alignment */ - - } while (ROLE < 0 || RACE < 0 || GEND < 0 || ALGN < 0); - - /* - * Role, race, &c have now been determined; - * ask for confirmation and maybe go back to choose all over again. - * - * Uses ynaq for familiarity, although 'a' is usually a - * superset of 'y' but here is an alternate form of 'n'. - * Menu layout: - * title: Is this ok? [ynaq] - * blank: - * text: $name, $alignment $gender $race $role - * blank: - * menu: y + yes; play - * n - no; pick again - * maybe: a - no; rename hero - * q - quit - * (end) - */ - getconfirmation = (picksomething && pick4u != 'a' && !flags.randomall); - while (getconfirmation) { - tty_clear_nhwindow(BASE_WINDOW); - role_selection_prolog(ROLE_NONE, BASE_WINDOW); - win = create_nhwindow(NHW_MENU); - start_menu(win, MENU_BEHAVE_STANDARD); - any = cg.zeroany; /* zero out all bits */ - any.a_int = 0; - if (!roles[ROLE].name.f - && (roles[ROLE].allow & ROLE_GENDMASK) - == (ROLE_MALE | ROLE_FEMALE)) - Sprintf(plbuf, " %s", genders[GEND].adj); - else - *plbuf = '\0'; /* omit redundant gender */ - Snprintf(pbuf, sizeof(pbuf), "%s, %s%s %s %s", gp.plname, - aligns[ALGN].adj, plbuf, races[RACE].adj, - (GEND == 1 && roles[ROLE].name.f) ? roles[ROLE].name.f - : roles[ROLE].name.m); - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, pbuf, - MENU_ITEMFLAGS_NONE); - /* blank separator */ - any.a_int = 0; - add_menu(win, &nul_glyphinfo, &any, 0, 0, - ATR_NONE, clr, "", MENU_ITEMFLAGS_NONE); - /* [ynaq] menu choices */ - any.a_int = 1; - add_menu(win, &nul_glyphinfo, &any, 'y', 0, - ATR_NONE, clr, "Yes; start game", MENU_ITEMFLAGS_SELECTED); - any.a_int = 2; - add_menu(win, &nul_glyphinfo, &any, 'n', 0, - ATR_NONE, clr, "No; choose role again", MENU_ITEMFLAGS_NONE); - if (iflags.renameallowed) { - any.a_int = 3; - add_menu(win, &nul_glyphinfo, &any, 'a', 0, ATR_NONE, - clr, "Not yet; choose another name", MENU_ITEMFLAGS_NONE); - } - any.a_int = -1; - add_menu(win, &nul_glyphinfo, &any, 'q', 0, - ATR_NONE, clr, "Quit", MENU_ITEMFLAGS_NONE); - Sprintf(pbuf, "Is this ok? [yn%sq]", iflags.renameallowed ? "a" : ""); - end_menu(win, pbuf); - n = select_menu(win, PICK_ONE, &selected); - /* [pick-one menus with a preselected entry behave oddly...] */ - choice = (n > 0) ? selected[n - 1].item.a_int : (n == 0) ? 1 : -1; - if (selected) - free((genericptr_t) selected), selected = 0; - destroy_nhwindow(win); - - switch (choice) { - default: /* 'q' or ESC */ - goto give_up; /* quit */ - break; - case 3: { /* 'a' */ - /* - * TODO: what, if anything, should be done if the name is - * changed to or from "wizard" after port-specific startup - * code has set flags.debug based on the original name? - */ - int saveROLE, saveRACE, saveGEND, saveALGN; - - iflags.renameinprogress = TRUE; - /* 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; - *gp.plname = '\0'; - plnamesuffix(); /* calls askname() when gp.plname[] is empty */ - ROLE = saveROLE, RACE = saveRACE, GEND = saveGEND, - ALGN = saveALGN; - break; /* getconfirmation is still True */ - } - case 2: /* 'n' */ - /* start fresh, but bypass "shall I pick everything for you?" - step; any partial role selection via config file, command - line, or name suffix is discarded this time */ - pick4u = 'n'; - ROLE = RACE = GEND = ALGN = ROLE_NONE; - goto makepicks; - break; - case 1: /* 'y' or Space or Return/Enter */ - /* success; drop out through end of function */ - getconfirmation = FALSE; - break; - } - } - - /* Success! */ - tty_display_nhwindow(BASE_WINDOW, FALSE); - return; - - give_up: - /* Quit */ - if (selected) - free((genericptr_t) selected); /* [obsolete] */ bail((char *) 0); /*NOTREACHED*/ - return; -} - -static boolean -reset_role_filtering(void) -{ - winid win; - anything any; - int i, n, clr = 0; - char filterprompt[QBUFSZ]; - menu_item *selected = 0; - - win = create_nhwindow(NHW_MENU); - start_menu(win, MENU_BEHAVE_STANDARD); - any = cg.zeroany; - - /* no extra blank line preceding this entry; end_menu supplies one */ - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, clr, - "Unacceptable roles", MENU_ITEMFLAGS_NONE); - setup_rolemenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE); - - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "", MENU_ITEMFLAGS_NONE); - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "Unacceptable races", MENU_ITEMFLAGS_NONE); - setup_racemenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE); - - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "", MENU_ITEMFLAGS_NONE); - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "Unacceptable genders", MENU_ITEMFLAGS_NONE); - setup_gendmenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE); - - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "", MENU_ITEMFLAGS_NONE); - add_menu(win, &nul_glyphinfo, &any, 0, 0, ATR_NONE, - clr, "Unacceptable alignments", MENU_ITEMFLAGS_NONE); - setup_algnmenu(win, FALSE, ROLE_NONE, ROLE_NONE, ROLE_NONE); - - Sprintf(filterprompt, "Pick all that apply%s", - gotrolefilter() ? " and/or unpick any that no longer apply" : ""); - end_menu(win, filterprompt); - n = select_menu(win, PICK_ANY, &selected); - - if (n >= 0) { /* n==0: clear current filters and don't set new ones */ - 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 void -setup_rolemenu( - winid win, - boolean filtering, /* True => exclude filtered roles; - * False => filter reset */ - int race, int gend, int algn) /* all ROLE_NONE for !filtering case */ -{ - anything any; - int i; - boolean role_ok; - char thisch, lastch = '\0', rolenamebuf[50]; - int clr = 0; - - any = cg.zeroany; /* zero out all bits */ - for (i = 0; roles[i].name.m; i++) { - /* role can be constrained by any of race, gender, or alignment */ - role_ok = (ok_role(i, race, gend, algn) - && ok_race(i, race, gend, algn) - && ok_gend(i, race, gend, algn) - && ok_align(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, &nul_glyphinfo, &any, thisch, 0, - ATR_NONE, clr, an(rolenamebuf), - (!filtering && !role_ok) - ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE); - lastch = thisch; - } -} - -static void -setup_racemenu(winid win, boolean filtering, int role, int gend, int algn) -{ - anything any; - boolean race_ok; - int i; - char this_ch; - int clr = 0; - - any = cg.zeroany; - for (i = 0; races[i].noun; i++) { - /* no ok_gend(); race isn't constrained by gender */ - race_ok = (ok_race(role, i, gend, algn) - && ok_role(role, i, gend, algn) - && ok_align(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, &nul_glyphinfo, &any, - filtering ? this_ch : highc(this_ch), - filtering ? highc(this_ch) : 0, - ATR_NONE, clr, races[i].noun, - (!filtering && !race_ok) - ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE); - } -} - -static void -setup_gendmenu(winid win, boolean filtering, int role, int race, int algn) -{ - anything any; - boolean gend_ok; - int i; - char this_ch; - int clr = 0; - - any = cg.zeroany; - for (i = 0; i < ROLE_GENDERS; i++) { - /* no ok_align(); gender isn't constrained by alignment */ - gend_ok = (ok_gend(role, race, i, algn) - && ok_role(role, race, i, algn) - && ok_race(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, &nul_glyphinfo, &any, - filtering ? this_ch : highc(this_ch), - filtering ? highc(this_ch) : 0, - ATR_NONE, clr, genders[i].adj, - (!filtering && !gend_ok) - ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE); - } -} - -static void -setup_algnmenu(winid win, boolean filtering, int role, int race, int gend) -{ - anything any; - boolean algn_ok; - int i; - char this_ch; - int clr = 0; - - any = cg.zeroany; - for (i = 0; i < ROLE_ALIGNS; i++) { - /* no ok_gend(); alignment isn't constrained by gender */ - algn_ok = (ok_align(role, race, gend, i) - && ok_role(role, race, gend, i) - && ok_race(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, &nul_glyphinfo, &any, - filtering ? this_ch : highc(this_ch), - filtering ? highc(this_ch) : 0, - ATR_NONE, clr, aligns[i].adj, - (!filtering && !algn_ok) - ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE); - } } /* @@ -2783,6 +2022,7 @@ tty_curs(winid window, ttyDisplay->cury = y; } +#ifndef STATUS_HILITES static void tty_putsym(winid window, int x, int y, char ch) { @@ -2815,6 +2055,7 @@ tty_putsym(winid window, int x, int y, char ch) break; } } +#endif static const char * compress_str(const char *str)