PageRenderTime 76ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/src/cmd-know.c

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