1/*	$NetBSD: pack.c,v 1.11 2009/08/12 08:44:45 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
38static char sccsid[] = "@(#)pack.c	8.1 (Berkeley) 5/31/93";
39#else
40__RCSID("$NetBSD: pack.c,v 1.11 2009/08/12 08:44:45 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
58const char curse_message[] = "you can't, it appears to be cursed";
59
60static object *check_duplicate(object *, object *);
61static boolean is_pack_letter(short *, unsigned short *);
62static boolean mask_pack(const object *, unsigned short);
63static short next_avail_ichar(void);
64
65object *
66add_to_pack(object *obj, object *pack, int condense)
67{
68	object *op;
69
70	if (condense) {
71		if ((op = check_duplicate(obj, pack)) != NULL) {
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
92void
93take_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
105object *
106pick_up(int row, int col, short *status)
107{
108	object *obj;
109
110	*status = 1;
111
112	if (levitate) {
113		messagef(0, "you're floating in the air!");
114		return NULL;
115	}
116	obj = object_at(&level_objects, row, col);
117	if (!obj) {
118		messagef(1, "pick_up(): inconsistent");
119		return(obj);
120	}
121	if (	(obj->what_is == SCROL) &&
122			(obj->which_kind == SCARE_MONSTER) &&
123			obj->picked_up) {
124		messagef(0, "the scroll turns to dust as you pick it up");
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		messagef(1, "pack too full");
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
151void
152drop(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		messagef(0, "there's already something there");
160		return;
161	}
162	if (!rogue.pack.next_object) {
163		messagef(0, "you have nothing to drop");
164		return;
165	}
166	if ((ch = pack_letter("drop what?", ALL_OBJECTS)) == CANCEL) {
167		return;
168	}
169	if (!(obj = get_letter_object(ch))) {
170		messagef(0, "no such item.");
171		return;
172	}
173	if (obj->in_use_flags & BEING_WIELDED) {
174		if (obj->is_cursed) {
175			messagef(0, "%s", curse_message);
176			return;
177		}
178		unwield(rogue.weapon);
179	} else if (obj->in_use_flags & BEING_WORN) {
180		if (obj->is_cursed) {
181			messagef(0, "%s", curse_message);
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			messagef(0, "%s", curse_message);
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	get_desc(obj, desc, sizeof(desc));
209	messagef(0, "dropped %s", desc);
210	(void)reg_move();
211}
212
213static object *
214check_duplicate(object *obj, object *pack)
215{
216	object *op;
217
218	if (!(obj->what_is & (WEAPON | FOOD | SCROL | POTION))) {
219		return(0);
220	}
221	if ((obj->what_is == FOOD) && (obj->which_kind == FRUIT)) {
222		return(0);
223	}
224	op = pack->next_object;
225
226	while (op) {
227		if ((op->what_is == obj->what_is) &&
228			(op->which_kind == obj->which_kind)) {
229
230			if ((obj->what_is != WEAPON) ||
231			((obj->what_is == WEAPON) &&
232			((obj->which_kind == ARROW) ||
233			(obj->which_kind == DAGGER) ||
234			(obj->which_kind == DART) ||
235			(obj->which_kind == SHURIKEN)) &&
236			(obj->quiver == op->quiver))) {
237				op->quantity += obj->quantity;
238				return(op);
239			}
240		}
241		op = op->next_object;
242	}
243	return(0);
244}
245
246static short
247next_avail_ichar(void)
248{
249	object *obj;
250	int i;
251	boolean ichars[26];
252
253	for (i = 0; i < 26; i++) {
254		ichars[i] = 0;
255	}
256	obj = rogue.pack.next_object;
257	while (obj) {
258		if (obj->ichar >= 'a' && obj->ichar <= 'z') {
259			ichars[(obj->ichar - 'a')] = 1;
260		}
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
271void
272wait_for_ack(void)
273{
274	while (rgetchar() != ' ')
275		;
276}
277
278short
279pack_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		messagef(0, "nothing appropriate");
286		return(CANCEL);
287	}
288	for (;;) {
289
290		messagef(0, "%s", prompt);
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
314void
315take_off(void)
316{
317	char desc[DCOLS];
318	object *obj;
319
320	if (rogue.armor) {
321		if (rogue.armor->is_cursed) {
322			messagef(0, "%s", curse_message);
323		} else {
324			mv_aquatars();
325			obj = rogue.armor;
326			unwear(rogue.armor);
327			get_desc(obj, desc, sizeof(desc));
328			messagef(0, "was wearing %s", desc);
329			print_stats(STAT_ARMOR);
330			(void)reg_move();
331		}
332	} else {
333		messagef(0, "not wearing any");
334	}
335}
336
337void
338wear(void)
339{
340	short ch;
341	object *obj;
342	char desc[DCOLS];
343
344	if (rogue.armor) {
345		messagef(0, "you're already wearing some");
346		return;
347	}
348	ch = pack_letter("wear what?", ARMOR);
349
350	if (ch == CANCEL) {
351		return;
352	}
353	if (!(obj = get_letter_object(ch))) {
354		messagef(0, "no such item.");
355		return;
356	}
357	if (obj->what_is != ARMOR) {
358		messagef(0, "you can't wear that");
359		return;
360	}
361	obj->identified = 1;
362	get_desc(obj, desc, sizeof(desc));
363	messagef(0, "wearing %s", desc);
364	do_wear(obj);
365	print_stats(STAT_ARMOR);
366	(void)reg_move();
367}
368
369void
370unwear(object *obj)
371{
372	if (obj) {
373		obj->in_use_flags &= (~BEING_WORN);
374	}
375	rogue.armor = NULL;
376}
377
378void
379do_wear(object *obj)
380{
381	rogue.armor = obj;
382	obj->in_use_flags |= BEING_WORN;
383	obj->identified = 1;
384}
385
386void
387wield(void)
388{
389	short ch;
390	object *obj;
391	char desc[DCOLS];
392
393	if (rogue.weapon && rogue.weapon->is_cursed) {
394		messagef(0, "%s", curse_message);
395		return;
396	}
397	ch = pack_letter("wield what?", WEAPON);
398
399	if (ch == CANCEL) {
400		return;
401	}
402	if (!(obj = get_letter_object(ch))) {
403		messagef(0, "No such item.");
404		return;
405	}
406	if (obj->what_is & (ARMOR | RING)) {
407		messagef(0, "you can't wield %s",
408			((obj->what_is == ARMOR) ? "armor" : "rings"));
409		return;
410	}
411	if (obj->in_use_flags & BEING_WIELDED) {
412		messagef(0, "in use");
413	} else {
414		unwield(rogue.weapon);
415		get_desc(obj, desc, sizeof(desc));
416		messagef(0, "wielding %s", desc);
417		do_wield(obj);
418		(void)reg_move();
419	}
420}
421
422void
423do_wield(object *obj)
424{
425	rogue.weapon = obj;
426	obj->in_use_flags |= BEING_WIELDED;
427}
428
429void
430unwield(object *obj)
431{
432	if (obj) {
433		obj->in_use_flags &= (~BEING_WIELDED);
434	}
435	rogue.weapon = NULL;
436}
437
438void
439call_it(void)
440{
441	short ch;
442	object *obj;
443	struct id *id_table;
444	char buf[MAX_TITLE_LENGTH+2];
445
446	ch = pack_letter("call what?", (SCROL | POTION | WAND | RING));
447
448	if (ch == CANCEL) {
449		return;
450	}
451	if (!(obj = get_letter_object(ch))) {
452		messagef(0, "no such item.");
453		return;
454	}
455	if (!(obj->what_is & (SCROL | POTION | WAND | RING))) {
456		messagef(0, "surely you already know what that's called");
457		return;
458	}
459	id_table = get_id_table(obj);
460
461	if (get_input_line("call it:", "", buf, sizeof(buf),
462			id_table[obj->which_kind].title, 1, 1)) {
463		id_table[obj->which_kind].id_status = CALLED;
464		(void)strlcpy(id_table[obj->which_kind].title, buf,
465				sizeof(id_table[obj->which_kind].title));
466	}
467}
468
469short
470pack_count(const object *new_obj)
471{
472	object *obj;
473	short count = 0;
474
475	obj = rogue.pack.next_object;
476
477	while (obj) {
478		if (obj->what_is != WEAPON) {
479			count += obj->quantity;
480		} else if (!new_obj) {
481			count++;
482		} else if ((new_obj->what_is != WEAPON) ||
483			((obj->which_kind != ARROW) &&
484			(obj->which_kind != DAGGER) &&
485			(obj->which_kind != DART) &&
486			(obj->which_kind != SHURIKEN)) ||
487			(new_obj->which_kind != obj->which_kind) ||
488			(obj->quiver != new_obj->quiver)) {
489			count++;
490		}
491		obj = obj->next_object;
492	}
493	return(count);
494}
495
496static boolean
497mask_pack(const object *pack, unsigned short mask)
498{
499	while (pack->next_object) {
500		pack = pack->next_object;
501		if (pack->what_is & mask) {
502			return(1);
503		}
504	}
505	return(0);
506}
507
508static boolean
509is_pack_letter(short *c, unsigned short *mask)
510{
511	if (((*c == '?') || (*c == '!') || (*c == ':') || (*c == '=') ||
512		(*c == ')') || (*c == ']') || (*c == '/') || (*c == ','))) {
513		switch(*c) {
514		case '?':
515			*mask = SCROL;
516			break;
517		case '!':
518			*mask = POTION;
519			break;
520		case ':':
521			*mask = FOOD;
522			break;
523		case ')':
524			*mask = WEAPON;
525			break;
526		case ']':
527			*mask = ARMOR;
528			break;
529		case '/':
530			*mask = WAND;
531			break;
532		case '=':
533			*mask = RING;
534			break;
535		case ',':
536			*mask = AMULET;
537			break;
538		}
539		*c = LIST;
540		return(1);
541	}
542	return(((*c >= 'a') && (*c <= 'z')) || (*c == CANCEL) || (*c == LIST));
543}
544
545boolean
546has_amulet(void)
547{
548	return(mask_pack(&rogue.pack, AMULET));
549}
550
551void
552kick_into_pack(void)
553{
554	object *obj;
555	char desc[DCOLS];
556	short stat;
557
558	if (!(dungeon[rogue.row][rogue.col] & OBJECT)) {
559		messagef(0, "nothing here");
560	} else {
561		if ((obj = pick_up(rogue.row, rogue.col, &stat)) != NULL) {
562			get_desc(obj, desc, sizeof(desc));
563			if (obj->what_is == GOLD) {
564				messagef(0, "%s", desc);
565				free_object(obj);
566			} else {
567				messagef(0, "%s(%c)", desc, obj->ichar);
568			}
569		}
570		if (obj || (!stat)) {
571			(void)reg_move();
572		}
573	}
574}
575