1 /**
2 * \file ui-context.c
3 * \brief Show player and terrain context menus.
4 *
5 * Copyright (c) 2011 Brett Reid
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 license":
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
19 #include "angband.h"
20 #include "cave.h"
21 #include "cmd-core.h"
22 #include "cmds.h"
23 #include "game-input.h"
24 #include "mon-desc.h"
25 #include "mon-lore.h"
26 #include "mon-util.h"
27 #include "obj-chest.h"
28 #include "obj-desc.h"
29 #include "obj-gear.h"
30 #include "obj-ignore.h"
31 #include "obj-info.h"
32 #include "obj-tval.h"
33 #include "obj-util.h"
34 #include "player-calcs.h"
35 #include "player-timed.h"
36 #include "player-util.h"
37 #include "store.h"
38 #include "target.h"
39 #include "ui-context.h"
40 #include "ui-game.h"
41 #include "ui-input.h"
42 #include "ui-keymap.h"
43 #include "ui-knowledge.h"
44 #include "ui-menu.h"
45 #include "ui-mon-lore.h"
46 #include "ui-object.h"
47 #include "ui-player.h"
48 #include "ui-spell.h"
49 #include "ui-store.h"
50 #include "ui-target.h"
51 #include "wizard.h"
52
53 #define ADD_LABEL(text, cmd, valid) { \
54 cmdkey = cmd_lookup_key_unktrl((cmd), mode); \
55 menu_dynamic_add_label_valid(m, (text), cmdkey, (cmd), labels, (valid)); \
56 }
57
58 /**
59 * Additional constants for menu item values. The values must not collide
60 * with the cmd_code enum, since those are the main values for these menu items.
61 */
62 enum context_menu_value_e {
63 MENU_VALUE_INSPECT = CMD_REPEAT + 1000,
64 MENU_VALUE_DROP_ALL,
65 MENU_VALUE_LOOK,
66 MENU_VALUE_RECALL,
67 MENU_VALUE_REST,
68 MENU_VALUE_INVENTORY,
69 MENU_VALUE_CENTER_MAP,
70 MENU_VALUE_FLOOR,
71 MENU_VALUE_CHARACTER,
72 MENU_VALUE_OTHER,
73 MENU_VALUE_KNOWLEDGE,
74 MENU_VALUE_MAP,
75 MENU_VALUE_MESSAGES,
76 MENU_VALUE_OBJECTS,
77 MENU_VALUE_MONSTERS,
78 MENU_VALUE_TOGGLE_IGNORED,
79 MENU_VALUE_OPTIONS,
80 MENU_VALUE_HELP,
81 };
82
83
context_menu_player_2(int mx,int my)84 static int context_menu_player_2(int mx, int my)
85 {
86 struct menu *m;
87 int selected;
88 char *labels;
89 bool allowed = true;
90 int mode = OPT(player, rogue_like_commands) ? KEYMAP_MODE_ROGUE : KEYMAP_MODE_ORIG;
91 unsigned char cmdkey;
92
93 m = menu_dynamic_new();
94 if (!m) {
95 return 0;
96 }
97
98 labels = string_make(lower_case);
99 m->selections = labels;
100
101 menu_dynamic_add_label(m, "Knowledge", '~', MENU_VALUE_KNOWLEDGE, labels);
102 menu_dynamic_add_label(m, "Show Map", 'M', MENU_VALUE_MAP, labels);
103 menu_dynamic_add_label(m, "^Show Messages", 'P', MENU_VALUE_MESSAGES,
104 labels);
105 menu_dynamic_add_label(m, "Show Monster List", '[', MENU_VALUE_MONSTERS,
106 labels);
107 menu_dynamic_add_label(m, "Show Object List", ']', MENU_VALUE_OBJECTS,
108 labels);
109
110 /* Ignore toggle has different keys, but we don't have a way to look them
111 * up (see ui-game.c). */
112 cmdkey = (mode == KEYMAP_MODE_ORIG) ? 'K' : 'O';
113 menu_dynamic_add_label(m, "Toggle Ignored", cmdkey,
114 MENU_VALUE_TOGGLE_IGNORED, labels);
115
116 ADD_LABEL("Ignore an item", CMD_IGNORE, MN_ROW_VALID);
117
118 menu_dynamic_add_label(m, "Options", '=', MENU_VALUE_OPTIONS, labels);
119 menu_dynamic_add_label(m, "Commands", '?', MENU_VALUE_HELP, labels);
120
121 /* Hack -- no flush needed */
122 msg_flag = false;
123 screen_save();
124
125 menu_dynamic_calc_location(m, mx, my);
126 region_erase_bordered(&m->boundary);
127
128 prt("(Enter to select, ESC) Command:", 0, 0);
129 selected = menu_dynamic_select(m);
130
131 menu_dynamic_free(m);
132 string_free(labels);
133
134 screen_load();
135
136 /* Check the command to see if it is allowed. */
137 switch (selected) {
138 case -1:
139 /* User cancelled the menu. */
140 return 3;
141
142 case MENU_VALUE_KNOWLEDGE:
143 case MENU_VALUE_MAP:
144 case MENU_VALUE_MESSAGES:
145 case MENU_VALUE_TOGGLE_IGNORED:
146 case MENU_VALUE_HELP:
147 case MENU_VALUE_MONSTERS:
148 case MENU_VALUE_OBJECTS:
149 case MENU_VALUE_OPTIONS:
150 allowed = true;
151 break;
152
153 case CMD_IGNORE:
154 cmdkey = cmd_lookup_key(selected, mode);
155 allowed = key_confirm_command(cmdkey);
156 break;
157
158 default:
159 /* Invalid command; prevent anything from happening. */
160 bell("Invalid context menu command.");
161 allowed = false;
162 break;
163 }
164
165 if (!allowed)
166 return 1;
167
168 /* Perform the command. */
169 switch (selected) {
170 case MENU_VALUE_KNOWLEDGE:
171 Term_keypress('~', 0);
172 break;
173
174 case MENU_VALUE_MAP:
175 Term_keypress('M', 0);
176 break;
177
178 case MENU_VALUE_MESSAGES:
179 Term_keypress(KTRL('p'), 0);
180 break;
181
182 case CMD_IGNORE:
183 cmdkey = cmd_lookup_key(selected, mode);
184 Term_keypress(cmdkey, 0);
185 break;
186
187 case MENU_VALUE_TOGGLE_IGNORED:
188 /* Ignore toggle has different keys, but we don't have a way to
189 * look them up (see ui-game.c). */
190 cmdkey = (mode == KEYMAP_MODE_ORIG) ? 'K' : 'O';
191 Term_keypress(cmdkey, 0);
192 break;
193
194 case MENU_VALUE_HELP:
195 context_menu_command(mx, my);
196 break;
197
198 case MENU_VALUE_MONSTERS:
199 Term_keypress('[', 0);
200 break;
201
202 case MENU_VALUE_OBJECTS:
203 Term_keypress(']', 0);
204 break;
205
206 case MENU_VALUE_OPTIONS:
207 Term_keypress('=', 0);
208 break;
209
210 default:
211 break;
212 }
213
214 return 1;
215 }
216
context_menu_player_display_floor(void)217 static void context_menu_player_display_floor(void)
218 {
219 int diff = weight_remaining(player);
220 struct object *obj;
221
222 /* There is an item on the floor, select from there */
223 player->upkeep->command_wrk = (USE_FLOOR);
224
225 /* Save screen */
226 screen_save();
227
228 /* Prompt for a command */
229 prt(format("(Inventory) Burden %d.%d lb (%d.%d lb %s). Item for command: ",
230 player->upkeep->total_weight / 10,
231 player->upkeep->total_weight % 10,
232 abs(diff) / 10, abs(diff) % 10,
233 (diff < 0 ? "overweight" : "remaining")), 0, 0);
234
235
236 /* Get an item to use a context command on */
237 if (get_item(&obj, NULL, NULL, CMD_NULL, NULL, USE_EQUIP | USE_INVEN | USE_QUIVER | USE_FLOOR | SHOW_EMPTY | IS_HARMLESS)) {
238 /* Track the object kind */
239 track_object(player->upkeep, obj);
240
241 context_menu_object(obj);
242 }
243
244 /* Load screen */
245 screen_load();
246 }
247
context_menu_player(int mx,int my)248 int context_menu_player(int mx, int my)
249 {
250 struct menu *m;
251 int selected;
252 char *labels;
253 bool allowed = true;
254 int mode = OPT(player, rogue_like_commands) ? KEYMAP_MODE_ROGUE : KEYMAP_MODE_ORIG;
255 unsigned char cmdkey;
256 struct object *obj;
257
258 m = menu_dynamic_new();
259 if (!m) {
260 return 0;
261 }
262
263 labels = string_make(lower_case);
264 m->selections = labels;
265
266 ADD_LABEL("Use", CMD_USE, MN_ROW_VALID);
267
268 /* if player can cast, add casting option */
269 if (player_can_cast(player, false)) {
270 ADD_LABEL("Cast", CMD_CAST, MN_ROW_VALID);
271 }
272
273 /* if player is on stairs add option to use them */
274 if (square_isupstairs(cave, player->grid)) {
275 ADD_LABEL("Go Up", CMD_GO_UP, MN_ROW_VALID);
276 }
277 else if (square_isdownstairs(cave, player->grid)) {
278 ADD_LABEL("Go Down", CMD_GO_DOWN, MN_ROW_VALID);
279 }
280
281 /* Looking has different keys, but we don't have a way to look them up
282 * (see ui-game.c). */
283 cmdkey = (mode == KEYMAP_MODE_ORIG) ? 'l' : 'x';
284 menu_dynamic_add_label(m, "Look", cmdkey, MENU_VALUE_LOOK, labels);
285
286 /* 'R' is used for resting in both keymaps. */
287 menu_dynamic_add_label(m, "Rest", 'R', MENU_VALUE_REST, labels);
288
289 /* 'i' is used for inventory in both keymaps. */
290 menu_dynamic_add_label(m, "Inventory", 'i', MENU_VALUE_INVENTORY, labels);
291
292 /* if object under player add pickup option */
293 obj = square_object(cave, player->grid);
294 if (obj && !ignore_item_ok(obj)) {
295 menu_row_validity_t valid;
296
297 /* 'f' isn't in rogue keymap, so we can use it here. */
298 menu_dynamic_add_label(m, "Floor", 'f', MENU_VALUE_FLOOR, labels);
299 valid = (inven_carry_okay(obj)) ? MN_ROW_VALID : MN_ROW_INVALID;
300 ADD_LABEL("Pick up", CMD_PICKUP, valid);
301 }
302
303 /* 'C' is used for the character sheet in both keymaps. */
304 menu_dynamic_add_label(m, "Character", 'C', MENU_VALUE_CHARACTER, labels);
305
306 if (!OPT(player, center_player)) {
307 menu_dynamic_add_label(m, "^Center Map", 'L', MENU_VALUE_CENTER_MAP,
308 labels);
309 }
310
311 menu_dynamic_add_label(m, "Other", ' ', MENU_VALUE_OTHER, labels);
312
313 /* Hack -- no flush needed */
314 msg_flag = false;
315 screen_save();
316
317 menu_dynamic_calc_location(m, mx, my);
318 region_erase_bordered(&m->boundary);
319
320 prt("(Enter to select, ESC) Command:", 0, 0);
321 selected = menu_dynamic_select(m);
322
323 menu_dynamic_free(m);
324 string_free(labels);
325
326 screen_load();
327
328 cmdkey = cmd_lookup_key(selected, mode);
329
330 /* Check the command to see if it is allowed. */
331 switch(selected) {
332 case -1:
333 /* User cancelled the menu. */
334 return 3;
335
336 case CMD_USE:
337 case CMD_CAST:
338 case CMD_GO_UP:
339 case CMD_GO_DOWN:
340 case CMD_PICKUP:
341 /* Only check for ^ inscriptions, since we don't have an object
342 * selected (if we need one). */
343 allowed = key_confirm_command(cmdkey);
344 break;
345
346 case MENU_VALUE_REST:
347 allowed = key_confirm_command('R');
348 break;
349
350 case MENU_VALUE_INVENTORY:
351 case MENU_VALUE_LOOK:
352 case MENU_VALUE_CHARACTER:
353 case MENU_VALUE_OTHER:
354 case MENU_VALUE_FLOOR:
355 case MENU_VALUE_CENTER_MAP:
356 allowed = true;
357 break;
358
359 default:
360 /* Invalid command; prevent anything from happening. */
361 bell("Invalid context menu command.");
362 allowed = false;
363 break;
364 }
365
366 if (!allowed)
367 return 1;
368
369 /* Perform the command. */
370 switch(selected) {
371 case CMD_USE:
372 case CMD_CAST:
373 cmdkey = cmd_lookup_key(selected, mode);
374 Term_keypress(cmdkey, 0);
375 break;
376
377 case CMD_GO_UP:
378 case CMD_GO_DOWN:
379 case CMD_PICKUP:
380 cmdq_push(selected);
381 break;
382
383 case MENU_VALUE_REST:
384 Term_keypress('R', 0);
385 break;
386
387 case MENU_VALUE_INVENTORY:
388 Term_keypress('i', 0);
389 break;
390
391 case MENU_VALUE_LOOK:
392 if (target_set_interactive(TARGET_LOOK, player->grid.x, player->grid.y))
393 msg("Target Selected.");
394 break;
395
396 case MENU_VALUE_CHARACTER:
397 Term_keypress('C', 0);
398 break;
399
400 case MENU_VALUE_OTHER:
401 context_menu_player_2(mx, my);
402 break;
403
404 case MENU_VALUE_FLOOR:
405 context_menu_player_display_floor();
406 break;
407
408 case MENU_VALUE_CENTER_MAP:
409 do_cmd_center_map();
410 break;
411
412 default:
413 break;
414 }
415
416 return 1;
417 }
418
context_menu_cave(struct chunk * c,int y,int x,int adjacent,int mx,int my)419 int context_menu_cave(struct chunk *c, int y, int x, int adjacent, int mx,
420 int my)
421 {
422 struct menu *m;
423 int selected;
424 char *labels;
425 bool allowed = true;
426 int mode = OPT(player, rogue_like_commands) ? KEYMAP_MODE_ROGUE : KEYMAP_MODE_ORIG;
427 unsigned char cmdkey;
428 struct loc grid = loc(x, y);
429 struct object *square_obj = square_object(c, grid);
430
431 m = menu_dynamic_new();
432 if (!m)
433 return 0;
434
435 labels = string_make(lower_case);
436 m->selections = labels;
437
438 /* Looking has different keys, but we don't have a way to look them up
439 * (see ui-game.c). */
440 cmdkey = (mode == KEYMAP_MODE_ORIG) ? 'l' : 'x';
441 menu_dynamic_add_label(m, "Look At", cmdkey, MENU_VALUE_LOOK, labels);
442
443 if (square(c, grid)->mon)
444 /* '/' is used for recall in both keymaps. */
445 menu_dynamic_add_label(m, "Recall Info", '/', MENU_VALUE_RECALL,
446 labels);
447
448 ADD_LABEL("Use Item On", CMD_USE, MN_ROW_VALID);
449
450 if (player_can_cast(player, false))
451 ADD_LABEL("Cast On", CMD_CAST, MN_ROW_VALID);
452
453 if (adjacent) {
454 struct object *obj = chest_check(grid, CHEST_ANY);
455 ADD_LABEL((square(c, grid)->mon) ? "Attack" : "Alter", CMD_ALTER,
456 MN_ROW_VALID);
457
458 if (obj && !ignore_item_ok(obj)) {
459 if (obj->known->pval) {
460 if (is_locked_chest(obj)) {
461 ADD_LABEL("Disarm Chest", CMD_DISARM, MN_ROW_VALID);
462 ADD_LABEL("Open Chest", CMD_OPEN, MN_ROW_VALID);
463 } else {
464 ADD_LABEL("Open Disarmed Chest", CMD_OPEN, MN_ROW_VALID);
465 }
466 } else {
467 ADD_LABEL("Open Chest", CMD_OPEN, MN_ROW_VALID);
468 }
469 }
470
471 if ((square(cave, grid)->mon > 0) && player_has(player, PF_STEAL)) {
472 ADD_LABEL("Steal", CMD_STEAL, MN_ROW_VALID);
473 }
474
475 if (square_isdisarmabletrap(c, grid)) {
476 ADD_LABEL("Disarm", CMD_DISARM, MN_ROW_VALID);
477 ADD_LABEL("Jump Onto", CMD_JUMP, MN_ROW_VALID);
478 }
479
480 if (square_isopendoor(c, grid)) {
481 ADD_LABEL("Close", CMD_CLOSE, MN_ROW_VALID);
482 }
483 else if (square_iscloseddoor(c, grid)) {
484 ADD_LABEL("Open", CMD_OPEN, MN_ROW_VALID);
485 ADD_LABEL("Lock", CMD_DISARM, MN_ROW_VALID);
486 }
487 else if (square_isdiggable(c, grid)) {
488 ADD_LABEL("Tunnel", CMD_TUNNEL, MN_ROW_VALID);
489 }
490
491 ADD_LABEL("Walk Towards", CMD_WALK, MN_ROW_VALID);
492 } else {
493 /* ',' is used for ignore in rogue keymap, so we'll just swap letters */
494 cmdkey = (mode == KEYMAP_MODE_ORIG) ? ',' : '.';
495 menu_dynamic_add_label(m, "Pathfind To", cmdkey, CMD_PATHFIND, labels);
496
497 ADD_LABEL("Walk Towards", CMD_WALK, MN_ROW_VALID);
498 ADD_LABEL("Run Towards", CMD_RUN, MN_ROW_VALID);
499 }
500
501 if (player_can_fire(player, false)) {
502 ADD_LABEL("Fire On", CMD_FIRE, MN_ROW_VALID);
503 }
504
505 ADD_LABEL("Throw To", CMD_THROW, MN_ROW_VALID);
506
507 /* Hack -- no flush needed */
508 msg_flag = false;
509 screen_save();
510
511 menu_dynamic_calc_location(m, mx, my);
512 region_erase_bordered(&m->boundary);
513
514 if (player->timed[TMD_IMAGE]) {
515 prt("(Enter to select command, ESC to cancel) You see something strange:", 0, 0);
516 } else if (square(c, grid)->mon) {
517 char m_name[80];
518 struct monster *mon = square_monster(c, grid);
519
520 /* Get the monster name ("a kobold") */
521 monster_desc(m_name, sizeof(m_name), mon, MDESC_IND_VIS);
522
523 prt(format("(Enter to select command, ESC to cancel) You see %s:",
524 m_name), 0, 0);
525 } else if (square_obj && !ignore_item_ok(square_obj)) {
526 char o_name[80];
527
528 /* Obtain an object description */
529 object_desc(o_name, sizeof (o_name), square_obj,
530 ODESC_PREFIX | ODESC_FULL);
531
532 prt(format("(Enter to select command, ESC to cancel) You see %s:",
533 o_name), 0, 0);
534 } else {
535 /* Feature (apply mimic) */
536 const char *name = square_apparent_name(c, player, grid);
537 const char *prefix = square_apparent_look_prefix(c, player, grid);
538
539 prt(format("(Enter to select command, ESC to cancel) You see %s%s:", prefix, name), 0, 0);
540 }
541
542 selected = menu_dynamic_select(m);
543
544 menu_dynamic_free(m);
545 string_free(labels);
546
547 screen_load();
548
549 cmdkey = cmd_lookup_key(selected, mode);
550
551 /* Check the command to see if it is allowed. */
552 switch (selected) {
553 case -1:
554 /* User cancelled the menu. */
555 return 3;
556
557 case MENU_VALUE_LOOK:
558 case MENU_VALUE_RECALL:
559 case CMD_PATHFIND:
560 allowed = true;
561 break;
562
563 case CMD_ALTER:
564 case CMD_STEAL:
565 case CMD_DISARM:
566 case CMD_JUMP:
567 case CMD_CLOSE:
568 case CMD_OPEN:
569 case CMD_TUNNEL:
570 case CMD_WALK:
571 case CMD_RUN:
572 case CMD_CAST:
573 case CMD_FIRE:
574 case CMD_THROW:
575 case CMD_USE:
576 /* Only check for ^ inscriptions, since we don't have an object
577 * selected (if we need one). */
578 allowed = key_confirm_command(cmdkey);
579 break;
580
581 default:
582 /* Invalid command; prevent anything from happening. */
583 bell("Invalid context menu command.");
584 allowed = false;
585 break;
586 }
587
588 if (!allowed)
589 return 1;
590
591 /* Perform the command. */
592 switch (selected) {
593 case MENU_VALUE_LOOK:
594 /* Look at the spot */
595 if (target_set_interactive(TARGET_LOOK, x, y)) {
596 msg("Target Selected.");
597 }
598 break;
599
600 case MENU_VALUE_RECALL: {
601 /* Recall monster Info */
602 struct monster *mon = square_monster(c, grid);
603 if (mon) {
604 struct monster_lore *lore = get_lore(mon->race);
605 lore_show_interactive(mon->race, lore);
606 }
607 }
608 break;
609
610 case CMD_PATHFIND:
611 cmdq_push(selected);
612 cmd_set_arg_point(cmdq_peek(), "point", loc(x, y));
613 break;
614
615 case CMD_ALTER:
616 case CMD_STEAL:
617 case CMD_DISARM:
618 case CMD_JUMP:
619 case CMD_CLOSE:
620 case CMD_OPEN:
621 case CMD_TUNNEL:
622 case CMD_WALK:
623 case CMD_RUN:
624 cmdq_push(selected);
625 cmd_set_arg_direction(cmdq_peek(), "direction",
626 motion_dir(player->grid, loc(x, y)));
627 break;
628
629 case CMD_CAST:
630 case CMD_FIRE:
631 case CMD_THROW:
632 case CMD_USE:
633 cmdq_push(selected);
634 cmd_set_arg_target(cmdq_peek(), "target", DIR_TARGET);
635 break;
636
637 default:
638 break;
639 }
640
641 return 1;
642 }
643
644 /**
645 * Pick the context menu options appropiate for the item
646 */
context_menu_object(struct object * obj)647 int context_menu_object(struct object *obj)
648 {
649 struct menu *m;
650 region r;
651 int selected;
652 char *labels;
653 char header[120];
654
655 textblock *tb;
656 region area = { 0, 0, 0, 0 };
657
658 bool allowed = true;
659 int mode = OPT(player, rogue_like_commands) ? KEYMAP_MODE_ROGUE : KEYMAP_MODE_ORIG;
660 unsigned char cmdkey;
661
662 m = menu_dynamic_new();
663 if (!m || !obj)
664 return 0;
665
666 object_desc(header, sizeof(header), obj, ODESC_PREFIX | ODESC_BASE);
667
668 labels = string_make(lower_case);
669 m->selections = labels;
670
671 /* 'I' is used for inspect in both keymaps. */
672 menu_dynamic_add_label(m, "Inspect", 'I', MENU_VALUE_INSPECT, labels);
673
674 if (obj_can_browse(obj)) {
675 if (obj_can_cast_from(obj) && player_can_cast(player, false))
676 ADD_LABEL("Cast", CMD_CAST, MN_ROW_VALID);
677
678 if (obj_can_study(obj) && player_can_study(player, false))
679 ADD_LABEL("Study", CMD_STUDY, MN_ROW_VALID);
680
681 if (player_can_read(player, false))
682 ADD_LABEL("Browse", CMD_BROWSE_SPELL, MN_ROW_VALID);
683 } else if (obj_is_useable(obj)) {
684 if (tval_is_wand(obj)) {
685 menu_row_validity_t valid = (obj_has_charges(obj)) ?
686 MN_ROW_VALID : MN_ROW_INVALID;
687 ADD_LABEL("Aim", CMD_USE_WAND, valid);
688 } else if (tval_is_rod(obj)) {
689 menu_row_validity_t valid = (obj_can_zap(obj)) ?
690 MN_ROW_VALID : MN_ROW_INVALID;
691 ADD_LABEL("Zap", CMD_USE_ROD, valid);
692 } else if (tval_is_staff(obj)) {
693 menu_row_validity_t valid = (obj_has_charges(obj)) ?
694 MN_ROW_VALID : MN_ROW_INVALID;
695 ADD_LABEL("Use", CMD_USE_STAFF, valid);
696 } else if (tval_is_scroll(obj)) {
697 menu_row_validity_t valid = (player_can_read(player, false)) ?
698 MN_ROW_VALID : MN_ROW_INVALID;
699 ADD_LABEL("Read", CMD_READ_SCROLL, valid);
700 } else if (tval_is_potion(obj)) {
701 ADD_LABEL("Quaff", CMD_QUAFF, MN_ROW_VALID);
702 } else if (tval_is_edible(obj)) {
703 ADD_LABEL("Eat", CMD_EAT, MN_ROW_VALID);
704 } else if (obj_is_activatable(obj)) {
705 menu_row_validity_t valid = (object_is_equipped(player->body, obj)
706 && obj_can_activate(obj)) ?
707 MN_ROW_VALID : MN_ROW_INVALID;
708 ADD_LABEL("Activate", CMD_ACTIVATE, valid);
709 } else if (obj_can_fire(obj)) {
710 ADD_LABEL("Fire", CMD_FIRE, MN_ROW_VALID);
711 } else {
712 ADD_LABEL("Use", CMD_USE, MN_ROW_VALID);
713 }
714 }
715
716 if (obj_can_refill(obj))
717 ADD_LABEL("Refill", CMD_REFILL, MN_ROW_VALID);
718
719 if (object_is_equipped(player->body, obj) && obj_can_takeoff(obj)) {
720 ADD_LABEL("Take off", CMD_TAKEOFF, MN_ROW_VALID);
721 } else if (!object_is_equipped(player->body, obj) && obj_can_wear(obj)) {
722 ADD_LABEL("Equip", CMD_WIELD, MN_ROW_VALID);
723 }
724
725 if (object_is_carried(player, obj)) {
726 if (!square_isshop(cave, player->grid)) {
727 ADD_LABEL("Drop", CMD_DROP, MN_ROW_VALID);
728
729 if (obj->number > 1) {
730 /* 'D' is used for ignore in rogue keymap, so swap letters. */
731 cmdkey = (mode == KEYMAP_MODE_ORIG) ? 'D' : 'k';
732 menu_dynamic_add_label(m, "Drop All", cmdkey,
733 MENU_VALUE_DROP_ALL, labels);
734 }
735 } else if (square_shopnum(cave, player->grid) == STORE_HOME) {
736 ADD_LABEL("Drop", CMD_DROP, MN_ROW_VALID);
737
738 if (obj->number > 1) {
739 /* 'D' is used for ignore in rogue keymap, so swap letters. */
740 cmdkey = (mode == KEYMAP_MODE_ORIG) ? 'D' : 'k';
741 menu_dynamic_add_label(m, "Drop All", cmdkey,
742 MENU_VALUE_DROP_ALL, labels);
743 }
744 } else if (store_will_buy_tester(obj)) {
745 ADD_LABEL("Sell", CMD_DROP, MN_ROW_VALID);
746 }
747 } else {
748 menu_row_validity_t valid = (inven_carry_okay(obj)) ?
749 MN_ROW_VALID : MN_ROW_INVALID;
750 ADD_LABEL("Pick up", CMD_PICKUP, valid);
751 }
752
753 ADD_LABEL("Throw", CMD_THROW, MN_ROW_VALID);
754 ADD_LABEL("Inscribe", CMD_INSCRIBE, MN_ROW_VALID);
755
756 if (obj_has_inscrip(obj))
757 ADD_LABEL("Uninscribe", CMD_UNINSCRIBE, MN_ROW_VALID);
758
759 ADD_LABEL( (object_is_ignored(obj) ? "Unignore" : "Ignore"), CMD_IGNORE,
760 MN_ROW_VALID);
761
762 /* work out display region */
763 r.width = (int)menu_dynamic_longest_entry(m) + 3 + 2; /* +3 for tag,
764 * 2 for pad */
765 r.col = Term->wid - r.width - 1;
766 r.row = 1;
767 r.page_rows = m->count;
768
769 area.width = -(r.width + 2);
770
771 /* Hack -- no flush needed */
772 msg_flag = false;
773 screen_save();
774
775 /* Display info */
776 tb = object_info(obj, OINFO_NONE);
777 object_desc(header, sizeof(header), obj, ODESC_PREFIX | ODESC_FULL);
778
779 textui_textblock_place(tb, area, format("%s", header));
780 textblock_free(tb);
781
782 menu_layout(m, &r);
783 region_erase_bordered(&r);
784
785 prt(format("(Enter to select, ESC) Command for %s:", header), 0, 0);
786 selected = menu_dynamic_select(m);
787
788 menu_dynamic_free(m);
789 string_free(labels);
790
791 screen_load();
792
793 cmdkey = cmd_lookup_key(selected, mode);
794
795 switch (selected) {
796 case -1:
797 /* User cancelled the menu. */
798 return 3;
799
800 case MENU_VALUE_INSPECT:
801 /* copied from textui_obj_examine */
802 /* Display info */
803 tb = object_info(obj, OINFO_NONE);
804 object_desc(header, sizeof(header), obj, ODESC_PREFIX | ODESC_FULL);
805
806 textui_textblock_show(tb, area, format("%s", header));
807 textblock_free(tb);
808 return 2;
809
810 case MENU_VALUE_DROP_ALL:
811 /* Drop entire stack without confirmation. */
812 if (square_isshop(cave, player->grid))
813 cmdq_push(CMD_STASH);
814 else
815 cmdq_push(CMD_DROP);
816 cmd_set_arg_item(cmdq_peek(), "item", obj);
817 cmd_set_arg_number(cmdq_peek(), "quantity", obj->number);
818 return 1;
819
820 case CMD_BROWSE_SPELL:
821 case CMD_STUDY:
822 case CMD_CAST:
823 case CMD_IGNORE:
824 case CMD_WIELD:
825 case CMD_TAKEOFF:
826 case CMD_INSCRIBE:
827 case CMD_UNINSCRIBE:
828 case CMD_PICKUP:
829 case CMD_DROP:
830 case CMD_REFILL:
831 case CMD_THROW:
832 case CMD_USE_WAND:
833 case CMD_USE_ROD:
834 case CMD_USE_STAFF:
835 case CMD_READ_SCROLL:
836 case CMD_QUAFF:
837 case CMD_EAT:
838 case CMD_ACTIVATE:
839 case CMD_FIRE:
840 case CMD_USE:
841 /* Check for inscriptions that trigger confirmation. */
842 allowed = key_confirm_command(cmdkey) &&
843 get_item_allow(obj, cmdkey, selected, false);
844 break;
845 default:
846 /* Invalid command; prevent anything from happening. */
847 bell("Invalid context menu command.");
848 allowed = false;
849 break;
850 }
851
852 if (!allowed)
853 return 1;
854
855 if (selected == CMD_IGNORE) {
856 /* ignore or unignore the item */
857 textui_cmd_ignore_menu(obj);
858 } else if (selected == CMD_BROWSE_SPELL) {
859 /* browse a spellbook */
860 /* copied from textui_spell_browse */
861 textui_book_browse(obj);
862 return 2;
863 } else if (selected == CMD_STUDY) {
864 cmdq_push(CMD_STUDY);
865 cmd_set_arg_item(cmdq_peek(), "item", obj);
866 } else if (selected == CMD_CAST) {
867 if (obj_can_cast_from(obj)) {
868 cmdq_push(CMD_CAST);
869 cmd_set_arg_item(cmdq_peek(), "book", obj);
870 }
871 } else {
872 cmdq_push(selected);
873 cmd_set_arg_item(cmdq_peek(), "item", obj);
874
875 /* If we're in a store, change the "drop" command to "stash". */
876 if (selected == CMD_DROP &&
877 square_isshop(cave, player->grid)) {
878 struct command *gc = cmdq_peek();
879 if (square_shopnum(cave, player->grid) == STORE_HOME)
880 gc->code = CMD_STASH;
881 else
882 gc->code = CMD_SELL;
883 }
884 }
885
886 return 1;
887 }
888
889
890
show_command_list(struct cmd_info cmd_list[],int size,int mx,int my)891 static int show_command_list(struct cmd_info cmd_list[], int size, int mx,
892 int my)
893 {
894 struct menu *m;
895 int selected;
896 int i;
897 char cmd_name[80];
898 char key[3];
899
900 int mode = OPT(player, rogue_like_commands) ? KEYMAP_MODE_ROGUE : KEYMAP_MODE_ORIG;
901
902 m = menu_dynamic_new();
903 if (!m) {
904 return 0;
905 }
906 m->selections = lower_case;
907 key[2] = '\0';
908
909 for (i = 0; i < size; ++i) {
910 if (KTRL(cmd_list[i].key[mode]) == cmd_list[i].key[mode]) {
911 key[0] = '^';
912 key[1] = UN_KTRL(cmd_list[i].key[mode]);
913 } else {
914 key[0] = cmd_list[i].key[mode];
915 key[1] = '\0';
916 }
917 strnfmt(cmd_name, 80, "%s (%s)", cmd_list[i].desc, key);
918 menu_dynamic_add(m, cmd_name, i+1);
919 }
920
921 menu_dynamic_calc_location(m, mx, my);
922
923 screen_save();
924 region_erase_bordered(&m->boundary);
925
926 prt("(Enter to select, ESC) Command:", 0, 0);
927 selected = menu_dynamic_select(m);
928 menu_dynamic_free(m);
929
930 screen_load();
931
932 if ((selected > 0) && (selected < size+1)) {
933 /* execute the command */
934 Term_keypress(cmd_list[selected-1].key[mode], 0);
935 }
936
937 return 1;
938 }
939
context_menu_command(int mx,int my)940 int context_menu_command(int mx, int my)
941 {
942 struct menu *m;
943 int selected;
944
945 m = menu_dynamic_new();
946 if (!m) {
947 return 0;
948 }
949
950 m->selections = lower_case;
951 menu_dynamic_add(m, "Item", 1);
952 menu_dynamic_add(m, "Action", 2);
953 menu_dynamic_add(m, "Item Management", 3);
954 menu_dynamic_add(m, "Info", 4);
955 menu_dynamic_add(m, "Util", 5);
956 menu_dynamic_add(m, "Misc", 6);
957
958 menu_dynamic_calc_location(m, mx, my);
959
960 screen_save();
961 region_erase_bordered(&m->boundary);
962
963 prt("(Enter to select, ESC) Command:", 0, 0);
964 selected = menu_dynamic_select(m);
965 menu_dynamic_free(m);
966
967 screen_load();
968
969 /* XXX-AS this is gross, as is the way there's two ways to display the
970 * entire command list. Fix me */
971 if (selected > 0) {
972 selected--;
973 show_command_list(cmds_all[selected].list, cmds_all[selected].len,
974 mx, my);
975 } else {
976 return 0;
977 }
978
979 return 1;
980 }
981
982 /**
983 * Handle a textui mouseclick.
984 */
textui_process_click(ui_event e)985 void textui_process_click(ui_event e)
986 {
987 int x, y;
988
989 if (!OPT(player, mouse_movement)) return;
990
991 y = KEY_GRID_Y(e);
992 x = KEY_GRID_X(e);
993
994 /* Check for a valid location */
995 if (!square_in_bounds_fully(cave, loc(x, y))) return;
996
997 /* XXX show context menu here */
998 if (loc_eq(player->grid, loc(x, y))) {
999 if (e.mouse.mods & KC_MOD_SHIFT) {
1000 /* shift-click - cast magic */
1001 if (e.mouse.button == 1) {
1002 cmdq_push(CMD_CAST);
1003 } else if (e.mouse.button == 2) {
1004 Term_keypress('i',0);
1005 }
1006 } else if (e.mouse.mods & KC_MOD_CONTROL) {
1007 /* ctrl-click - use feature / use inventory item */
1008 /* switch with default */
1009 if (e.mouse.button == 1) {
1010 if (square_isupstairs(cave, player->grid))
1011 cmdq_push(CMD_GO_UP);
1012 else if (square_isdownstairs(cave, player->grid))
1013 cmdq_push(CMD_GO_DOWN);
1014 } else if (e.mouse.button == 2) {
1015 cmdq_push(CMD_USE);
1016 }
1017 } else if (e.mouse.mods & KC_MOD_ALT) {
1018 /* alt-click - show char screen */
1019 /* XXX call a platform specific hook */
1020 if (e.mouse.button == 1) {
1021 Term_keypress('C',0);
1022 }
1023 } else {
1024 if (e.mouse.button == 1) {
1025 if (square_object(cave, loc(x, y))) {
1026 cmdq_push(CMD_PICKUP);
1027 } else {
1028 cmdq_push(CMD_HOLD);
1029 }
1030 } else if (e.mouse.button == 2) {
1031 /* Show a context menu */
1032 context_menu_player(e.mouse.x, e.mouse.y);
1033 }
1034 }
1035 } else if (e.mouse.button == 1) {
1036 if (player->timed[TMD_CONFUSED]) {
1037 cmdq_push(CMD_WALK);
1038 } else {
1039 if (e.mouse.mods & KC_MOD_SHIFT) {
1040 /* shift-click - run */
1041 cmdq_push(CMD_RUN);
1042 cmd_set_arg_direction(cmdq_peek(), "direction",
1043 motion_dir(player->grid, loc(x, y)));
1044 } else if (e.mouse.mods & KC_MOD_CONTROL) {
1045 /* control-click - alter */
1046 cmdq_push(CMD_ALTER);
1047 cmd_set_arg_direction(cmdq_peek(), "direction",
1048 motion_dir(player->grid, loc(x, y)));
1049 } else if (e.mouse.mods & KC_MOD_ALT) {
1050 /* alt-click - look */
1051 if (target_set_interactive(TARGET_LOOK, x, y)) {
1052 msg("Target Selected.");
1053 }
1054 } else {
1055 /* Pathfind does not work well on trap detection borders,
1056 * so if the click is next to the player, force a walk step */
1057 if ((y - player->grid.y >= -1) && (y - player->grid.y <= 1) &&
1058 (x - player->grid.x >= -1) && (x - player->grid.x <= 1)) {
1059 cmdq_push(CMD_WALK);
1060 cmd_set_arg_direction(cmdq_peek(), "direction",
1061 motion_dir(player->grid, loc(x, y)));
1062 } else {
1063 cmdq_push(CMD_PATHFIND);
1064 cmd_set_arg_point(cmdq_peek(), "point", loc(x, y));
1065 }
1066 }
1067 }
1068 } else if (e.mouse.button == 2) {
1069 struct monster *m = square_monster(cave, loc(x, y));
1070 if (m && target_able(m)) {
1071 /* Set up target information */
1072 monster_race_track(player->upkeep, m->race);
1073 health_track(player->upkeep, m);
1074 target_set_monster(m);
1075 } else {
1076 target_set_location(y, x);
1077 }
1078
1079 if (e.mouse.mods & KC_MOD_SHIFT) {
1080 /* shift-click - cast spell at target */
1081 cmdq_push(CMD_CAST);
1082 cmd_set_arg_target(cmdq_peek(), "target", DIR_TARGET);
1083 } else if (e.mouse.mods & KC_MOD_CONTROL) {
1084 /* control-click - fire at target */
1085 cmdq_push(CMD_USE);
1086 cmd_set_arg_target(cmdq_peek(), "target", DIR_TARGET);
1087 } else if (e.mouse.mods & KC_MOD_ALT) {
1088 /* alt-click - throw at target */
1089 cmdq_push(CMD_THROW);
1090 cmd_set_arg_target(cmdq_peek(), "target", DIR_TARGET);
1091 } else {
1092 /* see if the click was adjacent to the player */
1093 if ((y - player->grid.y >= -1) && (y - player->grid.y <= 1) &&
1094 (x - player->grid.x >= -1) && (x - player->grid.x <= 1)) {
1095 context_menu_cave(cave,y,x,1,e.mouse.x, e.mouse.y);
1096 } else {
1097 context_menu_cave(cave,y,x,0,e.mouse.x, e.mouse.y);
1098 }
1099 }
1100 }
1101 }
1102
1103
1104
1105
1106 /**
1107 * ------------------------------------------------------------------------
1108 * Menu functions
1109 * ------------------------------------------------------------------------ */
1110
1111 /**
1112 * Display an entry on a command menu
1113 */
cmd_sub_entry(struct menu * menu,int oid,bool cursor,int row,int col,int width)1114 static void cmd_sub_entry(struct menu *menu, int oid, bool cursor, int row,
1115 int col, int width)
1116 {
1117 byte attr = (cursor ? COLOUR_L_BLUE : COLOUR_WHITE);
1118 const struct cmd_info *commands = menu_priv(menu);
1119
1120 int mode = OPT(player, rogue_like_commands) ? KEYMAP_MODE_ROGUE : KEYMAP_MODE_ORIG;
1121 struct keypress kp = { EVT_KBRD, commands[oid].key[mode], 0 };
1122 char buf[16];
1123
1124 /* Write the description */
1125 Term_putstr(col, row, -1, attr, commands[oid].desc);
1126
1127 /* Include keypress */
1128 Term_addch(attr, L' ');
1129 Term_addch(attr, L'(');
1130
1131 /* Get readable version */
1132 keypress_to_readable(buf, sizeof buf, kp);
1133 Term_addstr(-1, attr, buf);
1134
1135 Term_addch(attr, L')');
1136 }
1137
1138 /**
1139 * Display a list of commands.
1140 */
cmd_menu(struct command_list * list,void * selection_p)1141 static bool cmd_menu(struct command_list *list, void *selection_p)
1142 {
1143 struct menu menu;
1144 menu_iter commands_menu = { NULL, NULL, cmd_sub_entry, NULL, NULL };
1145 region area = { 23, 4, 37, 13 };
1146
1147 ui_event evt;
1148 struct cmd_info **selection = selection_p;
1149
1150 /* Set up the menu */
1151 menu_init(&menu, MN_SKIN_SCROLL, &commands_menu);
1152 menu_setpriv(&menu, list->len, list->list);
1153 menu_layout(&menu, &area);
1154
1155 /* Set up the screen */
1156 screen_save();
1157 window_make(21, 3, 62, 17);
1158
1159 /* Select an entry */
1160 evt = menu_select(&menu, 0, true);
1161
1162 /* Load de screen */
1163 screen_load();
1164
1165 if (evt.type == EVT_SELECT)
1166 *selection = &list->list[menu.cursor];
1167
1168 return false;
1169 }
1170
1171
1172
cmd_list_action(struct menu * m,const ui_event * event,int oid)1173 static bool cmd_list_action(struct menu *m, const ui_event *event, int oid)
1174 {
1175 if (event->type == EVT_SELECT)
1176 return cmd_menu(&cmds_all[oid], menu_priv(m));
1177 else
1178 return false;
1179 }
1180
cmd_list_entry(struct menu * menu,int oid,bool cursor,int row,int col,int width)1181 static void cmd_list_entry(struct menu *menu, int oid, bool cursor, int row,
1182 int col, int width)
1183 {
1184 byte attr = (cursor ? COLOUR_L_BLUE : COLOUR_WHITE);
1185 Term_putstr(col, row, -1, attr, cmds_all[oid].name);
1186 }
1187
1188 static struct menu *command_menu;
1189 static menu_iter command_menu_iter =
1190 {
1191 NULL,
1192 NULL,
1193 cmd_list_entry,
1194 cmd_list_action,
1195 NULL
1196 };
1197
1198 /**
1199 * Display a list of command types, allowing the user to select one.
1200 */
textui_action_menu_choose(void)1201 struct cmd_info *textui_action_menu_choose(void)
1202 {
1203 region area = { 21, 5, 37, 6 };
1204 int len = 0;
1205
1206 struct cmd_info *chosen_command = NULL;
1207
1208 if (!command_menu)
1209 command_menu = menu_new(MN_SKIN_SCROLL, &command_menu_iter);
1210
1211 while (cmds_all[len].len) {
1212 if (cmds_all[len].len)
1213 len++;
1214 };
1215
1216 menu_setpriv(command_menu, len, &chosen_command);
1217 menu_layout(command_menu, &area);
1218
1219 /* Set up the screen */
1220 screen_save();
1221 window_make(19, 4, 58, 11);
1222
1223 menu_select(command_menu, 0, true);
1224
1225 screen_load();
1226
1227 return chosen_command;
1228 }
1229