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