1 /* $Id$ */
2 /* File: files.c */
3
4 /* Purpose: code dealing with files (and death) */
5
6 /*
7 * Copyright (c) 1989 James E. Wilson, Robert A. Koeneke
8 *
9 * This software may be copied and distributed for educational, research, and
10 * not for profit purposes provided that this copyright and statement are
11 * included in all such copies.
12 */
13
14 #define SERVER
15
16 #include "angband.h"
17
18 #ifdef HANDLE_SIGNALS
19 #include <signal.h>
20 #ifndef WINDOWS
21 #include <sys/wait.h>
22 #endif
23 #endif
24
25 /* The first x highscore entries that are displayed to players: */
26 #define SCORES_SHOWN 28
27
28 /*
29 * You may or may not want to use the following "#undef".
30 */
31 /* #undef _POSIX_SAVED_IDS */
32
33 /* Use first line of file as stationary title caption? If disabled,
34 'what' parm of do_cmd_help_aux() will be used instead (normal). - C. Blue */
35 //#define HELP_AUX_GRABS_TITLE
36
37
38 /*
39 * Extract the first few "tokens" from a buffer
40 *
41 * This function uses "colon" and "slash" as the delimeter characters.
42 *
43 * We never extract more than "num" tokens. The "last" token may include
44 * "delimeter" characters, allowing the buffer to include a "string" token.
45 *
46 * We save pointers to the tokens in "tokens", and return the number found.
47 *
48 * Hack -- Attempt to handle the 'c' character formalism
49 *
50 * Hack -- An empty buffer, or a final delimeter, yields an "empty" token.
51 *
52 * Hack -- We will always extract at least one token
53 */
54 #if 0
55 s16b tokenize(char *buf, s16b num, char **tokens)
56 {
57 int i = 0;
58
59 char *s = buf;
60
61
62 /* Process */
63 while (i < num - 1)
64 {
65 char *t;
66
67 /* Scan the string */
68 for (t = s; *t; t++)
69 {
70 /* Found a delimiter */
71 if ((*t == ':') || (*t == '/')) break;
72
73 /* Handle single quotes */
74 if (*t == '\'')
75 {
76 /* Advance */
77 t++;
78
79 /* Handle backslash */
80 if (*t == '\\') t++;
81
82 /* Require a character */
83 if (!*t) break;
84
85 /* Advance */
86 t++;
87
88 /* Hack -- Require a close quote */
89 if (*t != '\'') *t = '\'';
90 }
91
92 /* Handle back-slash */
93 if (*t == '\\') t++;
94 }
95
96 /* Nothing left */
97 if (!*t) break;
98
99 /* Nuke and advance */
100 *t++ = '\0';
101
102 /* Save the token */
103 tokens[i++] = s;
104
105 /* Advance */
106 s = t;
107 }
108
109 /* Save the token */
110 tokens[i++] = s;
111
112 /* Number found */
113 return (i);
114 }
115 #else // 0
tokenize(char * buf,s16b num,char ** tokens,char delim1,char delim2)116 s16b tokenize(char *buf, s16b num, char **tokens, char delim1, char delim2)
117 {
118 int i = 0;
119
120 char *s = buf;
121
122
123 /* Process */
124 while (i < num - 1)
125 {
126 char *t;
127
128 /* Scan the string */
129 for (t = s; *t; t++)
130 {
131 /* Found a delimiter */
132 if ((*t == delim1) || (*t == delim2)) break;
133
134 /* Handle single quotes */
135 if (*t == '\'')
136 {
137 /* Advance */
138 t++;
139
140 /* Handle backslash */
141 if (*t == '\\') t++;
142
143 /* Require a character */
144 if (!*t) break;
145
146 /* Advance */
147 t++;
148
149 /* Hack -- Require a close quote */
150 if (*t != '\'') *t = '\'';
151 }
152
153 /* Handle back-slash */
154 if (*t == '\\') t++;
155 }
156
157 /* Nothing left */
158 if (!*t) break;
159
160 /* Nuke and advance */
161 *t++ = '\0';
162
163 /* Save the token */
164 tokens[i++] = s;
165
166 /* Advance */
167 s = t;
168 }
169
170 /* Save the token */
171 tokens[i++] = s;
172
173 /* Number found */
174 return (i);
175 }
176 #endif // 0
177
178 #ifdef CHECK_TIME
179
180 /*
181 * Operating hours for ANGBAND (defaults to non-work hours)
182 */
183 static char days[7][29] =
184 {
185 "SUN:XXXXXXXXXXXXXXXXXXXXXXXX",
186 "MON:XXXXXXXX.........XXXXXXX",
187 "TUE:XXXXXXXX.........XXXXXXX",
188 "WED:XXXXXXXX.........XXXXXXX",
189 "THU:XXXXXXXX.........XXXXXXX",
190 "FRI:XXXXXXXX.........XXXXXXX",
191 "SAT:XXXXXXXXXXXXXXXXXXXXXXXX"
192 };
193
194 /*
195 * Restict usage (defaults to no restrictions)
196 */
197 static bool check_time_flag = FALSE;
198
199 #endif
200
201
202 /*
203 * Handle CHECK_TIME
204 */
check_time(void)205 errr check_time(void)
206 {
207
208 #ifdef CHECK_TIME
209
210 time_t c;
211 struct tm *tp;
212
213 /* No restrictions */
214 if (!check_time_flag) return (0);
215
216 /* Check for time violation */
217 c = time((time_t *)0);
218 tp = localtime(&c);
219
220 /* Violation */
221 if (days[tp->tm_wday][tp->tm_hour + 4] != 'X') return (1);
222
223 #endif
224
225 /* Success */
226 return (0);
227 }
228
229
230
231 /*
232 * Initialize CHECK_TIME
233 */
check_time_init(void)234 errr check_time_init(void)
235 {
236
237 #ifdef CHECK_TIME
238
239 FILE *fp;
240
241 char buf[1024];
242
243
244 /* Build the filename */
245 path_build(buf, 1024, ANGBAND_DIR_DATA, "time.txt");
246
247 /* Open the file */
248 fp = my_fopen(buf, "r");
249
250 /* No file, no restrictions */
251 if (!fp) return (0);
252
253 /* Assume restrictions */
254 check_time_flag = TRUE;
255
256 /* Parse the file */
257 while (0 == my_fgets(fp, buf, 80, FALSE))
258 {
259 /* Skip comments and blank lines */
260 if (!buf[0] || (buf[0] == '#')) continue;
261
262 /* Chop the buffer */
263 buf[29] = '\0';
264
265 /* Extract the info */
266 if (prefix(buf, "SUN:")) strcpy(days[0], buf);
267 if (prefix(buf, "MON:")) strcpy(days[1], buf);
268 if (prefix(buf, "TUE:")) strcpy(days[2], buf);
269 if (prefix(buf, "WED:")) strcpy(days[3], buf);
270 if (prefix(buf, "THU:")) strcpy(days[4], buf);
271 if (prefix(buf, "FRI:")) strcpy(days[5], buf);
272 if (prefix(buf, "SAT:")) strcpy(days[6], buf);
273 }
274
275 /* Close it */
276 my_fclose(fp);
277
278 #endif
279
280 /* Success */
281 return (0);
282 }
283
284
285
286 #ifdef CHECK_LOAD
287
288 #ifndef MAXHOSTNAMELEN
289 # define MAXHOSTNAMELEN 64
290 #endif
291
292 typedef struct statstime statstime;
293
294 struct statstime
295 {
296 int cp_time[4];
297 int dk_xfer[4];
298 unsigned int v_pgpgin;
299 unsigned int v_pgpgout;
300 unsigned int v_pswpin;
301 unsigned int v_pswpout;
302 unsigned int v_intr;
303 int if_ipackets;
304 int if_ierrors;
305 int if_opackets;
306 int if_oerrors;
307 int if_collisions;
308 unsigned int v_swtch;
309 long avenrun[3];
310 struct timeval boottime;
311 struct timeval curtime;
312 };
313
314 /*
315 * Maximal load (if any).
316 */
317 static int check_load_value = 0;
318
319 #endif
320
321
322 /*
323 * Handle CHECK_LOAD
324 */
check_load(void)325 errr check_load(void)
326 {
327
328 #ifdef CHECK_LOAD
329
330 struct statstime st;
331
332 /* Success if not checking */
333 if (!check_load_value) return (0);
334
335 /* Check the load */
336 if (0 == rstat("localhost", &st))
337 {
338 long val1 = (long)(st.avenrun[2]);
339 long val2 = (long)(check_load_value) * FSCALE;
340
341 /* Check for violation */
342 if (val1 >= val2) return (1);
343 }
344
345 #endif
346
347 /* Success */
348 return (0);
349 }
350
351
352 /*
353 * Initialize CHECK_LOAD
354 */
check_load_init(void)355 errr check_load_init(void)
356 {
357
358 #ifdef CHECK_LOAD
359
360 FILE *fp;
361
362 char buf[1024];
363
364 char temphost[MAXHOSTNAMELEN+1];
365 char thishost[MAXHOSTNAMELEN+1];
366
367
368 /* Build the filename */
369 path_build(buf, 1024, ANGBAND_DIR_TEXT, "load.txt");
370
371 /* Open the "load" file */
372 fp = my_fopen(buf, "r");
373
374 /* No file, no restrictions */
375 if (!fp) return (0);
376
377 /* Default load */
378 check_load_value = 100;
379
380 /* Get the host name */
381 (void)gethostname(thishost, (sizeof thishost) - 1);
382
383 /* Parse it */
384 while (0 == my_fgets(fp, buf, 1024, FALSE))
385 {
386 int value;
387
388 /* Skip comments and blank lines */
389 if (!buf[0] || (buf[0] == '#')) continue;
390
391 /* Parse, or ignore */
392 if (sscanf(buf, "%s%d", temphost, &value) != 2) continue;
393
394 /* Skip other hosts */
395 if (!streq(temphost, thishost) &&
396 !streq(temphost, "localhost")) continue;
397
398 /* Use that value */
399 check_load_value = value;
400
401 /* Done */
402 break;
403 }
404
405 /* Close the file */
406 my_fclose(fp);
407
408 #endif
409
410 /* Success */
411 return (0);
412 }
413
414
415
416
417 #if 0
418 /*
419 * Prints the following information on the screen.
420 *
421 * For this to look right, the following should be spaced the
422 * same as in the prt_lnum code... -CFT
423 *
424 * This will send the info to the client now --KLJ--
425 *
426 * Except that this (and display_player) are never called. --KLJ--
427 */
428 static void display_player_middle(int Ind) {
429 player_type *p_ptr = Players[Ind];
430 int bmh = 0, bmd = 0;
431 s32b adv_exp, adv_exp_prev;
432
433 int show_tohit_m = p_ptr->dis_to_h + p_ptr->to_h_melee;
434 int show_todam_m = p_ptr->dis_to_d + p_ptr->to_d_melee;
435 /* int show_tohit_m = p_ptr->to_h_melee;
436 int show_todam_m = p_ptr->to_d_melee;
437 */
438 int show_tohit_r = p_ptr->dis_to_h + p_ptr->to_h_ranged;
439 int show_todam_r = p_ptr->to_d_ranged;
440
441 /* well, about dual-wield.. we can only display the boni of one weapon or their average until we add another line
442 to squeeze info about the secondary weapon there too. so for now let's just stick with this. - C. Blue */
443 object_type *o_ptr = &p_ptr->inventory[INVEN_WIELD];
444 object_type *o_ptr2 = &p_ptr->inventory[INVEN_BOW];
445 object_type *o_ptr3 = &p_ptr->inventory[INVEN_AMMO];
446 object_type *o_ptr4 = &p_ptr->inventory[INVEN_ARM];
447
448 /* Hack -- add in weapon info if known */
449 if (object_known_p(Ind, o_ptr)) {
450 bmh += o_ptr->to_h;
451 bmd += o_ptr->to_d;
452 }
453 if (object_known_p(Ind, o_ptr2)) {
454 show_tohit_r += o_ptr2->to_h;
455 show_todam_r += o_ptr2->to_d;
456 }
457 if (object_known_p(Ind, o_ptr3)) {
458 show_tohit_r += o_ptr3->to_h;
459 show_todam_r += o_ptr3->to_d;
460 }
461 /* dual-wield..*/
462 if (o_ptr4->k_idx && o_ptr4->tval != TV_SHIELD) {
463 if (object_known_p(Ind, o_ptr4)) {
464 bmh += o_ptr4->to_h;
465 bmd += o_ptr4->to_d;
466 }
467 if (object_known_p(Ind, o_ptr) && object_known_p(Ind, o_ptr4)) {
468 /* average of both */
469 bmh /= 2;
470 bmd /= 2;
471 }
472 }
473
474 show_tohit_m += bmh;
475 show_todam_m += bmd;
476
477 /* Dump the bonuses to hit/dam */
478 // Send_plusses(Ind, show_tohit_m, show_todam_m, show_tohit_r, show_todam_r, p_ptr->to_h_melee, p_ptr->to_d_melee);
479 Send_plusses(Ind, 0, 0, show_tohit_r, show_todam_r, show_tohit_m, show_todam_m);
480
481 /* Dump the armor class bonus */
482 Send_ac(Ind, p_ptr->dis_ac, p_ptr->dis_to_a);
483
484 if (p_ptr->lev >= (is_admin(p_ptr) ? PY_MAX_LEVEL : PY_MAX_PLAYER_LEVEL))
485 adv_exp = 0;
486 else {
487 s64b adv_prev = 0;
488 #ifndef ALT_EXPRATIO
489 s64b adv = ((s64b)player_exp[p_ptr->lev - 1] * (s64b)p_ptr->expfact / 100L);
490 if (p_ptr->lev > 1) adv_prev = ((s64b)player_exp[p_ptr->lev - 2] * (s64b)p_ptr->expfact / 100L);
491 #else
492 s64b adv = (s64b)player_exp[p_ptr->lev - 1];
493 if (p_ptr->lev > 1) adv_prev = (s64b)player_exp[p_ptr->lev - 2];
494 #endif
495 adv_exp = (s32b)(adv);
496 adv_exp_prev = (s32b)(adv_prev);
497 }
498
499 Send_experience(Ind, p_ptr->lev, p_ptr->max_exp, p_ptr->exp, adv_exp, adv_exp_prev);
500 Send_gold(Ind, p_ptr->au);
501 Send_hp(Ind, p_ptr->mhp, p_ptr->chp);
502 Send_sp(Ind, p_ptr->msp, p_ptr->csp);
503 Send_stamina(Ind, p_ptr->mst, p_ptr->cst);
504 }
505
506
507
508 /*
509 * Display the character on the screen (with optional history)
510 *
511 * The top two and bottom two lines are left blank.
512 */
513 void display_player(int Ind)
514 {
515 player_type *p_ptr = Players[Ind];
516
517 int i;
518
519
520 /* Send basic information */
521 Send_char_info(Ind, p_ptr->prace, p_ptr->pclass, p_ptr->ptrait, p_ptr->male, p_ptr->mode, p_ptr->name);
522
523 /* Age, Height, Weight, Social */
524 Send_various(Ind, p_ptr->ht, p_ptr->wt, p_ptr->age, p_ptr->sc);
525
526 /* Send all the stats */
527 for (i = 0; i < 6; i++)
528 {
529 Send_stat(Ind, i, p_ptr->stat_top[i], p_ptr->stat_use[i]);
530 }
531
532 /* Extra info */
533 display_player_middle(Ind);
534
535 /* Display "history" info */
536 Send_history(Ind, i, p_ptr->history[i]);
537 }
538 #endif // 0
539
540 /*
541 * Recursive "help file" perusal. Return FALSE on "ESCAPE".
542 *
543 * XXX XXX XXX Consider using a temporary file.
544 *
545 * Added 'odd_line' flag: Usually FALSE -> 20 lines available per 'page'.
546 * If TRUE -> 21 lines available! Added this for
547 * @-screen w/ COMPACT_PLAYERLIST - C. Blue
548 * *changed it to 'div3_line', for cleaner addition of more of this type.
549 * *changed it to 'divl' to be most flexible.
550 */
do_cmd_help_aux(int Ind,cptr name,cptr what,s32b line,int color,int divl)551 static bool do_cmd_help_aux(int Ind, cptr name, cptr what, s32b line, int color, int divl)
552 {
553 int lines_per_page = 20 + HGT_PLUS;
554 int i, k = 0;
555 /* Number of "real" lines passed by */
556 s32b next = 0;
557 /* Number of "real" lines in the file */
558 s32b size = 0;
559 /* Backup value for "line" */
560 s32b back = 0;
561 /* This screen has sub-screens */
562 //bool menu = FALSE;
563 /* Current help file */
564 FILE *fff = NULL;
565 /* Find this string (if any) */
566 cptr find = NULL;
567 /* Hold a string to find */
568 char finder[128];
569 /* Hold a string to show */
570 char shower[128];
571 /* Describe this thing */
572 char caption[128];
573 /* Path buffer */
574 char path[MAX_PATH_LENGTH];
575 /* General buffer */
576 char buf[1024];
577 /* Sub-menu information */
578 char hook[10][32];
579 /* Stationary title bar, derived from 1st line of the file or from 'what' parm. */
580 #ifdef HELP_AUX_GRABS_TITLE
581 bool use_title = FALSE;
582 #endif
583
584 if (is_newer_than(&Players[Ind]->version, 4, 4, 7, 0, 0, 0)) {
585 /* use first line in the file as stationary title */
586 #ifdef HELP_AUX_GRABS_TITLE
587 use_title = TRUE;
588 #endif
589
590 if (divl) {
591 if (is_older_than(&Players[Ind]->version, 4, 4, 9, 4, 0, 0)) {
592 /* 21-lines-per-page feature requires client 4.4.7.1 aka 4.4.7a */
593 if (divl == 3) {
594 lines_per_page = 21 + HGT_PLUS - ((21 + HGT_PLUS) % 3);
595 /* hack: tell client we'd like a mod3 # of lines per page */
596 Send_special_line(Ind, 0, 21 + HGT_PLUS, 0, "");
597 }
598 } else {
599 lines_per_page = 21 + HGT_PLUS - ((21 + HGT_PLUS) % divl);
600 /* hack: tell client we'd like a mod4 # of lines per page */
601 Send_special_line(Ind, 0, 20 + divl + HGT_PLUS, 0, "");
602 }
603 }
604 }
605
606 /* Wipe finder */
607 strcpy(finder, "");
608 /* Wipe shower */
609 strcpy(shower, "");
610 /* Wipe caption */
611 strcpy(caption, "");
612
613 /* Wipe the hooks */
614 for (i = 0; i < 10; i++) hook[i][0] = '\0';
615
616
617 /* Hack XXX XXX XXX */
618 if (what) {
619 /* Caption */
620 strcpy(caption, what);
621 /* Access the "file" */
622 strcpy(path, name);
623 /* Open */
624 fff = my_fopen(path, "rb");
625 }
626
627 /* Look in "help" */
628 if (!fff) {
629 #if 0 /* will overwrite legens-rev.log's title if the file doesn't exist yet */
630 /* Caption */
631 snprintf(caption, sizeof(caption), "Help file '%s'", name);
632 #endif
633 /* Build the filename */
634 path_build(path, MAX_PATH_LENGTH, ANGBAND_DIR_TEXT, name);
635 /* Open the file */
636 fff = my_fopen(path, "rb");
637 }
638
639 #ifndef HELP_AUX_GRABS_TITLE
640 if (strlen(caption)) {
641 k = (72 - strlen(caption)) / 2;
642 strcpy(buf, "");
643 for (i = 0; i < k; i++) strcat(buf, " ");
644 strcat(buf, "\377W- [\377w");
645 strcat(buf, caption);
646 strcat(buf, "\377W] -");
647 Send_special_line(Ind, size, -1, TERM_WHITE, buf);
648 k = 0;
649 }
650 #endif
651
652 /* Oops */
653 if (!fff) {
654 #if 0 /* no spam - the msg is written many times at once and also not needed */
655 /* Message */
656 msg_format(Ind, "Cannot open '%s'.", name);
657 msg_print(Ind, NULL);
658 #endif
659
660 /* Oops */
661 return (TRUE);
662 }
663
664
665 /* Pre-Parse the file */
666 while (TRUE) {
667 /* Read a line or stop */
668 if (my_fgets(fff, buf, 1024, FALSE)) break;
669
670 /* XXX Parse "menu" items */
671 if (prefix(buf, "***** ")) {
672 char b1 = '[', b2 = ']';
673
674 /* Notice "menu" requests */
675 if ((buf[6] == b1) && isdigit(buf[7]) &&
676 (buf[8] == b2) && (buf[9] == ' '))
677 {
678 /* This is a menu file */
679 //menu = TRUE;
680
681 /* Extract the menu item */
682 k = buf[7] - '0';
683
684 /* Extract the menu item */
685 strcpy(hook[k], buf + 10);
686 }
687
688 /* Skip this */
689 continue;
690 }
691
692 /* Count the "real" lines */
693 next++;
694 }
695
696 /* Save the number of "real" lines */
697 size = next;
698 #ifdef HELP_AUX_GRABS_TITLE
699 if (use_title) size--;
700 #endif
701
702
703 /* Display the file */
704 /* Restart when necessary */
705 if (line >= size) line = 0;
706
707 /* (Consistent behavior with peruse_file() in c-files.c
708 and Receive_special_line() in nclient.c.) */
709 if (line > size - lines_per_page) {
710 line = size - lines_per_page;
711 if (line < 0) line = 0;
712 }
713
714
715 /* Re-open the file if needed */
716 if (next > line) {
717 /* Close it */
718 my_fclose(fff);
719
720 /* Hack -- Re-Open the file */
721 fff = my_fopen(path, "rb");
722
723 /* Oops */
724 if (!fff) return (FALSE);
725
726 /* File has been restarted */
727 next = 0;
728
729 #ifdef HELP_AUX_GRABS_TITLE
730 /* Use special 'title' line? -> Skip in regular use. */
731 if (use_title)
732 if (!my_fgets(fff, buf, 1024, FALSE))
733 Send_special_line(Ind, size, -1, TERM_WHITE, buf);
734 #endif
735 }
736
737 /* Skip lines if needed */
738 for (; next < line; next++) {
739 /* Skip a line */
740 if (my_fgets(fff, buf, 1024, FALSE)) break;
741 }
742
743
744 /* Dump the next 20 lines of the file */
745 for (i = 0; i < lines_per_page; ) {
746 byte attr = TERM_WHITE;
747
748 /* Hack -- track the "first" line */
749 if (!i) line = next;
750
751 /* Get a line of the file or stop */
752 if (my_fgets(fff, buf, 1024, FALSE)) break;
753
754 /* Hack -- skip "special" lines */
755 if (prefix(buf, "***** ")) continue;
756
757 /* Count the "real" lines */
758 next++;
759
760 /* Hack -- keep searching */
761 if (find && !i && !strstr(buf, find)) continue;
762
763 /* Hack -- stop searching */
764 find = NULL;
765
766 if (buf[0] == '\n') continue;
767
768 #if 0 // This will now be done by \377? codes! - C. Blue
769 /* Extract color */
770 if (color) attr = color_char_to_attr(buf[0]);
771 #endif
772
773 /* Hack -- show matches */
774 if (shower[0] && strstr(buf, shower)) attr = TERM_YELLOW;
775
776 /* Dump the line */
777 #if 0 // see above
778 Send_special_line(Ind, size, i, attr, &buf[color]);
779 #else
780 Send_special_line(Ind, size, i, attr, buf);
781 #endif
782 /* Count the printed lines */
783 i++;
784 }
785
786 /* Hack -- failed search */
787 if (find) {
788 bell();
789 line = back;
790 find = NULL;
791 my_fclose(fff); /* The evil file that waited open? */
792 return (TRUE);
793 }
794
795 /* Close the file */
796 my_fclose(fff);
797
798 /* Escape */
799 if (k == ESCAPE) return (FALSE);
800
801 /* Normal return */
802 return (TRUE);
803 }
804
805
806 /*
807 * Peruse the On-Line-Help, starting at the given file.
808 *
809 * Disabled --KLJ--
810 */
do_cmd_help(int Ind,int line)811 void do_cmd_help(int Ind, int line)
812 {
813 // cptr name = "mangband.hlp";
814 cptr name = "tomenet.hlp";
815
816 /* Peruse the main help file */
817 (void)do_cmd_help_aux(Ind, name, NULL, line, FALSE, 0);
818 }
819
820
821
822
823
824 /*
825 * Hack -- display the contents of a file on the screen
826 *
827 * XXX XXX XXX Use this function for commands such as the
828 * "examine object" command.
829 */
show_file(int Ind,cptr name,cptr what,s32b line,int color,int divl)830 errr show_file(int Ind, cptr name, cptr what, s32b line, int color, int divl)
831 {
832 /* Peruse the requested file */
833 (void)do_cmd_help_aux(Ind, name, what, line, color, divl);
834
835 /* Success */
836 return (0);
837 }
838
839
840
841
842 /*
843 * Process the player name.
844 * Extract a clean "base name".
845 * Build the savefile name if needed.
846 */
process_player_name(int Ind,bool sf)847 bool process_player_name(int Ind, bool sf)
848 {
849 player_type *p_ptr = Players[Ind];
850
851 int i, k = 0;
852
853
854 /* Cannot be too long */
855 if (strlen(p_ptr->name) > 15) //was 20 once?
856 {
857 /* Name too long */
858 Destroy_connection(p_ptr->conn, "Your name is too long!");
859
860 /* Abort */
861 return FALSE;
862 }
863
864 /* Cannot contain "icky" characters */
865 for (i = 0; p_ptr->name[i]; i++)
866 {
867 /* No control characters */
868 if (iscntrl(p_ptr->name[i]))
869 {
870 /* Illegal characters */
871 Destroy_connection(p_ptr->conn, "Your name contains control chars!");
872
873 /* Abort */
874 return FALSE;
875 }
876 }
877
878
879 #ifdef MACINTOSH
880
881 /* Extract "useful" letters */
882 for (i = 0; p_ptr->name[i]; i++)
883 {
884 char c = p_ptr->name[i];
885
886 /* Convert "dot" to "underscore" */
887 if (c == '.') c = '_';
888
889 /* Accept all the letters */
890 p_ptr->basename[k++] = c;
891 }
892
893 #else
894
895 /* Extract "useful" letters */
896 for (i = 0; p_ptr->name[i]; i++)
897 {
898 char c = p_ptr->name[i];
899
900 /* Accept some letters */
901 if (isalpha(c) || isdigit(c)) p_ptr->basename[k++] = c;
902
903 /* Convert space, dot, and underscore to underscore */
904 else if (strchr(SF_BAD_CHARS, c)) p_ptr->basename[k++] = '_';
905 }
906
907 #endif
908
909
910 #if defined(WINDOWS) || defined(MSDOS)
911
912 /* Hack -- max length */
913 if (k > 8) k = 8;
914
915 #endif
916
917 /* Terminate */
918 p_ptr->basename[k] = '\0';
919
920 /* Require a "base" name */
921 if (!p_ptr->basename[0]) strcpy(p_ptr->basename, "PLAYER");
922
923
924 #ifdef SAVEFILE_MUTABLE
925
926 /* Accept */
927 sf = TRUE;
928
929 #endif
930
931 /* Change the savefile name */
932 if (sf)
933 {
934 char temp[128];
935
936 /* Rename the savefile, using the player_base */
937 (void)snprintf(temp, sizeof(temp), "%s", p_ptr->basename);
938
939 #ifdef VM
940 /* Hack -- support "flat directory" usage on VM/ESA */
941 (void)snprintf(temp, sizeof(temp), "%s.sv", player_base);
942 #endif /* VM */
943
944 /* Build the filename */
945 path_build(p_ptr->savefile, MAX_PATH_LENGTH, ANGBAND_DIR_SAVE, temp);
946 }
947
948 /* Success */
949 return TRUE;
950 }
951
952
953 /*
954 * Gets a name for the character, reacting to name changes.
955 *
956 * Assumes that "display_player()" has just been called
957 * XXX Perhaps we should NOT ask for a name (at "birth()") on Unix?
958 *
959 * The name should be sent to us from the client, so this is unnecessary --KLJ--
960 */
get_name(int Ind)961 void get_name(int Ind)
962 {
963 }
964
965
966
967 /*
968 * Hack -- commit suicide
969 */
do_cmd_suicide(int Ind)970 void do_cmd_suicide(int Ind)
971 {
972 player_type *p_ptr = Players[Ind];
973
974 /* Mark as suicide */
975 p_ptr->alive = FALSE;
976
977 /* Hack -- set the cause of death */
978 if (!p_ptr->ghost) {
979 //strcpy(p_ptr->died_from, "");
980 strcpy(p_ptr->died_from_list, "self-inflicted wounds");
981 p_ptr->died_from_depth = getlevel(&p_ptr->wpos);
982 }
983
984 /* Hack -- clear ghost */
985 p_ptr->ghost = 0;
986
987 if (p_ptr->total_winner) {
988 if (p_ptr->iron_winner) kingly(Ind, 4);
989 else kingly(Ind, 1);
990 } else if (p_ptr->iron_winner) kingly(Ind, 3);
991 /* Retirement in Valinor? - C. Blue :) */
992 if (in_valinor(&p_ptr->wpos)) kingly(Ind, 2);
993
994 /* Kill him */
995 p_ptr->deathblow = 0;
996 player_death(Ind);
997 }
998
999
1000
1001 /*
1002 * Save a character
1003 */
do_cmd_save_game(int Ind)1004 void do_cmd_save_game(int Ind)
1005 {
1006 player_type *p_ptr = Players[Ind];
1007
1008 /* Disturb the player */
1009 disturb(Ind, 1, 0);
1010
1011 /* Clear messages */
1012 msg_print(Ind, NULL);
1013
1014 /* Handle stuff */
1015 handle_stuff(Ind);
1016
1017 /* Message */
1018 msg_print(Ind, "Saving game...");
1019
1020 /* The player is not dead */
1021 (void)strcpy(p_ptr->died_from, "(saved)");
1022
1023 /* Forbid suspend */
1024 signals_ignore_tstp();
1025
1026 /* Save the player */
1027 if (save_player(Ind))
1028 {
1029 msg_print(Ind, "Saving game... done.");
1030 }
1031
1032 /* Save failed (oops) */
1033 else
1034 {
1035 msg_print(Ind, "Saving game... failed!");
1036 }
1037
1038 /* Allow suspend again */
1039 signals_handle_tstp();
1040
1041 /* Note that the player is not dead */
1042 (void)strcpy(p_ptr->died_from, "(alive and well)");
1043 }
1044
1045
1046
1047 /*
1048 * Hack -- Calculates the total number of points earned -JWT-
1049 */
1050 /* FIXME: this function returns bad value when max_exp is stupidly large
1051 * (usually admin chars) */
total_points(int Ind)1052 int total_points(int Ind)
1053 {
1054 u32b points, tmp_base, tmp1, tmp2, tmp3, tmp3a, bonusm, bonusd;
1055 u32b lev_factoring;
1056 player_type *p_ptr = Players[Ind];
1057
1058 /* kill maggot for 100% bonus on total score? -> no
1059 why a little bonus for HELL mode? the honour for the player
1060 who chooses hell mode on his own is far greater without it. -> no
1061 add cash to exp? what if the player collected cool gear instead?
1062 make cash allow the player to skip lots of levels on the ladder? -> no
1063 max_dlv? just enter the staircase in lothlorien and back up. -> no
1064 For now let's calc it basing on pure progress in gameplay!: */
1065 //exp counts mainly, level factors in
1066 // return (p_ptr->max_exp * (p_ptr->max_plv + 30) / 30);
1067
1068 /* Bonus */
1069 bonusm = 100; bonusd = 100;
1070 if (p_ptr->mode & MODE_NO_GHOST)
1071 {
1072 bonusm += 25;
1073 }
1074 if (p_ptr->mode & MODE_HARD)
1075 {
1076 bonusm += 25;
1077 }
1078
1079 #ifndef ALT_EXPRATIO
1080 /* Bonus might cause overflow at lvl 94+ - so maybe compensate */
1081 tmp_base = (p_ptr->max_exp * 3) / 3; //leaving this, changing lev_factoring instead..
1082 lev_factoring = 58; //was 33 -> overflow at 94+
1083
1084 /* split the number against overflow bug */
1085 tmp1 = p_ptr->lev + lev_factoring;
1086 tmp2 = lev_factoring;
1087 tmp3a = tmp_base % 10000000;
1088 tmp3 = (tmp_base - tmp3a) / 10000000;
1089 points = (((tmp3a * bonusm) / bonusd) * tmp1) / tmp2;
1090 points += ((((10000000 * bonusm) / bonusd) * tmp1) / tmp2) * tmp3;
1091 #else
1092 tmp_base = p_ptr->max_exp;
1093 lev_factoring = 75; /* (yeek warrior -> tl mimic : *3) */
1094 lev_factoring = 230; /* (yeek warrior -> tl mimic : *2) */
1095
1096 /* split the number against overflow bug; divide by 10 to avoid overflow again */
1097 tmp1 = (p_ptr->expfact + lev_factoring) / 10;
1098 tmp2 = 310 / 10; /* (230 + 80 (yeek warrior)) */
1099 tmp3a = tmp_base % 10000000;
1100 tmp3 = (tmp_base - tmp3a) / 10000000;
1101 points = (((tmp3a * bonusm) / bonusd) * tmp1) / tmp2;
1102 points += ((((10000000 * bonusm) / bonusd) * tmp1) / tmp2) * tmp3;
1103 #endif
1104 return points;
1105
1106 //level counts mainly, exp factors in at higher levels
1107 //return (p_ptr->max_plv * (300 + (p_ptr->max_exp / 100000)) / 300);
1108 #if 0
1109 /* Maggot bonus.. beware, r_idx is hard-coded! */
1110 int i = p_ptr->r_killed[8]? 50 : 100;
1111 if (p_ptr->mode & MODE_HARD) i = i * 5 / 4;
1112
1113 if (p_ptr->mode & MODE_NO_GHOST) return (((p_ptr->max_exp + (100 * p_ptr->max_dlv)) * 4 / 3)*i/100);
1114 else return ((p_ptr->max_exp + (100 * p_ptr->max_dlv) + p_ptr->au)*i/100);
1115 #endif //0
1116 }
1117
1118 /*
1119 * Semi-Portable High Score List Entry (128 bytes) -- BEN
1120 *
1121 * All fields listed below are null terminated ascii strings.
1122 *
1123 * In addition, the "number" fields are right justified, and
1124 * space padded, to the full available length (minus the "null").
1125 *
1126 * Note that "string comparisons" are thus valid on "pts".
1127 */
1128
1129 typedef struct high_score high_score;
1130 struct high_score
1131 {
1132 char what[8]; /* Version info (string) */
1133
1134 char pts[11]; /* Total Score (number) */
1135
1136 char gold[11]; /* Total Gold (number) */
1137
1138 char turns[11]; /* Turns Taken (number) */
1139
1140 char day[11]; /* Time stamp (string) */
1141
1142 char who[16]; /* Player Name (string) */
1143 char whose[16]; /* Account Name (string) */
1144
1145 char sex[2]; /* Player Sex (string) */
1146 char p_r[3]; /* Player Race (number) */
1147 char p_c[3]; /* Player Class (number) */
1148
1149 char cur_lev[4]; /* Current Player Level (number) */
1150 char cur_dun[4]; /* Current Dungeon Level (number) */
1151 char max_lev[4]; /* Max Player Level (number) */
1152 char max_dun[4]; /* Max Dungeon Level (number) */
1153
1154 char how[50]; /* Method of death (string) */
1155
1156 char mode[1]; /* Difficulty/character mode */
1157 };
1158
1159 #if 0 /* different format? */
1160 typedef struct high_score_old high_score_old;
1161 struct high_score_old
1162 {
1163 char what[8]; /* Version info (string) */
1164
1165 char pts[11]; /* Total Score (number) */
1166
1167 char gold[11]; /* Total Gold (number) */
1168
1169 char turns[11]; /* Turns Taken (number) */
1170
1171 char day[10]; /* Time stamp (string) */
1172
1173 char who[16]; /* Player Name (string) */
1174 char whose[16]; /* Account Name (string) */
1175
1176 char sex[2]; /* Player Sex (string) */
1177 char p_r[3]; /* Player Race (number) */
1178 char p_c[3]; /* Player Class (number) */
1179
1180 char cur_lev[4]; /* Current Player Level (number) */
1181 char cur_dun[4]; /* Current Dungeon Level (number) */
1182 char max_lev[4]; /* Max Player Level (number) */
1183 char max_dun[4]; /* Max Dungeon Level (number) */
1184
1185 char how[50]; /* Method of death (string) */
1186
1187 char mode[1]; /* Difficulty/character mode */
1188 };
1189 #else /* same format actually */
1190 typedef struct high_score high_score_old;
1191 #endif
1192
1193
highscore_send(char * buffer,int max)1194 int highscore_send(char *buffer, int max) {
1195 int len = 0, len2;
1196 FILE *hsp;
1197 char buf[1024];
1198 struct high_score score;
1199 cptr mode, race, class;
1200
1201 path_build(buf, 1024, ANGBAND_DIR_DATA, "scores.raw");
1202
1203 hsp = fopen(buf, "rb");
1204 if (!hsp) return(0);
1205
1206 while (fread(&score, sizeof(struct high_score), 1, hsp)) {
1207 switch (score.mode[0] & MODE_MASK) {
1208 case MODE_NORMAL:
1209 mode = "Normal";
1210 break;
1211 case MODE_HARD:
1212 mode = "Hard";
1213 break;
1214 case MODE_NO_GHOST:
1215 mode = "Unworldly";
1216 break;
1217 case (MODE_HARD | MODE_NO_GHOST):
1218 mode = "Hellish";
1219 break;
1220 case MODE_EVERLASTING:
1221 mode = "Everlasting";
1222 break;
1223 case MODE_PVP:
1224 mode = "PvP";
1225 break;
1226 default:
1227 mode = "Unknown";
1228 break;
1229 }
1230
1231 race = race_info[atoi(score.p_r)].title;
1232 class = class_info[atoi(score.p_c)].title;
1233
1234 len2 = snprintf(buf, 1024, "pts=%s\ngold=%s\nturns=%s\nday=%s\nwho=%s\nwhose=%s\nsex=%s\nrace=%s\nclass=%s\ncur_lev=%s\ncur_dun=%s\nmax_lev=%s\nmax_dun=%s\nhow=%s\nmode=%s\n",
1235 score.pts, score.gold, score.turns, score.day, score.who, score.whose, score.sex, race, class, score.cur_lev, score.cur_dun, score.max_lev, score.max_dun, score.how, mode);
1236
1237 if (len2 + len < max) {
1238 memcpy(&buffer[len], buf, len2);
1239 len += len2;
1240 buffer[len] = '\0';
1241 }
1242 }
1243
1244 fclose(hsp);
1245
1246 return len;
1247 }
1248
houses_send(char * buffer,int max)1249 int houses_send(char *buffer, int max) {
1250 int i, len = 0, len2, screen_x, screen_y;
1251 house_type *h_ptr;
1252 struct dna_type *dna;
1253 char buf[1024];
1254 s32b price;
1255 cptr owner, wpos;
1256
1257 for (i = 0; i < num_houses; i++) {
1258 h_ptr = &houses[i];
1259 dna = h_ptr->dna;
1260
1261 screen_y = h_ptr->dy * 5 / MAX_HGT;
1262 screen_x = h_ptr->dx * 5 / MAX_WID;
1263 wpos = wpos_format(0, &h_ptr->wpos);
1264
1265 price = dna->price;
1266
1267 if (houses[i].dna->owner_type == OT_PLAYER)
1268 owner = lookup_player_name(houses[i].dna->owner);
1269 else if (houses[i].dna->owner_type == OT_GUILD)
1270 owner = guilds[houses[i].dna->owner].name;
1271 else owner = "";
1272
1273 if (!owner) owner = "";
1274
1275 if (houses[i].dna->owner_type == OT_GUILD)
1276 len2 = snprintf(buf, 1024, "location=[%d,%d] in %s\nprice=%d\nguild=%s\n",
1277 screen_y, screen_x, wpos, price, owner);
1278 else
1279 len2 = snprintf(buf, 1024, "location=[%d,%d] in %s\nprice=%d\nowner=%s\n",
1280 screen_y, screen_x, wpos, price, owner);
1281
1282 if (len + len2 < max) {
1283 memcpy(&buffer[len], buf, len2);
1284 len += len2;
1285 buffer[len] = '\0';
1286 }
1287 }
1288
1289 return len;
1290 }
1291
1292 /*
1293 * The "highscore" file descriptor, if available.
1294 */
1295 static int highscore_fd = -1;
1296
1297
1298 /*
1299 * Seek score 'i' in the highscore file
1300 */
highscore_seek(int i)1301 static int highscore_seek(int i)
1302 {
1303 /* Seek for the requested record */
1304 return (fd_seek(highscore_fd, (huge)(i) * sizeof(high_score)));
1305 }
1306
1307
1308 /*
1309 * Read one score from the highscore file
1310 */
highscore_read(high_score * score)1311 static errr highscore_read(high_score *score)
1312 {
1313 /* Read the record, note failure */
1314 return (fd_read(highscore_fd, (char*)(score), sizeof(high_score)));
1315 }
1316
1317
1318 /*
1319 * Write one score to the highscore file
1320 */
highscore_write(high_score * score)1321 static int highscore_write(high_score *score)
1322 {
1323 /* Write the record, note failure */
1324 return (fd_write(highscore_fd, (char*)(score), sizeof(high_score)));
1325 }
1326
highscore_seek_old(int i)1327 static int highscore_seek_old(int i)
1328 {
1329 /* Seek for the requested record */
1330 return (fd_seek(highscore_fd, (huge)(i) * sizeof(high_score_old)));
1331 }
highscore_read_old(high_score_old * score)1332 static errr highscore_read_old(high_score_old *score)
1333 {
1334 /* Read the record, note failure */
1335 return (fd_read(highscore_fd, (char*)(score), sizeof(high_score_old)));
1336 }
1337
1338
1339
1340 /*
1341 * Just determine where a new score *would* be placed
1342 * Return the location (0 is best) or -1 on failure
1343 */
1344 #ifndef NEW_HISCORE
highscore_where(high_score * score,bool * move_up,bool * move_down)1345 static int highscore_where(high_score *score, bool *move_up, bool *move_down)
1346 #else
1347 static int highscore_where(high_score *score, int *erased_slot)
1348 #endif
1349 {
1350 int i, slot_ret = -1;
1351 high_score the_score;
1352 #ifndef NEW_HISCORE
1353 int slot_pts = -1, slot_name = -1, slot_account = -1;
1354 #else
1355 bool flag_char, flag_acc, flag_c, flag_r;
1356 int slot_old = -1;
1357 int entries_from_same_account = 0;
1358 #endif
1359
1360 /* Paranoia -- it may not have opened */
1361 if (highscore_fd < 0) return (-1);
1362
1363 /* Go to the start of the highscore file */
1364 if (highscore_seek(0)) return (-1);
1365
1366
1367 #ifndef NEW_HISCORE /*restructuring with new hex flags, see #else branch - C. Blue */
1368 /* Read until we get to a higher score
1369 or appropriate rules are met -C. Blue */
1370 for (i = 0; i < MAX_HISCORES; i++)
1371 {
1372 if (highscore_read(&the_score))
1373 {
1374 if (slot_pts == -1) slot_pts = i;
1375 break;
1376 }
1377 if ((strcmp(the_score.pts, score->pts) < 0) && (slot_pts == -1))
1378 slot_pts = i;
1379 if ((!strcmp(the_score.who, score->who)) && (slot_name == -1))
1380 slot_name = i;
1381 if ((!strcmp(the_score.whose, score->whose)) && (slot_account == -1))
1382 slot_account = i;
1383 }
1384
1385 /* Insert score right before any higher score */
1386 (*move_up) = FALSE; (*move_down) = FALSE;
1387 if (slot_pts != -1) slot_ret = slot_pts;
1388
1389 /* If this char is already on high-score list,
1390 and replace_hiscore is set to 1 then replace it! */
1391 if ((cfg.replace_hiscore == 1) && (slot_name != -1)) {
1392 /* Replace old score of this character */
1393 if (slot_pts != -1) {
1394 if (slot_name > slot_pts) (*move_up) = TRUE;
1395 if (slot_name < slot_pts) (*move_down) = TRUE;
1396 } else {
1397 if (slot_name < MAX_HISCORES - 1) (*move_down) = TRUE;
1398 }
1399 }
1400 /* If this char is already on high-score list,
1401 and the new score is better than the old one
1402 and replace_hiscore is set to 2 then replace it! */
1403 else if ((cfg.replace_hiscore == 2) && (slot_name != -1)) {
1404 if ((slot_pts > slot_name) || (slot_pts == -1)) {
1405 /* ignore the new score */
1406 slot_ret = -2; /* hack */
1407 } else {
1408 /* If we just have to replace a score with a
1409 better one at the _same_ position, we still
1410 set move_up = TRUE since this will prevent
1411 the previous entry from sliding down and have
1412 it been overwritten instead */
1413 (*move_up) = TRUE;
1414 }
1415 }
1416
1417 if (slot_ret != -1) return (slot_ret);
1418
1419 /* The "last" entry is always usable */
1420 return (MAX_HISCORES - 1);
1421 #else
1422 /* Read entries until we find a slot with lower score or arrive at the end */
1423 for (i = 0; i < MAX_HISCORES; i++) {
1424 if (highscore_read(&the_score)) {
1425 /* arrived at the end of entries -> take this one then */
1426 slot_ret = i;
1427 break;
1428 }
1429 /* always valid rule: sort in after next-highest score */
1430 if (strcmp(the_score.pts, score->pts) < 0) {
1431 slot_ret = i;
1432 break;
1433 }
1434 }
1435 /* hack: last entry is always 'available' (effectively unused though!) */
1436 if (slot_ret == -1) slot_ret = MAX_HISCORES - 1;
1437
1438 #if 0 /* don't start after the appropriate slot, but check all slots */
1439 /* Go to next position in highscore file, if available. Otherwise, skip the for loop: */
1440 if (!highscore_seek(slot_ret + 1))
1441 /* now check previous entries before adding this one, if required
1442 according to our custom extended rule set from tomenet.cfg */
1443 for (i = slot_ret + 1; i < MAX_HISCORES; i++) {
1444 #else
1445 /* Go to the start of the highscore file */
1446 if (highscore_seek(0)) return (-1);
1447 /* now check previous entries before adding this one, if required
1448 according to our custom extended rule set from tomenet.cfg,
1449 whether we find an entry we have to actually replace with the
1450 new one.. */
1451 for (i = 0; i < MAX_HISCORES; i++) {
1452 #endif
1453 if (highscore_read(&the_score)) {
1454 /* arrived at the end of entries -> nothing to replace */
1455 break;
1456 }
1457 /* first, prepare flag test results */
1458 flag_char = flag_acc = flag_c = flag_r = TRUE;
1459 /* check for same char name */
1460 if (cfg.replace_hiscore & 0x08) {
1461 if (strcmp(the_score.who, score->who)) flag_char = FALSE;
1462 }
1463 /* check for same acc name */
1464 if (strcmp(the_score.whose, score->whose)) {
1465 if (cfg.replace_hiscore & 0x10) flag_acc = FALSE;
1466 } else { /* for cfg.replace_hiscore & 0x7 > 2 */
1467 entries_from_same_account++;
1468 }
1469 /* check for same class */
1470 if (cfg.replace_hiscore & 0x20) {
1471 if (strcmp(the_score.p_c, score->p_c)) flag_c = FALSE;
1472 }
1473 /* check for same race */
1474 if (cfg.replace_hiscore & 0x40) {
1475 if (strcmp(the_score.p_r, score->p_r)) flag_r = FALSE;
1476 }
1477
1478 /* now test special modes in combination with flag results */
1479 if ((cfg.replace_hiscore & 0x7) == 1) {
1480 /* replace older entries by newer entries */
1481 if (flag_char && flag_acc && flag_c && flag_r) {
1482 slot_old = i;
1483 break;
1484 }
1485 }
1486 if ((cfg.replace_hiscore & 0x7) == 2) {
1487 /* replace lower entries by higher entries */
1488 if (flag_char && flag_acc && flag_c && flag_r) {
1489 if (strcmp(the_score.pts, score->pts) < 0) {
1490 slot_old = i;
1491 break;
1492 } else {
1493 /* hack for display_scores_aux later:
1494 show that this entry is our 'goal' (use another colour) */
1495 slot_old = -2 -i; /* <= -2 -> 'there exists a slot, but we can't reach it yet' */
1496 }
1497 }
1498 }
1499 if ((cfg.replace_hiscore & 0x7) == 3) {
1500 /* replace lowest entry of this account which matches the flags */
1501 if (flag_char && flag_acc && flag_c && flag_r) {
1502 if (strcmp(the_score.pts, score->pts) < 0) {
1503 slot_old = i;
1504 } else {
1505 /* hack for display_scores_aux later:
1506 show that this entry is our 'goal' (use another colour) */
1507 slot_old = -2 -i; /* <= -2 -> 'there exists a slot, but we can't reach it yet' */
1508 }
1509 /* replace last entry of this account anyway due to limitation of this mode? */
1510 } else if (entries_from_same_account == 2) {
1511 if (strcmp(the_score.pts, score->pts) < 0) {
1512 slot_old = i;
1513 break;
1514 } else {
1515 /* hack for display_scores_aux later:
1516 show that this entry is our 'goal' (use another colour) */
1517 slot_old = -2 -i; /* <= -2 -> 'there exists a slot, but we can't reach it yet' */
1518 }
1519 }
1520 }
1521 if ((cfg.replace_hiscore & 0x7) == 4) {
1522 /* replace lowest entry of this account which matches the flags */
1523 if (flag_char && flag_acc && flag_c && flag_r) {
1524 if (strcmp(the_score.pts, score->pts) < 0) {
1525 slot_old = i;
1526 } else {
1527 /* hack for display_scores_aux later:
1528 show that this entry is our 'goal' (use another colour) */
1529 slot_old = -2 -i; /* <= -2 -> 'there exists a slot, but we can't reach it yet' */
1530 }
1531 /* replace last entry of this account anyway due to limitation of this mode? */
1532 } else if (entries_from_same_account == 3) {
1533 if (strcmp(the_score.pts, score->pts) < 0) {
1534 slot_old = i;
1535 break;
1536 } else {
1537 /* hack for display_scores_aux later:
1538 show that this entry is our 'goal' (use another colour) */
1539 slot_old = -2 -i; /* <= -2 -> 'there exists a slot, but we can't reach it yet' */
1540 }
1541 }
1542 }
1543 }
1544
1545 #if 0 /* basic version without -2 -i slot_old hack for display_scores_aux */
1546 /* If this char is already on high-score list,
1547 and replace_hiscore is set to values other than 0,
1548 - resulting in 'slot_old != -1' - then replace it! */
1549 (*erased_slot) = -1;
1550 /* found an old entry to remove? */
1551 if (slot_old > -1) {
1552 /* if the old entry is on last position, it'll be overwritten anyway */
1553 if (slot_old < MAX_HISCORES - 1) {
1554 (*erased_slot) = slot_old;
1555 }
1556 }
1557 /* Nothing to replace, although we're in replace mode,
1558 ie we theoretically made it into the high score, but
1559 our previous entry was too good to be replaced, and
1560 thereby our new entry is to be discarded? */
1561 else if ((cfg.replace_hiscore & 0x7) != 0)
1562 slot_ret = -1; /* hack: 'ignore the new score' */
1563 #else /* hacked for display_scores_aux best possible display */
1564 /* If this char is already on high-score list,
1565 and replace_hiscore is set to values other than 0,
1566 - resulting in 'slot_old > -1' - then replace it! */
1567 (*erased_slot) = slot_old;
1568 /* Nothing to replace, although we're in replace mode
1569 AND we found a previous matching entry (slot_old < -1):
1570 ie we theoretically made it into the high score, but
1571 our previous entry was too good to be replaced, and
1572 thereby our new entry is to be discarded? */
1573 if (((cfg.replace_hiscore & 0x7) != 0) && slot_old < -1)
1574 slot_ret = -2 - slot_ret; /* hack: 'ignore the new score'
1575 hack: useful display for display_scores_aux */
1576 #endif
1577
1578 return (slot_ret);
1579 #endif
1580 }
1581
1582
1583 /*
1584 * Actually place an entry into the high score file
1585 * Return the location (0 is best) or -1 on "failure"
1586 */
1587 static int highscore_add(high_score *score) {
1588 int i, slot;
1589 high_score the_score, tmpscore;
1590 bool done = FALSE;
1591 #ifndef NEW_HISCORE
1592 bool move_up, move_down;
1593 #else
1594 int erased_slot, cur_slots = MAX_HISCORES;
1595 #endif
1596
1597 /* Paranoia -- it may not have opened */
1598 if (highscore_fd < 0) return (-1);
1599
1600 /* Determine where the score should go */
1601 #ifndef NEW_HISCORE
1602 slot = highscore_where(score, &move_up, &move_down);
1603 #else
1604 slot = highscore_where(score, &erased_slot);
1605 #endif
1606 /* Hack -- Not on the list
1607 (ie didn't match conditions to replace previous score) */
1608 if (slot < 0) return (-1);
1609
1610 /* Hack -- prepare to dump the new score */
1611 the_score = (*score);
1612
1613 #ifndef NEW_HISCORE
1614 if (!move_down) {
1615 /* Slide all the scores down one */
1616 for (i = slot; !done && (i < MAX_HISCORES); i++) {
1617 /* Read the old guy, note errors */
1618 if (highscore_seek(i)) return (-1);
1619 if (highscore_read(&tmpscore)) done = TRUE;
1620
1621 /* Back up and dump the score we were holding */
1622 if (highscore_seek(i)) return (-1);
1623 if (highscore_write(&the_score)) return (-1);
1624
1625 if (move_up && !strcmp(score->who, tmpscore.who)) {
1626 /* If older score is to be removed, we can stop here */
1627 return(slot);
1628 }
1629
1630 /* Hack -- Save the old score, for the next pass */
1631 the_score = tmpscore;
1632 }
1633 } else {
1634 /* Move upwards through the score board */
1635 for (i = slot; !done && (i >= 0); i--) {
1636 /* Read the old guy, note errors */
1637 if (highscore_seek(i)) return (-1);
1638 if (highscore_read(&tmpscore)) done = TRUE;
1639
1640 /* Back up and dump the score we were holding */
1641 if (highscore_seek(i)) return (-1);
1642 if (highscore_write(&the_score)) return (-1);
1643
1644 if (!strcmp(score->who, tmpscore.who)) {
1645 /* If older score is to be removed, we can stop here */
1646 return(slot);
1647 }
1648
1649 /* Hack -- Save the old score, for the next pass */
1650 the_score = tmpscore;
1651 }
1652 }
1653 #else
1654 /* Erase one old entry first? */
1655 if (erased_slot > -1) {
1656 cur_slots = erased_slot + 1; /* how many used slots does the high score table currently have? */
1657 for (i = erased_slot; i < MAX_HISCORES; i++) {
1658 /* Read the following entry, if any */
1659 if (highscore_seek(i + 1)) break;
1660 if (highscore_read(&tmpscore)) break;
1661 /* do log file entry: */
1662 // if (i == erased_slot) s_printf("HISCORE_DEL: #%d %s\n", i, tmpscore.who);
1663 /* hack: count high score table slots in use: */
1664 cur_slots++;
1665 /* Overwrite current entry with it */
1666 if (highscore_seek(i)) return(-1);
1667 if (highscore_write(&tmpscore)) return(-1);
1668 }
1669 /* take note if the newly inserted slot has to be moved
1670 because its actually below the erased slot */
1671 if (slot > erased_slot) slot--;
1672 }
1673 /* Insert new entry - Just slide all the scores down one */
1674 /* note ('for' destination: if we previously erased one entry,
1675 then it means the last entry has become obsolete, so we
1676 skip it when we shift downwards again, otherwise we'd
1677 duplicate it. */
1678 // for (i = slot; !done && (i < (erased_slot > -1 ? MAX_HISCORES - 1 : MAX_HISCORES)); i++) {
1679 for (i = slot; !done && (i < cur_slots); i++) {
1680 /* Read the old guy, note errors */
1681 if (highscore_seek(i)) return (-1);
1682 if (highscore_read(&tmpscore)) done = TRUE;
1683 /* Back up and dump the score we were holding */
1684 if (highscore_seek(i)) return (-1);
1685 if (highscore_write(&the_score)) return (-1);
1686 /* Hack -- Save the old score, for the next pass */
1687 the_score = tmpscore;
1688 }
1689 #endif
1690
1691 /* Return location used */
1692 return (slot);
1693 }
1694
1695
1696
1697 /*
1698 * Display the scores in a given range.
1699 * Assumes the high score list is already open.
1700 * Only five entries per line, too much info.
1701 *
1702 * Mega-Hack -- allow "fake" entry at the given position.
1703 */
1704 #ifndef NEW_HISCORE
1705 static void display_scores_aux(int Ind, int line, int note, high_score *score)
1706 #else
1707 static void display_scores_aux(int Ind, int line, int note, int erased_slot, high_score *score)
1708 #endif
1709 {
1710 int i, j, from, to, place;//, attr
1711 char attrc[3];
1712
1713 high_score the_score;
1714
1715 char out_val[256];
1716
1717 FILE *fff;
1718 char file_name[MAX_PATH_LENGTH];
1719
1720 /* Hack - distinguish between wilderness and dungeon */
1721 bool wilderness;
1722
1723 /* Hack - display if the player was a former winner */
1724 char extra_info[40];
1725
1726 /* Paranoia -- it may not have opened */
1727 if (highscore_fd < 0) return;
1728
1729 /* Temporary file */
1730 if (path_temp(file_name, MAX_PATH_LENGTH)) return;
1731
1732 /* Open the temp file */
1733 fff = my_fopen(file_name, "wb");
1734 if (fff == (FILE*)NULL) return;
1735
1736 /* Assume we will show the first 20 */
1737 from = 0;
1738 to = SCORES_SHOWN;
1739 if (to > MAX_HISCORES) to = MAX_HISCORES;
1740
1741
1742 /* Seek to the beginning */
1743 if (highscore_seek(0)){
1744 my_fclose(fff);
1745 return;
1746 }
1747
1748 /* Hack -- Count the high scores */
1749 for (i = 0; i < MAX_HISCORES; i++)
1750 if (highscore_read(&the_score)) break;
1751
1752 #ifndef NEW_HISCORE
1753 /* Hack -- allow "fake" entry to be last */
1754 if ((note == i) && score) i++;
1755 #else
1756 if (note < -1) note = -(note + 2); /* unwrap display hack */
1757 if (score) i++; /* hack: insert fake entry */
1758 #endif
1759
1760 /* Forget about the last entries */
1761 if (i > to) i = to;
1762
1763 /* Show 5 per page, until "done" */
1764 for (j = from, place = j+1; j < i; j++, place++) {
1765 int pr, pc, clev, mlev, cdun, mdun;
1766 byte modebuf;
1767 char modestr[20], modecol[5];
1768 cptr gold, when, aged;
1769
1770
1771 /* Hack -- indicate death in yellow */
1772 //attr = (j == note) ? TERM_YELLOW : TERM_WHITE;
1773
1774
1775 /* Mega-Hack -- insert a "fake" record */
1776 if ((note == j) && score) {
1777 sprintf(attrc, "\377G");
1778 the_score = (*score);
1779 //attr = TERM_L_GREEN;
1780 score = NULL;
1781 note = -1;
1782 j--;
1783 i--;
1784 }
1785
1786 /* Read a normal record */
1787 else {
1788 sprintf(attrc, "\377w");
1789 #ifdef NEW_HISCORE
1790 /* unwrap display hack part 2 */
1791 if (erased_slot < -1 && -(erased_slot + 2) == j)
1792 /* indicate our 'goal' character we're chasing, score-wise, for replacing him */
1793 sprintf(attrc, "\377B");
1794 else if (erased_slot > -1 && erased_slot == j)
1795 /* indicate our ex-goal which we now seem to have reached, just for fun */
1796 sprintf(attrc, "\377B");
1797 #endif
1798 /* Read the proper record */
1799 if (highscore_seek(j)) break;
1800 if (highscore_read(&the_score)) break;
1801 }
1802
1803
1804 strcpy(extra_info, ".");
1805 wilderness = FALSE;
1806
1807 /* Extract the race/class */
1808 pr = atoi(the_score.p_r);
1809 pc = atoi(the_score.p_c);
1810
1811 /* Extract the level info */
1812 clev = atoi(the_score.cur_lev);
1813 mlev = atoi(the_score.max_lev);
1814 #if 0
1815 if (the_score.cur_dun[strlen(the_score.cur_dun) - 1] == '\001') {
1816 wilderness = TRUE;
1817 the_score.cur_dun[strlen(the_score.cur_dun) - 1] = '\0';
1818 }
1819 #endif
1820 cdun = atoi(the_score.cur_dun);
1821 mdun = atoi(the_score.max_dun);
1822
1823 /* Hack -- extract the gold and such */
1824 for (when = the_score.day; isspace(*when); when++) /* loop */;
1825 for (gold = the_score.gold; isspace(*gold); gold++) /* loop */;
1826 for (aged = the_score.turns; isspace(*aged); aged++) /* loop */;
1827
1828 modebuf = (the_score.mode[0] & MODE_MASK);
1829 strcpy(modestr, "");
1830 strcpy(modecol, "");
1831 switch (modebuf) {
1832 case MODE_HARD:
1833 strcpy(modestr, "purgatorial ");
1834 // strcpy(modecol, "\377s");
1835 break;
1836 case MODE_NO_GHOST:
1837 strcpy(modestr, "unworldly ");
1838 strcpy(modecol, "\377D");
1839 break;
1840 case (MODE_HARD + MODE_NO_GHOST):
1841 strcpy(modestr, "hellish ");
1842 strcpy(modecol, "\377D");
1843 break;
1844 case MODE_NORMAL:
1845 // strcpy(modecol, "\377w");
1846 break;
1847 }
1848
1849 /* Hack ;) Remember if the player was a former winner */
1850 if (the_score.how[strlen(the_score.how) - 1] == '\001')
1851 {
1852 strcpy(extra_info, ". (Defeated Morgoth)");
1853 the_score.how[strlen(the_score.how) - 1] = '\0';
1854 }
1855
1856 /* Dump some info */
1857 snprintf(out_val, sizeof(out_val), "%2s%3d.%10s %s%s the %s%s %s, Lv.%d",
1858 attrc, place, the_score.pts, modecol, the_score.who, modestr,
1859 race_info[pr].title, class_info[pc].title,
1860 clev);
1861
1862 /* Append a "maximum level" */
1863 if (mlev > clev) strcat(out_val, format(" (max %d)", mlev));
1864
1865 /* Dump the first line */
1866 fprintf(fff, "%s\n", out_val);
1867
1868 /* Another line of info */
1869 if (strcmp(the_score.how, "winner") && strcmp(the_score.how, "*winner*") && strcmp(the_score.how, "iron champion") && strcmp(the_score.how, "iron emperor"))
1870 snprintf(out_val, sizeof(out_val),
1871 " Killed by %s\n"
1872 " on %s %d%s%s",
1873 the_score.how, wilderness ? "wilderness level" : "dungeon level", cdun, mdun > cdun ? format(" (max %d)", mdun) : "", extra_info);
1874 else if (!strcmp(the_score.how, "winner"))
1875 snprintf(out_val, sizeof(out_val),
1876 " \377vRetired after a legendary career\n"
1877 " on %s %d%s%s", wilderness ? "wilderness level" : "dungeon level", cdun, mdun > cdun ? format(" (max %d)", mdun) : "", extra_info);
1878 else if (!strcmp(the_score.how, "*winner*"))
1879 snprintf(out_val, sizeof(out_val),
1880 " \377vRetired on the shores of Valinor\n"
1881 " on %s %d%s%s", wilderness ? "wilderness level" : "dungeon level", cdun, mdun > cdun ? format(" (max %d)", mdun) : "", extra_info);
1882 else if (!strcmp(the_score.how, "iron champion"))
1883 snprintf(out_val, sizeof(out_val),
1884 " \377sRetired iron champion\n"
1885 " on %s %d%s%s", wilderness ? "wilderness level" : "dungeon level", cdun, mdun > cdun ? format(" (max %d)", mdun) : "", extra_info);
1886 else if (!strcmp(the_score.how, "iron emperor"))
1887 snprintf(out_val, sizeof(out_val),
1888 " \377vRetired from the iron throne\n"
1889 " on %s %d%s%s", wilderness ? "wilderness level" : "dungeon level", cdun, mdun > cdun ? format(" (max %d)", mdun) : "", extra_info);
1890
1891 /* Hack -- some people die in the town */
1892 if (!cdun)
1893 {
1894 /* (can't be in Valinor while we're in town, can we) */
1895 if (strcmp(the_score.how, "winner"))
1896 snprintf(out_val, sizeof(out_val),
1897 " Killed by %s\n"
1898 " in town%s",
1899 the_score.how, mdun > cdun ? format(" (max %d)", mdun) : "");
1900 else
1901 snprintf(out_val, sizeof(out_val),
1902 " \377vRetired after a legendary career\n"
1903 " in town%s", mdun > cdun ? format(" (max %d)", mdun) : "");
1904 }
1905
1906 /* Append a "maximum level" */
1907 // if (mdun > cdun) strcat(out_val, format(" (max %d)", mdun));
1908
1909 /* Dump the info */
1910 fprintf(fff, "%s\n", out_val);
1911
1912 /* And still another line of info */
1913 sprintf(out_val,
1914 " (Date %s, Gold %s, Turn %s).",
1915 when, gold, aged);
1916 fprintf(fff, "%s\n", out_val);
1917
1918 #if 0 /* bad formatting in > 4.4.1.7, better without this */
1919 /* Print newline if this isn't the last one */
1920 if (j < i - 1)
1921 #endif
1922 fprintf(fff, "\n");
1923 }
1924
1925 /* Close the file */
1926 my_fclose(fff);
1927
1928 /* Display the file contents */
1929 show_file(Ind, file_name, "High Scores", line, 0, 5);
1930
1931 /* Remove the file */
1932 fd_kill(file_name);
1933 }
1934
1935
1936
1937
1938 /*
1939 * Enters a players name on a hi-score table, if "legal", and in any
1940 * case, displays some relevant portion of the high score list.
1941 *
1942 * Assumes "signals_ignore_tstp()" has been called.
1943 */
1944 static errr top_twenty(int Ind) {
1945 player_type *p_ptr = Players[Ind];
1946 //int j;
1947
1948 high_score the_score;
1949
1950 time_t ct = time((time_t*)0);
1951
1952 if (is_admin(p_ptr)) return 0;
1953
1954 /* No score file */
1955 if (highscore_fd < 0) {
1956 s_printf("Score file unavailable.\n");
1957 return (0);
1958 }
1959
1960 /* Interupted */
1961 if (!p_ptr->total_winner && streq(p_ptr->died_from, "Interrupting")) {
1962 msg_print(Ind, "Score not registered due to interruption.");
1963 /* display_scores_aux(0, 10, -1, NULL); */
1964 return (0);
1965 }
1966
1967 /* Quitter */
1968 if (!p_ptr->total_winner && streq(p_ptr->died_from, "Quitting")) {
1969 msg_print(Ind, "Score not registered due to quitting.");
1970 /* display_scores_aux(0, 10, -1, NULL); */
1971 return (0);
1972 }
1973
1974
1975 /* Clear the record */
1976 WIPE(&the_score, high_score);
1977
1978 /* Save the version */
1979 sprintf(the_score.what, "%u.%u.%u",
1980 VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
1981
1982 /* Calculate and save the points */
1983 sprintf(the_score.pts, "%10u", total_points(Ind));
1984 the_score.pts[10] = '\0';
1985
1986 /* Save the current gold */
1987 sprintf(the_score.gold, "%10u", p_ptr->au);
1988 the_score.gold[10] = '\0';
1989
1990 /* Save the current turn */
1991 sprintf(the_score.turns, "%10u", turn);
1992 the_score.turns[10] = '\0';
1993
1994 #ifdef HIGHSCORE_DATE_HACK
1995 /* Save the date in a hacked up form (9 chars) */
1996 sprintf(the_score.day, "%-.6s %-.2s", ctime(&ct) + 4, ctime(&ct) + 22);
1997 #else
1998 /* Save the date in standard form (8 chars) */
1999 // strftime(the_score.day, 9, "%m/%d/%y", localtime(&ct));
2000 /* Save the date in real standard form (10 chars) */
2001 strftime(the_score.day, 11, "%Y/%m/%d", localtime(&ct));
2002 #endif
2003
2004 /* Save the player name (15 chars) */
2005 sprintf(the_score.who, "%-.15s", p_ptr->name);
2006 /* Save the player account name (15 chars) */
2007 sprintf(the_score.whose, "%-.15s", p_ptr->accountname);
2008
2009 /* Save the player info */
2010 sprintf(the_score.sex, "%c", (p_ptr->male ? 'm' : 'f'));
2011 sprintf(the_score.p_r, "%2d", p_ptr->prace);
2012 sprintf(the_score.p_c, "%2d", p_ptr->pclass);
2013
2014 /* Save the level and such */
2015 sprintf(the_score.cur_lev, "%3d", p_ptr->lev);
2016 sprintf(the_score.cur_dun, "%3d", p_ptr->died_from_depth);
2017 sprintf(the_score.max_lev, "%3d", p_ptr->max_plv);
2018 sprintf(the_score.max_dun, "%3d", p_ptr->max_dlv);
2019
2020 /* Save the cause of death (31 chars) */
2021 /* HACKED to take the saved cause of death of the character, not the ghost */
2022 sprintf(the_score.how, "%-.49s", p_ptr->died_from_list);
2023
2024 /* Save the modus (hellish, bat..) */
2025 // sprintf(the_score.mode, "%c", p_ptr->mode);
2026 the_score.mode[0] = p_ptr->mode;
2027
2028 /* Lock (for writing) the highscore file, or fail */
2029 if (fd_lock(highscore_fd, F_WRLCK)) return (1);
2030
2031 /* Add a new entry to the score list, see where it went */
2032 //j = highscore_add(&the_score);
2033 highscore_add(&the_score);
2034
2035 /* Unlock the highscore file, or fail */
2036 if (fd_lock(highscore_fd, F_UNLCK)) return (1);
2037
2038
2039 #if 0
2040 /* Hack -- Display the top fifteen scores */
2041 if (j < 10) display_scores_aux(0, 15, j, NULL);
2042 /* Display the scores surrounding the player */
2043 else {
2044 display_scores_aux(0, 5, j, NULL);
2045 display_scores_aux(j - 2, j + 7, j, NULL);
2046 }
2047 #endif
2048
2049
2050 /* Success */
2051 return (0);
2052 }
2053
2054
2055 /*
2056 * Predict the players location, and display it.
2057 */
2058 static errr predict_score(int Ind, int line) {
2059 player_type *p_ptr = Players[Ind];
2060 int j;
2061 high_score the_score;
2062 #ifndef NEW_HISCORE
2063 bool move_up, move_down;
2064 #else
2065 int erased_slot;
2066 #endif
2067
2068 /* No score file */
2069 if (highscore_fd < 0) {
2070 s_printf("Score file unavailable.\n");
2071 return (0);
2072 }
2073
2074
2075 /* Save the version */
2076 sprintf(the_score.what, "%u.%u.%u",
2077 VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
2078
2079 /* Calculate and save the points */
2080 sprintf(the_score.pts, "%10u", total_points(Ind));
2081
2082 /* Save the current gold */
2083 sprintf(the_score.gold, "%10u", p_ptr->au);
2084
2085 /* Save the current turn */
2086 sprintf(the_score.turns, "%10u", turn);
2087
2088 /* Hack -- no time needed */
2089 strcpy(the_score.day, "TODAY");
2090
2091 /* Save the player name (15 chars) */
2092 sprintf(the_score.who, "%-.15s", p_ptr->name);
2093 /* Save the player name (15 chars) */
2094 sprintf(the_score.whose, "%-.15s", p_ptr->accountname);
2095
2096 /* Save the player info */
2097 sprintf(the_score.sex, "%c", (p_ptr->male ? 'm' : 'f'));
2098 sprintf(the_score.p_r, "%2d", p_ptr->prace);
2099 sprintf(the_score.p_c, "%2d", p_ptr->pclass);
2100
2101 /* Save the level and such */
2102 sprintf(the_score.cur_lev, "%3d", p_ptr->lev);
2103 sprintf(the_score.cur_dun, "%3d", getlevel(&p_ptr->wpos));
2104 sprintf(the_score.max_lev, "%3d", p_ptr->max_plv);
2105 sprintf(the_score.max_dun, "%3d", p_ptr->max_dlv);
2106
2107 /* Hack -- no cause of death */
2108 strcpy(the_score.how, "nobody (yet!)");
2109
2110 /* Modus.. */
2111 the_score.mode[0] = p_ptr->mode;
2112
2113 /* See where the entry would be placed */
2114 #ifndef NEW_HISCORE
2115 j = highscore_where(&the_score, &move_up, &move_down);
2116 /* Hack -- Display the top n scores */
2117 if (is_admin(p_ptr))
2118 display_scores_aux(Ind, line, -1, NULL);
2119 else if (j < (SCORES_SHOWN - 1))
2120 display_scores_aux(Ind, line, j, &the_score);
2121 /* Display some "useful" scores */
2122 else
2123 display_scores_aux(Ind, line, SCORES_SHOWN - 1, &the_score); /* -1, NULL */
2124 #else
2125 j = highscore_where(&the_score, &erased_slot);
2126 if (is_admin(p_ptr))
2127 display_scores_aux(Ind, line, -1, -1, NULL);
2128 else
2129 display_scores_aux(Ind, line, j, erased_slot, &the_score);
2130 #endif
2131
2132
2133 /* Success */
2134 return (0);
2135 }
2136
2137
2138
2139
2140 /*
2141 * Change a player into a King! -RAK-
2142 */
2143 void kingly(int Ind, int type) {
2144 player_type *p_ptr = Players[Ind];
2145
2146 #if 0 // No, this makes Delete_player fail!
2147 /* Hack -- retire in town */
2148 p_ptr->wpos.wx = 0; // pfft, not 0 maybe
2149 p_ptr->wpos.wy = 0;
2150 p_ptr->wpos.wz = 0;
2151 #endif // 0
2152
2153 /* Fake death */
2154 //(void)strcpy(p_ptr->died_from_list, "Ripe Old Age");
2155 switch (type) {
2156 case 1: strcpy(p_ptr->died_from_list, "winner"); break; //retirement
2157 case 2: strcpy(p_ptr->died_from_list, "*winner*"); break; //valinor retirement
2158 case 3: strcpy(p_ptr->died_from_list, "iron champion"); break; //made it through IDDC
2159 case 4: strcpy(p_ptr->died_from_list, "iron emperor"); break; //made it through IDDC and killed Morgoth
2160 }
2161
2162 /* Restore the experience */
2163 p_ptr->exp = p_ptr->max_exp;
2164
2165 /* Restore the level */
2166 p_ptr->lev = p_ptr->max_plv;
2167
2168 /* Hack -- Player gets an XP bonus for beating the game */
2169 /* p_ptr->exp = p_ptr->max_exp += 10000000L; */
2170 }
2171
2172
2173 /*
2174 * Add a player to the high score list.
2175 */
2176 void add_high_score(int Ind) {
2177 char buf[1024];
2178
2179 /* Build the filename */
2180 path_build(buf, 1024, ANGBAND_DIR_DATA, "scores.raw");
2181
2182 /* Open the high score file, for reading/writing */
2183 highscore_fd = fd_open(buf, O_RDWR);
2184
2185 /* Add them */
2186 top_twenty(Ind);
2187
2188 /* Shut the high score file */
2189 (void)fd_close(highscore_fd);
2190
2191 /* Forget the high score fd */
2192 highscore_fd = -1;
2193 }
2194
2195
2196 /*
2197 * Close up the current game (player may or may not be dead)
2198 *
2199 * This function is called only from "main.c" and "signals.c".
2200 *
2201 * In here we try to save everybody's game, as well as save the server state.
2202 */
2203 void close_game(void) {
2204 int i;
2205
2206 /* No suspending now */
2207 signals_ignore_tstp();
2208
2209 for (i = 1; i <= NumPlayers; i++) {
2210 player_type *p_ptr = Players[i];
2211
2212 /* Make sure the player is connected */
2213 if (p_ptr->conn == NOT_CONNECTED)
2214 continue;
2215
2216 /* Handle stuff */
2217 handle_stuff(i);
2218
2219 /* Flush the messages */
2220 msg_print(i, NULL);
2221
2222 /* Build the filename */
2223 /*path_build(buf, 1024, ANGBAND_DIR_APEX, "scores.raw");*/
2224
2225 /* Open the high score file, for reading/writing */
2226 /*highscore_fd = fd_open(buf, O_RDWR);*/
2227
2228
2229 /* Handle death */
2230 if (p_ptr->death) {
2231 /* Handle retirement */
2232 /* Retirement in Valinor? - C. Blue :) */
2233 if (in_valinor(&p_ptr->wpos)) kingly(i, 2);
2234 else if (p_ptr->total_winner) kingly(i, 1);
2235
2236 /* Save memories */
2237 if (!save_player(i)) msg_print(i, "death save failed!");
2238
2239 /* Handle score, show Top scores */
2240 top_twenty(i);
2241 }
2242
2243 /* Still alive */
2244 else {
2245 /* Save the game */
2246 do_cmd_save_game(i);
2247
2248 /* Prompt for scores XXX XXX XXX */
2249 /*prt("Press Return (or Escape).", 0, 40);*/
2250 }
2251
2252
2253 /* Shut the high score file */
2254 /*(void)fd_close(highscore_fd);*/
2255
2256 /* Forget the high score fd */
2257 /*highscore_fd = -1;*/
2258 }
2259
2260 /* Save list of banned players */
2261 save_banlist();
2262
2263 /* Save dynamic quest info */
2264 save_quests();
2265
2266 /* Try to save the server information */
2267 save_server_info();
2268
2269 /* Allow suspending now */
2270 signals_handle_tstp();
2271 }
2272
2273
2274 /*
2275 * Hack -- Display the scores in a given range and quit.
2276 *
2277 * This function is only called from "main.c" when the user asks
2278 * to see the "high scores".
2279 */
2280 void display_scores(int Ind, int line) {
2281 char buf[1024];
2282
2283 /* Build the filename */
2284 path_build(buf, 1024, ANGBAND_DIR_DATA, "scores.raw");
2285
2286 /* Open the binary high score file, for reading */
2287 highscore_fd = fd_open(buf, O_RDONLY);
2288
2289 /* Paranoia -- No score file */
2290 if (highscore_fd < 0) {
2291 /* Message to server admin */
2292 s_printf("Score file unavailable.\n");
2293
2294 /* Quit */
2295 return;
2296 }
2297
2298 /* Display the scores */
2299 predict_score(Ind, line);
2300
2301 /* Shut the high score file */
2302 (void)fd_close(highscore_fd);
2303
2304 /* Forget the high score fd */
2305 highscore_fd = -1;
2306
2307 /* Quit */
2308 /* quit(NULL); */
2309 }
2310
2311
2312 /*
2313 * Get a random line from a file
2314 * Based on the monster speech patch by Matt Graham,
2315 */
2316 errr get_rnd_line(cptr file_name, int entry, char *output, int max_len) {
2317 FILE *fp;
2318 char buf[1024];
2319 int line = 0, counter, test, numentries;
2320 int line_num = 0;
2321 //bool found = FALSE;
2322
2323
2324 /* Build the filename */
2325 path_build(buf, 1024, ANGBAND_DIR_TEXT, file_name);
2326
2327 /* Open the file */
2328 fp = my_fopen(buf, "r");
2329
2330 /* Failed */
2331 if (!fp) return (-1);
2332
2333 /* Find the entry of the monster */
2334 while (TRUE) {
2335 /* Get a line from the file */
2336 if (my_fgets(fp, buf, 1024, FALSE) == 0) {
2337 /* Count the lines */
2338 line_num++;
2339
2340 /* Look for lines starting with 'N:' */
2341 if ((buf[0] == 'N') && (buf[1] == ':')) {
2342 /* Allow default lines */
2343 if (buf[2] == '*') {
2344 /* Default lines */
2345 //found = TRUE;
2346 break;
2347 }
2348 /* Get the monster number */
2349 else if (sscanf(&(buf[2]), "%d", &test) != EOF) {
2350 /* Is it the right monster? */
2351 if (test == entry) {
2352 //found = TRUE;
2353 break;
2354 }
2355 } else {
2356 my_fclose(fp);
2357 return (-1);
2358 }
2359 }
2360 } else {
2361 /* Reached end of file */
2362 my_fclose(fp);
2363 return (-1);
2364 }
2365
2366 }
2367
2368 /* Get the number of entries */
2369 while (TRUE) {
2370 /* Get the line */
2371 if (my_fgets(fp, buf, 1024, FALSE) == 0) {
2372 /* Count the lines */
2373 line_num++;
2374
2375 /* Look for the number of entries */
2376 if (isdigit(buf[0])) {
2377 /* Get the number of entries */
2378 numentries = atoi(buf);
2379 break;
2380 }
2381 } else {
2382 /* Count the lines */
2383 line_num++;
2384
2385 my_fclose(fp);
2386 return (-1);
2387 }
2388 }
2389
2390 if (numentries > 0) {
2391 /* Grab an appropriate line number */
2392 line = rand_int(numentries);
2393
2394 /* Get the random line */
2395 for (counter = 0; counter <= line; counter++) {
2396 /* Count the lines */
2397 line_num++;
2398
2399 /* Try to read the line */
2400 if (my_fgets(fp, buf, 1024, FALSE) == 0) {
2401 /* Found the line */
2402 if (counter == line) break;
2403 } else {
2404 my_fclose(fp);
2405 return (-1);
2406 }
2407 }
2408
2409 /* Copy the line */
2410 strncpy(output, buf, max_len);
2411 }
2412
2413 /* Close the file */
2414 my_fclose(fp);
2415
2416 /* Success */
2417 return (line);
2418 }
2419
2420 /* Clear objects so that artifacts get saved.
2421 * This probably isn't neccecary, as all the objects on each of
2422 * these levels should have been cleared by now. However, paranoia
2423 * can't hurt all that much... -APD
2424 */
2425 /* totally inefficient replacement - sorry. */
2426 /* it also doesnt respect non existent world positions */
2427 /* rewrite */
2428 /* k rewritten.. but not so better? - Jir - */
2429 void wipeout_needless_objects() {
2430 #if 0 // exit_game_panic version
2431 int i = 1;
2432 int j,k;
2433 struct worldpos wpos;
2434 struct wilderness_type *wild;
2435
2436 for (i = 0; i < MAX_WILD_X; i++) {
2437 wpos.wx = i;
2438 for (j = 0; j < MAX_WILD_Y; j++) {
2439 wpos.wy = j;
2440 wild = &wild_info[j][i];
2441
2442 wpos.wz = 0;
2443 if(getcave(&wpos) && !players_on_depth(&wpos)) wipe_o_list(&wpos);
2444
2445 if (wild->flags&WILD_F_UP)
2446 for (k = 0;k < wild->tower->maxdepth; k++) {
2447 wpos.wz = k;
2448 if((getcave(&wpos)) && (!players_on_depth(&wpos))) wipe_o_list(&wpos);
2449 }
2450
2451 if (wild->flags&WILD_F_DOWN)
2452 for (k = 0;k < wild->dungeon->maxdepth; k++) {
2453 wpos.wz = -k;
2454 if((getcave(&wpos)) && (!players_on_depth(&wpos))) wipe_o_list(&wpos);
2455 }
2456 }
2457 }
2458 #else // 0
2459 struct worldpos cwpos;
2460 struct dungeon_type *d_ptr;
2461 wilderness_type *w_ptr;
2462 int x,y,z;
2463
2464 for (y = 0; y < MAX_WILD_Y; y++) {
2465 cwpos.wy = y;
2466 for (x = 0; x < MAX_WILD_X; x++) {
2467 cwpos.wx = x;
2468 cwpos.wz = 0;
2469 w_ptr = &wild_info[y][x];
2470 // if(getcave(&cwpos) && !players_on_depth(&cwpos)) wipe_o_list(&cwpos);
2471 if(w_ptr->flags & WILD_F_DOWN){
2472 d_ptr = w_ptr->dungeon;
2473 for (z = 1; z <= d_ptr->maxdepth; z++) {
2474 cwpos.wz = -z;
2475 if(d_ptr->level[z-1].ondepth && d_ptr->level[z-1].cave)
2476 wipe_o_list(&cwpos);
2477 }
2478 }
2479 if(w_ptr->flags & WILD_F_UP){
2480 d_ptr = w_ptr->tower;
2481 for (z = 1; z <= d_ptr->maxdepth; z++) {
2482 cwpos.wz = z;
2483 if(d_ptr->level[z-1].ondepth && d_ptr->level[z-1].cave)
2484 wipe_o_list(&cwpos);
2485 }
2486 }
2487 }
2488 }
2489 #endif // 0
2490 }
2491
2492 /*
2493 * Handle a fatal crash.
2494 *
2495 * Here we try to save every player's state, and the state of the server
2496 * in general. Note that we must be extremely careful not to have a crash
2497 * in this function, or some things may not get saved. Also, this function
2498 * may get called because some data structures are not in a "correct" state.
2499 * For this reason many paranoia checks are done to prevent bad pointer
2500 * dereferences.
2501 *
2502 * Note that this function would not be needed at all if there were no bugs.
2503 */
2504 void exit_game_panic(void) {
2505 int i = 1;
2506
2507 #ifndef WINDOWS
2508 int dumppid, dumpstatus;
2509
2510 /* fork() a child process that will abort() - mikaelh */
2511 dumppid = fork();
2512 if (dumppid == 0) {
2513 /* child process */
2514
2515 #ifdef HANDLE_SIGNALS
2516 signal(SIGABRT, SIG_IGN);
2517 #endif
2518
2519 /* dump core */
2520 abort();
2521 } else if (dumppid != -1) {
2522 /* wait for the child */
2523 waitpid(dumppid, &dumpstatus, 0);
2524 }
2525 #endif
2526
2527 /* If nothing important has happened, just quit */
2528 if (!server_generated || server_saved) quit("panic");
2529
2530 while (NumPlayers > (i - 1)) {
2531 player_type *p_ptr = Players[i];
2532
2533 /* Don't dereference bad pointers */
2534 if (!p_ptr) {
2535 /* Skip to next player */
2536 i++;
2537
2538 continue;
2539 }
2540
2541 /* Hack -- turn off some things */
2542 disturb(i, 1, 0);
2543
2544 /* Mega-Hack -- Delay death */
2545 if (p_ptr->chp < 0) p_ptr->death = FALSE;
2546
2547 /* Hardcode panic save */
2548 panic_save = 1;
2549
2550 /* Forbid suspend */
2551 signals_ignore_tstp();
2552
2553 /* Indicate panic save */
2554 (void)strcpy(p_ptr->died_from, "(panic save)");
2555
2556 /* Remember depth in the log files */
2557 s_printf("Trying panic saving %s on %d %d %d..\n", p_ptr->name, p_ptr->wpos.wx, p_ptr->wpos.wy, p_ptr->wpos.wz);
2558
2559 /* Panic save, or get worried */
2560 if (!save_player(i)) {
2561 if (!Destroy_connection(p_ptr->conn, "panic save failed!")) {
2562 /* Something extremely bad is going on, try to recover anyway */
2563 i++;
2564 }
2565 }
2566
2567 /* Successful panic save */
2568 if (!Destroy_connection(p_ptr->conn, "panic save succeeded!")) {
2569 /* Something very bad happened, skip to next player */
2570 i++;
2571 }
2572 }
2573
2574 // wipeout_needless_objects();
2575
2576 /* Save dynamic quest info */
2577 save_quests();
2578
2579 /* Save list of banned players */
2580 save_banlist();
2581
2582 if (!save_server_info()) quit("server panic info save failed!");
2583
2584 #if 0 /* abort() done above in a child process */
2585 /* make a core dump using abort() - mikaelh */
2586 # ifdef HANDLE_SIGNALS
2587 signal(SIGABRT, SIG_IGN);
2588 # endif
2589 abort();
2590 #endif
2591
2592 /* Successful panic save of server info */
2593 quit("server panic info save succeeded!");
2594 }
2595
2596
2597 /* Save all characters and server info with 'panic save' flag set,
2598 to prevent non-autorecalled players after a flush error restart - C. Blue */
2599 void save_game_panic(void) {
2600 int i = 1;
2601
2602 /* If nothing important has happened, just quit */
2603 // if (!server_generated || server_saved) return;
2604
2605 while (NumPlayers > (i - 1)) {
2606 player_type *p_ptr = Players[i];
2607
2608 /* Don't dereference bad pointers */
2609 if (!p_ptr) {
2610 /* Skip to next player */
2611 i++;
2612
2613 continue;
2614 }
2615
2616 /* Hack -- turn off some things */
2617 disturb(i, 1, 0);
2618
2619 /* Mega-Hack -- Delay death */
2620 // if (p_ptr->chp < 0) p_ptr->death = FALSE;
2621
2622 /* Hardcode panic save */
2623 panic_save = 1;
2624
2625 /* Forbid suspend */
2626 // signals_ignore_tstp();
2627
2628 /* Indicate panic save */
2629 // (void)strcpy(p_ptr->died_from, "(panic save)");
2630
2631 /* Remember depth in the log files */
2632 s_printf("Trying panic saving %s on %d %d %d..\n", p_ptr->name, p_ptr->wpos.wx, p_ptr->wpos.wy, p_ptr->wpos.wz);
2633
2634 /* Panic save */
2635 save_player(i);
2636 i++;
2637 }
2638
2639 // wipeout_needless_objects();
2640
2641 /* Save dynamic quest info */
2642 save_quests();
2643
2644 /* Save list of banned players */
2645 save_banlist();
2646
2647 save_server_info();
2648
2649 /* No more panicking */
2650 panic_save = 0;
2651 }
2652
2653
2654 /*
2655 * Windows specific replacement for signal handling [grk]
2656 */
2657 #ifdef WINDOWS
2658 #ifndef HANDLE_SIGNALS
2659
2660 LPTOP_LEVEL_EXCEPTION_FILTER old_handler;
2661
2662 /* Callback to be called by Windows when our term closes, the user
2663 * logs off, the system is shutdown, etc.
2664 */
2665 BOOL ctrl_handler( DWORD fdwCtrlType ) {
2666 /* Save everything and quit the game */
2667 shutdown_server();
2668
2669 return TRUE;
2670 }
2671
2672 /* Global unhandled exception handler */
2673 /* If the server crashes under Windows, this is where we end up */
2674 LONG WINAPI myUnhandledExceptionFilter(
2675 struct _EXCEPTION_POINTERS* ExceptionInfo) {
2676 /* We don't report to the meta server in this case, the meta
2677 * server will detect that we've gone anyway
2678 */
2679
2680 /* Call the previous exception handler, which we are assuming
2681 * is the MinGW exception handler which should have been implicitly
2682 * setup when we loaded the exchndl.dll library.
2683 */
2684 if(old_handler != NULL)
2685 {
2686 old_handler(ExceptionInfo);
2687 }
2688
2689 /* Save everything and quit the game */
2690 exit_game_panic();
2691
2692 /* We don't expect to ever get here... but for what it's worth... */
2693 return(EXCEPTION_EXECUTE_HANDLER);
2694
2695 }
2696
2697
2698 void setup_exit_handler(void) {
2699 /* Trap CTRL+C, Logoff, Shutdown, etc */
2700 if( SetConsoleCtrlHandler( (PHANDLER_ROUTINE) ctrl_handler, TRUE ) )
2701 {
2702 plog("Initialised exit save handler.");
2703 }else{
2704 plog("ERROR: Could not set panic save handler!");
2705 }
2706 /* Trap unhandled exceptions, i.e. server crashes */
2707 old_handler = SetUnhandledExceptionFilter( myUnhandledExceptionFilter );
2708 }
2709 #endif
2710 #endif
2711
2712
2713 #ifdef HANDLE_SIGNALS
2714
2715
2716 /*
2717 * Handle signals -- suspend
2718 *
2719 * Actually suspend the game, and then resume cleanly
2720 *
2721 * This will probably inflict much anger upon the suspender, but it is still
2722 * allowed (for now) --KLJ--
2723 */
2724 static void handle_signal_suspend(int sig) {
2725 /* Disable handler */
2726 (void)signal(sig, SIG_IGN);
2727
2728 #ifdef SIGSTOP
2729
2730 /* Suspend ourself */
2731 #ifndef WINDOWS
2732 (void)kill(0, SIGSTOP);
2733 #else
2734 raise(SIGSTOP);
2735 #endif
2736
2737 #endif
2738
2739 /* Restore handler */
2740 (void)signal(sig, handle_signal_suspend);
2741 }
2742
2743
2744 /*
2745 * Handle signals -- simple (interrupt and quit)
2746 *
2747 * This function was causing a *huge* number of problems, so it has
2748 * been simplified greatly. We keep a global variable which counts
2749 * the number of times the user attempts to kill the process, and
2750 * we commit suicide if the user does this a certain number of times.
2751 *
2752 * We attempt to give "feedback" to the user as he approaches the
2753 * suicide thresh-hold, but without penalizing accidental keypresses.
2754 *
2755 * To prevent messy accidents, we should reset this global variable
2756 * whenever the user enters a keypress, or something like that.
2757 *
2758 * This simply calls "exit_game_panic()", which should try to save
2759 * everyone's character and the server info, which is probably nicer
2760 * than killing everybody. --KLJ--
2761 */
2762 static void handle_signal_simple(int sig) {
2763 /* Disable handler */
2764 (void)signal(sig, SIG_IGN);
2765
2766
2767 /* Nothing to save, just quit */
2768 if (!server_generated || server_saved) quit(NULL);
2769
2770
2771 /* Count the signals */
2772 signal_count++;
2773
2774
2775 /* Allow suicide (after 5) */
2776 if (signal_count >= 5) {
2777 /* Tell the metaserver that we've quit */
2778 Report_to_meta(META_DIE);
2779
2780 /* Save everything and quit the game */
2781 // exit_game_panic();
2782 shutdown_server();
2783 }
2784
2785 /* Give warning (after 4) */
2786 else if (signal_count >= 4) {
2787 s_printf("Warning: Next signal kills server!\n");
2788 }
2789
2790 /* Restore handler */
2791 (void)signal(sig, handle_signal_simple);
2792 }
2793
2794 static void handle_signal_bpipe(int sig){
2795 (void)signal(sig, SIG_IGN); /* This should not happen, but for the sake of convention... */
2796 s_printf("SIGPIPE received\n");
2797 (void)signal(sig, handle_signal_bpipe);
2798 }
2799
2800 /*
2801 * Handle signal -- abort, kill, etc
2802 *
2803 * This one also calls exit_game_panic() --KLJ--
2804 */
2805 static void handle_signal_abort(int sig) {
2806 /* Disable handler */
2807 (void)signal(sig, SIG_IGN);
2808
2809 s_printf("Received signal %d.\n", sig);
2810
2811 /* Nothing to save, just quit */
2812 if (!server_generated || server_saved) quit(NULL);
2813
2814 /* Tell the metaserver that we're going down */
2815 Report_to_meta(META_DIE);
2816
2817 /* Save everybody and quit */
2818 exit_game_panic();
2819 }
2820
2821
2822
2823
2824 /*
2825 * Ignore SIGTSTP signals (keyboard suspend)
2826 */
2827 void signals_ignore_tstp(void) {
2828 #ifdef SIGTSTP
2829 (void)signal(SIGTSTP, SIG_IGN);
2830 #endif
2831
2832 }
2833
2834 /*
2835 * Handle SIGTSTP signals (keyboard suspend)
2836 */
2837 void signals_handle_tstp(void) {
2838 #ifdef SIGTSTP
2839 (void)signal(SIGTSTP, handle_signal_suspend);
2840 #endif
2841
2842 }
2843
2844
2845 /*
2846 * Prepare to handle the relevant signals
2847 */
2848 void signals_init(void) {
2849 #ifdef SIGHUP
2850 (void)signal(SIGHUP, SIG_IGN);
2851 #endif
2852
2853 #ifdef SIGTSTP
2854 (void)signal(SIGTSTP, handle_signal_suspend);
2855 #endif
2856
2857 #ifdef SIGINT
2858 (void)signal(SIGINT, handle_signal_simple);
2859 #endif
2860
2861 #ifdef SIGQUIT
2862 (void)signal(SIGQUIT, handle_signal_simple);
2863 #endif
2864
2865 #ifdef SIGFPE
2866 (void)signal(SIGFPE, handle_signal_abort);
2867 #endif
2868
2869 #ifdef SIGILL
2870 (void)signal(SIGILL, handle_signal_abort);
2871 #endif
2872
2873 #ifdef SIGTRAP
2874 (void)signal(SIGTRAP, handle_signal_abort);
2875 #endif
2876
2877 #ifdef SIGIOT
2878 (void)signal(SIGIOT, handle_signal_abort);
2879 #endif
2880
2881 #ifdef SIGKILL
2882 (void)signal(SIGKILL, handle_signal_abort);
2883 #endif
2884
2885 #ifdef SIGBUS
2886 (void)signal(SIGBUS, handle_signal_abort);
2887 #endif
2888
2889 #ifdef SIGSEGV
2890 (void)signal(SIGSEGV, handle_signal_abort);
2891 #endif
2892
2893 #ifdef SIGTERM
2894 (void)signal(SIGTERM, handle_signal_abort);
2895 #endif
2896
2897 #ifdef SIGPIPE
2898 (void)signal(SIGPIPE, handle_signal_bpipe);
2899 #endif
2900
2901 #ifdef SIGEMT
2902 (void)signal(SIGEMT, handle_signal_abort);
2903 #endif
2904
2905 #ifdef SIGDANGER
2906 (void)signal(SIGDANGER, handle_signal_abort);
2907 #endif
2908
2909 #ifdef SIGSYS
2910 (void)signal(SIGSYS, handle_signal_abort);
2911 #endif
2912
2913 #ifdef SIGXCPU
2914 (void)signal(SIGXCPU, handle_signal_abort);
2915 #endif
2916
2917 #ifdef SIGPWR
2918 (void)signal(SIGPWR, handle_signal_abort);
2919 #endif
2920 }
2921
2922
2923 #else /* HANDLE_SIGNALS */
2924
2925
2926 /*
2927 * Do nothing
2928 */
2929 void signals_ignore_tstp(void) { }
2930
2931 /*
2932 * Do nothing
2933 */
2934 void signals_handle_tstp(void) { }
2935
2936 /*
2937 * Do nothing
2938 */
2939 void signals_init(void) { }
2940
2941
2942 #endif /* HANDLE_SIGNALS */
2943
2944
2945 #if 0
2946
2947 /*
2948 * New v* functions for accessing files in memory
2949 * Uses non-blocking fds to read the files
2950 * - mikaelh
2951 */
2952
2953 /* Same as in nserver.c */
2954 #define MAX_FD 1023
2955
2956 #define VFILE_INPUT_INSTALLED 1
2957
2958 struct vfile {
2959 char *name;
2960 char *data;
2961 size_t alloc;
2962 size_t len;
2963 off_t pos;
2964 int flags;
2965 int vflags;
2966 int (*callback)(int, int);
2967 };
2968
2969 typedef struct vfile vfile;
2970
2971 struct vfile vfiles[MAX_FD];
2972
2973 void vfile_receive_input(int fd, int arg);
2974 int vopen(const char *pathname, int flags, int (*callback)(int, int));
2975 int vclose(int fd);
2976 ssize_t vread(int fd, char *buf, size_t len);
2977 off_t vseek(int fd, off_t offset, int whence);
2978
2979 void vfile_receive_input(int fd, int arg) {
2980 vfile *vf = &vfiles[fd];
2981 ssize_t read_len;
2982
2983 if (vf->alloc - vf->len < 4096) {
2984 vf->alloc += 4096;
2985 vf->data = realloc(vf->data, vf->alloc);
2986 }
2987
2988 read_len = read(fd, vf->data + vf->len, vf->alloc - vf->len);
2989 if (read_len > 0) {
2990 vf->len += read_len;
2991 }
2992 else if (read_len == 0) { /* end of file */
2993 remove_input(fd);
2994 vf->vflags &= ~VFILE_INPUT_INSTALLED;
2995 (*vf->callback)(fd, 0);
2996 }
2997 else if (errno != EAGAIN) {
2998 /* fatal error, close the file and notify callback */
2999 vclose(fd);
3000 (*vf->callback)(fd, -1);
3001 }
3002 }
3003
3004 int vopen(const char *pathname, int flags, int (*callback)(int, int)) {
3005 int fd;
3006 size_t len;
3007 vfile *vf;
3008
3009 flags |= O_NONBLOCK;
3010
3011 fd = open(pathname, flags);
3012
3013 if (fd >= 0) {
3014 vf = &vfiles[fd];
3015 memset(vf, 0, sizeof(struct vfile));
3016 len = strlen(pathname);
3017 vf->name = malloc(len + 1);
3018 strcpy(vf->name, pathname);
3019 vf->flags = flags;
3020 vf->callback = callback;
3021 vf->alloc = 4096;
3022 vf->data = malloc(vf->alloc);
3023
3024 install_input(vfile_receive_input, fd, 0);
3025 vf->vflags |= VFILE_INPUT_INSTALLED;
3026 }
3027
3028 return fd;
3029 }
3030
3031 int vclose(int fd) {
3032 vfile *vf = &vfiles[fd];
3033 int n;
3034
3035 if (vf->vflags & VFILE_INPUT_INSTALLED) {
3036 remove_input(fd);
3037 vf->vflags &= ~VFILE_INPUT_INSTALLED;
3038 }
3039
3040 n = close(fd);
3041
3042 if (!n) {
3043 free(vf->name);
3044 free(vf->data);
3045 memset(vf, 0, sizeof(struct vfile));
3046 }
3047
3048 return n;
3049 }
3050
3051 ssize_t vread(int fd, char *buf, size_t len) {
3052 vfile *vf = &vfiles[fd];
3053
3054 if (!vf->data) {
3055 return -1;
3056 }
3057
3058 if (vf->pos + len > vf->len) {
3059 len = vf->len - vf->pos;
3060 }
3061
3062 if (len < 0) return 0;
3063
3064 memcpy(buf, &vf->data[vf->pos], len);
3065 vf->pos += len;
3066
3067 return len;
3068 }
3069
3070 off_t vseek(int fd, off_t offset, int whence) {
3071 vfile *vf = &vfiles[fd];
3072
3073 if (whence == SEEK_SET) {
3074 vf->pos = offset;
3075 } else if (whence == SEEK_CUR) {
3076 vf->pos += offset;
3077 } else if (whence == SEEK_END) {
3078 vf->pos = vf->len + offset;
3079 } else {
3080 return -1;
3081 }
3082
3083 return vf->pos;
3084 }
3085
3086 #endif // 0
3087
3088 /* Erase the current highscore completely - C. Blue */
3089 bool highscore_reset(int Ind) {
3090 char buf[1024];
3091
3092 /* Build the filename */
3093 path_build(buf, 1024, ANGBAND_DIR_DATA, "scores.raw");
3094
3095 /* bam (delete file, simply) */
3096 highscore_fd = fd_open(buf, O_TRUNC);
3097 (void)fd_close(highscore_fd);
3098
3099 /* Forget the high score fd */
3100 highscore_fd = -1;
3101 return(TRUE);
3102 }
3103
3104 /* remove one specific entry from the highscore - C. Blue */
3105 bool highscore_remove(int Ind, int slot) {
3106 int i;
3107 high_score tmpscore;
3108 char buf[1024];
3109
3110 /* Build the filename */
3111 path_build(buf, 1024, ANGBAND_DIR_DATA, "scores.raw");
3112
3113 /* Open the binary high score file, for reading */
3114 highscore_fd = fd_open(buf, O_RDWR);
3115 /* Paranoia -- No score file */
3116 if (highscore_fd < 0) {
3117 if (Ind) msg_print(Ind, "Score file unavailable!");
3118 return(FALSE);
3119 }
3120 /* Lock (for writing) the highscore file, or fail */
3121 if (fd_lock(highscore_fd, F_WRLCK)) {
3122 if (Ind) msg_print(Ind, "Couldn't lock highscore file for writing!");
3123 return(FALSE);
3124 }
3125
3126 for (i = slot; i < MAX_HISCORES; i++) {
3127 /* Read the following entry, if any */
3128 if (highscore_seek(i + 1)) break;
3129 if (highscore_read(&tmpscore)) break;
3130 /* Overwrite current entry with it */
3131 if (highscore_seek(i)) return(FALSE);
3132 if (highscore_write(&tmpscore)) return(FALSE);
3133 }
3134
3135 /* zero final entry */
3136 WIPE(&tmpscore, sizeof(high_score));
3137 strcpy(tmpscore.who, "(nobody)"); /* hack: erase name instead of 0 */
3138 if (highscore_seek(i)) return(FALSE);
3139 if (highscore_write(&tmpscore)) return(FALSE);
3140
3141 /* Unlock the highscore file, or fail */
3142 if (fd_lock(highscore_fd, F_UNLCK)) {
3143 if (Ind) msg_print(Ind, "Couldn't unlock highscore file from writing!");
3144 return(FALSE);
3145 }
3146 /* Shut the high score file */
3147 (void)fd_close(highscore_fd);
3148
3149 /* Forget the high score fd */
3150 highscore_fd = -1;
3151
3152 return(TRUE);
3153 }
3154
3155 /* Hack: Update old high score file to new format - C. Blue */
3156 bool highscore_file_convert(int Ind) {
3157 int i, entries;
3158 high_score_old oldscore[MAX_HISCORES];
3159 high_score newscore[MAX_HISCORES];
3160 char buf[1024];
3161
3162 /* Build the filename */
3163 path_build(buf, 1024, ANGBAND_DIR_DATA, "scores.raw");
3164
3165 /* Open the binary high score file, for reading */
3166 highscore_fd = fd_open(buf, O_RDWR);
3167 /* Paranoia -- No score file */
3168 if (highscore_fd < 0) {
3169 if (Ind) msg_print(Ind, "Score file unavailable!");
3170 return(FALSE);
3171 }
3172
3173 for (i = 0; i < MAX_HISCORES; i++) {
3174 /* Read old entries */
3175 if (highscore_seek_old(i)) break;
3176 if (highscore_read_old(&oldscore[i])) break;
3177 }
3178 entries = i;
3179
3180 /* Shut the high score file */
3181 (void)fd_close(highscore_fd);
3182
3183 #if 0 /* old conversion done once, for example */
3184 /* convert entries */
3185 for (i = 0; i < entries; i++) {
3186 char mod[80];
3187
3188 strcpy(newscore[i].what, oldscore[i].what);
3189 strcpy(newscore[i].pts, oldscore[i].pts);
3190 strcpy(newscore[i].gold, oldscore[i].gold);
3191 strcpy(newscore[i].turns, oldscore[i].turns);
3192
3193 /* convert date format */
3194 mod[0] = '2';
3195 mod[1] = '0';
3196 mod[2] = oldscore[i].day[6];
3197 mod[3] = oldscore[i].day[7];
3198 mod[4] = '/';
3199 mod[5] = oldscore[i].day[0];
3200 mod[6] = oldscore[i].day[1];
3201 mod[7] = '/';
3202 mod[8] = oldscore[i].day[3];
3203 mod[9] = oldscore[i].day[4];
3204 mod[10] = '\0';
3205
3206 strcpy(newscore[i].day, mod);
3207
3208 strcpy(newscore[i].who, oldscore[i].who);
3209 strcpy(newscore[i].whose, oldscore[i].whose);
3210
3211 strcpy(newscore[i].sex, oldscore[i].sex);
3212 strcpy(newscore[i].p_r, oldscore[i].p_r);
3213 strcpy(newscore[i].p_c, oldscore[i].p_c);
3214
3215 strcpy(newscore[i].cur_lev, oldscore[i].cur_lev);
3216 strcpy(newscore[i].cur_dun, oldscore[i].cur_dun);
3217 strcpy(newscore[i].max_lev, oldscore[i].max_lev);
3218 strcpy(newscore[i].max_dun, oldscore[i].max_dun);
3219
3220 strcpy(newscore[i].how, oldscore[i].how);
3221 strcpy(newscore[i].mode, oldscore[i].mode);
3222 }
3223 #else /* new conversion: inserting RACE_KOBOLD */
3224 for (i = 0; i < entries; i++) {
3225 int r;
3226 strcpy(newscore[i].what, oldscore[i].what);
3227 strcpy(newscore[i].pts, oldscore[i].pts);
3228 strcpy(newscore[i].gold, oldscore[i].gold);
3229 strcpy(newscore[i].turns, oldscore[i].turns);
3230
3231 strcpy(newscore[i].day, oldscore[i].day);
3232
3233 strcpy(newscore[i].who, oldscore[i].who);
3234 strcpy(newscore[i].whose, oldscore[i].whose);
3235
3236 strcpy(newscore[i].sex, oldscore[i].sex);
3237
3238 r = atoi(oldscore[i].p_r);
3239 if (r >= RACE_KOBOLD) {
3240 r++;
3241 sprintf(newscore[i].p_r, "%2d", r);
3242 } else strcpy(newscore[i].p_r, oldscore[i].p_r);
3243
3244 strcpy(newscore[i].p_c, oldscore[i].p_c);
3245
3246 strcpy(newscore[i].cur_lev, oldscore[i].cur_lev);
3247 strcpy(newscore[i].cur_dun, oldscore[i].cur_dun);
3248 strcpy(newscore[i].max_lev, oldscore[i].max_lev);
3249 strcpy(newscore[i].max_dun, oldscore[i].max_dun);
3250
3251 strcpy(newscore[i].how, oldscore[i].how);
3252 strcpy(newscore[i].mode, oldscore[i].mode);
3253 }
3254 #endif
3255 /* bam (delete file, simply) */
3256 highscore_fd = fd_open(buf, O_TRUNC);
3257 (void)fd_close(highscore_fd);
3258
3259 /* Open the binary high score file, for writing */
3260 highscore_fd = fd_open(buf, O_RDWR);
3261 /* Paranoia -- No score file */
3262 if (highscore_fd < 0) {
3263 if (Ind) msg_print(Ind, "Score file unavailable!");
3264 return(FALSE);
3265 }
3266
3267 /* Lock (for writing) the highscore file, or fail */
3268 if (fd_lock(highscore_fd, F_WRLCK)) {
3269 if (Ind) msg_print(Ind, "Couldn't lock highscore file for writing!");
3270 return(FALSE);
3271 }
3272
3273 for (i = 0; i < entries; i++) {
3274 /* Skip to end */
3275 if (highscore_seek(i)) return (-1);
3276 /* add new entry */
3277 if (highscore_write(&newscore[i])) return (-1);
3278 }
3279
3280 /* Unlock the highscore file, or fail */
3281 if (fd_lock(highscore_fd, F_UNLCK)) {
3282 if (Ind) msg_print(Ind, "Couldn't unlock highscore file from writing!");
3283 return(FALSE);
3284 }
3285 /* Shut the high score file */
3286 (void)fd_close(highscore_fd);
3287
3288 /* Forget the high score fd */
3289 highscore_fd = -1;
3290
3291 return(TRUE);
3292 }
3293