e20e1d20b3c533a258459b103dfc9a36355e6553
[dragonfly.git] / games / rogue / pack.c
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.3 2003/08/26 23:52:50 drhodus 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 "rogue.h"
54
55 const char *curse_message = "you can't, it appears to be cursed";
56
57 extern short levitate;
58
59 object *
60 add_to_pack(obj, pack, condense)
61 object *obj, *pack;
62 int condense;
63 {
64         object *op;
65
66         if (condense) {
67                 if (op = check_duplicate(obj, pack)) {
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 take_from_pack(obj, pack)
89 object *obj, *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(row, col, status)
103 int row, col;
104 short *status;
105 {
106         object *obj;
107
108         *status = 1;
109
110         if (levitate) {
111                 message("you're floating in the air!", 0);
112                 return((object *) 0);
113         }
114         obj = object_at(&level_objects, row, col);
115         if (!obj) {
116                 message("pick_up(): inconsistent", 1);
117                 return(obj);
118         }
119         if (    (obj->what_is == SCROL) &&
120                         (obj->which_kind == SCARE_MONSTER) &&
121                         obj->picked_up) {
122                 message("the scroll turns to dust as you pick it up", 0);
123                 dungeon[row][col] &= (~OBJECT);
124                 vanish(obj, 0, &level_objects);
125                 *status = 0;
126                 if (id_scrolls[SCARE_MONSTER].id_status == UNIDENTIFIED) {
127                         id_scrolls[SCARE_MONSTER].id_status = IDENTIFIED;
128                 }
129                 return((object *) 0);
130         }
131         if (obj->what_is == GOLD) {
132                 rogue.gold += obj->quantity;
133                 dungeon[row][col] &= ~(OBJECT);
134                 take_from_pack(obj, &level_objects);
135                 print_stats(STAT_GOLD);
136                 return(obj);    /* obj will be free_object()ed in caller */
137         }
138         if (pack_count(obj) >= MAX_PACK_COUNT) {
139                 message("pack too full", 1);
140                 return((object *) 0);
141         }
142         dungeon[row][col] &= ~(OBJECT);
143         take_from_pack(obj, &level_objects);
144         obj = add_to_pack(obj, &rogue.pack, 1);
145         obj->picked_up = 1;
146         return(obj);
147 }
148
149 drop()
150 {
151         object *obj, *new;
152         short ch;
153         char desc[DCOLS];
154
155         if (dungeon[rogue.row][rogue.col] & (OBJECT | STAIRS | TRAP)) {
156                 message("there's already something there", 0);
157                 return;
158         }
159         if (!rogue.pack.next_object) {
160                 message("you have nothing to drop", 0);
161                 return;
162         }
163         if ((ch = pack_letter("drop what?", ALL_OBJECTS)) == CANCEL) {
164                 return;
165         }
166         if (!(obj = get_letter_object(ch))) {
167                 message("no such item.", 0);
168                 return;
169         }
170         if (obj->in_use_flags & BEING_WIELDED) {
171                 if (obj->is_cursed) {
172                         message(curse_message, 0);
173                         return;
174                 }
175                 unwield(rogue.weapon);
176         } else if (obj->in_use_flags & BEING_WORN) {
177                 if (obj->is_cursed) {
178                         message(curse_message, 0);
179                         return;
180                 }
181                 mv_aquatars();
182                 unwear(rogue.armor);
183                 print_stats(STAT_ARMOR);
184         } else if (obj->in_use_flags & ON_EITHER_HAND) {
185                 if (obj->is_cursed) {
186                         message(curse_message, 0);
187                         return;
188                 }
189                 un_put_on(obj);
190         }
191         obj->row = rogue.row;
192         obj->col = rogue.col;
193
194         if ((obj->quantity > 1) && (obj->what_is != WEAPON)) {
195                 obj->quantity--;
196                 new = alloc_object();
197                 *new = *obj;
198                 new->quantity = 1;
199                 obj = new;
200         } else {
201                 obj->ichar = 'L';
202                 take_from_pack(obj, &rogue.pack);
203         }
204         place_at(obj, rogue.row, rogue.col);
205         (void) strcpy(desc, "dropped ");
206         get_desc(obj, desc+8);
207         message(desc, 0);
208         (void) reg_move();
209 }
210
211 object *
212 check_duplicate(obj, pack)
213 object *obj, *pack;
214 {
215         object *op;
216
217         if (!(obj->what_is & (WEAPON | FOOD | SCROL | POTION))) {
218                 return(0);
219         }
220         if ((obj->what_is == FOOD) && (obj->which_kind == FRUIT)) {
221                 return(0);
222         }
223         op = pack->next_object;
224
225         while (op) {
226                 if ((op->what_is == obj->what_is) &&
227                         (op->which_kind == obj->which_kind)) {
228
229                         if ((obj->what_is != WEAPON) ||
230                         ((obj->what_is == WEAPON) &&
231                         ((obj->which_kind == ARROW) ||
232                         (obj->which_kind == DAGGER) ||
233                         (obj->which_kind == DART) ||
234                         (obj->which_kind == SHURIKEN)) &&
235                         (obj->quiver == op->quiver))) {
236                                 op->quantity += obj->quantity;
237                                 return(op);
238                         }
239                 }
240                 op = op->next_object;
241         }
242         return(0);
243 }
244
245 next_avail_ichar()
246 {
247         object *obj;
248         int i;
249         boolean ichars[26];
250
251         for (i = 0; i < 26; i++) {
252                 ichars[i] = 0;
253         }
254         obj = rogue.pack.next_object;
255         while (obj) {
256                 ichars[(obj->ichar - 'a')] = 1;
257                 obj = obj->next_object;
258         }
259         for (i = 0; i < 26; i++) {
260                 if (!ichars[i]) {
261                         return(i + 'a');
262                 }
263         }
264         return('?');
265 }
266
267 wait_for_ack()
268 {
269         if (!isatty(0) || !isatty(1))
270             return;
271         while (rgetchar() != ' ') ;
272 }
273
274 pack_letter(prompt, mask)
275 const char *prompt;
276 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 take_off()
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                         (void) strcpy(desc, "was wearing ");
324                         get_desc(obj, desc+12);
325                         message(desc, 0);
326                         print_stats(STAT_ARMOR);
327                         (void) reg_move();
328                 }
329         } else {
330                 message("not wearing any", 0);
331         }
332 }
333
334 wear()
335 {
336         short ch;
337         object *obj;
338         char desc[DCOLS];
339
340         if (rogue.armor) {
341                 message("you're already wearing some", 0);
342                 return;
343         }
344         ch = pack_letter("wear what?", ARMOR);
345
346         if (ch == CANCEL) {
347                 return;
348         }
349         if (!(obj = get_letter_object(ch))) {
350                 message("no such item.", 0);
351                 return;
352         }
353         if (obj->what_is != ARMOR) {
354                 message("you can't wear that", 0);
355                 return;
356         }
357         obj->identified = 1;
358         (void) strcpy(desc, "wearing ");
359         get_desc(obj, desc + 8);
360         message(desc, 0);
361         do_wear(obj);
362         print_stats(STAT_ARMOR);
363         (void) reg_move();
364 }
365
366 unwear(obj)
367 object *obj;
368 {
369         if (obj) {
370                 obj->in_use_flags &= (~BEING_WORN);
371         }
372         rogue.armor = (object *) 0;
373 }
374
375 do_wear(obj)
376 object *obj;
377 {
378         rogue.armor = obj;
379         obj->in_use_flags |= BEING_WORN;
380         obj->identified = 1;
381 }
382
383 wield()
384 {
385         short ch;
386         object *obj;
387         char desc[DCOLS];
388
389         if (rogue.weapon && rogue.weapon->is_cursed) {
390                 message(curse_message, 0);
391                 return;
392         }
393         ch = pack_letter("wield what?", WEAPON);
394
395         if (ch == CANCEL) {
396                 return;
397         }
398         if (!(obj = get_letter_object(ch))) {
399                 message("No such item.", 0);
400                 return;
401         }
402         if (obj->what_is & (ARMOR | RING)) {
403                 sprintf(desc, "you can't wield %s",
404                         ((obj->what_is == ARMOR) ? "armor" : "rings"));
405                 message(desc, 0);
406                 return;
407         }
408         if (obj->in_use_flags & BEING_WIELDED) {
409                 message("in use", 0);
410         } else {
411                 unwield(rogue.weapon);
412                 (void) strcpy(desc, "wielding ");
413                 get_desc(obj, desc + 9);
414                 message(desc, 0);
415                 do_wield(obj);
416                 (void) reg_move();
417         }
418 }
419
420 do_wield(obj)
421 object *obj;
422 {
423         rogue.weapon = obj;
424         obj->in_use_flags |= BEING_WIELDED;
425 }
426
427 unwield(obj)
428 object *obj;
429 {
430         if (obj) {
431                 obj->in_use_flags &= (~BEING_WIELDED);
432         }
433         rogue.weapon = (object *) 0;
434 }
435
436 call_it()
437 {
438         short ch;
439         object *obj;
440         struct id *id_table;
441         char buf[MAX_TITLE_LENGTH+2];
442
443         ch = pack_letter("call what?", (SCROL | POTION | WAND | RING));
444
445         if (ch == CANCEL) {
446                 return;
447         }
448         if (!(obj = get_letter_object(ch))) {
449                 message("no such item.", 0);
450                 return;
451         }
452         if (!(obj->what_is & (SCROL | POTION | WAND | RING))) {
453                 message("surely you already know what that's called", 0);
454                 return;
455         }
456         id_table = get_id_table(obj);
457
458         if (get_input_line("call it:","",buf,id_table[obj->which_kind].title,1,1)) {
459                 id_table[obj->which_kind].id_status = CALLED;
460                 (void) strcpy(id_table[obj->which_kind].title, buf);
461         }
462 }
463
464 pack_count(new_obj)
465 const object *new_obj;
466 {
467         object *obj;
468         short count = 0;
469
470         obj = rogue.pack.next_object;
471
472         while (obj) {
473                 if (obj->what_is != WEAPON) {
474                         count += obj->quantity;
475                 } else if (!new_obj) {
476                         count++;
477                 } else if ((new_obj->what_is != WEAPON) ||
478                         ((obj->which_kind != ARROW) &&
479                         (obj->which_kind != DAGGER) &&
480                         (obj->which_kind != DART) &&
481                         (obj->which_kind != SHURIKEN)) ||
482                         (new_obj->which_kind != obj->which_kind) ||
483                         (obj->quiver != new_obj->quiver)) {
484                         count++;
485                 }
486                 obj = obj->next_object;
487         }
488         return(count);
489 }
490
491 boolean
492 mask_pack(pack, mask)
493 const object *pack;
494 unsigned short mask;
495 {
496         while (pack->next_object) {
497                 pack = pack->next_object;
498                 if (pack->what_is & mask) {
499                         return(1);
500                 }
501         }
502         return(0);
503 }
504
505 is_pack_letter(c, mask)
506 short *c;
507 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 has_amulet()
544 {
545         return(mask_pack(&rogue.pack, AMULET));
546 }
547
548 kick_into_pack()
549 {
550         object *obj;
551         char desc[DCOLS];
552         short n, stat;
553
554         if (!(dungeon[rogue.row][rogue.col] & OBJECT)) {
555                 message("nothing here", 0);
556         } else {
557                 if (obj = pick_up(rogue.row, rogue.col, &stat)) {
558                         get_desc(obj, desc);
559                         if (obj->what_is == GOLD) {
560                                 message(desc, 0);
561                                 free_object(obj);
562                         } else {
563                                 n = strlen(desc);
564                                 desc[n] = '(';
565                                 desc[n+1] = obj->ichar;
566                                 desc[n+2] = ')';
567                                 desc[n+3] = 0;
568                                 message(desc, 0);
569                         }
570                 }
571                 if (obj || (!stat)) {
572                         (void) reg_move();
573                 }
574         }
575 }