1 /* $NetBSD: score.c,v 1.16 2011/08/26 06:18:17 dholland Exp $ */ 2 3 /* 4 * Copyright (c) 1988, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Timothy C. Stoehr. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <sys/cdefs.h> 36 #ifndef lint 37 #if 0 38 static char sccsid[] = "@(#)score.c 8.1 (Berkeley) 5/31/93"; 39 #else 40 __RCSID("$NetBSD: score.c,v 1.16 2011/08/26 06:18:17 dholland Exp $"); 41 #endif 42 #endif /* not lint */ 43 44 /* 45 * score.c 46 * 47 * This source herein may be modified and/or distributed by anybody who 48 * so desires, with the following restrictions: 49 * 1.) No portion of this notice shall be removed. 50 * 2.) Credit shall not be taken for the creation of this source. 51 * 3.) This code is not to be traded, sold, or used for personal 52 * gain or profit. 53 * 54 */ 55 56 #include <stdio.h> 57 #include "rogue.h" 58 #include "pathnames.h" 59 60 static void center(short, const char *); 61 static int get_value(const object *); 62 static void id_all(void); 63 static void sell_pack(void); 64 static void sf_error(void) __dead; 65 66 void 67 killed_by(const object *monster, short other) 68 { 69 const char *mechanism = "killed by something unknown (?)"; 70 char mechanism_buf[128]; 71 const char *article; 72 char message_buf[128]; 73 74 md_ignore_signals(); 75 76 if (other != QUIT) { 77 rogue.gold = ((rogue.gold * 9) / 10); 78 } 79 80 if (other) { 81 switch(other) { 82 case HYPOTHERMIA: 83 mechanism = "died of hypothermia"; 84 break; 85 case STARVATION: 86 mechanism = "died of starvation"; 87 break; 88 case POISON_DART: 89 mechanism = "killed by a dart"; 90 break; 91 case QUIT: 92 mechanism = "quit"; 93 break; 94 case KFIRE: 95 mechanism = "killed by fire"; 96 break; 97 } 98 } else { 99 if (is_vowel(m_names[monster->m_char - 'A'][0])) { 100 article = "an"; 101 } else { 102 article = "a"; 103 } 104 snprintf(mechanism_buf, sizeof(mechanism_buf), 105 "Killed by %s %s", 106 article, m_names[monster->m_char - 'A']); 107 mechanism = mechanism_buf; 108 } 109 snprintf(message_buf, sizeof(message_buf), 110 "%s with %ld gold", mechanism, rogue.gold); 111 112 if ((!other) && (!no_skull)) { 113 clear(); 114 mvaddstr(4, 32, "__---------__"); 115 mvaddstr(5, 30, "_~ ~_"); 116 mvaddstr(6, 29, "/ \\"); 117 mvaddstr(7, 28, "~ ~"); 118 mvaddstr(8, 27, "/ \\"); 119 mvaddstr(9, 27, "| XXXX XXXX |"); 120 mvaddstr(10, 27, "| XXXX XXXX |"); 121 mvaddstr(11, 27, "| XXX XXX |"); 122 mvaddstr(12, 28, "\\ @ /"); 123 mvaddstr(13, 29, "--\\ @@@ /--"); 124 mvaddstr(14, 30, "| | @@@ | |"); 125 mvaddstr(15, 30, "| | | |"); 126 mvaddstr(16, 30, "| vvVvvvvvvvVvv |"); 127 mvaddstr(17, 30, "| ^^^^^^^^^^^ |"); 128 mvaddstr(18, 31, "\\_ _/"); 129 mvaddstr(19, 33, "~---------~"); 130 center(21, nick_name); 131 center(22, message_buf); 132 } else { 133 messagef(0, "%s", message_buf); 134 } 135 messagef(0, "%s", ""); /* gcc objects to just "" */ 136 put_scores(monster, other); 137 } 138 139 void 140 win(void) 141 { 142 unwield(rogue.weapon); /* disarm and relax */ 143 unwear(rogue.armor); 144 un_put_on(rogue.left_ring); 145 un_put_on(rogue.right_ring); 146 147 clear(); 148 mvaddstr(10, 11, "@ @ @@@ @ @ @ @ @ @@@ @ @ @"); 149 mvaddstr(11, 11, " @ @ @ @ @ @ @ @ @ @ @ @@ @ @"); 150 mvaddstr(12, 11, " @ @ @ @ @ @ @ @ @ @ @ @ @ @"); 151 mvaddstr(13, 11, " @ @ @ @ @ @ @ @ @ @ @ @@"); 152 mvaddstr(14, 11, " @ @@@ @@@ @@ @@ @@@ @ @ @"); 153 mvaddstr(17, 11, "Congratulations, you have been admitted to the"); 154 mvaddstr(18, 11, "Fighters' Guild. You return home, sell all your"); 155 mvaddstr(19, 11, "treasures at great profit and retire into comfort."); 156 messagef(0, "%s", ""); /* gcc objects to just "" */ 157 messagef(0, "%s", ""); /* gcc objects to just "" */ 158 id_all(); 159 sell_pack(); 160 put_scores(NULL, WIN); 161 } 162 163 void 164 quit(boolean from_intrpt) 165 { 166 char buf[DCOLS]; 167 short i, orow, ocol; 168 boolean mc; 169 170 orow = ocol = 0; 171 mc = FALSE; 172 md_ignore_signals(); 173 174 if (from_intrpt) { 175 orow = rogue.row; 176 ocol = rogue.col; 177 178 mc = msg_cleared; 179 180 for (i = 0; i < DCOLS; i++) { 181 buf[i] = mvinch(0, i); 182 } 183 } 184 check_message(); 185 messagef(1, "really quit?"); 186 if (rgetchar() != 'y') { 187 md_heed_signals(); 188 check_message(); 189 if (from_intrpt) { 190 for (i = 0; i < DCOLS; i++) { 191 mvaddch(0, i, buf[i]); 192 } 193 msg_cleared = mc; 194 move(orow, ocol); 195 refresh(); 196 } 197 return; 198 } 199 if (from_intrpt) { 200 clean_up(byebye_string); 201 } 202 check_message(); 203 killed_by(NULL, QUIT); 204 } 205 206 /* 207 * The score file on disk is up to ten entries of the form 208 * score block [80 bytes] 209 * nickname block [30 bytes] 210 * 211 * The score block is to be parsed as follows: 212 * bytes 0-1 Rank (" 1" to "10") 213 * bytes 2-4 space padding 214 * bytes 5-15 Score/gold 215 * byte 15 up to a ':' Login name 216 * past the ':' Death mechanism 217 * 218 * The nickname block is an alternate name to be printed in place of the 219 * login name. Both blocks are supposed to contain a null-terminator. 220 */ 221 222 struct score_entry { 223 long gold; 224 char username[80]; 225 char death[80]; 226 char nickname[30]; 227 }; 228 229 #define NUM_SCORE_ENTRIES 10 230 231 static void make_score(struct score_entry *, const object *, int); 232 233 static 234 void 235 pad_spaces(char *str, size_t len) 236 { 237 size_t x; 238 for (x=strlen(str); x<len-1; x++) { 239 str[x] = ' '; 240 } 241 str[len-1] = 0; 242 } 243 244 static 245 void 246 unpad_spaces(char *str) 247 { 248 size_t x; 249 for (x=strlen(str); x>0 && str[x-1]==' '; x--); 250 str[x] = 0; 251 } 252 253 static 254 int 255 read_score_entry(struct score_entry *se, FILE *fp) 256 { 257 char score_block[80]; 258 char nickname_block[30]; 259 size_t n, x; 260 261 n = fread(score_block, 1, sizeof(score_block), fp); 262 if (n==0) { 263 /* EOF */ 264 return 0; 265 } 266 if (n != sizeof(score_block)) { 267 sf_error(); 268 } 269 270 n = fread(nickname_block, 1, sizeof(nickname_block), fp); 271 if (n != sizeof(nickname_block)) { 272 sf_error(); 273 } 274 275 xxxx(score_block, sizeof(score_block)); 276 xxxx(nickname_block, sizeof(nickname_block)); 277 278 /* Ensure null termination */ 279 score_block[sizeof(score_block)-1] = 0; 280 nickname_block[sizeof(nickname_block)-1] = 0; 281 282 /* If there are other nulls in the score block, file is corrupt */ 283 if (strlen(score_block)!=sizeof(score_block)-1) { 284 sf_error(); 285 } 286 /* but this is NOT true of the nickname block */ 287 288 /* quash trailing spaces */ 289 unpad_spaces(score_block); 290 unpad_spaces(nickname_block); 291 292 for (x=5; score_block[x] == ' '; x++); 293 se->gold = lget_number(score_block+x); 294 295 for (x=15; score_block[x] != 0 && score_block[x] != ':'; x++); 296 if (score_block[x] == 0) { 297 sf_error(); 298 } 299 score_block[x++] = 0; 300 strlcpy(se->username, score_block+15, sizeof(se->username)); 301 302 strlcpy(se->death, score_block+x, sizeof(se->death)); 303 strlcpy(se->nickname, nickname_block, sizeof(se->nickname)); 304 305 return 1; 306 } 307 308 static 309 void 310 write_score_entry(const struct score_entry *se, int rank, FILE *fp) 311 { 312 char score_block[80]; 313 char nickname_block[30]; 314 315 /* avoid writing crap to score file */ 316 memset(score_block, 0, sizeof(score_block)); 317 memset(nickname_block, 0, sizeof(nickname_block)); 318 319 snprintf(score_block, sizeof(score_block), 320 "%2d %6ld %s: %s", 321 rank+1, se->gold, se->username, se->death); 322 strlcpy(nickname_block, se->nickname, sizeof(nickname_block)); 323 324 /* pad blocks out with spaces */ 325 pad_spaces(score_block, sizeof(score_block)); 326 /*pad_spaces(nickname_block, sizeof(nickname_block)); -- wrong! */ 327 328 xxxx(score_block, sizeof(score_block)); 329 xxxx(nickname_block, sizeof(nickname_block)); 330 331 fwrite(score_block, 1, sizeof(score_block), fp); 332 fwrite(nickname_block, 1, sizeof(nickname_block), fp); 333 } 334 335 void 336 put_scores(const object *monster, short other) 337 { 338 short i, rank=-1, found_player = -1, numscores = 0; 339 struct score_entry scores[NUM_SCORE_ENTRIES]; 340 const char *name; 341 FILE *fp; 342 boolean dopause = score_only; 343 344 md_lock(1); 345 346 setegid(egid); 347 if ((fp = fopen(_PATH_SCOREFILE, "r+")) == NULL && 348 (fp = fopen(_PATH_SCOREFILE, "w+")) == NULL) { 349 setegid(gid); 350 messagef(0, "cannot read/write/create score file"); 351 sf_error(); 352 } 353 setegid(gid); 354 rewind(fp); 355 (void)xxx(1); 356 357 for (numscores = 0; numscores < NUM_SCORE_ENTRIES; numscores++) { 358 if (read_score_entry(&scores[numscores], fp) == 0) { 359 break; 360 } 361 } 362 363 /* Search the score list. */ 364 for (i=0; i<numscores; i++) { 365 if (!strcmp(scores[i].username, login_name)) { 366 /* found our score */ 367 if (rogue.gold < scores[i].gold) { 368 /* we didn't do as well as last time */ 369 score_only = 1; 370 } else { 371 /* we did better; mark entry for removal */ 372 found_player = i; 373 } 374 break; 375 } 376 } 377 378 /* Remove a superseded entry, if any. */ 379 if (found_player != -1) { 380 numscores--; 381 for (i = found_player; i < numscores; i++) { 382 scores[i] = scores[i+1]; 383 } 384 } 385 386 /* If we're going to insert ourselves, do it now */ 387 if (!score_only) { 388 389 /* if we aren't better than anyone, add at end. */ 390 rank = numscores; 391 392 /* Otherwise, find our slot. */ 393 for (i = 0; i < numscores; i++) { 394 if (rogue.gold >= scores[i].gold) { 395 rank = i; 396 break; 397 } 398 } 399 400 if (rank < NUM_SCORE_ENTRIES) { 401 /* Open up a slot */ 402 for (i = numscores; i > rank; i--) { 403 scores[i] = scores[i-1]; 404 } 405 numscores++; 406 407 /* Put our info in the slot */ 408 make_score(&scores[rank], monster, other); 409 } 410 411 /* Now rewrite the score file */ 412 413 md_ignore_signals(); 414 rewind(fp); 415 (void)xxx(1); 416 417 for (i = 0; i < numscores; i++) { 418 write_score_entry(&scores[i], i, fp); 419 } 420 } 421 md_lock(0); 422 fclose(fp); 423 424 /* Display the scores */ 425 426 clear(); 427 mvaddstr(3, 30, "Top Ten Rogueists"); 428 mvaddstr(8, 0, "Rank Score Name"); 429 430 for (i = 0; i < numscores; i++) { 431 if (i == rank) { 432 standout(); 433 } 434 435 if (scores[i].nickname[0]) { 436 name = scores[i].nickname; 437 } else { 438 name = scores[i].username; 439 } 440 441 mvprintw(i+10, 0, "%2d %6ld %s: %s", 442 i+1, scores[i].gold, name, scores[i].death); 443 444 if (i == rank) { 445 standend(); 446 } 447 } 448 refresh(); 449 messagef(0, "%s", ""); /* gcc objects to just "" */ 450 if (dopause) { 451 messagef(0, "%s", ""); 452 } 453 clean_up(""); 454 } 455 456 static 457 void 458 make_score(struct score_entry *se, const object *monster, int other) 459 { 460 const char *death = "bolts from the blue (?)"; 461 const char *hasamulet; 462 char deathbuf[80]; 463 464 se->gold = rogue.gold; 465 strlcpy(se->username, login_name, sizeof(se->username)); 466 467 if (other) { 468 switch(other) { 469 case HYPOTHERMIA: 470 death = "died of hypothermia"; 471 break; 472 case STARVATION: 473 death = "died of starvation"; 474 break; 475 case POISON_DART: 476 death = "killed by a dart"; 477 break; 478 case QUIT: 479 death = "quit"; 480 break; 481 case WIN: 482 death = "a total winner"; 483 break; 484 case KFIRE: 485 death = "killed by fire"; 486 break; 487 } 488 } else { 489 const char *mn, *article; 490 491 mn = m_names[monster->m_char - 'A']; 492 if (is_vowel(mn[0])) { 493 article = "an"; 494 } else { 495 article = "a"; 496 } 497 498 snprintf(deathbuf, sizeof(deathbuf), 499 "killed by %s %s", article, mn); 500 death = deathbuf; 501 } 502 503 if (other != WIN && has_amulet()) { 504 hasamulet = " with amulet"; 505 } else { 506 hasamulet = ""; 507 } 508 509 snprintf(se->death, sizeof(se->death), "%s on level %d%s", 510 death, max_level, hasamulet); 511 512 strlcpy(se->nickname, nick_name, sizeof(se->nickname)); 513 } 514 515 boolean 516 is_vowel(short ch) 517 { 518 return( (ch == 'a') || 519 (ch == 'e') || 520 (ch == 'i') || 521 (ch == 'o') || 522 (ch == 'u') ); 523 } 524 525 static void 526 sell_pack(void) 527 { 528 object *obj; 529 short row = 2, val; 530 char buf[DCOLS]; 531 532 obj = rogue.pack.next_object; 533 534 clear(); 535 mvaddstr(1, 0, "Value Item"); 536 537 while (obj) { 538 if (obj->what_is != FOOD) { 539 obj->identified = 1; 540 val = get_value(obj); 541 rogue.gold += val; 542 543 if (row < DROWS) { 544 get_desc(obj, buf, sizeof(buf)); 545 mvprintw(row++, 0, "%5d %s", val, buf); 546 } 547 } 548 obj = obj->next_object; 549 } 550 refresh(); 551 if (rogue.gold > MAX_GOLD) { 552 rogue.gold = MAX_GOLD; 553 } 554 messagef(0, "%s", ""); /* gcc objects to just "" */ 555 } 556 557 static int 558 get_value(const object *obj) 559 { 560 short wc; 561 int val; 562 563 val = 0; 564 wc = obj->which_kind; 565 566 switch(obj->what_is) { 567 case WEAPON: 568 val = id_weapons[wc].value; 569 if ((wc == ARROW) || (wc == DAGGER) || (wc == SHURIKEN) || 570 (wc == DART)) { 571 val *= obj->quantity; 572 } 573 val += (obj->d_enchant * 85); 574 val += (obj->hit_enchant * 85); 575 break; 576 case ARMOR: 577 val = id_armors[wc].value; 578 val += (obj->d_enchant * 75); 579 if (obj->is_protected) { 580 val += 200; 581 } 582 break; 583 case WAND: 584 val = id_wands[wc].value * (obj->class + 1); 585 break; 586 case SCROL: 587 val = id_scrolls[wc].value * obj->quantity; 588 break; 589 case POTION: 590 val = id_potions[wc].value * obj->quantity; 591 break; 592 case AMULET: 593 val = 5000; 594 break; 595 case RING: 596 val = id_rings[wc].value * (obj->class + 1); 597 break; 598 } 599 if (val <= 0) { 600 val = 10; 601 } 602 return(val); 603 } 604 605 static void 606 id_all(void) 607 { 608 short i; 609 610 for (i = 0; i < SCROLS; i++) { 611 id_scrolls[i].id_status = IDENTIFIED; 612 } 613 for (i = 0; i < WEAPONS; i++) { 614 id_weapons[i].id_status = IDENTIFIED; 615 } 616 for (i = 0; i < ARMORS; i++) { 617 id_armors[i].id_status = IDENTIFIED; 618 } 619 for (i = 0; i < WANDS; i++) { 620 id_wands[i].id_status = IDENTIFIED; 621 } 622 for (i = 0; i < POTIONS; i++) { 623 id_potions[i].id_status = IDENTIFIED; 624 } 625 } 626 627 void 628 xxxx(char *buf, short n) 629 { 630 short i; 631 unsigned char c; 632 633 for (i = 0; i < n; i++) { 634 635 /* It does not matter if accuracy is lost during this assignment */ 636 c = (unsigned char)xxx(0); 637 638 buf[i] ^= c; 639 } 640 } 641 642 long 643 xxx(boolean st) 644 { 645 static long f, s; 646 long r; 647 648 if (st) { 649 f = 37; 650 s = 7; 651 return(0L); 652 } 653 r = ((f * s) + 9337) % 8887; 654 f = s; 655 s = r; 656 return(r); 657 } 658 659 static void 660 center(short row, const char *buf) 661 { 662 short margin; 663 664 margin = ((DCOLS - strlen(buf)) / 2); 665 mvaddstr(row, margin, buf); 666 } 667 668 static void 669 sf_error(void) 670 { 671 md_lock(0); 672 messagef(1, "%s", ""); /* gcc objects to just "" */ 673 clean_up("sorry, score file is out of order"); 674 } 675