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