root/trunk/src/cmd3.c

Revision 950, 18.9 kB (checked in by takkaria, 2 months ago)

Fix some constness warnings.

  • Property svn:eol-style set to native
Line 
1 /*
2  * File: cmd3.c
3  * Purpose: Miscellaneous queries
4  *
5  * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
6  *
7  * This work is free software; you can redistribute it and/or modify it
8  * under the terms of either:
9  *
10  * a) the GNU General Public License as published by the Free Software
11  *    Foundation, version 2, or
12  *
13  * b) the "Angband licence":
14  *    This software may be copied and distributed for educational, research,
15  *    and not for profit purposes provided that this copyright and statement
16  *    are included in all such copies.  Other copyrights may also apply.
17  */
18 #include "angband.h"
19 #include "tvalsval.h"
20
21
22 /*
23  * Display inventory
24  */
25 void do_cmd_inven(void)
26 {
27         /* Hack -- Start in "inventory" mode */
28         p_ptr->command_wrk = (USE_INVEN);
29
30         /* Save screen */
31         screen_save();
32
33         /* Hack -- show empty slots */
34         item_tester_full = TRUE;
35
36         /* Display the inventory */
37         show_inven();
38
39         /* Hack -- hide empty slots */
40         item_tester_full = FALSE;
41
42         /* Prompt for a command */
43         prt(format("(Inventory) Burden %d.%dlb (%d%% capacity). Command: ",
44             p_ptr->total_weight / 10, p_ptr->total_weight % 10,
45             (10 * p_ptr->total_weight) / (6 * adj_str_wgt[p_ptr->stat_ind[A_STR]])), 0, 0);
46
47         /* Hack -- Get a new command */
48         p_ptr->command_new = inkey();
49
50         /* Load screen */
51         screen_load();
52
53
54         /* Hack -- Process "Escape" */
55         if (p_ptr->command_new == ESCAPE)
56         {
57                 /* Reset stuff */
58                 p_ptr->command_new = 0;
59         }
60 }
61
62
63 /*
64  * Display equipment
65  */
66 void do_cmd_equip(void)
67 {
68         /* Hack -- Start in "equipment" mode */
69         p_ptr->command_wrk = (USE_EQUIP);
70
71         /* Save screen */
72         screen_save();
73
74         /* Hack -- show empty slots */
75         item_tester_full = TRUE;
76
77         /* Display the equipment */
78         show_equip();
79
80         /* Hack -- undo the hack above */
81         item_tester_full = FALSE;
82
83         /* Prompt for a command */
84         prt("(Equipment) Command: ", 0, 0);
85
86         /* Hack -- Get a new command */
87         p_ptr->command_new = inkey();
88
89         /* Load screen */
90         screen_load();
91
92
93         /* Hack -- Process "Escape" */
94         if (p_ptr->command_new == ESCAPE)
95         {
96                 /* Reset stuff */
97                 p_ptr->command_new = 0;
98         }
99 }
100
101
102 /*
103  * Wield or wear a single item from the pack or floor
104  */
105 void wield_item(object_type *o_ptr, int item)
106 {
107         object_type object_type_body;
108         object_type *i_ptr = &object_type_body;
109
110         cptr act;
111         char o_name[80];
112
113         int slot = wield_slot(o_ptr);
114
115         /* Take a turn */
116         p_ptr->energy_use = 100;
117
118         /* Obtain local object */
119         object_copy(i_ptr, o_ptr);
120
121         /* Modify quantity */
122         i_ptr->number = 1;
123
124         /* Decrease the item (from the pack) */
125         if (item >= 0)
126         {
127                 inven_item_increase(item, -1);
128                 inven_item_optimize(item);
129         }
130
131         /* Decrease the item (from the floor) */
132         else
133         {
134                 floor_item_increase(0 - item, -1);
135                 floor_item_optimize(0 - item);
136         }
137
138         /* Get the wield slot */
139         o_ptr = &inventory[slot];
140
141         /* Take off existing item */
142         if (o_ptr->k_idx)
143         {
144                 /* Take off existing item */
145                 (void)inven_takeoff(slot, 255);
146         }
147
148         /* Wear the new stuff */
149         object_copy(o_ptr, i_ptr);
150
151         /* Increase the weight */
152         p_ptr->total_weight += i_ptr->weight;
153
154         /* Increment the equip counter by hand */
155         p_ptr->equip_cnt++;
156
157         /* Where is the item now */
158         if (slot == INVEN_WIELD)
159                 act = "You are wielding";
160         else if (slot == INVEN_BOW)
161                 act = "You are shooting with";
162         else if (slot == INVEN_LITE)
163                 act = "Your light source is";
164         else
165                 act = "You are wearing";
166
167         /* Describe the result */
168         object_desc(o_name, sizeof(o_name), o_ptr, TRUE, ODESC_FULL);
169
170         /* Message */
171         sound(MSG_WIELD);
172         msg_format("%s %s (%c).", act, o_name, index_to_label(slot));
173
174         /* Cursed! */
175         if (cursed_p(o_ptr))
176         {
177                 /* Warn the player */
178                 sound(MSG_CURSED);
179                 msg_print("Oops! It feels deathly cold!");
180
181                 /* Sense the object */
182                 o_ptr->pseudo = INSCRIP_CURSED;
183
184                 /* The object has been "sensed" */
185                 o_ptr->ident |= (IDENT_SENSE);
186
187                 /* Set squelched status */
188                 p_ptr->notice |= PN_SQUELCH;
189         }
190
191         /* Recalculate bonuses, torch, mana */
192         p_ptr->update |= (PU_BONUS | PU_TORCH | PU_MANA);
193         p_ptr->redraw |= (PR_INVEN | PR_EQUIP);
194 }
195
196
197
198 /*
199  * Destroy an item
200  */
201 void do_cmd_destroy(void)
202 {
203         int item, amt;
204
205         object_type *o_ptr;
206
207         object_type *i_ptr;
208         object_type object_type_body;
209
210         char o_name[120];
211         char out_val[160];
212
213         cptr q, s;
214
215         /* Get an item */
216         q = "Destroy which item? ";
217         s = "You have nothing to destroy.";
218         if (!get_item(&item, q, s, (USE_INVEN | USE_EQUIP | USE_FLOOR | CAN_SQUELCH))) return;
219
220         /* Deal with squelched items */
221         if (item == ALL_SQUELCHED)
222         {
223                 squelch_items();
224                 return;
225         }
226
227         /* Get the item (in the pack) */
228         if (item >= 0)
229         {
230                 o_ptr = &inventory[item];
231         }
232
233         /* Get the item (on the floor) */
234         else
235         {
236                 o_ptr = &o_list[0 - item];
237         }
238
239         /* Get a quantity */
240         amt = get_quantity(NULL, o_ptr->number);
241
242         /* Allow user abort */
243         if (amt <= 0) return;
244
245         /* Get local object */
246         i_ptr = &object_type_body;
247
248         /* Obtain a local object */
249         object_copy(i_ptr, o_ptr);
250
251         if ((o_ptr->tval == TV_WAND) ||
252             (o_ptr->tval == TV_STAFF) ||
253             (o_ptr->tval == TV_ROD))
254         {
255                 /* Calculate the amount of destroyed charges */
256                 i_ptr->pval = o_ptr->pval * amt / o_ptr->number;
257         }
258
259         /* Set quantity */
260         i_ptr->number = amt;
261
262         /* Describe the destroyed object */
263         object_desc(o_name, sizeof(o_name), i_ptr, TRUE, ODESC_FULL);
264
265         /* Verify destruction */
266         strnfmt(out_val, sizeof(out_val), "Really destroy %s? ", o_name);
267         if (!get_check(out_val)) return;
268
269         /* Artifacts cannot be destroyed */
270         if (artifact_p(o_ptr))
271         {
272                 /* Message */
273                 msg_format("You cannot destroy %s.", o_name);
274
275                 /* Don't mark id'ed objects */
276                 if (object_known_p(o_ptr)) return;
277
278                 /* It has already been sensed */
279                 if (o_ptr->ident & (IDENT_SENSE))
280                 {
281                         /* Already sensed objects always get improved feelings */
282                         if (cursed_p(o_ptr))
283                                 o_ptr->pseudo = INSCRIP_TERRIBLE;
284                         else
285                                 o_ptr->pseudo = INSCRIP_SPECIAL;
286                 }
287                 else
288                 {
289                         /* Mark the object as indestructible */
290                         o_ptr->pseudo = INSCRIP_INDESTRUCTIBLE;
291                 }
292
293                 /* Combine the pack */
294                 p_ptr->notice |= (PN_COMBINE);
295
296                 /* Redraw stuff */
297                 p_ptr->redraw |= (PR_INVEN | PR_EQUIP);
298
299                 /* Done */
300                 return;
301         }
302
303         /* Message */
304         message_format(MSG_DESTROY, 0, "You destroy %s.", o_name);
305
306         /* Reduce the charges of rods/wands/staves */
307         reduce_charges(o_ptr, amt);
308
309         /* Eliminate the item (from the pack) */
310         if (item >= 0)
311         {
312                 inven_item_increase(item, -amt);
313                 inven_item_describe(item);
314                 inven_item_optimize(item);
315         }
316
317         /* Eliminate the item (from the floor) */
318         else
319         {
320                 floor_item_increase(0 - item, -amt);
321                 floor_item_describe(0 - item);
322                 floor_item_optimize(0 - item);
323         }
324
325 #if 0
326         /*
327          * We can only re-enable this when it can be made to interact well with
328          * the repeat code.
329          */
330
331         /* We have destroyed a floor item, and the floor is not empty */
332         if ((item < 0) && (cave_o_idx[p_ptr->py][p_ptr->px]))
333         {
334                 /* Automatically repeat this command (unless disturbed) */
335                 p_ptr->command_cmd = 'k';
336                 p_ptr->command_rep = 2;
337         }
338 #endif
339 }
340
341
342
343
344 void refill_lamp(object_type *j_ptr, object_type *o_ptr, int item)
345 {
346         /* Refuel */
347         j_ptr->timeout += o_ptr->timeout ? o_ptr->timeout : o_ptr->pval;
348
349         /* Message */
350         msg_print("You fuel your lamp.");
351
352         /* Comment */
353         if (j_ptr->timeout >= FUEL_LAMP)
354         {
355                 j_ptr->timeout = FUEL_LAMP;
356                 msg_print("Your lamp is full.");
357         }
358
359         /* Refilled from a lantern */
360         if (o_ptr->sval == SV_LITE_LANTERN)
361         {
362                 /* Unstack if necessary */
363                 if (o_ptr->number > 1)
364                 {
365                         object_type *i_ptr;
366                         object_type object_type_body;
367
368                         /* Get local object */
369                         i_ptr = &object_type_body;
370
371                         /* Obtain a local object */
372                         object_copy(i_ptr, o_ptr);
373
374                         /* Modify quantity */
375                         i_ptr->number = 1;
376
377                         /* Remove fuel */
378                         i_ptr->timeout = 0;
379
380                         /* Unstack the used item */
381                         o_ptr->number--;
382                         p_ptr->total_weight -= i_ptr->weight;
383
384                         /* Carry or drop */
385                         if (item >= 0)
386                                 item = inven_carry(i_ptr);
387                         else
388                                 drop_near(i_ptr, 0, p_ptr->py, p_ptr->px);
389                 }
390
391                 /* Empty a single lantern */
392                 else
393                 {
394                         /* No more fuel */
395                         o_ptr->timeout = 0;
396                 }
397
398                 /* Combine / Reorder the pack (later) */
399                 p_ptr->notice |= (PN_COMBINE | PN_REORDER);
400
401                 /* Redraw stuff */
402                 p_ptr->redraw |= (PR_INVEN);
403         }
404
405         /* Refilled from a flask */
406         else
407         {
408                 /* Decrease the item (from the pack) */
409                 if (item >= 0)
410                 {
411                         inven_item_increase(item, -1);
412                         inven_item_describe(item);
413                         inven_item_optimize(item);
414                 }
415
416                 /* Decrease the item (from the floor) */
417                 else
418                 {
419                         floor_item_increase(0 - item, -1);
420                         floor_item_describe(0 - item);
421                         floor_item_optimize(0 - item);
422                 }
423         }
424
425         /* Recalculate torch */
426         p_ptr->update |= (PU_TORCH);
427
428         /* Redraw stuff */
429         p_ptr->redraw |= (PR_EQUIP);
430 }
431
432
433 void refuel_torch(object_type *j_ptr, object_type *o_ptr, int item)
434 {
435         /* Refuel */
436         j_ptr->timeout += o_ptr->timeout + 5;
437
438         /* Message */
439         msg_print("You combine the torches.");
440
441         /* Over-fuel message */
442         if (j_ptr->timeout >= FUEL_TORCH)
443         {
444                 j_ptr->timeout = FUEL_TORCH;
445                 msg_print("Your torch is fully fueled.");
446         }
447
448         /* Refuel message */
449         else
450         {
451                 msg_print("Your torch glows more brightly.");
452         }
453
454         /* Decrease the item (from the pack) */
455         if (item >= 0)
456         {
457                 inven_item_increase(item, -1);
458                 inven_item_describe(item);
459                 inven_item_optimize(item);
460         }
461
462         /* Decrease the item (from the floor) */
463         else
464         {
465                 floor_item_increase(0 - item, -1);
466                 floor_item_describe(0 - item);
467                 floor_item_optimize(0 - item);
468         }
469
470         /* Recalculate torch */
471         p_ptr->update |= (PU_TORCH);
472
473         /* Redraw stuff */
474         p_ptr->redraw |= (PR_EQUIP);
475 }
476
477
478
479
480
481
482 /*
483  * Target command
484  */
485 void do_cmd_target(void)
486 {
487         /* Target set */
488         if (target_set_interactive(TARGET_KILL, -1, -1))
489         {
490                 msg_print("Target Selected.");
491         }
492
493         /* Target aborted */
494         else
495         {
496                 msg_print("Target Aborted.");
497         }
498 }
499
500
501
502 /*
503  * Look command
504  */
505 void do_cmd_look(void)
506 {
507         /* Look around */
508         if (target_set_interactive(TARGET_LOOK, -1, -1))
509         {
510                 msg_print("Target Selected.");
511         }
512 }
513
514
515
516 /*
517  * Allow the player to examine other sectors on the map
518  */
519 void do_cmd_locate(void)
520 {
521         int dir, y1, x1, y2, x2;
522
523         char tmp_val[80];
524
525         char out_val[160];
526
527
528         /* Start at current panel */
529         y1 = Term->offset_y;
530         x1 = Term->offset_x;
531
532         /* Show panels until done */
533         while (1)
534         {
535                 /* Get the current panel */
536                 y2 = Term->offset_y;
537                 x2 = Term->offset_x;
538                
539                 /* Describe the location */
540                 if ((y2 == y1) && (x2 == x1))
541                 {
542                         tmp_val[0] = '\0';
543                 }
544                 else
545                 {
546                         strnfmt(tmp_val, sizeof(tmp_val), "%s%s of",
547                                 ((y2 < y1) ? " north" : (y2 > y1) ? " south" : ""),
548                                 ((x2 < x1) ? " west" : (x2 > x1) ? " east" : ""));
549                 }
550
551                 /* Prepare to ask which way to look */
552                 strnfmt(out_val, sizeof(out_val),
553                         "Map sector [%d,%d], which is%s your sector.  Direction?",
554                         (y2 / PANEL_HGT), (x2 / PANEL_WID), tmp_val);
555
556                 /* More detail */
557                 if (center_player)
558                 {
559                         strnfmt(out_val, sizeof(out_val),
560                                 "Map sector [%d(%02d),%d(%02d)], which is%s your sector.  Direction?",
561                                 (y2 / PANEL_HGT), (y2 % PANEL_HGT),
562                                 (x2 / PANEL_WID), (x2 % PANEL_WID), tmp_val);
563                 }
564
565                 /* Assume no direction */
566                 dir = 0;
567
568                 /* Get a direction */
569                 while (!dir)
570                 {
571                         char command;
572
573                         /* Get a command (or Cancel) */
574                         if (!get_com(out_val, &command)) break;
575
576                         /* Extract direction */
577                         dir = target_dir(command);
578
579                         /* Error */
580                         if (!dir) bell("Illegal direction for locate!");
581                 }
582
583                 /* No direction */
584                 if (!dir) break;
585
586                 /* Apply the motion */
587                 change_panel(dir);
588
589                 /* Handle stuff */
590                 handle_stuff();
591         }
592
593         /* Verify panel */
594         verify_panel();
595 }
596
597
598
599
600
601
602 /*
603  * The table of "symbol info" -- each entry is a string of the form
604  * "X:desc" where "X" is the trigger, and "desc" is the "info".
605  */
606 static cptr ident_info[] =
607 {
608         " :A dark grid",
609         "!:A potion (or oil)",
610         "\":An amulet (or necklace)",
611         "#:A wall (or secret door)",
612         "$:Treasure (gold or gems)",
613         "%:A vein (magma or quartz)",
614         /* "&:unused", */
615         "':An open door",
616         "(:Soft armor",
617         "):A shield",
618         "*:A vein with treasure",
619         "+:A closed door",
620         ",:Food (or mushroom patch)",
621         "-:A wand (or rod)",
622         ".:Floor",
623         "/:A polearm (Axe/Pike/etc)",
624         /* "0:unused", */
625         "1:Entrance to General Store",
626         "2:Entrance to Armory",
627         "3:Entrance to Weaponsmith",
628         "4:Entrance to Temple",
629         "5:Entrance to Alchemy shop",
630         "6:Entrance to Magic store",
631         "7:Entrance to Black Market",
632         "8:Entrance to your home",
633         /* "9:unused", */
634         "::Rubble",
635         ";:A glyph of warding",
636         "<:An up staircase",
637         "=:A ring",
638         ">:A down staircase",
639         "?:A scroll",
640         "@:You",
641         "A:Angel",
642         "B:Bird",
643         "C:Canine",
644         "D:Ancient Dragon/Wyrm",
645         "E:Elemental",
646         "F:Dragon Fly",
647         "G:Ghost",
648         "H:Hybrid",
649         "I:Insect",
650         "J:Snake",
651         "K:Killer Beetle",
652         "L:Lich",
653         "M:Multi-Headed Reptile",
654         /* "N:unused", */
655         "O:Ogre",
656         "P:Giant Humanoid",
657         "Q:Quylthulg (Pulsing Flesh Mound)",
658         "R:Reptile/Amphibian",
659         "S:Spider/Scorpion/Tick",
660         "T:Troll",
661         "U:Major Demon",
662         "V:Vampire",
663         "W:Wight/Wraith/etc",
664         "X:Xorn/Xaren/etc",
665         "Y:Yeti",
666         "Z:Zephyr Hound",
667         "[:Hard armor",
668         "\\:A hafted weapon (mace/whip/etc)",
669         "]:Misc. armor",
670         "^:A trap",
671         "_:A staff",
672         /* "`:unused", */
673         "a:Ant",
674         "b:Bat",
675         "c:Centipede",
676         "d:Dragon",
677         "e:Floating Eye",
678         "f:Feline",
679         "g:Golem",
680         "h:Hobbit/Elf/Dwarf",
681         "i:Icky Thing",
682         "j:Jelly",
683         "k:Kobold",
684         "l:Louse",
685         "m:Mold",
686         "n:Naga",
687         "o:Orc",
688         "p:Person/Human",
689         "q:Quadruped",
690         "r:Rodent",
691         "s:Skeleton",
692         "t:Townsperson",
693         "u:Minor Demon",
694         "v:Vortex",
695         "w:Worm/Worm-Mass",
696         /* "x:unused", */
697         "y:Yeek",
698         "z:Zombie/Mummy",
699         "{:A missile (arrow/bolt/shot)",
700         "|:An edged weapon (sword/dagger/etc)",
701         "}:A launcher (bow/crossbow/sling)",
702         "~:A tool (or miscellaneous item)",
703         NULL
704 };
705
706
707
708 /*
709  * Sorting hook -- Comp function -- see below
710  *
711  * We use "u" to point to array of monster indexes,
712  * and "v" to select the type of sorting to perform on "u".
713  */
714 bool ang_sort_comp_hook(const void *u, const void *v, int a, int b)
715 {
716         const u16b *who = (const u16b *)(u);
717         const u16b *why = (const u16b *)(v);
718
719         int w1 = who[a];
720         int w2 = who[b];
721
722         int z1, z2;
723
724
725         /* Sort by player kills */
726         if (*why >= 4)
727         {
728                 /* Extract player kills */
729                 z1 = l_list[w1].pkills;
730                 z2 = l_list[w2].pkills;
731
732                 /* Compare player kills */
733                 if (z1 < z2) return (TRUE);
734                 if (z1 > z2) return (FALSE);
735         }
736
737
738         /* Sort by total kills */
739         if (*why >= 3)
740         {
741                 /* Extract total kills */
742                 z1 = l_list[w1].tkills;
743                 z2 = l_list[w2].tkills;
744
745                 /* Compare total kills */
746                 if (z1 < z2) return (TRUE);
747                 if (z1 > z2) return (FALSE);
748         }
749
750
751         /* Sort by monster level */
752         if (*why >= 2)
753         {
754                 /* Extract levels */
755                 z1 = r_info[w1].level;
756                 z2 = r_info[w2].level;
757
758                 /* Compare levels */
759                 if (z1 < z2) return (TRUE);
760                 if (z1 > z2) return (FALSE);
761         }
762
763
764         /* Sort by monster experience */
765         if (*why >= 1)
766         {
767                 /* Extract experience */
768                 z1 = r_info[w1].mexp;
769                 z2 = r_info[w2].mexp;
770
771                 /* Compare experience */
772                 if (z1 < z2) return (TRUE);
773                 if (z1 > z2) return (FALSE);
774         }
775
776
777         /* Compare indexes */
778         return (w1 <= w2);
779 }
780
781
782 /*
783  * Sorting hook -- Swap function -- see below
784  *
785  * We use "u" to point to array of monster indexes,
786  * and "v" to select the type of sorting to perform.
787  */
788 void ang_sort_swap_hook(void *u, void *v, int a, int b)
789 {
790         u16b *who = (u16b*)(u);
791
792         u16b holder;
793
794         /* Unused parameter */
795         (void)v;
796
797         /* Swap */
798         holder = who[a];
799         who[a] = who[b];
800         who[b] = holder;
801 }
802
803
804 /*
805  * Identify a character, allow recall of monsters
806  *
807  * Several "special" responses recall "multiple" monsters:
808  *   ^A (all monsters)
809  *   ^U (all unique monsters)
810  *   ^N (all non-unique monsters)
811  *
812  * The responses may be sorted in several ways, see below.
813  *
814  * Note that the player ghosts are ignored, since they do not exist.
815  */
816 void do_cmd_query_symbol(void)
817 {
818         int i, n, r_idx;
819         char sym;
820         char buf[128];
821
822         ui_event_data query;
823
824         bool all = FALSE;
825         bool uniq = FALSE;
826         bool norm = FALSE;
827
828         bool recall = FALSE;
829
830         u16b why = 0;
831         u16b *who;
832
833
834         /* Get a character, or abort */
835         if (!get_com("Enter character to be identified: ", &sym)) return;
836
837         /* Find that character info, and describe it */
838         for (i = 0; ident_info[i]; ++i)
839         {
840                 if (sym == ident_info[i][0]) break;
841         }
842
843         /* Describe */
844         if (sym == KTRL('A'))
845         {
846                 all = TRUE;
847                 my_strcpy(buf, "Full monster list.", sizeof(buf));
848         }
849         else if (sym == KTRL('U'))
850         {
851                 all = uniq = TRUE;
852                 my_strcpy(buf, "Unique monster list.", sizeof(buf));
853         }
854         else if (sym == KTRL('N'))
855         {
856                 all = norm = TRUE;
857                 my_strcpy(buf, "Non-unique monster list.", sizeof(buf));
858         }
859         else if (ident_info[i])
860         {
861                 strnfmt(buf, sizeof(buf), "%c - %s.", sym, ident_info[i] + 2);
862         }
863         else
864         {
865                 strnfmt(buf, sizeof(buf), "%c - %s.", sym, "Unknown Symbol");
866         }
867
868         /* Display the result */
869         prt(buf, 0, 0);
870
871
872         /* Allocate the "who" array */
873         who = C_ZNEW(z_info->r_max, u16b);
874
875         /* Collect matching monsters */
876         for (n = 0, i = 1; i < z_info->r_max - 1; i++)
877         {
878                 monster_race *r_ptr = &r_info[i];
879                 monster_lore *l_ptr = &l_list[i];
880
881                 /* Nothing to recall */
882                 if (!cheat_know && !l_ptr->sights) continue;
883
884                 /* Require non-unique monsters if needed */
885                 if (norm && (r_ptr->flags[0] & (RF0_UNIQUE))) continue;
886
887                 /* Require unique monsters if needed */
888                 if (uniq && !(r_ptr->flags[0] & (RF0_UNIQUE))) continue;
889
890                 /* Collect "appropriate" monsters */
891                 if (all || (r_ptr->d_char == sym)) who[n++] = i;
892         }
893
894         /* Nothing to recall */
895         if (!n)
896         {
897                 /* XXX XXX Free the "who" array */
898                 FREE(who);
899
900                 return;
901         }
902
903         /* Buttons */
904         button_add("[y]", 'y');
905         button_add("[k]", 'k');
906         /* Don't collide with the repeat button */
907         button_add("[n]", 'q');
908         redraw_stuff();
909
910         /* Prompt */
911         put_str("Recall details? (y/k/n): ", 0, 40);
912
913         /* Query */
914         query = inkey_ex();
915
916         /* Restore */
917         prt(buf, 0, 0);
918
919         /* Buttons */
920         button_kill('y');
921         button_kill('k');
922         button_kill('q');
923         redraw_stuff();
924
925         /* Interpret the response */
926         if (query.key == 'k')
927         {
928                 /* Sort by kills (and level) */
929                 why = 4;
930         }
931         else if (query.key == 'y' || query.key == 'p')
932         {
933                 /* Sort by level; accept 'p' as legacy */
934                 why = 2;
935         }
936         else
937         {
938                 /* Any unsupported response is "nope, no history please" */
939        
940                 /* XXX XXX Free the "who" array */
941                 FREE(who);
942
943                 return;
944         }
945
946
947         /* Select the sort method */
948         ang_sort_comp = ang_sort_comp_hook;
949         ang_sort_swap = ang_sort_swap_hook;
950
951         /* Sort the array */
952         ang_sort(who, &why, n);
953
954         /* Start at the end */
955         i = n - 1;
956
957         /* Button */
958         button_add("[r]", 'r');
959         button_add("[-]", '-');
9