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