1 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 2 /* hack.end.c - version 1.0.3 */ 3 /* $FreeBSD: src/games/hack/hack.end.c,v 1.4 1999/11/16 10:26:36 marcel Exp $ */ 4 5 #include "hack.h" 6 #define Sprintf sprintf 7 8 #define newttentry() alloc(sizeof(struct toptenentry)) 9 #define NAMSZ 8 10 #define DTHSZ 40 11 #define PERSMAX 1 12 #define POINTSMIN 1 /* must be > 0 */ 13 #define ENTRYMAX 100 /* must be >= 10 */ 14 #define PERS_IS_UID /* delete for PERSMAX per name; now per uid */ 15 struct toptenentry { 16 struct toptenentry *tt_next; 17 long int points; 18 int level,maxlvl,hp,maxhp; 19 int uid; 20 char plchar; 21 char sex; 22 char name[NAMSZ+1]; 23 char death[DTHSZ+1]; 24 char date[7]; /* yymmdd */ 25 } *tt_head; 26 27 static void done_intr(int); 28 static void done_hangup(int); 29 static void topten(void); 30 static void outheader(void); 31 static int outentry(int, struct toptenentry *, int); 32 static char *itoa(int); 33 static const char *ordin(int); 34 35 xchar maxdlevel = 1; 36 37 void 38 done1(int unused __unused) 39 { 40 signal(SIGINT, SIG_IGN); 41 pline("Really quit?"); 42 if (readchar() != 'y') { 43 signal(SIGINT, done1); 44 clrlin(); 45 fflush(stdout); 46 if (multi > 0) 47 nomul(0); 48 return; 49 } 50 done("quit"); 51 /* NOTREACHED */ 52 } 53 54 int done_stopprint; 55 int done_hup; 56 57 static void 58 done_intr(int unused __unused) 59 { 60 done_stopprint++; 61 signal(SIGINT, SIG_IGN); 62 signal(SIGQUIT, SIG_IGN); 63 } 64 65 static void 66 done_hangup(int unused __unused) 67 { 68 done_hup++; 69 signal(SIGHUP, SIG_IGN); 70 done_intr(0); 71 } 72 73 void 74 done_in_by(struct monst *mtmp) 75 { 76 static char buf[BUFSZ]; 77 78 pline("You die ..."); 79 if (mtmp->data->mlet == ' ') { 80 Sprintf(buf, "the ghost of %s", (char *)mtmp->mextra); 81 killer = buf; 82 } else if (mtmp->mnamelth) { 83 Sprintf(buf, "%s called %s", 84 mtmp->data->mname, NAME(mtmp)); 85 killer = buf; 86 } else if (mtmp->minvis) { 87 Sprintf(buf, "invisible %s", mtmp->data->mname); 88 killer = buf; 89 } else 90 killer = mtmp->data->mname; 91 done("died"); 92 } 93 94 /* 95 * called with arg "died", "drowned", "escaped", "quit", "choked", 96 * "panicked", "burned", "starved" or "tricked" 97 */ 98 /* Be careful not to call panic from here! */ 99 void 100 done(const char *st1) 101 { 102 #ifdef WIZARD 103 if (wizard && *st1 == 'd') { 104 u.uswldtim = 0; 105 if (u.uhpmax < 0) /* arbitrary */ 106 u.uhpmax = 100; 107 u.uhp = u.uhpmax; 108 pline("For some reason you are still alive."); 109 flags.move = 0; 110 if (multi > 0) 111 multi = 0; 112 else 113 multi = -1; 114 flags.botl = 1; 115 return; 116 } 117 #endif /* WIZARD */ 118 signal(SIGINT, done_intr); 119 signal(SIGQUIT, done_intr); 120 signal(SIGHUP, done_hangup); 121 if (*st1 == 'q' && u.uhp < 1) { 122 st1 = "died"; 123 killer = "quit while already on Charon's boat"; 124 } 125 if (*st1 == 's') 126 killer = "starvation"; 127 else if (*st1 == 'd' && st1[1] == 'r') 128 killer = "drowning"; 129 else if (*st1 == 'p') 130 killer = "panic"; 131 else if (*st1 == 't') 132 killer = "trickery"; 133 else if (!strchr("bcd", *st1)) 134 killer = st1; 135 paybill(); 136 clearlocks(); 137 if (flags.toplin == 1) 138 more(); 139 if (strchr("bcds", *st1)) { 140 #ifdef WIZARD 141 if (!wizard) 142 #endif /* WIZARD */ 143 savebones(); 144 if (!flags.notombstone) 145 outrip(); 146 } 147 if (*st1 == 'c') /* after outrip() */ 148 killer = st1; 149 settty(NULL); /* does a clear_screen() */ 150 if (!done_stopprint) 151 printf("Goodbye %s %s...\n\n", pl_character, plname); 152 { 153 long int tmp; 154 tmp = u.ugold - u.ugold0; 155 if (tmp < 0) 156 tmp = 0; 157 if (*st1 == 'd' || *st1 == 'b') 158 tmp -= tmp / 10; 159 u.urexp += tmp; 160 u.urexp += 50 * maxdlevel; 161 if (maxdlevel > 20) 162 u.urexp += 1000 * ((maxdlevel > 30) ? 10 : maxdlevel - 20); 163 } 164 if (*st1 == 'e') { 165 struct monst *mtmp; 166 struct obj *otmp; 167 int i; 168 unsigned worthlessct = 0; 169 boolean has_amulet = FALSE; 170 171 killer = st1; 172 keepdogs(); 173 mtmp = mydogs; 174 if (mtmp) { 175 if (!done_stopprint) 176 printf("You"); 177 while (mtmp) { 178 if (!done_stopprint) 179 printf(" and %s", monnam(mtmp)); 180 if (mtmp->mtame) 181 u.urexp += mtmp->mhp; 182 mtmp = mtmp->nmon; 183 } 184 if (!done_stopprint) 185 printf("\nescaped from the dungeon with %ld points,\n", 186 u.urexp); 187 } else if (!done_stopprint) 188 printf("You escaped from the dungeon with %ld points,\n", 189 u.urexp); 190 for (otmp = invent; otmp; otmp = otmp->nobj) { 191 if (otmp->olet == GEM_SYM) { 192 objects[otmp->otyp].oc_name_known = 1; 193 i = otmp->quan * objects[otmp->otyp].g_val; 194 if (i == 0) { 195 worthlessct += otmp->quan; 196 continue; 197 } 198 u.urexp += i; 199 if (!done_stopprint) 200 printf("\t%s (worth %d Zorkmids),\n", 201 doname(otmp), i); 202 } else if (otmp->olet == AMULET_SYM) { 203 otmp->known = 1; 204 i = (otmp->spe < 0) ? 2 : 5000; 205 u.urexp += i; 206 if (!done_stopprint) 207 printf("\t%s (worth %d Zorkmids),\n", 208 doname(otmp), i); 209 if (otmp->spe >= 0) { 210 has_amulet = TRUE; 211 killer = "escaped (with amulet)"; 212 } 213 } 214 } 215 if (worthlessct) 216 if (!done_stopprint) 217 printf("\t%u worthless piece%s of coloured glass,\n", 218 worthlessct, plur(worthlessct)); 219 if (has_amulet) 220 u.urexp *= 2; 221 } else if (!done_stopprint) 222 printf("You %s on dungeon level %d with %ld points,\n", 223 st1, dlevel, u.urexp); 224 if (!done_stopprint) 225 printf("and %ld piece%s of gold, after %ld move%s.\n", 226 u.ugold, plur(u.ugold), moves, plur(moves)); 227 if (!done_stopprint) 228 printf("You were level %u with a maximum of %d hit points when you %s.\n", 229 u.ulevel, u.uhpmax, st1); 230 if (*st1 == 'e' && !done_stopprint) { 231 getret(); /* all those pieces of coloured glass ... */ 232 cls(); 233 } 234 #ifdef WIZARD 235 if (!wizard) 236 #endif /* WIZARD */ 237 topten(); 238 if (done_stopprint) 239 printf("\n\n"); 240 exit(0); 241 } 242 243 static void 244 topten(void) 245 { 246 int uid = getuid(); 247 int rank, rank0 = -1, rank1 = 0; 248 int occ_cnt = PERSMAX; 249 struct toptenentry *t0, *t1, *tprev; 250 const char *recfile = RECORD; 251 const char *reclock = "record_lock"; 252 int sleepct = 300; 253 FILE *rfile; 254 int flg = 0; 255 256 #define HUP if (!done_hup) 257 while (link(recfile, reclock) == -1) { 258 HUP perror(reclock); 259 if (!sleepct--) { 260 HUP puts("I give up. Sorry."); 261 HUP puts("Perhaps there is an old record_lock around?"); 262 return; 263 } 264 HUP printf("Waiting for access to record file. (%d)\n", 265 sleepct); 266 HUP fflush(stdout); 267 sleep(1); 268 } 269 if (!(rfile = fopen(recfile, "r"))) { 270 HUP puts("Cannot open record file!"); 271 goto unlock; 272 } 273 HUP putchar('\n'); 274 275 /* create a new 'topten' entry */ 276 t0 = newttentry(); 277 t0->level = dlevel; 278 t0->maxlvl = maxdlevel; 279 t0->hp = u.uhp; 280 t0->maxhp = u.uhpmax; 281 t0->points = u.urexp; 282 t0->plchar = pl_character[0]; 283 t0->sex = (flags.female ? 'F' : 'M'); 284 t0->uid = uid; 285 strncpy(t0->name, plname, NAMSZ); 286 (t0->name)[NAMSZ] = 0; 287 strncpy(t0->death, killer, DTHSZ); 288 (t0->death)[DTHSZ] = 0; 289 strcpy(t0->date, getdate()); 290 291 /* assure minimum number of points */ 292 if (t0->points < POINTSMIN) 293 t0->points = 0; 294 295 t1 = tt_head = newttentry(); 296 tprev = NULL; 297 /* rank0: -1 undefined, 0 not_on_list, n n_th on list */ 298 for (rank = 1;;) { 299 if (fscanf(rfile, "%6s %d %d %d %d %d %ld %c%c %[^,],%[^\n]", 300 t1->date, &t1->uid, 301 &t1->level, &t1->maxlvl, 302 &t1->hp, &t1->maxhp, &t1->points, 303 &t1->plchar, &t1->sex, t1->name, t1->death) != 11 304 || t1->points < POINTSMIN) 305 t1->points = 0; 306 if (rank0 < 0 && t1->points < t0->points) { 307 rank0 = rank++; 308 if (tprev == NULL) 309 tt_head = t0; 310 else 311 tprev->tt_next = t0; 312 t0->tt_next = t1; 313 occ_cnt--; 314 flg++; /* ask for a rewrite */ 315 } else 316 tprev = t1; 317 if (t1->points == 0) 318 break; 319 if ( 320 #ifdef PERS_IS_UID 321 t1->uid == t0->uid && 322 #else 323 strncmp(t1->name, t0->name, NAMSZ) == 0 && 324 #endif /* PERS_IS_UID */ 325 t1->plchar == t0->plchar && --occ_cnt <= 0) { 326 if (rank0 < 0) { 327 rank0 = 0; 328 rank1 = rank; 329 HUP printf("You didn't beat your previous score of %ld points.\n\n", 330 t1->points); 331 } 332 if (occ_cnt < 0) { 333 flg++; 334 continue; 335 } 336 } 337 if (rank <= ENTRYMAX) { 338 t1 = t1->tt_next = newttentry(); 339 rank++; 340 } 341 if (rank > ENTRYMAX) { 342 t1->points = 0; 343 break; 344 } 345 } 346 if (flg) { /* rewrite record file */ 347 fclose(rfile); 348 if (!(rfile = fopen(recfile, "w"))) { 349 HUP puts("Cannot write record file\n"); 350 goto unlock; 351 } 352 353 if (!done_stopprint) 354 if (rank0 > 0) { 355 if (rank0 <= 10) 356 puts("You made the top ten list!\n"); 357 else 358 printf("You reached the %d%s place on the top %d list.\n\n", 359 rank0, ordin(rank0), ENTRYMAX); 360 } 361 } 362 if (rank0 == 0) 363 rank0 = rank1; 364 if (rank0 <= 0) 365 rank0 = rank; 366 if (!done_stopprint) 367 outheader(); 368 t1 = tt_head; 369 for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) { 370 if (flg) 371 fprintf(rfile, "%6s %d %d %d %d %d %ld %c%c %s,%s\n", 372 t1->date, t1->uid, 373 t1->level, t1->maxlvl, 374 t1->hp, t1->maxhp, t1->points, 375 t1->plchar, t1->sex, t1->name, t1->death); 376 if (done_stopprint) 377 continue; 378 if (rank > (int)flags.end_top && 379 (rank < rank0 - (int)flags.end_around || rank > rank0 + 380 (int)flags.end_around) 381 && (!flags.end_own || 382 #ifdef PERS_IS_UID 383 t1->uid != t0->uid)) 384 #else 385 strncmp(t1->name, t0->name, NAMSZ))) 386 #endif /* PERS_IS_UID */ 387 continue; 388 if (rank == rank0 - (int)flags.end_around && 389 rank0 > (int)flags.end_top + (int)flags.end_around + 1 && 390 !flags.end_own) 391 putchar('\n'); 392 if (rank != rank0) 393 outentry(rank, t1, 0); 394 else if (!rank1) 395 outentry(rank, t1, 1); 396 else { 397 int t0lth = outentry(0, t0, -1); 398 int t1lth = outentry(rank, t1, t0lth); 399 if (t1lth > t0lth) 400 t0lth = t1lth; 401 outentry(0, t0, t0lth); 402 } 403 } 404 if (rank0 >= rank) 405 if (!done_stopprint) 406 outentry(0, t0, 1); 407 fclose(rfile); 408 unlock: 409 unlink(reclock); 410 } 411 412 static void 413 outheader(void) 414 { 415 char linebuf[BUFSZ]; 416 char *bp; 417 418 strcpy(linebuf, "Number Points Name"); 419 bp = eos(linebuf); 420 while (bp < linebuf + COLNO - 9) 421 *bp++ = ' '; 422 strcpy(bp, "Hp [max]"); 423 puts(linebuf); 424 } 425 426 /* so>0: standout line; so=0: ordinary line; so<0: no output, return lth */ 427 static int 428 outentry(int rank, struct toptenentry *t1, int so) 429 { 430 boolean quit = FALSE, dead = FALSE, starv = FALSE; 431 char linebuf[BUFSZ]; 432 433 linebuf[0] = 0; 434 if (rank) 435 Sprintf(eos(linebuf), "%3d", rank); 436 else 437 Sprintf(eos(linebuf), " "); 438 Sprintf(eos(linebuf), " %6ld %8s", t1->points, t1->name); 439 if (t1->plchar == 'X') 440 Sprintf(eos(linebuf), " "); 441 else 442 Sprintf(eos(linebuf), "-%c ", t1->plchar); 443 if (!strncmp("escaped", t1->death, 7)) { 444 if (!strcmp(" (with amulet)", t1->death + 7)) 445 Sprintf(eos(linebuf), "escaped the dungeon with amulet"); 446 else 447 Sprintf(eos(linebuf), "escaped the dungeon [max level %d]", 448 t1->maxlvl); 449 } else { 450 if (!strncmp(t1->death, "quit", 4)) { 451 quit = TRUE; 452 if (t1->maxhp < 3 * t1->hp && t1->maxlvl < 4) 453 Sprintf(eos(linebuf), "cravenly gave up"); 454 else 455 Sprintf(eos(linebuf), "quit"); 456 } else if (!strcmp(t1->death, "choked")) { 457 Sprintf(eos(linebuf), "choked on %s food", 458 (t1->sex == 'F') ? "her" : "his"); 459 } else if (!strncmp(t1->death, "starv", 5)) { 460 Sprintf(eos(linebuf), "starved to death"); 461 starv = TRUE; 462 } else { 463 Sprintf(eos(linebuf), "was killed"); 464 dead = TRUE; 465 } 466 Sprintf(eos(linebuf), " on%s level %d", 467 (dead || starv) ? "" : " dungeon", t1->level); 468 if (t1->maxlvl != t1->level) 469 Sprintf(eos(linebuf), " [max %d]", t1->maxlvl); 470 if (quit && t1->death[4]) 471 Sprintf(eos(linebuf), "%s", t1->death + 4); 472 } 473 if (dead) { 474 Sprintf(eos(linebuf), " by %s%s", 475 (!strncmp(t1->death, "trick", 5) || 476 !strncmp(t1->death, "the ", 4)) 477 ? "" : 478 strchr(vowels, *t1->death) ? "an " : "a ", 479 t1->death); 480 } 481 Sprintf(eos(linebuf), "."); 482 if (t1->maxhp) { 483 char *bp = eos(linebuf); 484 char hpbuf[10]; 485 int hppos; 486 487 snprintf(hpbuf, sizeof hpbuf, "%s", (t1->hp > 0) ? itoa(t1->hp) : "-"); 488 hppos = COLNO - 7 - strlen(hpbuf); 489 if (bp <= linebuf + hppos) { 490 while (bp < linebuf + hppos) 491 *bp++ = ' '; 492 strcpy(bp, hpbuf); 493 Sprintf(eos(bp), " [%d]", t1->maxhp); 494 } 495 } 496 if (so == 0) 497 puts(linebuf); 498 else if (so > 0) { 499 char *bp = eos(linebuf); 500 if (so >= COLNO) 501 so = COLNO - 1; 502 while (bp < linebuf + so) 503 *bp++ = ' '; 504 *bp = 0; 505 standoutbeg(); 506 fputs(linebuf, stdout); 507 standoutend(); 508 putchar('\n'); 509 } 510 return (strlen(linebuf)); 511 } 512 513 static char * 514 itoa(int a) 515 { 516 static char buf[10]; 517 518 snprintf(buf, sizeof buf, "%d", a); 519 return(buf); 520 } 521 522 static const char * 523 ordin(int n) 524 { 525 int d1 = n % 10; 526 527 return ((d1 == 0 || d1 > 3 || n / 10 == 1) ? "th" : (d1 == 1) ? "st" : 528 (d1 == 2) ? "nd" : "rd"); 529 } 530 531 void 532 clearlocks(void) 533 { 534 int x; 535 536 signal(SIGHUP, SIG_IGN); 537 for (x = maxdlevel; x >= 0; x--) { 538 glo(x); 539 unlink(lock); /* not all levels need be present */ 540 } 541 } 542 543 #ifdef NOSAVEONHANGUP 544 void 545 hangup(int unused __unused) 546 { 547 signal(SIGINT, SIG_IGN); 548 clearlocks(); 549 exit(1); 550 } 551 #endif /* NOSAVEONHANGUP */ 552 553 char * 554 eos(char *s) 555 { 556 while (*s) 557 s++; 558 return (s); 559 } 560 561 /* it is the callers responsibility to check that there is room for c */ 562 void 563 charcat(char *s, char c) 564 { 565 while (*s) 566 s++; 567 *s++ = c; 568 *s = 0; 569 } 570 571 /* 572 * Called with args from main if argc >= 0. In this case, list scores as 573 * requested. Otherwise, find scores for the current player (and list them 574 * if argc == -1). 575 */ 576 void 577 prscore(int argc, char **argv) 578 { 579 char **players = NULL; 580 int playerct; 581 int rank; 582 struct toptenentry *t1, *t2; 583 const char *recfile = RECORD; 584 FILE *rfile; 585 int flg = 0; 586 int i; 587 #ifdef nonsense 588 long total_score = 0L; 589 char totchars[10]; 590 int totcharct = 0; 591 #endif /* nonsense */ 592 int outflg = (argc >= -1); 593 #ifdef PERS_IS_UID 594 int uid = -1; 595 #else 596 char *player0; 597 #endif /* PERS_IS_UID */ 598 599 if (!(rfile = fopen(recfile, "r"))) { 600 puts("Cannot open record file!"); 601 return; 602 } 603 604 if (argc > 1 && !strncmp(argv[1], "-s", 2)) { 605 if (!argv[1][2]) { 606 argc--; 607 argv++; 608 } else if (!argv[1][3] && strchr("CFKSTWX", argv[1][2])) { 609 argv[1]++; 610 argv[1][0] = '-'; 611 } else 612 argv[1] += 2; 613 } 614 if (argc <= 1) { 615 #ifdef PERS_IS_UID 616 uid = getuid(); 617 playerct = 0; 618 #else 619 player0 = plname; 620 if (!*player0) 621 player0 = "hackplayer"; 622 playerct = 1; 623 players = &player0; 624 #endif /* PERS_IS_UID */ 625 } else { 626 playerct = --argc; 627 players = ++argv; 628 } 629 if (outflg) 630 putchar('\n'); 631 632 t1 = tt_head = newttentry(); 633 for (rank = 1;; rank++) { 634 if (fscanf(rfile, "%6s %d %d %d %d %d %ld %c%c %[^,],%[^\n]", 635 t1->date, &t1->uid, 636 &t1->level, &t1->maxlvl, 637 &t1->hp, &t1->maxhp, &t1->points, 638 &t1->plchar, &t1->sex, t1->name, t1->death) != 11) 639 t1->points = 0; 640 if (t1->points == 0) 641 break; 642 #ifdef PERS_IS_UID 643 if (!playerct && t1->uid == uid) 644 flg++; 645 else 646 #endif /* PERS_IS_UID */ 647 for (i = 0; i < playerct; i++) { 648 if (strcmp(players[i], "all") == 0 || 649 strncmp(t1->name, players[i], NAMSZ) == 0 || 650 (players[i][0] == '-' && 651 players[i][1] == t1->plchar && 652 players[i][2] == 0) || 653 (digit(players[i][0]) && rank <= atoi(players[i]))) 654 flg++; 655 } 656 t1 = t1->tt_next = newttentry(); 657 } 658 fclose(rfile); 659 if (!flg) { 660 if (outflg) { 661 printf("Cannot find any entries for "); 662 if (playerct < 1) 663 printf("you.\n"); 664 else { 665 if (playerct > 1) 666 printf("any of "); 667 for (i = 0; i < playerct; i++) 668 printf("%s%s", players[i], 669 (i < playerct - 1) ? ", " : ".\n"); 670 printf("Call is: %s -s [playernames]\n", hname); 671 } 672 } 673 return; 674 } 675 676 if (outflg) 677 outheader(); 678 t1 = tt_head; 679 for (rank = 1; t1->points != 0; rank++, t1 = t2) { 680 t2 = t1->tt_next; 681 #ifdef PERS_IS_UID 682 if (!playerct && t1->uid == uid) 683 goto outwithit; 684 else 685 #endif /* PERS_IS_UID */ 686 for (i = 0; i < playerct; i++) { 687 if (strcmp(players[i], "all") == 0 || 688 strncmp(t1->name, players[i], NAMSZ) == 0 || 689 (players[i][0] == '-' && 690 players[i][1] == t1->plchar && 691 players[i][2] == 0) || 692 (digit(players[i][0]) && rank <= 693 atoi(players[i]))) { 694 outwithit: 695 if (outflg) 696 outentry(rank, t1, 0); 697 #ifdef nonsense 698 total_score += t1->points; 699 if (totcharct < sizeof(totchars) - 1) 700 totchars[totcharct++] = t1->plchar; 701 #endif /* nonsense */ 702 break; 703 } 704 } 705 free(t1); 706 } 707 #ifdef nonsense 708 totchars[totcharct] = 0; 709 710 /* 711 * We would like to determine whether he is experienced. However, the 712 * information collected here only tells about the scores/roles that 713 * got into the topten (top 100?). We should maintain a .hacklog or 714 * something in his home directory. 715 */ 716 flags.beginner = (total_score < 6000); 717 for (i = 0; i < 6; i++) 718 if (!strchr(totchars, "CFKSTWX"[i])) { 719 flags.beginner = 1; 720 if (!pl_character[0]) 721 pl_character[0] = "CFKSTWX"[i]; 722 break; 723 } 724 #endif /* nonsense */ 725 } 726