1 | /* SCCS Id: @(#)options.c 3.3 2000/08/01 */
2 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 | /* NetHack may be freely redistributed. See license for details. */
4 |
5 | #ifdef OPTION_LISTS_ONLY /* (AMIGA) external program for opt lists */
6 | #include "config.h"
7 | #include "objclass.h"
8 | #include "flag.h"
9 | NEARDATA struct flag flags; /* provide linkage */
10 | NEARDATA struct instance_flags iflags; /* provide linkage */
11 | #define static
12 | #else
13 | #include "hack.h"
14 | #include "tcap.h"
15 | #include <ctype.h>
16 | #endif
17 |
18 | #define WINTYPELEN 16
19 |
20 | /*
21 | * NOTE: If you add (or delete) an option, please update the short
22 | * options help (option_help()), the long options help (dat/opthelp),
23 | * and the current options setting display function (doset()),
24 | * and also the Guidebooks.
25 | */
26 |
27 | static struct Bool_Opt
28 | {
29 | const char *name;
30 | boolean *addr, initvalue;
31 | } boolopt[] = {
32 | #ifdef AMIGA
33 | {"altmeta", &flags.altmeta, TRUE},
34 | #else
35 | {"altmeta", (boolean *)0, TRUE},
36 | #endif
37 | #ifdef MFLOPPY
38 | {"asksavedisk", &flags.asksavedisk, FALSE},
39 | #else
40 | {"asksavedisk", (boolean *)0, FALSE},
41 | #endif
42 | {"autopickup", &flags.pickup, TRUE},
43 | {"autoquiver", &flags.autoquiver, FALSE},
44 | #if defined(MICRO) && !defined(AMIGA)
45 | {"BIOS", &iflags.BIOS, FALSE},
46 | #else
47 | {"BIOS", (boolean *)0, FALSE},
48 | #endif
49 | #ifdef INSURANCE
50 | {"checkpoint", &flags.ins_chkpt, TRUE},
51 | #else
52 | {"checkpoint", (boolean *)0, FALSE},
53 | #endif
54 | #ifdef MFLOPPY
55 | {"checkspace", &iflags.checkspace, TRUE},
56 | #else
57 | {"checkspace", (boolean *)0, FALSE},
58 | #endif
59 | #ifdef TEXTCOLOR
60 | # ifdef MICRO
61 | {"color", &iflags.use_color, TRUE},
62 | # else /* systems that support multiple terminals, many monochrome */
63 | {"color", &iflags.use_color, FALSE},
64 | # endif
65 | #else
66 | {"color", (boolean *)0, FALSE},
67 | #endif
68 | {"confirm",&flags.confirm, TRUE},
69 | #ifdef TERMLIB
70 | {"DECgraphics", &iflags.DECgraphics, FALSE},
71 | #else
72 | {"DECgraphics", (boolean *)0, FALSE},
73 | #endif
74 | #ifdef TTY_GRAPHICS
75 | {"eight_bit_tty", &iflags.eight_bit_tty, FALSE},
76 | {"extmenu", &iflags.extmenu, FALSE},
77 | #else
78 | {"eight_bit_tty", (boolean *)0, FALSE},
79 | {"extmenu", (boolean *)0, FALSE},
80 | #endif
81 | #ifdef OPT_DISPMAP
82 | {"fast_map", &flags.fast_map, TRUE},
83 | #else
84 | {"fast_map", (boolean *)0, TRUE},
85 | #endif
86 | {"female", &flags.female, FALSE},
87 | {"fixinv", &flags.invlet_constant, TRUE},
88 | #ifdef AMIFLUSH
89 | {"flush", &flags.amiflush, FALSE},
90 | #else
91 | {"flush", (boolean *)0, FALSE},
92 | #endif
93 | {"help", &flags.help, TRUE},
94 | #ifdef TEXTCOLOR
95 | {"hilite_pet", &iflags.hilite_pet, FALSE},
96 | #else
97 | {"hilite_pet", (boolean *)0, FALSE},
98 | #endif
99 | #ifdef ASCIIGRAPH
100 | {"IBMgraphics", &iflags.IBMgraphics, FALSE},
101 | #else
102 | {"IBMgraphics", (boolean *)0, FALSE},
103 | #endif
104 | {"ignintr", &flags.ignintr, FALSE},
105 | #ifdef MAC_GRAPHICS_ENV
106 | {"large_font", &iflags.large_font, FALSE},
107 | #else
108 | {"large_font", (boolean *)0, FALSE},
109 | #endif
110 | {"legacy", &flags.legacy, TRUE},
111 | {"lit_corridor", &flags.lit_corridor, FALSE},
112 | #ifdef MAC_GRAPHICS_ENV
113 | {"Macgraphics", &iflags.MACgraphics, TRUE},
114 | #else
115 | {"Macgraphics", (boolean *)0, FALSE},
116 | #endif
117 | #ifdef MAIL
118 | {"mail", &flags.biff, TRUE},
119 | #else
120 | {"mail", (boolean *)0, TRUE},
121 | #endif
122 | #ifdef NEWS
123 | {"news", &iflags.news, TRUE},
124 | #else
125 | {"news", (boolean *)0, FALSE},
126 | #endif
127 | {"null", &flags.null, TRUE},
128 | {"number_pad", &iflags.num_pad, FALSE},
129 | #ifdef MAC
130 | {"page_wait", &flags.page_wait, TRUE},
131 | #else
132 | {"page_wait", (boolean *)0, FALSE},
133 | #endif
134 | {"perm_invent", &flags.perm_invent, FALSE},
135 | #ifdef MAC
136 | {"popup_dialog", &iflags.popup_dialog, FALSE},
137 | #else
138 | {"popup_dialog", (boolean *)0, FALSE},
139 | #endif
140 | {"prayconfirm", &flags.prayconfirm, TRUE},
141 | #if defined(MSDOS) && defined(USE_TILES)
142 | {"preload_tiles", &iflags.preload_tiles, TRUE},
143 | #else
144 | {"preload_tiles", (boolean *)0, FALSE},
145 | #endif
146 | {"pushweapon", &flags.pushweapon, FALSE},
147 | #if defined(MICRO) && !defined(AMIGA)
148 | {"rawio", &iflags.rawio, FALSE},
149 | #else
150 | {"rawio", (boolean *)0, FALSE},
151 | #endif
152 | {"rest_on_space", &flags.rest_on_space, FALSE},
153 | {"safe_pet", &flags.safe_dog, TRUE},
154 | #ifdef WIZARD
155 | {"sanity_check", &iflags.sanity_check, FALSE},
156 | #else
157 | {"sanity_check", (boolean *)0, FALSE},
158 | #endif
159 | #ifdef EXP_ON_BOTL
160 | {"showexp", &flags.showexp, FALSE},
161 | #else
162 | {"showexp", (boolean *)0, FALSE},
163 | #endif
164 | #ifdef SCORE_ON_BOTL
165 | {"showscore", &flags.showscore, FALSE},
166 | #else
167 | {"showscore", (boolean *)0, FALSE},
168 | #endif
169 | {"silent", &flags.silent, TRUE},
170 | {"sortpack", &flags.sortpack, TRUE},
171 | {"sound", &flags.soundok, TRUE},
172 | {"standout", &flags.standout, FALSE},
173 | {"time", &flags.time, FALSE},
174 | #ifdef TIMED_DELAY
175 | {"timed_delay", &flags.nap, TRUE},
176 | #else
177 | {"timed_delay", (boolean *)0, FALSE},
178 | #endif
179 | {"tombstone",&flags.tombstone, TRUE},
180 | {"toptenwin",&flags.toptenwin, FALSE},
181 | {"verbose", &flags.verbose, TRUE},
182 | {(char *)0, (boolean *)0, FALSE}
183 | };
184 |
185 | /* compound options, for option_help() and external programs like Amiga
186 | * frontend */
187 | #define SET_IN_FILE 0 /* config file option only, not visible in game
188 | * or via program */
189 | #define SET_VIA_PROG 1 /* may be set via extern program, not seen in game */
190 | #define DISP_IN_GAME 2 /* may be set via extern program, displayed in game */
191 | #define SET_IN_GAME 3 /* may be set via extern program or set in the game */
192 | static struct Comp_Opt
193 | {
194 | const char *name, *descr;
195 | int size; /* for frontends and such allocating space --
196 | * usually allowed size of data in game, but
197 | * occasionally maximum reasonable size for
198 | * typing when game maintains information in
199 | * a different format */
200 | int optflags;
201 | } compopt[] = {
202 | { "align", "your starting alignment (lawful, neutral, or chaotic)",
203 | 8, DISP_IN_GAME },
204 | #ifdef MAC
205 | { "background", "the color of the background (black or white)",
206 | 6, SET_IN_FILE },
207 | #endif
208 | { "catname", "the name of your (first) cat (e.g., catname:Tabby)",
209 | PL_PSIZ, DISP_IN_GAME },
210 | { "disclose", "the kinds of information to disclose at end of game",
211 | sizeof(flags.end_disclose),
212 | SET_IN_GAME },
213 | { "dogname", "the name of your (first) dog (e.g., dogname:Fang)",
214 | PL_PSIZ, DISP_IN_GAME },
215 | { "dungeon", "the symbols to use in drawing the dungeon map",
216 | MAXDCHARS+1, SET_IN_FILE },
217 | { "effects", "the symbols to use in drawing special effects",
218 | MAXECHARS+1, SET_IN_FILE },
219 | #ifdef MAC
220 | { "fontmap", "the font to use in the map window", 40, SET_IN_FILE },
221 | { "fontmessage", "the font to use in the message window",
222 | 40, SET_IN_FILE },
223 | { "fonttext", "the font to use in text windows", 40, SET_IN_FILE },
224 | #endif
225 | { "fruit", "the name of a fruit you enjoy eating",
226 | PL_FSIZ, SET_IN_GAME },
227 | { "gender", "your starting gender (male or female)",
228 | 8, DISP_IN_GAME },
229 | { "horsename", "the name of your (first) horse (e.g., horsename:Silver)",
230 | PL_PSIZ, DISP_IN_GAME },
231 | { "menustyle", "user interface for object selection",
232 | MENUTYPELEN, SET_IN_GAME },
233 | { "menu_deselect_all", "deselect all items in a menu", 4, SET_IN_FILE },
234 | { "menu_deselect_page", "deselect all items on this page of a menu",
235 | 4, SET_IN_FILE },
236 | { "menu_first_page", "jump to the first page in a menu",
237 | 4, SET_IN_FILE },
238 | { "menu_invert_all", "invert all items in a menu", 4, SET_IN_FILE },
239 | { "menu_invert_page", "invert all items on this page of a menu",
240 | 4, SET_IN_FILE },
241 | { "menu_last_page", "jump to the last page in a menu", 4, SET_IN_FILE },
242 | { "menu_next_page", "goto the next menu page", 4, SET_IN_FILE },
243 | { "menu_previous_page", "goto the previous menu page", 4, SET_IN_FILE },
244 | { "menu_search", "search for a menu item", 4, SET_IN_FILE },
245 | { "menu_select_all", "select all items in a menu", 4, SET_IN_FILE },
246 | { "menu_select_page", "select all items on this page of a menu",
247 | 4, SET_IN_FILE },
248 | { "monsters", "the symbols to use for monsters",
249 | MAXMCLASSES, SET_IN_FILE },
250 | { "msghistory", "number of top line messages to save",
251 | 5, DISP_IN_GAME },
252 | { "name", "your character's name (e.g., name:Merlin-W)",
253 | PL_NSIZ, DISP_IN_GAME },
254 | { "objects", "the symbols to use for objects",
255 | MAXOCLASSES, SET_IN_FILE },
256 | { "packorder", "the inventory order of the items in your pack",
257 | MAXOCLASSES, SET_IN_GAME },
258 | #ifdef CHANGE_COLOR
259 | { "palette", "palette (00c/880/-fff is blue/yellow/reverse white)",
260 | 15 , SET_IN_GAME },
261 | # if defined(MAC)
262 | { "hicolor", "same as palette, only order is reversed",
263 | 15, SET_IN_FILE },
264 | # endif
265 | #endif
266 | { "pettype", "your preferred initial pet type", 4, DISP_IN_GAME },
267 | { "pickup_burden", "maximum burden picked up before prompt",
268 | 20, SET_IN_GAME },
269 | { "pickup_types", "types of objects to pick up automatically",
270 | MAXOCLASSES, SET_IN_GAME },
271 | { "race", "your starting race (e.g., Human, Elf)",
272 | PL_CSIZ, DISP_IN_GAME },
273 | { "role", "your starting role (e.g., Barbarian, Valkyrie)",
274 | PL_CSIZ, DISP_IN_GAME },
275 | { "scores", "the parts of the score list you wish to see",
276 | 32, SET_IN_GAME },
277 | #ifdef MSDOS
278 | { "soundcard", "type of sound card to use", 20, SET_IN_FILE },
279 | #endif
280 | { "suppress_alert", "suppress alerts about version-specific features",
281 | 8, SET_IN_GAME },
282 | { "traps", "the symbols to use in drawing traps",
283 | MAXTCHARS+1, SET_IN_FILE },
284 | #ifdef MAC
285 | {"use_stone", "use stone background patterns", 8, SET_IN_FILE },
286 | #endif
287 | #ifdef MSDOS
288 | { "video", "method of video updating", 20, SET_IN_FILE },
289 | #endif
290 | #ifdef VIDEOSHADES
291 | { "videocolors", "color mappings for internal screen routines",
292 | 40, DISP_IN_GAME },
293 | { "videoshades", "gray shades to map to black/gray/white",
294 | 32, DISP_IN_GAME },
295 | #endif
296 | #if 0
297 | { "warnlevel", "minimum monster level to trigger warning", 4, SET_IN_GAME },
298 | #endif
299 | { "windowtype", "windowing system to use", WINTYPELEN, DISP_IN_GAME },
300 | { (char *)0, (char *)0, 0 }
301 | };
302 |
303 | #ifdef OPTION_LISTS_ONLY
304 | #undef static
305 |
306 | #else /* use rest of file */
307 |
308 | static boolean need_redraw; /* for doset() */
309 |
310 | #if defined(TOS) && defined(TEXTCOLOR)
311 | extern boolean colors_changed; /* in tos.c */
312 | #endif
313 |
314 | #ifdef VIDEOSHADES
315 | extern char *shade[3]; /* in sys/msdos/video.c */
316 | extern char ttycolors[CLR_MAX]; /* in sys/msdos/video.c */
317 | #endif
318 |
319 | static char def_inv_order[MAXOCLASSES] = {
320 | GOLD_CLASS, AMULET_CLASS, WEAPON_CLASS, ARMOR_CLASS, FOOD_CLASS,
321 | SCROLL_CLASS, SPBOOK_CLASS, POTION_CLASS, RING_CLASS, WAND_CLASS,
322 | TOOL_CLASS, GEM_CLASS, ROCK_CLASS, BALL_CLASS, CHAIN_CLASS, 0,
323 | };
324 |
325 | /*
326 | * Default menu manipulation command accelerators. These may _not_ be:
327 | *
328 | * + a number - reserved for counts
329 | * + an upper or lower case US ASCII letter - used for accelerators
330 | * + ESC - reserved for escaping the menu
331 | * + NULL, CR or LF - reserved for commiting the selection(s). NULL
332 | * is kind of odd, but the tty's xwaitforspace() will return it if
333 | * someone hits a <ret>.
334 | * + a default object class symbol - used for object class accelerators
335 | *
336 | * Standard letters (for now) are:
337 | *
338 | * < back 1 page
339 | * > forward 1 page
340 | * ^ first page
341 | * | last page
342 | * : search
343 | *
344 | * page all
345 | * , select .
346 | * \ deselect -
347 | * ~ invert @
348 | *
349 | * The command name list is duplicated in the compopt array.
350 | */
351 | typedef struct {
352 | const char *name;
353 | char cmd;
354 | } menu_cmd_t;
355 |
356 | #define NUM_MENU_CMDS 11
357 | static const menu_cmd_t default_menu_cmd_info[NUM_MENU_CMDS] = {
358 | /* 0*/ { "menu_first_page", MENU_FIRST_PAGE },
359 | { "menu_last_page", MENU_LAST_PAGE },
360 | { "menu_next_page", MENU_NEXT_PAGE },
361 | { "menu_previous_page", MENU_PREVIOUS_PAGE },
362 | { "menu_select_all", MENU_SELECT_ALL },
363 | /* 5*/ { "menu_deselect_all", MENU_UNSELECT_ALL },
364 | { "menu_invert_all", MENU_INVERT_ALL },
365 | { "menu_select_page", MENU_SELECT_PAGE },
366 | { "menu_deselect_page", MENU_UNSELECT_PAGE },
367 | { "menu_invert_page", MENU_INVERT_PAGE },
368 | /*10*/ { "menu_search", MENU_SEARCH },
369 | };
370 |
371 | /*
372 | * Allow the user to map incoming characters to various menu commands.
373 | * The accelerator list must be a valid C string.
374 | */
375 | #define MAX_MENU_MAPPED_CMDS 32 /* some number */
376 | char mapped_menu_cmds[MAX_MENU_MAPPED_CMDS+1]; /* exported */
377 | static char mapped_menu_op[MAX_MENU_MAPPED_CMDS+1];
378 | static short n_menu_mapped = 0;
379 |
380 |
381 | static boolean initial, from_file;
382 |
383 | STATIC_DCL void FDECL(doset_add_menu, (winid,const char *,int));
384 | STATIC_DCL void FDECL(nmcpy, (char *, const char *, int));
385 | STATIC_DCL void FDECL(escapes, (const char *, char *));
386 | STATIC_DCL int FDECL(boolopt_only_initial, (int));
387 | STATIC_DCL void FDECL(rejectoption, (const char *));
388 | STATIC_DCL void FDECL(badoption, (const char *));
389 | STATIC_DCL char *FDECL(string_for_opt, (char *,BOOLEAN_P));
390 | STATIC_DCL char *FDECL(string_for_env_opt, (const char *, char *,BOOLEAN_P));
391 | STATIC_DCL void FDECL(bad_negation, (const char *,BOOLEAN_P));
392 | STATIC_DCL int FDECL(change_inv_order, (char *));
393 | STATIC_DCL void FDECL(oc_to_str, (char *, char *));
394 | STATIC_DCL void FDECL(graphics_opts, (char *,const char *,int,int));
395 | STATIC_DCL int FDECL(feature_alert_opts, (char *, const char *));
396 | STATIC_DCL const char *FDECL(get_compopt_value, (const char *, char *));
397 | STATIC_DCL boolean FDECL(special_handling, (const char *, BOOLEAN_P, BOOLEAN_P));
398 | STATIC_DCL void FDECL(warning_opts, (char *,const char *));
399 | #if 0
400 | STATIC_DCL int FDECL(warnlevel_opts, (char *, const char *));
401 | #endif
402 |
403 | /* check whether a user-supplied option string is a proper leading
404 | substring of a particular option name; option string might have
405 | a colon or equals sign and arbitrary value appended to it */
406 | boolean
407 | match_optname(user_string, opt_name, min_length, val_allowed)
408 | const char *user_string, *opt_name;
409 | int min_length;
410 | boolean val_allowed;
411 | {
412 | int len = (int)strlen(user_string);
413 |
414 | if (val_allowed) {
415 | const char *p = index(user_string, ':'),
416 | *q = index(user_string, '=');
417 |
418 | if (!p || (q && q < p)) p = q;
419 | while(p && p > user_string && isspace(*(p-1))) p--;
420 | if (p) len = (int)(p - user_string);
421 | }
422 |
423 | return (len >= min_length) && !strncmpi(opt_name, user_string, len);
424 | }
425 |
426 | /* most environment variables will eventually be printed in an error
427 | * message if they don't work, and most error message paths go through
428 | * BUFSZ buffers, which could be overflowed by a maliciously long
429 | * environment variable. if a variable can legitimately be long, or
430 | * if it's put in a smaller buffer, the responsible code will have to
431 | * bounds-check itself.
432 | */
433 | char *
434 | nh_getenv(ev)
435 | const char *ev;
436 | {
437 | char *getev = getenv(ev);
438 |
439 | if (getev && strlen(getev) <= (BUFSZ / 2))
440 | return getev;
441 | else
442 | return (char *)0;
443 | }
444 |
445 | void
446 | initoptions()
447 | {
448 | char *opts;
449 | int i;
450 |
451 | /* initialize the random number generator */
452 | setrandom();
453 |
454 | for (i = 0; boolopt[i].name; i++) {
455 | if (boolopt[i].addr)
456 | *(boolopt[i].addr) = boolopt[i].initvalue;
457 | }
458 | flags.end_own = FALSE;
459 | flags.end_top = 3;
460 | flags.end_around = 2;
461 | iflags.msg_history = 20;
462 |
463 | /* Use negative indices to indicate not yet selected */
464 | flags.initrole = -1;
465 | flags.initrace = -1;
466 | flags.initgend = -1;
467 | flags.initalign = -1;
468 |
469 | /* Set the default monster and object class symbols. Don't use */
470 | /* memcpy() --- sizeof char != sizeof uchar on some machines. */
471 | for (i = 0; i < MAXOCLASSES; i++)
472 | oc_syms[i] = (uchar) def_oc_syms[i];
473 | for (i = 0; i < MAXMCLASSES; i++)
474 | monsyms[i] = (uchar) def_monsyms[i];
475 | for (i = 0; i < WARNCOUNT; i++)
476 | warnsyms[i] = def_warnsyms[i].sym;
477 | flags.warnlevel = 1;
478 | flags.warntype = 0L;
479 |
480 | /* assert( sizeof flags.inv_order == sizeof def_inv_order ); */
481 | (void)memcpy((genericptr_t)flags.inv_order,
482 | (genericptr_t)def_inv_order, sizeof flags.inv_order);
483 | flags.pickup_types[0] = '\0';
484 | flags.pickup_burden = MOD_ENCUMBER;
485 |
486 | switch_graphics(ASCII_GRAPHICS); /* set default characters */
487 | #if defined(UNIX) && defined(TTY_GRAPHICS)
488 | /*
489 | * Set defaults for some options depending on what we can
490 | * detect about the environment's capabilities.
491 | * This has to be done after the global initialization above
492 | * and before reading user-specific initialization via
493 | * config file/environment variable below.
494 | */
495 | /* this detects the IBM-compatible console on most 386 boxes */
496 | if (!strncmp(nh_getenv("TERM"), "AT", 2)) {
497 | switch_graphics(IBM_GRAPHICS);
498 | # ifdef TEXTCOLOR
499 | iflags.use_color = TRUE;
500 | # endif
501 | }
502 | #endif /* UNIX && TTY_GRAPHICS */
503 | #if defined(UNIX) || defined(VMS)
504 | # ifdef TTY_GRAPHICS
505 | /* detect whether a "vt" terminal can handle alternate charsets */
506 | if (!strncmpi(nh_getenv("TERM"), "vt", 2) && (AS && AE) &&
507 | index(AS, '\016') && index(AE, '\017')) {
508 | switch_graphics(DEC_GRAPHICS);
509 | }
510 | # endif
511 | #endif /* UNIX || VMS */
512 |
513 | #ifdef MAC_GRAPHICS_ENV
514 | switch_graphics(MAC_GRAPHICS);
515 | #endif /* MAC_GRAPHICS_ENV */
516 | flags.menu_style = MENU_FULL;
517 |
518 | /* since this is done before init_objects(), do partial init here */
519 | objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD;
520 | nmcpy(pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ);
521 | #ifndef MAC
522 | opts = getenv("NETHACKOPTIONS");
523 | if (!opts) opts = getenv("HACKOPTIONS");
524 | if (opts) {
525 | if (*opts == '/' || *opts == '\\' || *opts == '@') {
526 | if (*opts == '@') opts++; /* @filename */
527 | /* looks like a filename */
528 | if (strlen(opts) < BUFSZ/2)
529 | read_config_file(opts);
530 | } else {
531 | read_config_file((char *)0);
532 | /* let the total length of options be long;
533 | * parseoptions() will check each individually
534 | */
535 | parseoptions(opts, TRUE, FALSE);
536 | }
537 | } else
538 | #endif
539 | read_config_file((char *)0);
540 | #ifdef AMIGA
541 | ami_wbench_init(); /* must be here or can't set fruit */
542 | #endif
543 | (void)fruitadd(pl_fruit);
544 | /* Remove "slime mold" from list of object names; this will */
545 | /* prevent it from being wished unless it's actually present */
546 | /* as a named (or default) fruit. Wishing for "fruit" will */
547 | /* result in the player's preferred fruit [better than "\033"]. */
548 | obj_descr[SLIME_MOLD].oc_name = "fruit";
549 |
550 | return;
551 | }
552 |
553 | STATIC_OVL void
554 | nmcpy(dest, src, maxlen)
555 | char *dest;
556 | const char *src;
557 | int maxlen;
558 | {
559 | int count;
560 |
561 | for(count = 1; count < maxlen; count++) {
562 | if(*src == ',' || *src == '\0') break; /*exit on \0 terminator*/
563 | *dest++ = *src++;
564 | }
565 | *dest = 0;
566 | }
567 |
568 | /*
569 | * escapes: escape expansion for showsyms. C-style escapes understood include
570 | * \n, \b, \t, \r, \xnnn (hex), \onnn (octal), \nnn (decimal). The ^-prefix
571 | * for control characters is also understood, and \[mM] followed by any of the
572 | * previous forms or by a character has the effect of 'meta'-ing the value (so
573 | * that the alternate character set will be enabled).
574 | */
575 | STATIC_OVL void
576 | escapes(cp, tp)
577 | const char *cp;
578 | char *tp;
579 | {
580 | while (*cp)
581 | {
582 | int cval = 0, meta = 0;
583 |
584 | if (*cp == '\\' && index("mM", cp[1])) {
585 | meta = 1;
586 | cp += 2;
587 | }
588 | if (*cp == '\\' && index("0123456789xXoO", cp[1]))
589 | {
590 | const char *dp, *hex = "00112233445566778899aAbBcCdDeEfF";
591 | int dcount = 0;
592 |
593 | cp++;
594 | if (*cp == 'x' || *cp == 'X')
595 | for (++cp; (dp = index(hex, *cp)) && (dcount++ < 2); cp++)
596 | cval = (cval * 16) + (dp - hex) / 2;
597 | else if (*cp == 'o' || *cp == 'O')
598 | for (++cp; (index("01234567",*cp)) && (dcount++ < 3); cp++)
599 | cval = (cval * 8) + (*cp - '0');
600 | else
601 | for (; (index("0123456789",*cp)) && (dcount++ < 3); cp++)
602 | cval = (cval * 10) + (*cp - '0');
603 | }
604 | else if (*cp == '\\') /* C-style character escapes */
605 | {
606 | switch (*++cp)
607 | {
608 | case '\\': cval = '\\'; break;
609 | case 'n': cval = '\n'; break;
610 | case 't': cval = '\t'; break;
611 | case 'b': cval = '\b'; break;
612 | case 'r': cval = '\r'; break;
613 | default: cval = *cp;
614 | }
615 | cp++;
616 | }
617 | else if (*cp == '^') /* expand control-character syntax */
618 | {
619 | cval = (*++cp & 0x1f);
620 | cp++;
621 | }
622 | else
623 | cval = *cp++;
624 | if (meta)
625 | cval |= 0x80;
626 | *tp++ = cval;
627 | }
628 | *tp = '\0';
629 | }
630 |
631 | /* some boolean options can only be set on start-up */
632 | STATIC_OVL int
633 | boolopt_only_initial(i)
634 | int i;
635 | {
636 | return (boolopt[i].addr == &flags.female
637 | || boolopt[i].addr == &flags.legacy
638 | #if defined(MICRO) && !defined(AMIGA)
639 | || boolopt[i].addr == &iflags.rawio
640 | || boolopt[i].addr == &iflags.BIOS
641 | #endif
642 | #if defined(MSDOS) && defined(USE_TILES)
643 | || boolopt[i].addr == &iflags.preload_tiles
644 | #endif
645 | );
646 | }
647 |
648 | STATIC_OVL void
649 | rejectoption(optname)
650 | const char *optname;
651 | {
652 | #ifdef MICRO
653 | # ifdef AMIGA
654 | if(FromWBench){
655 | pline("\"%s\" settable only from %s or in icon.",
656 | optname, configfile);
657 | } else
658 | # endif
659 | pline("\"%s\" settable only from %s.", optname, configfile);
660 | #else
661 | pline("%s can be set only from NETHACKOPTIONS or %s.", optname,
662 | configfile);
663 | #endif
664 | }
665 |
666 | STATIC_OVL void
667 | badoption(opts)
668 | const char *opts;
669 | {
670 | if (!initial) {
671 | if (!strncmp(opts, "h", 1) || !strncmp(opts, "?", 1))
672 | option_help();
673 | else
674 | pline("Bad syntax: %s. Enter \"?g\" for help.", opts);
675 | return;
676 | }
677 | #ifdef MAC
678 | else return;
679 | #endif
680 |
681 | # ifdef AMIGA
682 | if(ami_wbench_badopt(opts)) {
683 | # endif
684 | if(from_file)
685 | raw_printf("Bad syntax in OPTIONS in %s: %s.", configfile, opts);
686 | else
687 | raw_printf("Bad syntax in NETHACKOPTIONS: %s.", opts);
688 | # ifdef AMIGA
689 | }
690 | # endif
691 | wait_synch();
692 | }
693 |
694 | STATIC_OVL char *
695 | string_for_opt(opts, val_optional)
696 | char *opts;
697 | boolean val_optional;
698 | {
699 | char *colon, *equals;
700 |
701 | colon = index(opts, ':');
702 | equals = index(opts, '=');
703 | if (!colon || (equals && equals < colon)) colon = equals;
704 |
705 | if (!colon || !*++colon) {
706 | if (!val_optional) badoption(opts);
707 | return (char *)0;
708 | }
709 | return colon;
710 | }
711 |
712 | STATIC_OVL char *
713 | string_for_env_opt(optname, opts, val_optional)
714 | const char *optname;
715 | char *opts;
716 | boolean val_optional;
717 | {
718 | if(!initial) {
719 | rejectoption(optname);
720 | return (char *)0;
721 | }
722 | return string_for_opt(opts, val_optional);
723 | }
724 |
725 | STATIC_OVL void
726 | bad_negation(optname, with_parameter)
727 | const char *optname;
728 | boolean with_parameter;
729 | {
730 | pline_The("%s option may not %sbe negated.",
731 | optname,
732 | with_parameter ? "both have a value and " : "");
733 | }
734 |
735 | /*
736 | * Change the inventory order, using the given string as the new order.
737 | * Missing characters in the new order are filled in at the end from
738 | * the current inv_order, except for gold, which is forced to be first
739 | * if not explicitly present.
740 | *
741 | * This routine returns 1 unless there is a duplicate or bad char in
742 | * the string.
743 | */
744 | STATIC_OVL int
745 | change_inv_order(op)
746 | char *op;
747 | {
748 | int oc_sym, num;
749 | char *sp, buf[BUFSZ];
750 |
751 | num = 0;
752 | if (!index(op, GOLD_SYM))
753 | buf[num++] = GOLD_CLASS;
754 |
755 | for (sp = op; *sp; sp++) {
756 | oc_sym = def_char_to_objclass(*sp);
757 | /* reject bad or duplicate entries */
758 | if (oc_sym == MAXOCLASSES ||
759 | oc_sym == RANDOM_CLASS || oc_sym == ILLOBJ_CLASS ||
760 | !index(flags.inv_order, oc_sym) || index(sp+1, *sp))
761 | return 0;
762 | /* retain good ones */
763 | buf[num++] = (char) oc_sym;
764 | }
765 | buf[num] = '\0';
766 |
767 | /* fill in any omitted classes, using previous ordering */
768 | for (sp = flags.inv_order; *sp; sp++)
769 | if (!index(buf, *sp)) {
770 | buf[num++] = *sp;
771 | buf[num] = '\0'; /* explicitly terminate for next index() */
772 | }
773 |
774 | Strcpy(flags.inv_order, buf);
775 | return 1;
776 | }
777 |
778 | STATIC_OVL void
779 | graphics_opts(opts, optype, maxlen, offset)
780 | register char *opts;
781 | const char *optype;
782 | int maxlen, offset;
783 | {
784 | uchar translate[MAXPCHARS+1];
785 | int length, i;
786 |
787 | if (!(opts = string_for_env_opt(optype, opts, FALSE)))
788 | return;
789 | escapes(opts, opts);
790 |
791 | length = strlen(opts);
792 | if (length > maxlen) length = maxlen;
793 | /* match the form obtained from PC configuration files */
794 | for (i = 0; i < length; i++)
795 | translate[i] = (uchar) opts[i];
796 | assign_graphics(translate, length, maxlen, offset);
797 | }
798 |
799 | STATIC_OVL void
800 | warning_opts(opts, optype)
801 | register char *opts;
802 | const char *optype;
803 | {
804 | uchar translate[MAXPCHARS+1];
805 | int length, i;
806 |
807 | if (!(opts = string_for_env_opt(optype, opts, FALSE)))
808 | return;
809 | escapes(opts, opts);
810 |
811 | length = strlen(opts);
812 | if (length > WARNCOUNT) length = WARNCOUNT;
813 | /* match the form obtained from PC configuration files */
814 | for (i = 0; i < length; i++)
815 | translate[i] = (((i < WARNCOUNT) && opts[i]) ?
816 | (uchar) opts[i] : def_warnsyms[i].sym);
817 | assign_warnings(translate);
818 | }
819 |
820 | void
821 | assign_warnings(graph_chars)
822 | register uchar *graph_chars;
823 | {
824 | int i;
825 | for (i = 0; i < WARNCOUNT; i++)
826 | warnsyms[i] = graph_chars[i];
827 | }
828 |
829 | #if 0
830 | /* warnlevel is unnecessary with the new warning introduced in 3.3.1 */
831 | STATIC_OVL int
832 | warnlevel_opts(op, optn)
833 | char *op;
834 | const char *optn;
835 | {
836 | char buf[BUFSZ];
837 | int twarnlevel;
838 | boolean rejectlevel = FALSE;
839 |
840 | if (op) {
841 | twarnlevel = atoi(op);
842 | if (twarnlevel >= WARNCOUNT || twarnlevel < 1)
843 | rejectlevel = TRUE;
844 | else {
845 | flags.warnlevel = twarnlevel;
846 | see_monsters();
847 | }
848 | }
849 | if (rejectlevel) {
850 | if (!initial)
851 | pline("warnlevel must be 1 to %d.", WARNCOUNT - 1);
852 | else {
853 | Sprintf(buf,
854 | "\n%s=%s Invalid warnlevel ignored (must be 1 to %d)",
855 | optn, op, WARNCOUNT - 1);
856 | badoption(buf);
857 | }
858 | return 0;
859 | }
860 | if (!initial) {
861 | if (flags.warnlevel < WARNCOUNT -1)
862 | Sprintf(buf, "s %d to %d", flags.warnlevel, WARNCOUNT - 1);
863 | else
864 | Sprintf(buf, " %d", flags.warnlevel);
865 | pline("Warning level%s will be displayed.", buf);
866 | }
867 | return 1;
868 | }
869 | #endif
870 |
871 | STATIC_OVL int
872 | feature_alert_opts(op, optn)
873 | char *op;
874 | const char *optn;
875 | {
876 | char buf[BUFSZ];
877 | boolean rejectver = FALSE;
878 | unsigned long fnv = get_feature_notice_ver(op); /* version.c */
879 | if (fnv == 0L) return 0;
880 | if (fnv > get_current_feature_ver())
881 | rejectver = TRUE;
882 | else
883 | flags.suppress_alert = fnv;
884 | if (rejectver) {
885 | if (!initial)
886 | You_cant("disable new feature alerts for future versions.");
887 | else {
888 | Sprintf(buf,
889 | "\n%s=%s Invalid reference to a future version ignored",
890 | optn, op);
891 | badoption(buf);
892 | }
893 | return 0;
894 | }
895 | if (!initial) {
896 | Sprintf(buf, "%lu.%lu.%lu", FEATURE_NOTICE_VER_MAJ,
897 | FEATURE_NOTICE_VER_MIN, FEATURE_NOTICE_VER_PATCH);
898 | pline("Feature change alerts disabled for NetHack %s features and prior.",
899 | buf);
900 | }
901 | return 1;
902 | }
903 |
904 | void
905 | parseoptions(opts, tinitial, tfrom_file)
906 | register char *opts;
907 | boolean tinitial, tfrom_file;
908 | {
909 | register char *op;
910 | unsigned num;
911 | boolean negated;
912 | int i;
913 | const char *fullname;
914 |
915 | initial = tinitial;
916 | from_file = tfrom_file;
917 | if ((op = index(opts, ',')) != 0) {
918 | *op++ = 0;
919 | parseoptions(op, initial, from_file);
920 | }
921 | if (strlen(opts) > BUFSZ/2) {
922 | badoption("option too long");
923 | return;
924 | }
925 |
926 | /* strip leading and trailing white space */
927 | while (isspace(*opts)) opts++;
928 | op = eos(opts);
929 | while (--op >= opts && isspace(*op)) *op = '\0';
930 |
931 | if (!*opts) return;
932 | negated = FALSE;
933 | while ((*opts == '!') || !strncmpi(opts, "no", 2)) {
934 | if (*opts == '!') opts++; else opts += 2;
935 | negated = !negated;
936 | }
937 |
938 | /* variant spelling */
939 |
940 | if (match_optname(opts, "colour", 5, FALSE))
941 | Strcpy(opts, "color"); /* fortunately this isn't longer */
942 |
943 | /* special boolean options */
944 |
945 | if (match_optname(opts, "female", 3, FALSE)) {
946 | if(!initial && flags.female == negated)
947 | pline("That is not anatomically possible.");
948 | else
949 | flags.initgend = flags.female = !negated;
950 | return;
951 | }
952 |
953 | if (match_optname(opts, "male", 4, FALSE)) {
954 | if(!initial && flags.female != negated)
955 | pline("That is not anatomically possible.");
956 | else
957 | flags.initgend = flags.female = negated;
958 | return;
959 | }
960 |
961 | #if defined(MICRO) && !defined(AMIGA)
962 | /* included for compatibility with old NetHack.cnf files */
963 | if (match_optname(opts, "IBM_", 4, FALSE)) {
964 | iflags.BIOS = !negated;
965 | return;
966 | }
967 | #endif /* MICRO */
968 |
969 | /* compound options */
970 |
971 | fullname = "pettype";
972 | if (match_optname(opts, fullname, 3, TRUE)) {
973 | if ((op = string_for_env_opt(fullname, opts, negated)) != 0) {
974 | if (negated) bad_negation(fullname, TRUE);
975 | else switch (*op) {
976 | case 'd': /* dog */
977 | case 'D':
978 | preferred_pet = 'd';
979 | break;
980 | case 'c': /* cat */
981 | case 'C':
982 | case 'f': /* feline */
983 | case 'F':
984 | preferred_pet = 'c';
985 | break;
986 | default:
987 | pline("Unrecognized pet type '%s'", op);
988 | break;
989 | }
990 | } else if (negated) preferred_pet = 0;
991 | return;
992 | }
993 |
994 | fullname = "catname";
995 | if (match_optname(opts, fullname, 3, TRUE)) {
996 | if (negated) bad_negation(fullname, FALSE);
997 | else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
998 | nmcpy(catname, op, PL_PSIZ);
999 | return;
1000 | }
1001 |
1002 | fullname = "dogname";
1003 | if (match_optname(opts, fullname, 3, TRUE)) {
1004 | if (negated) bad_negation(fullname, FALSE);
1005 | else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1006 | nmcpy(dogname, op, PL_PSIZ);
1007 | return;
1008 | }
1009 |
1010 | fullname = "horsename";
1011 | if (match_optname(opts, fullname, 5, TRUE)) {
1012 | if (negated) bad_negation(fullname, FALSE);
1013 | else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1014 | nmcpy(horsename, op, PL_PSIZ);
1015 | return;
1016 | }
1017 |
1018 | fullname = "msghistory";
1019 | if (match_optname(opts, fullname, 3, TRUE)) {
1020 | op = string_for_env_opt(fullname, opts, negated);
1021 | if ((negated && !op) || (!negated && op)) {
1022 | iflags.msg_history = negated ? 0 : atoi(op);
1023 | } else if (negated) bad_negation(fullname, TRUE);
1024 | return;
1025 | }
1026 |
1027 | #ifdef CHANGE_COLOR
1028 | #ifdef MAC
1029 | fullname = "use_stone";
1030 | if (match_optname(opts, fullname, 6, TRUE)) {
1031 | op = string_for_env_opt(fullname, opts, negated);
1032 | if ((negated && !op) || (!negated && op)) {
1033 | iflags.use_stone = negated ? 0 : atoi(op);
1034 | } else if (negated) bad_negation(fullname, TRUE);
1035 | return;
1036 | }
1037 |
1038 | fullname = "background";
1039 | if (match_optname(opts, fullname, 5,TRUE))
1040 | {
1041 | if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1042 | {
1043 | if (!strncmpi (op, "white", 5))
1044 | change_background (1);
1045 | else if (!strncmpi (op, "black", 5))
1046 | change_background (0);
1047 | }
1048 | return;
1049 | }
1050 |
1051 | fullname = "font";
1052 | if (!strncmpi(opts, fullname, 4))
1053 | { int wintype = -1;
1054 |
1055 | opts += 4;
1056 | if (!strncmpi (opts, "map", 3))
1057 | wintype = NHW_MAP;
1058 | else if (!strncmpi (opts, "message", 7))
1059 | wintype = NHW_MESSAGE;
1060 | else if (!strncmpi (opts, "text", 4))
1061 | wintype = NHW_TEXT;
1062 |
1063 | if (wintype > 0 && (op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1064 | { set_font_name (wintype, op);
1065 | }
1066 | return;
1067 | }
1068 | #endif
1069 | if (match_optname(opts, "palette", 3, TRUE)
1070 | # ifdef MAC
1071 | || match_optname(opts, "hicolor", 3, TRUE)
1072 | # endif
1073 | ) {
1074 | int color_number, color_incr;
1075 |
1076 | # ifdef MAC
1077 | if (match_optname(opts, "hicolor", 3, TRUE)) {
1078 | if (negated) {
1079 | bad_negation("hicolor", FALSE);
1080 | return;
1081 | }
1082 | color_number = CLR_MAX + 4; /* HARDCODED inverse number */
1083 | color_incr = -1;
1084 | } else {
1085 | # endif
1086 | if (negated) {
1087 | bad_negation("palette", FALSE);
1088 | return;
1089 | }
1090 | color_number = 0;
1091 | color_incr = 1;
1092 | # ifdef MAC
1093 | }
1094 | # endif
1095 | if ((op = string_for_opt(opts, FALSE)) != (char *)0) {
1096 | char *pt = op;
1097 | int cnt, tmp, reverse;
1098 | long rgb;
1099 |
1100 | while (*pt && color_number >= 0) {
1101 | cnt = 3;
1102 | rgb = 0L;
1103 | if (*pt == '-') {
1104 | reverse = 1;
1105 | pt++;
1106 | } else {
1107 | reverse = 0;
1108 | }
1109 | while (cnt-- > 0) {
1110 | if (*pt && *pt != '/') {
1111 | # ifdef AMIGA
1112 | rgb <<= 4;
1113 | # else
1114 | rgb <<= 8;
1115 | # endif
1116 | tmp = *(pt++);
1117 | if (isalpha(tmp)) {
1118 | tmp = (tmp + 9) & 0xf; /* Assumes ASCII... */
1119 | } else {
1120 | tmp &= 0xf; /* Digits in ASCII too... */
1121 | }
1122 | # ifndef AMIGA
1123 | /* Add an extra so we fill f -> ff and 0 -> 00 */
1124 | rgb += tmp << 4;
1125 | # endif
1126 | rgb += tmp;
1127 | }
1128 | }
1129 | if (*pt == '/') {
1130 | pt++;
1131 | }
1132 | change_color(color_number, rgb, reverse);
1133 | color_number += color_incr;
1134 | }
1135 | }
1136 | if (!initial) {
1137 | need_redraw = TRUE;
1138 | }
1139 | return;
1140 | }
1141 | #endif
1142 |
1143 | if (match_optname(opts, "fruit", 2, TRUE)) {
1144 | char empty_str = '\0';
1145 | op = string_for_opt(opts, negated);
1146 | if (negated) {
1147 | if (op) {
1148 | bad_negation("fruit", TRUE);
1149 | return;
1150 | }
1151 | op = &empty_str;
1152 | goto goodfruit;
1153 | }
1154 | if (!op) return;
1155 | if (!initial) {
1156 | struct fruit *f;
1157 |
1158 | num = 0;
1159 | for(f=ffruit; f; f=f->nextf) {
1160 | if (!strcmp(op, f->fname)) goto goodfruit;
1161 | num++;
1162 | }
1163 | if (num >= 100) {
1164 | pline("Doing that so many times isn't very fruitful.");
1165 | return;
1166 | }
1167 | }
1168 | goodfruit:
1169 | nmcpy(pl_fruit, op, PL_FSIZ);
1170 | /* OBJ_NAME(objects[SLIME_MOLD]) won't work after initialization */
1171 | if (!*pl_fruit)
1172 | nmcpy(pl_fruit, "slime mold", PL_FSIZ);
1173 | if (!initial)
1174 | (void)fruitadd(pl_fruit);
1175 | /* If initial, then initoptions is allowed to do it instead
1176 | * of here (initoptions always has to do it even if there's
1177 | * no fruit option at all. Also, we don't want people
1178 | * setting multiple fruits in their options.)
1179 | */
1180 | return;
1181 | }
1182 |
1183 | /* graphics:string */
1184 | fullname = "graphics";
1185 | if (match_optname(opts, fullname, 2, TRUE)) {
1186 | if (negated) bad_negation(fullname, FALSE);
1187 | else graphics_opts(opts, fullname, MAXPCHARS, 0);
1188 | return;
1189 | }
1190 | fullname = "dungeon";
1191 | if (match_optname(opts, fullname, 2, TRUE)) {
1192 | if (negated) bad_negation(fullname, FALSE);
1193 | else graphics_opts(opts, fullname, MAXDCHARS, 0);
1194 | return;
1195 | }
1196 | fullname = "traps";
1197 | if (match_optname(opts, fullname, 2, TRUE)) {
1198 | if (negated) bad_negation(fullname, FALSE);
1199 | else graphics_opts(opts, fullname, MAXTCHARS, MAXDCHARS);
1200 | return;
1201 | }
1202 | fullname = "effects";
1203 | if (match_optname(opts, fullname, 2, TRUE)) {
1204 | if (negated) bad_negation(fullname, FALSE);
1205 | else
1206 | graphics_opts(opts, fullname, MAXECHARS, MAXDCHARS+MAXTCHARS);
1207 | return;
1208 | }
1209 |
1210 | /* objects:string */
1211 | fullname = "objects";
1212 | if (match_optname(opts, fullname, 7, TRUE)) {
1213 | int length;
1214 |
1215 | if (negated) {
1216 | bad_negation(fullname, FALSE);
1217 | return;
1218 | }
1219 | if (!(opts = string_for_env_opt(fullname, opts, FALSE)))
1220 | return;
1221 | escapes(opts, opts);
1222 |
1223 | /*
1224 | * Override the default object class symbols. The first
1225 | * object in the object class is the "random object". I
1226 | * don't want to use 0 as an object class, so the "random
1227 | * object" is basically a place holder.
1228 | *
1229 | * The object class symbols have already been initialized in
1230 | * initoptions().
1231 | */
1232 | length = strlen(opts);
1233 | if (length >= MAXOCLASSES)
1234 | length = MAXOCLASSES-1; /* don't count RANDOM_OBJECT */
1235 |
1236 | for (i = 0; i < length; i++)
1237 | oc_syms[i+1] = (uchar) opts[i];
1238 | return;
1239 | }
1240 |
1241 | /* monsters:string */
1242 | fullname = "monsters";
1243 | if (match_optname(opts, fullname, 8, TRUE)) {
1244 | int length;
1245 |
1246 | if (negated) {
1247 | bad_negation(fullname, FALSE);
1248 | return;
1249 | }
1250 | if (!(opts = string_for_env_opt(fullname, opts, FALSE)))
1251 | return;
1252 | escapes(opts, opts);
1253 |
1254 | /* Override default mon class symbols set in initoptions(). */
1255 | length = strlen(opts);
1256 | if (length >= MAXMCLASSES)
1257 | length = MAXMCLASSES-1; /* mon class 0 unused */
1258 |
1259 | for (i = 0; i < length; i++)
1260 | monsyms[i+1] = (uchar) opts[i];
1261 | return;
1262 | }
1263 | fullname = "warnings";
1264 | if (match_optname(opts, fullname, 5, TRUE)) {
1265 | if (negated) bad_negation(fullname, FALSE);
1266 | else warning_opts(opts, fullname);
1267 | return;
1268 | }
1269 | #if 0
1270 | fullname = "warnlevel";
1271 | if (match_optname(opts, fullname, 5, TRUE)) {
1272 | op = string_for_opt(opts, negated);
1273 | if (negated) bad_negation(fullname, FALSE);
1274 | else if (op) (void) warnlevel_opts(op,fullname);
1275 | return;
1276 | }
1277 | #endif
1278 | /* name:string */
1279 | fullname = "name";
1280 | if (match_optname(opts, fullname, 4, TRUE)) {
1281 | if (negated) bad_negation(fullname, FALSE);
1282 | else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1283 | nmcpy(plname, op, PL_NSIZ);
1284 | return;
1285 | }
1286 |
1287 | /* role:string or character:string */
1288 | fullname = "role";
1289 | if (match_optname(opts, fullname, 4, TRUE) ||
1290 | match_optname(opts, (fullname = "character"), 4, TRUE)) {
1291 | if (negated) bad_negation(fullname, FALSE);
1292 | else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
1293 | if ((flags.initrole = str2role(op)) == ROLE_NONE)
1294 | badoption(opts);
1295 | else /* Backwards compatibility */
1296 | nmcpy(pl_character, op, PL_NSIZ);
1297 | }
1298 | return;
1299 | }
1300 |
1301 | /* race:string */
1302 | fullname = "race";
1303 | if (match_optname(opts, fullname, 4, TRUE)) {
1304 | if (negated) bad_negation(fullname, FALSE);
1305 | else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
1306 | if ((flags.initrace = str2race(op)) == ROLE_NONE)
1307 | badoption(opts);
1308 | else /* Backwards compatibility */
1309 | pl_race = *op;
1310 | }
1311 | return;
1312 | }
1313 |
1314 | /* gender:string */
1315 | fullname = "gender";
1316 | if (match_optname(opts, fullname, 4, TRUE)) {
1317 | if (negated) bad_negation(fullname, FALSE);
1318 | else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
1319 | if ((flags.initgend = str2gend(op)) == ROLE_NONE)
1320 | badoption(opts);
1321 | else
1322 | flags.female = flags.initgend;
1323 | }
1324 | return;
1325 | }
1326 |
1327 | /* align:string */
1328 | fullname = "align";
1329 | if (match_optname(opts, fullname, 4, TRUE)) {
1330 | if (negated) bad_negation(fullname, FALSE);
1331 | else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1332 | if ((flags.initalign = str2align(op)) == ROLE_NONE)
1333 | badoption(opts);
1334 | return;
1335 | }
1336 |
1337 | /* the order to list the pack */
1338 | fullname = "packorder";
1339 | if (match_optname(opts, fullname, 4, TRUE)) {
1340 | if (negated) {
1341 | bad_negation(fullname, FALSE);
1342 | return;
1343 | } else if (!(op = string_for_opt(opts, FALSE))) return;
1344 |
1345 | if (!change_inv_order(op))
1346 | badoption(opts);
1347 | return;
1348 | }
1349 |
1350 | /* maximum burden picked up before prompt (Warren Cheung) */
1351 | fullname = "pickup_burden";
1352 | if (match_optname(opts, fullname, 8, TRUE)) {
1353 | if (negated) {
1354 | bad_negation(fullname, FALSE);
1355 | return;
1356 | } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
1357 | switch (tolower(*op)) {
1358 | /* Unencumbered */
1359 | case 'u':
1360 | flags.pickup_burden = UNENCUMBERED;
1361 | break;
1362 | /* Burdened (slight encumberance) */
1363 | case 'b':
1364 | flags.pickup_burden = SLT_ENCUMBER;
1365 | break;
1366 | /* streSsed (moderate encumberance) */
1367 | case 's':
1368 | flags.pickup_burden = MOD_ENCUMBER;
1369 | break;
1370 | /* straiNed (heavy encumberance) */
1371 | case 'n':
1372 | flags.pickup_burden = HVY_ENCUMBER;
1373 | break;
1374 | /* OverTaxed (extreme encumberance) */
1375 | case 'o':
1376 | case 't':
1377 | flags.pickup_burden = EXT_ENCUMBER;
1378 | break;
1379 | /* overLoaded */
1380 | case 'l':
1381 | flags.pickup_burden = OVERLOADED;
1382 | break;
1383 | default:
1384 | badoption(opts);
1385 | }
1386 | }
1387 | return;
1388 | }
1389 |
1390 | /* types of objects to pick up automatically */
1391 | if (match_optname(opts, "pickup_types", 8, TRUE)) {
1392 | char ocl[MAXOCLASSES + 1], tbuf[MAXOCLASSES + 1],
1393 | qbuf[QBUFSZ], abuf[BUFSZ];
1394 | int oc_sym;
1395 | boolean badopt = FALSE, compat = (strlen(opts) <= 6), use_menu;
1396 |
1397 | oc_to_str(flags.pickup_types, tbuf);
1398 | flags.pickup_types[0] = '\0'; /* all */
1399 | op = string_for_opt(opts, (compat || !initial));
1400 | if (!op) {
1401 | if (compat || negated || initial) {
1402 | /* for backwards compatibility, "pickup" without a
1403 | value is a synonym for autopickup of all types
1404 | (and during initialization, we can't prompt yet) */
1405 | flags.pickup = !negated;
1406 | return;
1407 | }
1408 | oc_to_str(flags.inv_order, ocl);
1409 | use_menu = TRUE;
1410 | if (flags.menu_style == MENU_TRADITIONAL ||
1411 | flags.menu_style == MENU_COMBINATION) {
1412 | use_menu = FALSE;
1413 | Sprintf(qbuf, "New pickup_types: [%s am] (%s)",
1414 | ocl, *tbuf ? tbuf : "all");
1415 | getlin(qbuf, abuf);
1416 | op = mungspaces(abuf);
1417 | if (abuf[0] == '\0' || abuf[0] == '\033')
1418 | op = tbuf; /* restore */
1419 | else if (abuf[0] == 'm')
1420 | use_menu = TRUE;
1421 | }
1422 | if (use_menu) {
1423 | (void) choose_classes_menu("Auto-Pickup what?", 1,
1424 | TRUE, ocl, tbuf);
1425 | op = tbuf;
1426 | }
1427 | }
1428 | if (negated) {
1429 | bad_negation("pickup_types", TRUE);
1430 | return;
1431 | }
1432 | while (*op == ' ') op++;
1433 | if (*op != 'a' && *op != 'A') {
1434 | num = 0;
1435 | while (*op) {
1436 | oc_sym = def_char_to_objclass(*op);
1437 | /* make sure all are valid obj symbols occuring once */
1438 | if (oc_sym != MAXOCLASSES &&
1439 | !index(flags.pickup_types, oc_sym)) {
1440 | flags.pickup_types[num] = (char)oc_sym;
1441 | flags.pickup_types[++num] = '\0';
1442 | } else
1443 | badopt = TRUE;
1444 | op++;
1445 | }
1446 | if (badopt) badoption(opts);
1447 | }
1448 | return;
1449 | }
1450 |
1451 | /* things to disclose at end of game */
1452 | if (match_optname(opts, "disclose", 4, TRUE)) {
1453 | flags.end_disclose[0] = '\0'; /* all */
1454 | if (!(op = string_for_opt(opts, TRUE))) {
1455 | /* for backwards compatibility, "disclose" without a
1456 | * value means all (was inventory and attributes,
1457 | * the only things available then), but negated
1458 | * it means "none"
1459 | * (note "none" contains none of "iavkgc")
1460 | */
1461 | if (negated) Strcpy(flags.end_disclose, "none");
1462 | return;
1463 | }
1464 | if (negated) {
1465 | bad_negation("disclose", TRUE);
1466 | return;
1467 | }
1468 | num = 0;
1469 | while (*op && num < sizeof flags.end_disclose - 1) {
1470 | register char c;
1471 | c = lowc(*op);
1472 | if (c == 'k') c = 'v'; /* killed -> vanquished */
1473 | if (!index(flags.end_disclose, c)) {
1474 | flags.end_disclose[num++] = c;
1475 | flags.end_disclose[num] = '\0'; /* for index */
1476 | }
1477 | op++;
1478 | }
1479 | return;
1480 | }
1481 |
1482 | /* scores:5t[op] 5a[round] o[wn] */
1483 | if (match_optname(opts, "scores", 4, TRUE)) {
1484 | if (negated) {
1485 | bad_negation("scores", FALSE);
1486 | return;
1487 | }
1488 | if (!(op = string_for_opt(opts, FALSE))) return;
1489 |
1490 | while (*op) {
1491 | int inum = 1;
1492 |
1493 | if (digit(*op)) {
1494 | inum = atoi(op);
1495 | while (digit(*op)) op++;
1496 | } else if (*op == '!') {
1497 | negated = !negated;
1498 | op++;
1499 | }
1500 | while (*op == ' ') op++;
1501 |
1502 | switch (*op) {
1503 | case 't':
1504 | case 'T': flags.end_top = inum;
1505 | break;
1506 | case 'a':
1507 | case 'A': flags.end_around = inum;
1508 | break;
1509 | case 'o':
1510 | case 'O': flags.end_own = !negated;
1511 | break;
1512 | default: badoption(opts);
1513 | return;
1514 | }
1515 | while (letter(*++op) || *op == ' ') continue;
1516 | if (*op == '/') op++;
1517 | }
1518 | return;
1519 | }
1520 |
1521 | fullname = "suppress_alert";
1522 | if (match_optname(opts, fullname, 4, TRUE)) {
1523 | op = string_for_opt(opts, negated);
1524 | if (negated) bad_negation(fullname, FALSE);
1525 | else if (op) (void) feature_alert_opts(op,fullname);
1526 | return;
1527 | }
1528 |
1529 | #ifdef VIDEOSHADES
1530 | /* videocolors:string */
1531 | fullname = "videocolors";
1532 | if (match_optname(opts, fullname, 6, TRUE) ||
1533 | match_optname(opts, "videocolours", 10, TRUE)) {
1534 | if (negated) {
1535 | bad_negation(fullname, FALSE);
1536 | return;
1537 | }
1538 | else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
1539 | return;
1540 | }
1541 | if (!assign_videocolors(opts))
1542 | badoption(opts);
1543 | return;
1544 | }
1545 | /* videoshades:string */
1546 | fullname = "videoshades";
1547 | if (match_optname(opts, fullname, 6, TRUE)) {
1548 | if (negated) {
1549 | bad_negation(fullname, FALSE);
1550 | return;
1551 | }
1552 | else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
1553 | return;
1554 | }
1555 | if (!assign_videoshades(opts))
1556 | badoption(opts);
1557 | return;
1558 | }
1559 | #endif /* VIDEOSHADES */
1560 | #ifdef MSDOS
1561 | # ifdef NO_TERMS
1562 | /* video:string -- must be after longer tests */
1563 | fullname = "video";
1564 | if (match_optname(opts, fullname, 5, TRUE)) {
1565 | if (negated) {
1566 | bad_negation(fullname, FALSE);
1567 | return;
1568 | }
1569 | else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
1570 | return;
1571 | }
1572 | if (!assign_video(opts))
1573 | badoption(opts);
1574 | return;
1575 | }
1576 | # endif /* NO_TERMS */
1577 | /* soundcard:string -- careful not to match boolean 'sound' */
1578 | fullname = "soundcard";
1579 | if (match_optname(opts, fullname, 6, TRUE)) {
1580 | if (negated) {
1581 | bad_negation(fullname, FALSE);
1582 | return;
1583 | }
1584 | else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
1585 | return;
1586 | }
1587 | if (!assign_soundcard(opts))
1588 | badoption(opts);
1589 | return;
1590 | }
1591 | #endif /* MSDOS */
1592 |
1593 |
1594 | fullname = "windowtype";
1595 | if (match_optname(opts, fullname, 3, TRUE)) {
1596 | if (negated) {
1597 | bad_negation(fullname, FALSE);
1598 | return;
1599 | } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
1600 | char buf[WINTYPELEN];
1601 | nmcpy(buf, op, WINTYPELEN);
1602 | choose_windows(buf);
1603 | }
1604 | return;
1605 | }
1606 |
1607 | /* menustyle:traditional or combo or full or partial */
1608 | if (match_optname(opts, "menustyle", 4, TRUE)) {
1609 | int tmp;
1610 | boolean val_required = (strlen(opts) > 5 && !negated);
1611 |
1612 | if (!(op = string_for_opt(opts, !val_required))) {
1613 | if (val_required) return; /* string_for_opt gave feedback */
1614 | tmp = negated ? 'n' : 'f';
1615 | } else {
1616 | tmp = tolower(*op);
1617 | }
1618 | switch (tmp) {
1619 | case 'n': /* none */
1620 | case 't': /* traditional */
1621 | flags.menu_style = MENU_TRADITIONAL;
1622 | break;
1623 | case 'c': /* combo: trad.class sel+menu */
1624 | flags.menu_style = MENU_COMBINATION;
1625 | break;
1626 | case 'p': /* partial: no class menu */
1627 | flags.menu_style = MENU_PARTIAL;
1628 | break;
1629 | case 'f': /* full: class menu + menu */
1630 | flags.menu_style = MENU_FULL;
1631 | break;
1632 | default:
1633 | badoption(opts);
1634 | }
1635 | return;
1636 | }
1637 |
1638 | /* check for menu command mapping */
1639 | for (i = 0; i < NUM_MENU_CMDS; i++) {
1640 | fullname = default_menu_cmd_info[i].name;
1641 | if (match_optname(opts, fullname, (int)strlen(fullname), TRUE)) {
1642 | if (negated)
1643 | bad_negation(fullname, FALSE);
1644 | else if ((op = string_for_opt(opts, FALSE)) != 0) {
1645 | int j;
1646 | char c, op_buf[BUFSZ];
1647 | boolean isbad = FALSE;
1648 |
1649 | escapes(op, op_buf);
1650 | c = *op_buf;
1651 |
1652 | if (c == 0 || c == '\r' || c == '\n' || c == '\033' ||
1653 | c == ' ' || digit(c) || (letter(c) && c != '@'))
1654 | isbad = TRUE;
1655 | else /* reject default object class symbols */
1656 | for (j = 1; j < MAXOCLASSES; j++)
1657 | if (c == def_oc_syms[i]) {
1658 | isbad = TRUE;
1659 | break;
1660 | }
1661 |
1662 | if (isbad)
1663 | badoption(opts);
1664 | else
1665 | add_menu_cmd_alias(c, default_menu_cmd_info[i].cmd);
1666 | }
1667 | return;
1668 | }
1669 | }
1670 |
1671 | /* OK, if we still haven't recognized the option, check the boolean
1672 | * options list
1673 | */
1674 | for (i = 0; boolopt[i].name; i++) {
1675 | if (match_optname(opts, boolopt[i].name, 3, FALSE)) {
1676 | /* options that don't exist */
1677 | if (!boolopt[i].addr) {
1678 | if (!initial && !negated)
1679 | pline_The("\"%s\" option is not available.",
1680 | boolopt[i].name);
1681 | return;
1682 | }
1683 | /* options that must come from config file */
1684 | if (!initial && boolopt_only_initial(i)) {
1685 | rejectoption(boolopt[i].name);
1686 | return;
1687 | }
1688 |
1689 | *(boolopt[i].addr) = !negated;
1690 |
1691 | #if defined(TERMLIB) || defined(ASCIIGRAPH) || defined(MAC_GRAPHICS_ENV)
1692 | if (FALSE
1693 | # ifdef TERMLIB
1694 | || (boolopt[i].addr) == &iflags.DECgraphics
1695 | # endif
1696 | # ifdef ASCIIGRAPH
1697 | || (boolopt[i].addr) == &iflags.IBMgraphics
1698 | # endif
1699 | # ifdef MAC_GRAPHICS_ENV
1700 | || (boolopt[i].addr) == &iflags.MACgraphics
1701 | # endif
1702 | ) {
1703 | # ifdef REINCARNATION
1704 | if (!initial && Is_rogue_level(&u.uz))
1705 | assign_rogue_graphics(FALSE);
1706 | # endif
1707 | need_redraw = TRUE;
1708 | # ifdef TERMLIB
1709 | if ((boolopt[i].addr) == &iflags.DECgraphics)
1710 | switch_graphics(iflags.DECgraphics ?
1711 | DEC_GRAPHICS : ASCII_GRAPHICS);
1712 | # endif
1713 | # ifdef ASCIIGRAPH
1714 | if ((boolopt[i].addr) == &iflags.IBMgraphics)
1715 | switch_graphics(iflags.IBMgraphics ?
1716 | IBM_GRAPHICS : ASCII_GRAPHICS);
1717 | # endif
1718 | # ifdef MAC_GRAPHICS_ENV
1719 | if ((boolopt[i].addr) == &iflags.MACgraphics)
1720 | switch_graphics(iflags.MACgraphics ?
1721 | MAC_GRAPHICS : ASCII_GRAPHICS);
1722 | # endif
1723 | # ifdef REINCARNATION
1724 | if (!initial && Is_rogue_level(&u.uz))
1725 | assign_rogue_graphics(TRUE);
1726 | # endif
1727 | }
1728 | #endif /* TERMLIB || ASCIIGRAPH || MAC_GRAPHICS_ENV */
1729 |
1730 | /* only do processing below if setting with doset() */
1731 | if (initial) return;
1732 |
1733 | if ((boolopt[i].addr) == &flags.time
1734 | #ifdef EXP_ON_BOTL
1735 | || (boolopt[i].addr) == &flags.showexp
1736 | #endif
1737 | #ifdef SCORE_ON_BOTL
1738 | || (boolopt[i].addr) == &flags.showscore
1739 | #endif
1740 | )
1741 | flags.botl = TRUE;
1742 |
1743 | else if ((boolopt[i].addr) == &flags.invlet_constant) {
1744 | if (flags.invlet_constant) reassign();
1745 | }
1746 | #ifdef LAN_MAIL
1747 | else if ((boolopt[i].addr) == &flags.biff) {
1748 | if (flags.biff) lan_mail_init();
1749 | else lan_mail_finish();
1750 | }
1751 | #endif
1752 | else if ((boolopt[i].addr) == &iflags.num_pad)
1753 | number_pad(iflags.num_pad ? 1 : 0);
1754 |
1755 | else if ((boolopt[i].addr) == &flags.lit_corridor) {
1756 | /*
1757 | * All corridor squares seen via night vision or
1758 | * candles & lamps change. Update them by calling
1759 | * newsym() on them. Don't do this if we are
1760 | * initializing the options --- the vision system
1761 | * isn't set up yet.
1762 | */
1763 | vision_recalc(2); /* shut down vision */
1764 | vision_full_recalc = 1; /* delayed recalc */
1765 | }
1766 |
1767 | #ifdef TEXTCOLOR
1768 | else if ((boolopt[i].addr) == &iflags.use_color
1769 | || (boolopt[i].addr) == &iflags.hilite_pet) {
1770 | need_redraw = TRUE;
1771 | # ifdef TOS
1772 | if ((boolopt[i].addr) == &iflags.use_color
1773 | && iflags.BIOS) {
1774 | if (colors_changed)
1775 | restore_colors();
1776 | else
1777 | set_colors();
1778 | }
1779 | # endif
1780 | }
1781 | #endif
1782 |
1783 | return;
1784 | }
1785 | }
1786 |
1787 | /* out of valid options */
1788 | badoption(opts);
1789 | }
1790 |
1791 |
1792 | static NEARDATA const char *menutype[] = {
1793 | "traditional", "combination", "partial", "full"
1794 | };
1795 |
1796 | static NEARDATA const char *burdentype[] = {
1797 | "unencumbered", "burdened", "stressed",
1798 | "strained", "overtaxed", "overloaded"
1799 | };
1800 |
1801 |
1802 | /*
1803 | * Convert the given string of object classes to a string of default object
1804 | * symbols.
1805 | */
1806 | STATIC_OVL void
1807 | oc_to_str(src,dest)
1808 | char *src, *dest;
1809 | {
1810 | int i;
1811 |
1812 | while ((i = (int) *src++) != 0) {
1813 | if (i < 0 || i >= MAXOCLASSES)
1814 | impossible("oc_to_str: illegal object class %d", i);
1815 | else
1816 | *dest++ = def_oc_syms[i];
1817 | }
1818 | *dest = '\0';
1819 | }
1820 |
1821 | /*
1822 | * Add the given mapping to the menu command map list. Always keep the
1823 | * maps valid C strings.
1824 | */
1825 | void
1826 | add_menu_cmd_alias(from_ch, to_ch)
1827 | char from_ch, to_ch;
1828 | {
1829 | if (n_menu_mapped >= MAX_MENU_MAPPED_CMDS)
1830 | pline("out of menu map space");
1831 | else {
1832 | mapped_menu_cmds[n_menu_mapped] = from_ch;
1833 | mapped_menu_op[n_menu_mapped] = to_ch;
1834 | n_menu_mapped++;
1835 | mapped_menu_cmds[n_menu_mapped] = 0;
1836 | mapped_menu_op[n_menu_mapped] = 0;
1837 | }
1838 | }
1839 |
1840 | /*
1841 | * Map the given character to its corresponding menu command. If it
1842 | * doesn't match anything, just return the original.
1843 | */
1844 | char
1845 | map_menu_cmd(ch)
1846 | char ch;
1847 | {
1848 | char *found = index(mapped_menu_cmds, ch);
1849 | if (found) {
1850 | int idx = found - mapped_menu_cmds;
1851 | ch = mapped_menu_op[idx];
1852 | }
1853 | return ch;
1854 | }
1855 |
1856 |
1857 | #if defined(MICRO) || defined(MAC)
1858 | # define OPTIONS_HEADING "OPTIONS"
1859 | #else
1860 | # define OPTIONS_HEADING "NETHACKOPTIONS"
1861 | #endif
1862 |
1863 | static char fmtstr_doset_add_menu[] = "%s%-15s [%s] ";
1864 |
1865 | STATIC_OVL void
1866 | doset_add_menu(win, option, indexoffset)
1867 | winid win; /* window to add to */
1868 | const char *option; /* option name */
1869 | int indexoffset; /* value to add to index in compopt[], or zero
1870 | if option cannot be changed */
1871 | {
1872 | const char *value = "unknown"; /* current value */
1873 | char buf[BUFSZ], buf2[BUFSZ];
1874 | anything any;
1875 | int i;
1876 |
1877 | any.a_void = 0;
1878 | if (indexoffset == 0) {
1879 | any.a_int = 0;
1880 | value = get_compopt_value(option, buf2);
1881 | } else {
1882 | for (i=0; compopt[i].name; i++)
1883 | if (strcmp(option, compopt[i].name) == 0) break;
1884 |
1885 | if (compopt[i].name) {
1886 | any.a_int = i + 1 + indexoffset;
1887 | value = get_compopt_value(option, buf2);
1888 | } else {
1889 | /* We are trying to add an option not found in compopt[].
1890 | This is almost certainly bad, but we'll let it through anyway
1891 | (with a zero value, so it can't be selected). */
1892 | any.a_int = 0;
1893 | }
1894 | }
1895 | /* " " replaces "a - " -- assumes menus follow that style */
1896 | Sprintf(buf, fmtstr_doset_add_menu, (any.a_int ? "" : " "), option, value);
1897 | add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
1898 | }
1899 |
1900 | /* Changing options via menu by Per Liboriussen */
1901 | int
1902 | doset()
1903 | {
1904 | char buf[BUFSZ], buf2[BUFSZ];
1905 | int i, pass, boolcount, pick_cnt, pick_idx, opt_indx;
1906 | boolean *bool_p;
1907 | winid tmpwin;
1908 | anything any;
1909 | menu_item *pick_list;
1910 | int indexoffset, startpass, endpass;
1911 | boolean setinitial = FALSE, fromfile = FALSE;
1912 | int biggest_name = 0;
1913 |
1914 | tmpwin = create_nhwindow(NHW_MENU);
1915 | start_menu(tmpwin);
1916 |
1917 | any.a_void = 0;
1918 | add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1919 | "Booleans (selecting will toggle value):", MENU_UNSELECTED);
1920 | any.a_int = 0;
1921 | /* first list any other non-modifiable booleans, then modifiable ones */
1922 | for (pass = 0; pass <= 1; pass++)
1923 | for (i = 0; boolopt[i].name; i++)
1924 | if ((bool_p = boolopt[i].addr) != 0 &&
1925 | (boolopt_only_initial(i) ^ pass)) {
1926 | if (bool_p == &flags.female) continue; /* obsolete */
1927 | #ifdef WIZARD
1928 | if (bool_p == &iflags.sanity_check && !wizard) continue;
1929 | #endif
1930 | any.a_int = (pass == 0) ? 0 : i + 1;
1931 | Sprintf(buf, "%s%-13s [%s]",
1932 | pass == 0 ? " " : "",
1933 | boolopt[i].name, *bool_p ? "true" : "false");
1934 | add_menu(tmpwin, NO_GLYPH, &any, 0, 0,
1935 | ATR_NONE, buf, MENU_UNSELECTED);
1936 | }
1937 |
1938 | boolcount = i;
1939 | indexoffset = boolcount;
1940 | any.a_void = 0;
1941 | add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
1942 | add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1943 | "Compounds (selecting will prompt for new value):",
1944 | MENU_UNSELECTED);
1945 |
1946 | startpass = DISP_IN_GAME;
1947 | endpass = SET_IN_GAME;
1948 |
1949 | /* spin through the options to find the biggest name
1950 | and adjust the format string accordingly if needed */
1951 | biggest_name = 0;
1952 | for (i = 0; compopt[i].name; i++)
1953 | if (compopt[i].optflags >= startpass && compopt[i].optflags <= endpass &&
1954 | strlen(compopt[i].name) > (unsigned) biggest_name)
1955 | biggest_name = (int) strlen(compopt[i].name);
1956 | if (biggest_name > 30) biggest_name = 30;
1957 | Sprintf(fmtstr_doset_add_menu, "%%s%%-%ds [%%s]", biggest_name);
1958 |
1959 | /* deliberately put `name', `role', `race', `gender' first */
1960 | doset_add_menu(tmpwin, "name", 0);
1961 | doset_add_menu(tmpwin, "role", 0);
1962 | doset_add_menu(tmpwin, "race", 0);
1963 | doset_add_menu(tmpwin, "gender", 0);
1964 |
1965 | for (pass = startpass; pass <= endpass; pass++)
1966 | for (i = 0; compopt[i].name; i++)
1967 | if (compopt[i].optflags == pass) {
1968 | if (!strcmp(compopt[i].name, "name") ||
1969 | !strcmp(compopt[i].name, "role") ||
1970 | !strcmp(compopt[i].name, "race") ||
1971 | !strcmp(compopt[i].name, "gender"))
1972 | continue;
1973 | else
1974 | doset_add_menu(tmpwin, compopt[i].name,
1975 | (pass == DISP_IN_GAME) ? 0 : indexoffset);
1976 | }
1977 | #ifdef PREFIXES_IN_USE
1978 | add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
1979 | add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1980 | "Variable playground locations:", MENU_UNSELECTED);
1981 | for (i = 0; i < PREFIX_COUNT; i++)
1982 | doset_add_menu(tmpwin, fqn_prefix_names[i], 0);
1983 | #endif
1984 | end_menu(tmpwin, "Set what options?");
1985 | need_redraw = FALSE;
1986 | if ((pick_cnt = select_menu(tmpwin, PICK_ANY, &pick_list)) > 0) {
1987 | /*
1988 | * Walk down the selection list and either invert the booleans
1989 | * or prompt for new values. In most cases, call parseoptions()
1990 | * to take care of options that require special attention, like
1991 | * redraws.
1992 | */
1993 | for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
1994 | opt_indx = pick_list[pick_idx].item.a_int - 1;
1995 | if (opt_indx < boolcount) {
1996 | /* boolean option */
1997 | Sprintf(buf, "%s%s", *boolopt[opt_indx].addr ? "!" : "",
1998 | boolopt[opt_indx].name);
1999 | parseoptions(buf, setinitial, fromfile);
2000 | } else {
2001 | /* compound option */
2002 | opt_indx -= boolcount;
2003 |
2004 | if (!special_handling(compopt[opt_indx].name,
2005 | setinitial, fromfile)) {
2006 | Sprintf(buf, "Set %s to what?", compopt[opt_indx].name);
2007 | getlin(buf, buf2);
2008 | Sprintf(buf, "%s:%s", compopt[opt_indx].name, buf2);
2009 | /* pass the buck */
2010 | parseoptions(buf, setinitial, fromfile);
2011 | }
2012 | }
2013 | }
2014 | free((genericptr_t)pick_list);
2015 | pick_list = (menu_item *)0;
2016 | }
2017 |
2018 | destroy_nhwindow(tmpwin);
2019 | if (need_redraw)
2020 | (void) doredraw();
2021 | return 0;
2022 | }
2023 |
2024 | STATIC_OVL boolean
2025 | special_handling(optname, setinitial, setfromfile)
2026 | const char *optname;
2027 | boolean setinitial,setfromfile;
2028 | {
2029 | winid tmpwin;
2030 | anything any;
2031 | int i;
2032 | char buf[BUFSZ];
2033 | boolean retval = FALSE;
2034 |
2035 | /* Special handling of menustyle, pickup_burden, and pickup_types. */
2036 | if (!strcmp("menustyle", optname)) {
2037 | const char *style_name;
2038 | menu_item *style_pick = (menu_item *)0;
2039 | tmpwin = create_nhwindow(NHW_MENU);
2040 | start_menu(tmpwin);
2041 | for (i = 0; i < SIZE(menutype); i++) {
2042 | style_name = menutype[i];
2043 | /* note: separate `style_name' variable used
2044 | to avoid an optimizer bug in VAX C V2.3 */
2045 | any.a_int = i + 1;
2046 | add_menu(tmpwin, NO_GLYPH, &any, *style_name, 0,
2047 | ATR_NONE, style_name, MENU_UNSELECTED);
2048 | }
2049 | end_menu(tmpwin, "Select menustyle:");
2050 | if (select_menu(tmpwin, PICK_ONE, &style_pick) > 0) {
2051 | flags.menu_style = style_pick->item.a_int - 1;
2052 | free((genericptr_t)style_pick);
2053 | }
2054 | destroy_nhwindow(tmpwin);
2055 | retval = TRUE;
2056 | } else if (!strcmp("pickup_burden", optname)) {
2057 | const char *burden_name, *burden_letters = "ubsntl";
2058 | menu_item *burden_pick = (menu_item *)0;
2059 | tmpwin = create_nhwindow(NHW_MENU);
2060 | start_menu(tmpwin);
2061 | for (i = 0; i < SIZE(burdentype); i++) {
2062 | burden_name = burdentype[i];
2063 | any.a_int = i + 1;
2064 | add_menu(tmpwin, NO_GLYPH, &any, burden_letters[i], 0,
2065 | ATR_NONE, burden_name, MENU_UNSELECTED);
2066 | }
2067 | end_menu(tmpwin, "Select encumberence level:");
2068 | if (select_menu(tmpwin, PICK_ONE, &burden_pick) > 0) {
2069 | flags.pickup_burden = burden_pick->item.a_int - 1;
2070 | free((genericptr_t)burden_pick);
2071 | }
2072 | destroy_nhwindow(tmpwin);
2073 | retval = TRUE;
2074 | } else if (!strcmp("pickup_types", optname)) {
2075 | /* parseoptions will prompt for the list of types */
2076 | parseoptions(strcpy(buf, "pickup_types"), setinitial, setfromfile);
2077 | retval = TRUE;
2078 | }
2079 | return retval;
2080 | }
2081 |
2082 | #define rolestring(val,array,field) ((val >= 0) ? array[val].field : \
2083 | (val == ROLE_RANDOM) ? randomrole : none)
2084 |
2085 | /* This is ugly. We have all the option names in the compopt[] array,
2086 | but we need to look at each option individually to get the value. */
2087 | STATIC_OVL const char *
2088 | get_compopt_value(optname, buf)
2089 | const char *optname;
2090 | char *buf;
2091 | {
2092 | char ocl[MAXOCLASSES+1];
2093 | static const char none[] = "(none)", randomrole[] = "random",
2094 | to_be_done[] = "(to be done)";
2095 | #ifdef PREFIXES_IN_USE
2096 | int i;
2097 | #endif
2098 |
2099 | buf[0] = '\0';
2100 | if (!strcmp(optname,"align"))
2101 | Sprintf(buf, "%s", rolestring(flags.initalign, aligns, adj));
2102 | else if (!strcmp(optname, "catname"))
2103 | Sprintf(buf, "%s", catname[0] ? catname : none );
2104 | else if (!strcmp(optname, "disclose"))
2105 | Sprintf(buf, "%s",
2106 | flags.end_disclose[0] ? flags.end_disclose : "all" );
2107 | else if (!strcmp(optname, "dogname"))
2108 | Sprintf(buf, "%s", dogname[0] ? dogname : none );
2109 | else if (!strcmp(optname, "dungeon"))
2110 | Sprintf(buf, "%s", to_be_done);
2111 | else if (!strcmp(optname, "effects"))
2112 | Sprintf(buf, "%s", to_be_done);
2113 | else if (!strcmp(optname, "fruit"))
2114 | Sprintf(buf, "%s", pl_fruit);
2115 | else if (!strcmp(optname, "gender"))
2116 | Sprintf(buf, "%s", rolestring(flags.initgend, genders, adj));
2117 | else if (!strcmp(optname, "horsename"))
2118 | Sprintf(buf, "%s", horsename[0] ? horsename : none);
2119 | else if (!strcmp(optname, "menustyle"))
2120 | Sprintf(buf, "%s", menutype[(int)flags.menu_style] );
2121 | else if (!strcmp(optname, "menu_deselect_all"))
2122 | Sprintf(buf, "%s", to_be_done);
2123 | else if (!strcmp(optname, "menu_deselect_page"))
2124 | Sprintf(buf, "%s", to_be_done);
2125 | else if (!strcmp(optname, "menu_first_page"))
2126 | Sprintf(buf, "%s", to_be_done);
2127 | else if (!strcmp(optname, "menu_invert_all"))
2128 | Sprintf(buf, "%s", to_be_done);
2129 | else if (!strcmp(optname, "menu_invert_page"))
2130 | Sprintf(buf, "%s", to_be_done);
2131 | else if (!strcmp(optname, "menu_last_page"))
2132 | Sprintf(buf, "%s", to_be_done);
2133 | else if (!strcmp(optname, "menu_next_page"))
2134 | Sprintf(buf, "%s", to_be_done);
2135 | else if (!strcmp(optname, "menu_previous_page"))
2136 | Sprintf(buf, "%s", to_be_done);
2137 | else if (!strcmp(optname, "menu_search"))
2138 | Sprintf(buf, "%s", to_be_done);
2139 | else if (!strcmp(optname, "menu_select_all"))
2140 | Sprintf(buf, "%s", to_be_done);
2141 | else if (!strcmp(optname, "menu_select_page"))
2142 | Sprintf(buf, "%s", to_be_done);
2143 | else if (!strcmp(optname, "monsters"))
2144 | Sprintf(buf, "%s", to_be_done);
2145 | else if (!strcmp(optname, "msghistory"))
2146 | Sprintf(buf, "%u", iflags.msg_history);
2147 | else if (!strcmp(optname, "name"))
2148 | Sprintf(buf, "%s", plname);
2149 | else if (!strcmp(optname, "objects"))
2150 | Sprintf(buf, "%s", to_be_done);
2151 | else if (!strcmp(optname, "packorder")) {
2152 | oc_to_str(flags.inv_order, ocl);
2153 | Sprintf(buf, "%s", ocl);
2154 | }
2155 | #ifdef CHANGE_COLOR
2156 | else if (!strcmp(optname, "palette"))
2157 | Sprintf(buf, "%s", get_color_string());
2158 | #endif
2159 | else if (!strcmp(optname, "pettype"))
2160 | Sprintf(buf, "%s", (preferred_pet == 'c') ? "cat" :
2161 | (preferred_pet == 'd') ? "dog" : "random" );
2162 | else if (!strcmp(optname, "pickup_burden"))
2163 | Sprintf(buf, "%s", burdentype[flags.pickup_burden] );
2164 | else if (!strcmp(optname, "pickup_types")) {
2165 | oc_to_str(flags.pickup_types, ocl);
2166 | Sprintf(buf, "%s", ocl[0] ? ocl : "all" );
2167 | }
2168 | else if (!strcmp(optname, "race"))
2169 | Sprintf(buf, "%s", rolestring(flags.initrace, races, noun));
2170 | else if (!strcmp(optname, "role"))
2171 | Sprintf(buf, "%s", rolestring(flags.initrole, roles, name.m));
2172 | else if (!strcmp(optname, "scores")) {
2173 | Sprintf(buf, "%d top/%d around%s", flags.end_top,
2174 | flags.end_around, flags.end_own ? "/own" : "");
2175 | }
2176 | #ifdef MSDOS
2177 | else if (!strcmp(optname, "soundcard"))
2178 | Sprintf(buf, "%s", to_be_done);
2179 | #endif
2180 | else if (!strcmp(optname, "suppress_alert")) {
2181 | if (flags.suppress_alert == 0L)
2182 | Strcpy(buf, none);
2183 | else
2184 | Sprintf(buf, "%lu.%lu.%lu",
2185 | FEATURE_NOTICE_VER_MAJ,
2186 | FEATURE_NOTICE_VER_MIN,
2187 | FEATURE_NOTICE_VER_PATCH);
2188 | } else if (!strcmp(optname, "traps"))
2189 | Sprintf(buf, "%s", to_be_done);
2190 | #ifdef MSDOS
2191 | else if (!strcmp(optname, "video"))
2192 | Sprintf(buf, "%s", to_be_done);
2193 | #endif
2194 | #ifdef VIDEOSHADES
2195 | else if (!strcmp(optname, "videoshades"))
2196 | Sprintf(buf, "%s-%s-%s", shade[0],shade[1],shade[2]);
2197 | else if (!strcmp(optname, "videocolors"))
2198 | Sprintf(buf, "%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d",
2199 | ttycolors[CLR_RED], ttycolors[CLR_GREEN],
2200 | ttycolors[CLR_BROWN], ttycolors[CLR_BLUE],
2201 | ttycolors[CLR_MAGENTA], ttycolors[CLR_CYAN],
2202 | ttycolors[CLR_ORANGE], ttycolors[CLR_BRIGHT_GREEN],
2203 | ttycolors[CLR_YELLOW], ttycolors[CLR_BRIGHT_BLUE],
2204 | ttycolors[CLR_BRIGHT_MAGENTA],
2205 | ttycolors[CLR_BRIGHT_CYAN]);
2206 | #endif /* VIDEOSHADES */
2207 | #if 0
2208 | else if (!strcmp(optname, "warnlevel"))
2209 | Sprintf(buf, "%d", flags.warnlevel);
2210 | #endif
2211 | else if (!strcmp(optname, "windowtype"))
2212 | Sprintf(buf, "%s", windowprocs.name);
2213 | #ifdef PREFIXES_IN_USE
2214 | else {
2215 | for (i = 0; i < PREFIX_COUNT; ++i)
2216 | if (!strcmp(optname, fqn_prefix_names[i]) && fqn_prefix[i])
2217 | Sprintf(buf, "%s", fqn_prefix[i]);
2218 | }
2219 | #endif
2220 |
2221 | if (buf[0]) return buf;
2222 | else return "unknown";
2223 | }
2224 |
2225 | int
2226 | dotogglepickup()
2227 | {
2228 | char buf[BUFSZ], ocl[MAXOCLASSES+1];
2229 |
2230 | flags.pickup = !flags.pickup;
2231 | if (flags.pickup) {
2232 | oc_to_str(flags.pickup_types, ocl);
2233 | Sprintf(buf, "ON, for %s objects", ocl[0] ? ocl : "all");
2234 | } else {
2235 | Strcpy(buf, "OFF");
2236 | }
2237 | pline("Autopickup: %s.", buf);
2238 | return 0;
2239 | }
2240 |
2241 | /* data for option_help() */
2242 | static const char *opt_intro[] = {
2243 | "",
2244 | " NetHack Options Help:",
2245 | "",
2246 | #define CONFIG_SLOT 3 /* fill in next value at run-time */
2247 | (char *)0,
2248 | #if !defined(MICRO) && !defined(MAC)
2249 | "or use `NETHACKOPTIONS=\"<options>\"' in your environment",
2250 | #endif
2251 | "(<options> is a list of options separated by commas)",
2252 | #ifdef VMS
2253 | "-- for example, $ DEFINE NETHACKOPTIONS \"noautopickup,fruit:kumquat\"",
2254 | #endif
2255 | "or press \"O\" while playing and use the menu.",
2256 | "",
2257 | "Boolean options (which can be negated by prefixing them with '!' or \"no\"):",
2258 | (char *)0
2259 | };
2260 |
2261 | static const char *opt_epilog[] = {
2262 | "",
2263 | "Some of the options can be set only before the game is started; those",
2264 | "items will not be selectable in the 'O' command's menu.",
2265 | (char *)0
2266 | };
2267 |
2268 | void
2269 | option_help()
2270 | {
2271 | char buf[BUFSZ], buf2[BUFSZ];
2272 | register int i;
2273 | winid datawin;
2274 |
2275 | datawin = create_nhwindow(NHW_TEXT);
2276 | #ifdef AMIGA
2277 | if (FromWBench)
2278 | Sprintf(buf, "Set options as OPTIONS= in %s or in icon", configfile);
2279 | else
2280 | #endif
2281 | Sprintf(buf, "Set options as OPTIONS=<options> in %s", configfile);
2282 | opt_intro[CONFIG_SLOT] = (const char *) buf;
2283 | for (i = 0; opt_intro[i]; i++)
2284 | putstr(datawin, 0, opt_intro[i]);
2285 |
2286 | /* Boolean options */
2287 | for (i = 0; boolopt[i].name; i++) {
2288 | if (boolopt[i].addr) {
2289 | #ifdef WIZARD
2290 | if (boolopt[i].addr == &iflags.sanity_check && !wizard) continue;
2291 | #endif
2292 | next_opt(datawin, boolopt[i].name);
2293 | }
2294 | }
2295 | next_opt(datawin, "");
2296 |
2297 | /* Compound options */
2298 | putstr(datawin, 0, "Compound options:");
2299 | for (i = 0; compopt[i].name; i++) {
2300 | Sprintf(buf2, "`%s'", compopt[i].name);
2301 | Sprintf(buf, "%-20s - %s%c", buf2, compopt[i].descr,
2302 | compopt[i+1].name ? ',' : '.');
2303 | putstr(datawin, 0, buf);
2304 | }
2305 |
2306 | for (i = 0; opt_epilog[i]; i++)
2307 | putstr(datawin, 0, opt_epilog[i]);
2308 |
2309 | display_nhwindow(datawin, FALSE);
2310 | destroy_nhwindow(datawin);
2311 | return;
2312 | }
2313 |
2314 | /*
2315 | * prints the next boolean option, on the same line if possible, on a new
2316 | * line if not. End with next_opt("").
2317 | */
2318 | void
2319 | next_opt(datawin, str)
2320 | winid datawin;
2321 | const char *str;
2322 | {
2323 | static char *buf = 0;
2324 | int i;
2325 | char *s;
2326 |
2327 | if (!buf) *(buf = (char *)alloc(BUFSZ)) = '\0';
2328 |
2329 | if (!*str) {
2330 | s = eos(buf);
2331 | if (s > &buf[1] && s[-2] == ',')
2332 | Strcpy(s - 2, "."); /* replace last ", " */
2333 | i = COLNO; /* (greater than COLNO - 2) */
2334 | } else {
2335 | i = strlen(buf) + strlen(str) + 2;
2336 | }
2337 |
2338 | if (i > COLNO - 2) { /* rule of thumb */
2339 | putstr(datawin, 0, buf);
2340 | buf[0] = 0;
2341 | }
2342 | if (*str) {
2343 | Strcat(buf, str);
2344 | Strcat(buf, ", ");
2345 | } else {
2346 | putstr(datawin, 0, str);
2347 | free(buf), buf = 0;
2348 | }
2349 | return;
2350 | }
2351 |
2352 | /* Returns the fid of the fruit type; if that type already exists, it
2353 | * returns the fid of that one; if it does not exist, it adds a new fruit
2354 | * type to the chain and returns the new one.
2355 | */
2356 | int
2357 | fruitadd(str)
2358 | char *str;
2359 | {
2360 | register int i;
2361 | register struct fruit *f;
2362 | struct fruit *lastf = 0;
2363 | int highest_fruit_id = 0;
2364 | char buf[PL_FSIZ];
2365 | boolean user_specified = (str == pl_fruit);
2366 | /* if not user-specified, then it's a fruit name for a fruit on
2367 | * a bones level...
2368 | */
2369 |
2370 | /* Note: every fruit has an id (spe for fruit objects) of at least
2371 | * 1; 0 is an error.
2372 | */
2373 | if (user_specified) {
2374 | /* disallow naming after other foods (since it'd be impossible
2375 | * to tell the difference)
2376 | */
2377 |
2378 | boolean found = FALSE, numeric = FALSE;
2379 |
2380 | for (i = bases[FOOD_CLASS]; objects[i].oc_class == FOOD_CLASS;
2381 | i++) {
2382 | if (!strcmp(OBJ_NAME(objects[i]), pl_fruit)) {
2383 | found = TRUE;
2384 | break;
2385 | }
2386 | }
2387 | {
2388 | char *c;
2389 |
2390 | c = pl_fruit;
2391 |
2392 | for(c = pl_fruit; *c >= '0' && *c <= '9'; c++)
2393 | ;
2394 | if (isspace(*c) || *c == 0) numeric = TRUE;
2395 | }
2396 | if (found || numeric ||
2397 | !strncmp(str, "cursed ", 7) ||
2398 | !strncmp(str, "uncursed ", 9) ||
2399 | !strncmp(str, "blessed ", 8) ||
2400 | !strncmp(str, "partly eaten ", 13) ||
2401 | (!strncmp(str, "tin of ", 7) &&
2402 | (!strcmp(str+7, "spinach") ||
2403 | name_to_mon(str+7) >= LOW_PM)) ||
2404 | !strcmp(str, "empty tin") ||
2405 | ((!strncmp(eos(str)-7," corpse",7) ||
2406 | !strncmp(eos(str)-4, " egg",4)) &&
2407 | name_to_mon(str) >= LOW_PM))
2408 | {
2409 | Strcpy(buf, pl_fruit);
2410 | Strcpy(pl_fruit, "candied ");
2411 | nmcpy(pl_fruit+8, buf, PL_FSIZ-8);
2412 | }
2413 | }
2414 | for(f=ffruit; f; f = f->nextf) {
2415 | lastf = f;
2416 | if(f->fid > highest_fruit_id) highest_fruit_id = f->fid;
2417 | if(!strncmp(str, f->fname, PL_FSIZ))
2418 | goto nonew;
2419 | }
2420 | /* if adding another fruit would overflow spe, use a random
2421 | fruit instead... we've got a lot to choose from. */
2422 | if (highest_fruit_id >= 127) return rnd(127);
2423 | highest_fruit_id++;
2424 | f = newfruit();
2425 | if (ffruit) lastf->nextf = f;
2426 | else ffruit = f;
2427 | Strcpy(f->fname, str);
2428 | f->fid = highest_fruit_id;
2429 | f->nextf = 0;
2430 | nonew:
2431 | if (user_specified) current_fruit = highest_fruit_id;
2432 | return f->fid;
2433 | }
2434 |
2435 | /*
2436 | * This is a somewhat generic menu for taking a list of NetHack style
2437 | * class choices and presenting them via a description
2438 | * rather than the traditional NetHack characters.
2439 | * (Benefits users whose first exposure to NetHack is via tiles).
2440 | *
2441 | * prompt
2442 | * The title at the top of the menu.
2443 | *
2444 | * category: 0 = monster class
2445 | * 1 = object class
2446 | *
2447 | * way
2448 | * FALSE = PICK_ONE, TRUE = PICK_ANY
2449 | *
2450 | * class_list
2451 | * a null terminated string containing the list of choices.
2452 | *
2453 | * class_selection
2454 | * a null terminated string containing the selected characters.
2455 | *
2456 | * Returns number selected.
2457 | */
2458 | int
2459 | choose_classes_menu(prompt, category, way, class_list, class_select)
2460 | const char *prompt;
2461 | int category;
2462 | boolean way;
2463 | char *class_list;
2464 | char *class_select;
2465 | {
2466 | menu_item *pick_list = (menu_item *)0;
2467 | winid win;
2468 | anything any;
2469 | char buf[BUFSZ];
2470 | int i, n;
2471 | int ret;
2472 | int next_accelerator, accelerator;
2473 |
2474 | if (class_list == (char *)0 || class_select == (char *)0) return 0;
2475 | accelerator = 0;
2476 | next_accelerator = 'a';
2477 | any.a_void = 0;
2478 | win = create_nhwindow(NHW_MENU);
2479 | start_menu(win);
2480 | while (*class_list) {
2481 | const char *text;
2482 | boolean selected;
2483 |
2484 | text = (char *)0;
2485 | selected = FALSE;
2486 | switch (category) {
2487 | case 0:
2488 | text = monexplain[def_char_to_monclass(*class_list)];
2489 | accelerator = *class_list;
2490 | Sprintf(buf, "%s", text);
2491 | break;
2492 | case 1:
2493 | text = objexplain[def_char_to_objclass(*class_list)];
2494 | accelerator = next_accelerator;
2495 | Sprintf(buf, "%c %s", *class_list, text);
2496 | break;
2497 | default:
2498 | impossible("choose_classes_menu: invalid category %d",
2499 | category);
2500 | }
2501 | if (way && *class_select) { /* Selections there already */
2502 | if (index(class_select, *class_list)) {
2503 | selected = TRUE;
2504 | }
2505 | }
2506 | any.a_int = *class_list;
2507 | add_menu(win, NO_GLYPH, &any, accelerator,
2508 | category ? *class_list : 0,
2509 | ATR_NONE, buf, selected);
2510 | ++class_list;
2511 | if (category > 0) {
2512 | ++next_accelerator;
2513 | if (next_accelerator == ('z' + 1)) next_accelerator = 'A';
2514 | if (next_accelerator == ('Z' + 1)) break;
2515 | }
2516 | }
2517 | end_menu(win, prompt);
2518 | n = select_menu(win, way ? PICK_ANY : PICK_ONE, &pick_list);
2519 | destroy_nhwindow(win);
2520 | if (n > 0) {
2521 | for (i = 0; i < n; ++i)
2522 | *class_select++ = (char)pick_list[i].item.a_int;
2523 | free((genericptr_t)pick_list);
2524 | ret = n;
2525 | } else if (n == -1) {
2526 | class_select = eos(class_select);
2527 | ret = -1;
2528 | } else
2529 | ret = 0;
2530 | *class_select = '\0';
2531 | return ret;
2532 | }
2533 |
2534 | #endif /* OPTION_LISTS_ONLY */
2535 |
2536 | /*options.c*/