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