NHDT->ANH, nethack->anethack, nhdat->anhdat
[aNetHack.git] / src / options.c
blob872a46473ae1805abbdab870ea5ff5c7d5202b9f
1 /* aNetHack 0.0.1 options.c $ANH-Date: 1470357737 2016/08/05 00:42:17 $ $ANH-Branch: master $:$ANH-Revision: 1.279 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* aNetHack 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 anethack 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 anethack'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 anethack
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 { "status_updates", &iflags.status_updates, TRUE, DISP_IN_GAME },
212 { "tiled_map", &iflags.wc_tiled_map, PREFER_TILED, DISP_IN_GAME }, /*WC*/
213 { "time", &flags.time, FALSE, SET_IN_GAME },
214 #ifdef TIMED_DELAY
215 { "timed_delay", &flags.nap, TRUE, SET_IN_GAME },
216 #else
217 { "timed_delay", (boolean *) 0, FALSE, SET_IN_GAME },
218 #endif
219 { "tombstone", &flags.tombstone, TRUE, SET_IN_GAME },
220 { "toptenwin", &iflags.toptenwin, FALSE, SET_IN_GAME },
221 { "travel", &flags.travelcmd, TRUE, SET_IN_GAME },
222 { "use_darkgray", &iflags.wc2_darkgray, TRUE, SET_IN_FILE },
223 #ifdef WIN32
224 { "use_inverse", &iflags.wc_inverse, TRUE, SET_IN_GAME }, /*WC*/
225 #else
226 { "use_inverse", &iflags.wc_inverse, FALSE, SET_IN_GAME }, /*WC*/
227 #endif
228 { "verbose", &flags.verbose, TRUE, SET_IN_GAME },
229 #ifdef TTY_TILES_ESCCODES
230 { "vt_tiledata", &iflags.vt_tiledata, FALSE, SET_IN_FILE },
231 #else
232 { "vt_tiledata", (boolean *) 0, FALSE, SET_IN_FILE },
233 #endif
234 { "whatis_menu", &iflags.getloc_usemenu, FALSE, SET_IN_GAME },
235 { "whatis_inview", &iflags.getloc_limitview, FALSE, SET_IN_GAME },
236 { "wizweight", &iflags.wizweight, FALSE, SET_IN_WIZGAME },
237 { "wraptext", &iflags.wc2_wraptext, FALSE, SET_IN_GAME },
238 #ifdef ZEROCOMP
239 { "zerocomp", &iflags.zerocomp,
240 #if defined(COMPRESS) || defined(ZLIB_COMP)
241 FALSE,
242 #else
243 TRUE,
244 #endif
245 DISP_IN_GAME },
246 #endif
247 { (char *) 0, (boolean *) 0, FALSE, 0 }
250 /* compound options, for option_help() and external programs like Amiga
251 * frontend */
252 static struct Comp_Opt {
253 const char *name, *descr;
254 int size; /* for frontends and such allocating space --
255 * usually allowed size of data in game, but
256 * occasionally maximum reasonable size for
257 * typing when game maintains information in
258 * a different format */
259 int optflags;
260 } compopt[] = {
261 { "align", "your starting alignment (lawful, neutral, or chaotic)", 8,
262 DISP_IN_GAME },
263 { "align_message", "message window alignment", 20, DISP_IN_GAME }, /*WC*/
264 { "align_status", "status window alignment", 20, DISP_IN_GAME }, /*WC*/
265 { "altkeyhandler", "alternate key handler", 20, DISP_IN_GAME },
266 #ifdef BACKWARD_COMPAT
267 { "boulder", "deprecated (use S_boulder in sym file instead)", 1,
268 SET_IN_FILE },
269 #endif
270 { "catname", "the name of your (first) cat (e.g., catname:Tabby)",
271 PL_PSIZ, DISP_IN_GAME },
272 { "disclose", "the kinds of information to disclose at end of game",
273 sizeof(flags.end_disclose) * 2, SET_IN_GAME },
274 { "dogname", "the name of your (first) dog (e.g., dogname:Fang)", PL_PSIZ,
275 DISP_IN_GAME },
276 { "dungeon", "the symbols to use in drawing the dungeon map",
277 MAXDCHARS + 1, SET_IN_FILE },
278 { "effects", "the symbols to use in drawing special effects",
279 MAXECHARS + 1, SET_IN_FILE },
280 { "font_map", "the font to use in the map window", 40,
281 DISP_IN_GAME }, /*WC*/
282 { "font_menu", "the font to use in menus", 40, DISP_IN_GAME }, /*WC*/
283 { "font_message", "the font to use in the message window", 40,
284 DISP_IN_GAME }, /*WC*/
285 { "font_size_map", "the size of the map font", 20, DISP_IN_GAME }, /*WC*/
286 { "font_size_menu", "the size of the menu font", 20,
287 DISP_IN_GAME }, /*WC*/
288 { "font_size_message", "the size of the message font", 20,
289 DISP_IN_GAME }, /*WC*/
290 { "font_size_status", "the size of the status font", 20,
291 DISP_IN_GAME }, /*WC*/
292 { "font_size_text", "the size of the text font", 20,
293 DISP_IN_GAME }, /*WC*/
294 { "font_status", "the font to use in status window", 40,
295 DISP_IN_GAME }, /*WC*/
296 { "font_text", "the font to use in text windows", 40,
297 DISP_IN_GAME }, /*WC*/
298 { "fruit", "the name of a fruit you enjoy eating", PL_FSIZ, SET_IN_GAME },
299 { "gender", "your starting gender (male or female)", 8, DISP_IN_GAME },
300 { "horsename", "the name of your (first) horse (e.g., horsename:Silver)",
301 PL_PSIZ, DISP_IN_GAME },
302 { "map_mode", "map display mode under Windows", 20, DISP_IN_GAME }, /*WC*/
303 { "menustyle", "user interface for object selection", MENUTYPELEN,
304 SET_IN_GAME },
305 { "menu_deselect_all", "deselect all items in a menu", 4, SET_IN_FILE },
306 { "menu_deselect_page", "deselect all items on this page of a menu", 4,
307 SET_IN_FILE },
308 { "menu_first_page", "jump to the first page in a menu", 4, SET_IN_FILE },
309 { "menu_headings", "text attribute for menu headings", 9, SET_IN_GAME },
310 { "menu_invert_all", "invert all items in a menu", 4, SET_IN_FILE },
311 { "menu_invert_page", "invert all items on this page of a menu", 4,
312 SET_IN_FILE },
313 { "menu_last_page", "jump to the last page in a menu", 4, SET_IN_FILE },
314 { "menu_next_page", "goto the next menu page", 4, SET_IN_FILE },
315 { "menu_previous_page", "goto the previous menu page", 4, SET_IN_FILE },
316 { "menu_search", "search for a menu item", 4, SET_IN_FILE },
317 { "menu_select_all", "select all items in a menu", 4, SET_IN_FILE },
318 { "menu_select_page", "select all items on this page of a menu", 4,
319 SET_IN_FILE },
320 { "monsters", "the symbols to use for monsters", MAXMCLASSES,
321 SET_IN_FILE },
322 { "msghistory", "number of top line messages to save", 5, DISP_IN_GAME },
323 #ifdef TTY_GRAPHICS
324 { "msg_window", "the type of message window required", 1, SET_IN_GAME },
325 #else
326 { "msg_window", "the type of message window required", 1, SET_IN_FILE },
327 #endif
328 { "name", "your character's name (e.g., name:Merlin-W)", PL_NSIZ,
329 DISP_IN_GAME },
330 { "number_pad", "use the number pad for movement", 1, SET_IN_GAME },
331 { "objects", "the symbols to use for objects", MAXOCLASSES, SET_IN_FILE },
332 { "packorder", "the inventory order of the items in your pack",
333 MAXOCLASSES, SET_IN_GAME },
334 #ifdef CHANGE_COLOR
335 { "palette",
336 #ifndef WIN32
337 "palette (00c/880/-fff is blue/yellow/reverse white)", 15,
338 SET_IN_GAME },
339 #else
340 "palette (adjust an RGB color in palette (color-R-G-B)", 15,
341 SET_IN_FILE },
342 #endif
343 #if defined(MAC)
344 { "hicolor", "same as palette, only order is reversed", 15, SET_IN_FILE },
345 #endif
346 #endif
347 { "paranoid_confirmation", "extra prompting in certain situations", 28,
348 SET_IN_GAME },
349 { "pettype", "your preferred initial pet type", 4, DISP_IN_GAME },
350 { "pickup_burden", "maximum burden picked up before prompt", 20,
351 SET_IN_GAME },
352 { "pickup_types", "types of objects to pick up automatically",
353 MAXOCLASSES, SET_IN_GAME },
354 { "pile_limit", "threshold for \"there are many objects here\"", 24,
355 SET_IN_GAME },
356 { "playmode", "normal play, non-scoring explore mode, or debug mode", 8,
357 DISP_IN_GAME },
358 { "player_selection", "choose character via dialog or prompts", 12,
359 DISP_IN_GAME },
360 { "race", "your starting race (e.g., Human, Elf)", PL_CSIZ,
361 DISP_IN_GAME },
362 { "role", "your starting role (e.g., Barbarian, Valkyrie)", PL_CSIZ,
363 DISP_IN_GAME },
364 { "runmode", "display frequency when `running' or `travelling'",
365 sizeof "teleport", SET_IN_GAME },
366 { "scores", "the parts of the score list you wish to see", 32,
367 SET_IN_GAME },
368 { "scroll_amount", "amount to scroll map when scroll_margin is reached",
369 20, DISP_IN_GAME }, /*WC*/
370 { "scroll_margin", "scroll map when this far from the edge", 20,
371 DISP_IN_GAME }, /*WC*/
372 { "sortloot", "sort object selection lists by description", 4,
373 SET_IN_GAME },
374 #ifdef MSDOS
375 { "soundcard", "type of sound card to use", 20, SET_IN_FILE },
376 #endif
377 { "symset", "load a set of display symbols from the symbols file", 70,
378 SET_IN_GAME },
379 { "roguesymset",
380 "load a set of rogue display symbols from the symbols file", 70,
381 SET_IN_GAME },
382 #ifdef WIN32
383 { "subkeyvalue", "override keystroke value", 7, SET_IN_FILE },
384 #endif
385 { "suppress_alert", "suppress alerts about version-specific features", 8,
386 SET_IN_GAME },
387 { "tile_width", "width of tiles", 20, DISP_IN_GAME }, /*WC*/
388 { "tile_height", "height of tiles", 20, DISP_IN_GAME }, /*WC*/
389 { "tile_file", "name of tile file", 70, DISP_IN_GAME }, /*WC*/
390 { "traps", "the symbols to use in drawing traps", MAXTCHARS + 1,
391 SET_IN_FILE },
392 { "vary_msgcount", "show more old messages at a time", 20,
393 DISP_IN_GAME }, /*WC*/
394 #ifdef MSDOS
395 { "video", "method of video updating", 20, SET_IN_FILE },
396 #endif
397 #ifdef VIDEOSHADES
398 { "videocolors", "color mappings for internal screen routines", 40,
399 DISP_IN_GAME },
400 { "videoshades", "gray shades to map to black/gray/white", 32,
401 DISP_IN_GAME },
402 #endif
403 { "whatis_coord", "show coordinates when auto-describing cursor position",
404 1, SET_IN_GAME },
405 { "windowcolors", "the foreground/background colors of windows", /*WC*/
406 80, DISP_IN_GAME },
407 { "windowtype", "windowing system to use", WINTYPELEN, DISP_IN_GAME },
408 #ifdef WINCHAIN
409 { "windowchain", "window processor to use", WINTYPELEN, SET_IN_SYS },
410 #endif
411 #ifdef BACKWARD_COMPAT
412 { "DECgraphics", "load DECGraphics display symbols", 70, SET_IN_FILE },
413 { "IBMgraphics", "load IBMGraphics display symbols", 70, SET_IN_FILE },
414 #ifdef MAC_GRAPHICS_ENV
415 { "Macgraphics", "load MACGraphics display symbols", 70, SET_IN_FILE },
416 #endif
417 #endif
418 { (char *) 0, (char *) 0, 0, 0 }
421 #ifdef OPTION_LISTS_ONLY
422 #undef static
424 #else /* use rest of file */
426 extern char configfile[]; /* for messages */
428 extern struct symparse loadsyms[];
429 static boolean need_redraw; /* for doset() */
431 #if defined(TOS) && defined(TEXTCOLOR)
432 extern boolean colors_changed; /* in tos.c */
433 #endif
435 #ifdef VIDEOSHADES
436 extern char *shade[3]; /* in sys/msdos/video.c */
437 extern char ttycolors[CLR_MAX]; /* in sys/msdos/video.c */
438 #endif
440 static char def_inv_order[MAXOCLASSES] = {
441 COIN_CLASS, AMULET_CLASS, WEAPON_CLASS, ARMOR_CLASS, FOOD_CLASS,
442 SCROLL_CLASS, SPBOOK_CLASS, POTION_CLASS, RING_CLASS, WAND_CLASS,
443 TOOL_CLASS, GEM_CLASS, ROCK_CLASS, BALL_CLASS, CHAIN_CLASS, 0,
447 * Default menu manipulation command accelerators. These may _not_ be:
449 * + a number - reserved for counts
450 * + an upper or lower case US ASCII letter - used for accelerators
451 * + ESC - reserved for escaping the menu
452 * + NULL, CR or LF - reserved for commiting the selection(s). NULL
453 * is kind of odd, but the tty's xwaitforspace() will return it if
454 * someone hits a <ret>.
455 * + a default object class symbol - used for object class accelerators
457 * Standard letters (for now) are:
459 * < back 1 page
460 * > forward 1 page
461 * ^ first page
462 * | last page
463 * : search
465 * page all
466 * , select .
467 * \ deselect -
468 * ~ invert @
470 * The command name list is duplicated in the compopt array.
472 typedef struct {
473 const char *name;
474 char cmd;
475 const char *desc;
476 } menu_cmd_t;
478 static const menu_cmd_t default_menu_cmd_info[] = {
479 { "menu_first_page", MENU_FIRST_PAGE, "Go to first page" },
480 { "menu_last_page", MENU_LAST_PAGE, "Go to last page" },
481 { "menu_next_page", MENU_NEXT_PAGE, "Go to next page" },
482 { "menu_previous_page", MENU_PREVIOUS_PAGE, "Go to previous page" },
483 { "menu_select_all", MENU_SELECT_ALL, "Select all items" },
484 { "menu_deselect_all", MENU_UNSELECT_ALL, "Unselect all items" },
485 { "menu_invert_all", MENU_INVERT_ALL, "Insert selection" },
486 { "menu_select_page", MENU_SELECT_PAGE, "Select items in current page" },
487 { "menu_deselect_page", MENU_UNSELECT_PAGE, "Unselect items in current page" },
488 { "menu_invert_page", MENU_INVERT_PAGE, "Invert current page selection" },
489 { "menu_search", MENU_SEARCH, "Search and toggle matching items" },
493 * Allow the user to map incoming characters to various menu commands.
494 * The accelerator list must be a valid C string.
496 #define MAX_MENU_MAPPED_CMDS 32 /* some number */
497 char mapped_menu_cmds[MAX_MENU_MAPPED_CMDS + 1]; /* exported */
498 static char mapped_menu_op[MAX_MENU_MAPPED_CMDS + 1];
499 static short n_menu_mapped = 0;
501 static boolean initial, from_file;
503 STATIC_DCL void FDECL(nmcpy, (char *, const char *, int));
504 STATIC_DCL void FDECL(escapes, (const char *, char *));
505 STATIC_DCL void FDECL(rejectoption, (const char *));
506 STATIC_DCL void FDECL(badoptmsg, (const char *, const char *));
507 STATIC_DCL void FDECL(badoption, (const char *));
508 STATIC_DCL char *FDECL(string_for_opt, (char *, BOOLEAN_P));
509 STATIC_DCL char *FDECL(string_for_env_opt, (const char *, char *, BOOLEAN_P));
510 STATIC_DCL void FDECL(bad_negation, (const char *, BOOLEAN_P));
511 STATIC_DCL int FDECL(change_inv_order, (char *));
512 STATIC_DCL void FDECL(warning_opts, (char *, const char *));
513 STATIC_DCL int FDECL(feature_alert_opts, (char *, const char *));
514 STATIC_DCL boolean FDECL(duplicate_opt_detection, (const char *, int));
515 STATIC_DCL void FDECL(complain_about_duplicate, (const char *, int));
517 STATIC_DCL const char *FDECL(attr2attrname, (int));
518 STATIC_DCL int NDECL(query_color);
519 STATIC_DCL int FDECL(query_attr, (const char *));
520 STATIC_DCL const char * FDECL(msgtype2name, (int));
521 STATIC_DCL int NDECL(query_msgtype);
522 STATIC_DCL boolean FDECL(msgtype_add, (int, char *));
523 STATIC_DCL void FDECL(free_one_msgtype, (int));
524 STATIC_DCL int NDECL(msgtype_count);
525 STATIC_DCL boolean FDECL(add_menu_coloring_parsed, (char *, int, int));
526 STATIC_DCL void FDECL(free_one_menu_coloring, (int));
527 STATIC_DCL int NDECL(count_menucolors);
528 STATIC_DCL boolean FDECL(parse_role_opts, (BOOLEAN_P, const char *,
529 char *, char **));
531 STATIC_DCL void FDECL(oc_to_str, (char *, char *));
532 STATIC_DCL void FDECL(doset_add_menu, (winid, const char *, int));
533 STATIC_DCL void FDECL(opts_add_others, (winid, const char *, int,
534 char *, int));
535 STATIC_DCL int FDECL(handle_add_list_remove, (const char *, int));
536 STATIC_DCL boolean FDECL(special_handling, (const char *,
537 BOOLEAN_P, BOOLEAN_P));
538 STATIC_DCL const char *FDECL(get_compopt_value, (const char *, char *));
539 STATIC_DCL void FDECL(remove_autopickup_exception,
540 (struct autopickup_exception *));
541 STATIC_DCL int FDECL(count_ape_maps, (int *, int *));
543 STATIC_DCL boolean FDECL(is_wc_option, (const char *));
544 STATIC_DCL boolean FDECL(wc_supported, (const char *));
545 STATIC_DCL boolean FDECL(is_wc2_option, (const char *));
546 STATIC_DCL boolean FDECL(wc2_supported, (const char *));
547 STATIC_DCL void FDECL(wc_set_font_name, (int, char *));
548 STATIC_DCL int FDECL(wc_set_window_colors, (char *));
550 void
551 reglyph_darkroom()
553 xchar x, y;
555 for (x = 0; x < COLNO; x++)
556 for (y = 0; y < ROWNO; y++) {
557 struct rm *lev = &levl[x][y];
559 if (!flags.dark_room || !iflags.use_color
560 || Is_rogue_level(&u.uz)) {
561 if (lev->glyph == cmap_to_glyph(S_darkroom))
562 lev->glyph = lev->waslit ? cmap_to_glyph(S_room)
563 : cmap_to_glyph(S_stone);
564 } else {
565 if (lev->glyph == cmap_to_glyph(S_room) && lev->seenv
566 && lev->waslit && !cansee(x, y))
567 lev->glyph = cmap_to_glyph(S_darkroom);
568 else if (lev->glyph == cmap_to_glyph(S_stone)
569 && lev->typ == ROOM && lev->seenv && !cansee(x, y))
570 lev->glyph = cmap_to_glyph(S_darkroom);
573 if (flags.dark_room && iflags.use_color)
574 showsyms[S_darkroom] = showsyms[S_room];
575 else
576 showsyms[S_darkroom] = showsyms[S_stone];
579 /* check whether a user-supplied option string is a proper leading
580 substring of a particular option name; option string might have
581 a colon or equals sign and arbitrary value appended to it */
582 boolean
583 match_optname(user_string, opt_name, min_length, val_allowed)
584 const char *user_string, *opt_name;
585 int min_length;
586 boolean val_allowed;
588 int len = (int) strlen(user_string);
590 if (val_allowed) {
591 const char *p = index(user_string, ':'),
592 *q = index(user_string, '=');
594 if (!p || (q && q < p))
595 p = q;
596 if (p) {
597 /* 'user_string' hasn't necessarily been through mungspaces()
598 so might have tabs or consecutive spaces */
599 while (p > user_string && isspace((uchar) *(p - 1)))
600 p--;
601 len = (int) (p - user_string);
605 return (boolean) (len >= min_length
606 && !strncmpi(opt_name, user_string, len));
609 /* most environment variables will eventually be printed in an error
610 * message if they don't work, and most error message paths go through
611 * BUFSZ buffers, which could be overflowed by a maliciously long
612 * environment variable. If a variable can legitimately be long, or
613 * if it's put in a smaller buffer, the responsible code will have to
614 * bounds-check itself.
616 char *
617 nh_getenv(ev)
618 const char *ev;
620 char *getev = getenv(ev);
622 if (getev && strlen(getev) <= (BUFSZ / 2))
623 return getev;
624 else
625 return (char *) 0;
628 /* process options, possibly including SYSCF */
629 void
630 initoptions()
632 initoptions_init();
633 #ifdef SYSCF
634 /* someday there may be other SYSCF alternatives besides text file */
635 #ifdef SYSCF_FILE
636 /* If SYSCF_FILE is specified, it _must_ exist... */
637 assure_syscf_file();
638 /* ... and _must_ parse correctly. */
639 if (!read_config_file(SYSCF_FILE, SET_IN_SYS)) {
640 raw_printf("Error(s) found in SYSCF_FILE, quitting.");
641 terminate(EXIT_FAILURE);
644 * TODO [maybe]: parse the sysopt entries which are space-separated
645 * lists of usernames into arrays with one name per element.
647 #endif
648 #endif
649 initoptions_finish();
652 void
653 initoptions_init()
655 #if defined(UNIX) || defined(VMS)
656 char *opts;
657 #endif
658 int i;
660 /* set up the command parsing */
661 reset_commands(TRUE); /* init */
663 /* initialize the random number generator */
664 setrandom();
666 /* for detection of configfile options specified multiple times */
667 iflags.opt_booldup = iflags.opt_compdup = (int *) 0;
669 for (i = 0; boolopt[i].name; i++) {
670 if (boolopt[i].addr)
671 *(boolopt[i].addr) = boolopt[i].initvalue;
673 #if defined(COMPRESS) || defined(ZLIB_COMP)
674 set_savepref("externalcomp");
675 set_restpref("externalcomp");
676 #ifdef RLECOMP
677 set_savepref("!rlecomp");
678 set_restpref("!rlecomp");
679 #endif
680 #else
681 #ifdef ZEROCOMP
682 set_savepref("zerocomp");
683 set_restpref("zerocomp");
684 #endif
685 #ifdef RLECOMP
686 set_savepref("rlecomp");
687 set_restpref("rlecomp");
688 #endif
689 #endif
690 #ifdef SYSFLAGS
691 Strcpy(sysflags.sysflagsid, "sysflags");
692 sysflags.sysflagsid[9] = (char) sizeof(struct sysflag);
693 #endif
694 flags.end_own = FALSE;
695 flags.end_top = 3;
696 flags.end_around = 2;
697 flags.paranoia_bits = PARANOID_PRAY; /* old prayconfirm=TRUE */
698 flags.pile_limit = PILE_LIMIT_DFLT; /* 5 */
699 flags.runmode = RUN_LEAP;
700 iflags.msg_history = 20;
701 #ifdef TTY_GRAPHICS
702 iflags.prevmsg_window = 's';
703 #endif
704 iflags.menu_headings = ATR_INVERSE;
705 iflags.getpos_coords = GPCOORDS_NONE;
707 /* hero's role, race, &c haven't been chosen yet */
708 flags.initrole = flags.initrace = flags.initgend = flags.initalign =
709 ROLE_NONE;
711 /* Set the default monster and object class symbols. */
712 init_symbols();
713 for (i = 0; i < WARNCOUNT; i++)
714 warnsyms[i] = def_warnsyms[i].sym;
715 iflags.bouldersym = 0;
717 iflags.travelcc.x = iflags.travelcc.y = -1;
719 /* assert( sizeof flags.inv_order == sizeof def_inv_order ); */
720 (void) memcpy((genericptr_t) flags.inv_order,
721 (genericptr_t) def_inv_order, sizeof flags.inv_order);
722 flags.pickup_types[0] = '\0';
723 flags.pickup_burden = MOD_ENCUMBER;
724 flags.sortloot = 'l'; /* sort only loot by default */
726 for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++)
727 flags.end_disclose[i] = DISCLOSE_PROMPT_DEFAULT_NO;
728 switch_symbols(FALSE); /* set default characters */
729 #if defined(UNIX) && defined(TTY_GRAPHICS)
731 * Set defaults for some options depending on what we can
732 * detect about the environment's capabilities.
733 * This has to be done after the global initialization above
734 * and before reading user-specific initialization via
735 * config file/environment variable below.
737 /* this detects the IBM-compatible console on most 386 boxes */
738 if ((opts = nh_getenv("TERM")) && !strncmp(opts, "AT", 2)) {
739 if (!symset[PRIMARY].name)
740 load_symset("IBMGraphics", PRIMARY);
741 if (!symset[ROGUESET].name)
742 load_symset("RogueIBM", ROGUESET);
743 switch_symbols(TRUE);
744 #ifdef TEXTCOLOR
745 iflags.use_color = TRUE;
746 #endif
748 #endif /* UNIX && TTY_GRAPHICS */
749 #if defined(UNIX) || defined(VMS)
750 #ifdef TTY_GRAPHICS
751 /* detect whether a "vt" terminal can handle alternate charsets */
752 if ((opts = nh_getenv("TERM"))
753 /* [could also check "xterm" which emulates vtXXX by default] */
754 && !strncmpi(opts, "vt", 2)
755 && AS && AE && index(AS, '\016') && index(AE, '\017')) {
756 if (!symset[PRIMARY].name)
757 load_symset("DECGraphics", PRIMARY);
758 switch_symbols(TRUE);
760 #endif
761 #endif /* UNIX || VMS */
763 #ifdef MAC_GRAPHICS_ENV
764 if (!symset[PRIMARY].name)
765 load_symset("MACGraphics", PRIMARY);
766 switch_symbols(TRUE);
767 #endif /* MAC_GRAPHICS_ENV */
768 flags.menu_style = MENU_FULL;
770 /* since this is done before init_objects(), do partial init here */
771 objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD;
772 nmcpy(pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ);
775 void
776 initoptions_finish()
778 #ifndef MAC
779 char *opts = getenv("ANETHACKOPTIONS");
781 if (!opts)
782 opts = getenv("HACKOPTIONS");
783 if (opts) {
784 if (*opts == '/' || *opts == '\\' || *opts == '@') {
785 if (*opts == '@')
786 opts++; /* @filename */
787 /* looks like a filename */
788 if (strlen(opts) < BUFSZ / 2)
789 read_config_file(opts, SET_IN_FILE);
790 } else {
791 read_config_file((char *) 0, SET_IN_FILE);
792 /* let the total length of options be long;
793 * parseoptions() will check each individually
795 parseoptions(opts, TRUE, FALSE);
797 } else
798 #endif
799 read_config_file((char *) 0, SET_IN_FILE);
801 (void) fruitadd(pl_fruit, (struct fruit *) 0);
803 * Remove "slime mold" from list of object names. This will
804 * prevent it from being wished unless it's actually present
805 * as a named (or default) fruit. Wishing for "fruit" will
806 * result in the player's preferred fruit [better than "\033"].
808 obj_descr[SLIME_MOLD].oc_name = "fruit";
810 if (iflags.bouldersym)
811 update_bouldersym();
812 reglyph_darkroom();
813 return;
816 STATIC_OVL void
817 nmcpy(dest, src, maxlen)
818 char *dest;
819 const char *src;
820 int maxlen;
822 int count;
824 for (count = 1; count < maxlen; count++) {
825 if (*src == ',' || *src == '\0')
826 break; /*exit on \0 terminator*/
827 *dest++ = *src++;
829 *dest = 0;
833 * escapes(): escape expansion for showsyms. C-style escapes understood
834 * include \n, \b, \t, \r, \xnnn (hex), \onnn (octal), \nnn (decimal).
835 * The ^-prefix for control characters is also understood, and \[mM]
836 * has the effect of 'meta'-ing the value which follows (so that the
837 * alternate character set will be enabled).
839 * X normal key X
840 * ^X control-X
841 * \mX meta-X
843 * For 3.4.3 and earlier, input ending with "\M", backslash, or caret
844 * prior to terminating '\0' would pull that '\0' into the output and then
845 * keep processing past it, potentially overflowing the output buffer.
846 * Now, trailing \ or ^ will act like \\ or \^ and add '\\' or '^' to the
847 * output and stop there; trailing \M will fall through to \<other> and
848 * yield 'M', then stop. Any \X or \O followed by something other than
849 * an appropriate digit will also fall through to \<other> and yield 'X'
850 * or 'O', plus stop if the non-digit is end-of-string.
852 STATIC_OVL void
853 escapes(cp, tp)
854 const char *cp;
855 char *tp;
857 static NEARDATA const char oct[] = "01234567", dec[] = "0123456789",
858 hex[] = "00112233445566778899aAbBcCdDeEfF";
859 const char *dp;
860 int cval, meta, dcount;
862 while (*cp) {
863 /* \M has to be followed by something to do meta conversion,
864 otherwise it will just be \M which ultimately yields 'M' */
865 meta = (*cp == '\\' && (cp[1] == 'm' || cp[1] == 'M') && cp[2]);
866 if (meta)
867 cp += 2;
869 cval = dcount = 0; /* for decimal, octal, hexadecimal cases */
870 if ((*cp != '\\' && *cp != '^') || !cp[1]) {
871 /* simple character, or nothing left for \ or ^ to escape */
872 cval = *cp++;
873 } else if (*cp == '^') { /* expand control-character syntax */
874 cval = (*++cp & 0x1f);
875 ++cp;
877 /* remaining cases are all for backslash; we know cp[1] is not \0 */
878 } else if (index(dec, cp[1])) {
879 ++cp; /* move past backslash to first digit */
880 do {
881 cval = (cval * 10) + (*cp - '0');
882 } while (*++cp && index(dec, *cp) && ++dcount < 3);
883 } else if ((cp[1] == 'o' || cp[1] == 'O') && cp[2]
884 && index(oct, cp[2])) {
885 cp += 2; /* move past backslash and 'O' */
886 do {
887 cval = (cval * 8) + (*cp - '0');
888 } while (*++cp && index(oct, *cp) && ++dcount < 3);
889 } else if ((cp[1] == 'x' || cp[1] == 'X') && cp[2]
890 && (dp = index(hex, cp[2])) != 0) {
891 cp += 2; /* move past backslash and 'X' */
892 do {
893 cval = (cval * 16) + ((int) (dp - hex) / 2);
894 } while (*++cp && (dp = index(hex, *cp)) != 0 && ++dcount < 2);
895 } else { /* C-style character escapes */
896 switch (*++cp) {
897 case '\\':
898 cval = '\\';
899 break;
900 case 'n':
901 cval = '\n';
902 break;
903 case 't':
904 cval = '\t';
905 break;
906 case 'b':
907 cval = '\b';
908 break;
909 case 'r':
910 cval = '\r';
911 break;
912 default:
913 cval = *cp;
915 ++cp;
918 if (meta)
919 cval |= 0x80;
920 *tp++ = (char) cval;
922 *tp = '\0';
925 STATIC_OVL void
926 rejectoption(optname)
927 const char *optname;
929 #ifdef MICRO
930 pline("\"%s\" settable only from %s.", optname, configfile);
931 #else
932 pline("%s can be set only from ANETHACKOPTIONS or %s.", optname,
933 configfile);
934 #endif
937 STATIC_OVL void
938 badoptmsg(opts, reason)
939 const char *opts;
940 const char *reason; /* "Bad syntax" or "Missing value" */
942 const char *linesplit = "";
944 if (!initial) {
945 if (!strncmp(opts, "h", 1) || !strncmp(opts, "?", 1))
946 option_help();
947 else
948 pline("%s: %s. Enter \"?g\" for help.", reason, opts);
949 return;
950 #ifdef MAC
951 } else {
952 return;
953 #endif
956 #ifdef WIN32
957 linesplit = "\n";
958 #endif
959 if (from_file)
960 raw_printf("%s in OPTIONS in %s: %s%s.\n",
961 reason, configfile, linesplit, opts);
962 else
963 raw_printf("%s in ANETHACKOPTIONS: %s%s.\n",
964 reason, linesplit, opts);
965 wait_synch();
968 STATIC_OVL void
969 badoption(opts)
970 const char *opts;
972 badoptmsg(opts, "Bad syntax");
975 STATIC_OVL char *
976 string_for_opt(opts, val_optional)
977 char *opts;
978 boolean val_optional;
980 char *colon, *equals;
982 colon = index(opts, ':');
983 equals = index(opts, '=');
984 if (!colon || (equals && equals < colon))
985 colon = equals;
987 if (!colon || !*++colon) {
988 if (!val_optional)
989 badoptmsg(opts, "Missing value");
990 return (char *) 0;
992 return colon;
995 STATIC_OVL char *
996 string_for_env_opt(optname, opts, val_optional)
997 const char *optname;
998 char *opts;
999 boolean val_optional;
1001 if (!initial) {
1002 rejectoption(optname);
1003 return (char *) 0;
1005 return string_for_opt(opts, val_optional);
1008 STATIC_OVL void
1009 bad_negation(optname, with_parameter)
1010 const char *optname;
1011 boolean with_parameter;
1013 pline_The("%s option may not %sbe negated.", optname,
1014 with_parameter ? "both have a value and " : "");
1018 * Change the inventory order, using the given string as the new order.
1019 * Missing characters in the new order are filled in at the end from
1020 * the current inv_order, except for gold, which is forced to be first
1021 * if not explicitly present.
1023 * This routine returns 1 unless there is a duplicate or bad char in
1024 * the string.
1026 STATIC_OVL int
1027 change_inv_order(op)
1028 char *op;
1030 int oc_sym, num;
1031 char *sp, buf[QBUFSZ];
1033 num = 0;
1034 if (!index(op, GOLD_SYM))
1035 buf[num++] = COIN_CLASS;
1037 for (sp = op; *sp; sp++) {
1038 oc_sym = def_char_to_objclass(*sp);
1039 /* reject bad or duplicate entries */
1040 if (oc_sym == MAXOCLASSES /* not an object class char */
1041 /* VENOM_CLASS, RANDOM_CLASS, and ILLOBJ_CLASS are excluded
1042 because they aren't in def_inv_order[] so don't make it
1043 into flags.inv_order, hence always fail this index() test */
1044 || !index(flags.inv_order, oc_sym) || index(sp + 1, *sp))
1045 return 0;
1046 /* retain good ones */
1047 buf[num++] = (char) oc_sym;
1049 buf[num] = '\0';
1051 /* fill in any omitted classes, using previous ordering */
1052 for (sp = flags.inv_order; *sp; sp++)
1053 if (!index(buf, *sp))
1054 (void) strkitten(&buf[num++], *sp);
1055 buf[MAXOCLASSES - 1] = '\0';
1057 Strcpy(flags.inv_order, buf);
1058 return 1;
1061 STATIC_OVL void
1062 warning_opts(opts, optype)
1063 register char *opts;
1064 const char *optype;
1066 uchar translate[WARNCOUNT];
1067 int length, i;
1069 if (!(opts = string_for_env_opt(optype, opts, FALSE)))
1070 return;
1071 escapes(opts, opts);
1073 length = (int) strlen(opts);
1074 /* match the form obtained from PC configuration files */
1075 for (i = 0; i < WARNCOUNT; i++)
1076 translate[i] = (i >= length) ? 0
1077 : opts[i] ? (uchar) opts[i]
1078 : def_warnsyms[i].sym;
1079 assign_warnings(translate);
1082 void
1083 assign_warnings(graph_chars)
1084 register uchar *graph_chars;
1086 int i;
1088 for (i = 0; i < WARNCOUNT; i++)
1089 if (graph_chars[i])
1090 warnsyms[i] = graph_chars[i];
1093 STATIC_OVL int
1094 feature_alert_opts(op, optn)
1095 char *op;
1096 const char *optn;
1098 char buf[BUFSZ];
1099 boolean rejectver = FALSE;
1100 unsigned long fnv = get_feature_notice_ver(op); /* version.c */
1102 if (fnv == 0L)
1103 return 0;
1104 if (fnv > get_current_feature_ver())
1105 rejectver = TRUE;
1106 else
1107 flags.suppress_alert = fnv;
1108 if (rejectver) {
1109 if (!initial) {
1110 You_cant("disable new feature alerts for future versions.");
1111 } else {
1112 Sprintf(buf,
1113 "\n%s=%s Invalid reference to a future version ignored",
1114 optn, op);
1115 badoption(buf);
1117 return 0;
1119 if (!initial) {
1120 Sprintf(buf, "%lu.%lu.%lu", FEATURE_NOTICE_VER_MAJ,
1121 FEATURE_NOTICE_VER_MIN, FEATURE_NOTICE_VER_PATCH);
1122 pline(
1123 "Feature change alerts disabled for aNetHack %s features and prior.",
1124 buf);
1126 return 1;
1129 void
1130 set_duplicate_opt_detection(on_or_off)
1131 int on_or_off;
1133 int k, *optptr;
1135 if (on_or_off != 0) {
1136 /*-- ON --*/
1137 if (iflags.opt_booldup)
1138 impossible("iflags.opt_booldup already on (memory leak)");
1139 iflags.opt_booldup = (int *) alloc(SIZE(boolopt) * sizeof(int));
1140 optptr = iflags.opt_booldup;
1141 for (k = 0; k < SIZE(boolopt); ++k)
1142 *optptr++ = 0;
1144 if (iflags.opt_compdup)
1145 impossible("iflags.opt_compdup already on (memory leak)");
1146 iflags.opt_compdup = (int *) alloc(SIZE(compopt) * sizeof(int));
1147 optptr = iflags.opt_compdup;
1148 for (k = 0; k < SIZE(compopt); ++k)
1149 *optptr++ = 0;
1150 } else {
1151 /*-- OFF --*/
1152 if (iflags.opt_booldup)
1153 free((genericptr_t) iflags.opt_booldup);
1154 iflags.opt_booldup = (int *) 0;
1155 if (iflags.opt_compdup)
1156 free((genericptr_t) iflags.opt_compdup);
1157 iflags.opt_compdup = (int *) 0;
1161 STATIC_OVL boolean
1162 duplicate_opt_detection(opts, iscompound)
1163 const char *opts;
1164 int iscompound; /* 0 == boolean option, 1 == compound */
1166 int i, *optptr;
1168 if (!iscompound && iflags.opt_booldup && initial && from_file) {
1169 for (i = 0; boolopt[i].name; i++) {
1170 if (match_optname(opts, boolopt[i].name, 3, FALSE)) {
1171 optptr = iflags.opt_booldup + i;
1172 *optptr += 1;
1173 if (*optptr > 1)
1174 return TRUE;
1175 else
1176 return FALSE;
1179 } else if (iscompound && iflags.opt_compdup && initial && from_file) {
1180 for (i = 0; compopt[i].name; i++) {
1181 if (match_optname(opts, compopt[i].name, strlen(compopt[i].name),
1182 TRUE)) {
1183 optptr = iflags.opt_compdup + i;
1184 *optptr += 1;
1185 if (*optptr > 1)
1186 return TRUE;
1187 else
1188 return FALSE;
1192 return FALSE;
1195 STATIC_OVL void
1196 complain_about_duplicate(opts, iscompound)
1197 const char *opts;
1198 int iscompound; /* 0 == boolean option, 1 == compound */
1200 #ifdef MAC
1201 /* the Mac has trouble dealing with the output of messages while
1202 * processing the config file. That should get fixed one day.
1203 * For now just return.
1205 #else /* !MAC */
1206 raw_printf("\nWarning - %s option specified multiple times: %s.\n",
1207 iscompound ? "compound" : "boolean", opts);
1208 wait_synch();
1209 #endif /* ?MAC */
1210 return;
1213 /* paranoia[] - used by parseoptions() and special_handling() */
1214 STATIC_VAR const struct paranoia_opts {
1215 int flagmask; /* which paranoid option */
1216 const char *argname; /* primary name */
1217 int argMinLen; /* minimum number of letters to match */
1218 const char *synonym; /* alternate name (optional) */
1219 int synMinLen;
1220 const char *explain; /* for interactive menu */
1221 } paranoia[] = {
1222 /* there are some initial-letter conflicts: "a"ttack vs "a"ll, "attack"
1223 takes precedence and "all" isn't present in the interactive menu,
1224 and "d"ie vs "d"eath, synonyms for each other so doesn't matter;
1225 (also "p"ray vs "P"aranoia, "pray" takes precedence since "Paranoia"
1226 is just a synonym for "Confirm") */
1227 { PARANOID_CONFIRM, "Confirm", 1, "Paranoia", 2,
1228 "for \"yes\" confirmations, require \"no\" to reject" },
1229 { PARANOID_QUIT, "quit", 1, "explore", 1,
1230 "yes vs y to quit or to enter explore mode" },
1231 { PARANOID_DIE, "die", 1, "death", 2,
1232 "yes vs y to die (explore mode or debug mode)" },
1233 { PARANOID_BONES, "bones", 1, 0, 0,
1234 "yes vs y to save bones data when dying in debug mode" },
1235 { PARANOID_HIT, "attack", 1, "hit", 1,
1236 "yes vs y to attack a peaceful monster" },
1237 { PARANOID_PRAY, "pray", 1, 0, 0,
1238 "y to pray (supersedes old \"prayconfirm\" option)" },
1239 { PARANOID_REMOVE, "Remove", 1, "Takeoff", 1,
1240 "always pick from inventory for Remove and Takeoff" },
1241 { PARANOID_BREAKWAND, "wand", 1, "breakwand", 2,
1242 "yes vs y to break a wand" },
1243 /* for config file parsing; interactive menu skips these */
1244 { 0, "none", 4, 0, 0, 0 }, /* require full word match */
1245 { ~0, "all", 3, 0, 0, 0 }, /* ditto */
1248 extern struct menucoloring *menu_colorings;
1250 static const struct {
1251 const char *name;
1252 const int color;
1253 } colornames[] = {
1254 { "black", CLR_BLACK },
1255 { "red", CLR_RED },
1256 { "green", CLR_GREEN },
1257 { "brown", CLR_BROWN },
1258 { "blue", CLR_BLUE },
1259 { "magenta", CLR_MAGENTA },
1260 { "cyan", CLR_CYAN },
1261 { "gray", CLR_GRAY },
1262 { "orange", CLR_ORANGE },
1263 { "light green", CLR_BRIGHT_GREEN },
1264 { "yellow", CLR_YELLOW },
1265 { "light blue", CLR_BRIGHT_BLUE },
1266 { "light magenta", CLR_BRIGHT_MAGENTA },
1267 { "light cyan", CLR_BRIGHT_CYAN },
1268 { "white", CLR_WHITE },
1269 { NULL, CLR_BLACK }, /* everything after this is an alias */
1270 { "transparent", NO_COLOR },
1271 { "nocolor", NO_COLOR },
1272 { "purple", CLR_MAGENTA },
1273 { "light purple", CLR_BRIGHT_MAGENTA },
1274 { "bright purple", CLR_BRIGHT_MAGENTA },
1275 { "grey", CLR_GRAY },
1276 { "bright red", CLR_ORANGE },
1277 { "bright green", CLR_BRIGHT_GREEN },
1278 { "bright blue", CLR_BRIGHT_BLUE },
1279 { "bright magenta", CLR_BRIGHT_MAGENTA },
1280 { "bright cyan", CLR_BRIGHT_CYAN }
1283 static const struct {
1284 const char *name;
1285 const int attr;
1286 } attrnames[] = {
1287 { "none", ATR_NONE },
1288 { "bold", ATR_BOLD },
1289 { "dim", ATR_DIM },
1290 { "underline", ATR_ULINE },
1291 { "blink", ATR_BLINK },
1292 { "inverse", ATR_INVERSE }
1295 const char *
1296 clr2colorname(clr)
1297 int clr;
1299 int i;
1301 for (i = 0; i < SIZE(colornames); i++)
1302 if (colornames[i].name && colornames[i].color == clr)
1303 return colornames[i].name;
1304 return (char *) 0;
1308 match_str2clr(str)
1309 char *str;
1311 int i, c = NO_COLOR;
1313 /* allow "lightblue", "light blue", and "light-blue" to match "light blue"
1314 (also junk like "_l i-gh_t---b l u e" but we won't worry about that);
1315 also copes with trailing space; mungspaces removed any leading space */
1316 for (i = 0; i < SIZE(colornames); i++)
1317 if (colornames[i].name
1318 && fuzzymatch(str, colornames[i].name, " -_", TRUE)) {
1319 c = colornames[i].color;
1320 break;
1322 if (i == SIZE(colornames) && (*str >= '0' && *str <= '9'))
1323 c = atoi(str);
1324 return c;
1327 STATIC_OVL const char *
1328 attr2attrname(attr)
1329 int attr;
1331 int i;
1333 for (i = 0; i < SIZE(attrnames); i++)
1334 if (attrnames[i].attr == attr)
1335 return attrnames[i].name;
1336 return (char *) 0;
1339 STATIC_OVL int
1340 query_color()
1342 winid tmpwin;
1343 anything any;
1344 int i, pick_cnt;
1345 menu_item *picks = (menu_item *) 0;
1347 tmpwin = create_nhwindow(NHW_MENU);
1348 start_menu(tmpwin);
1349 any = zeroany;
1350 for (i = 0; i < SIZE(colornames); i++) {
1351 if (!colornames[i].name)
1352 break;
1353 any.a_int = i + 1;
1354 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, colornames[i].name,
1355 MENU_UNSELECTED);
1357 end_menu(tmpwin, "Pick a color");
1358 pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
1359 destroy_nhwindow(tmpwin);
1360 if (pick_cnt > 0) {
1361 i = colornames[picks->item.a_int - 1].color;
1362 free((genericptr_t) picks);
1363 return i;
1365 return -1;
1368 STATIC_OVL int
1369 query_attr(prompt)
1370 const char *prompt;
1372 winid tmpwin;
1373 anything any;
1374 int i, pick_cnt;
1375 menu_item *picks = (menu_item *) 0;
1377 tmpwin = create_nhwindow(NHW_MENU);
1378 start_menu(tmpwin);
1379 any = zeroany;
1380 for (i = 0; i < SIZE(attrnames); i++) {
1381 any.a_int = i + 1;
1382 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, attrnames[i].attr,
1383 attrnames[i].name, MENU_UNSELECTED);
1385 end_menu(tmpwin, prompt ? prompt : "Pick an attribute");
1386 pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
1387 destroy_nhwindow(tmpwin);
1388 if (pick_cnt > 0) {
1389 i = attrnames[picks->item.a_int - 1].attr;
1390 free((genericptr_t) picks);
1391 return i;
1393 return -1;
1396 static const struct {
1397 const char *name;
1398 const xchar msgtyp;
1399 const char *descr;
1400 } msgtype_names[] = {
1401 { "show", MSGTYP_NORMAL, "Show message normally" },
1402 { "hide", MSGTYP_NOSHOW, "Hide message" },
1403 { "noshow", MSGTYP_NOSHOW, NULL },
1404 { "stop", MSGTYP_STOP, "Prompt for more after the message" },
1405 { "more", MSGTYP_STOP, NULL },
1406 { "norep", MSGTYP_NOREP, "Do not repeat the message" }
1409 STATIC_OVL const char *
1410 msgtype2name(typ)
1411 int typ;
1413 int i;
1415 for (i = 0; i < SIZE(msgtype_names); i++)
1416 if (msgtype_names[i].descr && msgtype_names[i].msgtyp == typ)
1417 return msgtype_names[i].name;
1418 return (char *) 0;
1422 query_msgtype()
1424 winid tmpwin;
1425 anything any;
1426 int i, pick_cnt;
1427 menu_item *picks = (menu_item *) 0;
1429 tmpwin = create_nhwindow(NHW_MENU);
1430 start_menu(tmpwin);
1431 any = zeroany;
1432 for (i = 0; i < SIZE(msgtype_names); i++)
1433 if (msgtype_names[i].descr) {
1434 any.a_int = msgtype_names[i].msgtyp + 1;
1435 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1436 msgtype_names[i].descr, MENU_UNSELECTED);
1438 end_menu(tmpwin, "How to show the message");
1439 pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
1440 destroy_nhwindow(tmpwin);
1441 if (pick_cnt > 0) {
1442 i = picks->item.a_int - 1;
1443 free((genericptr_t) picks);
1444 return i;
1446 return -1;
1449 STATIC_OVL boolean
1450 msgtype_add(typ, pattern)
1451 int typ;
1452 char *pattern;
1454 struct plinemsg_type *tmp
1455 = (struct plinemsg_type *) alloc(sizeof (struct plinemsg_type));
1457 if (!tmp)
1458 return FALSE;
1459 tmp->msgtype = typ;
1460 tmp->regex = regex_init();
1461 if (!regex_compile(pattern, tmp->regex)) {
1462 static const char *re_error = "MSGTYPE regex error";
1464 if (!iflags.window_inited)
1465 raw_printf("\n%s: %s\n", re_error, regex_error_desc(tmp->regex));
1466 else
1467 pline("%s: %s", re_error, regex_error_desc(tmp->regex));
1468 wait_synch();
1469 regex_free(tmp->regex);
1470 free((genericptr_t) tmp);
1471 return FALSE;
1473 tmp->pattern = dupstr(pattern);
1474 tmp->next = plinemsg_types;
1475 plinemsg_types = tmp;
1476 return TRUE;
1479 void
1480 msgtype_free()
1482 struct plinemsg_type *tmp, *tmp2 = 0;
1484 for (tmp = plinemsg_types; tmp; tmp = tmp2) {
1485 tmp2 = tmp->next;
1486 free((genericptr_t) tmp->pattern);
1487 regex_free(tmp->regex);
1488 free((genericptr_t) tmp);
1490 plinemsg_types = (struct plinemsg_type *) 0;
1493 STATIC_OVL void
1494 free_one_msgtype(idx)
1495 int idx; /* 0 .. */
1497 struct plinemsg_type *tmp = plinemsg_types;
1498 struct plinemsg_type *prev = NULL;
1500 while (tmp) {
1501 if (idx == 0) {
1502 struct plinemsg_type *next = tmp->next;
1504 regex_free(tmp->regex);
1505 free((genericptr_t) tmp->pattern);
1506 free((genericptr_t) tmp);
1507 if (prev)
1508 prev->next = next;
1509 else
1510 plinemsg_types = next;
1511 return;
1513 idx--;
1514 prev = tmp;
1515 tmp = tmp->next;
1520 msgtype_type(msg, norepeat)
1521 const char *msg;
1522 boolean norepeat; /* called from Norep(via pline) */
1524 struct plinemsg_type *tmp = plinemsg_types;
1526 while (tmp) {
1527 /* we don't exclude entries with negative msgtype values
1528 because then the msg might end up matching a later pattern */
1529 if (regex_match(msg, tmp->regex))
1530 return tmp->msgtype;
1531 tmp = tmp->next;
1533 return norepeat ? MSGTYP_NOREP : MSGTYP_NORMAL;
1536 /* negate one or more types of messages so that their type handling will
1537 be disabled or re-enabled; MSGTYPE_NORMAL (value 0) is not affected */
1538 void
1539 hide_unhide_msgtypes(hide, hide_mask)
1540 boolean hide;
1541 int hide_mask;
1543 struct plinemsg_type *tmp;
1544 int mt;
1546 /* negative msgtype value won't be recognized by pline, so does nothing */
1547 for (tmp = plinemsg_types; tmp; tmp = tmp->next) {
1548 mt = tmp->msgtype;
1549 if (!hide)
1550 mt = -mt; /* unhide: negate negative, yielding positive */
1551 if (mt > 0 && ((1 << mt) & hide_mask))
1552 tmp->msgtype = -tmp->msgtype;
1556 STATIC_OVL int
1557 msgtype_count()
1559 int c = 0;
1560 struct plinemsg_type *tmp = plinemsg_types;
1562 while (tmp) {
1563 c++;
1564 tmp = tmp->next;
1566 return c;
1569 boolean
1570 msgtype_parse_add(str)
1571 char *str;
1573 char pattern[256];
1574 char msgtype[11];
1576 if (sscanf(str, "%10s \"%255[^\"]\"", msgtype, pattern) == 2) {
1577 int typ = -1;
1578 int i;
1580 for (i = 0; i < SIZE(msgtype_names); i++)
1581 if (!strncmpi(msgtype_names[i].name, msgtype, strlen(msgtype))) {
1582 typ = msgtype_names[i].msgtyp;
1583 break;
1585 if (typ != -1)
1586 return msgtype_add(typ, pattern);
1588 return FALSE;
1591 boolean
1592 add_menu_coloring_parsed(str, c, a)
1593 char *str;
1594 int c, a;
1596 static const char re_error[] = "Menucolor regex error";
1597 struct menucoloring *tmp;
1599 if (!str)
1600 return FALSE;
1601 tmp = (struct menucoloring *) alloc(sizeof (struct menucoloring));
1602 tmp->match = regex_init();
1603 if (!regex_compile(str, tmp->match)) {
1604 if (!iflags.window_inited)
1605 raw_printf("\n%s: %s\n", re_error, regex_error_desc(tmp->match));
1606 else
1607 pline("%s: %s", re_error, regex_error_desc(tmp->match));
1608 wait_synch();
1609 regex_free(tmp->match);
1610 free(tmp);
1611 return FALSE;
1612 } else {
1613 tmp->next = menu_colorings;
1614 tmp->origstr = dupstr(str);
1615 tmp->color = c;
1616 tmp->attr = a;
1617 menu_colorings = tmp;
1618 return TRUE;
1622 /* parse '"regex_string"=color&attr' and add it to menucoloring */
1623 boolean
1624 add_menu_coloring(str)
1625 char *str;
1627 int i, c = NO_COLOR, a = ATR_NONE;
1628 char *tmps, *cs, *amp;
1630 if (!str || (cs = index(str, '=')) == 0)
1631 return FALSE;
1633 tmps = cs + 1; /* advance past '=' */
1634 mungspaces(tmps);
1635 if ((amp = index(tmps, '&')) != 0)
1636 *amp = '\0';
1638 c = match_str2clr(tmps);
1639 if (c >= CLR_MAX)
1640 return FALSE;
1642 if (amp) {
1643 tmps = amp + 1; /* advance past '&' */
1644 /* unlike colors, none of he attribute names has any embedded spaces,
1645 but use of fuzzymatch() allows us ignore the presence of leading
1646 and/or trailing (and also embedded) spaces in the user's string;
1647 dash and underscore skipping could be omitted but does no harm */
1648 for (i = 0; i < SIZE(attrnames); i++)
1649 if (fuzzymatch(tmps, attrnames[i].name, " -_", TRUE)) {
1650 a = attrnames[i].attr;
1651 break;
1653 if (i == SIZE(attrnames) && (*tmps >= '0' && *tmps <= '9'))
1654 a = atoi(tmps);
1657 /* the regexp portion here has not been condensed by mungspaces() */
1658 *cs = '\0';
1659 tmps = str;
1660 if (*tmps == '"' || *tmps == '\'') {
1661 cs--;
1662 while (isspace((uchar) *cs))
1663 cs--;
1664 if (*cs == *tmps) {
1665 *cs = '\0';
1666 tmps++;
1670 return add_menu_coloring_parsed(tmps, c, a);
1673 boolean
1674 get_menu_coloring(str, color, attr)
1675 char *str;
1676 int *color, *attr;
1678 struct menucoloring *tmpmc;
1680 if (iflags.use_menu_color)
1681 for (tmpmc = menu_colorings; tmpmc; tmpmc = tmpmc->next)
1682 if (regex_match(str, tmpmc->match)) {
1683 *color = tmpmc->color;
1684 *attr = tmpmc->attr;
1685 return TRUE;
1687 return FALSE;
1690 void
1691 free_menu_coloring()
1693 struct menucoloring *tmp = menu_colorings;
1695 while (tmp) {
1696 struct menucoloring *tmp2 = tmp->next;
1698 regex_free(tmp->match);
1699 free((genericptr_t) tmp->origstr);
1700 free((genericptr_t) tmp);
1701 tmp = tmp2;
1705 STATIC_OVL void
1706 free_one_menu_coloring(idx)
1707 int idx; /* 0 .. */
1709 struct menucoloring *tmp = menu_colorings;
1710 struct menucoloring *prev = NULL;
1712 while (tmp) {
1713 if (idx == 0) {
1714 struct menucoloring *next = tmp->next;
1716 regex_free(tmp->match);
1717 free((genericptr_t) tmp->origstr);
1718 free((genericptr_t) tmp);
1719 if (prev)
1720 prev->next = next;
1721 else
1722 menu_colorings = next;
1723 return;
1725 idx--;
1726 prev = tmp;
1727 tmp = tmp->next;
1731 STATIC_OVL int
1732 count_menucolors()
1734 int count = 0;
1735 struct menucoloring *tmp = menu_colorings;
1737 while (tmp) {
1738 count++;
1739 tmp = tmp->next;
1741 return count;
1744 STATIC_OVL boolean
1745 parse_role_opts(negated, fullname, opts, opp)
1746 boolean negated;
1747 const char *fullname;
1748 char *opts;
1749 char **opp;
1751 char *op = *opp;
1753 if (negated) {
1754 bad_negation(fullname, FALSE);
1755 } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
1756 boolean val_negated = FALSE;
1758 while ((*op == '!') || !strncmpi(op, "no", 2)) {
1759 if (*op == '!')
1760 op++;
1761 else
1762 op += 2;
1763 val_negated = !val_negated;
1765 if (val_negated) {
1766 if (!setrolefilter(op))
1767 badoption(opts);
1768 } else {
1769 if (duplicate_opt_detection(opts, 1))
1770 complain_about_duplicate(opts, 1);
1771 *opp = op;
1772 return TRUE;
1775 return FALSE;
1778 /* Check if character c is illegal as a menu command key */
1779 boolean
1780 illegal_menu_cmd_key(c)
1781 char c;
1783 if (c == 0 || c == '\r' || c == '\n' || c == '\033'
1784 || c == ' ' || digit(c) || (letter(c) && c != '@'))
1785 return TRUE;
1786 else { /* reject default object class symbols */
1787 int j;
1788 for (j = 1; j < MAXOCLASSES; j++)
1789 if (c == def_oc_syms[j].sym)
1790 return TRUE;
1792 return FALSE;
1795 void
1796 parseoptions(opts, tinitial, tfrom_file)
1797 register char *opts;
1798 boolean tinitial, tfrom_file;
1800 char *op;
1801 unsigned num;
1802 boolean negated, duplicate;
1803 int i;
1804 const char *fullname;
1806 initial = tinitial;
1807 from_file = tfrom_file;
1808 if ((op = index(opts, ',')) != 0) {
1809 *op++ = 0;
1810 parseoptions(op, initial, from_file);
1812 if (strlen(opts) > BUFSZ / 2) {
1813 badoption("option too long");
1814 return;
1817 /* strip leading and trailing white space */
1818 while (isspace((uchar) *opts))
1819 opts++;
1820 op = eos(opts);
1821 while (--op >= opts && isspace((uchar) *op))
1822 *op = '\0';
1824 if (!*opts)
1825 return;
1826 negated = FALSE;
1827 while ((*opts == '!') || !strncmpi(opts, "no", 2)) {
1828 if (*opts == '!')
1829 opts++;
1830 else
1831 opts += 2;
1832 negated = !negated;
1835 /* variant spelling */
1837 if (match_optname(opts, "colour", 5, FALSE))
1838 Strcpy(opts, "color"); /* fortunately this isn't longer */
1840 /* special boolean options */
1842 if (match_optname(opts, "female", 3, FALSE)) {
1843 if (duplicate_opt_detection(opts, 0))
1844 complain_about_duplicate(opts, 0);
1845 if (!initial && flags.female == negated)
1846 pline("That is not anatomically possible.");
1847 else
1848 flags.initgend = flags.female = !negated;
1849 return;
1852 if (match_optname(opts, "male", 4, FALSE)) {
1853 if (duplicate_opt_detection(opts, 0))
1854 complain_about_duplicate(opts, 0);
1855 if (!initial && flags.female != negated)
1856 pline("That is not anatomically possible.");
1857 else
1858 flags.initgend = flags.female = negated;
1859 return;
1862 #if defined(MICRO) && !defined(AMIGA)
1863 /* included for compatibility with old aNetHack.cnf files */
1864 if (match_optname(opts, "IBM_", 4, FALSE)) {
1865 iflags.BIOS = !negated;
1866 return;
1868 #endif /* MICRO */
1870 /* compound options */
1872 /* This first batch can be duplicated if their values are negated */
1874 /* align:string */
1875 fullname = "align";
1876 if (match_optname(opts, fullname, sizeof("align") - 1, TRUE)) {
1877 if (parse_role_opts(negated, fullname, opts, &op)) {
1878 if ((flags.initalign = str2align(op)) == ROLE_NONE)
1879 badoption(opts);
1881 return;
1884 /* role:string or character:string */
1885 fullname = "role";
1886 if (match_optname(opts, fullname, 4, TRUE)
1887 || match_optname(opts, (fullname = "character"), 4, TRUE)) {
1888 if (parse_role_opts(negated, fullname, opts, &op)) {
1889 if ((flags.initrole = str2role(op)) == ROLE_NONE)
1890 badoption(opts);
1891 else /* Backwards compatibility */
1892 nmcpy(pl_character, op, PL_NSIZ);
1894 return;
1897 /* race:string */
1898 fullname = "race";
1899 if (match_optname(opts, fullname, 4, TRUE)) {
1900 if (parse_role_opts(negated, fullname, opts, &op)) {
1901 if ((flags.initrace = str2race(op)) == ROLE_NONE)
1902 badoption(opts);
1903 else /* Backwards compatibility */
1904 pl_race = *op;
1906 return;
1909 /* gender:string */
1910 fullname = "gender";
1911 if (match_optname(opts, fullname, 4, TRUE)) {
1912 if (parse_role_opts(negated, fullname, opts, &op)) {
1913 if ((flags.initgend = str2gend(op)) == ROLE_NONE)
1914 badoption(opts);
1915 else
1916 flags.female = flags.initgend;
1918 return;
1921 /* We always check for duplicates on the remaining compound options,
1922 although individual option processing can choose to complain or not */
1924 duplicate =
1925 duplicate_opt_detection(opts, 1); /* 1 means check compounds */
1927 fullname = "pettype";
1928 if (match_optname(opts, fullname, 3, TRUE)) {
1929 if (duplicate)
1930 complain_about_duplicate(opts, 1);
1931 if ((op = string_for_env_opt(fullname, opts, negated)) != 0) {
1932 if (negated)
1933 bad_negation(fullname, TRUE);
1934 else
1935 switch (lowc(*op)) {
1936 case 'd': /* dog */
1937 preferred_pet = 'd';
1938 break;
1939 case 'c': /* cat */
1940 case 'f': /* feline */
1941 preferred_pet = 'c';
1942 break;
1943 case 'h': /* horse */
1944 case 'q': /* quadruped */
1945 /* avoids giving "unrecognized type of pet" but
1946 pet_type(dog.c) won't actually honor this */
1947 preferred_pet = 'h';
1948 break;
1949 case 'n': /* no pet */
1950 preferred_pet = 'n';
1951 break;
1952 case '*': /* random */
1953 preferred_pet = '\0';
1954 break;
1955 default:
1956 pline("Unrecognized pet type '%s'.", op);
1957 break;
1959 } else if (negated)
1960 preferred_pet = 'n';
1961 return;
1964 fullname = "catname";
1965 if (match_optname(opts, fullname, 3, TRUE)) {
1966 if (duplicate)
1967 complain_about_duplicate(opts, 1);
1968 if (negated)
1969 bad_negation(fullname, FALSE);
1970 else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1971 nmcpy(catname, op, PL_PSIZ);
1972 sanitize_name(catname);
1973 return;
1976 fullname = "dogname";
1977 if (match_optname(opts, fullname, 3, TRUE)) {
1978 if (duplicate)
1979 complain_about_duplicate(opts, 1);
1980 if (negated)
1981 bad_negation(fullname, FALSE);
1982 else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1983 nmcpy(dogname, op, PL_PSIZ);
1984 sanitize_name(dogname);
1985 return;
1988 fullname = "horsename";
1989 if (match_optname(opts, fullname, 5, TRUE)) {
1990 if (duplicate)
1991 complain_about_duplicate(opts, 1);
1992 if (negated)
1993 bad_negation(fullname, FALSE);
1994 else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
1995 nmcpy(horsename, op, PL_PSIZ);
1996 sanitize_name(horsename);
1997 return;
2000 fullname = "number_pad";
2001 if (match_optname(opts, fullname, 10, TRUE)) {
2002 boolean compat = (strlen(opts) <= 10);
2004 if (duplicate)
2005 complain_about_duplicate(opts, 1);
2006 op = string_for_opt(opts, (compat || !initial));
2007 if (!op) {
2008 if (compat || negated || initial) {
2009 /* for backwards compatibility, "number_pad" without a
2010 value is a synonym for number_pad:1 */
2011 iflags.num_pad = !negated;
2012 iflags.num_pad_mode = 0;
2014 } else if (negated) {
2015 bad_negation("number_pad", TRUE);
2016 return;
2017 } else {
2018 int mode = atoi(op);
2020 if (mode < -1 || mode > 4 || (mode == 0 && *op != '0')) {
2021 badoption(opts);
2022 return;
2023 } else if (mode <= 0) {
2024 iflags.num_pad = FALSE;
2025 /* German keyboard; y and z keys swapped */
2026 iflags.num_pad_mode = (mode < 0); /* 0 or 1 */
2027 } else { /* mode > 0 */
2028 iflags.num_pad = TRUE;
2029 iflags.num_pad_mode = 0;
2030 /* PC Hack / MSDOS compatibility */
2031 if (mode == 2 || mode == 4)
2032 iflags.num_pad_mode |= 1;
2033 /* phone keypad layout */
2034 if (mode == 3 || mode == 4)
2035 iflags.num_pad_mode |= 2;
2038 reset_commands(FALSE);
2039 number_pad(iflags.num_pad ? 1 : 0);
2040 return;
2043 fullname = "roguesymset";
2044 if (match_optname(opts, fullname, 7, TRUE)) {
2045 if (duplicate)
2046 complain_about_duplicate(opts, 1);
2047 if (negated) {
2048 bad_negation(fullname, FALSE);
2049 } else if ((op = string_for_opt(opts, FALSE)) != 0) {
2050 symset[ROGUESET].name = dupstr(op);
2051 if (!read_sym_file(ROGUESET)) {
2052 clear_symsetentry(ROGUESET, TRUE);
2053 raw_printf("Unable to load symbol set \"%s\" from \"%s\".",
2054 op, SYMBOLS);
2055 wait_synch();
2056 } else {
2057 if (!initial && Is_rogue_level(&u.uz))
2058 assign_graphics(ROGUESET);
2059 need_redraw = TRUE;
2062 return;
2065 fullname = "symset";
2066 if (match_optname(opts, fullname, 6, TRUE)) {
2067 if (duplicate)
2068 complain_about_duplicate(opts, 1);
2069 if (negated) {
2070 bad_negation(fullname, FALSE);
2071 } else if ((op = string_for_opt(opts, FALSE)) != 0) {
2072 symset[PRIMARY].name = dupstr(op);
2073 if (!read_sym_file(PRIMARY)) {
2074 clear_symsetentry(PRIMARY, TRUE);
2075 raw_printf("Unable to load symbol set \"%s\" from \"%s\".",
2076 op, SYMBOLS);
2077 wait_synch();
2078 } else {
2079 switch_symbols(symset[PRIMARY].name != (char *) 0);
2080 need_redraw = TRUE;
2083 return;
2086 fullname = "runmode";
2087 if (match_optname(opts, fullname, 4, TRUE)) {
2088 if (duplicate)
2089 complain_about_duplicate(opts, 1);
2090 if (negated) {
2091 flags.runmode = RUN_TPORT;
2092 } else if ((op = string_for_opt(opts, FALSE)) != 0) {
2093 if (!strncmpi(op, "teleport", strlen(op)))
2094 flags.runmode = RUN_TPORT;
2095 else if (!strncmpi(op, "run", strlen(op)))
2096 flags.runmode = RUN_LEAP;
2097 else if (!strncmpi(op, "walk", strlen(op)))
2098 flags.runmode = RUN_STEP;
2099 else if (!strncmpi(op, "crawl", strlen(op)))
2100 flags.runmode = RUN_CRAWL;
2101 else
2102 badoption(opts);
2104 return;
2107 /* menucolor:"regex_string"=color */
2108 fullname = "menucolor";
2109 if (match_optname(opts, fullname, 9, TRUE)) {
2110 if (negated)
2111 bad_negation(fullname, FALSE);
2112 else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
2113 if (!add_menu_coloring(op))
2114 badoption(opts);
2115 return;
2118 fullname = "msghistory";
2119 if (match_optname(opts, fullname, 3, TRUE)) {
2120 if (duplicate)
2121 complain_about_duplicate(opts, 1);
2122 op = string_for_env_opt(fullname, opts, negated);
2123 if ((negated && !op) || (!negated && op)) {
2124 iflags.msg_history = negated ? 0 : atoi(op);
2125 } else if (negated)
2126 bad_negation(fullname, TRUE);
2127 return;
2130 fullname = "msg_window";
2131 /* msg_window:single, combo, full or reversed */
2132 if (match_optname(opts, fullname, 4, TRUE)) {
2133 /* allow option to be silently ignored by non-tty ports */
2134 #ifdef TTY_GRAPHICS
2135 int tmp;
2137 if (duplicate)
2138 complain_about_duplicate(opts, 1);
2139 if (!(op = string_for_opt(opts, TRUE))) {
2140 tmp = negated ? 's' : 'f';
2141 } else {
2142 if (negated) {
2143 bad_negation(fullname, TRUE);
2144 return;
2146 tmp = lowc(*op);
2148 switch (tmp) {
2149 case 's': /* single message history cycle (default if negated) */
2150 iflags.prevmsg_window = 's';
2151 break;
2152 case 'c': /* combination: two singles, then full page reversed */
2153 iflags.prevmsg_window = 'c';
2154 break;
2155 case 'f': /* full page (default if no opts) */
2156 iflags.prevmsg_window = 'f';
2157 break;
2158 case 'r': /* full page (reversed) */
2159 iflags.prevmsg_window = 'r';
2160 break;
2161 default:
2162 badoption(opts);
2164 #endif
2165 return;
2168 /* WINCAP
2169 * setting font options */
2170 fullname = "font";
2171 if (!strncmpi(opts, fullname, 4)) {
2172 int opttype = -1;
2173 char *fontopts = opts + 4;
2175 if (!strncmpi(fontopts, "map", 3) || !strncmpi(fontopts, "_map", 4))
2176 opttype = MAP_OPTION;
2177 else if (!strncmpi(fontopts, "message", 7)
2178 || !strncmpi(fontopts, "_message", 8))
2179 opttype = MESSAGE_OPTION;
2180 else if (!strncmpi(fontopts, "text", 4)
2181 || !strncmpi(fontopts, "_text", 5))
2182 opttype = TEXT_OPTION;
2183 else if (!strncmpi(fontopts, "menu", 4)
2184 || !strncmpi(fontopts, "_menu", 5))
2185 opttype = MENU_OPTION;
2186 else if (!strncmpi(fontopts, "status", 6)
2187 || !strncmpi(fontopts, "_status", 7))
2188 opttype = STATUS_OPTION;
2189 else if (!strncmpi(fontopts, "_size", 5)) {
2190 if (!strncmpi(fontopts, "_size_map", 8))
2191 opttype = MAP_OPTION;
2192 else if (!strncmpi(fontopts, "_size_message", 12))
2193 opttype = MESSAGE_OPTION;
2194 else if (!strncmpi(fontopts, "_size_text", 9))
2195 opttype = TEXT_OPTION;
2196 else if (!strncmpi(fontopts, "_size_menu", 9))
2197 opttype = MENU_OPTION;
2198 else if (!strncmpi(fontopts, "_size_status", 11))
2199 opttype = STATUS_OPTION;
2200 else {
2201 badoption(opts);
2202 return;
2204 if (duplicate)
2205 complain_about_duplicate(opts, 1);
2206 if (opttype > 0 && !negated
2207 && (op = string_for_opt(opts, FALSE)) != 0) {
2208 switch (opttype) {
2209 case MAP_OPTION:
2210 iflags.wc_fontsiz_map = atoi(op);
2211 break;
2212 case MESSAGE_OPTION:
2213 iflags.wc_fontsiz_message = atoi(op);
2214 break;
2215 case TEXT_OPTION:
2216 iflags.wc_fontsiz_text = atoi(op);
2217 break;
2218 case MENU_OPTION:
2219 iflags.wc_fontsiz_menu = atoi(op);
2220 break;
2221 case STATUS_OPTION:
2222 iflags.wc_fontsiz_status = atoi(op);
2223 break;
2226 return;
2227 } else {
2228 badoption(opts);
2230 if (opttype > 0 && (op = string_for_opt(opts, FALSE)) != 0) {
2231 wc_set_font_name(opttype, op);
2232 #ifdef MAC
2233 set_font_name(opttype, op);
2234 #endif
2235 return;
2236 } else if (negated)
2237 bad_negation(fullname, TRUE);
2238 return;
2241 #ifdef CHANGE_COLOR
2242 if (match_optname(opts, "palette", 3, TRUE)
2243 #ifdef MAC
2244 || match_optname(opts, "hicolor", 3, TRUE)
2245 #endif
2247 int color_number, color_incr;
2249 #ifndef WIN32
2250 if (duplicate)
2251 complain_about_duplicate(opts, 1);
2252 #endif
2253 #ifdef MAC
2254 if (match_optname(opts, "hicolor", 3, TRUE)) {
2255 if (negated) {
2256 bad_negation("hicolor", FALSE);
2257 return;
2259 color_number = CLR_MAX + 4; /* HARDCODED inverse number */
2260 color_incr = -1;
2261 } else
2262 #endif
2264 if (negated) {
2265 bad_negation("palette", FALSE);
2266 return;
2268 color_number = 0;
2269 color_incr = 1;
2271 #ifdef WIN32
2272 op = string_for_opt(opts, TRUE);
2273 if (!alternative_palette(op))
2274 badoption(opts);
2275 #else
2276 if ((op = string_for_opt(opts, FALSE)) != (char *) 0) {
2277 char *pt = op;
2278 int cnt, tmp, reverse;
2279 long rgb;
2281 while (*pt && color_number >= 0) {
2282 cnt = 3;
2283 rgb = 0L;
2284 if (*pt == '-') {
2285 reverse = 1;
2286 pt++;
2287 } else {
2288 reverse = 0;
2290 while (cnt-- > 0) {
2291 if (*pt && *pt != '/') {
2292 #ifdef AMIGA
2293 rgb <<= 4;
2294 #else
2295 rgb <<= 8;
2296 #endif
2297 tmp = *pt++;
2298 if (isalpha((uchar) tmp)) {
2299 tmp = (tmp + 9) & 0xf; /* Assumes ASCII... */
2300 } else {
2301 tmp &= 0xf; /* Digits in ASCII too... */
2303 #ifndef AMIGA
2304 /* Add an extra so we fill f -> ff and 0 -> 00 */
2305 rgb += tmp << 4;
2306 #endif
2307 rgb += tmp;
2310 if (*pt == '/')
2311 pt++;
2312 change_color(color_number, rgb, reverse);
2313 color_number += color_incr;
2316 #endif /* !WIN32 */
2317 if (!initial) {
2318 need_redraw = TRUE;
2320 return;
2322 #endif /* CHANGE_COLOR */
2324 if (match_optname(opts, "fruit", 2, TRUE)) {
2325 struct fruit *forig = 0;
2326 char empty_str = '\0';
2328 if (duplicate)
2329 complain_about_duplicate(opts, 1);
2330 op = string_for_opt(opts, negated);
2331 if (negated) {
2332 if (op) {
2333 bad_negation("fruit", TRUE);
2334 return;
2336 op = &empty_str;
2337 goto goodfruit;
2339 if (!op)
2340 return;
2341 if (!initial) {
2342 struct fruit *f;
2344 num = 0;
2345 for (f = ffruit; f; f = f->nextf) {
2346 if (!strcmp(op, f->fname))
2347 break;
2348 num++;
2350 if (!flags.made_fruit) {
2351 for (forig = ffruit; forig; forig = forig->nextf) {
2352 if (!strcmp(pl_fruit, forig->fname)) {
2353 break;
2357 if (!forig && num >= 100) {
2358 pline("Doing that so many times isn't very fruitful.");
2359 return;
2362 goodfruit:
2363 nmcpy(pl_fruit, op, PL_FSIZ);
2364 sanitize_name(pl_fruit);
2365 /* OBJ_NAME(objects[SLIME_MOLD]) won't work after initialization */
2366 if (!*pl_fruit)
2367 nmcpy(pl_fruit, "slime mold", PL_FSIZ);
2368 if (!initial) {
2369 (void) fruitadd(pl_fruit, forig);
2370 pline("Fruit is now \"%s\".", pl_fruit);
2372 /* If initial, then initoptions is allowed to do it instead
2373 * of here (initoptions always has to do it even if there's
2374 * no fruit option at all. Also, we don't want people
2375 * setting multiple fruits in their options.)
2377 return;
2380 fullname = "whatis_coord";
2381 if (match_optname(opts, fullname, 6, TRUE)) {
2382 if (duplicate)
2383 complain_about_duplicate(opts, 1);
2384 if (negated) {
2385 iflags.getpos_coords = GPCOORDS_NONE;
2386 return;
2387 } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
2388 static char gpcoords[] = { GPCOORDS_NONE, GPCOORDS_COMPASS,
2389 GPCOORDS_COMFULL, GPCOORDS_MAP,
2390 GPCOORDS_SCREEN, '\0' };
2391 char c = lowc(*op);
2393 if (c && index(gpcoords, c))
2394 iflags.getpos_coords = c;
2395 else
2396 badoption(opts);
2398 return;
2401 fullname = "warnings";
2402 if (match_optname(opts, fullname, 5, TRUE)) {
2403 if (duplicate)
2404 complain_about_duplicate(opts, 1);
2405 if (negated)
2406 bad_negation(fullname, FALSE);
2407 else
2408 warning_opts(opts, fullname);
2409 return;
2412 #ifdef BACKWARD_COMPAT
2413 /* boulder:symbol */
2414 fullname = "boulder";
2415 if (match_optname(opts, fullname, 7, TRUE)) {
2416 int clash = 0;
2417 if (duplicate)
2418 complain_about_duplicate(opts, 1);
2419 if (negated) {
2420 bad_negation(fullname, FALSE);
2421 return;
2423 /* if (!(opts = string_for_env_opt(fullname, opts, FALSE)))
2425 if (!(opts = string_for_opt(opts, FALSE)))
2426 return;
2427 escapes(opts, opts);
2428 if (def_char_to_monclass(opts[0]) != MAXMCLASSES)
2429 clash = 1;
2430 else if (opts[0] >= '1' && opts[0] <= '5')
2431 clash = 2;
2432 if (clash) {
2433 /* symbol chosen matches a used monster or warning
2434 symbol which is not good - reject it*/
2435 pline(
2436 "Badoption - boulder symbol '%c' conflicts with a %s symbol.",
2437 opts[0], (clash == 1) ? "monster" : "warning");
2438 } else {
2440 * Override the default boulder symbol.
2442 iflags.bouldersym = (uchar) opts[0];
2444 if (!initial)
2445 need_redraw = TRUE;
2446 return;
2448 #endif
2450 /* name:string */
2451 fullname = "name";
2452 if (match_optname(opts, fullname, 4, TRUE)) {
2453 if (duplicate)
2454 complain_about_duplicate(opts, 1);
2455 if (negated)
2456 bad_negation(fullname, FALSE);
2457 else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0)
2458 nmcpy(plname, op, PL_NSIZ);
2459 return;
2462 /* altkeyhandler:string */
2463 fullname = "altkeyhandler";
2464 if (match_optname(opts, fullname, 4, TRUE)) {
2465 if (duplicate)
2466 complain_about_duplicate(opts, 1);
2467 if (negated) {
2468 bad_negation(fullname, FALSE);
2469 } else if ((op = string_for_opt(opts, negated)) != 0) {
2470 #ifdef WIN32
2471 (void) strncpy(iflags.altkeyhandler, op, MAX_ALTKEYHANDLER - 5);
2472 load_keyboard_handler();
2473 #endif
2475 return;
2478 /* WINCAP
2479 * align_status:[left|top|right|bottom] */
2480 fullname = "align_status";
2481 if (match_optname(opts, fullname, sizeof("align_status") - 1, TRUE)) {
2482 op = string_for_opt(opts, negated);
2483 if (op && !negated) {
2484 if (!strncmpi(op, "left", sizeof("left") - 1))
2485 iflags.wc_align_status = ALIGN_LEFT;
2486 else if (!strncmpi(op, "top", sizeof("top") - 1))
2487 iflags.wc_align_status = ALIGN_TOP;
2488 else if (!strncmpi(op, "right", sizeof("right") - 1))
2489 iflags.wc_align_status = ALIGN_RIGHT;
2490 else if (!strncmpi(op, "bottom", sizeof("bottom") - 1))
2491 iflags.wc_align_status = ALIGN_BOTTOM;
2492 else
2493 badoption(opts);
2494 } else if (negated)
2495 bad_negation(fullname, TRUE);
2496 return;
2498 /* WINCAP
2499 * align_message:[left|top|right|bottom] */
2500 fullname = "align_message";
2501 if (match_optname(opts, fullname, sizeof("align_message") - 1, TRUE)) {
2502 if (duplicate)
2503 complain_about_duplicate(opts, 1);
2504 op = string_for_opt(opts, negated);
2505 if (op && !negated) {
2506 if (!strncmpi(op, "left", sizeof("left") - 1))
2507 iflags.wc_align_message = ALIGN_LEFT;
2508 else if (!strncmpi(op, "top", sizeof("top") - 1))
2509 iflags.wc_align_message = ALIGN_TOP;
2510 else if (!strncmpi(op, "right", sizeof("right") - 1))
2511 iflags.wc_align_message = ALIGN_RIGHT;
2512 else if (!strncmpi(op, "bottom", sizeof("bottom") - 1))
2513 iflags.wc_align_message = ALIGN_BOTTOM;
2514 else
2515 badoption(opts);
2516 } else if (negated)
2517 bad_negation(fullname, TRUE);
2518 return;
2520 /* the order to list the pack */
2521 fullname = "packorder";
2522 if (match_optname(opts, fullname, 4, TRUE)) {
2523 if (duplicate)
2524 complain_about_duplicate(opts, 1);
2525 if (negated) {
2526 bad_negation(fullname, FALSE);
2527 return;
2528 } else if (!(op = string_for_opt(opts, FALSE)))
2529 return;
2531 if (!change_inv_order(op))
2532 badoption(opts);
2533 return;
2536 /* user can change required response for some prompts (quit, die, hit),
2537 or add an extra prompt (pray, Remove) that isn't ordinarily there */
2538 fullname = "paranoid_confirmation";
2539 if (match_optname(opts, fullname, 8, TRUE)) {
2540 /* at present we don't complain about duplicates for this
2541 option, but we do throw away the old settings whenever
2542 we process a new one [clearing old flags is essential
2543 for handling default paranoid_confirm:pray sanely] */
2544 flags.paranoia_bits = 0; /* clear all */
2545 if (negated) {
2546 flags.paranoia_bits = 0; /* [now redundant...] */
2547 } else if ((op = string_for_opt(opts, TRUE)) != 0) {
2548 char *pp, buf[BUFSZ];
2550 strncpy(buf, op, sizeof buf - 1);
2551 buf[sizeof buf - 1] = '\0';
2552 op = mungspaces(buf);
2553 for (;;) {
2554 /* We're looking to parse
2555 "paranoid_confirm:whichone wheretwo whothree"
2556 and "paranoid_confirm:" prefix has already
2557 been stripped off by the time we get here */
2558 pp = index(op, ' ');
2559 if (pp)
2560 *pp = '\0';
2561 /* we aren't matching option names but match_optname
2562 does what we want once we've broken the space
2563 delimited aggregate into separate tokens */
2564 for (i = 0; i < SIZE(paranoia); ++i) {
2565 if (match_optname(op, paranoia[i].argname,
2566 paranoia[i].argMinLen, FALSE)
2567 || (paranoia[i].synonym
2568 && match_optname(op, paranoia[i].synonym,
2569 paranoia[i].synMinLen, FALSE))) {
2570 if (paranoia[i].flagmask)
2571 flags.paranoia_bits |= paranoia[i].flagmask;
2572 else /* 0 == "none", so clear all */
2573 flags.paranoia_bits = 0;
2574 break;
2577 if (i == SIZE(paranoia)) {
2578 /* didn't match anything, so arg is bad;
2579 any flags already set will stay set */
2580 badoption(opts);
2581 break;
2583 /* move on to next token */
2584 if (pp)
2585 op = pp + 1;
2586 else
2587 break; /* no next token */
2588 } /* for(;;) */
2590 return;
2593 /* accept deprecated boolean; superseded by paranoid_confirm:pray */
2594 fullname = "prayconfirm";
2595 if (match_optname(opts, fullname, 4, FALSE)) {
2596 if (negated)
2597 flags.paranoia_bits &= ~PARANOID_PRAY;
2598 else
2599 flags.paranoia_bits |= PARANOID_PRAY;
2600 return;
2603 /* maximum burden picked up before prompt (Warren Cheung) */
2604 fullname = "pickup_burden";
2605 if (match_optname(opts, fullname, 8, TRUE)) {
2606 if (duplicate)
2607 complain_about_duplicate(opts, 1);
2608 if (negated) {
2609 bad_negation(fullname, FALSE);
2610 return;
2611 } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
2612 switch (lowc(*op)) {
2613 case 'u': /* Unencumbered */
2614 flags.pickup_burden = UNENCUMBERED;
2615 break;
2616 case 'b': /* Burdened (slight encumbrance) */
2617 flags.pickup_burden = SLT_ENCUMBER;
2618 break;
2619 case 's': /* streSsed (moderate encumbrance) */
2620 flags.pickup_burden = MOD_ENCUMBER;
2621 break;
2622 case 'n': /* straiNed (heavy encumbrance) */
2623 flags.pickup_burden = HVY_ENCUMBER;
2624 break;
2625 case 'o': /* OverTaxed (extreme encumbrance) */
2626 case 't':
2627 flags.pickup_burden = EXT_ENCUMBER;
2628 break;
2629 case 'l': /* overLoaded */
2630 flags.pickup_burden = OVERLOADED;
2631 break;
2632 default:
2633 badoption(opts);
2636 return;
2639 /* types of objects to pick up automatically */
2640 if (match_optname(opts, "pickup_types", 8, TRUE)) {
2641 char ocl[MAXOCLASSES + 1], tbuf[MAXOCLASSES + 1], qbuf[QBUFSZ],
2642 abuf[BUFSZ];
2643 int oc_sym;
2644 boolean badopt = FALSE, compat = (strlen(opts) <= 6), use_menu;
2646 if (duplicate)
2647 complain_about_duplicate(opts, 1);
2648 oc_to_str(flags.pickup_types, tbuf);
2649 flags.pickup_types[0] = '\0'; /* all */
2650 op = string_for_opt(opts, (compat || !initial));
2651 if (!op) {
2652 if (compat || negated || initial) {
2653 /* for backwards compatibility, "pickup" without a
2654 value is a synonym for autopickup of all types
2655 (and during initialization, we can't prompt yet) */
2656 flags.pickup = !negated;
2657 return;
2659 oc_to_str(flags.inv_order, ocl);
2660 use_menu = TRUE;
2661 if (flags.menu_style == MENU_TRADITIONAL
2662 || flags.menu_style == MENU_COMBINATION) {
2663 use_menu = FALSE;
2664 Sprintf(qbuf, "New pickup_types: [%s am] (%s)", ocl,
2665 *tbuf ? tbuf : "all");
2666 getlin(qbuf, abuf);
2667 op = mungspaces(abuf);
2668 if (abuf[0] == '\0' || abuf[0] == '\033')
2669 op = tbuf; /* restore */
2670 else if (abuf[0] == 'm')
2671 use_menu = TRUE;
2673 if (use_menu) {
2674 (void) choose_classes_menu("Auto-Pickup what?", 1, TRUE, ocl,
2675 tbuf);
2676 op = tbuf;
2679 if (negated) {
2680 bad_negation("pickup_types", TRUE);
2681 return;
2683 while (*op == ' ')
2684 op++;
2685 if (*op != 'a' && *op != 'A') {
2686 num = 0;
2687 while (*op) {
2688 oc_sym = def_char_to_objclass(*op);
2689 /* make sure all are valid obj symbols occurring once */
2690 if (oc_sym != MAXOCLASSES
2691 && !index(flags.pickup_types, oc_sym)) {
2692 flags.pickup_types[num] = (char) oc_sym;
2693 flags.pickup_types[++num] = '\0';
2694 } else
2695 badopt = TRUE;
2696 op++;
2698 if (badopt)
2699 badoption(opts);
2701 return;
2704 /* pile limit: when walking over objects, number which triggers
2705 "there are several/many objects here" instead of listing them */
2706 fullname = "pile_limit";
2707 if (match_optname(opts, fullname, 4, TRUE)) {
2708 if (duplicate)
2709 complain_about_duplicate(opts, 1);
2710 op = string_for_opt(opts, negated);
2711 if ((negated && !op) || (!negated && op))
2712 flags.pile_limit = negated ? 0 : atoi(op);
2713 else if (negated)
2714 bad_negation(fullname, TRUE);
2715 else /* !op */
2716 flags.pile_limit = PILE_LIMIT_DFLT;
2717 /* sanity check */
2718 if (flags.pile_limit < 0)
2719 flags.pile_limit = PILE_LIMIT_DFLT;
2720 return;
2723 /* play mode: normal, explore/discovery, or debug/wizard */
2724 fullname = "playmode";
2725 if (match_optname(opts, fullname, 4, TRUE)) {
2726 if (duplicate)
2727 complain_about_duplicate(opts, 1);
2728 if (negated)
2729 bad_negation(fullname, FALSE);
2730 if (duplicate || negated)
2731 return;
2732 op = string_for_opt(opts, FALSE);
2733 if (!op)
2734 return;
2735 if (!strncmpi(op, "normal", 6) || !strcmpi(op, "play")) {
2736 wizard = discover = FALSE;
2737 } else if (!strncmpi(op, "explore", 6)
2738 || !strncmpi(op, "discovery", 6)) {
2739 wizard = FALSE, discover = TRUE;
2740 } else if (!strncmpi(op, "debug", 5) || !strncmpi(op, "wizard", 6)) {
2741 wizard = TRUE, discover = FALSE;
2742 } else {
2743 raw_printf("Invalid value for \"%s\":%s.", fullname, op);
2745 return;
2748 /* WINCAP
2749 * player_selection: dialog | prompts */
2750 fullname = "player_selection";
2751 if (match_optname(opts, fullname, sizeof("player_selection") - 1, TRUE)) {
2752 if (duplicate)
2753 complain_about_duplicate(opts, 1);
2754 op = string_for_opt(opts, negated);
2755 if (op && !negated) {
2756 if (!strncmpi(op, "dialog", sizeof("dialog") - 1))
2757 iflags.wc_player_selection = VIA_DIALOG;
2758 else if (!strncmpi(op, "prompt", sizeof("prompt") - 1))
2759 iflags.wc_player_selection = VIA_PROMPTS;
2760 else
2761 badoption(opts);
2762 } else if (negated)
2763 bad_negation(fullname, TRUE);
2764 return;
2767 /* things to disclose at end of game */
2768 if (match_optname(opts, "disclose", 7, TRUE)) {
2770 * The order that the end_disclose options are stored:
2771 * inventory, attribs, vanquished, genocided,
2772 * conduct, overview.
2773 * There is an array in flags:
2774 * end_disclose[NUM_DISCLOSURE_OPT];
2775 * with option settings for the each of the following:
2776 * iagvc [see disclosure_options in decl.c]:
2777 * Legal setting values in that array are:
2778 * DISCLOSE_PROMPT_DEFAULT_YES ask with default answer yes
2779 * DISCLOSE_PROMPT_DEFAULT_NO ask with default answer no
2780 * DISCLOSE_YES_WITHOUT_PROMPT always disclose and don't ask
2781 * DISCLOSE_NO_WITHOUT_PROMPT never disclose and don't ask
2783 * Those setting values can be used in the option
2784 * string as a prefix to get the desired behaviour.
2786 * For backward compatibility, no prefix is required,
2787 * and the presence of a i,a,g,v, or c without a prefix
2788 * sets the corresponding value to DISCLOSE_YES_WITHOUT_PROMPT.
2790 boolean badopt = FALSE;
2791 int idx, prefix_val;
2793 if (duplicate)
2794 complain_about_duplicate(opts, 1);
2795 op = string_for_opt(opts, TRUE);
2796 if (op && negated) {
2797 bad_negation("disclose", TRUE);
2798 return;
2800 /* "disclose" without a value means "all with prompting"
2801 and negated means "none without prompting" */
2802 if (!op || !strcmpi(op, "all") || !strcmpi(op, "none")) {
2803 if (op && !strcmpi(op, "none"))
2804 negated = TRUE;
2805 for (num = 0; num < NUM_DISCLOSURE_OPTIONS; num++)
2806 flags.end_disclose[num] = negated
2807 ? DISCLOSE_NO_WITHOUT_PROMPT
2808 : DISCLOSE_PROMPT_DEFAULT_YES;
2809 return;
2812 num = 0;
2813 prefix_val = -1;
2814 while (*op && num < sizeof flags.end_disclose - 1) {
2815 static char valid_settings[] = {
2816 DISCLOSE_PROMPT_DEFAULT_YES, DISCLOSE_PROMPT_DEFAULT_NO,
2817 DISCLOSE_PROMPT_DEFAULT_SPECIAL,
2818 DISCLOSE_YES_WITHOUT_PROMPT, DISCLOSE_NO_WITHOUT_PROMPT,
2819 DISCLOSE_SPECIAL_WITHOUT_PROMPT, '\0'
2821 register char c, *dop;
2823 c = lowc(*op);
2824 if (c == 'k')
2825 c = 'v'; /* killed -> vanquished */
2826 if (c == 'd')
2827 c = 'o'; /* dungeon -> overview */
2828 dop = index(disclosure_options, c);
2829 if (dop) {
2830 idx = (int) (dop - disclosure_options);
2831 if (idx < 0 || idx > NUM_DISCLOSURE_OPTIONS - 1) {
2832 impossible("bad disclosure index %d %c", idx, c);
2833 continue;
2835 if (prefix_val != -1) {
2836 if (*dop != 'v') {
2837 if (prefix_val == DISCLOSE_PROMPT_DEFAULT_SPECIAL)
2838 prefix_val = DISCLOSE_PROMPT_DEFAULT_YES;
2839 if (prefix_val == DISCLOSE_SPECIAL_WITHOUT_PROMPT)
2840 prefix_val = DISCLOSE_YES_WITHOUT_PROMPT;
2842 flags.end_disclose[idx] = prefix_val;
2843 prefix_val = -1;
2844 } else
2845 flags.end_disclose[idx] = DISCLOSE_YES_WITHOUT_PROMPT;
2846 } else if (index(valid_settings, c)) {
2847 prefix_val = c;
2848 } else if (c == ' ') {
2849 ; /* do nothing */
2850 } else
2851 badopt = TRUE;
2852 op++;
2854 if (badopt)
2855 badoption(opts);
2856 return;
2859 /* scores:5t[op] 5a[round] o[wn] */
2860 if (match_optname(opts, "scores", 4, TRUE)) {
2861 if (duplicate)
2862 complain_about_duplicate(opts, 1);
2863 if (negated) {
2864 bad_negation("scores", FALSE);
2865 return;
2867 if (!(op = string_for_opt(opts, FALSE)))
2868 return;
2870 while (*op) {
2871 int inum = 1;
2873 if (digit(*op)) {
2874 inum = atoi(op);
2875 while (digit(*op))
2876 op++;
2877 } else if (*op == '!') {
2878 negated = !negated;
2879 op++;
2881 while (*op == ' ')
2882 op++;
2884 switch (*op) {
2885 case 't':
2886 case 'T':
2887 flags.end_top = inum;
2888 break;
2889 case 'a':
2890 case 'A':
2891 flags.end_around = inum;
2892 break;
2893 case 'o':
2894 case 'O':
2895 flags.end_own = !negated;
2896 break;
2897 default:
2898 badoption(opts);
2899 return;
2901 while (letter(*++op) || *op == ' ')
2902 continue;
2903 if (*op == '/')
2904 op++;
2906 return;
2909 fullname = "sortloot";
2910 if (match_optname(opts, fullname, 4, TRUE)) {
2911 op = string_for_env_opt(fullname, opts, FALSE);
2912 if (op) {
2913 char c = lowc(*op);
2915 switch (c) {
2916 case 'n': /* none */
2917 case 'l': /* loot (pickup) */
2918 case 'f': /* full (pickup + invent) */
2919 flags.sortloot = c;
2920 break;
2921 default:
2922 badoption(opts);
2923 return;
2926 return;
2929 fullname = "suppress_alert";
2930 if (match_optname(opts, fullname, 4, TRUE)) {
2931 if (duplicate)
2932 complain_about_duplicate(opts, 1);
2933 op = string_for_opt(opts, negated);
2934 if (negated)
2935 bad_negation(fullname, FALSE);
2936 else if (op)
2937 (void) feature_alert_opts(op, fullname);
2938 return;
2941 #ifdef VIDEOSHADES
2942 /* videocolors:string */
2943 fullname = "videocolors";
2944 if (match_optname(opts, fullname, 6, TRUE)
2945 || match_optname(opts, "videocolours", 10, TRUE)) {
2946 if (duplicate)
2947 complain_about_duplicate(opts, 1);
2948 if (negated) {
2949 bad_negation(fullname, FALSE);
2950 return;
2951 } else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
2952 return;
2954 if (!assign_videocolors(opts))
2955 badoption(opts);
2956 return;
2958 /* videoshades:string */
2959 fullname = "videoshades";
2960 if (match_optname(opts, fullname, 6, TRUE)) {
2961 if (duplicate)
2962 complain_about_duplicate(opts, 1);
2963 if (negated) {
2964 bad_negation(fullname, FALSE);
2965 return;
2966 } else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
2967 return;
2969 if (!assign_videoshades(opts))
2970 badoption(opts);
2971 return;
2973 #endif /* VIDEOSHADES */
2974 #ifdef MSDOS
2975 #ifdef NO_TERMS
2976 /* video:string -- must be after longer tests */
2977 fullname = "video";
2978 if (match_optname(opts, fullname, 5, TRUE)) {
2979 if (duplicate)
2980 complain_about_duplicate(opts, 1);
2981 if (negated) {
2982 bad_negation(fullname, FALSE);
2983 return;
2984 } else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
2985 return;
2987 if (!assign_video(opts))
2988 badoption(opts);
2989 return;
2991 #endif /* NO_TERMS */
2992 /* soundcard:string -- careful not to match boolean 'sound' */
2993 fullname = "soundcard";
2994 if (match_optname(opts, fullname, 6, TRUE)) {
2995 if (duplicate)
2996 complain_about_duplicate(opts, 1);
2997 if (negated) {
2998 bad_negation(fullname, FALSE);
2999 return;
3000 } else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
3001 return;
3003 if (!assign_soundcard(opts))
3004 badoption(opts);
3005 return;
3007 #endif /* MSDOS */
3009 /* WINCAP
3011 * map_mode:[tiles|ascii4x6|ascii6x8|ascii8x8|ascii16x8|ascii7x12|
3012 * ascii8x12|ascii16x12|ascii12x16|ascii10x18|fit_to_screen]
3014 fullname = "map_mode";
3015 if (match_optname(opts, fullname, sizeof("map_mode") - 1, TRUE)) {
3016 if (duplicate)
3017 complain_about_duplicate(opts, 1);
3018 op = string_for_opt(opts, negated);
3019 if (op && !negated) {
3020 if (!strncmpi(op, "tiles", sizeof("tiles") - 1))
3021 iflags.wc_map_mode = MAP_MODE_TILES;
3022 else if (!strncmpi(op, "ascii4x6", sizeof("ascii4x6") - 1))
3023 iflags.wc_map_mode = MAP_MODE_ASCII4x6;
3024 else if (!strncmpi(op, "ascii6x8", sizeof("ascii6x8") - 1))
3025 iflags.wc_map_mode = MAP_MODE_ASCII6x8;
3026 else if (!strncmpi(op, "ascii8x8", sizeof("ascii8x8") - 1))
3027 iflags.wc_map_mode = MAP_MODE_ASCII8x8;
3028 else if (!strncmpi(op, "ascii16x8", sizeof("ascii16x8") - 1))
3029 iflags.wc_map_mode = MAP_MODE_ASCII16x8;
3030 else if (!strncmpi(op, "ascii7x12", sizeof("ascii7x12") - 1))
3031 iflags.wc_map_mode = MAP_MODE_ASCII7x12;
3032 else if (!strncmpi(op, "ascii8x12", sizeof("ascii8x12") - 1))
3033 iflags.wc_map_mode = MAP_MODE_ASCII8x12;
3034 else if (!strncmpi(op, "ascii16x12", sizeof("ascii16x12") - 1))
3035 iflags.wc_map_mode = MAP_MODE_ASCII16x12;
3036 else if (!strncmpi(op, "ascii12x16", sizeof("ascii12x16") - 1))
3037 iflags.wc_map_mode = MAP_MODE_ASCII12x16;
3038 else if (!strncmpi(op, "ascii10x18", sizeof("ascii10x18") - 1))
3039 iflags.wc_map_mode = MAP_MODE_ASCII10x18;
3040 else if (!strncmpi(op, "fit_to_screen",
3041 sizeof("fit_to_screen") - 1))
3042 iflags.wc_map_mode = MAP_MODE_ASCII_FIT_TO_SCREEN;
3043 else
3044 badoption(opts);
3045 } else if (negated)
3046 bad_negation(fullname, TRUE);
3047 return;
3049 /* WINCAP
3050 * scroll_amount:nn */
3051 fullname = "scroll_amount";
3052 if (match_optname(opts, fullname, sizeof("scroll_amount") - 1, TRUE)) {
3053 if (duplicate)
3054 complain_about_duplicate(opts, 1);
3055 op = string_for_opt(opts, negated);
3056 if ((negated && !op) || (!negated && op)) {
3057 iflags.wc_scroll_amount = negated ? 1 : atoi(op);
3058 } else if (negated)
3059 bad_negation(fullname, TRUE);
3060 return;
3062 /* WINCAP
3063 * scroll_margin:nn */
3064 fullname = "scroll_margin";
3065 if (match_optname(opts, fullname, sizeof("scroll_margin") - 1, TRUE)) {
3066 if (duplicate)
3067 complain_about_duplicate(opts, 1);
3068 op = string_for_opt(opts, negated);
3069 if ((negated && !op) || (!negated && op)) {
3070 iflags.wc_scroll_margin = negated ? 5 : atoi(op);
3071 } else if (negated)
3072 bad_negation(fullname, TRUE);
3073 return;
3075 fullname = "subkeyvalue";
3076 if (match_optname(opts, fullname, 5, TRUE)) {
3077 /* no duplicate complaint here */
3078 if (negated) {
3079 bad_negation(fullname, FALSE);
3080 } else {
3081 #if defined(WIN32)
3082 op = string_for_opt(opts, 0);
3083 map_subkeyvalue(op);
3084 #endif
3086 return;
3088 /* WINCAP
3089 * tile_width:nn */
3090 fullname = "tile_width";
3091 if (match_optname(opts, fullname, sizeof("tile_width") - 1, TRUE)) {
3092 if (duplicate)
3093 complain_about_duplicate(opts, 1);
3094 op = string_for_opt(opts, negated);
3095 if ((negated && !op) || (!negated && op)) {
3096 iflags.wc_tile_width = negated ? 0 : atoi(op);
3097 } else if (negated)
3098 bad_negation(fullname, TRUE);
3099 return;
3101 /* WINCAP
3102 * tile_file:name */
3103 fullname = "tile_file";
3104 if (match_optname(opts, fullname, sizeof("tile_file") - 1, TRUE)) {
3105 if (duplicate)
3106 complain_about_duplicate(opts, 1);
3107 if ((op = string_for_opt(opts, FALSE)) != 0) {
3108 if (iflags.wc_tile_file)
3109 free(iflags.wc_tile_file);
3110 iflags.wc_tile_file = dupstr(op);
3112 return;
3114 /* WINCAP
3115 * tile_height:nn */
3116 fullname = "tile_height";
3117 if (match_optname(opts, fullname, sizeof("tile_height") - 1, TRUE)) {
3118 if (duplicate)
3119 complain_about_duplicate(opts, 1);
3120 op = string_for_opt(opts, negated);
3121 if ((negated && !op) || (!negated && op)) {
3122 iflags.wc_tile_height = negated ? 0 : atoi(op);
3123 } else if (negated)
3124 bad_negation(fullname, TRUE);
3125 return;
3127 /* WINCAP
3128 * vary_msgcount:nn */
3129 fullname = "vary_msgcount";
3130 if (match_optname(opts, fullname, sizeof("vary_msgcount") - 1, TRUE)) {
3131 if (duplicate)
3132 complain_about_duplicate(opts, 1);
3133 op = string_for_opt(opts, negated);
3134 if ((negated && !op) || (!negated && op)) {
3135 iflags.wc_vary_msgcount = negated ? 0 : atoi(op);
3136 } else if (negated)
3137 bad_negation(fullname, TRUE);
3138 return;
3140 fullname = "windowtype";
3141 if (match_optname(opts, fullname, 3, TRUE)) {
3142 if (duplicate)
3143 complain_about_duplicate(opts, 1);
3144 if (negated) {
3145 bad_negation(fullname, FALSE);
3146 return;
3147 } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
3148 char buf[WINTYPELEN];
3149 nmcpy(buf, op, WINTYPELEN);
3150 choose_windows(buf);
3152 return;
3154 #ifdef WINCHAIN
3155 fullname = "windowchain";
3156 if (match_optname(opts, fullname, 3, TRUE)) {
3157 if (negated) {
3158 bad_negation(fullname, FALSE);
3159 return;
3160 } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) {
3161 char buf[WINTYPELEN];
3162 nmcpy(buf, op, WINTYPELEN);
3163 addto_windowchain(buf);
3165 return;
3167 #endif
3169 /* WINCAP
3170 * setting window colors
3171 * syntax: windowcolors=menu foregrnd/backgrnd text foregrnd/backgrnd
3173 fullname = "windowcolors";
3174 if (match_optname(opts, fullname, 7, TRUE)) {
3175 if (duplicate)
3176 complain_about_duplicate(opts, 1);
3177 if ((op = string_for_opt(opts, FALSE)) != 0) {
3178 if (!wc_set_window_colors(op))
3179 badoption(opts);
3180 } else if (negated)
3181 bad_negation(fullname, TRUE);
3182 return;
3185 /* menustyle:traditional or combination or full or partial */
3186 if (match_optname(opts, "menustyle", 4, TRUE)) {
3187 int tmp;
3188 boolean val_required = (strlen(opts) > 5 && !negated);
3190 if (duplicate)
3191 complain_about_duplicate(opts, 1);
3192 if (!(op = string_for_opt(opts, !val_required))) {
3193 if (val_required)
3194 return; /* string_for_opt gave feedback */
3195 tmp = negated ? 'n' : 'f';
3196 } else {
3197 tmp = lowc(*op);
3199 switch (tmp) {
3200 case 'n': /* none */
3201 case 't': /* traditional: prompt for class(es) by symbol,
3202 prompt for each item within class(es) one at a time */
3203 flags.menu_style = MENU_TRADITIONAL;
3204 break;
3205 case 'c': /* combination: prompt for class(es) by symbol,
3206 choose items within selected class(es) by menu */
3207 flags.menu_style = MENU_COMBINATION;
3208 break;
3209 case 'f': /* full: choose class(es) by first menu,
3210 choose items within selected class(es) by second menu */
3211 flags.menu_style = MENU_FULL;
3212 break;
3213 case 'p': /* partial: skip class filtering,
3214 choose items among all classes by menu */
3215 flags.menu_style = MENU_PARTIAL;
3216 break;
3217 default:
3218 badoption(opts);
3220 return;
3223 fullname = "menu_headings";
3224 if (match_optname(opts, fullname, 12, TRUE)) {
3225 if (duplicate)
3226 complain_about_duplicate(opts, 1);
3227 if (negated) {
3228 bad_negation(fullname, FALSE);
3229 return;
3230 } else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) {
3231 return;
3233 for (i = 0; i < SIZE(attrnames); i++)
3234 if (!strcmpi(opts, attrnames[i].name)) {
3235 iflags.menu_headings = attrnames[i].attr;
3236 return;
3238 badoption(opts);
3239 return;
3242 /* check for menu command mapping */
3243 for (i = 0; i < SIZE(default_menu_cmd_info); i++) {
3244 fullname = default_menu_cmd_info[i].name;
3245 if (duplicate)
3246 complain_about_duplicate(opts, 1);
3247 if (match_optname(opts, fullname, (int) strlen(fullname), TRUE)) {
3248 if (negated) {
3249 bad_negation(fullname, FALSE);
3250 } else if ((op = string_for_opt(opts, FALSE)) != 0) {
3251 char c, op_buf[BUFSZ];
3253 escapes(op, op_buf);
3254 c = *op_buf;
3256 if (illegal_menu_cmd_key(c))
3257 badoption(opts);
3258 else
3259 add_menu_cmd_alias(c, default_menu_cmd_info[i].cmd);
3261 return;
3264 #if defined(STATUS_VIA_WINDOWPORT) && defined(STATUS_HILITES)
3265 /* hilite fields in status prompt */
3266 if (match_optname(opts, "hilite_status", 13, TRUE)) {
3267 if (duplicate)
3268 complain_about_duplicate(opts, 1);
3269 op = string_for_opt(opts, TRUE);
3270 if (op && negated) {
3271 clear_status_hilites(tfrom_file);
3272 return;
3273 } else if (!op) {
3274 /* a value is mandatory */
3275 badoption(opts);
3276 return;
3278 if (!set_status_hilites(op, tfrom_file))
3279 badoption(opts);
3280 return;
3282 #endif
3284 #if defined(BACKWARD_COMPAT)
3285 fullname = "DECgraphics";
3286 if (match_optname(opts, fullname, 3, TRUE)) {
3287 boolean badflag = FALSE;
3289 if (duplicate)
3290 complain_about_duplicate(opts, 1);
3291 if (!negated) {
3292 /* There is no rogue level DECgraphics-specific set */
3293 if (symset[PRIMARY].name) {
3294 badflag = TRUE;
3295 } else {
3296 symset[PRIMARY].name = dupstr(fullname);
3297 if (!read_sym_file(PRIMARY)) {
3298 badflag = TRUE;
3299 clear_symsetentry(PRIMARY, TRUE);
3300 } else
3301 switch_symbols(TRUE);
3303 if (badflag) {
3304 pline("Failure to load symbol set %s.", fullname);
3305 wait_synch();
3308 return;
3310 fullname = "IBMgraphics";
3311 if (match_optname(opts, fullname, 3, TRUE)) {
3312 const char *sym_name = fullname;
3313 boolean badflag = FALSE;
3315 if (duplicate)
3316 complain_about_duplicate(opts, 1);
3317 if (!negated) {
3318 for (i = 0; i < NUM_GRAPHICS; ++i) {
3319 if (symset[i].name) {
3320 badflag = TRUE;
3321 } else {
3322 if (i == ROGUESET)
3323 sym_name = "RogueIBM";
3324 symset[i].name = dupstr(sym_name);
3325 if (!read_sym_file(i)) {
3326 badflag = TRUE;
3327 clear_symsetentry(i, TRUE);
3328 break;
3332 if (badflag) {
3333 pline("Failure to load symbol set %s.", sym_name);
3334 wait_synch();
3335 } else {
3336 switch_symbols(TRUE);
3337 if (!initial && Is_rogue_level(&u.uz))
3338 assign_graphics(ROGUESET);
3341 return;
3343 #endif
3344 #ifdef MAC_GRAPHICS_ENV
3345 fullname = "MACgraphics";
3346 if (match_optname(opts, fullname, 3, TRUE)) {
3347 boolean badflag = FALSE;
3349 if (duplicate)
3350 complain_about_duplicate(opts, 1);
3351 if (!negated) {
3352 if (symset[PRIMARY].name) {
3353 badflag = TRUE;
3354 } else {
3355 symset[PRIMARY].name = dupstr(fullname);
3356 if (!read_sym_file(PRIMARY)) {
3357 badflag = TRUE;
3358 clear_symsetentry(PRIMARY, TRUE);
3361 if (badflag) {
3362 pline("Failure to load symbol set %s.", fullname);
3363 wait_synch();
3364 } else {
3365 switch_symbols(TRUE);
3366 if (!initial && Is_rogue_level(&u.uz))
3367 assign_graphics(ROGUESET);
3370 return;
3372 #endif
3374 /* OK, if we still haven't recognized the option, check the boolean
3375 * options list
3377 for (i = 0; boolopt[i].name; i++) {
3378 if (match_optname(opts, boolopt[i].name, 3, TRUE)) {
3379 /* options that don't exist */
3380 if (!boolopt[i].addr) {
3381 if (!initial && !negated)
3382 pline_The("\"%s\" option is not available.",
3383 boolopt[i].name);
3384 return;
3386 /* options that must come from config file */
3387 if (!initial && (boolopt[i].optflags == SET_IN_FILE)) {
3388 rejectoption(boolopt[i].name);
3389 return;
3392 op = string_for_opt(opts, TRUE);
3394 if (op) {
3395 if (negated) {
3396 badoption(opts);
3397 return;
3399 if (!strcmp(op, "true") || !strcmp(op, "yes")) {
3400 negated = FALSE;
3401 } else if (!strcmp(op, "false") || !strcmp(op, "no")) {
3402 negated = TRUE;
3403 } else {
3404 badoption(opts);
3405 return;
3409 *(boolopt[i].addr) = !negated;
3411 /* 0 means boolean opts */
3412 if (duplicate_opt_detection(boolopt[i].name, 0))
3413 complain_about_duplicate(boolopt[i].name, 0);
3414 #ifdef RLECOMP
3415 if (boolopt[i].addr == &iflags.rlecomp)
3416 set_savepref(iflags.rlecomp ? "rlecomp" : "!rlecomp");
3417 #endif
3418 #ifdef ZEROCOMP
3419 if (boolopt[i].addr == &iflags.zerocomp)
3420 set_savepref(iflags.zerocomp ? "zerocomp" : "externalcomp");
3421 #endif
3422 /* only do processing below if setting with doset() */
3423 if (initial)
3424 return;
3426 if (boolopt[i].addr == &flags.time
3427 #ifdef SCORE_ON_BOTL
3428 || boolopt[i].addr == &flags.showscore
3429 #endif
3430 || boolopt[i].addr == &flags.showexp) {
3431 #ifdef STATUS_VIA_WINDOWPORT
3432 status_initialize(REASSESS_ONLY);
3433 #endif
3434 context.botl = TRUE;
3435 } else if (boolopt[i].addr == &flags.invlet_constant) {
3436 if (flags.invlet_constant)
3437 reassign();
3438 } else if (boolopt[i].addr == &flags.lit_corridor
3439 || boolopt[i].addr == &flags.dark_room) {
3441 * All corridor squares seen via night vision or
3442 * candles & lamps change. Update them by calling
3443 * newsym() on them. Don't do this if we are
3444 * initializing the options --- the vision system
3445 * isn't set up yet.
3447 vision_recalc(2); /* shut down vision */
3448 vision_full_recalc = 1; /* delayed recalc */
3449 if (iflags.use_color)
3450 need_redraw = TRUE; /* darkroom refresh */
3451 } else if (boolopt[i].addr == &iflags.wc_tiled_map
3452 || boolopt[i].addr == &flags.showrace
3453 || boolopt[i].addr == &iflags.use_inverse
3454 || boolopt[i].addr == &iflags.hilite_pile
3455 || boolopt[i].addr == &iflags.hilite_pet) {
3456 need_redraw = TRUE;
3457 #ifdef TEXTCOLOR
3458 } else if (boolopt[i].addr == &iflags.use_color) {
3459 need_redraw = TRUE;
3460 #ifdef TOS
3461 if (iflags.BIOS) {
3462 if (colors_changed)
3463 restore_colors();
3464 else
3465 set_colors();
3467 #endif
3468 #endif /* TEXTCOLOR */
3470 return;
3474 /* Is it a symbol? */
3475 if (strstr(opts, "S_") == opts && parsesymbols(opts)) {
3476 switch_symbols(TRUE);
3477 return;
3480 /* out of valid options */
3481 badoption(opts);
3484 /* parse key:command */
3485 void
3486 parsebindings(bindings)
3487 char* bindings;
3489 char *bind;
3490 char key;
3491 int i;
3493 /* break off first binding from the rest; parse the rest */
3494 if ((bind = index(bindings, ',')) != 0) {
3495 *bind++ = 0;
3496 parsebindings(bind);
3499 /* parse a single binding: first split around : */
3500 if (! (bind = index(bindings, ':'))) return; /* it's not a binding */
3501 *bind++ = 0;
3503 /* read the key to be bound */
3504 key = txt2key(bindings);
3505 if (!key) {
3506 raw_printf("Bad binding %s.", bindings);
3507 wait_synch();
3508 return;
3511 bind = trimspaces(bind);
3513 /* is it a special key? */
3514 if (bind_specialkey(key, bind))
3515 return;
3517 /* is it a menu command? */
3518 for (i = 0; i < SIZE(default_menu_cmd_info); i++) {
3519 if (!strcmp(default_menu_cmd_info[i].name, bind)) {
3520 if (illegal_menu_cmd_key(key)) {
3521 char tmp[BUFSZ];
3522 Sprintf(tmp, "Bad menu key %s:%s", visctrl(key), bind);
3523 badoption(tmp);
3524 } else
3525 add_menu_cmd_alias(key, default_menu_cmd_info[i].cmd);
3526 return;
3530 /* extended command? */
3531 bind_key(key, bind);
3534 static NEARDATA const char *menutype[] = { "traditional", "combination",
3535 "full", "partial" };
3537 static NEARDATA const char *burdentype[] = { "unencumbered", "burdened",
3538 "stressed", "strained",
3539 "overtaxed", "overloaded" };
3541 static NEARDATA const char *runmodes[] = { "teleport", "run", "walk",
3542 "crawl" };
3544 static NEARDATA const char *sortltype[] = { "none", "loot", "full" };
3547 * Convert the given string of object classes to a string of default object
3548 * symbols.
3550 STATIC_OVL void
3551 oc_to_str(src, dest)
3552 char *src, *dest;
3554 int i;
3556 while ((i = (int) *src++) != 0) {
3557 if (i < 0 || i >= MAXOCLASSES)
3558 impossible("oc_to_str: illegal object class %d", i);
3559 else
3560 *dest++ = def_oc_syms[i].sym;
3562 *dest = '\0';
3566 * Add the given mapping to the menu command map list. Always keep the
3567 * maps valid C strings.
3569 void
3570 add_menu_cmd_alias(from_ch, to_ch)
3571 char from_ch, to_ch;
3573 if (n_menu_mapped >= MAX_MENU_MAPPED_CMDS) {
3574 pline("out of menu map space.");
3575 } else {
3576 mapped_menu_cmds[n_menu_mapped] = from_ch;
3577 mapped_menu_op[n_menu_mapped] = to_ch;
3578 n_menu_mapped++;
3579 mapped_menu_cmds[n_menu_mapped] = 0;
3580 mapped_menu_op[n_menu_mapped] = 0;
3584 char
3585 get_menu_cmd_key(ch)
3586 char ch;
3588 char *found = index(mapped_menu_op, ch);
3589 if (found) {
3590 int idx = (int) (found - mapped_menu_op);
3591 ch = mapped_menu_cmds[idx];
3593 return ch;
3597 * Map the given character to its corresponding menu command. If it
3598 * doesn't match anything, just return the original.
3600 char
3601 map_menu_cmd(ch)
3602 char ch;
3604 char *found = index(mapped_menu_cmds, ch);
3605 if (found) {
3606 int idx = (int) (found - mapped_menu_cmds);
3607 ch = mapped_menu_op[idx];
3609 return ch;
3612 void
3613 show_menu_controls(win, dolist)
3614 winid win;
3615 boolean dolist;
3617 char buf[BUFSZ];
3619 putstr(win, 0, "Menu control keys:");
3620 if (dolist) {
3621 int i;
3622 for (i = 0; i < SIZE(default_menu_cmd_info); i++) {
3623 Sprintf(buf, "%-8s %s",
3624 visctrl(get_menu_cmd_key(default_menu_cmd_info[i].cmd)),
3625 default_menu_cmd_info[i].desc);
3626 putstr(win, 0, buf);
3628 } else {
3629 putstr(win, 0, "");
3630 putstr(win, 0, " Page All items");
3631 Sprintf(buf, " Select %s %s",
3632 visctrl(get_menu_cmd_key(MENU_SELECT_PAGE)),
3633 visctrl(get_menu_cmd_key(MENU_SELECT_ALL)));
3634 putstr(win, 0, buf);
3635 Sprintf(buf, "Deselect %s %s",
3636 visctrl(get_menu_cmd_key(MENU_UNSELECT_PAGE)),
3637 visctrl(get_menu_cmd_key(MENU_UNSELECT_ALL)));
3638 putstr(win, 0, buf);
3639 Sprintf(buf, " Invert %s %s",
3640 visctrl(get_menu_cmd_key(MENU_INVERT_PAGE)),
3641 visctrl(get_menu_cmd_key(MENU_INVERT_ALL)));
3642 putstr(win, 0, buf);
3643 putstr(win, 0, "");
3644 Sprintf(buf, " Go to %s Next page",
3645 visctrl(get_menu_cmd_key(MENU_NEXT_PAGE)));
3646 putstr(win, 0, buf);
3647 Sprintf(buf, " %s Previous page",
3648 visctrl(get_menu_cmd_key(MENU_PREVIOUS_PAGE)));
3649 putstr(win, 0, buf);
3650 Sprintf(buf, " %s First page",
3651 visctrl(get_menu_cmd_key(MENU_FIRST_PAGE)));
3652 putstr(win, 0, buf);
3653 Sprintf(buf, " %s Last page",
3654 visctrl(get_menu_cmd_key(MENU_LAST_PAGE)));
3655 putstr(win, 0, buf);
3656 putstr(win, 0, "");
3657 Sprintf(buf, " %s Search and toggle matching entries",
3658 visctrl(get_menu_cmd_key(MENU_SEARCH)));
3659 putstr(win, 0, buf);
3663 #if defined(MICRO) || defined(MAC) || defined(WIN32)
3664 #define OPTIONS_HEADING "OPTIONS"
3665 #else
3666 #define OPTIONS_HEADING "ANETHACKOPTIONS"
3667 #endif
3669 static char fmtstr_doset[] = "%s%-15s [%s] ";
3670 static char fmtstr_doset_tab[] = "%s\t[%s]";
3671 static char n_currently_set[] = "(%d currently set)";
3673 /* doset('O' command) menu entries for compound options */
3674 STATIC_OVL void
3675 doset_add_menu(win, option, indexoffset)
3676 winid win; /* window to add to */
3677 const char *option; /* option name */
3678 int indexoffset; /* value to add to index in compopt[], or zero
3679 if option cannot be changed */
3681 const char *value = "unknown"; /* current value */
3682 char buf[BUFSZ], buf2[BUFSZ];
3683 anything any;
3684 int i;
3686 any = zeroany;
3687 if (indexoffset == 0) {
3688 any.a_int = 0;
3689 value = get_compopt_value(option, buf2);
3690 } else {
3691 for (i = 0; compopt[i].name; i++)
3692 if (strcmp(option, compopt[i].name) == 0)
3693 break;
3695 if (compopt[i].name) {
3696 any.a_int = i + 1 + indexoffset;
3697 value = get_compopt_value(option, buf2);
3698 } else {
3699 /* We are trying to add an option not found in compopt[].
3700 This is almost certainly bad, but we'll let it through anyway
3701 (with a zero value, so it can't be selected). */
3702 any.a_int = 0;
3705 /* " " replaces "a - " -- assumes menus follow that style */
3706 if (!iflags.menu_tab_sep)
3707 Sprintf(buf, fmtstr_doset, any.a_int ? "" : " ", option,
3708 value);
3709 else
3710 Sprintf(buf, fmtstr_doset_tab, option, value);
3711 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
3714 STATIC_OVL void
3715 opts_add_others(win, name, id, bufx, nset)
3716 winid win;
3717 const char *name;
3718 int id;
3719 char *bufx;
3720 int nset;
3722 char buf[BUFSZ], buf2[BUFSZ];
3723 anything any = zeroany;
3725 any.a_int = id;
3726 if (!bufx)
3727 Sprintf(buf2, n_currently_set, nset);
3728 else
3729 Sprintf(buf2, "%s", bufx);
3730 if (!iflags.menu_tab_sep)
3731 Sprintf(buf, fmtstr_doset, any.a_int ? "" : " ",
3732 name, buf2);
3733 else
3734 Sprintf(buf, fmtstr_doset_tab, name, buf2);
3735 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
3738 enum opt_other_enums {
3739 OPT_OTHER_MSGTYPE = -4,
3740 OPT_OTHER_MENUCOLOR = -3,
3741 OPT_OTHER_STATHILITE = -2,
3742 OPT_OTHER_APEXC = -1
3743 /* these must be < 0 */
3746 /* presently only used when determining longest option name */
3747 static struct other_opts {
3748 const char *name;
3749 int optflags;
3750 enum opt_other_enums code;
3751 } othropt[] = {
3752 { "autopickup exceptions", SET_IN_GAME, OPT_OTHER_APEXC },
3753 { "menucolors", SET_IN_GAME, OPT_OTHER_MENUCOLOR },
3754 { "message types", SET_IN_GAME, OPT_OTHER_MSGTYPE },
3755 #ifdef STATUS_VIA_WINDOWPORT
3756 #ifdef STATUS_HILITES
3757 { "status_hilites", SET_IN_GAME, OPT_OTHER_STATHILITE },
3758 #endif
3759 #endif
3760 { (char *) 0, 0, (enum opt_other_enums) 0 },
3763 /* the 'O' command */
3765 doset() /* changing options via menu by Per Liboriussen */
3767 static boolean made_fmtstr = FALSE;
3768 char buf[BUFSZ], buf2[BUFSZ];
3769 const char *name;
3770 int i = 0, pass, boolcount, pick_cnt, pick_idx, opt_indx;
3771 boolean *bool_p;
3772 winid tmpwin;
3773 anything any;
3774 menu_item *pick_list;
3775 int indexoffset, startpass, endpass, optflags;
3776 boolean setinitial = FALSE, fromfile = FALSE;
3777 unsigned longest_name_len;
3779 tmpwin = create_nhwindow(NHW_MENU);
3780 start_menu(tmpwin);
3782 #ifdef notyet /* SYSCF */
3783 /* XXX I think this is still fragile. Fixing initial/from_file and/or
3784 changing the SET_* etc to bitmaps will let me make this better. */
3785 if (wizard)
3786 startpass = SET_IN_SYS;
3787 else
3788 #endif
3789 startpass = DISP_IN_GAME;
3790 endpass = (wizard) ? SET_IN_WIZGAME : SET_IN_GAME;
3792 if (!made_fmtstr && !iflags.menu_tab_sep) {
3793 /* spin through the options to find the longest name
3794 and adjust the format string accordingly */
3795 longest_name_len = 0;
3796 for (pass = 0; pass <= 2; pass++)
3797 for (i = 0; (name = ((pass == 0)
3798 ? boolopt[i].name
3799 : (pass == 1)
3800 ? compopt[i].name
3801 : othropt[i].name)) != 0; i++) {
3802 if (pass == 0 && !boolopt[i].addr)
3803 continue;
3804 optflags = (pass == 0) ? boolopt[i].optflags
3805 : (pass == 1)
3806 ? compopt[i].optflags
3807 : othropt[i].optflags;
3808 if (optflags < startpass || optflags > endpass)
3809 continue;
3810 if ((is_wc_option(name) && !wc_supported(name))
3811 || (is_wc2_option(name) && !wc2_supported(name)))
3812 continue;
3814 if (strlen(name) > longest_name_len)
3815 longest_name_len = strlen(name);
3817 Sprintf(fmtstr_doset, "%%s%%-%us [%%s]", longest_name_len);
3818 made_fmtstr = TRUE;
3821 any = zeroany;
3822 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
3823 "Booleans (selecting will toggle value):", MENU_UNSELECTED);
3824 any.a_int = 0;
3825 /* first list any other non-modifiable booleans, then modifiable ones */
3826 for (pass = 0; pass <= 1; pass++)
3827 for (i = 0; (name = boolopt[i].name) != 0; i++)
3828 if ((bool_p = boolopt[i].addr) != 0
3829 && ((boolopt[i].optflags <= DISP_IN_GAME && pass == 0)
3830 || (boolopt[i].optflags >= SET_IN_GAME && pass == 1))) {
3831 if (bool_p == &flags.female)
3832 continue; /* obsolete */
3833 if (boolopt[i].optflags == SET_IN_WIZGAME && !wizard)
3834 continue;
3835 if ((is_wc_option(name) && !wc_supported(name))
3836 || (is_wc2_option(name) && !wc2_supported(name)))
3837 continue;
3839 any.a_int = (pass == 0) ? 0 : i + 1;
3840 if (!iflags.menu_tab_sep)
3841 Sprintf(buf, fmtstr_doset, (pass == 0) ? " " : "",
3842 name, *bool_p ? "true" : "false");
3843 else
3844 Sprintf(buf, fmtstr_doset_tab,
3845 name, *bool_p ? "true" : "false");
3846 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf,
3847 MENU_UNSELECTED);
3850 boolcount = i;
3851 indexoffset = boolcount;
3852 any = zeroany;
3853 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
3854 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
3855 "Compounds (selecting will prompt for new value):",
3856 MENU_UNSELECTED);
3858 /* deliberately put playmode, name, role+race+gender+align first */
3859 doset_add_menu(tmpwin, "playmode", 0);
3860 doset_add_menu(tmpwin, "name", 0);
3861 doset_add_menu(tmpwin, "role", 0);
3862 doset_add_menu(tmpwin, "race", 0);
3863 doset_add_menu(tmpwin, "gender", 0);
3864 doset_add_menu(tmpwin, "align", 0);
3866 for (pass = startpass; pass <= endpass; pass++)
3867 for (i = 0; (name = compopt[i].name) != 0; i++)
3868 if (compopt[i].optflags == pass) {
3869 if (!strcmp(name, "playmode") || !strcmp(name, "name")
3870 || !strcmp(name, "role") || !strcmp(name, "race")
3871 || !strcmp(name, "gender") || !strcmp(name, "align"))
3872 continue;
3873 if ((is_wc_option(name) && !wc_supported(name))
3874 || (is_wc2_option(name) && !wc2_supported(name)))
3875 continue;
3877 doset_add_menu(tmpwin, name,
3878 (pass == DISP_IN_GAME) ? 0 : indexoffset);
3881 any = zeroany;
3882 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
3883 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
3884 "Other settings:",
3885 MENU_UNSELECTED);
3887 opts_add_others(tmpwin, "autopickup exceptions", OPT_OTHER_APEXC,
3888 NULL, count_ape_maps((int *) 0, (int *) 0));
3889 opts_add_others(tmpwin, "menucolors", OPT_OTHER_MENUCOLOR,
3890 NULL, count_menucolors());
3891 opts_add_others(tmpwin, "message types", OPT_OTHER_MSGTYPE,
3892 NULL, msgtype_count());
3893 #ifdef STATUS_VIA_WINDOWPORT
3894 #ifdef STATUS_HILITES
3895 get_status_hilites(buf2, 60);
3896 if (!*buf2)
3897 Sprintf(buf2, "%s", "(none)");
3898 opts_add_others(tmpwin, "status_hilites", OPT_OTHER_STATHILITE, buf2, 0);
3899 #endif
3900 #endif
3901 #ifdef PREFIXES_IN_USE
3902 any = zeroany;
3903 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
3904 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
3905 "Variable playground locations:", MENU_UNSELECTED);
3906 for (i = 0; i < PREFIX_COUNT; i++)
3907 doset_add_menu(tmpwin, fqn_prefix_names[i], 0);
3908 #endif
3909 end_menu(tmpwin, "Set what options?");
3910 need_redraw = FALSE;
3911 if ((pick_cnt = select_menu(tmpwin, PICK_ANY, &pick_list)) > 0) {
3913 * Walk down the selection list and either invert the booleans
3914 * or prompt for new values. In most cases, call parseoptions()
3915 * to take care of options that require special attention, like
3916 * redraws.
3918 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
3919 opt_indx = pick_list[pick_idx].item.a_int - 1;
3920 if (opt_indx < -1) opt_indx++; /* -1 offset for select_menu() */
3921 if (opt_indx == OPT_OTHER_APEXC) {
3922 (void) special_handling("autopickup_exception", setinitial,
3923 fromfile);
3924 #ifdef STATUS_VIA_WINDOWPORT
3925 #ifdef STATUS_HILITES
3926 } else if (opt_indx == OPT_OTHER_STATHILITE) {
3927 if (!status_hilite_menu()) {
3928 pline("Bad status hilite(s) specified.");
3929 } else {
3930 if (wc2_supported("status_hilites"))
3931 preference_update("status_hilites");
3933 #endif
3934 #endif
3935 } else if (opt_indx == OPT_OTHER_MENUCOLOR) {
3936 (void) special_handling("menucolors", setinitial,
3937 fromfile);
3938 } else if (opt_indx == OPT_OTHER_MSGTYPE) {
3939 (void) special_handling("msgtype", setinitial, fromfile);
3940 } else if (opt_indx < boolcount) {
3941 /* boolean option */
3942 Sprintf(buf, "%s%s", *boolopt[opt_indx].addr ? "!" : "",
3943 boolopt[opt_indx].name);
3944 parseoptions(buf, setinitial, fromfile);
3945 if (wc_supported(boolopt[opt_indx].name)
3946 || wc2_supported(boolopt[opt_indx].name))
3947 preference_update(boolopt[opt_indx].name);
3948 } else {
3949 /* compound option */
3950 opt_indx -= boolcount;
3952 if (!special_handling(compopt[opt_indx].name, setinitial,
3953 fromfile)) {
3954 Sprintf(buf, "Set %s to what?", compopt[opt_indx].name);
3955 getlin(buf, buf2);
3956 if (buf2[0] == '\033')
3957 continue;
3958 Sprintf(buf, "%s:%s", compopt[opt_indx].name, buf2);
3959 /* pass the buck */
3960 parseoptions(buf, setinitial, fromfile);
3962 if (wc_supported(compopt[opt_indx].name)
3963 || wc2_supported(compopt[opt_indx].name))
3964 preference_update(compopt[opt_indx].name);
3967 free((genericptr_t) pick_list);
3968 pick_list = (menu_item *) 0;
3971 destroy_nhwindow(tmpwin);
3972 if (need_redraw) {
3973 reglyph_darkroom();
3974 (void) doredraw();
3976 return 0;
3979 STATIC_OVL int
3980 handle_add_list_remove(optname, numtotal)
3981 const char *optname;
3982 int numtotal;
3984 winid tmpwin;
3985 anything any;
3986 int i, pick_cnt, opt_idx;
3987 menu_item *pick_list = (menu_item *) 0;
3988 static const struct action {
3989 char letr;
3990 const char *desc;
3991 } action_titles[] = {
3992 { 'a', "add new %s" }, /* [0] */
3993 { 'l', "list %s" }, /* [1] */
3994 { 'r', "remove existing %s" }, /* [2] */
3995 { 'x', "exit this menu" }, /* [3] */
3998 opt_idx = 0;
3999 tmpwin = create_nhwindow(NHW_MENU);
4000 start_menu(tmpwin);
4001 any = zeroany;
4002 for (i = 0; i < SIZE(action_titles); i++) {
4003 char tmpbuf[BUFSZ];
4005 any.a_int++;
4006 /* omit list and remove if there aren't any yet */
4007 if (!numtotal && (i == 1 || i == 2))
4008 continue;
4009 Sprintf(tmpbuf, action_titles[i].desc,
4010 (i == 1) ? makeplural(optname) : optname);
4011 add_menu(tmpwin, NO_GLYPH, &any, action_titles[i].letr, 0, ATR_NONE,
4012 tmpbuf, (i == 3) ? MENU_SELECTED : MENU_UNSELECTED);
4014 end_menu(tmpwin, "Do what?");
4015 if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &pick_list)) > 0) {
4016 opt_idx = pick_list[0].item.a_int - 1;
4017 if (pick_cnt > 1 && opt_idx == 3)
4018 opt_idx = pick_list[1].item.a_int - 1;
4019 free((genericptr_t) pick_list);
4020 } else
4021 opt_idx = 3; /* none selected, exit menu */
4022 destroy_nhwindow(tmpwin);
4023 return opt_idx;
4026 struct symsetentry *symset_list = 0; /* files.c will populate this with
4027 list of available sets */
4029 STATIC_OVL boolean
4030 special_handling(optname, setinitial, setfromfile)
4031 const char *optname;
4032 boolean setinitial, setfromfile;
4034 winid tmpwin;
4035 anything any;
4036 int i, n;
4037 char buf[BUFSZ];
4039 /* Special handling of menustyle, pickup_burden, pickup_types,
4040 * disclose, runmode, msg_window, menu_headings, sortloot,
4041 * and number_pad options.
4042 * Also takes care of interactive autopickup_exception_handling changes.
4044 if (!strcmp("menustyle", optname)) {
4045 const char *style_name;
4046 menu_item *style_pick = (menu_item *) 0;
4047 tmpwin = create_nhwindow(NHW_MENU);
4048 start_menu(tmpwin);
4049 any = zeroany;
4050 for (i = 0; i < SIZE(menutype); i++) {
4051 style_name = menutype[i];
4052 /* note: separate `style_name' variable used
4053 to avoid an optimizer bug in VAX C V2.3 */
4054 any.a_int = i + 1;
4055 add_menu(tmpwin, NO_GLYPH, &any, *style_name, 0, ATR_NONE,
4056 style_name, MENU_UNSELECTED);
4058 end_menu(tmpwin, "Select menustyle:");
4059 if (select_menu(tmpwin, PICK_ONE, &style_pick) > 0) {
4060 flags.menu_style = style_pick->item.a_int - 1;
4061 free((genericptr_t) style_pick);
4063 destroy_nhwindow(tmpwin);
4064 } else if (!strcmp("paranoid_confirmation", optname)) {
4065 menu_item *paranoia_picks = (menu_item *) 0;
4067 tmpwin = create_nhwindow(NHW_MENU);
4068 start_menu(tmpwin);
4069 any = zeroany;
4070 for (i = 0; paranoia[i].flagmask != 0; ++i) {
4071 if (paranoia[i].flagmask == PARANOID_BONES && !wizard)
4072 continue;
4073 any.a_int = paranoia[i].flagmask;
4074 add_menu(tmpwin, NO_GLYPH, &any, *paranoia[i].argname, 0,
4075 ATR_NONE, paranoia[i].explain,
4076 (flags.paranoia_bits & paranoia[i].flagmask)
4077 ? MENU_SELECTED
4078 : MENU_UNSELECTED);
4080 end_menu(tmpwin, "Actions requiring extra confirmation:");
4081 i = select_menu(tmpwin, PICK_ANY, &paranoia_picks);
4082 if (i >= 0) {
4083 /* player didn't cancel; we reset all the paranoia options
4084 here even if there were no items picked, since user
4085 could have toggled off preselected ones to end up with 0 */
4086 flags.paranoia_bits = 0;
4087 if (i > 0) {
4088 /* at least 1 item set, either preselected or newly picked */
4089 while (--i >= 0)
4090 flags.paranoia_bits |= paranoia_picks[i].item.a_int;
4091 free((genericptr_t) paranoia_picks);
4094 destroy_nhwindow(tmpwin);
4095 } else if (!strcmp("pickup_burden", optname)) {
4096 const char *burden_name, *burden_letters = "ubsntl";
4097 menu_item *burden_pick = (menu_item *) 0;
4099 tmpwin = create_nhwindow(NHW_MENU);
4100 start_menu(tmpwin);
4101 any = zeroany;
4102 for (i = 0; i < SIZE(burdentype); i++) {
4103 burden_name = burdentype[i];
4104 any.a_int = i + 1;
4105 add_menu(tmpwin, NO_GLYPH, &any, burden_letters[i], 0, ATR_NONE,
4106 burden_name, MENU_UNSELECTED);
4108 end_menu(tmpwin, "Select encumbrance level:");
4109 if (select_menu(tmpwin, PICK_ONE, &burden_pick) > 0) {
4110 flags.pickup_burden = burden_pick->item.a_int - 1;
4111 free((genericptr_t) burden_pick);
4113 destroy_nhwindow(tmpwin);
4114 } else if (!strcmp("pickup_types", optname)) {
4115 /* parseoptions will prompt for the list of types */
4116 parseoptions(strcpy(buf, "pickup_types"), setinitial, setfromfile);
4117 } else if (!strcmp("disclose", optname)) {
4118 /* order of disclose_names[] must correspond to
4119 disclosure_options in decl.c */
4120 static const char *disclosure_names[] = {
4121 "inventory", "attributes", "vanquished",
4122 "genocides", "conduct", "overview",
4124 int disc_cat[NUM_DISCLOSURE_OPTIONS];
4125 int pick_cnt, pick_idx, opt_idx;
4126 char c;
4127 menu_item *disclosure_pick = (menu_item *) 0;
4129 tmpwin = create_nhwindow(NHW_MENU);
4130 start_menu(tmpwin);
4131 any = zeroany;
4132 for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
4133 Sprintf(buf, "%-12s[%c%c]", disclosure_names[i],
4134 flags.end_disclose[i], disclosure_options[i]);
4135 any.a_int = i + 1;
4136 add_menu(tmpwin, NO_GLYPH, &any, disclosure_options[i], 0,
4137 ATR_NONE, buf, MENU_UNSELECTED);
4138 disc_cat[i] = 0;
4140 end_menu(tmpwin, "Change which disclosure options categories:");
4141 pick_cnt = select_menu(tmpwin, PICK_ANY, &disclosure_pick);
4142 if (pick_cnt > 0) {
4143 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
4144 opt_idx = disclosure_pick[pick_idx].item.a_int - 1;
4145 disc_cat[opt_idx] = 1;
4147 free((genericptr_t) disclosure_pick);
4148 disclosure_pick = (menu_item *) 0;
4150 destroy_nhwindow(tmpwin);
4152 for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
4153 if (disc_cat[i]) {
4154 c = flags.end_disclose[i];
4155 Sprintf(buf, "Disclosure options for %s:",
4156 disclosure_names[i]);
4157 tmpwin = create_nhwindow(NHW_MENU);
4158 start_menu(tmpwin);
4159 any = zeroany;
4160 /* 'y','n',and '+' work as alternate selectors; '-' doesn't */
4161 any.a_char = DISCLOSE_NO_WITHOUT_PROMPT;
4162 add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
4163 "Never disclose, without prompting",
4164 (c == any.a_char) ? MENU_SELECTED : MENU_UNSELECTED);
4165 any.a_char = DISCLOSE_YES_WITHOUT_PROMPT;
4166 add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
4167 "Always disclose, without prompting",
4168 (c == any.a_char) ? MENU_SELECTED : MENU_UNSELECTED);
4169 if (*disclosure_names[i] == 'v') {
4170 any.a_char = DISCLOSE_SPECIAL_WITHOUT_PROMPT; /* '#' */
4171 add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
4172 "Always disclose, pick sort order from menu",
4173 (c == any.a_char) ? MENU_SELECTED
4174 : MENU_UNSELECTED);
4176 any.a_char = DISCLOSE_PROMPT_DEFAULT_NO;
4177 add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
4178 "Prompt, with default answer of \"No\"",
4179 (c == any.a_char) ? MENU_SELECTED : MENU_UNSELECTED);
4180 any.a_char = DISCLOSE_PROMPT_DEFAULT_YES;
4181 add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
4182 "Prompt, with default answer of \"Yes\"",
4183 (c == any.a_char) ? MENU_SELECTED : MENU_UNSELECTED);
4184 if (*disclosure_names[i] == 'v') {
4185 any.a_char = DISCLOSE_PROMPT_DEFAULT_SPECIAL; /* '?' */
4186 add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
4187 "Prompt, with default answer of \"Ask\" to request sort menu",
4188 (c == any.a_char) ? MENU_SELECTED
4189 : MENU_UNSELECTED);
4191 end_menu(tmpwin, buf);
4192 n = select_menu(tmpwin, PICK_ONE, &disclosure_pick);
4193 if (n > 0) {
4194 flags.end_disclose[i] = disclosure_pick[0].item.a_char;
4195 if (n > 1 && flags.end_disclose[i] == c)
4196 flags.end_disclose[i] = disclosure_pick[1].item.a_char;
4197 free((genericptr_t) disclosure_pick);
4199 destroy_nhwindow(tmpwin);
4202 } else if (!strcmp("runmode", optname)) {
4203 const char *mode_name;
4204 menu_item *mode_pick = (menu_item *) 0;
4206 tmpwin = create_nhwindow(NHW_MENU);
4207 start_menu(tmpwin);
4208 any = zeroany;
4209 for (i = 0; i < SIZE(runmodes); i++) {
4210 mode_name = runmodes[i];
4211 any.a_int = i + 1;
4212 add_menu(tmpwin, NO_GLYPH, &any, *mode_name, 0, ATR_NONE,
4213 mode_name, MENU_UNSELECTED);
4215 end_menu(tmpwin, "Select run/travel display mode:");
4216 if (select_menu(tmpwin, PICK_ONE, &mode_pick) > 0) {
4217 flags.runmode = mode_pick->item.a_int - 1;
4218 free((genericptr_t) mode_pick);
4220 destroy_nhwindow(tmpwin);
4221 } else if (!strcmp("whatis_coord", optname)) {
4222 menu_item *window_pick = (menu_item *) 0;
4223 int pick_cnt;
4224 char gp = iflags.getpos_coords;
4226 tmpwin = create_nhwindow(NHW_MENU);
4227 start_menu(tmpwin);
4228 any = zeroany;
4229 any.a_char = GPCOORDS_COMPASS;
4230 add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_COMPASS,
4231 0, ATR_NONE, "compass ('east' or '3s' or '2n,4w')",
4232 (gp == GPCOORDS_COMPASS) ? MENU_SELECTED : MENU_UNSELECTED);
4233 any.a_char = GPCOORDS_COMFULL;
4234 add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_COMFULL,
4235 0, ATR_NONE, "full compass ('east' or '3south' or '2north,4west')",
4236 (gp == GPCOORDS_COMFULL) ? MENU_SELECTED : MENU_UNSELECTED);
4237 any.a_char = GPCOORDS_MAP;
4238 add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_MAP,
4239 0, ATR_NONE, "map <x,y>",
4240 (gp == GPCOORDS_MAP) ? MENU_SELECTED : MENU_UNSELECTED);
4241 any.a_char = GPCOORDS_SCREEN;
4242 add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_SCREEN,
4243 0, ATR_NONE, "screen [row,column]",
4244 (gp == GPCOORDS_SCREEN) ? MENU_SELECTED : MENU_UNSELECTED);
4245 any.a_char = GPCOORDS_NONE;
4246 add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_NONE,
4247 0, ATR_NONE, "none (no coordinates displayed)",
4248 (gp == GPCOORDS_NONE) ? MENU_SELECTED : MENU_UNSELECTED);
4249 any.a_long = 0L;
4250 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
4251 Sprintf(buf, "map: upper-left: <%d,%d>, lower-right: <%d,%d>%s",
4252 1, 0, COLNO - 1, ROWNO - 1,
4253 flags.verbose ? "; column 0 unused, off left edge" : "");
4254 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
4255 if (strcmp(windowprocs.name, "tty"))
4256 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
4257 "screen: row is offset to accommodate tty interface's use of top line",
4258 MENU_UNSELECTED);
4259 #if COLNO == 80
4260 #define COL80ARG flags.verbose ? "; column 80 is not used" : ""
4261 #else
4262 #define COL80ARG ""
4263 #endif
4264 Sprintf(buf, "screen: upper-left: [%02d,%02d], lower-right: [%d,%d]%s",
4265 0 + 2, 1, ROWNO - 1 + 2, COLNO - 1, COL80ARG);
4266 #undef COL80ARG
4267 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
4268 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
4269 end_menu(tmpwin,
4270 "Select coordinate display when auto-describing a map position:");
4271 if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &window_pick)) > 0) {
4272 iflags.getpos_coords = window_pick[0].item.a_char;
4273 /* PICK_ONE doesn't unselect preselected entry when
4274 selecting another one */
4275 if (pick_cnt > 1 && iflags.getpos_coords == gp)
4276 iflags.getpos_coords = window_pick[1].item.a_char;
4277 free((genericptr_t) window_pick);
4279 destroy_nhwindow(tmpwin);
4280 } else if (!strcmp("msg_window", optname)) {
4281 #ifdef TTY_GRAPHICS
4282 /* by Christian W. Cooper */
4283 menu_item *window_pick = (menu_item *) 0;
4285 tmpwin = create_nhwindow(NHW_MENU);
4286 start_menu(tmpwin);
4287 any = zeroany;
4288 any.a_char = 's';
4289 add_menu(tmpwin, NO_GLYPH, &any, 's', 0, ATR_NONE, "single",
4290 MENU_UNSELECTED);
4291 any.a_char = 'c';
4292 add_menu(tmpwin, NO_GLYPH, &any, 'c', 0, ATR_NONE, "combination",
4293 MENU_UNSELECTED);
4294 any.a_char = 'f';
4295 add_menu(tmpwin, NO_GLYPH, &any, 'f', 0, ATR_NONE, "full",
4296 MENU_UNSELECTED);
4297 any.a_char = 'r';
4298 add_menu(tmpwin, NO_GLYPH, &any, 'r', 0, ATR_NONE, "reversed",
4299 MENU_UNSELECTED);
4300 end_menu(tmpwin, "Select message history display type:");
4301 if (select_menu(tmpwin, PICK_ONE, &window_pick) > 0) {
4302 iflags.prevmsg_window = window_pick->item.a_char;
4303 free((genericptr_t) window_pick);
4305 destroy_nhwindow(tmpwin);
4306 #endif
4307 } else if (!strcmp("sortloot", optname)) {
4308 const char *sortl_name;
4309 menu_item *sortl_pick = (menu_item *) 0;
4311 tmpwin = create_nhwindow(NHW_MENU);
4312 start_menu(tmpwin);
4313 any = zeroany;
4314 for (i = 0; i < SIZE(sortltype); i++) {
4315 sortl_name = sortltype[i];
4316 any.a_char = *sortl_name;
4317 add_menu(tmpwin, NO_GLYPH, &any, *sortl_name, 0, ATR_NONE,
4318 sortl_name, (flags.sortloot == *sortl_name)
4319 ? MENU_SELECTED : MENU_UNSELECTED);
4321 end_menu(tmpwin, "Select loot sorting type:");
4322 n = select_menu(tmpwin, PICK_ONE, &sortl_pick);
4323 if (n > 0) {
4324 char c = sortl_pick[0].item.a_char;
4326 if (n > 1 && c == flags.sortloot)
4327 c = sortl_pick[1].item.a_char;
4328 flags.sortloot = c;
4329 free((genericptr_t) sortl_pick);
4331 destroy_nhwindow(tmpwin);
4332 } else if (!strcmp("align_message", optname)
4333 || !strcmp("align_status", optname)) {
4334 menu_item *window_pick = (menu_item *) 0;
4335 char abuf[BUFSZ];
4336 boolean msg = (*(optname + 6) == 'm');
4338 tmpwin = create_nhwindow(NHW_MENU);
4339 start_menu(tmpwin);
4340 any = zeroany;
4341 any.a_int = ALIGN_TOP;
4342 add_menu(tmpwin, NO_GLYPH, &any, 't', 0, ATR_NONE, "top",
4343 MENU_UNSELECTED);
4344 any.a_int = ALIGN_BOTTOM;
4345 add_menu(tmpwin, NO_GLYPH, &any, 'b', 0, ATR_NONE, "bottom",
4346 MENU_UNSELECTED);
4347 any.a_int = ALIGN_LEFT;
4348 add_menu(tmpwin, NO_GLYPH, &any, 'l', 0, ATR_NONE, "left",
4349 MENU_UNSELECTED);
4350 any.a_int = ALIGN_RIGHT;
4351 add_menu(tmpwin, NO_GLYPH, &any, 'r', 0, ATR_NONE, "right",
4352 MENU_UNSELECTED);
4353 Sprintf(abuf, "Select %s window placement relative to the map:",
4354 msg ? "message" : "status");
4355 end_menu(tmpwin, abuf);
4356 if (select_menu(tmpwin, PICK_ONE, &window_pick) > 0) {
4357 if (msg)
4358 iflags.wc_align_message = window_pick->item.a_int;
4359 else
4360 iflags.wc_align_status = window_pick->item.a_int;
4361 free((genericptr_t) window_pick);
4363 destroy_nhwindow(tmpwin);
4364 } else if (!strcmp("number_pad", optname)) {
4365 static const char *npchoices[] = {
4366 " 0 (off)", " 1 (on)", " 2 (on, MSDOS compatible)",
4367 " 3 (on, phone-style digit layout)",
4368 " 4 (on, phone-style layout, MSDOS compatible)",
4369 "-1 (off, 'z' to move upper-left, 'y' to zap wands)"
4371 menu_item *mode_pick = (menu_item *) 0;
4373 tmpwin = create_nhwindow(NHW_MENU);
4374 start_menu(tmpwin);
4375 any = zeroany;
4376 for (i = 0; i < SIZE(npchoices); i++) {
4377 any.a_int = i + 1;
4378 add_menu(tmpwin, NO_GLYPH, &any, 'a' + i, 0, ATR_NONE,
4379 npchoices[i], MENU_UNSELECTED);
4381 end_menu(tmpwin, "Select number_pad mode:");
4382 if (select_menu(tmpwin, PICK_ONE, &mode_pick) > 0) {
4383 switch (mode_pick->item.a_int - 1) {
4384 case 0:
4385 iflags.num_pad = FALSE;
4386 iflags.num_pad_mode = 0;
4387 break;
4388 case 1:
4389 iflags.num_pad = TRUE;
4390 iflags.num_pad_mode = 0;
4391 break;
4392 case 2:
4393 iflags.num_pad = TRUE;
4394 iflags.num_pad_mode = 1;
4395 break;
4396 case 3:
4397 iflags.num_pad = TRUE;
4398 iflags.num_pad_mode = 2;
4399 break;
4400 case 4:
4401 iflags.num_pad = TRUE;
4402 iflags.num_pad_mode = 3;
4403 break;
4404 /* last menu choice: number_pad == -1 */
4405 case 5:
4406 iflags.num_pad = FALSE;
4407 iflags.num_pad_mode = 1;
4408 break;
4410 reset_commands(FALSE);
4411 number_pad(iflags.num_pad ? 1 : 0);
4412 free((genericptr_t) mode_pick);
4414 destroy_nhwindow(tmpwin);
4415 } else if (!strcmp("menu_headings", optname)) {
4416 int mhattr = query_attr("How to highlight menu headings:");
4418 if (mhattr != -1)
4419 iflags.menu_headings = mhattr;
4420 } else if (!strcmp("msgtype", optname)) {
4421 int opt_idx, nmt, mttyp;
4422 char mtbuf[BUFSZ];
4424 msgtypes_again:
4425 nmt = msgtype_count();
4426 opt_idx = handle_add_list_remove("message type", nmt);
4427 if (opt_idx == 3) { /* done */
4428 return TRUE;
4429 } else if (opt_idx == 0) { /* add new */
4430 getlin("What new message pattern?", mtbuf);
4431 if (*mtbuf == '\033')
4432 return TRUE;
4433 if (*mtbuf
4434 && (mttyp = query_msgtype()) != -1
4435 && !msgtype_add(mttyp, mtbuf)) {
4436 pline("Error adding the message type.");
4437 wait_synch();
4439 goto msgtypes_again;
4440 } else { /* list (1) or remove (2) */
4441 int pick_idx, pick_cnt;
4442 int mt_idx;
4443 unsigned ln;
4444 const char *mtype;
4445 menu_item *pick_list = (menu_item *) 0;
4446 struct plinemsg_type *tmp = plinemsg_types;
4448 tmpwin = create_nhwindow(NHW_MENU);
4449 start_menu(tmpwin);
4450 any = zeroany;
4451 mt_idx = 0;
4452 while (tmp) {
4453 mtype = msgtype2name(tmp->msgtype);
4454 any.a_int = ++mt_idx;
4455 Sprintf(mtbuf, "%-5s \"", mtype);
4456 ln = sizeof mtbuf - strlen(mtbuf) - sizeof "\"";
4457 if (strlen(tmp->pattern) > ln)
4458 Strcat(strncat(mtbuf, tmp->pattern, ln - 3), "...\"");
4459 else
4460 Strcat(mtbuf, "\"");
4461 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, mtbuf,
4462 MENU_UNSELECTED);
4463 tmp = tmp->next;
4465 Sprintf(mtbuf, "%s message types",
4466 (opt_idx == 1) ? "List of" : "Remove which");
4467 end_menu(tmpwin, mtbuf);
4468 pick_cnt = select_menu(tmpwin,
4469 (opt_idx == 1) ? PICK_NONE : PICK_ANY,
4470 &pick_list);
4471 if (pick_cnt > 0) {
4472 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
4473 free_one_msgtype(pick_list[pick_idx].item.a_int - 1
4474 - pick_idx);
4475 free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
4477 destroy_nhwindow(tmpwin);
4478 if (pick_cnt >= 0)
4479 goto msgtypes_again;
4481 } else if (!strcmp("menucolors", optname)) {
4482 int opt_idx, nmc, mcclr, mcattr;
4483 char mcbuf[BUFSZ];
4485 menucolors_again:
4486 nmc = count_menucolors();
4487 opt_idx = handle_add_list_remove("menucolor", nmc);
4488 if (opt_idx == 3) { /* done */
4489 return TRUE;
4490 } else if (opt_idx == 0) { /* add new */
4491 getlin("What new menucolor pattern?", mcbuf);
4492 if (*mcbuf == '\033')
4493 return TRUE;
4494 if (*mcbuf
4495 && (mcclr = query_color()) != -1
4496 && (mcattr = query_attr((char *) 0)) != -1
4497 && !add_menu_coloring_parsed(mcbuf, mcclr, mcattr)) {
4498 pline("Error adding the menu color.");
4499 wait_synch();
4501 goto menucolors_again;
4502 } else { /* list (1) or remove (2) */
4503 int pick_idx, pick_cnt;
4504 int mc_idx;
4505 unsigned ln;
4506 const char *sattr, *sclr;
4507 menu_item *pick_list = (menu_item *) 0;
4508 struct menucoloring *tmp = menu_colorings;
4510 tmpwin = create_nhwindow(NHW_MENU);
4511 start_menu(tmpwin);
4512 any = zeroany;
4513 mc_idx = 0;
4514 while (tmp) {
4515 sattr = attr2attrname(tmp->attr);
4516 sclr = clr2colorname(tmp->color);
4517 any.a_int = ++mc_idx;
4518 /* construct suffix */
4519 Sprintf(buf, "\"\"=%s%s%s", sclr,
4520 (tmp->attr != ATR_NONE) ? " & " : "",
4521 (tmp->attr != ATR_NONE) ? sattr : "");
4522 /* now main string */
4523 ln = sizeof buf - strlen(buf) - 1; /* length available */
4524 Strcpy(mcbuf, "\"");
4525 if (strlen(tmp->origstr) > ln)
4526 Strcat(strncat(mcbuf, tmp->origstr, ln - 3), "...");
4527 else
4528 Strcat(mcbuf, tmp->origstr);
4529 /* combine main string and suffix */
4530 Strcat(mcbuf, &buf[1]); /* skip buf[]'s initial quote */
4531 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, mcbuf,
4532 MENU_UNSELECTED);
4533 tmp = tmp->next;
4535 Sprintf(mcbuf, "%s menu colors",
4536 (opt_idx == 1) ? "List of" : "Remove which");
4537 end_menu(tmpwin, mcbuf);
4538 pick_cnt = select_menu(tmpwin,
4539 (opt_idx == 1) ? PICK_NONE : PICK_ANY,
4540 &pick_list);
4541 if (pick_cnt > 0) {
4542 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
4543 free_one_menu_coloring(pick_list[pick_idx].item.a_int - 1
4544 - pick_idx);
4545 free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
4547 destroy_nhwindow(tmpwin);
4548 if (pick_cnt >= 0)
4549 goto menucolors_again;
4551 } else if (!strcmp("autopickup_exception", optname)) {
4552 int opt_idx, pass, totalapes = 0, numapes[2] = { 0, 0 };
4553 char apebuf[1 + BUFSZ]; /* so &apebuf[1] is BUFSZ long for getlin() */
4554 struct autopickup_exception *ape;
4556 ape_again:
4557 totalapes = count_ape_maps(&numapes[AP_LEAVE], &numapes[AP_GRAB]);
4558 opt_idx = handle_add_list_remove("autopickup exception", totalapes);
4559 if (opt_idx == 3) { /* done */
4560 return TRUE;
4561 } else if (opt_idx == 0) { /* add new */
4562 getlin("What new autopickup exception pattern?", &apebuf[1]);
4563 mungspaces(&apebuf[1]); /* regularize whitespace */
4564 if (apebuf[1] == '\033')
4565 return TRUE;
4566 if (apebuf[1]) {
4567 apebuf[0] = '\"';
4568 /* guarantee room for \" prefix and \"\0 suffix;
4569 -2 is good enough for apebuf[] but -3 makes
4570 sure the whole thing fits within normal BUFSZ */
4571 apebuf[sizeof apebuf - 3] = '\0';
4572 Strcat(apebuf, "\"");
4573 add_autopickup_exception(apebuf);
4575 goto ape_again;
4576 } else { /* list (1) or remove (2) */
4577 int pick_idx, pick_cnt;
4578 menu_item *pick_list = (menu_item *) 0;
4580 tmpwin = create_nhwindow(NHW_MENU);
4581 start_menu(tmpwin);
4582 for (pass = AP_LEAVE; pass <= AP_GRAB; ++pass) {
4583 if (numapes[pass] == 0)
4584 continue;
4585 ape = iflags.autopickup_exceptions[pass];
4586 any = zeroany;
4587 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
4588 (pass == 0) ? "Never pickup" : "Always pickup",
4589 MENU_UNSELECTED);
4590 for (i = 0; i < numapes[pass] && ape; i++) {
4591 any.a_void = (opt_idx == 1) ? 0 : ape;
4592 /* length of pattern plus quotes is less than BUFSZ */
4593 Sprintf(apebuf, "\"%s\"", ape->pattern);
4594 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, apebuf,
4595 MENU_UNSELECTED);
4596 ape = ape->next;
4599 Sprintf(apebuf, "%s autopickup exceptions",
4600 (opt_idx == 1) ? "List of" : "Remove which");
4601 end_menu(tmpwin, apebuf);
4602 pick_cnt = select_menu(tmpwin,
4603 (opt_idx == 1) ? PICK_NONE : PICK_ANY,
4604 &pick_list);
4605 if (pick_cnt > 0) {
4606 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
4607 remove_autopickup_exception(
4608 (struct autopickup_exception *)
4609 pick_list[pick_idx].item.a_void);
4610 free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
4612 destroy_nhwindow(tmpwin);
4613 if (pick_cnt >= 0)
4614 goto ape_again;
4616 } else if (!strcmp("symset", optname)
4617 || !strcmp("roguesymset", optname)) {
4618 menu_item *symset_pick = (menu_item *) 0;
4619 boolean primaryflag = (*optname == 's'),
4620 rogueflag = (*optname == 'r'),
4621 ready_to_switch = FALSE,
4622 nothing_to_do = FALSE;
4623 char *symset_name, fmtstr[20];
4624 struct symsetentry *sl;
4625 int res, which_set, setcount = 0, chosen = -2;
4627 if (rogueflag)
4628 which_set = ROGUESET;
4629 else
4630 which_set = PRIMARY;
4632 /* clear symset[].name as a flag to read_sym_file() to build list */
4633 symset_name = symset[which_set].name;
4634 symset[which_set].name = (char *) 0;
4635 symset_list = (struct symsetentry *) 0;
4637 res = read_sym_file(which_set);
4638 if (res && symset_list) {
4639 char symsetchoice[BUFSZ];
4640 int let = 'a', biggest = 0, thissize = 0;
4642 sl = symset_list;
4643 while (sl) {
4644 /* check restrictions */
4645 if ((!rogueflag && sl->rogue)
4646 || (!primaryflag && sl->primary)) {
4647 sl = sl->next;
4648 continue;
4650 setcount++;
4651 /* find biggest name */
4652 if (sl->name)
4653 thissize = strlen(sl->name);
4654 if (thissize > biggest)
4655 biggest = thissize;
4656 sl = sl->next;
4658 if (!setcount) {
4659 pline("There are no appropriate %ssymbol sets available.",
4660 (rogueflag) ? "rogue level "
4661 : (primaryflag) ? "primary " : "");
4662 return TRUE;
4665 Sprintf(fmtstr, "%%-%ds %%s", biggest + 5);
4666 tmpwin = create_nhwindow(NHW_MENU);
4667 start_menu(tmpwin);
4668 any = zeroany;
4669 any.a_int = 1;
4670 add_menu(tmpwin, NO_GLYPH, &any, let++, 0, ATR_NONE,
4671 "Default Symbols", MENU_UNSELECTED);
4673 sl = symset_list;
4674 while (sl) {
4675 /* check restrictions */
4676 if ((!rogueflag && sl->rogue)
4677 || (!primaryflag && sl->primary)) {
4678 sl = sl->next;
4679 continue;
4681 if (sl->name) {
4682 any.a_int = sl->idx + 2;
4683 Sprintf(symsetchoice, fmtstr, sl->name,
4684 sl->desc ? sl->desc : "");
4685 add_menu(tmpwin, NO_GLYPH, &any, let, 0, ATR_NONE,
4686 symsetchoice, MENU_UNSELECTED);
4687 if (let == 'z')
4688 let = 'A';
4689 else
4690 let++;
4692 sl = sl->next;
4694 Sprintf(buf, "Select %ssymbol set:", rogueflag ? "rogue level " : "");
4695 end_menu(tmpwin, buf);
4696 if (select_menu(tmpwin, PICK_ONE, &symset_pick) > 0) {
4697 chosen = symset_pick->item.a_int - 2;
4698 free((genericptr_t) symset_pick);
4700 destroy_nhwindow(tmpwin);
4702 if (chosen > -1) {
4703 /* chose an actual symset name from file */
4704 sl = symset_list;
4705 while (sl) {
4706 if (sl->idx == chosen) {
4707 if (symset_name) {
4708 free((genericptr_t) symset_name);
4709 symset_name = (char *) 0;
4711 /* free the now stale attributes */
4712 clear_symsetentry(which_set, TRUE);
4714 /* transfer only the name of the symbol set */
4715 symset[which_set].name = dupstr(sl->name);
4716 ready_to_switch = TRUE;
4717 break;
4719 sl = sl->next;
4721 } else if (chosen == -1) {
4722 /* explicit selection of defaults */
4723 /* free the now stale symset attributes */
4724 if (symset_name) {
4725 free((genericptr_t) symset_name);
4726 symset_name = (char *) 0;
4728 clear_symsetentry(which_set, TRUE);
4729 } else
4730 nothing_to_do = TRUE;
4731 } else if (!res) {
4732 /* The symbols file could not be accessed */
4733 pline("Unable to access \"%s\" file.", SYMBOLS);
4734 return TRUE;
4735 } else if (!symset_list) {
4736 /* The symbols file was empty */
4737 pline("There were no symbol sets found in \"%s\".", SYMBOLS);
4738 return TRUE;
4741 /* clean up */
4742 while (symset_list) {
4743 sl = symset_list;
4744 if (sl->name)
4745 free((genericptr_t) sl->name);
4746 sl->name = (char *) 0;
4748 if (sl->desc)
4749 free((genericptr_t) sl->desc);
4750 sl->desc = (char *) 0;
4752 symset_list = sl->next;
4753 free((genericptr_t) sl);
4756 if (nothing_to_do)
4757 return TRUE;
4759 if (!symset[which_set].name && symset_name)
4760 symset[which_set].name = symset_name; /* not dupstr() here */
4762 /* Set default symbols and clear the handling value */
4763 if (rogueflag)
4764 init_r_symbols();
4765 else
4766 init_l_symbols();
4768 if (symset[which_set].name) {
4769 if (read_sym_file(which_set)) {
4770 ready_to_switch = TRUE;
4771 } else {
4772 clear_symsetentry(which_set, TRUE);
4773 return TRUE;
4777 if (ready_to_switch)
4778 switch_symbols(TRUE);
4780 if (Is_rogue_level(&u.uz)) {
4781 if (rogueflag)
4782 assign_graphics(ROGUESET);
4783 } else if (!rogueflag)
4784 assign_graphics(PRIMARY);
4785 need_redraw = TRUE;
4786 return TRUE;
4788 } else {
4789 /* didn't match any of the special options */
4790 return FALSE;
4792 return TRUE;
4795 #define rolestring(val, array, field) \
4796 ((val >= 0) ? array[val].field : (val == ROLE_RANDOM) ? randomrole : none)
4798 /* This is ugly. We have all the option names in the compopt[] array,
4799 but we need to look at each option individually to get the value. */
4800 STATIC_OVL const char *
4801 get_compopt_value(optname, buf)
4802 const char *optname;
4803 char *buf;
4805 char ocl[MAXOCLASSES + 1];
4806 static const char none[] = "(none)", randomrole[] = "random",
4807 to_be_done[] = "(to be done)", defopt[] = "default",
4808 defbrief[] = "def";
4809 int i;
4811 buf[0] = '\0';
4812 if (!strcmp(optname, "align_message"))
4813 Sprintf(buf, "%s",
4814 iflags.wc_align_message == ALIGN_TOP
4815 ? "top"
4816 : iflags.wc_align_message == ALIGN_LEFT
4817 ? "left"
4818 : iflags.wc_align_message == ALIGN_BOTTOM
4819 ? "bottom"
4820 : iflags.wc_align_message == ALIGN_RIGHT
4821 ? "right"
4822 : defopt);
4823 else if (!strcmp(optname, "align_status"))
4824 Sprintf(buf, "%s",
4825 iflags.wc_align_status == ALIGN_TOP
4826 ? "top"
4827 : iflags.wc_align_status == ALIGN_LEFT
4828 ? "left"
4829 : iflags.wc_align_status == ALIGN_BOTTOM
4830 ? "bottom"
4831 : iflags.wc_align_status == ALIGN_RIGHT
4832 ? "right"
4833 : defopt);
4834 else if (!strcmp(optname, "align"))
4835 Sprintf(buf, "%s", rolestring(flags.initalign, aligns, adj));
4836 #ifdef WIN32
4837 else if (!strcmp(optname, "altkeyhandler"))
4838 Sprintf(buf, "%s",
4839 iflags.altkeyhandler[0] ? iflags.altkeyhandler : "default");
4840 #endif
4841 #ifdef BACKWARD_COMPAT
4842 else if (!strcmp(optname, "boulder"))
4843 Sprintf(buf, "%c",
4844 iflags.bouldersym
4845 ? iflags.bouldersym
4846 : showsyms[(int) objects[BOULDER].oc_class + SYM_OFF_O]);
4847 #endif
4848 else if (!strcmp(optname, "catname"))
4849 Sprintf(buf, "%s", catname[0] ? catname : none);
4850 else if (!strcmp(optname, "disclose"))
4851 for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
4852 if (i)
4853 (void) strkitten(buf, ' ');
4854 (void) strkitten(buf, flags.end_disclose[i]);
4855 (void) strkitten(buf, disclosure_options[i]);
4857 else if (!strcmp(optname, "dogname"))
4858 Sprintf(buf, "%s", dogname[0] ? dogname : none);
4859 else if (!strcmp(optname, "dungeon"))
4860 Sprintf(buf, "%s", to_be_done);
4861 else if (!strcmp(optname, "effects"))
4862 Sprintf(buf, "%s", to_be_done);
4863 else if (!strcmp(optname, "font_map"))
4864 Sprintf(buf, "%s", iflags.wc_font_map ? iflags.wc_font_map : defopt);
4865 else if (!strcmp(optname, "font_message"))
4866 Sprintf(buf, "%s",
4867 iflags.wc_font_message ? iflags.wc_font_message : defopt);
4868 else if (!strcmp(optname, "font_status"))
4869 Sprintf(buf, "%s",
4870 iflags.wc_font_status ? iflags.wc_font_status : defopt);
4871 else if (!strcmp(optname, "font_menu"))
4872 Sprintf(buf, "%s",
4873 iflags.wc_font_menu ? iflags.wc_font_menu : defopt);
4874 else if (!strcmp(optname, "font_text"))
4875 Sprintf(buf, "%s",
4876 iflags.wc_font_text ? iflags.wc_font_text : defopt);
4877 else if (!strcmp(optname, "font_size_map")) {
4878 if (iflags.wc_fontsiz_map)
4879 Sprintf(buf, "%d", iflags.wc_fontsiz_map);
4880 else
4881 Strcpy(buf, defopt);
4882 } else if (!strcmp(optname, "font_size_message")) {
4883 if (iflags.wc_fontsiz_message)
4884 Sprintf(buf, "%d", iflags.wc_fontsiz_message);
4885 else
4886 Strcpy(buf, defopt);
4887 } else if (!strcmp(optname, "font_size_status")) {
4888 if (iflags.wc_fontsiz_status)
4889 Sprintf(buf, "%d", iflags.wc_fontsiz_status);
4890 else
4891 Strcpy(buf, defopt);
4892 } else if (!strcmp(optname, "font_size_menu")) {
4893 if (iflags.wc_fontsiz_menu)
4894 Sprintf(buf, "%d", iflags.wc_fontsiz_menu);
4895 else
4896 Strcpy(buf, defopt);
4897 } else if (!strcmp(optname, "font_size_text")) {
4898 if (iflags.wc_fontsiz_text)
4899 Sprintf(buf, "%d", iflags.wc_fontsiz_text);
4900 else
4901 Strcpy(buf, defopt);
4902 } else if (!strcmp(optname, "fruit"))
4903 Sprintf(buf, "%s", pl_fruit);
4904 else if (!strcmp(optname, "gender"))
4905 Sprintf(buf, "%s", rolestring(flags.initgend, genders, adj));
4906 else if (!strcmp(optname, "horsename"))
4907 Sprintf(buf, "%s", horsename[0] ? horsename : none);
4908 else if (!strcmp(optname, "map_mode"))
4909 Sprintf(buf, "%s",
4910 iflags.wc_map_mode == MAP_MODE_TILES
4911 ? "tiles"
4912 : iflags.wc_map_mode == MAP_MODE_ASCII4x6
4913 ? "ascii4x6"
4914 : iflags.wc_map_mode == MAP_MODE_ASCII6x8
4915 ? "ascii6x8"
4916 : iflags.wc_map_mode == MAP_MODE_ASCII8x8
4917 ? "ascii8x8"
4918 : iflags.wc_map_mode == MAP_MODE_ASCII16x8
4919 ? "ascii16x8"
4920 : iflags.wc_map_mode == MAP_MODE_ASCII7x12
4921 ? "ascii7x12"
4922 : iflags.wc_map_mode == MAP_MODE_ASCII8x12
4923 ? "ascii8x12"
4924 : iflags.wc_map_mode
4925 == MAP_MODE_ASCII16x12
4926 ? "ascii16x12"
4927 : iflags.wc_map_mode
4928 == MAP_MODE_ASCII12x16
4929 ? "ascii12x16"
4930 : iflags.wc_map_mode
4931 == MAP_MODE_ASCII10x18
4932 ? "ascii10x18"
4933 : iflags.wc_map_mode
4934 == MAP_MODE_ASCII_FIT_TO_SCREEN
4935 ? "fit_to_screen"
4936 : defopt);
4937 else if (!strcmp(optname, "menustyle"))
4938 Sprintf(buf, "%s", menutype[(int) flags.menu_style]);
4939 else if (!strcmp(optname, "menu_deselect_all"))
4940 Sprintf(buf, "%s", to_be_done);
4941 else if (!strcmp(optname, "menu_deselect_page"))
4942 Sprintf(buf, "%s", to_be_done);
4943 else if (!strcmp(optname, "menu_first_page"))
4944 Sprintf(buf, "%s", to_be_done);
4945 else if (!strcmp(optname, "menu_invert_all"))
4946 Sprintf(buf, "%s", to_be_done);
4947 else if (!strcmp(optname, "menu_headings"))
4948 Sprintf(buf, "%s", attr2attrname(iflags.menu_headings));
4949 else if (!strcmp(optname, "menu_invert_page"))
4950 Sprintf(buf, "%s", to_be_done);
4951 else if (!strcmp(optname, "menu_last_page"))
4952 Sprintf(buf, "%s", to_be_done);
4953 else if (!strcmp(optname, "menu_next_page"))
4954 Sprintf(buf, "%s", to_be_done);
4955 else if (!strcmp(optname, "menu_previous_page"))
4956 Sprintf(buf, "%s", to_be_done);
4957 else if (!strcmp(optname, "menu_search"))
4958 Sprintf(buf, "%s", to_be_done);
4959 else if (!strcmp(optname, "menu_select_all"))
4960 Sprintf(buf, "%s", to_be_done);
4961 else if (!strcmp(optname, "menu_select_page"))
4962 Sprintf(buf, "%s", to_be_done);
4963 else if (!strcmp(optname, "monsters")) {
4964 Sprintf(buf, "%s", to_be_done);
4965 } else if (!strcmp(optname, "msghistory")) {
4966 Sprintf(buf, "%u", iflags.msg_history);
4967 #ifdef TTY_GRAPHICS
4968 } else if (!strcmp(optname, "msg_window")) {
4969 Sprintf(buf, "%s", (iflags.prevmsg_window == 's')
4970 ? "single"
4971 : (iflags.prevmsg_window == 'c')
4972 ? "combination"
4973 : (iflags.prevmsg_window == 'f')
4974 ? "full"
4975 : "reversed");
4976 #endif
4977 } else if (!strcmp(optname, "name")) {
4978 Sprintf(buf, "%s", plname);
4979 } else if (!strcmp(optname, "number_pad")) {
4980 static const char *numpadmodes[] = {
4981 "0=off", "1=on", "2=on, MSDOS compatible",
4982 "3=on, phone-style layout",
4983 "4=on, phone layout, MSDOS compatible",
4984 "-1=off, y & z swapped", /*[5]*/
4986 int indx = Cmd.num_pad
4987 ? (Cmd.phone_layout ? (Cmd.pcHack_compat ? 4 : 3)
4988 : (Cmd.pcHack_compat ? 2 : 1))
4989 : Cmd.swap_yz ? 5 : 0;
4991 Strcpy(buf, numpadmodes[indx]);
4992 } else if (!strcmp(optname, "objects")) {
4993 Sprintf(buf, "%s", to_be_done);
4994 } else if (!strcmp(optname, "packorder")) {
4995 oc_to_str(flags.inv_order, ocl);
4996 Sprintf(buf, "%s", ocl);
4997 #ifdef CHANGE_COLOR
4998 } else if (!strcmp(optname, "palette")) {
4999 Sprintf(buf, "%s", get_color_string());
5000 #endif
5001 } else if (!strcmp(optname, "paranoid_confirmation")) {
5002 char tmpbuf[QBUFSZ];
5004 tmpbuf[0] = '\0';
5005 if (ParanoidConfirm)
5006 Strcat(tmpbuf, " Confirm");
5007 if (ParanoidQuit)
5008 Strcat(tmpbuf, " quit");
5009 if (ParanoidDie)
5010 Strcat(tmpbuf, " die");
5011 if (ParanoidBones)
5012 Strcat(tmpbuf, " bones");
5013 if (ParanoidHit)
5014 Strcat(tmpbuf, " attack");
5015 if (ParanoidPray)
5016 Strcat(tmpbuf, " pray");
5017 if (ParanoidRemove)
5018 Strcat(tmpbuf, " Remove");
5019 Strcpy(buf, tmpbuf[0] ? &tmpbuf[1] : "none");
5020 } else if (!strcmp(optname, "pettype")) {
5021 Sprintf(buf, "%s", (preferred_pet == 'c') ? "cat"
5022 : (preferred_pet == 'd') ? "dog"
5023 : (preferred_pet == 'h') ? "horse"
5024 : (preferred_pet == 'n') ? "none"
5025 : "random");
5026 } else if (!strcmp(optname, "pickup_burden")) {
5027 Sprintf(buf, "%s", burdentype[flags.pickup_burden]);
5028 } else if (!strcmp(optname, "pickup_types")) {
5029 oc_to_str(flags.pickup_types, ocl);
5030 Sprintf(buf, "%s", ocl[0] ? ocl : "all");
5031 } else if (!strcmp(optname, "pile_limit")) {
5032 Sprintf(buf, "%d", flags.pile_limit);
5033 } else if (!strcmp(optname, "playmode")) {
5034 Strcpy(buf, wizard ? "debug" : discover ? "explore" : "normal");
5035 } else if (!strcmp(optname, "race")) {
5036 Sprintf(buf, "%s", rolestring(flags.initrace, races, noun));
5037 } else if (!strcmp(optname, "roguesymset")) {
5038 Sprintf(buf, "%s",
5039 symset[ROGUESET].name ? symset[ROGUESET].name : "default");
5040 if (currentgraphics == ROGUESET && symset[ROGUESET].name)
5041 Strcat(buf, ", active");
5042 } else if (!strcmp(optname, "role")) {
5043 Sprintf(buf, "%s", rolestring(flags.initrole, roles, name.m));
5044 } else if (!strcmp(optname, "runmode")) {
5045 Sprintf(buf, "%s", runmodes[flags.runmode]);
5046 } else if (!strcmp(optname, "whatis_coord")) {
5047 Sprintf(buf, "%s",
5048 (iflags.getpos_coords == GPCOORDS_MAP) ? "map"
5049 : (iflags.getpos_coords == GPCOORDS_COMPASS) ? "compass"
5050 : (iflags.getpos_coords == GPCOORDS_COMFULL) ? "full compass"
5051 : (iflags.getpos_coords == GPCOORDS_SCREEN) ? "screen"
5052 : "none");
5053 } else if (!strcmp(optname, "scores")) {
5054 Sprintf(buf, "%d top/%d around%s", flags.end_top, flags.end_around,
5055 flags.end_own ? "/own" : "");
5056 } else if (!strcmp(optname, "scroll_amount")) {
5057 if (iflags.wc_scroll_amount)
5058 Sprintf(buf, "%d", iflags.wc_scroll_amount);
5059 else
5060 Strcpy(buf, defopt);
5061 } else if (!strcmp(optname, "scroll_margin")) {
5062 if (iflags.wc_scroll_margin)
5063 Sprintf(buf, "%d", iflags.wc_scroll_margin);
5064 else
5065 Strcpy(buf, defopt);
5066 } else if (!strcmp(optname, "sortloot")) {
5067 for (i = 0; i < SIZE(sortltype); i++)
5068 if (flags.sortloot == sortltype[i][0]) {
5069 Strcpy(buf, sortltype[i]);
5070 break;
5072 } else if (!strcmp(optname, "player_selection")) {
5073 Sprintf(buf, "%s", iflags.wc_player_selection ? "prompts" : "dialog");
5074 #ifdef MSDOS
5075 } else if (!strcmp(optname, "soundcard")) {
5076 Sprintf(buf, "%s", to_be_done);
5077 #endif
5078 } else if (!strcmp(optname, "suppress_alert")) {
5079 if (flags.suppress_alert == 0L)
5080 Strcpy(buf, none);
5081 else
5082 Sprintf(buf, "%lu.%lu.%lu", FEATURE_NOTICE_VER_MAJ,
5083 FEATURE_NOTICE_VER_MIN, FEATURE_NOTICE_VER_PATCH);
5084 } else if (!strcmp(optname, "symset")) {
5085 Sprintf(buf, "%s",
5086 symset[PRIMARY].name ? symset[PRIMARY].name : "default");
5087 if (currentgraphics == PRIMARY && symset[PRIMARY].name)
5088 Strcat(buf, ", active");
5089 } else if (!strcmp(optname, "tile_file")) {
5090 Sprintf(buf, "%s",
5091 iflags.wc_tile_file ? iflags.wc_tile_file : defopt);
5092 } else if (!strcmp(optname, "tile_height")) {
5093 if (iflags.wc_tile_height)
5094 Sprintf(buf, "%d", iflags.wc_tile_height);
5095 else
5096 Strcpy(buf, defopt);
5097 } else if (!strcmp(optname, "tile_width")) {
5098 if (iflags.wc_tile_width)
5099 Sprintf(buf, "%d", iflags.wc_tile_width);
5100 else
5101 Strcpy(buf, defopt);
5102 } else if (!strcmp(optname, "traps")) {
5103 Sprintf(buf, "%s", to_be_done);
5104 } else if (!strcmp(optname, "vary_msgcount")) {
5105 if (iflags.wc_vary_msgcount)
5106 Sprintf(buf, "%d", iflags.wc_vary_msgcount);
5107 else
5108 Strcpy(buf, defopt);
5109 #ifdef MSDOS
5110 } else if (!strcmp(optname, "video")) {
5111 Sprintf(buf, "%s", to_be_done);
5112 #endif
5113 #ifdef VIDEOSHADES
5114 } else if (!strcmp(optname, "videoshades")) {
5115 Sprintf(buf, "%s-%s-%s", shade[0], shade[1], shade[2]);
5116 } else if (!strcmp(optname, "videocolors")) {
5117 Sprintf(buf, "%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d",
5118 ttycolors[CLR_RED], ttycolors[CLR_GREEN],
5119 ttycolors[CLR_BROWN], ttycolors[CLR_BLUE],
5120 ttycolors[CLR_MAGENTA], ttycolors[CLR_CYAN],
5121 ttycolors[CLR_ORANGE], ttycolors[CLR_BRIGHT_GREEN],
5122 ttycolors[CLR_YELLOW], ttycolors[CLR_BRIGHT_BLUE],
5123 ttycolors[CLR_BRIGHT_MAGENTA], ttycolors[CLR_BRIGHT_CYAN]);
5124 #endif /* VIDEOSHADES */
5125 } else if (!strcmp(optname, "windowtype")) {
5126 Sprintf(buf, "%s", windowprocs.name);
5127 } else if (!strcmp(optname, "windowcolors")) {
5128 Sprintf(
5129 buf, "%s/%s %s/%s %s/%s %s/%s",
5130 iflags.wc_foregrnd_menu ? iflags.wc_foregrnd_menu : defbrief,
5131 iflags.wc_backgrnd_menu ? iflags.wc_backgrnd_menu : defbrief,
5132 iflags.wc_foregrnd_message ? iflags.wc_foregrnd_message
5133 : defbrief,
5134 iflags.wc_backgrnd_message ? iflags.wc_backgrnd_message
5135 : defbrief,
5136 iflags.wc_foregrnd_status ? iflags.wc_foregrnd_status : defbrief,
5137 iflags.wc_backgrnd_status ? iflags.wc_backgrnd_status : defbrief,
5138 iflags.wc_foregrnd_text ? iflags.wc_foregrnd_text : defbrief,
5139 iflags.wc_backgrnd_text ? iflags.wc_backgrnd_text : defbrief);
5140 #ifdef PREFIXES_IN_USE
5141 } else {
5142 for (i = 0; i < PREFIX_COUNT; ++i)
5143 if (!strcmp(optname, fqn_prefix_names[i]) && fqn_prefix[i])
5144 Sprintf(buf, "%s", fqn_prefix[i]);
5145 #endif
5148 if (buf[0])
5149 return buf;
5150 else
5151 return "unknown";
5155 dotogglepickup()
5157 char buf[BUFSZ], ocl[MAXOCLASSES + 1];
5159 flags.pickup = !flags.pickup;
5160 if (flags.pickup) {
5161 oc_to_str(flags.pickup_types, ocl);
5162 Sprintf(buf, "ON, for %s objects%s", ocl[0] ? ocl : "all",
5163 (iflags.autopickup_exceptions[AP_LEAVE]
5164 || iflags.autopickup_exceptions[AP_GRAB])
5165 ? ((count_ape_maps((int *) 0, (int *) 0) == 1)
5166 ? ", with one exception"
5167 : ", with some exceptions")
5168 : "");
5169 } else {
5170 Strcpy(buf, "OFF");
5172 pline("Autopickup: %s.", buf);
5173 return 0;
5177 add_autopickup_exception(mapping)
5178 const char *mapping;
5180 static const char
5181 APE_regex_error[] = "regex error in AUTOPICKUP_EXCEPTION",
5182 APE_syntax_error[] = "syntax error in AUTOPICKUP_EXCEPTION";
5184 struct autopickup_exception *ape, **apehead;
5185 char text[256], end;
5186 int n;
5187 boolean grab = FALSE;
5189 /* scan length limit used to be 255, but smaller size allows the
5190 quoted value to fit within BUFSZ, simplifying formatting elsewhere;
5191 this used to ignore the possibility of trailing junk but now checks
5192 for it, accepting whitespace but rejecting anything else unless it
5193 starts with '#" for a comment */
5194 end = '\0';
5195 if ((n = sscanf(mapping, "\"<%253[^\"]\" %c", text, &end)) == 1
5196 || (n == 2 && end == '#')) {
5197 grab = TRUE;
5198 } else if ((n = sscanf(mapping, "\">%253[^\"]\" %c", text, &end)) == 1
5199 || (n = sscanf(mapping, "\"%253[^\"]\" %c", text, &end)) == 1
5200 || (n == 2 && end == '#')) {
5201 grab = FALSE;
5202 } else {
5203 if (!iflags.window_inited)
5204 raw_print(APE_syntax_error); /* from options file */
5205 else
5206 pline("%s", APE_syntax_error); /* via 'O' command */
5207 return 0;
5210 ape = (struct autopickup_exception *) alloc(sizeof *ape);
5211 ape->regex = regex_init();
5212 if (!regex_compile(text, ape->regex)) {
5213 if (!iflags.window_inited)
5214 raw_print(APE_regex_error);
5215 else
5216 pline("%s", APE_regex_error);
5217 regex_free(ape->regex);
5218 free((genericptr_t) ape);
5219 return 0;
5221 apehead = (grab) ? &iflags.autopickup_exceptions[AP_GRAB]
5222 : &iflags.autopickup_exceptions[AP_LEAVE];
5224 ape->pattern = dupstr(text);
5225 ape->grab = grab;
5226 ape->next = *apehead;
5227 *apehead = ape;
5228 return 1;
5231 STATIC_OVL void
5232 remove_autopickup_exception(whichape)
5233 struct autopickup_exception *whichape;
5235 struct autopickup_exception *ape, *prev = 0;
5236 int chain = whichape->grab ? AP_GRAB : AP_LEAVE;
5238 for (ape = iflags.autopickup_exceptions[chain]; ape;) {
5239 if (ape == whichape) {
5240 struct autopickup_exception *freeape = ape;
5242 ape = ape->next;
5243 if (prev)
5244 prev->next = ape;
5245 else
5246 iflags.autopickup_exceptions[chain] = ape;
5247 regex_free(freeape->regex);
5248 free((genericptr_t) freeape->pattern);
5249 free((genericptr_t) freeape);
5250 } else {
5251 prev = ape;
5252 ape = ape->next;
5257 STATIC_OVL int
5258 count_ape_maps(leave, grab)
5259 int *leave, *grab;
5261 struct autopickup_exception *ape;
5262 int pass, totalapes, numapes[2] = { 0, 0 };
5264 for (pass = AP_LEAVE; pass <= AP_GRAB; ++pass) {
5265 ape = iflags.autopickup_exceptions[pass];
5266 while (ape) {
5267 ape = ape->next;
5268 numapes[pass]++;
5271 totalapes = numapes[AP_LEAVE] + numapes[AP_GRAB];
5272 if (leave)
5273 *leave = numapes[AP_LEAVE];
5274 if (grab)
5275 *grab = numapes[AP_GRAB];
5276 return totalapes;
5279 void
5280 free_autopickup_exceptions()
5282 struct autopickup_exception *ape;
5283 int pass;
5285 for (pass = AP_LEAVE; pass <= AP_GRAB; ++pass) {
5286 while ((ape = iflags.autopickup_exceptions[pass]) != 0) {
5287 regex_free(ape->regex);
5288 free((genericptr_t) ape->pattern);
5289 iflags.autopickup_exceptions[pass] = ape->next;
5290 free((genericptr_t) ape);
5295 /* bundle some common usage into one easy-to-use routine */
5297 load_symset(s, which_set)
5298 const char *s;
5299 int which_set;
5301 clear_symsetentry(which_set, TRUE);
5303 if (symset[which_set].name)
5304 free((genericptr_t) symset[which_set].name);
5305 symset[which_set].name = dupstr(s);
5307 if (read_sym_file(which_set)) {
5308 switch_symbols(TRUE);
5309 } else {
5310 clear_symsetentry(which_set, TRUE);
5311 return 0;
5313 return 1;
5316 void
5317 free_symsets()
5319 clear_symsetentry(PRIMARY, TRUE);
5320 clear_symsetentry(ROGUESET, TRUE);
5322 /* symset_list is cleaned up as soon as it's used, so we shouldn't
5323 have to anything about it here */
5324 /* assert( symset_list == NULL ); */
5327 /* Parse the value of a SYMBOLS line from a config file */
5328 boolean
5329 parsesymbols(opts)
5330 register char *opts;
5332 int val;
5333 char *op, *symname, *strval;
5334 struct symparse *symp;
5336 if ((op = index(opts, ',')) != 0) {
5337 *op++ = 0;
5338 if (!parsesymbols(op)) return FALSE;
5341 /* S_sample:string */
5342 symname = opts;
5343 strval = index(opts, ':');
5344 if (!strval)
5345 strval = index(opts, '=');
5346 if (!strval)
5347 return FALSE;
5348 *strval++ = '\0';
5350 /* strip leading and trailing white space from symname and strval */
5351 mungspaces(symname);
5352 mungspaces(strval);
5354 symp = match_sym(symname);
5355 if (!symp)
5356 return FALSE;
5358 if (symp->range && symp->range != SYM_CONTROL) {
5359 val = sym_val(strval);
5360 update_l_symset(symp, val);
5362 return TRUE;
5365 struct symparse *
5366 match_sym(buf)
5367 char *buf;
5369 size_t len = strlen(buf);
5370 const char *p = index(buf, ':'), *q = index(buf, '=');
5371 struct symparse *sp = loadsyms;
5373 if (!p || (q && q < p))
5374 p = q;
5375 if (p) {
5376 /* note: there will be at most one space before the '='
5377 because caller has condensed buf[] with mungspaces() */
5378 if (p > buf && p[-1] == ' ')
5379 p--;
5380 len = (int) (p - buf);
5382 while (sp->range) {
5383 if ((len >= strlen(sp->name)) && !strncmpi(buf, sp->name, len))
5384 return sp;
5385 sp++;
5387 return (struct symparse *) 0;
5391 sym_val(strval)
5392 const char *strval;
5394 char buf[QBUFSZ];
5396 buf[0] = '\0';
5397 if (!strval[0] || !strval[1]) { /* empty, or single character */
5398 /* if single char is space or tab, leave buf[0]=='\0' */
5399 if (!isspace((uchar) strval[0]))
5400 buf[0] = strval[0];
5401 } else if (strval[0] == '\'') { /* single quote */
5402 /* simple matching single quote; we know strval[1] isn't '\0' */
5403 if (strval[2] == '\'' && !strval[3]) {
5404 /* accepts '\' as backslash and ''' as single quote */
5405 buf[0] = strval[1];
5407 /* if backslash, handle single or double quote or second backslash */
5408 } else if (strval[1] == '\\' && strval[2] && strval[3] == '\''
5409 && index("'\"\\", strval[2]) && !strval[4]) {
5410 buf[0] = strval[2];
5412 /* not simple quote or basic backslash;
5413 strip closing quote and let escapes() deal with it */
5414 } else {
5415 char *p, tmp[QBUFSZ];
5417 (void) strncpy(tmp, strval + 1, sizeof tmp - 1);
5418 tmp[sizeof tmp - 1] = '\0';
5419 if ((p = rindex(tmp, '\'')) != 0) {
5420 *p = '\0';
5421 escapes(tmp, buf);
5422 } /* else buf[0] stays '\0' */
5424 } else /* not lone char nor single quote */
5425 escapes(strval, buf);
5427 return (int) *buf;
5430 /* data for option_help() */
5431 static const char *opt_intro[] = {
5432 "", " aNetHack Options Help:", "",
5433 #define CONFIG_SLOT 3 /* fill in next value at run-time */
5434 (char *) 0,
5435 #if !defined(MICRO) && !defined(MAC)
5436 "or use `ANETHACKOPTIONS=\"<options>\"' in your environment",
5437 #endif
5438 "(<options> is a list of options separated by commas)",
5439 #ifdef VMS
5440 "-- for example, $ DEFINE ANETHACKOPTIONS \"noautopickup,fruit:kumquat\"",
5441 #endif
5442 "or press \"O\" while playing and use the menu.", "",
5443 "Boolean options (which can be negated by prefixing them with '!' or \"no\"):",
5444 (char *) 0
5447 static const char *opt_epilog[] = {
5449 "Some of the options can be set only before the game is started; those",
5450 "items will not be selectable in the 'O' command's menu.", (char *) 0
5453 void
5454 option_help()
5456 char buf[BUFSZ], buf2[BUFSZ];
5457 register int i;
5458 winid datawin;
5460 datawin = create_nhwindow(NHW_TEXT);
5461 Sprintf(buf, "Set options as OPTIONS=<options> in %s", configfile);
5462 opt_intro[CONFIG_SLOT] = (const char *) buf;
5463 for (i = 0; opt_intro[i]; i++)
5464 putstr(datawin, 0, opt_intro[i]);
5466 /* Boolean options */
5467 for (i = 0; boolopt[i].name; i++) {
5468 if (boolopt[i].addr) {
5469 if (boolopt[i].addr == &iflags.sanity_check && !wizard)
5470 continue;
5471 if (boolopt[i].addr == &iflags.menu_tab_sep && !wizard)
5472 continue;
5473 next_opt(datawin, boolopt[i].name);
5476 next_opt(datawin, "");
5478 /* Compound options */
5479 putstr(datawin, 0, "Compound options:");
5480 for (i = 0; compopt[i].name; i++) {
5481 Sprintf(buf2, "`%s'", compopt[i].name);
5482 Sprintf(buf, "%-20s - %s%c", buf2, compopt[i].descr,
5483 compopt[i + 1].name ? ',' : '.');
5484 putstr(datawin, 0, buf);
5487 for (i = 0; opt_epilog[i]; i++)
5488 putstr(datawin, 0, opt_epilog[i]);
5490 display_nhwindow(datawin, FALSE);
5491 destroy_nhwindow(datawin);
5492 return;
5496 * prints the next boolean option, on the same line if possible, on a new
5497 * line if not. End with next_opt("").
5499 void
5500 next_opt(datawin, str)
5501 winid datawin;
5502 const char *str;
5504 static char *buf = 0;
5505 int i;
5506 char *s;
5508 if (!buf)
5509 *(buf = (char *) alloc(BUFSZ)) = '\0';
5511 if (!*str) {
5512 s = eos(buf);
5513 if (s > &buf[1] && s[-2] == ',')
5514 Strcpy(s - 2, "."); /* replace last ", " */
5515 i = COLNO; /* (greater than COLNO - 2) */
5516 } else {
5517 i = strlen(buf) + strlen(str) + 2;
5520 if (i > COLNO - 2) { /* rule of thumb */
5521 putstr(datawin, 0, buf);
5522 buf[0] = 0;
5524 if (*str) {
5525 Strcat(buf, str);
5526 Strcat(buf, ", ");
5527 } else {
5528 putstr(datawin, 0, str);
5529 free((genericptr_t) buf), buf = 0;
5531 return;
5534 /* Returns the fid of the fruit type; if that type already exists, it
5535 * returns the fid of that one; if it does not exist, it adds a new fruit
5536 * type to the chain and returns the new one.
5537 * If replace_fruit is sent in, replace the fruit in the chain rather than
5538 * adding a new entry--for user specified fruits only.
5541 fruitadd(str, replace_fruit)
5542 char *str;
5543 struct fruit *replace_fruit;
5545 register int i;
5546 register struct fruit *f;
5547 int highest_fruit_id = 0;
5548 char buf[PL_FSIZ], altname[PL_FSIZ];
5549 boolean user_specified = (str == pl_fruit);
5550 /* if not user-specified, then it's a fruit name for a fruit on
5551 * a bones level...
5554 /* Note: every fruit has an id (kept in obj->spe) of at least 1;
5555 * 0 is an error.
5557 if (user_specified) {
5558 boolean found = FALSE, numeric = FALSE;
5560 /* force fruit to be singular; this handling is not
5561 needed--or wanted--for fruits from bones because
5562 they already received it in their original game */
5563 nmcpy(pl_fruit, makesingular(str), PL_FSIZ);
5564 /* assert( str == pl_fruit ); */
5566 /* disallow naming after other foods (since it'd be impossible
5567 * to tell the difference)
5570 for (i = bases[FOOD_CLASS]; objects[i].oc_class == FOOD_CLASS; i++) {
5571 if (!strcmp(OBJ_NAME(objects[i]), pl_fruit)) {
5572 found = TRUE;
5573 break;
5577 char *c;
5579 c = pl_fruit;
5581 for (c = pl_fruit; *c >= '0' && *c <= '9'; c++)
5583 if (isspace((uchar) *c) || *c == 0)
5584 numeric = TRUE;
5586 if (found || numeric || !strncmp(str, "cursed ", 7)
5587 || !strncmp(str, "uncursed ", 9) || !strncmp(str, "blessed ", 8)
5588 || !strncmp(str, "partly eaten ", 13)
5589 || (!strncmp(str, "tin of ", 7)
5590 && (!strcmp(str + 7, "spinach")
5591 || name_to_mon(str + 7) >= LOW_PM))
5592 || !strcmp(str, "empty tin")
5593 || ((str_end_is(str, " corpse")
5594 || str_end_is(str, " egg"))
5595 && name_to_mon(str) >= LOW_PM)) {
5596 Strcpy(buf, pl_fruit);
5597 Strcpy(pl_fruit, "candied ");
5598 nmcpy(pl_fruit + 8, buf, PL_FSIZ - 8);
5600 *altname = '\0';
5601 /* This flag indicates that a fruit has been made since the
5602 * last time the user set the fruit. If it hasn't, we can
5603 * safely overwrite the current fruit, preventing the user from
5604 * setting many fruits in a row and overflowing.
5605 * Possible expansion: check for specific fruit IDs, not for
5606 * any fruit.
5608 flags.made_fruit = FALSE;
5609 if (replace_fruit) {
5610 for (f = ffruit; f; f = f->nextf) {
5611 if (f == replace_fruit) {
5612 copynchars(f->fname, str, PL_FSIZ - 1);
5613 goto nonew;
5617 } else {
5618 /* not user_supplied, so assumed to be from bones */
5619 copynchars(altname, str, PL_FSIZ - 1);
5620 sanitize_name(altname);
5621 flags.made_fruit = TRUE; /* for safety. Any fruit name added from a
5622 bones level should exist anyway. */
5624 for (f = ffruit; f; f = f->nextf) {
5625 if (f->fid > highest_fruit_id)
5626 highest_fruit_id = f->fid;
5627 if (!strncmp(str, f->fname, PL_FSIZ - 1)
5628 || (*altname && !strcmp(altname, f->fname)))
5629 goto nonew;
5631 /* if adding another fruit would overflow spe, use a random
5632 fruit instead... we've got a lot to choose from.
5633 current_fruit remains as is. */
5634 if (highest_fruit_id >= 127)
5635 return rnd(127);
5637 f = newfruit();
5638 (void) memset((genericptr_t)f, 0, sizeof(struct fruit));
5639 copynchars(f->fname, *altname ? altname : str, PL_FSIZ - 1);
5640 f->fid = ++highest_fruit_id;
5641 /* we used to go out of our way to add it at the end of the list,
5642 but the order is arbitrary so use simpler insertion at start */
5643 f->nextf = ffruit;
5644 ffruit = f;
5645 nonew:
5646 if (user_specified)
5647 context.current_fruit = f->fid;
5648 return f->fid;
5652 * This is a somewhat generic menu for taking a list of aNetHack style
5653 * class choices and presenting them via a description
5654 * rather than the traditional aNetHack characters.
5655 * (Benefits users whose first exposure to aNetHack is via tiles).
5657 * prompt
5658 * The title at the top of the menu.
5660 * category: 0 = monster class
5661 * 1 = object class
5663 * way
5664 * FALSE = PICK_ONE, TRUE = PICK_ANY
5666 * class_list
5667 * a null terminated string containing the list of choices.
5669 * class_selection
5670 * a null terminated string containing the selected characters.
5672 * Returns number selected.
5675 choose_classes_menu(prompt, category, way, class_list, class_select)
5676 const char *prompt;
5677 int category;
5678 boolean way;
5679 char *class_list;
5680 char *class_select;
5682 menu_item *pick_list = (menu_item *) 0;
5683 winid win;
5684 anything any;
5685 char buf[BUFSZ];
5686 int i, n;
5687 int ret;
5688 int next_accelerator, accelerator;
5690 if (class_list == (char *) 0 || class_select == (char *) 0)
5691 return 0;
5692 accelerator = 0;
5693 next_accelerator = 'a';
5694 any = zeroany;
5695 win = create_nhwindow(NHW_MENU);
5696 start_menu(win);
5697 while (*class_list) {
5698 const char *text;
5699 boolean selected;
5701 text = (char *) 0;
5702 selected = FALSE;
5703 switch (category) {
5704 case 0:
5705 text = def_monsyms[def_char_to_monclass(*class_list)].explain;
5706 accelerator = *class_list;
5707 Sprintf(buf, "%s", text);
5708 break;
5709 case 1:
5710 text = def_oc_syms[def_char_to_objclass(*class_list)].explain;
5711 accelerator = next_accelerator;
5712 Sprintf(buf, "%c %s", *class_list, text);
5713 break;
5714 default:
5715 impossible("choose_classes_menu: invalid category %d", category);
5717 if (way && *class_select) { /* Selections there already */
5718 if (index(class_select, *class_list)) {
5719 selected = TRUE;
5722 any.a_int = *class_list;
5723 add_menu(win, NO_GLYPH, &any, accelerator, category ? *class_list : 0,
5724 ATR_NONE, buf, selected);
5725 ++class_list;
5726 if (category > 0) {
5727 ++next_accelerator;
5728 if (next_accelerator == ('z' + 1))
5729 next_accelerator = 'A';
5730 if (next_accelerator == ('Z' + 1))
5731 break;
5734 end_menu(win, prompt);
5735 n = select_menu(win, way ? PICK_ANY : PICK_ONE, &pick_list);
5736 destroy_nhwindow(win);
5737 if (n > 0) {
5738 for (i = 0; i < n; ++i)
5739 *class_select++ = (char) pick_list[i].item.a_int;
5740 free((genericptr_t) pick_list);
5741 ret = n;
5742 } else if (n == -1) {
5743 class_select = eos(class_select);
5744 ret = -1;
5745 } else
5746 ret = 0;
5747 *class_select = '\0';
5748 return ret;
5751 struct wc_Opt wc_options[] = { { "ascii_map", WC_ASCII_MAP },
5752 { "color", WC_COLOR },
5753 { "eight_bit_tty", WC_EIGHT_BIT_IN },
5754 { "hilite_pet", WC_HILITE_PET },
5755 { "popup_dialog", WC_POPUP_DIALOG },
5756 { "player_selection", WC_PLAYER_SELECTION },
5757 { "preload_tiles", WC_PRELOAD_TILES },
5758 { "tiled_map", WC_TILED_MAP },
5759 { "tile_file", WC_TILE_FILE },
5760 { "tile_width", WC_TILE_WIDTH },
5761 { "tile_height", WC_TILE_HEIGHT },
5762 { "use_inverse", WC_INVERSE },
5763 { "align_message", WC_ALIGN_MESSAGE },
5764 { "align_status", WC_ALIGN_STATUS },
5765 { "font_map", WC_FONT_MAP },
5766 { "font_menu", WC_FONT_MENU },
5767 { "font_message", WC_FONT_MESSAGE },
5768 #if 0
5769 {"perm_invent", WC_PERM_INVENT},
5770 #endif
5771 { "font_size_map", WC_FONTSIZ_MAP },
5772 { "font_size_menu", WC_FONTSIZ_MENU },
5773 { "font_size_message", WC_FONTSIZ_MESSAGE },
5774 { "font_size_status", WC_FONTSIZ_STATUS },
5775 { "font_size_text", WC_FONTSIZ_TEXT },
5776 { "font_status", WC_FONT_STATUS },
5777 { "font_text", WC_FONT_TEXT },
5778 { "map_mode", WC_MAP_MODE },
5779 { "scroll_amount", WC_SCROLL_AMOUNT },
5780 { "scroll_margin", WC_SCROLL_MARGIN },
5781 { "splash_screen", WC_SPLASH_SCREEN },
5782 { "vary_msgcount", WC_VARY_MSGCOUNT },
5783 { "windowcolors", WC_WINDOWCOLORS },
5784 { "mouse_support", WC_MOUSE_SUPPORT },
5785 { (char *) 0, 0L } };
5787 struct wc_Opt wc2_options[] = { { "fullscreen", WC2_FULLSCREEN },
5788 { "softkeyboard", WC2_SOFTKEYBOARD },
5789 { "wraptext", WC2_WRAPTEXT },
5790 { "use_darkgray", WC2_DARKGRAY },
5791 #ifdef STATUS_VIA_WINDOWPORT
5792 { "hilite_status", WC2_HILITE_STATUS },
5793 #endif
5794 { (char *) 0, 0L } };
5797 * If a port wants to change or ensure that the SET_IN_SYS,
5798 * SET_IN_FILE, DISP_IN_GAME, or SET_IN_GAME status of an option is
5799 * correct (for controlling its display in the option menu) call
5800 * set_option_mod_status()
5801 * with the appropriate second argument.
5803 void
5804 set_option_mod_status(optnam, status)
5805 const char *optnam;
5806 int status;
5808 int k;
5810 if (SET__IS_VALUE_VALID(status)) {
5811 impossible("set_option_mod_status: status out of range %d.", status);
5812 return;
5814 for (k = 0; boolopt[k].name; k++) {
5815 if (!strncmpi(boolopt[k].name, optnam, strlen(optnam))) {
5816 boolopt[k].optflags = status;
5817 return;
5820 for (k = 0; compopt[k].name; k++) {
5821 if (!strncmpi(compopt[k].name, optnam, strlen(optnam))) {
5822 compopt[k].optflags = status;
5823 return;
5829 * You can set several wc_options in one call to
5830 * set_wc_option_mod_status() by setting
5831 * the appropriate bits for each option that you
5832 * are setting in the optmask argument
5833 * prior to calling.
5834 * example: set_wc_option_mod_status(WC_COLOR|WC_SCROLL_MARGIN,
5835 * SET_IN_GAME);
5837 void
5838 set_wc_option_mod_status(optmask, status)
5839 unsigned long optmask;
5840 int status;
5842 int k = 0;
5844 if (SET__IS_VALUE_VALID(status)) {
5845 impossible("set_wc_option_mod_status: status out of range %d.",
5846 status);
5847 return;
5849 while (wc_options[k].wc_name) {
5850 if (optmask & wc_options[k].wc_bit) {
5851 set_option_mod_status(wc_options[k].wc_name, status);
5853 k++;
5857 STATIC_OVL boolean
5858 is_wc_option(optnam)
5859 const char *optnam;
5861 int k = 0;
5863 while (wc_options[k].wc_name) {
5864 if (strcmp(wc_options[k].wc_name, optnam) == 0)
5865 return TRUE;
5866 k++;
5868 return FALSE;
5871 STATIC_OVL boolean
5872 wc_supported(optnam)
5873 const char *optnam;
5875 int k = 0;
5877 while (wc_options[k].wc_name) {
5878 if (!strcmp(wc_options[k].wc_name, optnam)
5879 && (windowprocs.wincap & wc_options[k].wc_bit))
5880 return TRUE;
5881 k++;
5883 return FALSE;
5887 * You can set several wc2_options in one call to
5888 * set_wc2_option_mod_status() by setting
5889 * the appropriate bits for each option that you
5890 * are setting in the optmask argument
5891 * prior to calling.
5892 * example:
5893 * set_wc2_option_mod_status(WC2_FULLSCREEN|WC2_SOFTKEYBOARD|WC2_WRAPTEXT,
5894 * SET_IN_FILE);
5897 void
5898 set_wc2_option_mod_status(optmask, status)
5899 unsigned long optmask;
5900 int status;
5902 int k = 0;
5904 if (SET__IS_VALUE_VALID(status)) {
5905 impossible("set_wc2_option_mod_status: status out of range %d.",
5906 status);
5907 return;
5909 while (wc2_options[k].wc_name) {
5910 if (optmask & wc2_options[k].wc_bit) {
5911 set_option_mod_status(wc2_options[k].wc_name, status);
5913 k++;
5917 STATIC_OVL boolean
5918 is_wc2_option(optnam)
5919 const char *optnam;
5921 int k = 0;
5923 while (wc2_options[k].wc_name) {
5924 if (strcmp(wc2_options[k].wc_name, optnam) == 0)
5925 return TRUE;
5926 k++;
5928 return FALSE;
5931 STATIC_OVL boolean
5932 wc2_supported(optnam)
5933 const char *optnam;
5935 int k = 0;
5937 while (wc2_options[k].wc_name) {
5938 if (!strcmp(wc2_options[k].wc_name, optnam)
5939 && (windowprocs.wincap2 & wc2_options[k].wc_bit))
5940 return TRUE;
5941 k++;
5943 return FALSE;
5946 STATIC_OVL void
5947 wc_set_font_name(opttype, fontname)
5948 int opttype;
5949 char *fontname;
5951 char **fn = (char **) 0;
5953 if (!fontname)
5954 return;
5955 switch (opttype) {
5956 case MAP_OPTION:
5957 fn = &iflags.wc_font_map;
5958 break;
5959 case MESSAGE_OPTION:
5960 fn = &iflags.wc_font_message;
5961 break;
5962 case TEXT_OPTION:
5963 fn = &iflags.wc_font_text;
5964 break;
5965 case MENU_OPTION:
5966 fn = &iflags.wc_font_menu;
5967 break;
5968 case STATUS_OPTION:
5969 fn = &iflags.wc_font_status;
5970 break;
5971 default:
5972 return;
5974 if (fn) {
5975 if (*fn)
5976 free((genericptr_t) *fn);
5977 *fn = dupstr(fontname);
5979 return;
5982 STATIC_OVL int
5983 wc_set_window_colors(op)
5984 char *op;
5986 /* syntax:
5987 * menu white/black message green/yellow status white/blue text
5988 * white/black
5990 int j;
5991 char buf[BUFSZ];
5992 char *wn, *tfg, *tbg, *newop;
5993 static const char *wnames[] = { "menu", "message", "status", "text" };
5994 static const char *shortnames[] = { "mnu", "msg", "sts", "txt" };
5995 static char **fgp[] = { &iflags.wc_foregrnd_menu,
5996 &iflags.wc_foregrnd_message,
5997 &iflags.wc_foregrnd_status,
5998 &iflags.wc_foregrnd_text };
5999 static char **bgp[] = { &iflags.wc_backgrnd_menu,
6000 &iflags.wc_backgrnd_message,
6001 &iflags.wc_backgrnd_status,
6002 &iflags.wc_backgrnd_text };
6004 Strcpy(buf, op);
6005 newop = mungspaces(buf);
6006 while (newop && *newop) {
6007 wn = tfg = tbg = (char *) 0;
6009 /* until first non-space in case there's leading spaces - before
6010 * colorname*/
6011 if (*newop == ' ')
6012 newop++;
6013 if (*newop)
6014 wn = newop;
6015 else
6016 return 0;
6018 /* until first space - colorname*/
6019 while (*newop && *newop != ' ')
6020 newop++;
6021 if (*newop)
6022 *newop = '\0';
6023 else
6024 return 0;
6025 newop++;
6027 /* until first non-space - before foreground*/
6028 if (*newop == ' ')
6029 newop++;
6030 if (*newop)
6031 tfg = newop;
6032 else
6033 return 0;
6035 /* until slash - foreground */
6036 while (*newop && *newop != '/')
6037 newop++;
6038 if (*newop)
6039 *newop = '\0';
6040 else
6041 return 0;
6042 newop++;
6044 /* until first non-space (in case there's leading space after slash) -
6045 * before background */
6046 if (*newop == ' ')
6047 newop++;
6048 if (*newop)
6049 tbg = newop;
6050 else
6051 return 0;
6053 /* until first space - background */
6054 while (*newop && *newop != ' ')
6055 newop++;
6056 if (*newop)
6057 *newop++ = '\0';
6059 for (j = 0; j < 4; ++j) {
6060 if (!strcmpi(wn, wnames[j]) || !strcmpi(wn, shortnames[j])) {
6061 if (tfg && !strstri(tfg, " ")) {
6062 if (*fgp[j])
6063 free((genericptr_t) *fgp[j]);
6064 *fgp[j] = dupstr(tfg);
6066 if (tbg && !strstri(tbg, " ")) {
6067 if (*bgp[j])
6068 free((genericptr_t) *bgp[j]);
6069 *bgp[j] = dupstr(tbg);
6071 break;
6075 return 1;
6078 /* set up for wizard mode if player or save file has requested to it;
6079 called from port-specific startup code to handle `anethack -D' or
6080 OPTIONS=playmode:debug, or from dorecover()'s restgamestate() if
6081 restoring a game which was saved in wizard mode */
6082 void
6083 set_playmode()
6085 if (wizard) {
6086 if (authorize_wizard_mode())
6087 Strcpy(plname, "wizard");
6088 else
6089 wizard = FALSE; /* not allowed or not available */
6090 /* force explore mode if we didn't make it into wizard mode */
6091 discover = !wizard;
6092 iflags.deferred_X = FALSE;
6094 /* don't need to do anything special for explore mode or normal play */
6097 #endif /* OPTION_LISTS_ONLY */
6099 /*options.c*/