Source:NetHack 3.3.0/options.c

From NetHackWiki
Jump to navigation Jump to search

Below is the full text to options.c from the source code of NetHack 3.3.0. To link to a particular line, write [[NetHack 3.3.0/options.c#line123]], for example.

Warning! This is the source code from an old release. For the latest release, see Source code

The NetHack General Public License applies to screenshots, source code and other content from NetHack.

This content was modified from the original NetHack source code distribution (by splitting up NetHack content between wiki pages, and possibly further editing). See the page history for a list of who changed it, and on what dates.

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