1 /**
2 * \file ui-options.c
3 * \brief Text UI options handling code (everything accessible from '=')
4 *
5 * Copyright (c) 1997-2000 Robert A. Koeneke, James E. Wilson, Ben Harrison
6 * Copyright (c) 2007 Pete Mack
7 * Copyright (c) 2010 Andi Sidwell
8 *
9 * This work is free software; you can redistribute it and/or modify it
10 * under the terms of either:
11 *
12 * a) the GNU General Public License as published by the Free Software
13 * Foundation, version 2, or
14 *
15 * b) the "Angband licence":
16 * This software may be copied and distributed for educational, research,
17 * and not for profit purposes provided that this copyright and statement
18 * are included in all such copies. Other copyrights may also apply.
19 */
20 #include "angband.h"
21 #include "cmds.h"
22 #include "game-input.h"
23 #include "init.h"
24 #include "obj-desc.h"
25 #include "obj-ignore.h"
26 #include "obj-tval.h"
27 #include "obj-util.h"
28 #include "object.h"
29 #include "player-calcs.h"
30 #include "ui-display.h"
31 #include "ui-help.h"
32 #include "ui-input.h"
33 #include "ui-keymap.h"
34 #include "ui-knowledge.h"
35 #include "ui-menu.h"
36 #include "ui-options.h"
37 #include "ui-prefs.h"
38 #include "ui-target.h"
39
40
41 /**
42 * Prompt the user for a filename to save the pref file to.
43 */
get_pref_path(const char * what,int row,char * buf,size_t max)44 static bool get_pref_path(const char *what, int row, char *buf, size_t max)
45 {
46 char ftmp[80];
47 bool ok;
48
49 screen_save();
50
51 /* Prompt */
52 prt(format("%s to a pref file", what), row, 0);
53 prt("File: ", row + 2, 0);
54
55 /* Get the filesystem-safe name and append .prf */
56 player_safe_name(ftmp, sizeof(ftmp), player->full_name, true);
57 my_strcat(ftmp, ".prf", sizeof(ftmp));
58
59 /* Get a filename */
60
61 if(!arg_force_name)
62 ok = askfor_aux(ftmp, sizeof ftmp, NULL);
63
64 else
65 ok = get_check(format("Confirm writing to %s? ", ftmp));
66
67 screen_load();
68
69 /* Build the filename */
70 if (ok)
71 path_build(buf, max, ANGBAND_DIR_USER, ftmp);
72
73 return ok;
74 }
75
76
dump_pref_file(void (* dump)(ang_file *),const char * title,int row)77 static void dump_pref_file(void (*dump)(ang_file *), const char *title, int row)
78 {
79 char buf[1024];
80
81 /* Get filename from user */
82 if (!get_pref_path(title, row, buf, sizeof(buf)))
83 return;
84
85 /* Try to save */
86 if (prefs_save(buf, dump, title))
87 msg("Saved %s.", strstr(title, " ") + 1);
88 else
89 msg("Failed to save %s.", strstr(title, " ") + 1);
90
91 event_signal(EVENT_MESSAGE_FLUSH);
92
93 return;
94 }
95
96 static void do_cmd_pref_file_hack(long row);
97
98
99
100
101
102
103 /**
104 * ------------------------------------------------------------------------
105 * Options display and setting
106 * ------------------------------------------------------------------------ */
107
108
109 /**
110 * Displays an option entry.
111 */
option_toggle_display(struct menu * m,int oid,bool cursor,int row,int col,int width)112 static void option_toggle_display(struct menu *m, int oid, bool cursor,
113 int row, int col, int width)
114 {
115 byte attr = curs_attrs[CURS_KNOWN][cursor != 0];
116 bool *options = menu_priv(m);
117
118 c_prt(attr, format("%-45s: %s (%s)", option_desc(oid),
119 options[oid] ? "yes" : "no ", option_name(oid)), row, col);
120 }
121
122 /**
123 * Handle keypresses for an option entry.
124 */
option_toggle_handle(struct menu * m,const ui_event * event,int oid)125 static bool option_toggle_handle(struct menu *m, const ui_event *event,
126 int oid)
127 {
128 bool next = false;
129
130 if (event->type == EVT_SELECT) {
131 /* Hack -- birth options can not be toggled after birth */
132 /* At birth, m->flags == MN_DBL_TAP. */
133 /* After birth, m->flags == MN_NO_TAGS */
134 if (!((option_type(oid) == OP_BIRTH) && (m->flags == MN_NO_TAGS))) {
135 option_set(option_name(oid), !player->opts.opt[oid]);
136 }
137 } else if (event->type == EVT_KBRD) {
138 if (event->key.code == 'y' || event->key.code == 'Y') {
139 option_set(option_name(oid), true);
140 next = true;
141 } else if (event->key.code == 'n' || event->key.code == 'N') {
142 option_set(option_name(oid), false);
143 next = true;
144 } else if (event->key.code == 't' || event->key.code == 'T') {
145 option_set(option_name(oid), !player->opts.opt[oid]);
146 } else if (event->key.code == 's' || event->key.code == 'S') {
147 char dummy;
148
149 screen_save();
150 if (options_save_custom_birth(&player->opts)) {
151 get_com("Successfully saved. Press any key to continue.", &dummy);
152 } else {
153 get_com("Save failed. Press any key to continue.", &dummy);
154 }
155 screen_load();
156 /* Only allow restore from custom defaults at birth. */
157 } else if ((event->key.code == 'r' || event->key.code == 'R') &&
158 m->flags == MN_DBL_TAP) {
159 screen_save();
160 if (options_restore_custom_birth(&player->opts)) {
161 screen_load();
162 menu_refresh(m, false);
163 } else {
164 char dummy;
165
166 get_com("Restore failed. Press any key to continue.", &dummy);
167 screen_load();
168 }
169 /* Only allow reset to maintainer's defaults at birth. */
170 } else if ((event->key.code == 'm' || event->key.code == 'M') &&
171 m->flags == MN_DBL_TAP) {
172 options_reset_birth(&player->opts);
173 menu_refresh(m, false);
174 } else if (event->key.code == '?') {
175 screen_save();
176 show_file(format("option.txt#%s", option_name(oid)), NULL, 0, 0);
177 screen_load();
178 } else {
179 return false;
180 }
181 } else {
182 return false;
183 }
184
185 if (next) {
186 m->cursor++;
187 m->cursor = (m->cursor + m->filter_count) % m->filter_count;
188 }
189
190 return true;
191 }
192
193 /**
194 * Toggle option menu display and handling functions
195 */
196 static const menu_iter option_toggle_iter = {
197 NULL,
198 NULL,
199 option_toggle_display,
200 option_toggle_handle,
201 NULL
202 };
203
204
205 /**
206 * Interact with some options
207 */
option_toggle_menu(const char * name,int page)208 static void option_toggle_menu(const char *name, int page)
209 {
210 int i;
211
212 struct menu *m = menu_new(MN_SKIN_SCROLL, &option_toggle_iter);
213
214 /* for all menus */
215 m->prompt = "Set option (y/n/t), '?' for information";
216 m->cmd_keys = "?YyNnTt";
217 m->selections = "abcdefghijklmopqrsuvwxz";
218 m->flags = MN_DBL_TAP;
219
220 /* We add 10 onto the page amount to indicate we're at birth */
221 if (page == OPT_PAGE_BIRTH) {
222 m->prompt = "You can only modify these options at character birth. '?' for information";
223 m->cmd_keys = "?";
224 m->flags = MN_NO_TAGS;
225 } else if (page == OPT_PAGE_BIRTH + 10) {
226 m->prompt = "Set option (y/n/t), 's' to save, 'r' to restore, 'm' to reset, '?' for help";
227 m->cmd_keys = "?YyNnTtSsRrMm";
228 page -= 10;
229 }
230
231 /* for this particular menu */
232 m->title = name;
233
234 /* Find the number of valid entries */
235 for (i = 0; i < OPT_PAGE_PER; i++) {
236 if (option_page[page][i] == OPT_none)
237 break;
238 }
239
240 /* Set the data to the player's options */
241 menu_setpriv(m, OPT_MAX, &player->opts.opt);
242 menu_set_filter(m, option_page[page], i);
243 menu_layout(m, &SCREEN_REGION);
244
245 /* Run the menu */
246 screen_save();
247
248 clear_from(0);
249 menu_select(m, 0, false);
250
251 screen_load();
252
253 mem_free(m);
254 }
255
256 /**
257 * Edit birth options.
258 */
do_cmd_options_birth(void)259 void do_cmd_options_birth(void)
260 {
261 option_toggle_menu("Birth options", OPT_PAGE_BIRTH + 10);
262 }
263
264
265 /**
266 * Modify the "window" options
267 */
do_cmd_options_win(const char * name,int row)268 static void do_cmd_options_win(const char *name, int row)
269 {
270 int i, j, d;
271 int y = 0;
272 int x = 0;
273 ui_event ke;
274 u32b new_flags[ANGBAND_TERM_MAX];
275
276 /* Set new flags to the old values */
277 for (j = 0; j < ANGBAND_TERM_MAX; j++)
278 new_flags[j] = window_flag[j];
279
280 /* Clear screen */
281 screen_save();
282 clear_from(0);
283
284 /* Interact */
285 while (1) {
286 /* Prompt */
287 prt("Window flags (<dir> to move, 't'/Enter to toggle, or ESC)", 0, 0);
288
289 /* Display the windows */
290 for (j = 0; j < ANGBAND_TERM_MAX; j++) {
291 byte a = COLOUR_WHITE;
292
293 const char *s = angband_term_name[j];
294
295 /* Use color */
296 if (j == x) a = COLOUR_L_BLUE;
297
298 /* Window name, staggered, centered */
299 Term_putstr(35 + j * 5 - strlen(s) / 2, 2 + j % 2, -1, a, s);
300 }
301
302 /* Display the options */
303 for (i = 0; i < PW_MAX_FLAGS; i++) {
304 byte a = COLOUR_WHITE;
305
306 const char *str = window_flag_desc[i];
307
308 /* Use color */
309 if (i == y) a = COLOUR_L_BLUE;
310
311 /* Unused option */
312 if (!str) str = "(Unused option)";
313
314 /* Flag name */
315 Term_putstr(0, i + 5, -1, a, str);
316
317 /* Display the windows */
318 for (j = 0; j < ANGBAND_TERM_MAX; j++) {
319 wchar_t c = L'.';
320
321 a = COLOUR_WHITE;
322
323 /* Use color */
324 if ((i == y) && (j == x)) a = COLOUR_L_BLUE;
325
326 /* Active flag */
327 if (new_flags[j] & (1L << i)) c = L'X';
328
329 /* Flag value */
330 Term_putch(35 + j * 5, i + 5, a, c);
331 }
332 }
333
334 /* Place Cursor */
335 Term_gotoxy(35 + x * 5, y + 5);
336
337 /* Get key */
338 ke = inkey_ex();
339
340 /* Mouse or keyboard interaction */
341 if (ke.type == EVT_MOUSE) {
342 int choicey = ke.mouse.y - 5;
343 int choicex = (ke.mouse.x - 35)/5;
344
345 if (ke.mouse.button == 2)
346 break;
347
348 if ((choicey >= 0) && (choicey < PW_MAX_FLAGS)
349 && (choicex > 0) && (choicex < ANGBAND_TERM_MAX)
350 && !(ke.mouse.x % 5)) {
351 if ((choicey == y) && (choicex == x)) {
352 /* Toggle flag (off) */
353 if (new_flags[x] & (1L << y))
354 new_flags[x] &= ~(1L << y);
355 /* Toggle flag (on) */
356 else
357 new_flags[x] |= (1L << y);
358 } else {
359 y = choicey;
360 x = (ke.mouse.x - 35)/5;
361 }
362 }
363 } else if (ke.type == EVT_KBRD) {
364 if (ke.key.code == ESCAPE || ke.key.code == 'q')
365 break;
366
367 /* Toggle */
368 else if (ke.key.code == '5' || ke.key.code == 't' ||
369 ke.key.code == KC_ENTER) {
370 /* Hack -- ignore the main window */
371 if (x == 0)
372 bell("Cannot set main window flags!");
373
374 /* Toggle flag (off) */
375 else if (new_flags[x] & (1L << y))
376 new_flags[x] &= ~(1L << y);
377
378 /* Toggle flag (on) */
379 else
380 new_flags[x] |= (1L << y);
381
382 /* Continue */
383 continue;
384 }
385
386 /* Extract direction */
387 d = target_dir(ke.key);
388
389 /* Move */
390 if (d != 0) {
391 x = (x + ddx[d] + 8) % ANGBAND_TERM_MAX;
392 y = (y + ddy[d] + 16) % PW_MAX_FLAGS;
393 }
394 }
395 }
396
397 /* Notice changes */
398 subwindows_set_flags(new_flags, ANGBAND_TERM_MAX);
399
400 screen_load();
401 }
402
403
404
405 /**
406 * ------------------------------------------------------------------------
407 * Interact with keymaps
408 * ------------------------------------------------------------------------ */
409
410 /**
411 * Current (or recent) keymap action
412 */
413 static struct keypress keymap_buffer[KEYMAP_ACTION_MAX + 1];
414
415
416 /**
417 * Ask for, and display, a keymap trigger.
418 *
419 * Returns the trigger input.
420 *
421 * Note that both "event_signal(EVENT_INPUT_FLUSH)" calls are extremely
422 * important. This may
423 * no longer be true, since "util.c" is much simpler now. XXX XXX XXX
424 */
keymap_get_trigger(void)425 static struct keypress keymap_get_trigger(void)
426 {
427 char tmp[80];
428 struct keypress buf[2] = { KEYPRESS_NULL, KEYPRESS_NULL };
429
430 /* Flush */
431 event_signal(EVENT_INPUT_FLUSH);
432
433 /* Get a key */
434 buf[0] = inkey();
435
436 /* Convert to ascii */
437 keypress_to_text(tmp, sizeof(tmp), buf, false);
438
439 /* Hack -- display the trigger */
440 Term_addstr(-1, COLOUR_WHITE, tmp);
441
442 /* Flush */
443 event_signal(EVENT_INPUT_FLUSH);
444
445 /* Return trigger */
446 return buf[0];
447 }
448
449
450 /**
451 * Keymap menu action functions
452 */
453
ui_keymap_pref_load(const char * title,int row)454 static void ui_keymap_pref_load(const char *title, int row)
455 {
456 do_cmd_pref_file_hack(16);
457 }
458
ui_keymap_pref_append(const char * title,int row)459 static void ui_keymap_pref_append(const char *title, int row)
460 {
461 dump_pref_file(keymap_dump, "Dump keymaps", 13);
462 }
463
ui_keymap_query(const char * title,int row)464 static void ui_keymap_query(const char *title, int row)
465 {
466 char tmp[1024];
467 int mode = OPT(player, rogue_like_commands) ? KEYMAP_MODE_ROGUE : KEYMAP_MODE_ORIG;
468 struct keypress c;
469 const struct keypress *act;
470
471 prt(title, 13, 0);
472 prt("Key: ", 14, 0);
473
474 /* Get a keymap trigger & mapping */
475 c = keymap_get_trigger();
476 act = keymap_find(mode, c);
477
478 /* Keymap found? */
479 if (!act) {
480 /* Prompt */
481 prt("No keymap with that trigger. Press any key to continue.", 16, 0);
482 inkey();
483 } else {
484 /* Analyze the current action */
485 keypress_to_text(tmp, sizeof(tmp), act, false);
486
487 /* Display the current action */
488 prt("Found: ", 15, 0);
489 Term_addstr(-1, COLOUR_WHITE, tmp);
490
491 prt("Press any key to continue.", 17, 0);
492 inkey();
493 }
494 }
495
ui_keymap_create(const char * title,int row)496 static void ui_keymap_create(const char *title, int row)
497 {
498 bool done = false;
499 size_t n = 0;
500
501 struct keypress c;
502 char tmp[1024];
503 int mode = OPT(player, rogue_like_commands) ? KEYMAP_MODE_ROGUE : KEYMAP_MODE_ORIG;
504
505 prt(title, 13, 0);
506 prt("Key: ", 14, 0);
507
508 c = keymap_get_trigger();
509 if (c.code == '$') {
510 c_prt(COLOUR_L_RED, "The '$' key is reserved.", 16, 2);
511 prt("Press any key to continue.", 18, 0);
512 inkey();
513 return;
514 }
515
516 /* Get an encoded action, with a default response */
517 while (!done) {
518 struct keypress kp = {EVT_NONE, 0, 0};
519
520 int color = COLOUR_WHITE;
521 if (n == 0) color = COLOUR_YELLOW;
522 if (n == KEYMAP_ACTION_MAX) color = COLOUR_L_RED;
523
524 keypress_to_text(tmp, sizeof(tmp), keymap_buffer, false);
525 c_prt(color, format("Action: %s", tmp), 15, 0);
526
527 c_prt(COLOUR_L_BLUE, " Press '$' when finished.", 17, 0);
528 c_prt(COLOUR_L_BLUE, " Use 'CTRL-U' to reset.", 18, 0);
529 c_prt(COLOUR_L_BLUE, format("(Maximum keymap length is %d keys.)",
530 KEYMAP_ACTION_MAX), 19, 0);
531
532 kp = inkey();
533
534 if (kp.code == '$') {
535 done = true;
536 continue;
537 }
538
539 switch (kp.code) {
540 case KC_DELETE:
541 case KC_BACKSPACE: {
542 if (n > 0) {
543 n -= 1;
544 keymap_buffer[n].type = 0;
545 keymap_buffer[n].code = 0;
546 keymap_buffer[n].mods = 0;
547 }
548 break;
549 }
550
551 case KTRL('U'): {
552 memset(keymap_buffer, 0, sizeof keymap_buffer);
553 n = 0;
554 break;
555 }
556
557 default: {
558 if (n == KEYMAP_ACTION_MAX) continue;
559
560 if (n == 0) {
561 memset(keymap_buffer, 0, sizeof keymap_buffer);
562 }
563 keymap_buffer[n++] = kp;
564 break;
565 }
566 }
567 }
568
569 if (c.code && get_check("Save this keymap? ")) {
570 keymap_add(mode, c, keymap_buffer, true);
571 prt("Keymap added. Press any key to continue.", 17, 0);
572 inkey();
573 }
574 }
575
ui_keymap_remove(const char * title,int row)576 static void ui_keymap_remove(const char *title, int row)
577 {
578 struct keypress c;
579 int mode = OPT(player, rogue_like_commands) ? KEYMAP_MODE_ROGUE : KEYMAP_MODE_ORIG;
580
581 prt(title, 13, 0);
582 prt("Key: ", 14, 0);
583
584 c = keymap_get_trigger();
585
586 if (keymap_remove(mode, c))
587 prt("Removed.", 16, 0);
588 else
589 prt("No keymap to remove!", 16, 0);
590
591 /* Prompt */
592 prt("Press any key to continue.", 17, 0);
593 inkey();
594 }
595
keymap_browse_hook(int oid,void * db,const region * loc)596 static void keymap_browse_hook(int oid, void *db, const region *loc)
597 {
598 char tmp[1024];
599
600 event_signal(EVENT_MESSAGE_FLUSH);
601
602 clear_from(13);
603
604 /* Show current action */
605 prt("Current action (if any) shown below:", 13, 0);
606 keypress_to_text(tmp, sizeof(tmp), keymap_buffer, false);
607 prt(tmp, 14, 0);
608 }
609
610 static struct menu *keymap_menu;
611 static menu_action keymap_actions[] =
612 {
613 { 0, 0, "Load a user pref file", ui_keymap_pref_load },
614 { 0, 0, "Save keymaps to file", ui_keymap_pref_append },
615 { 0, 0, "Query a keymap", ui_keymap_query },
616 { 0, 0, "Create a keymap", ui_keymap_create },
617 { 0, 0, "Remove a keymap", ui_keymap_remove },
618 };
619
do_cmd_keymaps(const char * title,int row)620 static void do_cmd_keymaps(const char *title, int row)
621 {
622 region loc = {0, 0, 0, 12};
623
624 screen_save();
625 clear_from(0);
626
627 if (!keymap_menu) {
628 keymap_menu = menu_new_action(keymap_actions,
629 N_ELEMENTS(keymap_actions));
630
631 keymap_menu->title = title;
632 keymap_menu->selections = lower_case;
633 keymap_menu->browse_hook = keymap_browse_hook;
634 }
635
636 menu_layout(keymap_menu, &loc);
637 menu_select(keymap_menu, 0, false);
638
639 screen_load();
640 }
641
642
643
644 /**
645 * ------------------------------------------------------------------------
646 * Interact with visuals
647 * ------------------------------------------------------------------------ */
648
visuals_pref_load(const char * title,int row)649 static void visuals_pref_load(const char *title, int row)
650 {
651 do_cmd_pref_file_hack(15);
652 }
653
visuals_dump_monsters(const char * title,int row)654 static void visuals_dump_monsters(const char *title, int row)
655 {
656 dump_pref_file(dump_monsters, title, 15);
657 }
658
visuals_dump_objects(const char * title,int row)659 static void visuals_dump_objects(const char *title, int row)
660 {
661 dump_pref_file(dump_objects, title, 15);
662 }
663
visuals_dump_features(const char * title,int row)664 static void visuals_dump_features(const char *title, int row)
665 {
666 dump_pref_file(dump_features, title, 15);
667 }
668
visuals_dump_flavors(const char * title,int row)669 static void visuals_dump_flavors(const char *title, int row)
670 {
671 dump_pref_file(dump_flavors, title, 15);
672 }
673
visuals_reset(const char * title,int row)674 static void visuals_reset(const char *title, int row)
675 {
676 /* Reset */
677 reset_visuals(true);
678
679 /* Message */
680 prt("", 0, 0);
681 msg("Visual attr/char tables reset.");
682 event_signal(EVENT_MESSAGE_FLUSH);
683 }
684
685
686 static struct menu *visual_menu;
687 static menu_action visual_menu_items [] =
688 {
689 { 0, 0, "Load a user pref file", visuals_pref_load },
690 { 0, 0, "Save monster attr/chars", visuals_dump_monsters },
691 { 0, 0, "Save object attr/chars", visuals_dump_objects },
692 { 0, 0, "Save feature attr/chars", visuals_dump_features },
693 { 0, 0, "Save flavor attr/chars", visuals_dump_flavors },
694 { 0, 0, "Reset visuals", visuals_reset },
695 };
696
697
visuals_browse_hook(int oid,void * db,const region * loc)698 static void visuals_browse_hook(int oid, void *db, const region *loc)
699 {
700 event_signal(EVENT_MESSAGE_FLUSH);
701 clear_from(1);
702 }
703
704
705 /**
706 * Interact with "visuals"
707 */
do_cmd_visuals(const char * title,int row)708 static void do_cmd_visuals(const char *title, int row)
709 {
710 screen_save();
711 clear_from(0);
712
713 if (!visual_menu)
714 {
715 visual_menu = menu_new_action(visual_menu_items,
716 N_ELEMENTS(visual_menu_items));
717
718 visual_menu->title = title;
719 visual_menu->selections = lower_case;
720 visual_menu->browse_hook = visuals_browse_hook;
721 visual_menu->header = "To edit visuals, use the knowledge menu";
722 }
723
724 menu_layout(visual_menu, &SCREEN_REGION);
725 menu_select(visual_menu, 0, false);
726
727 screen_load();
728 }
729
730
731 /**
732 * ------------------------------------------------------------------------
733 * Interact with colours
734 * ------------------------------------------------------------------------ */
735
colors_pref_load(const char * title,int row)736 static void colors_pref_load(const char *title, int row)
737 {
738 /* Ask for and load a user pref file */
739 do_cmd_pref_file_hack(8);
740
741 /* XXX should probably be a cleaner way to tell UI about
742 * colour changes - how about doing this in the pref file
743 * loading code too? */
744 Term_xtra(TERM_XTRA_REACT, 0);
745 Term_redraw();
746 }
747
colors_pref_dump(const char * title,int row)748 static void colors_pref_dump(const char *title, int row)
749 {
750 dump_pref_file(dump_colors, title, 15);
751 }
752
colors_modify(const char * title,int row)753 static void colors_modify(const char *title, int row)
754 {
755 int i;
756
757 static byte a = 0;
758
759 /* Prompt */
760 prt("Command: Modify colors", 8, 0);
761
762 /* Hack -- query until done */
763 while (1) {
764 const char *name;
765 char index;
766
767 struct keypress cx;
768
769 /* Clear */
770 clear_from(10);
771
772 /* Exhibit the normal colors */
773 for (i = 0; i < BASIC_COLORS; i++) {
774 /* Exhibit this color */
775 Term_putstr(i*3, 20, -1, a, "##");
776
777 /* Exhibit character letter */
778 Term_putstr(i*3, 21, -1, (byte)i,
779 format(" %c", color_table[i].index_char));
780
781 /* Exhibit all colors */
782 Term_putstr(i*3, 22, -1, (byte)i, format("%2d", i));
783 }
784
785 /* Describe the color */
786 name = ((a < BASIC_COLORS) ? color_table[a].name : "undefined");
787 index = ((a < BASIC_COLORS) ? color_table[a].index_char : '?');
788
789 /* Describe the color */
790 Term_putstr(5, 10, -1, COLOUR_WHITE,
791 format("Color = %d, Name = %s, Index = %c",
792 a, name, index));
793
794 /* Label the Current values */
795 Term_putstr(5, 12, -1, COLOUR_WHITE,
796 format("K = 0x%02x / R,G,B = 0x%02x,0x%02x,0x%02x",
797 angband_color_table[a][0],
798 angband_color_table[a][1],
799 angband_color_table[a][2],
800 angband_color_table[a][3]));
801
802 /* Prompt */
803 Term_putstr(0, 14, -1, COLOUR_WHITE,
804 "Command (n/N/k/K/r/R/g/G/b/B): ");
805
806 /* Get a command */
807 cx = inkey();
808
809 /* All done */
810 if (cx.code == ESCAPE) break;
811
812 /* Analyze */
813 if (cx.code == 'n')
814 a = (byte)(a + 1);
815 if (cx.code == 'N')
816 a = (byte)(a - 1);
817 if (cx.code == 'k')
818 angband_color_table[a][0] = (byte)(angband_color_table[a][0] + 1);
819 if (cx.code == 'K')
820 angband_color_table[a][0] = (byte)(angband_color_table[a][0] - 1);
821 if (cx.code == 'r')
822 angband_color_table[a][1] = (byte)(angband_color_table[a][1] + 1);
823 if (cx.code == 'R')
824 angband_color_table[a][1] = (byte)(angband_color_table[a][1] - 1);
825 if (cx.code == 'g')
826 angband_color_table[a][2] = (byte)(angband_color_table[a][2] + 1);
827 if (cx.code == 'G')
828 angband_color_table[a][2] = (byte)(angband_color_table[a][2] - 1);
829 if (cx.code == 'b')
830 angband_color_table[a][3] = (byte)(angband_color_table[a][3] + 1);
831 if (cx.code == 'B')
832 angband_color_table[a][3] = (byte)(angband_color_table[a][3] - 1);
833
834 /* Hack -- react to changes */
835 Term_xtra(TERM_XTRA_REACT, 0);
836
837 /* Hack -- redraw */
838 Term_redraw();
839 }
840 }
841
colors_browse_hook(int oid,void * db,const region * loc)842 static void colors_browse_hook(int oid, void *db, const region *loc)
843 {
844 event_signal(EVENT_MESSAGE_FLUSH);
845 clear_from(1);
846 }
847
848
849 static struct menu *color_menu;
850 static menu_action color_events [] =
851 {
852 { 0, 0, "Load a user pref file", colors_pref_load },
853 { 0, 0, "Dump colors", colors_pref_dump },
854 { 0, 0, "Modify colors", colors_modify }
855 };
856
857 /**
858 * Interact with "colors"
859 */
do_cmd_colors(const char * title,int row)860 static void do_cmd_colors(const char *title, int row)
861 {
862 screen_save();
863 clear_from(0);
864
865 if (!color_menu)
866 {
867 color_menu = menu_new_action(color_events,
868 N_ELEMENTS(color_events));
869
870 color_menu->title = title;
871 color_menu->selections = lower_case;
872 color_menu->browse_hook = colors_browse_hook;
873 }
874
875 menu_layout(color_menu, &SCREEN_REGION);
876 menu_select(color_menu, 0, false);
877
878 screen_load();
879 }
880
881
882 /**
883 * ------------------------------------------------------------------------
884 * Non-complex menu actions
885 * ------------------------------------------------------------------------ */
886
askfor_aux_numbers(char * buf,size_t buflen,size_t * curs,size_t * len,struct keypress keypress,bool firsttime)887 static bool askfor_aux_numbers(char *buf, size_t buflen, size_t *curs, size_t *len, struct keypress keypress, bool firsttime)
888 {
889 switch (keypress.code)
890 {
891 case ESCAPE:
892 case KC_ENTER:
893 case ARROW_LEFT:
894 case ARROW_RIGHT:
895 case KC_DELETE:
896 case KC_BACKSPACE:
897 case '0':
898 case '1':
899 case '2':
900 case '3':
901 case '4':
902 case '5':
903 case '6':
904 case '7':
905 case '8':
906 case '9':
907 return askfor_aux_keypress(buf, buflen, curs, len, keypress,
908 firsttime);
909 }
910
911 return false;
912 }
913
914
915 /**
916 * Set base delay factor
917 */
do_cmd_delay(const char * name,int row)918 static void do_cmd_delay(const char *name, int row)
919 {
920 char tmp[4] = "";
921 int msec = player->opts.delay_factor;
922
923 strnfmt(tmp, sizeof(tmp), "%i", player->opts.delay_factor);
924
925 screen_save();
926
927 /* Prompt */
928 prt("Command: Base Delay Factor", 20, 0);
929
930 prt(format("Current base delay factor: %d msec",
931 player->opts.delay_factor, msec), 22, 0);
932 prt("New base delay factor (0-255): ", 21, 0);
933
934 /* Ask for a numeric value */
935 if (askfor_aux(tmp, sizeof(tmp), askfor_aux_numbers)) {
936 u16b val = (u16b) strtoul(tmp, NULL, 0);
937 player->opts.delay_factor = MIN(val, 255);
938 }
939
940 screen_load();
941 }
942
943 /**
944 * Set sidebar mode
945 */
do_cmd_sidebar_mode(const char * name,int row)946 static void do_cmd_sidebar_mode(const char *name, int row)
947 {
948 char tmp[20] = "";
949 const char *names[SIDEBAR_MAX] = {"Left", "Top", "None"};
950 struct keypress cx = KEYPRESS_NULL;
951
952 screen_save();
953
954 while (true) {
955
956 // Get the name
957 my_strcpy(tmp, names[SIDEBAR_MODE % SIDEBAR_MAX], sizeof(tmp));
958
959 /* Prompt */
960 prt("Command: Sidebar Mode", 20, 0);
961
962 prt("ESC: go back, other: cycle", 22, 0);
963
964 prt(format("Current mode: %s", tmp), 21, 0);
965
966 /* Get a command */
967 cx = inkey();
968
969 /* All done */
970 if (cx.code == ESCAPE) break;
971
972 // Cycle
973 SIDEBAR_MODE = (SIDEBAR_MODE + 1) % SIDEBAR_MAX;
974 }
975
976 screen_load();
977 }
978
979
980 /**
981 * Set hitpoint warning level
982 */
do_cmd_hp_warn(const char * name,int row)983 static void do_cmd_hp_warn(const char *name, int row)
984 {
985 bool res;
986 char tmp[4] = "";
987 byte warn;
988
989 strnfmt(tmp, sizeof(tmp), "%i", player->opts.hitpoint_warn);
990
991 screen_save();
992
993 /* Prompt */
994 prt("Command: Hitpoint Warning", 20, 0);
995
996 prt(format("Current hitpoint warning: %d (%d%%)",
997 player->opts.hitpoint_warn, player->opts.hitpoint_warn * 10), 22, 0);
998 prt("New hitpoint warning (0-9): ", 21, 0);
999
1000 /* Ask the user for a string */
1001 res = askfor_aux(tmp, sizeof(tmp), askfor_aux_numbers);
1002
1003 /* Process input */
1004 if (res) {
1005 warn = (byte) strtoul(tmp, NULL, 0);
1006
1007 /* Reset nonsensical warnings */
1008 if (warn > 9)
1009 warn = 0;
1010
1011 player->opts.hitpoint_warn = warn;
1012 }
1013
1014 screen_load();
1015 }
1016
1017
1018 /**
1019 * Set "lazy-movement" delay
1020 */
do_cmd_lazymove_delay(const char * name,int row)1021 static void do_cmd_lazymove_delay(const char *name, int row)
1022 {
1023 bool res;
1024 char tmp[4] = "";
1025
1026 strnfmt(tmp, sizeof(tmp), "%i", player->opts.lazymove_delay);
1027
1028 screen_save();
1029
1030 /* Prompt */
1031 prt("Command: Movement Delay Factor", 20, 0);
1032
1033 prt(format("Current movement delay: %d (%d msec)",
1034 player->opts.lazymove_delay, player->opts.lazymove_delay * 10), 22, 0);
1035 prt("New movement delay: ", 21, 0);
1036
1037 /* Ask the user for a string */
1038 res = askfor_aux(tmp, sizeof(tmp), askfor_aux_numbers);
1039
1040 /* Process input */
1041 if (res) {
1042 u16b delay = (u16b)strtoul(tmp, NULL, 0);
1043 player->opts.lazymove_delay = MIN(delay, 255);
1044 }
1045
1046 screen_load();
1047 }
1048
1049
1050
1051 /**
1052 * Ask for a "user pref file" and process it.
1053 *
1054 * This function should only be used by standard interaction commands,
1055 * in which a standard "Command:" prompt is present on the given row.
1056 *
1057 * Allow absolute file names? XXX XXX XXX
1058 */
do_cmd_pref_file_hack(long row)1059 static void do_cmd_pref_file_hack(long row)
1060 {
1061 char ftmp[80];
1062 bool ok;
1063
1064 screen_save();
1065
1066 /* Prompt */
1067 prt("Command: Load a user pref file", row, 0);
1068
1069 /* Prompt */
1070 prt("File: ", row + 2, 0);
1071
1072 /* Get the filesystem-safe name and append .prf */
1073 player_safe_name(ftmp, sizeof(ftmp), player->full_name, true);
1074 my_strcat(ftmp, ".prf", sizeof(ftmp));
1075
1076 if(!arg_force_name)
1077 ok = askfor_aux(ftmp, sizeof ftmp, NULL);
1078 else
1079 ok = get_check(format("Confirm loading %s? ", ftmp));
1080
1081 /* Ask for a file (or cancel) */
1082 if(ok) {
1083 /* Process the given filename */
1084 if (process_pref_file(ftmp, false, true) == false) {
1085 /* Mention failure */
1086 prt("", 0, 0);
1087 msg("Failed to load '%s'!", ftmp);
1088 } else {
1089 /* Mention success */
1090 prt("", 0, 0);
1091 msg("Loaded '%s'.", ftmp);
1092 }
1093 }
1094
1095 screen_load();
1096 }
1097
1098
1099
1100 /**
1101 * Write options to a file.
1102 */
do_dump_options(const char * title,int row)1103 static void do_dump_options(const char *title, int row) {
1104 dump_pref_file(option_dump, "Dump window settings", 20);
1105 }
1106
1107 /**
1108 * Write autoinscriptions to a file.
1109 */
do_dump_autoinsc(const char * title,int row)1110 static void do_dump_autoinsc(const char *title, int row) {
1111 dump_pref_file(dump_autoinscriptions, "Dump autoinscriptions", 20);
1112 }
1113
1114 /**
1115 * Write character screen customizations to a file.
1116 */
do_dump_charscreen_opt(const char * title,int row)1117 static void do_dump_charscreen_opt(const char *title, int row) {
1118 dump_pref_file(dump_ui_entry_renderers, "Dump char screen options", 20);
1119 }
1120
1121 /**
1122 * Load a pref file.
1123 */
options_load_pref_file(const char * n,int row)1124 static void options_load_pref_file(const char *n, int row)
1125 {
1126 do_cmd_pref_file_hack(20);
1127 }
1128
1129
1130
1131 /**
1132 * ------------------------------------------------------------------------
1133 * Ego item ignore menu
1134 * ------------------------------------------------------------------------ */
1135
1136 #define EGO_MENU_HELPTEXT \
1137 "{light green}Movement keys{/} scroll the list\n{light red}ESC{/} returns to the previous menu\n{light blue}Enter{/} toggles the current setting."
1138
1139 /**
1140 * Skip common prefixes in ego-item names.
1141 */
strip_ego_name(const char * name)1142 static const char *strip_ego_name(const char *name)
1143 {
1144 if (prefix(name, "of the "))
1145 return name + 7;
1146 if (prefix(name, "of "))
1147 return name + 3;
1148 return name;
1149 }
1150
1151
1152 /**
1153 * Display an ego-item type on the screen.
1154 */
ego_item_name(char * buf,size_t buf_size,struct ego_desc * desc)1155 int ego_item_name(char *buf, size_t buf_size, struct ego_desc *desc)
1156 {
1157 size_t i;
1158 int end;
1159 size_t prefix_size;
1160 const char *long_name;
1161
1162 struct ego_item *ego = &e_info[desc->e_idx];
1163
1164 /* Find the ignore type */
1165 for (i = 0; i < N_ELEMENTS(quality_choices); i++)
1166 if (desc->itype == i) break;
1167
1168 if (i == N_ELEMENTS(quality_choices)) return 0;
1169
1170 /* Initialize the buffer */
1171 end = my_strcat(buf, "[ ] ", buf_size);
1172
1173 /* Append the name */
1174 end += my_strcat(buf, quality_choices[i].name, buf_size);
1175
1176 /* Append an extra space */
1177 end += my_strcat(buf, " ", buf_size);
1178
1179 /* Get the full ego-item name */
1180 long_name = ego->name;
1181
1182 /* Get the length of the common prefix, if any */
1183 prefix_size = (desc->short_name - long_name);
1184
1185 /* Found a prefix? */
1186 if (prefix_size > 0) {
1187 char prefix[100];
1188
1189 /* Get a copy of the prefix */
1190 my_strcpy(prefix, long_name, prefix_size + 1);
1191
1192 /* Append the prefix */
1193 end += my_strcat(buf, prefix, buf_size);
1194 }
1195 /* Set the name to the right length */
1196 return end;
1197 }
1198
1199 /**
1200 * Utility function used for sorting an array of ego-item indices by
1201 * ego-item name.
1202 */
ego_comp_func(const void * a_ptr,const void * b_ptr)1203 static int ego_comp_func(const void *a_ptr, const void *b_ptr)
1204 {
1205 const struct ego_desc *a = a_ptr;
1206 const struct ego_desc *b = b_ptr;
1207
1208 /* Note the removal of common prefixes */
1209 return (strcmp(a->short_name, b->short_name));
1210 }
1211
1212 /**
1213 * Display an entry on the sval menu
1214 */
ego_display(struct menu * menu,int oid,bool cursor,int row,int col,int width)1215 static void ego_display(struct menu * menu, int oid, bool cursor, int row,
1216 int col, int width)
1217 {
1218 char buf[80] = "";
1219 struct ego_desc *choice = (struct ego_desc *) menu->menu_data;
1220 bool ignored = ego_is_ignored(choice[oid].e_idx, choice[oid].itype);
1221
1222 byte attr = (cursor ? COLOUR_L_BLUE : COLOUR_WHITE);
1223 byte sq_attr = (ignored ? COLOUR_L_RED : COLOUR_L_GREEN);
1224
1225 /* Acquire the "name" of object "i" */
1226 (void) ego_item_name(buf, sizeof(buf), &choice[oid]);
1227
1228 /* Print it */
1229 c_put_str(attr, format("%s", buf), row, col);
1230
1231 /* Show ignore mark, if any */
1232 if (ignored)
1233 c_put_str(COLOUR_L_RED, "*", row, col + 1);
1234
1235 /* Show the stripped ego-item name using another colour */
1236 c_put_str(sq_attr, choice[oid].short_name, row, col + strlen(buf));
1237 }
1238
1239 /**
1240 * Deal with events on the sval menu
1241 */
ego_action(struct menu * menu,const ui_event * event,int oid)1242 static bool ego_action(struct menu * menu, const ui_event * event, int oid)
1243 {
1244 struct ego_desc *choice = menu->menu_data;
1245
1246 /* Toggle */
1247 if (event->type == EVT_SELECT) {
1248 ego_ignore_toggle(choice[oid].e_idx, choice[oid].itype);
1249
1250 return true;
1251 }
1252
1253 return false;
1254 }
1255
1256 /**
1257 * Display list of ego items to be ignored.
1258 */
ego_menu(const char * unused,int also_unused)1259 static void ego_menu(const char *unused, int also_unused)
1260 {
1261 int max_num = 0;
1262 struct ego_item *ego;
1263 struct ego_desc *choice;
1264
1265 struct menu menu;
1266 menu_iter menu_f = { 0, 0, ego_display, ego_action, 0 };
1267 region area = { 1, 5, -1, -1 };
1268 int cursor = 0;
1269
1270 int i;
1271
1272 /* Create the array */
1273 choice = mem_zalloc(z_info->e_max * ITYPE_MAX * sizeof(struct ego_desc));
1274
1275 /* Get the valid ego-items */
1276 for (i = 0; i < z_info->e_max; i++) {
1277 int itype;
1278 ego = &e_info[i];
1279
1280 /* Only valid known ego-items allowed */
1281 if (!ego->name || !ego->everseen)
1282 continue;
1283
1284 /* Find appropriate ignore types */
1285 for (itype = ITYPE_NONE + 1; itype < ITYPE_MAX; itype++)
1286 if (ego_has_ignore_type(ego, itype)) {
1287
1288 /* Fill in the details */
1289 choice[max_num].e_idx = i;
1290 choice[max_num].itype = itype;
1291 choice[max_num].short_name = strip_ego_name(ego->name);
1292
1293 ++max_num;
1294 }
1295 }
1296
1297 /* Quickly sort the array by ego-item name */
1298 qsort(choice, max_num, sizeof(choice[0]), ego_comp_func);
1299
1300 /* Return here if there are no objects */
1301 if (!max_num) {
1302 mem_free(choice);
1303 return;
1304 }
1305
1306
1307 /* Save the screen and clear it */
1308 screen_save();
1309 clear_from(0);
1310
1311 /* Help text */
1312 prt("Ego item ignore menu", 0, 0);
1313
1314 /* Output to the screen */
1315 text_out_hook = text_out_to_screen;
1316
1317 /* Indent output */
1318 text_out_indent = 1;
1319 text_out_wrap = 79;
1320 Term_gotoxy(1, 1);
1321
1322 /* Display some helpful information */
1323 text_out_e(EGO_MENU_HELPTEXT);
1324
1325 text_out_indent = 0;
1326
1327 /* Set up the menu */
1328 memset(&menu, 0, sizeof(menu));
1329 menu_init(&menu, MN_SKIN_SCROLL, &menu_f);
1330 menu_setpriv(&menu, max_num, choice);
1331 menu_layout(&menu, &area);
1332
1333 /* Select an entry */
1334 (void) menu_select(&menu, cursor, false);
1335
1336 /* Free memory */
1337 mem_free(choice);
1338
1339 /* Load screen */
1340 screen_load();
1341
1342 return;
1343 }
1344
1345
1346 /**
1347 * ------------------------------------------------------------------------
1348 * Quality ignore menu
1349 * ------------------------------------------------------------------------ */
1350
1351 /**
1352 * Menu struct for differentiating aware from unaware ignore
1353 */
1354 typedef struct
1355 {
1356 struct object_kind *kind;
1357 bool aware;
1358 } ignore_choice;
1359
1360 /**
1361 * Ordering function for ignore choices.
1362 * Aware comes before unaware, and then sort alphabetically.
1363 */
cmp_ignore(const void * a,const void * b)1364 static int cmp_ignore(const void *a, const void *b)
1365 {
1366 char bufa[80];
1367 char bufb[80];
1368 const ignore_choice *x = a;
1369 const ignore_choice *y = b;
1370
1371 if (!x->aware && y->aware)
1372 return 1;
1373 if (x->aware && !y->aware)
1374 return -1;
1375
1376 object_kind_name(bufa, sizeof(bufa), x->kind, x->aware);
1377 object_kind_name(bufb, sizeof(bufb), y->kind, y->aware);
1378
1379 return strcmp(bufa, bufb);
1380 }
1381
1382 /**
1383 * Determine if an item is a valid choice
1384 */
quality_validity(struct menu * menu,int oid)1385 int quality_validity(struct menu *menu, int oid)
1386 {
1387 return oid ? 1 : 0;
1388 }
1389
1390 /**
1391 * Display an entry in the menu.
1392 */
quality_display(struct menu * menu,int oid,bool cursor,int row,int col,int width)1393 static void quality_display(struct menu *menu, int oid, bool cursor, int row,
1394 int col, int width)
1395 {
1396 /* Note: the order of the values in quality_choices do not align with the
1397 * ignore_type_t enum order. - fix? NRM*/
1398 const char *name = quality_choices[oid].name;
1399
1400 byte level = ignore_level[oid];
1401 const char *level_name = quality_values[level].name;
1402
1403 byte attr = (cursor ? COLOUR_L_BLUE : COLOUR_WHITE);
1404
1405 if (oid)
1406 c_put_str(attr, format("%-30s : %s", name, level_name), row, col);
1407 }
1408
1409
1410 /**
1411 * Display the quality ignore subtypes.
1412 */
quality_subdisplay(struct menu * menu,int oid,bool cursor,int row,int col,int width)1413 static void quality_subdisplay(struct menu *menu, int oid, bool cursor, int row,
1414 int col, int width)
1415 {
1416 const char *name = quality_values[oid].name;
1417 byte attr = (cursor ? COLOUR_L_BLUE : COLOUR_WHITE);
1418
1419 c_put_str(attr, name, row, col);
1420 }
1421
1422
1423 /**
1424 * Handle keypresses.
1425 */
quality_action(struct menu * m,const ui_event * event,int oid)1426 static bool quality_action(struct menu *m, const ui_event *event, int oid)
1427 {
1428 struct menu menu;
1429 menu_iter menu_f = { NULL, NULL, quality_subdisplay, NULL, NULL };
1430 region area = { 37, 2, 29, IGNORE_MAX };
1431 ui_event evt;
1432 int count;
1433
1434 /* Display at the right point */
1435 area.row += oid;
1436
1437 /* Save */
1438 screen_save();
1439
1440 /* Work out how many options we have */
1441 count = IGNORE_MAX;
1442 if ((oid == ITYPE_RING) || (oid == ITYPE_AMULET))
1443 count = area.page_rows = IGNORE_BAD + 1;
1444
1445 /* Run menu */
1446 menu_init(&menu, MN_SKIN_SCROLL, &menu_f);
1447 menu_setpriv(&menu, count, quality_values);
1448
1449 /* Stop menus from going off the bottom of the screen */
1450 if (area.row + menu.count > Term->hgt - 1)
1451 area.row += Term->hgt - 1 - area.row - menu.count;
1452
1453 menu_layout(&menu, &area);
1454
1455 window_make(area.col - 2, area.row - 1, area.col + area.width + 2,
1456 area.row + area.page_rows);
1457
1458 evt = menu_select(&menu, 0, true);
1459
1460 /* Set the new value appropriately */
1461 if (evt.type == EVT_SELECT)
1462 ignore_level[oid] = menu.cursor;
1463
1464 /* Load and finish */
1465 screen_load();
1466 return true;
1467 }
1468
1469 /**
1470 * Display quality ignore menu.
1471 */
quality_menu(void * unused,const char * also_unused)1472 static void quality_menu(void *unused, const char *also_unused)
1473 {
1474 struct menu menu;
1475 menu_iter menu_f = { NULL, quality_validity, quality_display,
1476 quality_action, NULL };
1477 region area = { 0, 0, 0, 0 };
1478
1479 /* Save screen */
1480 screen_save();
1481 clear_from(0);
1482
1483 /* Set up the menu */
1484 menu_init(&menu, MN_SKIN_SCROLL, &menu_f);
1485 menu.title = "Quality ignore menu";
1486 menu_setpriv(&menu, ITYPE_MAX, quality_values);
1487 menu_layout(&menu, &area);
1488
1489 /* Select an entry */
1490 menu_select(&menu, 0, false);
1491
1492 /* Load screen */
1493 screen_load();
1494 return;
1495 }
1496
1497
1498
1499 /**
1500 * ------------------------------------------------------------------------
1501 * Sval ignore menu
1502 * ------------------------------------------------------------------------ */
1503
1504 /**
1505 * Structure to describe tval/description pairings.
1506 */
1507 typedef struct
1508 {
1509 int tval;
1510 const char *desc;
1511 } tval_desc;
1512
1513 /**
1514 * Categories for sval-dependent ignore.
1515 */
1516 static tval_desc sval_dependent[] =
1517 {
1518 { TV_STAFF, "Staffs" },
1519 { TV_WAND, "Wands" },
1520 { TV_ROD, "Rods" },
1521 { TV_SCROLL, "Scrolls" },
1522 { TV_POTION, "Potions" },
1523 { TV_RING, "Rings" },
1524 { TV_AMULET, "Amulets" },
1525 { TV_FOOD, "Food" },
1526 { TV_MUSHROOM, "Mushrooms" },
1527 { TV_MAGIC_BOOK, "Magic books" },
1528 { TV_PRAYER_BOOK, "Prayer books" },
1529 { TV_NATURE_BOOK, "Nature books" },
1530 { TV_SHADOW_BOOK, "Shadow books" },
1531 { TV_OTHER_BOOK, "Mystery books" },
1532 { TV_LIGHT, "Lights" },
1533 { TV_FLASK, "Flasks of oil" },
1534 { TV_GOLD, "Money" },
1535 };
1536
1537
1538 /**
1539 * Determines whether a tval is eligible for sval-ignore.
1540 */
ignore_tval(int tval)1541 bool ignore_tval(int tval)
1542 {
1543 size_t i;
1544
1545 /* Only ignore if the tval's allowed */
1546 for (i = 0; i < N_ELEMENTS(sval_dependent); i++) {
1547 if (kb_info[tval].num_svals == 0) continue;
1548 if (tval == sval_dependent[i].tval)
1549 return true;
1550 }
1551
1552 return false;
1553 }
1554
1555
1556 /**
1557 * Display an entry on the sval menu
1558 */
ignore_sval_menu_display(struct menu * menu,int oid,bool cursor,int row,int col,int width)1559 static void ignore_sval_menu_display(struct menu *menu, int oid, bool cursor,
1560 int row, int col, int width)
1561 {
1562 char buf[80];
1563 const ignore_choice *choice = menu_priv(menu);
1564
1565 struct object_kind *kind = choice[oid].kind;
1566 bool aware = choice[oid].aware;
1567
1568 byte attr = curs_attrs[(int)aware][0 != cursor];
1569
1570 /* Acquire the "name" of object "i" */
1571 object_kind_name(buf, sizeof(buf), kind, aware);
1572
1573 /* Print it */
1574 c_put_str(attr, format("[ ] %s", buf), row, col);
1575 if ((aware && (kind->ignore & IGNORE_IF_AWARE)) ||
1576 (!aware && (kind->ignore & IGNORE_IF_UNAWARE)))
1577 c_put_str(COLOUR_L_RED, "*", row, col + 1);
1578 }
1579
1580
1581 /**
1582 * Deal with events on the sval menu
1583 */
ignore_sval_menu_action(struct menu * m,const ui_event * event,int oid)1584 static bool ignore_sval_menu_action(struct menu *m, const ui_event *event,
1585 int oid)
1586 {
1587 const ignore_choice *choice = menu_priv(m);
1588
1589 if (event->type == EVT_SELECT ||
1590 (event->type == EVT_KBRD && tolower(event->key.code) == 't')) {
1591 struct object_kind *kind = choice[oid].kind;
1592
1593 /* Toggle the appropriate flag */
1594 if (choice[oid].aware)
1595 kind->ignore ^= IGNORE_IF_AWARE;
1596 else
1597 kind->ignore ^= IGNORE_IF_UNAWARE;
1598
1599 player->upkeep->notice |= PN_IGNORE;
1600 return true;
1601 }
1602
1603 return false;
1604 }
1605
1606 static const menu_iter ignore_sval_menu =
1607 {
1608 NULL,
1609 NULL,
1610 ignore_sval_menu_display,
1611 ignore_sval_menu_action,
1612 NULL,
1613 };
1614
1615
1616 /**
1617 * Collect all tvals in the big ignore_choice array
1618 */
ignore_collect_kind(int tval,ignore_choice ** ch)1619 static int ignore_collect_kind(int tval, ignore_choice **ch)
1620 {
1621 ignore_choice *choice;
1622 int num = 0;
1623
1624 int i;
1625
1626 /* Create the array, with entries both for aware and unaware ignore */
1627 choice = mem_alloc(2 * z_info->k_max * sizeof *choice);
1628
1629 for (i = 1; i < z_info->k_max; i++) {
1630 struct object_kind *kind = &k_info[i];
1631
1632 /* Skip empty objects, unseen objects, and incorrect tvals */
1633 if (!kind->name || kind->tval != tval)
1634 continue;
1635
1636 if (!kind->aware) {
1637 /* can unaware ignore anything */
1638 choice[num].kind = kind;
1639 choice[num++].aware = false;
1640 }
1641
1642 if ((kind->everseen && !kf_has(kind->kind_flags, KF_INSTA_ART)) ||
1643 tval_is_money_k(kind)) {
1644 /* Do not display the artifact base kinds in this list
1645 * aware ignore requires everseen
1646 * do not require awareness for aware ignore, so people can set
1647 * at game start */
1648 choice[num].kind = kind;
1649 choice[num++].aware = true;
1650 }
1651 }
1652
1653 if (num == 0)
1654 mem_free(choice);
1655 else
1656 *ch = choice;
1657
1658 return num;
1659 }
1660
1661 /**
1662 * Display list of svals to be ignored.
1663 */
sval_menu(int tval,const char * desc)1664 static bool sval_menu(int tval, const char *desc)
1665 {
1666 struct menu *menu;
1667 region area = { 1, 2, -1, -1 };
1668
1669 ignore_choice *choices;
1670
1671 int n_choices = ignore_collect_kind(tval, &choices);
1672 if (!n_choices)
1673 return false;
1674
1675 /* Sort by name in ignore menus except for categories of items that are
1676 * aware from the start */
1677 switch (tval)
1678 {
1679 case TV_LIGHT:
1680 case TV_MAGIC_BOOK:
1681 case TV_PRAYER_BOOK:
1682 case TV_NATURE_BOOK:
1683 case TV_SHADOW_BOOK:
1684 case TV_OTHER_BOOK:
1685 case TV_DRAG_ARMOR:
1686 case TV_GOLD:
1687 /* leave sorted by sval */
1688 break;
1689
1690 default:
1691 /* sort by name */
1692 sort(choices, n_choices, sizeof(*choices), cmp_ignore);
1693 }
1694
1695
1696 /* Save the screen and clear it */
1697 screen_save();
1698 clear_from(0);
1699
1700 /* Help text */
1701 prt(format("Ignore the following %s:", desc), 0, 0);
1702
1703 /* Run menu */
1704 menu = menu_new(MN_SKIN_COLUMNS, &ignore_sval_menu);
1705 menu_setpriv(menu, n_choices, choices);
1706 menu->cmd_keys = "Tt";
1707 menu_layout(menu, &area);
1708 menu_set_cursor_x_offset(menu, 1); /* Place cursor in brackets. */
1709 menu_select(menu, 0, false);
1710
1711 /* Free memory */
1712 mem_free(menu);
1713 mem_free(choices);
1714
1715 /* Load screen */
1716 screen_load();
1717 return true;
1718 }
1719
1720
1721 /**
1722 * Returns true if there's anything to display a menu of
1723 */
seen_tval(int tval)1724 static bool seen_tval(int tval)
1725 {
1726 int i;
1727
1728 for (i = 1; i < z_info->k_max; i++) {
1729 struct object_kind *kind = &k_info[i];
1730
1731 /* Skip empty objects, unseen objects, and incorrect tvals */
1732 if (!kind->name) continue;
1733 if (!kind->everseen) continue;
1734 if (kind->tval != tval) continue;
1735
1736 return true;
1737 }
1738
1739
1740 return false;
1741 }
1742
1743
1744 /**
1745 * Extra options on the "item options" menu
1746 */
1747 static struct
1748 {
1749 char tag;
1750 const char *name;
1751 void (*action)(); /* this is a nasty hack */
1752 } extra_item_options[] = {
1753 { 'Q', "Quality ignoring options", quality_menu },
1754 { 'E', "Ego ignoring options", ego_menu},
1755 { '{', "Autoinscription setup", textui_browse_object_knowledge },
1756 };
1757
tag_options_item(struct menu * menu,int oid)1758 static char tag_options_item(struct menu *menu, int oid)
1759 {
1760 size_t line = (size_t) oid;
1761
1762 if (line < N_ELEMENTS(sval_dependent))
1763 return I2A(oid);
1764
1765 /* Separator - blank line. */
1766 if (line == N_ELEMENTS(sval_dependent))
1767 return 0;
1768
1769 line = line - N_ELEMENTS(sval_dependent) - 1;
1770
1771 if (line < N_ELEMENTS(extra_item_options))
1772 return extra_item_options[line].tag;
1773
1774 return 0;
1775 }
1776
valid_options_item(struct menu * menu,int oid)1777 static int valid_options_item(struct menu *menu, int oid)
1778 {
1779 size_t line = (size_t) oid;
1780
1781 if (line < N_ELEMENTS(sval_dependent))
1782 return 1;
1783
1784 /* Separator - blank line. */
1785 if (line == N_ELEMENTS(sval_dependent))
1786 return 0;
1787
1788 line = line - N_ELEMENTS(sval_dependent) - 1;
1789
1790 if (line < N_ELEMENTS(extra_item_options))
1791 return 1;
1792
1793 return 0;
1794 }
1795
display_options_item(struct menu * menu,int oid,bool cursor,int row,int col,int width)1796 static void display_options_item(struct menu *menu, int oid, bool cursor,
1797 int row, int col, int width)
1798 {
1799 size_t line = (size_t) oid;
1800
1801 /* Most of the menu is svals, with a small "extra options" section below */
1802 if (line < N_ELEMENTS(sval_dependent)) {
1803 bool known = seen_tval(sval_dependent[line].tval);
1804 byte attr = curs_attrs[known ? CURS_KNOWN: CURS_UNKNOWN][(int)cursor];
1805
1806 c_prt(attr, sval_dependent[line].desc, row, col);
1807 } else {
1808 byte attr = curs_attrs[CURS_KNOWN][(int)cursor];
1809
1810 line = line - N_ELEMENTS(sval_dependent) - 1;
1811
1812 if (line < N_ELEMENTS(extra_item_options))
1813 c_prt(attr, extra_item_options[line].name, row, col);
1814 }
1815 }
1816
handle_options_item(struct menu * menu,const ui_event * event,int oid)1817 static bool handle_options_item(struct menu *menu, const ui_event *event,
1818 int oid)
1819 {
1820 if (event->type == EVT_SELECT) {
1821 if ((size_t) oid < N_ELEMENTS(sval_dependent))
1822 {
1823 sval_menu(sval_dependent[oid].tval, sval_dependent[oid].desc);
1824 } else {
1825 oid = oid - (int)N_ELEMENTS(sval_dependent) - 1;
1826 assert((size_t) oid < N_ELEMENTS(extra_item_options));
1827 extra_item_options[oid].action();
1828 }
1829
1830 return true;
1831 }
1832
1833 return false;
1834 }
1835
1836
1837 static const menu_iter options_item_iter =
1838 {
1839 tag_options_item,
1840 valid_options_item,
1841 display_options_item,
1842 handle_options_item,
1843 NULL
1844 };
1845
1846
1847 /**
1848 * Display and handle the main ignoring menu.
1849 */
do_cmd_options_item(const char * title,int row)1850 void do_cmd_options_item(const char *title, int row)
1851 {
1852 struct menu menu;
1853
1854 menu_init(&menu, MN_SKIN_SCROLL, &options_item_iter);
1855 menu_setpriv(&menu, N_ELEMENTS(sval_dependent) +
1856 N_ELEMENTS(extra_item_options) + 1, NULL);
1857
1858 menu.title = title;
1859 menu_layout(&menu, &SCREEN_REGION);
1860
1861 screen_save();
1862 clear_from(0);
1863 menu_select(&menu, 0, false);
1864 screen_load();
1865
1866 player->upkeep->notice |= PN_IGNORE;
1867
1868 return;
1869 }
1870
1871
1872
1873 /**
1874 * ------------------------------------------------------------------------
1875 * Main menu definitions and display
1876 * ------------------------------------------------------------------------ */
1877
1878 static struct menu *option_menu;
1879 static menu_action option_actions[] =
1880 {
1881 { 0, 'a', "User interface options", option_toggle_menu },
1882 { 0, 'b', "Birth (difficulty) options", option_toggle_menu },
1883 { 0, 'x', "Cheat options", option_toggle_menu },
1884 { 0, 'w', "Subwindow setup", do_cmd_options_win },
1885 { 0, 'i', "Item ignoring setup", do_cmd_options_item },
1886 { 0, '{', "Auto-inscription setup", textui_browse_object_knowledge },
1887 { 0, 0, NULL, NULL },
1888 { 0, 'd', "Set base delay factor", do_cmd_delay },
1889 { 0, 'h', "Set hitpoint warning", do_cmd_hp_warn },
1890 { 0, 'm', "Set movement delay", do_cmd_lazymove_delay },
1891 { 0, 'o', "Set sidebar mode", do_cmd_sidebar_mode },
1892 { 0, 0, NULL, NULL },
1893 { 0, 's', "Save subwindow setup to pref file", do_dump_options },
1894 { 0, 't', "Save autoinscriptions to pref file", do_dump_autoinsc },
1895 { 0, 'u', "Save char screen options to pref file", do_dump_charscreen_opt },
1896 { 0, 0, NULL, NULL },
1897 { 0, 'l', "Load a user pref file", options_load_pref_file },
1898 { 0, 'k', "Edit keymaps (advanced)", do_cmd_keymaps },
1899 { 0, 'c', "Edit colours (advanced)", do_cmd_colors },
1900 { 0, 'v', "Save visuals (advanced)", do_cmd_visuals },
1901 };
1902
1903
1904 /**
1905 * Display the options main menu.
1906 */
do_cmd_options(void)1907 void do_cmd_options(void)
1908 {
1909 if (!option_menu) {
1910 /* Main option menu */
1911 option_menu = menu_new_action(option_actions,
1912 N_ELEMENTS(option_actions));
1913
1914 option_menu->title = "Options Menu";
1915 option_menu->flags = MN_CASELESS_TAGS;
1916 }
1917
1918 screen_save();
1919 clear_from(0);
1920
1921 menu_layout(option_menu, &SCREEN_REGION);
1922 menu_select(option_menu, 0, false);
1923
1924 screen_load();
1925 }
1926
cleanup_options(void)1927 void cleanup_options(void)
1928 {
1929 if (keymap_menu) menu_free(keymap_menu);
1930 if (visual_menu) menu_free(visual_menu);
1931 if (color_menu) menu_free(color_menu);
1932 if (option_menu) menu_free(option_menu);
1933 }
1934