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