root/trunk/src/cmd4.c

Revision 975, 96.3 kB (checked in by shanoah, 2 months ago)

Get rid of a bunch of compiler warnings.

  • Property svn:eol-style set to native
Line 
1 /*
2  * File: cmd4.c
3  * Purpose: Various kinds of browsing functions.
4  *
5  * Copyright (c) 1997-2007 Robert A. Koeneke, James E. Wilson, Ben Harrison,
6  * Eytan Zweig, Andrew Doull, Pete Mack.
7  *
8  * This work is free software; you can redistribute it and/or modify it
9  * under the terms of either:
10  *
11  * a) the GNU General Public License as published by the Free Software
12  *    Foundation, version 2, or
13  *
14  * b) the "Angband licence":
15  *    This software may be copied and distributed for educational, research,
16  *    and not for profit purposes provided that this copyright and statement
17  *    are included in all such copies.  Other copyrights may also apply.
18  */
19 #include "angband.h"
20 #include "tvalsval.h"
21 #include "option.h"
22 #include "ui.h"
23 #include "externs.h"
24 #include "ui-menu.h"
25
26
27 static void do_cmd_pref_file_hack(long row);
28
29
30
31 /* Flag value for missing array entry */
32 #define MISSING -17
33
34 #define APP_MACRO       101
35 #define ASK_MACRO       103
36 #define DEL_MACRO       104
37 #define NEW_MACRO       105
38 #define APP_KEYMAP      106
39 #define ASK_KEYMAP      107
40 #define DEL_KEYMAP      108
41 #define NEW_KEYMAP      109
42 #define ENTER_ACT       110
43 #define LOAD_PREF       111
44 #define DUMP_MON        112
45 #define DUMP_OBJ        113
46 #define DUMP_FEAT       114
47 #define DUMP_FLAV       115
48 #define MOD_MON         116
49 #define MOD_OBJ         117
50 #define MOD_FEAT        118
51 #define MOD_FLAV        119
52 #define DUMP_COL        120
53 #define MOD_COL         121
54 #define RESET_VIS       122
55
56
57 #define INFO_SCREENS 2 /* Number of screens in character info mode */
58
59
60
61 typedef struct
62 {
63         int maxnum;          /* Maximum possible item count for this class */
64         bool easy_know;      /* Items don't need to be IDed to recognize membership */
65
66         const char *(*name)(int gid);               /* Name of this group */
67
68         /* Compare, in group and display order (optional if already sorted) */
69         int (*gcomp)(const void *, const void *);   /* Compares gids of two oids */
70         int (*group)(int oid);                      /* Returns gid for an oid */
71
72         /* Summary function for the "object" information. */
73         void (*summary)(int gid, const int *object_list, int n, int top, int row, int col);
74
75 } group_funcs;
76
77 typedef struct
78 {
79         /* Displays an entry at specified location, including kill-count and graphics */
80         void (*display_member)(int col, int row, bool cursor, int oid);
81
82         void (*lore)(int oid);       /* Displays lore for an oid */
83
84
85         /* Required only for objects with modifiable display attributes */
86         /* Unknown 'flavors' return flavor attributes */
87         char *(*xchar)(int oid);     /* Get character attr for OID (by address) */
88         byte *(*xattr)(int oid);     /* Get color attr for OID (by address) */
89
90         const char *(*xtra_prompt)(int oid);  /* Returns optional extra prompt */
91         void (*xtra_act)(char ch, int oid);   /* Handles optional extra actions */
92
93         bool is_visual;                       /* Does this kind have visual editing? */
94
95 } member_funcs;
96
97
98 /* Helper class for generating joins */
99 typedef struct join
100 {
101                 int oid;
102                 int gid;
103 } join_t;
104
105 /* A default group-by */
106 static join_t *default_join;
107 #if 0
108 static int default_join_cmp(const void *a, const void *b)
109 {
110                 join_t *ja = &default_join[*(int*)a];
111                 join_t *jb = &default_join[*(int*)b];
112                 int c = ja->gid - jb->gid;
113                 if (c) return c;
114                 return ja->oid - jb->oid;
115 }
116 #endif
117 static int default_group(int oid) { return default_join[oid].gid; }
118
119
120 static int *obj_group_order;
121
122 /*
123  * Description of each monster group.
124  */
125 static struct
126 {
127         cptr chars;
128         cptr name;
129 } monster_group[] =
130 {
131         { (cptr)-1,     "Uniques" },
132         { "A",          "Angels" },
133         { "a",          "Ants" },
134         { "b",          "Bats" },
135         { "B",          "Birds" },
136         { "C",          "Canines" },
137         { "c",          "Centipedes" },
138         { "uU",         "Demons" },
139         { "dD",         "Dragons" },
140         { "vE",         "Elementals/Vortices" },
141         { "e",          "Eyes/Beholders" },
142         { "f",          "Felines" },
143         { "G",          "Ghosts" },
144         { "OP",         "Giants/Ogres" },
145         { "g",          "Golems" },
146         { "H",          "Harpies/Hybrids" },
147         { "h",          "Hominids (Elves, Dwarves)" },
148         { "M",          "Hydras" },
149         { "i",          "Icky Things" },
150         { "lFI",        "Insects" },
151         { "j",          "Jellies" },
152         { "K",          "Killer Beetles" },
153         { "k",          "Kobolds" },
154         { "L",          "Lichs" },
155         { "tp",         "Men" },
156         { ".$?!_",      "Mimics" },
157         { "m",          "Molds" },
158         { ",",          "Mushroom Patches" },
159         { "n",          "Nagas" },
160         { "o",          "Orcs" },
161         { "q",          "Quadrupeds" },
162         { "Q",          "Quylthulgs" },
163         { "R",          "Reptiles/Amphibians" },
164         { "r",          "Rodents" },
165         { "S",          "Scorpions/Spiders" },
166         { "s",          "Skeletons/Drujs" },
167         { "J",          "Snakes" },
168         { "T",          "Trolls" },
169         { "V",          "Vampires" },
170         { "W",          "Wights/Wraiths" },
171         { "w",          "Worms/Worm Masses" },
172         { "X",          "Xorns/Xarens" },
173         { "Y",          "Yeti" },
174         { "Z",          "Zephyr Hounds" },
175         { "z",          "Zombies" },
176         { NULL,         NULL }
177 };
178
179 /*
180  * Description of each feature group.
181  */
182 const char *feature_group_text[] =
183 {
184         "Floors",
185         "Traps",
186         "Doors",
187         "Stairs",
188         "Walls",
189         "Streamers",
190         "Obstructions",
191         "Stores",
192         "Other",
193         NULL
194 };
195
196
197
198 /* Useful method declarations */
199 static void display_visual_list(int col, int row, int height, int width,
200                                 byte attr_top, char char_left);
201
202 static bool visual_mode_command(ui_event_data ke, bool *visual_list_ptr,
203                                 int height, int width,
204                                 byte *attr_top_ptr, char *char_left_ptr,
205                                 byte *cur_attr_ptr, char *cur_char_ptr,
206                                 int col, int row, int *delay);
207
208 static void place_visual_list_cursor(int col, int row, byte a,
209                                 byte c, byte attr_top, byte char_left);
210
211 static void dump_pref_file(void (*dump)(ang_file *), const char *title, int row);
212
213 /*
214  * Clipboard variables for copy&paste in visual mode
215  */
216 static byte attr_idx = 0;
217 static byte char_idx = 0;
218
219 /*
220  * Return a specific ordering for the features
221  */
222 int feat_order(int feat)
223 {
224         feature_type *f_ptr = &f_info[feat];
225
226         switch (f_ptr->d_char)
227         {
228                 case '.':                               return 0;
229                 case '^':                               return 1;
230                 case '\'': case '+':    return 2;
231                 case '<': case '>':             return 3;
232                 case '#':                               return 4;
233                 case '*': case '%' :    return 5;
234                 case ';': case ':' :    return 6;
235
236                 default:
237                 {
238                         return 8;
239                 }
240         }
241 }
242
243
244 /* Emit a 'graphical' symbol and a padding character if appropriate */
245 static void big_pad(int col, int row, byte a, byte c)
246 {
247         Term_putch(col, row, a, c);
248         if (!use_bigtile) return;
249
250         if (a & 0x80)
251                 Term_putch(col + 1, row, 255, -1);
252         else
253                 Term_putch(col + 1, row, 1, ' ');
254 }
255
256 /* Return the actual width of a symbol */
257 static int actual_width(int width)
258 {
259 #ifdef UNANGBAND
260         if (use_trptile) width *= 3;
261         else if (use_dbltile) width *= 2;
262 #endif
263
264         if (use_bigtile) width *= 2;
265
266         return width;
267 }
268
269 /* Return the actual height of a symbol */
270 static int actual_height(int height)
271 {
272 #ifdef UNANGBAND
273         if (use_trptile) height = height * 3 / 2;
274         else if (use_dbltile) height *= 2;
275 #endif
276
277         if (use_bigtile) height *= 2;
278
279         return height;
280 }
281
282
283 /* From an actual width, return the logical width */
284 static int logical_width(int width)
285 {
286         int div = 1;
287
288 #ifdef UNANGBAND
289         if (use_trptile) div = 3;
290         else if (use_dbltile) div = 2;
291 #endif
292
293         if (use_bigtile) div *= 2;
294
295         return width / div;
296 }
297
298 /* From an actual height, return the logical height */
299 static int logical_height(int height)
300 {
301         int div = 1;
302
303 #ifdef UNANGBAND
304         if (use_trptile)
305         {
306                 height *= 2;
307                 div = 3;
308         }
309         else if (use_dbltile) div = 2;
310 #endif
311
312         if (use_bigtile) div *= 2;
313
314         return height / div;
315 }
316
317
318 static void display_group_member(menu_type *menu, int oid,
319                                                 bool cursor, int row, int col, int wid)
320 {
321         const member_funcs *o_funcs = menu->menu_data;
322         byte attr = curs_attrs[CURS_KNOWN][cursor == oid];
323
324         (void)wid;
325
326         /* Print the interesting part */
327         o_funcs->display_member(col, row, cursor, oid);
328
329 #if 0 /* Debugging code */
330         c_put_str(attr, format("%d", oid), row, 60);
331 #endif
332
333         /* Do visual mode */
334         if (o_funcs->is_visual && o_funcs->xattr)
335         {
336                 char c = *o_funcs->xchar(oid);
337                 byte a = *o_funcs->xattr(oid);
338
339                 c_put_str(attr, format((c & 0x80) ? "%02x/%02x" : "%02x/%d", a, c), row, 60);
340         }
341 }
342
343 static const char *recall_prompt(int oid)
344 {
345         (void)oid;
346         return ", 'r' to recall";
347 }
348
349 #define swap(a, b) (swapspace = (void*)(a)), ((a) = (b)), ((b) = swapspace)
350
351 /*
352  * Interactive group by.
353  * Recognises inscriptions, graphical symbols, lore
354  */
355 static void display_knowledge(const char *title, int *obj_list, int o_count,
356                                 group_funcs g_funcs, member_funcs o_funcs,
357                                 const char *otherfields)
358 {
359         /* maximum number of groups to display */
360         int max_group = g_funcs.maxnum < o_count ? g_funcs.maxnum : o_count ;
361
362         /* This could (should?) be (void **) */
363         int *g_list, *g_offset;
364
365         const char **g_names;
366
367         int g_name_len = 8;  /* group name length, minumum is 8 */
368
369         int grp_cnt = 0; /* total number groups */
370
371         int g_cur = 0, grp_old = -1; /* group list positions */
372         int o_cur = 0;                                  /* object list positions */
373         int g_o_count = 0;                               /* object count for group */
374         int oid = -1;                           /* object identifiers */
375
376         region title_area = { 0, 0, 0, 4 };
377         region group_region = { 0, 6, MISSING, -2 };
378         region object_region = { MISSING, 6, 0, -2 };
379
380         /* display state variables */
381         bool visual_list = FALSE;
382         byte attr_top = 0;
383         char char_left = 0;
384
385         int delay = 0;
386
387         menu_type group_menu;
388         menu_type object_menu;
389         menu_iter object_iter;
390
391         /* Panel state */
392         /* These are swapped in parallel whenever the actively browsing " */
393         /* changes */
394         int *active_cursor = &g_cur, *inactive_cursor = &o_cur;
395         menu_type *active_menu = &group_menu, *inactive_menu = &object_menu;
396         int panel = 0;
397
398         void *swapspace;
399         bool do_swap = FALSE;
400
401         bool flag = FALSE;
402         bool redraw = TRUE;
403
404         int browser_rows;
405         int wid, hgt;
406         int i;
407         int prev_g = -1;
408
409         int omode = rogue_like_commands;
410
411
412         /* Get size */
413         Term_get_size(&wid, &hgt);
414         browser_rows = hgt - 8;
415
416         /* Disable the roguelike commands for the duration */
417         rogue_like_commands = FALSE;
418
419
420
421         /* Do the group by. ang_sort only works on (void **) */
422         /* Maybe should make this a precondition? */
423         if (g_funcs.gcomp)
424                 qsort(obj_list, o_count, sizeof(*obj_list), g_funcs.gcomp);
425
426
427         /* Sort everything into group order */
428         g_list = C_ZNEW(max_group + 1, int);
429         g_offset = C_ZNEW(max_group + 1, int);
430
431         for (i = 0; i < o_count; i++)
432         {
433                 if (prev_g != g_funcs.group(obj_list[i]))
434                 {
435                         prev_g = g_funcs.group(obj_list[i]);
436                         g_offset[grp_cnt] = i;
437                         g_list[grp_cnt++] = prev_g;
438                 }
439         }
440
441         g_offset[grp_cnt] = o_count;
442         g_list[grp_cnt] = -1;
443
444
445         /* The compact set of group names, in display order */
446         g_names = C_ZNEW(grp_cnt, const char **);
447
448         for (i = 0; i < grp_cnt; i++)
449         {
450                 int len;
451                 g_names[i] = g_funcs.name(g_list[i]);
452                 len = strlen(g_names[i]);
453                 if (len > g_name_len) g_name_len = len;
454         }
455
456         /* Reasonable max group name len */
457         if (g_name_len >= 20) g_name_len = 20;
458
459         object_region.col = g_name_len + 3;
460         group_region.width = g_name_len;
461
462
463         /* Leave room for the group summary information */
464         if (g_funcs.summary) object_region.page_rows = -3;
465
466
467         /* Set up the two menus */
468         WIPE(&group_menu, menu_type);
469         group_menu.count = grp_cnt;
470         group_menu.cmd_keys = "\n\r6\x8C"/* Ignore these as menu commands */
471         group_menu.menu_data = g_names;
472
473         WIPE(&object_menu, menu_type);
474         object_menu.menu_data = &o_funcs;
475         WIPE(&object_iter, object_iter);
476         object_iter.display_row = display_group_member;
477
478         o_funcs.is_visual = FALSE;
479
480         menu_init(&group_menu, MN_SKIN_SCROLL, find_menu_iter(MN_ITER_STRINGS), &group_region);
481         menu_init(&object_menu, MN_SKIN_SCROLL, &object_iter, &object_region);
482
483
484         /* This is the event loop for a multi-region panel */
485         /* Panels are -- text panels, two menus, and visual browser */
486         /* with "pop-up menu" for lore */
487         while ((!flag) && (grp_cnt))
488         {
489                 ui_event_data ke, ke0;
490
491                 if (redraw)
492                 {
493                         /* Print the title bits */
494                         region_erase(&title_area);
495                         prt(format("Knowledge - %s", title), 2, 0);
496                         prt("Group", 4, 0);
497                         prt("Name", 4, g_name_len + 3);
498
499                         if (otherfields)
500                                 prt(otherfields, 4, 55);
501
502
503                         /* Print dividers: horizontal and vertical */
504                         for (i = 0; i < 79; i++)
505                                 Term_putch(i, 5, TERM_WHITE, '=');
506
507                         for (i = 0; i < browser_rows; i++)
508                                 Term_putch(g_name_len + 1, 6 + i, TERM_WHITE, '|');
509
510
511                         /* Reset redraw flag */
512                         redraw = FALSE;
513                 }
514
515                 if (g_cur != grp_old)
516                 {
517                         grp_old = g_cur;
518                         o_cur = 0;
519                         g_o_count = g_offset[g_cur+1] - g_offset[g_cur];
520                         menu_set_filter(&object_menu, obj_list + g_offset[g_cur], g_o_count);
521                         group_menu.cursor = g_cur;
522                         object_menu.cursor = 0;
523                 }
524
525                 /* HACK ... */
526                 if (!visual_list)
527                 {
528                         /* ... The object menu may be browsing the entire group... */
529                         o_funcs.is_visual = FALSE;
530                         menu_set_filter(&object_menu, obj_list + g_offset[g_cur], g_o_count);
531                         object_menu.cursor = o_cur;
532                 }
533                 else
534                 {
535                         /* ... or just a single element in the group. */
536                         o_funcs.is_visual = TRUE;
537                         menu_set_filter(&object_menu, obj_list + o_cur + g_offset[g_cur], 1);
538                         object_menu.cursor = 0;
539                 }
540
541                 oid = obj_list[g_offset[g_cur]+o_cur];
542
543                 /* Print prompt */
544                 {
545                         const char *pedit = (!o_funcs.xattr) ? "" :
546                                         (!(attr_idx|char_idx) ? ", 'c' to copy" : ", 'c', 'p' to paste");
547                         const char *xtra = o_funcs.xtra_prompt ? o_funcs.xtra_prompt(oid) : "";
548                         const char *pvs = "";
549
550                         if (visual_list) pvs = ", ENTER to accept";
551                         else if (o_funcs.xattr) pvs = ", 'v' for visuals";
552
553
554
555                         prt(format("<dir>%s%s%s, ESC", pvs, pedit, xtra), hgt - 1, 0);
556                 }
557
558                 if (do_swap)
559                 {
560                         do_swap = FALSE;
561                         swap(active_menu, inactive_menu);
562                         swap(active_cursor, inactive_cursor);
563                         panel = 1 - panel;
564                 }
565
566                 if (g_funcs.summary && !visual_list)
567                 {
568                         g_funcs.summary(g_cur, obj_list, g_o_count, g_offset[g_cur],
569                                         object_menu.boundary.row + object_menu.boundary.page_rows,
570                                         object_region.col);
571                 }
572
573                 menu_refresh(inactive_menu);
574                 menu_refresh(active_menu);
575
576                 handle_stuff();
577
578                 if (visual_list)
579                 {
580                         display_visual_list(g_name_len + 3, 7, browser_rows-1,
581                                                      wid - (g_name_len + 3), attr_top, char_left);
582                         place_visual_list_cursor(g_name_len + 3, 7, *o_funcs.xattr(oid),
583                                                                                 *o_funcs.xchar(oid), attr_top, char_left);
584                 }
585
586                 if (delay)
587                 {
588                         /* Force screen update */
589                         Term_fresh();
590
591                         /* Delay */
592                         Term_xtra(TERM_XTRA_DELAY, delay);
593
594                         delay = 0;
595                 }
596
597                 ke = inkey_ex();
598
599                 /* Do visual mode command if needed */
600                 if (o_funcs.xattr && o_funcs.xchar &&
601                                         visual_mode_command(ke, &visual_list,
602                                         browser_rows-1, wid - (g_name_len + 3),
603                                         &attr_top, &char_left,
604                                         o_funcs.xattr(oid), o_funcs.xchar(oid),
605                                         g_name_len + 3, 7, &delay))
606                 {
607                         continue;
608                 }
609
610                 if (ke.type == EVT_MOUSE)
611                 {
612                         /* Change active panels */
613                         if (region_inside(&inactive_menu->boundary, &ke))
614                         {
615                                 swap(active_menu, inactive_menu);
616                                 swap(active_cursor, inactive_cursor);
617                                 panel = 1-panel;
618                         }
619                 }
620
621                 ke0 = run_event_loop(&active_menu->target, &ke);
622                 if (ke0.type != EVT_AGAIN) ke = ke0;
623
624                 switch (ke.type)
625                 {
626                         case EVT_KBRD:
627                         {
628                                 break;
629                         }
630
631                         case ESCAPE:
632                         {
633                                 flag = TRUE;
634                                 continue;
635                         }
636
637                         case EVT_SELECT:
638                         {
639                                 if (panel == 1 && oid >= 0 && o_cur == active_menu->cursor)
640                                 {
641                                         o_funcs.lore(oid);
642                                         redraw = TRUE;
643                                 }
644                         }
645
646                         case EVT_MOVE:
647                         {
648                                 *active_cursor = active_menu->cursor;
649                                 continue;
650                         }
651
652                         case EVT_BACK:
653                         {
654                                 if (panel == 1)
655                                         do_swap = TRUE;
656                         }
657
658                         /* XXX Handle EVT_RESIZE */
659
660                         default:
661                         {
662                                 continue;
663                         }
664                 }
665
666                 switch (ke.key)
667                 {
668                         case ESCAPE:
669                         {
670                                 flag = TRUE;
671                                 break;
672                         }
673
674                         case 'R':
675                         case 'r':
676                         {
677                                 /* Recall on screen */
678                                 if (oid >= 0)
679                                         o_funcs.lore(oid);
680
681                                 redraw = TRUE;
682                                 break;
683                         }
684
685                         /* Jump down a page */
686                         case '3':
687                         {
688                                 *active_cursor += browser_rows;
689
690                                 if (g_cur >= grp_cnt) g_cur = grp_cnt - 1;
691                                 else if (o_cur >= g_o_count) o_cur = g_o_count - 1;
692
693                                 break;
694                         }
695
696                         /* Jump up a page */
697                         case '9':
698                         {
699                                 *active_cursor -= browser_rows;
700
701                                 if (*active_cursor < 0) *active_cursor = 0;
702
703                                 break;
704                         }
705
706                         default:
707                         {
708                                 int d = target_dir(ke.key);
709
710                                 /* Handle key-driven motion between panels */
711                                 if (ddx[d] && ((ddx[d] < 0) == (panel == 1)))
712                                 {
713                                         /* Silly hack -- diagonal arithmetic */
714                                         *inactive_cursor += ddy[d];
715                                         if (*inactive_cursor < 0) *inactive_cursor = 0;
716                                         else if (g_cur >= grp_cnt) g_cur = grp_cnt - 1;
717                                         else if (o_cur >= g_o_count) o_cur = g_o_count - 1;
718                                         do_swap = TRUE;
719                                 }
720                                 else if (o_funcs.xtra_act)
721                                 {
722                                         o_funcs.xtra_act(ke.key, oid);
723                                 }
724
725                                 break;
726                         }
727                 }
728         }
729
730         /* Restore roguelike option */
731         rogue_like_commands = omode;
732
733         /* Prompt */
734         if (!grp_cnt)
735                 prt(format("No %s known.", title), 15, 0);
736
737         FREE(g_names);
738         FREE(g_offset);
739         FREE(g_list);
740 }
741
742 /*
743  * Display visuals.
744  */
745 static void display_visual_list(int col, int row, int height, int width, byte attr_top, char char_left)
746 {
747         int i, j;
748
749         /* Clear the display lines */
750         for (i = 0; i < height; i++)
751                         Term_erase(col, row + i, width);
752
753         width = logical_width(width);
754
755         /* Display lines until done */
756         for (i = 0; i < height; i++)
757         {
758                 /* Display columns until done */
759                 for (j = 0; j < width; j++)
760                 {
761                         byte a;
762                         char c;
763                         int x = col + actual_width(j);
764                         int y = row + actual_width(i);
765                         int ia, ic;
766
767                         ia = attr_top + i;
768                         ic = char_left + j;
769
770                         a = (byte)ia;
771                         c = (char)ic;
772
773                         /* Display symbol */
774                         big_pad(x, y, a, c);
775                 }
776         }
777 }
778
779
780 /*
781  * Place the cursor at the collect position for visual mode
782  */
783 static void place_visual_list_cursor(int col, int row, byte a, byte c, byte attr_top, byte char_left)
784 {
785         int i = a - attr_top;
786         int j = c - char_left;
787
788         int x = col + actual_width(j);
789         int y = row + actual_height(i);
790
791         /* Place the cursor */
792         Term_gotoxy(x, y);
793 }
794
795
796 /*
797  *  Do visual mode command -- Change symbols
798  */
799 static bool visual_mode_command(ui_event_data ke, bool *visual_list_ptr,
800                                 int height, int width,
801                                 byte *attr_top_ptr, char *char_left_ptr,
802                                 byte *cur_attr_ptr, char *cur_char_ptr,
803                                 int col, int row, int *delay)
804 {
805         static byte attr_old = 0;
806         static char char_old = 0;
807
808         int frame_left = logical_width(10);
809         int frame_right = logical_width(10);
810         int frame_top = logical_height(4);
811         int frame_bottom = logical_height(4);
812
813         switch (ke.key)
814         {
815                 case ESCAPE:
816                 {
817                         if (*visual_list_ptr)
818                         {
819                                 /* Cancel change */