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