/src/cmd-know.c
C | 2116 lines | 1381 code | 437 blank | 298 comment | 312 complexity | 275e9eda16b12d12e87568f0eda25171 MD5 | raw file
1/* 2 * File: cmd-know.c 3 * Purpose: Knowledge screen stuff. 4 * 5 * Copyright (c) 2000-2007 Eytan Zweig, Andrew Doull, Pete Mack. 6 * (c) 2010 Peter Denison, Chris Carr. 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 "object/tvalsval.h" 21#include "ui.h" 22#include "ui-menu.h" 23#include "store.h" 24 25/* Flag value for missing array entry */ 26#define MISSING -17 27 28 29typedef struct 30{ 31 int maxnum; /* Maximum possible item count for this class */ 32 bool easy_know; /* Items don't need to be IDed to recognize membership */ 33 34 const char *(*name)(int gid); /* Name of this group */ 35 36 /* Compare, in group and display order (optional if already sorted) */ 37 int (*gcomp)(const void *, const void *); /* Compares gids of two oids */ 38 int (*group)(int oid); /* Returns gid for an oid */ 39 40 /* Summary function for the "object" information. */ 41 void (*summary)(int gid, const int *object_list, int n, int top, int row, int col); 42 43} group_funcs; 44 45typedef struct 46{ 47 /* Displays an entry at specified location, including kill-count and graphics */ 48 void (*display_member)(int col, int row, bool cursor, int oid); 49 50 void (*lore)(int oid); /* Displays lore for an oid */ 51 52 53 /* Required only for objects with modifiable display attributes */ 54 /* Unknown 'flavors' return flavor attributes */ 55 char *(*xchar)(int oid); /* Get character attr for OID (by address) */ 56 byte *(*xattr)(int oid); /* Get color attr for OID (by address) */ 57 58 const char *(*xtra_prompt)(int oid); /* Returns optional extra prompt */ 59 void (*xtra_act)(char ch, int oid); /* Handles optional extra actions */ 60 61 bool is_visual; /* Does this kind have visual editing? */ 62 63} member_funcs; 64 65 66/* Helper class for generating joins */ 67typedef struct join 68{ 69 int oid; 70 int gid; 71} join_t; 72 73/* A default group-by */ 74static join_t *default_join; 75#if 0 76static int default_join_cmp(const void *a, const void *b) 77{ 78 join_t *ja = &default_join[*(int*)a]; 79 join_t *jb = &default_join[*(int*)b]; 80 int c = ja->gid - jb->gid; 81 if (c) return c; 82 return ja->oid - jb->oid; 83} 84#endif 85static int default_group(int oid) { return default_join[oid].gid; } 86 87 88static int *obj_group_order; 89 90/* 91 * Description of each monster group. 92 */ 93static struct 94{ 95 cptr chars; 96 cptr name; 97} monster_group[] = 98{ 99 { (cptr)-1, "Uniques" }, 100 { "A", "Angels" }, 101 { "a", "Ants" }, 102 { "b", "Bats" }, 103 { "B", "Birds" }, 104 { "C", "Canines" }, 105 { "c", "Centipedes" }, 106 { "uU", "Demons" }, 107 { "dD", "Dragons" }, 108 { "vE", "Elementals/Vortices" }, 109 { "e", "Eyes/Beholders" }, 110 { "f", "Felines" }, 111 { "G", "Ghosts" }, 112 { "OP", "Giants/Ogres" }, 113 { "g", "Golems" }, 114 { "H", "Harpies/Hybrids" }, 115 { "h", "Hominids (Elves, Dwarves)" }, 116 { "M", "Hydras" }, 117 { "i", "Icky Things" }, 118 { "lFI", "Insects" }, 119 { "j", "Jellies" }, 120 { "K", "Killer Beetles" }, 121 { "k", "Kobolds" }, 122 { "L", "Lichs" }, 123 { "tp", "Men" }, 124 { ".$!?=~_", "Mimics" }, 125 { "m", "Molds" }, 126 { ",", "Mushroom Patches" }, 127 { "n", "Nagas" }, 128 { "o", "Orcs" }, 129 { "q", "Quadrupeds" }, 130 { "Q", "Quylthulgs" }, 131 { "R", "Reptiles/Amphibians" }, 132 { "r", "Rodents" }, 133 { "S", "Scorpions/Spiders" }, 134 { "s", "Skeletons/Drujs" }, 135 { "J", "Snakes" }, 136 { "T", "Trolls" }, 137 { "V", "Vampires" }, 138 { "W", "Wights/Wraiths" }, 139 { "w", "Worms/Worm Masses" }, 140 { "X", "Xorns/Xarens" }, 141 { "y", "Yeeks" }, 142 { "Y", "Yeti" }, 143 { "Z", "Zephyr Hounds" }, 144 { "z", "Zombies" }, 145 { NULL, NULL } 146}; 147 148/* 149 * Description of each feature group. 150 */ 151const char *feature_group_text[] = 152{ 153 "Floors", 154 "Traps", 155 "Doors", 156 "Stairs", 157 "Walls", 158 "Streamers", 159 "Obstructions", 160 "Stores", 161 "Other", 162 NULL 163}; 164 165 166 167/* Useful method declarations */ 168static void display_visual_list(int col, int row, int height, int width, 169 byte attr_top, byte char_left); 170 171static bool visual_mode_command(ui_event_data ke, bool *visual_list_ptr, 172 int height, int width, 173 byte *attr_top_ptr, byte *char_left_ptr, 174 byte *cur_attr_ptr, byte *cur_char_ptr, 175 int col, int row, int *delay); 176 177static void place_visual_list_cursor(int col, int row, byte a, 178 byte c, byte attr_top, byte char_left); 179 180/* 181 * Clipboard variables for copy&paste in visual mode 182 */ 183static byte attr_idx = 0; 184static byte char_idx = 0; 185 186/* 187 * Return a specific ordering for the features 188 */ 189static int feat_order(int feat) 190{ 191 feature_type *f_ptr = &f_info[feat]; 192 193 switch (f_ptr->d_char) 194 { 195 case '.': return 0; 196 case '^': return 1; 197 case '\'': case '+': return 2; 198 case '<': case '>': return 3; 199 case '#': return 4; 200 case '*': case '%' : return 5; 201 case ';': case ':' : return 6; 202 203 default: 204 { 205 return 8; 206 } 207 } 208} 209 210 211/* Emit a 'graphical' symbol and a padding character if appropriate */ 212static void big_pad(int col, int row, byte a, byte c) 213{ 214 Term_putch(col, row, a, c); 215 if (!use_bigtile) return; 216 217 if (a & 0x80) 218 Term_putch(col + 1, row, 255, -1); 219 else 220 Term_putch(col + 1, row, 1, ' '); 221} 222 223/* Return the actual width of a symbol */ 224static int actual_width(int width) 225{ 226#ifdef UNANGBAND 227 if (use_trptile) width *= 3; 228 else if (use_dbltile) width *= 2; 229#endif 230 231 if (use_bigtile) width *= 2; 232 233 return width; 234} 235 236/* Return the actual height of a symbol */ 237static int actual_height(int height) 238{ 239#ifdef UNANGBAND 240 if (use_trptile) height = height * 3 / 2; 241 else if (use_dbltile) height *= 2; 242#endif 243 244 if (use_bigtile) height *= 2; 245 246 return height; 247} 248 249 250/* From an actual width, return the logical width */ 251static int logical_width(int width) 252{ 253 int divider = 1; 254 255#ifdef UNANGBAND 256 if (use_trptile) divider = 3; 257 else if (use_dbltile) divider = 2; 258#endif 259 260 if (use_bigtile) divider *= 2; 261 262 return width / divider; 263} 264 265/* From an actual height, return the logical height */ 266static int logical_height(int height) 267{ 268 int divider = 1; 269 270#ifdef UNANGBAND 271 if (use_trptile) 272 { 273 height *= 2; 274 divider = 3; 275 } 276 else if (use_dbltile) divider = 2; 277#endif 278 279 if (use_bigtile) divider *= 2; 280 281 return height / divider; 282} 283 284 285static void display_group_member(menu_type *menu, int oid, 286 bool cursor, int row, int col, int wid) 287{ 288 const member_funcs *o_funcs = menu->menu_data; 289 byte attr = curs_attrs[CURS_KNOWN][cursor == oid]; 290 291 (void)wid; 292 293 /* Print the interesting part */ 294 o_funcs->display_member(col, row, cursor, oid); 295 296#if 0 /* Debugging code */ 297 c_put_str(attr, format("%d", oid), row, 60); 298#endif 299 300 /* Do visual mode */ 301 if (o_funcs->is_visual && o_funcs->xattr) 302 { 303 byte c = *o_funcs->xchar(oid); 304 byte a = *o_funcs->xattr(oid); 305 306 c_put_str(attr, format((c & 0x80) ? "%02x/%02x" : "%02x/%d", a, c), row, 60); 307 } 308} 309 310static const char *recall_prompt(int oid) 311{ 312 (void)oid; 313 return ", 'r' to recall"; 314} 315 316#define swap(a, b) (swapspace = (void*)(a)), ((a) = (b)), ((b) = swapspace) 317 318/* 319 * Interactive group by. 320 * Recognises inscriptions, graphical symbols, lore 321 */ 322static void display_knowledge(const char *title, int *obj_list, int o_count, 323 group_funcs g_funcs, member_funcs o_funcs, 324 const char *otherfields) 325{ 326 /* maximum number of groups to display */ 327 int max_group = g_funcs.maxnum < o_count ? g_funcs.maxnum : o_count ; 328 329 /* This could (should?) be (void **) */ 330 int *g_list, *g_offset; 331 332 const char **g_names; 333 334 int g_name_len = 8; /* group name length, minumum is 8 */ 335 336 int grp_cnt = 0; /* total number groups */ 337 338 int g_cur = 0, grp_old = -1; /* group list positions */ 339 int o_cur = 0; /* object list positions */ 340 int g_o_count = 0; /* object count for group */ 341 int oid = -1; /* object identifiers */ 342 343 region title_area = { 0, 0, 0, 4 }; 344 region group_region = { 0, 6, MISSING, -2 }; 345 region object_region = { MISSING, 6, 0, -2 }; 346 347 /* display state variables */ 348 bool visual_list = FALSE; 349 byte attr_top = 0; 350 byte char_left = 0; 351 352 int delay = 0; 353 354 menu_type group_menu; 355 menu_type object_menu; 356 menu_iter object_iter; 357 358 /* Panel state */ 359 /* These are swapped in parallel whenever the actively browsing " */ 360 /* changes */ 361 int *active_cursor = &g_cur, *inactive_cursor = &o_cur; 362 menu_type *active_menu = &group_menu, *inactive_menu = &object_menu; 363 int panel = 0; 364 365 void *swapspace; 366 bool do_swap = FALSE; 367 368 bool flag = FALSE; 369 bool redraw = TRUE; 370 371 int browser_rows; 372 int wid, hgt; 373 int i; 374 int prev_g = -1; 375 376 int omode = OPT(rogue_like_commands); 377 378 379 /* Get size */ 380 Term_get_size(&wid, &hgt); 381 browser_rows = hgt - 8; 382 383 /* Disable the roguelike commands for the duration */ 384 OPT(rogue_like_commands) = FALSE; 385 386 387 388 /* Do the group by. ang_sort only works on (void **) */ 389 /* Maybe should make this a precondition? */ 390 if (g_funcs.gcomp) 391 qsort(obj_list, o_count, sizeof(*obj_list), g_funcs.gcomp); 392 393 394 /* Sort everything into group order */ 395 g_list = C_ZNEW(max_group + 1, int); 396 g_offset = C_ZNEW(max_group + 1, int); 397 398 for (i = 0; i < o_count; i++) 399 { 400 if (prev_g != g_funcs.group(obj_list[i])) 401 { 402 prev_g = g_funcs.group(obj_list[i]); 403 g_offset[grp_cnt] = i; 404 g_list[grp_cnt++] = prev_g; 405 } 406 } 407 408 g_offset[grp_cnt] = o_count; 409 g_list[grp_cnt] = -1; 410 411 412 /* The compact set of group names, in display order */ 413 g_names = C_ZNEW(grp_cnt, const char *); 414 415 for (i = 0; i < grp_cnt; i++) 416 { 417 int len; 418 g_names[i] = g_funcs.name(g_list[i]); 419 len = strlen(g_names[i]); 420 if (len > g_name_len) g_name_len = len; 421 } 422 423 /* Reasonable max group name len */ 424 if (g_name_len >= 20) g_name_len = 20; 425 426 object_region.col = g_name_len + 3; 427 group_region.width = g_name_len; 428 429 430 /* Leave room for the group summary information */ 431 if (g_funcs.summary) object_region.page_rows = -3; 432 433 434 /* Set up the two menus */ 435 WIPE(&group_menu, menu_type); 436 group_menu.count = grp_cnt; 437 group_menu.cmd_keys = "\n\r6\x8C"; /* Ignore these as menu commands */ 438 group_menu.menu_data = g_names; 439 440 WIPE(&object_menu, menu_type); 441 object_menu.menu_data = &o_funcs; 442 WIPE(&object_iter, object_iter); 443 object_iter.display_row = display_group_member; 444 445 o_funcs.is_visual = FALSE; 446 447 menu_init(&group_menu, MN_SKIN_SCROLL, find_menu_iter(MN_ITER_STRINGS), &group_region); 448 menu_init(&object_menu, MN_SKIN_SCROLL, &object_iter, &object_region); 449 450 451 /* This is the event loop for a multi-region panel */ 452 /* Panels are -- text panels, two menus, and visual browser */ 453 /* with "pop-up menu" for lore */ 454 while ((!flag) && (grp_cnt)) 455 { 456 ui_event_data ke, ke0; 457 458 if (redraw) 459 { 460 /* Print the title bits */ 461 region_erase(&title_area); 462 prt(format("Knowledge - %s", title), 2, 0); 463 prt("Group", 4, 0); 464 prt("Name", 4, g_name_len + 3); 465 466 if (otherfields) 467 prt(otherfields, 4, 46); 468 469 470 /* Print dividers: horizontal and vertical */ 471 for (i = 0; i < 79; i++) 472 Term_putch(i, 5, TERM_WHITE, '='); 473 474 for (i = 0; i < browser_rows; i++) 475 Term_putch(g_name_len + 1, 6 + i, TERM_WHITE, '|'); 476 477 478 /* Reset redraw flag */ 479 redraw = FALSE; 480 } 481 482 if (g_cur != grp_old) 483 { 484 grp_old = g_cur; 485 o_cur = 0; 486 g_o_count = g_offset[g_cur+1] - g_offset[g_cur]; 487 menu_set_filter(&object_menu, obj_list + g_offset[g_cur], g_o_count); 488 group_menu.cursor = g_cur; 489 object_menu.cursor = 0; 490 } 491 492 /* HACK ... */ 493 if (!visual_list) 494 { 495 /* ... The object menu may be browsing the entire group... */ 496 o_funcs.is_visual = FALSE; 497 menu_set_filter(&object_menu, obj_list + g_offset[g_cur], g_o_count); 498 object_menu.cursor = o_cur; 499 } 500 else 501 { 502 /* ... or just a single element in the group. */ 503 o_funcs.is_visual = TRUE; 504 menu_set_filter(&object_menu, obj_list + o_cur + g_offset[g_cur], 1); 505 object_menu.cursor = 0; 506 } 507 508 oid = obj_list[g_offset[g_cur]+o_cur]; 509 510 /* Print prompt */ 511 { 512 const char *pedit = (!o_funcs.xattr) ? "" : 513 (!(attr_idx|char_idx) ? ", 'c' to copy" : ", 'c', 'p' to paste"); 514 const char *xtra = o_funcs.xtra_prompt ? o_funcs.xtra_prompt(oid) : ""; 515 const char *pvs = ""; 516 517 if (visual_list) pvs = ", ENTER to accept"; 518 else if (o_funcs.xattr) pvs = ", 'v' for visuals"; 519 520 521 522 prt(format("<dir>%s%s%s, ESC", pvs, pedit, xtra), hgt - 1, 0); 523 } 524 525 if (do_swap) 526 { 527 do_swap = FALSE; 528 swap(active_menu, inactive_menu); 529 swap(active_cursor, inactive_cursor); 530 panel = 1 - panel; 531 } 532 533 if (g_funcs.summary && !visual_list) 534 { 535 g_funcs.summary(g_cur, obj_list, g_o_count, g_offset[g_cur], 536 object_menu.boundary.row + object_menu.boundary.page_rows, 537 object_region.col); 538 } 539 540 menu_refresh(inactive_menu); 541 menu_refresh(active_menu); 542 543 handle_stuff(); 544 545 if (visual_list) 546 { 547 display_visual_list(g_name_len + 3, 7, browser_rows-1, 548 wid - (g_name_len + 3), attr_top, char_left); 549 place_visual_list_cursor(g_name_len + 3, 7, *o_funcs.xattr(oid), 550 *o_funcs.xchar(oid), attr_top, char_left); 551 } 552 553 if (delay) 554 { 555 /* Force screen update */ 556 Term_fresh(); 557 558 /* Delay */ 559 Term_xtra(TERM_XTRA_DELAY, delay); 560 561 delay = 0; 562 } 563 564 ke = inkey_ex(); 565 566 /* Do visual mode command if needed */ 567 if (o_funcs.xattr && o_funcs.xchar && 568 visual_mode_command(ke, &visual_list, 569 browser_rows-1, wid - (g_name_len + 3), 570 &attr_top, &char_left, 571 o_funcs.xattr(oid), (byte*) 572 o_funcs.xchar(oid), 573 g_name_len + 3, 7, &delay)) 574 { 575 continue; 576 } 577 578 if (ke.type == EVT_MOUSE) 579 { 580 /* Change active panels */ 581 if (region_inside(&inactive_menu->boundary, &ke)) 582 { 583 swap(active_menu, inactive_menu); 584 swap(active_cursor, inactive_cursor); 585 panel = 1-panel; 586 } 587 } 588 589 ke0 = run_event_loop(&active_menu->target, &ke); 590 if (ke0.type != EVT_AGAIN) ke = ke0; 591 592 switch (ke.type) 593 { 594 case EVT_KBRD: 595 { 596 break; 597 } 598 599 case ESCAPE: 600 { 601 flag = TRUE; 602 continue; 603 } 604 605 case EVT_SELECT: 606 { 607 if (panel == 1 && oid >= 0 && o_cur == active_menu->cursor) 608 { 609 o_funcs.lore(oid); 610 redraw = TRUE; 611 } 612 } 613 614 case EVT_MOVE: 615 { 616 *active_cursor = active_menu->cursor; 617 continue; 618 } 619 620 case EVT_BACK: 621 { 622 if (panel == 1) 623 do_swap = TRUE; 624 } 625 626 /* XXX Handle EVT_RESIZE */ 627 628 default: 629 { 630 continue; 631 } 632 } 633 634 switch (ke.key) 635 { 636 case ESCAPE: 637 { 638 flag = TRUE; 639 break; 640 } 641 642 case 'R': 643 case 'r': 644 { 645 /* Recall on screen */ 646 if (oid >= 0) 647 o_funcs.lore(oid); 648 649 redraw = TRUE; 650 break; 651 } 652 653 /* Jump down a page */ 654 case '3': 655 { 656 *active_cursor += browser_rows; 657 658 if (g_cur >= grp_cnt) g_cur = grp_cnt - 1; 659 else if (o_cur >= g_o_count) o_cur = g_o_count - 1; 660 661 break; 662 } 663 664 /* Jump up a page */ 665 case '9': 666 { 667 *active_cursor -= browser_rows; 668 669 if (*active_cursor < 0) *active_cursor = 0; 670 671 break; 672 } 673 674 default: 675 { 676 int d = target_dir(ke.key); 677 678 /* Handle key-driven motion between panels */ 679 if (ddx[d] && ((ddx[d] < 0) == (panel == 1))) 680 { 681 /* Silly hack -- diagonal arithmetic */ 682 *inactive_cursor += ddy[d]; 683 if (*inactive_cursor < 0) *inactive_cursor = 0; 684 else if (g_cur >= grp_cnt) g_cur = grp_cnt - 1; 685 else if (o_cur >= g_o_count) o_cur = g_o_count - 1; 686 do_swap = TRUE; 687 } 688 else if (o_funcs.xtra_act) 689 { 690 o_funcs.xtra_act(ke.key, oid); 691 } 692 693 break; 694 } 695 } 696 } 697 698 /* Restore roguelike option */ 699 OPT(rogue_like_commands) = omode; 700 701 /* Prompt */ 702 if (!grp_cnt) 703 prt(format("No %s known.", title), 15, 0); 704 705 FREE(g_names); 706 FREE(g_offset); 707 FREE(g_list); 708} 709 710/* 711 * Display visuals. 712 */ 713static void display_visual_list(int col, int row, int height, int width, byte attr_top, byte char_left) 714{ 715 int i, j; 716 717 /* Clear the display lines */ 718 for (i = 0; i < height; i++) 719 Term_erase(col, row + i, width); 720 721 width = logical_width(width); 722 723 /* Display lines until done */ 724 for (i = 0; i < height; i++) 725 { 726 /* Display columns until done */ 727 for (j = 0; j < width; j++) 728 { 729 byte a; 730 unsigned char c; 731 int x = col + actual_width(j); 732 int y = row + actual_width(i); 733 int ia, ic; 734 735 ia = attr_top + i; 736 ic = char_left + j; 737 738 a = (byte)ia; 739 c = (unsigned char)ic; 740 741 /* Display symbol */ 742 big_pad(x, y, a, c); 743 } 744 } 745} 746 747 748/* 749 * Place the cursor at the collect position for visual mode 750 */ 751static void place_visual_list_cursor(int col, int row, byte a, byte c, byte attr_top, byte char_left) 752{ 753 int i = a - attr_top; 754 int j = c - char_left; 755 756 int x = col + actual_width(j); 757 int y = row + actual_height(i); 758 759 /* Place the cursor */ 760 Term_gotoxy(x, y); 761} 762 763 764/* 765 * Do visual mode command -- Change symbols 766 */ 767static bool visual_mode_command(ui_event_data ke, bool *visual_list_ptr, 768 int height, int width, 769 byte *attr_top_ptr, byte *char_left_ptr, 770 byte *cur_attr_ptr, byte *cur_char_ptr, 771 int col, int row, int *delay) 772{ 773 static byte attr_old = 0; 774 static char char_old = 0; 775 776 /* These are the distance we want to maintain between the 777 * cursor and borders. 778 */ 779 int frame_left = logical_width(10); 780 int frame_right = logical_width(10); 781 int frame_top = logical_height(4); 782 int frame_bottom = logical_height(4); 783 784 switch (ke.key) 785 { 786 case ESCAPE: 787 { 788 if (*visual_list_ptr) 789 { 790 /* Cancel change */ 791 *cur_attr_ptr = attr_old; 792 *cur_char_ptr = char_old; 793 *visual_list_ptr = FALSE; 794 795 return TRUE; 796 } 797 798 break; 799 } 800 801 case '\n': 802 case '\r': 803 { 804 if (*visual_list_ptr) 805 { 806 /* Accept change */ 807 *visual_list_ptr = FALSE; 808 return TRUE; 809 } 810 811 break; 812 } 813 814 case 'V': 815 case 'v': 816 { 817 if (!*visual_list_ptr) 818 { 819 *visual_list_ptr = TRUE; 820 821 *attr_top_ptr = (byte)MAX(0, (int)*cur_attr_ptr - frame_top); 822 *char_left_ptr = (char)MAX(0, (int)*cur_char_ptr - frame_left); 823 824 attr_old = *cur_attr_ptr; 825 char_old = *cur_char_ptr; 826 } 827 else 828 { 829 /* Cancel change */ 830 *cur_attr_ptr = attr_old; 831 *cur_char_ptr = char_old; 832 *visual_list_ptr = FALSE; 833 } 834 835 return TRUE; 836 } 837 838 case 'C': 839 case 'c': 840 { 841 /* Set the visual */ 842 attr_idx = *cur_attr_ptr; 843 char_idx = *cur_char_ptr; 844 845 return TRUE; 846 } 847 848 case 'P': 849 case 'p': 850 { 851 if (attr_idx) 852 { 853 /* Set the char */ 854 *cur_attr_ptr = attr_idx; 855 *attr_top_ptr = (byte)MAX(0, (int)*cur_attr_ptr - frame_top); 856 } 857 858 if (char_idx) 859 { 860 /* Set the char */ 861 *cur_char_ptr = char_idx; 862 *char_left_ptr = (char)MAX(0, (int)*cur_char_ptr - frame_left); 863 } 864 865 return TRUE; 866 } 867 868 default: 869 { 870 if (*visual_list_ptr) 871 { 872 int eff_width = actual_width(width); 873 int eff_height = actual_height(height); 874 int d = target_dir(ke.key); 875 byte a = *cur_attr_ptr; 876 byte c = *cur_char_ptr; 877 878 /* Get mouse movement */ 879 if (ke.key == '\xff') 880 { 881 int my = ke.mousey - row; 882 int mx = ke.mousex - col; 883 884 my = logical_height(my); 885 mx = logical_width(mx); 886 887 if ((my >= 0) && (my < eff_height) && (mx >= 0) && (mx < eff_width) 888 && ((ke.index) || (a != *attr_top_ptr + my) 889 || (c != *char_left_ptr + mx))) 890 { 891 /* Set the visual */ 892 *cur_attr_ptr = a = *attr_top_ptr + my; 893 *cur_char_ptr = c = *char_left_ptr + mx; 894 895 /* Move the frame */ 896 if (*char_left_ptr > MAX(0, (int)c - frame_left)) 897 (*char_left_ptr)--; 898 if (*char_left_ptr + eff_width <= MIN(255, (int)c + frame_right)) 899 (*char_left_ptr)++; 900 if (*attr_top_ptr > MAX(0, (int)a - frame_top)) 901 (*attr_top_ptr)--; 902 if (*attr_top_ptr + eff_height <= MIN(255, (int)a + frame_bottom)) 903 (*attr_top_ptr)++; 904 905 /* Delay */ 906 *delay = 100; 907 908 /* Accept change */ 909 if (ke.index) *visual_list_ptr = FALSE; 910 911 return TRUE; 912 } 913 914 /* Cancel change */ 915 else if (ke.index) 916 { 917 *cur_attr_ptr = attr_old; 918 *cur_char_ptr = char_old; 919 *visual_list_ptr = FALSE; 920 921 return TRUE; 922 } 923 } 924 else 925 { 926 /* Restrict direction */ 927 if ((a == 0) && (ddy[d] < 0)) d = 0; 928 if ((c == 0) && (ddx[d] < 0)) d = 0; 929 if ((a == 255) && (ddy[d] > 0)) d = 0; 930 if ((c == 255) && (ddx[d] > 0)) d = 0; 931 932 a += ddy[d]; 933 c += ddx[d]; 934 935 /* Set the visual */ 936 *cur_attr_ptr = a; 937 *cur_char_ptr = c; 938 939 /* Move the frame */ 940 if ((ddx[d] < 0) && *char_left_ptr > MAX(0, (int)c - frame_left)) 941 (*char_left_ptr)--; 942 if ((ddx[d] > 0) && *char_left_ptr + eff_width <= 943 MIN(255, (int)c + frame_right)) 944 (*char_left_ptr)++; 945 946 if ((ddy[d] < 0) && *attr_top_ptr > MAX(0, (int)a - frame_top)) 947 (*attr_top_ptr)--; 948 if ((ddy[d] > 0) && *attr_top_ptr + eff_height <= 949 MIN(255, (int)a + frame_bottom)) 950 (*attr_top_ptr)++; 951 952 /* We need to always eat the input even if it is clipped, 953 * otherwise it will be interpreted as a change object 954 * selection command with messy results. 955 */ 956 return TRUE; 957 } 958 } 959 } 960 } 961 962 /* Visual mode command is not used */ 963 return FALSE; 964} 965 966 967/* The following sections implement "subclasses" of the 968 * abstract classes represented by member_funcs and group_funcs 969 */ 970 971/* =================== MONSTERS ==================================== */ 972/* Many-to-many grouping - use default auxiliary join */ 973 974/* 975 * Display a monster 976 */ 977static void display_monster(int col, int row, bool cursor, int oid) 978{ 979 /* HACK Get the race index. (Should be a wrapper function) */ 980 int r_idx = default_join[oid].oid; 981 982 /* Access the race */ 983 monster_race *r_ptr = &r_info[r_idx]; 984 monster_lore *l_ptr = &l_list[r_idx]; 985 986 /* Choose colors */ 987 byte attr = curs_attrs[CURS_KNOWN][(int)cursor]; 988 byte a = r_ptr->x_attr; 989 byte c = r_ptr->x_char; 990 991 /* Display the name */ 992 c_prt(attr, r_name + r_ptr->name, row, col); 993 994#ifdef UNANGBAND 995 if (use_dbltile || use_trptile) 996 return; 997#endif 998 999 /* Display symbol */ 1000 big_pad(66, row, a, c); 1001 1002 /* Display kills */ 1003 if (rf_has(r_ptr->flags, RF_UNIQUE)) 1004 put_str(format("%s", (r_ptr->max_num == 0)? " dead" : "alive"), row, 70); 1005 else 1006 put_str(format("%5d", l_ptr->pkills), row, 70); 1007} 1008 1009 1010static int m_cmp_race(const void *a, const void *b) 1011{ 1012 const monster_race *r_a = &r_info[default_join[*(const int *)a].oid]; 1013 const monster_race *r_b = &r_info[default_join[*(const int *)b].oid]; 1014 int gid = default_join[*(const int *)a].gid; 1015 1016 /* Group by */ 1017 int c = gid - default_join[*(const int *)b].gid; 1018 if (c) return c; 1019 1020 /* Order results */ 1021 c = r_a->d_char - r_b->d_char; 1022 if (c && gid != 0) 1023 { 1024 /* UNIQUE group is ordered by level & name only */ 1025 /* Others by order they appear in the group symbols */ 1026 return strchr(monster_group[gid].chars, r_a->d_char) 1027 - strchr(monster_group[gid].chars, r_b->d_char); 1028 } 1029 c = r_a->level - r_b->level; 1030 if (c) return c; 1031 1032 return strcmp(r_name + r_a->name, r_name + r_b->name); 1033} 1034 1035static char *m_xchar(int oid) { return &r_info[default_join[oid].oid].x_char; } 1036static byte *m_xattr(int oid) { return &r_info[default_join[oid].oid].x_attr; } 1037static const char *race_name(int gid) { return monster_group[gid].name; } 1038 1039static void mon_lore(int oid) 1040{ 1041 /* Update the monster recall window */ 1042 monster_race_track(default_join[oid].oid); 1043 handle_stuff(); 1044 1045 /* Save the screen */ 1046 screen_save(); 1047 1048 /* Describe */ 1049 text_out_hook = text_out_to_screen; 1050 1051 /* Recall monster */ 1052 roff_top(default_join[oid].oid); 1053 Term_gotoxy(0, 2); 1054 describe_monster(default_join[oid].oid, FALSE); 1055 1056 text_out_c(TERM_L_BLUE, "\n[Press any key to continue]\n"); 1057 (void)anykey(); 1058 1059 /* Load the screen */ 1060 screen_load(); 1061} 1062 1063static void mon_summary(int gid, const int *object_list, int n, int top, int row, int col) 1064{ 1065 int i; 1066 int kills = 0; 1067 1068 /* Access the race */ 1069 for (i = 0; i < n; i++) 1070 { 1071 int oid = default_join[object_list[i+top]].oid; 1072 kills += l_list[oid].pkills; 1073 } 1074 1075 /* Different display for the first item if we've got uniques to show */ 1076 if (gid == 0 && rf_has((&r_info[default_join[object_list[0]].oid])->flags, RF_UNIQUE)) 1077 { 1078 c_prt(TERM_L_BLUE, format("%d known uniques, %d slain.", n, kills), 1079 row, col); 1080 } 1081 else 1082 { 1083 int tkills = 0; 1084 1085 for (i = 0; i < z_info->r_max; i++) 1086 tkills += l_list[i].pkills; 1087 1088 c_prt(TERM_L_BLUE, format("Creatures slain: %d/%d (in group/in total)", kills, tkills), row, col); 1089 } 1090} 1091 1092static int count_known_monsters(void) 1093{ 1094 int m_count = 0; 1095 int i; 1096 size_t j; 1097 1098 for (i = 0; i < z_info->r_max; i++) 1099 { 1100 monster_race *r_ptr = &r_info[i]; 1101 if (!OPT(cheat_know) && !l_list[i].sights) continue; 1102 if (!r_ptr->name) continue; 1103 1104 if (rf_has(r_ptr->flags, RF_UNIQUE)) m_count++; 1105 1106 for (j = 1; j < N_ELEMENTS(monster_group) - 1; j++) 1107 { 1108 const char *pat = monster_group[j].chars; 1109 if (strchr(pat, r_ptr->d_char)) m_count++; 1110 } 1111 } 1112 1113 return m_count; 1114} 1115 1116/* 1117 * Display known monsters. 1118 */ 1119static void do_cmd_knowledge_monsters(void *obj, const char *name) 1120{ 1121 group_funcs r_funcs = {N_ELEMENTS(monster_group), FALSE, race_name, 1122 m_cmp_race, default_group, mon_summary}; 1123 1124 member_funcs m_funcs = {display_monster, mon_lore, m_xchar, m_xattr, recall_prompt, 0, 0}; 1125 1126 int *monsters; 1127 int m_count = 0; 1128 int i; 1129 size_t j; 1130 1131 (void)obj; 1132 (void)name; 1133 1134 for (i = 0; i < z_info->r_max; i++) 1135 { 1136 monster_race *r_ptr = &r_info[i]; 1137 if (!OPT(cheat_know) && !l_list[i].sights) continue; 1138 if (!r_ptr->name) continue; 1139 1140 if (rf_has(r_ptr->flags, RF_UNIQUE)) m_count++; 1141 1142 for (j = 1; j < N_ELEMENTS(monster_group) - 1; j++) 1143 { 1144 const char *pat = monster_group[j].chars; 1145 if (strchr(pat, r_ptr->d_char)) m_count++; 1146 } 1147 } 1148 1149 default_join = C_ZNEW(m_count, join_t); 1150 monsters = C_ZNEW(m_count, int); 1151 1152 m_count = 0; 1153 for (i = 0; i < z_info->r_max; i++) 1154 { 1155 monster_race *r_ptr = &r_info[i]; 1156 if (!OPT(cheat_know) && !l_list[i].sights) continue; 1157 if (!r_ptr->name) continue; 1158 1159 for (j = 0; j < N_ELEMENTS(monster_group)-1; j++) 1160 { 1161 const char *pat = monster_group[j].chars; 1162 if (j == 0 && !rf_has(r_ptr->flags, RF_UNIQUE)) 1163 continue; 1164 else if (j > 0 && !strchr(pat, r_ptr->d_char)) 1165 continue; 1166 1167 monsters[m_count] = m_count; 1168 default_join[m_count].oid = i; 1169 default_join[m_count++].gid = j; 1170 } 1171 } 1172 1173 display_knowledge("monsters", monsters, m_count, r_funcs, m_funcs, 1174 " Sym Kills"); 1175 FREE(default_join); 1176 FREE(monsters); 1177} 1178 1179/* =================== ARTIFACTS ==================================== */ 1180/* Many-to-one grouping */ 1181 1182static void get_artifact_display_name(char *o_name, size_t namelen, int a_idx) 1183{ 1184 object_type object_type_body; 1185 object_type *o_ptr = &object_type_body; 1186 1187 /* Make fake artifact */ 1188 o_ptr = &object_type_body; 1189 object_wipe(o_ptr); 1190 make_fake_artifact(o_ptr, a_idx); 1191 1192 /* Get its name */ 1193 object_desc(o_name, namelen, o_ptr, ODESC_PREFIX | ODESC_BASE | ODESC_SPOIL); 1194} 1195 1196/* 1197 * Display an artifact label 1198 */ 1199static void display_artifact(int col, int row, bool cursor, int oid) 1200{ 1201 char o_name[80]; 1202 1203 /* Choose a color */ 1204 byte attr = curs_attrs[CURS_KNOWN][(int)cursor]; 1205 1206 get_artifact_display_name(o_name, sizeof o_name, oid); 1207 1208 /* Display the name */ 1209 c_prt(attr, o_name, row, col); 1210} 1211 1212/* 1213 * Show artifact lore 1214 */ 1215static void desc_art_fake(int a_idx) 1216{ 1217 object_type *o_ptr; 1218 object_type object_type_body; 1219 bool lost = TRUE, abil = FALSE; 1220 int i, j; 1221 oinfo_detail_t mode = OINFO_NONE; 1222 1223 /* Get local object */ 1224 o_ptr = &object_type_body; 1225 1226 /* Wipe the object */ 1227 object_wipe(o_ptr); 1228 1229 /* Look for the artifact, either in inventory, store or the object list */ 1230 for (i = 0; i < z_info->o_max; i++) 1231 { 1232 if (o_list[i].name1 == a_idx) 1233 { 1234 o_ptr = &o_list[i]; 1235 lost = FALSE; 1236 break; 1237 } 1238 } 1239 1240 if (lost) 1241 { 1242 for (i = 0; i < INVEN_TOTAL; i++) 1243 { 1244 if (inventory[i].name1 == a_idx) 1245 { 1246 o_ptr = &inventory[i]; 1247 lost = FALSE; 1248 break; 1249 } 1250 } 1251 } 1252 1253 if (lost) 1254 { 1255 for (j = 1; j < (FEAT_SHOP_TAIL - FEAT_SHOP_HEAD + 1); j++) 1256 { 1257 for (i = 0; i < store[j].stock_size; i++) 1258 { 1259 if (store[j].stock[i].name1 == a_idx) 1260 { 1261 o_ptr = &store[j].stock[i]; 1262 lost = FALSE; 1263 break; 1264 } 1265 } 1266 if (!lost) break; 1267 } 1268 } 1269 1270 /* If it's been lost, make a fake artifact for it */ 1271 if (lost) 1272 { 1273 make_fake_artifact(o_ptr, a_idx); 1274 o_ptr->ident |= IDENT_NAME; 1275 1276 /* Check the history entry, to see if it was fully known before it 1277 * was lost */ 1278 if (history_is_artifact_known(a_idx)) 1279 mode = OINFO_FULL; 1280 } 1281 1282 /* Hack -- Handle stuff */ 1283 handle_stuff(); 1284 1285 text_out_hook = text_out_to_screen; 1286 screen_save(); 1287 1288 /* Print the artifact information */ 1289 Term_gotoxy(0, 0); 1290 object_info_header(o_ptr); 1291 abil = object_info(o_ptr, mode); 1292 if (lost) text_out("\nThis artifact has been lost."); 1293 if (!abil) text_out("\n\nThis item does not seem to possess any special abilities."); 1294 1295 text_out_c(TERM_L_BLUE, "\n\n[Press any key to continue]\n"); 1296 (void)anykey(); 1297 1298 screen_load(); 1299} 1300 1301static int a_cmp_tval(const void *a, const void *b) 1302{ 1303 const artifact_type *a_a = &a_info[*(const int *)a]; 1304 const artifact_type *a_b = &a_info[*(const int *)b]; 1305 1306 /*group by */ 1307 int ta = obj_group_order[a_a->tval]; 1308 int tb = obj_group_order[a_b->tval]; 1309 int c = ta - tb; 1310 if (c) return c; 1311 1312 /* order by */ 1313 c = a_a->sval - a_b->sval; 1314 if (c) return c; 1315 return strcmp(a_name+a_a->name, a_name+a_b->name); 1316} 1317 1318static const char *kind_name(int gid) { return object_text_order[gid].name; } 1319static int art2gid(int oid) { return obj_group_order[a_info[oid].tval]; } 1320 1321/* Check if the given artifact idx is something we should "Know" about */ 1322static bool artifact_is_known(int a_idx) 1323{ 1324 int i; 1325 1326 if (p_ptr->wizard) return TRUE; 1327 1328 /* Artifact doesn't exist at all, or not created yet */ 1329 if (!a_info[a_idx].name || a_info[a_idx].created == FALSE) return FALSE; 1330 1331 /* Check all objects to see if it exists but hasn't been IDed */ 1332 for (i = 0; i < z_info->o_max; i++) 1333 { 1334 int a = o_list[i].name1; 1335 1336 /* If we haven't actually sensed the artifact yet */ 1337 if (a && a == a_idx && !object_is_known_artifact(&o_list[i])) 1338 { 1339 return FALSE; 1340 } 1341 } 1342 1343 /* Check inventory for the same */ 1344 for (i = 0; i < INVEN_TOTAL; i++) 1345 { 1346 object_type *o_ptr = &inventory[i]; 1347 1348 /* Ignore non-objects */ 1349 if (!o_ptr->k_idx) continue; 1350 1351 if (o_ptr->name1 && o_ptr->name1 == a_idx && 1352 !object_is_known_artifact(o_ptr)) 1353 { 1354 return FALSE; 1355 } 1356 } 1357 1358 return TRUE; 1359} 1360 1361 1362/* If 'artifacts' is NULL, it counts the number of known artifacts, otherwise 1363 it collects the list of known artifacts into 'artifacts' as well. */ 1364static int collect_known_artifacts(int *artifacts, size_t artifacts_len) 1365{ 1366 int a_count = 0; 1367 int j; 1368 1369 if (artifacts) 1370 assert(artifacts_len >= z_info->a_max); 1371 1372 for (j = 0; j < z_info->a_max; j++) 1373 { 1374 /* Artifact doesn't exist */ 1375 if (!a_info[j].name) continue; 1376 1377 if (OPT(cheat_xtra) || artifact_is_known(j)) 1378 { 1379 if (artifacts) 1380 artifacts[a_count++] = j; 1381 else 1382 a_count++; 1383 } 1384 } 1385 1386 return a_count; 1387} 1388 1389/* 1390 * Display known artifacts 1391 */ 1392static void do_cmd_knowledge_artifacts(void *obj, const char *name) 1393{ 1394 /* HACK -- should be TV_MAX */ 1395 group_funcs obj_f = {TV_GOLD, FALSE, kind_name, a_cmp_tval, art2gid, 0}; 1396 member_funcs art_f = {display_artifact, desc_art_fake, 0, 0, recall_prompt, 0, 0}; 1397 1398 int *artifacts; 1399 int a_count = 0; 1400 1401 (void)obj; 1402 (void)name; 1403 1404 artifacts = C_ZNEW(z_info->a_max, int); 1405 1406 /* Collect valid artifacts */ 1407 a_count = collect_known_artifacts(artifacts, z_info->a_max); 1408 1409 display_knowledge("artifacts", artifacts, a_count, obj_f, art_f, NULL); 1410 FREE(artifacts); 1411} 1412 1413/* =================== EGO ITEMS ==================================== */ 1414/* Many-to-many grouping (uses default join) */ 1415 1416/* static u16b *e_note(int oid) {return &e_info[default_join[oid].oid].note;} */ 1417static const char *ego_grp_name(int gid) { return object_text_order[gid].name; } 1418 1419static void display_ego_item(int col, int row, bool cursor, int oid) 1420{ 1421 /* HACK: Access the object */ 1422 ego_item_type *e_ptr = &e_info[default_join[oid].oid]; 1423 1424 /* Choose a color */ 1425 byte attr = curs_attrs[0 != (int)e_ptr->everseen][0 != (int)cursor]; 1426 1427 /* Display the name */ 1428 c_prt(attr, e_name + e_ptr->name, row, col); 1429} 1430 1431/* 1432 * Describe fake ego item "lore" 1433 */ 1434static void desc_ego_fake(int oid) 1435{ 1436 /* Hack: dereference the join */ 1437 const char *cursed[] = { "permanently cursed", "heavily cursed", "cursed" }; 1438 const char *xtra[] = { "sustain", "higher resistance", "ability" }; 1439 int i; 1440 1441 int e_idx = default_join[oid].oid; 1442 ego_item_type *e_ptr = &e_info[e_idx]; 1443 1444 object_type dummy; 1445 WIPE(&dummy, dummy); 1446 1447 /* Save screen */ 1448 screen_save(); 1449 1450 /* Set text_out hook */ 1451 text_out_hook = text_out_to_screen; 1452 1453 /* Dump the name */ 1454 c_prt(TERM_L_BLUE, format("%s %s", ego_grp_name(default_group(oid)), 1455 e_name + e_ptr->name), 0, 0); 1456 1457 /* Begin recall */ 1458 Term_gotoxy(0, 1); 1459 text_out("\n"); 1460 1461 if (e_ptr->text) 1462 { 1463 int x, y; 1464 text_out("%s", e_text + e_ptr->text); 1465 Term_locate(&x, &y); 1466 Term_gotoxy(0, y+1); 1467 } 1468 1469 /* List ego flags */ 1470 dummy.name2 = e_idx; 1471 dummy.tval = e_ptr->tval[0]; 1472 object_info(&dummy, OINFO_FULL | OINFO_DUMMY); 1473 1474 if (e_ptr->xtra) 1475 text_out(format("It provides one random %s.", xtra[e_ptr->xtra - 1])); 1476 1477 if (flags_test(e_ptr->flags, OF_SIZE, OF_CURSE_MASK, FLAG_END)) 1478 { 1479 if (of_has(e_ptr->flags, OF_PERMA_CURSE)) 1480 i = 0; 1481 else if (of_has(e_ptr->flags, OF_PERMA_CURSE)) 1482 i = 1; 1483 else 1484 i = 2; 1485 1486 text_out_c(TERM_RED, format("It is %s.", cursed[i])); 1487 } 1488 1489 text_out_c(TERM_L_BLUE, "\n\n[Press any key to continue]\n"); 1490 (void)anykey(); 1491 1492 screen_load(); 1493} 1494 1495/* TODO? Currently ego items will order by e_idx */ 1496static int e_cmp_tval(const void *a, const void *b) 1497{ 1498 const ego_item_type *ea = &e_info[default_join[*(const int *)a].oid]; 1499 const ego_item_type *eb = &e_info[default_join[*(const int *)b].oid]; 1500 1501 /* Group by */ 1502 int c = default_join[*(const int *)a].gid - default_join[*(const int *)b].gid; 1503 if (c) return c; 1504 1505 /* Order by */ 1506 return strcmp(e_name + ea->name, e_name + eb->name); 1507} 1508 1509/* 1510 * Display known ego_items 1511 */ 1512static void do_cmd_knowledge_ego_items(void *obj, const char *name) 1513{ 1514 group_funcs obj_f = 1515 {TV_GOLD, FALSE, ego_grp_name, e_cmp_tval, default_group, 0}; 1516 1517 member_funcs ego_f = {display_ego_item, desc_ego_fake, 0, 0, recall_prompt, 0, 0}; 1518 1519 int *egoitems; 1520 int e_count = 0; 1521 int i, j; 1522 1523 (void)obj; 1524 (void)name; 1525 1526 /* HACK: currently no more than 3 tvals for one ego type */ 1527 egoitems = C_ZNEW(z_info->e_max * EGO_TVALS_MAX, int); 1528 default_join = C_ZNEW(z_info->e_max * EGO_TVALS_MAX, join_t); 1529 1530 for (i = 0; i < z_info->e_max; i++) 1531 { 1532 if (e_info[i].everseen || OPT(cheat_xtra)) 1533 { 1534 for (j = 0; j < EGO_TVALS_MAX && e_info[i].tval[j]; j++) 1535 { 1536 int gid = obj_group_order[e_info[i].tval[j]]; 1537 1538 /* Ignore duplicate gids */ 1539 if (j > 0 && gid == default_join[e_count - 1].gid) continue; 1540 1541 egoitems[e_count] = e_count; 1542 default_join[e_count].oid = i; 1543 default_join[e_count++].gid = gid; 1544 } 1545 } 1546 } 1547 1548 display_knowledge("ego items", egoitems, e_count, obj_f, ego_f, NULL); 1549 1550 FREE(default_join); 1551 FREE(egoitems); 1552} 1553 1554/* =================== ORDINARY OBJECTS ==================================== */ 1555/* Many-to-one grouping */ 1556 1557/* 1558 * Looks up an artifact idx given an object_kind *that's already known 1559 * to be an artifact*. Behaviour is distinctly unfriendly if passed 1560 * flavours which don't correspond to an artifact. 1561 */ 1562static int get_artifact_from_kind(object_kind *k_ptr) 1563{ 1564 int i; 1565 1566 assert(of_has(k_ptr->flags, OF_INSTA_ART)); 1567 1568 /* Look for the corresponding artifact */ 1569 for (i = 0; i < z_info->a_max; i++) 1570 { 1571 if (k_ptr->tval == a_info[i].tval && 1572 k_ptr->sval == a_info[i].sval) 1573 { 1574 break; 1575 } 1576 } 1577 1578 assert(i < z_info->a_max); 1579 return i; 1580} 1581 1582/* 1583 * Display the objects in a group. 1584 */ 1585static void display_object(int col, int row, bool cursor, int oid) 1586{ 1587 int k_idx = oid; 1588 1589 object_kind *k_ptr = &k_info[k_idx]; 1590 const char *inscrip = get_autoinscription(oid); 1591 1592 char o_name[80]; 1593 1594 /* Choose a color */ 1595 bool aware = (!k_ptr->flavor || k_ptr->aware); 1596 byte attr = curs_attrs[(int)aware][(int)cursor]; 1597 1598 /* Find graphics bits -- versions of the object_char and object_attr defines */ 1599 bool use_flavour = (k_ptr->flavor) && !(aware && k_ptr->tval == TV_SCROLL); 1600 1601 byte a = use_flavour ? flavor_info[k_ptr->flavor].x_attr : k_ptr->x_attr; 1602 byte c = use_flavour ? flavor_info[k_ptr->flavor].x_char : k_ptr->x_char; 1603 1604 /* Display known artifacts differently */ 1605 if (of_has(k_ptr->flags, OF_INSTA_ART) && artifact_is_known(get_artifact_from_kind(k_ptr))) 1606 { 1607 get_artifact_display_name(o_name, sizeof(o_name), get_artifact_from_kind(k_ptr)); 1608 } 1609 else 1610 { 1611 object_kind_name(o_name, sizeof(o_name), k_idx, OPT(cheat_know)); 1612 } 1613 1614 /* If the type is "tried", display that */ 1615 if (k_ptr->tried && !aware) 1616 my_strcat(o_name, " {tried}", sizeof(o_name)); 1617 1618 /* Display the name */ 1619 c_prt(attr, o_name, row, col); 1620 1621 /* Show squelch status */ 1622 if ((aware && kind_is_squelched_aware(k_ptr)) || 1623 (!aware && kind_is_squelched_unaware(k_ptr))) 1624 c_put_str(attr, "Yes", row, 46); 1625 else if (aware && OPT(squelch_worthless) && !k_ptr->cost) 1626 c_put_str(attr, "Yes*", row, 46); 1627 1628 1629 /* Show autoinscription if around */ 1630 if (aware && inscrip) 1631 c_put_str(TERM_YELLOW, inscrip, row, 55); 1632 1633#ifdef UNANGBAND 1634 /* Hack - don't use if double tile */ 1635 if (use_dbltile || use_trptile) 1636 return; 1637#endif 1638 1639 /* Display symbol */ 1640 big_pad(76, row, a, c); 1641} 1642 1643/* 1644 * Describe fake object 1645 */ 1646static void desc_obj_fake(int k_idx) 1647{ 1648 object_kind *k_ptr = &k_info[k_idx]; 1649 object_type object_type_body; 1650 object_type *o_ptr = &object_type_body; 1651 1652 /* Check for known artifacts, display them as artifacts */ 1653 if (of_has(k_ptr->flags, OF_INSTA_ART) && artifact_is_known(get_artifact_from_kind(k_ptr))) 1654 { 1655 desc_art_fake(get_artifact_from_kind(k_ptr)); 1656 return; 1657 } 1658 1659 /* Update the object recall window */ 1660 track_object_kind(k_idx); 1661 handle_stuff(); 1662 1663 /* Wipe the object */ 1664 object_wipe(o_ptr); 1665 1666 /* Create the artifact */ 1667 object_prep(o_ptr, k_idx, 0, EXTREMIFY); 1668 1669 /* Hack -- its in the store */ 1670 if (k_info[k_idx].aware) o_ptr->ident |= (IDENT_STORE); 1671 1672 /* It's fully know */ 1673 if (!k_info[k_idx].flavor) object_notice_everything(o_ptr); 1674 1675 /* Hack -- Handle stuff */ 1676 handle_stuff(); 1677 1678 /* Describe */ 1679 text_out_hook = text_out_to_screen; 1680 screen_save(); 1681 1682 Term_gotoxy(0,0); 1683 object_info_header(o_ptr); 1684 if (!object_info(o_ptr, OINFO_NONE)) 1685 text_out("\n\nThis item does not seem to possess any special abilities."); 1686 1687 text_out_c(TERM_L_BLUE, "\n\n[Press any key to continue]\n"); 1688 (void)anykey(); 1689 1690 screen_load(); 1691} 1692 1693static int o_cmp_tval(const void *a, const void *b) 1694{ 1695 const object_kind *k_a = &k_info[*(const int *)a]; 1696 const object_kind *k_b = &k_info[*(const int *)b]; 1697 1698 /* Group by */ 1699 int ta = obj_group_order[k_a->tval]; 1700 int tb = obj_group_order[k_b->tval]; 1701 int c = ta - tb; 1702 if (c) return c; 1703 1704 /* Order by */ 1705 c = k_a->aware - k_b->aware; 1706 if (c) return -c; /* aware has low sort weight */ 1707 1708 switch (k_a->tval) 1709 { 1710 case TV_LIGHT: 1711 case TV_MAGIC_BOOK: 1712 case TV_PRAYER_BOOK: 1713 case TV_DRAG_ARMOR: 1714 /* leave sorted by sval */ 1715 break; 1716 1717 default: 1718 if (k_a->aware) 1719 return strcmp(k_name + k_a->name, k_name + k_b->name); 1720 1721 /* Then in tried order */ 1722 c = k_a->tried - k_b->tried; 1723 if (c) return -c; 1724 1725 return strcmp(flavor_text + flavor_info[k_a->flavor].text, 1726 flavor_text + flavor_info[k_b->flavor].text); 1727 } 1728 1729 return k_a->sval - k_b->sval; 1730} 1731 1732static int obj2gid(int oid) { return obj_group_order[k_info[oid].tval]; } 1733 1734static char *o_xchar(int oid) 1735{ 1736 object_kind *k_ptr = &k_info[oid]; 1737 1738 if (!k_ptr->flavor || k_ptr->aware) 1739 return &k_ptr->x_char; 1740 else 1741 return &flavor_info[k_ptr->flavor].x_char; 1742} 1743 1744static byte *o_xattr(int oid) 1745{ 1746 object_kind *k_ptr = &k_info[oid]; 1747 1748 if (!k_ptr->flavor || k_ptr->aware) 1749 return &k_ptr->x_attr; 1750 else 1751 return &flavor_info[k_ptr->flavor].x_attr; 1752} 1753 1754/* 1755 * Display special prompt for object inscription. 1756 */ 1757static const char *o_xtra_prompt(int oid) 1758{ 1759 object_kind *k_ptr = &k_info[oid]; 1760 s16b idx = get_autoinscription_index(oid); 1761 1762 const char *no_insc = ", 's' to toggle squelch, 'r'ecall, '{'"; 1763 const char *with_insc = ", 's' to toggle squelch, 'r'ecall, '{', '}'"; 1764 1765 1766 /* Forget it if we've never seen the thing */ 1767 if (k_ptr->flavor && !k_ptr->aware) 1768 return ""; 1769 1770 /* If it's already inscribed */ 1771 if (idx != -1) 1772 return with_insc; 1773 1774 return no_insc; 1775} 1776 1777/* 1778 * Special key actions for object inscription. 1779 */ 1780static void o_xtra_act(char ch, int oid) 1781{ 1782 object_kind *k_ptr = &k_info[oid]; 1783 s16b idx = get_autoinscription_index(oid); 1784 1785 /* Toggle squelch */ 1786 if (squelch_tval(k_ptr->tval) && (ch == 's' || ch == 'S')) 1787 { 1788 if (k_ptr->aware) 1789 { 1790 if (kind_is_squelched_aware(k_ptr)) 1791 kind_squelch_clear(k_ptr); 1792 else 1793 kind_squelch_when_aware(k_ptr); 1794 } 1795 else 1796 { 1797 if (kind_is_squelched_unaware(k_ptr)) 1798 kind_squelch_clear(k_ptr); 1799 else 1800 kind_squelch_when_unaware(k_ptr); 1801 } 1802 1803 return; 1804 } 1805 1806 /* Forget it if we've never seen the thing */ 1807 if (k_ptr->flavor && !k_ptr->aware) 1808 return; 1809 1810 /* Uninscribe */ 1811 if (ch == '}') 1812 { 1813 if (idx != -1) remove_autoinscription(oid); 1814 return; 1815 } 1816 1817 /* Inscribe */ 1818 else if (ch == '{') 1819 { 1820 char note_text[80] = ""; 1821 1822 /* Avoid the prompt getting in the way */ 1823 screen_save(); 1824 1825 /* Prompt */ 1826 prt("Inscribe with: ", 0, 0); 1827 1828 /* Default note */ 1829 if (idx != -1) 1830 strnfmt(note_text, sizeof(note_text), "%s", get_autoinscription(oid)); 1831 1832 /* Get an inscription */ 1833 if (askfor_aux(note_text, sizeof(note_text), NULL)) 1834 { 1835 /* Remove old inscription if existent */ 1836 if (idx != -1) 1837 remove_autoinscription(oid); 1838 1839 /* Add the autoinscription */ 1840 add_autoinscription(oid, note_text); 1841 1842 /* Notice stuff (later) */ 1843 p_ptr->notice |= (PN_AUTOINSCRIBE); 1844 p_ptr->redraw |= (PR_INVEN | PR_EQUIP); 1845 } 1846 1847 /* Reload the screen */ 1848 screen_load(); 1849 } 1850} 1851 1852 1853 1854/* 1855 * Display known objects 1856 */ 1857void do_cmd_knowledge_objects(void *obj, const char *name) 1858{ 1859 group_funcs kind_f = {TV_GOLD, FALSE, kind_name, o_cmp_tval, obj2gid, 0}; 1860 member_funcs obj_f = {display_object, desc_obj_fake, o_xchar, o_xattr, o_xtra_prompt, o_xtra_act, 0}; 1861 1862 int *objects; 1863 int o_count = 0; 1864 int i; 1865 1866 (void)obj; 1867 (void)name; 1868 1869 objects = C_ZNEW(z_info->k_max, int); 1870 1871 for (i = 0; i < z_info->k_max; i++) 1872 { 1873 if ((k_info[i].everseen || k_info[i].flavor || OPT(cheat_xtra)) && 1874 !of_has(k_info[i].flags, OF_INSTA_ART)) 1875 { 1876 int c = obj_group_order[k_info[i].tval]; 1877 if (c >= 0) objects[o_count++] = i; 1878 } 1879 } 1880 1881 display_knowledge("known objects", objects, o_count, kind_f, obj_f, "Squelch Inscribed Sym"); 1882 1883 FREE(objects); 1884} 1885 1886/* =================== TERRAIN FEATURES ==================================== */ 1887/* Many-to-one grouping */ 1888 1889/* 1890 * Display the features in a group. 1891 */ 1892static void display_feature(int col, int row, bool cursor, int oid ) 1893{ 1894 /* Get the feature index */ 1895 int f_idx = oid; 1896 1897 /* Access the feature */ 1898 feature_type *f_ptr = &f_info[f_idx]; 1899 1900 /* Choose a color */ 1901 byte attr = curs_attrs[CURS_KNOWN][(int)cursor]; 1902 1903 /* Display the name */ 1904 c_prt(attr, f_name + f_ptr->name, row, col); 1905 1906#ifdef UNANGBAND 1907 if (use_dbltile || use_trptile) return; 1908#endif 1909 1910 /* Display symbol */ 1911 big_pad(68, row, f_ptr->x_attr, f_ptr->x_char); 1912 1913 /* ILLUMINATION AND DARKNESS GO HERE */ 1914 1915} 1916 1917 1918static int f_cmp_fkind(const void *a, const void *b) 1919{ 1920 const feature_type *fa = &f_info[*(const int *)a]; 1921 const feature_type *fb = &f_info[*(const int *)b]; 1922 1923 /* group by */ 1924 int c = feat_order(*(const int *)a) - feat_order(*(const int *)b); 1925 if (c) return c; 1926 1927 /* order by feature name */ 1928 return strcmp(f_name + fa->name, f_name + fb->name); 1929} 1930 1931static const char *fkind_name(int gid) { return feature_group_text[gid]; } 1932static byte *f_xattr(int oid) { return &f_info[oid].x_attr; } 1933static char *f_xchar(int oid) { return &f_info[oid].x_char; } 1934static void feat_lore(int oid) { (void)oid; /* noop */ } 1935 1936/* 1937 * Interact with feature visuals. 1938 */ 1939static void do_cmd_knowledge_features(void *obj, const char *name) 1940{ 1941 group_funcs fkind_f = {N_ELEMENTS(feature_group_text), FALSE, 1942 fkind_name, f_cmp_fkind, feat_order, 0}; 1943 1944 member_funcs feat_f = {display_feature, feat_lore, f_xchar, f_xattr, 0, 0, 0}; 1945 1946 int *features; 1947 int f_count = 0; 1948 int i; 1949 1950 (void)obj; 1951 (void)name; 1952 1953 features = C_ZNEW(z_info->f_max, int); 1954 1955 for (i = 0; i < z_info->f_max; i++) 1956 { 1957 /* Ignore non-features and mimics */ 1958 if (f_info[i].name == 0 || f_info[i].mimic != i) 1959 continue; 1960 1961 features[f_count++] = i; /* Currently no filter for features */ 1962 } 1963 1964 display_knowledge("features", features, f_count, fkind_f, feat_f, 1965 " Sym"); 1966 FREE(features); 1967} 1968 1969 1970/* =================== END JOIN DEFINITIONS ================================ */ 1971 1972static void do_cmd_knowledge_store(void *obj, const char *name) 1973{ 1974 (void)name; 1975 store_knowledge = (int)obj; 1976 do_cmd_store_knowledge(); 1977 store_knowledge = STORE_NONE; 1978} 1979 1980static void do_cmd_knowledge_scores(void *obj, const char *name) 1981{ 1982 (void)obj; 1983 (void)name; 1984 show_scores(); 1985} 1986 1987static void do_cmd_knowledge_history(void *obj, const char *name) 1988{ 1989 (void)obj; 1990 (void)name; 1991 history_display(); 1992} 1993 1994 1995 1996/* 1997 * Definition of the "player knowledge" menu. 1998 */ 1999static menu_item knowledge_actions[] = 2000{ 2001{ {0, "Display object knowledge", do_cmd_knowledge_objects, 0}, 'a', 0 }, 2002{ {0, "Display artifact knowledge", do_cmd_knowledge_artifacts, 0}, 'b', 0 }, 2003{ {0, "Display ego item knowledge", do_cmd_knowledge_ego_items, 0}, 'c', 0 }, 2004{ {0, "Display monster knowledge", do_cmd_knowledge_monsters, 0}, 'd', 0 }, 2005{ {0, "Display feature knowledge", do_cmd_knowledge_features, 0}, 'e', 0 }, 2006{ {0, "Display contents of general store", do_cmd_knowledge_store, 2007 (void*)STORE_GENERAL}, 'f', 0 }, 2008{ {0, "Display contents of armourer", do_cmd_knowledge_store, 2009 (void*)STORE_ARMOR}, 'g', 0 }, 2010{ {0, "Display contents of weaponsmith", do_cmd_knowledge_store, 2011 (void*)STORE_WEAPON}, 'h', 0 }, 2012{ {0, "Display contents of temple", do_cmd_knowledge_store, 2013 (void*)STORE_TEMPLE}, 'i', 0 }, 2014{ {0, "Display contents of alchemist", do_cmd_knowledge_store, 2015 (void*)STORE_ALCHEMY}, 'j', 0 }, 2016{ {0, "Display contents of magic shop", do_cmd_knowledge_store, 2017 (void*)STORE_MAGIC}, 'k', 0 }, 2018{ {0, "Display contents of black market", do_cmd_knowledge_store, 2019 (void*)STORE_B_MARKET}, 'l', 0 }, 2020{ {0, "Display contents of home", do_cmd_knowledge_store, 2021 (void*)STORE_HOME}, 'm', 0 }, 2022{ {0, "Display hall of fame", do_cmd_knowledge_scores, 0}, 'n', 0 }, 2023{ {0, "Display character history", do_cmd_knowledge_history, 0}, 'o', 0 }, 2024}; 2025 2026static menu_type knowledge_menu; 2027 2028 2029 2030 2031 2032 2033/* Keep macro counts happy. */ 2034static void cleanup_cmds(void) 2035{ 2036 FREE(obj_group_order); 2037} 2038 2039void init_cmd_know(void) 2040{ 2041 /* Initialize the menus */ 2042 menu_type *menu = &knowledge_menu; 2043 WIPE(menu, menu_type); 2044 menu->title = "Display current knowledge"; 2045 menu->menu_data = knowledge_actions; 2046 menu->count = N_ELEMENTS(knowledge_actions), 2047 menu_init(menu, MN_SKIN_SCROLL, find_menu_iter(MN_ITER_ITEMS), &SCREEN_REGION); 2048 2049 /* initialize other static variables */ 2050 if (!obj_group_order) 2051 { 2052 int i; 2053 int gid = -1; 2054 2055 obj_group_order = C_ZNEW(TV_GOLD + 1, int); 2056 atexit(cleanup_cmds); 2057 2058 /* Allow for missing values */ 2059 for (i = 0; i <= TV_GOLD; i++) 2060 obj_group_order[i] = -1; 2061 2062 for (i = 0; 0 != object_text_order[i].tval; i++) 2063 { 2064 if (object_text_order[i].name) gid = i; 2065 obj_group_order[object_text_order[i].tval] = gid; 2066 } 2067 } 2068} 2069 2070 2071 2072 2073/* 2074 * Display the "player knowledge" menu. 2075 */ 2076void do_cmd_knowledge(void) 2077{ 2078 int cursor = 0; 2079 int i; 2080 ui_event_data c = EVENT_EMPTY; 2081 region knowledge_region = { 0, 0, -1, 18 }; 2082 2083 /* Grey out menu items that won't display anything */ 2084 if (collect_known_artifacts(NULL, 0) > 0) 2085 knowledge_actions[1].flags = 0; 2086 else 2087 knowledge_actions[1].flags = MN_GREYED; 2088 2089 knowledge_actions[2].flags = MN_GREYED; 2090 for (i = 0; i < z_info->e_max; i++) 2091 { 2092 if (e_info[i].everseen || OPT(cheat_xtra)) 2093 { 2094 knowledge_actions[2].flags = 0; 2095 break; 2096 } 2097 } 2098 2099 if (count_known_monsters() > 0) 2100 knowledge_actions[3].flags = 0; 2101 else 2102 knowledge_actions[3].flags = MN_GREYED; 2103 2104 screen_save(); 2105 menu_layout(&knowledge_menu, &knowledge_region); 2106 2107 while (c.key != ESCAPE) 2108 { 2109 clear_from(0); 2110 c = menu_select(&knowledge_menu, &cursor, 0); 2111 } 2112 2113 screen_load(); 2114} 2115 2116