1 /* $Id$ */
2 /* File: util.c */
3 
4 /* Purpose: Angband utilities -BEN- */
5 
6 
7 #define SERVER
8 
9 #include "angband.h"
10 #ifdef TOMENET_WORLDS
11 #include "../world/world.h"
12 #endif
13 
14 #ifdef MINGW
15 /* For gettimeofday */
16 #include <sys/time.h>
17 #endif
18 
19 static void console_talk_aux(char *message);
20 
21 //static int name_lookup_loose_quiet(int Ind, cptr name, u16b party);
22 
23 #ifndef HAS_MEMSET
24 
25 /*
26  * For those systems that don't have "memset()"
27  *
28  * Set the value of each of 'n' bytes starting at 's' to 'c', return 's'
29  * If 'n' is negative, you will erase a whole lot of memory.
30  */
memset(char * s,int c,huge n)31 char *memset(char *s, int c, huge n)
32 {
33 	char *t;
34 	for (t = s; n--; ) *t++ = c;
35 	return (s);
36 }
37 
38 #endif
39 
40 
41 
42 #ifndef HAS_STRICMP
43 
44 /*
45  * For those systems that don't have "stricmp()"
46  *
47  * Compare the two strings "a" and "b" ala "strcmp()" ignoring case.
48  */
stricmp(cptr a,cptr b)49 int stricmp(cptr a, cptr b)
50 {
51 	cptr s1, s2;
52 	char z1, z2;
53 
54 	/* Scan the strings */
55 	for (s1 = a, s2 = b; TRUE; s1++, s2++)
56 	{
57 		z1 = FORCEUPPER(*s1);
58 		z2 = FORCEUPPER(*s2);
59 		if (z1 < z2) return (-1);
60 		if (z1 > z2) return (1);
61 		if (!z1) return (0);
62 	}
63 }
64 
65 #endif
66 
in_banlist(char * acc,char * addr,int * time,char * reason)67 int in_banlist(char *acc, char *addr, int *time, char *reason) {
68 	struct combo_ban *ptr;
69 	int found = 0x0;
70 
71 	for (ptr = banlist; ptr != (struct combo_ban*)NULL; ptr = ptr->next) {
72 		if (ptr->ip[0] && addr && !strcmp(addr, ptr->ip)) found |= 0x1;
73 		if (ptr->acc[0] && acc && !strcmp(acc, ptr->acc)) found |= 0x2;
74 
75 		if (reason) strcpy(reason, ptr->reason);
76 		if (time) *time = ptr->time;
77 
78 		return found;
79 	}
80 	return 0x0;
81 }
82 
check_banlist()83 void check_banlist() {
84 	struct combo_ban *ptr, *new, *old = (struct combo_ban*)NULL;
85 	ptr = banlist;
86 	while (ptr != (struct combo_ban*)NULL) {
87 		if (ptr->time) {
88 			if (!(--ptr->time)) {
89 				if (ptr->reason[0]) s_printf("Unbanning due to ban timeout (ban reason was '%s'):\n", ptr->reason);
90 				else s_printf("Unbanning due to ban timeout:\n", ptr->reason);
91 				if (ptr->ip[0]) s_printf(" Connections from %s\n", ptr->ip);
92 				if (ptr->acc[0]) s_printf(" Connections for %s\n", ptr->acc);
93 
94 				if (!old) {
95 					banlist = ptr->next;
96 					new = banlist;
97 				} else {
98 					old->next = ptr->next;
99 					new = old->next;
100 				}
101 				free(ptr);
102 				ptr = new;
103 				continue;
104 			}
105 		}
106 		ptr = ptr->next;
107 	}
108 }
109 
110 
111 #ifdef SET_UID
112 
113 # ifndef HAS_USLEEP
114 
115 /*
116  * For those systems that don't have "usleep()" but need it.
117  *
118  * Fake "usleep()" function grabbed from the inl netrek server -cba
119  */
usleep(huge microSeconds)120 static int usleep(huge microSeconds)
121 {
122 	struct timeval      Timer;
123 
124 	int                 nfds = 0;
125 
126 #ifdef FD_SET
127 	fd_set          *no_fds = NULL;
128 #else
129 	int                     *no_fds = NULL;
130 #endif
131 
132 
133 	/* Was: int readfds, writefds, exceptfds; */
134 	/* Was: readfds = writefds = exceptfds = 0; */
135 
136 
137 	/* Paranoia -- No excessive sleeping */
138 	if (microSeconds > 4000000L) core("Illegal usleep() call");
139 
140 
141 	/* Wait for it */
142 	Timer.tv_sec = (microSeconds / 1000000L);
143 	Timer.tv_usec = (microSeconds % 1000000L);
144 
145 	/* Wait for it */
146 	if (select(nfds, no_fds, no_fds, no_fds, &Timer) < 0)
147 	{
148 		/* Hack -- ignore interrupts */
149 		if (errno != EINTR) return -1;
150 	}
151 
152 	/* Success */
153 	return 0;
154 }
155 
156 # endif
157 
158 
159 /*
160  * Hack -- External functions
161  */
162 extern struct passwd *getpwuid();
163 extern struct passwd *getpwnam();
164 
165 /*
166  * Find a default user name from the system.
167  */
user_name(char * buf,int id)168 void user_name(char *buf, int id)
169 {
170 	struct passwd *pw;
171 
172 	/* Look up the user name */
173 	if ((pw = getpwuid(id)))
174 	{
175 		(void)strcpy(buf, pw->pw_name);
176 		buf[16] = '\0';
177 
178 #ifdef CAPITALIZE_USER_NAME
179 		/* Hack -- capitalize the user name */
180 		if (islower(buf[0])) buf[0] = toupper(buf[0]);
181 #endif
182 
183 		return;
184 	}
185 
186 	/* Oops.  Hack -- default to "PLAYER" */
187 	strcpy(buf, "PLAYER");
188 }
189 
190 #endif /* SET_UID */
191 
192 
193 
194 
195 /*
196  * The concept of the "file" routines below (and elsewhere) is that all
197  * file handling should be done using as few routines as possible, since
198  * every machine is slightly different, but these routines always have the
199  * same semantics.
200  *
201  * In fact, perhaps we should use the "path_parse()" routine below to convert
202  * from "canonical" filenames (optional leading tilde's, internal wildcards,
203  * slash as the path seperator, etc) to "system" filenames (no special symbols,
204  * system-specific path seperator, etc).  This would allow the program itself
205  * to assume that all filenames are "Unix" filenames, and explicitly "extract"
206  * such filenames if needed (by "path_parse()", or perhaps "path_canon()").
207  *
208  * Note that "path_temp" should probably return a "canonical" filename.
209  *
210  * Note that "my_fopen()" and "my_open()" and "my_make()" and "my_kill()"
211  * and "my_move()" and "my_copy()" should all take "canonical" filenames.
212  *
213  * Note that "canonical" filenames use a leading "slash" to indicate an absolute
214  * path, and a leading "tilde" to indicate a special directory, and default to a
215  * relative path, but MSDOS uses a leading "drivename plus colon" to indicate the
216  * use of a "special drive", and then the rest of the path is parsed "normally",
217  * and MACINTOSH uses a leading colon to indicate a relative path, and an embedded
218  * colon to indicate a "drive plus absolute path", and finally defaults to a file
219  * in the current working directory, which may or may not be defined.
220  *
221  * We should probably parse a leading "~~/" as referring to "ANGBAND_DIR". (?)
222  */
223 
224 
225 #ifdef ACORN
226 
227 
228 /*
229  * Most of the "file" routines for "ACORN" should be in "main-acn.c"
230  */
231 
232 
233 #else /* ACORN */
234 
235 
236 #ifdef SET_UID
237 
238 /*
239  * Extract a "parsed" path from an initial filename
240  * Normally, we simply copy the filename into the buffer
241  * But leading tilde symbols must be handled in a special way
242  * Replace "~user/" by the home directory of the user named "user"
243  * Replace "~/" by the home directory of the current user
244  */
path_parse(char * buf,int max,cptr file)245 errr path_parse(char *buf, int max, cptr file)
246 {
247 	cptr            u, s;
248 	struct passwd   *pw;
249 	char            user[128];
250 
251 
252 	/* Assume no result */
253 	buf[0] = '\0';
254 
255 	/* No file? */
256 	if (!file) return (-1);
257 
258 	/* File needs no parsing */
259 	if (file[0] != '~')
260 	{
261 		strcpy(buf, file);
262 		return (0);
263 	}
264 
265 	/* Point at the user */
266 	u = file+1;
267 
268 	/* Look for non-user portion of the file */
269 	s = strstr(u, PATH_SEP);
270 
271 	/* Hack -- no long user names */
272 	if (s && (s >= u + sizeof(user))) return (1);
273 
274 	/* Extract a user name */
275 	if (s)
276 	{
277 		int i;
278 		for (i = 0; u < s; ++i) user[i] = *u++;
279 		user[i] = '\0';
280 		u = user;
281 	}
282 
283 	/* Look up the "current" user */
284 	if (u[0] == '\0') u = getlogin();
285 
286 	/* Look up a user (or "current" user) */
287 	if (u) pw = getpwnam(u);
288 	else pw = getpwuid(getuid());
289 
290 	/* Nothing found? */
291 	if (!pw) return (1);
292 
293 	/* Make use of the info */
294 	(void)strcpy(buf, pw->pw_dir);
295 
296 	/* Append the rest of the filename, if any */
297 	if (s) (void)strcat(buf, s);
298 
299 	/* Success */
300 	return (0);
301 }
302 
303 
304 #else /* SET_UID */
305 
306 
307 /*
308  * Extract a "parsed" path from an initial filename
309  *
310  * This requires no special processing on simple machines,
311  * except for verifying the size of the filename.
312  */
path_parse(char * buf,int max,cptr file)313 errr path_parse(char *buf, int max, cptr file)
314 {
315 	/* Accept the filename */
316 	strnfmt(buf, max, "%s", file);
317 
318 	/* Success */
319 	return (0);
320 }
321 
322 
323 #endif /* SET_UID */
324 
325 
326 /*
327  * Hack -- acquire a "temporary" file name if possible
328  *
329  * This filename is always in "system-specific" form.
330  */
path_temp(char * buf,int max)331 errr path_temp(char *buf, int max)
332 {
333 #ifdef WINDOWS
334 	static u32b tmp_counter;
335 	static char valid_characters[] =
336 			"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
337 	char rand_ext[4];
338 
339 	rand_ext[0] = valid_characters[rand_int(sizeof (valid_characters))];
340 	rand_ext[1] = valid_characters[rand_int(sizeof (valid_characters))];
341 	rand_ext[2] = valid_characters[rand_int(sizeof (valid_characters))];
342 	rand_ext[3] = '\0';
343 	strnfmt(buf, max, "%s/server_%ud.%s", ANGBAND_DIR_DATA, tmp_counter, rand_ext);
344 	tmp_counter++;
345 #else
346 	cptr s;
347 
348 	/* Temp file */
349 	s = tmpnam(NULL);
350 
351 	/* Oops */
352 	if (!s) return (-1);
353 
354 	/* Format to length */
355 	strnfmt(buf, max, "%s", s);
356 #endif
357 	/* Success */
358 	return (0);
359 }
360 
361 /*
362  * Hack -- replacement for "fopen()"
363  */
my_fopen(cptr file,cptr mode)364 FILE *my_fopen(cptr file, cptr mode)
365 {
366 	char                buf[1024];
367 
368 	/* Hack -- Try to parse the path */
369 	if (path_parse(buf, 1024, file)) return (NULL);
370 
371 	/* Attempt to fopen the file anyway */
372 	return (fopen(buf, mode));
373 }
374 
375 
376 /*
377  * Hack -- replacement for "fclose()"
378  */
my_fclose(FILE * fff)379 errr my_fclose(FILE *fff)
380 {
381 	/* Require a file */
382 	if (!fff) return (-1);
383 
384 	/* Close, check for error */
385 	if (fclose(fff) == EOF) return (1);
386 
387 	/* Success */
388 	return (0);
389 }
390 
391 
392 #endif /* ACORN */
393 
394 
395 /*
396  * Hack -- replacement for "fgets()"
397  *
398  * Read a string, without a newline, to a file
399  *
400  * Process tabs, strip internal non-printables
401  */
my_fgets(FILE * fff,char * buf,huge n,bool conv)402 errr my_fgets(FILE *fff, char *buf, huge n, bool conv)
403 {
404 	huge i = 0;
405 
406 	char *s;
407 
408 	char tmp[1024];
409 
410 	/* Read a line */
411 	if (fgets(tmp, 1024, fff))
412 	{
413 		/* Convert weirdness */
414 		for (s = tmp; *s; s++)
415 		{
416 			/* Handle newline */
417 			if (*s == '\n')
418 			{
419 				/* Terminate */
420 				buf[i] = '\0';
421 
422 				/* Success */
423 				return (0);
424 			}
425 
426 			/* Handle tabs */
427 			else if (*s == '\t')
428 			{
429 				/* Hack -- require room */
430 				if (i + 8 >= n) break;
431 
432 				/* Append a space */
433 				buf[i++] = ' ';
434 
435 				/* Append some more spaces */
436 				while (!(i % 8)) buf[i++] = ' ';
437 			}
438 
439 			/* Handle printables */
440 			else if (isprint(*s) || *s == '\377')
441 			{
442 				/* easier to edit perma files */
443 				if (conv && *s == '{' && *(s + 1) != '{')
444 					*s = '\377';
445 				/* Copy */
446 				buf[i++] = *s;
447 
448 				/* Check length */
449 				if (i >= n) break;
450 			}
451 		}
452 	}
453 
454 	/* Nothing */
455 	buf[0] = '\0';
456 
457 	/* Failure */
458 	return (1);
459 }
460 
461 
462 /*
463  * Hack -- replacement for "fputs()"
464  *
465  * Dump a string, plus a newline, to a file
466  *
467  * XXX XXX XXX Process internal weirdness?
468  */
my_fputs(FILE * fff,cptr buf,huge n)469 errr my_fputs(FILE *fff, cptr buf, huge n)
470 {
471 	/* XXX XXX */
472 	n = n ? n : 0;
473 
474 	/* Dump, ignore errors */
475 	(void)fprintf(fff, "%s\n", buf);
476 
477 	/* Success */
478 	return (0);
479 }
480 
481 
482 #ifdef ACORN
483 
484 
485 /*
486  * Most of the "file" routines for "ACORN" should be in "main-acn.c"
487  *
488  * Many of them can be rewritten now that only "fd_open()" and "fd_make()"
489  * and "my_fopen()" should ever create files.
490  */
491 
492 
493 #else /* ACORN */
494 
495 
496 /*
497  * Code Warrior is a little weird about some functions
498  */
499 #ifdef BEN_HACK
500 extern int open(const char *, int, ...);
501 extern int close(int);
502 extern int read(int, void *, unsigned int);
503 extern int write(int, const void *, unsigned int);
504 extern long lseek(int, long, int);
505 #endif /* BEN_HACK */
506 
507 
508 /*
509  * The Macintosh is a little bit brain-dead sometimes
510  */
511 #ifdef MACINTOSH
512 # define open(N,F,M) open((char*)(N),F)
513 # define write(F,B,S) write(F,(char*)(B),S)
514 #endif /* MACINTOSH */
515 
516 
517 /*
518  * Several systems have no "O_BINARY" flag
519  */
520 #ifndef O_BINARY
521 # define O_BINARY 0
522 #endif /* O_BINARY */
523 
524 
525 /*
526  * Hack -- attempt to delete a file
527  */
fd_kill(cptr file)528 errr fd_kill(cptr file)
529 {
530 	char                buf[1024];
531 
532 	/* Hack -- Try to parse the path */
533 	if (path_parse(buf, 1024, file)) return (-1);
534 
535 	/* Remove */
536 	(void)remove(buf);
537 
538 	/* XXX XXX XXX */
539 	return (0);
540 }
541 
542 
543 /*
544  * Hack -- attempt to move a file
545  */
fd_move(cptr file,cptr what)546 errr fd_move(cptr file, cptr what)
547 {
548 	char                buf[1024];
549 	char                aux[1024];
550 
551 	/* Hack -- Try to parse the path */
552 	if (path_parse(buf, 1024, file)) return (-1);
553 
554 	/* Hack -- Try to parse the path */
555 	if (path_parse(aux, 1024, what)) return (-1);
556 
557 	/* Rename */
558 	(void)rename(buf, aux);
559 
560 	/* XXX XXX XXX */
561 	return (0);
562 }
563 
564 
565 /*
566  * Hack -- attempt to copy a file
567  */
fd_copy(cptr file,cptr what)568 errr fd_copy(cptr file, cptr what)
569 {
570 	char                buf[1024];
571 	char                aux[1024];
572 
573 	/* Hack -- Try to parse the path */
574 	if (path_parse(buf, 1024, file)) return (-1);
575 
576 	/* Hack -- Try to parse the path */
577 	if (path_parse(aux, 1024, what)) return (-1);
578 
579 	/* Copy XXX XXX XXX */
580 	/* (void)rename(buf, aux); */
581 
582 	/* XXX XXX XXX */
583 	return (1);
584 }
585 
586 
587 /*
588  * Hack -- attempt to open a file descriptor (create file)
589  *
590  * This function should fail if the file already exists
591  *
592  * Note that we assume that the file should be "binary"
593  *
594  * XXX XXX XXX The horrible "BEN_HACK" code is for compiling under
595  * the CodeWarrior compiler, in which case, for some reason, none
596  * of the "O_*" flags are defined, and we must fake the definition
597  * of "O_RDONLY", "O_WRONLY", and "O_RDWR" in "A-win-h", and then
598  * we must simulate the effect of the proper "open()" call below.
599  */
fd_make(cptr file,int mode)600 int fd_make(cptr file, int mode)
601 {
602 	char                buf[1024];
603 
604 	/* Hack -- Try to parse the path */
605 	if (path_parse(buf, 1024, file)) return (-1);
606 
607 #ifdef BEN_HACK
608 
609 	/* Check for existence */
610 	/* if (fd_close(fd_open(file, O_RDONLY | O_BINARY))) return (1); */
611 
612 	/* Mega-Hack -- Create the file */
613 	(void)my_fclose(my_fopen(file, "wb"));
614 
615 	/* Re-open the file for writing */
616 	return (open(buf, O_WRONLY | O_BINARY, mode));
617 
618 #else /* BEN_HACK */
619 
620 	/* Create the file, fail if exists, write-only, binary */
621 	return (open(buf, O_CREAT | O_EXCL | O_WRONLY | O_BINARY, mode));
622 
623 #endif /* BEN_HACK */
624 
625 }
626 
627 
628 /*
629  * Hack -- attempt to open a file descriptor (existing file)
630  *
631  * Note that we assume that the file should be "binary"
632  */
fd_open(cptr file,int flags)633 int fd_open(cptr file, int flags)
634 {
635 	char                buf[1024];
636 
637 	/* Hack -- Try to parse the path */
638 	if (path_parse(buf, 1024, file)) return (-1);
639 
640 	/* Attempt to open the file */
641 	return (open(buf, flags | O_BINARY, 0));
642 }
643 
644 
645 /*
646  * Hack -- attempt to lock a file descriptor
647  *
648  * Legal lock types -- F_UNLCK, F_RDLCK, F_WRLCK
649  */
fd_lock(int fd,int what)650 errr fd_lock(int fd, int what)
651 {
652 	/* XXX XXX */
653 	what = what ? what : 0;
654 
655 	/* Verify the fd */
656 	if (fd < 0) return (-1);
657 
658 #ifdef SET_UID
659 
660 # ifdef USG
661 
662 	/* Un-Lock */
663 	if (what == F_UNLCK)
664 	{
665 		/* Unlock it, Ignore errors */
666 		lockf(fd, F_ULOCK, 0);
667 	}
668 
669 	/* Lock */
670 	else
671 	{
672 		/* Lock the score file */
673 		if (lockf(fd, F_LOCK, 0) != 0) return (1);
674 	}
675 
676 #else
677 
678 	/* Un-Lock */
679 	if (what == F_UNLCK)
680 	{
681 		/* Unlock it, Ignore errors */
682 		(void)flock(fd, LOCK_UN);
683 	}
684 
685 	/* Lock */
686 	else
687 	{
688 		/* Lock the score file */
689 		if (flock(fd, LOCK_EX) != 0) return (1);
690 	}
691 
692 # endif
693 
694 #endif
695 
696 	/* Success */
697 	return (0);
698 }
699 
700 
701 /*
702  * Hack -- attempt to seek on a file descriptor
703  */
fd_seek(int fd,huge n)704 errr fd_seek(int fd, huge n)
705 {
706 	huge p;
707 
708 	/* Verify fd */
709 	if (fd < 0) return (-1);
710 
711 	/* Seek to the given position */
712 	p = lseek(fd, n, SEEK_SET);
713 
714 	/* Failure */
715 	if (p == (huge) -1) return (1);
716 
717 	/* Failure */
718 	if (p != n) return (1);
719 
720 	/* Success */
721 	return (0);
722 }
723 
724 
725 /*
726  * Hack -- attempt to read data from a file descriptor
727  */
fd_read(int fd,char * buf,huge n)728 errr fd_read(int fd, char *buf, huge n)
729 {
730 	/* Verify the fd */
731 	if (fd < 0) return (-1);
732 
733 #ifndef SET_UID
734 
735 	/* Read pieces */
736 	while (n >= 16384)
737 	{
738 		/* Read a piece */
739 		if (read(fd, buf, 16384) != 16384) return (1);
740 
741 		/* Shorten the task */
742 		buf += 16384;
743 
744 		/* Shorten the task */
745 		n -= 16384;
746 	}
747 
748 #endif
749 
750 	/* Read the final piece */
751 	if ((huge) read(fd, buf, n) != n) return (1);
752 
753 	/* Success */
754 	return (0);
755 }
756 
757 
758 /*
759  * Hack -- Attempt to write data to a file descriptor
760  */
fd_write(int fd,cptr buf,huge n)761 errr fd_write(int fd, cptr buf, huge n)
762 {
763 	/* Verify the fd */
764 	if (fd < 0) return (-1);
765 
766 #ifndef SET_UID
767 
768 	/* Write pieces */
769 	while (n >= 16384)
770 	{
771 		/* Write a piece */
772 		if (write(fd, buf, 16384) != 16384) return (1);
773 
774 		/* Shorten the task */
775 		buf += 16384;
776 
777 		/* Shorten the task */
778 		n -= 16384;
779 	}
780 
781 #endif
782 
783 	/* Write the final piece */
784 	if ((huge) write(fd, buf, n) != n) return (1);
785 
786 	/* Success */
787 	return (0);
788 }
789 
790 
791 /*
792  * Hack -- attempt to close a file descriptor
793  */
fd_close(int fd)794 errr fd_close(int fd)
795 {
796 	/* Verify the fd */
797 	if (fd < 0) return (-1);
798 
799 	/* Close */
800 	(void)close(fd);
801 
802 	/* XXX XXX XXX */
803 	return (0);
804 }
805 
806 
807 #endif /* ACORN */
808 
809 /*
810  * Convert a decimal to a single digit octal number
811  */
octify(uint i)812 static char octify(uint i)
813 {
814 	return (hexsym[i%8]);
815 }
816 
817 /*
818  * Convert a decimal to a single digit hex number
819  */
hexify(uint i)820 static char hexify(uint i)
821 {
822 	return (hexsym[i%16]);
823 }
824 
825 
826 /*
827  * Convert a octal-digit into a decimal
828  */
deoct(char c)829 static int deoct(char c)
830 {
831 	if (isdigit(c)) return (D2I(c));
832 	return (0);
833 }
834 
835 /*
836  * Convert a hexidecimal-digit into a decimal
837  */
dehex(char c)838 static int dehex(char c)
839 {
840 	if (isdigit(c)) return (D2I(c));
841 	if (islower(c)) return (A2I(c) + 10);
842 	if (isupper(c)) return (A2I(tolower(c)) + 10);
843 	return (0);
844 }
845 
846 
847 /*
848  * Hack -- convert a printable string into real ascii
849  *
850  * I have no clue if this function correctly handles, for example,
851  * parsing "\xFF" into a (signed) char.  Whoever thought of making
852  * the "sign" of a "char" undefined is a complete moron.  Oh well.
853  */
text_to_ascii(char * buf,cptr str)854 void text_to_ascii(char *buf, cptr str)
855 {
856 	char *s = buf;
857 
858 	/* Analyze the "ascii" string */
859 	while (*str)
860 	{
861 		/* Backslash codes */
862 		if (*str == '\\')
863 		{
864 			/* Skip the backslash */
865 			str++;
866 
867 			/* Hex-mode XXX */
868 			if (*str == 'x')
869 			{
870 				*s = 16 * dehex(*++str);
871 				*s++ += dehex(*++str);
872 			}
873 
874 			/* Hack -- simple way to specify "backslash" */
875 			else if (*str == '\\')
876 			{
877 				*s++ = '\\';
878 			}
879 
880 			/* Hack -- simple way to specify "caret" */
881 			else if (*str == '^')
882 			{
883 				*s++ = '^';
884 			}
885 
886 			/* Hack -- simple way to specify "space" */
887 			else if (*str == 's')
888 			{
889 				*s++ = ' ';
890 			}
891 
892 			/* Hack -- simple way to specify Escape */
893 			else if (*str == 'e')
894 			{
895 				*s++ = ESCAPE;
896 			}
897 
898 			/* Backspace */
899 			else if (*str == 'b')
900 			{
901 				*s++ = '\b';
902 			}
903 
904 			/* Newline */
905 			else if (*str == 'n')
906 			{
907 				*s++ = '\n';
908 			}
909 
910 			/* Return */
911 			else if (*str == 'r')
912 			{
913 				*s++ = '\r';
914 			}
915 
916 			/* Tab */
917 			else if (*str == 't')
918 			{
919 				*s++ = '\t';
920 			}
921 
922 			/* Octal-mode */
923 			else if (*str == '0')
924 			{
925 				*s = 8 * deoct(*++str);
926 				*s++ += deoct(*++str);
927 			}
928 
929 			/* Octal-mode */
930 			else if (*str == '1')
931 			{
932 				*s = 64 + 8 * deoct(*++str);
933 				*s++ += deoct(*++str);
934 			}
935 
936 			/* Octal-mode */
937 			else if (*str == '2')
938 			{
939 				*s = 64 * 2 + 8 * deoct(*++str);
940 				*s++ += deoct(*++str);
941 			}
942 
943 			/* Octal-mode */
944 			else if (*str == '3')
945 			{
946 				*s = 64 * 3 + 8 * deoct(*++str);
947 				*s++ += deoct(*++str);
948 			}
949 
950 			/* Skip the final char */
951 			str++;
952 		}
953 
954 		/* Normal Control codes */
955 		else if (*str == '^')
956 		{
957 			str++;
958 			*s++ = (*str++ & 037);
959 		}
960 
961 		/* Normal chars */
962 		else
963 		{
964 			*s++ = *str++;
965 		}
966 	}
967 
968 	/* Terminate */
969 	*s = '\0';
970 }
971 
972 
973 /*
974  * Hack -- convert a string into a printable form
975  */
ascii_to_text(char * buf,cptr str)976 void ascii_to_text(char *buf, cptr str)
977 {
978 	char *s = buf;
979 
980 	/* Analyze the "ascii" string */
981 	while (*str)
982 	{
983 		byte i = (byte)(*str++);
984 
985 		if (i == ESCAPE)
986 		{
987 			*s++ = '\\';
988 			*s++ = 'e';
989 		}
990 		else if (i == ' ')
991 		{
992 			*s++ = '\\';
993 			*s++ = 's';
994 		}
995 		else if (i == '\b')
996 		{
997 			*s++ = '\\';
998 			*s++ = 'b';
999 		}
1000 		else if (i == '\t')
1001 		{
1002 			*s++ = '\\';
1003 			*s++ = 't';
1004 		}
1005 		else if (i == '\n')
1006 		{
1007 			*s++ = '\\';
1008 			*s++ = 'n';
1009 		}
1010 		else if (i == '\r')
1011 		{
1012 			*s++ = '\\';
1013 			*s++ = 'r';
1014 		}
1015 		else if (i == '^')
1016 		{
1017 			*s++ = '\\';
1018 			*s++ = '^';
1019 		}
1020 		else if (i == '\\')
1021 		{
1022 			*s++ = '\\';
1023 			*s++ = '\\';
1024 		}
1025 		else if (i < 32)
1026 		{
1027 			*s++ = '^';
1028 			*s++ = i + 64;
1029 		}
1030 		else if (i < 127)
1031 		{
1032 			*s++ = i;
1033 		}
1034 		else if (i < 64)
1035 		{
1036 			*s++ = '\\';
1037 			*s++ = '0';
1038 			*s++ = octify(i / 8);
1039 			*s++ = octify(i % 8);
1040 		}
1041 		else
1042 		{
1043 			*s++ = '\\';
1044 			*s++ = 'x';
1045 			*s++ = hexify(i / 16);
1046 			*s++ = hexify(i % 16);
1047 		}
1048 	}
1049 
1050 	/* Terminate */
1051 	*s = '\0';
1052 }
1053 
1054 /*
1055  * Local variable -- we are inside a "control-underscore" sequence
1056  */
1057 /*static bool parse_under = FALSE;*/
1058 
1059 /*
1060  * Local variable -- we are inside a "control-backslash" sequence
1061  */
1062 /*static bool parse_slash = FALSE;*/
1063 
1064 /*
1065  * Local variable -- we are stripping symbols for a while
1066  */
1067 /*static bool strip_chars = FALSE;*/
1068 
1069 /*
1070  * Flush the screen, make a noise
1071  */
bell(void)1072 void bell(void)
1073 {
1074 }
1075 
1076 
1077 /*
1078  * Mega-Hack -- Make a (relevant?) sound
1079  */
1080 #ifndef USE_SOUND_2010
sound(int Ind,int val)1081 void sound(int Ind, int val) {
1082 //	Send_sound(Ind, val);
1083 	Send_sound(Ind, val, 0, 0, 100, 0);
1084 }
1085 #else
1086 /* 'type' is used client-side, for efficiency options concerning near-simultaneous sounds
1087    'nearby' means if other players nearby would be able to also hear the sound. - C. Blue */
sound(int Ind,cptr name,cptr alternative,int type,bool nearby)1088 void sound(int Ind, cptr name, cptr alternative, int type, bool nearby) {
1089 #if 0 /* non-optimized way (causes LUA errors if sound() is called in fire_ball() which is in turn called from LUA - C. Blue */
1090 	int val, val2;
1091 
1092 	val = exec_lua(0, format("return get_sound_index(\"%s\")", name));
1093 
1094 	if (alternative) {
1095 		val2 = exec_lua(0, format("return get_sound_index(\"%s\")", alternative));
1096 	} else {
1097 		val2 = -1;
1098 	}
1099 
1100 #else /* optimized way... */
1101 	int val = -1, val2 = -1, i, d;
1102 
1103 	if (name) for (i = 0; i < SOUND_MAX_2010; i++) {
1104 		if (!audio_sfx[i][0]) break;
1105 		if (!strcmp(audio_sfx[i], name)) {
1106 			val = i;
1107 			break;
1108 		}
1109 	}
1110 
1111 	if (alternative) for (i = 0; i < SOUND_MAX_2010; i++) {
1112 		if (!audio_sfx[i][0]) break;
1113 		if (!strcmp(audio_sfx[i], alternative)) {
1114 			val2 = i;
1115 			break;
1116 		}
1117 	}
1118 
1119 #endif
1120 
1121 //	if (is_admin(Players[Ind])) s_printf("USE_SOUND_2010: looking up sound %s -> %d.\n", name, val);
1122 
1123 	if (val == -1) {
1124 		if (val2 != -1) {
1125 			/* Use the alternative instead */
1126 			val = val2;
1127 			val2 = -1;
1128 		} else {
1129 			return;
1130 		}
1131 	}
1132 
1133 	/* also send sounds to nearby players, depending on sound or sound type */
1134 	if (nearby) {
1135 		for (i = 1; i <= NumPlayers; i++) {
1136 			if (Players[i]->conn == NOT_CONNECTED) continue;
1137 			if (!inarea(&Players[i]->wpos, &Players[Ind]->wpos)) continue;
1138 			if (Ind == i) continue;
1139 
1140 			d = distance(Players[Ind]->py, Players[Ind]->px, Players[i]->py, Players[i]->px);
1141 #if 0
1142 			if (d > 10) continue;
1143 			if (d == 0) d = 1; //paranoia oO
1144 
1145 			Send_sound(i, val, val2, type, 100 / d, Players[Ind]->id);
1146 //			Send_sound(i, val, val2, type, (6 - d) * 20, Players[Ind]->id);  hm or this?
1147 #else
1148 			if (d > MAX_SIGHT) continue;
1149 			d += 3;
1150 			d /= 2;
1151 
1152 			Send_sound(i, val, val2, type, 100 / d, Players[Ind]->id);
1153 #endif
1154 		}
1155 	}
1156 
1157 	Send_sound(Ind, val, val2, type, 100, Players[Ind]->id);
1158 }
sound_vol(int Ind,cptr name,cptr alternative,int type,bool nearby,int vol)1159 void sound_vol(int Ind, cptr name, cptr alternative, int type, bool nearby, int vol) {
1160 	int val = -1, val2 = -1, i, d;
1161 
1162 	if (name) for (i = 0; i < SOUND_MAX_2010; i++) {
1163 		if (!audio_sfx[i][0]) break;
1164 		if (!strcmp(audio_sfx[i], name)) {
1165 			val = i;
1166 			break;
1167 		}
1168 	}
1169 
1170 	if (alternative) for (i = 0; i < SOUND_MAX_2010; i++) {
1171 		if (!audio_sfx[i][0]) break;
1172 		if (!strcmp(audio_sfx[i], alternative)) {
1173 			val2 = i;
1174 			break;
1175 		}
1176 	}
1177 
1178 
1179 //	if (is_admin(Players[Ind])) s_printf("USE_SOUND_2010: looking up sound %s -> %d.\n", name, val);
1180 
1181 	if (val == -1) {
1182 		if (val2 != -1) {
1183 			/* Use the alternative instead */
1184 			val = val2;
1185 			val2 = -1;
1186 		} else {
1187 			return;
1188 		}
1189 	}
1190 
1191 	/* also send sounds to nearby players, depending on sound or sound type */
1192 	if (nearby) {
1193 		for (i = 1; i <= NumPlayers; i++) {
1194 			if (Players[i]->conn == NOT_CONNECTED) continue;
1195 			if (!inarea(&Players[i]->wpos, &Players[Ind]->wpos)) continue;
1196 			if (Ind == i) continue;
1197 
1198 			d = distance(Players[Ind]->py, Players[Ind]->px, Players[i]->py, Players[i]->px);
1199 #if 0
1200 			if (d > 10) continue;
1201 			if (d == 0) d = 1; //paranoia oO
1202 
1203 			Send_sound(i, val, val2, type, vol / d, Players[Ind]->id);
1204 //			Send_sound(i, val, val2, type, vol... (6 - d) * 20, Players[Ind]->id);  hm or this?
1205 #else
1206 			if (d > MAX_SIGHT) continue;
1207 			d += 3;
1208 			d /= 2;
1209 
1210 			Send_sound(i, val, val2, type, vol / d, Players[Ind]->id);
1211 #endif
1212 		}
1213 	}
1214 	Send_sound(Ind, val, val2, type, vol, Players[Ind]->id);
1215 }
1216 /* Send sound to everyone on a particular wpos sector/dungeon floor */
sound_floor_vol(struct worldpos * wpos,cptr name,cptr alternative,int type,int vol)1217 void sound_floor_vol(struct worldpos *wpos, cptr name, cptr alternative, int type, int vol) {
1218 	int val = -1, val2 = -1, i;
1219 
1220 	if (name) for (i = 0; i < SOUND_MAX_2010; i++) {
1221 		if (!audio_sfx[i][0]) break;
1222 		if (!strcmp(audio_sfx[i], name)) {
1223 			val = i;
1224 			break;
1225 		}
1226 	}
1227 
1228 	if (alternative) for (i = 0; i < SOUND_MAX_2010; i++) {
1229 		if (!audio_sfx[i][0]) break;
1230 		if (!strcmp(audio_sfx[i], alternative)) {
1231 			val2 = i;
1232 			break;
1233 		}
1234 	}
1235 
1236 	if (val == -1) {
1237 		if (val2 != -1) {
1238 			/* Use the alternative instead */
1239 			val = val2;
1240 			val2 = -1;
1241 		} else {
1242 			return;
1243 		}
1244 	}
1245 
1246 	for (i = 1; i <= NumPlayers; i++) {
1247 		if (Players[i]->conn == NOT_CONNECTED) continue;
1248 		if (!inarea(&Players[i]->wpos, wpos)) continue;
1249 		Send_sound(i, val, val2, type, vol, Players[i]->id);
1250 	}
1251 }
1252 /* send sound to player and everyone nearby at full volume, similar to
1253    msg_..._near(), except it also sends to the player himself.
1254    This is used for highly important and *loud* sounds such as 'shriek' - C. Blue */
sound_near(int Ind,cptr name,cptr alternative,int type)1255 void sound_near(int Ind, cptr name, cptr alternative, int type) {
1256 	int i, d;
1257 	for (i = 1; i <= NumPlayers; i++) {
1258 		if (Players[i]->conn == NOT_CONNECTED) continue;
1259 		if (!inarea(&Players[i]->wpos, &Players[Ind]->wpos)) continue;
1260 
1261 		/* Can player see the target player? */
1262 //		if (!(Players[i]->cave_flag[Players[Ind]->py][Players[Ind]->px] & CAVE_VIEW)) continue;
1263 
1264 		/* within audible range? */
1265 		d = distance(Players[Ind]->py, Players[Ind]->px, Players[i]->py, Players[i]->px);
1266 		if (d > MAX_SIGHT) continue;
1267 
1268 #ifndef SFX_SHRIEK_VOLUME
1269 		if (strcmp(name, "shriek") || Players[i]->sfx_shriek)
1270 			sound(i, name, alternative, type, FALSE);
1271 #else
1272 		if (strcmp(name, "shriek"))
1273 			sound(i, name, alternative, type, FALSE);
1274 		else if (Players[i]->sfx_shriek)
1275 			sound_vol(i, name, alternative, type, FALSE, SFX_SHRIEK_VOLUME);
1276 #endif
1277 	}
1278 }
1279 /* send sound to all players nearby a certain location, and allow to specify
1280    a player to exclude, same as msg_print_near_site() for messages. - C. Blue */
sound_near_site(int y,int x,worldpos * wpos,int Ind,cptr name,cptr alternative,int type,bool viewable)1281 void sound_near_site(int y, int x, worldpos *wpos, int Ind, cptr name, cptr alternative, int type, bool viewable) {
1282 	int i, d;
1283 	player_type *p_ptr;
1284 	int val = -1, val2 = -1;
1285 
1286 	if (name) for (i = 0; i < SOUND_MAX_2010; i++) {
1287 		if (!audio_sfx[i][0]) break;
1288 		if (!strcmp(audio_sfx[i], name)) {
1289 			val = i;
1290 			break;
1291 		}
1292 	}
1293 
1294 	if (alternative) for (i = 0; i < SOUND_MAX_2010; i++) {
1295 		if (!audio_sfx[i][0]) break;
1296 		if (!strcmp(audio_sfx[i], alternative)) {
1297 			val2 = i;
1298 			break;
1299 		}
1300 	}
1301 
1302 	if (val == -1) {
1303 		if (val2 != -1) {
1304 			/* Use the alternative instead */
1305 			val = val2;
1306 			val2 = -1;
1307 		} else {
1308 			return;
1309 		}
1310 	}
1311 
1312 	/* Check each player */
1313 	for (i = 1; i <= NumPlayers; i++) {
1314 		/* Check this player */
1315 		p_ptr = Players[i];
1316 
1317 		/* Make sure this player is in the game */
1318 		if (p_ptr->conn == NOT_CONNECTED) continue;
1319 
1320 		/* Skip specified player, if any */
1321 		if (i == Ind) continue;
1322 
1323 		/* Make sure this player is at this depth */
1324 		if (!inarea(&p_ptr->wpos, wpos)) continue;
1325 
1326 		/* Can player see the site via LOS? */
1327 		if (viewable && !(p_ptr->cave_flag[y][x] & CAVE_VIEW)) continue;
1328 
1329 		/* within audible range? */
1330 		d = distance(y, x, Players[i]->py, Players[i]->px);
1331 		/* NOTE: should be consistent with msg_print_near_site() */
1332 		if (d > MAX_SIGHT) continue;
1333 
1334 #if 0
1335 		/* limit for volume calc */
1336 		if (d > 20) d = 20;
1337 		d += 3;
1338 		d /= 3;
1339 		Send_sound(i, val, val2, type, 100 / d, 0);
1340 #else
1341 		/* limit for volume calc */
1342 		Send_sound(i, val, val2, type, 100 - (d * 50) / 11, 0);
1343 #endif
1344 	}
1345 }
1346 /* Play sfx at full volume to everyone in a house, and at normal-distance volume to
1347    everyone near the door (as sound_near_site() would). */
sound_house_knock(int h_idx,int dx,int dy)1348 void sound_house_knock(int h_idx, int dx, int dy) {
1349 	house_type *h_ptr = &houses[h_idx];
1350 
1351 	fill_house(h_ptr, FILL_SFX_KNOCK, NULL);
1352 
1353 	/* basically sound_near_site(), except we skip players on CAVE_ICKY grids, ie those who are in either this or other houses! */
1354 	int i, d;
1355 	player_type *p_ptr;
1356 	int val = -1, val2 = -1;
1357 
1358 	const char *name = (houses[h_idx].flags & HF_MOAT) ? "knock_castle" : "knock";
1359 	const char *alternative = "block_shield_projectile";
1360 	cave_type **zcave;
1361 
1362 	if (name) for (i = 0; i < SOUND_MAX_2010; i++) {
1363 		if (!audio_sfx[i][0]) break;
1364 		if (!strcmp(audio_sfx[i], name)) {
1365 			val = i;
1366 			break;
1367 		}
1368 	}
1369 
1370 	if (alternative) for (i = 0; i < SOUND_MAX_2010; i++) {
1371 		if (!audio_sfx[i][0]) break;
1372 		if (!strcmp(audio_sfx[i], alternative)) {
1373 			val2 = i;
1374 			break;
1375 		}
1376 	}
1377 
1378 	if (val == -1) {
1379 		if (val2 != -1) {
1380 			/* Use the alternative instead */
1381 			val = val2;
1382 			val2 = -1;
1383 		} else {
1384 			return;
1385 		}
1386 	}
1387 
1388 	/* Check each player */
1389 	for (i = 1; i <= NumPlayers; i++) {
1390 		/* Check this player */
1391 		p_ptr = Players[i];
1392 
1393 		/* Make sure this player is in the game */
1394 		if (p_ptr->conn == NOT_CONNECTED) continue;
1395 
1396 		/* Make sure this player is at this depth */
1397 		if (!inarea(&p_ptr->wpos, &h_ptr->wpos)) continue;
1398 
1399 		zcave = getcave(&p_ptr->wpos);
1400 		if (!zcave) continue;//paranoia
1401 		/* Specialty for sound_house(): Is player NOT in a house? */
1402 		if ((zcave[p_ptr->py][p_ptr->px].info & CAVE_ICKY)
1403 		    /* however, exempt the moat! */
1404 		    && zcave[p_ptr->py][p_ptr->px].feat != FEAT_DRAWBRIDGE
1405 		    && zcave[p_ptr->py][p_ptr->px].feat != FEAT_DEEP_WATER)
1406 			continue;
1407 
1408 		/* within audible range? */
1409 		d = distance(dy, dx, Players[i]->py, Players[i]->px);
1410 		/* hack: if we knock on the door, assume distance 0 between us and door */
1411 		if (d > 0) d--;
1412 		/* NOTE: should be consistent with msg_print_near_site() */
1413 		if (d > MAX_SIGHT) continue;
1414 
1415 #if 0
1416 		/* limit for volume calc */
1417 		if (d > 20) d = 20;
1418 		d += 3;
1419 		d /= 3;
1420 		Send_sound(i, val, val2, SFX_TYPE_COMMAND, 100 / d, 0);
1421 #else
1422 		/* limit for volume calc */
1423 		Send_sound(i, val, val2, SFX_TYPE_COMMAND, 100 - (d * 50) / 11, 0);
1424 #endif
1425 	}
1426 
1427 }
1428 /* like msg_print_near_monster() just for sounds,
1429    basically same as sound_near_site() except no player can be exempt - C. Blue */
sound_near_monster(int m_idx,cptr name,cptr alternative,int type)1430 void sound_near_monster(int m_idx, cptr name, cptr alternative, int type) {
1431 	int i, d;
1432 	player_type *p_ptr;
1433 	cave_type **zcave;
1434 
1435 	monster_type *m_ptr = &m_list[m_idx];
1436 	worldpos *wpos = &m_ptr->wpos;
1437 	int val = -1, val2 = -1;
1438 
1439 	if (!(zcave = getcave(wpos))) return;
1440 
1441 	if (name) for (i = 0; i < SOUND_MAX_2010; i++) {
1442 		if (!audio_sfx[i][0]) break;
1443 		if (!strcmp(audio_sfx[i], name)) {
1444 			val = i;
1445 			break;
1446 		}
1447 	}
1448 
1449 	if (alternative) for (i = 0; i < SOUND_MAX_2010; i++) {
1450 		if (!audio_sfx[i][0]) break;
1451 		if (!strcmp(audio_sfx[i], alternative)) {
1452 			val2 = i;
1453 			break;
1454 		}
1455 	}
1456 
1457 	if (val == -1) {
1458 		if (val2 != -1) {
1459 			/* Use the alternative instead */
1460 			val = val2;
1461 			val2 = -1;
1462 		} else {
1463 			return;
1464 		}
1465 	}
1466 
1467 	/* Check each player */
1468 	for (i = 1; i <= NumPlayers; i++) {
1469 		/* Check this player */
1470 		p_ptr = Players[i];
1471 
1472 		/* Make sure this player is in the game */
1473 		if (p_ptr->conn == NOT_CONNECTED) continue;
1474 
1475 		/* Make sure this player is at this depth */
1476 		if (!inarea(&p_ptr->wpos, wpos)) continue;
1477 
1478 		/* Skip if not visible */
1479 //		if (!p_ptr->mon_vis[m_idx]) continue;
1480 
1481 		/* Can player see this monster via LOS? */
1482 //		if (!(p_ptr->cave_flag[m_ptr->fy][m_ptr->fx] & CAVE_VIEW)) continue;
1483 
1484 		/* within audible range? */
1485 		d = distance(m_ptr->fy, m_ptr->fx, Players[i]->py, Players[i]->px);
1486 
1487 		/* NOTE: should be consistent with msg_print_near_site() */
1488 		if (d > MAX_SIGHT) continue;
1489 
1490 #if 0
1491 		/* limit for volume calc */
1492 		if (d > 20) d = 20;
1493 		d += 3;
1494 		d /= 3;
1495 		Send_sound(i, val, val2, type, 100 / d, 0);
1496 #else
1497 		/* limit for volume calc */
1498 		Send_sound(i, val, val2, type, 100 - (d * 50) / 11, 0);
1499 #endif
1500 	}
1501 }
1502 /* like msg_print_near_monster() just for sounds,
1503    basically same as sound_near_site(). - C. Blue
1504    NOTE: This function assumes that it's playing a MONSTER ATTACK type sound,
1505          because it's checking p_ptr->sfx_monsterattack to mute it. */
sound_near_monster_atk(int m_idx,int Ind,cptr name,cptr alternative,int type)1506 void sound_near_monster_atk(int m_idx, int Ind, cptr name, cptr alternative, int type) {
1507 	int i, d;
1508 	player_type *p_ptr;
1509 	cave_type **zcave;
1510 
1511 	monster_type *m_ptr = &m_list[m_idx];
1512 	worldpos *wpos = &m_ptr->wpos;
1513 	int val = -1, val2 = -1;
1514 
1515 	if (!(zcave = getcave(wpos))) return;
1516 
1517 	if (name) for (i = 0; i < SOUND_MAX_2010; i++) {
1518 		if (!audio_sfx[i][0]) break;
1519 		if (!strcmp(audio_sfx[i], name)) {
1520 			val = i;
1521 			break;
1522 		}
1523 	}
1524 
1525 	if (alternative) for (i = 0; i < SOUND_MAX_2010; i++) {
1526 		if (!audio_sfx[i][0]) break;
1527 		if (!strcmp(audio_sfx[i], alternative)) {
1528 			val2 = i;
1529 			break;
1530 		}
1531 	}
1532 
1533 	if (val == -1) {
1534 		if (val2 != -1) {
1535 			/* Use the alternative instead */
1536 			val = val2;
1537 			val2 = -1;
1538 		} else {
1539 			return;
1540 		}
1541 	}
1542 
1543 	/* Check each player */
1544 	for (i = 1; i <= NumPlayers; i++) {
1545 		/* Check this player */
1546 		p_ptr = Players[i];
1547 
1548 		/* Make sure this player is in the game */
1549 		if (p_ptr->conn == NOT_CONNECTED) continue;
1550 
1551 		/* Skip specified player, if any */
1552 		if (i == Ind) continue;
1553 
1554 		/* Skip players who don't want to hear attack sounds */
1555 		if (!p_ptr->sfx_monsterattack) continue;
1556 
1557 		/* Make sure this player is at this depth */
1558 		if (!inarea(&p_ptr->wpos, wpos)) continue;
1559 
1560 		/* Skip if not visible */
1561 //		if (!p_ptr->mon_vis[m_idx]) continue;
1562 
1563 		/* Can player see this monster via LOS? */
1564 //		if (!(p_ptr->cave_flag[m_ptr->fy][m_ptr->fx] & CAVE_VIEW)) continue;
1565 
1566 		/* within audible range? */
1567 		d = distance(m_ptr->fy, m_ptr->fx, Players[i]->py, Players[i]->px);
1568 
1569 		/* NOTE: should be consistent with msg_print_near_site() */
1570 		if (d > MAX_SIGHT) continue;
1571 
1572 #if 0
1573 		/* limit for volume calc */
1574 		if (d > 20) d = 20;
1575 		d += 3;
1576 		d /= 3;
1577 		Send_sound(i, val, val2, type, 100 / d, 0);
1578 #else
1579 		/* limit for volume calc */
1580 		Send_sound(i, val, val2, type, 100 - (d * 50) / 11, 0);
1581 #endif
1582 	}
1583 }
1584 
1585 /* Find correct music for the player based on his current location - C. Blue
1586  * Note - rarely played music:
1587  *     dungeons - generic/ironman/forcedownhellish
1588  *     towns - generic day/night (used for Menegroth/Nargothrond at times)
1589  * Note - dun-gen-iron and dun-gen-fdhell are currently swapped. */
handle_music(int Ind)1590 void handle_music(int Ind) {
1591 	player_type *p_ptr = Players[Ind];
1592 	dun_level *l_ptr = NULL;
1593 	int i = -1, tmus = 0, tmus_inverse = 0, dlev = getlevel(&p_ptr->wpos);
1594 	dungeon_type *d_ptr = getdungeon(&p_ptr->wpos);
1595 	cave_type **zcave = getcave(&p_ptr->wpos);
1596 
1597 #ifdef ARCADE_SERVER
1598 	/* Special music for Arcade Server stuff */
1599 	if (p_ptr->wpos.wx == WPOS_ARCADE_X && p_ptr->wpos.wy == WPOS_ARCADE_Y
1600 //	    && p_ptr->wpos.wz == WPOS_ARCADE_Z
1601 	    ) {
1602 		p_ptr->music_monster = -2;
1603  #if 0
1604 		if (p_ptr->wpos.wz == 0) Send_music(Ind, 1, -1); /* 'generic town' music instead of Bree default */
1605 		else {
1606 			//47 and 48 are actually pieces used in other arena events
1607 			if (rand_int(2)) Send_music(Ind, 47, 1);
1608 			else Send_music(Ind, 48, 1);
1609 		}
1610  #else
1611 		if (p_ptr->wpos.wz == 0) Send_music(Ind, 48, 1); /* 'arena' ;) sounds a bit like Unreal Tournament menu music hehe */
1612 		else Send_music(Ind, 47, 1); /* 'death match' music (pvp arena) */
1613  #endif
1614 		return;
1615 	}
1616 #endif
1617 
1618 #ifdef IRONDEEPDIVE_MIXED_TYPES
1619 	if (in_irondeepdive(&p_ptr->wpos)) {
1620 		l_ptr = getfloor(&p_ptr->wpos);
1621  #if 0 /* kinda annoying to have 2 floors of 'unspecified' music - C. Blue */
1622 		if (!iddc[ABS(p_ptr->wpos.wz)].step) i = iddc[ABS(p_ptr->wpos.wz)].type;
1623 		else i = 0; //Transition floors
1624  #else
1625 		if (iddc[ABS(p_ptr->wpos.wz)].step < 2) i = iddc[ABS(p_ptr->wpos.wz)].type;
1626 		else i = iddc[ABS(p_ptr->wpos.wz)].next;
1627  #endif
1628 	} else
1629 #endif
1630 	if (p_ptr->wpos.wz != 0) {
1631 		l_ptr = getfloor(&p_ptr->wpos);
1632 		if (p_ptr->wpos.wz < 0) {
1633 			i = wild_info[p_ptr->wpos.wy][p_ptr->wpos.wx].dungeon->type;
1634 #if 1			/* allow 'themed' music too? (or keep music as 'original dungeon theme' exclusively) */
1635 			if (!i && //except if this dungeon has particular flags (because flags are NOT affected by theme):
1636 			    wild_info[p_ptr->wpos.wy][p_ptr->wpos.wx].dungeon->theme &&
1637 			    !(d_ptr->flags1 & DF1_FORCE_DOWN) && !(d_ptr->flags2 & (DF2_NO_DEATH | DF2_IRON | DF2_HELL)))
1638 				i = wild_info[p_ptr->wpos.wy][p_ptr->wpos.wx].dungeon->theme;
1639 #endif
1640 		} else {
1641 			i = wild_info[p_ptr->wpos.wy][p_ptr->wpos.wx].tower->type;
1642 #if 1			/* allow 'themed' music too? (or keep music as 'original dungeon theme' exclusively) */
1643 			if (!i && //except if this dungeon has particular flags (because flags are NOT affected by theme):
1644 			    wild_info[p_ptr->wpos.wy][p_ptr->wpos.wx].tower->theme &&
1645 			    !(d_ptr->flags1 & DF1_FORCE_DOWN) && !(d_ptr->flags2 & (DF2_NO_DEATH | DF2_IRON | DF2_HELL)))
1646 				i = wild_info[p_ptr->wpos.wy][p_ptr->wpos.wx].tower->theme;
1647 #endif
1648 		}
1649 	}
1650 
1651 	if (in_netherrealm(&p_ptr->wpos) && dlev == netherrealm_end) {
1652 		//Zu-Aon
1653 		//hack: init music as 'higher priority than boss-specific':
1654 		p_ptr->music_monster = -2;
1655 		Send_music(Ind, 45, 14);
1656 		return;
1657 	} else if ((i != -1) && (l_ptr->flags1 & LF1_NO_GHOST)) {
1658 		//Morgoth
1659 		//hack: init music as 'higher priority than boss-specific':
1660 		p_ptr->music_monster = -2;
1661 		Send_music(Ind, 44, 14);
1662 		return;
1663 	}
1664 
1665 	if (in_valinor(&p_ptr->wpos)) {
1666 		//hack: init music as 'higher priority than boss-specific':
1667 		p_ptr->music_monster = -2;
1668 		Send_music(Ind, 8, 1); //Valinor
1669 		return;
1670 	} else if (p_ptr->wpos.wx == WPOS_PVPARENA_X &&
1671 	    p_ptr->wpos.wy == WPOS_PVPARENA_Y && p_ptr->wpos.wz == WPOS_PVPARENA_Z) {
1672 		//hack: init music as 'higher priority than boss-specific':
1673 		p_ptr->music_monster = -2;
1674 		Send_music(Ind, 47, 0); //PvP Arena
1675 		return;
1676 	} else if (ge_special_sector && p_ptr->wpos.wx == WPOS_ARENA_X &&
1677 	    p_ptr->wpos.wy == WPOS_ARENA_Y && p_ptr->wpos.wz == WPOS_ARENA_Z) {
1678 		//hack: init music as 'higher priority than boss-specific':
1679 		p_ptr->music_monster = -2;
1680 		Send_music(Ind, 48, 0); //Monster Arena Challenge
1681 		return;
1682 	} else if (in_sector00(&p_ptr->wpos)) {
1683 		//hack: init music as 'higher priority than boss-specific':
1684 		p_ptr->music_monster = -2;
1685 		Send_music(Ind, sector00music, 0);
1686 		return;
1687 	}
1688 
1689 	/* No-tele grid: Re-use 'terrifying' bgm for this */
1690 	if (zcave && (zcave[p_ptr->py][p_ptr->px].info & CAVE_STCK)) {
1691 #if 0 /* hack: init music as 'higher priority than boss-specific': */
1692 		p_ptr->music_monster = -2;
1693 #endif
1694 		Send_music(Ind, 46, 14); //No-Tele vault
1695 		return;
1696 	}
1697 
1698 	/* rest of the music has lower priority than already running, boss-specific music */
1699 	if (p_ptr->music_monster != -1) return;
1700 
1701 
1702 	/* on world surface */
1703 	if (p_ptr->wpos.wz == 0) {
1704 		/* play town music also in its surrounding area of houses, if we're coming from the town? */
1705 		if (istownarea(&p_ptr->wpos, MAX_TOWNAREA)) {
1706 			i = wild_info[p_ptr->wpos.wy][p_ptr->wpos.wx].town_idx;
1707 
1708 			if (night_surface) switch (town[i].type) {
1709 			default:
1710 			case TOWN_VANILLA: tmus = 49; tmus_inverse = 1; break; //default town
1711 			case TOWN_BREE: tmus = 50; tmus_inverse = 3; break; //Bree
1712 			case TOWN_GONDOLIN: tmus = 51; tmus_inverse = 4; break; //Gondo
1713 			case TOWN_MINAS_ANOR: tmus = 52; tmus_inverse = 5; break; //Minas
1714 			case TOWN_LOTHLORIEN:	tmus = 53; tmus_inverse = 6; break; //Loth
1715 			case TOWN_KHAZADDUM: tmus = 54; tmus_inverse = 7; break; //Khaz
1716 			}
1717 			else switch (town[i].type) {
1718 			default:
1719 			case TOWN_VANILLA: tmus = 1; tmus_inverse = 49; break; //default town
1720 			case TOWN_BREE: tmus = 3; tmus_inverse = 50; break; //Bree
1721 			case TOWN_GONDOLIN: tmus = 4; tmus_inverse = 51; break; //Gondo
1722 			case TOWN_MINAS_ANOR: tmus = 5; tmus_inverse = 52; break; //Minas
1723 			case TOWN_LOTHLORIEN: tmus = 6; tmus_inverse = 53; break; //Loth
1724 			case TOWN_KHAZADDUM: tmus = 7; tmus_inverse = 54; break; //Khaz
1725 			}
1726 
1727 			/* now the specialty: If we're coming from elsewhere,
1728 			   we only switch to town music when we enter the town.
1729 			   If we're coming from the town, however, we keep the
1730 			   music while being in its surrounding area of houses. */
1731 			if (istown(&p_ptr->wpos) || p_ptr->music_current == tmus
1732 			    /* don't switch from town area music to wild music on day/night change: */
1733 			    || p_ptr->music_current == tmus_inverse)
1734 				Send_music(Ind, tmus, night_surface ? tmus_inverse : 1);
1735 			else if (night_surface) Send_music(Ind, 10, 0);
1736 			else Send_music(Ind, 9, 0);
1737 			return;
1738 		} else {
1739 			if (night_surface) Send_music(Ind, 10, 0);
1740 			else Send_music(Ind, 9, 0);
1741 			return;
1742 		}
1743 	/* in the dungeon */
1744 	} else {
1745 		/* Dungeon towns have their own music to bolster the player's motivation ;) */
1746 		if (isdungeontown(&p_ptr->wpos)) {
1747 			if (is_fixed_irondeepdive_town(&p_ptr->wpos, dlev)) {
1748 #if 0
1749 				Send_music(Ind, 1, 0); /* 'generic town' music instead, for a change */
1750 #else /* different music for static towns? */
1751 				if (dlev == 40) Send_music(Ind, 1, 0); /* Menegroth: generic town */
1752 				else Send_music(Ind, 49, 1); /* Nargothrond: generic town night */
1753 #endif
1754 			} else Send_music(Ind, 2, 1); /* the usual music for this case */
1755 			return;
1756 		}
1757 
1758 		/* Floor-specific music (monster/boss-independant)? */
1759 		if ((i != 6) /*not in Nether Realm, really ^^*/
1760 		    && (!(d_ptr->flags2 & DF2_NO_DEATH)) /* nor in easy dungeons */
1761 		    && !(p_ptr->wpos.wx == WPOS_PVPARENA_X /* and not in pvp-arena */
1762 		    && p_ptr->wpos.wy == WPOS_PVPARENA_Y && p_ptr->wpos.wz == WPOS_PVPARENA_Z))
1763 		{
1764 			if (p_ptr->distinct_floor_feeling || is_admin(p_ptr)) {
1765 				if (l_ptr->flags2 & LF2_OOD_HI) {
1766 					Send_music(Ind, 46, 11);
1767 					return;
1768 				}
1769 			}
1770 		}
1771 
1772 		//we could just look through audio.lua, by querying get_music_name() I guess..
1773 		switch (i) {
1774 		default:
1775 		case 0:
1776 			if (d_ptr->flags2 & DF2_NO_DEATH) Send_music(Ind, 12, 11);//note: music file is identical to the one of the Training Tower
1777 			else if (d_ptr->flags2 & DF2_IRON) Send_music(Ind, 14, 11);//note: switched from 13 to 14, which is actually forcedown/hellish
1778 			else if ((d_ptr->flags2 & DF2_HELL) || (d_ptr->flags1 & DF1_FORCE_DOWN)) Send_music(Ind, 13, 11);//note: switched from 14 to 13, which is actually iron
1779 			else Send_music(Ind, 11, 0);
1780 			return;
1781 		case 1: Send_music(Ind, 32, 11); return; //Mirkwood
1782 		case 2: Send_music(Ind, 17, 11); return; //Mordor
1783 		case 3: Send_music(Ind, 19, 11); return; //Angband
1784 		case 4: Send_music(Ind, 16, 11); return; //Barrow-Downs
1785 		case 5: Send_music(Ind, 21, 11); return; //Mount Doom
1786 		case 6: Send_music(Ind, 22, 13); return; //Nether Realm
1787 		case 7: Send_music(Ind, 35, 11); return; //Submerged Ruins
1788 		case 8: Send_music(Ind, 26, 11); return; //Halls of Mandos
1789 		case 9: Send_music(Ind, 30, 11); return; //Cirith Ungol
1790 		case 10: Send_music(Ind, 28, 11); return; //The Heart of the Earth
1791 		case 16: Send_music(Ind, 18, 11); return; //The Paths of the Dead
1792 		case 17: Send_music(Ind, 37, 11); return; //The Illusory Castle
1793 		case 18: Send_music(Ind, 39, 11); return; //The Maze
1794 		case 19: Send_music(Ind, 20, 11); return; //The Orc Cave
1795 		case 20: Send_music(Ind, 36, 11); return; //Erebor
1796 		case 21: Send_music(Ind, 27, 11); return; //The Old Forest
1797 		case 22: Send_music(Ind, 29, 11); return; //The Mines of Moria
1798 		case 23: Send_music(Ind, 34, 11); return; //Dol Guldur
1799 		case 24: Send_music(Ind, 31, 11); return; //The Small Water Cave
1800 		case 25: Send_music(Ind, 38, 11); return; //The Sacred Land of Mountains
1801 		case 26: Send_music(Ind, 24, 11); return; //The Land of Rhun
1802 		case 27: Send_music(Ind, 25, 11); return; //The Sandworm Lair
1803 		case 28: Send_music(Ind, 33, 11); return; //Death Fate
1804 		case 29: Send_music(Ind, 23, 11); return; //The Helcaraxe
1805 		case 30: Send_music(Ind, 15, 12); return; //The Training Tower
1806 		//31 is handled above by in_valinor() check
1807 		case 32:
1808 			if (is_newer_than(&p_ptr->version, 4, 5, 6, 0, 0, 1)) Send_music(Ind, 56, 13); //The Cloud Planes
1809 			else {
1810 				if (p_ptr->audio_mus == 56) Send_music(Ind, 13, 0); /* outdated music pack? (use ironman music for now (forcedown/hellish doesn't fit)) */
1811 				else Send_music(Ind, 56, 13); /* the actual specific music for this dungeon */
1812 			}
1813 			return;
1814 		}
1815 	}
1816 
1817 	/* Shouldn't happen - send default (dungeon) music */
1818 	Send_music(Ind, 0, 0);
1819 }
1820 
handle_ambient_sfx(int Ind,cave_type * c_ptr,struct worldpos * wpos,bool smooth)1821 void handle_ambient_sfx(int Ind, cave_type *c_ptr, struct worldpos *wpos, bool smooth) {
1822 	player_type *p_ptr = Players[Ind];
1823 	dun_level *l_ptr = getfloor(wpos);
1824 
1825 	/* sounds that guaranteedly override everthing */
1826 	if (in_valinor(wpos)) {
1827 		Send_sfx_ambient(Ind, SFX_AMBIENT_SHORE, TRUE);
1828 		return;
1829 	}
1830 
1831 	/* don't play outdoor (or any other) ambient sfx if we're in a special pseudo-indoors sector */
1832 	if ((l_ptr && (l_ptr->flags2 & LF2_INDOORS)) ||
1833 	    (in_sector00(wpos) && (sector00flags2 & LF2_INDOORS))) {
1834 		Send_sfx_ambient(Ind, SFX_AMBIENT_NONE, FALSE);
1835 		return;
1836 	}
1837 
1838 	/* disable certain ambient sounds if they shouldn't be up here */
1839 	if (p_ptr->sound_ambient == SFX_AMBIENT_FIREPLACE && ((!(f_info[c_ptr->feat].flags1 & FF1_PROTECTED)) || !istown(wpos))) {
1840 		Send_sfx_ambient(Ind, SFX_AMBIENT_NONE, smooth);
1841 	} else if (p_ptr->sound_ambient == SFX_AMBIENT_SHORE && (wpos->wz != 0 || (wild_info[wpos->wy][wpos->wx].type != WILD_OCEAN && wild_info[wpos->wy][wpos->wx].bled != WILD_OCEAN))) {
1842 		Send_sfx_ambient(Ind, SFX_AMBIENT_NONE, smooth);
1843 	} else if (p_ptr->sound_ambient == SFX_AMBIENT_LAKE && (wpos->wz != 0 ||
1844 	    (wild_info[wpos->wy][wpos->wx].type != WILD_LAKE && wild_info[wpos->wy][wpos->wx].bled != WILD_LAKE &&
1845 	    wild_info[wpos->wy][wpos->wx].type != WILD_RIVER && wild_info[wpos->wy][wpos->wx].bled != WILD_RIVER &&
1846 	    wild_info[wpos->wy][wpos->wx].type != WILD_SWAMP && wild_info[wpos->wy][wpos->wx].bled != WILD_SWAMP))) {
1847 		Send_sfx_ambient(Ind, SFX_AMBIENT_NONE, smooth);
1848 	}
1849 
1850 	/* enable/switch to certain ambient loops */
1851 #if 0 /* buggy: enable no_house_sfx while inside a house, and the sfx will stay looping even when leaving the house */
1852 	if (p_ptr->sound_ambient != SFX_AMBIENT_FIREPLACE && (f_info[c_ptr->feat].flags1 & FF1_PROTECTED) && istown(wpos) && p_ptr->sfx_house) { /* sfx_house check is redundant with grid_affects_player() */
1853 #else /* still buggy :-p */
1854 	if (p_ptr->sound_ambient != SFX_AMBIENT_FIREPLACE && (f_info[c_ptr->feat].flags1 & FF1_PROTECTED) && istown(wpos)) {
1855 #endif
1856 		Send_sfx_ambient(Ind, SFX_AMBIENT_FIREPLACE, smooth);
1857 	} else if (p_ptr->sound_ambient != SFX_AMBIENT_FIREPLACE &&
1858 	    p_ptr->sound_ambient != SFX_AMBIENT_SHORE && wpos->wz == 0 && (wild_info[wpos->wy][wpos->wx].type == WILD_OCEAN || wild_info[wpos->wy][wpos->wx].bled == WILD_OCEAN)) {
1859 		Send_sfx_ambient(Ind, SFX_AMBIENT_SHORE, smooth);
1860 	} else if (p_ptr->sound_ambient != SFX_AMBIENT_FIREPLACE && p_ptr->sound_ambient != SFX_AMBIENT_SHORE &&
1861 	    p_ptr->sound_ambient != SFX_AMBIENT_LAKE && wpos->wz == 0 &&
1862 	    (wild_info[wpos->wy][wpos->wx].type == WILD_LAKE || wild_info[wpos->wy][wpos->wx].bled == WILD_LAKE ||
1863 	    wild_info[wpos->wy][wpos->wx].type == WILD_RIVER || wild_info[wpos->wy][wpos->wx].bled == WILD_RIVER ||
1864 	    wild_info[wpos->wy][wpos->wx].type == WILD_SWAMP || wild_info[wpos->wy][wpos->wx].bled == WILD_SWAMP)) {
1865 		Send_sfx_ambient(Ind, SFX_AMBIENT_LAKE, smooth);
1866 	}
1867 }
1868 
1869 /* play single ambient sfx, synched for all players, depending on worldmap terrain - C. Blue */
1870 void process_ambient_sfx(void) {
1871 	int i;
1872 	player_type *p_ptr;
1873 	wilderness_type *w_ptr;
1874 
1875 	for (i = 1; i <= NumPlayers; i++) {
1876 		p_ptr = Players[i];
1877 		if (p_ptr->conn == NOT_CONNECTED) continue;
1878 		if (p_ptr->wpos.wz) continue;
1879 
1880 		w_ptr = &wild_info[p_ptr->wpos.wy][p_ptr->wpos.wx];
1881 		if (w_ptr->ambient_sfx_counteddown) continue;
1882 		if (w_ptr->ambient_sfx) {
1883 			p_ptr->ambient_sfx_timer = 0;
1884 			continue;
1885 		}
1886 		if (w_ptr->ambient_sfx_timer) {
1887 			if (!w_ptr->ambient_sfx_counteddown) {
1888 				w_ptr->ambient_sfx_timer--;
1889 				w_ptr->ambient_sfx_counteddown = TRUE; //semaphore
1890 			}
1891 			continue;
1892 		}
1893 
1894 		switch (w_ptr->type) { /* ---- ensure consistency with alloc_dungeon_level() ---- */
1895 		case WILD_RIVER:
1896 		case WILD_LAKE:
1897 		case WILD_SWAMP:
1898 			if (season == SEASON_WINTER) break;
1899 			sound_floor_vol(&p_ptr->wpos, "animal_toad", NULL, SFX_TYPE_AMBIENT, 100);
1900 			w_ptr->ambient_sfx_timer = 4 + rand_int(4);
1901 			break;
1902 		case WILD_ICE:
1903 		case WILD_MOUNTAIN:
1904 		case WILD_WASTELAND:
1905 			if (IS_NIGHT) sound_floor_vol(&p_ptr->wpos, "animal_wolf", NULL, SFX_TYPE_AMBIENT, 100);
1906 			w_ptr->ambient_sfx_timer = 30 + rand_int(60);
1907 			break;
1908 		//case WILD_SHORE:
1909 		case WILD_OCEAN:
1910 			if (IS_DAY) sound_floor_vol(&p_ptr->wpos, "animal_seagull", NULL, SFX_TYPE_AMBIENT, 100);
1911 			w_ptr->ambient_sfx_timer = 30 + rand_int(60);
1912 			break;
1913 		case WILD_FOREST:
1914 		case WILD_DENSEFOREST:
1915 			if (IS_DAY) {
1916 				if (season == SEASON_WINTER) {
1917 					sound_floor_vol(&p_ptr->wpos, "animal_wolf", NULL, SFX_TYPE_AMBIENT, 100);
1918 					w_ptr->ambient_sfx_timer = 30 + rand_int(60);
1919 				} else {
1920 					sound_floor_vol(&p_ptr->wpos, "animal_bird", NULL, SFX_TYPE_AMBIENT, 100);
1921 					w_ptr->ambient_sfx_timer = 10 + rand_int(20);
1922 				}
1923 			} else {
1924 				if (!rand_int(3)) {
1925 					sound_floor_vol(&p_ptr->wpos, "animal_wolf", NULL, SFX_TYPE_AMBIENT, 100);
1926 					w_ptr->ambient_sfx_timer = 30 + rand_int(60);
1927 				} else {
1928 					sound_floor_vol(&p_ptr->wpos, "animal_owl", NULL, SFX_TYPE_AMBIENT, 100);
1929 					w_ptr->ambient_sfx_timer = 20 + rand_int(40);
1930 				}
1931 			}
1932 			break;
1933 		/* Note: Default terrain that doesn't have ambient sfx will automatically clear people's ambient_sfx_timer too.
1934 		   This is ok for towns but not cool for fast wilderness travel where terrain is mixed a lot.
1935 		   For that reason we give default terrain a "pseudo-timeout" to compromise a bit. */
1936 		default:
1937 			w_ptr->ambient_sfx_timer = 30 + rand_int(5); //should be > than time required for travelling across 1 wilderness sector
1938 			break;
1939 		}
1940 
1941 		w_ptr->ambient_sfx = TRUE;
1942 		p_ptr->ambient_sfx_timer = 0;
1943 	}
1944 
1945 	for (i = 1; i <= NumPlayers; i++) {
1946 		if (Players[i]->conn == NOT_CONNECTED) continue;
1947 		if (Players[i]->wpos.wz) continue;
1948 		wild_info[Players[i]->wpos.wy][Players[i]->wpos.wx].ambient_sfx = FALSE;
1949 		wild_info[Players[i]->wpos.wy][Players[i]->wpos.wx].ambient_sfx_counteddown = FALSE;
1950 	}
1951 }
1952 
1953 /* generate an item-type specific sound, depending on the action applied to it
1954    action: 0 = pickup, 1 = drop, 2 = wear/wield, 3 = takeoff, 4 = throw, 5 = destroy */
1955 void sound_item(int Ind, int tval, int sval, cptr action) {
1956 	char sound_name[30];
1957 	cptr item = NULL;
1958 
1959 	/* action hack: re-use sounds! */
1960 	action = "item_";
1961 
1962 	/* choose sound */
1963 	if (is_weapon(tval)) switch(tval) {
1964 		case TV_SWORD: item = "sword"; break;
1965 		case TV_BLUNT:
1966 			if (sval == SV_WHIP) item = "whip"; else item = "blunt";
1967 			break;
1968 		case TV_AXE: item = "axe"; break;
1969 		case TV_POLEARM: item = "polearm"; break;
1970 	} else if (is_armour(tval)) {
1971 		if (is_textile_armour(tval, sval))
1972 			item = "armour_light";
1973 		else
1974 			item = "armour_heavy";
1975 	} else switch(tval) {
1976 		/* equippable stuff */
1977 		case TV_LITE: item = "lightsource"; break;
1978 		case TV_RING: item = "ring"; break;
1979 		case TV_AMULET: item = "amulet"; break;
1980 		case TV_TOOL: item = "tool"; break;
1981 		case TV_DIGGING: item = "tool_digger"; break;
1982 		case TV_MSTAFF: item = "magestaff"; break;
1983 //		case TV_BOOMERANG: item = ""; break;
1984 //		case TV_BOW: item = ""; break;
1985 //		case TV_SHOT: item = ""; break;
1986 //		case TV_ARROW: item = ""; break;
1987 //		case TV_BOLT: item = ""; break;
1988 		/* other items */
1989 //		case TV_BOOK: item = "book"; break;
1990 		case TV_SCROLL: case TV_PARCHMENT:
1991 			item = "scroll"; break;
1992 /*		case TV_BOTTLE: item = "potion"; break;
1993 		case TV_POTION: case TV_POTION2: case TV_FLASK:
1994 			item = "potion"; break; */
1995 		case TV_RUNE: item = "rune"; break;
1996 //		case TV_SKELETON: item = ""; break;
1997 		case TV_FIRESTONE: item = "firestone"; break;
1998 /*		case TV_SPIKE: item = ""; break;
1999 		case TV_CHEST: item = ""; break;
2000 		case TV_JUNK: item = ""; break;
2001 		case TV_GAME: item = ""; break;
2002 		case TV_TRAPKIT: item = ""; break;
2003 		case TV_STAFF: item = ""; break;
2004 		case TV_WAND: item = ""; break;
2005 		case TV_ROD: item = ""; break;
2006 		case TV_ROD_MAIN: item = ""; break;
2007 		case TV_FOOD: item = ""; break; */
2008 		case TV_KEY: item = "key"; break;
2009 //		case TV_GOLEM: item = ""; break;
2010 		case TV_SPECIAL:
2011 			switch (sval) {
2012 			case SV_SEAL: item = "seal"; break;
2013 			}
2014 			break;
2015 	}
2016 
2017 	/* no sound effect available? */
2018 	if (item == NULL) return;
2019 
2020 	/* build sound name from action and item */
2021 	strcpy(sound_name, action);
2022 	strcat(sound_name, item);
2023 	/* off we go */
2024 	sound(Ind, sound_name, NULL, SFX_TYPE_COMMAND, FALSE);
2025 }
2026 #endif
2027 
2028 /*
2029  * We use a global array for all inscriptions to reduce the memory
2030  * spent maintaining inscriptions.  Of course, it is still possible
2031  * to run out of inscription memory, especially if too many different
2032  * inscriptions are used, but hopefully this will be rare.
2033  *
2034  * We use dynamic string allocation because otherwise it is necessary
2035  * to pre-guess the amount of quark activity.  We limit the total
2036  * number of quarks, but this is much easier to "expand" as needed.
2037  *
2038  * Any two items with the same inscription will have the same "quark"
2039  * index, which should greatly reduce the need for inscription space.
2040  *
2041  * Note that "quark zero" is NULL and should not be "dereferenced".
2042  */
2043 
2044 /*
2045  * Add a new "quark" to the set of quarks.
2046  */
2047 s16b quark_add(cptr str)
2048 {
2049 	int i;
2050 
2051 	/* Look for an existing quark */
2052 	for (i = 1; i < quark__num; i++)
2053 	{
2054 		/* Check for equality */
2055 		if (streq(quark__str[i], str)) return (i);
2056 	}
2057 
2058 	/* Paranoia -- Require room */
2059 	if (quark__num == QUARK_MAX) return (0);
2060 
2061 	/* New maximal quark */
2062 	quark__num = i + 1;
2063 
2064 	/* Add a new quark */
2065 	quark__str[i] = string_make(str);
2066 
2067 	/* Return the index */
2068 	return (i);
2069 }
2070 
2071 
2072 /*
2073  * This function looks up a quark
2074  */
2075 cptr quark_str(s16b i)
2076 {
2077 	cptr q;
2078 
2079 	/* Verify */
2080 	if ((i < 0) || (i >= quark__num)) i = 0;
2081 
2082 	/* Access the quark */
2083 	q = quark__str[i];
2084 
2085 	/* Return the quark */
2086 	return (q);
2087 }
2088 
2089 /*
2090  * Check to make sure they haven't inscribed an item against what
2091  * they are trying to do -Crimson
2092  * look for "!*Erm" type, and "!* !A !f" type.
2093  */
2094 
2095 bool check_guard_inscription( s16b quark, char what ) {
2096 	const char *ax = quark_str(quark);
2097 
2098 	if (ax == NULL) return FALSE;
2099 
2100 	while ((ax = strchr(ax, '!')) != NULL) {
2101 		while (ax++ != NULL) {
2102 			if (*ax == 0)  {
2103 				return FALSE; /* end of quark, stop */
2104 			}
2105 			if (*ax == ' ' || *ax == '@' || *ax == '#' || *ax == '-') {
2106 				break; /* end of segment, stop */
2107 			}
2108 			if (*ax == what) {
2109 				return TRUE; /* exact match, stop */
2110 			}
2111 			if (*ax == '*') {
2112 				/* why so much hassle? * = all, that's it */
2113 /*				return TRUE; -- well, !'B'ash if it's on the ground sucks ;) */
2114 
2115 				switch (what) { /* check for paranoid tags */
2116 				case 'd': /* no drop */
2117 				case 'h': /* (obsolete) no house ( sell a a key ) */
2118 				case 'k': /* no destroy */
2119 				case 's': /* no sell */
2120 				case 'v': /* no thowing */
2121 				case '=': /* force pickup */
2122 #if 0
2123 				case 'w': /* no wear/wield */
2124 				case 't': /* no take off */
2125 #endif
2126 					return TRUE;
2127 				}
2128 				//return FALSE;
2129 			}
2130 			if (*ax == '+') {
2131 				/* why so much hassle? * = all, that's it */
2132 /*				return TRUE; -- well, !'B'ash if it's on the ground sucks ;) */
2133 
2134 				switch (what) { /* check for paranoid tags */
2135 				case 'h': /* (obsolete) no house ( sell a a key ) */
2136 				case 'k': /* no destroy */
2137 				case 's': /* no sell */
2138 				case 'v': /* no thowing */
2139 				case '=': /* force pickup */
2140 #if 0
2141 				case 'w': /* no wear/wield */
2142 				case 't': /* no take off */
2143 #endif
2144 					return TRUE;
2145 				}
2146 				//return FALSE;
2147 			}
2148 		}
2149 	}
2150 	return FALSE;
2151 }
2152 
2153 
2154 /*
2155  * Output a message to the top line of the screen.
2156  *
2157  * Break long messages into multiple pieces (40-72 chars).
2158  *
2159  * Allow multiple short messages to "share" the top line.
2160  *
2161  * Prompt the user to make sure he has a chance to read them.
2162  *
2163  * These messages are memorized for later reference (see above).
2164  *
2165  * We could do "Term_fresh()" to provide "flicker" if needed.
2166  *
2167  * XXX XXX XXX Note that we must be very careful about using the
2168  * "msg_print()" functions without explicitly calling the special
2169  * "msg_print(NULL)" function, since this may result in the loss
2170  * of information if the screen is cleared, or if anything is
2171  * displayed on the top line.
2172  *
2173  * XXX XXX XXX Note that "msg_print(NULL)" will clear the top line
2174  * even if no messages are pending.  This is probably a hack.
2175  */
2176 bool suppress_message = FALSE, censor_message = FALSE, suppress_boni = FALSE;
2177 int censor_length = 0, censor_punish = 0;
2178 
2179 void msg_print(int Ind, cptr msg_raw)
2180 {
2181 	char msg_dup[MSG_LEN], *msg = msg_dup;
2182 	int line_len = 80; /* maximum length of a text line to be displayed;
2183 		     this is client-dependant, compare c_msg_print (c-util.c) */
2184 	char msg_buf[line_len + 2 + 2 * 80]; /* buffer for 1 line. + 2 bytes for colour code (+2*80 bytes for colour codeeeezz) */
2185 	char msg_minibuf[3]; /* temp buffer for adding characters */
2186 	int text_len, msg_scan = 0, space_scan, tab_spacer = 0, tmp;
2187 	char colour_code = 'w';
2188 	bool no_colour_code = FALSE;
2189 	bool first_character = TRUE;
2190 //	bool is_chat = ((msg_raw != NULL) && (strlen(msg_raw) > 2) && (msg_raw[2] == '['));
2191 	bool client_ctrlo = FALSE, client_chat = FALSE, client_all = FALSE;
2192 
2193 	/* for {- feature */
2194 	char first_colour_code = 'w';
2195 	bool first_colour_code_set = FALSE;
2196 
2197 	/* backward msg window width hack for windows clients (non-x11 clients rather) */
2198 	if (!is_newer_than(&Players[Ind]->version, 4, 4, 5, 3, 0, 0) && !strcmp(Players[Ind]->realname, "PLAYER")) line_len = 72;
2199 
2200 	/* Pfft, sorry to bother you.... --JIR-- */
2201 	if (suppress_message) return;
2202 
2203 	/* no message? */
2204 	if (msg_raw == NULL) return;
2205 
2206 	strcpy(msg_dup, msg_raw); /* in case msg_raw was constant */
2207 	/* censor swear words? */
2208 	if (censor_message) {
2209 		/* skip the name of the sender, etc. */
2210 		censor_punish = handle_censor(msg + strlen(msg) - censor_length);
2211 		/* we just needed to get censor_punish at least once, above.
2212 		   Now don't really censor the string if the player doesn't want to.. */
2213 		if (!Players[Ind]->censor_swearing) strcpy(msg_dup, msg_raw);
2214 	}
2215 
2216 	/* marker for client: add message to 'chat-only buffer', not to 'nochat buffer' */
2217 	if (msg[0] == '\375') {
2218 		client_chat = TRUE;
2219 		msg++;
2220 		/* hack: imply that chat-only messages are also always important messages */
2221 		client_ctrlo = TRUE;
2222 	}
2223 	/* marker for client: add message to 'nochat buffer' AND to 'chat-only buffer' */
2224 	else if (msg[0] == '\374') {
2225 		client_all = TRUE;
2226 		msg++;
2227 		/* hack: imply that messages that go to chat-buffer are also always important messages */
2228 		client_ctrlo = TRUE;
2229 	}
2230 	/* ADDITIONAL marker for client: add message to CTRL+O 'important scrollback buffer' */
2231 	if (msg[0] == '\376') {
2232 		client_ctrlo = TRUE;
2233 		msg++;
2234 	}
2235 	/* note: neither \375 nor \374 means:
2236 	   add message to 'nochat buffer', but not to 'chat-only buffer' (default) */
2237 
2238 	/* neutralize markers if client version too old */
2239 	if (!is_newer_than(&Players[Ind]->version, 4, 4, 2, 0, 0, 0))
2240 		client_ctrlo = client_chat = client_all = FALSE;
2241 
2242 #if 1	/* String longer than 1 line? -> Split it up! --C. Blue-- */
2243 	while (msg != NULL && msg[msg_scan] != '\0') {
2244 		/* Start a new line */
2245 		strcpy(msg_buf, "");
2246 		text_len = 0;
2247 
2248 		/* Tabbing the line? */
2249 		msg_minibuf[0] = ' ';
2250 		msg_minibuf[1] = '\0';
2251 #if 0 /* 0'ed to remove backward compatibility via '~' character. We want to switch to \374..6 codes exlusively */
2252 		if (is_chat && tab_spacer) {
2253 			/* Start the padding for chat messages with '~' */
2254 			strcat(msg_buf, "~");
2255 			tmp = tab_spacer - 1;
2256 		} else {
2257 			tmp = tab_spacer;
2258 		}
2259 #else
2260 		tmp = tab_spacer;
2261 #endif
2262 		while (tmp--) {
2263 			text_len++;
2264 			strcat(msg_buf, msg_minibuf);
2265 		}
2266 
2267 		/* Prefixing colour code? */
2268 		if (colour_code) {
2269 			msg_minibuf[0] = '\377';
2270 			msg_minibuf[1] = colour_code;
2271 			msg_minibuf[2] = '\0';
2272 			strcat(msg_buf, msg_minibuf);
2273 ///			colour_code = 0;
2274 		}
2275 
2276 		/* Process the string... */
2277 		while (text_len < line_len) {
2278 			switch (msg[msg_scan]) {
2279 			case '\0': /* String ends! */
2280 				text_len = line_len;
2281 				continue;
2282 			case '\377': /* Colour code! Text length does not increase. */
2283 				if (!no_colour_code) {
2284 					/* broken \377 at the end of the text? ignore */
2285 					if (color_char_to_attr(msg[msg_scan + 1]) == -1
2286 					    && msg[msg_scan + 1] != '-'	/* {- and {{ are handled (further) below */
2287 					    && msg[msg_scan + 1] != '\377') {
2288 						msg_scan++;
2289 						continue;
2290 					}
2291 
2292 					/* Capture double \377 which stand for a normal { char instead of a colour code: */
2293 					if (msg[msg_scan + 1] != '\377') {
2294 						msg_minibuf[0] = msg[msg_scan];
2295 						msg_scan++;
2296 
2297 						/* needed for new '\377-' feature in multi-line messages: resolve it to actual colour */
2298 						if (msg[msg_scan] == '-') {
2299 							msg[msg_scan] = colour_code = first_colour_code;
2300 						} else {
2301 							colour_code = msg[msg_scan];
2302 							if (!first_colour_code_set) {
2303 								first_colour_code_set = TRUE;
2304 								first_colour_code = colour_code;
2305 							}
2306 						}
2307 						msg_scan++;
2308 
2309 						msg_minibuf[1] = colour_code;
2310 						msg_minibuf[2] = '\0';
2311 						strcat(msg_buf, msg_minibuf);
2312 						break;
2313 					}
2314 					no_colour_code = TRUE;
2315 				} else no_colour_code = FALSE;
2316 				/* fall through if it's a '{' character */
2317 			default: /* Text length increases by another character.. */
2318 				/* Depending on message type, remember to tab the following
2319 				   lines accordingly to make it look better ^^
2320 				   depending on the first character of this line. */
2321 				if (first_character) {
2322 					switch (msg[msg_scan]) {
2323 					case '*': tab_spacer = 2; break; /* Kill message */
2324 					case '[': /* Chat message */
2325 #if 0
2326 						tab_spacer = 1;
2327 #else
2328 						{
2329 							const char *bracket = strchr(&msg[msg_scan], ']');
2330 
2331 							if (bracket) {
2332 								const char *ptr = &msg[msg_scan];
2333 
2334 								/* Pad lines according to how long the name is - mikaelh */
2335 								tab_spacer = bracket - &msg[msg_scan] + 2;
2336 
2337 								/* Ignore space reserved for colour codes:
2338 								   Guild chat has coloured [ ] brackets. - C. Blue */
2339 								while ((ptr = strchr(ptr, '\377')) && ptr < bracket) {
2340 									ptr++;
2341 									tab_spacer -= 2; /* colour code consists of two chars
2342 									    (we can guarantee that here, since the server
2343 									    generated this colour code) */
2344 								}
2345 
2346 								/* Hack: multiline /me emotes */
2347 								if (tab_spacer > 70) tab_spacer = 1;
2348 
2349 								/* Paranoia */
2350 								if (tab_spacer < 1) tab_spacer = 1;
2351 								if (tab_spacer > 30) tab_spacer = 30;
2352 							} else {
2353 								/* No ']' */
2354 								tab_spacer = 1;
2355 							}
2356 						}
2357 #endif
2358 						break;
2359 					default: tab_spacer = 1;
2360 					}
2361 				}
2362 
2363 				/* remember first actual chat colour (guild chat changes
2364 				   colour a few times for [..] brackets and name).
2365 				   This is for {- etc feature.
2366 				   However, if there is no new colour specified before
2367 				   beginning of chat text then use the one we had, or {-
2368 				   wouldn't work correctly in private chat anymore. - C. Blue */
2369 				if (msg[msg_scan] == ']' &&
2370 #if 0  /* this is wrong, because the colour code COULD already be from \\a feature! */
2371 				    ((msg[msg_scan + 1] == ' ' && msg[msg_scan + 2] == '\377') ||
2372 #endif
2373 				    msg[msg_scan + 1] == '\377') {
2374 					first_colour_code_set = FALSE;
2375 				}
2376 
2377 				/* Process text.. */
2378 				first_character = FALSE;
2379 				msg_minibuf[0] = msg[msg_scan];
2380 				msg_minibuf[1] = '\0';
2381 				strcat(msg_buf, msg_minibuf);
2382 				msg_scan++;
2383 				text_len++;
2384 
2385 				/* Avoid cutting words in two */
2386 				if ((text_len == line_len) && (msg[msg_scan] != '\0')
2387 				    && (
2388 
2389 				    (msg[msg_scan - 1] >= 'A' && msg[msg_scan - 1] <= 'Z') ||
2390 				    (msg[msg_scan - 1] >= 'a' && msg[msg_scan - 1] <= 'z') ||
2391 				    (msg[msg_scan - 1] >= '0' && msg[msg_scan - 1] <= '9') ||
2392 				    msg[msg_scan - 1] == '_' || /* for pasting lore-flags to chat! */
2393 				    msg[msg_scan - 1] == '(' ||
2394 				    msg[msg_scan - 1] == '[' ||
2395 				    msg[msg_scan - 1] == '{' ||
2396 #if 1 /* don't break smileys? */
2397 				    msg[msg_scan - 1] == ':' || msg[msg_scan - 1] == ';' ||
2398 #endif
2399 #if 0
2400 				    msg[msg_scan - 1] == ')' ||
2401 				    msg[msg_scan - 1] == ']' ||
2402 				    msg[msg_scan - 1] == '}' ||
2403 #endif
2404 				    /* (maybe too much) for pasting items to chat, (+1) or (-2,0) : */
2405 				    msg[msg_scan - 1] == '+' || msg[msg_scan - 1] == '-' ||
2406 				    /* Don't break colour codes */
2407 				    msg[msg_scan - 1] == '\377'
2408 
2409 				    ) && (
2410 
2411 				    (msg[msg_scan] >= 'A' && msg[msg_scan] <= 'Z') ||
2412 				    (msg[msg_scan] >= 'a' && msg[msg_scan] <= 'z') ||
2413 				    (msg[msg_scan] >= '0' && msg[msg_scan] <= '9') ||
2414 #if 0
2415 				    msg[msg_scan] == '(' ||
2416 				    msg[msg_scan] == '[' ||
2417 				    msg[msg_scan] == '{' ||
2418 #endif
2419 #if 1 /* don't break smileys? */
2420 				    msg[msg_scan] == '-' ||
2421 #endif
2422 				    msg[msg_scan] == '_' ||/* for pasting lore-flags to chat! */
2423 				    msg[msg_scan] == ')' ||
2424 				    msg[msg_scan] == ']' ||
2425 				    msg[msg_scan] == '}' ||
2426 				    /* (maybe too much) for pasting items to chat, (+1) or (-2,0) : */
2427 				    msg[msg_scan] == '+' || msg[msg_scan] == '-' ||
2428 				    /* interpunction at the end of a sentence */
2429 				    msg[msg_scan] == '.' || msg[msg_scan] == ',' ||
2430 				    msg[msg_scan] == ';' || msg[msg_scan] == ':' ||
2431 				    msg[msg_scan] == '!' || msg[msg_scan] == '?' ||
2432 				    /* Don't break colour codes */
2433 				    msg[msg_scan] == '\377'
2434 
2435 				    )) {
2436 					space_scan = msg_scan;
2437 					do {
2438 						space_scan--;
2439 					} while (((msg[space_scan - 1] >= 'A' && msg[space_scan - 1] <= 'Z') ||
2440 						(msg[space_scan - 1] >= 'a' && msg[space_scan - 1] <= 'z') ||
2441 						(msg[space_scan - 1] >= '0' && msg[space_scan - 1] <= '9') ||
2442 #if 1 /* don't break smileys? */
2443 						msg[space_scan - 1] == ':' || msg[space_scan - 1] == ';' ||
2444 #endif
2445 #if 0
2446 						msg[space_scan - 1] == ')' ||
2447 						msg[space_scan - 1] == ']' ||
2448 						msg[space_scan - 1] == '}' ||
2449 #endif
2450 #if 0
2451 						msg[space_scan - 1] == '_' ||/* for pasting lore-flags to chat! */
2452 #endif
2453 						msg[space_scan - 1] == '(' ||
2454 						msg[space_scan - 1] == '[' ||
2455 						msg[space_scan - 1] == '{' ||
2456 						/* (maybe too much) for pasting items to chat, (+1) or (-2,0) : */
2457 						msg[space_scan - 1] == '+' || msg[space_scan - 1] == '-' ||
2458 						/* pasting flags to chat ("SLAY_EVIL") */
2459 						msg[space_scan - 1] == '_' ||
2460 						msg[space_scan - 1] == '\377'
2461 						) && space_scan > 0);
2462 
2463 					/* Simply cut words that are very long - mikaelh */
2464 					if (msg_scan - space_scan > 36) {
2465 						space_scan = msg_scan;
2466 					}
2467 
2468 					if (space_scan) {
2469 						msg_buf[strlen(msg_buf) - msg_scan + space_scan] = '\0';
2470 						msg_scan = space_scan;
2471 					}
2472 				}
2473 			}
2474 		}
2475 		Send_message(Ind, format("%s%s%s",
2476 		    client_chat ? "\375" : (client_all ? "\374" : ""),
2477 		    client_ctrlo ? "\376" : "",
2478 		    msg_buf));
2479 		/* hack: avoid trailing space in the next sub-line */
2480 		//if (msg[msg_scan] == ' ') msg_scan++;
2481 		while (msg[msg_scan] == ' ') msg_scan++;//avoid all trailing spaces in the next sub-line
2482 	}
2483 
2484 	if (msg == NULL) Send_message(Ind, msg);
2485 
2486 	return;
2487 #endif // enable line breaks?
2488 
2489 	Send_message(Ind, format("%s%s%s",
2490 	    client_chat ? "\375" : (client_all ? "\374" : ""),
2491 	    client_ctrlo ? "\376" : "",
2492 	    msg));
2493 }
2494 
2495 void msg_broadcast(int Ind, cptr msg)
2496 {
2497 	int i;
2498 
2499 	/* Tell every player */
2500 	for (i = 1; i <= NumPlayers; i++)
2501 	{
2502 		/* Skip disconnected players */
2503 		if (Players[i]->conn == NOT_CONNECTED)
2504 			continue;
2505 
2506 		/* Skip the specified player */
2507 		if (i == Ind)
2508 			continue;
2509 
2510 		/* Tell this one */
2511 		msg_print(i, msg);
2512 	 }
2513 }
2514 
2515 void msg_admins(int Ind, cptr msg)
2516 {
2517 	int i;
2518 
2519 	/* Tell every player */
2520 	for (i = 1; i <= NumPlayers; i++)
2521 	{
2522 		/* Skip disconnected players */
2523 		if (Players[i]->conn == NOT_CONNECTED)
2524 			continue;
2525 
2526 		/* Skip the specified player */
2527 		if (i == Ind)
2528 			continue;
2529 
2530 		/* Skip non-admins */
2531 		if (!is_admin(Players[i]))
2532 			continue;
2533 
2534 		/* Tell this one */
2535 		msg_print(i, msg);
2536 	 }
2537 }
2538 
2539 void msg_broadcast_format(int Ind, cptr fmt, ...)
2540 {
2541 //	int i;
2542 
2543 	va_list vp;
2544 
2545 	char buf[1024];
2546 
2547 	/* Begin the Varargs Stuff */
2548 	va_start(vp, fmt);
2549 
2550 	/* Format the args, save the length */
2551 	(void)vstrnfmt(buf, 1024, fmt, vp);
2552 
2553 	/* End the Varargs Stuff */
2554 	va_end(vp);
2555 
2556 	msg_broadcast(Ind, buf);
2557 }
2558 
2559 /* Send a formatted message only to admin chars.	-Jir- */
2560 void msg_admin(cptr fmt, ...)
2561 //void msg_admin(int Ind, cptr fmt, ...)
2562 {
2563 	int i;
2564 	player_type *p_ptr;
2565 
2566 	va_list vp;
2567 
2568 	char buf[1024];
2569 
2570 	/* Begin the Varargs Stuff */
2571 	va_start(vp, fmt);
2572 
2573 	/* Format the args, save the length */
2574 	(void)vstrnfmt(buf, 1024, fmt, vp);
2575 
2576 	/* End the Varargs Stuff */
2577 	va_end(vp);
2578 
2579 	/* Tell every admin */
2580 	for (i = 1; i <= NumPlayers; i++)
2581 	{
2582 		p_ptr = Players[i];
2583 
2584 		/* Skip disconnected players */
2585 		if (p_ptr->conn == NOT_CONNECTED)
2586 			continue;
2587 
2588 
2589 		/* Tell Mama */
2590 		if (is_admin(p_ptr))
2591 			msg_print(i, buf);
2592 	 }
2593 }
2594 
2595 
2596 
2597 /*
2598  * Display a formatted message, using "vstrnfmt()" and "msg_print()".
2599  */
2600 void msg_format(int Ind, cptr fmt, ...)
2601 {
2602 	va_list vp;
2603 
2604 	char buf[1024];
2605 
2606 	/* Begin the Varargs Stuff */
2607 	va_start(vp, fmt);
2608 
2609 	/* Format the args, save the length */
2610 	(void)vstrnfmt(buf, 1024, fmt, vp);
2611 
2612 	/* End the Varargs Stuff */
2613 	va_end(vp);
2614 
2615 	/* Display */
2616 	msg_print(Ind, buf);
2617 }
2618 
2619 /*
2620  * Send a message to everyone on a floor.
2621  */
2622 static void floor_msg(struct worldpos *wpos, cptr msg) {
2623 	int i;
2624 //system-msg, currently unused anyway-	if(cfg.log_u) s_printf("[%s] %s\n", Players[sender]->name, msg);
2625 	/* Check for this guy */
2626 	for (i = 1; i <= NumPlayers; i++) {
2627         	if (Players[i]->conn == NOT_CONNECTED) continue;
2628 		/* Check this guy */
2629 		if (inarea(wpos, &Players[i]->wpos)) msg_print(i, msg);
2630 	}
2631 }
2632 /*
2633  * Send a formatted message to everyone on a floor. (currently unused)
2634  */
2635 void floor_msg_format(struct worldpos *wpos, cptr fmt, ...) {
2636 	va_list vp;
2637 	char buf[1024];
2638 	/* Begin the Varargs Stuff */
2639 	va_start(vp, fmt);
2640 	/* Format the args, save the length */
2641 	(void)vstrnfmt(buf, 1024, fmt, vp);
2642 	/* End the Varargs Stuff */
2643 	va_end(vp);
2644 	/* Display */
2645 	floor_msg(wpos, buf);
2646 }
2647 /*
2648  * Send a message to everyone on a floor, considering ignorance.
2649  */
2650 static void floor_msg_ignoring(int sender, struct worldpos *wpos, cptr msg) {
2651         int i;
2652 	if(cfg.log_u) s_printf("(%d,%d,%d)%s\n", wpos->wx, wpos->wy, wpos->wz, msg + 2);// Players[sender]->name, msg);
2653         /* Check for this guy */
2654         for (i = 1; i <= NumPlayers; i++) {
2655                 if (Players[i]->conn == NOT_CONNECTED) continue;
2656                 if (check_ignore(i, sender)) continue;
2657 	        /* Check this guy */
2658 	        if (inarea(wpos, &Players[i]->wpos)) msg_print(i, msg);
2659 	}
2660 }
2661 /*
2662  * Send a formatted message to everyone on a floor, considering ignorance.
2663  */
2664 static void floor_msg_format_ignoring(int sender, struct worldpos *wpos, cptr fmt, ...) {
2665 	va_list vp;
2666 	char buf[1024];
2667 	/* Begin the Varargs Stuff */
2668 	va_start(vp, fmt);
2669 	/* Format the args, save the length */
2670 	(void)vstrnfmt(buf, 1024, fmt, vp);
2671 	/* End the Varargs Stuff */
2672 	va_end(vp);
2673 	/* Display */
2674 	floor_msg_ignoring(sender, wpos, buf);
2675 }
2676 
2677 /*
2678  * Send a message to everyone on the world surface. (for season change)
2679  */
2680 void world_surface_msg(cptr msg) {
2681 	int i;
2682 //system-msg, currently unused anyway-	if(cfg.log_u) s_printf("[%s] %s\n", Players[sender]->name, msg);
2683 	/* Check for this guy */
2684 	for (i = 1; i <= NumPlayers; i++) {
2685         	if (Players[i]->conn == NOT_CONNECTED) continue;
2686 		/* Check this guy */
2687 		if (Players[i]->wpos.wz == 0) msg_print(i, msg);
2688 	}
2689 }
2690 
2691 
2692 /*
2693  * Display a message to everyone who is in sight on another player.
2694  *
2695  * This is mainly used to keep other players advised of actions done
2696  * by a player.  The message is not sent to the player who performed
2697  * the action.
2698  */
2699 void msg_print_near(int Ind, cptr msg)
2700 {
2701 	player_type *p_ptr = Players[Ind];
2702 	int y, x, i;
2703 	struct worldpos *wpos;
2704 	wpos = &p_ptr->wpos;
2705 
2706 	if (p_ptr->admin_dm) return;
2707 
2708 	y = p_ptr->py;
2709 	x = p_ptr->px;
2710 
2711 	/* Check each player */
2712 	for (i = 1; i <= NumPlayers; i++) {
2713 		/* Check this player */
2714 		p_ptr = Players[i];
2715 
2716 		/* Make sure this player is in the game */
2717 		if (p_ptr->conn == NOT_CONNECTED) continue;
2718 
2719 		/* Don't send the message to the player who caused it */
2720 		if (Ind == i) continue;
2721 
2722 		/* Make sure this player is at this depth */
2723 		if (!inarea(&p_ptr->wpos, wpos)) continue;
2724 
2725 		/* Can he see this player? */
2726 		if (p_ptr->cave_flag[y][x] & CAVE_VIEW) {
2727 			/* Send the message */
2728 			msg_print(i, msg);
2729 		}
2730 	}
2731 }
2732 
2733 /* Whispering: Send message to adjacent players */
2734 void msg_print_verynear(int Ind, cptr msg)
2735 {
2736 	player_type *p_ptr = Players[Ind];
2737 	int y, x, i;
2738 	struct worldpos *wpos;
2739 	wpos = &p_ptr->wpos;
2740 
2741 //	if(p_ptr->admin_dm) return;
2742 
2743 	y = p_ptr->py;
2744 	x = p_ptr->px;
2745 
2746 	/* Check each player */
2747 	for (i = 1; i <= NumPlayers; i++) {
2748 		/* Check this player */
2749 		p_ptr = Players[i];
2750 
2751 		/* Make sure this player is in the game */
2752 		if (p_ptr->conn == NOT_CONNECTED) continue;
2753 
2754 		/* Don't send the message to the player who caused it */
2755 		if (Ind == i) continue;
2756 
2757 		/* Make sure this player is at this depth */
2758 		if (!inarea(&p_ptr->wpos, wpos)) continue;
2759 
2760 		/* Is he in range? */
2761 		if (abs(p_ptr->py - y) <= 1 && abs(p_ptr->px - x) <= 1) {
2762 			/* Send the message */
2763 			msg_print(i, msg);
2764 		}
2765 	}
2766 }
2767 
2768 
2769 /*
2770  * Same as above, except send a formatted message.
2771  */
2772 void msg_format_near(int Ind, cptr fmt, ...)
2773 {
2774 	va_list vp;
2775 
2776 	char buf[1024];
2777 
2778 	/* Begin the Varargs Stuff */
2779 	va_start(vp, fmt);
2780 
2781 	/* Format the args, save the length */
2782 	(void)vstrnfmt(buf, 1024, fmt, vp);
2783 
2784 	/* End the Varargs Stuff */
2785 	va_end(vp);
2786 
2787 	/* Display */
2788 	msg_print_near(Ind, buf);
2789 }
2790 
2791 /* for whispering */
2792 void msg_format_verynear(int Ind, cptr fmt, ...)
2793 {
2794 	va_list vp;
2795 
2796 	char buf[1024];
2797 
2798 	/* Begin the Varargs Stuff */
2799 	va_start(vp, fmt);
2800 
2801 	/* Format the args, save the length */
2802 	(void)vstrnfmt(buf, 1024, fmt, vp);
2803 
2804 	/* End the Varargs Stuff */
2805 	va_end(vp);
2806 
2807 	/* Display */
2808 	msg_print_verynear(Ind, buf);
2809 }
2810 
2811 /* location-based - also, skip player Ind if non-zero */
2812 void msg_print_near_site(int y, int x, worldpos *wpos, int Ind, bool view, cptr msg)
2813 {
2814 	int i, d;
2815 	player_type *p_ptr;
2816 
2817 	/* Check each player */
2818 	for (i = 1; i <= NumPlayers; i++) {
2819 		/* Check this player */
2820 		p_ptr = Players[i];
2821 
2822 		/* Make sure this player is in the game */
2823 		if (p_ptr->conn == NOT_CONNECTED) continue;
2824 
2825 		/* Skip specified player, if any */
2826 		if (i == Ind) continue;
2827 
2828 		/* Make sure this player is at this depth */
2829 		if (!inarea(&p_ptr->wpos, wpos)) continue;
2830 
2831 		/* Can (s)he see the site? */
2832 		if (view) {
2833 			if (!(p_ptr->cave_flag[y][x] & CAVE_VIEW)) continue;
2834 		} else { /* can (s)he hear it? */
2835 			/* within audible range? */
2836 			d = distance(y, x, Players[i]->py, Players[i]->px);
2837 			/* NOTE: should be consistent with sound_near_site() */
2838 			if (d > MAX_SIGHT) continue;
2839 		}
2840 
2841 		/* Send the message */
2842 		msg_print(i, msg);
2843 	}
2844 }
2845 
2846 /*
2847  * Same as above, except send a formatted message.
2848  */
2849 void msg_format_near_site(int y, int x, worldpos *wpos, int Ind, bool view, cptr fmt, ...)
2850 {
2851 	va_list vp;
2852 
2853 	char buf[1024];
2854 
2855 	/* Begin the Varargs Stuff */
2856 	va_start(vp, fmt);
2857 
2858 	/* Format the args, save the length */
2859 	(void)vstrnfmt(buf, 1024, fmt, vp);
2860 
2861 	/* End the Varargs Stuff */
2862 	va_end(vp);
2863 
2864 	/* Display */
2865 	msg_print_near_site(y, x, wpos, Ind, view, buf);
2866 }
2867 
2868 /*
2869  * Send a message about a monster to everyone who can see it.
2870  * Monster name is appended at the beginning. (XXX kludgie!)	- Jir -
2871  *
2872  * Example: msg_print_near_monster(m_idx, "wakes up.");
2873  */
2874 /*
2875  * TODO: allow format
2876  * TODO: distinguish 'witnessing' and 'hearing'
2877  */
2878 void msg_print_near_monster(int m_idx, cptr msg)
2879 {
2880 	int i;
2881 	player_type *p_ptr;
2882 	cave_type **zcave;
2883 	char m_name[MNAME_LEN];
2884 
2885 	monster_type	*m_ptr = &m_list[m_idx];
2886 	worldpos *wpos = &m_ptr->wpos;
2887 
2888 	if (!(zcave = getcave(wpos))) return;
2889 
2890 
2891 	/* Check each player */
2892 	for (i = 1; i <= NumPlayers; i++) {
2893 		/* Check this player */
2894 		p_ptr = Players[i];
2895 
2896 		/* Make sure this player is in the game */
2897 		if (p_ptr->conn == NOT_CONNECTED) continue;
2898 
2899 		/* Make sure this player is at this depth */
2900 		if (!inarea(&p_ptr->wpos, wpos)) continue;
2901 
2902 		/* Skip if not visible */
2903 		if (!p_ptr->mon_vis[m_idx]) continue;
2904 
2905 		/* Can he see this monster? */
2906 		if (!(p_ptr->cave_flag[m_ptr->fy][m_ptr->fx] & CAVE_VIEW)) continue;
2907 
2908 		/* Acquire the monster name */
2909 		monster_desc(i, m_name, m_idx, 0);
2910 
2911 		msg_format(i, "%^s %s", m_name, msg);
2912 	}
2913 }
2914 
2915 /* send a message to all online party members */
2916 void msg_party_format(int Ind, cptr fmt, ...) {
2917 	int i;
2918 	char buf[1024];
2919 	va_list vp;
2920 
2921 	/* Begin the Varargs Stuff */
2922 	va_start(vp, fmt);
2923 	/* Format the args, save the length */
2924 	(void)vstrnfmt(buf, 1024, fmt, vp);
2925 	/* End the Varargs Stuff */
2926 	va_end(vp);
2927 
2928 	/* Tell every player */
2929 	for (i = 1; i <= NumPlayers; i++) {
2930 		/* Skip disconnected players */
2931 		if (Players[i]->conn == NOT_CONNECTED)
2932 			continue;
2933 
2934 #if 0
2935 		/* Skip the specified player */
2936 		if (i == Ind)
2937 			continue;
2938 #endif
2939 
2940 		/* skip players not in his party */
2941 		if (Players[i]->party != Players[Ind]->party)
2942 			continue;
2943 
2944 		/* Tell this one */
2945 		msg_print(i, buf);
2946 	 }
2947 }
2948 
2949 /* send a message to all online guild members */
2950 void msg_guild_format(int Ind, cptr fmt, ...) {
2951 	int i;
2952 	char buf[1024];
2953 	va_list vp;
2954 
2955 	/* Begin the Varargs Stuff */
2956 	va_start(vp, fmt);
2957 	/* Format the args, save the length */
2958 	(void)vstrnfmt(buf, 1024, fmt, vp);
2959 	/* End the Varargs Stuff */
2960 	va_end(vp);
2961 
2962 	/* Tell every player */
2963 	for (i = 1; i <= NumPlayers; i++) {
2964 		/* Skip disconnected players */
2965 		if (Players[i]->conn == NOT_CONNECTED)
2966 			continue;
2967 
2968 #if 0
2969 		/* Skip the specified player */
2970 		if (i == Ind)
2971 			continue;
2972 #endif
2973 
2974 		/* skip players not in his guild */
2975 		if (Players[i]->guild != Players[Ind]->guild)
2976 			continue;
2977 
2978 		/* Tell this one */
2979 		msg_print(i, buf);
2980 	 }
2981 }
2982 
2983 #if 0 /* unused atm */
2984 static char* dodge_diz(int chance) {
2985 	if (chance < 5)
2986 		return "almost no";
2987 	else if (chance < 14)
2988 		return "a slight";
2989 	else if (chance < 23)
2990 		return "a significant";
2991 	else if (chance < 30)
2992 		return "a good";
2993 	else if (chance < 40)
2994 		return "a very good";
2995 	else
2996 		return "a high";
2997 }
2998 #endif
2999 
3000 /*
3001  * Dodge Chance Feedback.
3002  */
3003 void use_ability_blade(int Ind)
3004 {
3005 	player_type *p_ptr = Players[Ind];
3006 #ifndef NEW_DODGING
3007 	int dun_level = getlevel(&p_ptr->wpos);
3008 	int chance = p_ptr->dodge_level - (dun_level * 5 / 6);
3009 
3010 	if (chance < 0) chance = 0;
3011 	if (chance > DODGE_MAX_CHANCE) chance = DODGE_MAX_CHANCE;	// see DODGE_MAX_CHANCE in melee1.c
3012 //	if (is_admin(p_ptr))
3013 		msg_format(Ind, "You have a %d%% chance of dodging a level %d monster.", chance, dun_level);
3014  #if 0
3015 	if (chance < 5)
3016 		msg_format(Ind, "You have almost no chance of dodging a level %d monster.", dun_level);
3017 	else if (chance < 10)
3018 		msg_format(Ind, "You have a slight chance of dodging a level %d monster.", dun_level);
3019 	else if (chance < 20)
3020 		msg_format(Ind, "You have a significant chance of dodging a level %d monster.", dun_level);
3021 	else if (chance < 40)
3022 		msg_format(Ind, "You have a large chance of dodging a level %d monster.", dun_level);
3023 	else if (chance < 70)
3024 		msg_format(Ind, "You have a high chance of dodging a level %d monster.", dun_level);
3025 	else
3026 		msg_format(Ind, "You will usually dodge a level %d monster.", dun_level);
3027  #endif
3028 #else
3029 	int lev;
3030 	lev = p_ptr->lev * 2 < 127 ? p_ptr->lev * 2 : 127;
3031 
3032 	if (is_admin(p_ptr))
3033 		msg_format(Ind, "You have a %d%%/%d%% chance of dodging a level %d/%d monster.",
3034 		    apply_dodge_chance(Ind, p_ptr->lev), apply_dodge_chance(Ind, lev), p_ptr->lev, lev);
3035 
3036 /*
3037 	msg_format(Ind, "You have %s/%s chance of dodging a level %d/%d monster.",
3038 		dodge_diz(apply_dodge_chance(Ind, p_ptr->lev)), dodge_diz(apply_dodge_chance(Ind, lev)),
3039 		p_ptr->lev, lev);
3040 
3041 	msg_format(Ind, "You have %s chance of dodging a level %d monster.",
3042 	    dodge_diz(apply_dodge_chance(Ind, p_ptr->lev)), p_ptr->lev);
3043 */
3044 
3045 	else msg_format(Ind, "You have a %d%% chance of dodging a level %d monster's attack.",
3046 	    apply_dodge_chance(Ind, p_ptr->lev), p_ptr->lev);
3047 #endif
3048 	return;
3049 }
3050 
3051 
3052 
3053 void check_parryblock(int Ind)
3054 {
3055 #ifdef ENABLE_NEW_MELEE
3056 	char msg[80];
3057 	player_type *p_ptr = Players[Ind];
3058 	int apc = apply_parry_chance(p_ptr, p_ptr->weapon_parry), abc = apply_block_chance(p_ptr, p_ptr->shield_deflect);
3059 
3060 	if (is_admin(p_ptr)) {
3061 		msg_format(Ind, "You have exactly %d%%/%d%% base chance of parrying/blocking.",
3062 			p_ptr->weapon_parry, p_ptr->shield_deflect);
3063 		msg_format(Ind, "You have exactly %d%%/%d%% real chance of parrying/blocking.",
3064 			apc, abc);
3065 	} else {
3066 		if (!apc)
3067 			strcpy(msg, "You cannot parry at the moment. ");
3068 			//msg_print(Ind, "You cannot parry at the moment.");
3069  #if 0
3070 		else if (apc < 5)
3071 			msg_print(Ind, "You have almost no chance of parrying.");
3072 		else if (apc < 10)
3073 			msg_print(Ind, "You have a slight chance of parrying.");
3074 		else if (apc < 20)
3075 			msg_print(Ind, "You have a significant chance of parrying.");
3076 		else if (apc < 30)
3077 			msg_print(Ind, "You have a good chance of parrying.");
3078 		else if (apc < 40)
3079 			msg_print(Ind, "You have a very good chance of parrying.");
3080 		else if (apc < 50)
3081 			msg_print(Ind, "You have an excellent chance of parrying.");
3082 		else
3083 			msg_print(Ind, "You have a superb chance of parrying.");
3084  #else
3085 		else strcpy(msg, format("You have a %d%% chance of parrying. ",
3086 			apc));
3087  #endif
3088 
3089 		if (!abc)
3090 			strcat(msg, "You cannot block at the moment.");
3091 			//msg_print(Ind, "You cannot block at the moment.");
3092  #if 0
3093 		else if (abc < 5)
3094 			msg_print(Ind, "You have almost no chance of blocking.");
3095 		else if (abc < 14)
3096 			msg_print(Ind, "You have a slight chance of blocking.");
3097 		else if (abc < 23)
3098 			msg_print(Ind, "You have a significant chance of blocking.");
3099 		else if (abc < 33)
3100 			msg_print(Ind, "You have a good chance of blocking.");
3101 		else if (abc < 43)
3102 			msg_print(Ind, "You have a very good chance of blocking.");
3103 		else if (abc < 48)
3104 			msg_print(Ind, "You have an excellent chance of blocking.");
3105 		else
3106 			msg_print(Ind, "You have a superb chance of blocking.");
3107  #else
3108 		else strcat(msg, format("You have a %d%% chance of blocking.",
3109 			abc));
3110  #endif
3111 		msg_print(Ind, msg);
3112 	}
3113 #endif
3114 	return;
3115 }
3116 
3117 
3118 
3119 void toggle_shoot_till_kill(int Ind)
3120 {
3121 	player_type *p_ptr = Players[Ind];
3122 	if (p_ptr->shoot_till_kill) {
3123 		msg_print(Ind, "\377wFire-till-kill mode now off.");
3124 		p_ptr->shooting_till_kill = FALSE;
3125 	} else {
3126 		msg_print(Ind, "\377wFire-till-kill mode now on!");
3127 	}
3128 	p_ptr->shoot_till_kill = !p_ptr->shoot_till_kill;
3129 s_printf("SHOOT_TILL_KILL: Player %s toggles %s.\n", p_ptr->name, p_ptr->shoot_till_kill ? "true" : "false");
3130 	p_ptr->redraw |= PR_STATE;
3131 	return;
3132 }
3133 
3134 void toggle_dual_mode(int Ind)
3135 {
3136 	player_type *p_ptr = Players[Ind];
3137 	if (p_ptr->dual_mode)
3138 		msg_print(Ind, "\377wDual-wield mode: Main-hand. (This disables all dual-wield boni.)");
3139 	else
3140 		msg_print(Ind, "\377wDual-wield mode: Dual-hand.");
3141 	p_ptr->dual_mode = !p_ptr->dual_mode;
3142 s_printf("DUAL_MODE: Player %s toggles %s.\n", p_ptr->name, p_ptr->dual_mode ? "true" : "false");
3143 	p_ptr->redraw |= PR_STATE | PR_PLUSSES;
3144 	calc_boni(Ind);
3145 	return;
3146 }
3147 
3148 #ifdef CENSOR_SWEARING
3149 /* similar to strstr(), but catches char repetitions and swap-arounds.
3150    TODO: current implementation is pretty naive, need to use more effective algo when less lazy. */
3151 #define GET_MOST_DELAYED_MATCH /* must be enabled */
3152 static char* censor_strstr(char *line, char *word, int *eff_len) {
3153 	char bufl[MSG_LEN], bufs[NAME_LEN], *best = NULL;
3154 	int i, j, add;
3155 
3156 	if (line[0] == '\0') return (char*)NULL;
3157 	if (word[0] == '\0') return (char*)NULL;//or line, I guess.
3158 
3159 	strcpy(bufl, line);
3160 	strcpy(bufs, word);
3161 	*eff_len = 0;
3162 
3163 	/* (Note: Since we're returning line[] without any char-mapping, we
3164 	   can only do replacements here, not shortening/expanding. */
3165 	/* replace 'ck' or 'kc' by just 'kk' */
3166 	i = 0;
3167 	while (bufl[i] && bufl[i + 1]) {
3168 		if (bufl[i] == 'c' && bufl[i + 1] == 'k') bufl[i] = 'k';
3169 		else if (bufl[i] == 'k' && bufl[i + 1] == 'c') bufl[i + 1] = 'k';
3170 		i++;
3171 	}
3172 	i = 0;
3173 	while (bufs[i] && bufs[i + 1]) {
3174 		if (bufs[i] == 'c' && bufs[i + 1] == 'k') bufs[i] = 'k';
3175 		else if (bufs[i] == 'k' && bufs[i + 1] == 'c') bufs[i + 1] = 'k';
3176 		i++;
3177 	}
3178 
3179 	/* search while allowing repetitions/swapping of characters.
3180 	   Important: Get the furthest possible match for the starting char of
3181 	   the search term, required for 'preceeding/separated' check which is
3182 	   executed directly on 'line' further below in censor_aux(). Eg:
3183 	   sshit -> match the 2nd s and return it as position, not the 1st.
3184 	   Otherwise, "this shit" -> "thisshit" -> the first s and the h would
3185 	   be separated by a space in 'line' and therefore trigger the nonswear
3186 	   special check. */
3187 	i = 0;
3188 	while (bufl[i]) {
3189 		j = 0;
3190 		add = 0; /* track duplicte chars */
3191 
3192 #ifdef GET_MOST_DELAYED_MATCH
3193 		/* if we already got a match and the test of the upfollowing
3194 		   position is negative, take that match, since we still want
3195 		   to get all occurances eventually, not just the last one. */
3196 		if (bufs[j] != bufl[i + j + add] && best) return best;
3197 #endif
3198 
3199 		while (bufs[j] == bufl[i + j + add]) {
3200 			if (bufs[j + 1] == '\0') {
3201 				*eff_len = j + add + 1;
3202 #ifndef GET_MOST_DELAYED_MATCH
3203 				return &line[i]; /* FOUND */
3204 #else
3205 				best = &line[i];
3206 				break;
3207 #endif
3208 			}
3209 			j++;
3210 
3211 			/* end of line? */
3212 			if (bufl[i + j + add] == '\0')
3213 #ifndef GET_MOST_DELAYED_MATCH
3214 				return (char*)NULL; /* NOT FOUND */
3215 #else
3216 				return best;
3217 #endif
3218 
3219 			/* reduce duplicate chars */
3220 			if (bufs[j] != bufl[i + j + add]) {
3221 				if (bufs[j - 1] == bufl[i + j + add])
3222 					add++;
3223 			}
3224 		}
3225 		/* NOT FOUND so far */
3226 		i++;
3227 	}
3228 #ifndef GET_MOST_DELAYED_MATCH
3229 	return (char*)NULL; /* NOT FOUND */
3230 #else
3231 	return best;
3232 #endif
3233 }
3234 
3235 /* Censor swear words while keeping good words, and determining punishment level */
3236 //NOTE: EXEMPT_BROKEN_SWEARWORDS and HIGHLY_EFFECTIVE_CENSOR shouldn't really work togehter (because latter one kills spaces etc.)
3237 #define EXEMPT_BROKEN_SWEARWORDS	/* don't 'recognize' swear words that are broken up into 'innocent' parts */
3238 #define HIGHLY_EFFECTIVE_CENSOR		/* strip all kinds of non-alpha chars too? ([disabled])*/
3239 #define CENSOR_PH_TO_F			/* (slightly picky) convert ph to f ?*/
3240 #define REDUCE_DUPLICATE_H		/* (slightly picky) reduce multiple h to just one? */
3241 #define REDUCE_H_CONSONANT		/* (slightly picky) drop h before consonants? */
3242 #define CENSOR_LEET			/* 433+ $p34k: Try to translate certain numbers and symbols to letters? ([disabled]) */
3243 #define EXEMPT_VSHORT_COMBINED		/* Exempt very short swear words if they're part of a bigger 'word'?
3244 					   //(Problem: They could just be preceeded by 'the' or 'you' w/o a space) -
3245 					   //practically unfeasible (~1250+ words!) to utilize nonswear list for this ([enabled]) */
3246 #ifdef EXEMPT_VSHORT_COMBINED
3247  #define VSHORT_STEALTH_CHECK		/* Perform a check if the swear word is masked by things like 'the', 'you', 'a(n)'.. */
3248 #endif
3249 static int censor_aux(char *buf, char *lcopy, int *c, bool leet, bool max_reduce) {
3250 	int i, j, k, offset, cc[MSG_LEN], pos, eff_len;
3251 	char line[MSG_LEN];
3252 	char *word, l0, l1, l2, l3;
3253 	int level = 0;
3254 #ifdef VSHORT_STEALTH_CHECK
3255 	bool masked;
3256 #endif
3257 
3258 	/* create working copies */
3259 	strcpy(line, buf);
3260 	for (i = 0; !i || c[i]; i++) cc[i] = c[i];
3261 	cc[i] = 0;
3262 
3263 	/* replace certain non-alpha chars by alpha chars (leet speak detection)? */
3264 	if (leet) {
3265 #ifdef HIGHLY_EFFECTIVE_CENSOR
3266 		bool is_leet = FALSE, was_leet;
3267 #endif
3268 		i = 0;
3269 		while (lcopy[i]) {
3270 #ifdef HIGHLY_EFFECTIVE_CENSOR
3271 			was_leet = is_leet;
3272 			is_leet = TRUE;//(i != 0);//meaning 'i > 0', for efficiency
3273 #endif
3274 
3275 			switch (lcopy[i]) {
3276 			case '@': lcopy[i] = 'a'; break;
3277 			case '<': lcopy[i] = 'c'; break;
3278 			case '!': lcopy[i] = 'i'; break;
3279 			case '|': lcopy[i] = 'l'; break;//hm, could be i too :/
3280 //			case '(': lcopy[i] = 'c'; break;
3281 //			case ')': lcopy[i] = 'i'; break;
3282 //			case '/': lcopy[i] = 'i'; break;
3283 //			case '\\': lcopy[i] = 'i'; break;
3284 			case '$': lcopy[i] = 's'; break;
3285 			case '+': lcopy[i] = 't'; break;
3286 			case '1': lcopy[i] = 'i'; break;
3287 			case '3': lcopy[i] = 'e'; break;
3288 			case '4': lcopy[i] = 'a'; break;
3289 			case '5': lcopy[i] = 's'; break;
3290 			case '7': lcopy[i] = 't'; break;
3291 			case '8': lcopy[i] = 'b'; break;
3292 			case '0': lcopy[i] = 'o'; break;
3293 #ifdef HIGHLY_EFFECTIVE_CENSOR
3294 			/* hack: Actually _counter_ the capabilities of highly-effective
3295 			   censoring being done further below after this. Reason:
3296 			   Catch numerical expressions that probably aren't l33t sp34k,
3297 			   such as "4.5", "4,5" or "4/5" but also '4} (55'!
3298 			   Exact rule: <leet char><non-leet non-alphanum char> = allowed. */
3299 			default:
3300 				is_leet = FALSE; //this character wasn't one of the leet cases above
3301 				/* inconspicuous non-alphanum char? */
3302 				if (was_leet &&
3303 				    (lcopy[i] < 'a' || lcopy[i] > 'z') && (lcopy[i] < '0' || lcopy[i] > '9'))
3304 					/* replace by, say 'x' (harmless^^) (Note: Capital character doesn't work, gets filtered out futher below */
3305 					lcopy[i] = 'x';
3306 				break;
3307 #else
3308 			default:
3309 				lcopy[i] = ' ';
3310  #if 0 /* nonsense, because nonswearing isn't applied after this anymore, but was long before in censor() already! */
3311 				lcopy[i] = '~'; /* ' ' is usually in use in nonswearing.txt,
3312 				    so it would need to be removed there whenever we turn off HIGHLY_EFFECTIVE_CENSOR
3313 				    and put back in when we reenable it. Using a non-used character instead of SPACE
3314 				    is therefore a much more comfortable way. */
3315  #endif
3316 				break;
3317 #endif
3318 			}
3319 
3320 			line[cc[i]] = lcopy[i];
3321 			i++;
3322 		}
3323 	}
3324 #ifdef REDUCE_H_CONSONANT
3325 	/* reduce 'h' before consonant */
3326 	//TODO: do HIGHLY_EFFECTIVE_CENSOR first probably
3327 	i = j = 0;
3328 	while (lcopy[j]) {
3329 		if (lcopy[j] == 'h' &&
3330 		    lcopy[j + 1] != '\0' &&
3331 		    lcopy[j + 1] != 'a' &&
3332 		    lcopy[j + 1] != 'e' &&
3333 		    lcopy[j + 1] != 'i' &&
3334 		    lcopy[j + 1] != 'o' &&
3335 		    lcopy[j + 1] != 'u' &&
3336 		    lcopy[j + 1] != 'y'
3337 		    && lcopy[j + 1] >= 'a' && lcopy[j + 1] <= 'z' /*TODO drop this, see 'TODO' above*/
3338 		    /* detect 'shlt' as masked form of 'shit', without reducing it to 'slt' */
3339 		    && (!j || (lcopy[j - 1] != 'c' && lcopy[j - 1] != 's'))
3340 		    ) {
3341 			j++;
3342 		}
3343 
3344 		/* build index map for stripped string */
3345 		cc[i] = cc[j];
3346 		lcopy[i] = lcopy[j];
3347 
3348 		i++;
3349 		j++;
3350 	}
3351 	/* ensure the reduced string is possibly terminated earlier */
3352 	lcopy[i] = '\0';
3353 	cc[i] = 0;
3354 
3355 	/* reduce 'h' after consonant except for c or s */
3356 	//TODO: do HIGHLY_EFFECTIVE_CENSOR first probably
3357 	i = j = 1;
3358 	if (lcopy[0])
3359 	while (lcopy[j]) {
3360 		if (lcopy[j] == 'h' &&
3361 		    lcopy[j - 1] != 'c' &&
3362 		    lcopy[j - 1] != 's' &&
3363 		    lcopy[j - 1] != 'a' &&
3364 		    lcopy[j - 1] != 'e' &&
3365 		    lcopy[j - 1] != 'i' &&
3366 		    lcopy[j - 1] != 'o' &&
3367 		    lcopy[j - 1] != 'u'
3368 		    && lcopy[j - 1] >= 'a' && lcopy[j - 1] <= 'z' /*TODO drop this, see 'TODO' above*/
3369 		    ) {
3370 			j++;
3371 			continue;
3372 		}
3373 
3374 		/* build index map for stripped string */
3375 		cc[i] = cc[j];
3376 		lcopy[i] = lcopy[j];
3377 
3378 		i++;
3379 		j++;
3380 	}
3381 	/* ensure the reduced string is possibly terminated earlier */
3382 	lcopy[i] = '\0';
3383 	cc[i] = 0;
3384 #endif
3385 
3386 #ifdef HIGHLY_EFFECTIVE_CENSOR
3387 	/* check for non-alpha chars and _drop_ them (!) */
3388 	i = j = 0;
3389 	while (lcopy[j]) {
3390 		if ((lcopy[j] < 'a' || lcopy[j] > 'z') &&
3391 		    lcopy[j] != 'Z') {
3392 			j++;
3393 			continue;
3394 		}
3395 
3396 		/* modify index map for stripped string */
3397 		cc[i] = cc[j];
3398 		lcopy[i] = lcopy[j];
3399 		i++;
3400 		j++;
3401 	}
3402 	/* ensure the reduced string is possibly terminated earlier */
3403 	lcopy[i] = '\0';
3404 	cc[i] = 0;
3405 #endif
3406 
3407 	/* reduce repeated chars (>3 for consonants, >2 for vowel) */
3408 	i = j = 0;
3409 	while (lcopy[j]) {
3410 		/* paranoia (if HIGHLY_EFFECTIVE_CENSOR is enabled) */
3411 		if (lcopy[j] < 'a' || lcopy[j] > 'z') {
3412 			cc[i] = cc[j];
3413 			lcopy[i] = lcopy[j];
3414 			i++;
3415 			j++;
3416 			continue;
3417 		}
3418 
3419 		/* reduce any char repetition and re-check for swear words */
3420 		if (max_reduce) {
3421 			if (lcopy[j + 1] == lcopy[j]) {
3422 				j++;
3423 				continue;
3424 			}
3425 		} else switch (lcopy[j]) {
3426 		case 'a': case 'e': case 'i': case 'o': case 'u': case 'y':
3427 			if (lcopy[j + 1] == lcopy[j] &&
3428 			    lcopy[j + 2] == lcopy[j]) {
3429 				j++;
3430 				continue;
3431 			}
3432 		default:
3433 			if (lcopy[j + 1] == lcopy[j] &&
3434 			    lcopy[j + 2] == lcopy[j] &&
3435 			    lcopy[j + 3] == lcopy[j]) {
3436 				j++;
3437 				continue;
3438 			}
3439 			break;
3440 		}
3441 
3442 		/* modify index map for reduced string */
3443 		cc[i] = cc[j];
3444 		lcopy[i] = lcopy[j];
3445 		i++;
3446 		j++;
3447 	}
3448 	/* ensure the reduced string is possibly terminated earlier */
3449 	lcopy[i] = '\0';
3450 	cc[i] = 0;
3451 
3452 	/* check for swear words and censor them */
3453 	for (i = 0; swear[i].word[0]; i++) {
3454 		offset = 0;
3455 
3456 		/* check for multiple occurrances of this swear word */
3457 		while ((word = censor_strstr(lcopy + offset, swear[i].word, &eff_len))) {
3458 			pos = word - lcopy;
3459 			l0 = tolower(line[cc[pos]]);
3460 			l1 = l2 = l3 = 0; //kill compiler warnings
3461 			if (cc[pos] >= 1) l1 = tolower(line[cc[pos] - 1]);
3462 			if (cc[pos] >= 2) {
3463 				l2 = tolower(line[cc[pos] - 2]);
3464 				/* catch & dismiss colour codes */
3465 				if (l2 == '\377') l1 = 'Y'; /* 'disable' char */
3466 			}
3467 			if (cc[pos] >= 3) {
3468 				l3 = tolower(line[cc[pos] - 3]);
3469 				/* catch & dismiss colour codes */
3470 				if (l3 == '\377') l2 = 'Y'; /* 'disable' char */
3471 			}
3472 
3473 			/* prevent checking the same occurance repeatedly */
3474 			offset = pos + 1;
3475 
3476 #ifdef EXEMPT_BROKEN_SWEARWORDS
3477 			/* hm, maybe don't track swear words if proceeded and postceeded by some other letters
3478 			   and separated by spaces inside it -- and if those nearby letters aren't the same letter,
3479 			   if someone just duplicates it like 'sshitt' */
3480 			/* check for swear-word-preceding non-duplicate alpha char */
3481 			if (cc[pos] > 0 &&
3482 			    l1 >= 'a' && l1 <= 'z' &&
3483 			    (l1 != l0 || strlen(swear[i].word) <= 3)) {
3484 #ifdef EXEMPT_VSHORT_COMBINED
3485 				/* special treatment for swear words of <= 3 chars length: */
3486 				if (strlen(swear[i].word) <= 3) {
3487  #ifdef VSHORT_STEALTH_CHECK
3488 					/* check for masking by 'the', 'you', 'a(n)' etc. */
3489 					masked = TRUE;
3490 					if (cc[pos] == 1 || cc[pos] == 2) {
3491 						/* if there's only 1 or 2 chars before the swear word, it depends on the first
3492 						   char of the swear word: vowel is ok, consonant is not. */
3493 						switch (l0) {
3494 						case 'a': case 'e': case 'i': case 'o': case 'u': case 'y':
3495 							masked = FALSE;
3496 							break;
3497 						}
3498 					}
3499 					/* if there are more chars before the swear word, it's maybe ok if
3500 					   the swear word starts on a vowel ('XXXXass' is very common!) */
3501 					else {
3502 						/* ..to be specific, if the char before the swear word is NOT a vowel (well, duh, true for most cases).
3503 						   Yeah yeah, just trying to fix 'ass' detection here, and a 'y' or 'u' in front of it remains swearing.. */
3504 						switch (l0) {
3505 						case 'a': case 'e': case 'i': case 'o': case 'u': case 'y':
3506 							switch (l1) {
3507 							case 'a': case 'e': case 'i': case 'o': case 'u': case 'y':
3508 								break;
3509 							default:
3510 								masked = FALSE;
3511 								break;
3512 							}
3513 							break;
3514 						}
3515 					}
3516 					/* if swear word is followed by a vowel, it's ok. */
3517 					switch (tolower(line[cc[pos] + strlen(swear[i].word)])) {
3518 					case 'a': case 'e': case 'i': case 'o': case 'u': case 'y':
3519 						masked = FALSE;
3520 						break;
3521 					}
3522 				    /* so if it is already determined by now to be a masked swear word, skip the vshort-exemption-checks */
3523 				    if (!masked) {
3524  #endif
3525 
3526  #if 0 /* softened this up, see below */
3527 					/* if there's UP TO 2 other chars before it or exactly 1 non-duplicate char, it's exempt.
3528 					   (for more leading chars, nonswear has to be used.) */
3529 					if (cc[pos] == 1 && l1 >= 'a' && l1 <= 'z' && l1 != l0) continue;
3530 					if (cc[pos] == 2) {
3531 						if (l1 >= 'a' && l1 <= 'z' && l2 >= 'a' && l2 <= 'z' &&
3532 						    (l0 != l1 || l0 != l2 || l1 != l2)) continue;
3533 						/* also test for only 1 leading alpha char */
3534 						if ((l2 < 'a' || l2 > 'z') &&
3535 						    l1 >= 'a' && l1 <= 'z' && l1 != l0) continue;
3536 					}
3537 					if (cc[pos] >= 3) {
3538 						 if ((l3 < 'a' || l3 > 'z') &&
3539 						    l1 >= 'a' && l1 <= 'z' && l2 >= 'a' && l2 <= 'z' &&
3540 						    (l0 != l1 || l0 != l2 || l1 != l2)) continue;
3541 						/* also test for only 1 leading alpha char */
3542 						if ((l2 < 'a' || l2 > 'z') &&
3543 						    l1 >= 'a' && l1 <= 'z' && l1 != l0) continue;
3544 					}
3545 					/* if there's no char before it but 2 other chars after it or 1 non-dup after it, it's exempt. */
3546 					//TODO maybe - or just use nonswear for that
3547  #else
3548 					/* if there's at least 2 other chars before it, which aren't both duplicates, or
3549 					   if there's exactly 1 non-duplicate char before it, it's exempt. */
3550 					if (cc[pos] == 1 && l1 >= 'a' && l1 <= 'z' && l1 != l0) continue;
3551 					if (cc[pos] >= 2) {
3552 						if (l1 >= 'a' && l1 <= 'z' && l2 >= 'a' && l2 <= 'z' &&
3553 						    (l0 != l1 || l0 != l2 || l1 != l2)) continue;
3554 						/* also test for only 1 leading alpha char */
3555 						if ((l2 < 'a' || l2 > 'z') &&
3556 						    l1 >= 'a' && l1 <= 'z' && l1 != l0) continue;
3557 					}
3558 					/* if there's no char before it but 2 other chars after it or 1 non-dup after it, it's exempt. */
3559 					//TODO maybe - or just use nonswear for that
3560  #endif
3561  #ifdef VSHORT_STEALTH_CHECK
3562 				    }
3563  #endif
3564 				}
3565 #endif
3566 
3567 				/* check that the swear word occurance was originally non-continuous, ie separated by space etc.
3568 				   (if this is FALSE then this is rather a case for nonswearwords.txt instead.) */
3569 				for (j = cc[pos]; j < cc[pos + strlen(swear[i].word) - 1]; j++) {
3570 					if (tolower(line[j]) < 'a' || tolower(line[j] > 'z')) {
3571 						/* heuristical non-swear word found! */
3572 						break;
3573 					}
3574 				}
3575 				/* found? */
3576 				if (j < cc[pos + strlen(swear[i].word) - 1]) {
3577 					/* prevent it from getting tested for swear words */
3578 					continue;
3579 				}
3580 			}
3581 #endif
3582 
3583 			/* we can skip ahead */
3584 			offset = pos + strlen(swear[i].word);
3585 
3586 #if 0
3587 			/* censor it! */
3588 			for (j = 0; j < eff_len); j++) {
3589 				line[cc[pos + j]] = buf[cc[pos + j]] = '*';
3590 				lcopy[pos + j] = '*';
3591 			}
3592 #else
3593 			/* actually censor separator chars too, just so it looks better ;) */
3594 			for (j = 0; j < eff_len - 1; j++) {
3595 				for (k = cc[pos + j]; k <= cc[pos + j + 1]; k++)
3596 					line[k] = buf[k] = '*';
3597 
3598 				/* MUST be disabled or interferes with detection overlaps: */
3599 				/* for processing lcopy in a while loop instead of just 'if'
3600 				   -- OBSOLETE ACTUALLY (thanks to 'offset')? */
3601 //				lcopy[pos + j] = '*';
3602 			}
3603 			/* see right above - MUST be disabled: */
3604 //			lcopy[pos + j] = '*';
3605 #endif
3606 			level = MAX(level, swear[i].level);
3607 		}
3608 	}
3609 	return(level);
3610 }
3611 
3612 static int censor(char *line) {
3613 	int i, j, im, jm, cc[MSG_LEN], offset;
3614 #ifdef CENSOR_LEET
3615 	int j_pre = 0, cc_pre[MSG_LEN];
3616 #endif
3617 	char lcopy[MSG_LEN], lcopy2[MSG_LEN];
3618 	char tmp1[MSG_LEN], tmp2[MSG_LEN], tmp3[MSG_LEN], tmp4[MSG_LEN];
3619 	char *word;
3620 
3621 	strcpy(lcopy, line);
3622 
3623 	/* convert to lower case */
3624 	i = 0;
3625 	while (lcopy[i]) {
3626 		lcopy[i] = tolower(lcopy[i]);
3627 		i++;
3628 	}
3629 
3630 	/* strip colour codes and initialize index map (cc[]) at that */
3631 	i = j = 0;
3632 	while (lcopy[j]) {
3633 		if (lcopy[j] == '\377') {
3634 			j++;
3635 			if (lcopy[j]) j++;
3636 			continue;
3637 		}
3638 
3639 		/* initially build index map for stripped string */
3640 		cc[i] = j;
3641 		lcopy[i] = lcopy[j];
3642 
3643 		i++;
3644 		j++;
3645 	}
3646 	lcopy[i] = '\0';
3647 	cc[i] = 0;
3648 
3649 #ifdef CENSOR_PH_TO_F
3650 	/* reduce ph to f */
3651 	i = j = 0;
3652 	while (lcopy[j]) {
3653 		if (lcopy[j] == 'p' && lcopy[j + 1] == 'h') {
3654 			cc[i] = cc[j];
3655 			lcopy[i] = 'f';
3656 			i++;
3657 			j += 2;
3658 			continue;
3659 		}
3660 
3661 		/* build index map for stripped string */
3662 		cc[i] = cc[j];
3663 		lcopy[i] = lcopy[j];
3664 
3665 		i++;
3666 		j++;
3667 	}
3668 	lcopy[i] = '\0';
3669 	cc[i] = 0;
3670 #endif
3671 
3672 #ifdef REDUCE_DUPLICATE_H
3673 	/* reduce hh to h */
3674 	i = j = 0;
3675 	while (lcopy[j]) {
3676 		if (lcopy[j] == 'h' && lcopy[j + 1] == 'h') {
3677 			j++;
3678 			continue;
3679 		}
3680 
3681 		/* build index map for stripped string */
3682 		cc[i] = cc[j];
3683 		lcopy[i] = lcopy[j];
3684 
3685 		i++;
3686 		j++;
3687 	}
3688 	lcopy[i] = '\0';
3689 	cc[i] = 0;
3690 #endif
3691 
3692 #ifdef CENSOR_LEET
3693 	/* special hardcoded case: variants of '455hole' leet-speak */
3694 	j = 0;
3695 	for (i = 0; i < strlen(lcopy); i++) {
3696 		/* reduce duplicate chars (must do it this way instead of i==i+1, because we need full cc-mapped string) */
3697 		if (i && lcopy[i] == lcopy[i - 1]) continue;
3698 		/* build un-leeted, mapped string */
3699 		if ((lcopy[i] >= '0' && lcopy[i] <= '9') || (lcopy[i] >= 'a' && lcopy[i] <= 'z')
3700 		    || lcopy[i] == '@' || lcopy[i] == '$' || lcopy[i] == '!' || lcopy[i] == '|'
3701 		    ) {
3702 			switch (lcopy[i]) {
3703 			case '@': lcopy2[j] = 'a'; break;
3704 			case '4': lcopy2[j] = 'a'; break;
3705 			case '$': lcopy2[j] = 's'; break;
3706 			case '5': lcopy2[j] = 's'; break;
3707 			case '0': lcopy2[j] = 'o'; break;
3708 			case '!': lcopy2[j] = 'i'; break;
3709 			case '|': lcopy2[j] = 'l'; break;
3710 			case '3': lcopy2[j] = 'e'; break;
3711 			default: lcopy2[j] = lcopy[i];
3712 			}
3713 			cc_pre[j] = i;
3714 			j++;
3715 		}
3716 	}
3717 	lcopy2[j] = 0;
3718 	if ((word = strstr(lcopy2, "ashole")) ||
3719 	    (word = strstr(lcopy2, "ashoie")))
3720 		/* use severity level of 'ashole' (condensed) for 'asshole' */
3721 		for (i = 0; swear[i].word[0]; i++) {
3722 			if (!strcmp(swear[i].word, "ashole")) {
3723 				j_pre = swear[i].level;
3724 				/* if it was to get censored, do so */
3725 				if (j_pre) {
3726  #if 0
3727 					for (offset = cc_pre[word - lcopy2]; offset <= cc_pre[(word - lcopy2) + 5]; offset++)
3728 						lcopy[offset] = '*';
3729  #endif
3730 					for (offset = cc[cc_pre[word - lcopy2]]; offset <= cc[cc_pre[(word - lcopy2) + 5]]; offset++)
3731 						line[offset] = '*';
3732 				}
3733 				break;
3734 			}
3735 		}
3736 #endif
3737 
3738 	/* check for legal words first */
3739 	//TODO: could be moved into censor_aux and after leet speek conversion, to allow leet speeking of non-swear words (eg "c00k")
3740 	strcpy(lcopy2, lcopy); /* use a 'working copy' to allow _overlapping_ nonswear words */
3741 	/*TODO!!! non-split swear words should take precedence over split-up non-swear words: "was shit" -> "as sh" is 'fine'.. BEEP!
3742 	  however, this can be done by disabling HIGHLY_EFFECTIVE_CENSORING and enabling EXEMPT_BROKEN_SWEARWORDS as a workaround. */
3743 	for (i = 0; nonswear[i][0]; i++) {
3744 #ifndef HIGHLY_EFFECTIVE_CENSOR
3745 		/* hack! If HIGHLY_EFFECTIVE_CENSOR is NOT enabled, skip all nonswearing-words that contain spaces!
3746 		   This is done because those nonswearing-words are usually supposed to counter wrong positives
3747 		   from HIGHLY_EFFECTIVE_CENSOR, and will lead to false negatives when it is disabled xD
3748 		   (eg: 'was shit' chat with "as sh" non-swear)  - C. Blue */
3749 		if (strchr(nonswear[i], ' ')) continue;
3750 #endif
3751 
3752 		offset = 0;
3753 		/* check for multiple occurrances of this nonswear word */
3754 		while ((word = strstr(lcopy + offset, nonswear[i]))) {
3755 			/* prevent checking the same occurance repeatedly */
3756 			offset = word - lcopy + strlen(nonswear[i]);
3757 
3758 			if ((nonswear_affix[i] & 0x1)) {
3759 				if (word == lcopy) continue; //beginning of lcopy string?
3760 				if (!((*(word - 1) >= 'a' && *(word - 1) <= 'z') ||
3761 				    (*(word - 1) >= '0' && *(word - 1) <= '1') ||
3762 				    *(word - 1) == '\''))
3763 					continue;
3764 			}
3765 			if ((nonswear_affix[i] & 0x2)) {
3766 				if (!word[strlen(nonswear[i])]) continue; //end of lcopy string?
3767 				if (!((lcopy[offset] >= 'a' && lcopy[offset] <= 'z') ||
3768 				    (lcopy[offset] >= '0' && lcopy[offset] <= '1') ||
3769 				    lcopy[offset] == '\''))
3770 					continue;
3771 			}
3772 
3773 			/* prevent it from getting tested for swear words */
3774 			for (j = 0; j < strlen(nonswear[i]); j++)
3775 				lcopy2[(word - lcopy) + j] = 'Z';
3776 		}
3777 	}
3778 
3779 	/* perform 2x2 more 'parallel' tests  */
3780 	strcpy(tmp1, line);
3781 	strcpy(tmp2, line);
3782 	strcpy(tmp3, line);
3783 	strcpy(tmp4, line);
3784 	strcpy(lcopy, lcopy2);
3785 	i = censor_aux(tmp1, lcopy, cc, FALSE, FALSE); /* continue with normal check */
3786 	strcpy(lcopy, lcopy2);
3787 #ifdef CENSOR_LEET
3788 	j = censor_aux(tmp2, lcopy, cc, TRUE, FALSE); /* continue with leet speek check */
3789 	if (j_pre > j) j = j_pre; //pre-calculated special case
3790 	strcpy(lcopy, lcopy2);
3791 #else
3792 	j = 0;
3793 #endif
3794 	im = censor_aux(tmp3, lcopy, cc, FALSE, TRUE); /* continue with normal check */
3795 	strcpy(lcopy, lcopy2);
3796 #ifdef CENSOR_LEET
3797 	jm = censor_aux(tmp4, lcopy, cc, TRUE, TRUE); /* continue with leet speek check */
3798 #else
3799 	jm = 0;
3800 #endif
3801 
3802 	/* combine all censored characters */
3803 	for (offset = 0; tmp1[offset]; offset++)
3804 		if (tmp1[offset] == '*') line[offset] = '*';
3805 #ifdef CENSOR_LEET
3806 	for (offset = 0; tmp2[offset]; offset++)
3807 		if (tmp2[offset] == '*') line[offset] = '*';
3808 #endif
3809 	for (offset = 0; tmp3[offset]; offset++)
3810 		if (tmp3[offset] == '*') line[offset] = '*';
3811 #ifdef CENSOR_LEET
3812 	for (offset = 0; tmp4[offset]; offset++)
3813 		if (tmp4[offset] == '*') line[offset] = '*';
3814 #endif
3815 
3816 	/* return 'worst' result */
3817 	if (j > i) i = j;
3818 	if (im > i) i = im;
3819 	if (jm > i) i = jm;
3820 	return i;
3821 }
3822 #endif
3823 
3824 /*
3825  * A message prefixed by a player name is sent only to that player.
3826  * Otherwise, it is sent to everyone.
3827  */
3828 /*
3829  * This function is hacked *ALOT* to add extra-commands w/o
3830  * client change.		- Jir -
3831  *
3832  * Yeah. totally.
3833  *
3834  * Muted players can't talk now (player_type.muted-- can be enabled
3835  * through /mute <name> and disabled through /unmute <name>).
3836  *				- the_sandman
3837  */
3838 static void player_talk_aux(int Ind, char *message) {
3839 	int i, len, target = 0, target_raw_len = 0;
3840 	char search[MSG_LEN], sender[MAX_CHARS];
3841 	char message2[MSG_LEN];
3842 	player_type *p_ptr = NULL, *q_ptr;
3843 	char *colon;
3844 	bool me = FALSE, log = TRUE, nocolon = FALSE;
3845 	char c_n = 'B'; /* colours of sender name and of brackets (unused atm) around this name */
3846 #ifdef KURZEL_PK
3847 	char c_b = 'B';
3848 #endif
3849 	int mycolor = 0;
3850 	bool admin = FALSE;
3851 	bool broadcast = FALSE;
3852 
3853 #ifdef TOMENET_WORLDS
3854 	char tmessage[MSG_LEN];		/* TEMPORARY! We will not send the name soon */
3855 #endif
3856 
3857 	if (!Ind) {
3858 		console_talk_aux(message);
3859 		return;
3860 	}
3861 
3862 	/* Get sender's name */
3863 	p_ptr = Players[Ind];
3864 	/* Get player name */
3865 	strcpy(sender, p_ptr->name);
3866 	admin = is_admin(p_ptr);
3867 
3868 	/* Default to no search string */
3869 	strcpy(search, "");
3870 
3871 	/* this ain't a proper colon (must have a receipient or chat code on the left side!) */
3872 	if (message[0] == ':') nocolon = TRUE;
3873 
3874 
3875 	/* Look for a player's name followed by a colon */
3876 
3877 	/* tBot's stuff */
3878 	/* moved here to allow tbot to see fake pvt messages. -Molt */
3879 	strncpy(last_chat_owner, sender, NAME_LEN);
3880 	strncpy(last_chat_line, message, MSG_LEN);
3881 	/* do exec_lua() not here instead of in dungeon.c - mikaelh */
3882 
3883 	/* '-:' at beginning of message sends to normal chat, cancelling special chat modes - C. Blue */
3884 	if (strlen(message) >= 4 && message[1] == ':' && message[2] == '-' && message[3] == ':')
3885 		message += 4;
3886 	/* Catch this case too anyway, just for comfort if someone uses fixed macros for -: */
3887 	else if (strlen(message) >= 2 && message[0] == '-' && message[1] == ':')
3888 		message += 2;
3889 
3890 
3891 	/* hack: preparse for private chat target, and if found
3892 	   then strip chat mode prefixes to switch to private chat mode - C. Blue */
3893 	/* Form a search string if we found a colon */
3894 	if (strlen(message) > 4 && message[1] == ':' && message[2] != ':' &&
3895 	    (message[0] == '!' || message[0] == '#' || message[0] == '$')
3896 	    && (colon = strchr(message + 3, ':'))) {
3897 		/* Copy everything up to the colon to the search string */
3898 		strncpy(search, message + 2, colon - message - 2);
3899 		search[colon - message - 2] = '\0';
3900 		/* Acquire length of search string */
3901 		len = strlen(search);
3902 		/* Look for a recipient who matches the search string */
3903 		if (len) {
3904 			struct rplist *w_player;
3905 			/* NAME_LOOKUP_LOOSE DESPERATELY NEEDS WORK */
3906 			target = name_lookup_loose_quiet(Ind, search, TRUE, TRUE);
3907 
3908 			if (target) {
3909 				/* strip leading chat mode prefix,
3910 				   pretend that it's a private message instead */
3911 				message += 2;
3912 			}
3913 #ifdef TOMENET_WORLDS
3914 			else if (cfg.worldd_privchat) {
3915 				w_player = world_find_player(search, 0);
3916 				if (w_player) {
3917 					/* strip leading chat mode prefix,
3918 					   pretend that it's a private message instead */
3919 					message += 2;
3920 				}
3921 			}
3922 #endif
3923 		}
3924 
3925 		/* erase our traces */
3926 		search[0] = '\0';
3927 		len = 0;
3928         }
3929 
3930 
3931 	colon = strchr(message, ':');
3932 	/* only assume targetted chat if the 'name' would acually be of acceptable length */
3933 	if (colon && (colon - message) > NAME_LEN) colon = NULL;
3934 
3935 	/* Ignore "smileys" or URL */
3936 //	if (colon && strchr(")(-/:", colon[1]))
3937 	/* (C. Blue) changing colon parsing. :: becomes
3938 	    textual :  - otherwise : stays control char */
3939 	if (colon) {
3940 		bool smiley = FALSE;
3941 		/* no double-colon '::' smileys inside possible item inscriptions */
3942 		char *bracer = strchr(message, '{');
3943 		bool maybe_inside_inscription = bracer != NULL && bracer < colon;
3944 
3945 		target_raw_len = colon - message;
3946 
3947 		/* if another colon followed this one then
3948 		   it was not meant to be a control char */
3949 		switch (colon[1]) {
3950 		/* accept these chars for smileys, but only if the colon is either first in the line or stands after a SPACE,
3951 		   otherwise it is to expect that someone tried to write to party/privmsg */
3952 		case '(':	case ')':
3953 		case '[':	case ']':
3954 		case '{':	case '}':
3955 /* staircases, so cannot be used for smileys here ->		case '<':	case '>': */
3956 		case '\\':	case '|':
3957 		case 'p': case 'P': case 'o': case 'O':
3958 		case 'D': case '3':
3959 			if (message == colon || colon[-1] == ' ' || colon[-1] == '>' || /* >:) -> evil smiley */
3960 			    ((message == colon - 1) && (colon[-1] != '!') && (colon[-1] != '#') && (colon[-1] != '%') && (colon[-1] != '$') && (colon[-1] != '+'))) /* <- party names must be at least 2 chars then */
3961 				colon = NULL; /* the check is mostly important for '(' */
3962 			break;
3963 		case '-':
3964 			if (message == colon || colon[-1] == ' ' || colon[-1] == '>' || /* here especially important: '-' often is for numbers/recall depth */
3965 			    ((message == colon - 1) && (colon[-1] != '!') && (colon[-1] != '#') && (colon[-1] != '%') && (colon[-1] != '$') && (colon[-1] != '+'))) /* <- party names must be at least 2 chars then */
3966 				if (!strchr("123456789", *(colon + 2))) colon = NULL;
3967 			break;
3968 		case '/':
3969 			/* only accept / at the end of a chat message */
3970 			if ('\0' == *(colon + 2)) colon = NULL;
3971 			/* or it might be a http:// style link */
3972 			else if ('/' == *(colon + 2)) colon = NULL;
3973 			break;
3974 		case ':':
3975 			/* remove the 1st colon found, .. */
3976 
3977 			/* always if there's no chat-target in front of the double-colon: */
3978 			if (message == colon || colon[-1] == ' ') {
3979 				i = (int) (colon - message);
3980 				do message[i] = message[i+1];
3981 				while (message[i++] != '\0');
3982 				colon = NULL;
3983 				break;
3984 			}
3985 
3986 			/* new hack: ..but only if the previous two chars aren't  !:  (party chat),
3987 			   and if it's appearently meant to be a smiley. */
3988 			if ((colon - message == 1) && (colon[-1] == '!' || colon[-1] == '#'
3989 			    || colon[-1] == '%' || colon[-1] == '$' || colon[-1] == '+'))
3990 			switch (*(colon + 2)) {
3991 			case '(': case ')':
3992 			case '[': case ']':
3993 			case '{': case '}':
3994 			case '<': case '>':
3995 			case '-': case '|':
3996 			case 'p': case 'P': case 'o': case 'O':
3997 			case 'D': case '3':
3998 			smiley = !maybe_inside_inscription; break; }
3999 
4000 			/* check for smiley at end of the line */
4001 			if ((message + strlen(message) - colon >= 3) &&
4002 			    (message + strlen(message) - colon <= 4)) // == 3 for 2-letter-smileys only.
4003 //			if ((*(colon + 2)) && !(*(colon + 3)))
4004 			switch (*(colon + 2)) {
4005 			case '(': case ')':
4006 			case '[': case ']':
4007 			case '{': case '}':
4008 			case '<': case '>':
4009 			case '-': case '/':
4010 			case '\\': case '|':
4011 			case 'p': case 'P': case 'o': case 'O':
4012 			case 'D': case '3':
4013 			smiley = !maybe_inside_inscription; break; }
4014 
4015 			if (smiley) break;
4016 
4017 			/* Pretend colon wasn't there */
4018 			i = (int) (colon - message);
4019 			do message[i] = message[i+1];
4020 			while (message[i++] != '\0');
4021 			colon = NULL;
4022 			break;
4023 		case '\0':
4024 			/* if colon is last char in the line, it's not a priv/party msg. */
4025 #if 0 /* disabled this 'feature', might be more convenient - C. Blue */
4026 			colon = NULL;
4027 #else
4028 			if (colon[-1] != '!' && colon[-1] != '#' && colon[-1] != '%' && colon[-1] != '$' && colon[-1] != '+')
4029 				colon = NULL;
4030 #endif
4031 			break;
4032 		}
4033 	}
4034 
4035 	/* don't log spammy slash commands */
4036 	if (prefix(message, "/untag ")
4037 	    || prefix(message, "/dis"))
4038 		log = FALSE;
4039 
4040 	/* no big brother */
4041 //	if(cfg.log_u && (!colon || message[0] != '#' || message[0] != '/')){ /* message[0] != '#' || message[0] != '/' is always true -> big brother mode - mikaelh */
4042 	if (cfg.log_u && log &&
4043 	    (!colon || nocolon || (message[0] == '/' && strncmp(message, "/note", 5)))) {
4044 		/* Shorten multiple identical messages to prevent log file spam,
4045 		   eg recall rod activation attempts. - Exclude admins. */
4046 		if (admin)
4047 			s_printf("[%s] %s\n", sender, message);
4048 		else if (strcmp(message, p_ptr->last_chat_line)) {
4049 			if (p_ptr->last_chat_line_cnt) {
4050 				s_printf("[%s (x%d)]\n", sender, p_ptr->last_chat_line_cnt + 1);
4051 				p_ptr->last_chat_line_cnt = 0;
4052 			}
4053 			s_printf("[%s] %s\n", sender, message);
4054 
4055 			/* Keep track of repeating chat lines to avoid log file spam (slash commands like '/rec' mostly) */
4056 			strncpy(p_ptr->last_chat_line, message, MSG_LEN - 1);
4057 			p_ptr->last_chat_line[MSG_LEN - 1] = 0;
4058 		} else p_ptr->last_chat_line_cnt++;
4059 	}
4060 
4061 	/* Special - shutdown command (for compatibility) */
4062 	if (prefix(message, "@!shutdown") && admin) {
4063 		/*world_reboot();*/
4064 		shutdown_server();
4065 		return;
4066 	}
4067 
4068 	if (message[0] == '/' ){
4069 		if (!strncmp(message, "/me ", 4)) me = TRUE;
4070 		else if (!strncmp(message, "/broadcast ", 11)) broadcast = TRUE;
4071 		else {
4072 			do_slash_cmd(Ind, message);	/* add check */
4073 			return;
4074 		}
4075 	}
4076 
4077 #ifndef ARCADE_SERVER
4078 	if (!colon) p_ptr->msgcnt++; /* !colon -> only prevent spam if not in party/private chat */
4079 	if (p_ptr->msgcnt > 12) {
4080 		time_t last = p_ptr->msg;
4081 		time(&p_ptr->msg);
4082 		if (p_ptr->msg - last < 6) {
4083 			p_ptr->spam++;
4084 			switch (p_ptr->spam) {
4085 			case 1:
4086 				msg_print(Ind, "\377yPlease don't spam the server");
4087 				break;
4088 			case 3:
4089 			case 4:
4090 				msg_print(Ind, "\374\377rWarning! this behaviour is unacceptable!");
4091 				break;
4092 			case 5:
4093 				p_ptr->chp = -3;
4094 				strcpy(p_ptr->died_from, "hypoxia");
4095 				p_ptr->spam = 1;
4096 				p_ptr->deathblow = 0;
4097 				player_death(Ind);
4098 				return;
4099 			}
4100 		}
4101 		if (p_ptr->msg - last > 240 && p_ptr->spam) p_ptr->spam--;
4102 		p_ptr->msgcnt = 0;
4103 	}
4104 	if (p_ptr->spam > 1 || p_ptr->muted) return;
4105 #endif
4106 	process_hooks(HOOK_CHAT, "d", Ind);
4107 
4108 	if (++p_ptr->talk > 10) {
4109 		imprison(Ind, 30, "talking too much.");
4110 		return;
4111 	}
4112 
4113 	for (i = 1; i <= NumPlayers; i++) {
4114 		if (Players[i]->conn == NOT_CONNECTED) continue;
4115 		Players[i]->talk = 0;
4116 	}
4117 
4118 
4119 
4120 	/* Special function '!:' at beginning of message sends to own party - sorry for hack, C. Blue */
4121 //	if ((strlen(message) >= 1) && (message[0] == ':') && (!colon) && (p_ptr->party))
4122 	if ((strlen(message) >= 2) && (message[0] == '!') && (message[1] == ':') && (colon) && (p_ptr->party)) {
4123 		target = p_ptr->party;
4124 
4125 #if 1 /* No private chat for invalid accounts ? */
4126 		if (p_ptr->inval) {
4127 			msg_print(Ind, "\377yYour account is not valid, wait for an admin to validate it.");
4128 			return;
4129 		}
4130 #endif
4131 
4132 		/* Send message to target party */
4133 		if (p_ptr->mutedchat < 2) {
4134 #ifdef GROUP_CHAT_NOCLUTTER
4135 			/* prevent buffer overflow */
4136 			message[MSG_LEN - 1 - 10 - strlen(sender)] = 0;
4137 			party_msg_format_ignoring(Ind, target, "\375\377%c[(P) %s] %s", COLOUR_CHAT_PARTY, sender, message + 2);
4138 #else
4139 			/* prevent buffer overflow */
4140 			message[MSG_LEN - 1 - 7 - strlen(sender) - strlen(parties[target].name)] = 0;
4141 			party_msg_format_ignoring(Ind, target, "\375\377%c[%s:%s] %s", COLOUR_CHAT_PARTY, parties[target].name, sender, message + 2);
4142 #endif
4143 //			party_msg_format_ignoring(Ind, target, "\375\377%c[%s:%s] %s", COLOUR_CHAT_PARTY, parties[target].name, sender, message + 1);
4144 		}
4145 
4146 		/* Done */
4147 		return;
4148 	}
4149 
4150 	/* '#:' at beginning of message sends to dungeon level - C. Blue */
4151 	if ((strlen(message) >= 2) && (message[0] == '#') && (message[1] == ':') && (colon)) {
4152 #if 1 /* No private chat for invalid accounts ? */
4153 		if (p_ptr->inval) {
4154 			msg_print(Ind, "\377yYour account is not valid, wait for an admin to validate it.");
4155 			return;
4156 		}
4157 #endif
4158 
4159 		if (!p_ptr->wpos.wz) {
4160 #if 0
4161 			msg_print(Ind, "You aren't in a dungeon or tower.");
4162 #else /* Darkie's idea: */
4163 			if (*(message + 2)) {
4164 				censor_message = TRUE;
4165 				censor_length = strlen(message + 2);
4166 
4167 				/* prevent buffer overflow */
4168 				message[MSG_LEN - 1 + 2 - strlen(p_ptr->name) - 9] = 0;
4169 				msg_format_near(Ind, "\377%c%^s says: %s", COLOUR_CHAT, p_ptr->name, message + 2);
4170 				msg_format(Ind, "\377%cYou say: %s", COLOUR_CHAT, message + 2);
4171 
4172 				censor_message = FALSE;
4173 				handle_punish(Ind, censor_punish);
4174 			} else {
4175 				msg_format_near(Ind, "\377%c%s clears %s throat.", COLOUR_CHAT, p_ptr->name, p_ptr->male ? "his" : "her");
4176 				msg_format(Ind, "\377%cYou clear your throat.", COLOUR_CHAT);
4177 			}
4178 #endif
4179 			return;
4180 		}
4181 
4182 		/* Send message to target floor */
4183 		if (p_ptr->mutedchat < 2) {
4184 			censor_message = TRUE;
4185 			censor_length = strlen(message + 2);
4186 
4187 			/* prevent buffer overflow */
4188 			message[MSG_LEN - 1 + 2 - strlen(sender) - 6] = 0;
4189 			floor_msg_format_ignoring(Ind, &p_ptr->wpos, "\375\377%c[%s] %s", COLOUR_CHAT_LEVEL, sender, message + 2);
4190 
4191 			censor_message = FALSE;
4192 			handle_punish(Ind, censor_punish);
4193 		}
4194 
4195 		/* Done */
4196 		return;
4197 	}
4198 
4199 	/* '%:' at the beginning sends to self - mikaelh */
4200 	if ((strlen(message) >= 2) && (message[0] == '%') && (message[1] == ':') && (colon)) {
4201 		/* prevent buffer overflow */
4202 		message[MSG_LEN - 1 + 2 - 8] = 0;
4203 		/* Send message to self */
4204 		msg_format(Ind, "\377o<%%>\377w %s", message + 2);
4205 
4206 		/* Done */
4207 		return;
4208 	}
4209 
4210 	/* '+:' at beginning of message sends reply to last player who sent us a private msg  - C. Blue */
4211 	if ((strlen(message) >= 2) && (message[0] == '+') && (message[1] == ':') && colon) {
4212 #if 1 /* No private chat for invalid accounts ? */
4213 		if (p_ptr->inval) {
4214 			msg_print(Ind, "\377yYour account is not valid, wait for an admin to validate it.");
4215 			return;
4216 		}
4217 #endif
4218 
4219 		/* Haven't received an initial private msg yet? */
4220 		if (!strlen(p_ptr->reply_name)) {
4221 			msg_print(Ind, "You haven't received any private message to reply to yet.");
4222 			return;
4223 		}
4224 
4225 		if (p_ptr->mutedchat == 2) return;
4226 
4227 		/* Forge private message */
4228 		strcpy(message2, p_ptr->reply_name);
4229 		strcat(message2, message + 1);
4230 		strcpy(message, message2);
4231 		colon = message + strlen(p_ptr->reply_name);
4232 
4233 		/* Continue with forged private message */
4234 	}
4235 
4236 	/* '$:' at beginning of message sends to guild - C. Blue */
4237 	if ((strlen(message) >= 2) && (message[0] == '$') && (message[1] == ':') && (colon) && (p_ptr->guild)) {
4238 #if 1 /* No private chat for invalid accounts ? */
4239 		if (p_ptr->inval) {
4240 			msg_print(Ind, "\377yYour account is not valid, wait for an admin to validate it.");
4241 			return;
4242 		}
4243 #endif
4244 
4245 		/* Send message to guild party */
4246 		if (p_ptr->mutedchat < 2) {
4247 #ifdef GROUP_CHAT_NOCLUTTER
4248 			/* prevent buffer overflow */
4249 			message[MSG_LEN - 1 + 2 - strlen(sender) - 16] = 0;
4250 			guild_msg_format(p_ptr->guild, "\375\377y[\377%c(G) %s\377y]\377%c %s", COLOUR_CHAT_GUILD, sender, COLOUR_CHAT_GUILD, message + 2);
4251 #else
4252 			/* prevent buffer overflow */
4253 			message[MSG_LEN - 1 + 2 - strlen(sender) - strlen(guilds[p_ptr->guild].name) - 18] = 0;
4254 			guild_msg_format(p_ptr->guild, "\375\377y[\377%c%s\377y:\377%c%s\377y]\377%c %s", COLOUR_CHAT_GUILD, guilds[p_ptr->guild].name, COLOUR_CHAT_GUILD, sender, COLOUR_CHAT_GUILD, message + 2);
4255 #endif
4256 		}
4257 
4258 		/* Done */
4259 		return;
4260 	}
4261 
4262 
4263 	/* Form a search string if we found a colon */
4264 	if (colon) {
4265 		if (p_ptr->mutedchat == 2) return;
4266 #if 1 /* No private chat for invalid accounts ? */
4267 		if (p_ptr->inval) {
4268 			msg_print(Ind, "\377yYour account is not valid, wait for an admin to validate it.");
4269 			return;
4270 		}
4271 #endif
4272 		/* Copy everything up to the colon to the search string */
4273 		strncpy(search, message, colon - message);
4274 
4275 		/* Add a trailing NULL */
4276 		search[colon - message] = '\0';
4277 	} else if (p_ptr->mutedchat) return;
4278 
4279 	/* Acquire length of search string */
4280 	len = strlen(search);
4281 
4282 	/* Look for a recipient who matches the search string */
4283 	if (len) {
4284 		struct rplist *w_player;
4285 
4286 		/* NAME_LOOKUP_LOOSE DESPERATELY NEEDS WORK */
4287 		target = name_lookup_loose_quiet(Ind, search, TRUE, TRUE);
4288 #ifdef TOMENET_WORLDS
4289 		if (!target && cfg.worldd_privchat) {
4290 			w_player = world_find_player(search, 0);
4291 			if (w_player) {
4292 				/* prevent buffer overflow */
4293 				message[MSG_LEN - strlen(p_ptr->name) - 7 - 8 - strlen(w_player->name) + target_raw_len] = 0;//8 are world server tax
4294 				world_pmsg_send(p_ptr->id, p_ptr->name, w_player->name, colon + 1);
4295 				msg_format(Ind, "\375\377s[%s:%s] %s", p_ptr->name, w_player->name, colon + 1);
4296 
4297 				/* hack: assume that the target player will become the
4298 				   one we want to 'reply' to, afterwards, if we don't
4299 				   have a reply-to target yet. */
4300 				if (!strlen(p_ptr->reply_name))
4301 					strcpy(p_ptr->reply_name, w_player->name);
4302 
4303 				return;
4304 			}
4305 		}
4306 #endif
4307 
4308 		/* Move colon pointer forward to next word */
4309 /* no, this isn't needed and actually has annoying effects if you try to write smileys:
4310 		while (*colon && (isspace(*colon) || *colon == ':')) colon++; */
4311 /* instead, this is sufficient: */
4312 		if (colon) colon++;
4313 
4314 		/* lookup failed */
4315 		if (!target) {
4316 			/* Bounce message to player who sent it */
4317 //			msg_format(Ind, "(no receipient for: %s)", colon);
4318 #ifndef ARCADE_SERVER
4319 			msg_print(Ind, "(Cannot match desired recipient)");
4320 #endif
4321 			/* Allow fake private msgs to be intercepted - Molt */
4322 			exec_lua(0, "chat_handler()");
4323 
4324 			/* Give up */
4325 			return;
4326 		}
4327 	}
4328 
4329 	/* Send to appropriate player */
4330 	if (len && target > 0) {
4331 		if (!check_ignore(target, Ind)) {
4332 			/* Set target player */
4333 			q_ptr = Players[target];
4334 			/* Remember sender, to be able to reply to him quickly */
4335 #if 0
4336 			strcpy(q_ptr->reply_name, sender);
4337 #else /* use his account name instead, since it's possible now */
4338 			if (!strcmp(sender, p_ptr->name)) strcpy(q_ptr->reply_name, p_ptr->accountname);
4339 			else strcpy(q_ptr->reply_name, sender);
4340 #endif
4341 
4342 			/* prevent buffer overflow */
4343 			message[MSG_LEN - strlen(sender) - 7 - strlen(q_ptr->name) + target_raw_len] = 0;
4344 
4345 			/* Send message to target */
4346 			msg_format(target, "\375\377g[%s:%s] %s", sender, q_ptr->name, colon);
4347 			if ((q_ptr->page_on_privmsg ||
4348 			    (q_ptr->page_on_afk_privmsg && q_ptr->afk)) &&
4349 			    q_ptr->paging == 0)
4350 				q_ptr->paging = 1;
4351 
4352 			/* Also send back to sender */
4353 			if (target != Ind)
4354 				msg_format(Ind, "\375\377g[%s:%s] %s", sender, q_ptr->name, colon);
4355 
4356 			/* Only display this message once now - mikaelh */
4357 			if (q_ptr->afk && !player_list_find(p_ptr->afk_noticed, q_ptr->id)) {
4358 				msg_format(Ind, "\377o%s seems to be Away From Keyboard.", q_ptr->name);
4359 				player_list_add(&p_ptr->afk_noticed, q_ptr->id);
4360 			}
4361 
4362 #if 0
4363 			/* hack: assume that the target player will become the
4364 			   one we want to 'reply' to, afterwards, if we don't
4365 			   have a reply-to target yet. */
4366 			if (!p_ptr->reply_name || !strlen(p_ptr->reply_name))
4367 				strcpy(p_ptr->reply_name, q_ptr->name);
4368 #else
4369 			/* hack: assume that the target player will become the
4370 			   one we want to 'reply' to, afterwards.
4371 			   This might get somewhat messy if we're privchatting
4372 			   to two players at the same time, but so would
4373 			   probably the other variant above. That one stays
4374 			   true to the '+:' definition given in the guide though,
4375 			   while this one diverges a bit. */
4376  #if 0
4377 			strcpy(p_ptr->reply_name, q_ptr->name);
4378  #else /* use his account name instead, since it's possible now */
4379 			strcpy(p_ptr->reply_name, q_ptr->accountname);
4380  #endif
4381 #endif
4382 
4383 			exec_lua(0, "chat_handler()");
4384 			return;
4385 		} else {
4386 			/* Tell the sender */
4387 			msg_print(Ind, "(That player has ignored you)");
4388 			return;
4389 		}
4390 	}
4391 
4392 	/* Send to appropriate party */
4393 	if (len && target < 0) {
4394 		/* Can't send msg to party from 'outside' as non-admin */
4395 		if (p_ptr->party != 0 - target && !admin) {
4396 			msg_print(Ind, "You aren't in that party.");
4397 			return;
4398 		}
4399 
4400 #ifdef GROUP_CHAT_NOCLUTTER
4401 		/* prevent buffer overflow */
4402 		message[MSG_LEN - strlen(sender) - 10] = 0;
4403 		/* Send message to target party */
4404 		party_msg_format_ignoring(Ind, 0 - target, "\375\377%c[(P) %s] %s", COLOUR_CHAT_PARTY, sender, colon);
4405 #else
4406 		/* prevent buffer overflow */
4407 		message[MSG_LEN - 7 - strlen(sender) - strlen(parties[0 - target].name) + target_raw_len] = 0;
4408 		party_msg_format_ignoring(Ind, 0 - target, "\375\377%c[%s:%s] %s", COLOUR_CHAT_PARTY, parties[target].name, sender, colon);
4409 #endif
4410 
4411 		//party_msg_format_ignoring(Ind, 0 - target, "\375\377%c[%s:%s] %s", COLOUR_CHAT_PARTY, parties[0 - target].name, sender, colon);
4412 
4413 		/* Also send back to sender if not in that party */
4414 		if (!player_in_party(0 - target, Ind)) {
4415 			msg_format(Ind, "\375\377%c[%s:%s] %s",
4416 			    COLOUR_CHAT_PARTY, parties[0 - target].name, sender, colon);
4417 		}
4418 
4419 		exec_lua(0, "chat_handler()");
4420 
4421 		/* Done */
4422 		return;
4423 	}
4424 
4425 	if (strlen(message) > 1) mycolor = (prefix(message, "}") && (color_char_to_attr(*(message + 1)) != -1)) ? 2 : 0;
4426 
4427 	if (!Ind) c_n = 'y';
4428 	/* Disabled this for now to avoid confusion. */
4429 	else if (mycolor) c_n = *(message + 1);
4430 	else {
4431 		/* Dungeon Master / Dungeon Wizard have their own colour now :) */
4432 		if (is_admin(p_ptr)) c_n = 'b';
4433 		else if (p_ptr->total_winner) c_n = 'v';
4434 		else if (p_ptr->ghost) c_n = 'r';
4435 		else if (p_ptr->mode & MODE_EVERLASTING) c_n = 'B';
4436 		else if (p_ptr->mode & MODE_PVP) c_n = COLOUR_MODE_PVP;
4437 		else if (p_ptr->mode & MODE_NO_GHOST) c_n = 'D';
4438 		else if (p_ptr->mode & MODE_HARD) c_n = 's';
4439 		else c_n = 'W'; /* normal mode */
4440 
4441 		/* Color the brackets of some players... (Enabled for PK) */
4442 #ifdef KURZEL_PK
4443 		if ((p_ptr->mode & MODE_HARD) && (p_ptr->mode & MODE_NO_GHOST))
4444 			c_b = 'r'; /* hellish mode */
4445 		else if (p_ptr->pkill & PKILL_SET) c_b = 'R'; //KURZEL_PK
4446 		else c_b = c_n;
4447 #endif
4448 	}
4449 
4450 	/* Admins have exclusive colour - the_sandman */
4451 	if (c_n == 'b' && !admin) return;
4452 #ifdef KURZEL_PK
4453 	if (admin) c_b = 'b';
4454 #endif
4455 
4456 
4457 	censor_message = TRUE;
4458 
4459 #ifdef TOMENET_WORLDS
4460 	if (broadcast) {
4461 		/* prevent buffer overflow */
4462 		message[MSG_LEN - 1 - strlen(sender) - 12 + 11] = 0;
4463 
4464 		snprintf(tmessage, sizeof(tmessage), "\375\377r[\377%c%s\377r]\377%c %s", c_n, sender, COLOUR_CHAT, message + 11);
4465 		censor_length = strlen(message + 11);
4466 	} else if (!me) {
4467 		/* prevent buffer overflow */
4468 		message[MSG_LEN - 1 - strlen(sender) - 8 + mycolor] = 0;
4469 
4470 #ifndef KURZEL_PK
4471 		snprintf(tmessage, sizeof(tmessage), "\375\377%c[%s]\377%c %s", c_n, sender, COLOUR_CHAT, message + mycolor);
4472 #else
4473 		snprintf(tmessage, sizeof(tmessage), "\375\377%c[\377%c%s\377%c]\377%c %s", c_b, c_n, sender, c_b, COLOUR_CHAT, message + mycolor);
4474 #endif
4475 		censor_length = strlen(message + mycolor);
4476 	} else {
4477 		/* Why not... */
4478 		if (strlen(message) > 4) mycolor = (prefix(&message[4], "}") && (color_char_to_attr(*(message + 5)) != -1)) ? 2 : 0;
4479 		else {
4480 			censor_message = FALSE;
4481 			return;
4482 		}
4483 		if (mycolor) c_n = message[5];
4484 
4485 		/* prevent buffer overflow */
4486 		message[MSG_LEN - 1 - strlen(sender) - 10 + 4 + mycolor] = 0;
4487 #ifndef KURZEL_PK
4488 		snprintf(tmessage, sizeof(tmessage), "\375\377%c[%s %s]", c_n, sender, message + 4 + mycolor);
4489 #else
4490 		snprintf(tmessage, sizeof(tmessage), "\375\377%c[\377%c%s %s\377%c]", c_b, c_n, sender, message + 4 + mycolor, c_b);
4491 #endif
4492 		censor_length = strlen(message + 4 + mycolor) + 1;
4493 	}
4494 
4495 #if 0
4496  #if 0
4497 	if ((broadcast && cfg.worldd_broadcast) || (!broadcast && cfg.worldd_pubchat))
4498 	    && !(len && (target != 0) && !cfg.worldd_privchat)) /* privchat = to party or to person */
4499 		world_chat(p_ptr->id, tmessage);	/* no ignores... */
4500  #else
4501 	/* Incoming chat is now filtered instead of outgoing which allows IRC relay to get public chat messages from worldd - mikaelh */
4502 	world_chat(p_ptr->id, tmessage);	/* no ignores... */
4503  #endif
4504 #else /* Remove current redundancy. Make worldd_pubchat decide if we broadcast all our chat out there or not. */
4505 	/* in case privchat wasn't handled above (because it's disabled),
4506 	   exempt it here so we only process real chat/broadcasts */
4507 	if (!(!cfg.worldd_privchat && len && target != 0)) {
4508 		if (broadcast && cfg.worldd_broadcast) {
4509 			world_chat(0, tmessage); /* can't ignore */
4510 		} else if (!broadcast && cfg.worldd_pubchat) {
4511 			world_chat(p_ptr->id, tmessage);
4512 		}
4513 	}
4514 #endif
4515 
4516 	for(i = 1; i <= NumPlayers; i++) {
4517 		q_ptr = Players[i];
4518 
4519 		if (!admin) {
4520 			if (check_ignore(i, Ind)) continue;
4521 			if (q_ptr->ignoring_chat) continue;
4522 			if (!broadcast && (p_ptr->limit_chat || q_ptr->limit_chat) &&
4523 			    !inarea(&p_ptr->wpos, &q_ptr->wpos)) continue;
4524 		}
4525 		msg_print(i, tmessage);
4526 	}
4527 #else
4528 	/* Send to everyone */
4529 	for (i = 1; i <= NumPlayers; i++) {
4530 		q_ptr = Players[i];
4531 
4532 		if (!admin) {
4533 			if (check_ignore(i, Ind)) continue;
4534 			if (q_ptr->ignoring_chat) continue;
4535 			if (!broadcast && (p_ptr->limit_chat || q_ptr->limit_chat) &&
4536 			    !inarea(&p_ptr->wpos, &q_ptr->wpos)) continue;
4537 		}
4538 
4539 		/* Send message */
4540 		if (broadcast) {
4541 			/* prevent buffer overflow */
4542 			message[MSG_LEN - 1 - strlen(sender) - 12 + 11] = 0;
4543 
4544 			censor_length = strlen(message + 11);
4545 			msg_format(i, "\375\377r[\377%c%s\377r]\377%c %s", c_n, sender, COLOUR_CHAT, message + 11);
4546 		} else if (!me) {
4547 			/* prevent buffer overflow */
4548 			message[MSG_LEN - 1 - strlen(sender) - 12 + mycolor] = 0;
4549 
4550 			censor_length = strlen(message + mycolor);
4551 #ifndef KURZEL_PK
4552 			msg_format(i, "\375\377%c[%s]\377%c %s", c_n, sender, COLOUR_CHAT, message + mycolor);
4553 #else
4554 			msg_format(i, "\375\377%c[\377%c%s\377%c]\377%c %s", c_b, c_n, sender, c_b, COLOUR_CHAT, message + mycolor);
4555 #endif
4556 			/* msg_format(i, "\375\377%c[%s] %s", Ind ? 'B' : 'y', sender, message); */
4557 		}
4558 		else {
4559 			/* prevent buffer overflow */
4560 			message[MSG_LEN - 1 - strlen(sender) - 1 + 4] = 0;
4561 
4562 			censor_length = strlen(message + 4);
4563 			msg_format(i, "%s %s", sender, message + 4);
4564 		}
4565 	}
4566 #endif
4567 
4568 	censor_message = FALSE;
4569 	p_ptr->warning_chat = 1;
4570 	handle_punish(Ind, censor_punish);
4571 
4572 	exec_lua(0, "chat_handler()");
4573 
4574 }
4575 /* Console talk is automatically sent by 'Server Admin' which is treated as an admin */
4576 static void console_talk_aux(char *message) {
4577  	int i;
4578 	cptr sender = "Server Admin";
4579 	bool me = FALSE, log = TRUE;
4580 	char c_n = 'y'; /* colours of sender name and of brackets (unused atm) around this name */
4581 #ifdef KURZEL_PK
4582 	char c_b = 'y';
4583 #endif
4584 	bool broadcast = FALSE;
4585 
4586 #ifdef TOMENET_WORLDS
4587 	char tmessage[MSG_LEN];		/* TEMPORARY! We will not send the name soon */
4588 #else
4589 	player_type *q_ptr;
4590 #endif
4591 
4592 	/* tBot's stuff */
4593 	/* moved here to allow tbot to see fake pvt messages. -Molt */
4594 	strncpy(last_chat_owner, sender, NAME_LEN);
4595 	strncpy(last_chat_line, message, MSG_LEN);
4596 
4597 	/* no big brother */
4598 	if (cfg.log_u && log) s_printf("[%s] %s\n", sender, message);
4599 
4600 	/* Special - shutdown command (for compatibility) */
4601 	if (prefix(message, "@!shutdown")) {
4602 		/*world_reboot();*/
4603 		shutdown_server();
4604 		return;
4605 	}
4606 
4607 	if (message[0] == '/' ) {
4608 		if (!strncmp(message, "/me ", 4)) me = TRUE;
4609 		else if (!strncmp(message, "/broadcast ", 11)) broadcast = TRUE;
4610 		else return;
4611 	}
4612 
4613 	for (i = 1; i <= NumPlayers; i++) {
4614 		if (Players[i]->conn == NOT_CONNECTED) continue;
4615 		Players[i]->talk = 0;
4616 	}
4617 
4618 	censor_message = TRUE;
4619 
4620 #ifdef TOMENET_WORLDS
4621 	if (broadcast) {
4622 		snprintf(tmessage, sizeof(tmessage), "\375\377r[\377%c%s\377r]\377%c %s", c_n, sender, COLOUR_CHAT, message + 11);
4623 		censor_length = strlen(message + 11);
4624 	} else if (!me) {
4625 #ifndef KURZEL_PK
4626 		snprintf(tmessage, sizeof(tmessage), "\375\377%c[%s]\377%c %s", c_n, sender, COLOUR_CHAT, message);
4627 #else
4628 		snprintf(tmessage, sizeof(tmessage), "\375\377%c[\377%c%s\377%c]\377%c %s", c_b, c_n, sender, c_b, COLOUR_CHAT, message);
4629 #endif
4630 		censor_length = strlen(message);
4631 	} else {
4632 #ifndef KURZEL_PK
4633 		snprintf(tmessage, sizeof(tmessage), "\375\377%c[%s %s]", c_n, sender, message + 4);
4634 #else
4635 		snprintf(tmessage, sizeof(tmessage), "\375\377%c[\377%c%s %s\377%c]", c_b, c_n, sender, message + 4, c_b);
4636 #endif
4637 		censor_length = strlen(message + 4);
4638 	}
4639 
4640 #else
4641 	/* Send to everyone */
4642 	for (i = 1; i <= NumPlayers; i++) {
4643 		q_ptr = Players[i];
4644 
4645 		/* Send message */
4646 		if (broadcast) {
4647 			censor_length = strlen(message + 11);
4648 			msg_format(i, "\375\377r[\377%c%s\377r]\377%c %s", c_n, sender, COLOUR_CHAT, message + 11);
4649 		} else if (!me) {
4650 			censor_length = strlen(message);
4651 #ifndef KURZEL_PK
4652 			msg_format(i, "\375\377%c[%s]\377%c %s", c_n, sender, COLOUR_CHAT, message);
4653 #else
4654 			msg_format(i, "\375\377%c[\377%c%s\377%c]\377%c %s", c_b, c_n, sender, c_b, COLOUR_CHAT, message);
4655 #endif
4656 		}
4657 		else {
4658 			censor_length = strlen(message + 4);
4659 			msg_format(i, "%s %s", sender, message + 4);
4660 		}
4661 	}
4662 #endif
4663 
4664 	censor_message = FALSE;
4665 
4666 	exec_lua(0, "chat_handler()");
4667 }
4668 
4669 /* Check for swear words, censor + punish */
4670 int handle_censor(char *message) {
4671 #ifdef CENSOR_SWEARING
4672 	if (censor_swearing) return censor(message);
4673 #endif
4674 	return 0;
4675 }
4676 void handle_punish(int Ind, int level) {
4677 	switch (level) {
4678 	case 0:
4679 		break;
4680 	case 1:
4681 		msg_print(Ind, "Please do not swear.");
4682 		break;
4683 	default:
4684 		imprison(Ind, level * 20, "swearing");
4685 	}
4686 }
4687 
4688 /* toggle AFK mode off if it's currently on, also reset idle time counter for in-game character.
4689    required for cloaking mode! - C. Blue */
4690 void un_afk_idle(int Ind) {
4691 	Players[Ind]->idle_char = 0;
4692 	if (Players[Ind]->afk && !(Players[Ind]->admin_dm && cfg.secret_dungeon_master)) toggle_afk(Ind, "");
4693 	stop_cloaking(Ind);
4694 }
4695 
4696 /*
4697  * toggle AFK mode on/off.	- Jir -
4698  */
4699 //#define ALLOW_AFK_COLOURCODES
4700 #ifdef ALLOW_AFK_COLOURCODES
4701 void toggle_afk(int Ind, char *msg0)
4702 #else
4703 void toggle_afk(int Ind, char *msg)
4704 #endif
4705 {
4706 	player_type *p_ptr = Players[Ind];
4707 	char afk[MAX_CHARS];
4708 	int i = 0;
4709 #ifdef ALLOW_AFK_COLOURCODES
4710 	char msg[MAX_CHARS], *m = msg0;
4711 
4712 	/* strip colour codes and cap message at len 60 */
4713 	while ((*m) && i < 60) {
4714 		if (*m == '\377') {
4715 			m++;
4716 			if (*m == '\377') {
4717 				msg[i++] = '{';
4718 			}
4719 		} else msg[i++] = *m;
4720 		m++;
4721 	}
4722 	msg[i] = 0;
4723 #endif
4724 
4725 	/* don't go un-AFK from auto-retaliation */
4726 	if (p_ptr->afk && p_ptr->auto_retaliaty) return;
4727 
4728 	/* don't go AFK while shooting-till-kill (target dummy, maybe ironwing ;)) */
4729 	if (!p_ptr->afk && p_ptr->shooting_till_kill) return;
4730 
4731 	strcpy(afk, "");
4732 
4733 	if (p_ptr->afk && !msg[0]) {
4734 		if (strlen(p_ptr->afk_msg) == 0)
4735 			msg_print(Ind, "AFK mode is turned \377GOFF\377w.");
4736 		else
4737 			msg_format(Ind, "AFK mode is turned \377GOFF\377w. (%s\377w)", p_ptr->afk_msg);
4738 		if (!p_ptr->admin_dm) {
4739 			if (strlen(p_ptr->afk_msg) == 0)
4740 				snprintf(afk, sizeof(afk), "\377%c%s has returned from AFK.", COLOUR_AFK, p_ptr->name);
4741 			else
4742 				snprintf(afk, sizeof(afk), "\377%c%s has returned from AFK. (%s\377%c)", COLOUR_AFK, p_ptr->name, p_ptr->afk_msg, COLOUR_AFK);
4743 		}
4744 		p_ptr->afk = FALSE;
4745 
4746 		/* Clear everyone's afk_noticed */
4747 		for (i = 1; i <= NumPlayers; i++)
4748 			player_list_del(&Players[i]->afk_noticed, p_ptr->id);
4749 	} else {
4750 		/* hack: lose health tracker so we actually get to see the 'AFK'
4751 		   (for example we might've attacked the target dummy before). */
4752 		health_track(Ind, 0);
4753 
4754 		/* stop every major action */
4755 		disturb(Ind, 1, 1); /* ,1) = keep resting! */
4756 
4757 		strncpy(p_ptr->afk_msg, msg, MAX_CHARS - 1);
4758 		p_ptr->afk_msg[MAX_CHARS - 1] = '\0';
4759 
4760 		if (strlen(p_ptr->afk_msg) == 0)
4761 			msg_print(Ind, "AFK mode is turned \377rON\377w.");
4762 		else
4763 			msg_format(Ind, "AFK mode is turned \377rON\377w. (%s\377w)", p_ptr->afk_msg);
4764 		if (!p_ptr->admin_dm) {
4765 			if (strlen(p_ptr->afk_msg) == 0)
4766 				snprintf(afk, sizeof(afk), "\377%c%s seems to be AFK now.", COLOUR_AFK, p_ptr->name);
4767 			else
4768 				snprintf(afk, sizeof(afk), "\377%c%s seems to be AFK now. (%s\377%c)", COLOUR_AFK, p_ptr->name, p_ptr->afk_msg, COLOUR_AFK);
4769 		}
4770 		p_ptr->afk = TRUE;
4771 
4772 		/* actually a hint for newbie rogues couldn't hurt */
4773 		if (p_ptr->tim_blacklist)
4774 			msg_print(Ind, "\376\377yNote: Your blacklist timer won't decrease while AFK.");
4775 
4776 		/* still too many starvations, so give a warning - C. Blue */
4777 		if (p_ptr->food < PY_FOOD_ALERT) {
4778 			p_ptr->paging = 6; /* add some beeps, too - mikaelh */
4779 			msg_print(Ind, "\374\377RWARNING: Going AFK while hungry or weak can result in starvation! Eat first!");
4780 		}
4781 
4782 		/* Since Mark's mimic died in front of Minas XBM due to this.. - C. Blue */
4783 		if (p_ptr->tim_wraith) {
4784 			p_ptr->paging = 6;
4785 			msg_print(Ind, "\374\377RWARNING: Going AFK in wraithform is very dangerous because wraithform impairs auto-retaliation!");
4786 		}
4787 	}
4788 
4789 	/* Replaced msg_broadcast by this, to allow /ignore and /ic */
4790 	/* Tell every player */
4791 	for (i = 1; i <= NumPlayers; i++) {
4792 		/* Skip disconnected players */
4793 		if (Players[i]->conn == NOT_CONNECTED) continue;
4794 		if (check_ignore(i, Ind)) continue;
4795 		if (Players[i]->no_afk_msg) continue;
4796 		if (Players[i]->ignoring_chat && !(p_ptr->party && player_in_party(p_ptr->party, i))) continue;
4797 
4798 		/* Skip himself */
4799 		if (i == Ind) continue;
4800 
4801 		/* Tell this one */
4802 		msg_print(i, afk);
4803 	}
4804 
4805 	p_ptr->redraw |= PR_EXTRA;
4806 	redraw_stuff(Ind);
4807 	return;
4808 }
4809 
4810 /*
4811  * A player has sent a message to the rest of the world.
4812  *
4813  * Parse it and send to everyone or to only the person(s) he requested.
4814  *
4815  * Note that more than one message may get sent at once, seperated by
4816  * tabs ('\t').  Thus, this function splits them and calls
4817  * "player_talk_aux" to do the dirty work.
4818  */
4819 void player_talk(int Ind, char *message)
4820 {
4821 	char *cur, *next;
4822 
4823 	/* Start at the beginning */
4824 	cur = message;
4825 
4826 	/* Process until out of messages */
4827 	while (cur)
4828 	{
4829 		/* Find the next tab */
4830 		next = strchr(cur, '\t');
4831 
4832 		/* Stop out the tab */
4833 		if (next)
4834 		{
4835 			/* Replace with \0 */
4836 			*next = '\0';
4837 		}
4838 
4839 		/* Process this message */
4840 		player_talk_aux(Ind, cur);
4841 
4842 		/* Move to the next one */
4843 		if (next)
4844 		{
4845 			/* One step past the \0 */
4846 			cur = next + 1;
4847 		}
4848 		else
4849 		{
4850 			/* No more message */
4851 			cur = NULL;
4852 		}
4853 	}
4854 }
4855 
4856 
4857 /*
4858  * Check a char for "vowel-hood"
4859  */
4860 bool is_a_vowel(int ch)
4861 {
4862 	switch (ch)
4863 	{
4864 		case 'a':
4865 		case 'e':
4866 		case 'i':
4867 		case 'o':
4868 		case 'u':
4869 		case 'A':
4870 		case 'E':
4871 		case 'I':
4872 		case 'O':
4873 		case 'U':
4874 		return (TRUE);
4875 	}
4876 
4877 	return (FALSE);
4878 }
4879 
4880 /*
4881  * Look up a player/party name, allowing abbreviations.  - Jir -
4882  * (looking up party name this way can be rather annoying though)
4883  *
4884  * returns 0 if not found/error(, minus value if party.)
4885  *
4886  * if 'party' is TRUE, party name is also looked up.
4887  */
4888 int name_lookup_loose(int Ind, cptr name, u16b party, bool include_account_names)
4889 {
4890 	int i, j, len, target = 0;
4891 	player_type *q_ptr, *p_ptr;
4892 	cptr problem = "";
4893 	bool party_online;
4894 
4895 	p_ptr = Players[Ind];
4896 
4897 	/* don't waste time */
4898 	if (p_ptr == (player_type*)NULL) return(0);
4899 
4900 	/* Acquire length of search string */
4901 	len = strlen(name);
4902 
4903 	/* Look for a recipient who matches the search string */
4904 	if (len) {
4905 		/* Check players */
4906 		for (i = 1; i <= NumPlayers; i++) {
4907 			/* Check this one */
4908 			q_ptr = Players[i];
4909 
4910 			/* Skip if disconnected */
4911 			if (q_ptr->conn == NOT_CONNECTED) continue;
4912 
4913 			/* let admins chat */
4914 			if (q_ptr->admin_dm && !q_ptr->admin_dm_chat && !is_admin(p_ptr)
4915 			    /* Hack: allow the following accounts nasty stuff (e.g., spam the DMs!) */
4916 			    && strcasecmp(p_ptr->accountname, "kurzel")
4917 			    && strcasecmp(p_ptr->accountname, "moltor")
4918 			    && strcasecmp(p_ptr->accountname, "the_sandman")
4919 			    && strcasecmp(p_ptr->accountname, "faith")
4920 			    && strcasecmp(p_ptr->accountname, "mikaelh")
4921 			    && strcasecmp(p_ptr->accountname, "c. blue")) continue;
4922 
4923 			/* Check name */
4924 			if (!strncasecmp(q_ptr->name, name, len)) {
4925 				/* Set target if not set already */
4926 				if (!target) {
4927 					target = i;
4928 				} else {
4929 					/* Matching too many people */
4930 					problem = "players";
4931 				}
4932 
4933 				/* Check for exact match */
4934 				if (len == (int)strlen(q_ptr->name)) {
4935 					/* Never a problem */
4936 					target = i;
4937 					problem = "";
4938 
4939 					/* Finished looking */
4940 					break;
4941 				}
4942 			}
4943 		}
4944 
4945 		/* Then check accounts */
4946 		if (include_account_names && !target) for (i = 1; i <= NumPlayers; i++) {
4947 			/* Check this one */
4948 			q_ptr = Players[i];
4949 
4950 			/* Skip if disconnected */
4951 			if (q_ptr->conn == NOT_CONNECTED) continue;
4952 
4953 			/* let admins chat */
4954 			if (q_ptr->admin_dm && !q_ptr->admin_dm_chat && !is_admin(p_ptr)
4955 			    /* Hack: allow the following accounts nasty stuff (e.g., spam the DMs!) */
4956 			    && strcasecmp(p_ptr->accountname, "kurzel")
4957 			    && strcasecmp(p_ptr->accountname, "moltor")
4958 			    && strcasecmp(p_ptr->accountname, "the_sandman")
4959 			    && strcasecmp(p_ptr->accountname, "faith")
4960 			    && strcasecmp(p_ptr->accountname, "mikaelh")
4961 			    && strcasecmp(p_ptr->accountname, "c. blue")) continue;
4962 
4963 			/* Check name */
4964 			if (!strncasecmp(q_ptr->accountname, name, len)) {
4965 				/* Set target if not set already */
4966 				if (!target) {
4967 					target = i;
4968 				} else {
4969 					/* Matching too many people */
4970 					problem = "players";
4971 				}
4972 
4973 				/* Check for exact match */
4974 				if (len == (int)strlen(q_ptr->accountname)) {
4975 					/* Never a problem */
4976 					target = i;
4977 					problem = "";
4978 
4979 					/* Finished looking */
4980 					break;
4981 				}
4982 			}
4983 		}
4984 
4985 		/* Check parties */
4986 		if (party && !target) {
4987 			for (i = 1; i < MAX_PARTIES; i++) {
4988 				/* Skip if empty */
4989 				if (!parties[i].members) continue;
4990 
4991 				/* Check that the party has players online - mikaelh */
4992 				party_online = FALSE;
4993 				for (j = 1; j <= NumPlayers; j++) {
4994 					/* Check this one */
4995 					q_ptr = Players[j];
4996 
4997 					/* Skip if disconnected */
4998 					if (q_ptr->conn == NOT_CONNECTED) continue;
4999 
5000 					/* Check if the player belongs to this party */
5001 					if (q_ptr->party == i) {
5002 						party_online = TRUE;
5003 						break;
5004 					}
5005 				}
5006 				if (!party_online) continue;
5007 
5008 				/* Check name */
5009 				if (!strncasecmp(parties[i].name, name, len)) {
5010 					/* Set target if not set already */
5011 					if (!target) {
5012 						target = 0 - i;
5013 					} else {
5014 						/* Matching too many parties */
5015 						problem = "parties";
5016 					}
5017 
5018 					/* Check for exact match */
5019 					if (len == (int)strlen(parties[i].name)) {
5020 						/* Never a problem */
5021 						target = 0 - i;
5022 						problem = "";
5023 
5024 						/* Finished looking */
5025 						break;
5026 					}
5027 				}
5028 			}
5029 		}
5030 	}
5031 
5032 	/* Check for recipient set but no match found */
5033 	if ((len && !target) || (target < 0 && !party)) {
5034 		/* Send an error message */
5035 		msg_format(Ind, "Could not match name '%s'.", name);
5036 
5037 		/* Give up */
5038 		return 0;
5039 	}
5040 
5041 	/* Check for multiple recipients found */
5042 	if (strlen(problem)) {
5043 		/* Send an error message */
5044 		msg_format(Ind, "'%s' matches too many %s.", name, problem);
5045 
5046 		/* Give up */
5047 		return 0;
5048 	}
5049 
5050 	/* Success */
5051 	return target;
5052 }
5053 
5054 /* same as name_lookup_loose, but without warning message if no name was found */
5055 int name_lookup_loose_quiet(int Ind, cptr name, u16b party, bool include_account_names)
5056 {
5057 	int i, j, len, target = 0;
5058 	player_type *q_ptr, *p_ptr;
5059 	cptr problem = "";
5060 	bool party_online;
5061 
5062 	p_ptr = Players[Ind];
5063 
5064 	/* don't waste time */
5065 	if(p_ptr == (player_type*)NULL) return(0);
5066 
5067 	/* Acquire length of search string */
5068 	len = strlen(name);
5069 
5070 	/* Look for a recipient who matches the search string */
5071 	if (len) {
5072 		/* Check players */
5073 		for (i = 1; i <= NumPlayers; i++) {
5074 			/* Check this one */
5075 			q_ptr = Players[i];
5076 
5077 			/* Skip if disconnected */
5078 			if (q_ptr->conn == NOT_CONNECTED) continue;
5079 
5080 			/* let admins chat */
5081 			if (q_ptr->admin_dm && !q_ptr->admin_dm_chat && !is_admin(p_ptr)
5082 			    /* Hack: allow the following accounts nasty stuff (e.g., spam the DMs!) */
5083 			    && strcasecmp(p_ptr->accountname, "kurzel")
5084 			    && strcasecmp(p_ptr->accountname, "moltor")
5085 			    && strcasecmp(p_ptr->accountname, "the_sandman")
5086 			    && strcasecmp(p_ptr->accountname, "faith")
5087 			    && strcasecmp(p_ptr->accountname, "mikaelh")
5088 			    && strcasecmp(p_ptr->accountname, "c. blue")) continue;
5089 
5090 			/* Check name */
5091 			if (!strncasecmp(q_ptr->name, name, len)) {
5092 				/* Set target if not set already */
5093 				if (!target) {
5094 					target = i;
5095 				} else {
5096 					/* Matching too many people */
5097 					problem = "players";
5098 				}
5099 
5100 				/* Check for exact match */
5101 				if (len == (int)strlen(q_ptr->name)) {
5102 					/* Never a problem */
5103 					target = i;
5104 					problem = "";
5105 
5106 					/* Finished looking */
5107 					break;
5108 				}
5109 			}
5110 		}
5111 
5112 		/* Then check accounts */
5113 		if (include_account_names && !target) for (i = 1; i <= NumPlayers; i++) {
5114 			/* Check this one */
5115 			q_ptr = Players[i];
5116 
5117 			/* Skip if disconnected */
5118 			if (q_ptr->conn == NOT_CONNECTED) continue;
5119 
5120 			/* let admins chat */
5121 			if (q_ptr->admin_dm && !q_ptr->admin_dm_chat && !is_admin(p_ptr)
5122 			    /* Hack: allow the following accounts nasty stuff (e.g., spam the DMs!) */
5123 			    && strcasecmp(p_ptr->accountname, "kurzel")
5124 			    && strcasecmp(p_ptr->accountname, "moltor")
5125 			    && strcasecmp(p_ptr->accountname, "the_sandman")
5126 			    && strcasecmp(p_ptr->accountname, "faith")
5127 			    && strcasecmp(p_ptr->accountname, "mikaelh")
5128 			    && strcasecmp(p_ptr->accountname, "c. blue")) continue;
5129 
5130 			/* Check name */
5131 			if (!strncasecmp(q_ptr->accountname, name, len)) {
5132 				/* Set target if not set already */
5133 				if (!target) {
5134 					target = i;
5135 				} else {
5136 					/* Matching too many people */
5137 					problem = "players";
5138 				}
5139 
5140 				/* Check for exact match */
5141 				if (len == (int)strlen(q_ptr->accountname)) {
5142 					/* Never a problem */
5143 					target = i;
5144 					problem = "";
5145 
5146 					/* Finished looking */
5147 					break;
5148 				}
5149 			}
5150 		}
5151 
5152 		/* Check parties */
5153 		if (party && !target) {
5154 			for (i = 1; i < MAX_PARTIES; i++) {
5155 				/* Skip if empty */
5156 				if (!parties[i].members) continue;
5157 
5158 				/* Check that the party has players online - mikaelh */
5159 				party_online = FALSE;
5160 				for (j = 1; j <= NumPlayers; j++) {
5161 					/* Check this one */
5162 					q_ptr = Players[j];
5163 
5164 					/* Skip if disconnected */
5165 					if (q_ptr->conn == NOT_CONNECTED) continue;
5166 
5167 					/* Check if the player belongs to this party */
5168 					if (q_ptr->party == i) {
5169 						party_online = TRUE;
5170 						break;
5171 					}
5172 				}
5173 				if (!party_online) continue;
5174 
5175 				/* Check name */
5176 				if (!strncasecmp(parties[i].name, name, len)) {
5177 					/* Set target if not set already */
5178 					if (!target) {
5179 						target = 0 - i;
5180 					} else {
5181 						/* Matching too many parties */
5182 						problem = "parties";
5183 					}
5184 
5185 					/* Check for exact match */
5186 					if (len == (int)strlen(parties[i].name)) {
5187 						/* Never a problem */
5188 						target = 0 - i;
5189 						problem = "";
5190 
5191 						/* Finished looking */
5192 						break;
5193 					}
5194 				}
5195 			}
5196 		}
5197 	}
5198 
5199 	/* Check for recipient set but no match found */
5200 	if ((len && !target) || (target < 0 && !party)) {
5201 		/* Give up */
5202 		return 0;
5203 	}
5204 
5205 	/* Check for multiple recipients found */
5206 	if (strlen(problem)) {
5207 		/* Send an error message */
5208 		msg_format(Ind, "'%s' matches too many %s.", name, problem);
5209 
5210 		/* Give up */
5211 		return 0;
5212 	}
5213 
5214 	/* Success */
5215 	return target;
5216 }
5217 
5218 /* copy/pasted from name_lookup_loose(), just without being loose.. */
5219 int name_lookup(int Ind, cptr name, u16b party, bool include_account_names)
5220 {
5221 	int i, j, len, target = 0;
5222 	player_type *q_ptr, *p_ptr;
5223 	bool party_online;
5224 
5225 	p_ptr = Players[Ind];
5226 
5227 	/* don't waste time */
5228 	if (p_ptr == (player_type*)NULL) return(0);
5229 
5230 	/* Acquire length of search string */
5231 	len = strlen(name);
5232 
5233 	/* Look for a recipient who matches the search string */
5234 	if (len) {
5235 		/* Check players */
5236 		for (i = 1; i <= NumPlayers; i++) {
5237 			/* Check this one */
5238 			q_ptr = Players[i];
5239 
5240 			/* Skip if disconnected */
5241 			if (q_ptr->conn == NOT_CONNECTED) continue;
5242 
5243 			/* let admins chat */
5244 			if (q_ptr->admin_dm && !q_ptr->admin_dm_chat && !is_admin(p_ptr)
5245 			    /* Hack: allow the following accounts nasty stuff (e.g., spam the DMs!) */
5246 			    && strcasecmp(p_ptr->accountname, "kurzel")
5247 			    && strcasecmp(p_ptr->accountname, "moltor")
5248 			    && strcasecmp(p_ptr->accountname, "the_sandman")
5249 			    && strcasecmp(p_ptr->accountname, "faith")
5250 			    && strcasecmp(p_ptr->accountname, "mikaelh")
5251 			    && strcasecmp(p_ptr->accountname, "c. blue")) continue;
5252 
5253 			/* Check name */
5254 			if (!strcasecmp(q_ptr->name, name)) {
5255 				/* Never a problem */
5256 				target = i;
5257 
5258 				/* Finished looking */
5259 				break;
5260 			}
5261 		}
5262 
5263 		/* Then check accounts */
5264 		if (include_account_names && !target) for (i = 1; i <= NumPlayers; i++) {
5265 			/* Check this one */
5266 			q_ptr = Players[i];
5267 
5268 			/* Skip if disconnected */
5269 			if (q_ptr->conn == NOT_CONNECTED) continue;
5270 
5271 			/* let admins chat */
5272 			if (q_ptr->admin_dm && !q_ptr->admin_dm_chat && !is_admin(p_ptr)
5273 			    /* Hack: allow the following accounts nasty stuff (e.g., spam the DMs!) */
5274 			    && strcasecmp(p_ptr->accountname, "kurzel")
5275 			    && strcasecmp(p_ptr->accountname, "moltor")
5276 			    && strcasecmp(p_ptr->accountname, "the_sandman")
5277 			    && strcasecmp(p_ptr->accountname, "faith")
5278 			    && strcasecmp(p_ptr->accountname, "mikaelh")
5279 			    && strcasecmp(p_ptr->accountname, "c. blue")) continue;
5280 
5281 			/* Check name */
5282 			if (!strcasecmp(q_ptr->accountname, name)) {
5283 				/* Never a problem */
5284 				target = i;
5285 
5286 				/* Finished looking */
5287 				break;
5288 			}
5289 		}
5290 
5291 		/* Check parties */
5292 		if (party && !target) {
5293 			for (i = 1; i < MAX_PARTIES; i++) {
5294 				/* Skip if empty */
5295 				if (!parties[i].members) continue;
5296 
5297 				/* Check that the party has players online - mikaelh */
5298 				party_online = FALSE;
5299 				for (j = 1; j <= NumPlayers; j++) {
5300 					/* Check this one */
5301 					q_ptr = Players[j];
5302 
5303 					/* Skip if disconnected */
5304 					if (q_ptr->conn == NOT_CONNECTED) continue;
5305 
5306 					/* Check if the player belongs to this party */
5307 					if (q_ptr->party == i) {
5308 						party_online = TRUE;
5309 						break;
5310 					}
5311 				}
5312 				if (!party_online) continue;
5313 
5314 				/* Check name */
5315 				if (!strcasecmp(parties[i].name, name)) {
5316 					/* Never a problem */
5317 					target = 0 - i;
5318 
5319 					/* Finished looking */
5320 					break;
5321 				}
5322 			}
5323 		}
5324 	}
5325 
5326 	/* Check for recipient set but no match found */
5327 	if ((len && !target) || (target < 0 && !party)) {
5328 		/* Send an error message */
5329 		msg_format(Ind, "Could not match name '%s'.", name);
5330 
5331 		/* Give up */
5332 		return 0;
5333 	}
5334 
5335 	/* Success */
5336 	return target;
5337 }
5338 
5339 /* copy/pasted from name_lookup_loose(), just without being loose.. but with quiet */
5340 int name_lookup_quiet(int Ind, cptr name, u16b party, bool include_account_names)
5341 {
5342 	int i, j, len, target = 0;
5343 	player_type *q_ptr, *p_ptr;
5344 	bool party_online;
5345 
5346 	p_ptr = Players[Ind];
5347 
5348 	/* don't waste time */
5349 	if (p_ptr == (player_type*)NULL) return(0);
5350 
5351 	/* Acquire length of search string */
5352 	len = strlen(name);
5353 
5354 	/* Look for a recipient who matches the search string */
5355 	if (len) {
5356 		/* Check players */
5357 		for (i = 1; i <= NumPlayers; i++) {
5358 			/* Check this one */
5359 			q_ptr = Players[i];
5360 
5361 			/* Skip if disconnected */
5362 			if (q_ptr->conn == NOT_CONNECTED) continue;
5363 
5364 			/* let admins chat */
5365 			if (q_ptr->admin_dm && !q_ptr->admin_dm_chat && !is_admin(p_ptr)
5366 			    /* Hack: allow the following accounts nasty stuff (e.g., spam the DMs!) */
5367 			    && strcasecmp(p_ptr->accountname, "kurzel")
5368 			    && strcasecmp(p_ptr->accountname, "moltor")
5369 			    && strcasecmp(p_ptr->accountname, "the_sandman")
5370 			    && strcasecmp(p_ptr->accountname, "faith")
5371 			    && strcasecmp(p_ptr->accountname, "mikaelh")
5372 			    && strcasecmp(p_ptr->accountname, "c. blue")) continue;
5373 
5374 			/* Check name */
5375 			if (!strcasecmp(q_ptr->name, name)) {
5376 				/* Never a problem */
5377 				target = i;
5378 
5379 				/* Finished looking */
5380 				break;
5381 			}
5382 		}
5383 
5384 		/* Then check accounts */
5385 		if (include_account_names && !target) for (i = 1; i <= NumPlayers; i++) {
5386 			/* Check this one */
5387 			q_ptr = Players[i];
5388 
5389 			/* Skip if disconnected */
5390 			if (q_ptr->conn == NOT_CONNECTED) continue;
5391 
5392 			/* let admins chat */
5393 			if (q_ptr->admin_dm && !q_ptr->admin_dm_chat && !is_admin(p_ptr)
5394 			    /* Hack: allow the following accounts nasty stuff (e.g., spam the DMs!) */
5395 			    && strcasecmp(p_ptr->accountname, "kurzel")
5396 			    && strcasecmp(p_ptr->accountname, "moltor")
5397 			    && strcasecmp(p_ptr->accountname, "the_sandman")
5398 			    && strcasecmp(p_ptr->accountname, "faith")
5399 			    && strcasecmp(p_ptr->accountname, "mikaelh")
5400 			    && strcasecmp(p_ptr->accountname, "c. blue")) continue;
5401 
5402 			/* Check name */
5403 			if (!strcasecmp(q_ptr->accountname, name)) {
5404 				/* Never a problem */
5405 				target = i;
5406 
5407 				/* Finished looking */
5408 				break;
5409 			}
5410 		}
5411 
5412 		/* Check parties */
5413 		if (party && !target) {
5414 			for (i = 1; i < MAX_PARTIES; i++) {
5415 				/* Skip if empty */
5416 				if (!parties[i].members) continue;
5417 
5418 				/* Check that the party has players online - mikaelh */
5419 				party_online = FALSE;
5420 				for (j = 1; j <= NumPlayers; j++) {
5421 					/* Check this one */
5422 					q_ptr = Players[j];
5423 
5424 					/* Skip if disconnected */
5425 					if (q_ptr->conn == NOT_CONNECTED) continue;
5426 
5427 					/* Check if the player belongs to this party */
5428 					if (q_ptr->party == i) {
5429 						party_online = TRUE;
5430 						break;
5431 					}
5432 				}
5433 				if (!party_online) continue;
5434 
5435 				/* Check name */
5436 				if (!strcasecmp(parties[i].name, name)) {
5437 					/* Never a problem */
5438 					target = 0 - i;
5439 
5440 					/* Finished looking */
5441 					break;
5442 				}
5443 			}
5444 		}
5445 	}
5446 
5447 	/* Check for recipient set but no match found */
5448 	if ((len && !target) || (target < 0 && !party)) {
5449 		/* Give up */
5450 		return 0;
5451 	}
5452 
5453 	/* Success */
5454 	return target;
5455 }
5456 
5457 /*
5458  * Convert bracer '{' into '\377'
5459  */
5460 void bracer_ff(char *buf)
5461 {
5462 	int i, len = strlen(buf);
5463 
5464 	for (i = 0; i < len; i++) {
5465 		if (buf[i] == '{') buf[i] = '\377';
5466 	}
5467 }
5468 
5469 /*
5470  * make strings from worldpos '-1550ft of (17,15)'	- Jir -
5471  */
5472 char *wpos_format(int Ind, worldpos *wpos)
5473 {
5474 	int i = Ind, n, d = 0;
5475 	cptr desc = "";
5476 	bool ville = istown(wpos) && !isdungeontown(wpos);
5477 	dungeon_type *d_ptr = NULL;
5478 
5479 	/* Hack for Valinor originally */
5480 	if (i < 0) i = -i;
5481 	if (wpos->wz > 0 && (d_ptr = wild_info[wpos->wy][wpos->wx].tower))
5482 		d = d_ptr->type;
5483 	if (wpos->wz < 0 && (d_ptr = wild_info[wpos->wy][wpos->wx].dungeon))
5484 		d = d_ptr->type;
5485 	if (!wpos->wz && ville)
5486 		for (n = 0; n < numtowns; n++)
5487 			if (town[n].x == wpos->wx && town[n].y == wpos->wy) {
5488 				desc = town_profile[town[n].type].name;
5489 				break;
5490 			}
5491 	if (!strcmp(desc, "")) ville = FALSE;
5492 
5493 	if (!i || Players[i]->depth_in_feet) {
5494 		if (Ind >= 0 || (!d && !ville)) {
5495 			return (format("%dft of (%d,%d)", wpos->wz * 50, wpos->wx, wpos->wy));
5496 		} else
5497 			if (!ville)
5498 				return (format("%dft %s", wpos->wz * 50, get_dun_name(wpos->wx, wpos->wy, (wpos->wz > 0), d_ptr, 0, FALSE)));
5499 			else
5500 				return (format("%s", desc));
5501 	} else {
5502 		if (Ind >= 0 || (!d && !ville)) {
5503 			return (format("Lv %d of (%d,%d)", wpos->wz, wpos->wx, wpos->wy));
5504 		} else
5505 			if (!ville)
5506 				return (format("Lv %d %s", wpos->wz, get_dun_name(wpos->wx, wpos->wy, (wpos->wz > 0), d_ptr, 0, FALSE)));
5507 			else
5508 				return (format("%s", desc));
5509 	}
5510 }
5511 char *wpos_format_compact(int Ind, worldpos *wpos)
5512 {
5513 	int n;
5514 	cptr desc = "";
5515 	bool ville = istown(wpos) && !isdungeontown(wpos);
5516 
5517 	if (!wpos->wz && ville)
5518 		for (n = 0; n < numtowns; n++)
5519 			if (town[n].x == wpos->wx && town[n].y == wpos->wy) {
5520 				desc = town_profile[town[n].type].name;
5521 				break;
5522 			}
5523 	if (!strcmp(desc, "")) ville = FALSE;
5524 
5525 	if (ville) return (format("%s", desc));
5526 	else {
5527 		if (Players[Ind]->depth_in_feet)
5528 			return (format("%dft (%d,%d)", wpos->wz * 50, wpos->wx, wpos->wy));
5529 		else
5530 			return (format("Lv %d (%d,%d)", wpos->wz, wpos->wx, wpos->wy));
5531 	}
5532 }
5533 
5534 
5535 byte count_bits(u32b array)
5536 {
5537 	byte k = 0, i;
5538 
5539 	if(array)
5540 		for(i = 0; i < 32; i++)
5541 			if(array & (1 << i)) k++;
5542 
5543 	return k;
5544 }
5545 
5546 /*
5547  * Find a player
5548  */
5549 int get_playerind(char *name)
5550 {
5551 	int i;
5552 
5553 	if (name == (char*)NULL) return(-1);
5554 	for(i = 1; i <= NumPlayers; i++) {
5555 		if(Players[i]->conn == NOT_CONNECTED) continue;
5556 		if(!stricmp(Players[i]->name, name)) return(i);
5557 	}
5558 	return(-1);
5559 }
5560 int get_playerind_loose(char *name)
5561 {
5562 	int i, len = strlen(name);
5563 
5564 	if (len == 0) return(-1);
5565 	if (name == (char*)NULL) return(-1);
5566 	for(i = 1; i <= NumPlayers; i++) {
5567 		if(Players[i]->conn == NOT_CONNECTED) continue;
5568 		if (!strncasecmp(Players[i]->name, name, len)) return(i);
5569 	}
5570 	return(-1);
5571 }
5572 
5573 int get_playerslot_loose(int Ind, char *iname) {
5574 	int i, j;
5575 	char o_name[ONAME_LEN], i_name[ONAME_LEN];
5576 
5577 	if (iname == (char*)NULL) return(-1);
5578 	if ((*iname) == 0) return(-1);
5579 
5580 	for (j = 0; iname[j]; j++) i_name[j] = tolower(iname[j]);
5581 	i_name[j] = 0;
5582 
5583 	for (i = 0; i < INVEN_TOTAL; i++) {
5584 		if (!Players[Ind]->inventory[i].k_idx) continue;
5585 
5586 		object_desc(0, o_name, &Players[Ind]->inventory[i], FALSE, 3+16+32);
5587 		for (j = 0; o_name[j]; j++) o_name[j] = tolower(o_name[j]);
5588 
5589 		if (strstr(o_name, i_name)) return(i);
5590 	}
5591 
5592 	return(-1);
5593 }
5594 
5595 /*
5596  * Tell the player of the floor feeling.	- Jir -
5597  * NOTE: differs from traditional 'boring' etc feeling!
5598  * NOTE: added traditional feelings, to warn of dangers - C. Blue
5599  */
5600 bool show_floor_feeling(int Ind, bool dungeon_feeling) {
5601 	player_type *p_ptr = Players[Ind];
5602 	worldpos *wpos = &p_ptr->wpos;
5603 	struct dungeon_type *d_ptr = getdungeon(wpos);
5604 	dun_level *l_ptr = getfloor(wpos);
5605 	bool felt = FALSE;
5606 
5607 	/* Hack for Valinor - C. Blue */
5608 	if (in_valinor(wpos)) {
5609 		msg_print(Ind, "\374\377gYou have a wonderful feeling of peace...");
5610 		return TRUE;
5611 	}
5612 
5613 	/* XXX devise a better formula */
5614 	if (!in_irondeepdive(&p_ptr->wpos) && (p_ptr->lev * ((p_ptr->lev >= 40) ? 3 : 2) + 5 < getlevel(wpos))) {
5615 		msg_print(Ind, "\374\377rYou feel an imminent danger!");
5616 		felt = TRUE;
5617 	}
5618 
5619 #ifdef DUNGEON_VISIT_BONUS
5620 	if (dungeon_feeling && d_ptr && dungeon_bonus[d_ptr->id]
5621 	    && !(d_ptr->flags3 & DF3_NO_DUNGEON_BONUS)) {
5622 		felt = TRUE;
5623 		switch (dungeon_bonus[d_ptr->id]) {
5624 		case 3: msg_print(Ind, "\377UThis place has not been explored in ages."); break;
5625 		case 2: msg_print(Ind, "\377UThis place has not been explored in a long time."); break;
5626 		case 1: msg_print(Ind, "\377UThis place has not been explored recently."); break;
5627 		}
5628 	}
5629 #endif
5630 
5631 	if (!l_ptr) return(felt);
5632 
5633 #ifdef EXTRA_LEVEL_FEELINGS
5634 	/* Display extra traditional feeling to warn of dangers. - C. Blue
5635 	   Note: Only display ONE feeling, thereby setting priorities here.
5636 	   Note: Don't display feelings in Training Tower (NO_DEATH). */
5637 	if ((!p_ptr->distinct_floor_feeling && !is_admin(p_ptr)) || (d_ptr->flags2 & DF2_NO_DEATH) ||
5638 	    (wpos->wx == WPOS_PVPARENA_X && wpos->wy == WPOS_PVPARENA_Y && wpos->wz == WPOS_PVPARENA_Z)) {
5639 //		msg_print(Ind, "\376\377yLooks like any other level..");
5640 //		msg_print(Ind, "\377ypfft");
5641 	}
5642 	else if (l_ptr->flags2 & LF2_OOD_HI) {
5643 		msg_print(Ind, "\374\377yWhat a terrifying place..");
5644 		felt = TRUE;
5645 	} else if ((l_ptr->flags2 & LF2_VAULT_HI) &&
5646 		(l_ptr->flags2 & LF2_OOD)) {
5647 		msg_print(Ind, "\374\377yWhat a terrifying place..");
5648 		felt = TRUE;
5649 	} else if ((l_ptr->flags2 & LF2_VAULT_OPEN) || // <- TODO: implement :/
5650 		 ((l_ptr->flags2 & LF2_VAULT) && (l_ptr->flags2 & LF2_OOD_FREE))) {
5651 		msg_print(Ind, "\374\377yYou sense an air of danger..");
5652 		felt = TRUE;
5653 	} else if (l_ptr->flags2 & LF2_VAULT) {
5654 		msg_print(Ind, "\374\377yFeels somewhat dangerous around here..");
5655 		felt = TRUE;
5656 	} else if (l_ptr->flags2 & LF2_PITNEST_HI) {
5657 		msg_print(Ind, "\374\377yFeels somewhat dangerous around here..");
5658 		felt = TRUE;
5659 	} else if (l_ptr->flags2 & LF2_OOD_FREE) {
5660 		msg_print(Ind, "\374\377yThere's a sensation of challenge..");
5661 		felt = TRUE;
5662 	} /*	else if (l_ptr->flags2 & LF2_PITNEST) { //maybe enable, maybe too cheezy
5663 		msg_print(Ind, "\374\377yYou feel your luck is turning..");
5664 		felt = TRUE;
5665 	} */ else if (l_ptr->flags2 & LF2_UNIQUE) {
5666 		msg_print(Ind, "\374\377yThere's a special feeling about this place..");
5667 		felt = TRUE;
5668 	} /* else if (l_ptr->flags2 & LF2_ARTIFACT) { //probably too cheezy, if not then might need combination with threat feelings above
5669 		msg_print(Ind, "\374\377y");
5670 		felt = TRUE;
5671 	} *//*	else if (l_ptr->flags2 & LF2_ITEM_OOD) { //probably too cheezy, if not then might need combination with threat feelings above
5672 		msg_print(Ind, "\374\377y");
5673 		felt = TRUE;
5674 	} */ else {
5675 		msg_print(Ind, "\374\377yWhat a boring place..");
5676 		felt = TRUE;
5677 	}
5678 #endif
5679 
5680 	/* Special feeling for dungeon bosses in IDDC */
5681 	if ((p_ptr->distinct_floor_feeling || is_admin(p_ptr)) &&
5682 	    in_irondeepdive(wpos) && (l_ptr->flags2 & LF2_DUN_BOSS))
5683 		msg_print(Ind, "\374\377UYou feel a commanding presence..");
5684 
5685 
5686 	/* Hack^2 -- display the 'feeling' */
5687 	if (l_ptr->flags1 & LF1_NO_MAGIC) {
5688 		msg_print(Ind, "\377oYou feel a suppressive air.");
5689 		/* Automatically dis/re-enable wraith form */
5690 	        p_ptr->update |= PU_BONUS;
5691 	}
5692 	if (l_ptr->flags1 & LF1_NO_GENO)
5693 //sounds as if it's good for the player		msg_print(Ind, "\377oYou have a feeling of peace...");
5694 		msg_print(Ind, "\377oThe air seems distorted.");
5695 	if (l_ptr->flags1 & LF1_NO_MAP)
5696 		msg_print(Ind, "\377oYou lose all sense of direction...");
5697 	if (l_ptr->flags1 & LF1_NO_MAGIC_MAP)
5698 		msg_print(Ind, "\377oYou feel like a stranger...");
5699 	if (l_ptr->flags1 & LF1_NO_DESTROY)
5700 		msg_print(Ind, "\377oThe walls here seem very solid.");
5701 	if (l_ptr->flags1 & LF1_NO_GHOST)
5702 		msg_print(Ind, "\377oYou feel that your life hangs in the balance!"); //credits to Moltor actually, ha!:)
5703 #if 0
5704 	if (l_ptr->flags1 & DF1_NO_RECALL)
5705 		msg_print(Ind, "\377oThere is strong magic enclosing this dungeon.");
5706 #endif
5707 	/* Can leave IRONMAN? */
5708 	if ((l_ptr->flags1 & LF1_IRON_RECALL || ((d_ptr->flags1 & DF1_FORCE_DOWN) && d_ptr->maxdepth == ABS(p_ptr->wpos.wz)))
5709 	    && !(d_ptr->flags2 & DF2_NO_EXIT_WOR))
5710 		msg_print(Ind, "\374\377gYou don't sense a magic barrier here!");
5711 
5712 	return(l_ptr->flags1 & LF1_FEELING_MASK ? TRUE : felt);
5713 }
5714 
5715 /*
5716  * Given item name as string, return the index in k_info array. Name
5717  * must exactly match (look out for commas and the like!), or else 0 is
5718  * returned. Case doesn't matter. -DG-
5719  */
5720 
5721 int test_item_name(cptr name)
5722 {
5723        int i;
5724 
5725        /* Scan the items */
5726        for (i = 1; i < max_k_idx; i++)
5727        {
5728 		object_kind *k_ptr = &k_info[i];
5729 		cptr obj_name = k_name + k_ptr->name;
5730 
5731 		/* If name matches, give us the number */
5732 		if (stricmp(name, obj_name) == 0) return (i);
5733        }
5734        return (0);
5735 }
5736 
5737 /*
5738  * Middle-Earth (Imladris) calendar code from ToME
5739  */
5740 /*
5741  * Break scalar time
5742  */
5743 s32b bst(s32b what, s32b t)
5744 {
5745 #if 0 /* TIMEREWRITE */
5746 	s32b turns = t + (10 * DAY_START);
5747 
5748 	switch (what)
5749 	{
5750 		case MINUTE:
5751 			return ((turns / 10 / MINUTE) % 60);
5752 		case HOUR:
5753 			return ((turns / 10 / HOUR) % 24);
5754 		case DAY:
5755 			return ((turns / 10 / DAY) % 365);
5756 		case YEAR:
5757 			return ((turns / 10 / YEAR));
5758 		default:
5759 			return (0);
5760 	}
5761 #else
5762 	s32b turns = t;
5763 
5764 	if (what == MINUTE)
5765 		return ((turns / MINUTE) % 60);
5766 	else if (what == HOUR)
5767 		return ((turns / HOUR) % 24);
5768 	else if (what == DAY)
5769 		return ((((turns / DAY) + START_DAY) % 365));
5770 	else if (what == YEAR)
5771 		return ((turns / YEAR) + START_YEAR);
5772 	else
5773 		return (0);
5774 #endif
5775 }
5776 
5777 cptr get_month_name(int day, bool full, bool compact)
5778 {
5779 	int i = 8;
5780 	static char buf[40];
5781 
5782 	/* Find the period name */
5783 	while ((i > 0) && (day < month_day[i]))
5784 	{
5785 		i--;
5786 	}
5787 
5788 	switch (i)
5789 	{
5790 		/* Yestare/Mettare */
5791 		case 0:
5792 		case 8:
5793 		{
5794 			char buf2[20];
5795 
5796 			snprintf(buf2, 20, "%s", get_day(day + 1));
5797 			if (full) snprintf(buf, 40, "%s (%s day)", month_name[i], buf2);
5798 			else snprintf(buf, 40, "%s", month_name[i]);
5799 			break;
5800 		}
5801 		/* 'Normal' months + Enderi */
5802 		default:
5803 		{
5804 			char buf2[20];
5805 			char buf3[20];
5806 
5807 			snprintf(buf2, 20, "%s", get_day(day + 1 - month_day[i]));
5808 			snprintf(buf3, 20, "%s", get_day(day + 1));
5809 
5810 			if (full) snprintf(buf, 40, "%s day of %s (%s day)", buf2, month_name[i], buf3);
5811 			else if (compact) snprintf(buf, 40, "%s day of %s", buf2, month_name[i]);
5812 			else snprintf(buf, 40, "%s %s", buf2, month_name[i]);
5813 			break;
5814 		}
5815 	}
5816 
5817 	return (buf);
5818 }
5819 
5820 cptr get_day(int day)
5821 {
5822 	static char buf[20];
5823 	cptr p = "th";
5824 
5825 	if ((day / 10) == 1) ;
5826 	else if ((day % 10) == 1) p = "st";
5827 	else if ((day % 10) == 2) p = "nd";
5828 	else if ((day % 10) == 3) p = "rd";
5829 
5830 	snprintf(buf, 20, "%d%s", day, p);
5831 	return (buf);
5832 }
5833 
5834 /* Fuzzy: Will allow one-off colour,
5835    Compact: Will increase stack size by factor [100] for all particular colour ranges. */
5836 int gold_colour(int amt, bool fuzzy, bool compact) {
5837 	int i, unit = 1;
5838 
5839 	//for (i = amt; i > 99; i >>= 1, unit++) /* naught */; --old
5840 
5841 #if 1
5842 	/* special: perform pre-rounding, to achieve smoother looking transitions */
5843 	for (i = 1; i * 10 <= amt; i *= 10);
5844 	/* keep first 2 digits, discard the rest */
5845 	if (i >= 10) amt = (amt / (i / 10)) * (i / 10);
5846 #endif
5847 
5848 	if (compact)
5849 #if 0 /* scale? */
5850 		for (i = amt / 100; i >= 40; i = (i * 2) / 3, unit++) /* naught */;
5851 #else /* stretch? */
5852  #if 0 /* was ok, but due to natural income flow many lower tier colours are sort of skipped */
5853 		//for (i = amt; i >= 60; i = (i * 2) / 4, unit++) /* naught */;
5854  #else /* seems to scale better, allowing all colours to shine */
5855 		for (i = amt; i >= 60; i = (i * 4) / 9, unit++) /* naught */;
5856  #endif
5857 #endif
5858 	else
5859 		for (i = amt; i >= 40; i = (i * 2) / 3, unit++) /* naught */;
5860 	if (fuzzy)
5861 		//unit = unit - 1 + rand_int(3);
5862 		//unit = unit - 1 + (rand_int(5) + 2) / 3;
5863 		unit = unit - 1 + (rand_int(7) + 4) / 5;
5864 	if (unit < 1) unit = 1;
5865 	if (unit > SV_GOLD_MAX) unit = SV_GOLD_MAX;
5866 
5867 	return (lookup_kind(TV_GOLD, unit));
5868 }
5869 
5870 /* Catching bad players who hack their client.. nasty! */
5871 void lua_intrusion(int Ind, char *problem_diz)
5872 {
5873 	s_printf(format("LUA INTRUSION: %s : %s\n", Players[Ind]->name, problem_diz));
5874 
5875 #if 0 /* 4.4.8 client had a bug, mass-near-killing people. Let's turn this silly stuff off. -C. Blue */
5876 	take_hit(Ind, Players[Ind]->chp - 1, "", 0);
5877 	msg_print(Ind, "\377rThat was close huh?!");
5878 #else
5879 	if (!strcmp(problem_diz, "bad spell level")) {
5880 		msg_print(Ind, "\376\377RERROR: You need higher skill to cast this spell. However, your book shows");
5881 		msg_print(Ind, "\376\377R       that you may cast it because your LUA spell scripts are out of date!");
5882 		msg_print(Ind, "\376\377R       Please update your client (or at least the LUA files in lib/scpt).");
5883 		// (and don't use '-u' command-line option)
5884 	} else {
5885 		msg_print(Ind, "\376\377RERROR: Your LUA spell scripts seem to be out of date!");
5886 		msg_print(Ind, "\376\377R       Please update your client (or at least the LUA files in lib/scpt).");
5887 		// (and don't use '-u' command-line option)
5888 	}
5889 #endif
5890 }
5891 
5892 void bbs_add_line(cptr textline)
5893 {
5894 	int i, j;
5895 	/* either find an empty bbs entry (store its position in j) */
5896 	for (i = 0; i < BBS_LINES; i++)
5897     		if(!strcmp(bbs_line[i], "")) break;
5898 	j = i;
5899 	/* or scroll up by one line, discarding the first line */
5900 	if (i == BBS_LINES)
5901 	        for (j = 0; j < BBS_LINES - 1; j++)
5902 	                strcpy(bbs_line[j], bbs_line[j + 1]);
5903 	/* write the line to the bbs */
5904 	strncpy(bbs_line[j], textline, MAX_CHARS_WIDE - 3); /* lines get one leading spaces on outputting, so it's 78-1  //  was 77 */
5905 }
5906 
5907 void bbs_del_line(int entry)
5908 {
5909 	int j;
5910 	if (entry < 0) return;
5911 	if (entry >= BBS_LINES) return;
5912         for (j = entry; j < BBS_LINES - 1; j++)
5913                 strcpy(bbs_line[j], bbs_line[j + 1]);
5914 	/* erase last line */
5915 	strcpy(bbs_line[BBS_LINES - 1], "");
5916 }
5917 
5918 void bbs_erase(void)
5919 {
5920 	int i;
5921         for (i = 0; i < BBS_LINES; i++)
5922                 strcpy(bbs_line[i], "");
5923 }
5924 
5925 void pbbs_add_line(u16b party, cptr textline)
5926 {
5927 	int i, j;
5928 	/* either find an empty bbs entry (store its position in j) */
5929 	for (i = 0; i < BBS_LINES; i++)
5930     		if(!strcmp(pbbs_line[party][i], "")) break;
5931 	j = i;
5932 	/* or scroll up by one line, discarding the first line */
5933 	if (i == BBS_LINES)
5934 	        for (j = 0; j < BBS_LINES - 1; j++)
5935 	                strcpy(pbbs_line[party][j], pbbs_line[party][j + 1]);
5936 	/* write the line to the bbs */
5937 	strncpy(pbbs_line[party][j], textline, MAX_CHARS_WIDE - 3);
5938 }
5939 
5940 void gbbs_add_line(byte guild, cptr textline)
5941 {
5942 	int i, j;
5943 	/* either find an empty bbs entry (store its position in j) */
5944 	for (i = 0; i < BBS_LINES; i++)
5945     		if(!strcmp(gbbs_line[guild][i], "")) break;
5946 	j = i;
5947 	/* or scroll up by one line, discarding the first line */
5948 	if (i == BBS_LINES)
5949 	        for (j = 0; j < BBS_LINES - 1; j++)
5950 	                strcpy(gbbs_line[guild][j], gbbs_line[guild][j + 1]);
5951 	/* write the line to the bbs */
5952 	strncpy(gbbs_line[guild][j], textline, MAX_CHARS_WIDE - 3);
5953 }
5954 
5955 
5956 /*
5957  * Add a player id to a linked list.
5958  * Doesn't check for duplicates
5959  * Takes a double pointer to the list
5960  */
5961 void player_list_add(player_list_type **list, s32b player)
5962 {
5963 	player_list_type *pl_ptr;
5964 
5965 	MAKE(pl_ptr, player_list_type);
5966 
5967 	pl_ptr->id = player;
5968 	pl_ptr->next = *list;
5969 	*list = pl_ptr;
5970 }
5971 
5972 /*
5973  * Check if a list contains an id.
5974  */
5975 bool player_list_find(player_list_type *list, s32b player)
5976 {
5977 	player_list_type *pl_ptr;
5978 
5979 	pl_ptr = list;
5980 
5981 	while (pl_ptr)
5982 	{
5983 		if (pl_ptr->id == player)
5984 		{
5985 			return TRUE;
5986 		}
5987 		pl_ptr = pl_ptr->next;
5988 	}
5989 
5990 	return FALSE;
5991 }
5992 
5993 /*
5994  * Delete an id from a list.
5995  * Takes a double pointer to the list
5996  */
5997 bool player_list_del(player_list_type **list, s32b player)
5998 {
5999 	player_list_type *pl_ptr, *prev;
6000 
6001 	if (*list == NULL) return FALSE;
6002 
6003 	/* Check the first node */
6004 	if ((*list)->id == player)
6005 	{
6006 		*list = (*list)->next;
6007 		return TRUE;
6008 	}
6009 
6010 	pl_ptr = (*list)->next;
6011 	prev = *list;
6012 
6013 	/* Check the rest of the nodes */
6014 	while (pl_ptr)
6015 	{
6016 		if (pl_ptr->id == player)
6017 		{
6018 			prev->next = pl_ptr->next;
6019 			FREE(pl_ptr, player_list_type);
6020 			return TRUE;
6021 		}
6022 
6023 		prev = pl_ptr;
6024 		pl_ptr = pl_ptr->next;
6025 	}
6026 
6027 	return FALSE;
6028 }
6029 
6030 /*
6031  * Free an entire list.
6032  */
6033 void player_list_free(player_list_type *list)
6034 {
6035 	player_list_type *pl_ptr, *tmp;
6036 
6037 	pl_ptr = list;
6038 
6039 	while (pl_ptr)
6040 	{
6041 		tmp = pl_ptr;
6042 		pl_ptr = pl_ptr->next;
6043 		FREE(tmp, player_list_type);
6044 	}
6045 }
6046 
6047 /*
6048  * Check if the client version fills the requirements.
6049  *
6050  * Branch has to be an exact match.
6051  */
6052 bool is_newer_than(version_type *version, int major, int minor, int patch, int extra, int branch, int build)
6053 {
6054 	if (version->major < major)
6055 		return FALSE; /* very old */
6056 	else if (version->major > major)
6057 		return TRUE; /* very new */
6058 	else if (version->minor < minor)
6059 		return FALSE; /* pretty old */
6060 	else if (version->minor > minor)
6061 		return TRUE; /* pretty new */
6062 	else if (version->patch < patch)
6063 		return FALSE; /* somewhat old */
6064 	else if (version->patch > patch)
6065 		return TRUE; /* somewhat new */
6066 	else if (version->extra < extra)
6067 		return FALSE; /* a little older */
6068 	else if (version->extra > extra)
6069 		return TRUE; /* a little newer */
6070 	/* Check that the branch is an exact match */
6071 	else if (version->branch == branch)
6072 	{
6073 		/* Now check the build */
6074 		if (version->build < build)
6075 			return FALSE;
6076 		else if (version->build > build)
6077 			return TRUE;
6078 	}
6079 
6080 	/* Default */
6081 	return FALSE;
6082 }
6083 
6084 bool is_older_than(version_type *version, int major, int minor, int patch, int extra, int branch, int build)
6085 {
6086 	if (version->major > major)
6087 		return FALSE; /* very new */
6088 	else if (version->major < major)
6089 		return TRUE; /* very old */
6090 	else if (version->minor > minor)
6091 		return FALSE; /* pretty new */
6092 	else if (version->minor < minor)
6093 		return TRUE; /* pretty old */
6094 	else if (version->patch > patch)
6095 		return FALSE; /* somewhat new */
6096 	else if (version->patch < patch)
6097 		return TRUE; /* somewhat old */
6098 	else if (version->extra > extra)
6099 		return FALSE; /* a little newer */
6100 	else if (version->extra < extra)
6101 		return TRUE; /* a little older */
6102 	/* Check that the branch is an exact match */
6103 	else if (version->branch == branch)
6104 	{
6105 		/* Now check the build */
6106 		if (version->build > build)
6107 			return FALSE;
6108 		else if (version->build < build)
6109 			return TRUE;
6110 	}
6111 
6112 	/* Default */
6113 	return FALSE;
6114 }
6115 
6116 bool is_same_as(version_type *version, int major, int minor, int patch, int extra, int branch, int build)
6117 {
6118 	if (version->major == major
6119 	    && version->minor == minor
6120 	    && version->patch == patch
6121 	    && version->extra == extra
6122 	    && version->branch == branch
6123 	    && version->build == build)
6124 		return TRUE;
6125 
6126 	return FALSE;
6127 }
6128 
6129 /*
6130  * Since only GNU libc has memfrob, we use our own.
6131  */
6132 void my_memfrob(void *s, int n)
6133 {
6134 	int i;
6135 	char *str;
6136 
6137 	str = (char*) s;
6138 
6139 	for (i = 0; i < n; i++)
6140 	{
6141 		/* XOR every byte with 42 */
6142 		str[i] ^= 42;
6143 	}
6144 }
6145 
6146 /* compare player mode compatibility - C. Blue
6147    Note: returns NULL if compatible.
6148    strict: Ignore IRONDEEPDIVE_ALLOW_INCOMPAT. */
6149 cptr compat_pmode(int Ind1, int Ind2, bool strict) {
6150 	player_type *p1_ptr = Players[Ind1], *p2_ptr = Players[Ind2];
6151 
6152 #ifdef IRONDEEPDIVE_ALLOW_INCOMPAT
6153 	/* EXPERIMENTAL */
6154 	if (!strict &&
6155 	    in_irondeepdive(&p1_ptr->wpos) &&
6156 	    in_irondeepdive(&p2_ptr->wpos))
6157 		return NULL;
6158 #endif
6159 
6160 	if (p1_ptr->mode & MODE_PVP) {
6161 		if (!(p2_ptr->mode & MODE_PVP)) {
6162 			return "non-pvp";
6163 		}
6164 	} else if (p1_ptr->mode & MODE_EVERLASTING) {
6165 		if (!(p2_ptr->mode & MODE_EVERLASTING)) {
6166 			return "non-everlasting";
6167 		}
6168 	} else if (p2_ptr->mode & MODE_PVP) {
6169 		return "pvp";
6170 	} else if (p2_ptr->mode & MODE_EVERLASTING) {
6171 		return "everlasting";
6172 	}
6173 	return NULL; /* means "is compatible" */
6174 }
6175 
6176 /* compare object and player mode compatibility - C. Blue
6177    Note: returns NULL if compatible. */
6178 cptr compat_pomode(int Ind, object_type *o_ptr) {
6179 	player_type *p_ptr = Players[Ind];
6180 
6181 #ifdef IRONDEEPDIVE_ALLOW_INCOMPAT
6182 	/* EXPERIMENTAL */
6183 	if (in_irondeepdive(&p_ptr->wpos) &&
6184 	    in_irondeepdive(&o_ptr->wpos))
6185 		return NULL;
6186 #endif
6187 
6188 #ifdef ALLOW_NR_CROSS_ITEMS
6189 	if (o_ptr->NR_tradable &&
6190 	    in_netherrealm(&p_ptr->wpos))
6191 		return NULL;
6192 #endif
6193 
6194 	if (!o_ptr->owner || is_admin(p_ptr)) return NULL; /* always compatible */
6195 	if (p_ptr->mode & MODE_PVP) {
6196 		if (!(o_ptr->mode & MODE_PVP)) {
6197 			if (o_ptr->mode & MODE_EVERLASTING) {
6198 				if (!(cfg.charmode_trading_restrictions & 2)) {
6199 					return "non-pvp";
6200 				}
6201 			} else if (!(cfg.charmode_trading_restrictions & 4)) {
6202 				return "non-pvp";
6203 			}
6204 		}
6205 	} else if (p_ptr->mode & MODE_EVERLASTING) {
6206 		if (o_ptr->mode & MODE_PVP) {
6207 			return "pvp";
6208 		} else if (!(o_ptr->mode & MODE_EVERLASTING)) {
6209 			if (!(cfg.charmode_trading_restrictions & 1)) return "non-everlasting";
6210 		}
6211 	} else if (o_ptr->mode & MODE_PVP) {
6212 		return "pvp";
6213 	} else if (o_ptr->mode & MODE_EVERLASTING) {
6214 		return "everlasting";
6215 	}
6216 	return NULL; /* means "is compatible" */
6217 }
6218 
6219 /* compare two objects' mode compatibility for stacking/absorbing - C. Blue
6220    Note: returns NULL if compatible. */
6221 cptr compat_omode(object_type *o1_ptr, object_type *o2_ptr) {
6222 #ifdef IRONDEEPDIVE_ALLOW_INCOMPAT
6223 	/* EXPERIMENTAL */
6224 	if ((in_irondeepdive(&o1_ptr->wpos)) &&
6225 	    in_irondeepdive(&o2_ptr->wpos))
6226 		return NULL;
6227 #endif
6228 
6229 	/* ownership given for both items? */
6230 	if (!o1_ptr->owner) {
6231 		if (!o2_ptr->owner) return NULL; /* always compatible */
6232 		else return "owned";
6233 	} else if (!o2_ptr->owner) return "owned";
6234 
6235 	/* both are owned. so compare actual modes */
6236 	if (o1_ptr->mode & MODE_PVP) {
6237 		if (!(o2_ptr->mode & MODE_PVP)) {
6238 			return "non-pvp";
6239 		}
6240 	} else if (o1_ptr->mode & MODE_EVERLASTING) {
6241 		if (!(o2_ptr->mode & MODE_EVERLASTING)) {
6242 			return "non-everlasting";
6243 		}
6244 	} else if (o2_ptr->mode & MODE_PVP) {
6245 		return "pvp";
6246 	} else if (o2_ptr->mode & MODE_EVERLASTING) {
6247 		return "everlasting";
6248 	}
6249 	return NULL; /* means "is compatible" */
6250 }
6251 
6252 cptr compat_mode(byte mode1, byte mode2) {
6253 	if (mode1 & MODE_PVP) {
6254 		if (!(mode2 & MODE_PVP)) {
6255 			return "non-pvp";
6256 		}
6257 	} else if (mode1 & MODE_EVERLASTING) {
6258 		if (!(mode2 & MODE_EVERLASTING)) {
6259 			return "non-everlasting";
6260 		}
6261 	} else if (mode2 & MODE_PVP) {
6262 		return "pvp";
6263 	} else if (mode2 & MODE_EVERLASTING) {
6264 		return "everlasting";
6265 	}
6266 	return NULL; /* means "is compatible" */
6267 }
6268 
6269 /* cut out pseudo-id inscriptions from a string (a note ie inscription usually),
6270    save resulting string in s2,
6271    save highest found pseudo-id string in psid. - C. Blue */
6272 void note_crop_pseudoid(char *s2, char *psid, cptr s) {
6273 	char *p, s0[80];
6274 	int id = -1; /* assume no pseudo-id inscription */
6275 
6276 	if (s == NULL) return;
6277 	strcpy(s2, s);
6278 
6279 	while (TRUE) {
6280 		strcpy(s0, s2);
6281 		strcpy(s2, "");
6282 
6283 		if ((p = strstr(s0, "uncursed-"))) {
6284 			strncpy(s2, s0, p - s0);
6285 			s2[p - s0] = '\0';
6286 			strcat(s2, p + 9);
6287 			if (id < 0) id = 0;
6288 		} else if ((p = strstr(s0, "uncursed"))) {
6289 			strncpy(s2, s0, p - s0);
6290 			s2[p - s0] = '\0';
6291 			strcat(s2, p + 8);
6292 			if (id < 0) id = 0;
6293 		} else if ((p = strstr(s0, "terrible-"))) {
6294 			strncpy(s2, s0, p - s0);
6295 			s2[p - s0] = '\0';
6296 			strcat(s2, p + 9);
6297 			if (id < 1) id = 1;
6298 		} else if ((p = strstr(s0, "terrible"))) {
6299 			strncpy(s2, s0, p - s0);
6300 			s2[p - s0] = '\0';
6301 			strcat(s2, p + 8);
6302 			if (id < 1) id = 1;
6303 		} else if ((p = strstr(s0, "cursed-"))) {
6304 			strncpy(s2, s0, p - s0);
6305 			s2[p - s0] = '\0';
6306 			strcat(s2, p + 7);
6307 			if (id < 2) id = 2;
6308 		} else if ((p = strstr(s0, "cursed"))) {
6309 			strncpy(s2, s0, p - s0);
6310 			s2[p - s0] = '\0';
6311 			strcat(s2, p + 6);
6312 			if (id < 2) id = 2;
6313 		} else if ((p = strstr(s0, "worthless-"))) {
6314 			strncpy(s2, s0, p - s0);
6315 			s2[p - s0] = '\0';
6316 			strcat(s2, p + 10);
6317 			if (id < 4) id = 4;
6318 		} else if ((p = strstr(s0, "worthless"))) {
6319 			strncpy(s2, s0, p - s0);
6320 			s2[p - s0] = '\0';
6321 			strcat(s2, p + 9);
6322 			if (id < 4) id = 4;
6323 		} else if ((p = strstr(s0, "broken-"))) {
6324 			strncpy(s2, s0, p - s0);
6325 			s2[p - s0] = '\0';
6326 			strcat(s2, p + 7);
6327 			if (id < 5) id = 5;
6328 		} else if ((p = strstr(s0, "broken"))) {
6329 			strncpy(s2, s0, p - s0);
6330 			s2[p - s0] = '\0';
6331 			strcat(s2, p + 6);
6332 			if (id < 5) id = 5;
6333 		} else if ((p = strstr(s0, "average-"))) {
6334 			strncpy(s2, s0, p - s0);
6335 			s2[p - s0] = '\0';
6336 			strcat(s2, p + 8);
6337 			if (id < 6) id = 6;
6338 		} else if ((p = strstr(s0, "average"))) {
6339 			strncpy(s2, s0, p - s0);
6340 			s2[p - s0] = '\0';
6341 			strcat(s2, p + 7);
6342 			if (id < 6) id = 6;
6343 		} else if ((p = strstr(s0, "good-"))) {
6344 			strncpy(s2, s0, p - s0);
6345 			s2[p - s0] = '\0';
6346 			strcat(s2, p + 5);
6347 			if (id < 7) id = 7;
6348 		} else if ((p = strstr(s0, "good"))) {
6349 			strncpy(s2, s0, p - s0);
6350 			s2[p - s0] = '\0';
6351 			strcat(s2, p + 4);
6352 			if (id < 7) id = 7;
6353 		} else if ((p = strstr(s0, "excellent-"))) {
6354 			strncpy(s2, s0, p - s0);
6355 			s2[p - s0] = '\0';
6356 			strcat(s2, p + 10);
6357 			if (id < 8) id = 8;
6358 		} else if ((p = strstr(s0, "excellent"))) {
6359 			strncpy(s2, s0, p - s0);
6360 			s2[p - s0] = '\0';
6361 			strcat(s2, p + 9);
6362 			if (id < 8) id = 8;
6363 		} else if ((p = strstr(s0, "special-"))) {
6364 			strncpy(s2, s0, p - s0);
6365 			s2[p - s0] = '\0';
6366 			strcat(s2, p + 8);
6367 			if (id < 9) id = 9;
6368 		} else if ((p = strstr(s0, "special"))) {
6369 			strncpy(s2, s0, p - s0);
6370 			s2[p - s0] = '\0';
6371 			strcat(s2, p + 7);
6372 			if (id < 9) id = 9;
6373 		} else {
6374 			/* no further replacements to make */
6375 			break;
6376 		}
6377 	}
6378 
6379 	strcpy(s2, s0);
6380 
6381 	switch (id) {
6382 	case 0: strcpy(psid, "uncursed"); break;
6383 	case 1: strcpy(psid, "terrible"); break;
6384 	case 2: strcpy(psid, "cursed"); break;
6385 	case 3: strcpy(psid, "bad"); break;
6386 	case 4: strcpy(psid, "worthless"); break;
6387 	case 5: strcpy(psid, "broken"); break;
6388 	case 6: strcpy(psid, "average"); break;
6389 	case 7: strcpy(psid, "good"); break;
6390 	case 8: strcpy(psid, "excellent"); break;
6391 	case 9: strcpy(psid, "special"); break;
6392 	default:
6393 		strcpy(psid, "");
6394 	}
6395 }
6396 
6397 /* For when an item re-curses itself on equipping:
6398    Remove any 'uncursed' part in its inscription. */
6399 void note_toggle_cursed(object_type *o_ptr, bool cursed) {
6400 	char *cn, note2[MAX_CHARS_WIDE], *cnp;
6401 
6402 	if (!o_ptr->note) {
6403 		if (cursed) o_ptr->note = quark_add("cursed");
6404 		else o_ptr->note = quark_add("uncursed");
6405 		return;
6406 	}
6407 
6408 	strcpy(note2, quark_str(o_ptr->note));
6409 
6410 	/* remove old 'uncursed' inscriptions */
6411 	if ((cn = strstr(note2, "uncursed"))) {
6412 		while (note2[0] && (cn = strstr(note2, "uncursed"))) {
6413 			cnp = cn + 7;
6414 			if (cn > note2 && //the 'uncursed' does not start on the first character of note2?
6415 			    *(cn - 1) == '-') cn--; /* cut out leading '-' delimiter before "uncursed" */
6416 
6417 			/* strip formerly trailing delimiter if it'd end up on first position in the new inscription */
6418 			if (cn == note2 && *(cnp + 1) == '-') cnp++;
6419 
6420 			do {
6421 				cnp++;
6422 				*cn = *cnp;
6423 				cn++;
6424 			} while (*cnp);
6425 		}
6426 	}
6427 
6428 	/* remove old 'cursed' inscription */
6429 	if ((cn = strstr(note2, "cursed"))) {
6430 		while (note2[0] && (cn = strstr(note2, "cursed"))) {
6431 			cnp = cn + 5;
6432 			if (cn > note2 && //the 'cursed' does not start on the first character of note2?
6433 			    *(cn - 1) == '-') cn--; /* cut out leading '-' delimiter before "cursed" */
6434 
6435 			/* strip formerly trailing delimiter if it'd end up on first position in the new inscription */
6436 			if (cn == note2 && *(cnp + 1) == '-') cnp++;
6437 
6438 			do {
6439 				cnp++;
6440 				*cn = *cnp;
6441 				cn++;
6442 			} while (*cnp);
6443 		}
6444 	}
6445 
6446 	/* append the new cursed-state indicator */
6447 	if (note2[0]) strcat(note2, "-");
6448 	if (cursed) {
6449 		strcat(note2, "cursed");
6450 		o_ptr->note = quark_add(note2);
6451 	} else {
6452 		strcat(note2, "uncursed");
6453 		o_ptr->note = quark_add(note2);
6454 	}
6455 }
6456 
6457 /* Convert certain characters into HTML entities
6458 * These characters are <, >, & and "
6459 * NOTE: The returned string is dynamically allocated
6460 */
6461 char *html_escape(const char *str) {
6462 	int i, new_len = 0;
6463 	const char *tmp;
6464 	char *result;
6465 
6466 	if (!str) {
6467 		/* Return an empty string */
6468 		result = malloc(1);
6469 		*result = '\0';
6470 		return result;
6471 	}
6472 
6473 	/* Calculate the resulting length */
6474 	tmp = str;
6475 	while (*tmp) {
6476 		switch (*tmp) {
6477 			case '<': case '>':
6478 				new_len += 3;
6479 				break;
6480 			case '&':
6481 				new_len += 4;
6482 				break;
6483 			case '"':
6484 				new_len += 5;
6485 				break;
6486 		}
6487 		new_len++;
6488 		tmp++;
6489 	}
6490 
6491 	result = malloc(new_len + 1);
6492 	i = 0;
6493 	tmp = str;
6494 
6495 	while (*tmp) {
6496 		switch (*tmp) {
6497 			case '<':
6498 				result[i++] = '&';
6499 				result[i++] = 'l';
6500 				result[i++] = 't';
6501 				result[i++] = ';';
6502 				break;
6503 			case '>':
6504 				result[i++] = '&';
6505 				result[i++] = 'g';
6506 				result[i++] = 't';
6507 				result[i++] = ';';
6508 				break;
6509 			case '&':
6510 				result[i++] = '&';
6511 				result[i++] = 'a';
6512 				result[i++] = 'm';
6513 				result[i++] = 'p';
6514 				result[i++] = ';';
6515 				break;
6516 			case '"':
6517 				result[i++] = '&';
6518 				result[i++] = 'q';
6519 				result[i++] = 'u';
6520 				result[i++] = 'o';
6521 				result[i++] = 't';
6522 				result[i++] = ';';
6523 				break;
6524 			default:
6525 				result[i++] = *tmp;
6526 		}
6527 		tmp++;
6528 	}
6529 
6530 	/* Terminate */
6531 	result[new_len] = '\0';
6532 
6533 	return result;
6534 }
6535 
6536 /* level generation benchmark */
6537 void do_benchmark(int Ind) {
6538 	int i, n = 100;
6539 	player_type *p_ptr = Players[Ind];
6540 	struct timeval begin, end;
6541 	int sec, usec;
6542 
6543 #ifndef WINDOWS
6544 	block_timer();
6545 #endif
6546 
6547 	/* Use gettimeofday to get precise time */
6548 	gettimeofday(&begin, NULL);
6549 
6550 	for (i = 0; i < n; i++) {
6551 		generate_cave(&p_ptr->wpos, p_ptr);
6552 	}
6553 
6554 	gettimeofday(&end, NULL);
6555 
6556 #ifndef WINDOWS
6557 	allow_timer();
6558 #endif
6559 
6560 	/* Calculate the time */
6561 	sec = end.tv_sec - begin.tv_sec;
6562 	usec = end.tv_usec - begin.tv_usec;
6563 	if (usec < 0) {
6564 		usec += 1000000;
6565 		sec--;
6566 	}
6567 
6568 	/* Print the result */
6569 	msg_format(Ind, "Generated %d levels in %d.%06d seconds.", n, sec, usec);
6570 
6571 	/* Log it too */
6572 	s_printf("BENCHMARK: Generated %d levels in %d.%06d seconds.\n", n, sec, usec);
6573 }
6574 
6575 /*
6576  * Get the difference between two timevals as a string.
6577  */
6578 cptr timediff(struct timeval *begin, struct timeval *end) {
6579 	static char buf[20];
6580 	int sec, msec, usec;
6581 
6582 	sec = end->tv_sec - begin->tv_sec;
6583 	usec = end->tv_usec - begin->tv_usec;
6584 
6585 	if (usec < 0) {
6586 		usec += 1000000;
6587 		sec--;
6588 	}
6589 
6590 	msec = usec / 1000;
6591 
6592 	snprintf(buf, 20, "%d.%03d", sec, msec);
6593 	return buf;
6594 }
6595 
6596 /* Strip special chars off player input 's', to prevent any problems/colours */
6597 void strip_control_codes(char *ss, char *s) {
6598 	char *sp = s, *ssp = ss;
6599 	bool skip = FALSE;
6600 	while(TRUE) {
6601 		if (*sp == '\0') { /* end of string has top priority */
6602 			*ssp = '\0';
6603 			break;
6604 		} else if (skip) skip = FALSE; /* a 'code parameter', needs to be stripped too */
6605 		else if ((*sp >= 32) && (*sp <= 127)) { /* normal characters */
6606 			*ssp = *sp;
6607 			ssp++;
6608 		} else if (*sp == '\377') {
6609 			if (*(sp + 1) == '\377') { /* make two '{' become one real '{' */
6610 				*ssp = '{';
6611 				ssp++;
6612 			}
6613 			skip = TRUE; /* strip colour codes */
6614 		}
6615 		sp++;
6616 	}
6617 }
6618 
6619 /* return a string displaying the flag state - C. Blue */
6620 cptr flags_str(u32b flags) {
6621 #if 0 /* disfunctional atm */
6622 	char *fsp = malloc(40 * sizeof(char));
6623 	int b;
6624 
6625 	for (b = 0; b < 32; b++) {
6626 		*fsp++ = (flags & (1 << b)) ? '1' : '0';
6627 		if (b % 4 == 3) *fsp++ = ' ';
6628 	}
6629 	*fsp++ = '\0';
6630 
6631 	return (fsp - 40);
6632 #else
6633 	return (NULL);
6634 #endif
6635 }
6636 
6637 /* get player's racial attribute */
6638 cptr get_prace(player_type *p_ptr) {
6639 #ifdef ENABLE_MAIA
6640 	if (p_ptr->prace == RACE_MAIA && p_ptr->ptrait) {
6641 		if (p_ptr->ptrait == TRAIT_ENLIGHTENED)
6642 			return "Enlightened";
6643 		else if (p_ptr->ptrait == TRAIT_CORRUPTED)
6644 			return "Corrupted";
6645 		else
6646 			return special_prace_lookup[p_ptr->prace];
6647 	} else
6648 #endif
6649 	return special_prace_lookup[p_ptr->prace];
6650 }
6651 
6652 /* get player's title */
6653 cptr get_ptitle(player_type *p_ptr, bool short_form) {
6654 	if (p_ptr->lev < 60) return player_title[p_ptr->pclass][((p_ptr->lev / 5) < 10)? (p_ptr->lev / 5) : 10][(short_form ? 3 : 1) - p_ptr->male];
6655 	return player_title_special[p_ptr->pclass][(p_ptr->lev < PY_MAX_PLAYER_LEVEL) ? (p_ptr->lev - 60) / 10 : 4][(short_form ? 3 : 1) - p_ptr->male];
6656 }
6657 
6658 #ifdef DUNGEON_VISIT_BONUS
6659 void reindex_dungeons() {
6660 # ifdef DUNGEON_VISIT_BONUS_DEPTHRANGE
6661 	int i;
6662 # endif
6663 	int x, y;
6664     wilderness_type *w_ptr;
6665     struct dungeon_type *d_ptr;
6666 
6667     dungeon_id_max = 0;
6668 
6669     for (y = 0; y < MAX_WILD_Y; y++) {
6670         for (x = 0; x < MAX_WILD_X; x++) {
6671             w_ptr = &wild_info[y][x];
6672             if (w_ptr->flags & WILD_F_UP) {
6673             	d_ptr = w_ptr->tower;
6674 	            d_ptr->id = ++dungeon_id_max;
6675 
6676     	        dungeon_visit_frequency[dungeon_id_max] = 0;
6677 				dungeon_x[dungeon_id_max] = x;
6678 				dungeon_y[dungeon_id_max] = y;
6679 				dungeon_tower[dungeon_id_max] = TRUE;
6680 
6681 				/* Initialize all dungeons at 'low rest bonus' */
6682 				set_dungeon_bonus(dungeon_id_max, TRUE);
6683 
6684 				s_printf("  indexed tower   at %d,%d: id = %d\n", x, y, dungeon_id_max);
6685 			}
6686             if (w_ptr->flags & WILD_F_DOWN) {
6687             	d_ptr = w_ptr->dungeon;
6688 	            d_ptr->id = ++dungeon_id_max;
6689 
6690     	        dungeon_visit_frequency[dungeon_id_max] = 0;
6691 				dungeon_x[dungeon_id_max] = x;
6692 				dungeon_y[dungeon_id_max] = y;
6693 				dungeon_tower[dungeon_id_max] = FALSE;
6694 
6695 				/* Initialize all dungeons at 'low rest bonus' */
6696 				set_dungeon_bonus(dungeon_id_max, TRUE);
6697 
6698 				s_printf("  indexed dungeon at %d,%d: id = %d\n", x, y, dungeon_id_max);
6699 			}
6700         }
6701     }
6702 
6703 # ifdef DUNGEON_VISIT_BONUS_DEPTHRANGE
6704     for (i = 0; i < 20; i++)
6705     	depthrange_visited[i] = 0;
6706 # endif
6707 
6708     s_printf("Reindexed %d dungeons/towers.\n", dungeon_id_max);
6709 }
6710 
6711 void set_dungeon_bonus(int id, bool reset) {
6712 	if (reset) {
6713 		/* Initialize dungeon at 'low rest bonus' */
6714 		dungeon_visit_frequency[id] = ((VISIT_TIME_CAP * 17) / 20) - 1; /* somewhat below the threshold */
6715 		dungeon_bonus[id] = 1;
6716 		return;
6717 	}
6718 
6719 	if (dungeon_visit_frequency[id] < VISIT_TIME_CAP / 10)
6720 		dungeon_bonus[id] = 3;
6721 	else if (dungeon_visit_frequency[id] < VISIT_TIME_CAP / 2)
6722 		dungeon_bonus[id] = 2;
6723 	else if (dungeon_visit_frequency[id] < (VISIT_TIME_CAP * 19) / 20)
6724 		dungeon_bonus[id] = 1;
6725 	else
6726 		dungeon_bonus[id] = 0;
6727 }
6728 #endif
6729 
6730 /*
6731  * Shuffle an array of integers using the Fisher-Yates algorithm.
6732  */
6733 void intshuffle(int *array, int size) {
6734 	int i, j, tmp;
6735 
6736 	for (i = size - 1; i > 0; i--) {
6737 		j = rand_int(i + 1);
6738 		tmp = array[i];
6739 		array[i] = array[j];
6740 		array[j] = tmp;
6741 	}
6742 }
6743 
6744 /* for all the dungeons/towers that are special, yet use the type 0 dungeon template - C. Blue
6745    todo: actually create own types for these. would also make DF3_JAIL_DUNGEON obsolete.
6746    If 'extra' is set, special info is added: Town name, to keep the two Angbands apart. */
6747 char *get_dun_name(int x, int y, bool tower, dungeon_type *d_ptr, int type, bool extra) {
6748 	static char *jail = "Jail Dungeon";
6749 	static char *pvp_arena = "PvP Arena";
6750 	static char *highlander = "Highlands";
6751 	static char *irondeepdive = "Ironman Deep Dive Challenge";
6752 
6753 	/* hacks for 'extra' info, ugh */
6754 	static char *angband_lothlorien = "Angband (Lothlorien)";
6755 	static char *angband_khazaddum = "Angband (Khazad-dum)";
6756 
6757 	if (d_ptr) type = d_ptr->type;
6758 
6759 	/* normal dungeon */
6760 	if (type) {
6761 		/* hack for the two Angbands - so much hardcoding.... */
6762 		if (extra && !strcmp(d_name + d_info[type].name, "Angband")) {
6763 			if (town[TIDX_LORIEN].x == x && town[TIDX_LORIEN].y == y)
6764 				return angband_lothlorien;
6765 			else return angband_khazaddum;
6766 		} else /* default */
6767 			return (d_name + d_info[type].name);
6768 	}
6769 
6770 	/* special type 0 (yet) dungeons */
6771 	if (x == WPOS_PVPARENA_X &&
6772 	    y == WPOS_PVPARENA_Y &&
6773 	    tower == (WPOS_PVPARENA_Z > 0))
6774 		return pvp_arena;
6775 
6776 	if (x == WPOS_HIGHLANDER_DUN_X &&
6777 	    y == WPOS_HIGHLANDER_DUN_Y &&
6778 	    tower == (WPOS_HIGHLANDER_DUN_Z > 0))
6779 		return highlander;
6780 
6781 	if (x == WPOS_IRONDEEPDIVE_X &&
6782 	    y == WPOS_IRONDEEPDIVE_Y &&
6783 	    tower == (WPOS_IRONDEEPDIVE_Z > 0))
6784 		return irondeepdive;
6785 
6786 	if (d_ptr && (
6787 #if 0 /* obsolete fortunately (/fixjaildun) */
6788 	    /* ughhh */
6789 	    (d_ptr->baselevel == 30 && d_ptr->maxdepth == 30 &&
6790 	    (d_ptr->flags1 & DF1_FORGET) &&
6791 	    (d_ptr->flags2 & DF2_IRON))
6792 	    ||
6793 #endif
6794 	    /* yay */
6795 	    (d_ptr->flags3 & DF3_JAIL_DUNGEON) ))
6796 		return jail;
6797 
6798 	/* really just "Wilderness" */
6799 	return (d_name + d_info[type].name);
6800 }
6801 
6802 /* Add gold to the player's gold, for easier handling. - C. Blue
6803    Returns FALSE if we already own 2E9 Au. */
6804 bool gain_au(int Ind, u32b amt, bool quiet, bool exempt) {
6805 	player_type *p_ptr = Players[Ind];
6806 
6807 	/* hack: prevent s32b overflow */
6808         if (2000000000 - amt < p_ptr->au) {
6809                 if (!quiet) msg_format(Ind, "\377yYou cannot carry more than 2 billion worth of gold!");
6810                 return FALSE;
6811         } else {
6812 #ifdef EVENT_TOWNIE_GOLD_LIMIT
6813 		if ((p_ptr->mode & MODE_DED_IDDC) && !in_irondeepdive(&p_ptr->wpos)
6814     	    	    && p_ptr->gold_picked_up == EVENT_TOWNIE_GOLD_LIMIT) {
6815 			msg_print(Ind, "\377yYou cannot collect any more cash or your life would be forfeit.");
6816 			return FALSE;
6817 		}
6818 #endif
6819 
6820 	        /* Collect the gold */
6821                 p_ptr->au += amt;
6822 	        p_ptr->redraw |= (PR_GOLD);
6823         }
6824 
6825 	if (exempt) return TRUE;
6826 #ifdef EVENT_TOWNIE_GOLD_LIMIT
6827 	/* if EVENT_TOWNIE_GOLD_LIMIT is 0 then nothing happens */
6828         if (p_ptr->gold_picked_up <= EVENT_TOWNIE_GOLD_LIMIT) {
6829                 p_ptr->gold_picked_up += (amt > EVENT_TOWNIE_GOLD_LIMIT) ? EVENT_TOWNIE_GOLD_LIMIT : amt;
6830                 if (p_ptr->gold_picked_up > EVENT_TOWNIE_GOLD_LIMIT
6831             	    && !p_ptr->max_exp) {
6832 			msg_print(Ind, "You gain a tiny bit of experience from collecting cash.");
6833             		gain_exp(Ind, 1);
6834             	}
6835         }
6836 #endif
6837         return TRUE;
6838 }
6839 
6840 /* backup all house prices and contents for all players to lib/save/estate/ */
6841 #define ESTATE_BACKUP_VERSION "v1"
6842 bool backup_estate(void) {
6843 	FILE *fp;
6844 	char buf[MAX_PATH_LENGTH], buf2[MAX_PATH_LENGTH], savefile[CHARACTERNAME_LEN], c;
6845 	cptr name;
6846 	int i, j, k;
6847 	int sy, sx, ey,ex , x, y;
6848         cave_type **zcave, *c_ptr;
6849         bool newly_created, allocated;
6850 	u32b au;
6851 	house_type *h_ptr;
6852 	struct dna_type *dna;
6853 	struct worldpos *wpos;
6854 	object_type *o_ptr;
6855 
6856 	s_printf("Backing up all real estate...\n");
6857 
6858 	/* create folder lib/save/estate if not existing */
6859         path_build(buf2, MAX_PATH_LENGTH, ANGBAND_DIR_SAVE, "estate");
6860 
6861 	/* scan all houses */
6862         for (i = 0; i < num_houses; i++) {
6863                 h_ptr = &houses[i];
6864                 if (!h_ptr->dna->owner) continue;
6865 
6866                 wpos = &h_ptr->wpos;
6867                 dna = h_ptr->dna;
6868 		/* assume worst possible charisma (3) */
6869 		au = dna->price / 100 * adj_chr_gold[0];
6870 		if (au < 100) au = 100;
6871 		//h_ptr->dy,dx
6872 
6873 		/* WARNING: For now unable to save guild halls */
6874 		if (h_ptr->dna->owner_type != OT_PLAYER) continue;
6875 
6876 		name = lookup_player_name(h_ptr->dna->owner);
6877 		if (!name) {
6878 			s_printf("  warning: house %d's owner %d doesn't have a name.\n", i, h_ptr->dna->owner);
6879 			continue;
6880 		}
6881 
6882 		/* create backup file if required, or append to it */
6883 		/* create actual filename from character name (same as used for sf_delete or process_player_name) */
6884 		k = 0;
6885 	        for (j = 0; name[j]; j++) {
6886 	                c = name[j];
6887         	        /* Accept some letters */
6888 	                if (isalpha(c) || isdigit(c)) savefile[k++] = c;
6889     	        	/* Convert space, dot, and underscore to underscore */
6890 	                else if (strchr(SF_BAD_CHARS, c)) savefile[k++] = '_';
6891     		}
6892     		savefile[k] = '\0';
6893 		/* build path name and try to create/append to player's backup file */
6894     		path_build(buf, MAX_PATH_LENGTH, buf2, savefile);
6895     		if ((fp = fopen(buf, "rb")) == NULL)
6896     			newly_created = TRUE;
6897     		else {
6898     			newly_created = FALSE;
6899     			fclose(fp);
6900     		}
6901     		if ((fp = fopen(buf, "ab")) == NULL) {
6902 	    		s_printf("  error: cannot open file '%s'.\nfailed.\n", buf);
6903     			return FALSE;
6904     		} else if (newly_created) {
6905     			newly_created = FALSE;
6906     			/* begin with a version tag */
6907     			fprintf(fp, "%s\n", ESTATE_BACKUP_VERSION);
6908 
6909 #if 0 /* guild info is no longer tied to map reset! */
6910     			/* add 2M Au if he's a guild master, since guilds will be erased if the server
6911     			   savefile gets deleted (which is the sole purpose of calling this function..) */
6912 			for (j = 0; j < MAX_GUILDS; j++)
6913 				if (guilds[j].master == h_ptr->dna->owner) {
6914 					fprintf(fp, "AU:%d\n", GUILD_PRICE);
6915 					s_printf("  guild master: '%s'.\n", name);
6916 					break;
6917 				}
6918 #endif
6919     		}
6920 
6921 		/* add house price to his backup file */
6922 		fprintf(fp, "AU:%d\n", au);
6923 
6924 		/* scan house contents and add them to his backup file */
6925 		/* traditional house? */
6926 		if (h_ptr->flags & HF_TRAD) {
6927 			for (j = 0; j < h_ptr->stock_num; j++) {
6928                     		o_ptr = &h_ptr->stock[j];
6929                     		/* add object to backup file */
6930                     		fprintf(fp, "OB:");
6931 				(void)fwrite(o_ptr, sizeof(object_type), 1, fp);
6932 				/* store inscription too! */
6933 				if (o_ptr->note) {
6934     					fprintf(fp, "%d\n", (int)strlen(quark_str(o_ptr->note)));
6935     					(void)fwrite(quark_str(o_ptr->note), sizeof(char), strlen(quark_str(o_ptr->note)), fp);
6936     				} else
6937     					fprintf(fp, "%d\n", -1);
6938                         }
6939                 }
6940                 /* mang-style house? */
6941                 else {
6942         		/* allocate sector of the house to access objects */
6943 			if (!(zcave = getcave(wpos))) {
6944 				alloc_dungeon_level(wpos);
6945 				wilderness_gen(wpos);
6946 				if (!(zcave = getcave(wpos))) {
6947 					s_printf("  error: cannot getcave(%d,%d,%d).\nfailed.\n", wpos->wx, wpos->wy, wpos->wz);
6948 					fclose(fp);
6949 					return FALSE;
6950 				}
6951 				allocated = TRUE;
6952 			} else allocated = FALSE;
6953 
6954 		        if (h_ptr->flags & HF_RECT) {
6955 		                sy = h_ptr->y + 1;
6956 		                sx = h_ptr->x + 1;
6957 		                ey = h_ptr->y + h_ptr->coords.rect.height - 1;
6958 		                ex = h_ptr->x + h_ptr->coords.rect.width - 1;
6959 		                for (y = sy; y < ey; y++) {
6960 		                        for (x = sx; x < ex; x++) {
6961     						c_ptr = &zcave[y][x];
6962 		                                if (c_ptr->o_idx) {
6963 		                        		o_ptr = &o_list[c_ptr->o_idx];
6964 		                        		/* add object to backup file */
6965 		                        		fprintf(fp, "OB:");
6966 			    				(void)fwrite(o_ptr, sizeof(object_type), 1, fp);
6967 			    				/* store inscription too! */
6968 			    				if (o_ptr->note) {
6969 			    					fprintf(fp, "%d\n", (int)strlen(quark_str(o_ptr->note)));
6970 			    					(void)fwrite(quark_str(o_ptr->note), sizeof(char), strlen(quark_str(o_ptr->note)), fp);
6971 			    				} else
6972 			    					fprintf(fp, "%d\n", -1);
6973 		                                }
6974 					}
6975 				}
6976 			} else {
6977 				/* Polygonal house */
6978 				//fill_house(h_ptr, FILL_CLEAR, NULL);
6979 			}
6980 
6981 			if (allocated) dealloc_dungeon_level(wpos);
6982                 }
6983 
6984 		/* done with this particular house */
6985 		fclose(fp);
6986 	}
6987 
6988 	s_printf("done.\n");
6989 	return TRUE;
6990 }
6991 
6992 /* helper function for restore_estate():
6993    copy all remaining data from our save file to the temporary file
6994    and turn the temporary file into the new save file. */
6995 void relay_estate(char *buf, char *buf2, FILE *fp, FILE *fp_tmp) {
6996 	int c;
6997 
6998 	/* relay the remaining data, if any */
6999 	while ((c = fgetc(fp)) != EOF) fputc(c, fp_tmp);
7000 
7001 	/* done */
7002 	fclose(fp);
7003 	fclose(fp_tmp);
7004 
7005 	/* erase old save file, make temporary file the new save file */
7006 	remove(buf);
7007 	rename(buf2, buf);
7008 }
7009 
7010 /* get back the backed-up real estate from files */
7011 void restore_estate(int Ind) {
7012 	player_type *p_ptr = Players[Ind];
7013 	FILE *fp, *fp_tmp;
7014 	char buf[MAX_PATH_LENGTH], buf2[MAX_PATH_LENGTH], version[MAX_CHARS];
7015 	char data[4], data_note[MSG_LEN];//MAX_OLEN?
7016 	char o_name[MSG_LEN], *rc;
7017 	unsigned long au;
7018 	int data_len, r;
7019 	object_type forge, *o_ptr = &forge;
7020 	bool gained_anything = FALSE;
7021 
7022 	s_printf("Restoring real estate for %s...\n", p_ptr->name);
7023 
7024 	/* create folder lib/save/estate if not existing */
7025         path_build(buf2, MAX_PATH_LENGTH, ANGBAND_DIR_SAVE, "estate");
7026 
7027 	/* build path name and try to create/append to player's backup file */
7028 	path_build(buf, MAX_PATH_LENGTH, buf2, p_ptr->basename);
7029 	if ((fp = fopen(buf, "rb")) == NULL) {
7030 		s_printf("  error: No file '%s' exists to request from.\nfailed.\n", buf);
7031 		msg_print(Ind, "\377yNo goods or money stored to request.");
7032 		return;
7033 	}
7034 	s_printf("  opened file '%s'.\n", buf);
7035 
7036 	/* Try to read version string */
7037 	if (fgets(version, MAX_CHARS, fp) == NULL) {
7038 		s_printf("  error: File is empty.\nfailed.\n");
7039 		msg_print(Ind, "\377oAn error occurred, please contact an administrator.");
7040 		fclose(fp);
7041 		return;
7042 	}
7043 	version[strlen(version) - 1] = '\0';
7044 	s_printf("  reading a version '%s' estate file.\n", version);
7045 
7046 	/* open temporary file for writing the stuff the player left over */
7047 	strcpy(buf2, buf);
7048 	strcat(buf2, ".$$$");
7049 	if ((fp_tmp = fopen(buf2, "wb")) == NULL) {
7050     		s_printf("  error: cannot open temporary file '%s'.\nfailed.\n", buf2);
7051     		msg_print(Ind, "\377oAn error occurred, please contact an administrator.");
7052     		fclose(fp);
7053 		return;
7054 	}
7055 
7056 	/* relay to temporary file */
7057 	fprintf(fp_tmp, "%s\n", version);
7058 
7059 	msg_print(Ind, "\377yChecking for money and items from estate stored for you...");
7060 	while (TRUE) {
7061 		/* scan file for either house price (AU:) or object (OB:) */
7062 		if (!fread(data, sizeof(char), 3, fp)) {
7063 			if (!gained_anything) {
7064 				s_printf("  nothing to request.\ndone.\n");
7065 				msg_print(Ind, "\377yNo goods or money left to request.");
7066 			} else {
7067 				s_printf("done.\n");
7068 				msg_print(Ind, "\377yYou received everything that was stored for you.");
7069 			}
7070 			relay_estate(buf, buf2, fp, fp_tmp);
7071 			return;
7072 		}
7073 		data[3] = '\0';
7074 
7075 		/* get house price from backup file */
7076 		if (!strcmp(data, "AU:")) {
7077 			au = 0;
7078 			r = fscanf(fp, "%lu\n", &au);
7079 			if (r == EOF || r == 0 || !au) {
7080 				s_printf("  error: Corrupted AU: line.\n");
7081 				msg_print(Ind, "\377oAn error occurred, please contact an administrator.");
7082 				relay_estate(buf, buf2, fp, fp_tmp);
7083 				return;
7084 			}
7085 
7086 			/* give gold to player if it doesn't overflow,
7087 			   otherwise relay rest to temporary file, swap and exit */
7088 			if (!gain_au(Ind, au, FALSE, FALSE)) {
7089 				msg_print(Ind, "\377yDrop/deposite some gold to be able to receive more gold.");
7090 
7091 				/* write failed gold gain back into new buffer file */
7092 				fprintf(fp_tmp, "AU:%lu\n", au);
7093 
7094 				relay_estate(buf, buf2, fp, fp_tmp);
7095 				return;
7096 			}
7097 			gained_anything = TRUE;
7098 			s_printf("  gained %d Au.\n", au);
7099 			msg_format(Ind, "You receive %d gold pieces.", au);
7100 			continue;
7101 		}
7102 		/* get object from backup file */
7103 		else if (!strcmp(data, "OB:")) {
7104 			r = fread(o_ptr, sizeof(object_type), 1, fp);
7105 			if (r == 0) {
7106 				s_printf("  error: Failed to read object.\n");
7107 				msg_print(Ind, "\377oAn error occurred, please contact an administrator.");
7108 				fprintf(fp_tmp, "OB:");
7109 				relay_estate(buf, buf2, fp, fp_tmp);
7110 				return;
7111 			}
7112 			/* Update item's kind-index in case k_info.txt has been modified */
7113 			o_ptr->k_idx = lookup_kind(o_ptr->tval, o_ptr->sval);
7114 #ifdef SEAL_INVALID_OBJECTS
7115 		        if (!seal_or_unseal_object(o_ptr)) continue;
7116 #endif
7117 
7118 			/* also read inscription */
7119 			o_ptr->note = 0;
7120 			data_len = -2;
7121 #if 0 /* scanf() sucks a bit */
7122 			data_note[0] = '\0';
7123 			r = fscanf(fp, "%d[^\n]", &data_len);
7124 			r = fread(data_note, 1, 1, fp); //strip the \n that fscanf had to miss
7125 #else
7126 			rc = fgets(data_note, 4, fp);
7127 			data_len = atoi(data_note);
7128 			data_note[0] = '\0';
7129 #endif
7130 			if (rc == NULL || data_len == -2) {
7131 			        object_desc(Ind, o_name, o_ptr, TRUE, 3);
7132 				s_printf("  error: Corrupted note line (item '%s').\n", o_name);
7133 				msg_print(Ind, "\377oAn error occurred, please contact an administrator.");
7134 				relay_estate(buf, buf2, fp, fp_tmp);
7135 				return;
7136 			}
7137 			if (data_len != -1) {
7138 				r = fread(data_note, sizeof(char), data_len, fp);
7139 				if (r == data_len) {
7140 					data_note[data_len] = '\0';
7141 					o_ptr->note = quark_add(data_note);
7142 				} else {
7143 					s_printf("  error: Failed to read note line (item '%s').\n", o_name);
7144 					msg_print(Ind, "\377oAn error occurred, please contact an administrator.");
7145 					relay_estate(buf, buf2, fp, fp_tmp);
7146 					return;
7147 				}
7148 			}
7149 
7150 			/* is it a pile of gold? */
7151 			if (o_ptr->tval == TV_GOLD) {
7152 				/* give gold to player if it doesn't overflow,
7153 				   otherwise relay rest to temporary file, swap and exit */
7154 				au = o_ptr->pval;
7155 				if (!gain_au(Ind, au, FALSE, FALSE)) {
7156 					msg_print(Ind, "\377yDrop/deposite some gold to be able to receive more gold.");
7157 
7158 					/* write failed gold gain back into new buffer file */
7159 					fprintf(fp_tmp, "OB:");
7160         				(void)fwrite(o_ptr, sizeof(object_type), 1, fp_tmp);
7161 
7162 					/* paranoia: should always be inscriptionless of course */
7163 					/* ..and its inscription */
7164 					if (o_ptr->note) {
7165 						fprintf(fp_tmp, "%d\n", (int)strlen(quark_str(o_ptr->note)));
7166 						(void)fwrite(quark_str(o_ptr->note), sizeof(char), strlen(quark_str(o_ptr->note)), fp_tmp);
7167 					} else
7168 						fprintf(fp_tmp, "%d\n", -1);
7169 
7170 					relay_estate(buf, buf2, fp, fp_tmp);
7171 					return;
7172 				}
7173 				gained_anything = TRUE;
7174 				s_printf("  gained %d Au.\n", au);
7175 				msg_format(Ind, "You receive %d gold pieces.", au);
7176 				continue;
7177 			}
7178 
7179 			/* give item to player if he has space left,
7180 			   otherwise relay rest to temporary file, swap and exit */
7181 			if (!inven_carry_okay(Ind, o_ptr, 0x0)) {
7182 				msg_print(Ind, "\377yYour inventory is full, make some space to receive more items.");
7183 
7184 				/* write failed item back into new buffer file */
7185 				fprintf(fp_tmp, "OB:");
7186 				(void)fwrite(o_ptr, sizeof(object_type), 1, fp_tmp);
7187 
7188 				/* ..and its inscription */
7189 				if (o_ptr->note) {
7190 					fprintf(fp_tmp, "%d\n", (int)strlen(quark_str(o_ptr->note)));
7191 					(void)fwrite(quark_str(o_ptr->note), sizeof(char), strlen(quark_str(o_ptr->note)), fp_tmp);
7192 				} else
7193 					fprintf(fp_tmp, "%d\n", -1);
7194 
7195 				relay_estate(buf, buf2, fp, fp_tmp);
7196 				return;
7197 			}
7198 
7199 			gained_anything = TRUE;
7200 			inven_carry(Ind, o_ptr);
7201 		        object_desc(Ind, o_name, o_ptr, TRUE, 3);
7202 		        msg_format(Ind, "You receive %s.", o_name);
7203 			s_printf("  gained %s.\n", o_name);
7204 			continue;
7205 		} else {
7206 			s_printf("  invalid data '%s'.\n", data);
7207 			msg_print(Ind, "\377oAn error occurred, please contact an administrator.");
7208 			relay_estate(buf, buf2, fp, fp_tmp);
7209 			return;
7210 		}
7211 	}
7212 }
7213 
7214 /* For gathering statistical Ironman Deep Dive Challenge data on players clearing out monsters */
7215 void log_floor_coverage(dun_level *l_ptr, struct worldpos *wpos) {
7216 	cptr feel;
7217 
7218 	if (!l_ptr) return;
7219 
7220 	if (l_ptr->flags2 & LF2_OOD_HI) feel = "terrifying";
7221 	else if ((l_ptr->flags2 & LF2_VAULT_HI) &&
7222 	    (l_ptr->flags2 & LF2_OOD)) feel = "terrifying";
7223 	else if ((l_ptr->flags2 & LF2_VAULT_OPEN) || // <- TODO: implement :/
7224 	     ((l_ptr->flags2 & LF2_VAULT) && (l_ptr->flags2 & LF2_OOD_FREE))) feel = "danger";
7225 	else if (l_ptr->flags2 & LF2_VAULT) feel = "dangerous";
7226 	else if (l_ptr->flags2 & LF2_PITNEST_HI) feel = "dangerous";
7227 	else if (l_ptr->flags2 & LF2_OOD_FREE) feel = "challenge";
7228 	else if (l_ptr->flags2 & LF2_UNIQUE) feel = "special";
7229 	else feel = "boring";
7230 
7231 	if (l_ptr->monsters_generated + l_ptr->monsters_spawned == 0)
7232 		s_printf("CVRG-IDDC: %3d, g:--- s:--- k:---, --%% (%s)\n", wpos->wz, feel);
7233 	else
7234 		s_printf("CVRG-IDDC: %3d, g:%3d s:%3d k:%3d, %2d%% (%s)\n", wpos->wz,
7235 		    l_ptr->monsters_generated, l_ptr->monsters_spawned, l_ptr->monsters_killed,
7236 		    (l_ptr->monsters_killed * 100) / (l_ptr->monsters_generated + l_ptr->monsters_spawned),
7237 		    feel);
7238 }
7239 
7240 /* whenever the player enters a new grid (walk, teleport..)
7241    check whether he is affected in certain ways.. */
7242 void grid_affects_player(int Ind) {
7243 	player_type *p_ptr = Players[Ind];
7244 	cave_type **zcave;
7245 	cave_type *c_ptr;
7246 	bool inn = FALSE;
7247 
7248 	if (!(zcave = getcave(&p_ptr->wpos))) return;
7249 	c_ptr = &zcave[p_ptr->py][p_ptr->px];
7250 
7251 	if (!p_ptr->wpos.wz && !night_surface && !(c_ptr->info & CAVE_PROT) &&
7252 	    !(f_info[c_ptr->feat].flags1 & FF1_PROTECTED) &&
7253 	    c_ptr->feat != FEAT_SHOP) {
7254 		if (!p_ptr->grid_sunlit) {
7255 			p_ptr->grid_sunlit = TRUE;
7256 			calc_boni(Ind);
7257 		}
7258 	} else if (p_ptr->grid_sunlit) {
7259 		p_ptr->grid_sunlit = FALSE;
7260 		calc_boni(Ind);
7261 	}
7262 
7263 	/* Hack: Inns count as houses */
7264 	if (!p_ptr->wpos.wz && ((c_ptr->info & CAVE_PROT) || (f_info[c_ptr->feat].flags1 & FF1_PROTECTED))) inn = TRUE;
7265 
7266 	if (inside_house(&p_ptr->wpos, p_ptr->px, p_ptr->py) || inn) {
7267 		if (!p_ptr->grid_house) {
7268 			p_ptr->grid_house = TRUE;
7269 			if (!p_ptr->sfx_house) Send_sfx_volume(Ind, 0, 0);
7270 			else if (p_ptr->sfx_house_quiet) Send_sfx_volume(Ind, p_ptr->sound_ambient == SFX_AMBIENT_FIREPLACE ? 100 : GRID_SFX_REDUCTION, GRID_SFX_REDUCTION);
7271 		}
7272 	} else if (p_ptr->grid_house) {
7273 		p_ptr->grid_house = FALSE;
7274 		if (p_ptr->sfx_house_quiet || !p_ptr->sfx_house) Send_sfx_volume(Ind, 100, 100);
7275 	}
7276 
7277 	/* quests - check if he has arrived at a designated exact x,y target location */
7278 	if (p_ptr->quest_any_deliver_xy_within_target) quest_check_goal_deliver(Ind);
7279 }
7280 
7281 /* Items that can be shared even between incompatible character modes or if level 0! */
7282 bool exceptionally_shareable_item(object_type *o_ptr) {
7283 	if (o_ptr->name1 || ((o_ptr->name2 || o_ptr->name2b) && o_ptr->tval != TV_FOOD)) return FALSE;
7284 
7285 	if ((o_ptr->tval == TV_SCROLL && o_ptr->sval == SV_SCROLL_WORD_OF_RECALL) ||
7286 	    (o_ptr->tval == TV_LITE && o_ptr->sval == SV_LITE_TORCH) ||
7287 	    (o_ptr->tval == TV_FLASK && o_ptr->sval == SV_FLASK_OIL) ||
7288 	    (o_ptr->tval == TV_SCROLL && o_ptr->sval == SV_SCROLL_SATISFY_HUNGER) ||
7289 	    // "Why not share ale? -Molt" <- good idea, here too!
7290 	    (o_ptr->tval == TV_FOOD && o_ptr->sval >= SV_FOOD_MIN_FOOD && o_ptr->sval <= SV_FOOD_MAX_FOOD))
7291 		return TRUE;
7292 	return FALSE;
7293 }
7294 /* Starter items that can be shared despite being level 0! */
7295 bool shareable_starter_item(object_type *o_ptr) {
7296 	if (o_ptr->name1 || ((o_ptr->name2 || o_ptr->name2b) && o_ptr->tval != TV_FOOD)) return FALSE;
7297 
7298 	if ((o_ptr->tval == TV_LITE && o_ptr->sval == SV_LITE_TORCH) ||
7299 	    (o_ptr->tval == TV_SCROLL && o_ptr->sval == SV_SCROLL_SATISFY_HUNGER) ||
7300 	    // "Why not share ale? -Molt" <- good idea, here too!
7301 	    (o_ptr->tval == TV_FOOD && o_ptr->sval >= SV_FOOD_MIN_FOOD && o_ptr->sval <= SV_FOOD_MAX_FOOD))
7302 		return TRUE;
7303 	return FALSE;
7304 }
7305 
7306 void kick_char(int Ind_kicker, int Ind_kickee, char *reason) {
7307 	char kickmsg[MSG_LEN];
7308 
7309 	if (reason) {
7310 		msg_format(Ind_kicker, "Kicking %s out (%s).", Players[Ind_kickee]->name, reason);
7311 		snprintf(kickmsg, MSG_LEN, "Kicked out (%s)", reason);
7312 		Destroy_connection(Players[Ind_kickee]->conn, kickmsg);
7313 	} else {
7314 		msg_format(Ind_kicker, "Kicking %s out.", Players[Ind_kickee]->name);
7315 		Destroy_connection(Players[Ind_kickee]->conn, "Kicked out");
7316 	}
7317 }
7318 
7319 void kick_ip(int Ind_kicker, char *ip_kickee, char *reason, bool msg) {
7320 	int i;
7321 	char kickmsg[MSG_LEN];
7322 	bool found = FALSE;
7323 
7324 	if (reason) {
7325 		if (msg) msg_format(Ind_kicker, "Kicking out connections from %s (%s).", ip_kickee, reason);
7326 		snprintf(kickmsg, MSG_LEN, "Kicked out - %s", reason);
7327 	} else {
7328 		if (msg) msg_format(Ind_kicker, "Kicking out connections from %s.", ip_kickee);
7329 		snprintf(kickmsg, MSG_LEN, "Kicked out");
7330 	}
7331 
7332 	/* Kick him out (note, this could affect multiple people at once if sharing an IP) */
7333 	for (i = 1; i <= NumPlayers; i++) {
7334 		if (!Players[i]) continue;
7335 		if (!strcmp(get_player_ip(i), ip_kickee)) {
7336 			found = TRUE;
7337 			if (reason) s_printf("IP-kicked '%s' (%s).\n", Players[i]->name, reason);
7338 			else s_printf("IP-kicked '%s'.\n", Players[i]->name);
7339 			Destroy_connection(Players[i]->conn, kickmsg);
7340 			i--;
7341 		}
7342 	}
7343 	if (msg && !found) msg_print(Ind_kicker, "No matching player online to kick.");
7344 }
7345 
7346 /* Calculate basic success chance for magic devices for a player,
7347    before any "minimum granted chance" is applied. */
7348 static int magic_device_base_chance(int Ind, object_type *o_ptr) {
7349 	u32b dummy, f4;
7350 	player_type *p_ptr = Players[Ind];
7351 
7352 	/* Extract the item level */
7353 	int lev = k_info[o_ptr->k_idx].level;
7354 
7355 #if 0 /* not needed anymore since x_dev and skill-ratios have been adjusted in tables.c */
7356 	/* Reduce very high levels */
7357 	lev = (400 - ((200 - lev) * (200 - lev)) / 100) / 4;//1..75
7358 #endif
7359 
7360 	/* Base chance of success */
7361 	int chance = p_ptr->skill_dev;
7362 
7363 	/* Hack -- use artifact level instead */
7364 	if (true_artifact_p(o_ptr)) lev = a_info[o_ptr->name1].level;
7365 
7366 	/* Extract object flags */
7367 	object_flags(o_ptr, &dummy, &dummy, &dummy, &f4, &dummy, &dummy, &dummy);
7368 
7369 	/* Is it simple to use ? */
7370 	if (f4 & TR4_EASY_USE) {
7371 		chance += USE_DEVICE;
7372 
7373 		/* High level objects are harder */
7374 		chance = chance - (lev * 4) / 5;
7375 	} else {
7376 		/* High level objects are harder */
7377 		//chance = chance - ((lev > 50) ? 50 : lev) - (p_ptr->antimagic * 2);
7378 		chance = chance - lev;
7379 	}
7380 
7381 	/* Hacks: Certain items are easier/harder to use in general: */
7382 
7383 	/* Runes */
7384 	if (o_ptr->tval == TV_RUNE) {
7385 		chance = chance / 10 - 20;
7386 		if (o_ptr->sval >= RCRAFT_MAX_ELEMENTS) chance -= 10;
7387 		switch (o_ptr->sval) {
7388 		/* basic tier */
7389 		case SV_R_LITE:
7390 			if (get_skill(p_ptr, SKILL_R_LITE))
7391 				chance += get_skill(p_ptr, SKILL_R_LITE) + 15;
7392 			break;
7393 		case SV_R_DARK:
7394 			if (get_skill(p_ptr, SKILL_R_DARK))
7395 				chance += get_skill(p_ptr, SKILL_R_DARK) + 15;
7396 			break;
7397 		case SV_R_NEXU:
7398 			if (get_skill(p_ptr, SKILL_R_NEXU))
7399 				chance += get_skill(p_ptr, SKILL_R_NEXU) + 15;
7400 			break;
7401 		case SV_R_NETH:
7402 			if (get_skill(p_ptr, SKILL_R_NETH))
7403 				chance += get_skill(p_ptr, SKILL_R_NETH) + 15;
7404 			break;
7405 		case SV_R_CHAO:
7406 			if (get_skill(p_ptr, SKILL_R_CHAO))
7407 				chance += get_skill(p_ptr, SKILL_R_CHAO) + 15;
7408 			break;
7409 		case SV_R_MANA:
7410 			if (get_skill(p_ptr, SKILL_R_MANA))
7411 				chance += get_skill(p_ptr, SKILL_R_MANA) + 15;
7412 			break;
7413 		/* high tier */
7414 		case SV_R_CONF:
7415 			if (get_skill(p_ptr, SKILL_R_LITE) && get_skill(p_ptr, SKILL_R_DARK))
7416 				chance += (get_skill(p_ptr, SKILL_R_LITE) + get_skill(p_ptr, SKILL_R_DARK)) / 2 + 25;
7417 			else if (get_skill(p_ptr, SKILL_R_LITE))
7418 				chance += get_skill(p_ptr, SKILL_R_LITE) / 2 + 10;
7419 			else if (get_skill(p_ptr, SKILL_R_DARK))
7420 				chance += get_skill(p_ptr, SKILL_R_DARK) / 2 + 10;
7421 			break;
7422 		case SV_R_INER:
7423 			if (get_skill(p_ptr, SKILL_R_LITE) && get_skill(p_ptr, SKILL_R_NEXU))
7424 				chance += (get_skill(p_ptr, SKILL_R_LITE) + get_skill(p_ptr, SKILL_R_NEXU)) / 2 + 25;
7425 			else if (get_skill(p_ptr, SKILL_R_LITE))
7426 				chance += get_skill(p_ptr, SKILL_R_LITE) / 2 + 10;
7427 			else if (get_skill(p_ptr, SKILL_R_NEXU))
7428 				chance += get_skill(p_ptr, SKILL_R_NEXU) / 2 + 10;
7429 			break;
7430 		case SV_R_ELEC:
7431 			if (get_skill(p_ptr, SKILL_R_LITE) && get_skill(p_ptr, SKILL_R_NETH))
7432 				chance += (get_skill(p_ptr, SKILL_R_LITE) + get_skill(p_ptr, SKILL_R_NETH)) / 2 + 25;
7433 			else if (get_skill(p_ptr, SKILL_R_LITE))
7434 				chance += get_skill(p_ptr, SKILL_R_LITE) / 2 + 10;
7435 			else if (get_skill(p_ptr, SKILL_R_NETH))
7436 				chance += get_skill(p_ptr, SKILL_R_NETH) / 2 + 10;
7437 			break;
7438 		case SV_R_FIRE:
7439 			if (get_skill(p_ptr, SKILL_R_LITE) && get_skill(p_ptr, SKILL_R_CHAO))
7440 				chance += (get_skill(p_ptr, SKILL_R_LITE) + get_skill(p_ptr, SKILL_R_CHAO)) / 2 + 25;
7441 			else if (get_skill(p_ptr, SKILL_R_LITE))
7442 				chance += get_skill(p_ptr, SKILL_R_LITE) / 2 + 10;
7443 			else if (get_skill(p_ptr, SKILL_R_CHAO))
7444 				chance += get_skill(p_ptr, SKILL_R_CHAO) / 2 + 10;
7445 			break;
7446 		case SV_R_WATE:
7447 			if (get_skill(p_ptr, SKILL_R_LITE) && get_skill(p_ptr, SKILL_R_MANA))
7448 				chance += (get_skill(p_ptr, SKILL_R_LITE) + get_skill(p_ptr, SKILL_R_MANA)) / 2 + 25;
7449 			else if (get_skill(p_ptr, SKILL_R_LITE))
7450 				chance += get_skill(p_ptr, SKILL_R_LITE) / 2 + 10;
7451 			else if (get_skill(p_ptr, SKILL_R_MANA))
7452 				chance += get_skill(p_ptr, SKILL_R_MANA) / 2 + 10;
7453 			break;
7454 
7455 		case SV_R_GRAV:
7456 			if (get_skill(p_ptr, SKILL_R_DARK) && get_skill(p_ptr, SKILL_R_NEXU))
7457 				chance += (get_skill(p_ptr, SKILL_R_DARK) + get_skill(p_ptr, SKILL_R_NEXU)) / 2 + 25;
7458 			else if (get_skill(p_ptr, SKILL_R_DARK))
7459 				chance += get_skill(p_ptr, SKILL_R_DARK) / 2 + 10;
7460 			else if (get_skill(p_ptr, SKILL_R_NEXU))
7461 				chance += get_skill(p_ptr, SKILL_R_NEXU) / 2 + 10;
7462 			break;
7463 		case SV_R_COLD:
7464 			if (get_skill(p_ptr, SKILL_R_DARK) && get_skill(p_ptr, SKILL_R_NETH))
7465 				chance += (get_skill(p_ptr, SKILL_R_DARK) + get_skill(p_ptr, SKILL_R_NETH)) / 2 + 25;
7466 			else if (get_skill(p_ptr, SKILL_R_DARK))
7467 				chance += get_skill(p_ptr, SKILL_R_DARK) / 2 + 10;
7468 			else if (get_skill(p_ptr, SKILL_R_NETH))
7469 				chance += get_skill(p_ptr, SKILL_R_NETH) / 2 + 10;
7470 			break;
7471 		case SV_R_ACID:
7472 			if (get_skill(p_ptr, SKILL_R_DARK) && get_skill(p_ptr, SKILL_R_CHAO))
7473 				chance += (get_skill(p_ptr, SKILL_R_DARK) + get_skill(p_ptr, SKILL_R_CHAO)) / 2 + 25;
7474 			else if (get_skill(p_ptr, SKILL_R_DARK))
7475 				chance += get_skill(p_ptr, SKILL_R_DARK) / 2 + 10;
7476 			else if (get_skill(p_ptr, SKILL_R_CHAO))
7477 				chance += get_skill(p_ptr, SKILL_R_CHAO) / 2 + 10;
7478 			break;
7479 		case SV_R_POIS:
7480 			if (get_skill(p_ptr, SKILL_R_DARK) && get_skill(p_ptr, SKILL_R_MANA))
7481 				chance += (get_skill(p_ptr, SKILL_R_DARK) + get_skill(p_ptr, SKILL_R_MANA)) / 2 + 25;
7482 			else if (get_skill(p_ptr, SKILL_R_DARK))
7483 				chance += get_skill(p_ptr, SKILL_R_DARK) / 2 + 10;
7484 			else if (get_skill(p_ptr, SKILL_R_MANA))
7485 				chance += get_skill(p_ptr, SKILL_R_MANA) / 2 + 10;
7486 			break;
7487 
7488 		case SV_R_TIME:
7489 			if (get_skill(p_ptr, SKILL_R_NEXU) && get_skill(p_ptr, SKILL_R_NETH))
7490 				chance += (get_skill(p_ptr, SKILL_R_NEXU) + get_skill(p_ptr, SKILL_R_NETH)) / 2 + 25;
7491 			else if (get_skill(p_ptr, SKILL_R_NEXU))
7492 				chance += get_skill(p_ptr, SKILL_R_NEXU) / 2 + 10;
7493 			else if (get_skill(p_ptr, SKILL_R_NETH))
7494 				chance += get_skill(p_ptr, SKILL_R_NETH) / 2 + 10;
7495 			break;
7496 		case SV_R_SOUN:
7497 			if (get_skill(p_ptr, SKILL_R_NEXU) && get_skill(p_ptr, SKILL_R_CHAO))
7498 				chance += (get_skill(p_ptr, SKILL_R_NEXU) + get_skill(p_ptr, SKILL_R_CHAO)) / 2 + 25;
7499 			else if (get_skill(p_ptr, SKILL_R_NEXU))
7500 				chance += get_skill(p_ptr, SKILL_R_NEXU) / 2 + 10;
7501 			else if (get_skill(p_ptr, SKILL_R_CHAO))
7502 				chance += get_skill(p_ptr, SKILL_R_CHAO) / 2 + 10;
7503 			break;
7504 		case SV_R_SHAR:
7505 			if (get_skill(p_ptr, SKILL_R_NEXU) && get_skill(p_ptr, SKILL_R_MANA))
7506 				chance += (get_skill(p_ptr, SKILL_R_NEXU) + get_skill(p_ptr, SKILL_R_MANA)) / 2 + 25;
7507 			else if (get_skill(p_ptr, SKILL_R_NEXU))
7508 				chance += get_skill(p_ptr, SKILL_R_NEXU) / 2 + 10;
7509 			else if (get_skill(p_ptr, SKILL_R_MANA))
7510 				chance += get_skill(p_ptr, SKILL_R_MANA) / 2 + 10;
7511 			break;
7512 
7513 		case SV_R_DISE:
7514 			if (get_skill(p_ptr, SKILL_R_NETH) && get_skill(p_ptr, SKILL_R_CHAO))
7515 				chance += (get_skill(p_ptr, SKILL_R_NETH) + get_skill(p_ptr, SKILL_R_CHAO)) / 2 + 25;
7516 			else if (get_skill(p_ptr, SKILL_R_NETH))
7517 				chance += get_skill(p_ptr, SKILL_R_NETH) / 2 + 10;
7518 			else if (get_skill(p_ptr, SKILL_R_CHAO))
7519 				chance += get_skill(p_ptr, SKILL_R_CHAO) / 2 + 10;
7520 			break;
7521 		case SV_R_FORC:
7522 			if (get_skill(p_ptr, SKILL_R_NETH) && get_skill(p_ptr, SKILL_R_MANA))
7523 				chance += (get_skill(p_ptr, SKILL_R_NETH) + get_skill(p_ptr, SKILL_R_MANA)) / 2 + 25;
7524 			else if (get_skill(p_ptr, SKILL_R_NETH))
7525 				chance += get_skill(p_ptr, SKILL_R_NETH) / 2 + 10;
7526 			else if (get_skill(p_ptr, SKILL_R_MANA))
7527 				chance += get_skill(p_ptr, SKILL_R_MANA) / 2 + 10;
7528 			break;
7529 
7530 		case SV_R_PLAS:
7531 			if (get_skill(p_ptr, SKILL_R_CHAO) && get_skill(p_ptr, SKILL_R_MANA))
7532 				chance += (get_skill(p_ptr, SKILL_R_CHAO) + get_skill(p_ptr, SKILL_R_MANA)) / 2 + 25;
7533 			else if (get_skill(p_ptr, SKILL_R_CHAO))
7534 				chance += get_skill(p_ptr, SKILL_R_CHAO) / 2 + 10;
7535 			else if (get_skill(p_ptr, SKILL_R_MANA))
7536 				chance += get_skill(p_ptr, SKILL_R_MANA) / 2 + 10;
7537 			break;
7538 		}
7539 	}
7540 #if 1
7541 	/* equippable magic devices are especially easy to use? (ie no wands/staves/rods)
7542 	   eg tele rings, serpent amulets, true artifacts */
7543 	else if (!is_magic_device(o_ptr->tval)) {
7544 		chance += 30;
7545 		chance = chance - lev / 10;
7546 	}
7547 #else
7548 	/* Rings of polymorphing and the Ring of Phasing shouldn't require magic device skill really */
7549 	else if ((o_ptr->tval == TV_RING && o_ptr->sval == SV_RING_POLYMORPH) ||
7550 	    (o_ptr->tval == TV_RING && o_ptr->sval == SV_RING_WRAITH)) {
7551 		if (chance < USE_DEVICE * 2) chance = USE_DEVICE * 2;
7552 	}
7553 #endif
7554 
7555 	/* Confusion makes it much harder (maybe TODO: blind/stun?) */
7556 	if (p_ptr->confused) chance = chance / 2;
7557 
7558 	return chance;
7559 }
7560 
7561 /* just for display purpose, return an actual average percentage value */
7562 int activate_magic_device_chance(int Ind, object_type *o_ptr) {
7563 	int chance = magic_device_base_chance(Ind, o_ptr);
7564 
7565 	/* Give everyone a (slight) chance */
7566 	if (chance < USE_DEVICE)
7567 		return ((100 / (USE_DEVICE - chance + 1)) * ((100 * (USE_DEVICE - 1)) / USE_DEVICE)) / 100;
7568 
7569 	/* Normal chance */
7570 	return 100 - ((USE_DEVICE - 1) * 100) / chance;
7571 }
7572 
7573 bool activate_magic_device(int Ind, object_type *o_ptr) {
7574 	player_type *p_ptr = Players[Ind];
7575 
7576 	int chance = magic_device_base_chance(Ind, o_ptr);
7577 
7578 	/* Certain items are heavily restricted (todo: use WINNERS_ONLY flag instead for cleanliness) */
7579 	if (o_ptr->name1 == ART_PHASING && !p_ptr->total_winner) {
7580 		msg_print(Ind, "Only royalties may activate this Ring!");
7581 		if (!is_admin(p_ptr)) return FALSE;
7582 	}
7583 
7584 	/* Give everyone a (slight) chance */
7585 	if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0))
7586 		chance = USE_DEVICE;
7587 
7588 	/* Roll for usage */
7589 	if ((chance < USE_DEVICE) || (randint(chance) < USE_DEVICE)) return FALSE;
7590 	return TRUE;
7591 }
7592 
7593 /* Condense an (account) name into a 'normalised' version, used to prevent
7594    new players from creating account names too similar to existing ones. - C. Blue */
7595 void condense_name(char *condensed, const char *name) {
7596 	char *bufptr = condensed, current, multiple = 0, *ptr;
7597 	bool space = TRUE;
7598 
7599 	for (ptr = (char*)name; *ptr; ptr++) {
7600 		/* skip spaces in the beginning */
7601 		if (space && *ptr == ' ') continue;
7602 		space = FALSE;
7603 
7604 		/* ignore lower/upper case */
7605 		current = tolower(*ptr);
7606 
7607 		//discard non-alphanumeric characters
7608 		if (!isalpha(current) && !isdigit(current)) continue;
7609 
7610 		//condense multiples of the same character
7611 		if (multiple == current) continue;
7612 		multiple = current;
7613 
7614 		//finally add the character
7615 		*bufptr++ = current;
7616 	}
7617 	*bufptr = 0; //terminate
7618 
7619 	//discard spaces at the end of the name
7620 	for (ptr = bufptr - 1; ptr >= condensed; ptr--)
7621 		if (*ptr == ' ') {
7622 			*ptr = 0;
7623 			bufptr--;
7624 		}
7625 		else break;
7626 
7627 	//extra strict: discard digits at the end of the name
7628 	for (ptr = bufptr - 1; ptr >= condensed; ptr--)
7629 		if (isdigit(*ptr))
7630 			*ptr = 0;
7631 		else break;
7632 }
7633