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, ¤t_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, ¤t_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, ¤t_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