Home | History | Annotate | Line # | Download | only in rogue
score.c revision 1.13
      1 /*	$NetBSD: score.c,v 1.13 2008/01/14 00:23:53 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.13 2008/01/14 00:23:53 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 void
     61 killed_by(monster, other)
     62 	const object *monster;
     63 	short other;
     64 {
     65 	const char *mechanism = "killed by something unknown (?)";
     66 	char mechanism_buf[128];
     67 	const char *article;
     68 	char message_buf[128];
     69 
     70 	md_ignore_signals();
     71 
     72 	if (other != QUIT) {
     73 		rogue.gold = ((rogue.gold * 9) / 10);
     74 	}
     75 
     76 	if (other) {
     77 		switch(other) {
     78 		case HYPOTHERMIA:
     79 			mechanism = "died of hypothermia";
     80 			break;
     81 		case STARVATION:
     82 			mechanism = "died of starvation";
     83 			break;
     84 		case POISON_DART:
     85 			mechanism = "killed by a dart";
     86 			break;
     87 		case QUIT:
     88 			mechanism = "quit";
     89 			break;
     90 		case KFIRE:
     91 			mechanism = "killed by fire";
     92 			break;
     93 		}
     94 	} else {
     95 		if (is_vowel(m_names[monster->m_char - 'A'][0])) {
     96 			article = "an";
     97 		} else {
     98 			article = "a";
     99 		}
    100 		snprintf(mechanism_buf, sizeof(mechanism_buf),
    101 			"Killed by %s %s",
    102 			 article, m_names[monster->m_char - 'A']);
    103 		mechanism = mechanism_buf;
    104 	}
    105 	snprintf(message_buf, sizeof(message_buf),
    106 		 "%s with %ld gold", mechanism, rogue.gold);
    107 
    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, message_buf);
    128 	} else {
    129 		messagef(0, "%s", message_buf);
    130 	}
    131 	messagef(0, "%s", "");		/* gcc objects to just "" */
    132 	put_scores(monster, other);
    133 }
    134 
    135 void
    136 win()
    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 	messagef(0, "%s", "");		/* gcc objects to just "" */
    153 	messagef(0, "%s", "");		/* gcc objects to just "" */
    154 	id_all();
    155 	sell_pack();
    156 	put_scores((object *)0, WIN);
    157 }
    158 
    159 void
    160 quit(from_intrpt)
    161 	boolean from_intrpt;
    162 {
    163 	char buf[DCOLS];
    164 	short i, orow, ocol;
    165 	boolean mc;
    166 
    167 	orow = ocol = 0;
    168 	mc = FALSE;
    169 	md_ignore_signals();
    170 
    171 	if (from_intrpt) {
    172 		orow = rogue.row;
    173 		ocol = rogue.col;
    174 
    175 		mc = msg_cleared;
    176 
    177 		for (i = 0; i < DCOLS; i++) {
    178 			buf[i] = mvinch(0, i);
    179 		}
    180 	}
    181 	check_message();
    182 	messagef(1, "really quit?");
    183 	if (rgetchar() != 'y') {
    184 		md_heed_signals();
    185 		check_message();
    186 		if (from_intrpt) {
    187 			for (i = 0; i < DCOLS; i++) {
    188 				mvaddch(0, i, buf[i]);
    189 			}
    190 			msg_cleared = mc;
    191 			move(orow, ocol);
    192 			refresh();
    193 		}
    194 		return;
    195 	}
    196 	if (from_intrpt) {
    197 		clean_up(byebye_string);
    198 	}
    199 	check_message();
    200 	killed_by((object *)0, QUIT);
    201 }
    202 
    203 /*
    204  * The score file on disk is up to ten entries of the form
    205  *      score block [80 bytes]
    206  *      nickname block [30 bytes]
    207  *
    208  * The score block is to be parsed as follows:
    209  *      bytes 0-1	Rank (" 1" to "10")
    210  *      bytes 2-4	space padding
    211  *      bytes 5-15	Score/gold
    212  *      byte 15 up to a ':'	Login name
    213  *      past the ':'    Death mechanism
    214  *
    215  * The nickname block is an alternate name to be printed in place of the
    216  * login name. Both blocks are supposed to contain a null-terminator.
    217  */
    218 
    219 struct score_entry {
    220 	long gold;
    221 	char username[80];
    222 	char death[80];
    223 	char nickname[30];
    224 };
    225 
    226 #define NUM_SCORE_ENTRIES 10
    227 
    228 static void pad_spaces __P((char *, size_t));
    229 static void unpad_spaces(char *);
    230 static int read_score_entry __P((struct score_entry *, FILE *));
    231 static void write_score_entry __P((const struct score_entry *, int, FILE *));
    232 static void make_score __P((struct score_entry *, const object *, int));
    233 
    234 
    235 static
    236 void
    237 pad_spaces(str, len)
    238 	char *str;
    239 	size_t len;
    240 {
    241 	size_t x;
    242 	for (x=strlen(str); x<len-1; x++) {
    243 		str[x] = ' ';
    244 	}
    245 	str[len-1] = 0;
    246 }
    247 
    248 static
    249 void
    250 unpad_spaces(str)
    251 	char *str;
    252 {
    253 	size_t x;
    254 	for (x=strlen(str); x>0 && str[x-1]==' '; x--);
    255 	str[x] = 0;
    256 }
    257 
    258 static
    259 int
    260 read_score_entry(se, fp)
    261 	struct score_entry *se;
    262 	FILE *fp;
    263 {
    264 	char score_block[80];
    265 	char nickname_block[30];
    266 	size_t n, x;
    267 
    268 	n = fread(score_block, 1, sizeof(score_block), fp);
    269 	if (n==0) {
    270 		/* EOF */
    271 		return 0;
    272 	}
    273 	if (n != sizeof(score_block)) {
    274 		sf_error();
    275 	}
    276 
    277 	n = fread(nickname_block, 1, sizeof(nickname_block), fp);
    278 	if (n != sizeof(nickname_block)) {
    279 		sf_error();
    280 	}
    281 
    282 	xxxx(score_block, sizeof(score_block));
    283 	xxxx(nickname_block, sizeof(nickname_block));
    284 
    285 	/* Ensure null termination */
    286 	score_block[sizeof(score_block)-1] = 0;
    287 	nickname_block[sizeof(nickname_block)-1] = 0;
    288 
    289 	/* If there are other nulls in the score block, file is corrupt */
    290 	if (strlen(score_block)!=sizeof(score_block)-1) {
    291 		sf_error();
    292 	}
    293 	/* but this is NOT true of the nickname block */
    294 
    295 	/* quash trailing spaces */
    296 	unpad_spaces(score_block);
    297 	unpad_spaces(nickname_block);
    298 
    299 	for (x=5; score_block[x] == ' '; x++);
    300 	se->gold = lget_number(score_block+x);
    301 
    302 	for (x=15; score_block[x] != 0 && score_block[x] != ':'; x++);
    303 	if (score_block[x] == 0) {
    304 		sf_error();
    305 	}
    306 	score_block[x++] = 0;
    307 	strlcpy(se->username, score_block+15, sizeof(se->username));
    308 
    309 	strlcpy(se->death, score_block+x, sizeof(se->death));
    310 	strlcpy(se->nickname, nickname_block, sizeof(se->nickname));
    311 
    312 	return 1;
    313 }
    314 
    315 static
    316 void
    317 write_score_entry(se, rank, fp)
    318 	const struct score_entry *se;
    319 	int rank;
    320 	FILE *fp;
    321 {
    322 	char score_block[80];
    323 	char nickname_block[30];
    324 
    325 	/* avoid writing crap to score file */
    326 	memset(score_block, 0, sizeof(score_block));
    327 	memset(nickname_block, 0, sizeof(nickname_block));
    328 
    329 	snprintf(score_block, sizeof(score_block),
    330 		 "%2d    %6ld   %s: %s",
    331 		 rank+1, se->gold, se->username, se->death);
    332 	strlcpy(nickname_block, se->nickname, sizeof(nickname_block));
    333 
    334 	/* pad blocks out with spaces */
    335 	pad_spaces(score_block, sizeof(score_block));
    336 	/*pad_spaces(nickname_block, sizeof(nickname_block)); -- wrong! */
    337 
    338 	xxxx(score_block, sizeof(score_block));
    339 	xxxx(nickname_block, sizeof(nickname_block));
    340 
    341 	fwrite(score_block, 1, sizeof(score_block), fp);
    342 	fwrite(nickname_block, 1, sizeof(nickname_block), fp);
    343 }
    344 
    345 void
    346 put_scores(monster, other)
    347 	const object *monster;
    348 	short other;
    349 {
    350 	short i, rank=-1, found_player = -1, numscores = 0;
    351 	struct score_entry scores[NUM_SCORE_ENTRIES];
    352 	const char *name;
    353 	FILE *fp;
    354 	boolean dopause = score_only;
    355 
    356 	md_lock(1);
    357 
    358 	setegid(egid);
    359 	if ((fp = fopen(_PATH_SCOREFILE, "r+")) == NULL &&
    360 	    (fp = fopen(_PATH_SCOREFILE, "w+")) == NULL) {
    361 		setegid(gid);
    362 		messagef(0, "cannot read/write/create score file");
    363 		sf_error();
    364 	}
    365 	setegid(gid);
    366 	rewind(fp);
    367 	(void)xxx(1);
    368 
    369 	for (numscores = 0; numscores < NUM_SCORE_ENTRIES; numscores++) {
    370 		if (read_score_entry(&scores[numscores], fp) == 0) {
    371 			break;
    372 		}
    373 	}
    374 
    375 	/* Search the score list. */
    376 	for (i=0; i<numscores; i++) {
    377 		if (!strcmp(scores[i].username, login_name)) {
    378 			/* found our score */
    379 			if (rogue.gold < scores[i].gold) {
    380 				/* we didn't do as well as last time */
    381 				score_only = 1;
    382 			} else {
    383 				/* we did better; mark entry for removal */
    384 				found_player = i;
    385 			}
    386 			break;
    387 		}
    388 	}
    389 
    390 	/* Remove a superseded entry, if any. */
    391 	if (found_player != -1) {
    392 		numscores--;
    393 		for (i = found_player; i < numscores; i++) {
    394 			scores[i] = scores[i+1];
    395 		}
    396 	}
    397 
    398 	/* If we're going to insert ourselves, do it now */
    399 	if (!score_only) {
    400 
    401 		/* if we aren't better than anyone, add at end. */
    402 		rank = numscores;
    403 
    404 		/* Otherwise, find our slot. */
    405 		for (i = 0; i < numscores; i++) {
    406 			if (rogue.gold >= scores[i].gold) {
    407 				rank = i;
    408 				break;
    409 			}
    410 		}
    411 
    412 		if (rank < NUM_SCORE_ENTRIES) {
    413 			/* Open up a slot */
    414 			for (i = numscores; i > rank; i--) {
    415 				scores[i] = scores[i-1];
    416 			}
    417 			numscores++;
    418 
    419 			/* Put our info in the slot */
    420 			make_score(&scores[rank], monster, other);
    421 		}
    422 
    423 		/* Now rewrite the score file */
    424 
    425 		md_ignore_signals();
    426 		rewind(fp);
    427 		(void)xxx(1);
    428 
    429 		for (i = 0; i < numscores; i++) {
    430 			write_score_entry(&scores[i], i, fp);
    431 		}
    432 	}
    433 	md_lock(0);
    434 	fclose(fp);
    435 
    436 	/* Display the scores */
    437 
    438 	clear();
    439 	mvaddstr(3, 30, "Top  Ten  Rogueists");
    440 	mvaddstr(8, 0, "Rank   Score   Name");
    441 
    442 	for (i = 0; i < numscores; i++) {
    443 		if (i == rank) {
    444 			standout();
    445 		}
    446 
    447 		if (scores[i].nickname[0]) {
    448 			name = scores[i].nickname;
    449 		} else {
    450 			name = scores[i].username;
    451 		}
    452 
    453 		mvprintw(i+10, 0, "%2d    %6ld   %s: %s",
    454 			 i+1, scores[i].gold, name, scores[i].death);
    455 
    456 		if (i == rank) {
    457 			standend();
    458 		}
    459 	}
    460 	refresh();
    461 	messagef(0, "%s", "");		/* gcc objects to just "" */
    462 	if (dopause) {
    463 		messagef(0, "%s", "");
    464 	}
    465 	clean_up("");
    466 }
    467 
    468 static
    469 void
    470 make_score(se, monster, other)
    471 	struct score_entry *se;
    472 	const object *monster;
    473 	int other;
    474 {
    475 	const char *death = "bolts from the blue (?)";
    476 	const char *hasamulet;
    477 	char deathbuf[80];
    478 
    479 	se->gold = rogue.gold;
    480 	strlcpy(se->username, login_name, sizeof(se->username));
    481 
    482 	if (other) {
    483 		switch(other) {
    484 		case HYPOTHERMIA:
    485 			death = "died of hypothermia";
    486 			break;
    487 		case STARVATION:
    488 			death = "died of starvation";
    489 			break;
    490 		case POISON_DART:
    491 			death = "killed by a dart";
    492 			break;
    493 		case QUIT:
    494 			death = "quit";
    495 			break;
    496 		case WIN:
    497 			death = "a total winner";
    498 			break;
    499 		case KFIRE:
    500 			death = "killed by fire";
    501 			break;
    502 		}
    503 	} else {
    504 		const char *mn, *article;
    505 
    506 		mn = m_names[monster->m_char - 'A'];
    507 		if (is_vowel(mn[0])) {
    508 			article = "an";
    509 		} else {
    510 			article = "a";
    511 		}
    512 
    513 		snprintf(deathbuf, sizeof(deathbuf),
    514 			 "killed by %s %s", article, mn);
    515 		death = deathbuf;
    516 	}
    517 
    518 	if (other != WIN && has_amulet()) {
    519 		hasamulet = " with amulet";
    520 	} else {
    521 		hasamulet = "";
    522 	}
    523 
    524 	snprintf(se->death, sizeof(se->death), "%s on level %d%s",
    525 		 death, max_level, hasamulet);
    526 
    527 	strlcpy(se->nickname, nick_name, sizeof(se->nickname));
    528 }
    529 
    530 boolean
    531 is_vowel(ch)
    532 	short ch;
    533 {
    534 	return( (ch == 'a') ||
    535 		(ch == 'e') ||
    536 		(ch == 'i') ||
    537 		(ch == 'o') ||
    538 		(ch == 'u') );
    539 }
    540 
    541 void
    542 sell_pack()
    543 {
    544 	object *obj;
    545 	short row = 2, val;
    546 	char buf[DCOLS];
    547 
    548 	obj = rogue.pack.next_object;
    549 
    550 	clear();
    551 	mvaddstr(1, 0, "Value      Item");
    552 
    553 	while (obj) {
    554 		if (obj->what_is != FOOD) {
    555 			obj->identified = 1;
    556 			val = get_value(obj);
    557 			rogue.gold += val;
    558 
    559 			if (row < DROWS) {
    560 				get_desc(obj, buf, sizeof(buf));
    561 				mvprintw(row++, 0, "%5d      %s", val, buf);
    562 			}
    563 		}
    564 		obj = obj->next_object;
    565 	}
    566 	refresh();
    567 	if (rogue.gold > MAX_GOLD) {
    568 		rogue.gold = MAX_GOLD;
    569 	}
    570 	messagef(0, "%s", "");		/* gcc objects to just "" */
    571 }
    572 
    573 int
    574 get_value(obj)
    575 	const object *obj;
    576 {
    577 	short wc;
    578 	int val;
    579 
    580 	val = 0;
    581 	wc = obj->which_kind;
    582 
    583 	switch(obj->what_is) {
    584 	case WEAPON:
    585 		val = id_weapons[wc].value;
    586 		if ((wc == ARROW) || (wc == DAGGER) || (wc == SHURIKEN) ||
    587 			(wc == DART)) {
    588 			val *= obj->quantity;
    589 		}
    590 		val += (obj->d_enchant * 85);
    591 		val += (obj->hit_enchant * 85);
    592 		break;
    593 	case ARMOR:
    594 		val = id_armors[wc].value;
    595 		val += (obj->d_enchant * 75);
    596 		if (obj->is_protected) {
    597 			val += 200;
    598 		}
    599 		break;
    600 	case WAND:
    601 		val = id_wands[wc].value * (obj->class + 1);
    602 		break;
    603 	case SCROL:
    604 		val = id_scrolls[wc].value * obj->quantity;
    605 		break;
    606 	case POTION:
    607 		val = id_potions[wc].value * obj->quantity;
    608 		break;
    609 	case AMULET:
    610 		val = 5000;
    611 		break;
    612 	case RING:
    613 		val = id_rings[wc].value * (obj->class + 1);
    614 		break;
    615 	}
    616 	if (val <= 0) {
    617 		val = 10;
    618 	}
    619 	return(val);
    620 }
    621 
    622 void
    623 id_all()
    624 {
    625 	short i;
    626 
    627 	for (i = 0; i < SCROLS; i++) {
    628 		id_scrolls[i].id_status = IDENTIFIED;
    629 	}
    630 	for (i = 0; i < WEAPONS; i++) {
    631 		id_weapons[i].id_status = IDENTIFIED;
    632 	}
    633 	for (i = 0; i < ARMORS; i++) {
    634 		id_armors[i].id_status = IDENTIFIED;
    635 	}
    636 	for (i = 0; i < WANDS; i++) {
    637 		id_wands[i].id_status = IDENTIFIED;
    638 	}
    639 	for (i = 0; i < POTIONS; i++) {
    640 		id_potions[i].id_status = IDENTIFIED;
    641 	}
    642 }
    643 
    644 void
    645 xxxx(buf, n)
    646 	char *buf;
    647 	short n;
    648 {
    649 	short i;
    650 	unsigned char c;
    651 
    652 	for (i = 0; i < n; i++) {
    653 
    654 		/* It does not matter if accuracy is lost during this assignment */
    655 		c = (unsigned char)xxx(0);
    656 
    657 		buf[i] ^= c;
    658 	}
    659 }
    660 
    661 long
    662 xxx(st)
    663 	boolean st;
    664 {
    665 	static long f, s;
    666 	long r;
    667 
    668 	if (st) {
    669 		f = 37;
    670 		s = 7;
    671 		return(0L);
    672 	}
    673 	r = ((f * s) + 9337) % 8887;
    674 	f = s;
    675 	s = r;
    676 	return(r);
    677 }
    678 
    679 void
    680 center(row, buf)
    681 	short row;
    682 	const char *buf;
    683 {
    684 	short margin;
    685 
    686 	margin = ((DCOLS - strlen(buf)) / 2);
    687 	mvaddstr(row, margin, buf);
    688 }
    689 
    690 void
    691 sf_error()
    692 {
    693 	md_lock(0);
    694 	messagef(1, "%s", "");		/* gcc objects to just "" */
    695 	clean_up("sorry, score file is out of order");
    696 }
    697