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