1 /* File: files.c */
2
3 /* Purpose: code dealing with files (and death) */
4
5 /*
6 * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
7 *
8 * This software may be copied and distributed for educational, research, and
9 * not for profit purposes provided that this copyright and statement are
10 * included in all such copies.
11 */
12
13 #include "mangband.h"
14
15 #ifdef HANDLE_SIGNALS
16 #include <signal.h>
17
18 volatile sig_atomic_t signalbusy = 0;
19
20 #endif
21
22
23
24 /*
25 * You may or may not want to use the following "#undef".
26 */
27 /* #undef _POSIX_SAVED_IDS */
28
29
30 /*
31 * Extract the first few "tokens" from a buffer
32 *
33 * This function uses "colon" and "slash" as the delimeter characters.
34 *
35 * We never extract more than "num" tokens. The "last" token may include
36 * "delimeter" characters, allowing the buffer to include a "string" token.
37 *
38 * We save pointers to the tokens in "tokens", and return the number found.
39 *
40 * Hack -- Attempt to handle the 'c' character formalism
41 *
42 * Hack -- An empty buffer, or a final delimeter, yields an "empty" token.
43 *
44 * Hack -- We will always extract at least one token
45 */
tokenize(char * buf,s16b num,char ** tokens)46 s16b tokenize(char *buf, s16b num, char **tokens)
47 {
48 int i = 0;
49
50 char *s = buf;
51
52
53 /* Process */
54 while (i < num - 1)
55 {
56 char *t;
57
58 /* Scan the string */
59 for (t = s; *t; t++)
60 {
61 /* Found a delimiter */
62 if ((*t == ':') || (*t == '/')) break;
63
64 /* Handle single quotes */
65 if (*t == '\'') /* ' */
66 {
67 /* Advance */
68 t++;
69
70 /* Handle backslash */
71 if (*t == '\\') t++;
72
73 /* Require a character */
74 if (!*t) break;
75
76 /* Advance */
77 t++;
78
79 /* Hack -- Require a close quote */
80 if (*t != '\'') *t = '\'';
81 }
82
83 /* Handle back-slash */
84 if (*t == '\\') t++;
85 }
86
87 /* Nothing left */
88 if (!*t) break;
89
90 /* Nuke and advance */
91 *t++ = '\0';
92
93 /* Save the token */
94 tokens[i++] = s;
95
96 /* Advance */
97 s = t;
98 }
99
100 /* Save the token */
101 tokens[i++] = s;
102
103 /* Number found */
104 return (i);
105 }
106
107
108 bool skip_next_line = FALSE;
109 /*
110 * Parse a sub-file of the "extra info" (format shown below)
111 *
112 * Each "action" line has an "action symbol" in the first column,
113 * followed by a colon, followed by some command specific info,
114 * usually in the form of "tokens" separated by colons or slashes.
115 *
116 * Blank lines, lines starting with white space, and lines starting
117 * with pound signs ("#") are ignored (as comments).
118 *
119 * Note the use of "tokenize()" to allow the use of both colons and
120 * slashes as delimeters, while still allowing final tokens which
121 * may contain any characters including "delimiters".
122 *
123 * Note the use of "strtol()" to allow all "integers" to be encoded
124 * in decimal, hexidecimal, or octal form.
125 *
126 * Note that "monster zero" is used for the "player" attr/char, "object
127 * zero" will be used for the "stack" attr/char, and "feature zero" is
128 * used for the "nothing" attr/char.
129 *
130 * Parse another file recursively, see below for details
131 * %:<filename>
132 *
133 * Specify the attr/char values for "monsters" by race index
134 * R:<num>:<a>:<c>
135 *
136 * Specify the attr/char values for "objects" by kind index
137 * K:<num>:<a>:<c>
138 *
139 * Specify the attr/char values for "features" by feature index
140 * F:<num>:<a>:<c>
141 *
142 * Specify the attr/char values for unaware "objects" by kind tval
143 * U:<tv>:<a>:<c>
144 *
145 * Specify the attr/char values for inventory "objects" by kind tval
146 * E:<tv>:<a>:<c>
147 *
148 * Define a macro action, given an encoded macro action
149 * A:<str>
150 *
151 * Create a normal macro, given an encoded macro trigger
152 * P:<str>
153 *
154 * Create a command macro, given an encoded macro trigger
155 * C:<str>
156 *
157 * Create a keyset mapping
158 * S:<key>:<key>:<dir>
159 *
160 * Turn an option off, given its name
161 * X:<str>
162 *
163 * Turn an option on, given its name
164 * Y:<str>
165 *
166 * Specify visual information, given an index, and some data
167 * V:<num>:<kv>:<rv>:<gv>:<bv>
168 */
process_pref_file_aux(char * buf)169 errr process_pref_file_aux(char *buf)
170 {
171 int i, j, k, n1, n2;
172
173 char *zz[16];
174
175 /* Skip "empty" && "blank" lines */
176 if (!buf[0] || isspace(buf[0]))
177 {
178 skip_next_line = FALSE;
179 return (0);
180 }
181
182 /* Skip comments */
183 if (buf[0] == '#') return (0);
184
185
186 /* Require "?:*" format */
187 if (buf[1] != ':') return (1);
188
189 /* Hack - Do not load any Evaluated Expressions */
190 if (skip_next_line) return(0);
191
192 /* Process "%:<fname>" */
193 if (buf[0] == '%')
194 {
195 /* Attempt to Process the given file */
196 return (process_pref_file(buf + 2));
197 }
198
199
200 /* Process "R:<num>:<a>/<c>" -- attr/char for monster races */
201 if (buf[0] == 'R')
202 {
203 if (tokenize(buf+2, 3, zz) == 3)
204 {
205 monster_race *r_ptr;
206 i = (huge)strtol(zz[0], NULL, 0);
207 n1 = strtol(zz[1], NULL, 0);
208 n2 = strtol(zz[2], NULL, 0);
209 if (i >= z_info->r_max) return (1);
210 /*r_ptr = &r_info[i];
211 if (n1) r_ptr->x_attr = n1;
212 if (n2) r_ptr->x_char = n2;*/
213 if (n1) r_attr_s[i] = n1;
214 if (n2) r_char_s[i] = n2;
215 return (0);
216 }
217 }
218
219
220 /* Process "K:<num>:<a>/<c>" -- attr/char for object kinds */
221 else if (buf[0] == 'K')
222 {
223 if (tokenize(buf+2, 3, zz) == 3)
224 {
225 object_kind *k_ptr;
226 i = (huge)strtol(zz[0], NULL, 0);
227 n1 = strtol(zz[1], NULL, 0);
228 n2 = strtol(zz[2], NULL, 0);
229 if (i >= z_info->k_max) return (1);
230 /*k_ptr = &k_info[i];
231 if (n1) k_ptr->x_attr = n1;
232 if (n2) k_ptr->x_char = n2;*/
233 if (n1) k_attr_s[i] = n1;
234 if (n2) k_char_s[i] = n1;
235 return (0);
236 }
237 }
238
239
240 /* Process "F:<num>:<a>/<c>" -- attr/char for terrain features */
241 else if (buf[0] == 'F')
242 {
243 if (tokenize(buf+2, 3, zz) == 3)
244 {
245 feature_type *f_ptr;
246 i = (huge)strtol(zz[0], NULL, 0);
247 n1 = strtol(zz[1], NULL, 0);
248 n2 = strtol(zz[2], NULL, 0);
249 if (i >= z_info->f_max) return (1);
250 /*f_ptr = &f_info[i];
251 if (n1) f_ptr->x_attr = n1;
252 if (n2) f_ptr->x_char = n2;*/
253 if (n1) f_attr_s[i] = n1;
254 if (n2) f_char_s[i] = n2;
255 return (0);
256 }
257 }
258
259
260 /* Process "U:<tv>:<a>/<c>" -- attr/char for unaware items */
261 else if (buf[0] == 'U')
262 {
263 if (tokenize(buf+2, 3, zz) == 3)
264 {
265 j = (huge)strtol(zz[0], NULL, 0);
266 n1 = strtol(zz[1], NULL, 0);
267 n2 = strtol(zz[2], NULL, 0);
268 for (i = 1; i < z_info->k_max; i++)
269 {
270 object_kind *k_ptr = &k_info[i];
271 if (k_ptr->tval == j)
272 {
273 if (n1) k_ptr->d_attr = n1;
274 if (n2) k_ptr->d_char = n2;
275 }
276 }
277 return (0);
278 }
279 }
280
281
282 /* Process "E:<tv>:<a>/<c>" -- attr/char for equippy chars */
283 else if (buf[0] == 'E')
284 {
285 if (tokenize(buf+2, 2, zz) == 2)
286 {
287 j = (byte)strtol(zz[0], NULL, 0) % 128;
288 n1 = strtol(zz[1], NULL, 0);
289 if (n1) tval_to_attr[j] = n1;
290 return (0);
291 }
292 if (tokenize(buf+2, 3, zz) == 3)
293 {
294 j = (byte)strtol(zz[0], NULL, 0) % 128;
295 n1 = strtol(zz[1], NULL, 0);
296 n2 = strtol(zz[2], NULL, 0);
297 if (n1) tval_to_attr[j] = n1;
298 if (n2) tval_to_char[j] = n2;
299 return (0);
300 }
301 }
302
303
304 /* Process "A:<str>" -- save an "action" for later */
305 else if (buf[0] == 'A')
306 {
307 text_to_ascii(macro__buf, buf+2);
308 return (0);
309 }
310
311 /* Process "P:<str>" -- create normal macro */
312 else if (buf[0] == 'P')
313 {
314 char tmp[1024];
315 text_to_ascii(tmp, buf+2);
316 macro_add(tmp, macro__buf, FALSE);
317 return (0);
318 }
319
320 /* Process "C:<str>" -- create command macro */
321 else if (buf[0] == 'C')
322 {
323 char tmp[1024];
324 text_to_ascii(tmp, buf+2);
325 macro_add(tmp, macro__buf, TRUE);
326 return (0);
327 }
328
329
330 /* Process "S:<num>:<a>/<c>" -- attr/char for special things */
331 else if (buf[0] == 'S')
332 {
333 if (tokenize(buf+2, 3, zz) == 3)
334 {
335 i = strtol(zz[0], NULL, 0);
336 n1 = strtol(zz[1], NULL, 0);
337 n2 = strtol(zz[2], NULL, 0);
338 if ((i < 0) || (i >= (long)N_ELEMENTS(misc_to_attr))) return (1);
339 misc_to_attr[i] = (byte)n1;
340 misc_to_char[i] = (char)n2;
341 return (0);
342 }
343
344 }
345 /* Process "S:<key>:<key>:<dir>" -- keymap */
346 else if (buf[0] == 'S')
347 {
348 if (tokenize(buf+2, 3, zz) == 3)
349 {
350 i = strtol(zz[0], NULL, 0) & 0x7F;
351 j = strtol(zz[0], NULL, 0) & 0x7F;
352 k = strtol(zz[0], NULL, 0) & 0x7F;
353 if ((k > 9) || (k == 5)) k = 0;
354 keymap_cmds[i] = j;
355 keymap_dirs[i] = k;
356 return (0);
357 }
358 }
359
360
361 /* Process "V:<num>:<kv>:<rv>:<gv>:<bv>" -- visual info */
362 else if (buf[0] == 'V')
363 {
364 if (tokenize(buf+2, 5, zz) == 5)
365 {
366 i = (byte)strtol(zz[0], NULL, 0);
367 color_table[i][0] = (byte)strtol(zz[1], NULL, 0);
368 color_table[i][1] = (byte)strtol(zz[2], NULL, 0);
369 color_table[i][2] = (byte)strtol(zz[3], NULL, 0);
370 color_table[i][3] = (byte)strtol(zz[4], NULL, 0);
371 return (0);
372 }
373 }
374
375
376 /* Process "X:<str>" -- turn option off */
377 else if (buf[0] == 'X')
378 {
379 return (0);
380 }
381
382 /* Process "Y:<str>" -- turn option on */
383 else if (buf[0] == 'Y')
384 {
385 return (0);
386 }
387
388 /* Process "W:<num>:<use> -- set window use */
389 else if (buf[0] == 'W')
390 {
391 return (0);
392 }
393
394
395 /* Process "?: -- expression */
396 else if (buf[0] == '?')
397 {
398 skip_next_line = TRUE;
399 return (0);
400 }
401
402 /* Failure */
403 return (1);
404 }
405
406
407 /*
408 * Process the "user pref file" with the given name
409 *
410 * See the function above for a list of legal "commands".
411 */
process_pref_file(cptr name)412 errr process_pref_file(cptr name)
413 {
414 ang_file* fp;
415
416 char buf[1024];
417
418
419 /* Build the filename */
420 path_build(buf, 1024, ANGBAND_DIR_PREF, name);
421
422 /* Open the file */
423 fp = file_open(buf, MODE_READ, -1);
424
425 /* Catch errors */
426 if (!fp) return (-1);
427
428 /* Process the file */
429 while (file_getl(fp, buf, 1024))
430 {
431 /* Process the line */
432 if (process_pref_file_aux(buf))
433 {
434 /* Useful error message */
435 plog(format("Error in '%s' parsing '%s'.", buf, name));
436 }
437 }
438
439 /* Close the file */
440 file_close(fp);
441
442 /* Success */
443 return (0);
444 }
445
446
447
448
449
450
451
452 #ifdef CHECK_TIME
453
454 /*
455 * Operating hours for ANGBAND (defaults to non-work hours)
456 */
457 static char days[7][29] =
458 {
459 "SUN:XXXXXXXXXXXXXXXXXXXXXXXX",
460 "MON:XXXXXXXX.........XXXXXXX",
461 "TUE:XXXXXXXX.........XXXXXXX",
462 "WED:XXXXXXXX.........XXXXXXX",
463 "THU:XXXXXXXX.........XXXXXXX",
464 "FRI:XXXXXXXX.........XXXXXXX",
465 "SAT:XXXXXXXXXXXXXXXXXXXXXXXX"
466 };
467
468 /*
469 * Restict usage (defaults to no restrictions)
470 */
471 static bool check_time_flag = FALSE;
472
473 #endif
474
475
476 /*
477 * Handle CHECK_TIME
478 */
check_time(void)479 errr check_time(void)
480 {
481
482 #ifdef CHECK_TIME
483
484 time_t c;
485 struct tm *tp;
486
487 /* No restrictions */
488 if (!check_time_flag) return (0);
489
490 /* Check for time violation */
491 c = time((time_t *)0);
492 tp = localtime(&c);
493
494 /* Violation */
495 if (days[tp->tm_wday][tp->tm_hour + 4] != 'X') return (1);
496
497 #endif
498
499 /* Success */
500 return (0);
501 }
502
503
504
505 /*
506 * Initialize CHECK_TIME
507 */
check_time_init(void)508 errr check_time_init(void)
509 {
510
511 #ifdef CHECK_TIME
512
513 ang_file* fp;
514
515 char buf[1024];
516
517
518 /* Build the filename */
519 path_build(buf, 1024, ANGBAND_DIR_DATA, "time.txt");
520
521 /* Open the file */
522 fp = file_open(buf, MODE_READ, -1);
523
524 /* No file, no restrictions */
525 if (!fp) return (0);
526
527 /* Assume restrictions */
528 check_time_flag = TRUE;
529
530 /* Parse the file */
531 while (file_getl(fp, buf, 80))
532 {
533 /* Skip comments and blank lines */
534 if (!buf[0] || (buf[0] == '#')) continue;
535
536 /* Chop the buffer */
537 buf[29] = '\0';
538
539 /* Extract the info */
540 if (prefix(buf, "SUN:")) strcpy(days[0], buf);
541 if (prefix(buf, "MON:")) strcpy(days[1], buf);
542 if (prefix(buf, "TUE:")) strcpy(days[2], buf);
543 if (prefix(buf, "WED:")) strcpy(days[3], buf);
544 if (prefix(buf, "THU:")) strcpy(days[4], buf);
545 if (prefix(buf, "FRI:")) strcpy(days[5], buf);
546 if (prefix(buf, "SAT:")) strcpy(days[6], buf);
547 }
548
549 /* Close it */
550 file_close(fp);
551
552 #endif
553
554 /* Success */
555 return (0);
556 }
557
558
559
560 #ifdef CHECK_LOAD
561
562 #ifndef MAXHOSTNAMELEN
563 # define MAXHOSTNAMELEN 64
564 #endif
565
566 typedef struct statstime statstime;
567
568 struct statstime
569 {
570 int cp_time[4];
571 int dk_xfer[4];
572 unsigned int v_pgpgin;
573 unsigned int v_pgpgout;
574 unsigned int v_pswpin;
575 unsigned int v_pswpout;
576 unsigned int v_intr;
577 int if_ipackets;
578 int if_ierrors;
579 int if_opackets;
580 int if_oerrors;
581 int if_collisions;
582 unsigned int v_swtch;
583 long avenrun[3];
584 struct timeval boottime;
585 struct timeval curtime;
586 };
587
588 /*
589 * Maximal load (if any).
590 */
591 static int check_load_value = 0;
592
593 #endif
594
595
596 /*
597 * Handle CHECK_LOAD
598 */
check_load(void)599 errr check_load(void)
600 {
601
602 #ifdef CHECK_LOAD
603
604 struct statstime st;
605
606 /* Success if not checking */
607 if (!check_load_value) return (0);
608
609 /* Check the load */
610 if (0 == rstat("localhost", &st))
611 {
612 long val1 = (long)(st.avenrun[2]);
613 long val2 = (long)(check_load_value) * FSCALE;
614
615 /* Check for violation */
616 if (val1 >= val2) return (1);
617 }
618
619 #endif
620
621 /* Success */
622 return (0);
623 }
624
625
626 /*
627 * Initialize CHECK_LOAD
628 */
check_load_init(void)629 errr check_load_init(void)
630 {
631
632 #ifdef CHECK_LOAD
633
634 ang_file* fp;
635
636 char buf[1024];
637
638 char temphost[MAXHOSTNAMELEN+1];
639 char thishost[MAXHOSTNAMELEN+1];
640
641
642 /* Build the filename */
643 path_build(buf, 1024, ANGBAND_DIR_TEXT, "load.txt");
644
645 /* Open the "load" file */
646 fp = file_open(buf, MODE_READ, -1);
647
648 /* No file, no restrictions */
649 if (!fp) return (0);
650
651 /* Default load */
652 check_load_value = 100;
653
654 /* Get the host name */
655 (void)gethostname(thishost, (sizeof thishost) - 1);
656
657 /* Parse it */
658 while (file_getl(fp, buf, 1024))
659 {
660 int value;
661
662 /* Skip comments and blank lines */
663 if (!buf[0] || (buf[0] == '#')) continue;
664
665 /* Parse, or ignore */
666 if (sscanf(buf, "%s%d", temphost, &value) != 2) continue;
667
668 /* Skip other hosts */
669 if (!streq(temphost, thishost) &&
670 !streq(temphost, "localhost")) continue;
671
672 /* Use that value */
673 check_load_value = value;
674
675 /* Done */
676 break;
677 }
678
679 /* Close the file */
680 file_close(fp);
681
682 #endif
683
684 /* Success */
685 return (0);
686 }
687
688
689
690
691
692 /*
693 * Prints the following information on the screen.
694 *
695 * For this to look right, the following should be spaced the
696 * same as in the prt_lnum code... -CFT
697 *
698 * This will send the info to the client now --KLJ--
699 *
700 * Except that this (and display_player) are never called. --KLJ--
701 */
display_player_middle(player_type * p_ptr)702 static void display_player_middle(player_type *p_ptr)
703 {
704 int show_tohit = p_ptr->dis_to_h;
705 int show_todam = p_ptr->dis_to_d;
706
707 s32b adv_exp;
708
709 object_type *o_ptr = &p_ptr->inventory[INVEN_WIELD];
710
711 /* Hack -- add in weapon info if known */
712 if (object_known_p(p_ptr, o_ptr)) show_tohit += o_ptr->to_h;
713 if (object_known_p(p_ptr, o_ptr)) show_todam += o_ptr->to_d;
714
715 /* Dump the bonuses to hit/dam */
716 //Send_plusses(Ind, show_tohit, show_todam);
717
718 /* Dump the armor class bonus */
719 //Send_ac(Ind, p_ptr->dis_ac, p_ptr->dis_to_a);
720
721 if (p_ptr->lev >= PY_MAX_LEVEL)
722 adv_exp = 0;
723 else adv_exp = (s32b)(player_exp[p_ptr->lev - 1] * p_ptr->expfact / 100L);
724
725 //Send_experience(Ind, p_ptr->lev, p_ptr->max_exp, p_ptr->exp, adv_exp);
726
727 //Send_gold(Ind, p_ptr->au);
728
729 //Send_hp(Ind, p_ptr->mhp, p_ptr->chp);
730
731 //Send_sp(Ind, p_ptr->msp, p_ptr->csp);
732 }
733
734
735
736 /*
737 * Display the character on the screen (with optional history)
738 *
739 * The top two and bottom two lines are left blank.
740 */
display_player(player_type * p_ptr)741 void display_player(player_type *p_ptr)
742 {
743 int i;
744
745
746 /* Send basic information */
747 //Send_char_info(Ind, p_ptr->prace, p_ptr->pclass, p_ptr->male);
748
749 /* Age, Height, Weight, Social */
750 //Send_various(Ind, p_ptr->ht, p_ptr->wt, p_ptr->age, p_ptr->sc);
751
752 /* Send all the stats */
753 for (i = 0; i < A_MAX; i++)
754 {
755 //Send_stat(Ind, i, p_ptr->stat_top[i], p_ptr->stat_use[i]);
756 //Send_maxstat(Ind, i, p_ptr->stat_max[i]);
757 }
758
759 /* Extra info */
760 display_player_middle(p_ptr);
761
762 /* Display "history" info */
763 //Send_history(Ind, i, p_ptr->history[i]);
764 }
765
766
c_put_str_b(char buffer[100][82],byte attr,cptr str,int row,int col)767 void c_put_str_b(char buffer[100][82], byte attr, cptr str, int row, int col)
768 {
769 /* Position cursor, Dump the attr/text */
770 char* s;
771 int i;
772 s = buffer[row-1];
773 i = col;
774 while(*str)
775 {
776 s[i++] = (*str);
777 str++;
778 }
779 }
put_str_b(char buffer[100][82],cptr str,int row,int col)780 void put_str_b(char buffer[100][82], cptr str, int row, int col)
781 {
782 c_put_str_b(buffer,0,str,row,col);
783 }
prt_num_b(char buffer[100][82],cptr header,int num,int row,int col,byte color)784 void prt_num_b(char buffer[100][82], cptr header, int num, int row, int col, byte color)
785 {
786 int len = strlen(header);
787 char out_val[32];
788 put_str_b(buffer,header, row, col);
789 put_str_b(buffer," ", row, col + len);
790 (void)sprintf(out_val, "%6ld", (long)num);
791 c_put_str_b(buffer,color, out_val, row, col + len + 3);
792 }
793
prt_lnum_b(char buffer[100][82],cptr header,s32b num,int row,int col,byte color)794 void prt_lnum_b(char buffer[100][82], cptr header, s32b num, int row, int col, byte color)
795 {
796 int len = strlen(header);
797 char out_val[32];
798 put_str_b(buffer,header, row, col);
799 (void)sprintf(out_val, "%9ld", (long)num);
800 c_put_str_b(buffer,color, out_val, row, col + len);
801 }
802
803 /*
804 * Returns a "rating" of x depending on y
805 */
likert(int x,int y)806 static cptr likert(int x, int y)
807 {
808 /* Paranoia */
809 if (y <= 0) y = 1;
810
811 /* Negative values */
812 if (x < 0)
813 {
814 return ("Very Bad");
815 }
816
817 /* Analyze the value */
818 switch ((x / y))
819 {
820 case 0:
821 case 1:
822 {
823 return ("Bad");
824 }
825 case 2:
826 {
827 return ("Poor");
828 }
829 case 3:
830 case 4:
831 {
832 return ("Fair");
833 }
834 case 5:
835 {
836 return ("Good");
837 }
838 case 6:
839 {
840 return ("Very Good");
841 }
842 case 7:
843 case 8:
844 {
845 return ("Excellent");
846 }
847 case 9:
848 case 10:
849 case 11:
850 case 12:
851 case 13:
852 {
853 return ("Superb");
854 }
855 case 14:
856 case 15:
857 case 16:
858 case 17:
859 {
860 return ("Heroic");
861 }
862 default:
863 {
864 return ("Legendary");
865 }
866 }
867 }
868
869
870 /*
871 * Similar to the function in c-xtra but modified to work server-side.
872 * We print text into a buffer rather than to a term.
873 * This is used for serverside character dumps.
874 */
display_player_server(player_type * p_ptr,char buffer[100][82])875 void display_player_server(player_type *p_ptr, char buffer[100][82])
876 {
877 int i;
878 char buf[80];
879 cptr desc;
880 /* bool hist = FALSE;*/
881
882 int show_tohit = p_ptr->dis_to_h;
883 int show_todam = p_ptr->dis_to_d;
884 object_type *o_ptr = &p_ptr->inventory[INVEN_WIELD];
885
886 /* Name, Sex, Race, Class */
887 put_str_b(buffer,"Name :", 2, 1);
888 put_str_b(buffer,"Sex :", 3, 1);
889 put_str_b(buffer,"Race :", 4, 1);
890 put_str_b(buffer,"Class :", 5, 1);
891
892 c_put_str_b(buffer,TERM_L_BLUE, p_ptr->name, 2, 15);
893 c_put_str_b(buffer,TERM_L_BLUE, (p_ptr->male ? "Male" : "Female"), 3, 15);
894 c_put_str_b(buffer,TERM_L_BLUE, p_name + p_info[p_ptr->prace].name, 4, 15);
895 c_put_str_b(buffer,TERM_L_BLUE, c_name + c_info[p_ptr->pclass].name, 5, 15);
896
897 /* Age, Height, Weight, Social */
898 prt_num_b(buffer,"Age ", (int)p_ptr->age, 2, 32, TERM_L_BLUE);
899 prt_num_b(buffer,"Height ", (int)p_ptr->ht, 3, 32, TERM_L_BLUE);
900 prt_num_b(buffer,"Weight ", (int)p_ptr->wt, 4, 32, TERM_L_BLUE);
901 prt_num_b(buffer,"Social Class ", (int)p_ptr->sc, 5, 32, TERM_L_BLUE);
902
903 /* Display the stats */
904 for (i = 0; i < A_MAX; i++)
905 {
906 /* Special treatment of "injured" stats */
907 if (p_ptr->stat_use[i] < p_ptr->stat_top[i])
908 {
909 int value;
910
911 /* Use lowercase stat name */
912 put_str_b(buffer,stat_names_reduced[i], 2 + i, 61);
913
914 /* Get the current stat */
915 value = p_ptr->stat_use[i];
916
917 /* Obtain the current stat (modified) */
918 cnv_stat(value, buf);
919
920 /* Display the current stat (modified) */
921 c_put_str_b(buffer,TERM_YELLOW, buf, 2 + i, 66);
922
923 /* Acquire the max stat */
924 value = p_ptr->stat_top[i];
925
926 /* Obtain the maximum stat (modified) */
927 cnv_stat(value, buf);
928
929 /* Display the maximum stat (modified) */
930 if (p_ptr->stat_max[i] == 18+100)
931 c_put_str_b(buffer,TERM_L_UMBER, buf, 2 + i, 73);
932 else
933 c_put_str_b(buffer,TERM_L_GREEN, buf, 2 + i, 73);
934 }
935
936 /* Normal treatment of "normal" stats */
937 else
938 {
939 /* Assume uppercase stat name */
940 put_str_b(buffer,stat_names[i], 2 + i, 61);
941
942 /* Obtain the current stat (modified) */
943 cnv_stat(p_ptr->stat_use[i], buf);
944
945 /* Display the current stat (modified) */
946 if (p_ptr->stat_max[i] == 18+100)
947 c_put_str_b(buffer,TERM_L_UMBER, buf, 2 + i, 66);
948 else
949 c_put_str_b(buffer,TERM_L_GREEN, buf, 2 + i, 66);
950 }
951 }
952
953 put_str_b(buffer,"(Miscellaneous Abilities)", 15, 25);
954
955 /* Display "skills" */
956 put_str_b(buffer,"Fighting :", 16, 1);
957 desc = likert(p_ptr->skill_thn, 12);
958 c_put_str_b(buffer,0, desc, 16, 15);
959
960 put_str_b(buffer,"Bows/Throw :", 17, 1);
961 desc = likert(p_ptr->skill_thb, 12);
962 c_put_str_b(buffer,0, desc, 17, 15);
963
964 put_str_b(buffer,"Saving Throw:", 18, 1);
965 desc = likert(p_ptr->skill_sav, 6);
966 c_put_str_b(buffer,0, desc, 18, 15);
967
968 put_str_b(buffer,"Stealth :", 19, 1);
969 desc = likert(p_ptr->skill_stl, 1);
970 c_put_str_b(buffer,0, desc, 19, 15);
971
972
973 put_str_b(buffer,"Perception :", 16, 28);
974 desc = likert(p_ptr->skill_fos, 6);
975 c_put_str_b(buffer,0, desc, 16, 42);
976
977 put_str_b(buffer,"Searching :", 17, 28);
978 desc = likert(p_ptr->skill_srh, 6);
979 c_put_str_b(buffer,0, desc, 17, 42);
980
981 put_str_b(buffer,"Disarming :", 18, 28);
982 desc = likert(p_ptr->skill_dis, 8);
983 c_put_str_b(buffer,0, desc, 18, 42);
984
985 put_str_b(buffer,"Magic Device:", 19, 28);
986 desc = likert(p_ptr->skill_dev, 6);
987 c_put_str_b(buffer,0, desc, 19, 42);
988
989
990 put_str_b(buffer,"Blows/Round:", 16, 55);
991 put_str_b(buffer,format("%d", p_ptr->num_blow), 16, 69);
992
993 put_str_b(buffer,"Shots/Round:", 17, 55);
994 put_str_b(buffer,format("%d", p_ptr->num_fire), 17, 69);
995
996 put_str_b(buffer,"Infra-Vision:", 19, 55);
997 put_str_b(buffer,format("%d feet", p_ptr->see_infra * 10), 19, 69);
998
999 /* Dump the bonuses to hit/dam */
1000 if (o_ptr->k_idx)
1001 {
1002 show_tohit += o_ptr->to_h;
1003 show_todam += o_ptr->to_d;
1004 }
1005 prt_num_b(buffer,"+ To Hit ", show_tohit, 9, 1, TERM_L_BLUE);
1006 prt_num_b(buffer,"+ To Damage ", show_todam, 10, 1, TERM_L_BLUE);
1007
1008 /* Dump the armor class bonus */
1009 prt_num_b(buffer,"+ To AC ", p_ptr->dis_to_a, 11, 1, TERM_L_BLUE);
1010
1011 /* Dump the total armor class */
1012 prt_num_b(buffer," Base AC ", p_ptr->dis_ac, 12, 1, TERM_L_BLUE);
1013
1014 prt_num_b(buffer,"Level ", (int)p_ptr->lev, 9, 28, TERM_L_GREEN);
1015
1016 if (p_ptr->exp >= p_ptr->max_exp)
1017 {
1018 prt_lnum_b(buffer,"Experience ", p_ptr->exp, 10, 28, TERM_L_GREEN);
1019 }
1020 else
1021 {
1022 prt_lnum_b(buffer,"Experience ", p_ptr->exp, 10, 28, TERM_YELLOW);
1023 }
1024
1025 prt_lnum_b(buffer,"Max Exp ", p_ptr->max_exp, 11, 28, TERM_L_GREEN);
1026
1027 if (p_ptr->lev >= PY_MAX_LEVEL)
1028 {
1029 put_str_b(buffer,"Exp to Adv.", 12, 28);
1030 c_put_str_b(buffer,TERM_L_GREEN, " *****", 12, 28+11);
1031 }
1032 else
1033 {
1034 /* prt_lnum_b(buffer,"Exp to Adv.", p_ptr->exp_adv, 12, 28, TERM_L_GREEN); */
1035 }
1036
1037 prt_lnum_b(buffer,"Gold ", p_ptr->au, 13, 28, TERM_L_GREEN);
1038
1039 prt_num_b(buffer,"Max Hit Points ", p_ptr->mhp, 9, 52, TERM_L_GREEN);
1040
1041 if (p_ptr->chp >= p_ptr->mhp)
1042 {
1043 prt_num_b(buffer,"Cur Hit Points ", p_ptr->chp, 10, 52, TERM_L_GREEN);
1044 }
1045 else if (p_ptr->chp > (p_ptr->mhp) / 10)
1046 {
1047 prt_num_b(buffer,"Cur Hit Points ", p_ptr->chp, 10, 52, TERM_YELLOW);
1048 }
1049 else
1050 {
1051 prt_num_b(buffer,"Cur Hit Points ", p_ptr->chp, 10, 52, TERM_RED);
1052 }
1053
1054 prt_num_b(buffer,"Max SP (Mana) ", p_ptr->msp, 11, 52, TERM_L_GREEN);
1055
1056 if (p_ptr->csp >= p_ptr->msp)
1057 {
1058 prt_num_b(buffer,"Cur SP (Mana) ", p_ptr->csp, 12, 52, TERM_L_GREEN);
1059 }
1060 else if (p_ptr->csp > (p_ptr->msp) / 10)
1061 {
1062 prt_num_b(buffer,"Cur SP (Mana) ", p_ptr->csp, 12, 52, TERM_YELLOW);
1063 }
1064 else
1065 {
1066 prt_num_b(buffer,"Cur SP (Mana) ", p_ptr->csp, 12, 52, TERM_RED);
1067 }
1068
1069 /* Check for history */
1070 put_str_b(buffer, "(Character Background)", 21, 25);
1071
1072 for (i = 0; i < 4; i++)
1073 {
1074 put_str_b(buffer,p_ptr->history[i], i + 22, 10);
1075 }
1076 }
1077
1078
1079 /*
1080 * Hack -- Dump a character description file
1081 * This is for server-side character dumps
1082 */
file_character_server(player_type * p_ptr,cptr name)1083 errr file_character_server(player_type *p_ptr, cptr name)
1084 {
1085 int i, j, x, y, x1, x2, y1, y2;
1086 byte a;
1087 char c, attr;
1088 cptr paren = ")";
1089 ang_file* fff = NULL;
1090 char o_name[80];
1091 char today[10];
1092 char buf[1024];
1093 cave_view_type status[80];
1094 char buffer[100][82];
1095 time_t ct = time((time_t*)0);
1096
1097 // Init buffer...
1098 for(i=0;i<100;i++)
1099 {
1100 for(x=0;x<80;x++)
1101 buffer[i][x] = ' ';
1102 buffer[i][80] = '\0';
1103 }
1104
1105 /* Drop priv's
1106 safe_setuid_drop(); */
1107
1108 /* Build the filename */
1109 path_build(buf, 1024, ANGBAND_DIR_BONE, name);
1110
1111 /* Grab priv's
1112 safe_setuid_grab(); */
1113
1114 /* Open the non-existing file */
1115 if (!file_exists(buf))
1116 {
1117 fff = file_open(buf, MODE_WRITE, FTYPE_TEXT);
1118 }
1119
1120 /* Invalid file */
1121 if (!fff)
1122 {
1123 /* Error */
1124 return (-1);
1125 }
1126
1127 /* Add ladder information, this line is used by the online ladder and
1128 * not displayed when viewing a character dump online.
1129 */
1130 strftime(today, 9, "%m/%d/%y", localtime(&ct));
1131 file_putf(fff, "# %lu|%lu|%-.8s|%-.25s|%c|%2d|%2d|%3d|%3d|%3d|%3d|%-.31s|%d.%d.%d\n",
1132 (long)total_points(p_ptr),
1133 (long)p_ptr->au,
1134 today,
1135 p_ptr->name,
1136 p_ptr->male ? 'm' : 'f',
1137 p_ptr->prace,
1138 p_ptr->pclass,
1139 p_ptr->lev,
1140 p_ptr->died_from_depth,
1141 p_ptr->max_plv,
1142 p_ptr->max_dlv,
1143 p_ptr->died_from_list,
1144 SERVER_VERSION_MAJOR, SERVER_VERSION_MINOR, SERVER_VERSION_PATCH);
1145
1146 #ifndef DEBUG
1147 /* Leave it at that for characters lower than level 20 */
1148 if( p_ptr->lev < 20 )
1149 {
1150 /* Close dump file */
1151 file_close(fff);
1152
1153 /* Success */
1154 return (0);
1155 }
1156 #endif
1157
1158 /* Begin dump */
1159 if (cfg_ironman)
1160 file_putf(fff, " [Ironman Mangband %d.%d.%d Character Dump]\n\n",
1161 SERVER_VERSION_MAJOR, SERVER_VERSION_MINOR, SERVER_VERSION_PATCH);
1162 else
1163 file_putf(fff, " [Mangband %d.%d.%d Character Dump]\n\n",
1164 SERVER_VERSION_MAJOR, SERVER_VERSION_MINOR, SERVER_VERSION_PATCH);
1165
1166 /* Display the player info */
1167 display_player_server(p_ptr, buffer);
1168
1169 /* Dump the buffer */
1170 for(i=0;i<26;i++)
1171 {
1172 file_putf(fff,"%s\n",buffer[i]);
1173 }
1174
1175 /* Dump the equipment */
1176 file_putf(fff, "%s", " [Character Equipment]\n\n");
1177 for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
1178 {
1179 object_desc(0, o_name, sizeof(o_name), &p_ptr->inventory[i], TRUE, 3);
1180 file_putf(fff, "%c%s %s\n",
1181 index_to_label(i), paren, o_name);
1182 }
1183 file_putf(fff, "%s", "\n\n");
1184
1185 /* Dump the inventory */
1186 file_putf(fff, "%s", " [Character Inventory]\n\n");
1187 for (i = 0; i < INVEN_PACK; i++)
1188 {
1189 object_desc(0, o_name, sizeof(o_name), &p_ptr->inventory[i], TRUE, 3);
1190 file_putf(fff, "%c%s %s\n",
1191 index_to_label(i), paren, o_name);
1192 }
1193 file_putf(fff, "%s", "\n\n");
1194
1195 /* Dump house inventory */
1196 file_putf(fff, "%s", " [Home Inventory]\n");
1197 for (i = 0; i < num_houses; i++)
1198 {
1199 if (house_owned_by(p_ptr, i))
1200 {
1201 int Depth = houses[i].depth;
1202 cave_type *c_ptr;
1203
1204 file_putf(fff, "%s", "\n"); j = 0;
1205 for(y=houses[i].y_1; y<=houses[i].y_2;y++)
1206 {
1207 for(x=houses[i].x_1; x<=houses[i].x_2;x++)
1208 {
1209 /* Paranoia -- unallocated Depth */
1210 if (!cave[Depth]) continue;
1211 c_ptr = &cave[Depth][y][x];
1212 if (c_ptr->o_idx)
1213 {
1214 if (j > 12) { file_putf(fff, "%s", "\n"); j = 0; }
1215 object_desc(0, o_name, sizeof(o_name), &o_list[c_ptr->o_idx], TRUE, 3);
1216 file_putf(fff, "%c%s %s\n",
1217 index_to_label(j), paren, o_name);
1218 j++;
1219 }
1220 }
1221 }
1222 }
1223 }
1224 file_putf(fff, "%s", "\n\n");
1225
1226 /* Dump character history */
1227 if(p_ptr->birth_turn.turn || p_ptr->birth_turn.era)
1228 {
1229 history_event *evt;
1230 file_putf(fff, "%s", " [Character History]\n\n");
1231 file_putf(fff, "%s", "Time Dungeon Char Event\n");
1232 file_putf(fff, "%s", " Level Level\n\n");
1233 for(evt = p_ptr->charhist; evt; evt = evt->next)
1234 {
1235 file_putf(fff, "%s\n", format_history_event(evt));
1236 }
1237 file_putf(fff, "%s", "\n\n");
1238 }
1239
1240 /* Dump last messages */
1241 file_putf(fff, "%s", " [Last Messages]\n\n");
1242 i = p_ptr->msg_hist_ptr;
1243 for(j=0;j<MAX_MSG_HIST;j++)
1244 {
1245 if(i >= MAX_MSG_HIST) i = 0;
1246 if(!STRZERO(p_ptr->msg_log[i]))
1247 file_putf(fff, "%s\n",p_ptr->msg_log[i]);
1248 i++;
1249 }
1250 file_putf(fff, "%s", "\n\n");
1251
1252 /* Dump the scene of death */
1253 file_putf(fff, "%s", " [Scene of Death]\n\n");
1254 /* Get an in bounds area */
1255 x1 = p_ptr->px - 39;
1256 x2 = p_ptr->px + 39;
1257 y1 = p_ptr->py - 10;
1258 y2 = p_ptr->py + 10;
1259 if (y1 < 0)
1260 {
1261 y2 = y2-y1;
1262 y1 = 0;
1263 }
1264 if (x1 < 0)
1265 {
1266 x2 = x2-x1;
1267 x1 = 0;
1268 }
1269 if (y2 > MAX_HGT-1)
1270 {
1271 y1 = y1 - (y2-(MAX_HGT-1));
1272 y2 = MAX_HGT-1;
1273 }
1274 if (x2 > MAX_WID-1)
1275 {
1276 x1 = x1 - (x2-(MAX_WID-1));
1277 x2 = MAX_WID-1;
1278 }
1279 /* Prepare status line */
1280 c_prt_status_line(p_ptr, &status[0], 80);
1281
1282 /* Describe each row */
1283 for(y=y1;y<=y2+1;y++)
1284 {
1285 for(x=x1;x<=x2;x++)
1286 {
1287 /* Get the features */
1288 if (y > y2)
1289 {
1290 /* Hack -- read from status line */
1291 if (x-x1 < 80) {
1292 a = status[x-x1].a;
1293 c = status[x-x1].c;
1294 } else {
1295 a = TERM_WHITE; c = ' ';
1296 }
1297 }
1298 else
1299 map_info(p_ptr, y, x, &a, &c, &a, &c, TRUE);
1300 /* Hack for the player who is already dead and gone */
1301 if( (x == p_ptr->px) && (y == p_ptr->py))
1302 {
1303 c = '@';
1304 a = 'W';
1305 }
1306 /* translate the attr */
1307 attr = 'w';
1308 switch (a)
1309 {
1310 case TERM_DARK: attr = 'd'; break;
1311 case TERM_WHITE: attr = 'w'; break;
1312 case TERM_SLATE: attr = 's'; break;
1313 case TERM_ORANGE: attr = 'o'; break;
1314 case TERM_RED: attr = 'r'; break;
1315 case TERM_GREEN: attr = 'g'; break;
1316 case TERM_BLUE: attr = 'b'; break;
1317 case TERM_UMBER: attr = 'u'; break;
1318 case TERM_L_DARK: attr = 'D'; break;
1319 case TERM_L_WHITE: attr = 'W'; break;
1320 case TERM_VIOLET: attr = 'v'; break;
1321 case TERM_YELLOW: attr = 'y'; break;
1322 case TERM_L_RED: attr = 'R'; break;
1323 case TERM_L_GREEN: attr = 'G'; break;
1324 case TERM_L_BLUE: attr = 'B'; break;
1325 case TERM_L_UMBER: attr = 'U'; break;
1326 }
1327 /* Config file controls if we output with color codes */
1328 if(cfg_chardump_color)
1329 {
1330 /* Output with attr colour code */
1331 file_putf(fff,"%c%c",attr,c);
1332 }
1333 else
1334 {
1335 /* Output plain ASCII */
1336 file_putf(fff,"%c",c);
1337 }
1338 }
1339 file_putf(fff, "%s", "\n");
1340 }
1341 file_putf(fff, "%s", "\n\n");
1342
1343
1344 /* Close it */
1345 file_close(fff);
1346
1347 /* Success */
1348 return (0);
1349 }
1350
1351
1352
1353
file_peruse_next(player_type * p_ptr,char query,int next)1354 int file_peruse_next(player_type *p_ptr, char query, int next)
1355 {
1356 /* Process query */
1357 if (query)
1358 {
1359 if (query == '1') /* 'End' */
1360 p_ptr->interactive_line = p_ptr->interactive_size-20;
1361 else /* Other keys */
1362 common_peruse(p_ptr, query);
1363
1364 /* Adjust viewport boundaries */
1365 if (p_ptr->interactive_line > p_ptr->interactive_size-20)
1366 p_ptr->interactive_line = p_ptr->interactive_size-20;
1367 if (p_ptr->interactive_line < 0)
1368 p_ptr->interactive_line = 0;
1369
1370 /* Shift window! */
1371 if ((p_ptr->interactive_line+20 > p_ptr->interactive_next+MAX_TXT_INFO)
1372 || (p_ptr->interactive_line < p_ptr->interactive_next))
1373 {
1374 next = p_ptr->interactive_line - MAX_TXT_INFO / 2;
1375 }
1376
1377 /* Adjust window boundaries */
1378 if (next > p_ptr->interactive_size - MAX_TXT_INFO)
1379 next = p_ptr->interactive_size - MAX_TXT_INFO;
1380 if (next < 0) next = 0;
1381 }
1382 return next;
1383 }
1384
1385
1386 /*
1387 * On-Line help.
1388 *
1389 * Process user commands, access sub-menu entries and browse files.
1390 * This function manages a virtual 'window' which buffers file
1391 * contents using "copy_file_info" function.
1392 */
common_file_peruse(player_type * p_ptr,char query)1393 void common_file_peruse(player_type *p_ptr, char query)
1394 {
1395 int next = p_ptr->interactive_next;
1396
1397 /* Enter sub-menu */
1398 if (isalpha(query))
1399 {
1400 /* Extract the requested menu item */
1401 int k = A2I(query);
1402
1403 /* Verify the menu item */
1404 if ((k >= 0) && (k <= 25) && !STRZERO(p_ptr->interactive_hook[k]))
1405 {
1406 /* Paranoia -- free string */
1407 string_free(p_ptr->interactive_file);
1408 /* Select that file */
1409 p_ptr->interactive_file = string_make(p_ptr->interactive_hook[k]);
1410 /* Hack: enforce update */
1411 p_ptr->interactive_next = -1;
1412 next = 0;
1413 /* Query processed */
1414 query = 0;
1415 }
1416 }
1417
1418 /* Use default file */
1419 if (!p_ptr->interactive_file)
1420 {
1421 /* Paranoia -- free string */
1422 string_free(p_ptr->interactive_file);
1423 /* Select default file */
1424 p_ptr->interactive_file = string_make("help.hlp");
1425 /* Hack: enforce update */
1426 p_ptr->interactive_next = -1;
1427 next = 0;
1428 }
1429
1430 /* We're just starting. Reset counter */
1431 if (!query)
1432 {
1433 p_ptr->interactive_line = 0;
1434 }
1435
1436 /* We're done. Clear file, exit */
1437 if (query == ESCAPE)
1438 {
1439 if (p_ptr->interactive_file)
1440 {
1441 string_free(p_ptr->interactive_file);
1442 p_ptr->interactive_file = NULL;
1443 }
1444 p_ptr->special_file_type = 0;
1445 return;
1446 }
1447
1448 /* Process query */
1449 if (query)
1450 {
1451 next = file_peruse_next(p_ptr, query, next);
1452 }
1453
1454 /* Hack -- something overwrote "info" */
1455 if (!p_ptr->last_info_line)
1456 {
1457 p_ptr->interactive_next = -1;
1458 }
1459
1460 /* Update file */
1461 if (next != p_ptr->interactive_next)
1462 {
1463 p_ptr->interactive_next = next;
1464 copy_file_info(p_ptr, p_ptr->interactive_file, next, 0);
1465 }
1466 }
1467
1468 /*
1469 * Read a file and copy a portion of it into player's "info[]" array.
1470 *
1471 * TODO: Add 'search' from do_cmd_help_aux()
1472 *
1473 */
copy_file_info(player_type * p_ptr,cptr name,int line,int color)1474 void copy_file_info(player_type *p_ptr, cptr name, int line, int color)
1475 {
1476 int i = 0, k;
1477
1478 /* Current help file */
1479 ang_file* fff = NULL;
1480
1481 /* Number of "real" lines passed by */
1482 int next = 0;
1483
1484 /* Path buffer */
1485 char path[1024];
1486
1487 /* General buffer */
1488 char buf[1024];
1489
1490 /* Strlen */
1491 int len;
1492
1493 /* Build the filename */
1494 path_build(path, 1024, ANGBAND_DIR_HELP, name);
1495
1496 /* Open the file */
1497 fff = file_open(path, MODE_READ, -1);
1498
1499 /* Oops */
1500 if (!fff)
1501 {
1502 /* Message */
1503 msg_format(p_ptr, "Cannot open '%s'.", name);
1504 msg_print(p_ptr, NULL);
1505
1506 /* Oops */
1507 return;
1508 }
1509
1510 /* Wipe the hooks */
1511 for (k = 0; k < 26; k++) p_ptr->interactive_hook[k][0] = '\0';
1512
1513 /* Parse the file */
1514 while (TRUE)
1515 {
1516 byte attr = TERM_WHITE;
1517
1518 /* Read a line or stop */
1519 if (!file_getl(fff, buf, 1024)) break;
1520
1521 /* XXX Parse "menu" items */
1522 if (prefix(buf, "***** "))
1523 {
1524 char b1 = '[', b2 = ']';
1525
1526 /* Notice "menu" requests */
1527 if ((buf[6] == b1) && isalpha(buf[7]) &&
1528 (buf[8] == b2) && (buf[9] == ' '))
1529 {
1530 /* Extract the menu item */
1531 k = A2I(buf[7]);
1532
1533 /* Store the menu item (if valid) */
1534 if ((k >= 0) && (k < 26))
1535 my_strcpy(p_ptr->interactive_hook[k], buf + 10, sizeof(p_ptr->interactive_hook[0]));
1536 }
1537
1538 /* Skip this */
1539 continue;
1540 }
1541
1542 /* Get length */
1543 len = strlen(buf);
1544
1545 /* Count the "real" lines */
1546 next++;
1547
1548 /* Wait for needed one */
1549 if (next <= line) continue;
1550
1551 /* Too much */
1552 if (i >= MAX_TXT_INFO) continue;
1553
1554 /* Extract color */
1555 if (color) attr = color_char_to_attr(buf[0]);
1556
1557 /* Clear rest of line with spaces */
1558 for (k = len; k < 80 + color; k++)
1559 {
1560 buf[k] = ' ';
1561 }
1562
1563 /* Dump the line */
1564 for (k = 0; k < 80; k++)
1565 {
1566 p_ptr->info[i][k].a = attr;
1567 p_ptr->info[i][k].c = buf[k+color];
1568 }
1569
1570 /* Count the "info[]" lines */
1571 i++;
1572 }
1573
1574 /* Save last "real" line */
1575 p_ptr->interactive_size = next;
1576
1577 /* Save last dumped line */
1578 p_ptr->last_info_line = i - 1;
1579
1580 /* Close the file */
1581 file_close(fff);
1582 }
1583
1584 #if 0
1585 /*
1586 * Recursive "help file" perusal. Return FALSE on "ESCAPE".
1587 *
1588 * XXX XXX XXX Consider using a temporary file.
1589 *
1590 */
1591 static bool do_cmd_help_aux(player_type *p_ptr, cptr name, cptr what, int line, int color)
1592 {
1593 int i, k;
1594
1595 /* Number of "real" lines passed by */
1596 int next = 0;
1597
1598 /* Number of "real" lines in the file */
1599 int size = 0;
1600
1601 /* Backup value for "line" */
1602 int back = 0;
1603
1604 /* This screen has sub-screens */
1605 bool menu = FALSE;
1606
1607 /* Current help file */
1608 ang_file* fff = NULL;
1609
1610 /* Find this string (if any) */
1611 cptr find = NULL;
1612
1613 /* Hold a string to find */
1614 char finder[128];
1615
1616 /* Hold a string to show */
1617 char shower[128];
1618
1619 /* Describe this thing */
1620 char caption[128];
1621
1622 /* Path buffer */
1623 char path[1024];
1624
1625 /* General buffer */
1626 char buf[1024];
1627
1628 /* Sub-menu information */
1629 char hook[10][32];
1630
1631
1632 /* Wipe finder */
1633 strcpy(finder, "");
1634
1635 /* Wipe shower */
1636 strcpy(shower, "");
1637
1638 /* Wipe caption */
1639 strcpy(caption, "");
1640
1641 /* Wipe the hooks */
1642 for (i = 0; i < 10; i++) hook[i][0] = '\0';
1643
1644
1645 /* Hack XXX XXX XXX */
1646 if (what)
1647 {
1648 /* Caption */
1649 strcpy(caption, what);
1650
1651 /* Access the "file" */
1652 strcpy(path, name);
1653
1654 /* Open */
1655 fff = file_open(path, MODE_READ, -1);
1656 }
1657
1658 /* Look in "help" */
1659 if (!fff)
1660 {
1661 /* Caption */
1662 sprintf(caption, "Help file '%s'", name);
1663
1664 /* Build the filename */
1665 path_build(path, 1024, ANGBAND_DIR_HELP, name);
1666
1667 /* Open the file */
1668 fff = file_open(path, MODE_READ, -1);
1669 }
1670
1671 /* Oops */
1672 if (!fff)
1673 {
1674 /* Message */
1675 msg_format(p_ptr, "Cannot open '%s'.", name);
1676 msg_print(Ind, NULL);
1677
1678 /* Oops */
1679 return (TRUE);
1680 }
1681
1682
1683 /* Pre-Parse the file */
1684 while (TRUE)
1685 {
1686 /* Read a line or stop */
1687 if (!file_getl(fff, buf, 1024)) break;
1688
1689 /* XXX Parse "menu" items */
1690 if (prefix(buf, "***** "))
1691 {
1692 char b1 = '[', b2 = ']';
1693
1694 /* Notice "menu" requests */
1695 if ((buf[6] == b1) && isdigit(buf[7]) &&
1696 (buf[8] == b2) && (buf[9] == ' '))
1697 {
1698 /* This is a menu file */
1699 menu = TRUE;
1700
1701 /* Extract the menu item */
1702 k = buf[7] - '0';
1703
1704 /* Extract the menu item */
1705 strcpy(hook[k], buf + 10);
1706 }
1707
1708 /* Skip this */
1709 continue;
1710 }
1711
1712 /* Count the "real" lines */
1713 next++;
1714 }
1715
1716 /* Save the number of "real" lines */
1717 size = next;
1718
1719
1720
1721 /* Display the file */
1722 #if 0
1723 while (TRUE)
1724 {
1725 /* Clear screen */
1726 Term_clear();
1727
1728 #endif
1729
1730 /* Restart when necessary */
1731 if (line >= size) Players[Ind]->interactive_line = line = 0;
1732
1733
1734 /* Re-open the file if needed */
1735 if (next > line)
1736 {
1737 /* Close it */
1738 file_close(fff);
1739
1740 /* Hack -- Re-Open the file */
1741 fff = file_open(path, MODE_READ, -1);
1742
1743 /* Oops */
1744 if (!fff) return (FALSE);
1745
1746 /* File has been restarted */
1747 next = 0;
1748 }
1749
1750 /* Skip lines if needed */
1751 for (; next < line; next++)
1752 {
1753 /* Skip a line */
1754 if (!file_getl(fff, buf, 1024)) break;
1755 }
1756
1757
1758 /* Dump the next 20 lines of the file */
1759 for (i = 0; i < 20; )
1760 {
1761 byte attr = TERM_WHITE;
1762
1763 /* Hack -- track the "first" line */
1764 if (!i) line = next;
1765
1766 /* Get a line of the file or stop */
1767 if (!file_getl(fff, buf, 1024)) break;
1768
1769 /* Hack -- skip "special" lines */
1770 if (prefix(buf, "***** ")) continue;
1771
1772 /* Count the "real" lines */
1773 next++;
1774
1775 /* Hack -- keep searching */
1776 if (find && !i && !strstr(buf, find)) continue;
1777
1778 /* Hack -- stop searching */
1779 find = NULL;
1780
1781 /* Extract color */
1782 if (color) attr = color_char_to_attr(buf[0]);
1783
1784 /* Hack -- show matches */
1785 if (shower[0] && strstr(buf, shower)) attr = TERM_YELLOW;
1786
1787 /* Dump the line */
1788 Send_special_line(Ind, size, i, attr, &buf[color]);
1789
1790 /* Count the printed lines */
1791 i++;
1792 }
1793
1794 /* Hack -- failed search */
1795 if (find)
1796 {
1797 bell();
1798 line = back;
1799 find = NULL;
1800 return (TRUE);
1801 }
1802
1803 /* Inform about empty file/list */
1804 if (!i)
1805 Send_special_line(Ind, 1, 0, TERM_WHITE, " (nothing)");
1806
1807 #if 0
1808 /* Show a general "title" */
1809 prt(format("[MAngband %d.%d.%d, %s, Line %d/%d]",
1810 SERVER_VERSION_MAJOR, SERVER_VERSION_MINOR, SERVER_VERSION_PATCH,
1811 caption, line, size), 0, 0);
1812
1813
1814 /* Prompt -- menu screen */
1815 if (menu)
1816 {
1817 /* Wait for it */
1818 prt("[Press a Number, or ESC to exit.]", 23, 0);
1819 }
1820
1821 /* Prompt -- small files */
1822 else if (size <= 20)
1823 {
1824 /* Wait for it */
1825 prt("[Press ESC to exit.]", 23, 0);
1826 }
1827
1828 /* Prompt -- large files */
1829 else
1830 {
1831 /* Wait for it */
1832 prt("[Press Return, Space, -, =, /, or ESC to exit.]", 23, 0);
1833 }
1834
1835 /* Get a keypress */
1836 k = inkey();
1837
1838 /* Hack -- return to last screen */
1839 if (k == '?') break;
1840
1841 /* Hack -- try showing */
1842 if (k == '=')
1843 {
1844 prt("Show: ", 23, 0);
1845 (void)askfor_aux(shower, 80);
1846 }
1847
1848 /* Hack -- try finding */
1849 if (k == '/')
1850 {
1851 prt("Find: ", 23, 0);
1852 if (askfor_aux(finder, 80))
1853 {
1854 find = finder;
1855 back = line;
1856 line = line + 1;
1857 }
1858 }
1859
1860 /* Hack -- go to a specific line */
1861 if (k == '#')
1862 {
1863 char tmp[80];
1864 prt("Goto Line: ", 23, 0);
1865 strcpy(tmp, "0");
1866 if (askfor_aux(tmp, 80))
1867 {
1868 line = atoi(tmp);
1869 }
1870 }
1871
1872 /* Hack -- go to a specific file */
1873 if (k == '%')
1874 {
1875 char tmp[80];
1876 prt("Goto File: ", 23, 0);
1877 strcpy(tmp, "help.hlp");
1878 if (askfor_aux(tmp, 80))
1879 {
1880 if (!do_cmd_help_aux(tmp, NULL, 0)) k = ESCAPE;
1881 }
1882 }
1883
1884 /* Hack -- Allow backing up */
1885 if (k == '-')
1886 {
1887 line = line - 10;
1888 if (line < 0) line = 0;
1889 }
1890
1891 /* Hack -- Advance a single line */
1892 if ((k == '\n') || (k == '\r'))
1893 {
1894 line = line + 1;
1895 }
1896
1897 /* Advance one page */
1898 if (k == ' ')
1899 {
1900 line = line + 20;
1901 }
1902
1903 /* Recurse on numbers */
1904 if (menu && isdigit(k) && hook[k-'0'][0])
1905 {
1906 /* Recurse on that file */
1907 if (!do_cmd_help_aux(hook[k-'0'], NULL, 0)) k = ESCAPE;
1908 }
1909
1910 /* Exit on escape */
1911 if (k == ESCAPE) break;
1912 }
1913
1914 #endif
1915
1916 /* Close the file */
1917 file_close(fff);
1918
1919 /* Escape */
1920 if (k == ESCAPE) return (FALSE);
1921
1922 /* Normal return */
1923 return (TRUE);
1924 }
1925
1926
1927 /*
1928 * Peruse the On-Line-Help, starting at the given file.
1929 *
1930 * Disabled --KLJ--
1931 */
1932 void do_cmd_help(player_type *p_ptr, int line)
1933 {
1934 cptr name = "help.hlp";
1935
1936 /* Peruse the main help file */
1937 (void)do_cmd_help_aux(p_ptr, name, NULL, line, FALSE);
1938 }
1939 #endif
1940
1941
1942 /*
1943 * Hack -- display the contents of a file on the screen
1944 *
1945 * XXX XXX XXX Use this function for commands such as the
1946 * "examine object" command.
1947 */
show_file(player_type * p_ptr,cptr name,cptr what,int line,int color)1948 errr show_file(player_type *p_ptr, cptr name, cptr what, int line, int color)
1949 {
1950 /* Prepare */
1951 clear_from(p_ptr, 0);
1952
1953 /* Peruse the requested file */
1954 copy_file_info(p_ptr, name, line, color);
1955
1956 /* Send header */
1957 Send_special_other(p_ptr, (char*)what);
1958
1959 /* Success */
1960 return (0);
1961 }
1962
1963 /* Given a player name in "nick_name", write out a new version to "wptr",
1964 * where a) all words are capitalized b) double-spaces are removed.
1965 * For example, "juG tHe brAve" should become "Jug The Brave".
1966 * If "bptr" is not NULL, the "base" version will be written to
1967 * it, replacing all spaces with underscore (-> "Jug_The_Brave").
1968 * It is assumed that the only characters that exist in "nick_name" are
1969 * letters (a-zA-Z), digits (0-9) and space ( ).
1970 * Some other function should've taken care of that.
1971 * It is assumed that "wptr" is at least of MAX_CHARS length.
1972 * It is assumed that "bptr" is at least of MAX_CHARS length.
1973 *
1974 * Note: because we actually want to allow names that do not follow
1975 * this format (i.e. "ZalPriest", "Master_of_Puppets"), this function
1976 * SHOULD NOT be called! However, if you're on an machine with
1977 * case-insensitive filesystem (looking at you, Win32), this function
1978 * MUST be called, as early as possible, to prevent savefile name collision.
1979 */
rewrite_player_name(char * wptr,char * bptr,const char * nick_name)1980 int rewrite_player_name(char *wptr, char *bptr, const char *nick_name)
1981 {
1982 /* Re-write nick, removing double spaces */
1983 const char *p;
1984 bool recap;
1985 const char *wptr_start;
1986 const char *bptr_start;
1987
1988 /* Nothing to do */
1989 if (!wptr && !bptr) return 0;
1990
1991 wptr_start = wptr;
1992 bptr_start = bptr;
1993
1994 if (wptr) *wptr = '\0';
1995 if (bptr) *bptr = '\0';
1996 recap = TRUE;
1997 for (p = nick_name; *p; p++)
1998 {
1999 char c = *p;
2000 if (c == ' ') /* A space */
2001 {
2002 if (recap) continue; /* Skip 2nd, 3rd, Nth space */
2003 recap = TRUE;
2004 }
2005 else /* It's a symbol or digit */
2006 {
2007 if (isalpha(c))
2008 {
2009 c = recap ? toupper(c) : tolower(c);
2010 }
2011 recap = FALSE;
2012 }
2013 /* Save one char */
2014 if (wptr) *wptr++ = c;
2015
2016 /* Save "base" version */
2017 if (bptr) *bptr++ = (c == ' ' ? '_': c);
2018 }
2019 /* Terminate strings */
2020 if (wptr) *wptr = '\0';
2021 if (bptr) *bptr = '\0';
2022
2023 /* Right-trim spaces */
2024 if (wptr) for ( ; wptr-- > wptr_start; )
2025 {
2026 if (*wptr == ' ') *wptr = '\0';
2027 else break;
2028 }
2029 if (bptr) for ( ; bptr-- > bptr_start; )
2030 {
2031 if (*bptr == '_') *bptr = '\0';
2032 else break;
2033 }
2034
2035 if (!wptr) return bptr - bptr_start;
2036 return wptr - wptr_start;
2037 }
2038
2039 /*
2040 * XXXXXXXXX
2041 */
process_player_name_aux(cptr name,cptr base,bool sf)2042 int process_player_name_aux(cptr name, cptr base, bool sf)
2043 {
2044 int i, k = 0;
2045 char local_base[MAX_CHARS];
2046 char *basename = (char*) base;
2047
2048
2049 /* Cannot be too long */
2050 if (strlen(name) > MAX_NAME_LEN)
2051 {
2052 /* Abort */
2053 return -1;
2054 }
2055
2056 /* Cannot contain "icky" characters */
2057 for (i = 0; name[i]; i++)
2058 {
2059 /* No control characters */
2060 if (iscntrl(name[i]))
2061 {
2062 /* Abort */
2063 return -2;
2064 }
2065 }
2066
2067 if (base == NULL) basename = &local_base[0];
2068
2069 #ifdef MACINTOSH
2070
2071 /* Extract "useful" letters */
2072 for (i = 0; name[i]; i++)
2073 {
2074 char c = name[i];
2075
2076 /* Convert "dot" to "underscore" */
2077 if (c == '.') c = '_';
2078
2079 /* Accept all the letters */
2080 basename[k++] = c;
2081 }
2082
2083 #else
2084
2085 /* Extract "useful" letters */
2086 for (i = 0; name[i]; i++)
2087 {
2088 char c = name[i];
2089
2090 /* Accept some letters */
2091 if (isalpha(c) || isdigit(c)) basename[k++] = c;
2092
2093 /* Convert space, dot, and underscore to underscore */
2094 else if (strchr(". _", c)) basename[k++] = '_';
2095 }
2096
2097 #endif
2098
2099 /* Terminate */
2100 basename[k] = '\0';
2101
2102 /* Require a "base" name */
2103 if (!basename[0]) strcpy(basename, "PLAYER");
2104
2105 /* Change the savefile name */
2106 if (sf)
2107 {
2108 char temp[128];
2109
2110 /* Rename the savefile, using the player_base */
2111 (void)sprintf(temp, "%s", basename);
2112
2113 #ifdef VM
2114 /* Hack -- support "flat directory" usage on VM/ESA */
2115 (void)sprintf(temp, "%s.sv", player_base);
2116 #endif /* VM */
2117
2118 /* Build the filename */
2119 path_build((char*)name, 1024, ANGBAND_DIR_SAVE, temp);
2120 }
2121
2122 /* Success */
2123 return 0;
2124 }
2125
2126
2127
2128 /*
2129 * Process the player name.
2130 * Extract a clean "base name".
2131 * Build the savefile name if needed.
2132 */
process_player_name(player_type * p_ptr,bool sf)2133 bool process_player_name(player_type *p_ptr, bool sf)
2134 {
2135 int ret;
2136
2137 #ifdef SAVEFILE_MUTABLE
2138
2139 /* Accept */
2140 sf = TRUE;
2141
2142 #endif
2143
2144 ret = process_player_name_aux(p_ptr->name, p_ptr->basename, FALSE);
2145
2146 /* Name is too long or contained illegal characters */
2147 if (ret < 0)
2148 {
2149 /* Abort */
2150 return FALSE;
2151 }
2152
2153 /* Change the savefile name */
2154 if (sf)
2155 {
2156 char temp[128];
2157
2158 /* Rename the savefile, using the player_base */
2159 (void)sprintf(temp, "%s", p_ptr->basename);
2160
2161 #ifdef VM
2162 /* Hack -- support "flat directory" usage on VM/ESA */
2163 (void)sprintf(temp, "%s.sv", player_base);
2164 #endif /* VM */
2165
2166 /* Build the filename */
2167 path_build(p_ptr->savefile, 1024, ANGBAND_DIR_SAVE, temp);
2168 }
2169
2170 /* Success */
2171 return TRUE;
2172 }
2173
2174
2175 /*
2176 * Gets a name for the character, reacting to name changes.
2177 *
2178 * Assumes that "display_player()" has just been called
2179 * XXX Perhaps we should NOT ask for a name (at "birth()") on Unix?
2180 *
2181 * The name should be sent to us from the client, so this is unnecessary --KLJ--
2182 */
get_name(player_type * p_ptr)2183 void get_name(player_type *p_ptr)
2184 {
2185 }
2186
2187
2188
2189 /*
2190 * Hack -- commit suicide
2191 */
do_cmd_suicide(player_type * p_ptr)2192 void do_cmd_suicide(player_type *p_ptr)
2193 {
2194 /* Mark as suicide */
2195 p_ptr->alive = FALSE;
2196
2197 /* Hack -- set the cause of death */
2198 if (!p_ptr->ghost)
2199 {
2200 strcpy(p_ptr->died_from_list, "self-inflicted wounds");
2201 p_ptr->died_from_depth = p_ptr->dun_depth;
2202 }
2203
2204 /* Hack -- clear ghost */
2205 p_ptr->ghost = FALSE;
2206
2207 if (p_ptr->total_winner) kingly(p_ptr);
2208
2209 /* Queue "death", to be handled in dungeon() tick, so it happens inside game turns */
2210 p_ptr->death = TRUE;
2211 }
2212
2213
2214
2215 /*
2216 * Save a character
2217 */
do_cmd_save_game(player_type * p_ptr)2218 void do_cmd_save_game(player_type *p_ptr)
2219 {
2220 /* Disturb the player */
2221 disturb(p_ptr, 1, 0);
2222
2223 /* Clear messages */
2224 msg_print(p_ptr, NULL);
2225
2226 /* Handle stuff */
2227 handle_stuff(p_ptr);
2228
2229 /* Message */
2230 msg_print(p_ptr, "Saving game...");
2231
2232 /* Refresh */
2233 /*Term_fresh();*/
2234
2235 /* The player is not dead */
2236 (void)strcpy(p_ptr->died_from, "(saved)");
2237
2238 /* Forbid suspend */
2239 signals_ignore_tstp();
2240
2241 /* Save the player */
2242 if (save_player(p_ptr))
2243 {
2244 msg_print(p_ptr, "Saving game... done.");
2245 }
2246
2247 /* Save failed (oops) */
2248 else
2249 {
2250 msg_print(p_ptr, "Saving game... failed!");
2251 }
2252
2253 /* Allow suspend again */
2254 signals_handle_tstp();
2255
2256 /* Refresh */
2257 /*Term_fresh();*/
2258
2259 /* Note that the player is not dead */
2260 (void)strcpy(p_ptr->died_from, "(alive and well)");
2261 }
2262
2263
2264
2265 /*
2266 * Hack -- Calculates the total number of points earned -JWT-
2267 */
total_points(player_type * p_ptr)2268 long total_points(player_type *p_ptr)
2269 {
2270 /* Standard scoring */
2271 long score = (p_ptr->max_exp + (100 * p_ptr->max_dlv));
2272
2273 /* Remove 10% for energy_buildup */
2274 if (option_p(p_ptr,ENERGY_BUILDUP))
2275 {
2276 score = (long)(score * 0.9);
2277 }
2278
2279 /* Add 50% for unburdened monsters */
2280 if (!option_p(p_ptr,MONSTER_RECOIL))
2281 {
2282 score = (long)(score * 1.5);
2283 }
2284
2285 /* We award a 50% score bonus for bravery with no_ghost characters */
2286 if (option_p(p_ptr, NO_GHOST) && !cfg_ironman)
2287 {
2288 score = (long)(score * 1.5);
2289 }
2290
2291 /* Standard scoring */
2292 return score;
2293 }
2294
2295
2296
2297
2298 /*
2299 * Display some character info
2300 *
2301 * FIXME -- This is very broken. There is no home. There is no way to get
2302 * most of this information transferred to the client. This isn't used all
2303 * that often. I'll worry about it later. --KLJ--
2304 */
show_info(player_type * p_ptr)2305 static void show_info(player_type *p_ptr)
2306 {
2307 #if 0
2308 int i, j, k;
2309
2310 object_type *o_ptr;
2311
2312 store_type *st_ptr = &store[7];
2313
2314
2315 /* Hack -- Know everything in the inven/equip */
2316 for (i = 0; i < INVEN_TOTAL; i++)
2317 {
2318 o_ptr = &inventory[i];
2319 if (o_ptr->k_idx)
2320 {
2321 object_aware(p_ptr, o_ptr);
2322 object_known(o_ptr);
2323 }
2324 }
2325
2326 /* Hack -- Know everything in the home */
2327 for (i = 0; i < st_ptr->stock_num; i++)
2328 {
2329 o_ptr = &st_ptr->stock[i];
2330 if (o_ptr->k_idx)
2331 {
2332 object_aware(p_ptr, o_ptr);
2333 object_known(o_ptr);
2334 }
2335 }
2336
2337 /* Hack -- Recalculate bonuses */
2338 p_ptr->update |= (PU_BONUS);
2339
2340 /* Handle stuff */
2341 handle_stuff();
2342
2343 /* Flush all input keys */
2344 flush();
2345
2346 /* Flush messages */
2347 msg_print(NULL);
2348
2349
2350 /* Describe options */
2351 prt("You may now dump a character record to one or more files.", 21, 0);
2352 prt("Then, hit RETURN to see the character, or ESC to abort.", 22, 0);
2353
2354 /* Dump character records as requested */
2355 while (TRUE)
2356 {
2357 char out_val[160];
2358
2359 /* Prompt */
2360 put_str("Filename: ", 23, 0);
2361
2362 /* Default */
2363 strcpy(out_val, "");
2364
2365 /* Ask for filename (or abort) */
2366 if (!askfor_aux(out_val, 60)) return;
2367
2368 /* Return means "show on screen" */
2369 if (!out_val[0]) break;
2370
2371 /* Dump a character file */
2372 (void)file_character(out_val, FALSE);
2373 }
2374
2375
2376 /* Show player on screen */
2377 display_player(FALSE);
2378
2379 /* Prompt for inventory */
2380 prt("Hit any key to see more information (ESC to abort): ", 23, 0);
2381
2382 /* Allow abort at this point */
2383 if (inkey() == ESCAPE) return;
2384
2385
2386 /* Show equipment and inventory */
2387
2388 /* Equipment -- if any */
2389 if (equip_cnt)
2390 {
2391 Term_clear();
2392 item_tester_full = TRUE;
2393 show_equip();
2394 prt("You are using: -more-", 0, 0);
2395 if (inkey() == ESCAPE) return;
2396 }
2397
2398 /* Inventory -- if any */
2399 if (inven_cnt)
2400 {
2401 Term_clear();
2402 item_tester_full = TRUE;
2403 show_inven();
2404 prt("You are carrying: -more-", 0, 0);
2405 if (inkey() == ESCAPE) return;
2406 }
2407
2408
2409
2410 /* Home -- if anything there */
2411 if (st_ptr->stock_num)
2412 {
2413 /* Display contents of the home */
2414 for (k = 0, i = 0; i < st_ptr->stock_num; k++)
2415 {
2416 /* Clear screen */
2417 Term_clear();
2418
2419 /* Show 12 items */
2420 for (j = 0; (j < 12) && (i < st_ptr->stock_num); j++, i++)
2421 {
2422 char o_name[80];
2423 char tmp_val[80];
2424
2425 /* Acquire item */
2426 o_ptr = &st_ptr->stock[i];
2427
2428 /* Print header, clear line */
2429 sprintf(tmp_val, "%c) ", I2A(j));
2430 prt(tmp_val, j+2, 4);
2431
2432 /* Display object description */
2433 object_desc(o_name, o_ptr, TRUE, 3);
2434 c_put_str(tval_to_attr[o_ptr->tval], o_name, j+2, 7);
2435 }
2436
2437 /* Caption */
2438 prt(format("Your home contains (page %d): -more-", k+1), 0, 0);
2439
2440 /* Wait for it */
2441 if (inkey() == ESCAPE) return;
2442 }
2443 }
2444 #endif
2445 }
2446
2447
2448
2449
2450
2451 /*
2452 * Semi-Portable High Score List Entry (128 bytes) -- BEN
2453 *
2454 * All fields listed below are null terminated ascii strings.
2455 *
2456 * In addition, the "number" fields are right justified, and
2457 * space padded, to the full available length (minus the "null").
2458 *
2459 * Note that "string comparisons" are thus valid on "pts".
2460 */
2461
2462 typedef struct high_score high_score;
2463
2464 struct high_score
2465 {
2466 char what[8]; /* Version info (string) */
2467
2468 char pts[10]; /* Total Score (number) */
2469
2470 char gold[10]; /* Total Gold (number) */
2471
2472 char turns[20]; /* Turns Taken (number) */
2473
2474 char day[10]; /* Time stamp (string) */
2475
2476 char who[16]; /* Player Name (string) */
2477
2478 char uid[8]; /* Player UID (number) */
2479
2480 char sex[2]; /* Player Sex (string) */
2481 char p_r[3]; /* Player Race (number) */
2482 char p_c[3]; /* Player Class (number) */
2483
2484 char cur_lev[4]; /* Current Player Level (number) */
2485 char cur_dun[4]; /* Current Dungeon Level (number) */
2486 char max_lev[4]; /* Max Player Level (number) */
2487 char max_dun[4]; /* Max Dungeon Level (number) */
2488
2489 char how[32]; /* Method of death (string) */
2490 };
2491
2492
2493
2494 /*
2495 * The "highscore" file descriptor, if available.
2496 */
2497 static ang_file* highscore_fd = NULL;
2498
2499
2500 /*
2501 * Seek score 'i' in the highscore file
2502 */
highscore_seek(int i)2503 static int highscore_seek(int i)
2504 {
2505 /* Seek for the requested record */
2506 return (file_seek(highscore_fd, (huge)(i) * sizeof(high_score))) ? 0 : -1;
2507 }
2508
2509
2510 /*
2511 * Read one score from the highscore file
2512 */
highscore_read(high_score * score)2513 static errr highscore_read(high_score *score)
2514 {
2515 /* Read the record, note failure */
2516 return (file_read(highscore_fd, (char*)(score), sizeof(high_score))) > 0 ? 0 : -1;
2517 }
2518
2519
2520 /*
2521 * Write one score to the highscore file
2522 */
highscore_write(high_score * score)2523 static int highscore_write(high_score *score)
2524 {
2525 /* Write the record, note failure */
2526 return (file_write(highscore_fd, (char*)(score), sizeof(high_score))) ? 0 : -1;
2527 }
2528
2529
2530
2531
2532 /*
2533 * Just determine where a new score *would* be placed
2534 * Return the location (0 is best) or -1 on failure
2535 */
highscore_where(high_score * score)2536 static int highscore_where(high_score *score)
2537 {
2538 int i;
2539
2540 high_score the_score;
2541
2542 /* Paranoia -- it may not have opened */
2543 if (highscore_fd == NULL) return (-1);
2544
2545 /* Go to the start of the highscore file */
2546 if (highscore_seek(0)) return (-1);
2547
2548 /* Read until we get to a higher score */
2549 for (i = 0; i < MAX_HISCORES; i++)
2550 {
2551 if (highscore_read(&the_score)) return (i);
2552 if (strcmp(the_score.pts, score->pts) < 0) return (i);
2553 }
2554
2555 /* The "last" entry is always usable */
2556 return (MAX_HISCORES - 1);
2557 }
2558
2559
2560 /*
2561 * Actually place an entry into the high score file
2562 * Return the location (0 is best) or -1 on "failure"
2563 */
highscore_add(high_score * score)2564 static int highscore_add(high_score *score)
2565 {
2566 int i, slot;
2567 bool done = FALSE;
2568
2569 high_score the_score, tmpscore;
2570
2571
2572 /* Paranoia -- it may not have opened */
2573 if (highscore_fd == NULL) return (-1);
2574
2575 /* Determine where the score should go */
2576 slot = highscore_where(score);
2577
2578 /* Hack -- Not on the list */
2579 if (slot < 0) return (-1);
2580
2581 /* Hack -- prepare to dump the new score */
2582 the_score = (*score);
2583
2584 /* Slide all the scores down one */
2585 for (i = slot; !done && (i < MAX_HISCORES); i++)
2586 {
2587 /* Read the old guy, note errors */
2588 if (highscore_seek(i)) return (-1);
2589 if (highscore_read(&tmpscore)) done = TRUE;
2590
2591 /* Back up and dump the score we were holding */
2592 if (highscore_seek(i)) return (-1);
2593 if (highscore_write(&the_score)) return (-1);
2594
2595 /* Hack -- Save the old score, for the next pass */
2596 the_score = tmpscore;
2597 }
2598
2599 /* Return location used */
2600 return (slot);
2601 }
2602
2603
2604
2605 /*
2606 * Display the scores in a given range.
2607 * Assumes the high score list is already open.
2608 * Only five entries per line, too much info.
2609 *
2610 * Mega-Hack -- allow "fake" entry at the given position.
2611 */
display_scores_aux(player_type * p_ptr,int line,int note,high_score * score)2612 static void display_scores_aux(player_type *p_ptr, int line, int note, high_score *score)
2613 {
2614 int i, j, from, to, attr, place;
2615
2616 high_score the_score;
2617
2618 char out_val[256];
2619
2620 ang_file* fff;
2621 char file_name[1024];
2622
2623 /* Paranoia -- it may not have opened */
2624 if (highscore_fd < 0) return;
2625
2626 /* Seek to the beginning (or abort) */
2627 if (highscore_seek(0)) return;
2628
2629 /* Temporary file */
2630 if (path_temp(file_name, 1024)) return;
2631
2632 /* Open the temp file */
2633 fff = file_open(file_name, MODE_WRITE, FTYPE_TEXT);
2634
2635 /* Paranoia */
2636 if (!fff)
2637 {
2638 plog(format("ERROR! %s (writing %s)", strerror(errno), file_name));
2639 return;
2640 }
2641
2642 /* Assume we will show the first 20 */
2643 from = 0;
2644 to = 20;
2645 if (to > MAX_HISCORES) to = MAX_HISCORES;
2646
2647
2648 /* Hack -- Count the high scores */
2649 for (i = 0; i < MAX_HISCORES; i++)
2650 {
2651 if (highscore_read(&the_score)) break;
2652 }
2653
2654 /* Hack -- allow "fake" entry to be last */
2655 if ((note == i) && score) i++;
2656
2657 /* Forget about the last entries */
2658 if (i > to) i = to;
2659
2660
2661 /* Show 5 per page, until "done" */
2662 for (j = from, place = j+1; j < i; j++, place++)
2663 {
2664 int pr, pc, clev, mlev, cdun, mdun;
2665
2666 cptr user, gold, when, aged;
2667
2668
2669 /* Hack -- indicate death in yellow */
2670 attr = (j == note) ? TERM_YELLOW : TERM_WHITE;
2671
2672
2673 /* Mega-Hack -- insert a "fake" record */
2674 if ((note == j) && score)
2675 {
2676 the_score = (*score);
2677 attr = TERM_L_GREEN;
2678 score = NULL;
2679 note = -1;
2680 j--;
2681 }
2682
2683 /* Read a normal record */
2684 else
2685 {
2686 /* Read the proper record */
2687 if (highscore_seek(j)) break;
2688 if (highscore_read(&the_score)) break;
2689 }
2690
2691 /* Extract the race/class */
2692 pr = atoi(the_score.p_r);
2693 pc = atoi(the_score.p_c);
2694
2695 /* Extract the level info */
2696 clev = atoi(the_score.cur_lev);
2697 mlev = atoi(the_score.max_lev);
2698 cdun = atoi(the_score.cur_dun);
2699 mdun = atoi(the_score.max_dun);
2700
2701 /* Hack -- extract the gold and such */
2702 for (user = the_score.uid; isspace(*user); user++) /* loop */;
2703 for (when = the_score.day; isspace(*when); when++) /* loop */;
2704 for (gold = the_score.gold; isspace(*gold); gold++) /* loop */;
2705 for (aged = the_score.turns; isspace(*aged); aged++) /* loop */;
2706
2707 /* Dump some info */
2708 sprintf(out_val, "%3d.%9s %s the %s %s, Level %d",
2709 place, the_score.pts, the_score.who,
2710 p_name + p_info[pr].name, c_name + c_info[pc].name,
2711 clev);
2712
2713 /* Append a "maximum level" */
2714 if (mlev > clev) my_strcat(out_val, format(" (Max %d)", mlev), sizeof(out_val));
2715
2716 /* Dump the first line */
2717 file_putf(fff, "%s\n", out_val);
2718
2719 /* Another line of info */
2720 if (strcmp(the_score.how, "winner"))
2721 sprintf(out_val, " Killed by %s on %s %d",
2722 the_score.how, "Dungeon Level", cdun);
2723 else
2724 sprintf(out_val, " Retired after a legendary career");
2725
2726 /* Hack -- some people die in the town */
2727 if ((!cdun) && (strcmp(the_score.how, "winner")))
2728 {
2729 sprintf(out_val, " Killed by %s in the Town",
2730 the_score.how);
2731 }
2732
2733 /* Append a "maximum level" */
2734 if (mdun > cdun) strcat(out_val, format(" (Max %d)", mdun));
2735
2736 /* Dump the info */
2737 file_putf(fff, "%s\n", out_val);
2738
2739 /* And still another line of info */
2740 sprintf(out_val,
2741 " (User %s, Date %s, Gold %s, Turn %s).",
2742 user, when, gold, aged);
2743 file_putf(fff, "%s\n", out_val);
2744
2745 /* Print newline if this isn't the last one */
2746 if (j < i - 1)
2747 file_putf(fff, "%s", "\n");
2748 }
2749
2750 /* Close the file */
2751 file_close(fff);
2752
2753 /* Display the file contents */
2754 show_file(p_ptr, file_name, "High Scores", line, 0);
2755
2756 /* Remove the file */
2757 file_delete(file_name);
2758 }
2759
2760
2761
2762
2763 /*
2764 * Enters a players name on a hi-score table, if "legal", and in any
2765 * case, displays some relevant portion of the high score list.
2766 *
2767 * Assumes "signals_ignore_tstp()" has been called.
2768 */
top_twenty(player_type * p_ptr)2769 static errr top_twenty(player_type *p_ptr)
2770 {
2771 int j;
2772
2773 high_score the_score;
2774
2775 time_t ct = time((time_t*)0);
2776
2777
2778 /* Clear screen */
2779 /*Term_clear();*/
2780
2781 /* No score file */
2782 if (highscore_fd == NULL)
2783 {
2784 plog("Score file unavailable.");
2785 return (0);
2786 }
2787
2788 #ifndef SCORE_WIZARDS
2789 /* Wizard-mode pre-empts scoring */
2790 if (p_ptr->noscore & 0x000F)
2791 {
2792 msg_print(p_ptr, "Score not registered for wizards.");
2793 /*display_scores_aux(0, 10, -1, NULL);*/
2794 return (0);
2795 }
2796 #endif
2797
2798 #ifndef SCORE_BORGS
2799 /* Borg-mode pre-empts scoring */
2800 if (p_ptr->noscore & 0x00F0)
2801 {
2802 msg_print(p_ptr, "Score not registered for borgs.");
2803 /*display_scores_aux(0, 10, -1, NULL);*/
2804 return (0);
2805 }
2806 #endif
2807
2808 #ifndef SCORE_CHEATERS
2809 /* Cheaters are not scored */
2810 if (p_ptr->noscore & 0xFF00)
2811 {
2812 msg_print(p_ptr, "Score not registered for cheaters.");
2813 /*display_scores_aux(0, 10, -1, NULL);*/
2814 return (0);
2815 }
2816 #endif
2817
2818 /* Interupted */
2819 if (!p_ptr->total_winner && streq(p_ptr->died_from, "Interrupting"))
2820 {
2821 msg_print(p_ptr, "Score not registered due to interruption.");
2822 /* display_scores_aux(0, 10, -1, NULL); */
2823 return (0);
2824 }
2825
2826 /* Quitter */
2827 if (!p_ptr->total_winner && streq(p_ptr->died_from, "Quitting"))
2828 {
2829 msg_print(p_ptr, "Score not registered due to quitting.");
2830 /* display_scores_aux(0, 10, -1, NULL); */
2831 return (0);
2832 }
2833
2834
2835 /* Clear the record */
2836 WIPE(&the_score, high_score);
2837
2838 /* Save the version */
2839 sprintf(the_score.what, "%u.%u.%u",
2840 SERVER_VERSION_MAJOR, SERVER_VERSION_MINOR, SERVER_VERSION_PATCH);
2841
2842 /* Calculate and save the points */
2843 sprintf(the_score.pts, "%9lu", (long)total_points(p_ptr));
2844 the_score.pts[9] = '\0';
2845
2846 /* Save the current gold */
2847 sprintf(the_score.gold, "%9lu", (long)p_ptr->au);
2848 the_score.gold[9] = '\0';
2849
2850 /* Save the current turn */
2851 my_strcpy(the_score.turns, ht_show(&turn,0), sizeof(the_score.turns));
2852
2853 #ifdef HIGHSCORE_DATE_HACK
2854 /* Save the date in a hacked up form (9 chars) */
2855 sprintf(the_score.day, "%-.6s %-.2s", ctime(&ct) + 4, ctime(&ct) + 22);
2856 #else
2857 /* Save the date in standard form (8 chars) */
2858 strftime(the_score.day, 9, "%m/%d/%y", localtime(&ct));
2859 #endif
2860
2861 /* Save the player name (15 chars) */
2862 sprintf(the_score.who, "%-.15s", p_ptr->name);
2863
2864 /* Save the player info */
2865 sprintf(the_score.uid, "%7u", 0 /*player_uid*/);
2866 sprintf(the_score.sex, "%c", (p_ptr->male ? 'm' : 'f'));
2867 sprintf(the_score.p_r, "%2d", p_ptr->prace);
2868 sprintf(the_score.p_c, "%2d", p_ptr->pclass);
2869
2870 /* Save the level and such */
2871 sprintf(the_score.cur_lev, "%3d", p_ptr->lev);
2872 sprintf(the_score.cur_dun, "%3d", p_ptr->died_from_depth);
2873 sprintf(the_score.max_lev, "%3d", p_ptr->max_plv);
2874 sprintf(the_score.max_dun, "%3d", p_ptr->max_dlv);
2875
2876 /* Save the cause of death (31 chars) */
2877 /* HACKED to take the saved cause of death of the character, not the ghost */
2878 sprintf(the_score.how, "%-.31s", p_ptr->died_from_list);
2879
2880
2881 /* Lock (for writing) the highscore file, or fail */
2882 if (!file_lock(highscore_fd)) return (1);
2883
2884 /* Add a new entry to the score list, see where it went */
2885 j = highscore_add(&the_score);
2886
2887 /* Unlock the highscore file, or fail */
2888 if (!file_unlock(highscore_fd)) return (1);
2889
2890
2891 #if 0
2892 /* Hack -- Display the top fifteen scores */
2893 if (j < 10)
2894 {
2895 display_scores_aux(0, 15, j, NULL);
2896 }
2897
2898 /* Display the scores surrounding the player */
2899 else
2900 {
2901 display_scores_aux(0, 5, j, NULL);
2902 display_scores_aux(j - 2, j + 7, j, NULL);
2903 }
2904 #endif
2905
2906
2907 /* Success */
2908 return (0);
2909 }
2910
2911
2912 /*
2913 * Predict the players location, and display it.
2914 */
predict_score(player_type * p_ptr,int line)2915 static errr predict_score(player_type *p_ptr, int line)
2916 {
2917 int j;
2918
2919 high_score the_score;
2920
2921
2922 /* No score file */
2923 if (highscore_fd < 0)
2924 {
2925 plog("Score file unavailable.");
2926 return (0);
2927 }
2928
2929
2930 /* Save the version */
2931 sprintf(the_score.what, "%u.%u.%u",
2932 SERVER_VERSION_MAJOR, SERVER_VERSION_MINOR, SERVER_VERSION_PATCH);
2933
2934 /* Calculate and save the points */
2935 sprintf(the_score.pts, "%9lu", (long)total_points(p_ptr));
2936
2937 /* Save the current gold */
2938 sprintf(the_score.gold, "%9lu", (long)p_ptr->au);
2939
2940 /* Save the current turn */
2941 my_strcpy(the_score.turns, ht_show(&turn,0), sizeof(the_score.turns));
2942
2943 /* Hack -- no time needed */
2944 strcpy(the_score.day, "TODAY");
2945
2946 /* Save the player name (15 chars) */
2947 sprintf(the_score.who, "%-.15s", p_ptr->name);
2948
2949 /* Save the player info */
2950 sprintf(the_score.uid, "%7u", 0 /*player_uid*/);
2951 sprintf(the_score.sex, "%c", (p_ptr->male ? 'm' : 'f'));
2952 sprintf(the_score.p_r, "%2d", p_ptr->prace);
2953 sprintf(the_score.p_c, "%2d", p_ptr->pclass);
2954
2955 /* Save the level and such */
2956 sprintf(the_score.cur_lev, "%3d", p_ptr->lev);
2957 sprintf(the_score.cur_dun, "%3d", p_ptr->dun_depth);
2958 sprintf(the_score.max_lev, "%3d", p_ptr->max_plv);
2959 sprintf(the_score.max_dun, "%3d", p_ptr->max_dlv);
2960
2961 /* Hack -- no cause of death */
2962 strcpy(the_score.how, "nobody (yet!)");
2963
2964
2965 /* See where the entry would be placed */
2966 j = highscore_where(&the_score);
2967
2968
2969 /* Hack -- Display the top fifteen scores */
2970 if (j < 10)
2971 {
2972 display_scores_aux(p_ptr, line, j, &the_score);
2973 }
2974
2975 /* Display some "useful" scores */
2976 else
2977 {
2978 display_scores_aux(p_ptr, line, -1, NULL);
2979 }
2980
2981
2982 /* Success */
2983 return (0);
2984 }
2985
2986
2987
2988
2989 /*
2990 * Change a player into a King! -RAK-
2991 */
kingly(player_type * p_ptr)2992 void kingly(player_type *p_ptr)
2993 {
2994 /* Fake death */
2995 //(void)strcpy(p_ptr->died_from_list, "Ripe Old Age");
2996 (void)strcpy(p_ptr->died_from_list, "winner");
2997
2998 /* Restore the experience */
2999 p_ptr->exp = p_ptr->max_exp;
3000
3001 /* Restore the level */
3002 p_ptr->lev = p_ptr->max_plv;
3003
3004 /* Hack -- Player gets an XP bonus for beating the game */
3005 p_ptr->exp = p_ptr->max_exp += 10000000L;
3006
3007 /* Hack -- Ensure we are retired */
3008 p_ptr->retire_timer = 0;
3009 }
3010
3011
3012 /*
3013 * Add a player to the high score list.
3014 */
add_high_score(player_type * p_ptr)3015 void add_high_score(player_type *p_ptr)
3016 {
3017 char buf[1024];
3018
3019 /* Build the filename */
3020 path_build(buf, 1024, ANGBAND_DIR_DATA, "scores.raw");
3021
3022 /* Open the high score file, for reading/writing */
3023 highscore_fd = file_open(buf, MODE_READWRITE, FTYPE_RAW);
3024
3025 /* Add them */
3026 top_twenty(p_ptr);
3027
3028 /* Shut the high score file */
3029 (void)file_close(highscore_fd);
3030
3031 /* Forget the high score fd */
3032 highscore_fd = NULL;
3033 }
3034
3035
3036 /*
3037 * Close up the current game (player may or may not be dead)
3038 *
3039 * This function is called only from "main.c" and "signals.c".
3040 *
3041 * In here we try to save everybody's game, as well as save the server state.
3042 */
close_game(void)3043 void close_game(void)
3044 {
3045 int i;
3046
3047 /* No suspending now */
3048 signals_ignore_tstp();
3049
3050 for (i = 0; i <= NumPlayers; i++)
3051 {
3052 player_type *p_ptr = Players[i];
3053
3054 /* Handle stuff */
3055 handle_stuff(p_ptr);
3056
3057 /* Flush the messages */
3058 msg_print(p_ptr, NULL);
3059
3060 /* Flush the input */
3061 /*flush();*/
3062
3063
3064 /* Hack -- Character is now "icky" */
3065 /*character_icky = TRUE;*/
3066
3067
3068 /* Build the filename */
3069 /*path_build(buf, 1024, ANGBAND_DIR_APEX, "scores.raw");*/
3070
3071 /* Open the high score file, for reading/writing */
3072 /*highscore_fd = fd_open(buf, O_RDWR);*/
3073
3074
3075 /* Handle death */
3076 if (p_ptr->death)
3077 {
3078 /* Handle retirement */
3079 if (p_ptr->total_winner) kingly(p_ptr);
3080
3081 /* Save memories */
3082 if (!save_player(p_ptr)) msg_print(p_ptr, "death save failed!");
3083
3084 /* Dump bones file
3085 make_bones(i);
3086 */
3087
3088 /* Show more info */
3089 show_info(p_ptr);
3090
3091 /* Handle score, show Top scores */
3092 top_twenty(p_ptr);
3093 }
3094
3095 /* Still alive */
3096 else
3097 {
3098 /* Save the game */
3099 do_cmd_save_game(p_ptr);
3100
3101 /* Prompt for scores XXX XXX XXX */
3102 /*prt("Press Return (or Escape).", 0, 40);*/
3103
3104 /* Predict score (or ESCAPE) */
3105 /*if (inkey() != ESCAPE) predict_score();*/
3106 }
3107
3108
3109 /* Shut the high score file */
3110 /*(void)file_close(highscore_fd);*/
3111
3112 /* Forget the high score fd */
3113 /*highscore_fd = NULL;*/
3114 }
3115
3116 /* Try to save the server information */
3117 save_server_info();
3118
3119 /* Allow suspending now */
3120 signals_handle_tstp();
3121 }
3122
3123
3124 /*
3125 * Hack -- Display the scores in a given range and quit.
3126 *
3127 * This function is only called from "main.c" when the user asks
3128 * to see the "high scores".
3129 */
display_scores(player_type * p_ptr,int line)3130 void display_scores(player_type *p_ptr, int line)
3131 {
3132 char buf[1024];
3133
3134 /* Build the filename */
3135 path_build(buf, 1024, ANGBAND_DIR_DATA, "scores.raw");
3136
3137 /* Open the binary high score file, for reading */
3138 highscore_fd = file_open(buf, MODE_READ, -1);
3139
3140 /* Paranoia -- No score file */
3141 if (highscore_fd < 0)
3142 {
3143 /* Message to server admin */
3144 plog("Score file unavailable.");
3145
3146 /* Quit */
3147 return;
3148 }
3149
3150 /* Clear screen */
3151 /* Term_clear(); */
3152
3153 /* Display the scores */
3154 predict_score(p_ptr, line);
3155
3156 /* Shut the high score file */
3157 (void)file_close(highscore_fd);
3158
3159 /* Forget the high score fd */
3160 highscore_fd = NULL;
3161
3162 /* Quit */
3163 /* quit(NULL); */
3164 }
3165
3166
3167 /*
3168 * Get a random line from a file
3169 * Based on the monster speech patch by Matt Graham,
3170 */
get_rnd_line(cptr file_name,int entry,char * output)3171 errr get_rnd_line(cptr file_name, int entry, char *output)
3172 {
3173 ang_file* fp;
3174 char buf[1024];
3175 int line, counter, test, numentries;
3176 int line_num = 0;
3177 bool found = FALSE;
3178
3179
3180 /* Build the filename */
3181 path_build(buf, 1024, ANGBAND_DIR_EDIT, file_name);
3182
3183 /* Open the file */
3184 fp = file_open(buf, MODE_READ, -1);
3185
3186 /* Failed */
3187 if (!fp) return (-1);
3188
3189 /* Find the entry of the monster */
3190 while (TRUE)
3191 {
3192 /* Get a line from the file */
3193 if (file_getl(fp, buf, 1024))
3194 {
3195 /* Count the lines */
3196 line_num++;
3197
3198 /* Look for lines starting with 'N:' */
3199 if ((buf[0] == 'N') && (buf[1] == ':'))
3200 {
3201 /* Allow default lines */
3202 if (buf[2] == '*')
3203 {
3204 /* Default lines */
3205 found = TRUE;
3206 break;
3207 }
3208 /* Get the monster number */
3209 else if (sscanf(&(buf[2]), "%d", &test) != EOF)
3210 {
3211 /* Is it the right monster? */
3212 if (test == entry)
3213 {
3214 found = TRUE;
3215 break;
3216 }
3217 }
3218 else
3219 {
3220 file_close(fp);
3221 return (-1);
3222 }
3223 }
3224 }
3225 else
3226 {
3227 /* Reached end of file */
3228 file_close(fp);
3229 return (-1);
3230 }
3231
3232 }
3233
3234 /* Get the number of entries */
3235 while (TRUE)
3236 {
3237 /* Get the line */
3238 if (file_getl(fp, buf, 1024))
3239 {
3240 /* Count the lines */
3241 line_num++;
3242
3243 /* Look for the number of entries */
3244 if (isdigit(buf[0]))
3245 {
3246 /* Get the number of entries */
3247 numentries = atoi(buf);
3248 break;
3249 }
3250 }
3251 else
3252 {
3253 /* Count the lines */
3254 line_num++;
3255
3256 file_close(fp);
3257 return (-1);
3258 }
3259 }
3260
3261 if (numentries > 0)
3262 {
3263 /* Grab an appropriate line number */
3264 line = randint0(numentries);
3265
3266 /* Get the random line */
3267 for (counter = 0; counter <= line; counter++)
3268 {
3269 /* Count the lines */
3270 line_num++;
3271
3272 /* Try to read the line */
3273 if (file_getl(fp, buf, 1024))
3274 {
3275 /* Found the line */
3276 if (counter == line) break;
3277 }
3278 else
3279 {
3280 file_close(fp);
3281 return (-1);
3282 }
3283 }
3284
3285 /* Copy the line */
3286 strcpy(output, buf);
3287 }
3288
3289 /* Close the file */
3290 file_close(fp);
3291
3292 /* Success */
3293 return (0);
3294 }
3295
3296
3297 /*
3298 * Handle a fatal crash.
3299 *
3300 * Here we try to save every player's state, and the state of the server
3301 * in general. Note that we must be extremely careful not to have a crash
3302 * in this function, or some things may not get saved. Also, this function
3303 * may get called because some data structures are not in a "correct" state.
3304 * For this reason many paranoia checks are done to prevent bad pointer
3305 * dereferences.
3306 *
3307 * Note that this function would not be needed at all if there were no bugs.
3308 */
exit_game_panic()3309 void exit_game_panic()
3310 {
3311 int i = 1;
3312
3313 /* If nothing important has happened, just return */
3314 if (!server_generated || server_saved) return;
3315
3316 /* Mega-Hack -- see "msg_print()" */
3317 msg_flag = FALSE;
3318
3319 while (NumPlayers > (i - 1))
3320 {
3321 player_type *p_ptr = Players[i];
3322
3323 /* Don't dereference bad pointers */
3324 if (!p_ptr)
3325 {
3326 /* Skip to next player */
3327 i++;
3328
3329 continue;
3330 }
3331
3332 /* Hack -- turn off some things */
3333 disturb(p_ptr, 1, 0);
3334
3335 /* Mega-Hack -- Delay death */
3336 if (p_ptr->chp < 0) p_ptr->death = FALSE;
3337
3338 /* Hardcode panic save */
3339 panic_save = 1;
3340
3341 /* Forbid suspend */
3342 signals_ignore_tstp();
3343
3344 /* Indicate panic save */
3345 (void)strcpy(p_ptr->died_from, "(panic save)");
3346
3347 /* Try to save the player, don't worry if this fails because there
3348 * is nothing we can do now anyway */
3349 save_player(p_ptr);
3350 i++;
3351
3352 }
3353
3354 /* Clear objects so that artifacts get saved.
3355 * This probably isn't neccecary, as all the objects on each of
3356 * these levels should have been cleared by now. However, paranoia
3357 * can't hurt all that much... -APD
3358 */
3359 for (i = 1; i < MAX_DEPTH; i++)
3360 {
3361
3362 /* Paranoia -- wipe this depth's objects if no players are on it*/
3363 if (!players_on_depth[i]) wipe_o_list(i);
3364 }
3365
3366 if (!save_server_info()) plog("server panic info save failed!");
3367
3368 /* Successful panic save of server info */
3369 plog("server panic info save succeeded!");
3370
3371 }
3372
3373 /*
3374 * Windows specific replacement for signal handling [grk]
3375 */
3376 #ifdef WINDOWS
3377 #ifndef HANDLE_SIGNALS
3378
3379 LPTOP_LEVEL_EXCEPTION_FILTER old_handler;
3380
3381 /* Callback to be called by Windows when our term closes, the user
3382 * logs off, the system is shutdown, etc.
3383 */
ctrl_handler(DWORD fdwCtrlType)3384 BOOL ctrl_handler( DWORD fdwCtrlType )
3385 {
3386 /* Save everything and quit the game */
3387 shutdown_server();
3388
3389 return TRUE;
3390 }
3391
3392 /* Global unhandled exception handler */
3393 /* If the server crashes under Windows, this is where we end up */
myUnhandledExceptionFilter(struct _EXCEPTION_POINTERS * ExceptionInfo)3394 LONG WINAPI myUnhandledExceptionFilter(
3395 struct _EXCEPTION_POINTERS* ExceptionInfo)
3396 {
3397 /* We don't report to the meta server in this case, the meta
3398 * server will detect that we've gone anyway
3399 */
3400
3401 /* Call the previous exception handler, which we are assuming
3402 * is the MinGW exception handler which should have been implicitly
3403 * setup when we loaded the exchndl.dll library.
3404 */
3405 if(old_handler != NULL)
3406 {
3407 old_handler(ExceptionInfo);
3408 }
3409
3410 /* Save everything and quit the game */
3411 exit_game_panic();
3412
3413 /* We don't expect to ever get here... but for what it's worth... */
3414 return(EXCEPTION_EXECUTE_HANDLER);
3415
3416 }
3417
3418
setup_exit_handler(void)3419 void setup_exit_handler(void)
3420 {
3421 /* Trap CTRL+C, Logoff, Shutdown, etc */
3422 if( SetConsoleCtrlHandler( (PHANDLER_ROUTINE) ctrl_handler, TRUE ) )
3423 {
3424 plog("Initialised exit save handler.");
3425 }else{
3426 plog("ERROR: Could not set panic save handler!");
3427 }
3428 /* Trap unhandled exceptions, i.e. server crashes */
3429 old_handler = SetUnhandledExceptionFilter( myUnhandledExceptionFilter );
3430 }
3431 #endif
3432 #endif
3433
3434 #ifdef HANDLE_SIGNALS
3435
3436
3437 /*
3438 * Handle signals -- suspend
3439 *
3440 * Actually suspend the game, and then resume cleanly
3441 *
3442 * This will probably inflict much anger upon the suspender, but it is still
3443 * allowed (for now) --KLJ--
3444 */
handle_signal_suspend(int sig)3445 static void handle_signal_suspend(int sig)
3446 {
3447 /* Disable handler */
3448 (void)signal(sig, SIG_IGN);
3449
3450 #ifdef SIGSTOP
3451
3452 /* Flush output */
3453 /*Term_fresh();*/
3454
3455 /* Suspend the "Term" */
3456 /*Term_xtra(TERM_XTRA_ALIVE, 0);*/
3457
3458 /* Suspend ourself */
3459 (void)kill(0, SIGSTOP);
3460
3461 /* Resume the "Term" */
3462 /*Term_xtra(TERM_XTRA_ALIVE, 1);*/
3463
3464 /* Redraw the term */
3465 /*Term_redraw();*/
3466
3467 /* Flush the term */
3468 /*Term_fresh();*/
3469
3470 #endif
3471
3472 /* Restore handler */
3473 (void)signal(sig, handle_signal_suspend);
3474 }
3475
3476
3477 /*
3478 * Handle signals -- simple (interrupt and quit)
3479 *
3480 * This function was causing a *huge* number of problems, so it has
3481 * been simplified greatly. We keep a global variable which counts
3482 * the number of times the user attempts to kill the process, and
3483 * we commit suicide if the user does this a certain number of times.
3484 *
3485 * We attempt to give "feedback" to the user as he approaches the
3486 * suicide thresh-hold, but without penalizing accidental keypresses.
3487 *
3488 * To prevent messy accidents, we should reset this global variable
3489 * whenever the user enters a keypress, or something like that.
3490 *
3491 * This simply calls "exit_game_panic()", which should try to save
3492 * everyone's character and the server info, which is probably nicer
3493 * than killing everybody. --KLJ--
3494 */
handle_signal_simple(int sig)3495 static void handle_signal_simple(int sig)
3496 {
3497 /* Disable handler */
3498 (void)signal(sig, SIG_IGN);
3499
3500
3501 /* Nothing to save, just quit */
3502 if (!server_generated || server_saved) quit(NULL);
3503
3504 /* Hack -- on SIGTERM, quit right away */
3505 if (sig == SIGTERM)
3506 {
3507 signal_count = 5;
3508 }
3509
3510 /* Count the signals */
3511 signal_count++;
3512
3513
3514 /* Allow suicide (after 5) */
3515 if (signal_count >= 5)
3516 {
3517 /* Perform a "clean" shutdown */
3518 shutdown_server();
3519 }
3520
3521 /* Give warning (after 4) */
3522 else if (signal_count >= 4)
3523 {
3524 plog("Warning: Next signal kills server!");
3525
3526 /* Make a noise */
3527 /*Term_xtra(TERM_XTRA_NOISE, 0);*/
3528
3529 /* Clear the top line */
3530 /*Term_erase(0, 0, 255);*/
3531
3532 /* Display the cause */
3533 /*Term_putstr(0, 0, -1, TERM_WHITE, "Contemplating suicide!");*/
3534
3535 /* Flush */
3536 /*Term_fresh();*/
3537 }
3538
3539 /* Give warning (after 2) */
3540 else if (signal_count >= 2)
3541 {
3542 /* Make a noise */
3543 /*Term_xtra(TERM_XTRA_NOISE, 0);*/
3544 }
3545
3546 /* Restore handler */
3547 (void)signal(sig, handle_signal_simple);
3548 }
3549
3550
3551 /*
3552 * Handle signal -- abort, kill, etc
3553 *
3554 * This one also calls exit_game_panic() --KLJ--
3555 */
handle_signal_abort(int sig)3556 static void handle_signal_abort(int sig)
3557 {
3558 /* We are *not* reentrant */
3559 if (signalbusy) raise(sig);
3560 signalbusy = 1;
3561
3562 plog("Unexpected signal, panic saving.");
3563
3564 /* Nothing to save, just quit */
3565 if (!server_generated || server_saved) quit(NULL);
3566
3567 /* Save everybody */
3568 exit_game_panic();
3569
3570 /* Enable default handler */
3571 (void)signal(sig, SIG_DFL);
3572
3573 /* Reraise */
3574 raise(sig);
3575 }
3576
3577
3578
3579
3580 /*
3581 * Ignore SIGTSTP signals (keyboard suspend)
3582 */
signals_ignore_tstp(void)3583 void signals_ignore_tstp(void)
3584 {
3585
3586 #ifdef SIGTSTP
3587 (void)signal(SIGTSTP, SIG_IGN);
3588 #endif
3589
3590 }
3591
3592 /*
3593 * Handle SIGTSTP signals (keyboard suspend)
3594 */
signals_handle_tstp(void)3595 void signals_handle_tstp(void)
3596 {
3597
3598 #ifdef SIGTSTP
3599 (void)signal(SIGTSTP, handle_signal_suspend);
3600 #endif
3601
3602 }
3603
3604
3605 /*
3606 * Prepare to handle the relevant signals
3607 */
signals_init(void)3608 void signals_init(void)
3609 {
3610
3611 #ifdef SIGHUP
3612 (void)signal(SIGHUP, SIG_IGN);
3613
3614 #ifdef TARGET_OS_OSX
3615 /* Closing Terminal.app on OSX sends SIGHUP, let's not ignore it! */
3616 (void)signal(SIGHUP, handle_signal_abort);
3617 #endif
3618
3619 #endif
3620
3621 #ifdef SIGTSTP
3622 (void)signal(SIGTSTP, handle_signal_suspend);
3623 #endif
3624
3625
3626 #ifdef SIGINT
3627 (void)signal(SIGINT, handle_signal_simple);
3628 #endif
3629
3630 #ifdef SIGQUIT
3631 (void)signal(SIGQUIT, handle_signal_simple);
3632 #endif
3633
3634
3635 #ifdef SIGFPE
3636 (void)signal(SIGFPE, handle_signal_abort);
3637 #endif
3638
3639 #ifdef SIGILL
3640 (void)signal(SIGILL, handle_signal_abort);
3641 #endif
3642
3643 #ifdef SIGTRAP
3644 (void)signal(SIGTRAP, handle_signal_abort);
3645 #endif
3646
3647 #ifdef SIGIOT
3648 (void)signal(SIGIOT, handle_signal_abort);
3649 #endif
3650
3651 #ifdef SIGKILL
3652 (void)signal(SIGKILL, handle_signal_abort);
3653 #endif
3654
3655 #ifdef SIGBUS
3656 (void)signal(SIGBUS, handle_signal_abort);
3657 #endif
3658
3659 #ifdef SIGSEGV
3660 (void)signal(SIGSEGV, handle_signal_abort);
3661 #endif
3662
3663 #ifdef SIGTERM
3664 (void)signal(SIGTERM, handle_signal_simple);
3665 #endif
3666
3667 #ifdef SIGPIPE
3668 (void)signal(SIGPIPE, SIG_IGN);
3669 #endif
3670
3671 #ifdef SIGEMT
3672 (void)signal(SIGEMT, handle_signal_abort);
3673 #endif
3674
3675 #ifdef SIGDANGER
3676 (void)signal(SIGDANGER, handle_signal_abort);
3677 #endif
3678
3679 #ifdef SIGSYS
3680 (void)signal(SIGSYS, handle_signal_abort);
3681 #endif
3682
3683 #ifdef SIGXCPU
3684 (void)signal(SIGXCPU, handle_signal_abort);
3685 #endif
3686
3687 #ifdef SIGPWR
3688 (void)signal(SIGPWR, handle_signal_abort);
3689 #endif
3690
3691 }
3692
3693
3694 #else /* HANDLE_SIGNALS */
3695
3696
3697 /*
3698 * Do nothing
3699 */
signals_ignore_tstp(void)3700 void signals_ignore_tstp(void)
3701 {
3702 }
3703
3704 /*
3705 * Do nothing
3706 */
signals_handle_tstp(void)3707 void signals_handle_tstp(void)
3708 {
3709 }
3710
3711 /*
3712 * Do nothing
3713 */
signals_init(void)3714 void signals_init(void)
3715 {
3716 }
3717
3718
3719 #endif /* HANDLE_SIGNALS */
3720