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