Home | History | Annotate | Line # | Download | only in rogue
      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