xref: /dragonfly/games/rogue/pack.c (revision 9ddb8543)
1 /*
2  * Copyright (c) 1988, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Timothy C. Stoehr.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * @(#)pack.c	8.1 (Berkeley) 5/31/93
37  * $FreeBSD: src/games/rogue/pack.c,v 1.8 1999/11/30 03:49:25 billf Exp $
38  * $DragonFly: src/games/rogue/pack.c,v 1.4 2006/09/02 19:31:07 pavalos Exp $
39  */
40 
41 /*
42  * pack.c
43  *
44  * This source herein may be modified and/or distributed by anybody who
45  * so desires, with the following restrictions:
46  *    1.)  No portion of this notice shall be removed.
47  *    2.)  Credit shall not be taken for the creation of this source.
48  *    3.)  This code is not to be traded, sold, or used for personal
49  *         gain or profit.
50  *
51  */
52 
53 #include <unistd.h>
54 #include "rogue.h"
55 
56 const char *curse_message = "you can't, it appears to be cursed";
57 
58 extern short levitate;
59 
60 static object	*check_duplicate(object *, object *);
61 static int	next_avail_ichar(void);
62 static boolean	mask_pack(const object *, unsigned short);
63 static boolean	is_pack_letter(short *, unsigned short *);
64 
65 object *
66 add_to_pack(object *obj, object *pack, int condense)
67 {
68 	object *op;
69 
70 	if (condense) {
71 		if ((op = check_duplicate(obj, pack))) {
72 			free_object(obj);
73 			return(op);
74 		} else {
75 			obj->ichar = next_avail_ichar();
76 		}
77 	}
78 	if (pack->next_object == 0) {
79 		pack->next_object = obj;
80 	} else {
81 		op = pack->next_object;
82 
83 		while (op->next_object) {
84 			op = op->next_object;
85 		}
86 		op->next_object = obj;
87 	}
88 	obj->next_object = 0;
89 	return(obj);
90 }
91 
92 void
93 take_from_pack(object *obj, object *pack)
94 {
95 	while (pack->next_object != obj) {
96 		pack = pack->next_object;
97 	}
98 	pack->next_object = pack->next_object->next_object;
99 }
100 
101 /* Note: *status is set to 0 if the rogue attempts to pick up a scroll
102  * of scare-monster and it turns to dust.  *status is otherwise set to 1.
103  */
104 
105 object *
106 pick_up(int row, int col, short *status)
107 {
108 	object *obj;
109 
110 	*status = 1;
111 
112 	if (levitate) {
113 		message("you're floating in the air!", 0);
114 		return(NULL);
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(NULL);
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(NULL);
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(void)
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 	strcpy(desc, "dropped ");
209 	get_desc(obj, desc+8);
210 	message(desc, 0);
211 	reg_move();
212 }
213 
214 static object *
215 check_duplicate(object *obj, object *pack)
216 {
217 	object *op;
218 
219 	if (!(obj->what_is & (WEAPON | FOOD | SCROL | POTION))) {
220 		return(0);
221 	}
222 	if ((obj->what_is == FOOD) && (obj->which_kind == FRUIT)) {
223 		return(0);
224 	}
225 	op = pack->next_object;
226 
227 	while (op) {
228 		if ((op->what_is == obj->what_is) &&
229 			(op->which_kind == obj->which_kind)) {
230 
231 			if ((obj->what_is != WEAPON) ||
232 			((obj->what_is == WEAPON) &&
233 			((obj->which_kind == ARROW) ||
234 			(obj->which_kind == DAGGER) ||
235 			(obj->which_kind == DART) ||
236 			(obj->which_kind == SHURIKEN)) &&
237 			(obj->quiver == op->quiver))) {
238 				op->quantity += obj->quantity;
239 				return(op);
240 			}
241 		}
242 		op = op->next_object;
243 	}
244 	return(0);
245 }
246 
247 static int
248 next_avail_ichar(void)
249 {
250 	object *obj;
251 	int i;
252 	boolean ichars[26];
253 
254 	for (i = 0; i < 26; i++) {
255 		ichars[i] = 0;
256 	}
257 	obj = rogue.pack.next_object;
258 	while (obj) {
259 		ichars[(obj->ichar - 'a')] = 1;
260 		obj = obj->next_object;
261 	}
262 	for (i = 0; i < 26; i++) {
263 		if (!ichars[i]) {
264 			return(i + 'a');
265 		}
266 	}
267 	return('?');
268 }
269 
270 void
271 wait_for_ack(void)
272 {
273 	if (!isatty(0) || !isatty(1))
274 	    return;
275 	while (rgetchar() != ' ') ;
276 }
277 
278 short
279 pack_letter(const char *prompt, unsigned short mask)
280 {
281 	short ch;
282 	unsigned short tmask = mask;
283 
284 	if (!mask_pack(&rogue.pack, mask)) {
285 		message("nothing appropriate", 0);
286 		return(CANCEL);
287 	}
288 	for (;;) {
289 
290 		message(prompt, 0);
291 
292 		for (;;) {
293 			ch = rgetchar();
294 			if (!is_pack_letter(&ch, &mask)) {
295 				sound_bell();
296 			} else {
297 				break;
298 			}
299 		}
300 
301 		if (ch == LIST) {
302 			check_message();
303 			mask = tmask;
304 			inventory(&rogue.pack, mask);
305 		} else {
306 			break;
307 		}
308 		mask = tmask;
309 	}
310 	check_message();
311 	return(ch);
312 }
313 
314 void
315 take_off(void)
316 {
317 	char desc[DCOLS];
318 	object *obj;
319 
320 	if (rogue.armor) {
321 		if (rogue.armor->is_cursed) {
322 			message(curse_message, 0);
323 		} else {
324 			mv_aquatars();
325 			obj = rogue.armor;
326 			unwear(rogue.armor);
327 			strcpy(desc, "was wearing ");
328 			get_desc(obj, desc+12);
329 			message(desc, 0);
330 			print_stats(STAT_ARMOR);
331 			reg_move();
332 		}
333 	} else {
334 		message("not wearing any", 0);
335 	}
336 }
337 
338 void
339 wear(void)
340 {
341 	short ch;
342 	object *obj;
343 	char desc[DCOLS];
344 
345 	if (rogue.armor) {
346 		message("you're already wearing some", 0);
347 		return;
348 	}
349 	ch = pack_letter("wear what?", ARMOR);
350 
351 	if (ch == CANCEL) {
352 		return;
353 	}
354 	if (!(obj = get_letter_object(ch))) {
355 		message("no such item.", 0);
356 		return;
357 	}
358 	if (obj->what_is != ARMOR) {
359 		message("you can't wear that", 0);
360 		return;
361 	}
362 	obj->identified = 1;
363 	strcpy(desc, "wearing ");
364 	get_desc(obj, desc + 8);
365 	message(desc, 0);
366 	do_wear(obj);
367 	print_stats(STAT_ARMOR);
368 	reg_move();
369 }
370 
371 void
372 unwear(object *obj)
373 {
374 	if (obj) {
375 		obj->in_use_flags &= (~BEING_WORN);
376 	}
377 	rogue.armor = NULL;
378 }
379 
380 void
381 do_wear(object *obj)
382 {
383 	rogue.armor = obj;
384 	obj->in_use_flags |= BEING_WORN;
385 	obj->identified = 1;
386 }
387 
388 void
389 wield(void)
390 {
391 	short ch;
392 	object *obj;
393 	char desc[DCOLS];
394 
395 	if (rogue.weapon && rogue.weapon->is_cursed) {
396 		message(curse_message, 0);
397 		return;
398 	}
399 	ch = pack_letter("wield what?", WEAPON);
400 
401 	if (ch == CANCEL) {
402 		return;
403 	}
404 	if (!(obj = get_letter_object(ch))) {
405 		message("No such item.", 0);
406 		return;
407 	}
408 	if (obj->what_is & (ARMOR | RING)) {
409 		sprintf(desc, "you can't wield %s",
410 			((obj->what_is == ARMOR) ? "armor" : "rings"));
411 		message(desc, 0);
412 		return;
413 	}
414 	if (obj->in_use_flags & BEING_WIELDED) {
415 		message("in use", 0);
416 	} else {
417 		unwield(rogue.weapon);
418 		strcpy(desc, "wielding ");
419 		get_desc(obj, desc + 9);
420 		message(desc, 0);
421 		do_wield(obj);
422 		reg_move();
423 	}
424 }
425 
426 void
427 do_wield(object *obj)
428 {
429 	rogue.weapon = obj;
430 	obj->in_use_flags |= BEING_WIELDED;
431 }
432 
433 void
434 unwield(object *obj)
435 {
436 	if (obj) {
437 		obj->in_use_flags &= (~BEING_WIELDED);
438 	}
439 	rogue.weapon = NULL;
440 }
441 
442 void
443 call_it(void)
444 {
445 	short ch;
446 	object *obj;
447 	struct id *id_table;
448 	char buf[MAX_TITLE_LENGTH+2];
449 
450 	ch = pack_letter("call what?", (SCROL | POTION | WAND | RING));
451 
452 	if (ch == CANCEL) {
453 		return;
454 	}
455 	if (!(obj = get_letter_object(ch))) {
456 		message("no such item.", 0);
457 		return;
458 	}
459 	if (!(obj->what_is & (SCROL | POTION | WAND | RING))) {
460 		message("surely you already know what that's called", 0);
461 		return;
462 	}
463 	id_table = get_id_table(obj);
464 
465 	if (get_input_line("call it:","",buf,id_table[obj->which_kind].title,1,1)) {
466 		id_table[obj->which_kind].id_status = CALLED;
467 		strcpy(id_table[obj->which_kind].title, buf);
468 	}
469 }
470 
471 short
472 pack_count(const object *new_obj)
473 {
474 	object *obj;
475 	short count = 0;
476 
477 	obj = rogue.pack.next_object;
478 
479 	while (obj) {
480 		if (obj->what_is != WEAPON) {
481 			count += obj->quantity;
482 		} else if (!new_obj) {
483 			count++;
484 		} else if ((new_obj->what_is != WEAPON) ||
485 			((obj->which_kind != ARROW) &&
486 			(obj->which_kind != DAGGER) &&
487 			(obj->which_kind != DART) &&
488 			(obj->which_kind != SHURIKEN)) ||
489 			(new_obj->which_kind != obj->which_kind) ||
490 			(obj->quiver != new_obj->quiver)) {
491 			count++;
492 		}
493 		obj = obj->next_object;
494 	}
495 	return(count);
496 }
497 
498 static boolean
499 mask_pack(const object *pack, unsigned short mask)
500 {
501 	while (pack->next_object) {
502 		pack = pack->next_object;
503 		if (pack->what_is & mask) {
504 			return(1);
505 		}
506 	}
507 	return(0);
508 }
509 
510 static boolean
511 is_pack_letter(short *c, unsigned short *mask)
512 {
513 	if (((*c == '?') || (*c == '!') || (*c == ':') || (*c == '=') ||
514 		(*c == ')') || (*c == ']') || (*c == '/') || (*c == ','))) {
515 		switch(*c) {
516 		case '?':
517 			*mask = SCROL;
518 			break;
519 		case '!':
520 			*mask = POTION;
521 			break;
522 		case ':':
523 			*mask = FOOD;
524 			break;
525 		case ')':
526 			*mask = WEAPON;
527 			break;
528 		case ']':
529 			*mask = ARMOR;
530 			break;
531 		case '/':
532 			*mask = WAND;
533 			break;
534 		case '=':
535 			*mask = RING;
536 			break;
537 		case ',':
538 			*mask = AMULET;
539 			break;
540 		}
541 		*c = LIST;
542 		return(1);
543 	}
544 	return(((*c >= 'a') && (*c <= 'z')) || (*c == CANCEL) || (*c == LIST));
545 }
546 
547 boolean
548 has_amulet(void)
549 {
550 	return(mask_pack(&rogue.pack, AMULET));
551 }
552 
553 void
554 kick_into_pack(void)
555 {
556 	object *obj;
557 	char desc[DCOLS];
558 	short n, stat;
559 
560 	if (!(dungeon[rogue.row][rogue.col] & OBJECT)) {
561 		message("nothing here", 0);
562 	} else {
563 		if ((obj = pick_up(rogue.row, rogue.col, &stat))) {
564 			get_desc(obj, desc);
565 			if (obj->what_is == GOLD) {
566 				message(desc, 0);
567 				free_object(obj);
568 			} else {
569 				n = strlen(desc);
570 				desc[n] = '(';
571 				desc[n+1] = obj->ichar;
572 				desc[n+2] = ')';
573 				desc[n+3] = 0;
574 				message(desc, 0);
575 			}
576 		}
577 		if (obj || (!stat)) {
578 			reg_move();
579 		}
580 	}
581 }
582