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