Home | History | Annotate | Line # | Download | only in rogue
pack.c revision 1.10
      1 /*	$NetBSD: pack.c,v 1.10 2008/01/14 03:50:02 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[] = "@(#)pack.c	8.1 (Berkeley) 5/31/93";
     39 #else
     40 __RCSID("$NetBSD: pack.c,v 1.10 2008/01/14 03:50:02 dholland Exp $");
     41 #endif
     42 #endif /* not lint */
     43 
     44 /*
     45  * pack.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 "rogue.h"
     57 
     58 const char *curse_message = "you can't, it appears to be cursed";
     59 
     60 object *
     61 add_to_pack(object *obj, object *pack, int condense)
     62 {
     63 	object *op;
     64 
     65 	if (condense) {
     66 		if ((op = check_duplicate(obj, pack)) != NULL) {
     67 			free_object(obj);
     68 			return(op);
     69 		} else {
     70 			obj->ichar = next_avail_ichar();
     71 		}
     72 	}
     73 	if (pack->next_object == 0) {
     74 		pack->next_object = obj;
     75 	} else {
     76 		op = pack->next_object;
     77 
     78 		while (op->next_object) {
     79 			op = op->next_object;
     80 		}
     81 		op->next_object = obj;
     82 	}
     83 	obj->next_object = 0;
     84 	return(obj);
     85 }
     86 
     87 void
     88 take_from_pack(object *obj, object *pack)
     89 {
     90 	while (pack->next_object != obj) {
     91 		pack = pack->next_object;
     92 	}
     93 	pack->next_object = pack->next_object->next_object;
     94 }
     95 
     96 /* Note: *status is set to 0 if the rogue attempts to pick up a scroll
     97  * of scare-monster and it turns to dust.  *status is otherwise set to 1.
     98  */
     99 
    100 object *
    101 pick_up(int row, int col, short *status)
    102 {
    103 	object *obj;
    104 
    105 	*status = 1;
    106 
    107 	if (levitate) {
    108 		messagef(0, "you're floating in the air!");
    109 		return NULL;
    110 	}
    111 	obj = object_at(&level_objects, row, col);
    112 	if (!obj) {
    113 		messagef(1, "pick_up(): inconsistent");
    114 		return(obj);
    115 	}
    116 	if (	(obj->what_is == SCROL) &&
    117 			(obj->which_kind == SCARE_MONSTER) &&
    118 			obj->picked_up) {
    119 		messagef(0, "the scroll turns to dust as you pick it up");
    120 		dungeon[row][col] &= (~OBJECT);
    121 		vanish(obj, 0, &level_objects);
    122 		*status = 0;
    123 		if (id_scrolls[SCARE_MONSTER].id_status == UNIDENTIFIED) {
    124 			id_scrolls[SCARE_MONSTER].id_status = IDENTIFIED;
    125 		}
    126 		return NULL;
    127 	}
    128 	if (obj->what_is == GOLD) {
    129 		rogue.gold += obj->quantity;
    130 		dungeon[row][col] &= ~(OBJECT);
    131 		take_from_pack(obj, &level_objects);
    132 		print_stats(STAT_GOLD);
    133 		return(obj);	/* obj will be free_object()ed in caller */
    134 	}
    135 	if (pack_count(obj) >= MAX_PACK_COUNT) {
    136 		messagef(1, "pack too full");
    137 		return NULL;
    138 	}
    139 	dungeon[row][col] &= ~(OBJECT);
    140 	take_from_pack(obj, &level_objects);
    141 	obj = add_to_pack(obj, &rogue.pack, 1);
    142 	obj->picked_up = 1;
    143 	return(obj);
    144 }
    145 
    146 void
    147 drop(void)
    148 {
    149 	object *obj, *new;
    150 	short ch;
    151 	char desc[DCOLS];
    152 
    153 	if (dungeon[rogue.row][rogue.col] & (OBJECT | STAIRS | TRAP)) {
    154 		messagef(0, "there's already something there");
    155 		return;
    156 	}
    157 	if (!rogue.pack.next_object) {
    158 		messagef(0, "you have nothing to drop");
    159 		return;
    160 	}
    161 	if ((ch = pack_letter("drop what?", ALL_OBJECTS)) == CANCEL) {
    162 		return;
    163 	}
    164 	if (!(obj = get_letter_object(ch))) {
    165 		messagef(0, "no such item.");
    166 		return;
    167 	}
    168 	if (obj->in_use_flags & BEING_WIELDED) {
    169 		if (obj->is_cursed) {
    170 			messagef(0, "%s", curse_message);
    171 			return;
    172 		}
    173 		unwield(rogue.weapon);
    174 	} else if (obj->in_use_flags & BEING_WORN) {
    175 		if (obj->is_cursed) {
    176 			messagef(0, "%s", curse_message);
    177 			return;
    178 		}
    179 		mv_aquatars();
    180 		unwear(rogue.armor);
    181 		print_stats(STAT_ARMOR);
    182 	} else if (obj->in_use_flags & ON_EITHER_HAND) {
    183 		if (obj->is_cursed) {
    184 			messagef(0, "%s", curse_message);
    185 			return;
    186 		}
    187 		un_put_on(obj);
    188 	}
    189 	obj->row = rogue.row;
    190 	obj->col = rogue.col;
    191 
    192 	if ((obj->quantity > 1) && (obj->what_is != WEAPON)) {
    193 		obj->quantity--;
    194 		new = alloc_object();
    195 		*new = *obj;
    196 		new->quantity = 1;
    197 		obj = new;
    198 	} else {
    199 		obj->ichar = 'L';
    200 		take_from_pack(obj, &rogue.pack);
    201 	}
    202 	place_at(obj, rogue.row, rogue.col);
    203 	get_desc(obj, desc, sizeof(desc));
    204 	messagef(0, "dropped %s", desc);
    205 	(void)reg_move();
    206 }
    207 
    208 object *
    209 check_duplicate(object *obj, object *pack)
    210 {
    211 	object *op;
    212 
    213 	if (!(obj->what_is & (WEAPON | FOOD | SCROL | POTION))) {
    214 		return(0);
    215 	}
    216 	if ((obj->what_is == FOOD) && (obj->which_kind == FRUIT)) {
    217 		return(0);
    218 	}
    219 	op = pack->next_object;
    220 
    221 	while (op) {
    222 		if ((op->what_is == obj->what_is) &&
    223 			(op->which_kind == obj->which_kind)) {
    224 
    225 			if ((obj->what_is != WEAPON) ||
    226 			((obj->what_is == WEAPON) &&
    227 			((obj->which_kind == ARROW) ||
    228 			(obj->which_kind == DAGGER) ||
    229 			(obj->which_kind == DART) ||
    230 			(obj->which_kind == SHURIKEN)) &&
    231 			(obj->quiver == op->quiver))) {
    232 				op->quantity += obj->quantity;
    233 				return(op);
    234 			}
    235 		}
    236 		op = op->next_object;
    237 	}
    238 	return(0);
    239 }
    240 
    241 short
    242 next_avail_ichar(void)
    243 {
    244 	object *obj;
    245 	int i;
    246 	boolean ichars[26];
    247 
    248 	for (i = 0; i < 26; i++) {
    249 		ichars[i] = 0;
    250 	}
    251 	obj = rogue.pack.next_object;
    252 	while (obj) {
    253 		if (obj->ichar >= 'a' && obj->ichar <= 'z') {
    254 			ichars[(obj->ichar - 'a')] = 1;
    255 		}
    256 		obj = obj->next_object;
    257 	}
    258 	for (i = 0; i < 26; i++) {
    259 		if (!ichars[i]) {
    260 			return(i + 'a');
    261 		}
    262 	}
    263 	return('?');
    264 }
    265 
    266 void
    267 wait_for_ack(void)
    268 {
    269 	while (rgetchar() != ' ')
    270 		;
    271 }
    272 
    273 short
    274 pack_letter(const char *prompt, unsigned short mask)
    275 {
    276 	short ch;
    277 	unsigned short tmask = mask;
    278 
    279 	if (!mask_pack(&rogue.pack, mask)) {
    280 		messagef(0, "nothing appropriate");
    281 		return(CANCEL);
    282 	}
    283 	for (;;) {
    284 
    285 		messagef(0, "%s", prompt);
    286 
    287 		for (;;) {
    288 			ch = rgetchar();
    289 			if (!is_pack_letter(&ch, &mask)) {
    290 				sound_bell();
    291 			} else {
    292 				break;
    293 			}
    294 		}
    295 
    296 		if (ch == LIST) {
    297 			check_message();
    298 			mask = tmask;
    299 			inventory(&rogue.pack, mask);
    300 		} else {
    301 			break;
    302 		}
    303 		mask = tmask;
    304 	}
    305 	check_message();
    306 	return(ch);
    307 }
    308 
    309 void
    310 take_off(void)
    311 {
    312 	char desc[DCOLS];
    313 	object *obj;
    314 
    315 	if (rogue.armor) {
    316 		if (rogue.armor->is_cursed) {
    317 			messagef(0, "%s", curse_message);
    318 		} else {
    319 			mv_aquatars();
    320 			obj = rogue.armor;
    321 			unwear(rogue.armor);
    322 			get_desc(obj, desc, sizeof(desc));
    323 			messagef(0, "was wearing %s", desc);
    324 			print_stats(STAT_ARMOR);
    325 			(void)reg_move();
    326 		}
    327 	} else {
    328 		messagef(0, "not wearing any");
    329 	}
    330 }
    331 
    332 void
    333 wear(void)
    334 {
    335 	short ch;
    336 	object *obj;
    337 	char desc[DCOLS];
    338 
    339 	if (rogue.armor) {
    340 		messagef(0, "you're already wearing some");
    341 		return;
    342 	}
    343 	ch = pack_letter("wear what?", ARMOR);
    344 
    345 	if (ch == CANCEL) {
    346 		return;
    347 	}
    348 	if (!(obj = get_letter_object(ch))) {
    349 		messagef(0, "no such item.");
    350 		return;
    351 	}
    352 	if (obj->what_is != ARMOR) {
    353 		messagef(0, "you can't wear that");
    354 		return;
    355 	}
    356 	obj->identified = 1;
    357 	get_desc(obj, desc, sizeof(desc));
    358 	messagef(0, "wearing %s", desc);
    359 	do_wear(obj);
    360 	print_stats(STAT_ARMOR);
    361 	(void)reg_move();
    362 }
    363 
    364 void
    365 unwear(object *obj)
    366 {
    367 	if (obj) {
    368 		obj->in_use_flags &= (~BEING_WORN);
    369 	}
    370 	rogue.armor = NULL;
    371 }
    372 
    373 void
    374 do_wear(object *obj)
    375 {
    376 	rogue.armor = obj;
    377 	obj->in_use_flags |= BEING_WORN;
    378 	obj->identified = 1;
    379 }
    380 
    381 void
    382 wield(void)
    383 {
    384 	short ch;
    385 	object *obj;
    386 	char desc[DCOLS];
    387 
    388 	if (rogue.weapon && rogue.weapon->is_cursed) {
    389 		messagef(0, "%s", curse_message);
    390 		return;
    391 	}
    392 	ch = pack_letter("wield what?", WEAPON);
    393 
    394 	if (ch == CANCEL) {
    395 		return;
    396 	}
    397 	if (!(obj = get_letter_object(ch))) {
    398 		messagef(0, "No such item.");
    399 		return;
    400 	}
    401 	if (obj->what_is & (ARMOR | RING)) {
    402 		messagef(0, "you can't wield %s",
    403 			((obj->what_is == ARMOR) ? "armor" : "rings"));
    404 		return;
    405 	}
    406 	if (obj->in_use_flags & BEING_WIELDED) {
    407 		messagef(0, "in use");
    408 	} else {
    409 		unwield(rogue.weapon);
    410 		get_desc(obj, desc, sizeof(desc));
    411 		messagef(0, "wielding %s", desc);
    412 		do_wield(obj);
    413 		(void)reg_move();
    414 	}
    415 }
    416 
    417 void
    418 do_wield(object *obj)
    419 {
    420 	rogue.weapon = obj;
    421 	obj->in_use_flags |= BEING_WIELDED;
    422 }
    423 
    424 void
    425 unwield(object *obj)
    426 {
    427 	if (obj) {
    428 		obj->in_use_flags &= (~BEING_WIELDED);
    429 	}
    430 	rogue.weapon = NULL;
    431 }
    432 
    433 void
    434 call_it(void)
    435 {
    436 	short ch;
    437 	object *obj;
    438 	struct id *id_table;
    439 	char buf[MAX_TITLE_LENGTH+2];
    440 
    441 	ch = pack_letter("call what?", (SCROL | POTION | WAND | RING));
    442 
    443 	if (ch == CANCEL) {
    444 		return;
    445 	}
    446 	if (!(obj = get_letter_object(ch))) {
    447 		messagef(0, "no such item.");
    448 		return;
    449 	}
    450 	if (!(obj->what_is & (SCROL | POTION | WAND | RING))) {
    451 		messagef(0, "surely you already know what that's called");
    452 		return;
    453 	}
    454 	id_table = get_id_table(obj);
    455 
    456 	if (get_input_line("call it:", "", buf, sizeof(buf),
    457 			id_table[obj->which_kind].title, 1, 1)) {
    458 		id_table[obj->which_kind].id_status = CALLED;
    459 		(void)strlcpy(id_table[obj->which_kind].title, buf,
    460 				sizeof(id_table[obj->which_kind].title));
    461 	}
    462 }
    463 
    464 short
    465 pack_count(const object *new_obj)
    466 {
    467 	object *obj;
    468 	short count = 0;
    469 
    470 	obj = rogue.pack.next_object;
    471 
    472 	while (obj) {
    473 		if (obj->what_is != WEAPON) {
    474 			count += obj->quantity;
    475 		} else if (!new_obj) {
    476 			count++;
    477 		} else if ((new_obj->what_is != WEAPON) ||
    478 			((obj->which_kind != ARROW) &&
    479 			(obj->which_kind != DAGGER) &&
    480 			(obj->which_kind != DART) &&
    481 			(obj->which_kind != SHURIKEN)) ||
    482 			(new_obj->which_kind != obj->which_kind) ||
    483 			(obj->quiver != new_obj->quiver)) {
    484 			count++;
    485 		}
    486 		obj = obj->next_object;
    487 	}
    488 	return(count);
    489 }
    490 
    491 boolean
    492 mask_pack(const object *pack, unsigned short mask)
    493 {
    494 	while (pack->next_object) {
    495 		pack = pack->next_object;
    496 		if (pack->what_is & mask) {
    497 			return(1);
    498 		}
    499 	}
    500 	return(0);
    501 }
    502 
    503 boolean
    504 is_pack_letter(short *c, unsigned short *mask)
    505 {
    506 	if (((*c == '?') || (*c == '!') || (*c == ':') || (*c == '=') ||
    507 		(*c == ')') || (*c == ']') || (*c == '/') || (*c == ','))) {
    508 		switch(*c) {
    509 		case '?':
    510 			*mask = SCROL;
    511 			break;
    512 		case '!':
    513 			*mask = POTION;
    514 			break;
    515 		case ':':
    516 			*mask = FOOD;
    517 			break;
    518 		case ')':
    519 			*mask = WEAPON;
    520 			break;
    521 		case ']':
    522 			*mask = ARMOR;
    523 			break;
    524 		case '/':
    525 			*mask = WAND;
    526 			break;
    527 		case '=':
    528 			*mask = RING;
    529 			break;
    530 		case ',':
    531 			*mask = AMULET;
    532 			break;
    533 		}
    534 		*c = LIST;
    535 		return(1);
    536 	}
    537 	return(((*c >= 'a') && (*c <= 'z')) || (*c == CANCEL) || (*c == LIST));
    538 }
    539 
    540 boolean
    541 has_amulet(void)
    542 {
    543 	return(mask_pack(&rogue.pack, AMULET));
    544 }
    545 
    546 void
    547 kick_into_pack(void)
    548 {
    549 	object *obj;
    550 	char desc[DCOLS];
    551 	short stat;
    552 
    553 	if (!(dungeon[rogue.row][rogue.col] & OBJECT)) {
    554 		messagef(0, "nothing here");
    555 	} else {
    556 		if ((obj = pick_up(rogue.row, rogue.col, &stat)) != NULL) {
    557 			get_desc(obj, desc, sizeof(desc));
    558 			if (obj->what_is == GOLD) {
    559 				messagef(0, "%s", desc);
    560 				free_object(obj);
    561 			} else {
    562 				messagef(0, "%s(%c)", desc, obj->ichar);
    563 			}
    564 		}
    565 		if (obj || (!stat)) {
    566 			(void)reg_move();
    567 		}
    568 	}
    569 }
    570