1 /* File: util.c */
2
3 /* Purpose: Angband utilities -BEN- */
4
5
6 #include "mangband.h"
7
8
9
10
11
12
13
14 #ifdef SET_UID
15
16
17
18 /*
19 * Hack -- External functions
20 */
21 extern struct passwd *getpwuid();
22 extern struct passwd *getpwnam();
23
24
25 #endif
26
27
28
29 /*
30 * The concept of the "file" routines below (and elsewhere) is that all
31 * file handling should be done using as few routines as possible, since
32 * every machine is slightly different, but these routines always have the
33 * same semantics.
34 *
35 * In fact, perhaps we should use the "path_parse()" routine below to convert
36 * from "canonical" filenames (optional leading tilde's, internal wildcards,
37 * slash as the path seperator, etc) to "system" filenames (no special symbols,
38 * system-specific path seperator, etc). This would allow the program itself
39 * to assume that all filenames are "Unix" filenames, and explicitly "extract"
40 * such filenames if needed (by "path_parse()", or perhaps "path_canon()").
41 *
42 * Note that "path_temp" should probably return a "canonical" filename.
43 *
44 * Note that "my_fopen()" and "my_open()" and "my_make()" and "my_kill()"
45 * and "my_move()" and "my_copy()" should all take "canonical" filenames.
46 *
47 * Note that "canonical" filenames use a leading "slash" to indicate an absolute
48 * path, and a leading "tilde" to indicate a special directory, and default to a
49 * relative path, but MSDOS uses a leading "drivename plus colon" to indicate the
50 * use of a "special drive", and then the rest of the path is parsed "normally",
51 * and MACINTOSH uses a leading colon to indicate a relative path, and an embedded
52 * colon to indicate a "drive plus absolute path", and finally defaults to a file
53 * in the current working directory, which may or may not be defined.
54 *
55 * We should probably parse a leading "~~/" as referring to "ANGBAND_DIR". (?)
56 */
57
58
59 #ifdef ACORN
60
61
62 /*
63 * Most of the "file" routines for "ACORN" should be in "main-acn.c"
64 */
65
66
67 #else /* ACORN */
68
69
70
71 #endif
72
73 /*
74 * Acquire a "temporary" file name if possible
75 *
76 * This filename is always in "system-specific" form.
77 */
path_temp(char * buf,int max)78 errr path_temp(char *buf, int max)
79 {
80 #ifdef WIN32
81 char prefix[] = "mng";
82 if(!GetTempPath(max,buf)) return(-1);
83 if(!GetTempFileName(buf,prefix,0,buf)) return(-1);
84 #else
85 int p;
86 strcpy(buf,"/tmp/mangXXXXXX");
87 if((p = mkstemp(buf)) < 0)
88 {
89 return(-1);
90 }
91 fclose(fdopen(p, "r"));
92 #endif
93
94 /* Success */
95 return (0);
96 }
97
98
99
100 /*
101 * XXX XXX XXX Important note about "colors" XXX XXX XXX
102 *
103 * The "TERM_*" color definitions list the "composition" of each
104 * "Angband color" in terms of "quarters" of each of the three color
105 * components (Red, Green, Blue), for example, TERM_UMBER is defined
106 * as 2/4 Red, 1/4 Green, 0/4 Blue.
107 *
108 * The following info is from "Torbjorn Lindgren" (see "main-xaw.c").
109 *
110 * These values are NOT gamma-corrected. On most machines (with the
111 * Macintosh being an important exception), you must "gamma-correct"
112 * the given values, that is, "correct for the intrinsic non-linearity
113 * of the phosphor", by converting the given intensity levels based
114 * on the "gamma" of the target screen, which is usually 1.7 (or 1.5).
115 *
116 * The actual formula for conversion is unknown to me at this time,
117 * but you can use the table below for the most common gamma values.
118 *
119 * So, on most machines, simply convert the values based on the "gamma"
120 * of the target screen, which is usually in the range 1.5 to 1.7, and
121 * usually is closest to 1.7. The converted value for each of the five
122 * different "quarter" values is given below:
123 *
124 * Given Gamma 1.0 Gamma 1.5 Gamma 1.7 Hex 1.7
125 * ----- ---- ---- ---- ---
126 * 0/4 0.00 0.00 0.00 #00
127 * 1/4 0.25 0.27 0.28 #47
128 * 2/4 0.50 0.55 0.56 #8f
129 * 3/4 0.75 0.82 0.84 #d7
130 * 4/4 1.00 1.00 1.00 #ff
131 *
132 * Note that some machines (i.e. most IBM machines) are limited to a
133 * hard-coded set of colors, and so the information above is useless.
134 *
135 * Also, some machines are limited to a pre-determined set of colors,
136 * for example, the IBM can only display 16 colors, and only 14 of
137 * those colors resemble colors used by Angband, and then only when
138 * you ignore the fact that "Slate" and "cyan" are not really matches,
139 * so on the IBM, we use "orange" for both "Umber", and "Light Umber"
140 * in addition to the obvious "Orange", since by combining all of the
141 * "indeterminate" colors into a single color, the rest of the colors
142 * are left with "meaningful" values.
143 */
144
145 /*
146 * Convert a "color letter" into an "actual" color
147 * The colors are: dwsorgbuDWvyRGBU, as shown below
148 */
color_char_to_attr(char c)149 int color_char_to_attr(char c)
150 {
151 switch (c)
152 {
153 case 'd': return (TERM_DARK);
154 case 'w': return (TERM_WHITE);
155 case 's': return (TERM_SLATE);
156 case 'o': return (TERM_ORANGE);
157 case 'r': return (TERM_RED);
158 case 'g': return (TERM_GREEN);
159 case 'b': return (TERM_BLUE);
160 case 'u': return (TERM_UMBER);
161
162 case 'D': return (TERM_L_DARK);
163 case 'W': return (TERM_L_WHITE);
164 case 'v': return (TERM_VIOLET);
165 case 'y': return (TERM_YELLOW);
166 case 'R': return (TERM_L_RED);
167 case 'G': return (TERM_L_GREEN);
168 case 'B': return (TERM_L_BLUE);
169 case 'U': return (TERM_L_UMBER);
170 }
171
172 return (-1);
173 }
174
175 /*
176 * Convert a color to it's opposite
177 */
color_opposite(int color)178 int color_opposite(int color) {
179 switch (color) {
180 case TERM_L_BLUE: return TERM_BLUE;
181 case TERM_L_GREEN: return TERM_GREEN;
182 case TERM_L_RED: return TERM_RED;
183 case TERM_L_WHITE: return TERM_SLATE;
184 case TERM_UMBER: return TERM_L_UMBER;
185 case TERM_ORANGE: return TERM_YELLOW;
186 case TERM_DARK: return TERM_L_DARK;
187 case TERM_VIOLET: return ( randint1(100) < 50 ? TERM_BLUE : TERM_RED );
188 /* and vice versa */
189 case TERM_BLUE: return TERM_L_BLUE;
190 case TERM_GREEN: return TERM_L_GREEN;
191 case TERM_RED: return TERM_L_RED;
192 case TERM_SLATE: return TERM_L_WHITE;
193 case TERM_L_UMBER: return TERM_UMBER;
194 case TERM_YELLOW: return TERM_ORANGE;
195 case TERM_L_DARK: return TERM_DARK;
196 }
197 return color;
198 }
199
200
201 /*
202 * Move the cursor
203 */
204 #if 0
205 void move_cursor(int row, int col)
206 {
207 Term_gotoxy(col, row);
208 }
209 #endif
210
211
212
213 /*
214 * Convert a decimal to a single digit octal number
215 */
octify(uint i)216 static char octify(uint i)
217 {
218 return (hexsym[i%8]);
219 }
220
221 /*
222 * Convert a decimal to a single digit hex number
223 */
hexify(uint i)224 static char hexify(uint i)
225 {
226 return (hexsym[i%16]);
227 }
228
229
230 /*
231 * Convert a octal-digit into a decimal
232 */
deoct(char c)233 static int deoct(char c)
234 {
235 if (isdigit(c)) return (D2I(c));
236 return (0);
237 }
238
239 /*
240 * Convert a hexidecimal-digit into a decimal
241 */
dehex(char c)242 static int dehex(char c)
243 {
244 if (isdigit(c)) return (D2I(c));
245 if (islower(c)) return (A2I(c) + 10);
246 if (isupper(c)) return (A2I(tolower(c)) + 10);
247 return (0);
248 }
249
250
251 /*
252 * Hack -- convert a printable string into real ascii
253 *
254 * I have no clue if this function correctly handles, for example,
255 * parsing "\xFF" into a (signed) char. Whoever thought of making
256 * the "sign" of a "char" undefined is a complete moron. Oh well.
257 */
text_to_ascii(char * buf,cptr str)258 void text_to_ascii(char *buf, cptr str)
259 {
260 char *s = buf;
261
262 /* Analyze the "ascii" string */
263 while (*str)
264 {
265 /* Backslash codes */
266 if (*str == '\\')
267 {
268 /* Skip the backslash */
269 str++;
270
271 /* Hex-mode XXX */
272 if (*str == 'x')
273 {
274 *s = 16 * dehex(*++str);
275 *s++ += dehex(*++str);
276 }
277
278 /* Hack -- simple way to specify "backslash" */
279 else if (*str == '\\')
280 {
281 *s++ = '\\';
282 }
283
284 /* Hack -- simple way to specify "caret" */
285 else if (*str == '^')
286 {
287 *s++ = '^';
288 }
289
290 /* Hack -- simple way to specify "space" */
291 else if (*str == 's')
292 {
293 *s++ = ' ';
294 }
295
296 /* Hack -- simple way to specify Escape */
297 else if (*str == 'e')
298 {
299 *s++ = ESCAPE;
300 }
301
302 /* Backspace */
303 else if (*str == 'b')
304 {
305 *s++ = '\b';
306 }
307
308 /* Newline */
309 else if (*str == 'n')
310 {
311 *s++ = '\n';
312 }
313
314 /* Return */
315 else if (*str == 'r')
316 {
317 *s++ = '\r';
318 }
319
320 /* Tab */
321 else if (*str == 't')
322 {
323 *s++ = '\t';
324 }
325
326 /* Octal-mode */
327 else if (*str == '0')
328 {
329 *s = 8 * deoct(*++str);
330 *s++ += deoct(*++str);
331 }
332
333 /* Octal-mode */
334 else if (*str == '1')
335 {
336 *s = 64 + 8 * deoct(*++str);
337 *s++ += deoct(*++str);
338 }
339
340 /* Octal-mode */
341 else if (*str == '2')
342 {
343 *s = 64 * 2 + 8 * deoct(*++str);
344 *s++ += deoct(*++str);
345 }
346
347 /* Octal-mode */
348 else if (*str == '3')
349 {
350 *s = 64 * 3 + 8 * deoct(*++str);
351 *s++ += deoct(*++str);
352 }
353
354 /* Skip the final char */
355 str++;
356 }
357
358 /* Normal Control codes */
359 else if (*str == '^')
360 {
361 str++;
362 *s++ = (*str++ & 037);
363 }
364
365 /* Normal chars */
366 else
367 {
368 *s++ = *str++;
369 }
370 }
371
372 /* Terminate */
373 *s = '\0';
374 }
375
376
377 /*
378 * Hack -- convert a string into a printable form
379 */
ascii_to_text(char * buf,cptr str)380 void ascii_to_text(char *buf, cptr str)
381 {
382 char *s = buf;
383
384 /* Analyze the "ascii" string */
385 while (*str)
386 {
387 byte i = (byte)(*str++);
388
389 if (i == ESCAPE)
390 {
391 *s++ = '\\';
392 *s++ = 'e';
393 }
394 else if (i == ' ')
395 {
396 *s++ = '\\';
397 *s++ = 's';
398 }
399 else if (i == '\b')
400 {
401 *s++ = '\\';
402 *s++ = 'b';
403 }
404 else if (i == '\t')
405 {
406 *s++ = '\\';
407 *s++ = 't';
408 }
409 else if (i == '\n')
410 {
411 *s++ = '\\';
412 *s++ = 'n';
413 }
414 else if (i == '\r')
415 {
416 *s++ = '\\';
417 *s++ = 'r';
418 }
419 else if (i == '^')
420 {
421 *s++ = '\\';
422 *s++ = '^';
423 }
424 else if (i == '\\')
425 {
426 *s++ = '\\';
427 *s++ = '\\';
428 }
429 else if (i < 32)
430 {
431 *s++ = '^';
432 *s++ = i + 64;
433 }
434 else if (i < 127)
435 {
436 *s++ = i;
437 }
438 else if (i < 64)
439 {
440 *s++ = '\\';
441 *s++ = '0';
442 *s++ = octify(i / 8);
443 *s++ = octify(i % 8);
444 }
445 else
446 {
447 *s++ = '\\';
448 *s++ = 'x';
449 *s++ = hexify(i / 16);
450 *s++ = hexify(i % 16);
451 }
452 }
453
454 /* Terminate */
455 *s = '\0';
456 }
457
458
459 #if 0
460 /*
461 * Variable used by the functions below
462 */
463 static int hack_dir = 0;
464
465
466 /*
467 * Convert a "Rogue" keypress into an "Angband" keypress
468 * Pass extra information as needed via "hack_dir"
469 *
470 * Note that many "Rogue" keypresses encode a direction.
471 */
472 static char roguelike_commands(char command)
473 {
474 /* Process the command */
475 switch (command)
476 {
477 /* Movement (rogue keys) */
478 case 'b': hack_dir = 1; return (';');
479 case 'j': hack_dir = 2; return (';');
480 case 'n': hack_dir = 3; return (';');
481 case 'h': hack_dir = 4; return (';');
482 case 'l': hack_dir = 6; return (';');
483 case 'y': hack_dir = 7; return (';');
484 case 'k': hack_dir = 8; return (';');
485 case 'u': hack_dir = 9; return (';');
486
487 /* Running (shift + rogue keys) */
488 case 'B': hack_dir = 1; return ('.');
489 case 'J': hack_dir = 2; return ('.');
490 case 'N': hack_dir = 3; return ('.');
491 case 'H': hack_dir = 4; return ('.');
492 case 'L': hack_dir = 6; return ('.');
493 case 'Y': hack_dir = 7; return ('.');
494 case 'K': hack_dir = 8; return ('.');
495 case 'U': hack_dir = 9; return ('.');
496
497 /* Tunnelling (control + rogue keys) */
498 case KTRL('B'): hack_dir = 1; return ('+');
499 case KTRL('J'): hack_dir = 2; return ('+');
500 case KTRL('N'): hack_dir = 3; return ('+');
501 case KTRL('H'): hack_dir = 4; return ('+');
502 case KTRL('L'): hack_dir = 6; return ('+');
503 case KTRL('Y'): hack_dir = 7; return ('+');
504 case KTRL('K'): hack_dir = 8; return ('+');
505 case KTRL('U'): hack_dir = 9; return ('+');
506
507 /* Hack -- White-space */
508 case KTRL('M'): return ('\r');
509
510 /* Allow use of the "destroy" command */
511 case KTRL('D'): return ('k');
512
513 /* Hack -- Commit suicide */
514 case KTRL('C'): return ('Q');
515
516 /* Locate player on map */
517 case 'W': return ('L');
518
519 /* Browse a book (Peruse) */
520 case 'P': return ('b');
521
522 /* Jam a door (Spike) */
523 case 'S': return ('j');
524
525 /* Toggle search mode */
526 case '#': return ('S');
527
528 /* Use a staff (Zap) */
529 case 'Z': return ('u');
530
531 /* Take off equipment */
532 case 'T': return ('t');
533
534 /* Fire an item */
535 case 't': return ('f');
536
537 /* Bash a door (Force) */
538 case 'f': return ('B');
539
540 /* Look around (examine) */
541 case 'x': return ('l');
542
543 /* Aim a wand (Zap) */
544 case 'z': return ('a');
545
546 /* Zap a rod (Activate) */
547 case 'a': return ('z');
548
549 /* Run */
550 case ',': return ('.');
551
552 /* Stay still (fake direction) */
553 case '.': hack_dir = 5; return (',');
554
555 /* Stay still (fake direction) */
556 case '5': hack_dir = 5; return (',');
557
558 /* Standard walking */
559 case '1': hack_dir = 1; return (';');
560 case '2': hack_dir = 2; return (';');
561 case '3': hack_dir = 3; return (';');
562 case '4': hack_dir = 4; return (';');
563 case '6': hack_dir = 6; return (';');
564 case '7': hack_dir = 7; return (';');
565 case '8': hack_dir = 8; return (';');
566 case '9': hack_dir = 9; return (';');
567 }
568
569 /* Default */
570 return (command);
571 }
572
573
574 /*
575 * Convert an "Original" keypress into an "Angband" keypress
576 * Pass direction information back via "hack_dir".
577 *
578 * Note that "Original" and "Angband" are very similar.
579 */
580 static char original_commands(char command)
581 {
582 /* Process the command */
583 switch (command)
584 {
585 /* Hack -- White space */
586 case KTRL('J'): return ('\r');
587 case KTRL('M'): return ('\r');
588
589 /* Tunnel */
590 case 'T': return ('+');
591
592 /* Run */
593 case '.': return ('.');
594
595 /* Stay still (fake direction) */
596 case ',': hack_dir = 5; return (',');
597
598 /* Stay still (fake direction) */
599 case '5': hack_dir = 5; return (',');
600
601 /* Standard walking */
602 case '1': hack_dir = 1; return (';');
603 case '2': hack_dir = 2; return (';');
604 case '3': hack_dir = 3; return (';');
605 case '4': hack_dir = 4; return (';');
606 case '6': hack_dir = 6; return (';');
607 case '7': hack_dir = 7; return (';');
608 case '8': hack_dir = 8; return (';');
609 case '9': hack_dir = 9; return (';');
610
611 /* Hack -- Commit suicide */
612 case KTRL('K'): return ('Q');
613 case KTRL('C'): return ('Q');
614 }
615
616 /* Default */
617 return (command);
618 }
619
620
621 /*
622 * React to new value of "rogue_like_commands".
623 *
624 * Initialize the "keymap" arrays based on the current value of
625 * "rogue_like_commands". Note that all "undefined" keypresses
626 * by default map to themselves with no direction. This allows
627 * "standard" commands to use the same keys in both keysets.
628 *
629 * To reset the keymap, simply set "rogue_like_commands" to -1,
630 * call this function, restore its value, call this function.
631 *
632 * The keymap arrays map keys to "command_cmd" and "command_dir".
633 *
634 * It is illegal for keymap_cmds[N] to be zero, except for
635 * keymaps_cmds[0], which is unused.
636 *
637 * You can map a key to "tab" to make it "non-functional".
638 */
639
640 void keymap_init(void)
641 {
642 int i, k;
643
644 /* Notice changes in the "rogue_like_commands" flag */
645 static char old_rogue_like = -1;
646
647 /* Hack -- notice changes in "rogue_like_commands" */
648 if (old_rogue_like == rogue_like_commands) return;
649
650 /* Initialize every entry */
651 for (i = 0; i < 128; i++)
652 {
653 /* Default to "no direction" */
654 hack_dir = 0;
655
656 /* Attempt to translate */
657 if (rogue_like_commands)
658 {
659 k = roguelike_commands(i);
660 }
661 else
662 {
663 k = original_commands(i);
664 }
665
666 /* Save the keypress */
667 keymap_cmds[i] = k;
668
669 /* Save the direction */
670 keymap_dirs[i] = hack_dir;
671 }
672
673 /* Save the "rogue_like_commands" setting */
674 old_rogue_like = rogue_like_commands;
675 }
676 #endif
677
678
679
680
681
682
683
684
685 /*
686 * Legal bit-flags for macro__use[X]
687 */
688 #define MACRO_USE_CMD 0x01 /* X triggers a command macro */
689 #define MACRO_USE_STD 0x02 /* X triggers a standard macro */
690
691 /*
692 * Fast check for trigger of any macros
693 */
694 static byte macro__use[256];
695
696
697
698 /*
699 * Hack -- add a macro definition (or redefinition).
700 *
701 * If "cmd_flag" is set then this macro is only active when
702 * the user is being asked for a command (see below).
703 */
macro_add(cptr pat,cptr act,bool cmd_flag)704 void macro_add(cptr pat, cptr act, bool cmd_flag)
705 {
706 int n;
707
708
709 /* Paranoia -- require data */
710 if (!pat || !act) return;
711
712
713 /* Look for a re-usable slot */
714 for (n = 0; n < macro__num; n++)
715 {
716 /* Notice macro redefinition */
717 if (streq(macro__pat[n], pat))
718 {
719 /* Free the old macro action */
720 string_free(macro__act[n]);
721
722 /* Save the macro action */
723 macro__act[n] = string_make(act);
724
725 /* Save the "cmd_flag" */
726 macro__cmd[n] = cmd_flag;
727
728 /* All done */
729 return;
730 }
731 }
732
733
734 /* Save the pattern */
735 macro__pat[macro__num] = string_make(pat);
736
737 /* Save the macro action */
738 macro__act[macro__num] = string_make(act);
739
740 /* Save the "cmd_flag" */
741 macro__cmd[macro__num] = cmd_flag;
742
743 /* One more macro */
744 macro__num++;
745
746
747 /* Hack -- Note the "trigger" char */
748 macro__use[(byte)(pat[0])] |= MACRO_USE_STD;
749
750 /* Hack -- Note the "trigger" char of command macros */
751 if (cmd_flag) macro__use[(byte)(pat[0])] |= MACRO_USE_CMD;
752 }
753
754
755
756 /*
757 * Check for possibly pending macros
758 */
759 #if 0
760 static int macro_maybe(cptr buf, int n)
761 {
762 int i;
763
764 /* Scan the macros */
765 for (i = n; i < macro__num; i++)
766 {
767 /* Skip inactive macros */
768 if (macro__cmd[i] && !inkey_flag) continue;
769
770 /* Check for "prefix" */
771 if (prefix(macro__pat[i], buf))
772 {
773 /* Ignore complete macros */
774 if (!streq(macro__pat[i], buf)) return (i);
775 }
776 }
777
778 /* No matches */
779 return (-1);
780 }
781 #endif
782
783
784 /*
785 * Find the longest completed macro
786 */
787 #if 0
788 static int macro_ready(cptr buf)
789 {
790 int i, t, n = -1, s = -1;
791
792 /* Scan the macros */
793 for (i = 0; i < macro__num; i++)
794 {
795 /* Skip inactive macros */
796 if (macro__cmd[i] && !inkey_flag) continue;
797
798 /* Check for "prefix" */
799 if (!prefix(buf, macro__pat[i])) continue;
800
801 /* Check the length of this entry */
802 t = strlen(macro__pat[i]);
803
804 /* Find the "longest" entry */
805 if ((n >= 0) && (s > t)) continue;
806
807 /* Track the entry */
808 n = i;
809 s = t;
810 }
811
812 /* Return the result */
813 return (n);
814 }
815 #endif
816
817
818
819 /*
820 * Local "need flush" variable
821 */
822 static bool flush_later = FALSE;
823
824
825 /*
826 * Local variable -- we just finished a macro action
827 */
828 /*static bool after_macro = FALSE;*/
829
830 /*
831 * Local variable -- we are inside a macro action
832 */
833 /*static bool parse_macro = FALSE;*/
834
835 /*
836 * Local variable -- we are inside a "control-underscore" sequence
837 */
838 /*static bool parse_under = FALSE;*/
839
840 /*
841 * Local variable -- we are inside a "control-backslash" sequence
842 */
843 /*static bool parse_slash = FALSE;*/
844
845 /*
846 * Local variable -- we are stripping symbols for a while
847 */
848 /*static bool strip_chars = FALSE;*/
849
850
851
852 /*
853 * Flush all input chars. Actually, remember the flush,
854 * and do a "special flush" before the next "inkey()".
855 *
856 * This is not only more efficient, but also necessary to make sure
857 * that various "inkey()" codes are not "lost" along the way.
858 */
flush(void)859 void flush(void)
860 {
861 /* Do it later */
862 flush_later = TRUE;
863 }
864
865
866 /*
867 * Flush the screen, make a noise
868 */
bell(void)869 void bell(void)
870 {
871 #if 0
872 /* Mega-Hack -- Flush the output */
873 Term_fresh();
874
875 /* Make a bell noise (if allowed) */
876 if (ring_bell) Term_xtra(TERM_XTRA_NOISE, 0);
877
878 /* Flush the input (later!) */
879 flush();
880 #endif
881 }
882
883
884 /*
885 * Mega-Hack -- Make a (relevant?) sound
886 */
sound(player_type * p_ptr,int val)887 void sound(player_type *p_ptr, int val)
888 {
889 /* Make a sound */
890 send_sound(p_ptr, val);
891 }
892
893
894
895
896 /*
897 * Helper function called only from "inkey()"
898 *
899 * This function does most of the "macro" processing.
900 *
901 * We use the "Term_key_push()" function to handle "failed" macros,
902 * as well as "extra" keys read in while choosing a macro, and the
903 * actual action for the macro.
904 *
905 * Embedded macros are illegal, although "clever" use of special
906 * control chars may bypass this restriction. Be very careful.
907 *
908 * The user only gets 500 (1+2+...+29+30) milliseconds for the macro.
909 *
910 * Note the annoying special processing to "correctly" handle the
911 * special "control-backslash" codes following a "control-underscore"
912 * macro sequence. See "main-x11.c" and "main-xaw.c" for details.
913 */
914 #if 0
915 static char inkey_aux(void)
916 {
917 int k = 0, n, p = 0, w = 0;
918
919 char ch;
920
921 cptr pat, act;
922
923 char buf[1024];
924
925
926 /* Wait for keypress */
927 (void)(Term_inkey(&ch, TRUE, TRUE));
928
929
930 /* End of internal macro */
931 if (ch == 29) parse_macro = FALSE;
932
933
934 /* Do not check "ascii 28" */
935 if (ch == 28) return (ch);
936
937 /* Do not check "ascii 29" */
938 if (ch == 29) return (ch);
939
940
941 /* Do not check macro actions */
942 if (parse_macro) return (ch);
943
944 /* Do not check "control-underscore" sequences */
945 if (parse_under) return (ch);
946
947 /* Do not check "control-backslash" sequences */
948 if (parse_slash) return (ch);
949
950
951 /* Efficiency -- Ignore impossible macros */
952 if (!macro__use[(byte)(ch)]) return (ch);
953
954 /* Efficiency -- Ignore inactive macros */
955 if (!inkey_flag && (macro__use[(byte)(ch)] == MACRO_USE_CMD)) return (ch);
956
957
958 /* Save the first key, advance */
959 buf[p++] = ch;
960 buf[p] = '\0';
961
962
963 /* Wait for a macro, or a timeout */
964 while (TRUE)
965 {
966 /* Check for possible macros */
967 k = macro_maybe(buf, k);
968
969 /* Nothing matches */
970 if (k < 0) break;
971
972 /* Check for (and remove) a pending key */
973 if (0 == Term_inkey(&ch, FALSE, TRUE))
974 {
975 /* Append the key */
976 buf[p++] = ch;
977 buf[p] = '\0';
978
979 /* Restart wait */
980 w = 0;
981 }
982
983 /* No key ready */
984 else
985 {
986 /* Increase "wait" */
987 w += 10;
988
989 /* Excessive delay */
990 if (w >= 100) break;
991
992 /* Delay */
993 Term_xtra(TERM_XTRA_DELAY, w);
994 }
995 }
996
997
998 /* Check for a successful macro */
999 k = macro_ready(buf);
1000
1001 /* No macro available */
1002 if (k < 0)
1003 {
1004 /* Push all the keys back on the queue */
1005 while (p > 0)
1006 {
1007 /* Push the key, notice over-flow */
1008 if (Term_key_push(buf[--p])) return (0);
1009 }
1010
1011 /* Wait for (and remove) a pending key */
1012 (void)Term_inkey(&ch, TRUE, TRUE);
1013
1014 /* Return the key */
1015 return (ch);
1016 }
1017
1018
1019 /* Access the macro pattern */
1020 pat = macro__pat[k];
1021
1022 /* Get the length of the pattern */
1023 n = strlen(pat);
1024
1025 /* Push the "extra" keys back on the queue */
1026 while (p > n)
1027 {
1028 /* Push the key, notice over-flow */
1029 if (Term_key_push(buf[--p])) return (0);
1030 }
1031
1032
1033 /* We are now inside a macro */
1034 parse_macro = TRUE;
1035
1036 /* Push the "macro complete" key */
1037 if (Term_key_push(29)) return (0);
1038
1039
1040 /* Access the macro action */
1041 act = macro__act[k];
1042
1043 /* Get the length of the action */
1044 n = strlen(act);
1045
1046 /* Push the macro "action" onto the key queue */
1047 while (n > 0)
1048 {
1049 /* Push the key, notice over-flow */
1050 if (Term_key_push(act[--n])) return (0);
1051 }
1052
1053
1054 /* Force "inkey()" to call us again */
1055 return (0);
1056 }
1057 #endif
1058
1059
1060
1061
1062 /*
1063 * Get a keypress from the user.
1064 *
1065 * This function recognizes a few "global parameters". These are variables
1066 * which, if set to TRUE before calling this function, will have an effect
1067 * on this function, and which are always reset to FALSE by this function
1068 * before this function returns. Thus they function just like normal
1069 * parameters, except that most calls to this function can ignore them.
1070 *
1071 * Normally, this function will process "macros", but if "inkey_base" is
1072 * TRUE, then we will bypass all "macro" processing. This allows direct
1073 * usage of the "Term_inkey()" function.
1074 *
1075 * Normally, this function will do something, but if "inkey_xtra" is TRUE,
1076 * then something else will happen.
1077 *
1078 * Normally, this function will wait until a "real" key is ready, but if
1079 * "inkey_scan" is TRUE, then we will return zero if no keys are ready.
1080 *
1081 * Normally, this function will show the cursor, and will process all normal
1082 * macros, but if "inkey_flag" is TRUE, then we will only show the cursor if
1083 * "hilite_player" is TRUE, and also, we will only process "command" macros.
1084 *
1085 * Note that the "flush()" function does not actually flush the input queue,
1086 * but waits until "inkey()" is called to perform the "flush".
1087 *
1088 * Refresh the screen if waiting for a keypress and no key is ready.
1089 *
1090 * Note that "back-quote" is automatically converted into "escape" for
1091 * convenience on machines with no "escape" key. This is done after the
1092 * macro matching, so the user can still make a macro for "backquote".
1093 *
1094 * Note the special handling of a few "special" control-keys, which
1095 * are reserved to simplify the use of various "main-xxx.c" files,
1096 * or used by the "macro" code above.
1097 *
1098 * Ascii 27 is "control left bracket" -- normal "Escape" key
1099 * Ascii 28 is "control backslash" -- special macro delimiter
1100 * Ascii 29 is "control right bracket" -- end of macro action
1101 * Ascii 30 is "control caret" -- indicates "keypad" key
1102 * Ascii 31 is "control underscore" -- begin macro-trigger
1103 *
1104 * Hack -- Make sure to allow calls to "inkey()" even if "term_screen"
1105 * is not the active Term, this allows the various "main-xxx.c" files
1106 * to only handle input when "term_screen" is "active".
1107 *
1108 * Note the nasty code used to process the "inkey_base" flag, which allows
1109 * various "macro triggers" to be entered as normal key-sequences, with the
1110 * appropriate timing constraints, but without actually matching against any
1111 * macro sequences. Most of the nastiness is to handle "ascii 28" (see below).
1112 *
1113 * The "ascii 28" code is a complete hack, used to allow "default actions"
1114 * to be associated with a given keypress, and used only by the X11 module,
1115 * it may or may not actually work. The theory is that a keypress can send
1116 * a special sequence, consisting of a "macro trigger" plus a "default action",
1117 * with the "default action" surrounded by "ascii 28" symbols. Then, when that
1118 * key is pressed, if the trigger matches any macro, the correct action will be
1119 * executed, and the "strip default action" code will remove the "default action"
1120 * from the keypress queue, while if it does not match, the trigger itself will
1121 * be stripped, and then the "ascii 28" symbols will be stripped as well, leaving
1122 * the "default action" keys in the "key queue". Again, this may not work.
1123 */
1124 #if 0
1125 char inkey(int Ind)
1126 {
1127 int v;
1128
1129 char kk, ch;
1130
1131 bool done = FALSE;
1132
1133 term *old = Term;
1134
1135 int w = 0;
1136
1137 int skipping = FALSE;
1138
1139
1140 /* Hack -- handle delayed "flush()" */
1141 if (flush_later)
1142 {
1143 /* Done */
1144 flush_later = FALSE;
1145
1146 /* Cancel "macro" info */
1147 parse_macro = after_macro = FALSE;
1148
1149 /* Cancel "sequence" info */
1150 parse_under = parse_slash = FALSE;
1151
1152 /* Cancel "strip" mode */
1153 strip_chars = FALSE;
1154
1155 /* Forget old keypresses */
1156 Term_flush();
1157 }
1158
1159
1160 /* Access cursor state */
1161 (void)Term_get_cursor(&v);
1162
1163 /* Show the cursor if waiting, except sometimes in "command" mode */
1164 if (!inkey_scan && (!inkey_flag || hilite_player))
1165 {
1166 /* Show the cursor */
1167 (void)Term_set_cursor(1);
1168 }
1169
1170
1171 /* Hack -- Activate the screen */
1172 Term_activate(term_screen);
1173
1174
1175 /* Get a (non-zero) keypress */
1176 for (ch = 0; !ch; )
1177 {
1178 /* Nothing ready, not waiting, and not doing "inkey_base" */
1179 if (!inkey_base && inkey_scan && (0 != Term_inkey(&ch, FALSE, FALSE))) break;
1180
1181
1182 /* Hack -- flush output once when no key ready */
1183 if (!done && (0 != Term_inkey(&ch, FALSE, FALSE)))
1184 {
1185 /* Hack -- activate proper term */
1186 Term_activate(old);
1187
1188 /* Flush output */
1189 Term_fresh();
1190
1191 /* Hack -- activate the screen */
1192 Term_activate(term_screen);
1193
1194 /* Mega-Hack -- reset saved flag */
1195 character_saved = FALSE;
1196
1197 /* Mega-Hack -- reset signal counter */
1198 signal_count = 0;
1199
1200 /* Only once */
1201 done = TRUE;
1202 }
1203
1204
1205 /* Hack */
1206 if (inkey_base)
1207 {
1208 char xh;
1209
1210 /* Check for keypress, optional wait */
1211 (void)Term_inkey(&xh, !inkey_scan, TRUE);
1212
1213 /* Key ready */
1214 if (xh)
1215 {
1216 /* Reset delay */
1217 w = 0;
1218
1219 /* Mega-Hack */
1220 if (xh == 28)
1221 {
1222 /* Toggle "skipping" */
1223 skipping = !skipping;
1224 }
1225
1226 /* Use normal keys */
1227 else if (!skipping)
1228 {
1229 /* Use it */
1230 ch = xh;
1231 }
1232 }
1233
1234 /* No key ready */
1235 else
1236 {
1237 /* Increase "wait" */
1238 w += 10;
1239
1240 /* Excessive delay */
1241 if (w >= 100) break;
1242
1243 /* Delay */
1244 Term_xtra(TERM_XTRA_DELAY, w);
1245 }
1246
1247 /* Continue */
1248 continue;
1249 }
1250
1251
1252 /* Get a key (see above) */
1253 kk = ch = inkey_aux();
1254
1255
1256 /* Finished a "control-underscore" sequence */
1257 if (parse_under && (ch <= 32))
1258 {
1259 /* Found the edge */
1260 parse_under = FALSE;
1261
1262 /* Stop stripping */
1263 strip_chars = FALSE;
1264
1265 /* Strip this key */
1266 ch = 0;
1267 }
1268
1269
1270 /* Finished a "control-backslash" sequence */
1271 if (parse_slash && (ch == 28))
1272 {
1273 /* Found the edge */
1274 parse_slash = FALSE;
1275
1276 /* Stop stripping */
1277 strip_chars = FALSE;
1278
1279 /* Strip this key */
1280 ch = 0;
1281 }
1282
1283
1284 /* Handle some special keys */
1285 switch (ch)
1286 {
1287 /* Hack -- convert back-quote into escape */
1288 case '`':
1289
1290 /* Convert to "Escape" */
1291 ch = ESCAPE;
1292
1293 /* Done */
1294 break;
1295
1296 /* Hack -- strip "control-right-bracket" end-of-macro-action */
1297 case 29:
1298
1299 /* Strip this key */
1300 ch = 0;
1301
1302 /* Done */
1303 break;
1304
1305 /* Hack -- strip "control-caret" special-keypad-indicator */
1306 case 30:
1307
1308 /* Strip this key */
1309 ch = 0;
1310
1311 /* Done */
1312 break;
1313
1314 /* Hack -- strip "control-underscore" special-macro-triggers */
1315 case 31:
1316
1317 /* Strip this key */
1318 ch = 0;
1319
1320 /* Inside a "underscore" sequence */
1321 parse_under = TRUE;
1322
1323 /* Strip chars (always) */
1324 strip_chars = TRUE;
1325
1326 /* Done */
1327 break;
1328
1329 /* Hack -- strip "control-backslash" special-fallback-strings */
1330 case 28:
1331
1332 /* Strip this key */
1333 ch = 0;
1334
1335 /* Inside a "control-backslash" sequence */
1336 parse_slash = TRUE;
1337
1338 /* Strip chars (sometimes) */
1339 strip_chars = after_macro;
1340
1341 /* Done */
1342 break;
1343 }
1344
1345
1346 /* Hack -- Set "after_macro" code */
1347 after_macro = ((kk == 29) ? TRUE : FALSE);
1348
1349
1350 /* Hack -- strip chars */
1351 if (strip_chars) ch = 0;
1352 }
1353
1354
1355 /* Hack -- restore the term */
1356 Term_activate(old);
1357
1358
1359 /* Restore the cursor */
1360 Term_set_cursor(v);
1361
1362
1363 /* Cancel the various "global parameters" */
1364 inkey_base = inkey_xtra = inkey_flag = inkey_scan = FALSE;
1365
1366
1367 /* Return the keypress */
1368 return (ch);
1369 }
1370 #endif
1371
1372
1373
1374
1375
1376 /*
1377 * We use a global array for all inscriptions to reduce the memory
1378 * spent maintaining inscriptions. Of course, it is still possible
1379 * to run out of inscription memory, especially if too many different
1380 * inscriptions are used, but hopefully this will be rare.
1381 *
1382 * We use dynamic string allocation because otherwise it is necessary
1383 * to pre-guess the amount of quark activity. We limit the total
1384 * number of quarks, but this is much easier to "expand" as needed.
1385 *
1386 * Any two items with the same inscription will have the same "quark"
1387 * index, which should greatly reduce the need for inscription space.
1388 *
1389 * Note that "quark zero" is NULL and should not be "dereferenced".
1390 */
1391
1392 /*
1393 * Add a new "quark" to the set of quarks.
1394 */
quark_add(cptr str)1395 s16b quark_add(cptr str)
1396 {
1397 int i;
1398
1399 /* Look for an existing quark */
1400 for (i = 1; i < quark__num; i++)
1401 {
1402 /* Check for equality */
1403 if (streq(quark__str[i], str)) return (i);
1404 }
1405
1406 /* Paranoia -- Require room */
1407 if (quark__num == QUARK_MAX) return (0);
1408
1409 /* New maximal quark */
1410 quark__num = i + 1;
1411
1412 /* Add a new quark */
1413 quark__str[i] = string_make(str);
1414
1415 /* Return the index */
1416 return (i);
1417 }
1418
1419
1420 /*
1421 * This function looks up a quark
1422 */
quark_str(s16b i)1423 cptr quark_str(s16b i)
1424 {
1425 cptr q;
1426
1427 /* Verify */
1428 if ((i < 0) || (i >= quark__num)) i = 0;
1429
1430 /* Access the quark */
1431 q = quark__str[i];
1432
1433 /* Return the quark */
1434 return (q);
1435 }
1436
1437
1438 #define end_of_segment(A) ((A) == ' ' || (A) == '!' || (A) == '@' || (A) == '^')
1439 /*
1440 * Parse item's inscriptons, extract "^abc" and "^a ^b ^c"
1441 * cases and cache them. (adapted from check_guard_inscription)
1442 */
fill_prevent_inscription(bool * arr,s16b quark)1443 void fill_prevent_inscription(bool *arr, s16b quark)
1444 {
1445 const char *ax;
1446
1447 /* Init quark */
1448 ax = quark_str(quark);
1449 if (ax == NULL) return;
1450
1451 /* Find start of segment */
1452 while((ax = strchr(ax, '^')) != NULL)
1453 {
1454 /* Parse segment */
1455 while(ax++ != NULL)
1456 {
1457 /* Reached end of quark, stop */
1458 if (*ax == 0) break;
1459
1460 /* Reached end of segment, stop */
1461 if (end_of_segment(*ax)) break;
1462
1463 /* Found a "Preventing Inscription" */
1464 arr[MIN(127,(byte)(*ax))] = TRUE;
1465 }
1466 }
1467 }
1468 /*
1469 * Refresh combined list of player's preventive inscriptons
1470 * after an update to his equipment was made.
1471 */
update_prevent_inscriptions(player_type * p_ptr)1472 void update_prevent_inscriptions(player_type *p_ptr)
1473 {
1474 object_type *o_ptr;
1475 int i;
1476
1477 /* Clear flags */
1478 for (i = 0; i < 128; i++)
1479 {
1480 p_ptr->prevents[i] = FALSE;
1481 }
1482
1483 /* Scan equipment */
1484 for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
1485 {
1486 o_ptr = &p_ptr->inventory[i];
1487
1488 /* Item exists and has inscription */
1489 if (o_ptr->tval && o_ptr->note)
1490 {
1491 /* Fill */
1492 fill_prevent_inscription(p_ptr->prevents, o_ptr->note);
1493 }
1494 }
1495 }
1496
1497 /*
1498 * Check to make sure they haven't inscribed an item against what
1499 * they are trying to do -Crimson
1500 * look for "!*Erm" type, and "!* !A !f" type.
1501 */
1502
check_guard_inscription(s16b quark,char what)1503 bool check_guard_inscription( s16b quark, char what ) {
1504 const char * ax;
1505 ax=quark_str(quark);
1506 if( ax == NULL ) { return FALSE; };
1507 while( (ax=strchr(ax,'!')) != NULL ) {
1508 while( ax++ != NULL ) {
1509 if (*ax==0) {
1510 return FALSE; /* end of quark, stop */
1511 }
1512 if (*ax==' ') {
1513 break; /* end of segment, stop */
1514 }
1515 if (*ax==what) {
1516 return TRUE; /* exact match, stop */
1517 }
1518 if(*ax =='*') {
1519 switch( what ) { /* check for paraniod tags */
1520 case '{': /* no inscribe */
1521 case '}': /* no unscribe */
1522 case 'g': case ',': /* no pickup! */
1523 /* ^ Owner must override those */
1524 /* Protect against loss: */
1525 case 'd': /* no drop */
1526 case 'k': /* no destroy */
1527 #if 0
1528 case 's': /* no sell */
1529 #endif
1530 case 'v': /* no thowing */
1531 case 'f': /* no firing */
1532 /* Protect against consumption: */
1533 case 'q': /* no quaff */
1534 case 'E': /* no eat */
1535 case 'r': /* no read */
1536 case 'a': case 'z': case 'u': /* no magic devices */
1537 return TRUE;
1538 };
1539 };
1540 };
1541 };
1542 return FALSE;
1543 };
1544
1545
1546
1547
1548 /*
1549 * Second try for the "message" handling routines.
1550 *
1551 * Each call to "message_add(s)" will add a new "most recent" message
1552 * to the "message recall list", using the contents of the string "s".
1553 *
1554 * The messages will be stored in such a way as to maximize "efficiency",
1555 * that is, we attempt to maximize the number of sequential messages that
1556 * can be retrieved, given a limited amount of storage space.
1557 *
1558 * We keep a buffer of chars to hold the "text" of the messages, not
1559 * necessarily in "order", and an array of offsets into that buffer,
1560 * representing the actual messages. This is made more complicated
1561 * by the fact that both the array of indexes, and the buffer itself,
1562 * are both treated as "circular arrays" for efficiency purposes, but
1563 * the strings may not be "broken" across the ends of the array.
1564 *
1565 * The "message_add()" function is rather "complex", because it must be
1566 * extremely efficient, both in space and time, for use with the Borg.
1567 */
1568
1569
1570
1571 /*
1572 * How many messages are "available"?
1573 */
message_num(void)1574 s16b message_num(void)
1575 {
1576 int last, next, n;
1577
1578 /* Extract the indexes */
1579 last = message__last;
1580 next = message__next;
1581
1582 /* Handle "wrap" */
1583 if (next < last) next += MESSAGE_MAX;
1584
1585 /* Extract the space */
1586 n = (next - last);
1587
1588 /* Return the result */
1589 return (n);
1590 }
1591
1592
1593
1594 /*
1595 * Recall the "text" of a saved message
1596 */
message_str(s16b age)1597 cptr message_str(s16b age)
1598 {
1599 s16b x;
1600 s16b o;
1601 cptr s;
1602
1603 /* Forgotten messages have no text */
1604 if ((age < 0) || (age >= message_num())) return ("");
1605
1606 /* Acquire the "logical" index */
1607 x = (message__next + MESSAGE_MAX - (age + 1)) % MESSAGE_MAX;
1608
1609 /* Get the "offset" for the message */
1610 o = message__ptr[x];
1611
1612 /* Access the message text */
1613 s = &message__buf[o];
1614
1615 /* Return the message text */
1616 return (s);
1617 }
1618
1619
1620
1621 /*
1622 * Add a new message, with great efficiency
1623 */
message_add(cptr str)1624 void message_add(cptr str)
1625 {
1626 #if 0
1627 int i, k, x, n;
1628
1629
1630 /*** Step 1 -- Analyze the message ***/
1631
1632 /* Hack -- Ignore "non-messages" */
1633 if (!str) return;
1634
1635 /* Message length */
1636 n = strlen(str);
1637
1638 /* Important Hack -- Ignore "long" messages */
1639 if (n >= MESSAGE_BUF / 4) return;
1640
1641
1642 /*** Step 2 -- Attempt to optimize ***/
1643
1644 /* Limit number of messages to check */
1645 k = message_num() / 4;
1646
1647 /* Limit number of messages to check */
1648 if (k > MESSAGE_MAX / 32) k = MESSAGE_MAX / 32;
1649
1650 /* Check the last few messages (if any to count) */
1651 for (i = message__next; k; k--)
1652 {
1653 u16b q;
1654
1655 cptr old;
1656
1657 /* Back up and wrap if needed */
1658 if (i-- == 0) i = MESSAGE_MAX - 1;
1659
1660 /* Stop before oldest message */
1661 if (i == message__last) break;
1662
1663 /* Extract "distance" from "head" */
1664 q = (message__head + MESSAGE_BUF - message__ptr[i]) % MESSAGE_BUF;
1665
1666 /* Do not optimize over large distance */
1667 if (q > MESSAGE_BUF / 2) continue;
1668
1669 /* Access the old string */
1670 old = &message__buf[message__ptr[i]];
1671
1672 /* Compare */
1673 if (!streq(old, str)) continue;
1674
1675 /* Get the next message index, advance */
1676 x = message__next++;
1677
1678 /* Handle wrap */
1679 if (message__next == MESSAGE_MAX) message__next = 0;
1680
1681 /* Kill last message if needed */
1682 if (message__next == message__last) message__last++;
1683
1684 /* Handle wrap */
1685 if (message__last == MESSAGE_MAX) message__last = 0;
1686
1687 /* Assign the starting address */
1688 message__ptr[x] = message__ptr[i];
1689
1690 /* Success */
1691 return;
1692 }
1693
1694
1695 /*** Step 3 -- Ensure space before end of buffer ***/
1696
1697 /* Kill messages and Wrap if needed */
1698 if (message__head + n + 1 >= MESSAGE_BUF)
1699 {
1700 /* Kill all "dead" messages */
1701 for (i = message__last; TRUE; i++)
1702 {
1703 /* Wrap if needed */
1704 if (i == MESSAGE_MAX) i = 0;
1705
1706 /* Stop before the new message */
1707 if (i == message__next) break;
1708
1709 /* Kill "dead" messages */
1710 if (message__ptr[i] >= message__head)
1711 {
1712 /* Track oldest message */
1713 message__last = i + 1;
1714 }
1715 }
1716
1717 /* Wrap "tail" if needed */
1718 if (message__tail >= message__head) message__tail = 0;
1719
1720 /* Start over */
1721 message__head = 0;
1722 }
1723
1724
1725 /*** Step 4 -- Ensure space before next message ***/
1726
1727 /* Kill messages if needed */
1728 if (message__head + n + 1 > message__tail)
1729 {
1730 /* Grab new "tail" */
1731 message__tail = message__head + n + 1;
1732
1733 /* Advance tail while possible past first "nul" */
1734 while (message__buf[message__tail-1]) message__tail++;
1735
1736 /* Kill all "dead" messages */
1737 for (i = message__last; TRUE; i++)
1738 {
1739 /* Wrap if needed */
1740 if (i == MESSAGE_MAX) i = 0;
1741
1742 /* Stop before the new message */
1743 if (i == message__next) break;
1744
1745 /* Kill "dead" messages */
1746 if ((message__ptr[i] >= message__head) &&
1747 (message__ptr[i] < message__tail))
1748 {
1749 /* Track oldest message */
1750 message__last = i + 1;
1751 }
1752 }
1753 }
1754
1755
1756 /*** Step 5 -- Grab a new message index ***/
1757
1758 /* Get the next message index, advance */
1759 x = message__next++;
1760
1761 /* Handle wrap */
1762 if (message__next == MESSAGE_MAX) message__next = 0;
1763
1764 /* Kill last message if needed */
1765 if (message__next == message__last) message__last++;
1766
1767 /* Handle wrap */
1768 if (message__last == MESSAGE_MAX) message__last = 0;
1769
1770
1771
1772 /*** Step 6 -- Insert the message text ***/
1773
1774 /* Assign the starting address */
1775 message__ptr[x] = message__head;
1776
1777 /* Append the new part of the message */
1778 for (i = 0; i < n; i++)
1779 {
1780 /* Copy the message */
1781 message__buf[message__head + i] = str[i];
1782 }
1783
1784 /* Terminate */
1785 message__buf[message__head + i] = '\0';
1786
1787 /* Advance the "head" pointer */
1788 message__head += n + 1;
1789 #endif
1790 }
1791
1792
1793
1794 /*
1795 * Hack -- flush
1796 */
1797 #if 0
1798 static void msg_flush(int x)
1799 {
1800 byte a = TERM_L_BLUE;
1801
1802 /* Hack -- fake monochrome */
1803 if (!use_color) a = TERM_WHITE;
1804
1805 /* Pause for response */
1806 Term_putstr(x, 0, -1, a, "-more-");
1807
1808 /* Get an acceptable keypress */
1809 while (1)
1810 {
1811 int cmd = inkey();
1812 if (quick_messages) break;
1813 if ((cmd == ESCAPE) || (cmd == ' ')) break;
1814 if ((cmd == '\n') || (cmd == '\r')) break;
1815 bell();
1816 }
1817
1818 /* Clear the line */
1819 Term_erase(0, 0, 255);
1820 }
1821 #endif
1822
1823
1824 /*
1825 * Output a message to the top line of the screen.
1826 *
1827 * Break long messages into multiple pieces (40-72 chars).
1828 *
1829 * Allow multiple short messages to "share" the top line.
1830 *
1831 * Prompt the user to make sure he has a chance to read them.
1832 *
1833 * These messages are memorized for later reference (see above).
1834 *
1835 * We could do "Term_fresh()" to provide "flicker" if needed.
1836 *
1837 * The global "msg_flag" variable can be cleared to tell us to
1838 * "erase" any "pending" messages still on the screen.
1839 *
1840 * XXX XXX XXX Note that we must be very careful about using the
1841 * "msg_print()" functions without explicitly calling the special
1842 * "msg_print(NULL)" function, since this may result in the loss
1843 * of information if the screen is cleared, or if anything is
1844 * displayed on the top line.
1845 *
1846 * XXX XXX XXX Note that "msg_print(NULL)" will clear the top line
1847 * even if no messages are pending. This is probably a hack.
1848 */
msg_print(player_type * p_ptr,cptr msg)1849 void msg_print(player_type *p_ptr, cptr msg)
1850 {
1851 msg_print_aux(p_ptr, msg, MSG_GENERIC);
1852 }
msg_print_aux(player_type * p_ptr,cptr msg,u16b type)1853 void msg_print_aux(player_type *p_ptr, cptr msg, u16b type)
1854 {
1855 bool log = TRUE;
1856 bool add = FALSE;
1857 bool dup = FALSE;
1858 char multiplier[12];
1859 s16b ptr;
1860
1861 /* We don't need to log *everything* */
1862 if(type > MSG_CHAT || (msg && strchr("[",*msg)))
1863 {
1864 log = FALSE;
1865 }
1866
1867 /* Log messages for each player, so we can dump last messages
1868 * in server-side character dumps */
1869 if(msg && p_ptr && log)
1870 {
1871 add = TRUE;
1872 /* Ensure we know where the last message is */
1873 ptr = p_ptr->msg_hist_ptr - 1;
1874 if(ptr < 0) ptr = MAX_MSG_HIST-1;
1875 /* If this message is already in the buffer, count it as a dupe */
1876 if(!strcmp(p_ptr->msg_log[ptr],msg))
1877 {
1878 p_ptr->msg_hist_dupe++;
1879 /* And don't add another copy to the buffer */
1880 add = FALSE;
1881 dup = TRUE;
1882 }
1883 /* This message is the end of a series of dupes */
1884 else if(p_ptr->msg_hist_dupe > 0)
1885 {
1886 /* Add the dupe counter to the end of the last message */
1887 sprintf(multiplier," (x%d)",p_ptr->msg_hist_dupe+1);
1888 strcat(p_ptr->msg_log[ptr],multiplier);
1889 p_ptr->msg_hist_dupe = 0;
1890 }
1891 if(add)
1892 {
1893 /* Standard, unique (for the moment) message */
1894 strncpy(p_ptr->msg_log[p_ptr->msg_hist_ptr], msg, 78);
1895 p_ptr->msg_log[p_ptr->msg_hist_ptr++][78] = '\0';
1896 }
1897 /* Maintain a circular buffer */
1898 if(p_ptr->msg_hist_ptr == MAX_MSG_HIST)
1899 p_ptr->msg_hist_ptr = 0;
1900 plog_fmt("%s: %s", p_ptr->name, msg);
1901 }
1902 else if(msg && log)
1903 {
1904 plog_fmt("%d: %s", 0, msg);
1905 }
1906
1907 /* Hack -- repeated message of the same type */
1908 if (dup && type == p_ptr->msg_last_type)
1909 {
1910 send_message_repeat(p_ptr, type);
1911 return;
1912 }
1913
1914 /* Remember last type sent */
1915 p_ptr->msg_last_type = type;
1916
1917 /* Ahh, the beautiful simplicity of it.... --KLJ-- */
1918 send_message(p_ptr, msg, type);
1919 }
1920
msg_broadcast(player_type * p_ptr,cptr msg)1921 void msg_broadcast(player_type *p_ptr, cptr msg)
1922 {
1923 int i;
1924
1925 /* Tell every player */
1926 for (i = 1; i <= NumPlayers; i++)
1927 {
1928 /* Skip the specified player */
1929 if (same_player(Players[i], p_ptr)) continue;
1930 printf("Broadcasting: %s\n", msg);
1931 /* Tell this one */
1932 msg_print_aux(Players[i], msg, MSG_CHAT);
1933 }
1934
1935 /* Send to console */
1936 console_print((char*)msg, 0);
1937 }
1938
msg_channel(int chan,cptr msg)1939 void msg_channel(int chan, cptr msg)
1940 {
1941 int i;
1942 /* Log to file */
1943 if (channels[chan].mode & CM_PLOG)
1944 {
1945 plog(msg);
1946 }
1947 /* Tell every player */
1948 for (i = 1; i <= NumPlayers; i++)
1949 {
1950 if (Players[i]->on_channel[chan] & UCM_EAR)
1951 msg_print_aux(Players[i], msg, MSG_CHAT + chan);
1952 }
1953 /* And every console */
1954 console_print((char*)msg, chan);
1955 }
1956
1957
1958 /*
1959 * Display a formatted message, using "vstrnfmt()" and "msg_print()".
1960 */
msg_format(player_type * p_ptr,cptr fmt,...)1961 void msg_format(player_type *p_ptr, cptr fmt, ...)
1962 {
1963 va_list vp;
1964
1965 char buf[1024];
1966
1967 /* Begin the Varargs Stuff */
1968 va_start(vp, fmt);
1969
1970 /* Format the args, save the length */
1971 (void)vstrnfmt(buf, 1024, fmt, vp);
1972
1973 /* End the Varargs Stuff */
1974 va_end(vp);
1975
1976 /* Display */
1977 msg_print(p_ptr, buf);
1978 }
1979 /* Dirty hack */
msg_format_type(player_type * p_ptr,u16b type,cptr fmt,...)1980 void msg_format_type(player_type *p_ptr, u16b type, cptr fmt, ...)
1981 {
1982 va_list vp;
1983
1984 char buf[1024];
1985
1986 /* Begin the Varargs Stuff */
1987 va_start(vp, fmt);
1988
1989 /* Format the args, save the length */
1990 (void)vstrnfmt(buf, 1024, fmt, vp);
1991
1992 /* End the Varargs Stuff */
1993 va_end(vp);
1994
1995 /* Display */
1996 msg_print_aux(p_ptr, buf, type);
1997 }
1998
1999
2000 /*
2001 * Display a message originated by a monster "m_idx".
2002 * Everyone nearby should see it.
2003 * If "q_ptr" is not NULL, this player will be skipped.
2004 *
2005 * Provide 2 messages, "fmt_vis" and "fmt_inv", one for players who can
2006 * see this monster, and one for players who can't (but are still nearby).
2007 * For example, "%%s picks %s's pocket!" and "%%s makes some grunting noises."
2008 * -> "a kobold picks Player's pocket", "something makes some grunting noises."
2009 *
2010 * "fmt_inv" can be NULL, in which case no message will be displayed for
2011 * players who don't see this monster (even though they are very close).
2012 *
2013 * Note, that the fomrat strings are expected to have "%%s" in them, which
2014 * will be substitued with monster's name (or "it" or "something").
2015 */
msg_format_monster(int m_idx,player_type * q_ptr,u16b type,cptr fmt_vis,cptr fmt_inv,...)2016 void msg_format_monster(int m_idx, player_type *q_ptr, u16b type, cptr fmt_vis, cptr fmt_inv, ...)
2017 {
2018 va_list vp;
2019
2020 int Depth, y, x, i;
2021
2022 char m_name_vis[80];
2023 char m_name_invis[80];
2024
2025 char buf[1024];
2026 char buf_vis[1024];
2027 char buf_invis[1024];
2028
2029 monster_type *m_ptr = &m_list[m_idx];
2030
2031 /* Obtain monster name(s) */
2032 monster_desc(NULL, m_name_vis, m_idx, 0x80); /* mode 0x80 - Forced visible */
2033 monster_desc(NULL, m_name_invis, m_idx, 0x40); /* mode 0x40 - Forced hidden */
2034
2035 /* Prepare the "normal" message" (Varargs Stuff) */
2036 va_start(vp, fmt_inv);
2037 (void)vstrnfmt(buf, 1024, fmt_vis, vp);
2038 (void)strnfmt(buf_vis, 1024, buf, m_name_vis);
2039 va_end(vp);
2040
2041 /* Is there a secondary message? */
2042 if (fmt_inv)
2043 {
2044 /* Prepare the "invisible" message" (Varargs Stuff) */
2045 va_start(vp, fmt_inv);
2046 (void)vstrnfmt(buf, 1024, fmt_inv, vp);
2047 (void)strnfmt(buf_invis, 1024, buf, m_name_invis, buf);
2048 va_end(vp);
2049 }
2050
2051 /* Extract monster's location */
2052 Depth = m_ptr->dun_depth;
2053 y = m_ptr->fy;
2054 x = m_ptr->fx;
2055
2056 /* Check each player */
2057 for (i = 1; i <= NumPlayers; i++)
2058 {
2059 /* Check this player */
2060 player_type *qq_ptr = Players[i];
2061
2062 /* Don't send the message to the ignoree */
2063 if (same_player(qq_ptr, q_ptr)) continue;
2064
2065 /* Make sure this player is at this depth */
2066 if (qq_ptr->dun_depth != Depth) continue;
2067
2068 /* Is the player near? (we also check if monster considers him near)*/
2069 if (!player_has_los_bold(qq_ptr, y, x) &&
2070 !(m_ptr->closest_player == qq_ptr->Ind)) continue;
2071
2072 /* Can he see this monster? */
2073 if (qq_ptr->mon_vis[m_idx])
2074 {
2075 /* Send "normal" message */
2076 msg_print_aux(qq_ptr, buf_vis, type);
2077 }
2078 else if (fmt_inv)
2079 {
2080 /* Send "invisible" message */
2081 msg_print_aux(qq_ptr, buf_invis, type);
2082 }
2083 }
2084 }
2085
2086
2087 /*
2088 * Display a message to everyone who is on the same dungeon level.
2089 *
2090 * This serves two functions: a dungeon level-wide chat, and a way
2091 * to attract attention of other nearby players.
2092 */
msg_format_complex_far(player_type * p_ptr,player_type * q_ptr,u16b type,cptr fmt,cptr sender,...)2093 void msg_format_complex_far(player_type *p_ptr, player_type *q_ptr, u16b type, cptr fmt, cptr sender, ...)
2094 {
2095 va_list vp;
2096
2097 int Depth, y, x, i;
2098
2099 char buf[1024];
2100 char buf_vis[1024];
2101 char buf_invis[1024];
2102
2103 /* Begin the Varargs Stuff */
2104 va_start(vp, sender);
2105
2106 /* Format the args, save the length */
2107 (void)vstrnfmt(buf, 1024, fmt, vp);
2108 (void)strnfmt(buf_vis, 1024, "%s %s", sender, buf);
2109 (void)strnfmt(buf_invis, 1024, "%s %s", "Someone", buf);
2110
2111 /* End the Varargs Stuff */
2112 va_end(vp);
2113
2114 /* Extract player's location */
2115 Depth = p_ptr->dun_depth;
2116 y = p_ptr->py;
2117 x = p_ptr->px;
2118
2119 /* Check each player */
2120 for (i = 1; i <= NumPlayers; i++)
2121 {
2122 /* Check this player */
2123 player_type *qq_ptr = Players[i];
2124
2125 /* Don't send the message to the player who caused it */
2126 if (same_player(qq_ptr, p_ptr)) continue;
2127
2128 /* Don't send the message to the second ignoree */
2129 if (same_player(qq_ptr, q_ptr)) continue;
2130
2131 /* Make sure this player is at this depth */
2132 if (qq_ptr->dun_depth != Depth) continue;
2133
2134 /* Can he see this player? */
2135 if (qq_ptr->cave_flag[y][x] & CAVE_VIEW)
2136 {
2137 /* Send the message */
2138 msg_print_aux(qq_ptr, buf_vis, type);
2139 /* Disturb player */
2140 disturb(qq_ptr, 0, 0);
2141 }
2142 else
2143 {
2144 /* Send "invisible" message (e.g. "Someone yells") */
2145 msg_print_aux(qq_ptr, buf_invis, type);
2146 }
2147 }
2148 }
2149
2150
2151 /*
2152 * Display a message to everyone who is in sight of another player.
2153 *
2154 * This is mainly used to keep other players advised of actions done
2155 * by a player. The message is not sent to the player who performed
2156 * the action.
2157 */
msg_print_complex_near(player_type * p_ptr,player_type * q_ptr,u16b type,cptr msg)2158 void msg_print_complex_near(player_type *p_ptr, player_type *q_ptr, u16b type, cptr msg)
2159 {
2160 int Depth, y, x, i;
2161
2162 /* Extract player's location */
2163 Depth = p_ptr->dun_depth;
2164 y = p_ptr->py;
2165 x = p_ptr->px;
2166
2167 /* Check each player */
2168 for (i = 1; i <= NumPlayers; i++)
2169 {
2170 /* Check this player */
2171 player_type *qq_ptr = Players[i];
2172
2173 /* Don't send the message to the player who caused it */
2174 if (same_player(qq_ptr, p_ptr)) continue;
2175
2176 /* Don't send the message to the second ignoree */
2177 if (same_player(qq_ptr, q_ptr)) continue;
2178
2179 /* Make sure this player is at this depth */
2180 if (qq_ptr->dun_depth != Depth) continue;
2181
2182 /* Can he see this player? */
2183 if (qq_ptr->cave_flag[y][x] & CAVE_VIEW)
2184 {
2185 /* Send the message */
2186 msg_print_aux(qq_ptr, msg, type);
2187 }
2188 }
2189 }
msg_print_near(player_type * p_ptr,cptr msg)2190 void msg_print_near(player_type *p_ptr, cptr msg)
2191 {
2192 msg_print_complex_near(p_ptr, p_ptr, MSG_GENERIC, msg);
2193 }
2194
2195
2196 /*
2197 * Same as above, except send a formatted message.
2198 */
msg_format_complex_near(player_type * p_ptr,player_type * q_ptr,u16b type,cptr fmt,...)2199 void msg_format_complex_near(player_type *p_ptr, player_type *q_ptr, u16b type, cptr fmt, ...)
2200 {
2201 va_list vp;
2202
2203 char buf[1024];
2204
2205 /* Begin the Varargs Stuff */
2206 va_start(vp, fmt);
2207
2208 /* Format the args, save the length */
2209 (void)vstrnfmt(buf, 1024, fmt, vp);
2210
2211 /* End the Varargs Stuff */
2212 va_end(vp);
2213
2214 /* Display */
2215 msg_print_complex_near(p_ptr, q_ptr, type, buf);
2216 }
msg_format_near(player_type * p_ptr,cptr fmt,...)2217 void msg_format_near(player_type *p_ptr, cptr fmt, ...)
2218 {
2219 va_list vp;
2220
2221 char buf[1024];
2222
2223 /* Begin the Varargs Stuff */
2224 va_start(vp, fmt);
2225
2226 /* Format the args, save the length */
2227 (void)vstrnfmt(buf, 1024, fmt, vp);
2228
2229 /* End the Varargs Stuff */
2230 va_end(vp);
2231
2232 /* Display */
2233 msg_print_near(p_ptr, buf);
2234 }
2235
2236
2237 /* Analyze the 'search' string and determine if it has any special
2238 * target.
2239 * Returns 0 - on error, and an error string is put into 'error'
2240 * > 0 - player index
2241 * < 0 - party index
2242 */
2243
2244 #define VIRTUAL_CHANNELS 8
2245 cptr virt_channels[VIRTUAL_CHANNELS] = { NULL, "&say", "&yell", NULL };
find_chat_target(cptr search,char * error)2246 int find_chat_target(cptr search, char *error)
2247 {
2248 int i, j, len, target = 0;
2249 cptr problem = "";
2250 player_type *q_ptr;
2251 bool party_trap = FALSE;
2252 bool channel_trap = FALSE;
2253
2254 /* Acquire length of search string */
2255 len = strlen(search);
2256
2257 /* Virtual channels ? */
2258 if (len && search[0] == '&')
2259 {
2260 channel_trap = TRUE;
2261
2262 /* Find one */
2263 for (i = 1; i < VIRTUAL_CHANNELS; i++)
2264 {
2265 /* Done */
2266 if (!virt_channels[i]) break;
2267
2268 /* Compare names */
2269 if (!my_strnicmp(virt_channels[i], search, len))
2270 {
2271 /* Set target if not set already or an exact match */
2272 if ((!target) || (len == strlen(virt_channels[i])))
2273 {
2274 target = i;
2275 problem = "";
2276 }
2277 else
2278 {
2279 /* Matching too many */
2280 /* Make sure we don't already have an exact match */
2281 if (len != strlen(parties[0 - target].name))
2282 problem = "channels";
2283 }
2284 break;
2285 }
2286 }
2287 }
2288
2289 /* Look for a recipient who matches the search string */
2290 if (len && !channel_trap)
2291 {
2292 /* Check for party hinter */
2293 if (search[0] == '^')
2294 {
2295 party_trap = TRUE;
2296 search = search + 1;
2297 }
2298
2299 /* First check parties */
2300 for (i = 1; i < MAX_PARTIES; i++)
2301 {
2302 /* Skip if empty */
2303 if (!parties[i].num) continue;
2304
2305 /* Check name */
2306 if (!my_strnicmp(parties[i].name, search, len))
2307 {
2308 /* Make sure one of the party members is actually
2309 * logged on. */
2310 for (j = 1; j <= NumPlayers; j++)
2311 {
2312 if (Players[j]->conn == NOT_CONNECTED)
2313 continue;
2314
2315 /* Check this guy */
2316 if (player_in_party(i, Players[j]))
2317 {
2318 /* Set target if not set already or an exact match */
2319 if ((!target) || (len == strlen(parties[i].name)))
2320 {
2321 target = 0 - i;
2322 problem = "";
2323 }
2324 else
2325 {
2326 /* Matching too many parties */
2327 /* Make sure we don't already have an exact match */
2328 if (len != strlen(parties[0 - target].name))
2329 problem = "parties";
2330 }
2331 break;
2332 }
2333 }
2334 }
2335 }
2336
2337 /* Was hinting at party, Ignore players */
2338 if (!party_trap) {
2339 /* Then check players */
2340 for (i = 1; i <= NumPlayers; i++)
2341 {
2342 /* Check this one */
2343 q_ptr = Players[i];
2344
2345 /* Skip DM */
2346 if (q_ptr->dm_flags & DM_SECRET_PRESENCE) continue;
2347
2348 /* Check name */
2349 if (!my_strnicmp(q_ptr->name, search, len))
2350 {
2351 /* Set target if not set already or an exact match */
2352 if ((!target) || (len == strlen(q_ptr->name)))
2353 {
2354 target = i;
2355 problem = "";
2356 }
2357 /* Matching too many people */
2358 else if (target > 0)
2359 {
2360 /* Make sure we don't already have an exact match */
2361 if (len != strlen(Players[target]->name))
2362 problem = "players";
2363 }
2364 else problem = "players or parties";
2365 }
2366 }
2367 /* End party hinter */ }
2368 }
2369
2370 /* Check for recipient set but no match found */
2371 if (len && !target)
2372 {
2373 /* Prepare an error message */
2374 sprintf(error, "Could not match name '%s'.", search);
2375
2376 /* Give up */
2377 return 0;
2378 }
2379
2380 /* Check for multiple recipients found */
2381 if (!STRZERO(problem))
2382 {
2383 /* Send an error message */
2384 sprintf(error, "'%s' matches too many %s.", search, problem);
2385
2386 /* Give up */
2387 return 0;
2388 }
2389
2390 /* Hack -- pack player targets and virtual channels together */
2391 if (target > 0 && !channel_trap) target += VIRTUAL_CHANNELS;
2392
2393 return target;
2394 }
2395
2396 /* Instruct client to listen on a specific channel for an incoming message. */
assist_whisper(player_type * p_ptr,cptr search)2397 void assist_whisper(player_type *p_ptr, cptr search)
2398 {
2399 int target;
2400 char error[80];
2401
2402 target = find_chat_target(search, error);
2403
2404 /* No match */
2405 if (!target)
2406 {
2407 /* Relay error */
2408 msg_print(p_ptr, error);
2409
2410 /* Give up */
2411 return;
2412 }
2413
2414 /* All 'virtual channels' occupy the MAX_CHANNELS slot,
2415 * while all real channels are < MAX_CHANNELS. */
2416
2417 /* Virtual channel -- what he sent */
2418 else if (target > 0 && target < VIRTUAL_CHANNELS)
2419 {
2420 send_channel(p_ptr, CHAN_SELECT, MAX_CHANNELS, virt_channels[target]);
2421 }
2422 /* A Player */
2423 else if (target > 0)
2424 {
2425 send_channel(p_ptr, CHAN_SELECT, MAX_CHANNELS, Players[target - VIRTUAL_CHANNELS]->name);
2426 }
2427 /* A Party */
2428 else if (target < 0)
2429 {
2430 send_channel(p_ptr, CHAN_SELECT, MAX_CHANNELS, parties[0 - target].name);
2431 }
2432 }
2433
channel_join(player_type * p_ptr,cptr channel,bool quiet)2434 void channel_join(player_type *p_ptr, cptr channel, bool quiet)
2435 {
2436 int i, last_free = 0;
2437
2438 /* Find channel */
2439 for (i = 0; i < MAX_CHANNELS; i++)
2440 {
2441 if (!last_free && STRZERO(channels[i].name)) last_free = i;
2442
2443 /* Name match */
2444 if (!strcmp(channels[i].name, channel))
2445 {
2446 /* Not present on this channel */
2447 if (!on_channel(p_ptr, i))
2448 {
2449 /* Hack -- can't join due to modes? */
2450 if (((channels[i].mode & CM_KEYLOCK) && !is_dm_p(p_ptr)) ||
2451 (p_ptr->on_channel[i] & UCM_BAN) )
2452 {
2453 /* Hack -- route to "unable to join" message */
2454 last_free = 0;
2455 break;
2456 }
2457 /* Enter channel */
2458 channels[i].num++;
2459 p_ptr->on_channel[i] |= UCM_EAR;
2460 send_channel(p_ptr, CHAN_JOIN, i, channel);
2461 if (!quiet) msg_format(p_ptr,"Listening to channel %s",channel);
2462 }
2463 /* Select channel */
2464 else
2465 {
2466 p_ptr->main_channel = i;
2467 send_channel(p_ptr, CHAN_SELECT, i, channel);
2468 if (!quiet) msg_format(p_ptr,"Channel changed to %s",channel);
2469 }
2470 return;
2471 }
2472 }
2473
2474 /* No such channel */
2475
2476 /* We have free space */
2477 if (last_free)
2478 {
2479 /* Create channel */
2480 my_strcpy(channels[last_free].name, channel, MAX_CHARS);
2481 channels[last_free].num = 1;
2482 p_ptr->on_channel[last_free] |= (UCM_EAR | UCM_OPER);
2483 send_channel(p_ptr, CHAN_JOIN, last_free, channel);
2484 if (!quiet) msg_format(p_ptr, "Listening to channel %s", channel);
2485 }
2486 /* All channel slots are used up */
2487 else
2488 {
2489 if (!quiet) msg_format(p_ptr, "Unable to join channel %s", channel);
2490 }
2491 }
2492 /* Actual code for leaving channels */
channel_leave_id(player_type * p_ptr,int i,bool quiet)2493 void channel_leave_id(player_type *p_ptr, int i, bool quiet)
2494 {
2495 if (!i || !(p_ptr->on_channel[i] & UCM_EAR)) return;
2496
2497 channels[i].num--;
2498 if (!quiet) msg_format(p_ptr, "Left channel %s", channels[i].name);
2499 if (channels[i].num <= 0 && !(channels[i].mode & CM_SERVICE))
2500 {
2501 channels[i].name[0] = '\0';
2502 channels[i].id = 0;
2503 }
2504 if (p_ptr->main_channel == i)
2505 {
2506 p_ptr->main_channel = 0;
2507 }
2508 p_ptr->on_channel[i] &= ~(UCM_LEAVE);
2509 if (!quiet)
2510 send_channel(p_ptr, CHAN_LEAVE, i, "");
2511 }
2512 /* Find channel by name and leave it */
channel_leave(player_type * p_ptr,cptr channel)2513 void channel_leave(player_type *p_ptr, cptr channel)
2514 {
2515 int i;
2516 for (i = 0; i < MAX_CHANNELS; i++)
2517 {
2518 if (!strcmp(channels[i].name, channel))
2519 {
2520 channel_leave_id(p_ptr, i, FALSE);
2521 break;
2522 }
2523 }
2524 }
2525 /* Leave all channels */
channels_leave(player_type * p_ptr)2526 void channels_leave(player_type *p_ptr)
2527 {
2528 int i;
2529 for (i = 0; i < MAX_CHANNELS; i++)
2530 {
2531 if (p_ptr->on_channel[i] & UCM_EAR)
2532 {
2533 channel_leave_id(p_ptr, i, TRUE);
2534 }
2535 }
2536 }
2537
2538
2539 /*
2540 * A message prefixed by a player name is sent only to that player.
2541 * Otherwise, it is sent to everyone.
2542 */
player_talk_aux(player_type * p_ptr,cptr message)2543 void player_talk_aux(player_type *p_ptr, cptr message)
2544 {
2545 int i, target = 0;
2546 char search[80], sender[80], error[80], tmp_chan[MAX_CHAN_LEN];
2547 int dest_chan = 0; //#public
2548 player_type *q_ptr;
2549 cptr colon, chan_prefix;
2550 bool msg_off = FALSE;
2551
2552 /* Get sender's name */
2553 if (p_ptr)
2554 {
2555 /* Get player name */
2556 my_strcpy(sender, p_ptr->name, 80);
2557 }
2558 else
2559 {
2560 /* Default name */
2561 my_strcpy(sender, "", 80);
2562 }
2563
2564 /* Default to no search string */
2565 strcpy(search, "");
2566
2567 /* Default to #public channel if not originated by a player */
2568 dest_chan = 0;
2569
2570 if (p_ptr)
2571 {
2572 /* Default to the senders main channel */
2573 dest_chan = p_ptr->main_channel;
2574 /* Set search string from senders secondary channel */
2575 strcpy(search, p_ptr->second_channel);
2576 }
2577
2578 /* Is the message destined for a particular channel? */
2579 if(strchr("#", *message))
2580 {
2581 /* Yes, examine in more detail */
2582 chan_prefix = strchr(message,' ');
2583 if(!chan_prefix && strlen(message) < MAX_CHAN_LEN)
2584 {
2585 /* Channel name only? Change the players default channel */
2586 if (p_ptr)
2587 {
2588 strncpy(tmp_chan,message,MAX_CHAN_LEN);
2589 channel_join(p_ptr, tmp_chan, FALSE);
2590 return;
2591 }
2592 }
2593 else if(!chan_prefix || chan_prefix-message >= MAX_CHAN_LEN)
2594 {
2595 /* Invalid channel prefix? Forget about the channel. */
2596 }
2597 else
2598 {
2599 /* Channel name followed by text? Extract the channel name */
2600 strncpy(tmp_chan, message, chan_prefix - message);
2601 tmp_chan[chan_prefix - message] = '\0';
2602 dest_chan = -1;
2603 for (i = 0; i < MAX_CHANNELS; i++)
2604 {
2605 if (!strcmp(channels[i].name, tmp_chan))
2606 {
2607 dest_chan = i;
2608 break;
2609 }
2610 }
2611 message += (chan_prefix - message)+1;
2612 /* Forget about search string */
2613 msg_off = FALSE;
2614 strcpy(search, "");
2615 }
2616 }
2617
2618 /* Look for a player's name followed by a colon */
2619 colon = strchr(message, ':');
2620
2621 /* Pretend colon wasn't there */
2622 if (colon)
2623 {
2624 /* messanger is undefined OR colon is last symbol OR colon is part of "smiley" */
2625 if (!p_ptr || !*(colon + 1) || strchr(")(-|\\/", *(colon + 1))) colon = NULL;
2626 }
2627
2628 /* Form a search string if we found a colon */
2629 if (colon)
2630 {
2631 /* Copy everything up to the colon to the search string */
2632 strncpy(search, message, colon - message);
2633
2634 /* Add a trailing NULL */
2635 search[colon - message] = '\0';
2636
2637 /* Move colon pointer forward to next word */
2638 while (*colon && (isspace(*colon) || *colon == ':')) colon++;
2639
2640 /* Offset message */
2641 msg_off = TRUE;
2642 }
2643
2644 /* Find special target */
2645 if (strlen(search))
2646 {
2647 /* There's nothing else , prepare for whisper */
2648 if (colon - message == strlen(message))
2649 {
2650 assist_whisper(p_ptr, search);
2651 return;
2652 }
2653 /* Hack -- empty 'party hinter' hints to own party */
2654 if (search[0] == '^' && p_ptr->party && search[1] == '\0')
2655 {
2656 strcpy(search, parties[p_ptr->party].name);
2657 }
2658 if (!(target = find_chat_target(search, error)))
2659 {
2660 /* Error */
2661 msg_print(p_ptr, error);
2662
2663 /* Done */
2664 return;
2665 }
2666 }
2667
2668 /* No need to offset message */
2669 if (!msg_off)
2670 {
2671 colon = message;
2672 }
2673
2674
2675 /* Send to a virtual channel */
2676 if (target > 0)
2677 {
2678 /* Make sure it's a channel, not player */
2679 if (target < VIRTUAL_CHANNELS)
2680 {
2681 cptr verb = "say";
2682 char punct = '.';
2683 char msg[60];
2684 my_strcpy(msg, colon, 60);
2685 switch (target)
2686 {
2687 case 1: /* "&say" */
2688 for (i = strlen(msg) - 1; i > 0; i--)
2689 {
2690 switch (msg[i])
2691 {
2692 case ' ':
2693 continue;
2694 case '?':
2695 verb = "ask";
2696 /* fallthrough */
2697 case '!':
2698 case '.':
2699 punct = msg[i];
2700 msg[i] = '\0';
2701 default:
2702 break;
2703 }
2704 break;
2705 }
2706 /* Send somewhere */
2707 msg_format_type(p_ptr, MSG_TALK, "You %s, \"%s\"%c", verb, msg, punct);
2708 msg_format_complex_near(p_ptr, p_ptr, MSG_TALK, "%s %ss, \"%s\"%c", sender, verb, msg, punct);
2709 break;
2710 case 2: /* "&yell" */
2711 verb = "yell";
2712 punct = '!';
2713 for (i = strlen(msg) - 1; i > 0; i--)
2714 {
2715 switch (msg[i])
2716 {
2717 case ' ':
2718 continue;
2719 case '?':
2720 case '!':
2721 case '.':
2722 msg[i] = '\0';
2723 default:
2724 break;
2725 }
2726 break;
2727 }
2728 /* Send somewhere */
2729 msg_format_type(p_ptr, MSG_YELL, "You %s, \"%s\"%c", verb, msg, punct);
2730 msg_format_complex_far(p_ptr, p_ptr, MSG_YELL, "%ss, \"%s\"%c", sender, verb, msg, punct);
2731 break;
2732 }
2733 return;
2734 }
2735 /* It was a player */
2736 else target -= VIRTUAL_CHANNELS;
2737 }
2738
2739 /* Send to appropriate player */
2740 if (target > 0)
2741 {
2742 /* Set target player */
2743 q_ptr = Players[target];
2744
2745 /* Send message to target */
2746 msg_format_type(q_ptr, MSG_WHISPER, "[%s:%s] %s", q_ptr->name, sender, colon);
2747
2748 /* Also send back to sender */
2749 msg_format_type(p_ptr, MSG_WHISPER, "[%s:%s] %s", q_ptr->name, sender, colon);
2750
2751 /* Done */
2752 return;
2753 }
2754
2755 /* Send to appropriate party */
2756 if (target < 0)
2757 {
2758 /* Send message to target party */
2759 party_msg_format(0 - target, "[%s:%s] %s",
2760 parties[0 - target].name, sender, colon);
2761
2762 /* Also send back to sender if the sender is not in
2763 * the party being messaged. */
2764 if (p_ptr->party != 0 - target)
2765 msg_format(p_ptr, "[%s:%s] %s",
2766 parties[0 - target].name, sender, colon);
2767
2768 /* Done */
2769 return;
2770 }
2771
2772 /* Total failure... */
2773 if (dest_chan == -1) return;
2774 else if (p_ptr && !can_talk(p_ptr, dest_chan)) return;
2775
2776 /* Send to everyone in this channel */
2777 for (i = 1; i <= NumPlayers; i++)
2778 {
2779 q_ptr = Players[i];
2780 if(q_ptr->on_channel[dest_chan] & UCM_EAR)
2781 {
2782 /* Send message */
2783 if (p_ptr)
2784 {
2785 msg_format_type(q_ptr, MSG_CHAT + dest_chan, "[%s] %s", sender, message);
2786 }
2787 else
2788 {
2789 msg_format_type(q_ptr, MSG_CHAT + dest_chan, "%s", message);
2790 }
2791 }
2792 }
2793
2794 /* Send to the console too */
2795 console_print(format("[%s] %s", sender, message), dest_chan);
2796 }
2797
2798
2799 /*
2800 * A player has sent a message to the rest of the world.
2801 *
2802 * Parse it and send to everyone or to only the person(s) he requested.
2803 *
2804 * Note that more than one message may get sent at once, seperated by
2805 * tabs ('\t'). Thus, this function splits them and calls
2806 * "player_talk_aux" to do the dirty work.
2807 */
player_talk(player_type * p_ptr,char * message)2808 void player_talk(player_type *p_ptr, char *message)
2809 {
2810 char *cur, *next;
2811
2812 /* Start at the beginning */
2813 cur = message;
2814
2815 /* Process until out of messages */
2816 while (cur)
2817 {
2818 /* Find the next tab */
2819 next = strchr(cur, '\t');
2820
2821 /* Stop out the tab */
2822 if (next)
2823 {
2824 /* Replace with \0 */
2825 *next = '\0';
2826 }
2827
2828 /* Process this message */
2829 player_talk_aux(p_ptr, cur);
2830
2831 /* Move to the next one */
2832 if (next)
2833 {
2834 /* One step past the \0 */
2835 cur = next + 1;
2836 }
2837 else
2838 {
2839 /* No more message */
2840 cur = NULL;
2841 }
2842 }
2843 }
2844
2845
2846 /*
2847 * Check a char for "vowel-hood"
2848 */
is_a_vowel(int ch)2849 bool is_a_vowel(int ch)
2850 {
2851 switch (ch)
2852 {
2853 case 'a':
2854 case 'e':
2855 case 'i':
2856 case 'o':
2857 case 'u':
2858 case 'A':
2859 case 'E':
2860 case 'I':
2861 case 'O':
2862 case 'U':
2863 return (TRUE);
2864 }
2865
2866 return (FALSE);
2867 }
2868
2869 /*
2870 * Converts a string to a terminal color byte.
2871 */
color_text_to_attr(cptr name)2872 int color_text_to_attr(cptr name)
2873 {
2874 if (my_stricmp(name, "dark") == 0) return (TERM_DARK);
2875 if (my_stricmp(name, "white") == 0) return (TERM_WHITE);
2876 if (my_stricmp(name, "slate") == 0) return (TERM_SLATE);
2877 if (my_stricmp(name, "orange") == 0) return (TERM_ORANGE);
2878 if (my_stricmp(name, "red") == 0) return (TERM_RED);
2879 if (my_stricmp(name, "green") == 0) return (TERM_GREEN);
2880 if (my_stricmp(name, "blue") == 0) return (TERM_BLUE);
2881 if (my_stricmp(name, "umber") == 0) return (TERM_UMBER);
2882 if (my_stricmp(name, "violet") == 0) return (TERM_VIOLET);
2883 if (my_stricmp(name, "yellow") == 0) return (TERM_YELLOW);
2884 if (my_stricmp(name, "lightdark") == 0) return (TERM_L_DARK);
2885 if (my_stricmp(name, "lightwhite") == 0) return (TERM_L_WHITE);
2886 if (my_stricmp(name, "lightred") == 0) return (TERM_L_RED);
2887 if (my_stricmp(name, "lightgreen") == 0) return (TERM_L_GREEN);
2888 if (my_stricmp(name, "lightblue") == 0) return (TERM_L_BLUE);
2889 if (my_stricmp(name, "lightumber") == 0) return (TERM_L_UMBER);
2890
2891 /* Oops */
2892 return (-1);
2893 }
2894
2895
2896 /*
2897 * Extract a textual representation of an attribute
2898 */
attr_to_text(byte a)2899 cptr attr_to_text(byte a)
2900 {
2901 switch (a)
2902 {
2903 case TERM_DARK: return ("Dark");
2904 case TERM_WHITE: return ("White");
2905 case TERM_SLATE: return ("Slate");
2906 case TERM_ORANGE: return ("Orange");
2907 case TERM_RED: return ("Red");
2908 case TERM_GREEN: return ("Green");
2909 case TERM_BLUE: return ("Blue");
2910 case TERM_UMBER: return ("Umber");
2911 case TERM_L_DARK: return ("L.Dark");
2912 case TERM_L_WHITE: return ("L.Slate");
2913 case TERM_VIOLET: return ("Violet");
2914 case TERM_YELLOW: return ("Yellow");
2915 case TERM_L_RED: return ("L.Red");
2916 case TERM_L_GREEN: return ("L.Green");
2917 case TERM_L_BLUE: return ("L.Blue");
2918 case TERM_L_UMBER: return ("L.Umber");
2919 }
2920
2921 /* Oops */
2922 return ("Icky");
2923 }
2924
2925
2926 /*
2927 * Record a message in the character history
2928 */
log_history_event(player_type * p_ptr,char * msg,bool unique)2929 void log_history_event(player_type *p_ptr, char *msg, bool unique)
2930 {
2931 int days, hours, mins, i;
2932 huge seconds, turn;
2933
2934 history_event *evt;
2935 history_event *last = NULL;
2936 history_event *evt_forge = NULL;
2937
2938 u16b note = quark_add(msg);
2939
2940 /* Walk throu event list */
2941 for (evt = p_ptr->charhist; evt; evt = evt->next)
2942 {
2943 /* Duplicate entries not allowed */
2944 if (evt->message == note && unique)
2945 return;
2946 /* Find last in chain */
2947 last = evt;
2948 }
2949
2950 /* Convert turn counter to real time */
2951 seconds = days = hours = mins = turn = 0;
2952 for (i = 0; i < p_ptr->turn.era+2; i++)
2953 {
2954 int plus_days = 0, plus_hours = 0, plus_mins = 0;
2955 turn = HTURN_ERA_FLIP;
2956 if (i == p_ptr->turn.era+1) turn = p_ptr->turn.turn;
2957 seconds = turn / cfg_fps;
2958 plus_days = seconds / 86400;
2959 plus_hours = (seconds / 3600) - (24 * days);
2960 plus_mins = (seconds / 60) % 60;
2961 days += plus_days;
2962 hours += plus_hours;
2963 mins += plus_mins;
2964 }
2965 while (mins >= 60) { mins -= 60; hours += 1; }
2966 while (hours >= 24) { hours -= 24; days += 1; }
2967
2968 /* Create new entry */
2969 MAKE(evt_forge, history_event);
2970 evt_forge->days = days;
2971 evt_forge->hours = hours;
2972 evt_forge->mins = mins;
2973 evt_forge->depth = p_ptr->dun_depth;
2974 evt_forge->level = p_ptr->lev;
2975 evt_forge->message = note;
2976
2977 /* Add to chain */
2978 if (!p_ptr->charhist)
2979 p_ptr->charhist = evt_forge;
2980 else
2981 last->next = evt_forge;
2982 }
2983 /*
2984 * Destroy player's history
2985 */
history_wipe(history_event * evt)2986 void history_wipe(history_event *evt) {
2987 history_event *next;
2988 while (evt)
2989 {
2990 /* Remember */
2991 next = NULL; if (evt->next) next = evt->next;
2992
2993 /* KILL */
2994 KILL(evt);
2995
2996 /* Recall */
2997 evt = NULL; if (next) evt = next;
2998 }
2999 }
3000 /*
3001 * Format 1 string of event history
3002 */
format_history_event(history_event * evt)3003 cptr format_history_event(history_event *evt)
3004 {
3005 static char buf[160];
3006 sprintf(buf, "%02i:%02i:%02i %4ift %2i %s",
3007 evt->days, evt->hours, evt->mins,
3008 evt->depth*50, evt->level, quark_str(evt->message));
3009 return &buf[0];
3010 }
3011
send_prepared_info(player_type * p_ptr,byte win,byte stream,byte extra_params)3012 void send_prepared_info(player_type *p_ptr, byte win, byte stream, byte extra_params) {
3013 byte old_term;
3014 int i;
3015
3016 /* Save 'current' terminal */
3017 old_term = p_ptr->remote_term;
3018
3019 /* Activte new terminal */
3020 send_term_info(p_ptr, NTERM_ACTIVATE, win);
3021
3022 /* Clear, Send, Refresh */
3023 send_term_info(p_ptr, NTERM_CLEAR, 0);
3024 for (i = 0; i < p_ptr->last_info_line + 1; i++)
3025 stream_line_as(p_ptr, stream, i, i);
3026 send_term_info(p_ptr, NTERM_FRESH | extra_params, 0);
3027 /* Restore active term */
3028 send_term_info(p_ptr, NTERM_ACTIVATE, old_term);
3029
3030 /* Hack -- erase 'prepared info' */
3031 p_ptr->last_info_line = -1;
3032 }
3033
send_prepared_popup(player_type * p_ptr,cptr header)3034 void send_prepared_popup(player_type *p_ptr, cptr header)
3035 {
3036 int i;
3037 byte old_term;
3038
3039 old_term = p_ptr->remote_term;
3040
3041 send_term_info(p_ptr, NTERM_ACTIVATE, NTERM_WIN_SPECIAL);
3042 Send_special_other(p_ptr, header);
3043
3044 /* Clear, Send, Popup! */
3045 send_term_info(p_ptr, NTERM_CLEAR, 0);
3046 for (i = 0; i < p_ptr->last_info_line + 1; i++)
3047 stream_line_as(p_ptr, STREAM_SPECIAL_TEXT, i, i);
3048 send_term_info(p_ptr, NTERM_POP, 0);
3049
3050 send_term_info(p_ptr, NTERM_ACTIVATE, old_term);
3051
3052 /* HACK -- Assume this was NOT monster recall */
3053 /* This is implied, because monster recall doesn't use send_prepared_popup() */
3054 monster_race_track_hack(p_ptr);
3055 }
3056
3057 /* This hacky function resets monster tracking after STREAM_SPECIAL_TEXT
3058 * was used for anything other than actual monster recall. This way,
3059 * server will definitely send new monster info, once it's required again. */
monster_race_track_hack(player_type * p_ptr)3060 void monster_race_track_hack(player_type *p_ptr)
3061 {
3062 /* Paranoia -- Player is not yet in the game */
3063 if (p_ptr->conn == -1 || !IS_PLAYING(p_ptr)) return;
3064 /* Only relevant if Player has no dedicated window for monster text */
3065 if (!p_ptr->stream_wid[STREAM_MONSTER_TEXT])
3066 {
3067 /* Hack -- cancel monster tracking */
3068 monster_race_track(p_ptr, 0);
3069 }
3070 }
3071
text_out_init(player_type * p_ptr)3072 void text_out_init(player_type *p_ptr)
3073 {
3074 player_textout = p_ptr;
3075 p_ptr->cur_wid = 0;
3076 p_ptr->cur_hgt = 0;
3077
3078 p_ptr->last_info_line = -1;
3079 }
3080
text_out_done()3081 void text_out_done()
3082 {
3083 int i;
3084 player_type *p_ptr = player_textout;
3085 player_textout = NULL;
3086
3087 /* BAD HACK -- notify client about abrupt endings */
3088 if (p_ptr->cur_hgt >= MAX_TXT_INFO-1)
3089 {
3090 char *msg = " {Out of buffer space!!!} ";
3091 size_t len = strlen(msg);
3092 for (i = 0; i < len; i++)
3093 {
3094 p_ptr->info[p_ptr->cur_hgt][i].c = msg[i];
3095 p_ptr->info[p_ptr->cur_hgt][i].a = TERM_RED;
3096 }
3097 p_ptr->cur_wid = i;
3098 }
3099
3100 /* HACK!! Clear rest of the line */
3101 for (i = p_ptr->cur_wid; i < 80; i++)
3102 {
3103 p_ptr->info[p_ptr->cur_hgt][i].c = ' ';
3104 p_ptr->info[p_ptr->cur_hgt][i].a = TERM_WHITE;
3105 }
3106
3107 p_ptr->last_info_line = p_ptr->cur_hgt;
3108
3109 /* Restore height and width of current dungeon level */
3110 p_ptr->cur_hgt = MAX_HGT;
3111 p_ptr->cur_wid = MAX_WID;
3112 }
3113
3114 /* Taking (bad) ques from client code, here we copy one
3115 * buffer into another, instead of just storing pointer
3116 * to the correct buffer somewhere... */
3117 /* The reason is all the current functions are hard-wired
3118 * to use p_ptr->info, so instead of massive overhaul (like making
3119 * *IT* a pointer), we add a literal workaround. */
3120 /* TODO: Kill this. */
text_out_save(player_type * p_ptr)3121 void text_out_save(player_type *p_ptr)
3122 {
3123 int i, j;
3124 /* memcpy is for cowards */
3125 for (j = 0; j < MAX_TXT_INFO; j++)
3126 {
3127 for (i = 0; i < MAX_WID; i++)
3128 {
3129 p_ptr->file[j][i].a = p_ptr->info[j][i].a;
3130 p_ptr->file[j][i].c = p_ptr->info[j][i].c;
3131 }
3132 }
3133 p_ptr->last_file_line = p_ptr->last_info_line;
3134 }
text_out_load(player_type * p_ptr)3135 void text_out_load(player_type *p_ptr)
3136 {
3137 /* mindless code duplication. */
3138 int i, j;
3139
3140 for (j = 0; j < MAX_TXT_INFO; j++)
3141 {
3142 for (i = 0; i < MAX_WID; i++)
3143 {
3144 p_ptr->info[j][i].a = p_ptr->file[j][i].a;
3145 p_ptr->info[j][i].c = p_ptr->file[j][i].c;
3146 }
3147 }
3148 p_ptr->last_info_line = p_ptr->last_file_line;
3149 /* I hope you'll delete those functions ASAP */
3150 /* WHATEVER HAPPENS, PLEASE DON'T UPGRADE THIS */
3151 /* TO ALLOW STACKING... */
3152 }
3153
text_out_c(byte a,cptr buf)3154 void text_out_c(byte a, cptr buf)
3155 {
3156 int i, j, shorten, buflen;
3157 player_type *p_ptr = player_textout;
3158 static char line_buf[80] = {'\0'};
3159
3160 bool simple = FALSE;
3161 bool warped = FALSE;
3162 i = j = shorten = 0;
3163 buflen = strlen(buf);
3164
3165 #if 0
3166 /* Add "auto-paragraph" spaces */
3167 if (p_ptr->cur_wid == 0)
3168 {
3169 line_buf[0] = '\0';
3170 strcat(line_buf, " ");
3171 p_ptr->cur_wid += 2;
3172 }
3173 #endif
3174
3175 while (TRUE)
3176 {
3177 /* Problem -- Out of stack space :( */
3178 if (p_ptr->cur_hgt >= MAX_TXT_INFO-1) break;
3179
3180 #if 0
3181 /* Add 1 space between stuff (auto-separate) */
3182 if (buf[shorten] != ' ' && p_ptr->cur_wid)
3183 {
3184 strcat(line_buf, " ");
3185 p_ptr->cur_wid += 1;
3186 }
3187 #endif
3188
3189 /* We can fit the info on the same line */
3190 if (buflen - shorten < 80 - p_ptr->cur_wid)
3191 {
3192 /* Set to copy whole buffer */
3193 j = buflen - shorten;
3194 simple = TRUE;
3195 }
3196 /* We can't, let's find a suitable wrap point */
3197 else
3198 {
3199 /* Default to whole line */
3200 j = 0;
3201 /* Find some nice space near the end */
3202 for (i = shorten; i < buflen; i++)
3203 {
3204 if (buf[i] == ' ' || buf[i] == '\n')
3205 {
3206 if (i - shorten < 80 - p_ptr->cur_wid)
3207 {
3208 j = i - shorten;
3209 }
3210 else
3211 {
3212 break;
3213 }
3214 }
3215 }
3216 simple = FALSE;
3217 warped = TRUE;
3218 }
3219
3220 /* Copy first part */
3221 for (i = 0; i < j; i++)
3222 {
3223 if (buf[i+shorten] != '\n')
3224 {
3225 line_buf[p_ptr->cur_wid + i] = buf[i + shorten];
3226 }
3227 else
3228 {
3229 /* If we encounter a '\n', and it's our first
3230 * line ever, we ignore it... */
3231 if (p_ptr->cur_hgt == 0 && p_ptr->cur_wid <= 0)
3232 {
3233 p_ptr->cur_wid -= 1; //ignore, backup a bit
3234 }
3235 else
3236 {
3237 j = i + 1;
3238 simple = warped = FALSE;
3239 break;
3240 }
3241 }
3242 }
3243
3244 /* Advance forward */
3245 p_ptr->cur_wid += i;
3246
3247 /* Fill the rest with spaces */
3248 for (i = p_ptr->cur_wid; i < 80; i++)
3249 {
3250 line_buf[i] = ' ';
3251 }
3252
3253 /* Dump it */
3254 for (i = p_ptr->cur_wid-j; i < 80; i++)
3255 {
3256 p_ptr->info[p_ptr->cur_hgt][i].c = line_buf[i];
3257 p_ptr->info[p_ptr->cur_hgt][i].a = a;
3258 }
3259
3260 /* End function for simple cases */
3261 if (simple) break;
3262
3263 /* Advance to next line */
3264 p_ptr->cur_hgt += 1;
3265 p_ptr->cur_wid = 0;
3266
3267 /* Shorten the text */
3268 shorten += j;
3269
3270 /* Handle spaces */
3271 if (warped && buf[shorten] == ' ') shorten++;
3272
3273 /* Finish when we're done */
3274 if (shorten >= buflen) break;
3275 }
3276 }
3277
text_out(cptr str)3278 void text_out(cptr str)
3279 {
3280 text_out_c(TERM_WHITE, str);
3281 }
3282
c_prt(player_type * p_ptr,byte attr,cptr str,int row,int col)3283 void c_prt(player_type *p_ptr, byte attr, cptr str, int row, int col)
3284 {
3285 /* Paranoia */
3286 if (row > MAX_TXT_INFO) return;
3287
3288 while (*str)
3289 {
3290 /* Limit */
3291 if (col > 80) break;
3292
3293 p_ptr->info[row][col].c = *str;
3294 p_ptr->info[row][col].a = attr;
3295
3296 col++;
3297 str++;
3298 }
3299 }
prt(player_type * p_ptr,cptr str,int row,int col)3300 void prt(player_type *p_ptr, cptr str, int row, int col)
3301 {
3302 c_prt(p_ptr, TERM_WHITE, str, row, col);
3303 }
3304
clear_line(player_type * p_ptr,int row)3305 void clear_line(player_type *p_ptr, int row)
3306 {
3307 int i;
3308 for (i = 0; i < 80; i++)
3309 {
3310 p_ptr->info[row][i].c = ' ';
3311 p_ptr->info[row][i].a = TERM_WHITE;
3312 }
3313 }
clear_from(player_type * p_ptr,int row)3314 void clear_from(player_type *p_ptr, int row)
3315 {
3316 int i;
3317 while (row < MAX_TXT_INFO)
3318 {
3319 for (i = 0; i < 80; i++)
3320 {
3321 p_ptr->info[row][i].c = ' ';
3322 p_ptr->info[row][i].a = TERM_DARK;
3323 }
3324 row++;
3325 }
3326 }
3327
askfor_aux(player_type * p_ptr,char query,char * buf,int row,int col,cptr prompt,cptr default_value,byte prompt_attr,byte input_attr)3328 bool askfor_aux(player_type *p_ptr, char query, char *buf, int row, int col, cptr prompt, cptr default_value, byte prompt_attr, byte input_attr)
3329 {
3330 char * mark = &(p_ptr->interactive_hook[0][1]);
3331 char * len = &(p_ptr->interactive_hook[0][2]);
3332 char * y = &(p_ptr->interactive_hook[0][3]);
3333 char * x = &(p_ptr->interactive_hook[0][4]);
3334 char * attr = &(p_ptr->interactive_hook[0][5]);
3335 char * mlen = &(p_ptr->interactive_hook[0][6]);
3336 char * str = p_ptr->interactive_hook[1];
3337
3338 if (*mark)
3339 {
3340 *mark = 0;
3341 strncpy(buf, str, *len);
3342 buf[(byte)*len] = '\0';
3343 return TRUE;
3344 }
3345 else
3346 {
3347 *mark = query;
3348 *attr = input_attr;
3349 *y = row;
3350 *x = col;
3351 *len = 0;
3352 *mlen =0;
3353
3354 if (!STRZERO(prompt))
3355 {
3356 (*x) += strlen(prompt);
3357 clear_line(p_ptr, row);
3358 c_prt(p_ptr, prompt_attr, prompt, row, col);
3359 Stream_line_p(p_ptr, STREAM_SPECIAL_TEXT, row);
3360 }
3361 if (!STRZERO(default_value))
3362 {
3363 /* Hack: ask for 1 character */
3364 if (default_value[0] == '*') *mlen =1;
3365 else
3366 {
3367 *len = strlen(default_value);
3368 strncpy(str, default_value, *len);
3369 }
3370 }
3371
3372 do_cmd_interactive_input(p_ptr, 0); /* ! */
3373
3374 return FALSE;
3375 }
3376 }
ask_for(player_type * p_ptr,char query,char * buf)3377 bool ask_for(player_type *p_ptr, char query, char *buf)
3378 {
3379 return askfor_aux(p_ptr, query, buf, 0, 0, "", "", TERM_DARK, TERM_WHITE);
3380 }
3381