PageRenderTime 57ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/src/obj-ui.c

https://github.com/NickMcConnell/Beleriand
C | 1532 lines | 899 code | 281 blank | 352 comment | 278 complexity | a51924cf5e7205c6d32cf22a4322ff06 MD5 | raw file
  1. /*
  2. * File: obj-ui.c
  3. * Purpose: Mainly object descriptions and generic UI functions
  4. *
  5. * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
  6. *
  7. * This work is free software; you can redistribute it and/or modify it
  8. * under the terms of either:
  9. *
  10. * a) the GNU General Public License as published by the Free Software
  11. * Foundation, version 2, or
  12. *
  13. * b) the "Angband licence":
  14. * This software may be copied and distributed for educational, research,
  15. * and not for profit purposes provided that this copyright and statement
  16. * are included in all such copies. Other copyrights may also apply.
  17. */
  18. #include "angband.h"
  19. #include "button.h"
  20. #include "cave.h"
  21. #include "cmds.h"
  22. #include "keymap.h"
  23. #include "target.h"
  24. #include "tvalsval.h"
  25. #include "ui-menu.h"
  26. /* Variables for item display and selection */
  27. struct object_menu_data {
  28. char label[80];
  29. char key;
  30. object_type *object;
  31. int index;
  32. };
  33. const char *prompt;
  34. int i1, i2;
  35. int e1, e2;
  36. int f1, f2;
  37. int floor_list[MAX_FLOOR_STACK];
  38. struct object_menu_data items[50];
  39. int num_obj;
  40. int item_mode;
  41. cmd_code item_cmd;
  42. static int offset = 0;
  43. static bool need_spacer;
  44. static char selection;
  45. static bool show_list;
  46. static olist_detail_t olist_mode = 0;
  47. /*
  48. * Display an object. Each object may be prefixed with a label.
  49. * Used by show_inven(), show_equip(), and show_floor(). Mode flags are
  50. * documented in object.h
  51. */
  52. static void show_obj(int onum, size_t max_len, char label[80],
  53. const object_type * object, bool cursor,
  54. olist_detail_t mode)
  55. {
  56. int row = 0, col = 0;
  57. int ex_width = 0, ex_offset, ex_offset_ctr;
  58. object_type *o_ptr = (object_type *) object;
  59. char o_name[160];
  60. char tmp_val[80];
  61. bool in_term;
  62. byte attr = proc_list_color_hack(o_ptr);
  63. /* Highlight */
  64. if (cursor)
  65. attr = get_color(attr, ATTR_HIGH, 1);
  66. in_term = (mode & OLIST_WINDOW) ? TRUE : FALSE;
  67. /* Object name */
  68. object_desc(o_name, sizeof(o_name), o_ptr, ODESC_PREFIX | ODESC_FULL);
  69. /* Width of extra fields */
  70. if (mode & OLIST_WEIGHT)
  71. ex_width += 9;
  72. if (mode & OLIST_PRICE)
  73. ex_width += 9;
  74. if (mode & OLIST_FAIL)
  75. ex_width += 10;
  76. /* Determine beginning row and column */
  77. if (in_term) {
  78. /* Term window */
  79. row = offset;
  80. col = 0;
  81. } else {
  82. /* Main window */
  83. row = 1;
  84. col = Term->wid - 3 - max_len - ex_width;
  85. col = MIN(col, COL_MAP + tile_width);
  86. if (col < 3)
  87. col = 0;
  88. }
  89. /* Column offset of the first extra field */
  90. ex_offset = MIN(max_len, (size_t) (Term->wid - 1 - ex_width - col));
  91. /* Clear the line */
  92. prt("", row + onum, MAX(col - 2, 0));
  93. /* Print the label */
  94. put_str(label, row + onum, col);
  95. /* Print the object */
  96. if (o_ptr != NULL) {
  97. /* Limit object name */
  98. if (strlen(label) + strlen(o_name) > (size_t) ex_offset) {
  99. int truncate = ex_offset - strlen(label);
  100. if (truncate < 0)
  101. truncate = 0;
  102. if ((size_t) truncate > sizeof(o_name) - 1)
  103. truncate = sizeof(o_name) - 1;
  104. o_name[truncate] = '\0';
  105. }
  106. /* Object name */
  107. c_put_str(attr, o_name, row + onum, col + strlen(label));
  108. /* Extra fields */
  109. ex_offset_ctr = ex_offset;
  110. if (mode & OLIST_FAIL) {
  111. int fail = (9 + get_use_device_chance(o_ptr)) / 10;
  112. if (object_aware_p(o_ptr))
  113. strnfmt(tmp_val, sizeof(tmp_val), "%4d%% fail", fail);
  114. else
  115. my_strcpy(tmp_val, " ? fail", sizeof(tmp_val));
  116. put_str(tmp_val, row + onum, col + ex_offset_ctr);
  117. ex_offset_ctr += 10;
  118. }
  119. if (mode & OLIST_WEIGHT) {
  120. int weight = o_ptr->weight * o_ptr->number;
  121. strnfmt(tmp_val, sizeof(tmp_val), "%4d.%1d lb", weight / 10,
  122. weight % 10);
  123. put_str(tmp_val, row + onum, col + ex_offset_ctr);
  124. ex_offset_ctr += 9;
  125. }
  126. }
  127. }
  128. /*
  129. * Build the object list. Note that only the equipment has first non-zero.
  130. */
  131. static void build_obj_list(int first, int last, const int *floor_list,
  132. olist_detail_t mode)
  133. {
  134. int i;
  135. object_type *o_ptr;
  136. bool in_term = (mode & OLIST_WINDOW) ? TRUE : FALSE;
  137. need_spacer = FALSE;
  138. offset = 0;
  139. num_obj = 0;
  140. /* Clear the existing contents */
  141. for (i = 0; i < 50; i++) {
  142. items[i].object = NULL;
  143. items[i].index = 0;
  144. items[i].key = '\0';
  145. my_strcpy(items[i].label, "", sizeof(items[i].label));
  146. }
  147. /* Leave top line clear for inventory subwindow */
  148. if (!first && !floor_list && in_term)
  149. offset = 1;
  150. for (i = first; i <= last; i++) {
  151. if (floor_list)
  152. o_ptr = &o_list[floor_list[i]];
  153. else
  154. o_ptr = &p_ptr->inventory[i];
  155. /* May need a blank line to separate equipment and quiver */
  156. if ((i == INVEN_TOTAL) && (first)) {
  157. int j;
  158. /* Scan the rest of the items for acceptable entries */
  159. for (j = i; j <= last; j++) {
  160. o_ptr = &p_ptr->inventory[j];
  161. if (item_tester_okay(o_ptr) || in_term)
  162. need_spacer = TRUE;
  163. }
  164. continue;
  165. }
  166. /* Tester always skips gold. When gold should be displayed,
  167. * only test items that are not gold.
  168. */
  169. if (((o_ptr->tval == TV_GOLD) && (mode & OLIST_GOLD)) ||
  170. item_tester_okay(o_ptr))
  171. strnfmt(items[num_obj].label, sizeof(items[num_obj].label),
  172. "%c) ", index_to_label(i));
  173. /* Unacceptable carried items are still displayed in term windows */
  174. else if ((in_term) && (!floor_list))
  175. my_strcpy(items[num_obj].label, " ",
  176. sizeof(items[num_obj].label));
  177. /* Unacceptable items are skipped in the main window */
  178. else
  179. continue;
  180. /* Special labels for equipment */
  181. if (first) {
  182. char tmp_val[80];
  183. /* Show full slot labels */
  184. if (OPT(show_labels)) {
  185. strnfmt(tmp_val, sizeof(tmp_val), "%-14s: ",
  186. mention_use(i));
  187. my_strcat(items[num_obj].label, tmp_val,
  188. sizeof(items[num_obj].label));
  189. }
  190. /* Otherwise only show short quiver labels */
  191. else if (i >= QUIVER_START) {
  192. strnfmt(tmp_val, sizeof(tmp_val), "[f%d]: ",
  193. i - QUIVER_START);
  194. my_strcat(items[num_obj].label, tmp_val,
  195. sizeof(items[num_obj].label));
  196. }
  197. }
  198. /* Save the object */
  199. items[num_obj].object = o_ptr;
  200. items[num_obj].index = i;
  201. items[num_obj].key = (items[num_obj].label)[0];
  202. num_obj++;
  203. }
  204. }
  205. /* Get the maximum object name length. Only makes sense after building the
  206. * object list
  207. */
  208. static void get_max_len(size_t * max_len)
  209. {
  210. int i;
  211. size_t max = 0;
  212. char o_name[160];
  213. object_type *o_ptr;
  214. /* Calculate name offset and max name length */
  215. for (i = 0; i < num_obj; i++) {
  216. o_ptr = items[i].object;
  217. /* Null objects are used to skip lines, or display only a label */
  218. if (o_ptr == NULL)
  219. continue;
  220. /* Max length of label + object name */
  221. object_desc(o_name, sizeof(o_name), o_ptr,
  222. ODESC_PREFIX | ODESC_FULL);
  223. max = MAX(max, strlen(items[i].label) + strlen(o_name));
  224. }
  225. /* Enforce external maximum */
  226. *max_len = MIN(*max_len, max);
  227. }
  228. /*
  229. * Display a list of objects. Each object may be prefixed with a label.
  230. * Used by show_inven(), show_equip(), and show_floor(). Mode flags are
  231. * documented in object.h
  232. */
  233. static void show_obj_list(int num_obj, u32b display, olist_detail_t mode)
  234. {
  235. int i, row = 0, col = 0, sp = 0;
  236. size_t max_len = Term->wid - 1;
  237. int ex_width = 0;
  238. object_type *o_ptr;
  239. char tmp_val[80];
  240. bool in_term;
  241. in_term = (mode & OLIST_WINDOW) ? TRUE : FALSE;
  242. get_max_len(&max_len);
  243. /* Check for window size restrictions */
  244. if (in_term) {
  245. /* Scan windows */
  246. for (i = 0; i < ANGBAND_TERM_MAX; i++) {
  247. /* Unused */
  248. if (!angband_term[i])
  249. continue;
  250. /* Count windows displaying inven */
  251. if (op_ptr->window_flag[i] & display)
  252. max_len = MIN((int) max_len, angband_term[i]->wid);
  253. }
  254. }
  255. /* Width of extra fields */
  256. if (mode & OLIST_WEIGHT)
  257. ex_width += 9;
  258. if (mode & OLIST_PRICE)
  259. ex_width += 9;
  260. if (mode & OLIST_FAIL)
  261. ex_width += 10;
  262. /* Determine beginning row and column */
  263. if (in_term) {
  264. /* Term window */
  265. row = 0;
  266. col = 0;
  267. } else {
  268. /* Main window */
  269. row = 1;
  270. col = Term->wid - 1 - max_len - ex_width;
  271. col = MIN(col, 20);
  272. if (col < 3)
  273. col = 0;
  274. }
  275. /* Output the list */
  276. for (i = 0; i < num_obj; i++) {
  277. o_ptr = items[i].object;
  278. /* Display each line */
  279. show_obj(i + sp, max_len, items[i].label, o_ptr, FALSE, mode);
  280. if ((i == (INVEN_FEET - INVEN_WIELD)) && need_spacer) {
  281. sp = 1;
  282. prt("", i + sp + 1, MAX(col - 2, 0));
  283. }
  284. }
  285. /* For the inventory: print the quiver count */
  286. if (mode & OLIST_QUIVER) {
  287. int count, j;
  288. /* Adjust for subwindow */
  289. int skip = (in_term) ? 1 : 0;
  290. /* Quiver may take multiple lines */
  291. for (j = 0; j < p_ptr->quiver_slots; j++, i++) {
  292. /* Number of missiles in this "slot" */
  293. if (j == p_ptr->quiver_slots - 1
  294. && p_ptr->quiver_remainder > 0)
  295. count = p_ptr->quiver_remainder;
  296. else
  297. count = 99;
  298. /* Clear the line */
  299. prt("", row + i + skip, MAX(col - 2, 0));
  300. /* Print the (disabled) label */
  301. strnfmt(tmp_val, sizeof(tmp_val), "%c) ", index_to_label(i));
  302. c_put_str(TERM_SLATE, tmp_val, row + i + skip, col);
  303. /* Print the count */
  304. strnfmt(tmp_val, sizeof(tmp_val), "in Quiver: %d missile%s",
  305. count, count == 1 ? "" : "s");
  306. c_put_str(TERM_L_UMBER, tmp_val, row + i + skip, col + 3);
  307. }
  308. }
  309. /* Clear term windows */
  310. if (in_term) {
  311. for (; i < Term->hgt; i++) {
  312. prt("", row + i + offset + sp, MAX(col - 2, 0));
  313. }
  314. }
  315. /* Print a drop shadow for the main window if necessary */
  316. else if (i > 0 && row + i < 24) {
  317. prt("", row + i + sp, MAX(col - 2, 0));
  318. }
  319. }
  320. /*
  321. * Display the inventory. Builds a list of objects and passes them
  322. * off to show_obj_list() for display. Mode flags documented in
  323. * object.h
  324. */
  325. void show_inven(olist_detail_t mode)
  326. {
  327. int i, last_slot = 0;
  328. int diff = weight_remaining();
  329. char header[80];
  330. object_type *o_ptr;
  331. bool in_term = (mode & OLIST_WINDOW) ? TRUE : FALSE;
  332. /* Include burden for term windows */
  333. if (in_term) {
  334. strnfmt(header, sizeof(header),
  335. "Burden %d.%d lb (%d.%d lb %s) ", p_ptr->total_weight / 10,
  336. p_ptr->total_weight % 10, abs(diff) / 10, abs(diff) % 10,
  337. (diff < 0 ? "overweight" : "remaining"));
  338. put_str(header, 0, 0);
  339. }
  340. /* Find the last occupied inventory slot */
  341. for (i = 0; i < INVEN_PACK; i++) {
  342. o_ptr = &p_ptr->inventory[i];
  343. if (o_ptr->k_idx)
  344. last_slot = i;
  345. }
  346. /* Build the object list */
  347. build_obj_list(0, last_slot, NULL, mode);
  348. /* Display the object list */
  349. show_obj_list(num_obj, PW_INVEN, mode);
  350. }
  351. /*
  352. * Display the equipment. Builds a list of objects and passes them
  353. * off to show_obj_list() for display. Mode flags documented in
  354. * object.h
  355. */
  356. void show_equip(olist_detail_t mode)
  357. {
  358. int i, last_slot = 0;
  359. object_type *o_ptr;
  360. /* Find the last equipment slot to display */
  361. for (i = INVEN_WIELD; i < ALL_INVEN_TOTAL; i++) {
  362. o_ptr = &p_ptr->inventory[i];
  363. if (i < ALL_INVEN_TOTAL && o_ptr->k_idx)
  364. last_slot = i;
  365. }
  366. /* Build the object list */
  367. build_obj_list(INVEN_WIELD, last_slot, NULL, mode);
  368. /* Display the object list */
  369. show_obj_list(num_obj, PW_EQUIP, mode);
  370. }
  371. /*
  372. * Display the floor. Builds a list of objects and passes them
  373. * off to show_obj_list() for display. Mode flags documented in
  374. * object.h
  375. */
  376. void show_floor(const int *floor_list, int floor_num, olist_detail_t mode)
  377. {
  378. if (floor_num > MAX_FLOOR_STACK)
  379. floor_num = MAX_FLOOR_STACK;
  380. /* Build the object list */
  381. build_obj_list(0, floor_num - 1, floor_list, mode);
  382. /* Display the object list */
  383. show_obj_list(num_obj, 0, mode);
  384. }
  385. /*
  386. * Verify the choice of an item.
  387. *
  388. * The item can be negative to mean "item on floor".
  389. */
  390. bool verify_item(const char *prompt, int item)
  391. {
  392. char o_name[80];
  393. char out_val[160];
  394. object_type *o_ptr;
  395. /* Inventory */
  396. if (item >= 0) {
  397. o_ptr = &p_ptr->inventory[item];
  398. }
  399. /* Floor */
  400. else {
  401. o_ptr = &o_list[0 - item];
  402. }
  403. /* Describe */
  404. object_desc(o_name, sizeof(o_name), o_ptr, ODESC_PREFIX | ODESC_FULL);
  405. /* Prompt */
  406. strnfmt(out_val, sizeof(out_val), "%s %s? ", prompt, o_name);
  407. /* Query */
  408. return (get_check(out_val));
  409. }
  410. /*
  411. * Hack -- allow user to "prevent" certain choices.
  412. *
  413. * The item can be negative to mean "item on floor".
  414. */
  415. static bool get_item_allow(int item, unsigned char ch, bool is_harmless)
  416. {
  417. object_type *o_ptr;
  418. char verify_inscrip[] = "!*";
  419. unsigned n;
  420. /* Inventory or floor */
  421. if (item >= 0)
  422. o_ptr = &p_ptr->inventory[item];
  423. else
  424. o_ptr = &o_list[0 - item];
  425. /* Check for a "prevention" inscription */
  426. verify_inscrip[1] = ch;
  427. /* Find both sets of inscriptions, add together, and prompt that number of
  428. * times */
  429. n = check_for_inscrip(o_ptr, verify_inscrip);
  430. if (!is_harmless)
  431. n += check_for_inscrip(o_ptr, "!*");
  432. while (n--) {
  433. if (!verify_item("Really try", item))
  434. return (FALSE);
  435. }
  436. /* Allow it */
  437. return (TRUE);
  438. }
  439. /*
  440. * Find the "first" inventory object with the given "tag".
  441. *
  442. * A "tag" is a char "n" appearing as "@n" anywhere in the
  443. * inscription of an object.
  444. *
  445. * Also, the tag "@xn" will work as well, where "n" is a tag-char,
  446. * and "x" is the action that tag will work for.
  447. */
  448. static int get_tag(int *cp, char tag, cmd_code cmd, bool quiver_tags)
  449. {
  450. int i;
  451. const char *s;
  452. /* (f)ire is handled differently from all others, due to the quiver */
  453. if (quiver_tags) {
  454. i = QUIVER_START + tag - '0';
  455. if (p_ptr->inventory[i].k_idx) {
  456. *cp = i;
  457. return (TRUE);
  458. }
  459. return (FALSE);
  460. }
  461. /* Check every object */
  462. for (i = 0; i < ALL_INVEN_TOTAL; ++i) {
  463. object_type *o_ptr = &p_ptr->inventory[i];
  464. /* Skip non-objects */
  465. if (!o_ptr->k_idx)
  466. continue;
  467. /* Skip empty inscriptions */
  468. if (!o_ptr->note)
  469. continue;
  470. /* Find a '@' */
  471. s = strchr(quark_str(o_ptr->note), '@');
  472. /* Process all tags */
  473. while (s) {
  474. /* Check the normal tags */
  475. if (s[1] == tag) {
  476. /* Save the actual inventory ID */
  477. *cp = i;
  478. /* Success */
  479. return (TRUE);
  480. }
  481. /* Check the special tags */
  482. if ((cmd_lookup(s[1], KEYMAP_MODE_ORIG) == cmd)
  483. && (s[2] == tag)) {
  484. /* Save the actual inventory ID */
  485. *cp = i;
  486. /* Success */
  487. return (TRUE);
  488. }
  489. /* Find another '@' */
  490. s = strchr(s + 1, '@');
  491. }
  492. }
  493. /* No such tag */
  494. return (FALSE);
  495. }
  496. /**
  497. * Make the correct prompt for items, handle mouse buttons
  498. */
  499. void item_prompt(int mode)
  500. {
  501. char tmp_val[160];
  502. char out_val[160];
  503. bool use_inven = ((mode & USE_INVEN) ? TRUE : FALSE);
  504. bool use_equip = ((mode & USE_EQUIP) ? TRUE : FALSE);
  505. bool can_squelch = ((mode & CAN_SQUELCH) && !show_list ? TRUE : FALSE);
  506. bool allow_floor = (f1 <= f2);
  507. /* Viewing inventory */
  508. if (p_ptr->command_wrk == USE_INVEN) {
  509. /* Begin the prompt */
  510. strnfmt(out_val, sizeof(out_val), "Inven:");
  511. /* List choices */
  512. if (i1 <= i2) {
  513. /* Build the prompt */
  514. strnfmt(tmp_val, sizeof(tmp_val), " %c-%c,",
  515. index_to_label(i1), index_to_label(i2));
  516. /* Append */
  517. my_strcat(out_val, tmp_val, sizeof(out_val));
  518. }
  519. /* Indicate ability to "view" */
  520. if (!show_list) {
  521. my_strcat(out_val, " * to see,", sizeof(out_val));
  522. button_add("[*]", '*');
  523. }
  524. /* Indicate legality of "toggle" */
  525. if (use_equip) {
  526. my_strcat(out_val, " / for Equip,", sizeof(out_val));
  527. button_add("[/]", '/');
  528. }
  529. /* Indicate legality of the "floor" */
  530. if (allow_floor) {
  531. my_strcat(out_val, " - for floor,", sizeof(out_val));
  532. button_add("[-]", '-');
  533. }
  534. /* Indicate that squelched items can be selected */
  535. if (can_squelch) {
  536. my_strcat(out_val, " ! for squelched,", sizeof(out_val));
  537. button_add("[!]", '!');
  538. }
  539. }
  540. /* Viewing equipment */
  541. else if (p_ptr->command_wrk == USE_EQUIP) {
  542. /* Begin the prompt */
  543. strnfmt(out_val, sizeof(out_val), "Equip:");
  544. /* List choices */
  545. if (e1 <= e2) {
  546. /* Build the prompt */
  547. strnfmt(tmp_val, sizeof(tmp_val), " %c-%c,",
  548. index_to_label(e1), index_to_label(e2));
  549. /* Append */
  550. my_strcat(out_val, tmp_val, sizeof(out_val));
  551. }
  552. /* Indicate ability to "view" */
  553. if (!show_list) {
  554. my_strcat(out_val, " * to see,", sizeof(out_val));
  555. button_add("[*]", '*');
  556. }
  557. /* Indicate legality of "toggle" */
  558. if (use_inven) {
  559. my_strcat(out_val, " / for Inven,", sizeof(out_val));
  560. button_add("[/]", '/');
  561. }
  562. /* Indicate legality of the "floor" */
  563. if (allow_floor) {
  564. my_strcat(out_val, " - for floor,", sizeof(out_val));
  565. button_add("[!]", '!');
  566. }
  567. }
  568. /* Viewing floor */
  569. else {
  570. /* Begin the prompt */
  571. strnfmt(out_val, sizeof(out_val), "Floor:");
  572. /* List choices */
  573. if (f1 <= f2) {
  574. /* Build the prompt */
  575. strnfmt(tmp_val, sizeof(tmp_val), " %c-%c,", I2A(f1), I2A(f2));
  576. /* Append */
  577. my_strcat(out_val, tmp_val, sizeof(out_val));
  578. }
  579. /* Indicate ability to "view" */
  580. if (!show_list) {
  581. my_strcat(out_val, " * to see,", sizeof(out_val));
  582. button_add("[*]", '*');
  583. }
  584. /* Append */
  585. if (use_inven) {
  586. my_strcat(out_val, " / for Inven,", sizeof(out_val));
  587. button_add("[/]", '/');
  588. }
  589. /* Append */
  590. else if (use_equip) {
  591. my_strcat(out_val, " / for Equip,", sizeof(out_val));
  592. button_add("[/]", '/');
  593. }
  594. /* Indicate that squelched items can be selected */
  595. if (can_squelch) {
  596. my_strcat(out_val, " ! for squelched,", sizeof(out_val));
  597. button_add("[!]", '!');
  598. }
  599. }
  600. /* Finish the prompt */
  601. my_strcat(out_val, " ESC", sizeof(out_val));
  602. /* Build the prompt */
  603. strnfmt(tmp_val, sizeof(tmp_val), "(%s) %s", out_val, prompt);
  604. /* Show the prompt */
  605. prt(tmp_val, 0, 0);
  606. }
  607. cmd_code menu_cmd;
  608. int menu_mode;
  609. static region area = { 20, 1, -1, -2 };
  610. /**
  611. * Display an entry on the item menu
  612. */
  613. void get_item_display(menu_type * menu, int oid, bool cursor, int row,
  614. int col, int width)
  615. {
  616. struct object_menu_data *choice = menu_priv(menu);
  617. size_t max_len = Term->wid - 1;
  618. const object_type *o_ptr = choice[oid].object;
  619. int ex_width = 0;
  620. /* Do we even have a menu? */
  621. if (!show_list)
  622. return;
  623. /* Width of extra fields */
  624. if (olist_mode & OLIST_WEIGHT)
  625. ex_width += 9;
  626. if (olist_mode & OLIST_PRICE)
  627. ex_width += 9;
  628. if (olist_mode & OLIST_FAIL)
  629. ex_width += 10;
  630. /* Get max length */
  631. get_max_len(&max_len);
  632. /* Print it */
  633. show_obj(oid, max_len, items[oid].label, o_ptr, cursor, olist_mode);
  634. }
  635. /**
  636. * Deal with events on the get_item menu
  637. */
  638. bool get_item_action(menu_type * menu, const ui_event * event, int oid)
  639. {
  640. bool refresh = FALSE;
  641. struct object_menu_data *choice = menu_priv(menu);
  642. char key = event->key.code;
  643. int i, k;
  644. char *selections = (char *) menu->selections;
  645. if (event->type == EVT_SELECT) {
  646. selection = choice[oid].key;
  647. return FALSE;
  648. }
  649. if (event->type == EVT_KBRD) {
  650. if (key == '/') {
  651. /* Toggle to inventory */
  652. if ((item_mode & USE_INVEN)
  653. && (p_ptr->command_wrk != USE_INVEN)) {
  654. p_ptr->command_wrk = USE_INVEN;
  655. build_obj_list(0, i2, NULL, olist_mode);
  656. refresh = TRUE;
  657. }
  658. /* Toggle to equipment */
  659. else if ((item_mode & USE_EQUIP) &&
  660. (p_ptr->command_wrk != USE_EQUIP)) {
  661. p_ptr->command_wrk = USE_EQUIP;
  662. build_obj_list(INVEN_WIELD, e2, NULL, olist_mode);
  663. refresh = TRUE;
  664. }
  665. /* No toggle allowed */
  666. else {
  667. bell("Cannot switch item selector!");
  668. }
  669. }
  670. else if (key == '-') {
  671. /* No toggle allowed */
  672. if (f1 > f2) {
  673. bell("Cannot select floor!");
  674. }
  675. /* Toggle to floor */
  676. else {
  677. p_ptr->command_wrk = (USE_FLOOR);
  678. build_obj_list(0, f2, floor_list, olist_mode);
  679. refresh = TRUE;
  680. }
  681. }
  682. else if ((key >= '0') && (key <= '9')) {
  683. /* Look up the tag */
  684. if (!get_tag(&k, key, item_cmd, item_mode & QUIVER_TAGS)) {
  685. bell("Illegal object choice (tag)!");
  686. return TRUE;
  687. }
  688. /* Match the item */
  689. for (i = 0; i < menu->count; i++) {
  690. if (choice[i].object == &p_ptr->inventory[k]) {
  691. Term_keypress(choice[i].key, 0);
  692. return TRUE;
  693. }
  694. }
  695. }
  696. if (refresh) {
  697. /* Load screen */
  698. screen_load();
  699. Term_fresh();
  700. /* Save screen */
  701. screen_save();
  702. /* Show the prompt */
  703. item_prompt(item_mode);
  704. menu_setpriv(menu, num_obj, items);
  705. for (i = 0; i < num_obj; i++)
  706. selections[i] = items[i].key;
  707. area.page_rows = menu->count + 1;
  708. menu_layout(menu, &area);
  709. menu_refresh(menu, TRUE);
  710. redraw_stuff(p_ptr);
  711. }
  712. return FALSE;
  713. }
  714. return TRUE;
  715. }
  716. /**
  717. * Display list items to choose from
  718. */
  719. ui_event item_menu(cmd_code cmd, int mode)
  720. {
  721. menu_type menu;
  722. menu_iter menu_f = { 0, 0, get_item_display, get_item_action, 0 };
  723. ui_event evt = { 0 };
  724. size_t max_len = Term->wid - 1;
  725. char selections[40];
  726. int i;
  727. /* Set up the menu */
  728. WIPE(&menu, menu);
  729. menu_init(&menu, MN_SKIN_SCROLL, &menu_f);
  730. menu_setpriv(&menu, num_obj, items);
  731. for (i = 0; i < num_obj; i++)
  732. selections[i] = items[i].key;
  733. menu.selections = selections;
  734. menu.cmd_keys = "/-0123456789";
  735. get_max_len(&max_len);
  736. area.page_rows = menu.count + 1;
  737. area.width = max_len;
  738. area.col = MIN(Term->wid - 1 - (int) max_len, COL_MAP + tile_width);
  739. menu_layout(&menu, &area);
  740. evt = menu_select(&menu, 0, TRUE);
  741. if (evt.type != EVT_ESCAPE) {
  742. evt.key.code = selection;
  743. }
  744. /* Result */
  745. return (evt);
  746. }
  747. /**
  748. * Let the user select an item, save its "index"
  749. *
  750. * Return TRUE only if an acceptable item was chosen by the user.
  751. *
  752. * The selected item must satisfy the "item_tester_hook()" function,
  753. * if that hook is set, and the "item_tester_tval", if that value is set.
  754. *
  755. * All "item_tester" restrictions are cleared before this function returns.
  756. *
  757. * The user is allowed to choose acceptable items from the equipment,
  758. * inventory, or floor, respectively, if the proper flag was given,
  759. * and there are any acceptable items in that location.
  760. *
  761. * The equipment or inventory are displayed (even if no acceptable
  762. * items are in that location) if the proper flag was given.
  763. *
  764. * If there are no acceptable items available anywhere, and "str" is
  765. * not NULL, then it will be used as the text of a warning message
  766. * before the function returns.
  767. *
  768. * Note that the user must press "-" to specify the item on the floor,
  769. * and there is no way to "examine" the item on the floor, while the
  770. * use of "capital" letters will "examine" an inventory/equipment item,
  771. * and prompt for its use.
  772. *
  773. * If a legal item is selected from the inventory, we save it in "cp"
  774. * directly (0 to 35), and return TRUE.
  775. *
  776. * If a legal item is selected from the floor, we save it in "cp" as
  777. * a negative (-1 to -511), and return TRUE.
  778. *
  779. * If no item is available, we do nothing to "cp", and we display a
  780. * warning message, using "str" if available, and return FALSE.
  781. *
  782. * If no item is selected, we do nothing to "cp", and return FALSE.
  783. *
  784. * Global "p_ptr->command_wrk" is used to choose between equip/inven/floor
  785. * listings. It is equal to USE_INVEN or USE_EQUIP or USE_FLOOR, except
  786. * when this function is first called, when it is equal to zero, which will
  787. * cause it to be set to USE_INVEN.
  788. *
  789. * We always erase the prompt when we are done, leaving a blank line,
  790. * or a warning message, if appropriate, if no items are available.
  791. *
  792. * Note that only "acceptable" floor objects get indexes, so between two
  793. * commands, the indexes of floor objects may change. XXX XXX XXX
  794. */
  795. bool get_item(int *cp, const char *pmt, const char *str, cmd_code cmd,
  796. int mode)
  797. {
  798. s16b py = p_ptr->py;
  799. s16b px = p_ptr->px;
  800. unsigned char cmdkey = cmd_lookup_key(cmd, KEYMAP_MODE_ORIG);
  801. bool done, item;
  802. int j, k;
  803. bool oops = FALSE;
  804. bool toggle = FALSE;
  805. bool use_inven = ((mode & USE_INVEN) ? TRUE : FALSE);
  806. bool use_equip = ((mode & USE_EQUIP) ? TRUE : FALSE);
  807. bool use_floor = ((mode & (USE_FLOOR | USE_TARGET)) ? TRUE : FALSE);
  808. bool use_quiver = ((mode & QUIVER_TAGS) ? TRUE : FALSE);
  809. bool can_squelch = ((mode & CAN_SQUELCH) ? TRUE : FALSE);
  810. bool is_harmless = ((mode & IS_HARMLESS) ? TRUE : FALSE);
  811. bool quiver_tags = ((mode & QUIVER_TAGS) ? TRUE : FALSE);
  812. bool allow_inven = FALSE;
  813. bool allow_equip = FALSE;
  814. bool allow_floor = FALSE;
  815. int floor_num;
  816. ui_event which;
  817. prompt = pmt;
  818. olist_mode = 0;
  819. item_mode = mode;
  820. item_cmd = cmd;
  821. /* Not done */
  822. done = FALSE;
  823. /* No item selected */
  824. item = FALSE;
  825. *cp = 0;
  826. /* Object list display modes */
  827. if (mode & SHOW_FAIL)
  828. olist_mode |= (OLIST_FAIL);
  829. else
  830. olist_mode |= (OLIST_WEIGHT);
  831. if (mode & SHOW_PRICES)
  832. olist_mode |= (OLIST_PRICE);
  833. show_list = OPT(show_lists) ? TRUE : FALSE;
  834. /* Set target for telekinesis */
  835. if (mode & (USE_TARGET)) {
  836. target_get(&px, &py);
  837. if (!(px && py))
  838. return FALSE;
  839. }
  840. /* Full inventory */
  841. i1 = 0;
  842. i2 = INVEN_PACK - 1;
  843. /* Forbid inventory */
  844. if (!use_inven)
  845. i2 = -1;
  846. /* Restrict inventory indexes */
  847. while ((i1 <= i2) && (!get_item_okay(i1)))
  848. i1++;
  849. while ((i1 <= i2) && (!get_item_okay(i2)))
  850. i2--;
  851. /* Accept inventory */
  852. if (i1 <= i2)
  853. allow_inven = TRUE;
  854. /* Full equipment */
  855. e1 = INVEN_WIELD;
  856. e2 = ALL_INVEN_TOTAL - 1;
  857. /* Forbid equipment */
  858. if (!use_equip)
  859. e2 = -1;
  860. /* Restrict equipment indexes */
  861. while ((e1 <= e2) && (!get_item_okay(e1)))
  862. e1++;
  863. while ((e1 <= e2) && (!get_item_okay(e2)))
  864. e2--;
  865. /* Accept equipment */
  866. if (e1 <= e2)
  867. allow_equip = TRUE;
  868. /* Reject quiver */
  869. if ((e2 < QUIVER_START) || !allow_equip)
  870. use_quiver = FALSE;
  871. /* Scan all non-gold objects in the grid */
  872. floor_num =
  873. scan_floor(floor_list, N_ELEMENTS(floor_list), py, px, 0x03);
  874. /* Full floor */
  875. f1 = 0;
  876. f2 = floor_num - 1;
  877. /* Forbid floor */
  878. if (!use_floor)
  879. f2 = -1;
  880. /* Restrict floor indexes */
  881. while ((f1 <= f2) && (!get_item_okay(0 - floor_list[f1])))
  882. f1++;
  883. while ((f1 <= f2) && (!get_item_okay(0 - floor_list[f2])))
  884. f2--;
  885. /* Accept floor */
  886. if (f1 <= f2)
  887. allow_floor = TRUE;
  888. /* Require at least one legal choice */
  889. if (!allow_inven && !allow_equip && !allow_floor) {
  890. /* Oops */
  891. oops = TRUE;
  892. done = TRUE;
  893. }
  894. /* Analyze choices, prepare for initial menu */
  895. else {
  896. /* Start where requested */
  897. if ((p_ptr->command_wrk == USE_EQUIP) && allow_equip) {
  898. p_ptr->command_wrk = USE_EQUIP;
  899. build_obj_list(INVEN_WIELD, e2, NULL, olist_mode);
  900. } else if ((p_ptr->command_wrk == USE_INVEN) && allow_inven) {
  901. p_ptr->command_wrk = USE_INVEN;
  902. build_obj_list(0, i2, NULL, olist_mode);
  903. } else if ((p_ptr->command_wrk == USE_FLOOR) && allow_floor) {
  904. p_ptr->command_wrk = USE_FLOOR;
  905. build_obj_list(0, f2, floor_list, olist_mode);
  906. }
  907. /* If we are using the quiver then start on equipment */
  908. else if (use_quiver && allow_equip) {
  909. p_ptr->command_wrk = USE_EQUIP;
  910. build_obj_list(INVEN_WIELD, e2, NULL, olist_mode);
  911. }
  912. /* Use inventory if allowed */
  913. else if (use_inven && allow_inven) {
  914. p_ptr->command_wrk = USE_INVEN;
  915. build_obj_list(0, i2, NULL, olist_mode);
  916. }
  917. /* Use equipment if allowed */
  918. else if (use_equip && allow_equip) {
  919. p_ptr->command_wrk = USE_EQUIP;
  920. build_obj_list(INVEN_WIELD, e2, NULL, olist_mode);
  921. }
  922. /* Use floor if allowed */
  923. else if (use_floor && allow_floor) {
  924. p_ptr->command_wrk = USE_FLOOR;
  925. build_obj_list(0, f2, floor_list, olist_mode);
  926. }
  927. /* Hack -- Use (empty) inventory */
  928. else {
  929. p_ptr->command_wrk = USE_INVEN;
  930. build_obj_list(0, i2, NULL, olist_mode);
  931. }
  932. }
  933. /* Clear all current messages */
  934. msg_flag = FALSE;
  935. /* Start out in "display" mode */
  936. if (show_list) {
  937. /* Save screen */
  938. screen_save();
  939. }
  940. /* Repeat until done */
  941. while (!done) {
  942. static bool refresh;
  943. int ni = 0;
  944. int ne = 0;
  945. /* Scan windows */
  946. for (j = 0; j < ANGBAND_TERM_MAX; j++) {
  947. /* Unused */
  948. if (!angband_term[j])
  949. continue;
  950. /* Count windows displaying inven */
  951. if (op_ptr->window_flag[j] & (PW_INVEN))
  952. ni++;
  953. /* Count windows displaying equip */
  954. if (op_ptr->window_flag[j] & (PW_EQUIP))
  955. ne++;
  956. }
  957. /* Toggle if needed */
  958. if (((p_ptr->command_wrk == USE_EQUIP) && ni && !ne)
  959. || ((p_ptr->command_wrk == USE_INVEN) && !ni && ne)) {
  960. /* Toggle */
  961. toggle_inven_equip();
  962. /* Track toggles */
  963. toggle = !toggle;
  964. }
  965. /* Redraw */
  966. p_ptr->redraw |= (PR_INVEN | PR_EQUIP);
  967. /* Redraw windows */
  968. redraw_stuff(p_ptr);
  969. /* Change the list if needed */
  970. if (refresh) {
  971. /* Rebuild object list */
  972. if (p_ptr->command_wrk == USE_INVEN)
  973. build_obj_list(0, i2, NULL, olist_mode);
  974. else if (p_ptr->command_wrk == USE_EQUIP)
  975. build_obj_list(INVEN_WIELD, e2, NULL, olist_mode);
  976. else
  977. build_obj_list(0, f2, floor_list, mode);
  978. refresh = FALSE;
  979. }
  980. /* Show the prompt */
  981. item_prompt(mode);
  982. redraw_stuff(p_ptr);
  983. /* Menu if requested */
  984. if (show_list) {
  985. which = item_menu(cmd, mode);
  986. if (which.type == EVT_ESCAPE)
  987. which.key.code = ESCAPE;
  988. }
  989. /* Get a key */
  990. else
  991. which = inkey_ex();
  992. /* Parse it */
  993. switch (which.key.code) {
  994. case ESCAPE:
  995. {
  996. done = TRUE;
  997. break;
  998. }
  999. case '*':
  1000. case '?':
  1001. case ' ':
  1002. {
  1003. if (!OPT(show_lists)) {
  1004. /* Save screen */
  1005. screen_save();
  1006. /* Flip flag */
  1007. show_list = TRUE;
  1008. }
  1009. refresh = TRUE;
  1010. break;
  1011. }
  1012. case '/':
  1013. {
  1014. /* Toggle to inventory */
  1015. if (use_inven && (p_ptr->command_wrk != USE_INVEN)) {
  1016. p_ptr->command_wrk = USE_INVEN;
  1017. refresh = TRUE;
  1018. }
  1019. /* Toggle to equipment */
  1020. else if (use_equip && (p_ptr->command_wrk != USE_EQUIP)) {
  1021. p_ptr->command_wrk = USE_EQUIP;
  1022. refresh = TRUE;
  1023. }
  1024. /* No toggle allowed */
  1025. else {
  1026. bell("Cannot switch item selector!");
  1027. break;
  1028. }
  1029. /* Need to redraw */
  1030. break;
  1031. }
  1032. case '-':
  1033. {
  1034. /* Paranoia */
  1035. if (!allow_floor) {
  1036. bell("Cannot select floor!");
  1037. break;
  1038. }
  1039. /* There is only one item */
  1040. if (OPT(pickup_single) && (floor_num == 1)) {
  1041. /* Fall through */
  1042. } else {
  1043. p_ptr->command_wrk = (USE_FLOOR);
  1044. refresh = TRUE;
  1045. break;
  1046. }
  1047. }
  1048. case '.':
  1049. {
  1050. /*
  1051. * If we are allow to use the floor, select
  1052. * the top item. -BR-
  1053. */
  1054. if (allow_floor) {
  1055. int k;
  1056. /* Special index */
  1057. k = 0 - floor_list[0];
  1058. /* Allow player to "refuse" certain actions */
  1059. if (!get_item_allow(k, cmdkey, is_harmless)) {
  1060. done = TRUE;
  1061. break;
  1062. }
  1063. /* Accept that choice */
  1064. (*cp) = k;
  1065. item = TRUE;
  1066. done = TRUE;
  1067. }
  1068. break;
  1069. }
  1070. case '0':
  1071. case '1':
  1072. case '2':
  1073. case '3':
  1074. case '4':
  1075. case '5':
  1076. case '6':
  1077. case '7':
  1078. case '8':
  1079. case '9':
  1080. {
  1081. /* Look up the tag */
  1082. if (!get_tag(&k, which.key.code, cmd, quiver_tags)) {
  1083. bell("Illegal object choice (tag)!");
  1084. break;
  1085. }
  1086. /* Hack -- Validate the item */
  1087. if ((k < INVEN_WIELD) ? !allow_inven : !allow_equip) {
  1088. bell("Illegal object choice (tag)!");
  1089. break;
  1090. }
  1091. /* Validate the item */
  1092. if (!get_item_okay(k)) {
  1093. bell("Illegal object choice (tag)!");
  1094. break;
  1095. }
  1096. /* Allow player to "refuse" certain actions */
  1097. if (!get_item_allow(k, cmdkey, is_harmless)) {
  1098. done = TRUE;
  1099. break;
  1100. }
  1101. /* Accept that choice */
  1102. (*cp) = k;
  1103. item = TRUE;
  1104. done = TRUE;
  1105. break;
  1106. }
  1107. case KC_ENTER:
  1108. {
  1109. /* Choose "default" inventory item */
  1110. if (p_ptr->command_wrk == USE_INVEN) {
  1111. if (i1 != i2) {
  1112. bell("Illegal object choice (default)!");
  1113. break;
  1114. }
  1115. k = i1;
  1116. }
  1117. /* Choose the "default" slot (0) of the quiver */
  1118. else if (quiver_tags)
  1119. k = e1;
  1120. /* Choose "default" equipment item */
  1121. else if (p_ptr->command_wrk == USE_EQUIP) {
  1122. if (e1 != e2) {
  1123. bell("Illegal object choice (default)!");
  1124. break;
  1125. }
  1126. k = e1;
  1127. }
  1128. /* Choose "default" floor item */
  1129. else {
  1130. if (f1 != f2) {
  1131. bell("Illegal object choice (default)!");
  1132. break;
  1133. }
  1134. k = 0 - floor_list[f1];
  1135. }
  1136. /* Validate the item */
  1137. if (!get_item_okay(k)) {
  1138. bell("Illegal object choice (default)!");
  1139. break;
  1140. }
  1141. /* Allow player to "refuse" certain actions */
  1142. if (!get_item_allow(k, cmdkey, is_harmless)) {
  1143. done = TRUE;
  1144. break;
  1145. }
  1146. /* Accept that choice */
  1147. (*cp) = k;
  1148. item = TRUE;
  1149. done = TRUE;
  1150. break;
  1151. }
  1152. case '!':
  1153. {
  1154. /* Try squelched items */
  1155. if (can_squelch) {
  1156. (*cp) = ALL_SQUELCHED;
  1157. item = TRUE;
  1158. done = TRUE;
  1159. break;
  1160. }
  1161. /* Just fall through */
  1162. }
  1163. default:
  1164. {
  1165. bool verify;
  1166. /* Note verify */
  1167. verify =
  1168. (isupper((unsigned char) which.key.code) ? TRUE :
  1169. FALSE);
  1170. /* Lowercase */
  1171. which.key.code = tolower((unsigned char) which.key.code);
  1172. /* Convert letter to inventory index */
  1173. if (p_ptr->command_wrk == USE_INVEN) {
  1174. k = label_to_inven(which.key.code);
  1175. if (k < 0) {
  1176. bell("Illegal object choice (inven)!");
  1177. break;
  1178. }
  1179. }
  1180. /* Convert letter to equipment index */
  1181. else if (p_ptr->command_wrk == USE_EQUIP) {
  1182. k = label_to_equip(which.key.code);
  1183. if (k < 0) {
  1184. bell("Illegal object choice (equip)!");
  1185. break;
  1186. }
  1187. }
  1188. /* Convert letter to floor index */
  1189. else {
  1190. k = (islower((unsigned char) which.key.code) ?
  1191. A2I((unsigned char) which.key.code) : -1);
  1192. if (k < 0 || k >= floor_num) {
  1193. bell("Illegal object choice (floor)!");
  1194. break;
  1195. }
  1196. /* Special index */
  1197. k = 0 - floor_list[k];
  1198. }
  1199. /* Validate the item */
  1200. if (!get_item_okay(k)) {
  1201. bell("Illegal object choice (normal)!");
  1202. break;
  1203. }
  1204. /* Verify the item */
  1205. if (verify && !verify_item("Try", k)) {
  1206. done = TRUE;
  1207. break;
  1208. }
  1209. /* Allow player to "refuse" certain actions */
  1210. if (!get_item_allow(k, cmdkey, is_harmless)) {
  1211. done = TRUE;
  1212. break;
  1213. }
  1214. /* Accept that choice */
  1215. (*cp) = k;
  1216. item = TRUE;
  1217. done = TRUE;
  1218. break;
  1219. }
  1220. }
  1221. }
  1222. /* Fix the screen if necessary */
  1223. if (show_list) {
  1224. /* Load screen */
  1225. screen_load();
  1226. /* Hack -- Cancel "display" */
  1227. show_list = FALSE;
  1228. }
  1229. /* Kill buttons */
  1230. button_kill('*');
  1231. button_kill('/');
  1232. button_kill('-');
  1233. button_kill('!');
  1234. /* Forget the item_tester_tval restriction */
  1235. item_tester_tval = 0;
  1236. /* Forget the item_tester_hook restriction */
  1237. item_tester_hook = NULL;
  1238. /* Toggle again if needed */
  1239. if (toggle)
  1240. toggle_inven_equip();
  1241. /* Update */
  1242. p_ptr->redraw |= (PR_INVEN | PR_EQUIP);
  1243. redraw_stuff(p_ptr);
  1244. /* Clear the prompt line */
  1245. prt("", 0, 0);
  1246. /* Warning if needed */
  1247. if (oops && str)
  1248. msg(str);
  1249. /* Result */
  1250. return (item);
  1251. }