PageRenderTime 55ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/src/ui-knowledge.c

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