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