1 /*        $NetBSD: pack.c,v 1.12 2011/05/23 23:01:17 joerg 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.12 2011/05/23 23:01:17 joerg 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 static object *check_duplicate(object *, object *);
61 static boolean is_pack_letter(short *, unsigned short *);
62 static boolean mask_pack(const object *, unsigned short);
63 static short next_avail_ichar(void);
64 
65 object *
add_to_pack(object * obj,object * pack,int condense)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)) != 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 
92 void
take_from_pack(object * obj,object * pack)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 *
pick_up(int row,int col,short * status)106 pick_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 
151 void
drop(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                     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 
213 static object *
check_duplicate(object * obj,object * pack)214 check_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 
246 static short
next_avail_ichar(void)247 next_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 
271 void
wait_for_ack(void)272 wait_for_ack(void)
273 {
274           while (rgetchar() != ' ')
275                     ;
276 }
277 
278 short
pack_letter(const char * prompt,unsigned short mask)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                     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 
314 void
take_off(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                               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 
337 void
wear(void)338 wear(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 
369 void
unwear(object * obj)370 unwear(object *obj)
371 {
372           if (obj) {
373                     obj->in_use_flags &= (~BEING_WORN);
374           }
375           rogue.armor = NULL;
376 }
377 
378 void
do_wear(object * obj)379 do_wear(object *obj)
380 {
381           rogue.armor = obj;
382           obj->in_use_flags |= BEING_WORN;
383           obj->identified = 1;
384 }
385 
386 void
wield(void)387 wield(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 
422 void
do_wield(object * obj)423 do_wield(object *obj)
424 {
425           rogue.weapon = obj;
426           obj->in_use_flags |= BEING_WIELDED;
427 }
428 
429 void
unwield(object * obj)430 unwield(object *obj)
431 {
432           if (obj) {
433                     obj->in_use_flags &= (~BEING_WIELDED);
434           }
435           rogue.weapon = NULL;
436 }
437 
438 void
call_it(void)439 call_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 
469 short
pack_count(const object * new_obj)470 pack_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 
496 static boolean
mask_pack(const object * pack,unsigned short mask)497 mask_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 
508 static boolean
is_pack_letter(short * c,unsigned short * mask)509 is_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 
545 boolean
has_amulet(void)546 has_amulet(void)
547 {
548           return(mask_pack(&rogue.pack, AMULET));
549 }
550 
551 void
kick_into_pack(void)552 kick_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