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