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