xref: /netbsd/games/rogue/pack.c (revision bf9ec67e)
1 /*	$NetBSD: pack.c,v 1.6 1998/11/10 13:01:32 hubertf 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. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #include <sys/cdefs.h>
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)pack.c	8.1 (Berkeley) 5/31/93";
43 #else
44 __RCSID("$NetBSD: pack.c,v 1.6 1998/11/10 13:01:32 hubertf Exp $");
45 #endif
46 #endif /* not lint */
47 
48 /*
49  * pack.c
50  *
51  * This source herein may be modified and/or distributed by anybody who
52  * so desires, with the following restrictions:
53  *    1.)  No portion of this notice shall be removed.
54  *    2.)  Credit shall not be taken for the creation of this source.
55  *    3.)  This code is not to be traded, sold, or used for personal
56  *         gain or profit.
57  *
58  */
59 
60 #include "rogue.h"
61 
62 const char *curse_message = "you can't, it appears to be cursed";
63 
64 object *
65 add_to_pack(obj, pack, condense)
66 	object *obj, *pack;
67 	int condense;
68 {
69 	object *op;
70 
71 	if (condense) {
72 		if ((op = check_duplicate(obj, pack)) != NULL) {
73 			free_object(obj);
74 			return(op);
75 		} else {
76 			obj->ichar = next_avail_ichar();
77 		}
78 	}
79 	if (pack->next_object == 0) {
80 		pack->next_object = obj;
81 	} else {
82 		op = pack->next_object;
83 
84 		while (op->next_object) {
85 			op = op->next_object;
86 		}
87 		op->next_object = obj;
88 	}
89 	obj->next_object = 0;
90 	return(obj);
91 }
92 
93 void
94 take_from_pack(obj, pack)
95 	object *obj, *pack;
96 {
97 	while (pack->next_object != obj) {
98 		pack = pack->next_object;
99 	}
100 	pack->next_object = pack->next_object->next_object;
101 }
102 
103 /* Note: *status is set to 0 if the rogue attempts to pick up a scroll
104  * of scare-monster and it turns to dust.  *status is otherwise set to 1.
105  */
106 
107 object *
108 pick_up(row, col, status)
109 	short *status;
110 	int row, col;
111 {
112 	object *obj;
113 
114 	*status = 1;
115 
116 	if (levitate) {
117 		message("you're floating in the air!", 0);
118 		return((object *) 0);
119 	}
120 	obj = object_at(&level_objects, row, col);
121 	if (!obj) {
122 		message("pick_up(): inconsistent", 1);
123 		return(obj);
124 	}
125 	if (	(obj->what_is == SCROL) &&
126 			(obj->which_kind == SCARE_MONSTER) &&
127 			obj->picked_up) {
128 		message("the scroll turns to dust as you pick it up", 0);
129 		dungeon[row][col] &= (~OBJECT);
130 		vanish(obj, 0, &level_objects);
131 		*status = 0;
132 		if (id_scrolls[SCARE_MONSTER].id_status == UNIDENTIFIED) {
133 			id_scrolls[SCARE_MONSTER].id_status = IDENTIFIED;
134 		}
135 		return((object *) 0);
136 	}
137 	if (obj->what_is == GOLD) {
138 		rogue.gold += obj->quantity;
139 		dungeon[row][col] &= ~(OBJECT);
140 		take_from_pack(obj, &level_objects);
141 		print_stats(STAT_GOLD);
142 		return(obj);	/* obj will be free_object()ed in caller */
143 	}
144 	if (pack_count(obj) >= MAX_PACK_COUNT) {
145 		message("pack too full", 1);
146 		return((object *) 0);
147 	}
148 	dungeon[row][col] &= ~(OBJECT);
149 	take_from_pack(obj, &level_objects);
150 	obj = add_to_pack(obj, &rogue.pack, 1);
151 	obj->picked_up = 1;
152 	return(obj);
153 }
154 
155 void
156 drop()
157 {
158 	object *obj, *new;
159 	short ch;
160 	char desc[DCOLS];
161 
162 	if (dungeon[rogue.row][rogue.col] & (OBJECT | STAIRS | TRAP)) {
163 		message("there's already something there", 0);
164 		return;
165 	}
166 	if (!rogue.pack.next_object) {
167 		message("you have nothing to drop", 0);
168 		return;
169 	}
170 	if ((ch = pack_letter("drop what?", ALL_OBJECTS)) == CANCEL) {
171 		return;
172 	}
173 	if (!(obj = get_letter_object(ch))) {
174 		message("no such item.", 0);
175 		return;
176 	}
177 	if (obj->in_use_flags & BEING_WIELDED) {
178 		if (obj->is_cursed) {
179 			message(curse_message, 0);
180 			return;
181 		}
182 		unwield(rogue.weapon);
183 	} else if (obj->in_use_flags & BEING_WORN) {
184 		if (obj->is_cursed) {
185 			message(curse_message, 0);
186 			return;
187 		}
188 		mv_aquatars();
189 		unwear(rogue.armor);
190 		print_stats(STAT_ARMOR);
191 	} else if (obj->in_use_flags & ON_EITHER_HAND) {
192 		if (obj->is_cursed) {
193 			message(curse_message, 0);
194 			return;
195 		}
196 		un_put_on(obj);
197 	}
198 	obj->row = rogue.row;
199 	obj->col = rogue.col;
200 
201 	if ((obj->quantity > 1) && (obj->what_is != WEAPON)) {
202 		obj->quantity--;
203 		new = alloc_object();
204 		*new = *obj;
205 		new->quantity = 1;
206 		obj = new;
207 	} else {
208 		obj->ichar = 'L';
209 		take_from_pack(obj, &rogue.pack);
210 	}
211 	place_at(obj, rogue.row, rogue.col);
212 	(void) strcpy(desc, "dropped ");
213 	get_desc(obj, desc+8);
214 	message(desc, 0);
215 	(void) reg_move();
216 }
217 
218 object *
219 check_duplicate(obj, pack)
220 	object *obj, *pack;
221 {
222 	object *op;
223 
224 	if (!(obj->what_is & (WEAPON | FOOD | SCROL | POTION))) {
225 		return(0);
226 	}
227 	if ((obj->what_is == FOOD) && (obj->which_kind == FRUIT)) {
228 		return(0);
229 	}
230 	op = pack->next_object;
231 
232 	while (op) {
233 		if ((op->what_is == obj->what_is) &&
234 			(op->which_kind == obj->which_kind)) {
235 
236 			if ((obj->what_is != WEAPON) ||
237 			((obj->what_is == WEAPON) &&
238 			((obj->which_kind == ARROW) ||
239 			(obj->which_kind == DAGGER) ||
240 			(obj->which_kind == DART) ||
241 			(obj->which_kind == SHURIKEN)) &&
242 			(obj->quiver == op->quiver))) {
243 				op->quantity += obj->quantity;
244 				return(op);
245 			}
246 		}
247 		op = op->next_object;
248 	}
249 	return(0);
250 }
251 
252 short
253 next_avail_ichar()
254 {
255 	object *obj;
256 	int i;
257 	boolean ichars[26];
258 
259 	for (i = 0; i < 26; i++) {
260 		ichars[i] = 0;
261 	}
262 	obj = rogue.pack.next_object;
263 	while (obj) {
264 		ichars[(obj->ichar - 'a')] = 1;
265 		obj = obj->next_object;
266 	}
267 	for (i = 0; i < 26; i++) {
268 		if (!ichars[i]) {
269 			return(i + 'a');
270 		}
271 	}
272 	return('?');
273 }
274 
275 void
276 wait_for_ack()
277 {
278 	while (rgetchar() != ' ') ;
279 }
280 
281 short
282 pack_letter(prompt, mask)
283 	const char *prompt;
284 	unsigned short mask;
285 {
286 	short ch;
287 	unsigned short tmask = mask;
288 
289 	if (!mask_pack(&rogue.pack, mask)) {
290 		message("nothing appropriate", 0);
291 		return(CANCEL);
292 	}
293 	for (;;) {
294 
295 		message(prompt, 0);
296 
297 		for (;;) {
298 			ch = rgetchar();
299 			if (!is_pack_letter(&ch, &mask)) {
300 				sound_bell();
301 			} else {
302 				break;
303 			}
304 		}
305 
306 		if (ch == LIST) {
307 			check_message();
308 			mask = tmask;
309 			inventory(&rogue.pack, mask);
310 		} else {
311 			break;
312 		}
313 		mask = tmask;
314 	}
315 	check_message();
316 	return(ch);
317 }
318 
319 void
320 take_off()
321 {
322 	char desc[DCOLS];
323 	object *obj;
324 
325 	if (rogue.armor) {
326 		if (rogue.armor->is_cursed) {
327 			message(curse_message, 0);
328 		} else {
329 			mv_aquatars();
330 			obj = rogue.armor;
331 			unwear(rogue.armor);
332 			(void) strcpy(desc, "was wearing ");
333 			get_desc(obj, desc+12);
334 			message(desc, 0);
335 			print_stats(STAT_ARMOR);
336 			(void) reg_move();
337 		}
338 	} else {
339 		message("not wearing any", 0);
340 	}
341 }
342 
343 void
344 wear()
345 {
346 	short ch;
347 	object *obj;
348 	char desc[DCOLS];
349 
350 	if (rogue.armor) {
351 		message("your already wearing some", 0);
352 		return;
353 	}
354 	ch = pack_letter("wear what?", ARMOR);
355 
356 	if (ch == CANCEL) {
357 		return;
358 	}
359 	if (!(obj = get_letter_object(ch))) {
360 		message("no such item.", 0);
361 		return;
362 	}
363 	if (obj->what_is != ARMOR) {
364 		message("you can't wear that", 0);
365 		return;
366 	}
367 	obj->identified = 1;
368 	(void) strcpy(desc, "wearing ");
369 	get_desc(obj, desc + 8);
370 	message(desc, 0);
371 	do_wear(obj);
372 	print_stats(STAT_ARMOR);
373 	(void) reg_move();
374 }
375 
376 void
377 unwear(obj)
378 	object *obj;
379 {
380 	if (obj) {
381 		obj->in_use_flags &= (~BEING_WORN);
382 	}
383 	rogue.armor = (object *) 0;
384 }
385 
386 void
387 do_wear(obj)
388 	object *obj;
389 {
390 	rogue.armor = obj;
391 	obj->in_use_flags |= BEING_WORN;
392 	obj->identified = 1;
393 }
394 
395 void
396 wield()
397 {
398 	short ch;
399 	object *obj;
400 	char desc[DCOLS];
401 
402 	if (rogue.weapon && rogue.weapon->is_cursed) {
403 		message(curse_message, 0);
404 		return;
405 	}
406 	ch = pack_letter("wield what?", WEAPON);
407 
408 	if (ch == CANCEL) {
409 		return;
410 	}
411 	if (!(obj = get_letter_object(ch))) {
412 		message("No such item.", 0);
413 		return;
414 	}
415 	if (obj->what_is & (ARMOR | RING)) {
416 		sprintf(desc, "you can't wield %s",
417 			((obj->what_is == ARMOR) ? "armor" : "rings"));
418 		message(desc, 0);
419 		return;
420 	}
421 	if (obj->in_use_flags & BEING_WIELDED) {
422 		message("in use", 0);
423 	} else {
424 		unwield(rogue.weapon);
425 		(void) strcpy(desc, "wielding ");
426 		get_desc(obj, desc + 9);
427 		message(desc, 0);
428 		do_wield(obj);
429 		(void) reg_move();
430 	}
431 }
432 
433 void
434 do_wield(obj)
435 	object *obj;
436 {
437 	rogue.weapon = obj;
438 	obj->in_use_flags |= BEING_WIELDED;
439 }
440 
441 void
442 unwield(obj)
443 	object *obj;
444 {
445 	if (obj) {
446 		obj->in_use_flags &= (~BEING_WIELDED);
447 	}
448 	rogue.weapon = (object *) 0;
449 }
450 
451 void
452 call_it()
453 {
454 	short ch;
455 	object *obj;
456 	struct id *id_table;
457 	char buf[MAX_TITLE_LENGTH+2];
458 
459 	ch = pack_letter("call what?", (SCROL | POTION | WAND | RING));
460 
461 	if (ch == CANCEL) {
462 		return;
463 	}
464 	if (!(obj = get_letter_object(ch))) {
465 		message("no such item.", 0);
466 		return;
467 	}
468 	if (!(obj->what_is & (SCROL | POTION | WAND | RING))) {
469 		message("surely you already know what that's called", 0);
470 		return;
471 	}
472 	id_table = get_id_table(obj);
473 
474 	if (get_input_line("call it:","",buf,id_table[obj->which_kind].title,1,1)) {
475 		id_table[obj->which_kind].id_status = CALLED;
476 		(void) strcpy(id_table[obj->which_kind].title, buf);
477 	}
478 }
479 
480 short
481 pack_count(new_obj)
482 	const object *new_obj;
483 {
484 	object *obj;
485 	short count = 0;
486 
487 	obj = rogue.pack.next_object;
488 
489 	while (obj) {
490 		if (obj->what_is != WEAPON) {
491 			count += obj->quantity;
492 		} else if (!new_obj) {
493 			count++;
494 		} else if ((new_obj->what_is != WEAPON) ||
495 			((obj->which_kind != ARROW) &&
496 			(obj->which_kind != DAGGER) &&
497 			(obj->which_kind != DART) &&
498 			(obj->which_kind != SHURIKEN)) ||
499 			(new_obj->which_kind != obj->which_kind) ||
500 			(obj->quiver != new_obj->quiver)) {
501 			count++;
502 		}
503 		obj = obj->next_object;
504 	}
505 	return(count);
506 }
507 
508 boolean
509 mask_pack(pack, mask)
510 	const object *pack;
511 	unsigned short mask;
512 {
513 	while (pack->next_object) {
514 		pack = pack->next_object;
515 		if (pack->what_is & mask) {
516 			return(1);
517 		}
518 	}
519 	return(0);
520 }
521 
522 boolean
523 is_pack_letter(c, mask)
524 	short *c;
525 	unsigned short *mask;
526 {
527 	if (((*c == '?') || (*c == '!') || (*c == ':') || (*c == '=') ||
528 		(*c == ')') || (*c == ']') || (*c == '/') || (*c == ','))) {
529 		switch(*c) {
530 		case '?':
531 			*mask = SCROL;
532 			break;
533 		case '!':
534 			*mask = POTION;
535 			break;
536 		case ':':
537 			*mask = FOOD;
538 			break;
539 		case ')':
540 			*mask = WEAPON;
541 			break;
542 		case ']':
543 			*mask = ARMOR;
544 			break;
545 		case '/':
546 			*mask = WAND;
547 			break;
548 		case '=':
549 			*mask = RING;
550 			break;
551 		case ',':
552 			*mask = AMULET;
553 			break;
554 		}
555 		*c = LIST;
556 		return(1);
557 	}
558 	return(((*c >= 'a') && (*c <= 'z')) || (*c == CANCEL) || (*c == LIST));
559 }
560 
561 boolean
562 has_amulet()
563 {
564 	return(mask_pack(&rogue.pack, AMULET));
565 }
566 
567 void
568 kick_into_pack()
569 {
570 	object *obj;
571 	char desc[DCOLS];
572 	short n, stat;
573 
574 	if (!(dungeon[rogue.row][rogue.col] & OBJECT)) {
575 		message("nothing here", 0);
576 	} else {
577 		if ((obj = pick_up(rogue.row, rogue.col, &stat)) != NULL) {
578 			get_desc(obj, desc);
579 			if (obj->what_is == GOLD) {
580 				message(desc, 0);
581 				free_object(obj);
582 			} else {
583 				n = strlen(desc);
584 				desc[n] = '(';
585 				desc[n+1] = obj->ichar;
586 				desc[n+2] = ')';
587 				desc[n+3] = 0;
588 				message(desc, 0);
589 			}
590 		}
591 		if (obj || (!stat)) {
592 			(void) reg_move();
593 		}
594 	}
595 }
596