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