Show menu control keys in help menu
[aNetHack.git] / src / options.c
blob2d30ba03a2a51cc93c8abf8c333f4993fe673793
1 /* NetHack 3.6 options.c $NHDT-Date: 1470357737 2016/08/05 00:42:17 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.279 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
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 #ifdef SYSFLAGS
11 NEARDATA struct sysflag sysflags; /* provide linkage */
12 #endif
13 NEARDATA struct instance_flags iflags; /* provide linkage */
14 #define static
15 #else
16 #include "hack.h"
17 #include "tcap.h"
18 #include <ctype.h>
19 #endif
21 #define BACKWARD_COMPAT
22 #define WINTYPELEN 16
24 #ifdef DEFAULT_WC_TILED_MAP
25 #define PREFER_TILED TRUE
26 #else
27 #define PREFER_TILED FALSE
28 #endif
30 #define MESSAGE_OPTION 1
31 #define STATUS_OPTION 2
32 #define MAP_OPTION 3
33 #define MENU_OPTION 4
34 #define TEXT_OPTION 5
36 #define PILE_LIMIT_DFLT 5
39 * NOTE: If you add (or delete) an option, please update the short
40 * options help (option_help()), the long options help (dat/opthelp),
41 * and the current options setting display function (doset()),
42 * and also the Guidebooks.
44 * The order matters. If an option is a an initial substring of another
45 * option (e.g. time and timed_delay) the shorter one must come first.
48 static struct Bool_Opt {
49 const char *name;
50 boolean *addr, initvalue;
51 int optflags;
52 } boolopt[] = {
53 { "acoustics", &flags.acoustics, TRUE, SET_IN_GAME },
54 #if defined(SYSFLAGS) && defined(AMIGA)
55 /* Amiga altmeta causes Alt+key to be converted into Meta+key by
56 low level nethack code; on by default, can be toggled off if
57 Alt+key is needed for some ASCII chars on non-ASCII keyboard */
58 { "altmeta", &sysflags.altmeta, TRUE, DISP_IN_GAME },
59 #else
60 #ifdef ALTMETA
61 /* non-Amiga altmeta causes nethack's top level command loop to treat
62 two character sequence "ESC c" as M-c, for terminals or emulators
63 which send "ESC c" when Alt+c is pressed; off by default, enabling
64 this can potentially make trouble if user types ESC when nethack
65 is honoring this conversion request (primarily after starting a
66 count prefix prior to a command and then deciding to cancel it) */
67 { "altmeta", &iflags.altmeta, FALSE, SET_IN_GAME },
68 #else
69 { "altmeta", (boolean *) 0, TRUE, DISP_IN_GAME },
70 #endif
71 #endif
72 { "ascii_map", &iflags.wc_ascii_map, !PREFER_TILED, SET_IN_GAME }, /*WC*/
73 #if defined(SYSFLAGS) && defined(MFLOPPY)
74 { "asksavedisk", &sysflags.asksavedisk, FALSE, SET_IN_GAME },
75 #else
76 { "asksavedisk", (boolean *) 0, FALSE, SET_IN_FILE },
77 #endif
78 { "autodescribe", &iflags.autodescribe, FALSE, SET_IN_GAME },
79 { "autodig", &flags.autodig, FALSE, SET_IN_GAME },
80 { "autoopen", &flags.autoopen, TRUE, SET_IN_GAME },
81 { "autopickup", &flags.pickup, TRUE, SET_IN_GAME },
82 { "autoquiver", &flags.autoquiver, FALSE, SET_IN_GAME },
83 #if defined(MICRO) && !defined(AMIGA)
84 { "BIOS", &iflags.BIOS, FALSE, SET_IN_FILE },
85 #else
86 { "BIOS", (boolean *) 0, FALSE, SET_IN_FILE },
87 #endif
88 { "blind", &u.uroleplay.blind, FALSE, DISP_IN_GAME },
89 { "bones", &flags.bones, TRUE, SET_IN_FILE },
90 #ifdef INSURANCE
91 { "checkpoint", &flags.ins_chkpt, TRUE, SET_IN_GAME },
92 #else
93 { "checkpoint", (boolean *) 0, FALSE, SET_IN_FILE },
94 #endif
95 #ifdef MFLOPPY
96 { "checkspace", &iflags.checkspace, TRUE, SET_IN_GAME },
97 #else
98 { "checkspace", (boolean *) 0, FALSE, SET_IN_FILE },
99 #endif
100 { "clicklook", &iflags.clicklook, FALSE, SET_IN_GAME },
101 { "cmdassist", &iflags.cmdassist, TRUE, SET_IN_GAME },
102 #if defined(MICRO) || defined(WIN32)
103 { "color", &iflags.wc_color, TRUE, SET_IN_GAME }, /*WC*/
104 #else /* systems that support multiple terminals, many monochrome */
105 { "color", &iflags.wc_color, FALSE, SET_IN_GAME }, /*WC*/
106 #endif
107 { "confirm", &flags.confirm, TRUE, SET_IN_GAME },
108 { "dark_room", &flags.dark_room, TRUE, SET_IN_GAME },
109 { "eight_bit_tty", &iflags.wc_eight_bit_input, FALSE,
110 SET_IN_GAME }, /*WC*/
111 #ifdef TTY_GRAPHICS
112 { "extmenu", &iflags.extmenu, FALSE, SET_IN_GAME },
113 #else
114 { "extmenu", (boolean *) 0, FALSE, SET_IN_FILE },
115 #endif
116 #ifdef OPT_DISPMAP
117 { "fast_map", &flags.fast_map, TRUE, SET_IN_GAME },
118 #else
119 { "fast_map", (boolean *) 0, TRUE, SET_IN_FILE },
120 #endif
121 { "female", &flags.female, FALSE, DISP_IN_GAME },
122 { "fixinv", &flags.invlet_constant, TRUE, SET_IN_GAME },
123 #if defined(SYSFLAGS) && defined(AMIFLUSH)
124 { "flush", &sysflags.amiflush, FALSE, SET_IN_GAME },
125 #else
126 { "flush", (boolean *) 0, FALSE, SET_IN_FILE },
127 #endif
128 { "fullscreen", &iflags.wc2_fullscreen, FALSE, SET_IN_FILE },
129 { "help", &flags.help, TRUE, SET_IN_GAME },
130 { "hilite_pet", &iflags.wc_hilite_pet, FALSE, SET_IN_GAME }, /*WC*/
131 { "hilite_pile", &iflags.hilite_pile, FALSE, SET_IN_GAME },
132 #ifndef MAC
133 { "ignintr", &flags.ignintr, FALSE, SET_IN_GAME },
134 #else
135 { "ignintr", (boolean *) 0, FALSE, SET_IN_FILE },
136 #endif
137 { "implicit_uncursed", &iflags.implicit_uncursed, TRUE, SET_IN_GAME },
138 { "large_font", &iflags.obsolete, FALSE, SET_IN_FILE }, /* OBSOLETE */
139 { "legacy", &flags.legacy, TRUE, DISP_IN_GAME },
140 { "lit_corridor", &flags.lit_corridor, FALSE, SET_IN_GAME },
141 { "lootabc", &flags.lootabc, FALSE, SET_IN_GAME },
142 #ifdef MAIL
143 { "mail", &flags.biff, TRUE, SET_IN_GAME },
144 #else
145 { "mail", (boolean *) 0, TRUE, SET_IN_FILE },
146 #endif
147 { "mention_walls", &iflags.mention_walls, FALSE, SET_IN_GAME },
148 { "menucolors", &iflags.use_menu_color, FALSE, SET_IN_GAME },
149 /* for menu debugging only*/
150 { "menu_tab_sep", &iflags.menu_tab_sep, FALSE, SET_IN_WIZGAME },
151 { "menu_objsyms", &iflags.menu_head_objsym, FALSE, SET_IN_GAME },
152 #ifdef TTY_GRAPHICS
153 { "menu_overlay", &iflags.menu_overlay, TRUE, SET_IN_GAME },
154 #else
155 { "menu_overlay", (boolean *) 0, FALSE, SET_IN_FILE },
156 #endif
157 { "mouse_support", &iflags.wc_mouse_support, TRUE, DISP_IN_GAME }, /*WC*/
158 #ifdef NEWS
159 { "news", &iflags.news, TRUE, DISP_IN_GAME },
160 #else
161 { "news", (boolean *) 0, FALSE, SET_IN_FILE },
162 #endif
163 { "nudist", &u.uroleplay.nudist, FALSE, DISP_IN_GAME },
164 { "null", &flags.null, TRUE, SET_IN_GAME },
165 #if defined(SYSFLAGS) && defined(MAC)
166 { "page_wait", &sysflags.page_wait, TRUE, SET_IN_GAME },
167 #else
168 { "page_wait", (boolean *) 0, FALSE, SET_IN_FILE },
169 #endif
170 { "perm_invent", &flags.perm_invent, FALSE, SET_IN_GAME },
171 { "pickup_thrown", &flags.pickup_thrown, TRUE, SET_IN_GAME },
172 { "popup_dialog", &iflags.wc_popup_dialog, FALSE, SET_IN_GAME }, /*WC*/
173 { "preload_tiles", &iflags.wc_preload_tiles, TRUE, DISP_IN_GAME }, /*WC*/
174 { "pushweapon", &flags.pushweapon, FALSE, SET_IN_GAME },
175 #if defined(MICRO) && !defined(AMIGA)
176 { "rawio", &iflags.rawio, FALSE, DISP_IN_GAME },
177 #else
178 { "rawio", (boolean *) 0, FALSE, SET_IN_FILE },
179 #endif
180 { "rest_on_space", &flags.rest_on_space, FALSE, SET_IN_GAME },
181 #ifdef RLECOMP
182 { "rlecomp", &iflags.rlecomp,
183 #if defined(COMPRESS) || defined(ZLIB_COMP)
184 FALSE,
185 #else
186 TRUE,
187 #endif
188 DISP_IN_GAME },
189 #endif
190 { "safe_pet", &flags.safe_dog, TRUE, SET_IN_GAME },
191 { "sanity_check", &iflags.sanity_check, FALSE, SET_IN_WIZGAME },
192 { "selectsaved", &iflags.wc2_selectsaved, TRUE, DISP_IN_GAME }, /*WC*/
193 { "showexp", &flags.showexp, FALSE, SET_IN_GAME },
194 { "showrace", &flags.showrace, FALSE, SET_IN_GAME },
195 #ifdef SCORE_ON_BOTL
196 { "showscore", &flags.showscore, FALSE, SET_IN_GAME },
197 #else
198 { "showscore", (boolean *) 0, FALSE, SET_IN_FILE },
199 #endif
200 { "silent", &flags.silent, TRUE, SET_IN_GAME },
201 { "softkeyboard", &iflags.wc2_softkeyboard, FALSE, SET_IN_FILE },
202 { "sortpack", &flags.sortpack, TRUE, SET_IN_GAME },
203 { "sparkle", &flags.sparkle, TRUE, SET_IN_GAME },
204 { "splash_screen", &iflags.wc_splash_screen, TRUE, DISP_IN_GAME }, /*WC*/
205 { "standout", &flags.standout, FALSE, SET_IN_GAME },
206 #if defined(STATUS_VIA_WINDOWPORT) && defined(STATUS_HILITES)
207 { "statushilites", &iflags.use_status_hilites, TRUE, SET_IN_GAME },
208 #else
209 { "statushilites", &iflags.use_status_hilites, FALSE, DISP_IN_GAME },
210 #endif
211 { "tiled_map", &iflags.wc_tiled_map, PREFER_TILED, DISP_IN_GAME }, /*WC*/
212 { "time", &flags.time, FALSE, SET_IN_GAME },
213 #ifdef TIMED_DELAY
214 { "timed_delay", &flags.nap, TRUE, SET_IN_GAME },
215 #else
216 { "timed_delay", (boolean *) 0, FALSE, SET_IN_GAME },
217 #endif
218 { "tombstone", &flags.tombstone, TRUE, SET_IN_GAME },
219 { "toptenwin", &iflags.toptenwin, FALSE, SET_IN_GAME },
220 { "travel", &flags.travelcmd, TRUE, SET_IN_GAME },
221 { "use_darkgray", &iflags.wc2_darkgray, TRUE, SET_IN_FILE },
222 #ifdef WIN32
223 { "use_inverse", &iflags.wc_inverse, TRUE, SET_IN_GAME }, /*WC*/
224 #else
225 { "use_inverse", &iflags.wc_inverse, FALSE, SET_IN_GAME }, /*WC*/
226 #endif
227 { "verbose", &flags.verbose, TRUE, SET_IN_GAME },
228 #ifdef TTY_TILES_ESCCODES
229 { "vt_tiledata", &iflags.vt_tiledata, FALSE, SET_IN_FILE },
230 #else
231 { "vt_tiledata", (boolean *) 0, FALSE, SET_IN_FILE },
232 #endif
233 { "wizweight", &iflags.wizweight, FALSE, SET_IN_WIZGAME },
234 { "wraptext", &iflags.wc2_wraptext, FALSE, SET_IN_GAME },
235 #ifdef ZEROCOMP
236 { "zerocomp", &iflags.zerocomp,
237 #if defined(COMPRESS) || defined(ZLIB_COMP)
238 FALSE,
239 #else
240 TRUE,
241 #endif
242 DISP_IN_GAME },
243 #endif
244 { (char *) 0, (boolean *) 0, FALSE, 0 }
247 /* compound options, for option_help() and external programs like Amiga
248 * frontend */
249 static struct Comp_Opt {
250 const char *name, *descr;
251 int size; /* for frontends and such allocating space --
252 * usually allowed size of data in game, but
253 * occasionally maximum reasonable size for
254 * typing when game maintains information in
255 * a different format */
256 int optflags;
257 } compopt[] = {
258 { "align", "your starting alignment (lawful, neutral, or chaotic)", 8,
259 DISP_IN_GAME },
260 { "align_message", "message window alignment", 20, DISP_IN_GAME }, /*WC*/
261 { "align_status", "status window alignment", 20, DISP_IN_GAME }, /*WC*/
262 { "altkeyhandler", "alternate key handler", 20, DISP_IN_GAME },
263 #ifdef BACKWARD_COMPAT
264 { "boulder", "deprecated (use S_boulder in sym file instead)", 1,
265 SET_IN_FILE },
266 #endif
267 { "catname", "the name of your (first) cat (e.g., catname:Tabby)",
268 PL_PSIZ, DISP_IN_GAME },
269 { "disclose", "the kinds of information to disclose at end of game",
270 sizeof(flags.end_disclose) * 2, SET_IN_GAME },
271 { "dogname", "the name of your (first) dog (e.g., dogname:Fang)", PL_PSIZ,
272 DISP_IN_GAME },
273 { "dungeon", "the symbols to use in drawing the dungeon map",
274 MAXDCHARS + 1, SET_IN_FILE },
275 { "effects", "the symbols to use in drawing special effects",
276 MAXECHARS + 1, SET_IN_FILE },
277 { "font_map", "the font to use in the map window", 40,
278 DISP_IN_GAME }, /*WC*/
279 { "font_menu", "the font to use in menus", 40, DISP_IN_GAME }, /*WC*/
280 { "font_message", "the font to use in the message window", 40,
281 DISP_IN_GAME }, /*WC*/
282 { "font_size_map", "the size of the map font", 20, DISP_IN_GAME }, /*WC*/
283 { "font_size_menu", "the size of the menu font", 20,
284 DISP_IN_GAME }, /*WC*/
285 { "font_size_message", "the size of the message font", 20,
286 DISP_IN_GAME }, /*WC*/
287 { "font_size_status", "the size of the status font", 20,
288 DISP_IN_GAME }, /*WC*/
289 { "font_size_text", "the size of the text font", 20,
290 DISP_IN_GAME }, /*WC*/
291 { "font_status", "the font to use in status window", 40,
292 DISP_IN_GAME }, /*WC*/
293 { "font_text", "the font to use in text windows", 40,
294 DISP_IN_GAME }, /*WC*/
295 { "fruit", "the name of a fruit you enjoy eating", PL_FSIZ, SET_IN_GAME },
296 { "gender", "your starting gender (male or female)", 8, DISP_IN_GAME },
297 { "horsename", "the name of your (first) horse (e.g., horsename:Silver)",
298 PL_PSIZ, DISP_IN_GAME },
299 { "map_mode", "map display mode under Windows", 20, DISP_IN_GAME }, /*WC*/
300 { "menustyle", "user interface for object selection", MENUTYPELEN,
301 SET_IN_GAME },
302 { "menu_deselect_all", "deselect all items in a menu", 4, SET_IN_FILE },
303 { "menu_deselect_page", "deselect all items on this page of a menu", 4,
304 SET_IN_FILE },
305 { "menu_first_page", "jump to the first page in a menu", 4, SET_IN_FILE },
306 { "menu_headings", "text attribute for menu headings", 9, SET_IN_GAME },
307 { "menu_invert_all", "invert all items in a menu", 4, SET_IN_FILE },
308 { "menu_invert_page", "invert all items on this page of a menu", 4,
309 SET_IN_FILE },
310 { "menu_last_page", "jump to the last page in a menu", 4, SET_IN_FILE },
311 { "menu_next_page", "goto the next menu page", 4, SET_IN_FILE },
312 { "menu_previous_page", "goto the previous menu page", 4, SET_IN_FILE },
313 { "menu_search", "search for a menu item", 4, SET_IN_FILE },
314 { "menu_select_all", "select all items in a menu", 4, SET_IN_FILE },
315 { "menu_select_page", "select all items on this page of a menu", 4,
316 SET_IN_FILE },
317 { "monsters", "the symbols to use for monsters", MAXMCLASSES,
318 SET_IN_FILE },
319 { "msghistory", "number of top line messages to save", 5, DISP_IN_GAME },
320 #ifdef TTY_GRAPHICS
321 { "msg_window", "the type of message window required", 1, SET_IN_GAME },
322 #else
323 { "msg_window", "the type of message window required", 1, SET_IN_FILE },
324 #endif
325 { "name", "your character's name (e.g., name:Merlin-W)", PL_NSIZ,
326 DISP_IN_GAME },
327 { "number_pad", "use the number pad for movement", 1, SET_IN_GAME },
328 { "objects", "the symbols to use for objects", MAXOCLASSES, SET_IN_FILE },
329 { "packorder", "the inventory order of the items in your pack",
330 MAXOCLASSES, SET_IN_GAME },
331 #ifdef CHANGE_COLOR
332 { "palette",
333 #ifndef WIN32
334 "palette (00c/880/-fff is blue/yellow/reverse white)", 15,
335 SET_IN_GAME },
336 #else
337 "palette (adjust an RGB color in palette (color-R-G-B)", 15,
338 SET_IN_FILE },
339 #endif
340 #if defined(MAC)
341 { "hicolor", "same as palette, only order is reversed", 15, SET_IN_FILE },
342 #endif
343 #endif
344 { "paranoid_confirmation", "extra prompting in certain situations", 28,
345 SET_IN_GAME },
346 { "pettype", "your preferred initial pet type", 4, DISP_IN_GAME },
347 { "pickup_burden", "maximum burden picked up before prompt", 20,
348 SET_IN_GAME },
349 { "pickup_types", "types of objects to pick up automatically",
350 MAXOCLASSES, SET_IN_GAME },
351 { "pile_limit", "threshold for \"there are many objects here\"", 24,
352 SET_IN_GAME },
353 { "playmode", "normal play, non-scoring explore mode, or debug mode", 8,
354 DISP_IN_GAME },
355 { "player_selection", "choose character via dialog or prompts", 12,
356 DISP_IN_GAME },
357 { "race", "your starting race (e.g., Human, Elf)", PL_CSIZ,
358 DISP_IN_GAME },
359 { "role", "your starting role (e.g., Barbarian, Valkyrie)", PL_CSIZ,
360 DISP_IN_GAME },
361 { "runmode", "display frequency when `running' or `travelling'",
362 sizeof "teleport", SET_IN_GAME },
363 { "scores", "the parts of the score list you wish to see", 32,
364 SET_IN_GAME },
365 { "scroll_amount", "amount to scroll map when scroll_margin is reached",
366 20, DISP_IN_GAME }, /*WC*/
367 { "scroll_margin", "scroll map when this far from the edge", 20,
368 DISP_IN_GAME }, /*WC*/
369 { "sortloot", "sort object selection lists by description", 4,
370 SET_IN_GAME },
371 #ifdef MSDOS
372 { "soundcard", "type of sound card to use", 20, SET_IN_FILE },
373 #endif
374 { "symset", "load a set of display symbols from the symbols file", 70,
375 SET_IN_GAME },
376 { "roguesymset",
377 "load a set of rogue display symbols from the symbols file", 70,
378 SET_IN_GAME },
379 #ifdef WIN32
380 { "subkeyvalue", "override keystroke value", 7, SET_IN_FILE },
381 #endif
382 { "suppress_alert", "suppress alerts about version-specific features", 8,
383 SET_IN_GAME },
384 { "tile_width", "width of tiles", 20, DISP_IN_GAME }, /*WC*/
385 { "tile_height", "height of tiles", 20, DISP_IN_GAME }, /*WC*/
386 { "tile_file", "name of tile file", 70, DISP_IN_GAME }, /*WC*/
387 { "traps", "the symbols to use in drawing traps", MAXTCHARS + 1,
388 SET_IN_FILE },
389 { "vary_msgcount", "show more old messages at a time", 20,
390 DISP_IN_GAME }, /*WC*/
391 #ifdef MSDOS
392 { "video", "method of video updating", 20, SET_IN_FILE },
393 #endif
394 #ifdef VIDEOSHADES
395 { "videocolors", "color mappings for internal screen routines", 40,
396 DISP_IN_GAME },
397 { "videoshades", "gray shades to map to black/gray/white", 32,
398 DISP_IN_GAME },
399 #endif
400 { "whatis_coord", "show coordinates when auto-describing cursor position",
401 1, SET_IN_GAME },
402 { "windowcolors", "the foreground/background colors of windows", /*WC*/
403 80, DISP_IN_GAME },
404 { "windowtype", "windowing system to use", WINTYPELEN, DISP_IN_GAME },
405 #ifdef WINCHAIN
406 { "windowchain", "window processor to use", WINTYPELEN, SET_IN_SYS },
407 #endif
408 #ifdef BACKWARD_COMPAT
409 { "DECgraphics", "load DECGraphics display symbols", 70, SET_IN_FILE },
410 { "IBMgraphics", "load IBMGraphics display symbols", 70, SET_IN_FILE },
411 #ifdef MAC_GRAPHICS_ENV
412 { "Macgraphics", "load MACGraphics display symbols", 70, SET_IN_FILE },
413 #endif
414 #endif
415 { (char *) 0, (char *) 0, 0, 0 }
418 #ifdef OPTION_LISTS_ONLY
419 #undef static
421 #else /* use rest of file */
423 extern char configfile[]; /* for messages */
425 extern struct symparse loadsyms[];
426 static boolean need_redraw; /* for doset() */
428 #if defined(TOS) && defined(TEXTCOLOR)
429 extern boolean colors_changed; /* in tos.c */
430 #endif
432 #ifdef VIDEOSHADES
433 extern char *shade[3]; /* in sys/msdos/video.c */
434 extern char ttycolors[CLR_MAX]; /* in sys/msdos/video.c */
435 #endif
437 static char def_inv_order[MAXOCLASSES] = {
438 COIN_CLASS, AMULET_CLASS, WEAPON_CLASS, ARMOR_CLASS, FOOD_CLASS,
439 SCROLL_CLASS, SPBOOK_CLASS, POTION_CLASS, RING_CLASS, WAND_CLASS,
440 TOOL_CLASS, GEM_CLASS, ROCK_CLASS, BALL_CLASS, CHAIN_CLASS, 0,
444 * Default menu manipulation command accelerators. These may _not_ be:
446 * + a number - reserved for counts
447 * + an upper or lower case US ASCII letter - used for accelerators
448 * + ESC - reserved for escaping the menu
449 * + NULL, CR or LF - reserved for commiting the selection(s). NULL
450 * is kind of odd, but the tty's xwaitforspace() will return it if
451 * someone hits a <ret>.
452 * + a default object class symbol - used for object class accelerators
454 * Standard letters (for now) are:
456 * < back 1 page
457 * > forward 1 page
458 * ^ first page
459 * | last page
460 * : search
462 * page all
463 * , select .
464 * \ deselect -
465 * ~ invert @
467 * The command name list is duplicated in the compopt array.
469 typedef struct {
470 const char *name;
471 char cmd;
472 const char *desc;
473 } menu_cmd_t;
475 static const menu_cmd_t default_menu_cmd_info[] = {
476 { "menu_first_page", MENU_FIRST_PAGE, "Go to first page" },
477 { "menu_last_page", MENU_LAST_PAGE, "Go to last page" },
478 { "menu_next_page", MENU_NEXT_PAGE, "Go to next page" },
479 { "menu_previous_page", MENU_PREVIOUS_PAGE, "Go to previous page" },
480 { "menu_select_all", MENU_SELECT_ALL, "Select all items" },
481 { "menu_deselect_all", MENU_UNSELECT_ALL, "Unselect all items" },
482 { "menu_invert_all", MENU_INVERT_ALL, "Insert selection" },
483 { "menu_select_page", MENU_SELECT_PAGE, "Select items in current page" },
484 { "menu_deselect_page", MENU_UNSELECT_PAGE, "Unselect items in current page" },
485 { "menu_invert_page", MENU_INVERT_PAGE, "Invert current page selection" },
486 { "menu_search", MENU_SEARCH, "Search and toggle matching items" },
490 * Allow the user to map incoming characters to various menu commands.
491 * The accelerator list must be a valid C string.
493 #define MAX_MENU_MAPPED_CMDS 32 /* some number */
494 char mapped_menu_cmds[MAX_MENU_MAPPED_CMDS + 1]; /* exported */
495 static char mapped_menu_op[MAX_MENU_MAPPED_CMDS + 1];
496 static short n_menu_mapped = 0;
498 static boolean initial, from_file;
500 STATIC_DCL void FDECL(nmcpy, (char *, const char *, int));
501 STATIC_DCL void FDECL(escapes, (const char *, char *));
502 STATIC_DCL void FDECL(rejectoption, (const char *));
503 STATIC_DCL void FDECL(badoptmsg, (const char *, const char *));
504 STATIC_DCL void FDECL(badoption, (const char *));
505 STATIC_DCL char *FDECL(string_for_opt, (char *, BOOLEAN_P));
506 STATIC_DCL char *FDECL(string_for_env_opt, (const char *, char *, BOOLEAN_P));
507 STATIC_DCL void FDECL(bad_negation, (const char *, BOOLEAN_P));
508 STATIC_DCL int FDECL(change_inv_order, (char *));
509 STATIC_DCL void FDECL(warning_opts, (char *, const char *));
510 STATIC_DCL int FDECL(feature_alert_opts, (char *, const char *));
511 STATIC_DCL boolean FDECL(duplicate_opt_detection, (const char *, int));
512 STATIC_DCL void FDECL(complain_about_duplicate, (const char *, int));
514 STATIC_DCL const char *FDECL(attr2attrname, (int));
515 STATIC_DCL int NDECL(query_color);
516 STATIC_DCL int FDECL(query_attr, (const char *));
517 STATIC_DCL const char * FDECL(msgtype2name, (int));
518 STATIC_DCL int NDECL(query_msgtype);
519 STATIC_DCL boolean FDECL(msgtype_add, (int, char *));
520 STATIC_DCL void FDECL(free_one_msgtype, (int));
521 STATIC_DCL int NDECL(msgtype_count);
522 STATIC_DCL boolean FDECL(add_menu_coloring_parsed, (char *, int, int));
523 STATIC_DCL void FDECL(free_one_menu_coloring, (int));
524 STATIC_DCL int NDECL(count_menucolors);
525 STATIC_DCL boolean FDECL(parse_role_opts, (BOOLEAN_P, const char *,
526 char *, char **));
528 STATIC_DCL void FDECL(oc_to_str, (char *, char *));
529 STATIC_DCL void FDECL(doset_add_menu, (winid, const char *, int));
530 STATIC_DCL void FDECL(opts_add_others, (winid, const char *, int,
531 char *, int));
532 STATIC_DCL int FDECL(handle_add_list_remove, (const char *, int));
533 STATIC_DCL boolean FDECL(special_handling, (const char *,
534 BOOLEAN_P, BOOLEAN_P));
535 STATIC_DCL const char *FDECL(get_compopt_value, (const char *, char *));
536 STATIC_DCL void FDECL(remove_autopickup_exception,
537 (struct autopickup_exception *));
538 STATIC_DCL int FDECL(count_ape_maps, (int *, int *));
540 STATIC_DCL boolean FDECL(is_wc_option, (const char *));
541 STATIC_DCL boolean FDECL(wc_supported, (const char *));
542 STATIC_DCL boolean FDECL(is_wc2_option, (const char *));
543 STATIC_DCL boolean FDECL(wc2_supported, (const char *));
544 STATIC_DCL void FDECL(wc_set_font_name, (int, char *));
545 STATIC_DCL int FDECL(wc_set_window_colors, (char *));
547 void
548 reglyph_darkroom()
550 xchar x, y;
552 for (x = 0; x < COLNO; x++)
553 for (y = 0; y < ROWNO; y++) {
554 struct rm *lev = &levl[x][y];
556 if (!flags.dark_room || !iflags.use_color
557 || Is_rogue_level(&u.uz)) {
558 if (lev->glyph == cmap_to_glyph(S_darkroom))
559 lev->glyph = lev->waslit ? cmap_to_glyph(S_room)
560 : cmap_to_glyph(S_stone);
561 } else {
562 if (lev->glyph == cmap_to_glyph(S_room) && lev->seenv
563 && lev->waslit && !cansee(x, y))
564 lev->glyph = cmap_to_glyph(S_darkroom);
565 else if (lev->glyph == cmap_to_glyph(S_stone)
566 && lev->typ == ROOM && lev->seenv && !cansee(x, y))
567 lev->glyph = cmap_to_glyph(S_darkroom);
570 if (flags.dark_room && iflags.use_color)
571 showsyms[S_darkroom] = showsyms[S_room];
572 else
573 showsyms[S_darkroom] = showsyms[S_stone];
576 /* check whether a user-supplied option string is a proper leading
577 substring of a particular option name; option string might have
578 a colon or equals sign and arbitrary value appended to it */
579 boolean
580 match_optname(user_string, opt_name, min_length, val_allowed)
581 const char *user_string, *opt_name;
582 int min_length;
583 boolean val_allowed;
585 int len = (int) strlen(user_string);
587 if (val_allowed) {
588 const char *p = index(user_string, ':'),
589 *q = index(user_string, '=');
591 if (!p || (q && q < p))
592 p = q;
593 if (p) {
594 /* 'user_string' hasn't necessarily been through mungspaces()
595 so might have tabs or consecutive spaces */
596 while (p > user_string && isspace((uchar) *(p - 1)))
597 p--;
598 len = (int) (p - user_string);
602 return (boolean) (len >= min_length
603 && !strncmpi(opt_name, user_string, len));
606 /* most environment variables will eventually be printed in an error
607 * message if they don't work, and most error message paths go through
608 * BUFSZ buffers, which could be overflowed by a maliciously long
609 * environment variable. If a variable can legitimately be long, or
610 * if it's put in a smaller buffer, the responsible code will have to
611 * bounds-check itself.
613 char *
614 nh_getenv(ev)
615 const char *ev;
617 char *getev = getenv(ev);
619 if (getev && strlen(getev) <= (BUFSZ / 2))
620 return getev;
621 else
622 return (char *) 0;
625 /* process options, possibly including SYSCF */
626 void
627 initoptions()
629 initoptions_init();
630 #ifdef SYSCF
631 /* someday there may be other SYSCF alternatives besides text file */
632 #ifdef SYSCF_FILE
633 /* If SYSCF_FILE is specified, it _must_ exist... */
634 assure_syscf_file();
635 /* ... and _must_ parse correctly. */
636 if (!read_config_file(SYSCF_FILE, SET_IN_SYS)) {
637 raw_printf("Error(s) found in SYSCF_FILE, quitting.");
638 terminate(EXIT_FAILURE);
641 * TODO [maybe]: parse the sysopt entries which are space-separated
642 * lists of usernames into arrays with one name per element.
644 #endif
645 #endif
646 initoptions_finish();
649 void
650 initoptions_init()
652 #if defined(UNIX) || defined(VMS)
653 char *opts;
654 #endif
655 int i;
657 /* set up the command parsing */
658 reset_commands(TRUE); /* init */
660 /* initialize the random number generator */
661 setrandom();
663 /* for detection of configfile options specified multiple times */
664 iflags.opt_booldup = iflags.opt_compdup = (int *) 0;
666 for (i = 0; boolopt[i].name; i++) {
667 if (boolopt[i].addr)
668 *(boolopt[i].addr) = boolopt[i].initvalue;
670 #if defined(COMPRESS) || defined(ZLIB_COMP)
671 set_savepref("externalcomp");
672 set_restpref("externalcomp");
673 #ifdef RLECOMP
674 set_savepref("!rlecomp");
675 set_restpref("!rlecomp");
676 #endif
677 #else
678 #ifdef ZEROCOMP
679 set_savepref("zerocomp");
680 set_restpref("zerocomp");
681 #endif
682 #ifdef RLECOMP
683 set_savepref("rlecomp");
684 set_restpref("rlecomp");
685 #endif
686 #endif
687 #ifdef SYSFLAGS
688 Strcpy(sysflags.sysflagsid, "sysflags");
689 sysflags.sysflagsid[9] = (char) sizeof(struct sysflag);
690 #endif
691 flags.end_own = FALSE;
692 flags.end_top = 3;
693 flags.end_around = 2;
694 flags.paranoia_bits = PARANOID_PRAY; /* old prayconfirm=TRUE */
695 flags.pile_limit = PILE_LIMIT_DFLT; /* 5 */
696 flags.runmode = RUN_LEAP;
697 iflags.msg_history = 20;
698 #ifdef TTY_GRAPHICS
699 iflags.prevmsg_window = 's';
700 #endif
701 iflags.menu_headings = ATR_INVERSE;
702 iflags.getpos_coords = GPCOORDS_NONE;
704 /* hero's role, race, &c haven't been chosen yet */
705 flags.initrole = flags.initrace = flags.initgend = flags.initalign =
706 ROLE_NONE;
708 /* Set the default monster and object class symbols. */
709 init_symbols();
710 for (i = 0; i < WARNCOUNT; i++)
711 warnsyms[i] = def_warnsyms[i].sym;
712 iflags.bouldersym = 0;
714 iflags.travelcc.x = iflags.travelcc.y = -1;
716 /* assert( sizeof flags.inv_order == sizeof def_inv_order ); */
717 (void) memcpy((genericptr_t) flags.inv_order,
718 (genericptr_t) def_inv_order, sizeof flags.inv_order);
719 flags.pickup_types[0] = '\0';
720 flags.pickup_burden = MOD_ENCUMBER;
721 flags.sortloot = 'l'; /* sort only loot by default */
723 for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++)
724 flags.end_disclose[i] = DISCLOSE_PROMPT_DEFAULT_NO;
725 switch_symbols(FALSE); /* set default characters */
726 #if defined(UNIX) && defined(TTY_GRAPHICS)
728 * Set defaults for some options depending on what we can
729 * detect about the environment's capabilities.
730 * This has to be done after the global initialization above
731 * and before reading user-specific initialization via
732 * config file/environment variable below.
734 /* this detects the IBM-compatible console on most 386 boxes */
735 if ((opts = nh_getenv("TERM")) && !strncmp(opts, "AT", 2)) {
736 if (!symset[PRIMARY].name)
737 load_symset("IBMGraphics", PRIMARY);
738 if (!symset[ROGUESET].name)
739 load_symset("RogueIBM", ROGUESET);
740 switch_symbols(TRUE);
741 #ifdef TEXTCOLOR
742 iflags.use_color = TRUE;
743 #endif
745 #endif /* UNIX && TTY_GRAPHICS */
746 #if defined(UNIX) || defined(VMS)
747 #ifdef TTY_GRAPHICS
748 /* detect whether a "vt" terminal can handle alternate charsets */
749 if ((opts = nh_getenv("TERM"))
750 /* [could also check "xterm" which emulates vtXXX by default] */
751 && !strncmpi(opts, "vt", 2)
752 && AS && AE && index(AS, '\016') && index(AE, '\017')) {
753 if (!symset[PRIMARY].name)
754 load_symset("DECGraphics", PRIMARY);
755 switch_symbols(TRUE);
757 #endif
758 #endif /* UNIX || VMS */
760 #ifdef MAC_GRAPHICS_ENV
761 if (!symset[PRIMARY].name)
762 load_symset("MACGraphics", PRIMARY);
763 switch_symbols(TRUE);
764 #endif /* MAC_GRAPHICS_ENV */
765 flags.menu_style = MENU_FULL;
767 /* since this is done before init_objects(), do partial init here */
768 objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD;
769 nmcpy(pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ);
772 void
773 initoptions_finish()
775 #ifndef MAC
776 char *opts = getenv("NETHACKOPTIONS");
778 if (!opts)
779 opts = getenv("HACKOPTIONS");
780 if (opts) {
781 if (*opts == '/' || *opts == '\\' || *opts == '@') {
782 if (*opts == '@')
783 opts++; /* @filename */
784 /* looks like a filename */
785 if (strlen(opts) < BUFSZ / 2)
786 read_config_file(opts, SET_IN_FILE);
787 } else {
788 read_config_file((char *) 0, SET_IN_FILE);
789 /* let the total length of options be long;
790 * parseoptions() will check each individually
792 parseoptions(opts, TRUE, FALSE);
794 } else
795 #endif
796 read_config_file((char *) 0, SET_IN_FILE);
798 (void) fruitadd(pl_fruit, (struct fruit *) 0);
800 * Remove "slime mold" from list of object names. This will
801 * prevent it from being wished unless it's actually present
802 * as a named (or default) fruit. Wishing for "fruit" will
803 * result in the player's preferred fruit [better than "\033"].
805 obj_descr[SLIME_MOLD].oc_name = "fruit";
807 if (iflags.bouldersym)
808 update_bouldersym();
809 reglyph_darkroom();
810 return;
813 STATIC_OVL void
814 nmcpy(dest, src, maxlen)
815 char *dest;
816 const char *src;
817 int maxlen;
819 int count;
821 for (count = 1; count < maxlen; count++) {
822 if (*src == ',' || *src == '\0')
823 break; /*exit on \0 terminator*/
824 *dest++ = *src++;
826 *dest = 0;
830 * escapes(): escape expansion for showsyms. C-style escapes understood
831 * include \n, \b, \t, \r, \xnnn (hex), \onnn (octal), \nnn (decimal).
832 * The ^-prefix for control characters is also understood, and \[mM]
833 * has the effect of 'meta'-ing the value which follows (so that the
834 * alternate character set will be enabled).
836 * X normal key X
837 * ^X control-X
838 * \mX meta-X
840 * For 3.4.3 and earlier, input ending with "\M", backslash, or caret
841 * prior to terminating '\0' would pull that '\0' into the output and then
842 * keep processing past it, potentially overflowing the output buffer.
843 * Now, trailing \ or ^ will act like \\ or \^ and add '\\' or '^' to the
844 * output and stop there; trailing \M will fall through to \<other> and
845 * yield 'M', then stop. Any \X or \O followed by something other than
846 * an appropriate digit will also fall through to \<other> and yield 'X'
847 * or 'O', plus stop if the non-digit is end-of-string.
849 STATIC_OVL void
850 escapes(cp, tp)
851 const char *cp;
852 char *tp;
854 static NEARDATA const char oct[] = "01234567", dec[] = "0123456789",
855 hex[] = "00112233445566778899aAbBcCdDeEfF";
856 const char *dp;
857 int cval, meta, dcount;
859 while (*cp) {
860 /* \M has to be followed by something to do meta conversion,
861 otherwise it will just be \M which ultimately yields 'M' */
862 meta = (*cp == '\\' && (cp[1] == 'm' || cp[1] == 'M') && cp[2]);
863 if (meta)
864 cp += 2;
866 cval = dcount = 0; /* for decimal, octal, hexadecimal cases */
867 if ((*cp != '\\' && *cp != '^') || !cp[1]) {
868 /* simple character, or nothing left for \ or ^ to escape */
869 cval = *cp++;
870 } else if (*cp == '^') { /* expand control-character syntax */
871 cval = (*++cp & 0x1f);
872 ++cp;
874 /* remaining cases are all for backslash; we know cp[1] is not \0 */
875 } else if (index(dec, cp[1])) {
876 ++cp; /* move past backslash to first digit */
877 do {
878 cval = (cval * 10) + (*cp - '0');
879 } while (*++cp && index(dec, *cp) && ++dcount < 3);
880 } else if ((cp[1] == 'o' || cp[1] == 'O') && cp[2]
881 && index(oct, cp[2])) {
882 cp += 2; /* move past backslash and 'O' */
883 do {
884 cval = (cval * 8) + (*cp - '0');
885 } while (*++cp && index(oct, *cp) && ++dcount < 3);
886 } else if ((cp[1] == 'x' || cp[1] == 'X') && cp[2]
887 && (dp = index(hex, cp[2])) != 0) {
888 cp += 2; /* move past backslash and 'X' */
889 do {
890 cval = (cval * 16) + ((int) (dp - hex) / 2);
891 } while (*++cp && (dp = index(hex, *cp)) != 0 && ++dcount < 2);
892 } else { /* C-style character escapes */
893 switch (*++cp) {
894 case '\\':
895 cval = '\\';
896 break;
897 case 'n':
898 cval = '\n';
899 break;
900 case 't':
901 cval = '\t';
902 break;
903 case 'b':
904 cval = '\b';
905 break;
906 case 'r':
907 cval = '\r';
908 break;
909 default:
910 cval = *cp;
912 ++cp;
915 if (meta)
916 cval |= 0x80;
917 *tp++ = (char) cval;
919 *tp = '\0';
922 STATIC_OVL void
923 rejectoption(optname)
924 const char *optname;
926 #ifdef MICRO
927 pline("\"%s\" settable only from %s.", optname, configfile);
928 #else
929 pline("%s can be set only from NETHACKOPTIONS or %s.", optname,
930 configfile);
931 #endif
934 STATIC_OVL void
935 badoptmsg(opts, reason)
936 const char *opts;
937 const char *reason; /* "Bad syntax" or "Missing value" */
939 const char *linesplit = "";
941 if (!initial) {
942 if (!strncmp(opts, "h", 1) || !strncmp(opts, "?", 1))
943 option_help();
944 else
945 pline("%s: %s. Enter \"?g\" for help.", reason, opts);
946 return;
947 #ifdef MAC
948 } else {
949 return;
950 #endif
953 #ifdef WIN32
954 linesplit = "\n";
955 #endif
956 if (from_file)
957 raw_printf("%s in OPTIONS in %s: %s%s.\n",
958 reason, configfile, linesplit, opts);
959 else
960 raw_printf("%s in NETHACKOPTIONS: %s%s.\n",
961 reason, linesplit, opts);
962 wait_synch();
965 STATIC_OVL void
966 badoption(opts)
967 const char *opts;
969 badoptmsg(opts, "Bad syntax");
972 STATIC_OVL char *
973 string_for_opt(opts, val_optional)
974 char *opts;
975 boolean val_optional;
977 char *colon, *equals;
979 colon = index(opts, ':');
980 equals = index(opts, '=');
981 if (!colon || (equals && equals < colon))
982 colon = equals;
984 if (!colon || !*++colon) {
985 if (!val_optional)
986 badoptmsg(opts, "Missing value");
987 return (char *) 0;
989 return colon;
992 STATIC_OVL char *
993 string_for_env_opt(optname, opts, val_optional)
994 const char *optname;
995 char *opts;
996 boolean val_optional;
998 if (!initial) {
999 rejectoption(optname);
1000 return (char *) 0;
1002 return string_for_opt(opts, val_optional);
1005 STATIC_OVL void
1006 bad_negation(optname, with_parameter)
1007 const char *optname;
1008 boolean with_parameter;
1010 pline_The("%s option may not %sbe negated.", optname,
1011 with_parameter ? "both have a value and " : "");
1015 * Change the inventory order, using the given string as the new order.
1016 * Missing characters in the new order are filled in at the end from
1017 * the current inv_order, except for gold, which is forced to be first
1018 * if not explicitly present.
1020 * This routine returns 1 unless there is a duplicate or bad char in
1021 * the string.
1023 STATIC_OVL int
1024 change_inv_order(op)
1025 char *op;
1027 int oc_sym, num;
1028 char *sp, buf[QBUFSZ];
1030 num = 0;
1031 if (!index(op, GOLD_SYM))
1032 buf[num++] = COIN_CLASS;
1034 for (sp = op; *sp; sp++) {
1035 oc_sym = def_char_to_objclass(*sp);
1036 /* reject bad or duplicate entries */
1037 if (oc_sym == MAXOCLASSES /* not an object class char */
1038 /* VENOM_CLASS, RANDOM_CLASS, and ILLOBJ_CLASS are excluded
1039 because they aren't in def_inv_order[] so don't make it
1040 into flags.inv_order, hence always fail this index() test */
1041 || !index(flags.inv_order, oc_sym) || index(sp + 1, *sp))
1042 return 0;
1043 /* retain good ones */
1044 buf[num++] = (char) oc_sym;
1046 buf[num] = '\0';
1048 /* fill in any omitted classes, using previous ordering */
1049 for (sp = flags.inv_order; *sp; sp++)
1050 if (!index(buf, *sp))
1051 (void) strkitten(&buf[num++], *sp);
1052 buf[MAXOCLASSES - 1] = '\0';
1054 Strcpy(flags.inv_order, buf);
1055 return 1;
1058 STATIC_OVL void
1059 warning_opts(opts, optype)
1060 register char *opts;
1061 const char *optype;
1063 uchar translate[WARNCOUNT];
1064 int length, i;
1066 if (!(opts = string_for_env_opt(optype, opts, FALSE)))
1067 return;
1068 escapes(opts, opts);
1070 length = (int) strlen(opts);
1071 /* match the form obtained from PC configuration files */
1072 for (i = 0; i < WARNCOUNT; i++)
1073 translate[i] = (i >= length) ? 0
1074 : opts[i] ? (uchar) opts[i]
1075 : def_warnsyms[i].sym;
1076 assign_warnings(translate);
1079 void
1080 assign_warnings(graph_chars)
1081 register uchar *graph_chars;
1083 int i;
1085 for (i = 0; i < WARNCOUNT; i++)
1086 if (graph_chars[i])
1087 warnsyms[i] = graph_chars[i];
1090 STATIC_OVL int
1091 feature_alert_opts(op, optn)
1092 char *op;
1093 const char *optn;
1095 char buf[BUFSZ];
1096 boolean rejectver = FALSE;
1097 unsigned long fnv = get_feature_notice_ver(op); /* version.c */
1099 if (fnv == 0L)
1100 return 0;
1101 if (fnv > get_current_feature_ver())
1102 rejectver = TRUE;
1103 else
1104 flags.suppress_alert = fnv;
1105 if (rejectver) {
1106 if (!initial) {
1107 You_cant("disable new feature alerts for future versions.");
1108 } else {
1109 Sprintf(buf,
1110 "\n%s=%s Invalid reference to a future version ignored",
1111 optn, op);
1112 badoption(buf);
1114 return 0;
1116 if (!initial) {
1117 Sprintf(buf, "%lu.%lu.%lu", FEATURE_NOTICE_VER_MAJ,
1118 FEATURE_NOTICE_VER_MIN, FEATURE_NOTICE_VER_PATCH);
1119 pline(
1120 "Feature change alerts disabled for NetHack %s features and prior.",
1121 buf);
1123 return 1;
1126 void
1127 set_duplicate_opt_detection(on_or_off)
1128 int on_or_off;
1130 int k, *optptr;
1132 if (on_or_off != 0) {
1133 /*-- ON --*/
1134 if (iflags.opt_booldup)
1135 impossible("iflags.opt_booldup already on (memory leak)");
1136 iflags.opt_booldup = (int *) alloc(SIZE(boolopt) * sizeof(int));
1137 optptr = iflags.opt_booldup;
1138 for (k = 0; k < SIZE(boolopt); ++k)
1139 *optptr++ = 0;
1141 if (iflags.opt_compdup)
1142 impossible("iflags.opt_compdup already on (memory leak)");
1143 iflags.opt_compdup = (int *) alloc(SIZE(compopt) * sizeof(int));
1144 optptr = iflags.opt_compdup;
1145 for (k = 0; k < SIZE(compopt); ++k)
1146 *optptr++ = 0;
1147 } else {
1148 /*-- OFF --*/
1149 if (iflags.opt_booldup)
1150 free((genericptr_t) iflags.opt_booldup);
1151 iflags.opt_booldup = (int *) 0;
1152 if (iflags.opt_compdup)
1153 free((genericptr_t) iflags.opt_compdup);
1154 iflags.opt_compdup = (int *) 0;
1158 STATIC_OVL boolean
1159 duplicate_opt_detection(opts, iscompound)
1160 const char *opts;
1161 int iscompound; /* 0 == boolean option, 1 == compound */
1163 int i, *optptr;
1165 if (!iscompound && iflags.opt_booldup && initial && from_file) {
1166 for (i = 0; boolopt[i].name; i++) {
1167 if (match_optname(opts, boolopt[i].name, 3, FALSE)) {
1168 optptr = iflags.opt_booldup + i;
1169 *optptr += 1;
1170 if (*optptr > 1)
1171 return TRUE;
1172 else
1173 return FALSE;
1176 } else if (iscompound && iflags.opt_compdup && initial && from_file) {
1177 for (i = 0; compopt[i].name; i++) {
1178 if (match_optname(opts, compopt[i].name, strlen(compopt[i].name),
1179 TRUE)) {
1180 optptr = iflags.opt_compdup + i;
1181 *optptr += 1;
1182 if (*optptr > 1)
1183 return TRUE;
1184 else
1185 return FALSE;
1189 return FALSE;
1192 STATIC_OVL void
1193 complain_about_duplicate(opts, iscompound)
1194 const char *opts;
1195 int iscompound; /* 0 == boolean option, 1 == compound */
1197 #ifdef MAC
1198 /* the Mac has trouble dealing with the output of messages while
1199 * processing the config file. That should get fixed one day.
1200 * For now just return.
1202 #else /* !MAC */
1203 raw_printf("\nWarning - %s option specified multiple times: %s.\n",
1204 iscompound ? "compound" : "boolean", opts);
1205 wait_synch();
1206 #endif /* ?MAC */
1207 return;
1210 /* paranoia[] - used by parseoptions() and special_handling() */
1211 STATIC_VAR const struct paranoia_opts {
1212 int flagmask; /* which paranoid option */
1213 const char *argname; /* primary name */
1214 int argMinLen; /* minimum number of letters to match */
1215 const char *synonym; /* alternate name (optional) */
1216 int synMinLen;
1217 const char *explain; /* for interactive menu */
1218 } paranoia[] = {
1219 /* there are some initial-letter conflicts: "a"ttack vs "a"ll, "attack"
1220 takes precedence and "all" isn't present in the interactive menu,
1221 and "d"ie vs "d"eath, synonyms for each other so doesn't matter;
1222 (also "p"ray vs "P"aranoia, "pray" takes precedence since "Paranoia"
1223 is just a synonym for "Confirm") */
1224 { PARANOID_CONFIRM, "Confirm", 1, "Paranoia", 2,
1225 "for \"yes\" confirmations, require \"no\" to reject" },
1226 { PARANOID_QUIT, "quit", 1, "explore", 1,
1227 "yes vs y to quit or to enter explore mode" },
1228 { PARANOID_DIE, "die", 1, "death", 2,
1229 "yes vs y to die (explore mode or debug mode)" },
1230 { PARANOID_BONES, "bones", 1, 0, 0,
1231 "yes vs y to save bones data when dying in debug mode" },
1232 { PARANOID_HIT, "attack", 1, "hit", 1,
1233 "yes vs y to attack a peaceful monster" },
1234 { PARANOID_PRAY, "pray", 1, 0, 0,
1235 "y to pray (supersedes old \"prayconfirm\" option)" },
1236 { PARANOID_REMOVE, "Remove", 1, "Takeoff", 1,
1237 "always pick from inventory for Remove and Takeoff" },
1238 { PARANOID_BREAKWAND, "wand", 1, "breakwand", 2,
1239 "yes vs y to break a wand" },
1240 /* for config file parsing; interactive menu skips these */
1241 { 0, "none", 4, 0, 0, 0 }, /* require full word match */
1242 { ~0, "all", 3, 0, 0, 0 }, /* ditto */
1245 extern struct menucoloring *menu_colorings;
1247 static const struct {
1248 const char *name;
1249 const int color;
1250 } colornames[] = {
1251 { "black", CLR_BLACK },
1252 { "red", CLR_RED },
1253 { "green", CLR_GREEN },
1254 { "brown", CLR_BROWN },
1255 { "blue", CLR_BLUE },
1256 { "magenta", CLR_MAGENTA },
1257 { "cyan", CLR_CYAN },
1258 { "gray", CLR_GRAY },
1259 { "orange", CLR_ORANGE },
1260 { "light green", CLR_BRIGHT_GREEN },
1261 { "yellow", CLR_YELLOW },
1262 { "light blue", CLR_BRIGHT_BLUE },
1263 { "light magenta", CLR_BRIGHT_MAGENTA },
1264 { "light cyan", CLR_BRIGHT_CYAN },
1265 { "white", CLR_WHITE },
1266 { NULL, CLR_BLACK }, /* everything after this is an alias */
1267 { "transparent", NO_COLOR },
1268 { "nocolor", NO_COLOR },
1269 { "purple", CLR_MAGENTA },
1270 { "light purple", CLR_BRIGHT_MAGENTA },
1271 { "bright purple", CLR_BRIGHT_MAGENTA },
1272 { "grey", CLR_GRAY },
1273 { "bright red", CLR_ORANGE },
1274 { "bright green", CLR_BRIGHT_GREEN },
1275 { "bright blue", CLR_BRIGHT_BLUE },
1276 { "bright magenta", CLR_BRIGHT_MAGENTA },
1277 { "bright cyan", CLR_BRIGHT_CYAN }
1280 static const struct {
1281 const char *name;
1282 const int attr;
1283 } attrnames[] = {
1284 { "none", ATR_NONE },
1285 { "bold", ATR_BOLD },
1286 { "dim", ATR_DIM },
1287 { "underline", ATR_ULINE },
1288 { "blink", ATR_BLINK },
1289 { "inverse", ATR_INVERSE }
1292 const char *
1293 clr2colorname(clr)
1294 int clr;
1296 int i;
1298 for (i = 0; i < SIZE(colornames); i++)
1299 if (colornames[i].name && colornames[i].color == clr)
1300 return colornames[i].name;
1301 return (char *) 0;
1305 match_str2clr(str)
1306 char *str;
1308 int i, c = NO_COLOR;
1310 /* allow "lightblue", "light blue", and "light-blue" to match "light blue"
1311 (also junk like "_l i-gh_t---b l u e" but we won't worry about that);
1312 also copes with trailing space; mungspaces removed any leading space */
1313 for (i = 0; i < SIZE(colornames); i++)
1314 if (colornames[i].name
1315 && fuzzymatch(str, colornames[i].name, " -_", TRUE)) {
1316 c = colornames[i].color;
1317 break;
1319 if (i == SIZE(colornames) && (*str >= '0' && *str <= '9'))
1320 c = atoi(str);
1321 return c;
1324 STATIC_OVL const char *
1325 attr2attrname(attr)
1326 int attr;
1328 int i;
1330 for (i = 0; i < SIZE(attrnames); i++)
1331 if (attrnames[i].attr == attr)
1332 return attrnames[i].name;
1333 return (char *) 0;
1336 STATIC_OVL int
1337 query_color()
1339 winid tmpwin;
1340 anything any;
1341 int i, pick_cnt;
1342 menu_item *picks = (menu_item *) 0;
1344 tmpwin = create_nhwindow(NHW_MENU);
1345 start_menu(tmpwin);
1346 any = zeroany;
1347 for (i = 0; i < SIZE(colornames); i++) {
1348 if (!colornames[i].name)
1349 break;
1350 any.a_int = i + 1;
1351 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, colornames[i].name,
1352 MENU_UNSELECTED);
1354 end_menu(tmpwin, "Pick a color");
1355 pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
1356 destroy_nhwindow(tmpwin);
1357 if (pick_cnt > 0) {
1358 i = colornames[picks->item.a_int - 1].color;
1359 free((genericptr_t) picks);
1360 return i;
1362 return -1;
1365 STATIC_OVL int
1366 query_attr(prompt)
1367 const char *prompt;
1369 winid tmpwin;
1370 anything any;
1371 int i, pick_cnt;
1372 menu_item *picks = (menu_item *) 0;
1374 tmpwin = create_nhwindow(NHW_MENU);
1375 start_menu(tmpwin);
1376 any = zeroany;
1377 for (i = 0; i < SIZE(attrnames); i++) {
1378 any.a_int = i + 1;
1379 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, attrnames[i].attr,
1380 attrnames[i].name, MENU_UNSELECTED);
1382 end_menu(tmpwin, prompt ? prompt : "Pick an attribute");
1383 pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
1384 destroy_nhwindow(tmpwin);
1385 if (pick_cnt > 0) {
1386 i = attrnames[picks->item.a_int - 1].attr;
1387 free((genericptr_t) picks);
1388 return i;
1390 return -1;
1393 static const struct {
1394 const char *name;
1395 const xchar msgtyp;
1396 const char *descr;
1397 } msgtype_names[] = {
1398 { "show", MSGTYP_NORMAL, "Show message normally" },
1399 { "hide", MSGTYP_NOSHOW, "Hide message" },
1400 { "noshow", MSGTYP_NOSHOW, NULL },
1401 { "stop", MSGTYP_STOP, "Prompt for more after the message" },
1402 { "more", MSGTYP_STOP, NULL },
1403 { "norep", MSGTYP_NOREP, "Do not repeat the message" }
1406 STATIC_OVL const char *
1407 msgtype2name(typ)
1408 int typ;
1410 int i;
1412 for (i = 0; i < SIZE(msgtype_names); i++)
1413 if (msgtype_names[i].descr && msgtype_names[i].msgtyp == typ)
1414 return msgtype_names[i].name;
1415 return (char *) 0;
1419 query_msgtype()
1421 winid tmpwin;
1422 anything any;
1423 int i, pick_cnt;
1424 menu_item *picks = (menu_item *) 0;
1426 tmpwin = create_nhwindow(NHW_MENU);
1427 start_menu(tmpwin);
1428 any = zeroany;
1429 for (i = 0; i < SIZE(msgtype_names); i++)
1430 if (msgtype_names[i].descr) {
1431 any.a_int = msgtype_names[i].msgtyp + 1;
1432 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1433 msgtype_names[i].descr, MENU_UNSELECTED);
1435 end_menu(tmpwin, "How to show the message");
1436 pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
1437 destroy_nhwindow(tmpwin);
1438 if (pick_cnt > 0) {
1439 i = picks->item.a_int - 1;
1440 free((genericptr_t) picks);
1441 return i;
1443 return -1;
1446 STATIC_OVL boolean
1447 msgtype_add(typ, pattern)
1448 int typ;
1449 char *pattern;
1451 struct plinemsg_type *tmp
1452 = (struct plinemsg_type *) alloc(sizeof (struct plinemsg_type));
1454 if (!tmp)
1455 return FALSE;
1456 tmp->msgtype = typ;
1457 tmp->regex = regex_init();
1458 if (!regex_compile(pattern, tmp->regex)) {
1459 static const char *re_error = "MSGTYPE regex error";
1461 if (!iflags.window_inited)
1462 raw_printf("\n%s: %s\n", re_error, regex_error_desc(tmp->regex));
1463 else
1464 pline("%s: %s", re_error, regex_error_desc(tmp->regex));
1465 wait_synch();
1466 regex_free(tmp->regex);
1467 free((genericptr_t) tmp);
1468 return FALSE;
1470 tmp->pattern = dupstr(pattern);
1471 tmp->next = plinemsg_types;
1472 plinemsg_types = tmp;
1473 return TRUE;
1476 void
1477 msgtype_free()
1479 struct plinemsg_type *tmp, *tmp2 = 0;
1481 for (tmp = plinemsg_types; tmp; tmp = tmp2) {
1482 tmp2 = tmp->next;
1483 free((genericptr_t) tmp->pattern);
1484 regex_free(tmp->regex);
1485 free((genericptr_t) tmp);
1487 plinemsg_types = (struct plinemsg_type *) 0;
1490 STATIC_OVL void
1491 free_one_msgtype(idx)
1492 int idx; /* 0 .. */
1494 struct plinemsg_type *tmp = plinemsg_types;
1495 struct plinemsg_type *prev = NULL;
1497 while (tmp) {
1498 if (idx == 0) {
1499 struct plinemsg_type *next = tmp->next;
1501 regex_free(tmp->regex);
1502 free((genericptr_t) tmp->pattern);
1503 free((genericptr_t) tmp);
1504 if (prev)
1505 prev->next = next;
1506 else
1507 plinemsg_types = next;
1508 return;
1510 idx--;
1511 prev = tmp;
1512 tmp = tmp->next;
1517 msgtype_type(msg, norepeat)
1518 const char *msg;
1519 boolean norepeat; /* called from Norep(via pline) */
1521 struct plinemsg_type *tmp = plinemsg_types;
1523 while (tmp) {
1524 /* we don't exclude entries with negative msgtype values
1525 because then the msg might end up matching a later pattern */
1526 if (regex_match(msg, tmp->regex))
1527 return tmp->msgtype;
1528 tmp = tmp->next;
1530 return norepeat ? MSGTYP_NOREP : MSGTYP_NORMAL;
1533 /* negate one or more types of messages so that their type handling will
1534 be disabled or re-enabled; MSGTYPE_NORMAL (value 0) is not affected */
1535 void
1536 hide_unhide_msgtypes(hide, hide_mask)
1537 boolean hide;
1538 int hide_mask;
1540 struct plinemsg_type *tmp;
1541 int mt;
1543 /* negative msgtype value won't be recognized by pline, so does nothing */
1544 for (tmp = plinemsg_types; tmp; tmp = tmp->next) {
1545 mt = tmp->msgtype;
1546 if (!hide)
1547 mt = -mt; /* unhide: negate negative, yielding positive */
1548 if (mt > 0 && ((1 << mt) & hide_mask))
1549 tmp->msgtype = -tmp->msgtype;
1553 STATIC_OVL int
1554 msgtype_count()
1556 int c = 0;
1557 struct plinemsg_type *tmp = plinemsg_types;
1559 while (tmp) {
1560 c++;
1561 tmp = tmp->next;
1563 return c;
1566 boolean
1567 msgtype_parse_add(str)
1568 char *str;
1570 char pattern[256];
1571 char msgtype[11];
1573 if (sscanf(str, "%10s \"%255[^\"]\"", msgtype, pattern) == 2) {
1574 int typ = -1;
1575 int i;
1577 for (i = 0; i < SIZE(msgtype_names); i++)
1578 if (!strncmpi(msgtype_names[i].name, msgtype, strlen(msgtype))) {
1579 typ = msgtype_names[i].msgtyp;
1580 break;
1582 if (typ != -1)
1583 return msgtype_add(typ, pattern);
1585 return FALSE;
1588 boolean
1589 add_menu_coloring_parsed(str, c, a)
1590 char *str;
1591 int c, a;
1593 static const char re_error[] = "Menucolor regex error";
1594 struct menucoloring *tmp;
1596 if (!str)
1597 return FALSE;
1598 tmp = (struct menucoloring *) alloc(sizeof (struct menucoloring));
1599 tmp->match = regex_init();
1600 if (!regex_compile(str, tmp->match)) {
1601 if (!iflags.window_inited)
1602 raw_printf("\n%s: %s\n", re_error, regex_error_desc(tmp->match));
1603 else
1604 pline("%s: %s", re_error, regex_error_desc(tmp->match));
1605 wait_synch();
1606 regex_free(tmp->match);
1607 free(tmp);
1608 return FALSE;
1609 } else {
1610 tmp->next = menu_colorings;
1611 tmp->origstr = dupstr(str);
1612 tmp->color = c;
1613 tmp->attr = a;
1614 menu_colorings = tmp;
1615 return TRUE;
1619 /* parse '"regex_string"=color&attr' and add it to menucoloring */
1620 boolean
1621 add_menu_coloring(str)
1622 char *str;
1624 int i, c = NO_COLOR, a = ATR_NONE;
1625 char *tmps, *cs, *amp;
1627 if (!str || (cs = index(str, '=')) == 0)
1628 return FALSE;
1630 tmps = cs + 1; /* advance past '=' */
1631 mungspaces(tmps);
1632 if ((amp = index(tmps, '&')) != 0)
1633 *amp = '\0';
1635 c = match_str2clr(tmps);
1636 if (c >= CLR_MAX)
1637 return FALSE;
1639 if (amp) {
1640 tmps = amp + 1; /* advance past '&' */
1641 /* unlike colors, none of he attribute names has any embedded spaces,
1642 but use of fuzzymatch() allows us ignore the presence of leading
1643 and/or trailing (and also embedded) spaces in the user's string;
1644 dash and underscore skipping could be omitted but does no harm */
1645 for (i = 0; i < SIZE(attrnames); i++)
1646 if (fuzzymatch(tmps, attrnames[i].name, " -_", TRUE)) {
1647 a = attrnames[i].attr;
1648 break;
1650 if (i == SIZE(attrnames) && (*tmps >= '0' && *tmps <= '9'))
1651 a = atoi(tmps);
1654 /* the regexp portion here has not been condensed by mungspaces() */
1655 *cs = '\0';
1656 tmps = str;
1657 if (*tmps == '"' || *tmps == '\'') {
1658 cs--;
1659 while (isspace((uchar) *cs))
1660 cs--;
1661 if (*cs == *tmps) {
1662 *cs = '\0';
1663 tmps++;
1667 return add_menu_coloring_parsed(tmps, c, a);
1670 boolean
1671 get_menu_coloring(str, color, attr)
1672 char *str;
1673 int *color, *attr;
1675 struct menucoloring *tmpmc;
1677 if (iflags.use_menu_color)
1678 for (tmpmc = menu_colorings; tmpmc; tmpmc = tmpmc->next)
1679 if (regex_match(str, tmpmc->match)) {
1680 *color = tmpmc->color;
1681 *attr = tmpmc->attr;
1682 return TRUE;
1684 return FALSE;
1687 void
1688 free_menu_coloring()
1690 struct menucoloring *tmp = menu_colorings;
1692 while (tmp) {
1693 struct menucoloring *tmp2 = tmp->next;
1695 regex_free(tmp->match);
1696 free((genericptr_t) tmp->origstr);
1697 free((genericptr_t) tmp);
1698 tmp = tmp2;
1702 STATIC_OVL void
1703 free_one_menu_coloring(idx)
1704 int idx; /* 0 .. */
1706 struct menucoloring *tmp = menu_colorings;
1707 struct menucoloring *prev = NULL;
1709 while (tmp) {
1710 if (idx == 0) {
1711 struct menucoloring *next = tmp->next;
1713 regex_free(tmp->match);
1714 free((genericptr_t) tmp->origstr);
1715 free((genericptr_t) tmp);
1716 if (prev)
1717 prev->next = next;
1718 else
1719 menu_colorings = next;
1720 return;
1722 idx--;
1723 prev = tmp;
1724 tmp = tmp->next;
1728 STATIC_OVL int
1729 count_menucolors()
1731 int count = 0;
1732 struct menucoloring *tmp = menu_colorings;
1734 while (tmp) {
1735 count++;
1736 tmp = tmp->next;
1738 return count;
1741 STATIC_OVL boolean
1742 parse_role_opts(negated, fullname, opts, opp)
1743 boolean negated;
1744 const char *fullname;
1745 char *opts;
1746 char **opp;
1748 char *op = *opp;
1750 if (negated) {
1751 bad_negation(fullname, FALSE);
1752 } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
1753 boolean val_negated = FALSE;
1755 while ((*op == '!') || !strncmpi(op, "no", 2)) {
1756 if (*op == '!')
1757 op++;
1758 else
1759 op += 2;
1760 val_negated = !val_negated;
1762 if (val_negated) {
1763 if (!setrolefilter(op))
1764 badoption(opts);
1765 } else {
1766 if (duplicate_opt_detection(opts, 1))
1767 complain_about_duplicate(opts, 1);
1768 *opp = op;
1769 return TRUE;
1772 return FALSE;
1775 /* Check if character c is illegal as a menu command key */
1776 boolean
1777 illegal_menu_cmd_key(c)
1778 char c;
1780 if (c == 0 || c == '\r' || c == '\n' || c == '\033'
1781 || c == ' ' || digit(c) || (letter(c) && c != '@'))
1782 return TRUE;
1783 else { /* reject default object class symbols */
1784 int j;
1785 for (j = 1; j < MAXOCLASSES; j++)
1786 if (c == def_oc_syms[j].sym)
1787 return TRUE;
1789 return FALSE;
1792 void
1793 parseoptions(opts, tinitial, tfrom_file)
1794 register char *opts;
1795 boolean tinitial, tfrom_file;
1797 char *op;
1798 unsigned num;
1799 boolean negated, duplicate;
1800 int i;
1801 const char *fullname;
1803 initial = tinitial;
1804 from_file = tfrom_file;
1805 if ((op = index(opts, ',')) != 0) {
1806 *op++ = 0;
1807 parseoptions(op, initial, from_file);
1809 if (strlen(opts) > BUFSZ / 2) {
1810 badoption("option too long");
1811 return;
1814 /* strip leading and trailing white space */
1815 while (isspace((uchar) *opts))
1816 opts++;
1817 op = eos(opts);
1818 while (--op >= opts && isspace((uchar) *op))
1819 *op = '\0';
1821 if (!*opts)
1822 return;
1823 negated = FALSE;
1824 while ((*opts == '!') || !strncmpi(opts, "no", 2)) {
1825 if (*opts == '!')
1826 opts++;
1827 else
1828 opts += 2;
1829 negated = !negated;
1832 /* variant spelling */
1834 if (match_optname(opts, "colour", 5, FALSE))
1835 Strcpy(opts, "color"); /* fortunately this isn't longer */
1837 /* special boolean options */
1839 if (match_optname(opts, "female", 3, FALSE)) {
1840 if (duplicate_opt_detection(opts, 0))
1841 complain_about_duplicate(opts, 0);
1842 if (!initial && flags.female == negated)
1843 pline("That is not anatomically possible.");
1844 else
1845 flags.initgend = flags.female = !negated;
1846 return;
1849 if (match_optname(opts, "male", 4, FALSE)) {
1850 if (duplicate_opt_detection(opts, 0))
1851 complain_about_duplicate(opts, 0);
1852 if (!initial && flags.female != negated)
1853 pline("That is not anatomically possible.");
1854 else
1855 flags.initgend = flags.female = negated;
1856 return;
1859 #if defined(MICRO) && !defined(AMIGA)
1860 /* included for compatibility with old NetHack.cnf files */
1861 if (match_optname(opts, "IBM_", 4, FALSE)) {
1862 iflags.BIOS = !negated;
1863 return;
1865 #endif /* MICRO */
1867 /* compound options */
1869 /* This first batch can be duplicated if their values are negated */
1871 /* align:string */
1872 fullname = "align";
1873 if (match_optname(opts, fullname, sizeof("align") - 1, TRUE)) {
1874 if (parse_role_opts(negated, fullname, opts, &op)) {
1875 if ((flags.initalign = str2align(op)) == ROLE_NONE)
1876 badoption(opts);
1878 return;
1881 /* role:string or character:string */
1882 fullname = "role";
1883 if (match_optname(opts, fullname, 4, TRUE)
1884 || match_optname(opts, (fullname = "character"), 4, TRUE)) {
1885 if (parse_role_opts(negated, fullname, opts, &op)) {
1886 if ((flags.initrole = str2role(op)) == ROLE_NONE)
1887 badoption(opts);
1888 else /* Backwards compatibility */
1889 nmcpy(pl_character, op, PL_NSIZ);
1891 return;
1894 /* race:string */
1895 fullname = "race";
1896 if (match_optname(opts, fullname, 4, TRUE)) {
1897 if (parse_role_opts(negated, fullname, opts, &op)) {
1898 if ((flags.initrace = str2race(op)) == ROLE_NONE)
1899 badoption(opts);
1900 else /* Backwards compatibility */
1901 pl_race = *op;
1903 return;
1906 /* gender:string */
1907 fullname = "gender";
1908 if (match_optname(opts, fullname, 4, TRUE)) {
1909 if (parse_role_opts(negated, fullname, opts, &op)) {
1910 if ((flags.initgend = str2gend(op)) == ROLE_NONE)
1911 badoption(opts);
1912 else
1913 flags.female = flags.initgend;
1915 return;
1918 /* We always check for duplicates on the remaining compound options,
1919 although individual option processing can choose to complain or not */
1921 duplicate =
1922 duplicate_opt_detection(opts, 1); /* 1 means check compounds */
1924 fullname = "pettype";
1925 if (match_optname(opts, fullname, 3, TRUE)) {
1926 if (duplicate)
1927 complain_about_duplicate(opts, 1);
1928 if ((op = string_for_env_opt(fullname, opts, negated)) != 0) {
1929 if (negated)
1930 bad_negation(fullname, TRUE);
1931 else
1932 switch (lowc(*op)) {
1933 case 'd': /* dog */
1934 preferred_pet = 'd';
1935 break;
1936 case 'c': /* cat */
1937 case 'f': /* feline */
1938 preferred_pet = 'c';
1939 break;
1940 case 'h': /* horse */
1941 case 'q': /* quadruped */
1942 /* avoids giving "unrecognized type of pet" but
1943 pet_type(dog.c) won't actually honor this */
1944 preferred_pet = 'h';
1945 break;
1946 case 'n': /* no pet */
1947 preferred_pet = 'n';
1948 break;
1949 case '*': /* random */
1950 preferred_pet = '\0';
1951 break;
1952 default:
1953 pline("Unrecognized pet type '%s'.", op);
1954 break;
1956 } else if (negated)
1957 preferred_pet = 'n';
1958 return;
1961 fullname = "catname";
1962 if (match_optname(opts, fullname, 3, TRUE)) {
1963 if (duplicate)
1964 complain_about_duplicate(opts, 1);
1965 if (negated)
1966 bad_negation(fullname, FALSE);
1967 else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1968 nmcpy(catname, op, PL_PSIZ);
1969 sanitize_name(catname);
1970 return;
1973 fullname = "dogname";
1974 if (match_optname(opts, fullname, 3, TRUE)) {
1975 if (duplicate)
1976 complain_about_duplicate(opts, 1);
1977 if (negated)
1978 bad_negation(fullname, FALSE);
1979 else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1980 nmcpy(dogname, op, PL_PSIZ);
1981 sanitize_name(dogname);
1982 return;
1985 fullname = "horsename";
1986 if (match_optname(opts, fullname, 5, TRUE)) {
1987 if (duplicate)
1988 complain_about_duplicate(opts, 1);
1989 if (negated)
1990 bad_negation(fullname, FALSE);
1991 else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1992 nmcpy(horsename, op, PL_PSIZ);
1993 sanitize_name(horsename);
1994 return;
1997 fullname = "number_pad";
1998 if (match_optname(opts, fullname, 10, TRUE)) {
1999 boolean compat = (strlen(opts) <= 10);
2001 if (duplicate)
2002 complain_about_duplicate(opts, 1);
2003 op = string_for_opt(opts, (compat || !initial));
2004 if (!op) {
2005 if (compat || negated || initial) {
2006 /* for backwards compatibility, "number_pad" without a
2007 value is a synonym for number_pad:1 */
2008 iflags.num_pad = !negated;
2009 iflags.num_pad_mode = 0;
2011 } else if (negated) {
2012 bad_negation("number_pad", TRUE);
2013 return;
2014 } else {
2015 int mode = atoi(op);
2017 if (mode < -1 || mode > 4 || (mode == 0 && *op != '0')) {
2018 badoption(opts);
2019 return;
2020 } else if (mode <= 0) {
2021 iflags.num_pad = FALSE;
2022 /* German keyboard; y and z keys swapped */
2023 iflags.num_pad_mode = (mode < 0); /* 0 or 1 */
2024 } else { /* mode > 0 */
2025 iflags.num_pad = TRUE;
2026 iflags.num_pad_mode = 0;
2027 /* PC Hack / MSDOS compatibility */
2028 if (mode == 2 || mode == 4)
2029 iflags.num_pad_mode |= 1;
2030 /* phone keypad layout */
2031 if (mode == 3 || mode == 4)
2032 iflags.num_pad_mode |= 2;
2035 reset_commands(FALSE);
2036 number_pad(iflags.num_pad ? 1 : 0);
2037 return;
2040 fullname = "roguesymset";
2041 if (match_optname(opts, fullname, 7, TRUE)) {
2042 if (duplicate)
2043 complain_about_duplicate(opts, 1);
2044 if (negated) {
2045 bad_negation(fullname, FALSE);
2046 } else if ((op = string_for_opt(opts, FALSE)) != 0) {
2047 symset[ROGUESET].name = dupstr(op);
2048 if (!read_sym_file(ROGUESET)) {
2049 clear_symsetentry(ROGUESET, TRUE);
2050 raw_printf("Unable to load symbol set \"%s\" from \"%s\".",
2051 op, SYMBOLS);
2052 wait_synch();
2053 } else {
2054 if (!initial && Is_rogue_level(&u.uz))
2055 assign_graphics(ROGUESET);
2056 need_redraw = TRUE;
2059 return;
2062 fullname = "symset";
2063 if (match_optname(opts, fullname, 6, TRUE)) {
2064 if (duplicate)
2065 complain_about_duplicate(opts, 1);
2066 if (negated) {
2067 bad_negation(fullname, FALSE);
2068 } else if ((op = string_for_opt(opts, FALSE)) != 0) {
2069 symset[PRIMARY].name = dupstr(op);
2070 if (!read_sym_file(PRIMARY)) {
2071 clear_symsetentry(PRIMARY, TRUE);
2072 raw_printf("Unable to load symbol set \"%s\" from \"%s\".",
2073 op, SYMBOLS);
2074 wait_synch();
2075 } else {
2076 switch_symbols(symset[PRIMARY].name != (char *) 0);
2077 need_redraw = TRUE;
2080 return;
2083 fullname = "runmode";
2084 if (match_optname(opts, fullname, 4, TRUE)) {
2085 if (duplicate)
2086 complain_about_duplicate(opts, 1);
2087 if (negated) {
2088 flags.runmode = RUN_TPORT;
2089 } else if ((op = string_for_opt(opts, FALSE)) != 0) {
2090 if (!strncmpi(op, "teleport", strlen(op)))
2091 flags.runmode = RUN_TPORT;
2092 else if (!strncmpi(op, "run", strlen(op)))
2093 flags.runmode = RUN_LEAP;
2094 else if (!strncmpi(op, "walk", strlen(op)))
2095 flags.runmode = RUN_STEP;
2096 else if (!strncmpi(op, "crawl", strlen(op)))
2097 flags.runmode = RUN_CRAWL;
2098 else
2099 badoption(opts);
2101 return;
2104 /* menucolor:"regex_string"=color */
2105 fullname = "menucolor";
2106 if (match_optname(opts, fullname, 9, TRUE)) {
2107 if (negated)
2108 bad_negation(fullname, FALSE);
2109 else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
2110 if (!add_menu_coloring(op))
2111 badoption(opts);
2112 return;
2115 fullname = "msghistory";
2116 if (match_optname(opts, fullname, 3, TRUE)) {
2117 if (duplicate)
2118 complain_about_duplicate(opts, 1);
2119 op = string_for_env_opt(fullname, opts, negated);
2120 if ((negated && !op) || (!negated && op)) {
2121 iflags.msg_history = negated ? 0 : atoi(op);
2122 } else if (negated)
2123 bad_negation(fullname, TRUE);
2124 return;
2127 fullname = "msg_window";
2128 /* msg_window:single, combo, full or reversed */
2129 if (match_optname(opts, fullname, 4, TRUE)) {
2130 /* allow option to be silently ignored by non-tty ports */
2131 #ifdef TTY_GRAPHICS
2132 int tmp;
2134 if (duplicate)
2135 complain_about_duplicate(opts, 1);
2136 if (!(op = string_for_opt(opts, TRUE))) {
2137 tmp = negated ? 's' : 'f';
2138 } else {
2139 if (negated) {
2140 bad_negation(fullname, TRUE);
2141 return;
2143 tmp = lowc(*op);
2145 switch (tmp) {
2146 case 's': /* single message history cycle (default if negated) */
2147 iflags.prevmsg_window = 's';
2148 break;
2149 case 'c': /* combination: two singles, then full page reversed */
2150 iflags.prevmsg_window = 'c';
2151 break;
2152 case 'f': /* full page (default if no opts) */
2153 iflags.prevmsg_window = 'f';
2154 break;
2155 case 'r': /* full page (reversed) */
2156 iflags.prevmsg_window = 'r';
2157 break;
2158 default:
2159 badoption(opts);
2161 #endif
2162 return;
2165 /* WINCAP
2166 * setting font options */
2167 fullname = "font";
2168 if (!strncmpi(opts, fullname, 4)) {
2169 int opttype = -1;
2170 char *fontopts = opts + 4;
2172 if (!strncmpi(fontopts, "map", 3) || !strncmpi(fontopts, "_map", 4))
2173 opttype = MAP_OPTION;
2174 else if (!strncmpi(fontopts, "message", 7)
2175 || !strncmpi(fontopts, "_message", 8))
2176 opttype = MESSAGE_OPTION;
2177 else if (!strncmpi(fontopts, "text", 4)
2178 || !strncmpi(fontopts, "_text", 5))
2179 opttype = TEXT_OPTION;
2180 else if (!strncmpi(fontopts, "menu", 4)
2181 || !strncmpi(fontopts, "_menu", 5))
2182 opttype = MENU_OPTION;
2183 else if (!strncmpi(fontopts, "status", 6)
2184 || !strncmpi(fontopts, "_status", 7))
2185 opttype = STATUS_OPTION;
2186 else if (!strncmpi(fontopts, "_size", 5)) {
2187 if (!strncmpi(fontopts, "_size_map", 8))
2188 opttype = MAP_OPTION;
2189 else if (!strncmpi(fontopts, "_size_message", 12))
2190 opttype = MESSAGE_OPTION;
2191 else if (!strncmpi(fontopts, "_size_text", 9))
2192 opttype = TEXT_OPTION;
2193 else if (!strncmpi(fontopts, "_size_menu", 9))
2194 opttype = MENU_OPTION;
2195 else if (!strncmpi(fontopts, "_size_status", 11))
2196 opttype = STATUS_OPTION;
2197 else {
2198 badoption(opts);
2199 return;
2201 if (duplicate)
2202 complain_about_duplicate(opts, 1);
2203 if (opttype > 0 && !negated
2204 && (op = string_for_opt(opts, FALSE)) != 0) {
2205 switch (opttype) {
2206 case MAP_OPTION:
2207 iflags.wc_fontsiz_map = atoi(op);
2208 break;
2209 case MESSAGE_OPTION:
2210 iflags.wc_fontsiz_message = atoi(op);
2211 break;
2212 case TEXT_OPTION:
2213 iflags.wc_fontsiz_text = atoi(op);
2214 break;
2215 case MENU_OPTION:
2216 iflags.wc_fontsiz_menu = atoi(op);
2217 break;
2218 case STATUS_OPTION:
2219 iflags.wc_fontsiz_status = atoi(op);
2220 break;
2223 return;
2224 } else {
2225 badoption(opts);
2227 if (opttype > 0 && (op = string_for_opt(opts, FALSE)) != 0) {
2228 wc_set_font_name(opttype, op);
2229 #ifdef MAC
2230 set_font_name(opttype, op);
2231 #endif
2232 return;
2233 } else if (negated)
2234 bad_negation(fullname, TRUE);
2235 return;
2238 #ifdef CHANGE_COLOR
2239 if (match_optname(opts, "palette", 3, TRUE)
2240 #ifdef MAC
2241 || match_optname(opts, "hicolor", 3, TRUE)
2242 #endif
2244 int color_number, color_incr;
2246 #ifndef WIN32
2247 if (duplicate)
2248 complain_about_duplicate(opts, 1);
2249 #endif
2250 #ifdef MAC
2251 if (match_optname(opts, "hicolor", 3, TRUE)) {
2252 if (negated) {
2253 bad_negation("hicolor", FALSE);
2254 return;
2256 color_number = CLR_MAX + 4; /* HARDCODED inverse number */
2257 color_incr = -1;
2258 } else
2259 #endif
2261 if (negated) {
2262 bad_negation("palette", FALSE);
2263 return;
2265 color_number = 0;
2266 color_incr = 1;
2268 #ifdef WIN32
2269 op = string_for_opt(opts, TRUE);
2270 if (!alternative_palette(op))
2271 badoption(opts);
2272 #else
2273 if ((op = string_for_opt(opts, FALSE)) != (char *) 0) {
2274 char *pt = op;
2275 int cnt, tmp, reverse;
2276 long rgb;
2278 while (*pt && color_number >= 0) {
2279 cnt = 3;
2280 rgb = 0L;
2281 if (*pt == '-') {
2282 reverse = 1;
2283 pt++;
2284 } else {
2285 reverse = 0;
2287 while (cnt-- > 0) {
2288 if (*pt && *pt != '/') {
2289 #ifdef AMIGA
2290 rgb <<= 4;
2291 #else
2292 rgb <<= 8;
2293 #endif
2294 tmp = *pt++;
2295 if (isalpha((uchar) tmp)) {
2296 tmp = (tmp + 9) & 0xf; /* Assumes ASCII... */
2297 } else {
2298 tmp &= 0xf; /* Digits in ASCII too... */
2300 #ifndef AMIGA
2301 /* Add an extra so we fill f -> ff and 0 -> 00 */
2302 rgb += tmp << 4;
2303 #endif
2304 rgb += tmp;
2307 if (*pt == '/')
2308 pt++;
2309 change_color(color_number, rgb, reverse);
2310 color_number += color_incr;
2313 #endif /* !WIN32 */
2314 if (!initial) {
2315 need_redraw = TRUE;
2317 return;
2319 #endif /* CHANGE_COLOR */
2321 if (match_optname(opts, "fruit", 2, TRUE)) {
2322 struct fruit *forig = 0;
2323 char empty_str = '\0';
2325 if (duplicate)
2326 complain_about_duplicate(opts, 1);
2327 op = string_for_opt(opts, negated);
2328 if (negated) {
2329 if (op) {
2330 bad_negation("fruit", TRUE);
2331 return;
2333 op = &empty_str;
2334 goto goodfruit;
2336 if (!op)
2337 return;
2338 if (!initial) {
2339 struct fruit *f;
2341 num = 0;
2342 for (f = ffruit; f; f = f->nextf) {
2343 if (!strcmp(op, f->fname))
2344 break;
2345 num++;
2347 if (!flags.made_fruit) {
2348 for (forig = ffruit; forig; forig = forig->nextf) {
2349 if (!strcmp(pl_fruit, forig->fname)) {
2350 break;
2354 if (!forig && num >= 100) {
2355 pline("Doing that so many times isn't very fruitful.");
2356 return;
2359 goodfruit:
2360 nmcpy(pl_fruit, op, PL_FSIZ);
2361 sanitize_name(pl_fruit);
2362 /* OBJ_NAME(objects[SLIME_MOLD]) won't work after initialization */
2363 if (!*pl_fruit)
2364 nmcpy(pl_fruit, "slime mold", PL_FSIZ);
2365 if (!initial) {
2366 (void) fruitadd(pl_fruit, forig);
2367 pline("Fruit is now \"%s\".", pl_fruit);
2369 /* If initial, then initoptions is allowed to do it instead
2370 * of here (initoptions always has to do it even if there's
2371 * no fruit option at all. Also, we don't want people
2372 * setting multiple fruits in their options.)
2374 return;
2377 fullname = "whatis_coord";
2378 if (match_optname(opts, fullname, 6, TRUE)) {
2379 if (duplicate)
2380 complain_about_duplicate(opts, 1);
2381 if (negated) {
2382 iflags.getpos_coords = GPCOORDS_NONE;
2383 return;
2384 } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
2385 static char gpcoords[] = { GPCOORDS_NONE, GPCOORDS_COMPASS,
2386 GPCOORDS_COMFULL, GPCOORDS_MAP,
2387 GPCOORDS_SCREEN, '\0' };
2388 char c = lowc(*op);
2390 if (c && index(gpcoords, c))
2391 iflags.getpos_coords = c;
2392 else
2393 badoption(opts);
2395 return;
2398 fullname = "warnings";
2399 if (match_optname(opts, fullname, 5, TRUE)) {
2400 if (duplicate)
2401 complain_about_duplicate(opts, 1);
2402 if (negated)
2403 bad_negation(fullname, FALSE);
2404 else
2405 warning_opts(opts, fullname);
2406 return;
2409 #ifdef BACKWARD_COMPAT
2410 /* boulder:symbol */
2411 fullname = "boulder";
2412 if (match_optname(opts, fullname, 7, TRUE)) {
2413 int clash = 0;
2414 if (duplicate)
2415 complain_about_duplicate(opts, 1);
2416 if (negated) {
2417 bad_negation(fullname, FALSE);
2418 return;
2420 /* if (!(opts = string_for_env_opt(fullname, opts, FALSE)))
2422 if (!(opts = string_for_opt(opts, FALSE)))
2423 return;
2424 escapes(opts, opts);
2425 if (def_char_to_monclass(opts[0]) != MAXMCLASSES)
2426 clash = 1;
2427 else if (opts[0] >= '1' && opts[0] <= '5')
2428 clash = 2;
2429 if (clash) {
2430 /* symbol chosen matches a used monster or warning
2431 symbol which is not good - reject it*/
2432 pline(
2433 "Badoption - boulder symbol '%c' conflicts with a %s symbol.",
2434 opts[0], (clash == 1) ? "monster" : "warning");
2435 } else {
2437 * Override the default boulder symbol.
2439 iflags.bouldersym = (uchar) opts[0];
2441 if (!initial)
2442 need_redraw = TRUE;
2443 return;
2445 #endif
2447 /* name:string */
2448 fullname = "name";
2449 if (match_optname(opts, fullname, 4, TRUE)) {
2450 if (duplicate)
2451 complain_about_duplicate(opts, 1);
2452 if (negated)
2453 bad_negation(fullname, FALSE);
2454 else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
2455 nmcpy(plname, op, PL_NSIZ);
2456 return;
2459 /* altkeyhandler:string */
2460 fullname = "altkeyhandler";
2461 if (match_optname(opts, fullname, 4, TRUE)) {
2462 if (duplicate)
2463 complain_about_duplicate(opts, 1);
2464 if (negated) {
2465 bad_negation(fullname, FALSE);
2466 } else if ((op = string_for_opt(opts, negated)) != 0) {
2467 #ifdef WIN32
2468 (void) strncpy(iflags.altkeyhandler, op, MAX_ALTKEYHANDLER - 5);
2469 load_keyboard_handler();
2470 #endif
2472 return;
2475 /* WINCAP
2476 * align_status:[left|top|right|bottom] */
2477 fullname = "align_status";
2478 if (match_optname(opts, fullname, sizeof("align_status") - 1, TRUE)) {
2479 op = string_for_opt(opts, negated);
2480 if (op && !negated) {
2481 if (!strncmpi(op, "left", sizeof("left") - 1))
2482 iflags.wc_align_status = ALIGN_LEFT;
2483 else if (!strncmpi(op, "top", sizeof("top") - 1))
2484 iflags.wc_align_status = ALIGN_TOP;
2485 else if (!strncmpi(op, "right", sizeof("right") - 1))
2486 iflags.wc_align_status = ALIGN_RIGHT;
2487 else if (!strncmpi(op, "bottom", sizeof("bottom") - 1))
2488 iflags.wc_align_status = ALIGN_BOTTOM;
2489 else
2490 badoption(opts);
2491 } else if (negated)
2492 bad_negation(fullname, TRUE);
2493 return;
2495 /* WINCAP
2496 * align_message:[left|top|right|bottom] */
2497 fullname = "align_message";
2498 if (match_optname(opts, fullname, sizeof("align_message") - 1, TRUE)) {
2499 if (duplicate)
2500 complain_about_duplicate(opts, 1);
2501 op = string_for_opt(opts, negated);
2502 if (op && !negated) {
2503 if (!strncmpi(op, "left", sizeof("left") - 1))
2504 iflags.wc_align_message = ALIGN_LEFT;
2505 else if (!strncmpi(op, "top", sizeof("top") - 1))
2506 iflags.wc_align_message = ALIGN_TOP;
2507 else if (!strncmpi(op, "right", sizeof("right") - 1))
2508 iflags.wc_align_message = ALIGN_RIGHT;
2509 else if (!strncmpi(op, "bottom", sizeof("bottom") - 1))
2510 iflags.wc_align_message = ALIGN_BOTTOM;
2511 else
2512 badoption(opts);
2513 } else if (negated)
2514 bad_negation(fullname, TRUE);
2515 return;
2517 /* the order to list the pack */
2518 fullname = "packorder";
2519 if (match_optname(opts, fullname, 4, TRUE)) {
2520 if (duplicate)
2521 complain_about_duplicate(opts, 1);
2522 if (negated) {
2523 bad_negation(fullname, FALSE);
2524 return;
2525 } else if (!(op = string_for_opt(opts, FALSE)))
2526 return;
2528 if (!change_inv_order(op))
2529 badoption(opts);
2530 return;
2533 /* user can change required response for some prompts (quit, die, hit),
2534 or add an extra prompt (pray, Remove) that isn't ordinarily there */
2535 fullname = "paranoid_confirmation";
2536 if (match_optname(opts, fullname, 8, TRUE)) {
2537 /* at present we don't complain about duplicates for this
2538 option, but we do throw away the old settings whenever
2539 we process a new one [clearing old flags is essential
2540 for handling default paranoid_confirm:pray sanely] */
2541 flags.paranoia_bits = 0; /* clear all */
2542 if (negated) {
2543 flags.paranoia_bits = 0; /* [now redundant...] */
2544 } else if ((op = string_for_opt(opts, TRUE)) != 0) {
2545 char *pp, buf[BUFSZ];
2547 strncpy(buf, op, sizeof buf - 1);
2548 buf[sizeof buf - 1] = '\0';
2549 op = mungspaces(buf);
2550 for (;;) {
2551 /* We're looking to parse
2552 "paranoid_confirm:whichone wheretwo whothree"
2553 and "paranoid_confirm:" prefix has already
2554 been stripped off by the time we get here */
2555 pp = index(op, ' ');
2556 if (pp)
2557 *pp = '\0';
2558 /* we aren't matching option names but match_optname
2559 does what we want once we've broken the space
2560 delimited aggregate into separate tokens */
2561 for (i = 0; i < SIZE(paranoia); ++i) {
2562 if (match_optname(op, paranoia[i].argname,
2563 paranoia[i].argMinLen, FALSE)
2564 || (paranoia[i].synonym
2565 && match_optname(op, paranoia[i].synonym,
2566 paranoia[i].synMinLen, FALSE))) {
2567 if (paranoia[i].flagmask)
2568 flags.paranoia_bits |= paranoia[i].flagmask;
2569 else /* 0 == "none", so clear all */
2570 flags.paranoia_bits = 0;
2571 break;
2574 if (i == SIZE(paranoia)) {
2575 /* didn't match anything, so arg is bad;
2576 any flags already set will stay set */
2577 badoption(opts);
2578 break;
2580 /* move on to next token */
2581 if (pp)
2582 op = pp + 1;
2583 else
2584 break; /* no next token */
2585 } /* for(;;) */
2587 return;
2590 /* accept deprecated boolean; superseded by paranoid_confirm:pray */
2591 fullname = "prayconfirm";
2592 if (match_optname(opts, fullname, 4, FALSE)) {
2593 if (negated)
2594 flags.paranoia_bits &= ~PARANOID_PRAY;
2595 else
2596 flags.paranoia_bits |= PARANOID_PRAY;
2597 return;
2600 /* maximum burden picked up before prompt (Warren Cheung) */
2601 fullname = "pickup_burden";
2602 if (match_optname(opts, fullname, 8, TRUE)) {
2603 if (duplicate)
2604 complain_about_duplicate(opts, 1);
2605 if (negated) {
2606 bad_negation(fullname, FALSE);
2607 return;
2608 } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
2609 switch (lowc(*op)) {
2610 case 'u': /* Unencumbered */
2611 flags.pickup_burden = UNENCUMBERED;
2612 break;
2613 case 'b': /* Burdened (slight encumbrance) */
2614 flags.pickup_burden = SLT_ENCUMBER;
2615 break;
2616 case 's': /* streSsed (moderate encumbrance) */
2617 flags.pickup_burden = MOD_ENCUMBER;
2618 break;
2619 case 'n': /* straiNed (heavy encumbrance) */
2620 flags.pickup_burden = HVY_ENCUMBER;
2621 break;
2622 case 'o': /* OverTaxed (extreme encumbrance) */
2623 case 't':
2624 flags.pickup_burden = EXT_ENCUMBER;
2625 break;
2626 case 'l': /* overLoaded */
2627 flags.pickup_burden = OVERLOADED;
2628 break;
2629 default:
2630 badoption(opts);
2633 return;
2636 /* types of objects to pick up automatically */
2637 if (match_optname(opts, "pickup_types", 8, TRUE)) {
2638 char ocl[MAXOCLASSES + 1], tbuf[MAXOCLASSES + 1], qbuf[QBUFSZ],
2639 abuf[BUFSZ];
2640 int oc_sym;
2641 boolean badopt = FALSE, compat = (strlen(opts) <= 6), use_menu;
2643 if (duplicate)
2644 complain_about_duplicate(opts, 1);
2645 oc_to_str(flags.pickup_types, tbuf);
2646 flags.pickup_types[0] = '\0'; /* all */
2647 op = string_for_opt(opts, (compat || !initial));
2648 if (!op) {
2649 if (compat || negated || initial) {
2650 /* for backwards compatibility, "pickup" without a
2651 value is a synonym for autopickup of all types
2652 (and during initialization, we can't prompt yet) */
2653 flags.pickup = !negated;
2654 return;
2656 oc_to_str(flags.inv_order, ocl);
2657 use_menu = TRUE;
2658 if (flags.menu_style == MENU_TRADITIONAL
2659 || flags.menu_style == MENU_COMBINATION) {
2660 use_menu = FALSE;
2661 Sprintf(qbuf, "New pickup_types: [%s am] (%s)", ocl,
2662 *tbuf ? tbuf : "all");
2663 getlin(qbuf, abuf);
2664 op = mungspaces(abuf);
2665 if (abuf[0] == '\0' || abuf[0] == '\033')
2666 op = tbuf; /* restore */
2667 else if (abuf[0] == 'm')
2668 use_menu = TRUE;
2670 if (use_menu) {
2671 (void) choose_classes_menu("Auto-Pickup what?", 1, TRUE, ocl,
2672 tbuf);
2673 op = tbuf;
2676 if (negated) {
2677 bad_negation("pickup_types", TRUE);
2678 return;
2680 while (*op == ' ')
2681 op++;
2682 if (*op != 'a' && *op != 'A') {
2683 num = 0;
2684 while (*op) {
2685 oc_sym = def_char_to_objclass(*op);
2686 /* make sure all are valid obj symbols occurring once */
2687 if (oc_sym != MAXOCLASSES
2688 && !index(flags.pickup_types, oc_sym)) {
2689 flags.pickup_types[num] = (char) oc_sym;
2690 flags.pickup_types[++num] = '\0';
2691 } else
2692 badopt = TRUE;
2693 op++;
2695 if (badopt)
2696 badoption(opts);
2698 return;
2701 /* pile limit: when walking over objects, number which triggers
2702 "there are several/many objects here" instead of listing them */
2703 fullname = "pile_limit";
2704 if (match_optname(opts, fullname, 4, TRUE)) {
2705 if (duplicate)
2706 complain_about_duplicate(opts, 1);
2707 op = string_for_opt(opts, negated);
2708 if ((negated && !op) || (!negated && op))
2709 flags.pile_limit = negated ? 0 : atoi(op);
2710 else if (negated)
2711 bad_negation(fullname, TRUE);
2712 else /* !op */
2713 flags.pile_limit = PILE_LIMIT_DFLT;
2714 /* sanity check */
2715 if (flags.pile_limit < 0)
2716 flags.pile_limit = PILE_LIMIT_DFLT;
2717 return;
2720 /* play mode: normal, explore/discovery, or debug/wizard */
2721 fullname = "playmode";
2722 if (match_optname(opts, fullname, 4, TRUE)) {
2723 if (duplicate)
2724 complain_about_duplicate(opts, 1);
2725 if (negated)
2726 bad_negation(fullname, FALSE);
2727 if (duplicate || negated)
2728 return;
2729 op = string_for_opt(opts, FALSE);
2730 if (!op)
2731 return;
2732 if (!strncmpi(op, "normal", 6) || !strcmpi(op, "play")) {
2733 wizard = discover = FALSE;
2734 } else if (!strncmpi(op, "explore", 6)
2735 || !strncmpi(op, "discovery", 6)) {
2736 wizard = FALSE, discover = TRUE;
2737 } else if (!strncmpi(op, "debug", 5) || !strncmpi(op, "wizard", 6)) {
2738 wizard = TRUE, discover = FALSE;
2739 } else {
2740 raw_printf("Invalid value for \"%s\":%s.", fullname, op);
2742 return;
2745 /* WINCAP
2746 * player_selection: dialog | prompts */
2747 fullname = "player_selection";
2748 if (match_optname(opts, fullname, sizeof("player_selection") - 1, TRUE)) {
2749 if (duplicate)
2750 complain_about_duplicate(opts, 1);
2751 op = string_for_opt(opts, negated);
2752 if (op && !negated) {
2753 if (!strncmpi(op, "dialog", sizeof("dialog") - 1))
2754 iflags.wc_player_selection = VIA_DIALOG;
2755 else if (!strncmpi(op, "prompt", sizeof("prompt") - 1))
2756 iflags.wc_player_selection = VIA_PROMPTS;
2757 else
2758 badoption(opts);
2759 } else if (negated)
2760 bad_negation(fullname, TRUE);
2761 return;
2764 /* things to disclose at end of game */
2765 if (match_optname(opts, "disclose", 7, TRUE)) {
2767 * The order that the end_disclose options are stored:
2768 * inventory, attribs, vanquished, genocided,
2769 * conduct, overview.
2770 * There is an array in flags:
2771 * end_disclose[NUM_DISCLOSURE_OPT];
2772 * with option settings for the each of the following:
2773 * iagvc [see disclosure_options in decl.c]:
2774 * Legal setting values in that array are:
2775 * DISCLOSE_PROMPT_DEFAULT_YES ask with default answer yes
2776 * DISCLOSE_PROMPT_DEFAULT_NO ask with default answer no
2777 * DISCLOSE_YES_WITHOUT_PROMPT always disclose and don't ask
2778 * DISCLOSE_NO_WITHOUT_PROMPT never disclose and don't ask
2780 * Those setting values can be used in the option
2781 * string as a prefix to get the desired behaviour.
2783 * For backward compatibility, no prefix is required,
2784 * and the presence of a i,a,g,v, or c without a prefix
2785 * sets the corresponding value to DISCLOSE_YES_WITHOUT_PROMPT.
2787 boolean badopt = FALSE;
2788 int idx, prefix_val;
2790 if (duplicate)
2791 complain_about_duplicate(opts, 1);
2792 op = string_for_opt(opts, TRUE);
2793 if (op && negated) {
2794 bad_negation("disclose", TRUE);
2795 return;
2797 /* "disclose" without a value means "all with prompting"
2798 and negated means "none without prompting" */
2799 if (!op || !strcmpi(op, "all") || !strcmpi(op, "none")) {
2800 if (op && !strcmpi(op, "none"))
2801 negated = TRUE;
2802 for (num = 0; num < NUM_DISCLOSURE_OPTIONS; num++)
2803 flags.end_disclose[num] = negated
2804 ? DISCLOSE_NO_WITHOUT_PROMPT
2805 : DISCLOSE_PROMPT_DEFAULT_YES;
2806 return;
2809 num = 0;
2810 prefix_val = -1;
2811 while (*op && num < sizeof flags.end_disclose - 1) {
2812 static char valid_settings[] = {
2813 DISCLOSE_PROMPT_DEFAULT_YES, DISCLOSE_PROMPT_DEFAULT_NO,
2814 DISCLOSE_PROMPT_DEFAULT_SPECIAL,
2815 DISCLOSE_YES_WITHOUT_PROMPT, DISCLOSE_NO_WITHOUT_PROMPT,
2816 DISCLOSE_SPECIAL_WITHOUT_PROMPT, '\0'
2818 register char c, *dop;
2820 c = lowc(*op);
2821 if (c == 'k')
2822 c = 'v'; /* killed -> vanquished */
2823 if (c == 'd')
2824 c = 'o'; /* dungeon -> overview */
2825 dop = index(disclosure_options, c);
2826 if (dop) {
2827 idx = (int) (dop - disclosure_options);
2828 if (idx < 0 || idx > NUM_DISCLOSURE_OPTIONS - 1) {
2829 impossible("bad disclosure index %d %c", idx, c);
2830 continue;
2832 if (prefix_val != -1) {
2833 if (*dop != 'v') {
2834 if (prefix_val == DISCLOSE_PROMPT_DEFAULT_SPECIAL)
2835 prefix_val = DISCLOSE_PROMPT_DEFAULT_YES;
2836 if (prefix_val == DISCLOSE_SPECIAL_WITHOUT_PROMPT)
2837 prefix_val = DISCLOSE_YES_WITHOUT_PROMPT;
2839 flags.end_disclose[idx] = prefix_val;
2840 prefix_val = -1;
2841 } else
2842 flags.end_disclose[idx] = DISCLOSE_YES_WITHOUT_PROMPT;
2843 } else if (index(valid_settings, c)) {
2844 prefix_val = c;
2845 } else if (c == ' ') {
2846 ; /* do nothing */
2847 } else
2848 badopt = TRUE;
2849 op++;
2851 if (badopt)
2852 badoption(opts);
2853 return;
2856 /* scores:5t[op] 5a[round] o[wn] */
2857 if (match_optname(opts, "scores", 4, TRUE)) {
2858 if (duplicate)
2859 complain_about_duplicate(opts, 1);
2860 if (negated) {
2861 bad_negation("scores", FALSE);
2862 return;
2864 if (!(op = string_for_opt(opts, FALSE)))
2865 return;
2867 while (*op) {
2868 int inum = 1;
2870 if (digit(*op)) {
2871 inum = atoi(op);
2872 while (digit(*op))
2873 op++;
2874 } else if (*op == '!') {
2875 negated = !negated;
2876 op++;
2878 while (*op == ' ')
2879 op++;
2881 switch (*op) {
2882 case 't':
2883 case 'T':
2884 flags.end_top = inum;
2885 break;
2886 case 'a':
2887 case 'A':
2888 flags.end_around = inum;
2889 break;
2890 case 'o':
2891 case 'O':
2892 flags.end_own = !negated;
2893 break;
2894 default:
2895 badoption(opts);
2896 return;
2898 while (letter(*++op) || *op == ' ')
2899 continue;
2900 if (*op == '/')
2901 op++;
2903 return;
2906 fullname = "sortloot";
2907 if (match_optname(opts, fullname, 4, TRUE)) {
2908 op = string_for_env_opt(fullname, opts, FALSE);
2909 if (op) {
2910 char c = lowc(*op);
2912 switch (c) {
2913 case 'n': /* none */
2914 case 'l': /* loot (pickup) */
2915 case 'f': /* full (pickup + invent) */
2916 flags.sortloot = c;
2917 break;
2918 default:
2919 badoption(opts);
2920 return;
2923 return;
2926 fullname = "suppress_alert";
2927 if (match_optname(opts, fullname, 4, TRUE)) {
2928 if (duplicate)
2929 complain_about_duplicate(opts, 1);
2930 op = string_for_opt(opts, negated);
2931 if (negated)
2932 bad_negation(fullname, FALSE);
2933 else if (op)
2934 (void) feature_alert_opts(op, fullname);
2935 return;
2938 #ifdef VIDEOSHADES
2939 /* videocolors:string */
2940 fullname = "videocolors";
2941 if (match_optname(opts, fullname, 6, TRUE)
2942 || match_optname(opts, "videocolours", 10, TRUE)) {
2943 if (duplicate)
2944 complain_about_duplicate(opts, 1);
2945 if (negated) {
2946 bad_negation(fullname, FALSE);
2947 return;
2948 } else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
2949 return;
2951 if (!assign_videocolors(opts))
2952 badoption(opts);
2953 return;
2955 /* videoshades:string */
2956 fullname = "videoshades";
2957 if (match_optname(opts, fullname, 6, TRUE)) {
2958 if (duplicate)
2959 complain_about_duplicate(opts, 1);
2960 if (negated) {
2961 bad_negation(fullname, FALSE);
2962 return;
2963 } else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
2964 return;
2966 if (!assign_videoshades(opts))
2967 badoption(opts);
2968 return;
2970 #endif /* VIDEOSHADES */
2971 #ifdef MSDOS
2972 #ifdef NO_TERMS
2973 /* video:string -- must be after longer tests */
2974 fullname = "video";
2975 if (match_optname(opts, fullname, 5, TRUE)) {
2976 if (duplicate)
2977 complain_about_duplicate(opts, 1);
2978 if (negated) {
2979 bad_negation(fullname, FALSE);
2980 return;
2981 } else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
2982 return;
2984 if (!assign_video(opts))
2985 badoption(opts);
2986 return;
2988 #endif /* NO_TERMS */
2989 /* soundcard:string -- careful not to match boolean 'sound' */
2990 fullname = "soundcard";
2991 if (match_optname(opts, fullname, 6, TRUE)) {
2992 if (duplicate)
2993 complain_about_duplicate(opts, 1);
2994 if (negated) {
2995 bad_negation(fullname, FALSE);
2996 return;
2997 } else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
2998 return;
3000 if (!assign_soundcard(opts))
3001 badoption(opts);
3002 return;
3004 #endif /* MSDOS */
3006 /* WINCAP
3008 * map_mode:[tiles|ascii4x6|ascii6x8|ascii8x8|ascii16x8|ascii7x12|
3009 * ascii8x12|ascii16x12|ascii12x16|ascii10x18|fit_to_screen]
3011 fullname = "map_mode";
3012 if (match_optname(opts, fullname, sizeof("map_mode") - 1, TRUE)) {
3013 if (duplicate)
3014 complain_about_duplicate(opts, 1);
3015 op = string_for_opt(opts, negated);
3016 if (op && !negated) {
3017 if (!strncmpi(op, "tiles", sizeof("tiles") - 1))
3018 iflags.wc_map_mode = MAP_MODE_TILES;
3019 else if (!strncmpi(op, "ascii4x6", sizeof("ascii4x6") - 1))
3020 iflags.wc_map_mode = MAP_MODE_ASCII4x6;
3021 else if (!strncmpi(op, "ascii6x8", sizeof("ascii6x8") - 1))
3022 iflags.wc_map_mode = MAP_MODE_ASCII6x8;
3023 else if (!strncmpi(op, "ascii8x8", sizeof("ascii8x8") - 1))
3024 iflags.wc_map_mode = MAP_MODE_ASCII8x8;
3025 else if (!strncmpi(op, "ascii16x8", sizeof("ascii16x8") - 1))
3026 iflags.wc_map_mode = MAP_MODE_ASCII16x8;
3027 else if (!strncmpi(op, "ascii7x12", sizeof("ascii7x12") - 1))
3028 iflags.wc_map_mode = MAP_MODE_ASCII7x12;
3029 else if (!strncmpi(op, "ascii8x12", sizeof("ascii8x12") - 1))
3030 iflags.wc_map_mode = MAP_MODE_ASCII8x12;
3031 else if (!strncmpi(op, "ascii16x12", sizeof("ascii16x12") - 1))
3032 iflags.wc_map_mode = MAP_MODE_ASCII16x12;
3033 else if (!strncmpi(op, "ascii12x16", sizeof("ascii12x16") - 1))
3034 iflags.wc_map_mode = MAP_MODE_ASCII12x16;
3035 else if (!strncmpi(op, "ascii10x18", sizeof("ascii10x18") - 1))
3036 iflags.wc_map_mode = MAP_MODE_ASCII10x18;
3037 else if (!strncmpi(op, "fit_to_screen",
3038 sizeof("fit_to_screen") - 1))
3039 iflags.wc_map_mode = MAP_MODE_ASCII_FIT_TO_SCREEN;
3040 else
3041 badoption(opts);
3042 } else if (negated)
3043 bad_negation(fullname, TRUE);
3044 return;
3046 /* WINCAP
3047 * scroll_amount:nn */
3048 fullname = "scroll_amount";
3049 if (match_optname(opts, fullname, sizeof("scroll_amount") - 1, TRUE)) {
3050 if (duplicate)
3051 complain_about_duplicate(opts, 1);
3052 op = string_for_opt(opts, negated);
3053 if ((negated && !op) || (!negated && op)) {
3054 iflags.wc_scroll_amount = negated ? 1 : atoi(op);
3055 } else if (negated)
3056 bad_negation(fullname, TRUE);
3057 return;
3059 /* WINCAP
3060 * scroll_margin:nn */
3061 fullname = "scroll_margin";
3062 if (match_optname(opts, fullname, sizeof("scroll_margin") - 1, TRUE)) {
3063 if (duplicate)
3064 complain_about_duplicate(opts, 1);
3065 op = string_for_opt(opts, negated);
3066 if ((negated && !op) || (!negated && op)) {
3067 iflags.wc_scroll_margin = negated ? 5 : atoi(op);
3068 } else if (negated)
3069 bad_negation(fullname, TRUE);
3070 return;
3072 fullname = "subkeyvalue";
3073 if (match_optname(opts, fullname, 5, TRUE)) {
3074 /* no duplicate complaint here */
3075 if (negated) {
3076 bad_negation(fullname, FALSE);
3077 } else {
3078 #if defined(WIN32)
3079 op = string_for_opt(opts, 0);
3080 map_subkeyvalue(op);
3081 #endif
3083 return;
3085 /* WINCAP
3086 * tile_width:nn */
3087 fullname = "tile_width";
3088 if (match_optname(opts, fullname, sizeof("tile_width") - 1, TRUE)) {
3089 if (duplicate)
3090 complain_about_duplicate(opts, 1);
3091 op = string_for_opt(opts, negated);
3092 if ((negated && !op) || (!negated && op)) {
3093 iflags.wc_tile_width = negated ? 0 : atoi(op);
3094 } else if (negated)
3095 bad_negation(fullname, TRUE);
3096 return;
3098 /* WINCAP
3099 * tile_file:name */
3100 fullname = "tile_file";
3101 if (match_optname(opts, fullname, sizeof("tile_file") - 1, TRUE)) {
3102 if (duplicate)
3103 complain_about_duplicate(opts, 1);
3104 if ((op = string_for_opt(opts, FALSE)) != 0) {
3105 if (iflags.wc_tile_file)
3106 free(iflags.wc_tile_file);
3107 iflags.wc_tile_file = dupstr(op);
3109 return;
3111 /* WINCAP
3112 * tile_height:nn */
3113 fullname = "tile_height";
3114 if (match_optname(opts, fullname, sizeof("tile_height") - 1, TRUE)) {
3115 if (duplicate)
3116 complain_about_duplicate(opts, 1);
3117 op = string_for_opt(opts, negated);
3118 if ((negated && !op) || (!negated && op)) {
3119 iflags.wc_tile_height = negated ? 0 : atoi(op);
3120 } else if (negated)
3121 bad_negation(fullname, TRUE);
3122 return;
3124 /* WINCAP
3125 * vary_msgcount:nn */
3126 fullname = "vary_msgcount";
3127 if (match_optname(opts, fullname, sizeof("vary_msgcount") - 1, TRUE)) {
3128 if (duplicate)
3129 complain_about_duplicate(opts, 1);
3130 op = string_for_opt(opts, negated);
3131 if ((negated && !op) || (!negated && op)) {
3132 iflags.wc_vary_msgcount = negated ? 0 : atoi(op);
3133 } else if (negated)
3134 bad_negation(fullname, TRUE);
3135 return;
3137 fullname = "windowtype";
3138 if (match_optname(opts, fullname, 3, TRUE)) {
3139 if (duplicate)
3140 complain_about_duplicate(opts, 1);
3141 if (negated) {
3142 bad_negation(fullname, FALSE);
3143 return;
3144 } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
3145 char buf[WINTYPELEN];
3146 nmcpy(buf, op, WINTYPELEN);
3147 choose_windows(buf);
3149 return;
3151 #ifdef WINCHAIN
3152 fullname = "windowchain";
3153 if (match_optname(opts, fullname, 3, TRUE)) {
3154 if (negated) {
3155 bad_negation(fullname, FALSE);
3156 return;
3157 } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
3158 char buf[WINTYPELEN];
3159 nmcpy(buf, op, WINTYPELEN);
3160 addto_windowchain(buf);
3162 return;
3164 #endif
3166 /* WINCAP
3167 * setting window colors
3168 * syntax: windowcolors=menu foregrnd/backgrnd text foregrnd/backgrnd
3170 fullname = "windowcolors";
3171 if (match_optname(opts, fullname, 7, TRUE)) {
3172 if (duplicate)
3173 complain_about_duplicate(opts, 1);
3174 if ((op = string_for_opt(opts, FALSE)) != 0) {
3175 if (!wc_set_window_colors(op))
3176 badoption(opts);
3177 } else if (negated)
3178 bad_negation(fullname, TRUE);
3179 return;
3182 /* menustyle:traditional or combination or full or partial */
3183 if (match_optname(opts, "menustyle", 4, TRUE)) {
3184 int tmp;
3185 boolean val_required = (strlen(opts) > 5 && !negated);
3187 if (duplicate)
3188 complain_about_duplicate(opts, 1);
3189 if (!(op = string_for_opt(opts, !val_required))) {
3190 if (val_required)
3191 return; /* string_for_opt gave feedback */
3192 tmp = negated ? 'n' : 'f';
3193 } else {
3194 tmp = lowc(*op);
3196 switch (tmp) {
3197 case 'n': /* none */
3198 case 't': /* traditional: prompt for class(es) by symbol,
3199 prompt for each item within class(es) one at a time */
3200 flags.menu_style = MENU_TRADITIONAL;
3201 break;
3202 case 'c': /* combination: prompt for class(es) by symbol,
3203 choose items within selected class(es) by menu */
3204 flags.menu_style = MENU_COMBINATION;
3205 break;
3206 case 'f': /* full: choose class(es) by first menu,
3207 choose items within selected class(es) by second menu */
3208 flags.menu_style = MENU_FULL;
3209 break;
3210 case 'p': /* partial: skip class filtering,
3211 choose items among all classes by menu */
3212 flags.menu_style = MENU_PARTIAL;
3213 break;
3214 default:
3215 badoption(opts);
3217 return;
3220 fullname = "menu_headings";
3221 if (match_optname(opts, fullname, 12, TRUE)) {
3222 if (duplicate)
3223 complain_about_duplicate(opts, 1);
3224 if (negated) {
3225 bad_negation(fullname, FALSE);
3226 return;
3227 } else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
3228 return;
3230 for (i = 0; i < SIZE(attrnames); i++)
3231 if (!strcmpi(opts, attrnames[i].name)) {
3232 iflags.menu_headings = attrnames[i].attr;
3233 return;
3235 badoption(opts);
3236 return;
3239 /* check for menu command mapping */
3240 for (i = 0; i < SIZE(default_menu_cmd_info); i++) {
3241 fullname = default_menu_cmd_info[i].name;
3242 if (duplicate)
3243 complain_about_duplicate(opts, 1);
3244 if (match_optname(opts, fullname, (int) strlen(fullname), TRUE)) {
3245 if (negated) {
3246 bad_negation(fullname, FALSE);
3247 } else if ((op = string_for_opt(opts, FALSE)) != 0) {
3248 char c, op_buf[BUFSZ];
3250 escapes(op, op_buf);
3251 c = *op_buf;
3253 if (illegal_menu_cmd_key(c))
3254 badoption(opts);
3255 else
3256 add_menu_cmd_alias(c, default_menu_cmd_info[i].cmd);
3258 return;
3261 #if defined(STATUS_VIA_WINDOWPORT) && defined(STATUS_HILITES)
3262 /* hilite fields in status prompt */
3263 if (match_optname(opts, "hilite_status", 13, TRUE)) {
3264 if (duplicate)
3265 complain_about_duplicate(opts, 1);
3266 op = string_for_opt(opts, TRUE);
3267 if (op && negated) {
3268 clear_status_hilites(tfrom_file);
3269 return;
3270 } else if (!op) {
3271 /* a value is mandatory */
3272 badoption(opts);
3273 return;
3275 if (!set_status_hilites(op, tfrom_file))
3276 badoption(opts);
3277 return;
3279 #endif
3281 #if defined(BACKWARD_COMPAT)
3282 fullname = "DECgraphics";
3283 if (match_optname(opts, fullname, 3, TRUE)) {
3284 boolean badflag = FALSE;
3286 if (duplicate)
3287 complain_about_duplicate(opts, 1);
3288 if (!negated) {
3289 /* There is no rogue level DECgraphics-specific set */
3290 if (symset[PRIMARY].name) {
3291 badflag = TRUE;
3292 } else {
3293 symset[PRIMARY].name = dupstr(fullname);
3294 if (!read_sym_file(PRIMARY)) {
3295 badflag = TRUE;
3296 clear_symsetentry(PRIMARY, TRUE);
3297 } else
3298 switch_symbols(TRUE);
3300 if (badflag) {
3301 pline("Failure to load symbol set %s.", fullname);
3302 wait_synch();
3305 return;
3307 fullname = "IBMgraphics";
3308 if (match_optname(opts, fullname, 3, TRUE)) {
3309 const char *sym_name = fullname;
3310 boolean badflag = FALSE;
3312 if (duplicate)
3313 complain_about_duplicate(opts, 1);
3314 if (!negated) {
3315 for (i = 0; i < NUM_GRAPHICS; ++i) {
3316 if (symset[i].name) {
3317 badflag = TRUE;
3318 } else {
3319 if (i == ROGUESET)
3320 sym_name = "RogueIBM";
3321 symset[i].name = dupstr(sym_name);
3322 if (!read_sym_file(i)) {
3323 badflag = TRUE;
3324 clear_symsetentry(i, TRUE);
3325 break;
3329 if (badflag) {
3330 pline("Failure to load symbol set %s.", sym_name);
3331 wait_synch();
3332 } else {
3333 switch_symbols(TRUE);
3334 if (!initial && Is_rogue_level(&u.uz))
3335 assign_graphics(ROGUESET);
3338 return;
3340 #endif
3341 #ifdef MAC_GRAPHICS_ENV
3342 fullname = "MACgraphics";
3343 if (match_optname(opts, fullname, 3, TRUE)) {
3344 boolean badflag = FALSE;
3346 if (duplicate)
3347 complain_about_duplicate(opts, 1);
3348 if (!negated) {
3349 if (symset[PRIMARY].name) {
3350 badflag = TRUE;
3351 } else {
3352 symset[PRIMARY].name = dupstr(fullname);
3353 if (!read_sym_file(PRIMARY)) {
3354 badflag = TRUE;
3355 clear_symsetentry(PRIMARY, TRUE);
3358 if (badflag) {
3359 pline("Failure to load symbol set %s.", fullname);
3360 wait_synch();
3361 } else {
3362 switch_symbols(TRUE);
3363 if (!initial && Is_rogue_level(&u.uz))
3364 assign_graphics(ROGUESET);
3367 return;
3369 #endif
3371 /* OK, if we still haven't recognized the option, check the boolean
3372 * options list
3374 for (i = 0; boolopt[i].name; i++) {
3375 if (match_optname(opts, boolopt[i].name, 3, TRUE)) {
3376 /* options that don't exist */
3377 if (!boolopt[i].addr) {
3378 if (!initial && !negated)
3379 pline_The("\"%s\" option is not available.",
3380 boolopt[i].name);
3381 return;
3383 /* options that must come from config file */
3384 if (!initial && (boolopt[i].optflags == SET_IN_FILE)) {
3385 rejectoption(boolopt[i].name);
3386 return;
3389 op = string_for_opt(opts, TRUE);
3391 if (op) {
3392 if (negated) {
3393 badoption(opts);
3394 return;
3396 if (!strcmp(op, "true") || !strcmp(op, "yes")) {
3397 negated = FALSE;
3398 } else if (!strcmp(op, "false") || !strcmp(op, "no")) {
3399 negated = TRUE;
3400 } else {
3401 badoption(opts);
3402 return;
3406 *(boolopt[i].addr) = !negated;
3408 /* 0 means boolean opts */
3409 if (duplicate_opt_detection(boolopt[i].name, 0))
3410 complain_about_duplicate(boolopt[i].name, 0);
3411 #ifdef RLECOMP
3412 if (boolopt[i].addr == &iflags.rlecomp)
3413 set_savepref(iflags.rlecomp ? "rlecomp" : "!rlecomp");
3414 #endif
3415 #ifdef ZEROCOMP
3416 if (boolopt[i].addr == &iflags.zerocomp)
3417 set_savepref(iflags.zerocomp ? "zerocomp" : "externalcomp");
3418 #endif
3419 /* only do processing below if setting with doset() */
3420 if (initial)
3421 return;
3423 if (boolopt[i].addr == &flags.time
3424 #ifdef SCORE_ON_BOTL
3425 || boolopt[i].addr == &flags.showscore
3426 #endif
3427 || boolopt[i].addr == &flags.showexp) {
3428 #ifdef STATUS_VIA_WINDOWPORT
3429 status_initialize(REASSESS_ONLY);
3430 #endif
3431 context.botl = TRUE;
3432 } else if (boolopt[i].addr == &flags.invlet_constant) {
3433 if (flags.invlet_constant)
3434 reassign();
3435 } else if (boolopt[i].addr == &flags.lit_corridor
3436 || boolopt[i].addr == &flags.dark_room) {
3438 * All corridor squares seen via night vision or
3439 * candles & lamps change. Update them by calling
3440 * newsym() on them. Don't do this if we are
3441 * initializing the options --- the vision system
3442 * isn't set up yet.
3444 vision_recalc(2); /* shut down vision */
3445 vision_full_recalc = 1; /* delayed recalc */
3446 if (iflags.use_color)
3447 need_redraw = TRUE; /* darkroom refresh */
3448 } else if (boolopt[i].addr == &iflags.wc_tiled_map
3449 || boolopt[i].addr == &flags.showrace
3450 || boolopt[i].addr == &iflags.use_inverse
3451 || boolopt[i].addr == &iflags.hilite_pile
3452 || boolopt[i].addr == &iflags.hilite_pet) {
3453 need_redraw = TRUE;
3454 #ifdef TEXTCOLOR
3455 } else if (boolopt[i].addr == &iflags.use_color) {
3456 need_redraw = TRUE;
3457 #ifdef TOS
3458 if (iflags.BIOS) {
3459 if (colors_changed)
3460 restore_colors();
3461 else
3462 set_colors();
3464 #endif
3465 #endif /* TEXTCOLOR */
3467 return;
3471 /* Is it a symbol? */
3472 if (strstr(opts, "S_") == opts && parsesymbols(opts)) {
3473 switch_symbols(TRUE);
3474 return;
3477 /* out of valid options */
3478 badoption(opts);
3481 /* parse key:command */
3482 void
3483 parsebindings(bindings)
3484 char* bindings;
3486 char *bind;
3487 char key;
3488 int i;
3490 /* break off first binding from the rest; parse the rest */
3491 if ((bind = index(bindings, ',')) != 0) {
3492 *bind++ = 0;
3493 parsebindings(bind);
3496 /* parse a single binding: first split around : */
3497 if (! (bind = index(bindings, ':'))) return; /* it's not a binding */
3498 *bind++ = 0;
3500 /* read the key to be bound */
3501 key = txt2key(bindings);
3502 if (!key) {
3503 raw_printf("Bad binding %s.", bindings);
3504 wait_synch();
3505 return;
3508 bind = trimspaces(bind);
3510 /* is it a special key? */
3511 if (bind_specialkey(key, bind))
3512 return;
3514 /* is it a menu command? */
3515 for (i = 0; i < SIZE(default_menu_cmd_info); i++) {
3516 if (!strcmp(default_menu_cmd_info[i].name, bind)) {
3517 if (illegal_menu_cmd_key(key)) {
3518 char tmp[BUFSZ];
3519 Sprintf(tmp, "Bad menu key %s:%s", visctrl(key), bind);
3520 badoption(tmp);
3521 } else
3522 add_menu_cmd_alias(key, default_menu_cmd_info[i].cmd);
3523 return;
3527 /* extended command? */
3528 bind_key(key, bind);
3531 static NEARDATA const char *menutype[] = { "traditional", "combination",
3532 "full", "partial" };
3534 static NEARDATA const char *burdentype[] = { "unencumbered", "burdened",
3535 "stressed", "strained",
3536 "overtaxed", "overloaded" };
3538 static NEARDATA const char *runmodes[] = { "teleport", "run", "walk",
3539 "crawl" };
3541 static NEARDATA const char *sortltype[] = { "none", "loot", "full" };
3544 * Convert the given string of object classes to a string of default object
3545 * symbols.
3547 STATIC_OVL void
3548 oc_to_str(src, dest)
3549 char *src, *dest;
3551 int i;
3553 while ((i = (int) *src++) != 0) {
3554 if (i < 0 || i >= MAXOCLASSES)
3555 impossible("oc_to_str: illegal object class %d", i);
3556 else
3557 *dest++ = def_oc_syms[i].sym;
3559 *dest = '\0';
3563 * Add the given mapping to the menu command map list. Always keep the
3564 * maps valid C strings.
3566 void
3567 add_menu_cmd_alias(from_ch, to_ch)
3568 char from_ch, to_ch;
3570 if (n_menu_mapped >= MAX_MENU_MAPPED_CMDS) {
3571 pline("out of menu map space.");
3572 } else {
3573 mapped_menu_cmds[n_menu_mapped] = from_ch;
3574 mapped_menu_op[n_menu_mapped] = to_ch;
3575 n_menu_mapped++;
3576 mapped_menu_cmds[n_menu_mapped] = 0;
3577 mapped_menu_op[n_menu_mapped] = 0;
3581 char
3582 get_menu_cmd_key(ch)
3583 char ch;
3585 char *found = index(mapped_menu_op, ch);
3586 if (found) {
3587 int idx = (int) (found - mapped_menu_op);
3588 ch = mapped_menu_cmds[idx];
3590 return ch;
3594 * Map the given character to its corresponding menu command. If it
3595 * doesn't match anything, just return the original.
3597 char
3598 map_menu_cmd(ch)
3599 char ch;
3601 char *found = index(mapped_menu_cmds, ch);
3602 if (found) {
3603 int idx = (int) (found - mapped_menu_cmds);
3604 ch = mapped_menu_op[idx];
3606 return ch;
3609 void
3610 show_menu_controls(win, dolist)
3611 winid win;
3612 boolean dolist;
3614 char buf[BUFSZ];
3616 putstr(win, 0, "Menu control keys:");
3617 if (dolist) {
3618 int i;
3619 for (i = 0; i < SIZE(default_menu_cmd_info); i++) {
3620 Sprintf(buf, "%-8s %s",
3621 visctrl(get_menu_cmd_key(default_menu_cmd_info[i].cmd)),
3622 default_menu_cmd_info[i].desc);
3623 putstr(win, 0, buf);
3625 } else {
3626 putstr(win, 0, "");
3627 putstr(win, 0, " Page All items");
3628 Sprintf(buf, " Select %s %s",
3629 visctrl(get_menu_cmd_key(MENU_SELECT_PAGE)),
3630 visctrl(get_menu_cmd_key(MENU_SELECT_ALL)));
3631 putstr(win, 0, buf);
3632 Sprintf(buf, "Deselect %s %s",
3633 visctrl(get_menu_cmd_key(MENU_UNSELECT_PAGE)),
3634 visctrl(get_menu_cmd_key(MENU_UNSELECT_ALL)));
3635 putstr(win, 0, buf);
3636 Sprintf(buf, " Invert %s %s",
3637 visctrl(get_menu_cmd_key(MENU_INVERT_PAGE)),
3638 visctrl(get_menu_cmd_key(MENU_INVERT_ALL)));
3639 putstr(win, 0, buf);
3640 putstr(win, 0, "");
3641 Sprintf(buf, " Go to %s Next page",
3642 visctrl(get_menu_cmd_key(MENU_NEXT_PAGE)));
3643 putstr(win, 0, buf);
3644 Sprintf(buf, " %s Previous page",
3645 visctrl(get_menu_cmd_key(MENU_PREVIOUS_PAGE)));
3646 putstr(win, 0, buf);
3647 Sprintf(buf, " %s First page",
3648 visctrl(get_menu_cmd_key(MENU_FIRST_PAGE)));
3649 putstr(win, 0, buf);
3650 Sprintf(buf, " %s Last page",
3651 visctrl(get_menu_cmd_key(MENU_LAST_PAGE)));
3652 putstr(win, 0, buf);
3653 putstr(win, 0, "");
3654 Sprintf(buf, " %s Search and toggle matching entries",
3655 visctrl(get_menu_cmd_key(MENU_SEARCH)));
3656 putstr(win, 0, buf);
3660 #if defined(MICRO) || defined(MAC) || defined(WIN32)
3661 #define OPTIONS_HEADING "OPTIONS"
3662 #else
3663 #define OPTIONS_HEADING "NETHACKOPTIONS"
3664 #endif
3666 static char fmtstr_doset[] = "%s%-15s [%s] ";
3667 static char fmtstr_doset_tab[] = "%s\t[%s]";
3668 static char n_currently_set[] = "(%d currently set)";
3670 /* doset('O' command) menu entries for compound options */
3671 STATIC_OVL void
3672 doset_add_menu(win, option, indexoffset)
3673 winid win; /* window to add to */
3674 const char *option; /* option name */
3675 int indexoffset; /* value to add to index in compopt[], or zero
3676 if option cannot be changed */
3678 const char *value = "unknown"; /* current value */
3679 char buf[BUFSZ], buf2[BUFSZ];
3680 anything any;
3681 int i;
3683 any = zeroany;
3684 if (indexoffset == 0) {
3685 any.a_int = 0;
3686 value = get_compopt_value(option, buf2);
3687 } else {
3688 for (i = 0; compopt[i].name; i++)
3689 if (strcmp(option, compopt[i].name) == 0)
3690 break;
3692 if (compopt[i].name) {
3693 any.a_int = i + 1 + indexoffset;
3694 value = get_compopt_value(option, buf2);
3695 } else {
3696 /* We are trying to add an option not found in compopt[].
3697 This is almost certainly bad, but we'll let it through anyway
3698 (with a zero value, so it can't be selected). */
3699 any.a_int = 0;
3702 /* " " replaces "a - " -- assumes menus follow that style */
3703 if (!iflags.menu_tab_sep)
3704 Sprintf(buf, fmtstr_doset, any.a_int ? "" : " ", option,
3705 value);
3706 else
3707 Sprintf(buf, fmtstr_doset_tab, option, value);
3708 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
3711 STATIC_OVL void
3712 opts_add_others(win, name, id, bufx, nset)
3713 winid win;
3714 const char *name;
3715 int id;
3716 char *bufx;
3717 int nset;
3719 char buf[BUFSZ], buf2[BUFSZ];
3720 anything any = zeroany;
3722 any.a_int = id;
3723 if (!bufx)
3724 Sprintf(buf2, n_currently_set, nset);
3725 else
3726 Sprintf(buf2, "%s", bufx);
3727 if (!iflags.menu_tab_sep)
3728 Sprintf(buf, fmtstr_doset, any.a_int ? "" : " ",
3729 name, buf2);
3730 else
3731 Sprintf(buf, fmtstr_doset_tab, name, buf2);
3732 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
3735 enum opt_other_enums {
3736 OPT_OTHER_MSGTYPE = -4,
3737 OPT_OTHER_MENUCOLOR = -3,
3738 OPT_OTHER_STATHILITE = -2,
3739 OPT_OTHER_APEXC = -1
3740 /* these must be < 0 */
3743 /* presently only used when determining longest option name */
3744 static struct other_opts {
3745 const char *name;
3746 int optflags;
3747 enum opt_other_enums code;
3748 } othropt[] = {
3749 { "autopickup exceptions", SET_IN_GAME, OPT_OTHER_APEXC },
3750 { "menucolors", SET_IN_GAME, OPT_OTHER_MENUCOLOR },
3751 { "message types", SET_IN_GAME, OPT_OTHER_MSGTYPE },
3752 #ifdef STATUS_VIA_WINDOWPORT
3753 #ifdef STATUS_HILITES
3754 { "status_hilites", SET_IN_GAME, OPT_OTHER_STATHILITE },
3755 #endif
3756 #endif
3757 { (char *) 0, 0, (enum opt_other_enums) 0 },
3760 /* the 'O' command */
3762 doset() /* changing options via menu by Per Liboriussen */
3764 static boolean made_fmtstr = FALSE;
3765 char buf[BUFSZ], buf2[BUFSZ];
3766 const char *name;
3767 int i = 0, pass, boolcount, pick_cnt, pick_idx, opt_indx;
3768 boolean *bool_p;
3769 winid tmpwin;
3770 anything any;
3771 menu_item *pick_list;
3772 int indexoffset, startpass, endpass, optflags;
3773 boolean setinitial = FALSE, fromfile = FALSE;
3774 unsigned longest_name_len;
3776 tmpwin = create_nhwindow(NHW_MENU);
3777 start_menu(tmpwin);
3779 #ifdef notyet /* SYSCF */
3780 /* XXX I think this is still fragile. Fixing initial/from_file and/or
3781 changing the SET_* etc to bitmaps will let me make this better. */
3782 if (wizard)
3783 startpass = SET_IN_SYS;
3784 else
3785 #endif
3786 startpass = DISP_IN_GAME;
3787 endpass = (wizard) ? SET_IN_WIZGAME : SET_IN_GAME;
3789 if (!made_fmtstr && !iflags.menu_tab_sep) {
3790 /* spin through the options to find the longest name
3791 and adjust the format string accordingly */
3792 longest_name_len = 0;
3793 for (pass = 0; pass <= 2; pass++)
3794 for (i = 0; (name = ((pass == 0)
3795 ? boolopt[i].name
3796 : (pass == 1)
3797 ? compopt[i].name
3798 : othropt[i].name)) != 0; i++) {
3799 if (pass == 0 && !boolopt[i].addr)
3800 continue;
3801 optflags = (pass == 0) ? boolopt[i].optflags
3802 : (pass == 1)
3803 ? compopt[i].optflags
3804 : othropt[i].optflags;
3805 if (optflags < startpass || optflags > endpass)
3806 continue;
3807 if ((is_wc_option(name) && !wc_supported(name))
3808 || (is_wc2_option(name) && !wc2_supported(name)))
3809 continue;
3811 if (strlen(name) > longest_name_len)
3812 longest_name_len = strlen(name);
3814 Sprintf(fmtstr_doset, "%%s%%-%us [%%s]", longest_name_len);
3815 made_fmtstr = TRUE;
3818 any = zeroany;
3819 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
3820 "Booleans (selecting will toggle value):", MENU_UNSELECTED);
3821 any.a_int = 0;
3822 /* first list any other non-modifiable booleans, then modifiable ones */
3823 for (pass = 0; pass <= 1; pass++)
3824 for (i = 0; (name = boolopt[i].name) != 0; i++)
3825 if ((bool_p = boolopt[i].addr) != 0
3826 && ((boolopt[i].optflags <= DISP_IN_GAME && pass == 0)
3827 || (boolopt[i].optflags >= SET_IN_GAME && pass == 1))) {
3828 if (bool_p == &flags.female)
3829 continue; /* obsolete */
3830 if (boolopt[i].optflags == SET_IN_WIZGAME && !wizard)
3831 continue;
3832 if ((is_wc_option(name) && !wc_supported(name))
3833 || (is_wc2_option(name) && !wc2_supported(name)))
3834 continue;
3836 any.a_int = (pass == 0) ? 0 : i + 1;
3837 if (!iflags.menu_tab_sep)
3838 Sprintf(buf, fmtstr_doset, (pass == 0) ? " " : "",
3839 name, *bool_p ? "true" : "false");
3840 else
3841 Sprintf(buf, fmtstr_doset_tab,
3842 name, *bool_p ? "true" : "false");
3843 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf,
3844 MENU_UNSELECTED);
3847 boolcount = i;
3848 indexoffset = boolcount;
3849 any = zeroany;
3850 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
3851 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
3852 "Compounds (selecting will prompt for new value):",
3853 MENU_UNSELECTED);
3855 /* deliberately put playmode, name, role+race+gender+align first */
3856 doset_add_menu(tmpwin, "playmode", 0);
3857 doset_add_menu(tmpwin, "name", 0);
3858 doset_add_menu(tmpwin, "role", 0);
3859 doset_add_menu(tmpwin, "race", 0);
3860 doset_add_menu(tmpwin, "gender", 0);
3861 doset_add_menu(tmpwin, "align", 0);
3863 for (pass = startpass; pass <= endpass; pass++)
3864 for (i = 0; (name = compopt[i].name) != 0; i++)
3865 if (compopt[i].optflags == pass) {
3866 if (!strcmp(name, "playmode") || !strcmp(name, "name")
3867 || !strcmp(name, "role") || !strcmp(name, "race")
3868 || !strcmp(name, "gender") || !strcmp(name, "align"))
3869 continue;
3870 if ((is_wc_option(name) && !wc_supported(name))
3871 || (is_wc2_option(name) && !wc2_supported(name)))
3872 continue;
3874 doset_add_menu(tmpwin, name,
3875 (pass == DISP_IN_GAME) ? 0 : indexoffset);
3878 any = zeroany;
3879 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
3880 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
3881 "Other settings:",
3882 MENU_UNSELECTED);
3884 opts_add_others(tmpwin, "autopickup exceptions", OPT_OTHER_APEXC,
3885 NULL, count_ape_maps((int *) 0, (int *) 0));
3886 opts_add_others(tmpwin, "menucolors", OPT_OTHER_MENUCOLOR,
3887 NULL, count_menucolors());
3888 opts_add_others(tmpwin, "message types", OPT_OTHER_MSGTYPE,
3889 NULL, msgtype_count());
3890 #ifdef STATUS_VIA_WINDOWPORT
3891 #ifdef STATUS_HILITES
3892 get_status_hilites(buf2, 60);
3893 if (!*buf2)
3894 Sprintf(buf2, "%s", "(none)");
3895 opts_add_others(tmpwin, "status_hilites", OPT_OTHER_STATHILITE, buf2, 0);
3896 #endif
3897 #endif
3898 #ifdef PREFIXES_IN_USE
3899 any = zeroany;
3900 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
3901 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
3902 "Variable playground locations:", MENU_UNSELECTED);
3903 for (i = 0; i < PREFIX_COUNT; i++)
3904 doset_add_menu(tmpwin, fqn_prefix_names[i], 0);
3905 #endif
3906 end_menu(tmpwin, "Set what options?");
3907 need_redraw = FALSE;
3908 if ((pick_cnt = select_menu(tmpwin, PICK_ANY, &pick_list)) > 0) {
3910 * Walk down the selection list and either invert the booleans
3911 * or prompt for new values. In most cases, call parseoptions()
3912 * to take care of options that require special attention, like
3913 * redraws.
3915 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
3916 opt_indx = pick_list[pick_idx].item.a_int - 1;
3917 if (opt_indx < -1) opt_indx++; /* -1 offset for select_menu() */
3918 if (opt_indx == OPT_OTHER_APEXC) {
3919 (void) special_handling("autopickup_exception", setinitial,
3920 fromfile);
3921 #ifdef STATUS_VIA_WINDOWPORT
3922 #ifdef STATUS_HILITES
3923 } else if (opt_indx == OPT_OTHER_STATHILITE) {
3924 if (!status_hilite_menu()) {
3925 pline("Bad status hilite(s) specified.");
3926 } else {
3927 if (wc2_supported("status_hilites"))
3928 preference_update("status_hilites");
3930 #endif
3931 #endif
3932 } else if (opt_indx == OPT_OTHER_MENUCOLOR) {
3933 (void) special_handling("menucolors", setinitial,
3934 fromfile);
3935 } else if (opt_indx == OPT_OTHER_MSGTYPE) {
3936 (void) special_handling("msgtype", setinitial, fromfile);
3937 } else if (opt_indx < boolcount) {
3938 /* boolean option */
3939 Sprintf(buf, "%s%s", *boolopt[opt_indx].addr ? "!" : "",
3940 boolopt[opt_indx].name);
3941 parseoptions(buf, setinitial, fromfile);
3942 if (wc_supported(boolopt[opt_indx].name)
3943 || wc2_supported(boolopt[opt_indx].name))
3944 preference_update(boolopt[opt_indx].name);
3945 } else {
3946 /* compound option */
3947 opt_indx -= boolcount;
3949 if (!special_handling(compopt[opt_indx].name, setinitial,
3950 fromfile)) {
3951 Sprintf(buf, "Set %s to what?", compopt[opt_indx].name);
3952 getlin(buf, buf2);
3953 if (buf2[0] == '\033')
3954 continue;
3955 Sprintf(buf, "%s:%s", compopt[opt_indx].name, buf2);
3956 /* pass the buck */
3957 parseoptions(buf, setinitial, fromfile);
3959 if (wc_supported(compopt[opt_indx].name)
3960 || wc2_supported(compopt[opt_indx].name))
3961 preference_update(compopt[opt_indx].name);
3964 free((genericptr_t) pick_list);
3965 pick_list = (menu_item *) 0;
3968 destroy_nhwindow(tmpwin);
3969 if (need_redraw) {
3970 reglyph_darkroom();
3971 (void) doredraw();
3973 return 0;
3976 STATIC_OVL int
3977 handle_add_list_remove(optname, numtotal)
3978 const char *optname;
3979 int numtotal;
3981 winid tmpwin;
3982 anything any;
3983 int i, pick_cnt, opt_idx;
3984 menu_item *pick_list = (menu_item *) 0;
3985 static const struct action {
3986 char letr;
3987 const char *desc;
3988 } action_titles[] = {
3989 { 'a', "add new %s" }, /* [0] */
3990 { 'l', "list %s" }, /* [1] */
3991 { 'r', "remove existing %s" }, /* [2] */
3992 { 'x', "exit this menu" }, /* [3] */
3995 opt_idx = 0;
3996 tmpwin = create_nhwindow(NHW_MENU);
3997 start_menu(tmpwin);
3998 any = zeroany;
3999 for (i = 0; i < SIZE(action_titles); i++) {
4000 char tmpbuf[BUFSZ];
4002 any.a_int++;
4003 /* omit list and remove if there aren't any yet */
4004 if (!numtotal && (i == 1 || i == 2))
4005 continue;
4006 Sprintf(tmpbuf, action_titles[i].desc,
4007 (i == 1) ? makeplural(optname) : optname);
4008 add_menu(tmpwin, NO_GLYPH, &any, action_titles[i].letr, 0, ATR_NONE,
4009 tmpbuf, (i == 3) ? MENU_SELECTED : MENU_UNSELECTED);
4011 end_menu(tmpwin, "Do what?");
4012 if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &pick_list)) > 0) {
4013 opt_idx = pick_list[0].item.a_int - 1;
4014 if (pick_cnt > 1 && opt_idx == 3)
4015 opt_idx = pick_list[1].item.a_int - 1;
4016 free((genericptr_t) pick_list);
4017 } else
4018 opt_idx = 3; /* none selected, exit menu */
4019 destroy_nhwindow(tmpwin);
4020 return opt_idx;
4023 struct symsetentry *symset_list = 0; /* files.c will populate this with
4024 list of available sets */
4026 STATIC_OVL boolean
4027 special_handling(optname, setinitial, setfromfile)
4028 const char *optname;
4029 boolean setinitial, setfromfile;
4031 winid tmpwin;
4032 anything any;
4033 int i, n;
4034 char buf[BUFSZ];
4036 /* Special handling of menustyle, pickup_burden, pickup_types,
4037 * disclose, runmode, msg_window, menu_headings, sortloot,
4038 * and number_pad options.
4039 * Also takes care of interactive autopickup_exception_handling changes.
4041 if (!strcmp("menustyle", optname)) {
4042 const char *style_name;
4043 menu_item *style_pick = (menu_item *) 0;
4044 tmpwin = create_nhwindow(NHW_MENU);
4045 start_menu(tmpwin);
4046 any = zeroany;
4047 for (i = 0; i < SIZE(menutype); i++) {
4048 style_name = menutype[i];
4049 /* note: separate `style_name' variable used
4050 to avoid an optimizer bug in VAX C V2.3 */
4051 any.a_int = i + 1;
4052 add_menu(tmpwin, NO_GLYPH, &any, *style_name, 0, ATR_NONE,
4053 style_name, MENU_UNSELECTED);
4055 end_menu(tmpwin, "Select menustyle:");
4056 if (select_menu(tmpwin, PICK_ONE, &style_pick) > 0) {
4057 flags.menu_style = style_pick->item.a_int - 1;
4058 free((genericptr_t) style_pick);
4060 destroy_nhwindow(tmpwin);
4061 } else if (!strcmp("paranoid_confirmation", optname)) {
4062 menu_item *paranoia_picks = (menu_item *) 0;
4064 tmpwin = create_nhwindow(NHW_MENU);
4065 start_menu(tmpwin);
4066 any = zeroany;
4067 for (i = 0; paranoia[i].flagmask != 0; ++i) {
4068 if (paranoia[i].flagmask == PARANOID_BONES && !wizard)
4069 continue;
4070 any.a_int = paranoia[i].flagmask;
4071 add_menu(tmpwin, NO_GLYPH, &any, *paranoia[i].argname, 0,
4072 ATR_NONE, paranoia[i].explain,
4073 (flags.paranoia_bits & paranoia[i].flagmask)
4074 ? MENU_SELECTED
4075 : MENU_UNSELECTED);
4077 end_menu(tmpwin, "Actions requiring extra confirmation:");
4078 i = select_menu(tmpwin, PICK_ANY, &paranoia_picks);
4079 if (i >= 0) {
4080 /* player didn't cancel; we reset all the paranoia options
4081 here even if there were no items picked, since user
4082 could have toggled off preselected ones to end up with 0 */
4083 flags.paranoia_bits = 0;
4084 if (i > 0) {
4085 /* at least 1 item set, either preselected or newly picked */
4086 while (--i >= 0)
4087 flags.paranoia_bits |= paranoia_picks[i].item.a_int;
4088 free((genericptr_t) paranoia_picks);
4091 destroy_nhwindow(tmpwin);
4092 } else if (!strcmp("pickup_burden", optname)) {
4093 const char *burden_name, *burden_letters = "ubsntl";
4094 menu_item *burden_pick = (menu_item *) 0;
4096 tmpwin = create_nhwindow(NHW_MENU);
4097 start_menu(tmpwin);
4098 any = zeroany;
4099 for (i = 0; i < SIZE(burdentype); i++) {
4100 burden_name = burdentype[i];
4101 any.a_int = i + 1;
4102 add_menu(tmpwin, NO_GLYPH, &any, burden_letters[i], 0, ATR_NONE,
4103 burden_name, MENU_UNSELECTED);
4105 end_menu(tmpwin, "Select encumbrance level:");
4106 if (select_menu(tmpwin, PICK_ONE, &burden_pick) > 0) {
4107 flags.pickup_burden = burden_pick->item.a_int - 1;
4108 free((genericptr_t) burden_pick);
4110 destroy_nhwindow(tmpwin);
4111 } else if (!strcmp("pickup_types", optname)) {
4112 /* parseoptions will prompt for the list of types */
4113 parseoptions(strcpy(buf, "pickup_types"), setinitial, setfromfile);
4114 } else if (!strcmp("disclose", optname)) {
4115 /* order of disclose_names[] must correspond to
4116 disclosure_options in decl.c */
4117 static const char *disclosure_names[] = {
4118 "inventory", "attributes", "vanquished",
4119 "genocides", "conduct", "overview",
4121 int disc_cat[NUM_DISCLOSURE_OPTIONS];
4122 int pick_cnt, pick_idx, opt_idx;
4123 char c;
4124 menu_item *disclosure_pick = (menu_item *) 0;
4126 tmpwin = create_nhwindow(NHW_MENU);
4127 start_menu(tmpwin);
4128 any = zeroany;
4129 for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
4130 Sprintf(buf, "%-12s[%c%c]", disclosure_names[i],
4131 flags.end_disclose[i], disclosure_options[i]);
4132 any.a_int = i + 1;
4133 add_menu(tmpwin, NO_GLYPH, &any, disclosure_options[i], 0,
4134 ATR_NONE, buf, MENU_UNSELECTED);
4135 disc_cat[i] = 0;
4137 end_menu(tmpwin, "Change which disclosure options categories:");
4138 pick_cnt = select_menu(tmpwin, PICK_ANY, &disclosure_pick);
4139 if (pick_cnt > 0) {
4140 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
4141 opt_idx = disclosure_pick[pick_idx].item.a_int - 1;
4142 disc_cat[opt_idx] = 1;
4144 free((genericptr_t) disclosure_pick);
4145 disclosure_pick = (menu_item *) 0;
4147 destroy_nhwindow(tmpwin);
4149 for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
4150 if (disc_cat[i]) {
4151 c = flags.end_disclose[i];
4152 Sprintf(buf, "Disclosure options for %s:",
4153 disclosure_names[i]);
4154 tmpwin = create_nhwindow(NHW_MENU);
4155 start_menu(tmpwin);
4156 any = zeroany;
4157 /* 'y','n',and '+' work as alternate selectors; '-' doesn't */
4158 any.a_char = DISCLOSE_NO_WITHOUT_PROMPT;
4159 add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
4160 "Never disclose, without prompting",
4161 (c == any.a_char) ? MENU_SELECTED : MENU_UNSELECTED);
4162 any.a_char = DISCLOSE_YES_WITHOUT_PROMPT;
4163 add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
4164 "Always disclose, without prompting",
4165 (c == any.a_char) ? MENU_SELECTED : MENU_UNSELECTED);
4166 if (*disclosure_names[i] == 'v') {
4167 any.a_char = DISCLOSE_SPECIAL_WITHOUT_PROMPT; /* '#' */
4168 add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
4169 "Always disclose, pick sort order from menu",
4170 (c == any.a_char) ? MENU_SELECTED
4171 : MENU_UNSELECTED);
4173 any.a_char = DISCLOSE_PROMPT_DEFAULT_NO;
4174 add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
4175 "Prompt, with default answer of \"No\"",
4176 (c == any.a_char) ? MENU_SELECTED : MENU_UNSELECTED);
4177 any.a_char = DISCLOSE_PROMPT_DEFAULT_YES;
4178 add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
4179 "Prompt, with default answer of \"Yes\"",
4180 (c == any.a_char) ? MENU_SELECTED : MENU_UNSELECTED);
4181 if (*disclosure_names[i] == 'v') {
4182 any.a_char = DISCLOSE_PROMPT_DEFAULT_SPECIAL; /* '?' */
4183 add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
4184 "Prompt, with default answer of \"Ask\" to request sort menu",
4185 (c == any.a_char) ? MENU_SELECTED
4186 : MENU_UNSELECTED);
4188 end_menu(tmpwin, buf);
4189 n = select_menu(tmpwin, PICK_ONE, &disclosure_pick);
4190 if (n > 0) {
4191 flags.end_disclose[i] = disclosure_pick[0].item.a_char;
4192 if (n > 1 && flags.end_disclose[i] == c)
4193 flags.end_disclose[i] = disclosure_pick[1].item.a_char;
4194 free((genericptr_t) disclosure_pick);
4196 destroy_nhwindow(tmpwin);
4199 } else if (!strcmp("runmode", optname)) {
4200 const char *mode_name;
4201 menu_item *mode_pick = (menu_item *) 0;
4203 tmpwin = create_nhwindow(NHW_MENU);
4204 start_menu(tmpwin);
4205 any = zeroany;
4206 for (i = 0; i < SIZE(runmodes); i++) {
4207 mode_name = runmodes[i];
4208 any.a_int = i + 1;
4209 add_menu(tmpwin, NO_GLYPH, &any, *mode_name, 0, ATR_NONE,
4210 mode_name, MENU_UNSELECTED);
4212 end_menu(tmpwin, "Select run/travel display mode:");
4213 if (select_menu(tmpwin, PICK_ONE, &mode_pick) > 0) {
4214 flags.runmode = mode_pick->item.a_int - 1;
4215 free((genericptr_t) mode_pick);
4217 destroy_nhwindow(tmpwin);
4218 } else if (!strcmp("whatis_coord", optname)) {
4219 menu_item *window_pick = (menu_item *) 0;
4220 int pick_cnt;
4221 char gp = iflags.getpos_coords;
4223 tmpwin = create_nhwindow(NHW_MENU);
4224 start_menu(tmpwin);
4225 any = zeroany;
4226 any.a_char = GPCOORDS_COMPASS;
4227 add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_COMPASS,
4228 0, ATR_NONE, "compass ('east' or '3s' or '2n,4w')",
4229 (gp == GPCOORDS_COMPASS) ? MENU_SELECTED : MENU_UNSELECTED);
4230 any.a_char = GPCOORDS_COMFULL;
4231 add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_COMFULL,
4232 0, ATR_NONE, "full compass ('east' or '3south' or '2north,4west')",
4233 (gp == GPCOORDS_COMFULL) ? MENU_SELECTED : MENU_UNSELECTED);
4234 any.a_char = GPCOORDS_MAP;
4235 add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_MAP,
4236 0, ATR_NONE, "map <x,y>",
4237 (gp == GPCOORDS_MAP) ? MENU_SELECTED : MENU_UNSELECTED);
4238 any.a_char = GPCOORDS_SCREEN;
4239 add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_SCREEN,
4240 0, ATR_NONE, "screen [row,column]",
4241 (gp == GPCOORDS_SCREEN) ? MENU_SELECTED : MENU_UNSELECTED);
4242 any.a_char = GPCOORDS_NONE;
4243 add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_NONE,
4244 0, ATR_NONE, "none (no coordinates displayed)",
4245 (gp == GPCOORDS_NONE) ? MENU_SELECTED : MENU_UNSELECTED);
4246 any.a_long = 0L;
4247 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
4248 Sprintf(buf, "map: upper-left: <%d,%d>, lower-right: <%d,%d>%s",
4249 1, 0, COLNO - 1, ROWNO - 1,
4250 flags.verbose ? "; column 0 unused, off left edge" : "");
4251 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
4252 if (strcmp(windowprocs.name, "tty"))
4253 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
4254 "screen: row is offset to accommodate tty interface's use of top line",
4255 MENU_UNSELECTED);
4256 #if COLNO == 80
4257 #define COL80ARG flags.verbose ? "; column 80 is not used" : ""
4258 #else
4259 #define COL80ARG ""
4260 #endif
4261 Sprintf(buf, "screen: upper-left: [%02d,%02d], lower-right: [%d,%d]%s",
4262 0 + 2, 1, ROWNO - 1 + 2, COLNO - 1, COL80ARG);
4263 #undef COL80ARG
4264 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
4265 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
4266 end_menu(tmpwin,
4267 "Select coordinate display when auto-describing a map position:");
4268 if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &window_pick)) > 0) {
4269 iflags.getpos_coords = window_pick[0].item.a_char;
4270 /* PICK_ONE doesn't unselect preselected entry when
4271 selecting another one */
4272 if (pick_cnt > 1 && iflags.getpos_coords == gp)
4273 iflags.getpos_coords = window_pick[1].item.a_char;
4274 free((genericptr_t) window_pick);
4276 destroy_nhwindow(tmpwin);
4277 } else if (!strcmp("msg_window", optname)) {
4278 #ifdef TTY_GRAPHICS
4279 /* by Christian W. Cooper */
4280 menu_item *window_pick = (menu_item *) 0;
4282 tmpwin = create_nhwindow(NHW_MENU);
4283 start_menu(tmpwin);
4284 any = zeroany;
4285 any.a_char = 's';
4286 add_menu(tmpwin, NO_GLYPH, &any, 's', 0, ATR_NONE, "single",
4287 MENU_UNSELECTED);
4288 any.a_char = 'c';
4289 add_menu(tmpwin, NO_GLYPH, &any, 'c', 0, ATR_NONE, "combination",
4290 MENU_UNSELECTED);
4291 any.a_char = 'f';
4292 add_menu(tmpwin, NO_GLYPH, &any, 'f', 0, ATR_NONE, "full",
4293 MENU_UNSELECTED);
4294 any.a_char = 'r';
4295 add_menu(tmpwin, NO_GLYPH, &any, 'r', 0, ATR_NONE, "reversed",
4296 MENU_UNSELECTED);
4297 end_menu(tmpwin, "Select message history display type:");
4298 if (select_menu(tmpwin, PICK_ONE, &window_pick) > 0) {
4299 iflags.prevmsg_window = window_pick->item.a_char;
4300 free((genericptr_t) window_pick);
4302 destroy_nhwindow(tmpwin);
4303 #endif
4304 } else if (!strcmp("sortloot", optname)) {
4305 const char *sortl_name;
4306 menu_item *sortl_pick = (menu_item *) 0;
4308 tmpwin = create_nhwindow(NHW_MENU);
4309 start_menu(tmpwin);
4310 any = zeroany;
4311 for (i = 0; i < SIZE(sortltype); i++) {
4312 sortl_name = sortltype[i];
4313 any.a_char = *sortl_name;
4314 add_menu(tmpwin, NO_GLYPH, &any, *sortl_name, 0, ATR_NONE,
4315 sortl_name, (flags.sortloot == *sortl_name)
4316 ? MENU_SELECTED : MENU_UNSELECTED);
4318 end_menu(tmpwin, "Select loot sorting type:");
4319 n = select_menu(tmpwin, PICK_ONE, &sortl_pick);
4320 if (n > 0) {
4321 char c = sortl_pick[0].item.a_char;
4323 if (n > 1 && c == flags.sortloot)
4324 c = sortl_pick[1].item.a_char;
4325 flags.sortloot = c;
4326 free((genericptr_t) sortl_pick);
4328 destroy_nhwindow(tmpwin);
4329 } else if (!strcmp("align_message", optname)
4330 || !strcmp("align_status", optname)) {
4331 menu_item *window_pick = (menu_item *) 0;
4332 char abuf[BUFSZ];
4333 boolean msg = (*(optname + 6) == 'm');
4335 tmpwin = create_nhwindow(NHW_MENU);
4336 start_menu(tmpwin);
4337 any = zeroany;
4338 any.a_int = ALIGN_TOP;
4339 add_menu(tmpwin, NO_GLYPH, &any, 't', 0, ATR_NONE, "top",
4340 MENU_UNSELECTED);
4341 any.a_int = ALIGN_BOTTOM;
4342 add_menu(tmpwin, NO_GLYPH, &any, 'b', 0, ATR_NONE, "bottom",
4343 MENU_UNSELECTED);
4344 any.a_int = ALIGN_LEFT;
4345 add_menu(tmpwin, NO_GLYPH, &any, 'l', 0, ATR_NONE, "left",
4346 MENU_UNSELECTED);
4347 any.a_int = ALIGN_RIGHT;
4348 add_menu(tmpwin, NO_GLYPH, &any, 'r', 0, ATR_NONE, "right",
4349 MENU_UNSELECTED);
4350 Sprintf(abuf, "Select %s window placement relative to the map:",
4351 msg ? "message" : "status");
4352 end_menu(tmpwin, abuf);
4353 if (select_menu(tmpwin, PICK_ONE, &window_pick) > 0) {
4354 if (msg)
4355 iflags.wc_align_message = window_pick->item.a_int;
4356 else
4357 iflags.wc_align_status = window_pick->item.a_int;
4358 free((genericptr_t) window_pick);
4360 destroy_nhwindow(tmpwin);
4361 } else if (!strcmp("number_pad", optname)) {
4362 static const char *npchoices[] = {
4363 " 0 (off)", " 1 (on)", " 2 (on, MSDOS compatible)",
4364 " 3 (on, phone-style digit layout)",
4365 " 4 (on, phone-style layout, MSDOS compatible)",
4366 "-1 (off, 'z' to move upper-left, 'y' to zap wands)"
4368 menu_item *mode_pick = (menu_item *) 0;
4370 tmpwin = create_nhwindow(NHW_MENU);
4371 start_menu(tmpwin);
4372 any = zeroany;
4373 for (i = 0; i < SIZE(npchoices); i++) {
4374 any.a_int = i + 1;
4375 add_menu(tmpwin, NO_GLYPH, &any, 'a' + i, 0, ATR_NONE,
4376 npchoices[i], MENU_UNSELECTED);
4378 end_menu(tmpwin, "Select number_pad mode:");
4379 if (select_menu(tmpwin, PICK_ONE, &mode_pick) > 0) {
4380 switch (mode_pick->item.a_int - 1) {
4381 case 0:
4382 iflags.num_pad = FALSE;
4383 iflags.num_pad_mode = 0;
4384 break;
4385 case 1:
4386 iflags.num_pad = TRUE;
4387 iflags.num_pad_mode = 0;
4388 break;
4389 case 2:
4390 iflags.num_pad = TRUE;
4391 iflags.num_pad_mode = 1;
4392 break;
4393 case 3:
4394 iflags.num_pad = TRUE;
4395 iflags.num_pad_mode = 2;
4396 break;
4397 case 4:
4398 iflags.num_pad = TRUE;
4399 iflags.num_pad_mode = 3;
4400 break;
4401 /* last menu choice: number_pad == -1 */
4402 case 5:
4403 iflags.num_pad = FALSE;
4404 iflags.num_pad_mode = 1;
4405 break;
4407 reset_commands(FALSE);
4408 number_pad(iflags.num_pad ? 1 : 0);
4409 free((genericptr_t) mode_pick);
4411 destroy_nhwindow(tmpwin);
4412 } else if (!strcmp("menu_headings", optname)) {
4413 int mhattr = query_attr("How to highlight menu headings:");
4415 if (mhattr != -1)
4416 iflags.menu_headings = mhattr;
4417 } else if (!strcmp("msgtype", optname)) {
4418 int opt_idx, nmt, mttyp;
4419 char mtbuf[BUFSZ];
4421 msgtypes_again:
4422 nmt = msgtype_count();
4423 opt_idx = handle_add_list_remove("message type", nmt);
4424 if (opt_idx == 3) { /* done */
4425 return TRUE;
4426 } else if (opt_idx == 0) { /* add new */
4427 getlin("What new message pattern?", mtbuf);
4428 if (*mtbuf == '\033')
4429 return TRUE;
4430 if (*mtbuf
4431 && (mttyp = query_msgtype()) != -1
4432 && !msgtype_add(mttyp, mtbuf)) {
4433 pline("Error adding the message type.");
4434 wait_synch();
4436 goto msgtypes_again;
4437 } else { /* list (1) or remove (2) */
4438 int pick_idx, pick_cnt;
4439 int mt_idx;
4440 unsigned ln;
4441 const char *mtype;
4442 menu_item *pick_list = (menu_item *) 0;
4443 struct plinemsg_type *tmp = plinemsg_types;
4445 tmpwin = create_nhwindow(NHW_MENU);
4446 start_menu(tmpwin);
4447 any = zeroany;
4448 mt_idx = 0;
4449 while (tmp) {
4450 mtype = msgtype2name(tmp->msgtype);
4451 any.a_int = ++mt_idx;
4452 Sprintf(mtbuf, "%-5s \"", mtype);
4453 ln = sizeof mtbuf - strlen(mtbuf) - sizeof "\"";
4454 if (strlen(tmp->pattern) > ln)
4455 Strcat(strncat(mtbuf, tmp->pattern, ln - 3), "...\"");
4456 else
4457 Strcat(mtbuf, "\"");
4458 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, mtbuf,
4459 MENU_UNSELECTED);
4460 tmp = tmp->next;
4462 Sprintf(mtbuf, "%s message types",
4463 (opt_idx == 1) ? "List of" : "Remove which");
4464 end_menu(tmpwin, mtbuf);
4465 pick_cnt = select_menu(tmpwin,
4466 (opt_idx == 1) ? PICK_NONE : PICK_ANY,
4467 &pick_list);
4468 if (pick_cnt > 0) {
4469 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
4470 free_one_msgtype(pick_list[pick_idx].item.a_int - 1
4471 - pick_idx);
4472 free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
4474 destroy_nhwindow(tmpwin);
4475 if (pick_cnt >= 0)
4476 goto msgtypes_again;
4478 } else if (!strcmp("menucolors", optname)) {
4479 int opt_idx, nmc, mcclr, mcattr;
4480 char mcbuf[BUFSZ];
4482 menucolors_again:
4483 nmc = count_menucolors();
4484 opt_idx = handle_add_list_remove("menucolor", nmc);
4485 if (opt_idx == 3) { /* done */
4486 return TRUE;
4487 } else if (opt_idx == 0) { /* add new */
4488 getlin("What new menucolor pattern?", mcbuf);
4489 if (*mcbuf == '\033')
4490 return TRUE;
4491 if (*mcbuf
4492 && (mcclr = query_color()) != -1
4493 && (mcattr = query_attr((char *) 0)) != -1
4494 && !add_menu_coloring_parsed(mcbuf, mcclr, mcattr)) {
4495 pline("Error adding the menu color.");
4496 wait_synch();
4498 goto menucolors_again;
4499 } else { /* list (1) or remove (2) */
4500 int pick_idx, pick_cnt;
4501 int mc_idx;
4502 unsigned ln;
4503 const char *sattr, *sclr;
4504 menu_item *pick_list = (menu_item *) 0;
4505 struct menucoloring *tmp = menu_colorings;
4507 tmpwin = create_nhwindow(NHW_MENU);
4508 start_menu(tmpwin);
4509 any = zeroany;
4510 mc_idx = 0;
4511 while (tmp) {
4512 sattr = attr2attrname(tmp->attr);
4513 sclr = clr2colorname(tmp->color);
4514 any.a_int = ++mc_idx;
4515 /* construct suffix */
4516 Sprintf(buf, "\"\"=%s%s%s", sclr,
4517 (tmp->attr != ATR_NONE) ? " & " : "",
4518 (tmp->attr != ATR_NONE) ? sattr : "");
4519 /* now main string */
4520 ln = sizeof buf - strlen(buf) - 1; /* length available */
4521 Strcpy(mcbuf, "\"");
4522 if (strlen(tmp->origstr) > ln)
4523 Strcat(strncat(mcbuf, tmp->origstr, ln - 3), "...");
4524 else
4525 Strcat(mcbuf, tmp->origstr);
4526 /* combine main string and suffix */
4527 Strcat(mcbuf, &buf[1]); /* skip buf[]'s initial quote */
4528 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, mcbuf,
4529 MENU_UNSELECTED);
4530 tmp = tmp->next;
4532 Sprintf(mcbuf, "%s menu colors",
4533 (opt_idx == 1) ? "List of" : "Remove which");
4534 end_menu(tmpwin, mcbuf);
4535 pick_cnt = select_menu(tmpwin,
4536 (opt_idx == 1) ? PICK_NONE : PICK_ANY,
4537 &pick_list);
4538 if (pick_cnt > 0) {
4539 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
4540 free_one_menu_coloring(pick_list[pick_idx].item.a_int - 1
4541 - pick_idx);
4542 free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
4544 destroy_nhwindow(tmpwin);
4545 if (pick_cnt >= 0)
4546 goto menucolors_again;
4548 } else if (!strcmp("autopickup_exception", optname)) {
4549 int opt_idx, pass, totalapes = 0, numapes[2] = { 0, 0 };
4550 char apebuf[1 + BUFSZ]; /* so &apebuf[1] is BUFSZ long for getlin() */
4551 struct autopickup_exception *ape;
4553 ape_again:
4554 totalapes = count_ape_maps(&numapes[AP_LEAVE], &numapes[AP_GRAB]);
4555 opt_idx = handle_add_list_remove("autopickup exception", totalapes);
4556 if (opt_idx == 3) { /* done */
4557 return TRUE;
4558 } else if (opt_idx == 0) { /* add new */
4559 getlin("What new autopickup exception pattern?", &apebuf[1]);
4560 mungspaces(&apebuf[1]); /* regularize whitespace */
4561 if (apebuf[1] == '\033')
4562 return TRUE;
4563 if (apebuf[1]) {
4564 apebuf[0] = '\"';
4565 /* guarantee room for \" prefix and \"\0 suffix;
4566 -2 is good enough for apebuf[] but -3 makes
4567 sure the whole thing fits within normal BUFSZ */
4568 apebuf[sizeof apebuf - 3] = '\0';
4569 Strcat(apebuf, "\"");
4570 add_autopickup_exception(apebuf);
4572 goto ape_again;
4573 } else { /* list (1) or remove (2) */
4574 int pick_idx, pick_cnt;
4575 menu_item *pick_list = (menu_item *) 0;
4577 tmpwin = create_nhwindow(NHW_MENU);
4578 start_menu(tmpwin);
4579 for (pass = AP_LEAVE; pass <= AP_GRAB; ++pass) {
4580 if (numapes[pass] == 0)
4581 continue;
4582 ape = iflags.autopickup_exceptions[pass];
4583 any = zeroany;
4584 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
4585 (pass == 0) ? "Never pickup" : "Always pickup",
4586 MENU_UNSELECTED);
4587 for (i = 0; i < numapes[pass] && ape; i++) {
4588 any.a_void = (opt_idx == 1) ? 0 : ape;
4589 /* length of pattern plus quotes is less than BUFSZ */
4590 Sprintf(apebuf, "\"%s\"", ape->pattern);
4591 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, apebuf,
4592 MENU_UNSELECTED);
4593 ape = ape->next;
4596 Sprintf(apebuf, "%s autopickup exceptions",
4597 (opt_idx == 1) ? "List of" : "Remove which");
4598 end_menu(tmpwin, apebuf);
4599 pick_cnt = select_menu(tmpwin,
4600 (opt_idx == 1) ? PICK_NONE : PICK_ANY,
4601 &pick_list);
4602 if (pick_cnt > 0) {
4603 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
4604 remove_autopickup_exception(
4605 (struct autopickup_exception *)
4606 pick_list[pick_idx].item.a_void);
4607 free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
4609 destroy_nhwindow(tmpwin);
4610 if (pick_cnt >= 0)
4611 goto ape_again;
4613 } else if (!strcmp("symset", optname)
4614 || !strcmp("roguesymset", optname)) {
4615 menu_item *symset_pick = (menu_item *) 0;
4616 boolean primaryflag = (*optname == 's'),
4617 rogueflag = (*optname == 'r'),
4618 ready_to_switch = FALSE,
4619 nothing_to_do = FALSE;
4620 char *symset_name, fmtstr[20];
4621 struct symsetentry *sl;
4622 int res, which_set, setcount = 0, chosen = -2;
4624 if (rogueflag)
4625 which_set = ROGUESET;
4626 else
4627 which_set = PRIMARY;
4629 /* clear symset[].name as a flag to read_sym_file() to build list */
4630 symset_name = symset[which_set].name;
4631 symset[which_set].name = (char *) 0;
4632 symset_list = (struct symsetentry *) 0;
4634 res = read_sym_file(which_set);
4635 if (res && symset_list) {
4636 char symsetchoice[BUFSZ];
4637 int let = 'a', biggest = 0, thissize = 0;
4639 sl = symset_list;
4640 while (sl) {
4641 /* check restrictions */
4642 if ((!rogueflag && sl->rogue)
4643 || (!primaryflag && sl->primary)) {
4644 sl = sl->next;
4645 continue;
4647 setcount++;
4648 /* find biggest name */
4649 if (sl->name)
4650 thissize = strlen(sl->name);
4651 if (thissize > biggest)
4652 biggest = thissize;
4653 sl = sl->next;
4655 if (!setcount) {
4656 pline("There are no appropriate %ssymbol sets available.",
4657 (rogueflag) ? "rogue level "
4658 : (primaryflag) ? "primary " : "");
4659 return TRUE;
4662 Sprintf(fmtstr, "%%-%ds %%s", biggest + 5);
4663 tmpwin = create_nhwindow(NHW_MENU);
4664 start_menu(tmpwin);
4665 any = zeroany;
4666 any.a_int = 1;
4667 add_menu(tmpwin, NO_GLYPH, &any, let++, 0, ATR_NONE,
4668 "Default Symbols", MENU_UNSELECTED);
4670 sl = symset_list;
4671 while (sl) {
4672 /* check restrictions */
4673 if ((!rogueflag && sl->rogue)
4674 || (!primaryflag && sl->primary)) {
4675 sl = sl->next;
4676 continue;
4678 if (sl->name) {
4679 any.a_int = sl->idx + 2;
4680 Sprintf(symsetchoice, fmtstr, sl->name,
4681 sl->desc ? sl->desc : "");
4682 add_menu(tmpwin, NO_GLYPH, &any, let, 0, ATR_NONE,
4683 symsetchoice, MENU_UNSELECTED);
4684 if (let == 'z')
4685 let = 'A';
4686 else
4687 let++;
4689 sl = sl->next;
4691 Sprintf(buf, "Select %ssymbol set:", rogueflag ? "rogue level " : "");
4692 end_menu(tmpwin, buf);
4693 if (select_menu(tmpwin, PICK_ONE, &symset_pick) > 0) {
4694 chosen = symset_pick->item.a_int - 2;
4695 free((genericptr_t) symset_pick);
4697 destroy_nhwindow(tmpwin);
4699 if (chosen > -1) {
4700 /* chose an actual symset name from file */
4701 sl = symset_list;
4702 while (sl) {
4703 if (sl->idx == chosen) {
4704 if (symset_name) {
4705 free((genericptr_t) symset_name);
4706 symset_name = (char *) 0;
4708 /* free the now stale attributes */
4709 clear_symsetentry(which_set, TRUE);
4711 /* transfer only the name of the symbol set */
4712 symset[which_set].name = dupstr(sl->name);
4713 ready_to_switch = TRUE;
4714 break;
4716 sl = sl->next;
4718 } else if (chosen == -1) {
4719 /* explicit selection of defaults */
4720 /* free the now stale symset attributes */
4721 if (symset_name) {
4722 free((genericptr_t) symset_name);
4723 symset_name = (char *) 0;
4725 clear_symsetentry(which_set, TRUE);
4726 } else
4727 nothing_to_do = TRUE;
4728 } else if (!res) {
4729 /* The symbols file could not be accessed */
4730 pline("Unable to access \"%s\" file.", SYMBOLS);
4731 return TRUE;
4732 } else if (!symset_list) {
4733 /* The symbols file was empty */
4734 pline("There were no symbol sets found in \"%s\".", SYMBOLS);
4735 return TRUE;
4738 /* clean up */
4739 while (symset_list) {
4740 sl = symset_list;
4741 if (sl->name)
4742 free((genericptr_t) sl->name);
4743 sl->name = (char *) 0;
4745 if (sl->desc)
4746 free((genericptr_t) sl->desc);
4747 sl->desc = (char *) 0;
4749 symset_list = sl->next;
4750 free((genericptr_t) sl);
4753 if (nothing_to_do)
4754 return TRUE;
4756 if (!symset[which_set].name && symset_name)
4757 symset[which_set].name = symset_name; /* not dupstr() here */
4759 /* Set default symbols and clear the handling value */
4760 if (rogueflag)
4761 init_r_symbols();
4762 else
4763 init_l_symbols();
4765 if (symset[which_set].name) {
4766 if (read_sym_file(which_set)) {
4767 ready_to_switch = TRUE;
4768 } else {
4769 clear_symsetentry(which_set, TRUE);
4770 return TRUE;
4774 if (ready_to_switch)
4775 switch_symbols(TRUE);
4777 if (Is_rogue_level(&u.uz)) {
4778 if (rogueflag)
4779 assign_graphics(ROGUESET);
4780 } else if (!rogueflag)
4781 assign_graphics(PRIMARY);
4782 need_redraw = TRUE;
4783 return TRUE;
4785 } else {
4786 /* didn't match any of the special options */
4787 return FALSE;
4789 return TRUE;
4792 #define rolestring(val, array, field) \
4793 ((val >= 0) ? array[val].field : (val == ROLE_RANDOM) ? randomrole : none)
4795 /* This is ugly. We have all the option names in the compopt[] array,
4796 but we need to look at each option individually to get the value. */
4797 STATIC_OVL const char *
4798 get_compopt_value(optname, buf)
4799 const char *optname;
4800 char *buf;
4802 char ocl[MAXOCLASSES + 1];
4803 static const char none[] = "(none)", randomrole[] = "random",
4804 to_be_done[] = "(to be done)", defopt[] = "default",
4805 defbrief[] = "def";
4806 int i;
4808 buf[0] = '\0';
4809 if (!strcmp(optname, "align_message"))
4810 Sprintf(buf, "%s",
4811 iflags.wc_align_message == ALIGN_TOP
4812 ? "top"
4813 : iflags.wc_align_message == ALIGN_LEFT
4814 ? "left"
4815 : iflags.wc_align_message == ALIGN_BOTTOM
4816 ? "bottom"
4817 : iflags.wc_align_message == ALIGN_RIGHT
4818 ? "right"
4819 : defopt);
4820 else if (!strcmp(optname, "align_status"))
4821 Sprintf(buf, "%s",
4822 iflags.wc_align_status == ALIGN_TOP
4823 ? "top"
4824 : iflags.wc_align_status == ALIGN_LEFT
4825 ? "left"
4826 : iflags.wc_align_status == ALIGN_BOTTOM
4827 ? "bottom"
4828 : iflags.wc_align_status == ALIGN_RIGHT
4829 ? "right"
4830 : defopt);
4831 else if (!strcmp(optname, "align"))
4832 Sprintf(buf, "%s", rolestring(flags.initalign, aligns, adj));
4833 #ifdef WIN32
4834 else if (!strcmp(optname, "altkeyhandler"))
4835 Sprintf(buf, "%s",
4836 iflags.altkeyhandler[0] ? iflags.altkeyhandler : "default");
4837 #endif
4838 #ifdef BACKWARD_COMPAT
4839 else if (!strcmp(optname, "boulder"))
4840 Sprintf(buf, "%c",
4841 iflags.bouldersym
4842 ? iflags.bouldersym
4843 : showsyms[(int) objects[BOULDER].oc_class + SYM_OFF_O]);
4844 #endif
4845 else if (!strcmp(optname, "catname"))
4846 Sprintf(buf, "%s", catname[0] ? catname : none);
4847 else if (!strcmp(optname, "disclose"))
4848 for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
4849 if (i)
4850 (void) strkitten(buf, ' ');
4851 (void) strkitten(buf, flags.end_disclose[i]);
4852 (void) strkitten(buf, disclosure_options[i]);
4854 else if (!strcmp(optname, "dogname"))
4855 Sprintf(buf, "%s", dogname[0] ? dogname : none);
4856 else if (!strcmp(optname, "dungeon"))
4857 Sprintf(buf, "%s", to_be_done);
4858 else if (!strcmp(optname, "effects"))
4859 Sprintf(buf, "%s", to_be_done);
4860 else if (!strcmp(optname, "font_map"))
4861 Sprintf(buf, "%s", iflags.wc_font_map ? iflags.wc_font_map : defopt);
4862 else if (!strcmp(optname, "font_message"))
4863 Sprintf(buf, "%s",
4864 iflags.wc_font_message ? iflags.wc_font_message : defopt);
4865 else if (!strcmp(optname, "font_status"))
4866 Sprintf(buf, "%s",
4867 iflags.wc_font_status ? iflags.wc_font_status : defopt);
4868 else if (!strcmp(optname, "font_menu"))
4869 Sprintf(buf, "%s",
4870 iflags.wc_font_menu ? iflags.wc_font_menu : defopt);
4871 else if (!strcmp(optname, "font_text"))
4872 Sprintf(buf, "%s",
4873 iflags.wc_font_text ? iflags.wc_font_text : defopt);
4874 else if (!strcmp(optname, "font_size_map")) {
4875 if (iflags.wc_fontsiz_map)
4876 Sprintf(buf, "%d", iflags.wc_fontsiz_map);
4877 else
4878 Strcpy(buf, defopt);
4879 } else if (!strcmp(optname, "font_size_message")) {
4880 if (iflags.wc_fontsiz_message)
4881 Sprintf(buf, "%d", iflags.wc_fontsiz_message);
4882 else
4883 Strcpy(buf, defopt);
4884 } else if (!strcmp(optname, "font_size_status")) {
4885 if (iflags.wc_fontsiz_status)
4886 Sprintf(buf, "%d", iflags.wc_fontsiz_status);
4887 else
4888 Strcpy(buf, defopt);
4889 } else if (!strcmp(optname, "font_size_menu")) {
4890 if (iflags.wc_fontsiz_menu)
4891 Sprintf(buf, "%d", iflags.wc_fontsiz_menu);
4892 else
4893 Strcpy(buf, defopt);
4894 } else if (!strcmp(optname, "font_size_text")) {
4895 if (iflags.wc_fontsiz_text)
4896 Sprintf(buf, "%d", iflags.wc_fontsiz_text);
4897 else
4898 Strcpy(buf, defopt);
4899 } else if (!strcmp(optname, "fruit"))
4900 Sprintf(buf, "%s", pl_fruit);
4901 else if (!strcmp(optname, "gender"))
4902 Sprintf(buf, "%s", rolestring(flags.initgend, genders, adj));
4903 else if (!strcmp(optname, "horsename"))
4904 Sprintf(buf, "%s", horsename[0] ? horsename : none);
4905 else if (!strcmp(optname, "map_mode"))
4906 Sprintf(buf, "%s",
4907 iflags.wc_map_mode == MAP_MODE_TILES
4908 ? "tiles"
4909 : iflags.wc_map_mode == MAP_MODE_ASCII4x6
4910 ? "ascii4x6"
4911 : iflags.wc_map_mode == MAP_MODE_ASCII6x8
4912 ? "ascii6x8"
4913 : iflags.wc_map_mode == MAP_MODE_ASCII8x8
4914 ? "ascii8x8"
4915 : iflags.wc_map_mode == MAP_MODE_ASCII16x8
4916 ? "ascii16x8"
4917 : iflags.wc_map_mode == MAP_MODE_ASCII7x12
4918 ? "ascii7x12"
4919 : iflags.wc_map_mode == MAP_MODE_ASCII8x12
4920 ? "ascii8x12"
4921 : iflags.wc_map_mode
4922 == MAP_MODE_ASCII16x12
4923 ? "ascii16x12"
4924 : iflags.wc_map_mode
4925 == MAP_MODE_ASCII12x16
4926 ? "ascii12x16"
4927 : iflags.wc_map_mode
4928 == MAP_MODE_ASCII10x18
4929 ? "ascii10x18"
4930 : iflags.wc_map_mode
4931 == MAP_MODE_ASCII_FIT_TO_SCREEN
4932 ? "fit_to_screen"
4933 : defopt);
4934 else if (!strcmp(optname, "menustyle"))
4935 Sprintf(buf, "%s", menutype[(int) flags.menu_style]);
4936 else if (!strcmp(optname, "menu_deselect_all"))
4937 Sprintf(buf, "%s", to_be_done);
4938 else if (!strcmp(optname, "menu_deselect_page"))
4939 Sprintf(buf, "%s", to_be_done);
4940 else if (!strcmp(optname, "menu_first_page"))
4941 Sprintf(buf, "%s", to_be_done);
4942 else if (!strcmp(optname, "menu_invert_all"))
4943 Sprintf(buf, "%s", to_be_done);
4944 else if (!strcmp(optname, "menu_headings"))
4945 Sprintf(buf, "%s", attr2attrname(iflags.menu_headings));
4946 else if (!strcmp(optname, "menu_invert_page"))
4947 Sprintf(buf, "%s", to_be_done);
4948 else if (!strcmp(optname, "menu_last_page"))
4949 Sprintf(buf, "%s", to_be_done);
4950 else if (!strcmp(optname, "menu_next_page"))
4951 Sprintf(buf, "%s", to_be_done);
4952 else if (!strcmp(optname, "menu_previous_page"))
4953 Sprintf(buf, "%s", to_be_done);
4954 else if (!strcmp(optname, "menu_search"))
4955 Sprintf(buf, "%s", to_be_done);
4956 else if (!strcmp(optname, "menu_select_all"))
4957 Sprintf(buf, "%s", to_be_done);
4958 else if (!strcmp(optname, "menu_select_page"))
4959 Sprintf(buf, "%s", to_be_done);
4960 else if (!strcmp(optname, "monsters")) {
4961 Sprintf(buf, "%s", to_be_done);
4962 } else if (!strcmp(optname, "msghistory")) {
4963 Sprintf(buf, "%u", iflags.msg_history);
4964 #ifdef TTY_GRAPHICS
4965 } else if (!strcmp(optname, "msg_window")) {
4966 Sprintf(buf, "%s", (iflags.prevmsg_window == 's')
4967 ? "single"
4968 : (iflags.prevmsg_window == 'c')
4969 ? "combination"
4970 : (iflags.prevmsg_window == 'f')
4971 ? "full"
4972 : "reversed");
4973 #endif
4974 } else if (!strcmp(optname, "name")) {
4975 Sprintf(buf, "%s", plname);
4976 } else if (!strcmp(optname, "number_pad")) {
4977 static const char *numpadmodes[] = {
4978 "0=off", "1=on", "2=on, MSDOS compatible",
4979 "3=on, phone-style layout",
4980 "4=on, phone layout, MSDOS compatible",
4981 "-1=off, y & z swapped", /*[5]*/
4983 int indx = Cmd.num_pad
4984 ? (Cmd.phone_layout ? (Cmd.pcHack_compat ? 4 : 3)
4985 : (Cmd.pcHack_compat ? 2 : 1))
4986 : Cmd.swap_yz ? 5 : 0;
4988 Strcpy(buf, numpadmodes[indx]);
4989 } else if (!strcmp(optname, "objects")) {
4990 Sprintf(buf, "%s", to_be_done);
4991 } else if (!strcmp(optname, "packorder")) {
4992 oc_to_str(flags.inv_order, ocl);
4993 Sprintf(buf, "%s", ocl);
4994 #ifdef CHANGE_COLOR
4995 } else if (!strcmp(optname, "palette")) {
4996 Sprintf(buf, "%s", get_color_string());
4997 #endif
4998 } else if (!strcmp(optname, "paranoid_confirmation")) {
4999 char tmpbuf[QBUFSZ];
5001 tmpbuf[0] = '\0';
5002 if (ParanoidConfirm)
5003 Strcat(tmpbuf, " Confirm");
5004 if (ParanoidQuit)
5005 Strcat(tmpbuf, " quit");
5006 if (ParanoidDie)
5007 Strcat(tmpbuf, " die");
5008 if (ParanoidBones)
5009 Strcat(tmpbuf, " bones");
5010 if (ParanoidHit)
5011 Strcat(tmpbuf, " attack");
5012 if (ParanoidPray)
5013 Strcat(tmpbuf, " pray");
5014 if (ParanoidRemove)
5015 Strcat(tmpbuf, " Remove");
5016 Strcpy(buf, tmpbuf[0] ? &tmpbuf[1] : "none");
5017 } else if (!strcmp(optname, "pettype")) {
5018 Sprintf(buf, "%s", (preferred_pet == 'c') ? "cat"
5019 : (preferred_pet == 'd') ? "dog"
5020 : (preferred_pet == 'h') ? "horse"
5021 : (preferred_pet == 'n') ? "none"
5022 : "random");
5023 } else if (!strcmp(optname, "pickup_burden")) {
5024 Sprintf(buf, "%s", burdentype[flags.pickup_burden]);
5025 } else if (!strcmp(optname, "pickup_types")) {
5026 oc_to_str(flags.pickup_types, ocl);
5027 Sprintf(buf, "%s", ocl[0] ? ocl : "all");
5028 } else if (!strcmp(optname, "pile_limit")) {
5029 Sprintf(buf, "%d", flags.pile_limit);
5030 } else if (!strcmp(optname, "playmode")) {
5031 Strcpy(buf, wizard ? "debug" : discover ? "explore" : "normal");
5032 } else if (!strcmp(optname, "race")) {
5033 Sprintf(buf, "%s", rolestring(flags.initrace, races, noun));
5034 } else if (!strcmp(optname, "roguesymset")) {
5035 Sprintf(buf, "%s",
5036 symset[ROGUESET].name ? symset[ROGUESET].name : "default");
5037 if (currentgraphics == ROGUESET && symset[ROGUESET].name)
5038 Strcat(buf, ", active");
5039 } else if (!strcmp(optname, "role")) {
5040 Sprintf(buf, "%s", rolestring(flags.initrole, roles, name.m));
5041 } else if (!strcmp(optname, "runmode")) {
5042 Sprintf(buf, "%s", runmodes[flags.runmode]);
5043 } else if (!strcmp(optname, "whatis_coord")) {
5044 Sprintf(buf, "%s",
5045 (iflags.getpos_coords == GPCOORDS_MAP) ? "map"
5046 : (iflags.getpos_coords == GPCOORDS_COMPASS) ? "compass"
5047 : (iflags.getpos_coords == GPCOORDS_COMFULL) ? "full compass"
5048 : (iflags.getpos_coords == GPCOORDS_SCREEN) ? "screen"
5049 : "none");
5050 } else if (!strcmp(optname, "scores")) {
5051 Sprintf(buf, "%d top/%d around%s", flags.end_top, flags.end_around,
5052 flags.end_own ? "/own" : "");
5053 } else if (!strcmp(optname, "scroll_amount")) {
5054 if (iflags.wc_scroll_amount)
5055 Sprintf(buf, "%d", iflags.wc_scroll_amount);
5056 else
5057 Strcpy(buf, defopt);
5058 } else if (!strcmp(optname, "scroll_margin")) {
5059 if (iflags.wc_scroll_margin)
5060 Sprintf(buf, "%d", iflags.wc_scroll_margin);
5061 else
5062 Strcpy(buf, defopt);
5063 } else if (!strcmp(optname, "sortloot")) {
5064 for (i = 0; i < SIZE(sortltype); i++)
5065 if (flags.sortloot == sortltype[i][0]) {
5066 Strcpy(buf, sortltype[i]);
5067 break;
5069 } else if (!strcmp(optname, "player_selection")) {
5070 Sprintf(buf, "%s", iflags.wc_player_selection ? "prompts" : "dialog");
5071 #ifdef MSDOS
5072 } else if (!strcmp(optname, "soundcard")) {
5073 Sprintf(buf, "%s", to_be_done);
5074 #endif
5075 } else if (!strcmp(optname, "suppress_alert")) {
5076 if (flags.suppress_alert == 0L)
5077 Strcpy(buf, none);
5078 else
5079 Sprintf(buf, "%lu.%lu.%lu", FEATURE_NOTICE_VER_MAJ,
5080 FEATURE_NOTICE_VER_MIN, FEATURE_NOTICE_VER_PATCH);
5081 } else if (!strcmp(optname, "symset")) {
5082 Sprintf(buf, "%s",
5083 symset[PRIMARY].name ? symset[PRIMARY].name : "default");
5084 if (currentgraphics == PRIMARY && symset[PRIMARY].name)
5085 Strcat(buf, ", active");
5086 } else if (!strcmp(optname, "tile_file")) {
5087 Sprintf(buf, "%s",
5088 iflags.wc_tile_file ? iflags.wc_tile_file : defopt);
5089 } else if (!strcmp(optname, "tile_height")) {
5090 if (iflags.wc_tile_height)
5091 Sprintf(buf, "%d", iflags.wc_tile_height);
5092 else
5093 Strcpy(buf, defopt);
5094 } else if (!strcmp(optname, "tile_width")) {
5095 if (iflags.wc_tile_width)
5096 Sprintf(buf, "%d", iflags.wc_tile_width);
5097 else
5098 Strcpy(buf, defopt);
5099 } else if (!strcmp(optname, "traps")) {
5100 Sprintf(buf, "%s", to_be_done);
5101 } else if (!strcmp(optname, "vary_msgcount")) {
5102 if (iflags.wc_vary_msgcount)
5103 Sprintf(buf, "%d", iflags.wc_vary_msgcount);
5104 else
5105 Strcpy(buf, defopt);
5106 #ifdef MSDOS
5107 } else if (!strcmp(optname, "video")) {
5108 Sprintf(buf, "%s", to_be_done);
5109 #endif
5110 #ifdef VIDEOSHADES
5111 } else if (!strcmp(optname, "videoshades")) {
5112 Sprintf(buf, "%s-%s-%s", shade[0], shade[1], shade[2]);
5113 } else if (!strcmp(optname, "videocolors")) {
5114 Sprintf(buf, "%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d",
5115 ttycolors[CLR_RED], ttycolors[CLR_GREEN],
5116 ttycolors[CLR_BROWN], ttycolors[CLR_BLUE],
5117 ttycolors[CLR_MAGENTA], ttycolors[CLR_CYAN],
5118 ttycolors[CLR_ORANGE], ttycolors[CLR_BRIGHT_GREEN],
5119 ttycolors[CLR_YELLOW], ttycolors[CLR_BRIGHT_BLUE],
5120 ttycolors[CLR_BRIGHT_MAGENTA], ttycolors[CLR_BRIGHT_CYAN]);
5121 #endif /* VIDEOSHADES */
5122 } else if (!strcmp(optname, "windowtype")) {
5123 Sprintf(buf, "%s", windowprocs.name);
5124 } else if (!strcmp(optname, "windowcolors")) {
5125 Sprintf(
5126 buf, "%s/%s %s/%s %s/%s %s/%s",
5127 iflags.wc_foregrnd_menu ? iflags.wc_foregrnd_menu : defbrief,
5128 iflags.wc_backgrnd_menu ? iflags.wc_backgrnd_menu : defbrief,
5129 iflags.wc_foregrnd_message ? iflags.wc_foregrnd_message
5130 : defbrief,
5131 iflags.wc_backgrnd_message ? iflags.wc_backgrnd_message
5132 : defbrief,
5133 iflags.wc_foregrnd_status ? iflags.wc_foregrnd_status : defbrief,
5134 iflags.wc_backgrnd_status ? iflags.wc_backgrnd_status : defbrief,
5135 iflags.wc_foregrnd_text ? iflags.wc_foregrnd_text : defbrief,
5136 iflags.wc_backgrnd_text ? iflags.wc_backgrnd_text : defbrief);
5137 #ifdef PREFIXES_IN_USE
5138 } else {
5139 for (i = 0; i < PREFIX_COUNT; ++i)
5140 if (!strcmp(optname, fqn_prefix_names[i]) && fqn_prefix[i])
5141 Sprintf(buf, "%s", fqn_prefix[i]);
5142 #endif
5145 if (buf[0])
5146 return buf;
5147 else
5148 return "unknown";
5152 dotogglepickup()
5154 char buf[BUFSZ], ocl[MAXOCLASSES + 1];
5156 flags.pickup = !flags.pickup;
5157 if (flags.pickup) {
5158 oc_to_str(flags.pickup_types, ocl);
5159 Sprintf(buf, "ON, for %s objects%s", ocl[0] ? ocl : "all",
5160 (iflags.autopickup_exceptions[AP_LEAVE]
5161 || iflags.autopickup_exceptions[AP_GRAB])
5162 ? ((count_ape_maps((int *) 0, (int *) 0) == 1)
5163 ? ", with one exception"
5164 : ", with some exceptions")
5165 : "");
5166 } else {
5167 Strcpy(buf, "OFF");
5169 pline("Autopickup: %s.", buf);
5170 return 0;
5174 add_autopickup_exception(mapping)
5175 const char *mapping;
5177 static const char
5178 APE_regex_error[] = "regex error in AUTOPICKUP_EXCEPTION",
5179 APE_syntax_error[] = "syntax error in AUTOPICKUP_EXCEPTION";
5181 struct autopickup_exception *ape, **apehead;
5182 char text[256], end;
5183 int n;
5184 boolean grab = FALSE;
5186 /* scan length limit used to be 255, but smaller size allows the
5187 quoted value to fit within BUFSZ, simplifying formatting elsewhere;
5188 this used to ignore the possibility of trailing junk but now checks
5189 for it, accepting whitespace but rejecting anything else unless it
5190 starts with '#" for a comment */
5191 end = '\0';
5192 if ((n = sscanf(mapping, "\"<%253[^\"]\" %c", text, &end)) == 1
5193 || (n == 2 && end == '#')) {
5194 grab = TRUE;
5195 } else if ((n = sscanf(mapping, "\">%253[^\"]\" %c", text, &end)) == 1
5196 || (n = sscanf(mapping, "\"%253[^\"]\" %c", text, &end)) == 1
5197 || (n == 2 && end == '#')) {
5198 grab = FALSE;
5199 } else {
5200 if (!iflags.window_inited)
5201 raw_print(APE_syntax_error); /* from options file */
5202 else
5203 pline("%s", APE_syntax_error); /* via 'O' command */
5204 return 0;
5207 ape = (struct autopickup_exception *) alloc(sizeof *ape);
5208 ape->regex = regex_init();
5209 if (!regex_compile(text, ape->regex)) {
5210 if (!iflags.window_inited)
5211 raw_print(APE_regex_error);
5212 else
5213 pline("%s", APE_regex_error);
5214 regex_free(ape->regex);
5215 free((genericptr_t) ape);
5216 return 0;
5218 apehead = (grab) ? &iflags.autopickup_exceptions[AP_GRAB]
5219 : &iflags.autopickup_exceptions[AP_LEAVE];
5221 ape->pattern = dupstr(text);
5222 ape->grab = grab;
5223 ape->next = *apehead;
5224 *apehead = ape;
5225 return 1;
5228 STATIC_OVL void
5229 remove_autopickup_exception(whichape)
5230 struct autopickup_exception *whichape;
5232 struct autopickup_exception *ape, *prev = 0;
5233 int chain = whichape->grab ? AP_GRAB : AP_LEAVE;
5235 for (ape = iflags.autopickup_exceptions[chain]; ape;) {
5236 if (ape == whichape) {
5237 struct autopickup_exception *freeape = ape;
5239 ape = ape->next;
5240 if (prev)
5241 prev->next = ape;
5242 else
5243 iflags.autopickup_exceptions[chain] = ape;
5244 regex_free(freeape->regex);
5245 free((genericptr_t) freeape->pattern);
5246 free((genericptr_t) freeape);
5247 } else {
5248 prev = ape;
5249 ape = ape->next;
5254 STATIC_OVL int
5255 count_ape_maps(leave, grab)
5256 int *leave, *grab;
5258 struct autopickup_exception *ape;
5259 int pass, totalapes, numapes[2] = { 0, 0 };
5261 for (pass = AP_LEAVE; pass <= AP_GRAB; ++pass) {
5262 ape = iflags.autopickup_exceptions[pass];
5263 while (ape) {
5264 ape = ape->next;
5265 numapes[pass]++;
5268 totalapes = numapes[AP_LEAVE] + numapes[AP_GRAB];
5269 if (leave)
5270 *leave = numapes[AP_LEAVE];
5271 if (grab)
5272 *grab = numapes[AP_GRAB];
5273 return totalapes;
5276 void
5277 free_autopickup_exceptions()
5279 struct autopickup_exception *ape;
5280 int pass;
5282 for (pass = AP_LEAVE; pass <= AP_GRAB; ++pass) {
5283 while ((ape = iflags.autopickup_exceptions[pass]) != 0) {
5284 regex_free(ape->regex);
5285 free((genericptr_t) ape->pattern);
5286 iflags.autopickup_exceptions[pass] = ape->next;
5287 free((genericptr_t) ape);
5292 /* bundle some common usage into one easy-to-use routine */
5294 load_symset(s, which_set)
5295 const char *s;
5296 int which_set;
5298 clear_symsetentry(which_set, TRUE);
5300 if (symset[which_set].name)
5301 free((genericptr_t) symset[which_set].name);
5302 symset[which_set].name = dupstr(s);
5304 if (read_sym_file(which_set)) {
5305 switch_symbols(TRUE);
5306 } else {
5307 clear_symsetentry(which_set, TRUE);
5308 return 0;
5310 return 1;
5313 void
5314 free_symsets()
5316 clear_symsetentry(PRIMARY, TRUE);
5317 clear_symsetentry(ROGUESET, TRUE);
5319 /* symset_list is cleaned up as soon as it's used, so we shouldn't
5320 have to anything about it here */
5321 /* assert( symset_list == NULL ); */
5324 /* Parse the value of a SYMBOLS line from a config file */
5325 boolean
5326 parsesymbols(opts)
5327 register char *opts;
5329 int val;
5330 char *op, *symname, *strval;
5331 struct symparse *symp;
5333 if ((op = index(opts, ',')) != 0) {
5334 *op++ = 0;
5335 if (!parsesymbols(op)) return FALSE;
5338 /* S_sample:string */
5339 symname = opts;
5340 strval = index(opts, ':');
5341 if (!strval)
5342 strval = index(opts, '=');
5343 if (!strval)
5344 return FALSE;
5345 *strval++ = '\0';
5347 /* strip leading and trailing white space from symname and strval */
5348 mungspaces(symname);
5349 mungspaces(strval);
5351 symp = match_sym(symname);
5352 if (!symp)
5353 return FALSE;
5355 if (symp->range && symp->range != SYM_CONTROL) {
5356 val = sym_val(strval);
5357 update_l_symset(symp, val);
5359 return TRUE;
5362 struct symparse *
5363 match_sym(buf)
5364 char *buf;
5366 size_t len = strlen(buf);
5367 const char *p = index(buf, ':'), *q = index(buf, '=');
5368 struct symparse *sp = loadsyms;
5370 if (!p || (q && q < p))
5371 p = q;
5372 if (p) {
5373 /* note: there will be at most one space before the '='
5374 because caller has condensed buf[] with mungspaces() */
5375 if (p > buf && p[-1] == ' ')
5376 p--;
5377 len = (int) (p - buf);
5379 while (sp->range) {
5380 if ((len >= strlen(sp->name)) && !strncmpi(buf, sp->name, len))
5381 return sp;
5382 sp++;
5384 return (struct symparse *) 0;
5388 sym_val(strval)
5389 const char *strval;
5391 char buf[QBUFSZ];
5393 buf[0] = '\0';
5394 if (!strval[0] || !strval[1]) { /* empty, or single character */
5395 /* if single char is space or tab, leave buf[0]=='\0' */
5396 if (!isspace((uchar) strval[0]))
5397 buf[0] = strval[0];
5398 } else if (strval[0] == '\'') { /* single quote */
5399 /* simple matching single quote; we know strval[1] isn't '\0' */
5400 if (strval[2] == '\'' && !strval[3]) {
5401 /* accepts '\' as backslash and ''' as single quote */
5402 buf[0] = strval[1];
5404 /* if backslash, handle single or double quote or second backslash */
5405 } else if (strval[1] == '\\' && strval[2] && strval[3] == '\''
5406 && index("'\"\\", strval[2]) && !strval[4]) {
5407 buf[0] = strval[2];
5409 /* not simple quote or basic backslash;
5410 strip closing quote and let escapes() deal with it */
5411 } else {
5412 char *p, tmp[QBUFSZ];
5414 (void) strncpy(tmp, strval + 1, sizeof tmp - 1);
5415 tmp[sizeof tmp - 1] = '\0';
5416 if ((p = rindex(tmp, '\'')) != 0) {
5417 *p = '\0';
5418 escapes(tmp, buf);
5419 } /* else buf[0] stays '\0' */
5421 } else /* not lone char nor single quote */
5422 escapes(strval, buf);
5424 return (int) *buf;
5427 /* data for option_help() */
5428 static const char *opt_intro[] = {
5429 "", " NetHack Options Help:", "",
5430 #define CONFIG_SLOT 3 /* fill in next value at run-time */
5431 (char *) 0,
5432 #if !defined(MICRO) && !defined(MAC)
5433 "or use `NETHACKOPTIONS=\"<options>\"' in your environment",
5434 #endif
5435 "(<options> is a list of options separated by commas)",
5436 #ifdef VMS
5437 "-- for example, $ DEFINE NETHACKOPTIONS \"noautopickup,fruit:kumquat\"",
5438 #endif
5439 "or press \"O\" while playing and use the menu.", "",
5440 "Boolean options (which can be negated by prefixing them with '!' or \"no\"):",
5441 (char *) 0
5444 static const char *opt_epilog[] = {
5446 "Some of the options can be set only before the game is started; those",
5447 "items will not be selectable in the 'O' command's menu.", (char *) 0
5450 void
5451 option_help()
5453 char buf[BUFSZ], buf2[BUFSZ];
5454 register int i;
5455 winid datawin;
5457 datawin = create_nhwindow(NHW_TEXT);
5458 Sprintf(buf, "Set options as OPTIONS=<options> in %s", configfile);
5459 opt_intro[CONFIG_SLOT] = (const char *) buf;
5460 for (i = 0; opt_intro[i]; i++)
5461 putstr(datawin, 0, opt_intro[i]);
5463 /* Boolean options */
5464 for (i = 0; boolopt[i].name; i++) {
5465 if (boolopt[i].addr) {
5466 if (boolopt[i].addr == &iflags.sanity_check && !wizard)
5467 continue;
5468 if (boolopt[i].addr == &iflags.menu_tab_sep && !wizard)
5469 continue;
5470 next_opt(datawin, boolopt[i].name);
5473 next_opt(datawin, "");
5475 /* Compound options */
5476 putstr(datawin, 0, "Compound options:");
5477 for (i = 0; compopt[i].name; i++) {
5478 Sprintf(buf2, "`%s'", compopt[i].name);
5479 Sprintf(buf, "%-20s - %s%c", buf2, compopt[i].descr,
5480 compopt[i + 1].name ? ',' : '.');
5481 putstr(datawin, 0, buf);
5484 for (i = 0; opt_epilog[i]; i++)
5485 putstr(datawin, 0, opt_epilog[i]);
5487 display_nhwindow(datawin, FALSE);
5488 destroy_nhwindow(datawin);
5489 return;
5493 * prints the next boolean option, on the same line if possible, on a new
5494 * line if not. End with next_opt("").
5496 void
5497 next_opt(datawin, str)
5498 winid datawin;
5499 const char *str;
5501 static char *buf = 0;
5502 int i;
5503 char *s;
5505 if (!buf)
5506 *(buf = (char *) alloc(BUFSZ)) = '\0';
5508 if (!*str) {
5509 s = eos(buf);
5510 if (s > &buf[1] && s[-2] == ',')
5511 Strcpy(s - 2, "."); /* replace last ", " */
5512 i = COLNO; /* (greater than COLNO - 2) */
5513 } else {
5514 i = strlen(buf) + strlen(str) + 2;
5517 if (i > COLNO - 2) { /* rule of thumb */
5518 putstr(datawin, 0, buf);
5519 buf[0] = 0;
5521 if (*str) {
5522 Strcat(buf, str);
5523 Strcat(buf, ", ");
5524 } else {
5525 putstr(datawin, 0, str);
5526 free((genericptr_t) buf), buf = 0;
5528 return;
5531 /* Returns the fid of the fruit type; if that type already exists, it
5532 * returns the fid of that one; if it does not exist, it adds a new fruit
5533 * type to the chain and returns the new one.
5534 * If replace_fruit is sent in, replace the fruit in the chain rather than
5535 * adding a new entry--for user specified fruits only.
5538 fruitadd(str, replace_fruit)
5539 char *str;
5540 struct fruit *replace_fruit;
5542 register int i;
5543 register struct fruit *f;
5544 int highest_fruit_id = 0;
5545 char buf[PL_FSIZ], altname[PL_FSIZ];
5546 boolean user_specified = (str == pl_fruit);
5547 /* if not user-specified, then it's a fruit name for a fruit on
5548 * a bones level...
5551 /* Note: every fruit has an id (kept in obj->spe) of at least 1;
5552 * 0 is an error.
5554 if (user_specified) {
5555 boolean found = FALSE, numeric = FALSE;
5557 /* force fruit to be singular; this handling is not
5558 needed--or wanted--for fruits from bones because
5559 they already received it in their original game */
5560 nmcpy(pl_fruit, makesingular(str), PL_FSIZ);
5561 /* assert( str == pl_fruit ); */
5563 /* disallow naming after other foods (since it'd be impossible
5564 * to tell the difference)
5567 for (i = bases[FOOD_CLASS]; objects[i].oc_class == FOOD_CLASS; i++) {
5568 if (!strcmp(OBJ_NAME(objects[i]), pl_fruit)) {
5569 found = TRUE;
5570 break;
5574 char *c;
5576 c = pl_fruit;
5578 for (c = pl_fruit; *c >= '0' && *c <= '9'; c++)
5580 if (isspace((uchar) *c) || *c == 0)
5581 numeric = TRUE;
5583 if (found || numeric || !strncmp(str, "cursed ", 7)
5584 || !strncmp(str, "uncursed ", 9) || !strncmp(str, "blessed ", 8)
5585 || !strncmp(str, "partly eaten ", 13)
5586 || (!strncmp(str, "tin of ", 7)
5587 && (!strcmp(str + 7, "spinach")
5588 || name_to_mon(str + 7) >= LOW_PM))
5589 || !strcmp(str, "empty tin")
5590 || ((str_end_is(str, " corpse")
5591 || str_end_is(str, " egg"))
5592 && name_to_mon(str) >= LOW_PM)) {
5593 Strcpy(buf, pl_fruit);
5594 Strcpy(pl_fruit, "candied ");
5595 nmcpy(pl_fruit + 8, buf, PL_FSIZ - 8);
5597 *altname = '\0';
5598 /* This flag indicates that a fruit has been made since the
5599 * last time the user set the fruit. If it hasn't, we can
5600 * safely overwrite the current fruit, preventing the user from
5601 * setting many fruits in a row and overflowing.
5602 * Possible expansion: check for specific fruit IDs, not for
5603 * any fruit.
5605 flags.made_fruit = FALSE;
5606 if (replace_fruit) {
5607 for (f = ffruit; f; f = f->nextf) {
5608 if (f == replace_fruit) {
5609 copynchars(f->fname, str, PL_FSIZ - 1);
5610 goto nonew;
5614 } else {
5615 /* not user_supplied, so assumed to be from bones */
5616 copynchars(altname, str, PL_FSIZ - 1);
5617 sanitize_name(altname);
5618 flags.made_fruit = TRUE; /* for safety. Any fruit name added from a
5619 bones level should exist anyway. */
5621 for (f = ffruit; f; f = f->nextf) {
5622 if (f->fid > highest_fruit_id)
5623 highest_fruit_id = f->fid;
5624 if (!strncmp(str, f->fname, PL_FSIZ - 1)
5625 || (*altname && !strcmp(altname, f->fname)))
5626 goto nonew;
5628 /* if adding another fruit would overflow spe, use a random
5629 fruit instead... we've got a lot to choose from.
5630 current_fruit remains as is. */
5631 if (highest_fruit_id >= 127)
5632 return rnd(127);
5634 f = newfruit();
5635 (void) memset((genericptr_t)f, 0, sizeof(struct fruit));
5636 copynchars(f->fname, *altname ? altname : str, PL_FSIZ - 1);
5637 f->fid = ++highest_fruit_id;
5638 /* we used to go out of our way to add it at the end of the list,
5639 but the order is arbitrary so use simpler insertion at start */
5640 f->nextf = ffruit;
5641 ffruit = f;
5642 nonew:
5643 if (user_specified)
5644 context.current_fruit = f->fid;
5645 return f->fid;
5649 * This is a somewhat generic menu for taking a list of NetHack style
5650 * class choices and presenting them via a description
5651 * rather than the traditional NetHack characters.
5652 * (Benefits users whose first exposure to NetHack is via tiles).
5654 * prompt
5655 * The title at the top of the menu.
5657 * category: 0 = monster class
5658 * 1 = object class
5660 * way
5661 * FALSE = PICK_ONE, TRUE = PICK_ANY
5663 * class_list
5664 * a null terminated string containing the list of choices.
5666 * class_selection
5667 * a null terminated string containing the selected characters.
5669 * Returns number selected.
5672 choose_classes_menu(prompt, category, way, class_list, class_select)
5673 const char *prompt;
5674 int category;
5675 boolean way;
5676 char *class_list;
5677 char *class_select;
5679 menu_item *pick_list = (menu_item *) 0;
5680 winid win;
5681 anything any;
5682 char buf[BUFSZ];
5683 int i, n;
5684 int ret;
5685 int next_accelerator, accelerator;
5687 if (class_list == (char *) 0 || class_select == (char *) 0)
5688 return 0;
5689 accelerator = 0;
5690 next_accelerator = 'a';
5691 any = zeroany;
5692 win = create_nhwindow(NHW_MENU);
5693 start_menu(win);
5694 while (*class_list) {
5695 const char *text;
5696 boolean selected;
5698 text = (char *) 0;
5699 selected = FALSE;
5700 switch (category) {
5701 case 0:
5702 text = def_monsyms[def_char_to_monclass(*class_list)].explain;
5703 accelerator = *class_list;
5704 Sprintf(buf, "%s", text);
5705 break;
5706 case 1:
5707 text = def_oc_syms[def_char_to_objclass(*class_list)].explain;
5708 accelerator = next_accelerator;
5709 Sprintf(buf, "%c %s", *class_list, text);
5710 break;
5711 default:
5712 impossible("choose_classes_menu: invalid category %d", category);
5714 if (way && *class_select) { /* Selections there already */
5715 if (index(class_select, *class_list)) {
5716 selected = TRUE;
5719 any.a_int = *class_list;
5720 add_menu(win, NO_GLYPH, &any, accelerator, category ? *class_list : 0,
5721 ATR_NONE, buf, selected);
5722 ++class_list;
5723 if (category > 0) {
5724 ++next_accelerator;
5725 if (next_accelerator == ('z' + 1))
5726 next_accelerator = 'A';
5727 if (next_accelerator == ('Z' + 1))
5728 break;
5731 end_menu(win, prompt);
5732 n = select_menu(win, way ? PICK_ANY : PICK_ONE, &pick_list);
5733 destroy_nhwindow(win);
5734 if (n > 0) {
5735 for (i = 0; i < n; ++i)
5736 *class_select++ = (char) pick_list[i].item.a_int;
5737 free((genericptr_t) pick_list);
5738 ret = n;
5739 } else if (n == -1) {
5740 class_select = eos(class_select);
5741 ret = -1;
5742 } else
5743 ret = 0;
5744 *class_select = '\0';
5745 return ret;
5748 struct wc_Opt wc_options[] = { { "ascii_map", WC_ASCII_MAP },
5749 { "color", WC_COLOR },
5750 { "eight_bit_tty", WC_EIGHT_BIT_IN },
5751 { "hilite_pet", WC_HILITE_PET },
5752 { "popup_dialog", WC_POPUP_DIALOG },
5753 { "player_selection", WC_PLAYER_SELECTION },
5754 { "preload_tiles", WC_PRELOAD_TILES },
5755 { "tiled_map", WC_TILED_MAP },
5756 { "tile_file", WC_TILE_FILE },
5757 { "tile_width", WC_TILE_WIDTH },
5758 { "tile_height", WC_TILE_HEIGHT },
5759 { "use_inverse", WC_INVERSE },
5760 { "align_message", WC_ALIGN_MESSAGE },
5761 { "align_status", WC_ALIGN_STATUS },
5762 { "font_map", WC_FONT_MAP },
5763 { "font_menu", WC_FONT_MENU },
5764 { "font_message", WC_FONT_MESSAGE },
5765 #if 0
5766 {"perm_invent", WC_PERM_INVENT},
5767 #endif
5768 { "font_size_map", WC_FONTSIZ_MAP },
5769 { "font_size_menu", WC_FONTSIZ_MENU },
5770 { "font_size_message", WC_FONTSIZ_MESSAGE },
5771 { "font_size_status", WC_FONTSIZ_STATUS },
5772 { "font_size_text", WC_FONTSIZ_TEXT },
5773 { "font_status", WC_FONT_STATUS },
5774 { "font_text", WC_FONT_TEXT },
5775 { "map_mode", WC_MAP_MODE },
5776 { "scroll_amount", WC_SCROLL_AMOUNT },
5777 { "scroll_margin", WC_SCROLL_MARGIN },
5778 { "splash_screen", WC_SPLASH_SCREEN },
5779 { "vary_msgcount", WC_VARY_MSGCOUNT },
5780 { "windowcolors", WC_WINDOWCOLORS },
5781 { "mouse_support", WC_MOUSE_SUPPORT },
5782 { (char *) 0, 0L } };
5784 struct wc_Opt wc2_options[] = { { "fullscreen", WC2_FULLSCREEN },
5785 { "softkeyboard", WC2_SOFTKEYBOARD },
5786 { "wraptext", WC2_WRAPTEXT },
5787 { "use_darkgray", WC2_DARKGRAY },
5788 #ifdef STATUS_VIA_WINDOWPORT
5789 { "hilite_status", WC2_HILITE_STATUS },
5790 #endif
5791 { (char *) 0, 0L } };
5794 * If a port wants to change or ensure that the SET_IN_SYS,
5795 * SET_IN_FILE, DISP_IN_GAME, or SET_IN_GAME status of an option is
5796 * correct (for controlling its display in the option menu) call
5797 * set_option_mod_status()
5798 * with the appropriate second argument.
5800 void
5801 set_option_mod_status(optnam, status)
5802 const char *optnam;
5803 int status;
5805 int k;
5807 if (SET__IS_VALUE_VALID(status)) {
5808 impossible("set_option_mod_status: status out of range %d.", status);
5809 return;
5811 for (k = 0; boolopt[k].name; k++) {
5812 if (!strncmpi(boolopt[k].name, optnam, strlen(optnam))) {
5813 boolopt[k].optflags = status;
5814 return;
5817 for (k = 0; compopt[k].name; k++) {
5818 if (!strncmpi(compopt[k].name, optnam, strlen(optnam))) {
5819 compopt[k].optflags = status;
5820 return;
5826 * You can set several wc_options in one call to
5827 * set_wc_option_mod_status() by setting
5828 * the appropriate bits for each option that you
5829 * are setting in the optmask argument
5830 * prior to calling.
5831 * example: set_wc_option_mod_status(WC_COLOR|WC_SCROLL_MARGIN,
5832 * SET_IN_GAME);
5834 void
5835 set_wc_option_mod_status(optmask, status)
5836 unsigned long optmask;
5837 int status;
5839 int k = 0;
5841 if (SET__IS_VALUE_VALID(status)) {
5842 impossible("set_wc_option_mod_status: status out of range %d.",
5843 status);
5844 return;
5846 while (wc_options[k].wc_name) {
5847 if (optmask & wc_options[k].wc_bit) {
5848 set_option_mod_status(wc_options[k].wc_name, status);
5850 k++;
5854 STATIC_OVL boolean
5855 is_wc_option(optnam)
5856 const char *optnam;
5858 int k = 0;
5860 while (wc_options[k].wc_name) {
5861 if (strcmp(wc_options[k].wc_name, optnam) == 0)
5862 return TRUE;
5863 k++;
5865 return FALSE;
5868 STATIC_OVL boolean
5869 wc_supported(optnam)
5870 const char *optnam;
5872 int k = 0;
5874 while (wc_options[k].wc_name) {
5875 if (!strcmp(wc_options[k].wc_name, optnam)
5876 && (windowprocs.wincap & wc_options[k].wc_bit))
5877 return TRUE;
5878 k++;
5880 return FALSE;
5884 * You can set several wc2_options in one call to
5885 * set_wc2_option_mod_status() by setting
5886 * the appropriate bits for each option that you
5887 * are setting in the optmask argument
5888 * prior to calling.
5889 * example:
5890 * set_wc2_option_mod_status(WC2_FULLSCREEN|WC2_SOFTKEYBOARD|WC2_WRAPTEXT,
5891 * SET_IN_FILE);
5894 void
5895 set_wc2_option_mod_status(optmask, status)
5896 unsigned long optmask;
5897 int status;
5899 int k = 0;
5901 if (SET__IS_VALUE_VALID(status)) {
5902 impossible("set_wc2_option_mod_status: status out of range %d.",
5903 status);
5904 return;
5906 while (wc2_options[k].wc_name) {
5907 if (optmask & wc2_options[k].wc_bit) {
5908 set_option_mod_status(wc2_options[k].wc_name, status);
5910 k++;
5914 STATIC_OVL boolean
5915 is_wc2_option(optnam)
5916 const char *optnam;
5918 int k = 0;
5920 while (wc2_options[k].wc_name) {
5921 if (strcmp(wc2_options[k].wc_name, optnam) == 0)
5922 return TRUE;
5923 k++;
5925 return FALSE;
5928 STATIC_OVL boolean
5929 wc2_supported(optnam)
5930 const char *optnam;
5932 int k = 0;
5934 while (wc2_options[k].wc_name) {
5935 if (!strcmp(wc2_options[k].wc_name, optnam)
5936 && (windowprocs.wincap2 & wc2_options[k].wc_bit))
5937 return TRUE;
5938 k++;
5940 return FALSE;
5943 STATIC_OVL void
5944 wc_set_font_name(opttype, fontname)
5945 int opttype;
5946 char *fontname;
5948 char **fn = (char **) 0;
5950 if (!fontname)
5951 return;
5952 switch (opttype) {
5953 case MAP_OPTION:
5954 fn = &iflags.wc_font_map;
5955 break;
5956 case MESSAGE_OPTION:
5957 fn = &iflags.wc_font_message;
5958 break;
5959 case TEXT_OPTION:
5960 fn = &iflags.wc_font_text;
5961 break;
5962 case MENU_OPTION:
5963 fn = &iflags.wc_font_menu;
5964 break;
5965 case STATUS_OPTION:
5966 fn = &iflags.wc_font_status;
5967 break;
5968 default:
5969 return;
5971 if (fn) {
5972 if (*fn)
5973 free((genericptr_t) *fn);
5974 *fn = dupstr(fontname);
5976 return;
5979 STATIC_OVL int
5980 wc_set_window_colors(op)
5981 char *op;
5983 /* syntax:
5984 * menu white/black message green/yellow status white/blue text
5985 * white/black
5987 int j;
5988 char buf[BUFSZ];
5989 char *wn, *tfg, *tbg, *newop;
5990 static const char *wnames[] = { "menu", "message", "status", "text" };
5991 static const char *shortnames[] = { "mnu", "msg", "sts", "txt" };
5992 static char **fgp[] = { &iflags.wc_foregrnd_menu,
5993 &iflags.wc_foregrnd_message,
5994 &iflags.wc_foregrnd_status,
5995 &iflags.wc_foregrnd_text };
5996 static char **bgp[] = { &iflags.wc_backgrnd_menu,
5997 &iflags.wc_backgrnd_message,
5998 &iflags.wc_backgrnd_status,
5999 &iflags.wc_backgrnd_text };
6001 Strcpy(buf, op);
6002 newop = mungspaces(buf);
6003 while (newop && *newop) {
6004 wn = tfg = tbg = (char *) 0;
6006 /* until first non-space in case there's leading spaces - before
6007 * colorname*/
6008 if (*newop == ' ')
6009 newop++;
6010 if (*newop)
6011 wn = newop;
6012 else
6013 return 0;
6015 /* until first space - colorname*/
6016 while (*newop && *newop != ' ')
6017 newop++;
6018 if (*newop)
6019 *newop = '\0';
6020 else
6021 return 0;
6022 newop++;
6024 /* until first non-space - before foreground*/
6025 if (*newop == ' ')
6026 newop++;
6027 if (*newop)
6028 tfg = newop;
6029 else
6030 return 0;
6032 /* until slash - foreground */
6033 while (*newop && *newop != '/')
6034 newop++;
6035 if (*newop)
6036 *newop = '\0';
6037 else
6038 return 0;
6039 newop++;
6041 /* until first non-space (in case there's leading space after slash) -
6042 * before background */
6043 if (*newop == ' ')
6044 newop++;
6045 if (*newop)
6046 tbg = newop;
6047 else
6048 return 0;
6050 /* until first space - background */
6051 while (*newop && *newop != ' ')
6052 newop++;
6053 if (*newop)
6054 *newop++ = '\0';
6056 for (j = 0; j < 4; ++j) {
6057 if (!strcmpi(wn, wnames[j]) || !strcmpi(wn, shortnames[j])) {
6058 if (tfg && !strstri(tfg, " ")) {
6059 if (*fgp[j])
6060 free((genericptr_t) *fgp[j]);
6061 *fgp[j] = dupstr(tfg);
6063 if (tbg && !strstri(tbg, " ")) {
6064 if (*bgp[j])
6065 free((genericptr_t) *bgp[j]);
6066 *bgp[j] = dupstr(tbg);
6068 break;
6072 return 1;
6075 /* set up for wizard mode if player or save file has requested to it;
6076 called from port-specific startup code to handle `nethack -D' or
6077 OPTIONS=playmode:debug, or from dorecover()'s restgamestate() if
6078 restoring a game which was saved in wizard mode */
6079 void
6080 set_playmode()
6082 if (wizard) {
6083 if (authorize_wizard_mode())
6084 Strcpy(plname, "wizard");
6085 else
6086 wizard = FALSE; /* not allowed or not available */
6087 /* force explore mode if we didn't make it into wizard mode */
6088 discover = !wizard;
6089 iflags.deferred_X = FALSE;
6091 /* don't need to do anything special for explore mode or normal play */
6094 #endif /* OPTION_LISTS_ONLY */
6096 /*options.c*/