home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Games / NetHack 3.1.3 / source / src / options.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-08-01  |  32.5 KB  |  1,323 lines  |  [TEXT/R*ch]

  1. /*    SCCS Id: @(#)options.c    3.1    93/06/27    */
  2. /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
  3. /* NetHack may be freely redistributed.  See license for details. */
  4.  
  5. #ifdef OPTION_LISTS_ONLY    /* want option lists for external program */
  6. #include "config.h"
  7. #include "objclass.h"
  8. #include "flag.h"
  9. NEARDATA struct flag flags;    /* provide linkage */
  10. #define static
  11. #else
  12. #include "hack.h"
  13. #include "termcap.h"
  14. #include <ctype.h>
  15. #endif
  16.  
  17. /*
  18.  *  NOTE:  If you add (or delete) an option, please update the short
  19.  *  options help (option_help()), the long options help (dat/opthelp),
  20.  *  and the current options setting display function (doset()),
  21.  *  and also the Guidebooks.
  22.  */
  23.  
  24. static struct Bool_Opt
  25. {
  26.     const char *name;
  27.     boolean    *addr, initvalue;
  28. } boolopt[] = {
  29. #ifdef MFLOPPY
  30.     {"asksavedisk", &flags.asksavedisk, FALSE},
  31. #else
  32.     {"asksavedisk", (boolean *)0, FALSE},
  33. #endif
  34.     {"autopickup", &flags.pickup, TRUE},
  35. #if defined(MICRO) && !defined(AMIGA)
  36.     {"BIOS", &flags.BIOS, FALSE},
  37. #else
  38.     {"BIOS", (boolean *)0, FALSE},
  39. #endif
  40. #ifdef INSURANCE
  41.     {"checkpoint", &flags.ins_chkpt, TRUE},
  42. #else
  43.     {"checkpoint", (boolean *)0, FALSE},
  44. #endif
  45. #ifdef TEXTCOLOR
  46. # ifdef MICRO
  47.     {"color", &flags.use_color, TRUE},
  48. # else    /* systems that support multiple terminals, many monochrome */
  49.     {"color", &flags.use_color, FALSE},
  50. # endif
  51. #else
  52.     {"color", (boolean *)0, FALSE},
  53. #endif
  54.     {"confirm",&flags.confirm, TRUE},
  55. #ifdef TERMLIB
  56.     {"DECgraphics", &flags.DECgraphics, FALSE},
  57. #else
  58.     {"DECgraphics", (boolean *)0, FALSE},
  59. #endif
  60.     {"female", &flags.female, FALSE},
  61.     {"fixinv", &flags.invlet_constant, TRUE},
  62. #ifdef AMIFLUSH
  63.     {"flush", &flags.amiflush, FALSE},
  64. #else
  65.     {"flush", (boolean *)0, FALSE},
  66. #endif
  67.     {"help", &flags.help, TRUE},
  68. #ifdef TEXTCOLOR
  69.     {"hilite_pet", &flags.hilite_pet, FALSE},
  70. #else
  71.     {"hilite_pet", (boolean *)0, FALSE},
  72. #endif
  73. #ifdef ASCIIGRAPH
  74.     {"IBMgraphics", &flags.IBMgraphics, FALSE},
  75. #else
  76.     {"IBMgraphics", (boolean *)0, FALSE},
  77. #endif
  78.     {"ignintr", &flags.ignintr, FALSE},
  79. #ifdef MAC_GRAPHICS_ENV
  80.     {"large_font", &flags.large_font, FALSE},
  81. #else
  82.     {"large_font", (boolean *)0, FALSE},
  83. #endif
  84.     {"legacy",&flags.legacy, TRUE},
  85.     {"lit_corridor", &flags.lit_corridor, FALSE},
  86. #ifdef MAC_GRAPHICS_ENV
  87.     {"Macgraphics", &flags.MACgraphics, TRUE},
  88. #else
  89.     {"Macgraphics", (boolean *)0, FALSE},
  90. #endif
  91. #ifdef NEWS
  92.     {"news", &flags.news, TRUE},
  93. #else
  94.     {"news", (boolean *)0, FALSE},
  95. #endif
  96.     {"null", &flags.null, TRUE},
  97.     {"number_pad", &flags.num_pad, FALSE},
  98. #ifdef MAC
  99.     {"page_wait", &flags.page_wait, TRUE},
  100. #else
  101.     {"page_wait", (boolean *)0, FALSE},
  102. #endif
  103. #ifdef MAC
  104.     {"popup_dialog", &flags.popup_dialog, FALSE},
  105. #else
  106.     {"popup_dialog", (boolean *)0, FALSE},
  107. #endif
  108. #if defined(MICRO) && !defined(AMIGA)
  109.     {"rawio", &flags.rawio, FALSE},
  110. #else
  111.     {"rawio", (boolean *)0, FALSE},
  112. #endif
  113.     {"rest_on_space", &flags.rest_on_space, FALSE},
  114.     {"safe_pet", &flags.safe_dog, TRUE},
  115. #ifdef EXP_ON_BOTL
  116.     {"showexp", &flags.showexp, FALSE},
  117. #else
  118.     {"showexp", (boolean *)0, FALSE},
  119. #endif
  120. #ifdef SCORE_ON_BOTL
  121.     {"showscore", &flags.showscore, FALSE},
  122. #else
  123.     {"showscore", (boolean *)0, FALSE},
  124. #endif
  125.     {"silent", &flags.silent, TRUE},
  126.     {"sortpack", &flags.sortpack, TRUE},
  127.     {"sound", &flags.soundok, TRUE},
  128.     {"standout", &flags.standout, FALSE},
  129.     {"time", &flags.time, FALSE},
  130.     {"tombstone",&flags.tombstone, TRUE},
  131.     {"verbose", &flags.verbose, TRUE},
  132.     {NULL, (boolean *)0, FALSE}
  133. };
  134.  
  135. /* compound options, for option_help() and external programs like Amiga
  136.  * frontend */
  137. static struct Comp_Opt
  138. {
  139.     const char *name, *descr;
  140. } compopt[] = {
  141.     { "catname",  "the name of your (first) cat (e.g., catname:Tabby)," },
  142.     { "disclose", "the kinds of information to disclose at end of game," },
  143.     { "dogname",  "the name of your (first) dog (e.g., dogname:Fang)," },
  144. #ifdef TUTTI_FRUTTI
  145.     { "fruit",    "the name of a fruit you enjoy eating," },
  146. #endif
  147.     { "graphics", "the symbols to use in drawing the dungeon map," },
  148.     { "monsters", "the symbols to use for monsters," },
  149.     { "msghistory", "number of top line messages to save," },
  150.     { "name",     "your character's name (e.g., name:Merlin-W)," },
  151.     { "objects",  "the symbols to use for objects," },
  152.     { "packorder", "the inventory order of the items in your pack," },
  153. #ifdef CHANGE_COLOR
  154.     { "palette",  "palette (00c/880/-fff is blue/yellow/reverse white)," },
  155. # if defined(MAC)
  156.     { "hicolor",  "same as palette, only order is reversed," },
  157. # endif
  158. #endif
  159.     { "pettype",  "your preferred initial pet type," },
  160.     { "pickup_types", "types of objects to pick up automatically," },
  161.     { "scores",   "the parts of the score list you wish to see," },
  162. #ifdef VIDEOSHADES
  163.     { "videocolors", "color mappings for internal screen routines," },
  164.     { "videoshades", "gray shades to map to black/gray/white," },
  165. #endif
  166.     { "windowtype", "windowing system to use." },
  167.     { NULL, NULL }
  168. };
  169.  
  170. #ifndef OPTION_LISTS_ONLY    /* use rest of file */
  171.  
  172. static boolean need_redraw; /* for doset() */
  173.  
  174. #if defined(TOS) && defined(TEXTCOLOR)
  175. extern boolean colors_changed;    /* in tos.c */
  176. #endif
  177.  
  178. #ifdef VIDEOSHADES
  179. extern char *shade[3];          /* in sys/msdos/video.c */
  180. extern char ttycolors[MAXCOLORS]; /* in sys/msdos/video.c */
  181. #endif
  182.  
  183. extern const char *roles[];    /* from u_init.c */
  184.  
  185. static char def_inv_order[MAXOCLASSES] = {
  186.     AMULET_CLASS, WEAPON_CLASS, ARMOR_CLASS, FOOD_CLASS, SCROLL_CLASS,
  187.     SPBOOK_CLASS, POTION_CLASS, RING_CLASS, WAND_CLASS, TOOL_CLASS, 
  188.     GEM_CLASS, ROCK_CLASS, BALL_CLASS, CHAIN_CLASS, 0,
  189. };
  190.  
  191. static boolean initial, from_file;
  192.  
  193. static void FDECL(nmcpy, (char *, const char *, int));
  194. static void FDECL(escapes, (const char *, char *));
  195. static void FDECL(rejectoption, (const char *));
  196. static void FDECL(badoption, (const char *));
  197. static char *FDECL(string_for_opt, (char *,BOOLEAN_P));
  198. static char *FDECL(string_for_env_opt, (const char *, char *,BOOLEAN_P));
  199. static int FDECL(change_inv_order, (char *));
  200. static void FDECL(oc_to_str, (char *, char *));
  201.  
  202. void
  203. initoptions()
  204. {
  205.     register char *opts;
  206.     int i;
  207.  
  208.     for (i = 0; boolopt[i].name; i++) {
  209.         if (boolopt[i].addr)
  210.             *(boolopt[i].addr) = boolopt[i].initvalue;
  211.     }
  212.     flags.end_own = FALSE;
  213.     flags.end_top = 3;
  214.     flags.end_around = 2;
  215.     flags.msg_history = 20;
  216.  
  217.     /* Set the default monster and object class symbols.  Don't use */
  218.     /* memcpy() --- sizeof char != sizeof uchar on some machines.    */
  219.     for (i = 0; i < MAXOCLASSES; i++)
  220.         oc_syms[i] = (uchar) def_oc_syms[i];
  221.     for (i = 0; i < MAXMCLASSES; i++)
  222.         monsyms[i] = (uchar) def_monsyms[i];
  223.  
  224.      /* assert( sizeof flags.inv_order == sizeof def_inv_order ); */
  225.     (void)memcpy((genericptr_t)flags.inv_order,
  226.              (genericptr_t)def_inv_order, sizeof flags.inv_order);
  227.     flags.pickup_types[0] = '\0';
  228.  
  229.     switch_graphics(ASCII_GRAPHICS);    /* set default characters */
  230. #if defined(UNIX) && defined(TTY_GRAPHICS)
  231.     /*
  232.      * Set defaults for some options depending on what we can
  233.      * detect about the environment's capabilities.
  234.      * This has to be done after the global initialization above
  235.      * and before reading user-specific initialization via
  236.      * config file/environment variable below.
  237.      */
  238.     /* this detects the IBM-compatible console on most 386 boxes */
  239.     if (!strncmp(getenv("TERM"), "AT", 2)) {
  240.         switch_graphics(IBM_GRAPHICS);
  241. # ifdef TEXTCOLOR
  242.         flags.use_color = TRUE;
  243. # endif
  244.     }
  245. #endif /* UNIX && TTY_GRAPHICS */
  246. #if defined(UNIX) || defined(VMS)
  247. # ifdef TTY_GRAPHICS
  248.     /* detect whether a "vt" terminal can handle alternate charsets */
  249.     if (!strncmpi(getenv("TERM"), "vt", 2) && (AS && AE) &&
  250.         index(AS, '\016') && index(AE, '\017')) {
  251.         switch_graphics(DEC_GRAPHICS);
  252.     }
  253. # endif
  254. #endif /* UNIX || VMS */
  255.  
  256. #ifdef MAC_GRAPHICS_ENV
  257.     switch_graphics(MAC_GRAPHICS);
  258. #endif /* MAC_GRAPHICS_ENV */
  259.  
  260. #ifdef TUTTI_FRUTTI
  261.     /* since this is done before init_objects(), do partial init here */
  262.     objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD;
  263.     nmcpy(pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ);
  264. #endif
  265.     opts = getenv("NETHACKOPTIONS");
  266.     if (!opts) opts = getenv("HACKOPTIONS");
  267.     if (opts) {
  268.         if (*opts == '/' || *opts == '\\' || *opts == '@') {
  269.             if (*opts == '@') opts++;    /* @filename */
  270.             /* looks like a filename */
  271.             read_config_file(opts);
  272.         } else {
  273.             read_config_file(NULL);
  274.             parseoptions(opts, TRUE, FALSE);
  275.         }
  276.     } else {
  277.         read_config_file(NULL);
  278.     }
  279. #ifdef AMIGA
  280.     ami_wbench_init();    /* must be here or can't set fruit */
  281. #endif
  282. #ifdef TUTTI_FRUTTI
  283.     (void)fruitadd(pl_fruit);
  284.     /* Remove "slime mold" from list of object names; this will    */
  285.     /* prevent it from being wished unless it's actually present    */
  286.     /* as a named (or default) fruit.  Wishing for "fruit" will    */
  287.     /* result in the player's preferred fruit [better than "\033"].    */
  288.     obj_descr[SLIME_MOLD].oc_name = "fruit";
  289. #endif
  290.     if (flags.female)  {    /* should have been set in NETHACKOPTIONS */
  291.         roles[2] = "Cavewoman";
  292.         roles[6] = "Priestess";
  293.     }
  294. }
  295.  
  296. static void
  297. nmcpy(dest, src, maxlen)
  298.     char    *dest;
  299.     const char *src;
  300.     int    maxlen;
  301. {
  302.     int    count;
  303.  
  304.     for(count = 1; count < maxlen; count++) {
  305.         if(*src == ',' || *src == '\0') break; /*exit on \0 terminator*/
  306.         *dest++ = *src++;
  307.     }
  308.     *dest = 0;
  309. }
  310.  
  311. /*
  312.  * escapes: escape expansion for showsyms. C-style escapes understood include
  313.  * \n, \b, \t, \r, \xnnn (hex), \onnn (octal), \nnn (decimal). The ^-prefix
  314.  * for control characters is also understood, and \[mM] followed by any of the
  315.  * previous forms or by a character has the effect of 'meta'-ing the value (so
  316.  * that the alternate character set will be enabled).
  317.  */
  318. static void
  319. escapes(cp, tp)
  320. const char    *cp;
  321. char *tp;
  322. {
  323.     while (*cp)
  324.     {
  325.     int    cval = 0, meta = 0;
  326.  
  327.     if (*cp == '\\' && index("mM", cp[1])) {
  328.         meta = 1;
  329.         cp += 2;
  330.     }
  331.     if (*cp == '\\' && index("0123456789xXoO", cp[1]))
  332.     {
  333.         const char *dp, *hex = "00112233445566778899aAbBcCdDeEfF";
  334.         int dcount = 0;
  335.  
  336.         cp++;
  337.         if (*cp == 'x' || *cp == 'X')
  338.         for (++cp; (dp = index(hex, *cp)) && (dcount++ < 2); cp++)
  339.             cval = (cval * 16) + (dp - hex) / 2;
  340.         else if (*cp == 'o' || *cp == 'O')
  341.         for (++cp; (index("01234567",*cp)) && (dcount++ < 3); cp++)
  342.             cval = (cval * 8) + (*cp - '0');
  343.         else
  344.         for (; (index("0123456789",*cp)) && (dcount++ < 3); cp++)
  345.             cval = (cval * 10) + (*cp - '0');
  346.     }
  347.     else if (*cp == '\\')        /* C-style character escapes */
  348.     {
  349.         switch (*++cp)
  350.         {
  351.         case '\\': cval = '\\'; break;
  352.         case 'n': cval = '\n'; break;
  353.         case 't': cval = '\t'; break;
  354.         case 'b': cval = '\b'; break;
  355.         case 'r': cval = '\r'; break;
  356.         default: cval = *cp;
  357.         }
  358.         cp++;
  359.     }
  360.     else if (*cp == '^')        /* expand control-character syntax */
  361.     {
  362.         cval = (*++cp & 0x1f);
  363.         cp++;
  364.     }
  365.     else
  366.         cval = *cp++;
  367.     if (meta)
  368.         cval |= 0x80;
  369.     *tp++ = cval;
  370.     }
  371.     *tp = '\0';
  372. }
  373.  
  374. static void
  375. rejectoption(optname)
  376. const char *optname;
  377. {
  378. #ifdef MICRO
  379. # ifdef AMIGA
  380.     if(FromWBench){
  381.         pline("\"%s\" settable only from %s or in icon.",
  382.             optname, configfile);
  383.     } else
  384. # endif
  385.         pline("\"%s\" settable only from %s.", optname, configfile);
  386. #else
  387.     pline("%s can be set only from NETHACKOPTIONS or %s.", optname,
  388.             configfile);
  389. #endif
  390. }
  391.  
  392. static void
  393. badoption(opts)
  394. const char *opts;
  395. {
  396.     if (!initial) {
  397.         if (!strncmp(opts, "h", 1) || !strncmp(opts, "?", 1))
  398.         option_help();
  399.         else
  400.         pline("Bad syntax: %s.  Enter \"?g\" for help.", opts);
  401.         return;
  402.     }
  403. # ifdef AMIGA
  404.     if(ami_wbench_badopt(opts)) {
  405. # endif
  406.     if(from_file)
  407.         raw_printf("Bad syntax in OPTIONS in %s: %s.", configfile, opts);
  408.     else
  409.         raw_printf("Bad syntax in NETHACKOPTIONS: %s.", opts);
  410. # ifdef AMIGA
  411.     }
  412. # endif
  413.     wait_synch();
  414. }
  415.  
  416. static char *
  417. string_for_opt(opts, val_optional)
  418. char *opts;
  419. boolean val_optional;
  420. {
  421.     register char *colon;
  422.  
  423.     colon = index(opts, ':');
  424.     if(!colon) {
  425.         if (!val_optional) badoption(opts);
  426.         return NULL;
  427.     }
  428.     return ++colon;
  429. }
  430.  
  431. static char *
  432. string_for_env_opt(optname, opts, val_optional)
  433. const char *optname;
  434. char *opts;
  435. boolean val_optional;
  436. {
  437.     if(!initial) {
  438.         rejectoption(optname);
  439.         return NULL;
  440.     }
  441.     return string_for_opt(opts, val_optional);
  442. }
  443.  
  444. /*
  445.  * Change the inventory order, using the given string as the new order.
  446.  * Missing characters in the new order are filled in at the end from
  447.  * the current inv_order.
  448.  *
  449.  * This routine returns 1 unless there is a duplicate or bad char in
  450.  * the string.
  451.  */
  452. static int
  453. change_inv_order(op)
  454. char *op;
  455. {
  456.     int oc_sym, num;
  457.     char *sp, buf[BUFSZ];
  458.  
  459.     for (sp = op; *sp; sp++) {
  460.     oc_sym = def_char_to_objclass(*sp);
  461.  
  462.     /* Remove bad or duplicate entries. */
  463.     if (oc_sym == MAXOCLASSES ||
  464.         (!index(flags.inv_order, oc_sym)) || (index(sp+1, *sp)))
  465.  
  466.         return 0;
  467.  
  468.     *sp = (char) oc_sym;
  469.     } 
  470.     Strcpy(buf, op);
  471.     for (sp = flags.inv_order, num = strlen(buf); *sp; sp++)
  472.     if (!index(buf, *sp)) {
  473.         buf[num++] = *sp;
  474.         buf[num] = '\0';    /* explicitly terminate for next index() */
  475.     }
  476.  
  477.     buf[num] = 0;
  478.     Strcpy(flags.inv_order, buf);
  479.     return 1;
  480. }
  481.  
  482. void
  483. parseoptions(opts, tinitial, tfrom_file)
  484. register char *opts;
  485. boolean tinitial, tfrom_file;
  486. {
  487.     register char *op;
  488.     unsigned num;
  489.     boolean negated;
  490.     int i;
  491.  
  492.     initial = tinitial;
  493.     from_file = tfrom_file;
  494.     if ((op = index(opts, ',')) != 0) {
  495.         *op++ = 0;
  496.         parseoptions(op, initial, from_file);
  497.     }
  498.  
  499.     /* strip leading and trailing white space */
  500.     while (isspace(*opts)) opts++;
  501.     op = eos(opts);
  502.     while (--op >= opts && isspace(*op)) *op = '\0';
  503.  
  504.     if (!*opts) return;
  505.     negated = FALSE;
  506.     while ((*opts == '!') || !strncmpi(opts, "no", 2)) {
  507.         if (*opts == '!') opts++; else opts += 2;
  508.         negated = !negated;
  509.     }
  510.     
  511.     /* special boolean options */
  512.  
  513.     if (!strncmpi(opts, "female", 3)) {
  514.         if(!initial && flags.female == negated)
  515.             pline("That is not anatomically possible.");
  516.         else
  517.             flags.female = !negated;
  518.         return;
  519.     }
  520.  
  521.     if (!strncmpi(opts, "male", 4)) {
  522.         if(!initial && flags.female != negated)
  523.             pline("That is not anatomically possible.");
  524.         else
  525.             flags.female = negated;
  526.         return;
  527.     }
  528.  
  529. #if defined(MICRO) && !defined(AMIGA)
  530.     /* included for compatibility with old NetHack.cnf files */
  531.     if (!strncmp(opts, "IBM_", 4)) {
  532.         flags.BIOS = !negated;
  533.         return;
  534.     }
  535. #endif /* MICRO */
  536.  
  537.     /* compound options */
  538.  
  539.     if (!strncmpi(opts, "pettype", 3)) {
  540.         if ((op = string_for_env_opt("pettype", opts, FALSE)) != 0)
  541.             switch (*op) {
  542.             case 'd':    /* dog */
  543.             case 'D':
  544.                 preferred_pet = 'd';
  545.                 break;
  546.             case 'c':    /* cat */
  547.             case 'C':
  548.             case 'f':    /* feline */
  549.             case 'F':
  550.                 preferred_pet = 'c';
  551.                 break;
  552.             default:
  553.                 pline("Unrecognized pet type '%s'", op);
  554.                 break;
  555.             }
  556.         return;
  557.     }
  558.  
  559.     if (!strncmpi(opts, "catname", 3)) {
  560.         if ((op = string_for_env_opt("catname", opts, FALSE)) != 0)
  561.             nmcpy(catname, op, 62);
  562.         return;
  563.     }
  564.  
  565.     if (!strncmpi(opts, "dogname", 3)) {
  566.         if ((op = string_for_env_opt("dogname", opts, FALSE)) != 0)
  567.             nmcpy(dogname, op, 62);
  568.         return;
  569.     }
  570.  
  571.     if (!strncmpi(opts, "msghistory", 3)) {
  572.         if ((op = string_for_env_opt("msghistory", opts, FALSE)) != 0) {
  573.             flags.msg_history = atoi(op);
  574.         }
  575.         return;
  576.     }
  577.  
  578. #ifdef CHANGE_COLOR
  579.     if (!strncmpi(opts, "palette", 3)
  580. # ifdef MAC
  581.                     || !strncmpi(opts, "hicolor", 3)
  582. # endif
  583.                                     ) {
  584.         int color_number, color_incr;
  585. # ifdef MAC
  586.         if (!strncmpi(opts, "hicolor", 3)) {
  587.         color_number = MAXCOLORS + 4;    /* HARDCODED inverse number */
  588.         color_incr = -1;
  589.         } else {
  590. # endif
  591.         color_number = 0;
  592.         color_incr = 1;
  593. # ifdef MAC
  594.         }
  595. # endif
  596.         if ((op = string_for_opt(opts, FALSE)) != NULL) {
  597.         char *pt = op;
  598.         int cnt, tmp, reverse;
  599.         long rgb;
  600.  
  601.         while (*pt && color_number >= 0) {
  602.             cnt = 3;
  603.             rgb = 0L;
  604.             if (*pt == '-') {
  605.             reverse = 1;
  606.             pt++;
  607.             } else {
  608.             reverse = 0;
  609.             }
  610.             while (cnt-- > 0) {
  611.             if (*pt && *pt != '/') {
  612. # ifdef AMIGA
  613.                 rgb <<= 4;
  614. # else
  615.                 rgb <<= 8;
  616. # endif
  617.                 tmp = *(pt++);
  618.                 if (isalpha(tmp)) {
  619.                 tmp = (tmp + 9) & 0xf;    /* Assumes ASCII... */
  620.                 } else {
  621.                 tmp &= 0xf;    /* Digits in ASCII too... */
  622.                 }
  623. # ifndef AMIGA
  624.                 /* Add an extra so we fill f -> ff and 0 -> 00 */
  625.                 rgb += tmp << 4;
  626. # endif
  627.                 rgb += tmp;
  628.             }
  629.             }
  630.             if (*pt == '/') {
  631.             pt++;
  632.             }
  633.             change_color(color_number, rgb, reverse);
  634.             color_number += color_incr;
  635.         }
  636.         }
  637.         if (!initial) {
  638.         need_redraw = TRUE;
  639.         }
  640.         return;
  641.     }
  642. #endif
  643.  
  644. #ifdef TUTTI_FRUTTI
  645.     if (!strncmpi(opts, "fruit", 2)) {
  646.         if (!(op = string_for_opt(opts, FALSE))) return;
  647.         if (!initial) {
  648.             struct fruit *f;
  649.  
  650.             num = 0;
  651.             for(f=ffruit; f; f=f->nextf) {
  652.             if (!strcmp(op, f->fname)) goto goodfruit;
  653.             num++;
  654.             }
  655.             if (num >= 100) {
  656.             pline("Doing that so many times isn't very fruitful.");
  657.             return;
  658.             }
  659.         }
  660. goodfruit:
  661.         nmcpy(pl_fruit, op, PL_FSIZ);
  662.         if (!*pl_fruit)
  663.             nmcpy(pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ);
  664.         if (!initial)
  665.             (void)fruitadd(pl_fruit);
  666.         /* If initial, then initoptions is allowed to do it instead
  667.          * of here (initoptions always has to do it even if there's
  668.          * no fruit option at all.  Also, we don't want people
  669.          * setting multiple fruits in their options.)
  670.          */
  671.         return;
  672.     }
  673. #endif
  674.     /* graphics:string */
  675.     if (!strncmpi(opts, "graphics", 2)) {
  676.         uchar translate[MAXPCHARS+1];
  677.         int length;
  678.  
  679.         if (!(opts = string_for_env_opt("graphics", opts, FALSE)))
  680.             return;
  681.         escapes(opts, opts);
  682.  
  683.         length = strlen(opts);
  684.         if (length > MAXPCHARS) length = MAXPCHARS;
  685.         /* match the form obtained from PC configuration files */
  686.         for (i = 0; i < length; i++)
  687.             translate[i] = (uchar) opts[i];
  688.         assign_graphics(translate, length);
  689.         return;
  690.     }
  691.  
  692.     /* objects:string */
  693.     if (!strncmpi(opts, "objects", 7)) {
  694.         int length;
  695.  
  696.         if (!(opts = string_for_env_opt("objects", opts, FALSE)))
  697.             return;
  698.         escapes(opts, opts);
  699.  
  700.         /*
  701.          * Override the default object class symbols.  The first
  702.          * object in the object class is the "random object".  I
  703.          * don't want to use 0 as an object class, so the "random
  704.          * object" is basically a place holder.
  705.          *
  706.          * The object class symbols have already been initialized in
  707.          * initoptions().
  708.          */
  709.         length = strlen(opts);
  710.         if (length >= MAXOCLASSES)
  711.             length = MAXOCLASSES-1;    /* don't count RANDOM_OBJECT */
  712.  
  713.         for (i = 0; i < length; i++)
  714.             oc_syms[i+1] = (uchar) opts[i];
  715.         return;
  716.     }
  717.  
  718.     /* monsters:string */
  719.     if (!strncmpi(opts, "monsters", 8)) {
  720.         int length;
  721.  
  722.         if (!(opts = string_for_env_opt("monsters", opts, FALSE)))
  723.             return;
  724.         escapes(opts, opts);
  725.  
  726.         /* Override default mon class symbols set in initoptions(). */
  727.         length = strlen(opts);
  728.         if (length >= MAXMCLASSES)
  729.             length = MAXMCLASSES-1;    /* mon class 0 unused */
  730.  
  731.         for (i = 0; i < length; i++)
  732.             monsyms[i+1] = (uchar) opts[i];
  733.         return;
  734.     }
  735.  
  736.     /* name:string */
  737.     if (!strncmpi(opts, "name", 4)) {
  738.         if ((op = string_for_env_opt("name", opts, FALSE)) != 0)
  739.             nmcpy(plname, op, (int)sizeof(plname)-1);
  740.         return;
  741.     }
  742.  
  743.     /* the order to list the pack */
  744.     if (!strncmpi(opts, "packorder", 4)) {
  745.         if (!(op = string_for_opt(opts, FALSE))) return;
  746.  
  747.         if (!change_inv_order(op))
  748.             badoption(opts);
  749.         return;
  750.     }
  751.  
  752.     /* types of objects to pick up automatically */
  753.     if (!strncmpi(opts, "pickup_types", 4)) {
  754.         int oc_sym;
  755.         boolean badopt = FALSE, compat = (strlen(opts) <= 6);
  756.  
  757.         flags.pickup_types[0] = '\0';    /* all */
  758.         if (!(op = string_for_opt(opts, compat))) {
  759.             /* for backwards compatibility, "pickup" without a value
  760.                is a synonym for boolean autopickup, and pickup_types
  761.                gets reset to "all"                    */
  762.             flags.pickup = !negated;
  763.             return;
  764.         }
  765.  
  766.         while (*op == ' ') op++;
  767.         if (*op != 'a' && *op != 'A') {
  768.             num = 0;
  769.             while (*op) {
  770.             oc_sym = def_char_to_objclass(*op);
  771.             /* make sure all are valid obj symbols occuring once */
  772.             if (oc_sym != MAXOCLASSES &&
  773.                 !index(flags.pickup_types, oc_sym)) {
  774.                 flags.pickup_types[num] = (char)oc_sym;
  775.                 flags.pickup_types[++num] = '\0';
  776.             } else
  777.                 badopt = TRUE;
  778.             op++;
  779.             }
  780.             if (badopt) badoption(opts);
  781.         }
  782.         return;
  783.     }
  784.  
  785.     /* things to disclose at end of game */
  786.     if (!strncmpi(opts, "disclose", 4)) {
  787.         flags.end_disclose[0] = '\0';    /* all */
  788.         if (!(op = string_for_opt(opts, TRUE))) {
  789.             /* for backwards compatibility, "disclose" without a
  790.              * value means all (was inventory and attributes,
  791.              * the only things available then), but negated
  792.              * it means "none"
  793.              * (note "none" contains none of "iavkg")
  794.              */
  795.             if (negated) Strcpy(flags.end_disclose, "none");
  796.             return;
  797.         }
  798.         num = 0;
  799.         while (*op && num < sizeof flags.end_disclose - 1) {
  800.             register char c;
  801.             c = lowc(*op);
  802.             if (c == 'k') c = 'v';    /* killed -> vanquished */
  803.             if (!index(flags.end_disclose, c)) {
  804.                 flags.end_disclose[num++] = c;
  805.                 flags.end_disclose[num] = '\0';    /* for index */
  806.             }
  807.             op++;
  808.         }
  809.         return;
  810.     }
  811.  
  812.     /* scores:5t[op] 5a[round] o[wn] */
  813.     if (!strncmpi(opts, "scores", 6)) {
  814.         if (!(op = string_for_opt(opts, FALSE))) return;
  815.  
  816.         while (*op) {
  817.             num = 1;
  818.             if(digit(*op)) {
  819.                 num = atoi(op);
  820.                 while(digit(*op)) op++;
  821.             } else if(*op == '!') {
  822.                 negated = !negated;
  823.                 op++;
  824.             }
  825.             while(*op == ' ') op++;
  826.  
  827.             switch(*op) {
  828.                 case 't':
  829.                 case 'T':
  830.                     flags.end_top = num;
  831.                     break;
  832.                 case 'a':
  833.                 case 'A':
  834.                     flags.end_around = num;
  835.                     break;
  836.                 case 'o':
  837.                 case 'O':
  838.                     flags.end_own = !negated;
  839.                     break;
  840.                 default:
  841.                     badoption(opts);
  842.                     return;
  843.             }
  844.             while(letter(*++op) || *op == ' ') ;
  845.             if(*op == '/') op++;
  846.         }
  847.         return;
  848.     }
  849.  
  850. #ifdef VIDEOSHADES
  851.     /* videocolors:string */
  852.     if (!strncmpi(opts, "videocolors", 6)) {
  853.         int length;
  854.  
  855.         if (!(opts = string_for_env_opt("videocolors", opts, FALSE))) {
  856.             return;
  857.         }
  858.         badoption(opts);
  859.         return;
  860.     }
  861.     /* videoshades:string */
  862.     if (!strncmpi(opts, "videoshades", 6)) {
  863.         int length;
  864.  
  865.         if (!(opts = string_for_env_opt("videoshades", opts, FALSE))) {
  866.             return;
  867.         }
  868.         badoption(opts);
  869.         return;
  870.     }
  871. #endif /* VIDEOSHADES */
  872.  
  873.     if (!strncmpi(opts, "windowtype", 3)) {
  874.         if ((op = string_for_env_opt("windowtype", opts, FALSE)) != 0) {
  875.         char buf[16];
  876.         nmcpy(buf, op, 15);
  877.         choose_windows(buf);
  878.         }
  879.         return;
  880.     }
  881.  
  882.     /* OK, if we still haven't recognized the option, check the boolean
  883.      * options list
  884.      */
  885.     for (i = 0; boolopt[i].name; i++) {
  886.         if (strlen(opts) >= 3 &&
  887.             !strncmpi(boolopt[i].name, opts, strlen(opts))) {
  888.             /* options that don't exist */
  889.             if (!boolopt[i].addr) {
  890.                 if (!initial && !negated)
  891.                 pline("The \"%s\" option is not available.",
  892.                     boolopt[i].name);
  893.                 return;
  894.             }
  895.             /* options that must come from config file */
  896.             if (!initial &&
  897.                 ((boolopt[i].addr) == &flags.legacy
  898. #if defined(MICRO) && !defined(AMIGA)
  899.               || (boolopt[i].addr) == &flags.rawio
  900. #endif
  901.                  )) {
  902.                 rejectoption(boolopt[i].name);
  903.                 return;
  904.             }
  905.  
  906.             *(boolopt[i].addr) = !negated;
  907.  
  908. #if defined(TERMLIB) || defined(ASCIIGRAPH) || defined(MAC_GRAPHICS_ENV)
  909.             if (FALSE
  910. # ifdef TERMLIB
  911.                  || (boolopt[i].addr) == &flags.DECgraphics
  912. # endif
  913. # ifdef ASCIIGRAPH
  914.                  || (boolopt[i].addr) == &flags.IBMgraphics
  915. # endif
  916. # ifdef MAC_GRAPHICS_ENV
  917.                  || (boolopt[i].addr) == &flags.MACgraphics
  918. # endif
  919.                 ) {
  920. # ifdef REINCARNATION
  921.                 if (!initial && Is_rogue_level(&u.uz))
  922.                 assign_rogue_graphics(FALSE);
  923. # endif
  924.                 need_redraw = TRUE;
  925. # ifdef TERMLIB
  926.                 if ((boolopt[i].addr) == &flags.DECgraphics)
  927.                 switch_graphics(flags.DECgraphics ?
  928.                         DEC_GRAPHICS : ASCII_GRAPHICS);
  929. # endif
  930. # ifdef ASCIIGRAPH
  931.                 if ((boolopt[i].addr) == &flags.IBMgraphics)
  932.                 switch_graphics(flags.IBMgraphics ?
  933.                         IBM_GRAPHICS : ASCII_GRAPHICS);
  934. # endif
  935. # ifdef MAC_GRAPHICS_ENV
  936.                 if ((boolopt[i].addr) == &flags.MACgraphics)
  937.                 switch_graphics(flags.MACgraphics ?
  938.                         MAC_GRAPHICS : ASCII_GRAPHICS);
  939. # endif
  940. # ifdef REINCARNATION
  941.                 if (!initial && Is_rogue_level(&u.uz))
  942.                 assign_rogue_graphics(TRUE);
  943. # endif
  944.             }
  945. #endif /* TERMLIB || ASCIIGRAPH || MAC_GRAPHICS_ENV */
  946.  
  947.             /* only do processing below if setting with doset() */
  948.             if (initial) return;
  949.  
  950.             if ((boolopt[i].addr) == &flags.time
  951. #ifdef EXP_ON_BOTL
  952.              || (boolopt[i].addr) == &flags.showexp
  953. #endif
  954. #ifdef SCORE_ON_BOTL
  955.              || (boolopt[i].addr) == &flags.showscore
  956. #endif
  957.                 )
  958.                 flags.botl = TRUE;
  959.  
  960.             else if ((boolopt[i].addr) == &flags.invlet_constant) {
  961.                 if (flags.invlet_constant) reassign();
  962.             }
  963.  
  964.             else if ((boolopt[i].addr) == &flags.num_pad)
  965.                 number_pad(flags.num_pad ? 1 : 0);
  966.  
  967.             else if ((boolopt[i].addr) == &flags.lit_corridor) {
  968.                 /*
  969.                  * All corridor squares seen via night vision or
  970.                  * candles & lamps change.  Update them by calling
  971.                  * newsym() on them.  Don't do this if we are
  972.                  * initializing the options --- the vision system
  973.                  * isn't set up yet.
  974.                  */
  975.                 vision_recalc(2);        /* shut down vision */
  976.                 vision_full_recalc = 1;    /* delayed recalc */
  977.             }
  978.  
  979. #ifdef TEXTCOLOR
  980.             else if ((boolopt[i].addr) == &flags.use_color
  981.                   || (boolopt[i].addr) == &flags.hilite_pet) {
  982.                 need_redraw = TRUE;
  983. # ifdef TOS
  984.                 if ((boolopt[i].addr) == &flags.use_color
  985.                 && flags.BIOS) {
  986.                 if (colors_changed)
  987.                     restore_colors();
  988.                 else
  989.                     set_colors();
  990.                 }
  991. # endif
  992.             }
  993. #endif
  994.  
  995.             return;
  996.         }
  997.     }
  998.  
  999.     /* out of valid options */
  1000.     badoption(opts);
  1001. }
  1002.  
  1003. /*
  1004.  * Convert the given string of object classes to a string of default object
  1005.  * symbols.
  1006.  */
  1007. static void
  1008. oc_to_str(src,dest)
  1009.     char *src, *dest;
  1010. {
  1011.     int i;
  1012.  
  1013.     while ((i = (int) *src++) != 0) {
  1014.     if (i < 0 || i >= MAXOCLASSES)
  1015.         impossible("oc_to_str:  illegal object class %d", i);
  1016.     else
  1017.         *dest++ = def_oc_syms[i];
  1018.     }
  1019.     *dest = '\0';
  1020. }
  1021.  
  1022. #if defined(MICRO) || defined(MAC)
  1023. # define OPTIONS_HEADING "OPTIONS"
  1024. #else
  1025. # define OPTIONS_HEADING "NETHACKOPTIONS"
  1026. #endif
  1027.  
  1028. int
  1029. doset()
  1030. {
  1031.     char buf[BUFSZ], ocl[MAXOCLASSES+1], on_off;
  1032.     const char *opt_name;
  1033.     int i;
  1034.     winid tmpwin;
  1035.  
  1036.     switch (yn_function("Show the current settings [c], or set options [s]?",
  1037.                 "csq", 'q')) {
  1038.     default:
  1039.     case 'q':
  1040.         clear_nhwindow(WIN_MESSAGE);
  1041.         return 0;
  1042.     case 'c':
  1043.         tmpwin = create_nhwindow(NHW_MENU);
  1044.         putstr(tmpwin, 0, OPTIONS_HEADING);
  1045.         putstr(tmpwin, 0, "");
  1046.         /* print the booleans */
  1047.         for (i = 0; boolopt[i].name; i++) {
  1048.         if (!boolopt[i].addr) continue;
  1049.         opt_name = boolopt[i].name;
  1050.         if (*(boolopt[i].addr)) {
  1051.             on_off = ' ';               /* on */
  1052.         } else {
  1053.             if (!strcmp(opt_name, "female"))
  1054.             opt_name = "male",  on_off = ' ';
  1055.             else
  1056.             on_off = '!';           /* off */
  1057.         }
  1058.         Sprintf(buf, "%c%s", on_off, opt_name);
  1059.         putstr(tmpwin, 0, buf);
  1060.         }
  1061.         /* print the compounds */
  1062.         Sprintf(buf, " catname: %s",
  1063.             (catname[0] != 0) ? catname : "(null)");
  1064.         putstr(tmpwin, 0, buf);
  1065.         Sprintf(buf, " disclose: %s",
  1066.             (flags.end_disclose[0]) ? flags.end_disclose : "all");
  1067.         putstr(tmpwin, 0, buf);
  1068.         Sprintf(buf, " dogname: %s",
  1069.             (dogname[0] != 0) ? dogname : "(null)");
  1070.         putstr(tmpwin, 0, buf);
  1071. #ifdef TUTTI_FRUTTI
  1072.         Sprintf(buf, " fruit: %s", pl_fruit);
  1073.         putstr(tmpwin, 0, buf);
  1074. #endif
  1075.         Sprintf(buf, " msghistory: %u", flags.msg_history);
  1076.         putstr(tmpwin, 0, buf);
  1077.         Sprintf(buf, " name: %s", plname);
  1078.         putstr(tmpwin, 0, buf);
  1079.         oc_to_str(flags.inv_order, ocl);
  1080.         Sprintf(buf, " packorder: %s", ocl);
  1081.         putstr(tmpwin, 0, buf);
  1082. #ifdef CHANGE_COLOR
  1083.         Sprintf(buf, " palette: %s", get_color_string());
  1084.         putstr(tmpwin, 0, buf);
  1085. #endif
  1086.         Sprintf(buf, " pettype: %s", preferred_pet == 'c' ? "cat" :
  1087.                     preferred_pet == 'd' ? "dog" : "random");
  1088.         putstr(tmpwin, 0, buf);
  1089.         oc_to_str(flags.pickup_types, ocl);
  1090.         Sprintf(buf, " pickup_types: %s", (ocl[0]) ? ocl : "all");
  1091.         putstr(tmpwin, 0, buf);
  1092.         Sprintf(buf, " scores: %u top/%u around%s",
  1093.                 flags.end_top, flags.end_around,
  1094.                 (flags.end_own ? "/own" : ""));
  1095.         putstr(tmpwin, 0, buf);
  1096. #ifdef VIDEOSHADES
  1097.             Sprintf(buf, " videoshades: %s %s %s",
  1098.                 shade[0],shade[1],shade[2]);
  1099.             putstr(tmpwin, 0, buf);
  1100.             Sprintf(buf, " videocolors: %d %d %d %d %d %d %d %d %d %d %d %d",
  1101.          ttycolors[RED],ttycolors[GREEN],ttycolors[BROWN],
  1102.                  ttycolors[BLUE],ttycolors[MAGENTA],ttycolors[CYAN],
  1103.                  ttycolors[ORANGE_COLORED],ttycolors[BRIGHT_GREEN],
  1104.          ttycolors[YELLOW],ttycolors[BRIGHT_BLUE],
  1105.                  ttycolors[BRIGHT_MAGENTA],ttycolors[BRIGHT_CYAN]);
  1106.             putstr(tmpwin, 0, buf);
  1107. #endif /* VIDEOSHADES */
  1108.         Sprintf(buf, " windowtype: %s", windowprocs.name);
  1109.         putstr(tmpwin, 0, buf);
  1110.         display_nhwindow(tmpwin, TRUE);
  1111.         destroy_nhwindow(tmpwin);
  1112.         break;
  1113.     case 's':
  1114.         clear_nhwindow(WIN_MESSAGE);
  1115.         getlin("What options do you want to set?", buf);
  1116.         if(buf[0] == '\033') return 0;
  1117.         need_redraw = FALSE;
  1118.         parseoptions(buf, FALSE, FALSE);
  1119.         if(need_redraw)
  1120.         (void) doredraw();
  1121.         break;
  1122.     }
  1123.  
  1124.     return 0;
  1125. }
  1126.  
  1127. int
  1128. dotogglepickup()
  1129. {
  1130.     char buf[BUFSZ], ocl[MAXOCLASSES+1];
  1131.  
  1132.     flags.pickup = !flags.pickup;
  1133.     if (flags.pickup) {
  1134.         oc_to_str(flags.pickup_types, ocl);
  1135.         Sprintf(buf, "ON, for %s objects", ocl[0] ? ocl : "all");
  1136.     } else {
  1137.         Strcpy(buf, "OFF");
  1138.     }
  1139.     pline("Autopickup: %s.", buf);
  1140.     return 0;
  1141. }
  1142.  
  1143. /* data for option_help() */
  1144. static const char *opt_intro[] = {
  1145.     "",
  1146.     "                 NetHack Options Help:",
  1147.     "",
  1148. #define CONFIG_SLOT 3    /* fill in next value at run-time */
  1149.     NULL,
  1150. #if !defined(MICRO) && !defined(MAC)
  1151.     "or use `NETHACKOPTIONS=\"<options>\"' in your environment;",
  1152. # ifdef VMS
  1153.     "-- for example, $ DEFINE NETHACKOPTIONS \"noautopickup,fruit:kumquat\"",
  1154. # endif
  1155. #endif
  1156.     "or press \"O\" while playing, and type your <options> at the prompt.",
  1157.     "In all cases, <options> is a list of options separated by commas.",
  1158.     "",
  1159.  "Boolean options (which can be negated by prefixing them with '!' or \"no\"):",
  1160.     NULL
  1161. };
  1162.  
  1163. static const char *opt_epilog[] = {
  1164.     "",
  1165.  "Some of the options can be set only before the game is started.  You will",
  1166.     "be so informed, if you attempt to set them while in the game.",
  1167.     NULL
  1168. };
  1169.  
  1170. void
  1171. option_help()
  1172. {
  1173.     char buf[BUFSZ], buf2[BUFSZ];
  1174.     register int i;
  1175.     winid datawin;
  1176.  
  1177.     datawin = create_nhwindow(NHW_TEXT);
  1178. #ifdef AMIGA
  1179.     if(FromWBench){
  1180.     Sprintf(buf,"Set options as OPTIONS= in %s or in icon;",configfile);
  1181.     } else
  1182. #endif
  1183.     Sprintf(buf, "Set options as OPTIONS=<options> in %s;", configfile);
  1184.     opt_intro[CONFIG_SLOT] = (const char *) buf;
  1185.     for (i = 0; opt_intro[i]; i++)
  1186.     putstr(datawin, 0, opt_intro[i]);
  1187.  
  1188.     /* Boolean options */
  1189.     for (i = 0; boolopt[i].name; i++) {
  1190.     if (boolopt[i].addr)
  1191.         next_opt(datawin, boolopt[i].name);
  1192.     }
  1193.     next_opt(datawin, "");
  1194.  
  1195.     /* Compound options */
  1196.     putstr(datawin, 0, "Compound options:");
  1197.     for (i = 0; compopt[i].name; i++) {
  1198.     Sprintf(buf2, "`%s'", compopt[i].name);
  1199.     Sprintf(buf, "%-14s - %s", buf2, compopt[i].descr);
  1200.     putstr(datawin, 0, buf);
  1201.     }
  1202.  
  1203.     for (i = 0; opt_epilog[i]; i++)
  1204.     putstr(datawin, 0, opt_epilog[i]);
  1205.  
  1206.     display_nhwindow(datawin, FALSE);
  1207.     destroy_nhwindow(datawin);
  1208.     return;
  1209. }
  1210.  
  1211. /*
  1212.  * prints the next boolean option, on the same line if possible, on a new
  1213.  * line if not. End with next_opt("").
  1214.  */
  1215. void
  1216. next_opt(datawin, str)
  1217. winid datawin;
  1218. const char *str;
  1219. {
  1220.     static char buf[121];
  1221.     int i;
  1222.     char *s;
  1223.  
  1224.     if (!*str) {
  1225.         for (s = buf; *s; s++);    /* find end of string */
  1226.         if (s > &buf[1] && s[-2] == ',')
  1227.             s[-2] = 0;    /* strip last ", " */
  1228.         i = 121;
  1229.     }
  1230.     else    
  1231.         i = strlen(buf) + strlen(str) + 2;
  1232.  
  1233.     if (i > COLNO - 2) { /* rule of thumb */
  1234.         putstr(datawin, 0, buf);
  1235.         buf[0] = 0;
  1236.     }
  1237.     if (*str) {
  1238.         Strcat(buf, str);
  1239.         Strcat(buf, ", ");
  1240.     }
  1241.     else
  1242.         putstr(datawin, 0, str);
  1243.     return;
  1244. }
  1245.  
  1246. #ifdef TUTTI_FRUTTI
  1247. /* Returns the fid of the fruit type; if that type already exists, it
  1248.  * returns the fid of that one; if it does not exist, it adds a new fruit
  1249.  * type to the chain and returns the new one.
  1250.  */
  1251. int
  1252. fruitadd(str)
  1253. char *str;
  1254. {
  1255.     register int i,j;
  1256.     register struct fruit *f;
  1257. #ifdef GCC_WARN
  1258.     struct fruit *lastf = (struct fruit *)0;
  1259. #else
  1260.     struct fruit *lastf;
  1261. #endif
  1262.     int highest_fruit_id = 0;
  1263.     char buf[PL_FSIZ];
  1264.     boolean user_specified = (str == pl_fruit);
  1265.     /* if not user-specified, then it's a fruit name for a fruit on
  1266.      * a bones level...
  1267.      */
  1268.  
  1269.     /* Note: every fruit has an id (spe for fruit objects) of at least
  1270.      * 1; 0 is an error.
  1271.      */
  1272.     if (user_specified) {
  1273.         /* disallow naming after other foods (since it'd be impossible
  1274.          * to tell the difference)
  1275.          */
  1276.  
  1277.         boolean found = FALSE;
  1278.  
  1279.         for(i = bases[j=letindex(FOOD_CLASS)]; i < bases[j+1]; i++) {
  1280.             if (!strcmp(OBJ_NAME(objects[i]), pl_fruit)) {
  1281.                 found = TRUE;
  1282.                 break;
  1283.             }
  1284.         }
  1285.         if (found ||
  1286.             (!strncmp(str, "tin of ", 7) && name_to_mon(str+7) > -1) ||
  1287.             !strcmp(str, "empty tin") ||
  1288.             !strcmp(str, "tin of spinach") ||
  1289.             ((!strncmp(eos(str)-6," corpse",7) ||
  1290.                         !strncmp(eos(str)-3, " egg",4))
  1291.             && name_to_mon(str) > -1))
  1292.             {
  1293.                 Strcpy(buf, pl_fruit);
  1294.                 Strcpy(pl_fruit, "candied ");
  1295.                 nmcpy(pl_fruit+8, buf, PL_FSIZ-8);
  1296.         }
  1297.     }
  1298.     for(f=ffruit; f; f = f->nextf) {
  1299.         lastf = f;
  1300.         if(f->fid > highest_fruit_id) highest_fruit_id = f->fid;
  1301.         if(!strncmp(str, f->fname, PL_FSIZ))
  1302.             goto nonew;
  1303.     }
  1304.     /* if adding another fruit would overflow spe, use a random
  1305.        fruit instead... we've got a lot to choose from. */
  1306.     if (highest_fruit_id >= 127) return rnd(127);
  1307.     highest_fruit_id++;
  1308.     f = newfruit();
  1309.     if (ffruit) lastf->nextf = f;
  1310.     else ffruit = f;
  1311.     Strcpy(f->fname, str);
  1312.     f->fid = highest_fruit_id;
  1313.     f->nextf = 0;
  1314. nonew:
  1315.     if (user_specified) current_fruit = highest_fruit_id;
  1316.     return f->fid;
  1317. }
  1318. #endif
  1319.  
  1320. #endif    /* OPTION_LISTS_ONLY */
  1321.  
  1322. /*options.c*/
  1323.