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