xref: /netbsd-src/games/rogue/pack.c (revision 96230fab84e26a6435963032070e916a951a8b2e)
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