1 #include "c-angband.h"
2 #include "z-term.h"
3 
4 #define MACRO_USE_CMD	0x01
5 #define MACRO_USE_STD	0x02
6 
7 
8 static bool after_macro = FALSE;
9 static bool parse_macro = FALSE;
10 static bool parse_under = FALSE;
11 static bool parse_slash = FALSE;
12 static bool strip_chars = FALSE;
13 
14 #define flush_later inkey_xtra
15 
16 static byte macro__use[256];
17 
octify(uint i)18 static char octify(uint i)
19 {
20 	return (hexsym[i%8]);
21 }
22 
hexify(uint i)23 static char hexify(uint i)
24 {
25 	return (hexsym[i%16]);
26 }
27 
move_cursor(int row,int col)28 void move_cursor(int row, int col)
29 {
30 	Term_gotoxy(col, row);
31 }
32 
flush(void)33 void flush(void)
34 {
35 	flush_later = TRUE;
36 }
37 
flush_now(void)38 void flush_now(void)
39 {
40 	/* Clear various flags */
41 	flush_later = FALSE;
42 
43 	/* Cancel "macro" info */
44 	parse_macro = after_macro = FALSE;
45 
46 	/* Cancel "sequence" info */
47 	parse_under = parse_slash = FALSE;
48 
49 	/* Cancel "strip" mode */
50 	strip_chars = FALSE;
51 
52 	/* Restore "first escape" */
53 	first_escape = TRUE;
54 
55 	/* Forget old keypresses */
56 	Term_flush();
57 }
58 
59 /*
60  * Free the macro trigger package
61  */
macro_trigger_free(void)62 errr macro_trigger_free(void)
63 {
64 	int i;
65 	int num;
66 
67 	if (macro_template != NULL)
68 	{
69 		/* Free the template */
70 		string_free(macro_template);
71 		macro_template = NULL;
72 
73 		/* Free the trigger names and keycodes */
74 		for (i = 0; i < max_macrotrigger; i++)
75 		{
76 			string_free(macro_trigger_name[i]);
77 
78 			string_free(macro_trigger_keycode[0][i]);
79 			string_free(macro_trigger_keycode[1][i]);
80 		}
81 
82 		/* No more macro triggers */
83 		max_macrotrigger = 0;
84 
85 		/* Count modifier-characters */
86 		num = strlen(macro_modifier_chr);
87 
88 		/* Free modifier names */
89 		for (i = 0; i < num; i++)
90 		{
91 			string_free(macro_modifier_name[i]);
92 		}
93 
94 		/* Free modifier chars */
95 		string_free(macro_modifier_chr);
96 		macro_modifier_chr = NULL;
97 	}
98 
99 	/* Success */
100 	return (0);
101 }
102 
103 
104 /*
105  * Check for possibly pending macros
106  */
macro_maybe(cptr buf,int n)107 static int macro_maybe(cptr buf, int n)
108 {
109 	int i;
110 
111 	/* Scan the macros */
112 	for (i = n; i < macro__num; i++)
113 	{
114 		/* Skip inactive macros */
115 		if (macro__cmd[i] && !inkey_flag) continue;
116 
117 		/* Check for "prefix" */
118 		if (prefix(macro__pat[i], buf))
119 		{
120 			/* Ignore complete macros */
121 			if (!streq(macro__pat[i], buf)) return (i);
122 		}
123 	}
124 
125 	/* No matches */
126 	return (-1);
127 }
128 
129 
130 /*
131  * Find the longest completed macro
132  */
macro_ready(cptr buf)133 static int macro_ready(cptr buf)
134 {
135 	int i, t, n = -1, s = -1;
136 
137 	/* Scan the macros */
138 	for (i = 0; i < macro__num; i++)
139 	{
140 		/* Skip inactive macros */
141 		if (macro__cmd[i] && !inkey_flag) continue;
142 
143 		/* Check for "prefix" */
144 		if (!prefix(buf, macro__pat[i])) continue;
145 
146 		/* Check the length of this entry */
147 		t = strlen(macro__pat[i]);
148 
149 		/* Find the "longest" entry */
150 		if ((n >= 0) && (s > t)) continue;
151 
152 		/* Track the entry */
153 		n = i;
154 		s = t;
155 	}
156 
157 	/* Return the result */
158 	return (n);
159 }
160 
161 /*
162  * Hack -- set graphics mode
163  */
set_graphics(int mode)164 void set_graphics(int mode) {
165 	use_graphics = mode;
166 	if (mode == 0) ANGBAND_GRAF = "none";
167 	if (mode == 1) ANGBAND_GRAF = "old";
168 	if (mode == 2) ANGBAND_GRAF = "new";
169 	if (mode == 3) ANGBAND_GRAF = "david";
170 }
171 
172 /*
173  * Hack -- add a macro definition (or redefinition).
174  *
175  * If "cmd_flag" is set then this macro is only active when
176  * the user is being asked for a command (see below).
177  */
macro_add(cptr pat,cptr act,bool cmd_flag)178 void macro_add(cptr pat, cptr act, bool cmd_flag)
179 {
180         int n;
181 
182 
183         /* Paranoia -- require data */
184         if (!pat || !act) return;
185 
186 
187         /* Look for a re-usable slot */
188         for (n = 0; n < macro__num; n++)
189         {
190                 /* Notice macro redefinition */
191                 if (streq(macro__pat[n], pat))
192                 {
193                         /* Free the old macro action */
194                         string_free(macro__act[n]);
195 
196                         /* Save the macro action */
197                         macro__act[n] = string_make(act);
198 
199                         /* Save the "cmd_flag" */
200                         macro__cmd[n] = cmd_flag;
201 
202                         /* All done */
203                         return;
204                 }
205         }
206 
207 
208         /* Save the pattern */
209         macro__pat[macro__num] = string_make(pat);
210 
211         /* Save the macro action */
212         macro__act[macro__num] = string_make(act);
213 
214         /* Save the "cmd_flag" */
215         macro__cmd[macro__num] = cmd_flag;
216 
217         /* One more macro */
218         macro__num++;
219 
220 
221         /* Hack -- Note the "trigger" char */
222         macro__use[(byte)(pat[0])] |= MACRO_USE_STD;
223 
224         /* Hack -- Note the "trigger" char of command macros */
225         if (cmd_flag) macro__use[(byte)(pat[0])] |= MACRO_USE_CMD;
226 }
227 
228 
229 /*
230  * Helper function called only from "inkey()"
231  *
232  * This function does most of the "macro" processing.
233  *
234  * We use the "Term_key_push()" function to handle "failed" macros,
235  * as well as "extra" keys read in while choosing a macro, and the
236  * actual action for the macro.
237  *
238  * Embedded macros are illegal, although "clever" use of special
239  * control chars may bypass this restriction.  Be very careful.
240  *
241  * The user only gets 500 (1+2+...+29+30) milliseconds for the macro.
242  *
243  * Note the annoying special processing to "correctly" handle the
244  * special "control-backslash" codes following a "control-underscore"
245  * macro sequence.  See "main-x11.c" and "main-xaw.c" for details.
246  *
247  *
248  * Note that we NEVER wait for a keypress without also checking the network
249  * for incoming packets.  This stops annoying "timeouts" and also lets
250  * the screen get redrawn if need be while we are waiting for a key. --KLJ--
251  */
inkey_aux(void)252 static event_type inkey_aux(void)
253 {
254 	int	k = 0, n, p = 0, w = 0;
255 
256 	char	ch = 0;
257 	event_type ke, ke0;
258 
259 	cptr	pat, act;
260 
261 	char	buf[1024];
262 
263 	/* Initialize the no return */
264 	ke0.type = EVT_KBRD;
265 	ke0.key = 0;
266 	ke0.index = 0; /* To fix GCC warnings on X11 */
267 	ke0.mousey = 0;
268 	ke0.mousex = 0;
269 
270 	/* Fetch keypress */
271 	/* Unset hack before we start */
272 	inkey_exit = FALSE;
273 
274 	/* Wait for keypress, while also checking for net input */
275 	do
276 	{
277 		/* Look for a keypress */
278 		(void)(Term_inkey(&ke, FALSE, TRUE));
279 		ch = ke.key;
280 
281 		/* If we got a key, break */
282 		if (ch) break;
283 
284 		/* Do networking */
285 		network_loop();
286 
287 		/* Update screen */
288 		flush_updates();
289 
290 		/* Hack: perform emergency network-ordered escape */
291 		if (inkey_exit)
292 		{
293 			inkey_exit = FALSE;
294 			ke0.key = ESCAPE;
295 			return ke0;
296 		}
297 
298 		/* Return as soon as possible */
299 		if (inkey_nonblock)
300 		{
301 			ke0.key = 0;
302 			return ke0;
303 		}
304 	} while (!ch);
305 
306 
307 	/* ARCANE MAGIC STARTS BELOW: */
308 
309 	/* Do not check "ascii 28" */
310 	if (ch == 28) return (ke);
311 
312 	/* Begin "synthetic macro" sequence on "ascii 29" */
313 	if (ch == 29) parse_macro = TRUE;
314 
315 	/* End "macro action" */
316 	if ((ch == 30) || (ch == '\xff'))
317 	{
318 		parse_macro = FALSE;
319 		strip_chars = FALSE;
320 		first_escape = TRUE;
321 		return (ke);
322 	}
323 
324 	/* Cancel queue clearing */
325 	if (ch == '\f' && parse_macro) { first_escape = FALSE; return (ke0); }
326 
327 	/* Do not check macro actions */
328 	if (parse_macro) return (ke);
329 
330 	/* Do not check "control-underscore" sequences */
331 	if (parse_under) return (ke);
332 
333 	/* Do not check "control-backslash" sequences */
334 	if (parse_slash) return (ke);
335 
336 
337 	/* Efficiency -- Ignore impossible macros */
338 	if (!macro__use[(byte)(ch)]) return (ke);
339 
340 	/* Efficiency -- Ignore inactive macros */
341 	if (!inkey_flag && (macro__use[(byte)(ch)] == MACRO_USE_CMD)) return (ke);
342 
343 #ifdef USE_GCU
344 	/* NCurses client actually uses escape as "begin macro" sequence */
345 	if (escape_in_macro_triggers)
346 	{
347 		/* So we allow it */
348 	} else
349 #endif
350 	/* Efficiency/Hack -- Ignore escape key for macros */
351 	if (ch == ESCAPE) { first_escape = TRUE; return (ke); }
352 
353 	/* Save the first key, advance */
354 	buf[p++] = ch;
355 	buf[p] = '\0';
356 
357 
358 	/* Wait for a macro, or a timeout */
359 	while (TRUE)
360 	{
361 		/* Check for possible macros */
362 		k = macro_maybe(buf, k);
363 
364 		/* Nothing matches */
365 		if (k < 0) break;
366 
367 		/* Check for (and remove) a pending key */
368 		if (0 == Term_inkey(&ke, FALSE, TRUE))
369 		{
370 			ch = ke.key;
371 
372 			/* Append the key */
373 			buf[p++] = ch;
374 			buf[p] = '\0';
375 
376 			/* Restart wait */
377 			w = 0;
378 		}
379 
380 		/* No key ready */
381 		else
382 		{
383 			/* Increase "wait" */
384 			w += 10;
385 
386 			/* Excessive delay */
387 			if (w >= 100) break;
388 
389 			/* Delay */
390 			Term_xtra(TERM_XTRA_DELAY, w);
391 		}
392 	}
393 
394 
395 	/* Check for a successful macro */
396 	k = macro_ready(buf);
397 
398 	/* No macro available */
399 	if (k < 0)
400 	{
401 		/* Push all the "keys" back on the queue */
402 		/* The most recent event may not be a keypress. */
403 		if(p)
404 		{
405 			if(Term_event_push(&ke)) return (ke0);
406 			p--;
407 		}
408 		/* Push all the keys back on the queue */
409 		while (p > 0)
410 		{
411 			/* Push the key, notice over-flow */
412 			if (Term_key_push(buf[--p])) return (ke0);
413 		}
414 
415 		/* Wait for (and remove) a pending key */
416 		(void)Term_inkey(&ke, TRUE, TRUE);
417 
418 		/* Return the key */
419 		return (ke);
420 	}
421 
422 
423 	/* Access the macro pattern */
424 	pat = macro__pat[k];
425 
426 	/* Get the length of the pattern */
427 	n = strlen(pat);
428 
429 	/* Push the "extra" keys back on the queue */
430 	while (p > n)
431 	{
432 		if (Term_key_push(buf[--p])) return (ke0);
433 	}
434 
435 	/* We are now inside a macro */
436 	parse_macro = TRUE;
437 	first_escape = TRUE;
438 
439 	/* Push the "macro complete" key */
440 	if (Term_key_push(30)) return (ke0);
441 
442 
443 	/* Access the macro action */
444 	act = macro__act[k];
445 
446 	/* Get the length of the action */
447 	n = strlen(act);
448 
449 	/* Push the macro "action" onto the key queue */
450 	while (n > 0)
451 	{
452 		/* Push the key, notice over-flow */
453 		if (Term_key_push(act[--n])) return (ke0);
454 	}
455 
456 	/* Force "inkey()" to call us again */
457 	return (ke0);
458 }
459 
460 /*
461  * Mega-Hack -- special "inkey_next" pointer.  XXX XXX XXX
462  *
463  * This special pointer allows a sequence of keys to be "inserted" into
464  * the stream of keys returned by "inkey()".  This key sequence will not
465  * trigger any macros, and cannot be bypassed by the Borg.  It is used
466  * in Angband to handle "keymaps".
467  */
468 static cptr inkey_next = NULL;
469 
470 /*
471  * Get a keypress from the user.
472  *
473  * This function recognizes a few "global parameters".  These are variables
474  * which, if set to TRUE before calling this function, will have an effect
475  * on this function, and which are always reset to FALSE by this function
476  * before this function returns.  Thus they function just like normal
477  * parameters, except that most calls to this function can ignore them.
478  *
479  * If "inkey_xtra" is TRUE, then all pending keypresses will be flushed,
480  * and any macro processing in progress will be aborted.  This flag is
481  * set by the "flush()" function, which does not actually flush anything
482  * itself, but rather, triggers delayed input flushing via "inkey_xtra".
483  *
484  * If "inkey_scan" is TRUE, then we will immediately return "zero" if no
485  * keypress is available, instead of waiting for a keypress.
486  *
487  * If "inkey_base" is TRUE, then all macro processing will be bypassed.
488  * If "inkey_base" and "inkey_scan" are both TRUE, then this function will
489  * not return immediately, but will wait for a keypress for as long as the
490  * normal macro matching code would, allowing the direct entry of macro
491  * triggers.  The "inkey_base" flag is extremely dangerous!
492  *
493  * If "inkey_flag" is TRUE, then we will assume that we are waiting for a
494  * normal command, and we will only show the cursor if "hilite_player" is
495  * TRUE (or if the player is in a store), instead of always showing the
496  * cursor.  The various "main-xxx.c" files should avoid saving the game
497  * in response to a "menu item" request unless "inkey_flag" is TRUE, to
498  * prevent savefile corruption.
499  *
500  * If we are waiting for a keypress, and no keypress is ready, then we will
501  * refresh (once) the window which was active when this function was called.
502  *
503  * Note that "back-quote" is automatically converted into "escape" for
504  * convenience on machines with no "escape" key.  This is done after the
505  * macro matching, so the user can still make a macro for "backquote".
506  *
507  * Note the special handling of "ascii 30" (ctrl-caret, aka ctrl-shift-six)
508  * and "ascii 31" (ctrl-underscore, aka ctrl-shift-minus), which are used to
509  * provide support for simple keyboard "macros".  These keys are so strange
510  * that their loss as normal keys will probably be noticed by nobody.  The
511  * "ascii 30" key is used to indicate the "end" of a macro action, which
512  * allows recursive macros to be avoided.  The "ascii 31" key is used by
513  * some of the "main-xxx.c" files to introduce macro trigger sequences.
514  *
515  * If "angband_term[0]" is not active, we will make it active during this
516  * function, so that the various "main-xxx.c" files can assume that input
517  * is only requested (via "Term_inkey()") when "angband_term[0]" is active.
518  *
519  * Mega-Hack -- This function is used as the entry point for clearing the
520  * "signal_count" variable, and of the "character_saved" variable.
521  *
522  * Hack -- Note the use of "inkey_next" to allow "keymaps" to be processed.
523  *
524  * Mega-Hack -- Note the use of "inkey_hack" to allow the "Borg" to steal
525  * control of the keyboard from the user.
526  *
527  * Note the nasty code used to process the "inkey_base" flag, which allows
528  * various "macro triggers" to be entered as normal key-sequences, with the
529  * appropriate timing constraints, but without actually matching against any
530  * macro sequences.  Most of the nastiness is to handle "ascii 28" (see below).
531  *
532  * The "ascii 28" code is a complete hack, used to allow "default actions"
533  * to be associated with a given keypress, and used only by the X11 module,
534  * it may or may not actually work.  The theory is that a keypress can send
535  * a special sequence, consisting of a "macro trigger" plus a "default action",
536  * with the "default action" surrounded by "ascii 28" symbols.  Then, when that
537  * key is pressed, if the trigger matches any macro, the correct action will be
538  * executed, and the "strip default action" code will remove the "default action"
539  * from the keypress queue, while if it does not match, the trigger itself will
540  * be stripped, and then the "ascii 28" symbols will be stripped as well, leaving
541  * the "default action" keys in the "key queue".  Again, this may not work.
542  */
inkey_ex(void)543 event_type inkey_ex(void)
544 {
545 	bool cursor_state;
546 	event_type kk;
547 	event_type ke;
548 
549 	bool done = FALSE;
550 
551 	term *old = Term;
552 
553 	int skipping = FALSE; /* Evil handler for char 28 */
554 
555 	/* Initialise keypress */
556 	ke.key = 0;
557 	ke.type = EVT_KBRD;
558 
559 	/* Hack -- Use the "inkey_next" pointer */
560 	if (inkey_next && *inkey_next && !inkey_xtra)
561 	{
562 		/* Get next character, and advance */
563 		ke.key = *inkey_next++;
564 
565 		/* Cancel the various "global parameters" */
566 		inkey_base = inkey_xtra = inkey_flag = inkey_scan = FALSE;
567 
568 		/* Accept result */
569 		return (ke);
570 	}
571 
572 	/* Forget pointer */
573 	inkey_next = NULL;
574 
575 
576 #ifdef ALLOW_BORG
577 
578 	/* Mega-Hack -- Use the special hook */
579 	if (inkey_hack && ((ch = (*inkey_hack)(inkey_xtra)) != 0))
580 	{
581 		/* Cancel the various "global parameters" */
582 		inkey_base = inkey_xtra = inkey_flag = inkey_scan = FALSE;
583 		ke.type = EVT_KBRD;
584 
585 		/* Accept result */
586 		return (ke);
587 	}
588 
589 #endif /* ALLOW_BORG */
590 
591 
592 	/* Hack -- handle delayed "flush()" */
593 	if (inkey_xtra)
594 	{
595 		/* End "macro action" */
596 		parse_macro = FALSE;
597 		ke.type = EVT_KBRD;
598 
599 		/* End "macro trigger" */
600 		parse_under = FALSE;
601 
602 		/* Cancel arcane flags */
603 		parse_slash = after_macro = FALSE;
604 		strip_chars = FALSE;
605 
606 		/* Forget old keypresses */
607 		Term_flush();
608 	}
609 
610 /* MAngband-specific: do nothing to the cursor */
611 #if 0
612 	/* Get the cursor state */
613 	(void)Term_get_cursor(&cursor_state);
614 
615 	/* Show the cursor if waiting, except sometimes in "command" mode */
616 	if (!inkey_scan && (!inkey_flag /*|| hilite_player || character_icky*/))
617 	{
618 		/* Show the cursor */
619 		(void)Term_set_cursor(TRUE);
620 	}
621 #endif
622 
623 	/* Hack -- Activate main screen */
624 	Term_activate(term_screen);
625 
626 
627 	/* Get a key */
628 	while (!ke.key)
629 	{
630 		/* Hack -- Handle "inkey_scan" */
631 		if (!inkey_base && inkey_scan &&
632 		   (0 != Term_inkey(&kk, FALSE, FALSE)))
633 		{
634 			break;
635 		}
636 
637 
638 		/* Hack -- Flush output once when no key ready */
639 		if (!done && (0 != Term_inkey(&kk, FALSE, FALSE)))
640 		{
641 
642 			/* Hack -- activate proper term */
643 			Term_activate(old);
644 
645 			/* Flush output */
646 			Term_fresh();
647 
648 			/* Hack -- activate main screen */
649 			Term_activate(term_screen);
650 
651 			/* Mega-Hack -- reset saved flag */
652 			/*character_saved = FALSE;*/
653 
654 			/* Mega-Hack -- reset signal counter */
655 			/*signal_count = 0;*/
656 
657 			/* Only once */
658 			done = TRUE;
659 		}
660 
661 
662 		/* Hack -- Handle "inkey_base" */
663 		if (inkey_base)
664 		{
665 			int w = 0;
666 			event_type xh;
667 
668 			/* Wait forever */
669 			if (!inkey_scan)
670 			{
671 				/* Wait for (and remove) a pending key */
672 				if (0 == Term_inkey(&xh, TRUE, TRUE))
673 				{
674 					/* Mega-Hack */
675 					if (xh.key == 28)
676 					{
677 						/* Toggle "skipping" */
678 						skipping = !skipping;
679 					}
680 					/* Use normal keys */
681 					else if (!skipping)
682 					{
683 						/* Use it */
684 						ke.key = xh.key;
685 					}
686 					/* Done */
687 					break;
688 				}
689 
690 				/* Oops */
691 				break;
692 			}
693 
694 			/* Wait only as long as macro activation would wait*/
695 			while (TRUE)
696 			{
697 				/* Check for (and remove) a pending key */
698 				if (0 == Term_inkey(&xh, FALSE, TRUE))
699 				{
700 					/* Mega-Hack */
701 					if (xh.key == 28)
702 					{
703 						/* Toggle "skipping" */
704 						skipping = !skipping;
705 					}
706 					/* Use normal keys */
707 					else if (!skipping)
708 					{
709 						/* Use it */
710 						ke.key = xh.key;
711 					}
712 					break;
713 				}
714 
715 				/* No key ready */
716 				else
717 				{
718 					/* Increase "wait" */
719 					w += 10;
720 
721 					/* Excessive delay */
722 					if (w >= 100) break;
723 
724 					/* Delay */
725 					Term_xtra(TERM_XTRA_DELAY, w);
726 
727 				}
728 			}
729 
730 			/* Done */
731 			ke.type = EVT_KBRD;
732 			break;
733 		}
734 
735 
736 		/* Get a key (see above) */
737 		ke = inkey_aux();
738 
739 		/* Hack -- special nonblock mode */
740 		if (!ke.key && inkey_nonblock) return (ke);
741 
742 		/* Handle "control-right-bracket" */
743 		/* Note: as far as I can tell, this can be safely removed. -flm */
744 		if (ke.key == 29)
745 		{
746 			/* Strip this key */
747 			ke.key = 0;
748 
749 			/* Continue */
750 			continue;
751 		}
752 
753 		/* Hack -- Strip "control-backslash" special-fallback-strings  */
754 		if (ke.key == 28)
755 		{
756 			/* Strip this key */
757 			ke.key = 0;
758 
759 			/* Inside a "control-backslash" sequence */
760 			parse_slash = TRUE;
761 
762 			/* Strip chars (sometimes) */
763 			strip_chars = after_macro;
764 
765 			/* Continue */
766 			continue;
767 		}
768 
769 		/* Treat back-quote as escape */
770 		if (ke.key == '`') ke.key = ESCAPE;
771 
772 
773 		/* End "macro trigger" */
774 		if (parse_under && (ke.key <= 32))
775 		{
776 			/* Strip this key */
777 			ke.key = 0;
778 
779 			/* End "macro trigger" */
780 			parse_under = FALSE;
781 
782 			/* Stop stripping */
783 			strip_chars = FALSE;
784 		}
785 
786 		/* Handle "control-caret" */
787 		if (ke.key == 30)
788 		{
789 			/* Strip this key */
790 			ke.key = 0;
791 		}
792 
793 		/* Handle "control-underscore" */
794 		else if (ke.key == 31)
795 		{
796 			/* Strip this key */
797 			ke.key = 0;
798 
799 			/* Begin "macro trigger" */
800 			parse_under = TRUE;
801 
802 			/* Hack -- Strip chars (always) */
803 			strip_chars = TRUE;
804 		}
805 
806 		/* Inside "macro trigger" */
807 		else if (parse_under)
808 		{
809 			/* Strip this key */
810 			ke.key = 0;
811 		}
812 
813 		/* Hack -- Set "after_macro" code */
814 		after_macro = ((kk.key == 30) ? TRUE : FALSE);
815 
816 		/* Hack -- strip chars */
817 		if (strip_chars) ke.key = 0;
818 	}
819 
820 
821 	/* Hack -- restore the term */
822 	Term_activate(old);
823 
824 
825 /* MAngband-specific: do nothing to the cursor */
826 #if 0
827 	/* Restore the cursor */
828 	Term_set_cursor(cursor_state);
829 #endif
830 
831 	/* Cancel the various "global parameters" */
832 	inkey_base = inkey_xtra = inkey_flag = inkey_scan = FALSE;
833 
834 
835 	/* Return the keypress */
836 	return (ke);
837 }
838 
839 
840 /*
841  * Get a "keypress" from the user.
842  */
inkey(void)843 char inkey(void)
844 {
845 	event_type ke;
846 
847 	/* Only accept a keypress */
848 	do
849 	{
850 		ke = inkey_ex();
851 	} while (!(ke.type  & (EVT_KBRD|EVT_ESCAPE)));
852 	/* Paranoia */
853 	if(ke.type == EVT_ESCAPE) ke.key = ESCAPE;
854 
855 	return ke.key;
856 }
857 
858 #if 0
859 static int hack_dir = 0;
860 /*
861  * Convert a "Rogue" keypress into an "Angband" keypress
862  * Pass extra information as needed via "hack_dir"
863  *
864  * Note that many "Rogue" keypresses encode a direction.
865  */
866 static char roguelike_commands(char command)
867 {
868         /* Process the command */
869         switch (command)
870         {
871                 /* Movement (rogue keys) */
872                 case 'b': hack_dir = 1; return (';');
873                 case 'j': hack_dir = 2; return (';');
874                 case 'n': hack_dir = 3; return (';');
875                 case 'h': hack_dir = 4; return (';');
876                 case 'l': hack_dir = 6; return (';');
877                 case 'y': hack_dir = 7; return (';');
878                 case 'k': hack_dir = 8; return (';');
879                 case 'u': hack_dir = 9; return (';');
880 
881                 /* Running (shift + rogue keys) */
882                 case 'B': hack_dir = 1; return ('.');
883                 case 'J': hack_dir = 2; return ('.');
884                 case 'N': hack_dir = 3; return ('.');
885                 case 'H': hack_dir = 4; return ('.');
886                 case 'L': hack_dir = 6; return ('.');
887                 case 'Y': hack_dir = 7; return ('.');
888                 case 'K': hack_dir = 8; return ('.');
889                 case 'U': hack_dir = 9; return ('.');
890 
891                 /* Tunnelling (control + rogue keys) */
892                 case KTRL('B'): hack_dir = 1; return ('+');
893                 case KTRL('J'): hack_dir = 2; return ('+');
894                 case KTRL('N'): hack_dir = 3; return ('+');
895                 case KTRL('H'): hack_dir = 4; return ('+');
896                 case KTRL('L'): hack_dir = 6; return ('+');
897                 case KTRL('Y'): hack_dir = 7; return ('+');
898                 case KTRL('K'): hack_dir = 8; return ('+');
899                 case KTRL('U'): hack_dir = 9; return ('+');
900 
901                 /* Hack -- White-space */
902                 case KTRL('M'): return ('\r');
903 
904                 /* Allow use of the "destroy" command */
905                 case KTRL('D'): return ('k');
906 
907                 /* Hack -- Commit suicide */
908                 case KTRL('C'): return ('Q');
909 
910                 /* Locate player on map */
911                 case 'W': return ('L');
912 
913                 /* Browse a book (Peruse) */
914                 case 'P': return ('b');
915 
916                 /* Steal */
917                 case 'S': return ('j');
918 
919                 /* Toggle search mode */
920                 case '#': return ('S');
921 
922                 /* Use a staff (Zap) */
923                 case 'Z': return ('u');
924 
925                 /* Take off equipment */
926                 case 'T': return ('t');
927 
928                 /* Fire an item */
929                 case 't': return ('f');
930 
931                 /* Bash a door (Force) */
932                 case 'f': return ('B');
933 
934                 /* Look around (examine) */
935                 case 'x': return ('l');
936 
937                 /* Aim a wand (Zap) */
938                 case 'z': return ('a');
939 
940                 /* Zap a rod (Activate) */
941                 case 'a': return ('z');
942 
943 		/* Party mode */
944 		case 'O': return ('P');
945 
946                 /* Run */
947                 case ',': return ('.');
948 
949                 /* Stay still (fake direction) */
950                 case '.': hack_dir = 5; return (',');
951 
952                 /* Stay still (fake direction) */
953                 case '5': hack_dir = 5; return (',');
954 
955                 /* Standard walking */
956                 case '1': hack_dir = 1; return (';');
957                 case '2': hack_dir = 2; return (';');
958                 case '3': hack_dir = 3; return (';');
959                 case '4': hack_dir = 4; return (';');
960                 case '6': hack_dir = 6; return (';');
961                 case '7': hack_dir = 7; return (';');
962                 case '8': hack_dir = 8; return (';');
963                 case '9': hack_dir = 9; return (';');
964         }
965 
966         /* Default */
967         return (command);
968 }
969 
970 
971 
972 /*
973  * Convert an "Original" keypress into an "Angband" keypress
974  * Pass direction information back via "hack_dir".
975  *
976  * Note that "Original" and "Angband" are very similar.
977  */
978 static char original_commands(char command)
979 {
980 	/* Process the command */
981 	switch(command)
982 	{
983 		/* Hack -- White space */
984 		case KTRL('J'): return ('\r');
985 		case KTRL('M'): return ('\r');
986 
987 		/* Tunnel */
988 		case 'T': return ('+');
989 
990 		/* Run */
991 		case '.': return ('.');
992 
993 		/* Stay still (fake direction) */
994 		case ',': hack_dir = 5; return (',');
995 
996 		/* Stay still (fake direction) */
997 		case '5': hack_dir = 5; return (',');
998 
999 		/* Standard walking */
1000 		case '1': hack_dir = 1; return (';');
1001 		case '2': hack_dir = 2; return (';');
1002 		case '3': hack_dir = 3; return (';');
1003 		case '4': hack_dir = 4; return (';');
1004 		case '6': hack_dir = 6; return (';');
1005 		case '7': hack_dir = 7; return (';');
1006 		case '8': hack_dir = 8; return (';');
1007 		case '9': hack_dir = 9; return (';');
1008 
1009 		/* Hack -- Commit suicide */
1010 		case KTRL('K'): return ('Q');
1011 		case KTRL('C'): return ('Q');
1012 	}
1013 
1014 	/* Default */
1015 	return (command);
1016 }
1017 #endif
1018 
1019 
1020 /*
1021  * Flush the screen, make a noise
1022  */
bell(void)1023 void bell(void)
1024 {
1025 	/* Mega-Hack -- Flush the output */
1026 	Term_fresh();
1027 
1028 	/* Make a bell noise (if allowed) */
1029 	if (ring_bell) Term_xtra(TERM_XTRA_NOISE, 0);
1030 
1031 	/* Flush the input (later!) */
1032 	flush();
1033 }
1034 
1035 /*
1036  * Display a string on the screen using an attribute, and clear
1037  * to the end of the line.
1038  */
c_prt(byte attr,cptr str,int row,int col)1039 void c_prt(byte attr, cptr str, int row, int col)
1040 {
1041 	/* Hack -- fake monochrome */
1042 	/* if (!use_color) attr = TERM_WHITE; */
1043 
1044 	/* Clear line, position cursor */
1045 	Term_erase(col, row, 255);
1046 
1047 	/* Dump the attr/text */
1048 	Term_addstr(-1, attr, str);
1049 }
1050 
1051 /*
1052  * As above, but in "white"
1053  */
prt(cptr str,int row,int col)1054 void prt(cptr str, int row, int col)
1055 {
1056 	/* Spawn */
1057 	c_prt(TERM_WHITE, str, row, col);
1058 }
1059 
1060 
1061 /*
1062  * Get some input at the cursor location.
1063  * Assume the buffer is initialized to a default string.
1064  * Note: "len" MUST BE LESS than total "buf" size.
1065  * Note that this string is often "empty" (see below).
1066  * The default buffer is displayed in yellow until cleared.
1067  * Pressing RETURN right away accepts the default entry.
1068  * Normal chars clear the default and append the char.
1069  * Backspace clears the default or deletes the final char.
1070  * ESCAPE clears the buffer and the window and returns FALSE.
1071  * RETURN accepts the current buffer contents and returns TRUE.
1072  */
1073 
1074 /* APD -- added private so passwords will not be displayed. */
askfor_aux(char * buf,int len,char m_private)1075 bool askfor_aux(char *buf, int len, char m_private)
1076 {
1077 	int y, x;
1078 
1079 	int i = 0;
1080 
1081 	int k = 0;
1082 
1083 	bool done = FALSE;
1084 
1085 	byte default_color = TERM_YELLOW;
1086 
1087 	/* Locate the cursor */
1088 	Term_locate(&x, &y);
1089 
1090 	/* The top line is "icky" */
1091 	topline_icky = TRUE;
1092 
1093 	/* Paranoia -- check len */
1094 	if (len < 1) len = 1;
1095 
1096 	/* Paranoia -- check column */
1097 	if ((x < 0) || (x >= 80)) x = 0;
1098 
1099 	/* Restrict the length */
1100 	if (x + len > 80) len = 80 - x;
1101 
1102 
1103 	/* Paranoia -- Clip the default entry */
1104 	buf[len] = '\0';
1105 
1106 	/* HACK -- "append mode" */
1107 	if (m_private == -2)
1108 	{
1109 		m_private = FALSE;
1110 		k = strlen(buf);
1111 		default_color = TERM_WHITE;
1112 	}
1113 
1114 	/* Display the default answer */
1115 	Term_erase(x, y, len);
1116 	Term_putstr(x, y, -1, default_color, m_private ? "xxxxxx" : buf);
1117 
1118 	/* Show on-screen keyboard */
1119 	Term_show_keyboard(0);
1120 
1121 	/* Process input */
1122 	while (!done)
1123 	{
1124 		/* Place cursor */
1125 		Term_gotoxy(x + k, y);
1126 		Term_show_ui_cursor();
1127 
1128 		/* Get a key */
1129 		i = inkey();
1130 
1131 		/* Evil hack -- pretend quote is Return */
1132 		if (prompt_quote_hack && i == '"') i = '\r';
1133 
1134 		/* Analyze the key */
1135 		switch (i)
1136 		{
1137 			case ESCAPE:
1138 			k = 0;
1139 			done = TRUE;
1140 			break;
1141 
1142 			case '\n':
1143 			case '\r':
1144 			k = strlen(buf);
1145 			done = TRUE;
1146 			break;
1147 
1148 			case 0x7F:
1149 			case '\010':
1150 			if (k > 0) k--;
1151 			break;
1152 
1153 			default:
1154 			if ((k < len) && (isprint(i)))
1155 			{
1156 				buf[k++] = i;
1157 
1158 				/* Update the entry */
1159 				Term_putch(x+k-1, y, TERM_WHITE, m_private ? 'x' : buf[k-1]);
1160 			}
1161 			else
1162 			{
1163 				bell();
1164 			}
1165 			break;
1166 		}
1167 
1168 		Term_erase(x+k, y, len - k);
1169 
1170 		/* Terminate */
1171 		buf[k] = '\0';
1172 
1173 	}
1174 
1175 	/* Hide on-screen keyboard */
1176 	Term_hide_keyboard();
1177 
1178 	/* Hide cursor */
1179 	Term_hide_ui_cursor();
1180 
1181 	/* The top line is OK now */
1182 	topline_icky = FALSE;
1183 	Flush_queue();
1184 
1185 	/* Reset global flags */
1186 	prompt_quote_hack = FALSE;
1187 
1188 	/* Aborted */
1189 	if (i == ESCAPE) return (FALSE);
1190 
1191 	/* Success */
1192 	return (TRUE);
1193 }
1194 
1195 
1196 /*
1197  * Get a string from the user
1198  *
1199  * The "prompt" should take the form "Prompt: "
1200  *
1201  * Note: "len" MUST BE LESS than total "buf" size.
1202  *
1203  * Note that the initial contents of the string is used as
1204  * the default response, so be sure to "clear" it if needed.
1205  *
1206  * We clear the input, and return FALSE, on "ESCAPE".
1207  */
get_string(cptr prompt,char * buf,int len)1208 bool get_string(cptr prompt, char *buf, int len)
1209 {
1210 	bool res;
1211 
1212 	/* Display prompt */
1213 	prt(prompt, 0, 0);
1214 
1215 	/* Ask the user for a string */
1216 	res = askfor_aux(buf, len, 0);
1217 
1218 	/* Clear prompt */
1219 	prt("", 0, 0);
1220 
1221 	/* Result */
1222 	return (res);
1223 }
1224 
1225 /* Same as get_string, but with ** .. stupid code duplication :( */
get_string_masked(cptr prompt,char * buf,int len)1226 bool get_string_masked(cptr prompt, char *buf, int len)
1227 {
1228 	bool res;
1229 
1230 	/* Display prompt */
1231 	prt(prompt, 0, 0);
1232 
1233 	/* Ask the user for a string */
1234 	res = askfor_aux(buf, len, 1);
1235 
1236 	/* Clear prompt */
1237 	prt("", 0, 0);
1238 
1239 	/* Result */
1240 	return (res);
1241 }
1242 
1243 /*
1244  * Prompts for a keypress OR a mouse press.
1245  *
1246  * The "prompt" should take the form "Command: "
1247  *
1248  * Returns TRUE unless the character is "Escape"
1249  */
get_com_ex(cptr prompt,char * command,event_type * xe)1250 bool get_com_ex(cptr prompt, char *command, event_type *xe)
1251 {
1252 	event_type ke;
1253 
1254 	/* The top line is "icky" */
1255 	topline_icky = TRUE;
1256 
1257 	/* Display a prompt */
1258 	prt(prompt, 0, 0);
1259 
1260 	/* Show cursor */
1261 	Term_show_ui_cursor();
1262 
1263 	/* Get a key */
1264 	while(1) {
1265 		ke = inkey_ex();
1266 		if (ke.type == EVT_ESCAPE) ke.key = ESCAPE;
1267 		if (ke.key == '\xff' && ke.index == 0) continue;
1268 		break;
1269 	}
1270 	*command = ke.key;
1271 	*xe = ke;
1272 
1273 	/* Clear the prompt */
1274 	prt("", 0, 0);
1275 
1276 	/* Hide cursor */
1277 	Term_hide_ui_cursor();
1278 
1279 	/* Fix the top line */
1280 	topline_icky = FALSE;
1281 
1282 	/* Flush any events */
1283 	Flush_queue();
1284 
1285 	/* Handle "cancel" */
1286 	if (*command == ESCAPE) return (FALSE);
1287 
1288 	/* Success */
1289 	return (TRUE);
1290 }
1291 /*
1292  * Prompts for a keypress
1293  *
1294  * The "prompt" should take the form "Command: "
1295  *
1296  * Returns TRUE unless the character is "Escape"
1297  */
get_com(cptr prompt,char * command)1298 bool get_com(cptr prompt, char *command)
1299 {
1300 	event_type ke;
1301 
1302 	/* The top line is "icky" */
1303 	topline_icky = TRUE;
1304 
1305 	/* Display a prompt */
1306 	prt(prompt, 0, 0);
1307 
1308 	/* Show cursor */
1309 	Term_show_ui_cursor();
1310 
1311 	/* Get a key */
1312 	*command = inkey();
1313 
1314 	/* Clear the prompt */
1315 	prt("", 0, 0);
1316 
1317 	/* Hide cursor */
1318 	Term_hide_ui_cursor();
1319 
1320 	/* Fix the top line */
1321 	topline_icky = FALSE;
1322 
1323 	/* Flush any events */
1324 	Flush_queue();
1325 
1326 	/* Handle "cancel" */
1327 	if (*command == ESCAPE) return (FALSE);
1328 
1329 	/* Success */
1330 	return (TRUE);
1331 }
1332 
1333 /*
1334  * Hack -- special buffer to hold the action of the current keymap
1335  */
1336 static char request_command_buffer[256];
1337 
1338 /*
1339  * Request a command from the user.
1340  *
1341  * Sets command_cmd, command_dir, command_rep, command_arg.
1342  *
1343  * Note that "caret" ("^") is treated special, and is used to
1344  * allow manual input of control characters.  This can be used
1345  * on many machines to request repeated tunneling (Ctrl-H) and
1346  * on the Macintosh to request "Control-Caret".
1347  *
1348  * Note that this command is used both in the dungeon and in
1349  * stores, and must be careful to work in both situations.
1350  */
request_command(bool shopping)1351 void request_command(bool shopping)
1352 {
1353 	event_type ke = EVENT_EMPTY;
1354 
1355 	int mode;
1356 
1357 	cptr act;
1358 
1359 	/* Roguelike */
1360 	if (rogue_like_commands)
1361 	{
1362 		mode = KEYMAP_MODE_ROGUE;
1363 	}
1364 
1365 	/* Original */
1366 	else
1367 	{
1368 		mode = KEYMAP_MODE_ORIG;
1369 	}
1370 
1371 	/* Flush the input */
1372 	/* flush(); */
1373 
1374 	if (z_ask_command_aux) z_ask_command_aux(NULL, shopping);
1375 
1376 	/* Get a keypress in "command" mode */
1377 
1378 	/* Activate "command mode" */
1379 	inkey_flag = TRUE;
1380 
1381 	/* Activate "scan mode" */
1382 	inkey_scan = TRUE;
1383 
1384 	/* Get a command */
1385 	ke = inkey_ex();
1386 
1387 	/* Return if no key was pressed */
1388 	if (!ke.key) return;
1389 
1390 	msg_flag = FALSE;
1391 	c_msg_print(NULL);
1392 
1393 	/* Clear top line */
1394 	prt("", 0, 0);
1395 
1396 	/* Bypass "keymap" */
1397 	if (ke.key == '\\')
1398 	{
1399 		/* Get a char to use without casting */
1400 		(void)(get_com("Command: ", &ke.key));
1401 
1402 		/* Hack -- bypass keymaps */
1403 		if (!inkey_next) inkey_next = "";
1404 
1405 		/* Hack -- allow "control chars" to be entered */
1406 		if (ke.key == '^')
1407 		{
1408 			/* Get a char to "cast" into a control char */
1409 			(void)(get_com("Command: Control: ", &ke.key));
1410 
1411 			/* Convert */
1412 			ke.key = KTRL(ke.key);
1413 		}
1414 
1415 		/* Use the key directly */
1416 		command_cmd = ke.key;
1417 		command_cmd_ex = ke;
1418 	}
1419 
1420 	else
1421 	{
1422 		/* Hack -- allow "control chars" to be entered */
1423 		if (ke.key == '^')
1424 		{
1425 			/* Get a char to "cast" into a control char */
1426 			(void)(get_com("Control: ", &ke.key));
1427 
1428 			/* Convert */
1429 			ke.key = KTRL(ke.key);
1430 		}
1431 
1432 		/* Look up applicable keymap */
1433 		act = keymap_act[mode][(byte)(ke.key)];
1434 
1435 		/* Apply keymap if not inside a keymap already */
1436 		if (act && !inkey_next)
1437 		{
1438 			/* Install the keymap */
1439 			my_strcpy(request_command_buffer, act,
1440 			          sizeof(request_command_buffer));
1441 
1442 			/* Start using the buffer */
1443 			inkey_next = request_command_buffer;
1444 
1445 			/* Continue */
1446 			return;
1447 		}
1448 
1449 		/* Accept command */
1450 		command_cmd = ke.key;
1451 		command_cmd_ex = ke;
1452 	}
1453 
1454 	/* Paranoia */
1455 	if (!command_cmd) command_cmd = ESCAPE;
1456 
1457 	/* Shopping */
1458 	if (shopping)
1459 	{
1460 		/* Convert */
1461 		switch (command_cmd)
1462 		{
1463 			/* Command "p" -> "purchase" (get) */
1464 			case 'p': command_cmd = 'g'; break;
1465 
1466 			/* Command "m" -> "purchase" (get) */
1467 			case 'm': command_cmd = 'g'; break;
1468 
1469 			/* Command "s" -> "sell" (drop) */
1470 			case 's': command_cmd = 'd'; break;
1471 
1472 			/* Command "I" -> "look" (Inspect) */
1473 			case 'I': command_cmd = 'l'; break;
1474 
1475 			/* Command "x" -> "look" (examine) */
1476 			case 'x': command_cmd = 'l'; break;
1477 		}
1478 	}
1479 
1480 	/* Hack -- erase the message line. */
1481 	prt("", 0, 0);
1482 }
1483 
c_get_dir(char * dp,cptr prompt,bool allow_target,bool allow_friend)1484 bool c_get_dir(char *dp, cptr prompt, bool allow_target, bool allow_friend)
1485 {
1486 	int	dir = 0;
1487 
1488 	char	command;
1489 
1490 	cptr	p;
1491 
1492 	char buf[80];
1493 	buf[0] = '\0';
1494 	my_strcpy(buf, prompt, 80);
1495 
1496 	if (allow_target)
1497 		p = "Direction ('*' to choose target, non-direction cancels) ?";
1498 	else
1499 		p = "Direction (non-direction cancels)?";
1500 	strcat(buf, p);
1501 
1502 	if (z_ask_dir_aux) z_ask_dir_aux(buf, allow_target, allow_friend);
1503 
1504 	get_com(buf, &command);
1505 
1506 	/* Handle target request */
1507 	if (command == '*' && allow_target)
1508 	{
1509 		if (cmd_target())
1510 			dir = 5;
1511 	}
1512 
1513 	else if (command == '(' && allow_friend)
1514 	{
1515 		if (cmd_target_friendly())
1516 			dir = 5;
1517 	}
1518 
1519 	else dir = target_dir(command);
1520 
1521 	*dp = (byte)dir;
1522 
1523 	if (!dir) return (FALSE);
1524 
1525 	return (TRUE);
1526 }
1527 
1528 /*
1529  * Extract a direction (or zero) from a character
1530  */
target_dir(char ch)1531 int target_dir(char ch)
1532 {
1533 	int d = 0;
1534 
1535 	int mode;
1536 
1537 	cptr act;
1538 
1539 	cptr s;
1540 
1541 
1542 	/* Already a direction? */
1543 	if (isdigit((unsigned char)ch))
1544 	{
1545 		d = D2I(ch);
1546 	}
1547 	else
1548 	{
1549 		/* Roguelike */
1550 		if (rogue_like_commands)
1551 		{
1552 			mode = KEYMAP_MODE_ROGUE;
1553 		}
1554 
1555 		/* Original */
1556 		else
1557 		{
1558 			mode = KEYMAP_MODE_ORIG;
1559 		}
1560 
1561 		/* Extract the action (if any) */
1562 		act = keymap_act[mode][(byte)(ch)];
1563 
1564 		/* Analyze */
1565 		if (act)
1566 		{
1567 			/* Convert to a direction */
1568 			for (s = act; *s; ++s)
1569 			{
1570 				/* Use any digits in keymap */
1571 				if (isdigit((unsigned char)*s)) d = D2I(*s);
1572 			}
1573 		}
1574 	}
1575 
1576 	/* Paranoia */
1577 	if (d == 5) d = 0;
1578 
1579 	/* Return direction */
1580 	return (d);
1581 }
1582 
get_dir(int * dp)1583 bool get_dir(int *dp)
1584 {
1585 	int	dir = 0;
1586 
1587 	char	command;
1588 
1589 	cptr	p;
1590 
1591 	p = "Direction ('*' to choose a target, non-direction cancels) ? ";
1592 
1593 	get_com(p, &command);
1594 
1595 	/* Handle target request */
1596 	if (command == '*')
1597 	{
1598 		if (cmd_target())
1599 			dir = 5;
1600 	}
1601 
1602 	else dir = target_dir(command);
1603 
1604 	*dp = dir;
1605 
1606 	if (!dir) return (FALSE);
1607 
1608 	return (TRUE);
1609 }
1610 
1611 
1612 /*
1613  * Display a string on the screen using an attribute.
1614  *
1615  * At the given location, using the given attribute, if allowed,
1616  * add the given string.  Do not clear the line.
1617  */
c_put_str(byte attr,cptr str,int row,int col)1618 void c_put_str(byte attr, cptr str, int row, int col)
1619 {
1620 	/* Position cursor, Dump the attr/text */
1621 	Term_putstr(col, row, -1, attr, str);
1622 }
1623 
1624 
1625 /*
1626  * As above, but in "white"
1627  */
put_str(cptr str,int row,int col)1628 void put_str(cptr str, int row, int col)
1629 {
1630 	/* Spawn */
1631 	Term_putstr(col, row, -1, TERM_WHITE, str);
1632 }
1633 
1634 /*
1635  * Memorize an already drawn line.
1636  */
mem_line(int row,int col,int n)1637 void mem_line(int row, int col, int n)
1638 {
1639 	int i;
1640 	/* No ickyness */
1641 	if (!Term->mem) return;
1642 
1643 	/* Check row bounds */
1644 	if (row < 0) return;
1645 	if (row >= Term->hgt) return;
1646 
1647 	for (i = 0; i < n; i++)
1648 	{
1649 		if (col + i < 0) continue;
1650 		if (col + i >= Term->wid) break;
1651 		Term_mem_ch(col + i, row,
1652 			Term->scr->a[row][col+i],
1653 			Term->scr->c[row][col+i],
1654 			Term->scr->ta[row][col+i],
1655 			Term->scr->tc[row][col+i]);
1656 	}
1657 }
1658 
1659 /*
1660  * Verify something with the user
1661  *
1662  * The "prompt" should take the form "Query? "
1663  *
1664  * Note that "[y/n]" is appended to the prompt.
1665  */
get_check(cptr prompt)1666 bool get_check(cptr prompt)
1667 {
1668 	int i;
1669 
1670 	char buf[80];
1671 
1672 	/* Option -- "auto_accept" */
1673 	if (auto_accept) return (TRUE);
1674 
1675 	/* Hack -- Build a "useful" prompt */
1676 	strnfmt(buf, 78, "%.70s[y/n] ", prompt);
1677 
1678 	/* The top line is "icky" */
1679 	topline_icky = TRUE;
1680 
1681 	/* Prompt for it */
1682 	prt(buf, 0, 0);
1683 
1684 	/* With cursor */
1685 	Term_show_ui_cursor();
1686 
1687 	/* Get an acceptable answer */
1688 	while (TRUE)
1689 	{
1690 		/* Hack -- ask term2 */
1691 		if (z_ask_confirm_aux) z_ask_confirm_aux(buf);
1692 
1693 		i = inkey();
1694 		if (i == ESCAPE) break;
1695 		if (strchr("YyNn", i)) break;
1696 		bell();
1697 	}
1698 
1699 	/* Hide cursor */
1700 	Term_hide_ui_cursor();
1701 
1702 	/* Erase the prompt */
1703 	prt("", 0, 0);
1704 
1705 	/* The top line is OK again */
1706 	topline_icky = FALSE;
1707 
1708 	/* Flush any events that came in while we were icky */
1709 	Flush_queue();
1710 
1711 	/* Normal negation */
1712 	if ((i != 'Y') && (i != 'y')) return (FALSE);
1713 
1714 	/* Success */
1715 	return (TRUE);
1716 }
1717 
1718 
1719 /*
1720  * Calculate the index of a message
1721  */
message_age2idx(int age)1722 static s16b message_age2idx(int age)
1723 {
1724 	return ((message__next + MESSAGE_MAX - (age + 1)) % MESSAGE_MAX);
1725 }
1726 
1727 /*
1728  * Recall the "text" of a saved message
1729  */
message_str(s16b age)1730 cptr message_str(s16b age)
1731 {
1732 	static char buf[1024];
1733 	s16b x;
1734 	u16b o;
1735 	cptr s;
1736 
1737 	/* Forgotten messages have no text */
1738 	if ((age < 0) || (age >= message_num())) return ("");
1739 
1740 	/* Get the "logical" index */
1741 	x = message_age2idx(age);
1742 
1743 	/* Get the "offset" for the message */
1744 	o = message__ptr[x];
1745 
1746 	/* Get the message text */
1747 	s = &message__buf[o];
1748 
1749 	/* HACK - Handle repeated messages */
1750 	if (message__count[x] > 1)
1751 	{
1752 		strnfmt(buf, sizeof(buf), "%s <%dx>", s, message__count[x]);
1753 		s = buf;
1754 	}
1755 
1756 	/* Return the message text */
1757 	return (s);
1758 }
1759 
1760 /*
1761  * Recall the "text" of a last saved message, which is NON-LOCAL
1762  */
message_last()1763 cptr message_last()
1764 {
1765     s16b x;
1766     u16b o;
1767     cptr s = "";
1768 
1769     /* Get the "logical" last index */
1770     x = message_age2idx(0);
1771 
1772     /* Loop */
1773     while (x != message__last)
1774     {
1775         /* Get the "offset" for the message */
1776         o = message__ptr[x];
1777 
1778         /* Get the message text */
1779         s = &message__buf[o];
1780 
1781         /* Make sure it's not "local" */
1782         if (message__type[x] != MSG_LOCAL) break;
1783 
1784         /* Advance x, wrap if needed */
1785         if (x == 0) x = MESSAGE_MAX - 1;
1786         else x--;
1787     }
1788 
1789     /* Return the message text */
1790     return (s);
1791 }
1792 
1793 /*
1794  * How many messages are "available"?
1795  */
message_num(void)1796 s16b message_num(void)
1797 {
1798         int last, next, n;
1799 
1800         /* Extract the indexes */
1801         last = message__last;
1802         next = message__next;
1803 
1804         /* Handle "wrap" */
1805         if (next < last) next += MESSAGE_MAX;
1806 
1807         /* Extract the space */
1808         n = (next - last);
1809 
1810         /* Return the result */
1811         return (n);
1812 }
1813 
1814 /*
1815  * Recall the "type" of a saved message
1816  */
message_type(s16b age)1817 u16b message_type(s16b age)
1818 {
1819 	s16b x;
1820 
1821 	/* Paranoia */
1822 	if (!message__type) return (MSG_GENERIC);
1823 
1824 	/* Forgotten messages are generic */
1825 	if ((age < 0) || (age >= message_num())) return (MSG_GENERIC);
1826 
1827 	/* Get the "logical" index */
1828 	x = message_age2idx(age);
1829 
1830 	/* Return the message type */
1831 	return (message__type[x]);
1832 }
1833 
1834 /*
1835  * Add a new message, with great efficiency
1836  */
c_message_add(cptr str,u16b type)1837 void c_message_add(cptr str, u16b type)
1838 {
1839 	int k, i, x, o;
1840 	size_t n;
1841 
1842 	cptr s;
1843 
1844 	cptr u;
1845 	char *v;
1846 
1847 	/*** Step 1 -- Analyze the message ***/
1848 
1849 	/* Hack -- Ignore "non-messages" */
1850 	if (!str) return;
1851 
1852 	/* Message length */
1853 	n = strlen(str);
1854 
1855 	/* Hack -- Ignore "long" messages */
1856 	if (n >= MESSAGE_BUF / 4) return;
1857 
1858 
1859 	/*** Step 2 -- Attempt to optimize ***/
1860 
1861 	/* Get the "logical" last index */
1862 	x = message_age2idx(0);
1863 
1864 	/* Get the "offset" for the last message */
1865 	o = message__ptr[x];
1866 
1867 	/* Get the message text */
1868 	s = &message__buf[o];
1869 
1870 	/* Last message repeated? */
1871 	if (streq(str, s))
1872 	{
1873 		/* Increase the message count */
1874 		message__count[x]++;
1875 
1876 		/* Redraw */
1877 		p_ptr->window |= (PW_MESSAGE | PW_MESSAGE_CHAT);
1878 
1879 		/* Success */
1880 		return;
1881 	}
1882 
1883 	/*** Step 3 -- Attempt to optimize ***/
1884 
1885 	/* Limit number of messages to check */
1886 	k = message_num() / 4;
1887 
1888 	/* Limit number of messages to check */
1889 	if (k > 32) k = 32;
1890 
1891 	/* Start just after the most recent message */
1892 	i = message__next;
1893 
1894 	/* Check the last few messages for duplication */
1895 	for ( ; k; k--)
1896 	{
1897 		u16b q;
1898 
1899 		cptr old;
1900 
1901 		/* Back up, wrap if needed */
1902 		if (i-- == 0) i = MESSAGE_MAX - 1;
1903 
1904 		/* Stop before oldest message */
1905 		if (i == message__last) break;
1906 
1907 		/* Index */
1908 		o = message__ptr[i];
1909 
1910 		/* Extract "distance" from "head" */
1911 		q = (message__head + MESSAGE_BUF - o) % MESSAGE_BUF;
1912 
1913 		/* Do not optimize over large distances */
1914 		if (q >= MESSAGE_BUF / 4) continue;
1915 
1916 		/* Get the old string */
1917 		old = &message__buf[o];
1918 
1919 		/* Continue if not equal */
1920 		if (!streq(str, old)) continue;
1921 
1922 		/* Get the next available message index */
1923 		x = message__next;
1924 
1925 		/* Advance 'message__next', wrap if needed */
1926 		if (++message__next == MESSAGE_MAX) message__next = 0;
1927 
1928 		/* Kill last message if needed */
1929 		if (message__next == message__last)
1930 		{
1931 			/* Advance 'message__last', wrap if needed */
1932 			if (++message__last == MESSAGE_MAX) message__last = 0;
1933 		}
1934 
1935 		/* Assign the starting address */
1936 		message__ptr[x] = message__ptr[i];
1937 
1938 		/* Store the message type */
1939 		message__type[x] = type;
1940 
1941 		/* Store the message count */
1942 		message__count[x] = 1;
1943 
1944 		/* Redraw */
1945 		p_ptr->window |= (PW_MESSAGE | PW_MESSAGE_CHAT);
1946 
1947 		/* Success */
1948 		return;
1949 	}
1950 
1951 	/*** Step 4 -- Ensure space before end of buffer ***/
1952 
1953 	/* Kill messages, and wrap, if needed */
1954 	if (message__head + (n + 1) >= MESSAGE_BUF)
1955 	{
1956 		/* Kill all "dead" messages */
1957 		for (i = message__last; TRUE; i++)
1958 		{
1959 			/* Wrap if needed */
1960 			if (i == MESSAGE_MAX) i = 0;
1961 
1962 			/* Stop before the new message */
1963 			if (i == message__next) break;
1964 
1965 			/* Get offset */
1966 			o = message__ptr[i];
1967 
1968 			/* Kill "dead" messages */
1969 			if (o >= message__head)
1970 			{
1971 				/* Track oldest message */
1972 				message__last = i + 1;
1973 			}
1974 		}
1975 
1976 		/* Wrap "tail" if needed */
1977 		if (message__tail >= message__head) message__tail = 0;
1978 
1979 		/* Start over */
1980 		message__head = 0;
1981 	}
1982 
1983 
1984 	/*** Step 5 -- Ensure space for actual characters ***/
1985 
1986 	/* Kill messages, if needed */
1987 	if (message__head + (n + 1) > message__tail)
1988 	{
1989 		/* Advance to new "tail" location */
1990 		message__tail += (MESSAGE_BUF / 4);
1991 
1992 		/* Kill all "dead" messages */
1993 		for (i = message__last; TRUE; i++)
1994 		{
1995 			/* Wrap if needed */
1996 			if (i == MESSAGE_MAX) i = 0;
1997 
1998 			/* Stop before the new message */
1999 			if (i == message__next) break;
2000 
2001 			/* Get offset */
2002 			o = message__ptr[i];
2003 
2004 			/* Kill "dead" messages */
2005 			if ((o >= message__head) && (o < message__tail))
2006 			{
2007 				/* Track oldest message */
2008 				message__last = i + 1;
2009 			}
2010 		}
2011 	}
2012 
2013 
2014 	/*** Step 6 -- Grab a new message index ***/
2015 
2016 	/* Get the next available message index */
2017 	x = message__next;
2018 
2019 	/* Advance 'message__next', wrap if needed */
2020 	if (++message__next == MESSAGE_MAX) message__next = 0;
2021 
2022 	/* Kill last message if needed */
2023 	if (message__next == message__last)
2024 	{
2025 		/* Advance 'message__last', wrap if needed */
2026 		if (++message__last == MESSAGE_MAX) message__last = 0;
2027 	}
2028 
2029 
2030 	/*** Step 7 -- Insert the message text ***/
2031 
2032 	/* Assign the starting address */
2033 	message__ptr[x] = message__head;
2034 
2035 	/* Inline 'strcpy(message__buf + message__head, str)' */
2036 	v = message__buf + message__head;
2037 	for (u = str; *u; ) *v++ = *u++;
2038 	*v = '\0';
2039 
2040 	/* Advance the "head" pointer */
2041 	message__head += ((u16b)n + 1); /* should work, n (strlen) is unlikely to be larger than u16b */
2042 
2043 	/* Store the message type */
2044 	message__type[x] = type;
2045 
2046 	/* Store the message count */
2047 	message__count[x] = 1;
2048 
2049 
2050 	/* Window stuff */
2051 	p_ptr->window |= PW_MESSAGE | PW_MESSAGE_CHAT;
2052 }
2053 
2054 /* Hack: Instead of deleting the message, it's being converted to GENERIC
2055  * TODO: Remove it completly */
c_message_del(s16b age)2056 void c_message_del(s16b age)
2057 {
2058 	/* static char buf[1024]; */
2059 	s16b x;
2060 	u16b o;
2061 
2062 	/* Forgotten messages have no text */
2063 	if ((age < 0) || (age >= message_num())) return;
2064 
2065 	/* Get the "logical" index */
2066 	x = message_age2idx(age);
2067 
2068 	/* Get the "offset" for the message */
2069 	o = message__ptr[x];
2070 
2071 	/* Hide */
2072 	message__type[x] = MSG_GENERIC;
2073 
2074 	/* Return */
2075 	return;
2076 }
2077 
2078 
2079 /*
2080  * Hack -- flush
2081  *
2082  * Not needed --KLJ--
2083  */
2084 #if 0
2085 static void msg_flush(int x)
2086 {
2087 	byte a = TERM_L_BLUE;
2088 
2089 	/* The top line is "icky" */
2090 	topline_icky = TRUE;
2091 
2092 	/* Pause for response */
2093 	Term_putstr(x, 0, -1, a, "-more-");
2094 
2095 	/* Get an acceptable keypress */
2096 	while (1)
2097 	{
2098 		int cmd = inkey();
2099 		if (quick_messages) break;
2100 		if ((cmd == ESCAPE) || (cmd == ' ')) break;
2101 		if ((cmd == '\n') || (cmd == '\r')) break;
2102 		bell();
2103 	}
2104 
2105 	/* Clear the line */
2106 	Term_erase(0, 0, 255);
2107 
2108 	/* The top line is OK */
2109 	topline_icky = FALSE;
2110 
2111 	Flush_queue();
2112 }
2113 #endif
2114 
2115 static char last_msg[1024];
2116 static int  last_msg_more = FALSE;
2117 static int  last_msg_type = MSG_LOCAL;
2118 
msg_flush(void)2119 void msg_flush(void)
2120 {
2121 	if (last_msg_more == FALSE) return;
2122 
2123 	msg_flag = TRUE;
2124 
2125 	c_msg_print_aux(last_msg, last_msg_type);
2126 
2127 	msg_flag = FALSE;
2128 }
2129 
2130 
2131 #if 0
2132 #define MORE_PROMPT "  -more-"
2133 #define MORE_PROMPT_LEN 8
2134 #else
2135 #define MORE_PROMPT "-"
2136 #define MORE_PROMPT_LEN 1
2137 #endif
2138 
2139 /*
2140  * Output a message to the top line of the screen.
2141  *
2142  * Break long messages into multiple pieces (40-72 chars).
2143  *
2144  * Allow multiple short messages to "share" the top line.
2145  *
2146  * Prompt the user to make sure he has a chance to read them.
2147  *
2148  * These messages are memorized for later reference (see above).
2149  *
2150  * We could do "Term_fresh()" to provide "flicker" if needed.
2151  *
2152  * The global "msg_flag" variable can be cleared to tell us to
2153  * "erase" any "pending" messages still on the screen.
2154  *
2155  * XXX XXX XXX Note that we must be very careful about using the
2156  * "msg_print()" functions without explicitly calling the special
2157  * "msg_print(NULL)" function, since this may result in the loss
2158  * of information if the screen is cleared, or if anything is
2159  * displayed on the top line.
2160  *
2161  * XXX XXX XXX Note that "msg_print(NULL)" will clear the top line
2162  * even if no messages are pending.  This is probably a hack.
2163  */
c_msg_print(cptr msg)2164 void c_msg_print(cptr msg)
2165 {
2166 	c_msg_print_aux(msg, MSG_LOCAL);
2167 }
c_msg_print_aux(cptr msg,u16b type)2168 void c_msg_print_aux(cptr msg, u16b type)
2169 {
2170 	static int p = 0;
2171 
2172 	int n;
2173 	int maxcol;
2174 	size_t truncate;
2175 #if 0
2176 	char *t;
2177 #endif
2178 	char buf[1024];
2179 
2180 	/* Hack -- Reset */
2181 	if (!msg_flag) p = 0;
2182 
2183 	/* Keldon-Hack -- Always reset */
2184 	p = 0;
2185 	prt("", 0, 0);
2186 
2187 	/* Message length */
2188 	n = (msg ? strlen(msg) : 0);
2189 
2190 #if 0
2191 	/* Hack -- flush when requested or needed */
2192 	if (p && (!msg || ((p + n) > 72)))
2193 	{
2194 		/* Flush */
2195 		/*msg_flush(p);*/
2196 
2197 		/* Forget it */
2198 		msg_flag = FALSE;
2199 
2200 		/* Reset */
2201 		p = 0;
2202 	}
2203 #endif
2204 
2205 	/* No message */
2206 	if (!msg) return;
2207 
2208 	/* Paranoia */
2209 	if (n > 1000) return;
2210 
2211 
2212 	if (!msg_flag) /* Hack -- unless something is preventing it */
2213 	/* Memorize the message */
2214 	c_message_add(msg, type);
2215 
2216 
2217 	/* Display it */
2218 	Term_putstr(0, 0, Term->wid, TERM_WHITE, msg);
2219 
2220 	/* Display "-more-" prompt */
2221 	maxcol = (Term->wid - MORE_PROMPT_LEN);
2222 	if (n > maxcol)
2223 	{
2224 		Term_putstr(maxcol, 0, MORE_PROMPT_LEN, TERM_YELLOW, MORE_PROMPT);
2225 	}
2226 	last_msg_more = FALSE;
2227 
2228 #if 1
2229 	/* Analyze the buffer */
2230 	/* t = buf; */
2231 
2232 	/* Split message */
2233 	while (n > maxcol)
2234 	{
2235 		int check, split;
2236 
2237 		/* Default split */
2238 		split = maxcol;
2239 
2240 		/* Find the "best" split point */
2241 		for (check = 40; check < maxcol; check++)
2242 		{
2243 			/* Found a valid split point */
2244 			if (msg[check] == ' ') split = check;
2245 		}
2246 
2247 		/* Save part of the message */
2248 		my_strcpy(last_msg, &msg[split], sizeof(last_msg));
2249 		last_msg_type = type;
2250 		last_msg_more = TRUE;
2251 
2252 		break;
2253 	}
2254 #endif
2255 #if 0
2256 	/* Remember the message */
2257 	msg_flag = TRUE;
2258 
2259 	/* Remember the position */
2260 	p += n + 1;
2261 #endif
2262 }
2263 
2264 
2265 /* Call Term_putstr multiple times, word-wraping the "msg".
2266  * "sx" and "sy" are the coordinates we start at.
2267  * "n" is maximum "msg" len, if -1 is passed, strlen(msg) will be used.
2268  * "m" is maximum lines we have left. If passed as a negative number,
2269  * "prt_multi" will draw from bottom to top.
2270  *
2271  * Returns number of lines printed. No actual changes should be seen
2272  * until Term_fresh().
2273  */
prt_multi(int sx,int sy,int n,int m,int attr,cptr msg)2274 int prt_multi(int sx, int sy, int n, int m, int attr, cptr msg)
2275 {
2276 	char *t;
2277 	char buf[1024];
2278 	char *line_ptr[256];
2279 	size_t line_end[256];
2280 	int lines = 0, i;
2281 	bool reverse = FALSE;
2282 	int maxcol = Term->wid - sx;
2283 
2284 	/* If "m" is passed as negative, assume reverse mode */
2285 	if (m < 0)
2286 	{
2287 		m = 0 - m;
2288 		reverse = TRUE;
2289 	}
2290 
2291 	/* Analyze the buffer */
2292 	my_strcpy(buf, msg, 1024);
2293 	t = buf;
2294 	n = n < 0 ? strlen(buf) : MIN(n, 1024);
2295 
2296 	/* Split message */
2297 	while (n > maxcol)
2298 	{
2299 		int check, split;
2300 
2301 		/* Default split */
2302 		split = maxcol;
2303 
2304 		/* Find the "best" split point */
2305 		for (check = 40; check < maxcol; check++)
2306 		{
2307 			/* Found a valid split point */
2308 			if (t[check] == ' ') split = check;
2309 		}
2310 
2311 		/* Save part of the message */
2312 		line_ptr[lines] = t;
2313 		line_end[lines] = split;
2314 		lines++;
2315 
2316 		/* Prepare to recurse on the rest of "buf" */
2317 		t += split; n -= split;
2318 	}
2319 
2320 	/* Save the tail of the message */
2321 	line_ptr[lines] = t;
2322 	line_end[lines] = n;
2323 	lines++;
2324 
2325 	/* Draw lines */
2326 	for (i = 0; i < lines; i++)
2327 	{
2328 		int x, y;
2329 		int l = reverse ? lines - 1 - i : i ;
2330 		int d = reverse ? -1 : 1 ;
2331 		Term_putstr(sx, sy + i * d, line_end[l], attr, line_ptr[l]);
2332 		Term_locate(&x, &y);
2333 		Term_erase(x, y, 255);
2334 		if (m - i <= 0) break;
2335 	}
2336 
2337 	return i;
2338 }
2339 
2340 
2341 /*
2342  * Request a "quantity" from the user
2343  *
2344  * Hack -- allow "command_arg" to specify a quantity
2345  */
c_get_quantity(cptr prompt,s32b max)2346 s32b c_get_quantity(cptr prompt, s32b max)
2347 {
2348 	int amt;
2349 
2350 	char tmp[80];
2351 
2352 	char buf[80];
2353 
2354 
2355 	/* Build a prompt if needed */
2356 	if (!prompt)
2357 	{
2358 		/* Build a prompt */
2359 		sprintf(tmp, "Quantity (1-%" PRId32 "): ", max);
2360 
2361 		/* Use that prompt */
2362 		prompt = tmp;
2363 	}
2364 
2365 
2366 	/* Default to one */
2367 	amt = 1;
2368 
2369 	/* Build the default */
2370 	sprintf(buf, "%d", amt);
2371 
2372 	/* Ask for a quantity */
2373 	if (!get_string(prompt, buf, 8)) return (0);
2374 
2375 	/* Extract a number */
2376 	amt = atoi(buf);
2377 
2378 	/* A letter means "all" */
2379 	if (isalpha(buf[0])) amt = max;
2380 
2381 	/* Enforce the maximum, if maximum is defined */
2382 	if ((max >= 0) && (amt > max)) amt = max;
2383 
2384 	/* Enforce the minimum */
2385 	if (amt < 0) amt = 0;
2386 
2387 	/* Return the result */
2388 	return (amt);
2389 }
2390 
2391 /*
2392  * Handle the slash effects
2393  */
2394 /*
2395  * Note: this function uses up the static_timer(3). This means you can't use
2396  * static_timer(3) anywhere else from now on.
2397  */
slashfx_dir_offset(int * x,int * y,int dir,bool invert)2398 void slashfx_dir_offset(int *x, int *y, int dir, bool invert)
2399 {
2400 	const int dir_offset_y[9] = { 1, 1, 1,  0, 0, 0,  -1,-1,-1 };
2401 	const int dir_offset_x[9] = { -1, 0, 1,  -1, 0, 1,  -1, 0, 1 };
2402 	*x = dir_offset_x[dir - 1] * (invert ? 1 : 1);
2403 	*y = dir_offset_y[dir - 1] * (invert ? 1 : 1);
2404 }
update_slashfx()2405 void update_slashfx()
2406 {
2407 	micro passed = static_timer(3);
2408 	u16b milli = (u16b)(passed / 1000);
2409 	int j, i;
2410 	for (j = 0; j < MAX_HGT; j++)
2411 	{
2412 		for (i = 0; i < MAX_WID; i++)
2413 		{
2414 			if (sfx_delay[j][i] > 0)
2415 			{
2416 				sfx_delay[j][i] -= milli;
2417 				/* Delay timeout */
2418 				if (sfx_delay[j][i] <= 0)
2419 				{
2420 					sfx_delay[j][i] = 0;
2421 				}
2422 				/* Draw same tile */
2423 				refresh_char_aux(i, j);
2424 			}
2425 		}
2426 	}
2427 }
discard_slashfx(int y,int x)2428 void discard_slashfx(int y, int x)
2429 {
2430 	if (sfx_delay[y][x] > 0 && (refresh_char_aux))
2431 	{
2432 		sfx_delay[y][x] = 0;
2433 		refresh_char_aux(x, y);
2434 	}
2435 }
2436 
2437 
clear_from(int row)2438 void clear_from(int row)
2439 {
2440 	int y;
2441 
2442 	/* Erase requested rows */
2443 	for (y = row; y < Term->hgt; y++)
2444 	{
2445 		/* Erase part of the screen */
2446 		Term_erase(0, y, 255);
2447 	}
2448 }
2449 
caveclr(cave_view_type * dest,int len)2450 int caveclr(cave_view_type* dest, int len)
2451 {
2452 	int i;
2453 	/* Erase a character n times */
2454 	for (i = 0; i < len; i++)
2455 	{
2456 		dest[i].a = 0x80;
2457 		dest[i].c = 0x80;
2458 	}
2459 	return 1;
2460 }
2461 
cavecpy(cave_view_type * dest,cave_view_type * src,int len)2462 int cavecpy(cave_view_type* dest, cave_view_type *src, int len)
2463 {
2464 	int i;
2465 	/* Copy a line with length "len" */
2466 	for (i = 0; i < len; i++)
2467 	{
2468 		dest[i].a = src[i].a;
2469 		dest[i].c = src[i].c;
2470 	}
2471 	return 1;
2472 }
2473 
cavemem(cave_view_type * src,int len,s16b x,s16b y)2474 int cavemem(cave_view_type* src, int len, s16b x, s16b y)
2475 {
2476 	int i;
2477 	s16b dx = x + DUNGEON_OFFSET_X;
2478 	s16b dy = y + DUNGEON_OFFSET_Y;
2479 
2480 	/* Draw a character n times */
2481 	for (i = 0; i < len; i++)
2482 	{
2483 		byte ta = p_ptr->trn_info[y][x + i].a;
2484 		char tc = p_ptr->trn_info[y][x + i].c;
2485 		Term_mem_ch(i + dx, dy, src[i].a, src[i].c, ta, tc);
2486 	}
2487 	return 1;
2488 }
2489 
cavedraw(cave_view_type * src,int len,s16b x,s16b y)2490 int cavedraw(cave_view_type* src, int len, s16b x, s16b y)
2491 {
2492 	int i;
2493 	s16b dx = x + DUNGEON_OFFSET_X;
2494 	s16b dy = y + DUNGEON_OFFSET_Y;
2495 
2496 	/* Paranoia - bounds */
2497 	if (dx < 0) return -1;
2498 	if (dy < 0 || dy >= Term->hgt) return -1;
2499 
2500 	/* Draw a character n times */
2501 	for (i = 0; i < len; i++)
2502 	{
2503 		/* Paranoia - bounds */
2504 		if (dx + i >= Term->wid) return -1;
2505 
2506 		/* Don't draw on screen if character is 0 */
2507 		if (src[i].c)
2508 		{
2509 			byte ta = p_ptr->trn_info[y][x + i].a;
2510 			char tc = p_ptr->trn_info[y][x + i].c;
2511 			Term_queue_char(Term, i + dx, dy, src[i].a, src[i].c, ta, tc);
2512 		}
2513 	}
2514 	return 1;
2515 }
2516 
caveprt(cave_view_type * src,int len,s16b x,s16b y)2517 int caveprt(cave_view_type* src, int len, s16b x, s16b y)
2518 {
2519 	int i;
2520 
2521 	/* Paranoia - bounds */
2522 	if (x < 0 || x >= Term->wid) return -1;
2523 	if (y < 0 || y >= Term->hgt) return -1;
2524 
2525 	/* Draw a character n times */
2526 	for (i = 0; i < len; i++)
2527 	{
2528 		/* Don't draw on screen if character is 0 */
2529 		if (src[i].c)
2530 		{
2531 			Term_draw(i + x, y, src[i].a, src[i].c);
2532 		}
2533 	}
2534 	return 1;
2535 }
2536 
cavestr(cave_view_type * dest,cptr str,byte attr,int max_col)2537 int cavestr(cave_view_type* dest, cptr str, byte attr, int max_col)
2538 {
2539 	int i, e;
2540 	e = strlen(str);
2541 	for (i = 0; i < e; i++)
2542 	{
2543 		dest[i].a = attr;
2544 		dest[i].c = str[i];
2545 	}
2546 	for (i = e; i < max_col; i++)
2547 	{
2548 		dest[i].a = TERM_WHITE;
2549 		dest[i].c = ' ';
2550 	}
2551 	return 1;
2552 }
2553 
2554 /* A version of "cavedraw()" for Term2 hack */
Term2_cave_line(int st,int sy,int y,int cols)2555 bool Term2_cave_line(int st, int sy, int y, int cols)
2556 {
2557 	cave_view_type* src = stream_cave(st, sy);
2558 	int x = 0;
2559 	int dx = x;
2560 	int dy = y;
2561 	int i;
2562 	bool complete = FALSE;
2563 	/* Draw a character n times */
2564 	for (i = 0; i < cols; i++)
2565 	{
2566 		/* Don't draw on screen if character is 0 */
2567 		//if (src[i].c)
2568 		{
2569 			byte a = src[i].a;
2570 			char c = src[i].c;
2571 			byte ta = p_ptr->trn_info[y][x + i].a;
2572 			char tc = p_ptr->trn_info[y][x + i].c;
2573 			bool _ret;
2574 			//if (!ta) ta = a;
2575 			//if (!tc) tc = c;
2576 			discard_slashfx(y, x + i);
2577 			_ret = cave_char_aux(dx + i, dy, a, c, ta, tc);
2578 			if (_ret) complete = TRUE;
2579 		}
2580 	}
2581 	return complete;
2582 }
2583 
2584 
2585 /* Draw (or don't) a char depending on screen ickyness */
show_char(s16b y,s16b x,byte a,char c,byte ta,char tc,bool mem)2586 void show_char(s16b y, s16b x, byte a, char c, byte ta, char tc, bool mem)
2587 {
2588 	bool draw = TRUE;
2589 
2590 	/* If we have a hook, use it */
2591 	if (cave_char_aux && mem)
2592 	{
2593 		discard_slashfx(y, x);
2594 		/* Hook will return TRUE if no further processing is needed */
2595 		if (cave_char_aux(x, y, a, c, ta, tc)) return;
2596 	}
2597 
2598 	/* Manipulate offset: */
2599 	x += DUNGEON_OFFSET_X;
2600 	y += DUNGEON_OFFSET_Y;
2601 
2602 	/* Test ickyness */
2603 	if (screen_icky || section_icky_row || shopping) draw = FALSE;
2604 	if (section_icky_row)
2605 	{
2606 		if (y >= section_icky_row) draw = TRUE;
2607 		else if (section_icky_col > 0 && x >= section_icky_col) draw = TRUE;
2608 		else if (section_icky_col < 0 && x < Term->wid + section_icky_col) draw = TRUE;
2609 	}
2610 
2611 	/* Test terminal size */
2612 	if (x >= Term->wid || y >= Term->hgt) mem = draw = FALSE;
2613 
2614 	if (mem && Term->mem)
2615 		Term_mem_ch(x, y, a, c, ta, tc);
2616 
2617 	if (draw)
2618 	{
2619 		/* Update secondary layer */
2620 		if (p_ptr->trn_info[y][x].a != ta || p_ptr->trn_info[y][x].c != tc)
2621 		{
2622 			/* Hack -- force refresh of that grid no matter what */
2623 			Term->scr->a[y][x] = 0;
2624 			Term->scr->c[y][x] = 0;
2625 			Term->old->a[y][x] = 0;
2626 			Term->old->c[y][x] = 0;
2627 		}
2628 
2629 		Term_queue_char(Term, x, y, a, c, ta, tc);
2630 	}
2631 
2632 }
2633 
2634 /* Show (or don't) a line depending on screen ickyness */
show_line(int sy,s16b cols,bool mem,int st)2635 void show_line(int sy, s16b cols, bool mem, int st)
2636 {
2637 	s16b xoff, coff;
2638 	bool draw;
2639 	int y;
2640 
2641 	draw = mem ? !screen_icky : interactive_mode;
2642 	xoff = coff = 0;
2643 	y = sy + (mem ? DUNGEON_OFFSET_Y : 0);
2644 
2645 	/* Dungeon, and we have a dungeon hook. */
2646 	if ((streams[st].addr == NTERM_WIN_OVERHEAD)
2647 	&& !(streams[st].flag & SF_OVERLAYED)
2648 	&& (cave_char_aux != NULL))
2649 	{
2650 		/* Hook will return TRUE if no further processing is needed */
2651 		if (Term2_cave_line(st, sy, sy, cols)) return;
2652 	}
2653 
2654 	/* Ugly Hack - Shopping */
2655 	if (shopping) draw = FALSE;
2656 
2657 	/* Hang on! Icky section! */
2658 	if (section_icky_row && y < section_icky_row)
2659 	{
2660 		if (section_icky_col > 0) xoff = section_icky_col - DUNGEON_OFFSET_X;
2661 		if (section_icky_col < 0) coff = section_icky_col;
2662 		if (xoff >= cols || cols-coff <= 0) draw = FALSE;
2663 	}
2664 
2665 	/* Another possible issue - terminal is too small */
2666 	if (cols+coff >= Term->wid) coff -= (Term->wid - (cols+coff));
2667 	if (y >= Term->hgt || cols+coff <= 0) mem = draw = FALSE;
2668 
2669 	/* Check the max line count */
2670 	if (last_line_info < sy)
2671 		last_line_info = sy;
2672 
2673 	/* Remember screen */
2674 	if (mem && Term->mem)
2675 		cavemem(stream_cave(st, sy), cols, 0, sy);
2676 
2677 	/* Put data to screen */
2678 	if (draw)
2679 		cavedraw(stream_cave(st, sy)+xoff, cols+coff, xoff, sy);
2680 }
2681 
2682 /*
2683  * Handle the air layer
2684  */
2685 /*
2686  * Each "air tile" has a delay and a fadeout value, stored in
2687  *  air_delay[][] and air_fade[][] arrays. The unit is milliseconds.
2688  *
2689  * Both "fade" and "delay" values constantly go down. As soon as "delay"
2690  * reaches zero, the tile appears on-screen, and should stay there,
2691  * until the "fade" value also reaches zero.
2692  *
2693  * It's up for the client to select the fadeout threshold.
2694  *  A large value (i.e. 100) would draw air tiles with large tracers,
2695  *  as tiles will "hang" in the air for quite some time. A small
2696  *  value (i.e. 1 or 2) will make them much more flickerish.
2697  *
2698  * Feel free to play around with this (AIR_FADE_THRESHOLD define, see also
2699  * "recv_air()" in net-client.c)
2700  *
2701  * Note: when "air tiles" are used to display projectiles which actually land
2702  * and then appear on the ground (i.e. arrows, thrown items), the final
2703  * tile is drawn BEFORE any of the air tiles have a chance to appear/fade.
2704  *
2705  * Note: *In theory* main-xxx ports might feel that they can handle the air
2706  * layer by themselves and more gracefully (e.g. with alpha blendend fades).
2707  * If you're working on this, and the direct Term_draw writes are too
2708  * intrusive for you, set global variable "air_refresh" to FALSE.
2709  * To disable this code completely, set "air_updates" to FALSE.
2710  *
2711  * Note: this function uses up the static_timer(2). This means you can't use
2712  * static_timer(2) anywhere else from now on.
2713  *
2714  * Note: this function probably breaks graphics mode in some way. Untested.
2715  */
update_air()2716 void update_air()
2717 {
2718 	micro passed = static_timer(2);
2719 	u16b milli = (u16b)(passed / 1000);
2720 	int j, i;
2721 	for (j = 0; j < MAX_HGT; j++)
2722 	{
2723 		for (i = 0; i < MAX_WID; i++)
2724 		{
2725 			if (air_delay[j][i] > 0)
2726 			{
2727 				air_delay[j][i] -= milli;
2728 				/* Delay timeout */
2729 				if (air_delay[j][i] <= 0)
2730 				{
2731 					air_delay[j][i] = 0;
2732 					if (air_refresh)
2733 					{
2734 						/* Draw air tile */
2735 						show_char(j, i,
2736 							air_info[j][i].a, air_info[j][i].c,
2737 							p_ptr->trn_info[j][i].a,
2738 							p_ptr->trn_info[j][i].c, FALSE);
2739 					}
2740 				}
2741 			}
2742 			if (air_fade[j][i] > 0)
2743 			{
2744 				air_fade[j][i] -= milli;
2745 				/* Fade timeout */
2746 				if (air_fade[j][i] <= 0)
2747 				{
2748 					air_fade[j][i] = 0;
2749 					if (air_refresh)
2750 					{
2751 						/* Erase air tile */
2752 						cave_view_type *scr_info = stream_cave(0, j);
2753 						show_char(j, i,
2754 							scr_info[i].a, scr_info[i].c,
2755 							p_ptr->trn_info[j][i].a,
2756 							p_ptr->trn_info[j][i].c, FALSE);
2757 					}
2758 				}
2759 			}
2760 		}
2761 	}
2762 }
2763 
prt_num(cptr header,int num,int row,int col,byte color)2764 void prt_num(cptr header, int num, int row, int col, byte color)
2765 {
2766 	int len = strlen(header);
2767 	char out_val[32];
2768 	put_str(header, row, col);
2769 	put_str("   ", row, col + len);
2770 	(void)sprintf(out_val, "%6ld", (long)num);
2771 	c_put_str(color, out_val, row, col + len + 3);
2772 }
2773 
prt_lnum(cptr header,s32b num,int row,int col,byte color)2774 void prt_lnum(cptr header, s32b num, int row, int col, byte color)
2775 {
2776 	int len = strlen(header);
2777 	char out_val[32];
2778 	put_str(header, row, col);
2779 	(void)sprintf(out_val, "%9ld", (long)num);
2780 	c_put_str(color, out_val, row, col + len);
2781 }
2782 
2783 /*
2784  * Transform macro trigger key code ('^_O_64\r' or etc..)
2785  * into macro trigger name ('\[alt-D]' etc..)
2786  */
trigger_ascii_to_text(char * buf,size_t max,cptr * strptr)2787 static size_t trigger_ascii_to_text(char *buf, size_t max, cptr *strptr)
2788 {
2789 	cptr str = *strptr;
2790 	char key_code[100];
2791 	int i;
2792 	cptr tmp;
2793 	size_t current_len = strlen(buf);
2794 
2795 
2796 	/* No definition of trigger names */
2797 	if (macro_template == NULL) return 0;
2798 
2799 	/* Trigger name will be written as '\[name]' */
2800 	strnfcat(buf, max, &current_len, "\\[");
2801 
2802 	/* Use template to read key-code style trigger */
2803 	for (i = 0; macro_template[i]; i++)
2804 	{
2805 		int j;
2806 		char ch = macro_template[i];
2807 
2808 		switch(ch)
2809 		{
2810 		case '&':
2811 			/* Read modifier */
2812 			while ((tmp = strchr(macro_modifier_chr, *str)))
2813 			{
2814 				j = (int)(tmp - macro_modifier_chr);
2815 				strnfcat(buf, max, &current_len, "%s", macro_modifier_name[j]);
2816 				str++;
2817 			}
2818 			break;
2819 		case '#':
2820 			/* Read key code */
2821 			for (j = 0; *str && (*str != '\r') && (j < sizeof(key_code) - 1); j++)
2822 				key_code[j] = *str++;
2823 			key_code[j] = '\0';
2824 			break;
2825 		default:
2826 			/* Skip fixed strings */
2827 			if (ch != *str) return 0;
2828 			str++;
2829 		}
2830 	}
2831 
2832 	/* Key code style triggers always end with '\r' */
2833 	if (*str++ != '\r') return 0;
2834 
2835 	/* Look for trigger name with given keycode (normal or shifted keycode) */
2836 	for (i = 0; i < max_macrotrigger; i++)
2837 	{
2838 		if (!my_stricmp(key_code, macro_trigger_keycode[0][i]) ||
2839 		    !my_stricmp(key_code, macro_trigger_keycode[1][i]))
2840 			break;
2841 	}
2842 
2843 	/* Not found? */
2844 	if (i == max_macrotrigger) return 0;
2845 
2846 	/* Write trigger name + "]" */
2847 	strnfcat(buf, max, &current_len, "%s]", macro_trigger_name[i]);
2848 
2849 	/* Succeed */
2850 	*strptr = str;
2851 	return current_len;
2852 }
2853 
2854 
ascii_to_text(char * buf,size_t len,cptr str)2855 void ascii_to_text(char *buf, size_t len, cptr str)
2856 {
2857 	char *s = buf;
2858 
2859 	/* Analyze the "ascii" string */
2860 	while (*str)
2861 	{
2862 		byte i = (byte)(*str++);
2863 
2864 		if (i == ESCAPE)
2865 		{
2866 			*s++ = '\\';
2867 			*s++ = 'e';
2868 		}
2869 		else if (i == ' ')
2870 		{
2871 			*s++ = '\\';
2872 			*s++ = 's';
2873 		}
2874 		else if (i == '\b')
2875 		{
2876 			*s++ = '\\';
2877 			*s++ = 'b';
2878 		}
2879 		else if (i == '\t')
2880 		{
2881 			*s++ = '\\';
2882 			*s++ = 't';
2883 		}
2884 		else if (i == '\n')
2885 		{
2886 			*s++ = '\\';
2887 			*s++ = 'n';
2888 		}
2889 		else if (i == '\r')
2890 		{
2891 			*s++ = '\\';
2892 			*s++ = 'r';
2893 		}
2894 		else if (i == '^')
2895 		{
2896 			*s++ = '\\';
2897 			*s++ = '^';
2898 		}
2899 		else if (i == '\\')
2900 		{
2901 			*s++ = '\\';
2902 			*s++ = '\\';
2903 		}
2904 		else if (i == '\f')
2905 		{
2906 			*s++ = '\\';
2907 			*s++ = 'f';
2908 		}
2909 
2910 		/* Macro Trigger */
2911 		else if (i == 31)
2912 		{
2913 			size_t offset;
2914 
2915 			/* Terminate before appending the trigger */
2916 			*s = '\0';
2917 
2918 			offset = trigger_ascii_to_text(buf, len, &str);
2919 
2920 			if (offset == 0)
2921 			{
2922 				/* No trigger found */
2923 				*s++ = '^';
2924 				*s++ = '_';
2925 			}
2926 			else
2927 				s += offset;
2928 		}
2929 		else if (i < 32)
2930 		{
2931 			*s++ = '^';
2932 			*s++ = i + 64;
2933 		}
2934 		else if (i < 127)
2935 		{
2936 			*s++ = i;
2937 		}
2938 		else if (i < 64)
2939 		{
2940 			*s++ = '\\';
2941 			*s++ = '0';
2942 			*s++ = octify(i / 8);
2943 			*s++ = octify(i % 8);
2944 		}
2945 		else
2946 		{
2947 			*s++ = '\\';
2948 			*s++ = 'x';
2949 			*s++ = hexify(i / 16);
2950 			*s++ = hexify(i % 16);
2951 		}
2952 	}
2953 
2954 	/* Terminate */
2955 	*s = '\0';
2956 }
2957 
2958 /*
2959  * Hack -- Append all keymaps to the given file.
2960  *
2961  * Hack -- We only append the keymaps for the "active" mode.
2962  */
keymap_dump(ang_file * fff)2963 static void keymap_dump(ang_file *fff)
2964 {
2965 	int i;
2966 	int mode;
2967 	char buf[1024];
2968 	cptr keymap_name;
2969 
2970 	/* Roguelike */
2971 	if (rogue_like_commands)
2972 	{
2973 		mode = KEYMAP_MODE_ROGUE;
2974 		keymap_name = "Roguelike Keyset";
2975 	}
2976 	/* Original */
2977 	else
2978 	{
2979 		mode = KEYMAP_MODE_ORIG;
2980 		keymap_name = "Original Keyset";
2981 	}
2982 
2983 	/* Dump the header */
2984 	file_putf(fff, "#\n", keymap_name);
2985 	file_putf(fff, "# ====== Keymaps (%s) ======\n", keymap_name);
2986 	file_putf(fff, "#\n\n", keymap_name);
2987 
2988 	for (i = 0; i < 256; i++)
2989 	{
2990 		int tmp_cmd;
2991 		char tmp_buf[256];
2992 		char key_buf[256];
2993 
2994 		char key[2] = "?";
2995 
2996 		cptr act;
2997 
2998 		/* Loop up the keymap */
2999 		act = keymap_act[mode][i];
3000 
3001 		/* Skip empty keymaps */
3002 		if (!act) continue;
3003 
3004 		/* Convert the key into a string */
3005 		key[0] = i;
3006 
3007 		/* Encode the key */
3008 		ascii_to_text(key_buf, sizeof(key_buf), key);
3009 
3010 		/* Try to extract the comment */
3011 		text_to_ascii(tmp_buf, sizeof(tmp_buf), act);
3012 		tmp_cmd = command_from_keystroke(tmp_buf);
3013 		tmp_buf[0] = '\0';
3014 		command_to_display_name(tmp_cmd, tmp_buf, sizeof(tmp_buf));
3015 		/* Dump the comment */
3016 		file_putf(fff, "# %s (%s)\n", tmp_buf, key_buf);
3017 
3018 		/* Encode the action */
3019 		ascii_to_text(buf, sizeof(buf), act);
3020 
3021 		/* Dump the keymap action */
3022 		file_putf(fff, "A:%s\n", buf);
3023 
3024 		/* Dump the keymap pattern */
3025 		file_putf(fff, "C:%d:%s\n", mode, key_buf);
3026 
3027 		/* Skip a line */
3028 		file_putf(fff, "\n");
3029 	}
3030 
3031 }
3032 
macro_dump(cptr fname)3033 static errr macro_dump(cptr fname)
3034 {
3035 	int i;
3036 
3037 	ang_file* fff;
3038 
3039 	char buf[1024];
3040 
3041 
3042 	/* Build the filename */
3043 	path_build(buf, 1024, ANGBAND_DIR_USER, fname);
3044 
3045 	/* Write to the file */
3046 	fff = file_open(buf, MODE_WRITE, FTYPE_TEXT);
3047 
3048 	/* Failure */
3049 	if (!fff) return (-1);
3050 
3051 	/* Start dumping */
3052 	file_putf(fff, "#\n");
3053 	file_putf(fff, "# ====== Automatic macro dump ======\n");
3054 	file_putf(fff, "#\n");
3055 	/* Explain a little */
3056 	file_putf(fff, "# Macros defined with 'G' are 'command macros' and are only executed when\n# the game is waiting for a command (command context).\n");
3057 	file_putf(fff, "# Macros defined with 'P' are 'normal macros' and are executed everywhere,\n# at any context.\n");
3058 	file_putf(fff, "#\n");
3059 
3060 	/* Skip space */
3061 	file_putf(fff, "\n\n");
3062 
3063 	/* Dump them */
3064 	for (i = 0; i < macro__num; i++)
3065 	{
3066 		/* Start the macro */
3067 		file_putf(fff, "# Macro '%d'%s\n\n", i, macro__cmd[i] ? " (command context)" : "");
3068 
3069 		/* Extract the action */
3070 		ascii_to_text(buf, sizeof(buf), macro__act[i]);
3071 
3072 		/* Dump the macro */
3073 		file_putf(fff, "A:%s\n", buf);
3074 
3075 		/* Extract the action */
3076 		ascii_to_text(buf, sizeof(buf), macro__pat[i]);
3077 
3078 		/* Dump command macro */
3079 		if (macro__cmd[i]) file_putf(fff, "G:%s\n", buf);
3080 
3081 		/* Dump normal macros */
3082 		else file_putf(fff, "P:%s\n", buf);
3083 
3084 		/* End the macro */
3085 		file_putf(fff, "\n\n");
3086 	}
3087 
3088 
3089 	/* Hack -- will be dumped into separate file in MAngband */
3090 #if 0
3091 	/* Skip space */
3092 	file_putf(fff, "\n\n");
3093 
3094 	/* Dump keymaps */
3095 	keymap_dump(fff);
3096 #endif
3097 
3098 	/* Finish dumping */
3099 	file_putf(fff, "\n\n");
3100 
3101 	/* Close */
3102 	file_close(fff);
3103 
3104 	/* Success */
3105 	return (0);
3106 }
3107 
keymap_dump_file(cptr fname)3108 static errr keymap_dump_file(cptr fname)
3109 {
3110 	int i;
3111 
3112 	ang_file* fff;
3113 
3114 	char buf[1024];
3115 
3116 	/* Build the filename */
3117 	path_build(buf, 1024, ANGBAND_DIR_USER, fname);
3118 
3119 	/* Write to the file */
3120 	fff = file_open(buf, MODE_WRITE, FTYPE_TEXT);
3121 
3122 	/* Failure */
3123 	if (!fff) return (-1);
3124 
3125 	/* Dump */
3126 	keymap_dump(fff);
3127 
3128 	/* Close */
3129 	file_close(fff);
3130 
3131 	/* Success */
3132 	return (0);
3133 }
3134 
get_macro_trigger(char * buf)3135 static bool get_macro_trigger(char *buf)
3136 {
3137 	int i, n = 0;
3138 
3139 	char tmp[1024];
3140 
3141 	/* Flush */
3142 	flush();
3143 
3144 	/* Refresh screen? */
3145 	Term_fresh();
3146 	Term_xtra(TERM_XTRA_BORED, 0);
3147 
3148 	/* Do not process macros */
3149 	inkey_base = TRUE;
3150 
3151 	/* First key */
3152 	i = inkey();
3153 
3154 #ifdef USE_GCU
3155 	/* If we allow escape as macro trigger (ncurses) */
3156 	if (escape_in_macro_triggers)
3157 	{
3158 		/* Then, backtick acts as actual escape */
3159 		if (i == '`') return FALSE;
3160 	} else
3161 #endif
3162 	/* Escape on Escape */
3163 	if (i == ESCAPE) return FALSE;
3164 
3165 	/* Read the pattern */
3166 	while (i)
3167 	{
3168 		/* Save the key */
3169 		buf[n++] = i;
3170 
3171 		/* Do not process macros */
3172 		inkey_base = TRUE;
3173 
3174 		/* Do not wait for keys */
3175 		inkey_scan = TRUE;
3176 
3177 		/* Attempt to read a key */
3178 		i = inkey();
3179 	}
3180 
3181 	/* Terminate */
3182 	buf[n] = '\0';
3183 
3184 	/* Flush */
3185 	flush();
3186 
3187 
3188 	/* Convert the trigger */
3189 	ascii_to_text(tmp, sizeof(tmp), buf);
3190 
3191 	/* Hack -- display the trigger */
3192 	Term_addstr(-1, TERM_WHITE, tmp);
3193 
3194 	return TRUE;
3195 }
3196 
3197 /*
3198  * Find the macro (if any) which exactly matches the given pattern
3199  */
macro_find_exact(cptr pat)3200 int macro_find_exact(cptr pat)
3201 {
3202 	int i;
3203 
3204 	/* Scan the macros */
3205 	for (i = 0; i < macro__num; ++i)
3206 	{
3207 		/* Skip macros which do not match the pattern */
3208 		if (!streq(macro__pat[i], pat)) continue;
3209 
3210 		/* Found one */
3211 		return (i);
3212 	}
3213 
3214 	/* No matches */
3215 	return (-1);
3216 }
3217 
3218 /*
3219  * Hack -- ask for a keymap "trigger" (see below)
3220  *
3221  * Note that both "flush()" calls are extremely important.  This may
3222  * no longer be true, since "util.c" is much simpler now.  XXX XXX XXX
3223  */
do_cmd_macro_aux_keymap(char * buf)3224 static void do_cmd_macro_aux_keymap(char *buf)
3225 {
3226 	char tmp[1024];
3227 
3228 	/* Flush */
3229 	flush();
3230 
3231 	/* Get a key */
3232 	buf[0] = inkey();
3233 	buf[1] = '\0';
3234 
3235 	/* Hack -- ignore ESCAPE */
3236 	if (buf[0] == ESCAPE)
3237 	{
3238 		buf[0] = '\0';
3239 		return;
3240 	}
3241 
3242 	/* Convert to ascii */
3243 	ascii_to_text(tmp, sizeof(tmp), buf);
3244 
3245 	/* Hack -- display the trigger */
3246 	Term_addstr(-1, TERM_WHITE, tmp);
3247 
3248 	/* Flush */
3249 	flush();
3250 }
3251 
3252 /* Display keymaps as a list and allow user to navigate through it
3253  * Logic in this function is somewhat broken.
3254  */
browse_keymaps(void)3255 void browse_keymaps(void)
3256 {
3257 	int i;
3258 	int total;
3259 	int hgt = Term->hgt - 4;
3260 	int j = 0;
3261 	int o = 0;
3262 	int sel = -1;
3263 	char tmp_buf[120];
3264 	char buf[120];
3265 	char act[120];
3266 	char a = TERM_WHITE, a2 = TERM_WHITE;
3267 	byte mode = rogue_like_commands ? 1 : 0;
3268 
3269 	/* Process requests until done */
3270 	while (1)
3271 	{
3272 		/* Clear screen */
3273 		Term_clear();
3274 
3275 		/* Describe */
3276 		Term_putstr(0, 0, -1, TERM_WHITE, "Browse Keymaps     (D delete, A/T to set, ESC to accept)");
3277 		if (mode) Term_addstr(-1, TERM_WHITE, "    [Roguelike keyset]");
3278 		Term_putstr(0, 1, -1, TERM_SLATE, "Keypress                      Action              Comment");
3279 
3280 		/* Dump them */
3281 		for (i = 0, total = 0; i < 256; i++)
3282 		{
3283 			int k = total;
3284 			char tmp_cmd;
3285 
3286 			/* Skip undefined keymaps */
3287 			if (keymap_act[mode][i] == NULL) continue;
3288 
3289 			/* Extract the action */
3290 			ascii_to_text(act, sizeof(act), keymap_act[mode][i]);
3291 
3292 			/* Extract the trigger */
3293 			tmp_buf[0] = (char)i;
3294 			tmp_buf[1] = '\0';
3295 			ascii_to_text(buf, sizeof(buf), tmp_buf);
3296 
3297 			/* Deleted keymap */
3298 			if (!strcmp(buf, act)) continue;
3299 
3300 			/* It's ok */
3301 			total++;
3302 
3303 			/* Too early */
3304 			if (k < o) continue;
3305 
3306 			/* Too late */
3307 			if (k - o >= hgt-2) continue;
3308 
3309 			/* Selected */
3310 			a = TERM_WHITE; a2 = TERM_L_WHITE;
3311 			if (j == k)
3312 			{
3313 				a = a2 = TERM_L_BLUE;
3314 				sel = i;
3315 				/* Move cursor there */
3316 				Term_gotoxy(0, 2+k-o);
3317 				Term_show_ui_cursor();
3318 			}
3319 
3320 			/* Dump the trigger */
3321 			Term_putstr(00, 2+k-o, -1, a, buf);
3322 
3323 			/* Dump the action */
3324 			Term_putstr(30, 2+k-o, -1, a, act);
3325 
3326 			/* Try to extract the comment */
3327 			text_to_ascii(tmp_buf, sizeof(tmp_buf), act);
3328 			tmp_cmd = command_from_keystroke(tmp_buf);
3329 			tmp_buf[0] = '\0';
3330 			command_to_display_name(tmp_cmd, tmp_buf, 1024);
3331 
3332 			/* Dump the comment */
3333 			Term_putstr(50, 2+k-o, -1, TERM_L_WHITE, tmp_buf);
3334 		}
3335 
3336 		/* Get a key */
3337 		i = inkey();
3338 
3339 		/* Leave */
3340 		if (i == ESCAPE) break;
3341 
3342 		else if (i == 'D') /* Delete */
3343 		{
3344 			/* Keep atleast 1 */
3345 			if (total == 1) continue;
3346 
3347 			/* (un)Link the keymap */
3348 			string_free(keymap_act[mode][sel]);
3349 			keymap_act[mode][sel] = string_make(format("%c", sel));
3350 
3351 			/* Change offsets */
3352 			if (j >= total-1) j--;
3353 			if (j < 0) j = 0;
3354 			else if (o && j - o < hgt/2) o--;
3355 		}
3356 
3357 		else if (i == 'T') /* Change trigger */
3358 		{
3359 			cptr prev_action;
3360 			byte new_map;
3361 
3362 			/* Prompt */
3363 			clear_from(hgt);
3364 			Term_putstr(0, hgt+1, -1, TERM_WHITE, "Keypress: ");
3365 			do_cmd_macro_aux_keymap(tmp_buf);
3366 			new_map = tmp_buf[0];
3367 
3368 			/* (un)Link old keymap */
3369 			prev_action = keymap_act[mode][sel];
3370 			keymap_act[mode][sel] = string_make(format("%c", sel));
3371 
3372 			/* (un)Create new keymap */
3373 			if (keymap_act[mode][new_map]) string_free(keymap_act[mode][new_map]);
3374 			keymap_act[mode][new_map] = prev_action;
3375 		}
3376 
3377 		else if (i == 'A') /* Change action */
3378 		{
3379 			/* Prompt */
3380 			clear_from(hgt);
3381 			Term_putstr(0, hgt+1, -1, TERM_WHITE, "Action: ");
3382 
3383 			/* Copy 'current action' */
3384 			ascii_to_text(act, sizeof(act), keymap_act[mode][sel]);
3385 //			my_strcpy(act, keymap_act[mode][sel], sizeof(act));
3386 
3387 			/* Get an encoded action */
3388 			if (!askfor_aux(act, 80, 0)) continue;
3389 
3390 			/* Convert to ascii */
3391 			text_to_ascii(tmp_buf, sizeof(tmp_buf), act);
3392 			tmp_buf[strlen(act)] = '\0';
3393 
3394 			/* Do not allow empty */
3395 			if (strlen(tmp_buf) < 1) continue;
3396 
3397 			/* (re)Link the keymap */
3398 			string_free(keymap_act[mode][sel]);
3399 			keymap_act[mode][sel] = string_make(tmp_buf);
3400 		}
3401 
3402 		else if (i == ' ') /* Cycle Down */
3403 		{
3404 			j++;
3405 			if (j > total-1) { j = 0; o = 0; }
3406 			else if (j - o > hgt/2 && j < total) o++;
3407 		}
3408 		else if (i == '2') /* Down */
3409 		{
3410 			j++;
3411 			if (j > total-1) j = total-1;
3412 			else if (j - o > hgt/2 && j < total) o++;
3413 		}
3414 		else if (i == '7') /* Home */
3415 		{
3416 			o = j = 0;
3417 		}
3418 		else if (i == '9') /* Page up */
3419 		{
3420 			j -= hgt;
3421 			if (j < 0) j = 0;
3422 			o = j;
3423 		}
3424 		else if (i == '3') /* Page down */
3425 		{
3426 			j += Term->hgt;
3427 			if (j > total-1) j = total-1;
3428 			o = j - hgt/2;
3429 		}
3430 		else if (i == '1') /* End */
3431 		{
3432 			j = total - 1;
3433 			o = j - hgt/2;
3434 		}
3435 		else if (i == '8') /* Up */
3436 		{
3437 			j--;
3438 			if (j < 0) j = 0;
3439 			else if (o && j - o < hgt/2) o--;
3440 		}
3441 	}
3442 	/* Hide cursor */
3443 	Term_hide_ui_cursor();
3444 }
3445 
3446 /* HACK -- horrible function for "drawing", replace this
3447  * with Angband menus or at least use their decorations... */
_clear_rect(int x,int y,int w,int h)3448 void _clear_rect(int x, int y, int w, int h)
3449 {
3450 	int i, j;
3451 	/* Draw blackness */
3452 	for (j = 0; j < h; j++) for (i = 0; i < w; i++)
3453 		Term_putstr(x + i, y + j, -1, TERM_WHITE, " ");
3454 }
_draw_rect(int x,int y,int w,int h)3455 void _draw_rect(int x, int y, int w, int h)
3456 {
3457 	int i, j;
3458 	_clear_rect(x, y, w, h);
3459 	/* Draw edges */
3460 	Term_putstr(x + 0 + 1, y + 1, -1, TERM_WHITE, "+");
3461 	Term_putstr(x + w - 2, y + 1, -1, TERM_WHITE, "+");
3462 	Term_putstr(x + 0 + 1, y + h - 2, -1, TERM_WHITE, "+");
3463 	Term_putstr(x + w - 2, y + h - 2, -1, TERM_WHITE, "+");
3464 	/* Draw borders */
3465 	for (i = 2; i < w - 2; i++) {
3466 		Term_putstr(x + i, y + 0 + 1, -1, TERM_WHITE, "-");
3467 		Term_putstr(x + i, y + h - 2, -1, TERM_WHITE, "-");
3468 	}
3469 	for (j = 2; j < h - 2; j++) {
3470 		Term_putstr(x + 0 + 1, y + j, -1, TERM_WHITE, "|");
3471 		Term_putstr(x + w - 2, y + j, -1, TERM_WHITE, "|");
3472 	}
3473 }
3474 
new_macro_window(void)3475 bool new_macro_window(void)
3476 {
3477 	char action[1024];
3478 	char action_ascii[1024];
3479 	char trigger_ascii[1024];
3480 	char trigger[1024];
3481 	byte old_school_macros = FALSE;
3482 	bool redraw = FALSE;
3483 	int wx = 4;
3484 	int wy = 4;
3485 	int wid = Term->wid - 8;
3486 	int hgt = Term->hgt - 8;
3487 	bool proceed = TRUE;
3488 	bool cmd_flag = FALSE;
3489 
3490 	_draw_rect(wx, wy, wid, hgt);
3491 
3492 	wx += 3;
3493 	wy += 1;
3494 	wid -= 2;
3495 	hgt -= 2;
3496 
3497 	/* Describe */
3498 	Term_putstr(wx, wy+1, -1, TERM_WHITE, "i) Macro Item");
3499 	Term_putstr(wx, wy+2, -1, TERM_WHITE, "m) Macro Spell");
3500 	Term_putstr(wx, wy+3, -1, TERM_WHITE, "c) Custom Macro");
3501 	Term_putstr(wx, wy+4, -1, TERM_WHITE, "ESC) Abort");
3502 
3503 	/* Process requests until done */
3504 	while (1)
3505 	{
3506 		char ch = inkey();
3507 		if (ch == ESCAPE)
3508 		{
3509 			proceed = FALSE;
3510 			break;
3511 		}
3512 
3513 		else if (ch == 'i')
3514 		{
3515 			int item, n;
3516 			char cmd;
3517 			if (!c_get_item(&item, "Macro which item?", TRUE, TRUE, TRUE))
3518 			{
3519 				continue;
3520 			}
3521 			cmd = command_by_item(item, 0);
3522 			if (!cmd)
3523 			{
3524 				bell();
3525 				continue;
3526 			}
3527 
3528 			item_as_keystroke(item, cmd, action, MAX_COLS,
3529 				old_school_macros ?
3530 				(CTXT_WITH_CMD | CTXT_WITH_DIR | CTXT_PREFER_SHORT)
3531 				:
3532 				(CTXT_WITH_CMD | CTXT_PREFER_NAME)
3533 			);
3534 			break;
3535 		}
3536 		else if (ch == 'm' || ch == 'p')
3537 		{
3538 			int spell, book = 0, n;
3539 			char cmd;
3540 			n = get_spell(&spell, "/=Next book,", "Macro which spell?", &book, FALSE, FALSE);
3541 			screen_icky = TRUE;
3542 			/* No spell selected */
3543 			if (!n)
3544 			{
3545 				continue;
3546 			}
3547 
3548 			cmd = command_by_item(book, 1);
3549 			if (!cmd)
3550 			{
3551 				bell();
3552 				continue;
3553 			}
3554 
3555 			spell_as_keystroke(spell, book, cmd, action, sizeof(action),
3556 				old_school_macros ?
3557 				(CTXT_FULL | CTXT_PREFER_SHORT)
3558 				:
3559 				(CTXT_WITH_CMD | CTXT_WITH_DIR | CTXT_PREFER_NAME)
3560 			);
3561 			break;
3562 
3563 		}
3564 		else if (ch == 'c' || ch == '3')
3565 		{
3566 			Term_putstr(wx, wy+5, -1, TERM_WHITE, "Action: ");
3567 
3568 			/* Get an encoded action */
3569 			action[0] = '\0';
3570 			if (askfor_aux(action, 40, 0)
3571 			    && !STRZERO(action)) break;
3572 		}
3573 		else bell();
3574 	}
3575 
3576 	if (proceed)
3577 	{
3578 			/* Extract an action */
3579 			text_to_ascii(action_ascii, sizeof(action_ascii), action);
3580 
3581 			Term_putstr(wx, wy+5, -1, TERM_WHITE, "Action: ");
3582 			Term_putstr(wx+8, wy+5, -1, TERM_SLATE, action);
3583 
3584 			/* Hack -- if it's a one-key macro, make it a command macro */
3585 			if ((action[1] == '\0') && (
3586 				isalpha(action[0]) || ispunct(action[0])
3587 				|| isdigit(action[0])
3588 			)) cmd_flag = TRUE;
3589 
3590 			/* Prompt & Show cursor */
3591 			Term_putstr(wx, wy+7, -1, TERM_WHITE, "Press Trigger Button: ");
3592 			Term_show_ui_cursor();
3593 
3594 			/* Get a macro trigger */
3595 			if (get_macro_trigger(trigger_ascii))
3596 			{
3597 				/* Save key for later */
3598 				ascii_to_text(trigger, sizeof(trigger), trigger_ascii);
3599 			}
3600 			else
3601 			{
3602 				proceed = FALSE;
3603 			}
3604 	}
3605 	Term_hide_ui_cursor();
3606 	Term_putstr(wx+12, wy+5, -1, TERM_RED, trigger);
3607 
3608 	_clear_rect(wx-1, wy+1, wid-2, hgt-2);
3609 	Term_putstr(wx, wy+3, -1, TERM_L_WHITE, "Create ");
3610 	Term_addstr(-1, TERM_WHITE, cmd_flag ? "command" : "normal");
3611 	Term_addstr(-1, TERM_L_WHITE, " macro, which executes");
3612 	Term_putstr(wx, wy+5, -1, TERM_WHITE, "Action: ");
3613 	Term_putstr(wx+8, wy+5, -1, TERM_SLATE, action);
3614 	Term_putstr(wx, wy+6, -1, TERM_WHITE, "when");
3615 	Term_putstr(wx, wy+7, -1, TERM_WHITE, "Trigger: ");
3616 	Term_addstr(-1, TERM_L_WHITE, trigger);
3617 	Term_putstr(wx, wy+9, -1, TERM_L_WHITE, "is pressed?");
3618 	Term_putstr(wx, wy+11, -1, TERM_WHITE, "RET) Confirm");
3619 	Term_putstr(wx, wy+12, -1, TERM_WHITE, "ESC) Abort");
3620 	Term_fresh();
3621 
3622 	/* Process requests until done */
3623 	while (proceed)
3624 	{
3625 		char ch = inkey();
3626 		if (ch == ESCAPE)
3627 		{
3628 			proceed = FALSE;
3629 			break;
3630 		}
3631 		else if (ch == '\r') break;
3632 		else bell();
3633 	}
3634 	if (proceed)
3635 	{
3636 			/* Link the macro */
3637 			macro_add(trigger_ascii, action_ascii, cmd_flag);
3638 	}
3639 	return proceed;
3640 }
3641 
3642 /* Display macros as a list and allow user to navigate through it
3643  * Logic in this function is somewhat broken.
3644  */
browse_macros(void)3645 void browse_macros(void)
3646 {
3647 	int i;
3648 	int total;
3649 	int hgt = Term->hgt - 4;
3650 	int j = 0;
3651 	int o = 0;
3652 	int sel = -1;
3653 	char tmp_buf[120];
3654 	char buf[120];
3655 	char act[120];
3656 	char a = TERM_WHITE;
3657 
3658 	/* Process requests until done */
3659 	while (1)
3660 	{
3661 		/* Clear screen */
3662 		Term_clear();
3663 
3664 		/* Describe */
3665 		Term_putstr(0, 0, -1, TERM_WHITE, "Browse Macros     (N new, D delete, A/T to set, C switch context, ESC to accept)");
3666 
3667 		/* Dump them */
3668 		for (i = 0, total = 0; i < macro__num; i++)
3669 		{
3670 			int k = total;
3671 
3672 			/* Skip command macro */
3673 			/* if (macro__cmd[i]) continue; */
3674 
3675 			/* Extract the action */
3676 			ascii_to_text(act, sizeof(act), macro__act[i]);
3677 
3678 			/* Most likely a system action */
3679 			if (strlen(act) == 1) continue;
3680 
3681 			/* Extract the trigger */
3682 			ascii_to_text(buf, sizeof(buf), macro__pat[i]);
3683 
3684 			/* Deleted macro */
3685 			if (!strcmp(buf, act)) continue;
3686 
3687 			/* It's ok */
3688 			total++;
3689 
3690 			/* Too early */
3691 			if (k < o) continue;
3692 
3693 			/* Too late */
3694 			if (k - o >= hgt-2) continue;
3695 
3696 			/* Selected */
3697 			a = TERM_WHITE;
3698 			if (j == k)
3699 			{
3700 				a = TERM_L_BLUE;
3701 				sel = i;
3702 				/* Move cursor there */
3703 				Term_gotoxy(0, 2+k-o);
3704 				Term_show_ui_cursor();
3705 			}
3706 
3707 			/* Dump the trigger */
3708 			Term_putstr(00, 2+k-o, -1, a, buf);
3709 
3710 			/* Dump the action */
3711 			Term_putstr(30, 2+k-o, -1, a, act);
3712 
3713 			/* Dump the conext */
3714 			Term_putstr(78, 2+k-o, -1, (j==k)?TERM_L_BLUE:TERM_L_WHITE, macro__cmd[i] ? " c" : " *");
3715 		}
3716 
3717 		/* Get a key */
3718 		i = inkey();
3719 
3720 		/* Leave */
3721 		if (i == ESCAPE) break;
3722 
3723 		else if (i == 'N') /* New */
3724 		{
3725 			bool added = new_macro_window();
3726 			if (added) {
3727 				j = total - 1;
3728 				o = j - hgt/2;
3729 			}
3730 		}
3731 
3732 		else if (i == 'D') /* Delete */
3733 		{
3734 			/* Keep atleast 1 */
3735 			if (total == 1) continue;
3736 
3737 			/* Get a macro trigger */
3738 			my_strcpy(buf, macro__pat[sel], sizeof(buf));
3739 
3740 			/* (un)Link the macro */
3741 			macro_add(buf, buf, FALSE);
3742 
3743 			/* Change offsets */
3744 			if (j >= total-1) j--;
3745 			if (j < 0) j = 0;
3746 			else if (o && j - o < hgt/2) o--;
3747 		}
3748 
3749 		else if (i == 'T') /* Change trigger */
3750 		{
3751 			/* Get current action */
3752 			my_strcpy(act, macro__act[sel], sizeof(act));
3753 
3754 			/* Prompt */
3755 			clear_from(hgt);
3756 			Term_putstr(0, hgt+1, -1, TERM_WHITE, "Trigger: ");
3757 
3758 			/* Get a macro trigger */
3759 			get_macro_trigger(buf);
3760 			text_to_ascii(tmp_buf, sizeof(tmp_buf), buf);
3761 
3762 			/* Same */
3763 			if (!strcmp(macro__pat[sel], tmp_buf)) continue;
3764 
3765 			/* (re)Link the macro */
3766 			macro_add(tmp_buf, act, FALSE);
3767 		}
3768 
3769 		else if (i == 'A') /* Change action */
3770 		{
3771 			/* Prompt */
3772 			clear_from(hgt);
3773 			Term_putstr(0, hgt+1, -1, TERM_WHITE, "Action: ");
3774 
3775 			/* Copy 'current action' */
3776 			ascii_to_text(act, sizeof(act), macro__act[sel]);
3777 
3778 			/* Get an encoded action */
3779 			if (!askfor_aux(act, 80, 0)) continue;
3780 
3781 			/* Convert to ascii */
3782 			text_to_ascii(tmp_buf, sizeof(tmp_buf), act);
3783 			tmp_buf[strlen(act)] = '\0';
3784 
3785 			/* Do not allow empty OR short */
3786 			if (strlen(tmp_buf) <= 1) continue;
3787 
3788 			/* (re)Link the macro */
3789 			macro_add(macro__pat[sel], tmp_buf, FALSE);
3790 		}
3791 
3792 		else if (i == 'C') /* Change context */
3793 		{
3794 			macro__cmd[sel] = macro__cmd[sel] ? FALSE : TRUE;
3795 			if (!macro__cmd[sel]) macro__use[sel] &= ~(MACRO_USE_CMD);
3796 			else macro__use[sel] |= (MACRO_USE_CMD);
3797 			continue;
3798 		}
3799 
3800 		else if (i == ' ') /* Cycle Down */
3801 		{
3802 			j++;
3803 			if (j > total-1) { j = 0; o = 0; }
3804 			else if (j - o > hgt/2 && j < total) o++;
3805 		}
3806 		else if (i == '2') /* Down */
3807 		{
3808 			j++;
3809 			if (j > total-1) j = total-1;
3810 			else if (j - o > hgt/2 && j < total) o++;
3811 		}
3812 		else if (i == '7') /* Home */
3813 		{
3814 			o = j = 0;
3815 		}
3816 		else if (i == '9') /* Page up */
3817 		{
3818 			j -= hgt;
3819 			if (j < 0) j = 0;
3820 			o = j;
3821 		}
3822 		else if (i == '3') /* Page down */
3823 		{
3824 			j += Term->hgt;
3825 			if (j > total-1) j = total-1;
3826 			o = j - hgt/2;
3827 		}
3828 		else if (i == '1') /* End */
3829 		{
3830 			j = total - 1;
3831 			o = j - hgt/2;
3832 		}
3833 		else if (i == '8') /* Up */
3834 		{
3835 			j--;
3836 			if (j < 0) j = 0;
3837 			else if (o && j - o < hgt/2) o--;
3838 		}
3839 	}
3840 	/* Hide cursor */
3841 	Term_hide_ui_cursor();
3842 }
interact_macros(void)3843 void interact_macros(void)
3844 {
3845 	int i;
3846 
3847 	static bool old_school_macros = FALSE;
3848 	byte km_mode = 0;
3849 
3850 	int opt_page = 0;
3851 
3852 	char tmp[160], buf[1024], tmp_buf[160];
3853 	char* str;
3854 	tmp_buf[0] = '\0';
3855 
3856 	/* Screen is icky */
3857 	screen_icky = TRUE;
3858 
3859 	/* Save screen */
3860 	Term_save();
3861 
3862 	/* Process requests until done */
3863 	while (1)
3864 	{
3865 		/* Clear screen */
3866 		Term_clear();
3867 
3868 		/* Hide cursor */
3869 		Term_hide_ui_cursor();
3870 
3871 		/* Describe */
3872 		Term_putstr(0, 2, -1, TERM_WHITE, "Interact with Macros");
3873 
3874 		/* Notice keymap mode */
3875 		km_mode = rogue_like_commands ? 1 : 0;
3876 
3877 		/* Describe the trigger */
3878 		if (!STRZERO(tmp_buf))
3879 		{
3880 			Term_putstr(0, 17, -1, TERM_WHITE, "Current trigger: ");
3881 			Term_addstr(-1, TERM_WHITE, tmp_buf);
3882 		}
3883 
3884 		/* Describe that action */
3885 		Term_putstr(0, 19, -1, TERM_WHITE, "Current action (if any) shown below:");
3886 
3887 		/* Analyze the current action */
3888 		ascii_to_text(buf, sizeof(buf), macro__buf);
3889 
3890 		/* Display the current action */
3891 		Term_putstr(0, 21, -1, TERM_WHITE, buf);
3892 
3893 
3894 		/* Selections */
3895 		if (opt_page == 0)
3896 		{
3897 
3898 		Term_putstr(5,  4, -1, TERM_WHITE, "(1) Load macros");
3899 		Term_putstr(5,  5, -1, TERM_WHITE, "(2) Save macros");
3900 		Term_putstr(5,  6, -1, TERM_WHITE, "(3) Enter a new action");
3901 		Term_putstr(5,  7, -1, TERM_WHITE, "(4) Query key for macro");
3902 		Term_putstr(5,  8, -1, TERM_WHITE, "(5) Create a normal macro");
3903 		Term_putstr(5,  9, -1, TERM_WHITE, "(6) Remove a macro");
3904 		Term_putstr(5, 10, -1, TERM_WHITE, "(7) Browse macros");
3905 		Term_putstr(5, 11, -1, TERM_WHITE, "(N) New macro wizard");
3906 
3907 		} else {
3908 
3909 		Term_putstr(5,  4, -1, TERM_WHITE, "(1) Load macros");
3910 		Term_putstr(5,  5, -1, TERM_WHITE, "(@) Save keymaps");
3911 		Term_putstr(5,  6, -1, TERM_WHITE, "(3) Enter a new action");
3912 		Term_putstr(5,  7, -1, TERM_WHITE, "($) Query key for keymap");
3913 		Term_putstr(5,  8, -1, TERM_WHITE, "(%) Create a command macro");
3914 		Term_putstr(5,  9, -1, TERM_WHITE, "(^) Create keymap");
3915 		Term_putstr(5, 10, -1, TERM_WHITE, "(=) Browse keymaps");
3916 		Term_putstr(5, 11, -1, TERM_WHITE, "(N) New macro wizard");
3917 
3918 		}
3919 #if 1
3920 		Term_putstr(5+24,  6, -1, TERM_L_DARK, ".........");
3921 		Term_putstr(5+34,  6, -1, TERM_L_DARK, "(8) Macro by command");
3922 		Term_putstr(5+34,  7, -1, TERM_L_DARK, "(9) Macro by item");
3923 		Term_putstr(5+34,  8, -1, TERM_L_DARK, "(0) Macro by spell");
3924 
3925 		if (opt_page)
3926 		Term_putstr(5+34-1,  9, -1, TERM_L_DARK, format("(^O) Macro by item: %s", old_school_macros ? "old-school" : "by name"));
3927 
3928 		Term_putstr(5+34-4,  10, -1, TERM_L_DARK, "(SPACE) Show other options");
3929 #endif
3930 
3931 #if 0
3932 		Term_putstr(5, 10, -1, TERM_WHITE, "(8) Create a command macro");
3933 		Term_putstr(5, 12, -1, TERM_WHITE, "(X) Turn off an option (by name)");
3934 		Term_putstr(5, 13, -1, TERM_WHITE, "(Y) Turn on an option (by name)");
3935 #endif
3936 
3937 		/* Prompt */
3938 		Term_putstr(0, 15, -1, TERM_WHITE, "Command: ");
3939 		/* Show cursor */
3940 		Term_show_ui_cursor();
3941 
3942 		/* Get a key */
3943 		i = inkey();
3944 
3945 		/* Leave */
3946 		if (i == ESCAPE) break;
3947 
3948 		else if (i == ' ') opt_page = 1 - opt_page;
3949 
3950 		/* Browse keymaps */
3951 		else if (i == '=') browse_keymaps();
3952 
3953 		/* Browse */
3954 		else if (i == '7') browse_macros();
3955 
3956 		/* New macro window */
3957 		else if (i == 'N')
3958 		{
3959 			bool added;
3960 			Term_hide_ui_cursor();
3961 			added = new_macro_window();
3962 			if (added)
3963 			c_msg_print("Created a new macro.");
3964 		}
3965 
3966 		/* Load a pref file */
3967 		else if (i == '1')
3968 		{
3969 			/* Prompt */
3970 			Term_putstr(0, 15, -1, TERM_WHITE, "Command: Load a user pref file");
3971 
3972 			/* Get a filename, handle ESCAPE */
3973 			Term_putstr(0, 17, -1, TERM_WHITE, "File: ");
3974 
3975 			/* Default filename */
3976 			sprintf(tmp, "user-%s.prf", ANGBAND_SYS);
3977 
3978 			/* Ask for a file */
3979 			if (!askfor_aux(tmp, 70, 0)) continue;
3980 
3981 			/* Process the given filename */
3982 			(void)process_pref_file(tmp);
3983 		}
3984 
3985 		/* Save a 'macro' file */
3986 		else if (i == '2')
3987 		{
3988 			/* Prompt */
3989 			Term_putstr(0, 15, -1, TERM_WHITE, "Command: Save a macro file");
3990 
3991 			/* Get a filename, handle ESCAPE */
3992 			Term_putstr(0, 17, -1, TERM_WHITE, "File: ");
3993 
3994 			/* Default filename */
3995 			sprintf(tmp, "user-%s.prf", ANGBAND_SYS);
3996 
3997 			/* Ask for a file */
3998 			if (!askfor_aux(tmp, 70, 0)) continue;
3999 
4000 			/* Lowercase the filename */
4001 			for(str=tmp;*str;str++) *str=tolower(*str);
4002 
4003 			/* Dump the macros */
4004 			(void)macro_dump(tmp);
4005 		}
4006 
4007 		/* Save a 'keymap' file */
4008 		else if (i == '@')
4009 		{
4010 			/* Prompt */
4011 			Term_putstr(0, 15, -1, TERM_WHITE, "Command: Save a keymap file");
4012 
4013 			/* Get a filename, handle ESCAPE */
4014 			Term_putstr(0, 17, -1, TERM_WHITE, "File: ");
4015 
4016 			/* Default filename */
4017 			sprintf(tmp, "keymap.prf");/* "keymap-%s.prf", ANGBAND_SYS); */
4018 
4019 			/* Ask for a file */
4020 			if (!askfor_aux(tmp, 70, 0)) continue;
4021 
4022 			/* Lowercase the filename */
4023 			for(str=tmp;*str;str++) *str=tolower(*str);
4024 
4025 			/* Dump the keymaps */
4026 			(void)keymap_dump_file(tmp);
4027 		}
4028 
4029 		/* Change wizard mode */
4030 		else if (i == KTRL('O'))
4031 		{
4032 			old_school_macros = !(old_school_macros);
4033 		}
4034 
4035 		/* Enter a new action (spell) */
4036 		else if (i == '0')
4037 		{
4038 			int spell, book = 0, n;
4039 			char cmd;
4040 			n = get_spell(&spell, "/=Next book,", "Macro which spell?", &book, FALSE, FALSE);
4041 			screen_icky = TRUE;
4042 			/* No spell selected */
4043 			if (!n)
4044 			{
4045 				continue;
4046 			}
4047 
4048 			cmd = command_by_item(book, 1);
4049 			if (!cmd)
4050 			{
4051 				bell();
4052 				continue;
4053 			}
4054 
4055 			spell_as_keystroke(spell, book, cmd, buf, MAX_COLS,
4056 				old_school_macros ?
4057 				(CTXT_FULL | CTXT_PREFER_SHORT)
4058 				:
4059 				(CTXT_WITH_CMD | CTXT_WITH_DIR | CTXT_PREFER_NAME)
4060 			);
4061 
4062 			/* Prompt */
4063 			Term_putstr(0, 15, -1, TERM_WHITE, "Command: Enter a new action");
4064 
4065 			/* Go to the correct location */
4066 			Term_gotoxy(0, 21);
4067 
4068 			/* Get an encoded action */
4069 			if (!askfor_aux(buf, MAX_COLS, -2)) continue;
4070 
4071 			/* Extract an action */
4072 			text_to_ascii(macro__buf, 1024, buf);
4073 
4074 		}
4075 
4076 		/* Enter a new action (via items) */
4077 		else if (i == '9')
4078 		{
4079 			int item, n;
4080 			char cmd;
4081 			if (!c_get_item(&item, "Macro which item?", TRUE, TRUE, TRUE))
4082 			{
4083 				continue;
4084 			}
4085 			cmd = command_by_item(item, 0);
4086 			if (!cmd)
4087 			{
4088 				bell();
4089 				continue;
4090 			}
4091 
4092 			item_as_keystroke(item, cmd, buf, MAX_COLS,
4093 				old_school_macros ?
4094 				(CTXT_WITH_CMD | CTXT_WITH_DIR | CTXT_PREFER_SHORT)
4095 				:
4096 				(CTXT_WITH_CMD | CTXT_PREFER_NAME)
4097 			);
4098 
4099 			/* Prompt */
4100 			Term_putstr(0, 15, -1, TERM_WHITE, "Command: Enter a new action");
4101 
4102 			/* Go to the correct location */
4103 			Term_gotoxy(0, 21);
4104 
4105 			/* Get an encoded action */
4106 			if (!askfor_aux(buf, MAX_COLS, -2)) continue;
4107 
4108 			/* Extract an action */
4109 			text_to_ascii(macro__buf, 1024, buf);
4110 		}
4111 
4112 		/* Enter a new action (via menu) */
4113 		else if (i == '8')
4114 		{
4115 			char cmd = do_cmd_menu();
4116 			if (!cmd || cmd == '\r')
4117 			{
4118 				bell();
4119 				continue;
4120 			}
4121 			/* Prompt */
4122 			Term_putstr(0, 15, -1, TERM_WHITE, "Command: Enter a new action");
4123 
4124 			/* Go to the correct location */
4125 			Term_gotoxy(0, 21);
4126 
4127 			/* Hack -- dump the value */
4128 			command_as_keystroke(cmd, buf, MAX_COLS);
4129 
4130 			/* Get an encoded action */
4131 			if (!askfor_aux(buf, MAX_COLS, -2)) continue;
4132 
4133 			/* Extract an action */
4134 			text_to_ascii(macro__buf, 1024, buf);
4135 		}
4136 
4137 		/* Enter a new action */
4138 		else if (i == '3')
4139 		{
4140 			/* Prompt */
4141 			Term_putstr(0, 15, -1, TERM_WHITE, "Command: Enter a new action");
4142 
4143 			/* Go to the correct location */
4144 			Term_gotoxy(0, 21);
4145 
4146 			/* Hack -- limit the value */
4147 			tmp[MAX_COLS] = '\0';
4148 
4149 			/* Get an encoded action */
4150 			if (!askfor_aux(buf, MAX_COLS, 0)) continue;
4151 
4152 			/* Extract an action */
4153 			text_to_ascii(macro__buf, 1024, buf);
4154 		}
4155 
4156 		/* Query key */
4157 		else if (i == '4')
4158 		{
4159 			int k;
4160 
4161 			/* Prompt */
4162 			Term_putstr(0, 15, -1, TERM_WHITE, "Command: Query key for macro");
4163 
4164 			/* Prompt */
4165 			Term_erase(0, 17, 255);
4166 			Term_putstr(0, 17, -1, TERM_WHITE, "Trigger: ");
4167 
4168 			/* Show cursor */
4169 			Term_show_ui_cursor();
4170 
4171 			/* Get a macro trigger */
4172 			get_macro_trigger(buf);
4173 
4174 			/* Get the action */
4175 			k = macro_find_exact(buf);
4176 
4177 
4178 			/* Nothing found */
4179 			if (k < 0)
4180 			{
4181 				/* Prompt */
4182 				c_msg_print("Found no macro.");
4183 			}
4184 
4185 			/* It's an identity macro (empty) */
4186 			else if (streq(buf, macro__act[k]))
4187 			{
4188 				/* Prompt */
4189 				c_msg_print("Found no macro.");
4190 			}
4191 
4192 			/* Found one */
4193 			else
4194 			{
4195 				/* Obtain the action */
4196 				my_strcpy(macro__buf, macro__act[k], strlen(macro__buf)+1);
4197 
4198 				/* Analyze the current action */
4199 				ascii_to_text(tmp, sizeof(tmp), macro__buf);
4200 
4201 				/* Display the current action */
4202 				prt(tmp, 21, 0);
4203 
4204 				/* Prompt */
4205 				c_msg_print("Found a macro.");
4206 
4207 				/* Save key for delayed prompt */
4208 				ascii_to_text(tmp_buf, sizeof(tmp), buf);
4209 			}
4210 		}
4211 
4212 		/* Query key for keymap */
4213 		else if (i == '$')
4214 		{
4215 			int k;
4216 
4217 			/* Prompt */
4218 			Term_putstr(0, 15, -1, TERM_WHITE, "Command: Query key for keymap");
4219 
4220 			/* Prompt */
4221 			Term_erase(0, 17, 255);
4222 			Term_putstr(0, 17, -1, TERM_WHITE, "Keypress: ");
4223 			/* Show cursor */
4224 			Term_show_ui_cursor();
4225 
4226 			/* Get a keymap trigger */
4227 			do_cmd_macro_aux_keymap(buf);
4228 			k = buf[0];
4229 			if (!k) continue;
4230 
4231 			/* Save key for later */
4232 			ascii_to_text(tmp_buf, sizeof(tmp), buf);
4233 
4234 			/* Found noting */
4235 			if (!keymap_act[km_mode][k])
4236 			{
4237 				/* Prompt */
4238 				c_msg_print("Found no keymap.");
4239 			}
4240 			/* It's an identity macro (empty) */
4241 			else if (streq(buf, keymap_act[km_mode][k]))
4242 			{
4243 				/* Prompt */
4244 				c_msg_print("Found no keymap.");
4245 			}
4246 			/* Found one */
4247 			else
4248 			{
4249 				/* Analyze the current action */
4250 				ascii_to_text(tmp, sizeof(tmp), keymap_act[km_mode][k]);
4251 
4252 				/* Display the current action */
4253 				my_strcpy(macro__buf, tmp, 1024);
4254 
4255 				/* Prompt */
4256 				c_msg_print("Found a keymap.");
4257 			}
4258 		}
4259 
4260 		/* Create a keymap */
4261 		else if (i == '^')
4262 		{
4263 			byte new_map;
4264 
4265 			/* Prompt */
4266 			Term_putstr(0, 15, -1, TERM_WHITE, "Command: Create a keymap");
4267 
4268 			/* Prompt */
4269 			Term_erase(0, 17, 255);
4270 			Term_putstr(0, 17, -1, TERM_WHITE, "Keypress: ");
4271 			Term_show_ui_cursor();
4272 
4273 			/* Get a keymap trigger */
4274 			do_cmd_macro_aux_keymap(buf);
4275 			new_map = buf[0];
4276 			if (!new_map) continue;
4277 
4278 			/* Save key for later */
4279 			ascii_to_text(tmp_buf, sizeof(tmp), buf);
4280 
4281 			/* (un)Create new keymap */
4282 			if (keymap_act[km_mode][new_map]) string_free(keymap_act[km_mode][new_map]);
4283 			keymap_act[km_mode][new_map] = string_make(macro__buf);
4284 
4285 			/* Message */
4286 			c_msg_print("Created a new keymap.");
4287 		}
4288 
4289 
4290 		/* Create a normal macro */
4291 		else if (i == '5' || i == '%')
4292 		{
4293 			/* If '%' was pressed, create a command macro */
4294 			bool cmd_flag = (i == '%') ? TRUE : FALSE;
4295 
4296 			/* Prompt */
4297 			if (cmd_flag) Term_putstr(0, 15, -1, TERM_WHITE, "Command: Create a command macro");
4298 			else
4299 			Term_putstr(0, 15, -1, TERM_WHITE, "Command: Create a normal macro");
4300 
4301 			/* Prompt */
4302 			Term_erase(0, 17, 255);
4303 			Term_putstr(0, 17, -1, TERM_WHITE, "Trigger: ");
4304 			Term_show_ui_cursor();
4305 
4306 			/* Get a macro trigger */
4307 			if (!get_macro_trigger(buf)) continue;
4308 
4309 			/* Save key for later */
4310 			ascii_to_text(tmp_buf, sizeof(tmp), buf);
4311 
4312 			/* Hack -- if it's a one-key macro, make it a command macro */
4313 			if ((tmp_buf[1] == '\0') && (
4314 				isalpha(tmp_buf[0]) || ispunct(tmp_buf[0])
4315 				|| isdigit(tmp_buf[0])
4316 			)) cmd_flag = TRUE;
4317 
4318 			/* Link the macro */
4319 			macro_add(buf, macro__buf, cmd_flag);
4320 
4321 			/* Message */
4322 			if (cmd_flag) c_msg_print("Created a new command macro.");
4323 			else
4324 			c_msg_print("Created a new normal macro.");
4325 		}
4326 
4327 		/* Remove a macro */
4328 		else if (i == '6')
4329 		{
4330 			/* Prompt */
4331 			Term_putstr(0, 15, -1, TERM_WHITE, "Command: Remove a macro");
4332 
4333 			/* Prompt */
4334 			Term_erase(0, 17, 255);
4335 			Term_putstr(0, 17, -1, TERM_WHITE, "Trigger: ");
4336 			Term_show_ui_cursor();
4337 
4338 			/* Get a macro trigger */
4339 			get_macro_trigger(buf);
4340 
4341 			/* Link the macro */
4342 			macro_add(buf, buf, FALSE);
4343 
4344 			/* Message */
4345 			c_msg_print("Removed a macro.");
4346 		}
4347 
4348 		/* Oops */
4349 		else
4350 		{
4351 			/* Oops */
4352 			bell();
4353 		}
4354 	}
4355 
4356 	/* Reload screen */
4357 	Term_load();
4358 
4359 	/* Hide cursor */
4360 	Term_hide_ui_cursor();
4361 
4362 	/* Screen is no longer icky */
4363 	screen_icky = FALSE;
4364 
4365 	/* Flush the queue */
4366 	Flush_queue();
4367 }
4368 
4369 
4370 /*
4371  * Interact with some options
4372  */
do_cmd_options_aux(int page,bool local,cptr info)4373 static void do_cmd_options_aux(int page, bool local, cptr info)
4374 {
4375 	char	ch;
4376 
4377 	int		i, k = 0, n = 0;
4378 
4379 	int		opt[24];
4380 
4381 	char	buf[80];
4382 
4383 
4384 	/* Lookup the options */
4385 	for (i = 0; i < 24; i++) opt[i] = 0;
4386 
4387 	/* Scan the options */
4388 	if (local)
4389 	{
4390 		/* Local */
4391 		for (i = 0; local_option_info[i].o_desc; i++)
4392 		{
4393 			/* Notice options on this "page" */
4394 			if (local_option_info[i].o_page == page) opt[n++] = i;
4395 		}
4396 	}
4397 	else
4398 	{
4399 		/* Server */
4400 		for (i = 0; i < options_max; i++)
4401 		{
4402 			/* Notice options on this "page" */
4403 			if (option_info[i].o_page == page) opt[n++] = i;
4404 		}
4405 	}
4406 
4407 	/* Paranoia - zero options */
4408 	if (!n) return;
4409 
4410 	/* Clear screen */
4411 	Term_clear();
4412 
4413 	/* Interact with the player */
4414 	while (TRUE)
4415 	{
4416 		bool set_must = FALSE;
4417 		bool set_what;
4418 		int  set_id;
4419 
4420 		/* Prompt XXX XXX XXX */
4421 		sprintf(buf, "%-30s (RET to advance, y/n to set, ESC to accept) ", info);
4422 		prt(buf, 0, 0);
4423 
4424 		/* Display the options */
4425 		for (i = 0; i < n; i++)
4426 		{
4427 			byte a = TERM_WHITE;
4428 
4429 			/* Color current option */
4430 			if (i == k) a = TERM_L_BLUE;
4431 
4432 			/* Display the option text */
4433 			if (local)
4434 			{
4435 				sprintf(buf, "%-48s: %s  (%s)",
4436 				        local_option_info[opt[i]].o_desc,
4437 				        (*local_option_info[opt[i]].o_var ? "yes" : "no "),
4438 				        local_option_info[opt[i]].o_text);
4439 			}
4440 			else
4441 			{
4442 				sprintf(buf, "%-48s: %s  (%s)",
4443 				        option_info[opt[i]].o_desc,
4444 				        (p_ptr->options[opt[i]] ? "yes" : "no "),
4445 				        option_info[opt[i]].o_text);
4446 			}
4447 			c_prt(a, buf, i + 2, 0);
4448 		}
4449 
4450 		/* Hilite current option */
4451 		move_cursor(k + 2, 50);
4452 
4453 		/* Show cursor */
4454 		Term_show_ui_cursor();
4455 
4456 		/* Get a key */
4457 		ch = inkey();
4458 
4459 		/* Analyze */
4460 		switch (ch)
4461 		{
4462 			case ESCAPE:
4463 			{
4464 				return;
4465 			}
4466 
4467 			case '-':
4468 			case '8':
4469 			{
4470 				k = (n + k - 1) % n;
4471 				break;
4472 			}
4473 
4474 			case ' ':
4475 			case '\n':
4476 			case '\r':
4477 			case '2':
4478 			{
4479 				k = (k + 1) % n;
4480 				break;
4481 			}
4482 
4483 			case 'y':
4484 			case 'Y':
4485 			case '6':
4486 			{
4487 				set_must = TRUE;
4488 				set_what = TRUE;
4489 				set_id = k;
4490 				k = (k + 1) % n;
4491 				break;
4492 			}
4493 
4494 			case 'n':
4495 			case 'N':
4496 			case '4':
4497 			{
4498 				set_must = TRUE;
4499 				set_what = FALSE;
4500 				set_id = k;
4501 				k = (k + 1) % n;
4502 				break;
4503 			}
4504 
4505 			default:
4506 			{
4507 				bell();
4508 				break;
4509 			}
4510 		}
4511 		/* Set option */
4512 		if (set_must)
4513 		{
4514 			int on_opt = -1;
4515 			int on_var = -1;
4516 			if (local)
4517 			{
4518 				if (local_option_info[opt[set_id]].o_set)
4519 				{
4520 					on_opt = local_option_info[opt[set_id]].o_set;
4521 				}
4522 				on_var = opt[set_id];
4523 			}
4524 			else
4525 			{
4526 				if (option_info[opt[set_id]].o_set)
4527 				{
4528 					on_var = option_info[opt[set_id]].o_set;
4529 				}
4530 				on_opt = opt[set_id];
4531 			}
4532 			if (on_opt != -1)
4533 				p_ptr->options[on_opt] = set_what;
4534 			if (on_var != -1)
4535 				(*local_option_info[on_var].o_var) = set_what;
4536 		}
4537 	}
4538 
4539 	Term_hide_ui_cursor();
4540 }
4541 
4542 
4543 /*
4544  * Modify the "window" options
4545  */
do_cmd_options_win(void)4546 static void do_cmd_options_win(void)
4547 {
4548 	int i, j, d;
4549 
4550 	int y = 0;
4551 	int x = 0;
4552 
4553 	char ch;
4554 
4555 	bool go = TRUE;
4556 
4557 	u32b old_flag[8];
4558 
4559 
4560 	/* Memorize old flags */
4561 	for (j = 0; j < 8; j++)
4562 	{
4563 		/* Acquire current flags */
4564 		old_flag[j] = window_flag[j];
4565 	}
4566 
4567 
4568 	/* Clear screen */
4569 	Term_clear();
4570 
4571 	/* Interact */
4572 	while (go)
4573 	{
4574 		byte st = 0;
4575 
4576 		/* Prompt XXX XXX XXX */
4577 		prt("Window flags (<dir>, t, y, n, ESC) ", 0, 0);
4578 
4579 		/* Display the windows */
4580 		for (j = 0; j < 8; j++)
4581 		{
4582 			byte a = TERM_WHITE;
4583 
4584 			cptr s = ang_term_name[j];
4585 
4586 			/* Use color */
4587 			if (use_color && (j == x)) a = TERM_L_BLUE;
4588 
4589 			/* Window name, staggered, centered */
4590 			Term_putstr(35 + j * 5 - strlen(s) / 2, 2 + j % 2, -1, a, s);
4591 		}
4592 
4593 		/* Display the options */
4594 		for (i = 0; i < 16; i++)
4595 		{
4596 			byte a = TERM_WHITE;
4597 
4598 			cptr str = window_flag_desc[i];
4599 
4600 			/* Use color */
4601 			if (use_color && (i == y)) a = TERM_L_BLUE;
4602 
4603 			/* Unused option */
4604 			if (!str) str = "(Unused option)";
4605 
4606 			/* Flag name */
4607 			Term_putstr(0, i + 5, -1, a, str);
4608 
4609 			/* Display the windows */
4610 			for (j = 0; j < 8; j++)
4611 			{
4612 				byte a = TERM_WHITE;
4613 
4614 				char c = '.';
4615 
4616 				/* Use color */
4617 				if (use_color && (i == y) && (j == x)) a = TERM_L_BLUE;
4618 
4619 				/* Active flag */
4620 				if (window_flag[j] & (1L << i)) c = 'X';
4621 
4622 				/* Flag value */
4623 				Term_putch(35 + j * 5, i + 5, a, c);
4624 			}
4625 		}
4626 
4627 		/* Place Cursor */
4628 		Term_gotoxy(35 + x * 5, y + 5);
4629 
4630 		/* And show it */
4631 		Term_show_ui_cursor();
4632 
4633 		/* Get key */
4634 		ch = inkey();
4635 
4636 		/* Analyze */
4637 		switch (ch)
4638 		{
4639 			case ESCAPE:
4640 			{
4641 				go = FALSE;
4642 				break;
4643 			}
4644 
4645 			case 'T':
4646 			case 't':
4647 			{
4648 #ifdef PMSG_TERM
4649 				/* Ignore any change on PMSG_TERM */
4650 				if (x == PMSG_TERM) break;
4651 #endif
4652 				/* Clear windows */
4653 				for (j = 0; j < 8; j++)
4654 				{
4655 					/* Ignore screen (but not for Status AND Compact)*/
4656 					if ((j == 0) && ((1L << y) != PW_STATUS) && ((1L << y) != PW_PLAYER_2)) break;
4657 
4658 					window_flag[j] &= ~(1L << y);
4659 				}
4660 
4661 				/* Clear flags */
4662 				for (i = 0; i < 16; i++)
4663 				{
4664 					/* Ignore screen (but not for Status AND Compact)*/
4665 					if ((x == 0) && ((1L << i) != PW_STATUS) && ((1L << i) != PW_PLAYER_2)) break;
4666 
4667 					window_flag[x] &= ~(1L << i);
4668 				}
4669 
4670 				/* Fall through */
4671 			}
4672 
4673 			case 'y':
4674 			case 'Y':
4675 			{
4676 #ifdef PMSG_TERM
4677 				/* Ignore any change on PMSG_TERM */
4678 				if (x == PMSG_TERM) break;
4679 #endif
4680 				/* Ignore screen (but not for Status AND Compact)*/
4681 				if ((x == 0) && ((1L << y) != PW_STATUS) && ((1L << y) != PW_PLAYER_2)) break;
4682 
4683 				/* Set flag */
4684 				window_flag[x] |= (1L << y);
4685 				break;
4686 			}
4687 
4688 			case 'n':
4689 			case 'N':
4690 			{
4691 #ifdef PMSG_TERM
4692 				/* Ignore any change on PMSG_TERM */
4693 				if (x == PMSG_TERM) break;
4694 #endif
4695 				/* Ignore screen (but not for Status AND Compact)*/
4696 				if ((x == 0) && ((1L << y) != PW_STATUS) && ((1L << y) != PW_PLAYER_2)) break;
4697 
4698 				/* Clear flag */
4699 				window_flag[x] &= ~(1L << y);
4700 				break;
4701 			}
4702 
4703 			default:
4704 			{
4705 				d = target_dir(ch);
4706 
4707 				x = (x + ddx[d] + 8) % 8;
4708 				y = (y + ddy[d] + 16) % 16;
4709 
4710 				if (!d) bell();
4711 			}
4712 		}
4713 	}
4714 
4715 	/* Hack -- Store user choice */
4716 	for (j = 0; j < 8; j++)
4717 	{
4718 		window_flag_o[j] = window_flag[j];
4719 	}
4720 
4721 	/* Notice changes */
4722 	p_ptr->window |= net_term_manage(old_flag, window_flag, TRUE);
4723 
4724 	/* Notify terminal (optional) */
4725 	Term_xtra(TERM_XTRA_REACT, (TERM_XTRA_REACT_WINDOWS));
4726 
4727 	/* Update windows */
4728 	p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_MESSAGE | PW_MESSAGE_CHAT | PW_PLAYER | PW_PLAYER_1 | PW_STATUS);
4729 
4730 	/* Hide cursor */
4731 	Term_hide_ui_cursor();
4732 
4733 	/* Update windows */
4734 	window_stuff();
4735 }
4736 
4737 
4738 
4739 
4740 /*
4741  * Set or unset various options.
4742  *
4743  * The user must use the "Ctrl-R" command to "adapt" to changes
4744  * in any options which control "visual" aspects of the game.
4745  */
do_cmd_options(void)4746 void do_cmd_options(void)
4747 {
4748 	int k;
4749 	int col, i, label;
4750 
4751 	int old_hitpoint_warn = Client_setup.settings[3];
4752 	int old_window_flags = Client_setup.settings[4];
4753 
4754 	/* Enter "icky" mode */
4755 	screen_icky = TRUE;
4756 
4757 	/* Save the screen */
4758 	Term_save();
4759 
4760 
4761 	/* Interact */
4762 	while (1)
4763 	{
4764 		/* Clear screen */
4765 		Term_clear();
4766 
4767 		/* Why are we here */
4768 		prt("MAngband options", 2, 0);
4769 
4770 		/* Prepare */
4771 		i = label = 0;
4772 		col = 4;
4773 
4774 		/* Give some choices */
4775 		for (k = 0; local_option_group[k]; k++)
4776 		{
4777 			prt(format("(%d) %s", ++i, local_option_group[k]), col++, 5);
4778 		}
4779 		label = k+1;
4780 		for (k = 0; k < options_groups_max; k++)
4781 		{
4782 			prt(format("(%d) %s", ++i, option_group[k]), col++, 5);
4783 		}
4784 
4785 		/* Window flags */
4786 		col += 1;
4787 		prt("(W) Window flags", col++, 5);
4788 
4789 		/* Load and Append
4790 		col += 1;
4791 		prt("(L) Load a user pref file", col++, 5);
4792 		prt("(A) Append options to a file", col++, 5); */
4793 
4794 		/* Special choices */
4795 		col += 1;
4796 		/* prt("(D) Base Delay Factor", col++, 5); */
4797 		prt("(H) Hitpoint Warning", col++, 5);
4798 
4799 
4800 
4801 		/* Prompt */
4802 		col += 2;
4803 		prt("Command: ", col, 0);
4804 		Term_show_ui_cursor();
4805 
4806 		/* Get command */
4807 		k = inkey();
4808 
4809 		/* Exit */
4810 		if (k == ESCAPE) break;
4811 
4812 		/* Entered some group */
4813 		if (isdigit(k))
4814 		{
4815 			i = D2I(k);
4816 			/* Unknown digit ? */
4817 			if (i >= options_groups_max + label)
4818 			{
4819 				/* Oops */
4820 				bell();
4821 				continue;
4822 			}
4823 			/* Local */
4824 			if (i < label)
4825 			{
4826 				do_cmd_options_aux(i, TRUE, local_option_group[i - 1]);
4827 			}
4828 			/* Server */
4829 			else
4830 			{
4831 				do_cmd_options_aux(i - label + 1, FALSE, option_group[i - label]);
4832 			}
4833 		}
4834 
4835 		/* Window flags */
4836 		else if (k == 'W')
4837 		{
4838 			/* Spawn */
4839 			do_cmd_options_win();
4840 		}
4841 
4842 		/* Hack -- hitpoint warning factor */
4843 		else if ((k == 'H') || (k == 'h'))
4844 		{
4845 			/* Prompt */
4846 			prt("Command: Hitpoint Warning", col, 0);
4847 
4848 			/* Get a new value */
4849 			while (1)
4850 			{
4851 				char cx;
4852 				prt(format("Current hitpoint warning: %2d%%",
4853 				           p_ptr->hitpoint_warn * 10), col+2, 0);
4854 				prt("New hitpoint warning (0-9 or ESC to accept): ", col+1, 0);
4855 				Term_show_ui_cursor();
4856 
4857 				cx = inkey();
4858 				if (cx == ESCAPE) break;
4859 				if (cx == '0') hitpoint_warn_toggle = 0;
4860 				if (isdigit((unsigned char)cx)) p_ptr->hitpoint_warn = D2I(cx);
4861 				else if (cx == '=') p_ptr->hitpoint_warn = 10; /* 100% */
4862 				else if (cx == '-')
4863 				{	/* Toggle between last value and 0% */
4864 					bool on = p_ptr->hitpoint_warn > 0 ? TRUE : FALSE;
4865 					hitpoint_warn_toggle = on ? p_ptr->hitpoint_warn : hitpoint_warn_toggle;
4866 					p_ptr->hitpoint_warn = on ? 0 : hitpoint_warn_toggle;
4867 				}
4868 				else bell();/*"Illegal hitpoint warning!");*/
4869 			}
4870 		}
4871 
4872 		/* Unknown option */
4873 		else
4874 		{
4875 			/* Oops */
4876 			bell();
4877 		}
4878 	}
4879 
4880 	/* Hide cursor */
4881 	Term_hide_ui_cursor();
4882 
4883 	/* Restore the screen */
4884 	Term_load();
4885 
4886 	/* Leave "icky" mode */
4887 	screen_icky = FALSE;
4888 	Flush_queue();
4889 
4890 	/* HACK -- hitpoint warning changed */
4891 	gather_settings();
4892 	if (old_hitpoint_warn != Client_setup.settings[3] ||
4893 		old_window_flags != Client_setup.settings[4])
4894 	send_settings();
4895 
4896 	/* Resend options to server */
4897 	send_options();
4898 
4899 	/* Save options to file */
4900 	Save_options();
4901 
4902 	/* Send a redraw request */
4903 	send_redraw();
4904 
4905 	/* Notify terminal (optional) */
4906 	Term_xtra(TERM_XTRA_REACT, (TERM_XTRA_REACT_OPTIONS | TERM_XTRA_REACT_SETTINGS));
4907 }
do_cmd_options_birth()4908 void do_cmd_options_birth()
4909 {
4910 	/* Save the screen */
4911 	Term_save();
4912 
4913 	/* Hack -- asume Group 1 (index0) as "Birth Options" */
4914 	do_cmd_options_aux(1, FALSE, option_group[0]);
4915 
4916 	/* Hack -- make those ones a priority above ones read from pref file */
4917 	ignore_birth_options = TRUE;
4918 
4919 	/* Restore the screen */
4920 	Term_load();
4921 }
4922 
4923 /* HACK -- Count samples in "val" sound */
sound_count(int val)4924 int sound_count(int val)
4925 {
4926 	int i;
4927 
4928 	/* No sound */
4929 	if (!use_sound) return (0);
4930 
4931 	/* Illegal sound */
4932 	if ((val < 0) || (val >= MSG_MAX)) return (0);
4933 
4934 
4935 	/* Count the samples */
4936 	for (i = 0; i < SAMPLE_MAX; i++)
4937 	{
4938 		if (!sound_file[val][i])
4939 			break;
4940 	}
4941 
4942 	/* Return number of samples (might be 0) */
4943 	return (i);
4944 }
4945 
4946 #ifdef USE_SOUND
4947 
4948 /*
4949  * XXX XXX XXX - Taken from files.c.
4950  *
4951  * Extract "tokens" from a buffer
4952  *
4953  * This function uses "whitespace" as delimiters, and treats any amount of
4954  * whitespace as a single delimiter.  We will never return any empty tokens.
4955  * When given an empty buffer, or a buffer containing only "whitespace", we
4956  * will return no tokens.  We will never extract more than "num" tokens.
4957  *
4958  * By running a token through the "text_to_ascii()" function, you can allow
4959  * that token to include (encoded) whitespace, using "\s" to encode spaces.
4960  *
4961  * We save pointers to the tokens in "tokens", and return the number found.
4962  */
tokenize_whitespace(char * buf,s16b num,char ** tokens)4963 static s16b tokenize_whitespace(char *buf, s16b num, char **tokens)
4964 {
4965 	int k = 0;
4966 	char *s = buf;
4967 
4968 	/* Process */
4969 	while (k < num)
4970 	{
4971 		char *t;
4972 
4973 		/* Skip leading whitespace */
4974 		for ( ; *s && isspace((unsigned char)*s); ++s) /* loop */;
4975 
4976 		/* All done */
4977 		if (!*s) break;
4978 
4979 		/* Find next whitespace, if any */
4980 		for (t = s; *t && !isspace((unsigned char)*t); ++t) /* loop */;
4981 
4982 		/* Nuke and advance (if necessary) */
4983 		if (*t) *t++ = '\0';
4984 
4985 		/* Save the token */
4986 		tokens[k++] = s;
4987 
4988 		/* Advance */
4989 		s = t;
4990 	}
4991 
4992 	/* Count */
4993 	return (k);
4994 }
4995 
4996 
4997 
load_sound_prefs(void)4998 void load_sound_prefs(void)
4999 {
5000 	int i, j, num;
5001 	char tmp[MSG_LEN];
5002 	char ini_path[MSG_LEN];
5003 	char wav_path[MSG_LEN];
5004 	char *zz[SAMPLE_MAX];
5005 
5006 	/* Access the sound.cfg */
5007 	path_build(ini_path, sizeof(ini_path), ANGBAND_DIR_XTRA_SOUND, "sound.cfg");
5008 
5009 	/* Add it to 'global config' */
5010 	conf_append_section("Sound", ini_path);
5011 
5012 	for (i = 0; i < MSG_MAX; i++)
5013 	{
5014 		/* Ignore empty sound strings */
5015 		if (!angband_sound_name[i][0]) continue;
5016 
5017 		my_strcpy(tmp, conf_get_string("Sound", angband_sound_name[i], ""), sizeof(tmp));
5018 
5019 		num = tokenize_whitespace(tmp, SAMPLE_MAX, zz);
5020 
5021 		for (j = 0; j < num; j++)
5022 		{
5023 			/* Access the sound */
5024 			path_build(wav_path, sizeof(wav_path), ANGBAND_DIR_XTRA_SOUND, zz[j]);
5025 
5026 			/* Save the sound filename, if it exists */
5027 			if (file_exists(wav_path))
5028 				sound_file[i][j] = string_make(zz[j]);
5029 		}
5030 	}
5031 }
5032 
5033 #endif /* USE_SOUND */
5034