1 /**
2  * \file ui-target.c
3  * \brief UI for targetting code
4  *
5  * Copyright (c) 1997-2014 Angband contributors
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 
19 #include "angband.h"
20 #include "cave.h"
21 #include "game-input.h"
22 #include "init.h"
23 #include "mon-desc.h"
24 #include "mon-lore.h"
25 #include "mon-predicate.h"
26 #include "monster.h"
27 #include "obj-desc.h"
28 #include "obj-pile.h"
29 #include "obj-util.h"
30 #include "player-attack.h"
31 #include "player-calcs.h"
32 #include "player-timed.h"
33 #include "project.h"
34 #include "target.h"
35 #include "trap.h"
36 #include "ui-display.h"
37 #include "ui-input.h"
38 #include "ui-keymap.h"
39 #include "ui-map.h"
40 #include "ui-mon-lore.h"
41 #include "ui-object.h"
42 #include "ui-output.h"
43 #include "ui-target.h"
44 #include "ui-term.h"
45 
46 /**
47  * Extract a direction (or zero) from a character
48  */
target_dir(struct keypress ch)49 int target_dir(struct keypress ch)
50 {
51 	return target_dir_allow(ch, false);
52 }
53 
target_dir_allow(struct keypress ch,bool allow_5)54 int target_dir_allow(struct keypress ch, bool allow_5)
55 {
56 	int d = 0;
57 
58 	/* Already a direction? */
59 	if (isdigit((unsigned char)ch.code)) {
60 		d = D2I(ch.code);
61 	} else if (isarrow(ch.code)) {
62 		switch (ch.code) {
63 			case ARROW_DOWN:  d = 2; break;
64 			case ARROW_LEFT:  d = 4; break;
65 			case ARROW_RIGHT: d = 6; break;
66 			case ARROW_UP:    d = 8; break;
67 		}
68 	} else {
69 		int mode;
70 		const struct keypress *act;
71 
72 		if (OPT(player, rogue_like_commands))
73 			mode = KEYMAP_MODE_ROGUE;
74 		else
75 			mode = KEYMAP_MODE_ORIG;
76 
77 		/* XXX see if this key has a digit in the keymap we can use */
78 		act = keymap_find(mode, ch);
79 		if (act) {
80 			const struct keypress *cur;
81 			for (cur = act; cur->type == EVT_KBRD; cur++) {
82 				if (isdigit((unsigned char) cur->code))
83 					d = D2I(cur->code);
84 			}
85 		}
86 	}
87 
88 	/* Paranoia */
89 	if (d == 5 && !allow_5) d = 0;
90 
91 	/* Return direction */
92 	return (d);
93 }
94 
95 /**
96  * Display targeting help at the bottom of the screen.
97  */
target_display_help(bool monster,bool free)98 void target_display_help(bool monster, bool free)
99 {
100 	/* Determine help location */
101 	int wid, hgt, help_loc;
102 	Term_get_size(&wid, &hgt);
103 	help_loc = hgt - HELP_HEIGHT;
104 
105 	/* Clear */
106 	clear_from(help_loc);
107 
108 	/* Prepare help hooks */
109 	text_out_hook = text_out_to_screen;
110 	text_out_indent = 1;
111 	Term_gotoxy(1, help_loc);
112 
113 	/* Display help */
114 	text_out_c(COLOUR_L_GREEN, "<dir>");
115 	text_out(" and ");
116 	text_out_c(COLOUR_L_GREEN, "<click>");
117 	text_out(" look around. '");
118 	text_out_c(COLOUR_L_GREEN, "g");
119 	text_out(" moves to the selection. '");
120 	text_out_c(COLOUR_L_GREEN, "p");
121 	text_out("' selects the player. '");
122 	text_out_c(COLOUR_L_GREEN, "q");
123 	text_out("' exits. '");
124 	text_out_c(COLOUR_L_GREEN, "r");
125 	text_out("' displays details. '");
126 
127 	if (free)
128 	{
129 		text_out_c(COLOUR_L_GREEN, "m");
130 		text_out("' restricts to interesting places. ");
131 	}
132 	else
133 	{
134 		text_out_c(COLOUR_L_GREEN, "+");
135 		text_out("' and '");
136 		text_out_c(COLOUR_L_GREEN, "-");
137 		text_out("' cycle through interesting places. '");
138 		text_out_c(COLOUR_L_GREEN, "o");
139 		text_out("' allows free selection. ");
140 	}
141 
142 	if (monster || free)
143 	{
144 		text_out("'");
145 		text_out_c(COLOUR_L_GREEN, "t");
146 		text_out("' targets the current selection.");
147 	}
148 
149 	/* Reset */
150 	text_out_indent = 0;
151 }
152 
153 
154 /**
155  * Perform the minimum "whole panel" adjustment to ensure that the given
156  * location is contained inside the current panel, and return true if any
157  * such adjustment was performed. Optionally accounts for the targeting
158  * help window.
159  */
adjust_panel_help(int y,int x,bool help)160 static bool adjust_panel_help(int y, int x, bool help)
161 {
162 	bool changed = false;
163 
164 	int j;
165 
166 	int screen_hgt_main = help ? (Term->hgt - ROW_MAP - 3)
167 			 : (Term->hgt - ROW_MAP - 1);
168 
169 	/* Scan windows */
170 	for (j = 0; j < ANGBAND_TERM_MAX; j++)
171 	{
172 		int wx, wy;
173 		int screen_hgt, screen_wid;
174 
175 		term *t = angband_term[j];
176 
177 		/* No window */
178 		if (!t) continue;
179 
180 		/* No relevant flags */
181 		if ((j > 0) && !(window_flag[j] & PW_MAPS)) continue;
182 
183 		wy = t->offset_y;
184 		wx = t->offset_x;
185 
186 		screen_hgt = (j == 0) ? screen_hgt_main : t->hgt;
187 		screen_wid = (j == 0) ? (Term->wid - COL_MAP - 1) : t->wid;
188 
189 		/* Bigtile panels need adjustment */
190 		screen_wid = screen_wid / tile_width;
191 		screen_hgt = screen_hgt / tile_height;
192 
193 		/* Adjust as needed */
194 		while (y >= wy + screen_hgt) wy += screen_hgt / 2;
195 		while (y < wy) wy -= screen_hgt / 2;
196 
197 		/* Adjust as needed */
198 		while (x >= wx + screen_wid) wx += screen_wid / 2;
199 		while (x < wx) wx -= screen_wid / 2;
200 
201 		/* Use "modify_panel" */
202 		if (modify_panel(t, wy, wx)) changed = true;
203 	}
204 
205 	return (changed);
206 }
207 
208 
209 /**
210  * Display the object name of the selected object and allow for full object
211  * recall. Returns an event that occurred display.
212  *
213  * This will only work for a single object on the ground and not a pile. This
214  * loop is similar to the monster recall loop in target_set_interactive_aux().
215  * The out_val array size needs to match the size that is passed in (since
216  * this code was extracted from there).
217  *
218  * \param obj is the object to describe.
219  * \param y is the cave row of the object.
220  * \param x is the cave column of the object.
221  * \param out_val is the string that holds the name of the object and is
222  * returned to the caller.
223  * \param s1 is part of the output string.
224  * \param s2 is part of the output string.
225  * \param s3 is part of the output string.
226  * \param coords is part of the output string
227  */
target_recall_loop_object(struct object * obj,int y,int x,char out_val[TARGET_OUT_VAL_SIZE],const char * s1,const char * s2,const char * s3,char * coords)228 static ui_event target_recall_loop_object(struct object *obj, int y, int x,
229 										  char out_val[TARGET_OUT_VAL_SIZE],
230 										  const char *s1, const char *s2,
231 										  const char *s3, char *coords)
232 {
233 	bool recall = false;
234 	ui_event press;
235 
236 	while (1) {
237 		if (recall) {
238 			display_object_recall_interactive(cave->objects[obj->oidx]);
239 			press = inkey_m();
240 		} else {
241 			char o_name[80];
242 
243 			/* Obtain an object description */
244 			object_desc(o_name, sizeof(o_name), cave->objects[obj->oidx],
245 						ODESC_PREFIX | ODESC_FULL);
246 
247 			/* Describe the object */
248 			if (player->wizard) {
249 				strnfmt(out_val, TARGET_OUT_VAL_SIZE,
250 						"%s%s%s%s, %s (%d:%d, noise=%d, scent=%d).", s1, s2, s3,
251 						o_name, coords, y, x, (int)cave->noise.grids[y][x],
252 						(int)cave->scent.grids[y][x]);
253 			} else {
254 				strnfmt(out_val, TARGET_OUT_VAL_SIZE,
255 						"%s%s%s%s, %s.", s1, s2, s3, o_name, coords);
256 			}
257 
258 			prt(out_val, 0, 0);
259 			move_cursor_relative(y, x);
260 			press = inkey_m();
261 		}
262 
263 		if ((press.type == EVT_MOUSE) && (press.mouse.button == 1) &&
264 			(KEY_GRID_X(press) == x) && (KEY_GRID_Y(press) == y))
265 			recall = !recall;
266 		else if ((press.type == EVT_KBRD) && (press.key.code == 'r'))
267 			recall = !recall;
268 		else
269 			break;
270 	}
271 
272 	return press;
273 }
274 
275 /**
276  * Examine a grid, return a keypress.
277  *
278  * The "mode" argument contains the "TARGET_LOOK" bit flag, which
279  * indicates that the "space" key should scan through the contents
280  * of the grid, instead of simply returning immediately.  This lets
281  * the "look" command get complete information, without making the
282  * "target" command annoying.
283  *
284  * The "info" argument contains the "commands" which should be shown
285  * inside the "[xxx]" text.  This string must never be empty, or grids
286  * containing monsters will be displayed with an extra comma.
287  *
288  * Note that if a monster is in the grid, we update both the monster
289  * recall info and the health bar info to track that monster.
290  *
291  * This function correctly handles multiple objects per grid, and objects
292  * and terrain features in the same grid, though the latter never happens.
293  *
294  * This function must handle blindness/hallucination.
295  */
target_set_interactive_aux(int y,int x,int mode)296 static ui_event target_set_interactive_aux(int y, int x, int mode)
297 {
298 	struct object *obj = NULL;
299 
300 	const char *s1, *s2, *s3;
301 
302 	bool boring;
303 
304 	int floor_max = z_info->floor_size;
305 	struct object **floor_list = mem_zalloc(floor_max * sizeof(*floor_list));
306 	int floor_num;
307 
308 	ui_event press;
309 
310 	char out_val[TARGET_OUT_VAL_SIZE];
311 
312 	char coords[20];
313 
314 	const char *name;
315 
316 	/* Describe the square location */
317 	coords_desc(coords, sizeof(coords), y, x);
318 
319 	/* Repeat forever */
320 	while (1) {
321 		/* Make the default event to focus on the player */
322 		press.type = EVT_KBRD;
323 		press.key.code = 'p';
324 		press.key.mods = 0;
325 
326 		/* Assume boring */
327 		boring = true;
328 
329 		/* Default */
330 		s1 = "You see ";
331 		s2 = "";
332 		s3 = "";
333 
334 		/* Bail if looking at a forbidden grid */
335 		if (!square_in_bounds(cave, loc(x, y))) {
336 			break;
337 		}
338 
339 		/* The player */
340 		if (square(cave, loc(x, y))->mon < 0) {
341 			/* Description */
342 			s1 = "You are ";
343 
344 			/* Preposition */
345 			s2 = "on ";
346 		}
347 
348 		/* Hallucination messes things up */
349 		if (player->timed[TMD_IMAGE]) {
350 			const char *name_strange = "something strange";
351 
352 			/* Display a message */
353 			if (player->wizard)
354 				strnfmt(out_val, sizeof(out_val),
355 						"%s%s%s%s, %s (%d:%d, noise=%d, scent=%d).", s1, s2, s3,
356 						name_strange, coords, y, x, (int)cave->noise.grids[y][x],
357 						(int)cave->scent.grids[y][x]);
358 			else
359 				strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.",
360 						s1, s2, s3, name_strange, coords);
361 
362 			prt(out_val, 0, 0);
363 			move_cursor_relative(y, x);
364 
365 			press.key = inkey();
366 
367 			/* Stop on everything but "return" */
368 			if (press.key.code == KC_ENTER)
369 				continue;
370 
371 			mem_free(floor_list);
372 			return press;
373 		}
374 
375 		/* Actual monsters */
376 		if (square(cave, loc(x, y))->mon > 0) {
377 			struct monster *mon = square_monster(cave, loc(x, y));
378 			const struct monster_lore *lore = get_lore(mon->race);
379 
380 			/* Visible */
381 			if (monster_is_obvious(mon)) {
382 				bool recall = false;
383 
384 				char m_name[80];
385 
386 				/* Not boring */
387 				boring = false;
388 
389 				/* Get the monster name ("a kobold") */
390 				monster_desc(m_name, sizeof(m_name), mon, MDESC_IND_VIS);
391 
392 				/* Hack -- track this monster race */
393 				monster_race_track(player->upkeep, mon->race);
394 
395 				/* Hack -- health bar for this monster */
396 				health_track(player->upkeep, mon);
397 
398 				/* Hack -- handle stuff */
399 				handle_stuff(player);
400 
401 				/* Interact */
402 				while (1) {
403 					/* Recall or target */
404 					if (recall) {
405 						lore_show_interactive(mon->race, lore);
406 						press = inkey_m();
407 					} else {
408 						char buf[80];
409 
410 						/* Describe the monster */
411 						look_mon_desc(buf, sizeof(buf),
412 									  square(cave, loc(x, y))->mon);
413 
414 						/* Describe, and prompt for recall */
415 						if (player->wizard) {
416 							strnfmt(out_val, sizeof(out_val),
417 									"%s%s%s%s (%s), %s (%d:%d, noise=%d, scent=%d).",
418 									s1, s2, s3, m_name, buf, coords, y, x,
419 									(int)cave->noise.grids[y][x],
420 									(int)cave->scent.grids[y][x]);
421 						} else {
422 							strnfmt(out_val, sizeof(out_val),
423 									"%s%s%s%s (%s), %s.",
424 									s1, s2, s3, m_name, buf, coords);
425 						}
426 
427 						prt(out_val, 0, 0);
428 
429 						/* Place cursor */
430 						move_cursor_relative(y, x);
431 
432 						/* Command */
433 						press = inkey_m();
434 					}
435 
436 					/* Normal commands */
437 					if ((press.type == EVT_MOUSE) && (press.mouse.button == 1)
438 						&& (KEY_GRID_X(press) == x) && (KEY_GRID_Y(press) == y))
439 						recall = !recall;
440 					else
441 					if ((press.type == EVT_KBRD) && (press.key.code == 'r'))
442 						recall = !recall;
443 					else
444 						break;
445 				}
446 
447 				if (press.type == EVT_MOUSE) {
448 					/* Stop on right click */
449 					if (press.mouse.button == 2)
450 						break;
451 
452 					/* Sometimes stop at "space" key */
453 					if (press.mouse.button && !(mode & (TARGET_LOOK))) break;
454 				} else {
455 					/* Stop on everything but "return"/"space" */
456 					if (press.key.code != KC_ENTER && press.key.code != ' ')
457 						break;
458 
459 					/* Sometimes stop at "space" key */
460 					if ((press.key.code == ' ') && !(mode & (TARGET_LOOK)))
461 						break;
462 				}
463 
464 				/* Take account of gender */
465 				if (rf_has(mon->race->flags, RF_FEMALE)) s1 = "She is ";
466 				else if (rf_has(mon->race->flags, RF_MALE)) s1 = "He is ";
467 				else s1 = "It is ";
468 
469 				/* Describe carried objects (wizards only) */
470 				if (player->wizard) {
471 					/* Use a verb */
472 					s2 = "carrying ";
473 
474 					/* Scan all objects being carried */
475 					for (obj = mon->held_obj; obj; obj = obj->next) {
476 						char o_name[80];
477 
478 						/* Obtain an object description */
479 						object_desc(o_name, sizeof(o_name), obj,
480 									ODESC_PREFIX | ODESC_FULL);
481 
482 						strnfmt(out_val, sizeof(out_val),
483 								"%s%s%s%s, %s (%d:%d, noise=%d, scent=%d).",
484 								s1, s2, s3, o_name, coords, y, x,
485 								(int)cave->noise.grids[y][x],
486 								(int)cave->scent.grids[y][x]);
487 
488 						prt(out_val, 0, 0);
489 						move_cursor_relative(y, x);
490 						press = inkey_m();
491 
492 						if (press.type == EVT_MOUSE) {
493 							/* Stop on right click */
494 							if (press.mouse.button == 2)
495 								break;
496 
497 							/* Sometimes stop at "space" key */
498 							if (press.mouse.button && !(mode & (TARGET_LOOK)))
499 								break;
500 						} else {
501 							/* Stop on everything but "return"/"space" */
502 							if ((press.key.code != KC_ENTER) &&
503 								(press.key.code != ' '))
504 								break;
505 
506 							/* Sometimes stop at "space" key */
507 							if ((press.key.code == ' ') &&
508 								!(mode & (TARGET_LOOK)))
509 								break;
510 						}
511 
512 						/* Change the intro */
513 						s2 = "also carrying ";
514 					}
515 				}
516 
517 				/* Double break */
518 				if (obj) break;
519 
520 				/* Use a preposition */
521 				s2 = "on ";
522 			}
523 		}
524 
525 		/* A trap */
526 		if (square_isvisibletrap(cave, loc(x, y))) {
527 			struct trap *trap = square(cave, loc(x, y))->trap;
528 
529 			/* Not boring */
530 			boring = false;
531 
532 			/* Interact */
533 			while (1) {
534 				/* Change the intro */
535 				if (square(cave, loc(x, y))->mon < 0) {
536 					s1 = "You are ";
537 					s2 = "on ";
538 				} else {
539 					s1 = "You see ";
540 					s2 = "";
541 				}
542 
543 				/* Pick proper indefinite article */
544 				s3 = (is_a_vowel(trap->kind->desc[0])) ? "an " : "a ";
545 
546 				/* Describe, and prompt for recall */
547 				if (player->wizard) {
548 					strnfmt(out_val, sizeof(out_val),
549 							"%s%s%s%s, %s (%d:%d, noise=%d, scent=%d).", s1, s2,
550 							s3, trap->kind->name, coords, y, x,
551 							(int)cave->noise.grids[y][x],
552 							(int)cave->scent.grids[y][x]);
553 				} else {
554 					strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.",
555 							s1, s2, s3, trap->kind->desc, coords);
556 				}
557 
558 				prt(out_val, 0, 0);
559 
560 				/* Place cursor */
561 				move_cursor_relative(y, x);
562 
563 				/* Command */
564 				press = inkey_m();
565 
566 				/* Stop on everything but "return"/"space" */
567 				if ((press.key.code != KC_ENTER) && (press.key.code != ' '))
568 					break;
569 
570 				/* Sometimes stop at "space" key */
571 				if ((press.key.code == ' ') && !(mode & (TARGET_LOOK)))
572 					break;
573 			}
574 		}
575 
576 		/* Double break */
577 		if (square_isvisibletrap(cave, loc(x, y)))
578 			break;
579 
580 		/* Scan all sensed objects in the grid */
581 		floor_num = scan_distant_floor(floor_list, floor_max, loc(x, y));
582 		if ((floor_num > 0) &&
583 		    (!(player->timed[TMD_BLIND]) || loc_eq(loc(x, y), player->grid))) {
584 			/* Not boring */
585 			boring = false;
586 
587 			track_object(player->upkeep, floor_list[0]);
588 			handle_stuff(player);
589 
590 			/* If there is more than one item... */
591 			if (floor_num > 1)
592 				while (1) {
593 					/* Describe the pile */
594 					if (player->wizard) {
595 						strnfmt(out_val, sizeof(out_val),
596 								"%s%s%sa pile of %d objects, %s (%d:%d, noise=%d, scent=%d).",
597 								s1, s2, s3, floor_num, coords, y, x,
598 								(int)cave->noise.grids[y][x],
599 								(int)cave->scent.grids[y][x]);
600 					} else {
601 						strnfmt(out_val, sizeof(out_val),
602 								"%s%s%sa pile of %d objects, %s.",
603 								s1, s2, s3, floor_num, coords);
604 					}
605 
606 					prt(out_val, 0, 0);
607 					move_cursor_relative(y, x);
608 					press = inkey_m();
609 
610 					/* Display objects */
611 					if (((press.type == EVT_MOUSE) && (press.mouse.button == 1)
612 						 && (KEY_GRID_X(press) == x) &&
613 						 (KEY_GRID_Y(press) == y)) ||
614 						((press.type == EVT_KBRD) && (press.key.code == 'r'))) {
615 						int rdone = 0;
616 						int pos;
617 						while (!rdone) {
618 							/* Save screen */
619 							screen_save();
620 
621 							/* Display */
622 							show_floor(floor_list, floor_num,
623 									   (OLIST_WEIGHT | OLIST_GOLD), NULL);
624 
625 							/* Describe the pile */
626 							prt(out_val, 0, 0);
627 							press = inkey_m();
628 
629 							/* Load screen */
630 							screen_load();
631 
632 							if (press.type == EVT_MOUSE) {
633 								pos = press.mouse.y-1;
634 							} else {
635 								pos = press.key.code - 'a';
636 							}
637 							if (0 <= pos && pos < floor_num) {
638 								track_object(player->upkeep, floor_list[pos]);
639 								handle_stuff(player);
640 								continue;
641 							}
642 							rdone = 1;
643 						}
644 
645 						/* Now that the user's done with the display loop,
646 						 * let's do the outer loop over again */
647 						continue;
648 					}
649 
650 					/* Done */
651 					break;
652 				}
653 			/* Only one object to display */
654 			else {
655 				/* Get the single object in the list */
656 				struct object *obj_local = floor_list[0];
657 
658 				/* Allow user to recall an object */
659 				press = target_recall_loop_object(obj_local, y, x, out_val, s1, s2,
660 												  s3, coords);
661 
662 				/* Stop on everything but "return"/"space" */
663 				if ((press.key.code != KC_ENTER) && (press.key.code != ' '))
664 					break;
665 
666 				/* Sometimes stop at "space" key */
667 				if ((press.key.code == ' ') && !(mode & (TARGET_LOOK))) break;
668 
669 				/* Plurals */
670 				s1 = VERB_AGREEMENT(obj_local->number, "It is ", "They are ");
671 
672 				/* Preposition */
673 				s2 = "on ";
674 			}
675 
676 		}
677 
678 		/* Double break */
679 		if (obj) break;
680 
681 		name = square_apparent_name(cave, player, loc(x, y));
682 
683 		/* Terrain feature if needed */
684 		if (boring || square_isinteresting(cave, loc(x, y))) {
685 			/* Hack -- handle unknown grids */
686 
687 			/* Pick a preposition if needed */
688 			if (*s2) s2 = square_apparent_look_in_preposition(cave, player, loc(x, y));
689 
690 			/* Pick prefix for the name */
691 			s3 = square_apparent_look_prefix(cave, player, loc(x, y));
692 
693 			/* Display a message */
694 			if (player->wizard) {
695 				strnfmt(out_val, sizeof(out_val),
696 						"%s%s%s%s, %s (%d:%d, noise=%d, scent=%d).", s1, s2, s3,
697 						name, coords, y, x, (int)cave->noise.grids[y][x],
698 						(int)cave->scent.grids[y][x]);
699 			} else {
700 				strnfmt(out_val, sizeof(out_val),
701 						"%s%s%s%s, %s.", s1, s2, s3, name, coords);
702 			}
703 
704 			prt(out_val, 0, 0);
705 			move_cursor_relative(y, x);
706 			press = inkey_m();
707 
708 			if (press.type == EVT_MOUSE) {
709 				/* Stop on right click */
710 				if (press.mouse.button == 2)
711 					break;
712 			} else {
713 				/* Stop on everything but "return"/"space" */
714 				if ((press.key.code != KC_ENTER) && (press.key.code != ' '))
715 					break;
716 			}
717 		}
718 
719 		/* Stop on everything but "return" */
720 		if (press.type == EVT_MOUSE) {
721 				/* Stop on right click */
722 				if (press.mouse.button != 2)
723 					break;
724 		} else {
725     			if (press.key.code != KC_ENTER) break;
726 		}
727 	}
728 
729 	mem_free(floor_list);
730 
731 	/* Keep going */
732 	return (press);
733 }
734 
735 /**
736  * Target command
737  */
textui_target(void)738 void textui_target(void)
739 {
740 	if (target_set_interactive(TARGET_KILL, -1, -1))
741 		msg("Target Selected.");
742 	else
743 		msg("Target Aborted.");
744 }
745 
746 /**
747  * Target closest monster.
748  *
749  * XXX: Move to using CMD_TARGET_CLOSEST at some point instead of invoking
750  * target_set_closest() directly.
751  */
textui_target_closest(void)752 void textui_target_closest(void)
753 {
754 	if (target_set_closest(TARGET_KILL, NULL)) {
755 		bool visibility;
756 		struct loc target;
757 
758 		target_get(&target);
759 
760 		/* Visual cue */
761 		Term_fresh();
762 		Term_get_cursor(&visibility);
763 		(void)Term_set_cursor(true);
764 		move_cursor_relative(target.y, target.x);
765 		Term_redraw_section(target.y, target.x, target.y, target.x);
766 
767 		/* TODO: what's an appropriate amount of time to spend highlighting */
768 		Term_xtra(TERM_XTRA_DELAY, 150);
769 		(void)Term_set_cursor(visibility);
770 	}
771 }
772 
773 
774 /**
775  * Draw a visible path over the squares between (x1,y1) and (x2,y2).
776  *
777  * The path consists of "*", which are white except where there is a
778  * monster, object or feature in the grid.
779  *
780  * This routine has (at least) three weaknesses:
781  * - remembered objects/walls which are no longer present are not shown,
782  * - squares which (e.g.) the player has walked through in the dark are
783  *   treated as unknown space.
784  * - walls which appear strange due to hallucination aren't treated correctly.
785  *
786  * The first two result from information being lost from the dungeon arrays,
787  * which requires changes elsewhere
788  */
draw_path(u16b path_n,struct loc * path_g,wchar_t * c,int * a,int y1,int x1)789 static int draw_path(u16b path_n, struct loc *path_g, wchar_t *c, int *a,
790 					 int y1, int x1)
791 {
792 	int i;
793 	bool on_screen;
794 	bool pastknown = false;
795 
796 	/* No path, so do nothing. */
797 	if (path_n < 1) return 0;
798 
799 	/* The starting square is never drawn, but notice if it is being
800      * displayed. In theory, it could be the last such square.
801      */
802 	on_screen = panel_contains(y1, x1);
803 
804 	/* Draw the path. */
805 	for (i = 0; i < path_n; i++) {
806 		byte colour;
807 
808 		/* Find the co-ordinates on the level. */
809 		struct loc grid = path_g[i];
810 		struct monster *mon = square_monster(cave, grid);
811 		struct object *obj = square_object(player->cave, grid);
812 
813 		/*
814 		 * As path[] is a straight line and the screen is oblong,
815 		 * there is only section of path[] on-screen.
816 		 * If the square being drawn is visible, this is part of it.
817 		 * If none of it has been drawn, continue until some of it
818 		 * is found or the last square is reached.
819 		 * If some of it has been drawn, finish now as there are no
820 		 * more visible squares to draw.
821 		 */
822 		 if (panel_contains(grid.y, grid.x)) on_screen = true;
823 		 else if (on_screen) break;
824 		 else continue;
825 
826 	 	/* Find the position on-screen */
827 		move_cursor_relative(grid.y, grid.x);
828 
829 		/* This square is being overwritten, so save the original. */
830 		Term_what(Term->scr->cx, Term->scr->cy, a + i, c + i);
831 
832 		/* Choose a colour. */
833 		if (pastknown) {
834 			/* Once we pass an unknown square, we no longer know
835 			 * if we will reach later squares */
836 			colour = COLOUR_L_DARK;
837 		} else if (mon && monster_is_visible(mon)) {
838 			/* Mimics act as objects */
839 			if (monster_is_camouflaged(mon))
840 				colour = COLOUR_YELLOW;
841 			else
842 				/* Visible monsters are red. */
843 				colour = COLOUR_L_RED;
844 		} else if (obj)
845 			/* Known objects are yellow. */
846 			colour = COLOUR_YELLOW;
847 
848 		else if (!square_isprojectable(cave, grid) &&
849 				 (square_isknown(cave, grid) || square_isseen(cave, grid)))
850 			/* Known walls are blue. */
851 			colour = COLOUR_BLUE;
852 
853 		else if (!square_isknown(cave, grid) && !square_isseen(cave, grid)) {
854 			/* Unknown squares are grey. */
855 			pastknown = true;
856 			colour = COLOUR_L_DARK;
857 
858 		} else
859 			/* Unoccupied squares are white. */
860 			colour = COLOUR_WHITE;
861 
862 		/* Draw the path segment */
863 		(void)Term_addch(colour, L'*');
864 	}
865 	return i;
866 }
867 
868 
869 /**
870  * Load the attr/char at each point along "path" which is on screen from
871  * "a" and "c". This was saved in draw_path().
872  */
load_path(u16b path_n,struct loc * path_g,wchar_t * c,int * a)873 static void load_path(u16b path_n, struct loc *path_g, wchar_t *c, int *a)
874 {
875 	int i;
876 	for (i = 0; i < path_n; i++) {
877 		int y = path_g[i].y;
878 		int x = path_g[i].x;
879 
880 		if (!panel_contains(y, x)) continue;
881 		move_cursor_relative(y, x);
882 		Term_addch(a[i], c[i]);
883 	}
884 
885 	Term_fresh();
886 }
887 
888 
889 /**
890  * Handle "target" and "look".
891  *
892  * Note that this code can be called from "get_aim_dir()".
893  *
894  * Currently, when "flag" is true, that is, when
895  * "interesting" grids are being used, and a directional key is used, we
896  * only scroll by a single panel, in the direction requested, and check
897  * for any interesting grids on that panel.  The "correct" solution would
898  * actually involve scanning a larger set of grids, including ones in
899  * panels which are adjacent to the one currently scanned, but this is
900  * overkill for this function.  XXX XXX
901  *
902  * Hack -- targetting/observing an "outer border grid" may induce
903  * problems, so this is not currently allowed.
904  *
905  * The player can use the direction keys to move among "interesting"
906  * grids in a heuristic manner, or the "space", "+", and "-" keys to
907  * move through the "interesting" grids in a sequential manner, or
908  * can enter "location" mode, and use the direction keys to move one
909  * grid at a time in any direction.  The "t" (set target) command will
910  * only target a monster (as opposed to a location) if the monster is
911  * target_able and the "interesting" mode is being used.
912  *
913  * The current grid is described using the "look" method above, and
914  * a new command may be entered at any time, but note that if the
915  * "TARGET_LOOK" bit flag is set (or if we are in "location" mode,
916  * where "space" has no obvious meaning) then "space" will scan
917  * through the description of the current grid until done, instead
918  * of immediately jumping to the next "interesting" grid.  This
919  * allows the "target" command to retain its old semantics.
920  *
921  * The "*", "+", and "-" keys may always be used to jump immediately
922  * to the next (or previous) interesting grid, in the proper mode.
923  *
924  * The "return" key may always be used to scan through a complete
925  * grid description (forever).
926  *
927  * This command will cancel any old target, even if used from
928  * inside the "look" command.
929  *
930  *
931  * 'mode' is one of TARGET_LOOK or TARGET_KILL.
932  * 'x' and 'y' are the initial position of the target to be highlighted,
933  * or -1 if no location is specified.
934  * Returns true if a target has been successfully set, false otherwise.
935  */
target_set_interactive(int mode,int x,int y)936 bool target_set_interactive(int mode, int x, int y)
937 {
938 	int py = player->grid.y;
939 	int px = player->grid.x;
940 
941 	int path_n;
942 	struct loc path_g[256];
943 
944 	int i, d, m, t, bd;
945 	int wid, hgt, help_prompt_loc;
946 
947 	bool done = false;
948 	bool flag = true;
949 	bool help = false;
950 
951 	ui_event press;
952 
953 	/* These are used for displaying the path to the target */
954 	wchar_t *path_char = mem_zalloc(z_info->max_range * sizeof(wchar_t));
955 	int *path_attr = mem_zalloc(z_info->max_range * sizeof(int));
956 	struct point_set *targets;
957 
958 	/* If we haven't been given an initial location, start on the
959 	   player, otherwise  honour it by going into "free targetting" mode. */
960 	if (x == -1 || y == -1 || !square_in_bounds_fully(cave, loc(x, y))) {
961 		x = player->grid.x;
962 		y = player->grid.y;
963 	} else {
964 		flag = false;
965 	}
966 
967 	/* Cancel target */
968 	target_set_monster(0);
969 
970 	/* Prevent animations */
971 	disallow_animations();
972 
973 	/* Calculate the window location for the help prompt */
974 	Term_get_size(&wid, &hgt);
975 	help_prompt_loc = hgt - 1;
976 
977 	/* Display the help prompt */
978 	prt("Press '?' for help.", help_prompt_loc, 0);
979 
980 	/* Prepare the target set */
981 	targets = target_get_monsters(mode, NULL);
982 
983 	/* Start near the player */
984 	m = 0;
985 
986 	/* Interact */
987 	while (!done) {
988 		bool path_drawn = false;
989 
990 		/* Interesting grids if chosen and there are any, otherwise arbitrary */
991 		if (flag && point_set_size(targets)) {
992 			y = targets->pts[m].y;
993 			x = targets->pts[m].x;
994 
995 			/* Adjust panel if needed */
996 			if (adjust_panel_help(y, x, help)) handle_stuff(player);
997 
998 			/* Update help */
999 			if (help) {
1000 				bool good_target =
1001 					target_able(square_monster(cave, targets->pts[m]));
1002 				target_display_help(good_target,
1003 									!(flag && point_set_size(targets)));
1004 			}
1005 
1006 			/* Find the path. */
1007 			path_n = project_path(path_g, z_info->max_range, loc(px, py),
1008 								  loc(x, y), PROJECT_THRU | PROJECT_INFO);
1009 
1010 			/* Draw the path in "target" mode. If there is one */
1011 			if (mode & (TARGET_KILL))
1012 				path_drawn = draw_path(path_n, path_g, path_char, path_attr,
1013 									   py, px);
1014 
1015 			/* Describe and Prompt */
1016 			press = target_set_interactive_aux(y, x, mode);
1017 
1018 			/* Remove the path */
1019 			if (path_drawn) load_path(path_n, path_g, path_char, path_attr);
1020 
1021 			/* Assume no "direction" */
1022 			d = 0;
1023 
1024 
1025 			/* Analyze */
1026 			if (press.type == EVT_MOUSE) {
1027 				if (press.mouse.button == 3) {
1028 					/* give the target selection command */
1029 					press.mouse.button = 2;
1030 					press.mouse.mods = KC_MOD_CONTROL;
1031 				}
1032 				if (press.mouse.button == 2) {
1033 					y = KEY_GRID_Y(press);
1034 					x = KEY_GRID_X(press);
1035 					if (press.mouse.mods & KC_MOD_CONTROL) {
1036 						/* same as keyboard target selection command below */
1037 						struct monster *m_local = square_monster(cave, loc(x, y));
1038 
1039 						if (target_able(m_local)) {
1040 							/* Set up target information */
1041 							monster_race_track(player->upkeep, m_local->race);
1042 							health_track(player->upkeep, m_local);
1043 							target_set_monster(m_local);
1044 							done = true;
1045 						} else {
1046 							bell("Illegal target!");
1047 							/*
1048 							 * So there's something
1049 							 * to work with in the
1050 							 * next pass through
1051 							 * the loop.
1052 							 */
1053 							if (!square_in_bounds(cave, loc(x, y))) {
1054 							    x = player->grid.x;
1055 							    y = player->grid.y;
1056 							}
1057 						}
1058 					} else if (press.mouse.mods & KC_MOD_ALT) {
1059 						/* go to spot - same as 'g' command below */
1060 						cmdq_push(CMD_PATHFIND);
1061 						cmd_set_arg_point(cmdq_peek(), "point", loc(x, y));
1062 						done = true;
1063 					} else {
1064 						/* cancel look mode */
1065 						done = true;
1066 					}
1067 				} else {
1068 					y = KEY_GRID_Y(press);
1069 					x = KEY_GRID_X(press);
1070 					if (square_monster(cave, loc(x, y)) ||
1071 						square_object(cave, loc(x, y))) {
1072 							/* reset the flag, to make sure we stay in this
1073 							 * mode if something is actually there */
1074 						flag = false;
1075 						/* scan the interesting list and see if there is
1076 						 * anything here */
1077 						for (i = 0; i < point_set_size(targets); i++) {
1078 							if ((y == targets->pts[i].y) &&
1079 								(x == targets->pts[i].x)) {
1080 								m = i;
1081 								flag = true;
1082 								break;
1083 							}
1084 						}
1085 					} else {
1086 						flag = false;
1087 						if (! square_in_bounds(cave, loc(x, y))) {
1088 						    x = player->grid.x;
1089 						    y = player->grid.y;
1090 						}
1091 					}
1092 				}
1093 			} else
1094 				switch (press.key.code)
1095 				{
1096 					case ESCAPE:
1097 					case 'q':
1098 					{
1099 						done = true;
1100 						break;
1101 					}
1102 
1103 					case ' ':
1104 					case '*':
1105 					case '+':
1106 					{
1107 						if (++m == point_set_size(targets))
1108 							m = 0;
1109 
1110 						break;
1111 					}
1112 
1113 					case '-':
1114 					{
1115 						if (m-- == 0)
1116 							m = point_set_size(targets) - 1;
1117 
1118 						break;
1119 					}
1120 
1121 					case 'p':
1122 					{
1123 						/* Recenter around player */
1124 						verify_panel();
1125 
1126 						/* Handle stuff */
1127 						handle_stuff(player);
1128 
1129 						y = player->grid.y;
1130 						x = player->grid.x;
1131 						flag = false;
1132 						break;
1133 					}
1134 
1135 					case 'o':
1136 					{
1137 						flag = false;
1138 						break;
1139 					}
1140 
1141 					case 'm':
1142 					{
1143 						break;
1144 					}
1145 
1146 					case 't':
1147 					case '5':
1148 					case '0':
1149 					case '.':
1150 					{
1151 						struct monster *m_local = square_monster(cave, loc(x, y));
1152 
1153 						if (target_able(m_local)) {
1154 							health_track(player->upkeep, m_local);
1155 							target_set_monster(m_local);
1156 							done = true;
1157 						} else {
1158 							bell("Illegal target!");
1159 						}
1160 						break;
1161 					}
1162 
1163 					case 'g':
1164 					{
1165 						cmdq_push(CMD_PATHFIND);
1166 						cmd_set_arg_point(cmdq_peek(), "point", loc(x, y));
1167 						done = true;
1168 						break;
1169 					}
1170 
1171 					case '?':
1172 					{
1173 						help = !help;
1174 
1175 						/* Redraw main window */
1176 						player->upkeep->redraw |= (PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIP);
1177 						Term_clear();
1178 						handle_stuff(player);
1179 						if (!help)
1180 							prt("Press '?' for help.", help_prompt_loc, 0);
1181 
1182 						break;
1183 					}
1184 
1185 					default:
1186 					{
1187 						/* Extract direction */
1188 						d = target_dir(press.key);
1189 
1190 						/* Oops */
1191 						if (!d) bell("Illegal command for target mode!");
1192 
1193 						break;
1194 					}
1195 				}
1196 
1197 			/* Hack -- move around */
1198 			if (d) {
1199 				int old_y = targets->pts[m].y;
1200 				int old_x = targets->pts[m].x;
1201 
1202 				/* Find a new monster */
1203 				i = target_pick(old_y, old_x, ddy[d], ddx[d], targets);
1204 
1205 				/* Scroll to find interesting grid */
1206 				if (i < 0) {
1207 					int old_wy = Term->offset_y;
1208 					int old_wx = Term->offset_x;
1209 
1210 					/* Change if legal */
1211 					if (change_panel(d)) {
1212 						/* Recalculate interesting grids */
1213 						point_set_dispose(targets);
1214 						targets = target_get_monsters(mode, NULL);
1215 
1216 						/* Find a new monster */
1217 						i = target_pick(old_y, old_x, ddy[d], ddx[d], targets);
1218 
1219 						/* Restore panel if needed */
1220 						if ((i < 0) && modify_panel(Term, old_wy, old_wx)) {
1221 							/* Recalculate interesting grids */
1222 							point_set_dispose(targets);
1223 							targets = target_get_monsters(mode, NULL);
1224 						}
1225 
1226 						/* Handle stuff */
1227 						handle_stuff(player);
1228 					}
1229 				}
1230 
1231 				/* Use interesting grid if found */
1232 				if (i >= 0) m = i;
1233 			}
1234 		} else {
1235 			/* Update help */
1236 			if (help) {
1237 				bool good_target = target_able(square_monster(cave, loc(x, y)));
1238 				target_display_help(good_target,
1239 									!(flag && point_set_size(targets)));
1240 			}
1241 
1242 			/* Find the path. */
1243 			path_n = project_path(path_g, z_info->max_range, loc(px, py),
1244 								  loc(x, y), PROJECT_THRU | PROJECT_INFO);
1245 
1246 			/* Draw the path in "target" mode. If there is one */
1247 			if (mode & (TARGET_KILL))
1248 				path_drawn = draw_path (path_n, path_g, path_char, path_attr,
1249 										py, px);
1250 
1251 			/* Describe and Prompt (enable "TARGET_LOOK") */
1252 			press = target_set_interactive_aux(y, x, mode | TARGET_LOOK);
1253 
1254 			/* Remove the path */
1255 			if (path_drawn)  load_path(path_n, path_g, path_char, path_attr);
1256 
1257 			/* Assume no direction */
1258 			d = 0;
1259 
1260 			/* Analyze the keypress */
1261 			if (press.type == EVT_MOUSE) {
1262 				if (press.mouse.button == 3) {
1263 					/* give the target selection command */
1264 					press.mouse.button = 2;
1265 					press.mouse.mods = KC_MOD_CONTROL;
1266 				}
1267 				if (press.mouse.button == 2) {
1268 					if (mode & (TARGET_KILL)) {
1269 						if ((y == KEY_GRID_Y(press))
1270 								&& (x == KEY_GRID_X(press))) {
1271 							d = -1;
1272 						}
1273 					}
1274 					y = KEY_GRID_Y(press);
1275 					x = KEY_GRID_X(press);
1276 					if (press.mouse.mods & KC_MOD_CONTROL) {
1277 						/* same as keyboard target selection command below */
1278 						target_set_location(y, x);
1279 						done = true;
1280 					} else if (press.mouse.mods & KC_MOD_ALT) {
1281 						/* go to spot - same as 'g' command below */
1282 						cmdq_push(CMD_PATHFIND);
1283 						cmd_set_arg_point(cmdq_peek(), "point", loc(x, y));
1284 						done = true;
1285 					} else {
1286 						/* cancel look mode */
1287 						done = true;
1288 						if (d == -1) {
1289 							target_set_location(y, x);
1290 							d = 0;
1291 						}
1292 					}
1293 				} else {
1294 					int dungeon_hgt = cave->height;
1295 					int dungeon_wid = cave->width;
1296 
1297 					y = KEY_GRID_Y(press);
1298 					x = KEY_GRID_X(press);
1299 
1300 					if (press.mouse.y <= 1) {
1301 						/* move the screen north */
1302 						y--;
1303 					} else if (press.mouse.y >= (Term->hgt - 2)) {
1304 						/* move the screen south */
1305 						y++;
1306 					} else if (press.mouse.x <= COL_MAP) {
1307 						/* move the screen in west */
1308 						x--;
1309 					} else if (press.mouse.x >= (Term->wid - 2)) {
1310 						/* move the screen east */
1311 						x++;
1312 					}
1313 
1314 					if (y < 0) y = 0;
1315 					if (x < 0) x = 0;
1316 					if (y >= dungeon_hgt-1) y = dungeon_hgt-1;
1317 					if (x >= dungeon_wid-1) x = dungeon_wid-1;
1318 
1319 					/* Adjust panel if needed */
1320 					if (adjust_panel_help(y, x, help)) {
1321 						/* Handle stuff */
1322 						handle_stuff(player);
1323 
1324 						/* Recalculate interesting grids */
1325 						point_set_dispose(targets);
1326 						targets = target_get_monsters(mode, NULL);
1327 					}
1328 
1329 					if (square_monster(cave, loc(x, y)) ||
1330 						square_object(cave, loc(x, y))) {
1331 						/* scan the interesting list and see if there in
1332 						 * anything here */
1333 						for (i = 0; i < point_set_size(targets); i++) {
1334 							if ((y == targets->pts[i].y) &&
1335 								(x == targets->pts[i].x)) {
1336 								m = i;
1337 								flag = true;
1338 								break;
1339 							}
1340 						}
1341 					} else {
1342 						flag = false;
1343 					}
1344 				}
1345 			} else
1346 				switch (press.key.code)
1347 				{
1348 					case ESCAPE:
1349 					case 'q':
1350 					{
1351 						done = true;
1352 						break;
1353 					}
1354 
1355 					case ' ':
1356 					case '*':
1357 					case '+':
1358 					case '-':
1359 					{
1360 						break;
1361 					}
1362 
1363 					case 'p':
1364 					{
1365 						/* Recenter around player */
1366 						verify_panel();
1367 
1368 						/* Handle stuff */
1369 						handle_stuff(player);
1370 
1371 						y = player->grid.y;
1372 						x = player->grid.x;
1373 					}
1374 
1375 					case 'o':
1376 					{
1377 						break;
1378 					}
1379 
1380 					case 'm':
1381 					{
1382 						flag = true;
1383 
1384 						m = 0;
1385 						bd = 999;
1386 
1387 						/* Pick a nearby monster */
1388 						for (i = 0; i < point_set_size(targets); i++) {
1389 							t = distance(loc(x, y), targets->pts[i]);
1390 
1391 							/* Pick closest */
1392 							if (t < bd) {
1393 								m = i;
1394 								bd = t;
1395 							}
1396 						}
1397 
1398 						/* Nothing interesting */
1399 						if (bd == 999) flag = false;
1400 
1401 						break;
1402 					}
1403 
1404 					case 't':
1405 					case '5':
1406 					case '0':
1407 					case '.':
1408 					{
1409 						target_set_location(y, x);
1410 						done = true;
1411 						break;
1412 					}
1413 
1414 					case 'g':
1415 					{
1416 						cmdq_push(CMD_PATHFIND);
1417 						cmd_set_arg_point(cmdq_peek(), "point", loc(x, y));
1418 						done = true;
1419 						break;
1420 					}
1421 
1422 					case '?':
1423 					{
1424 						help = !help;
1425 
1426 						/* Redraw main window */
1427 						player->upkeep->redraw |= (PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIP);
1428 						Term_clear();
1429 						handle_stuff(player);
1430 						if (!help)
1431 							prt("Press '?' for help.", help_prompt_loc, 0);
1432 
1433 						break;
1434 					}
1435 
1436 					default:
1437 					{
1438 						/* Extract a direction */
1439 						d = target_dir(press.key);
1440 
1441 						/* Oops */
1442 						if (!d) bell("Illegal command for target mode!");
1443 
1444 						break;
1445 					}
1446 				}
1447 
1448 			/* Handle "direction" */
1449 			if (d) {
1450 				int dungeon_hgt = cave->height;
1451 				int dungeon_wid = cave->width;
1452 
1453 				/* Move */
1454 				x += ddx[d];
1455 				y += ddy[d];
1456 
1457 				/* Slide into legality */
1458 				if (x >= dungeon_wid - 1) x--;
1459 				else if (x <= 0) x++;
1460 
1461 				/* Slide into legality */
1462 				if (y >= dungeon_hgt - 1) y--;
1463 				else if (y <= 0) y++;
1464 
1465 				/* Adjust panel if needed */
1466 				if (adjust_panel_help(y, x, help)) {
1467 					/* Handle stuff */
1468 					handle_stuff(player);
1469 
1470 					/* Recalculate interesting grids */
1471 					point_set_dispose(targets);
1472 					targets = target_get_monsters(mode, NULL);
1473 				}
1474 			}
1475 		}
1476 	}
1477 
1478 	/* Forget */
1479 	point_set_dispose(targets);
1480 
1481 	/* Redraw as necessary */
1482 	if (help) {
1483 		player->upkeep->redraw |= (PR_BASIC | PR_EXTRA | PR_MAP | PR_EQUIP);
1484 		Term_clear();
1485 	} else {
1486 		prt("", 0, 0);
1487 		prt("", help_prompt_loc, 0);
1488 		player->upkeep->redraw |= (PR_DEPTH | PR_STATUS);
1489 	}
1490 
1491 	/* Recenter around player */
1492 	verify_panel();
1493 
1494 	/* Handle stuff */
1495 	handle_stuff(player);
1496 
1497 	mem_free(path_attr);
1498 	mem_free(path_char);
1499 
1500 	/* Allow animations again */
1501 	allow_animations();
1502 
1503 	/* Failure to set target */
1504 	if (!target_is_set()) return (false);
1505 
1506 	/* Success */
1507 	return (true);
1508 }
1509 
1510 
1511