Initial import from FreeBSD RELENG_4:
[games.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
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)pack.c      8.1 (Berkeley) 5/31/93";
40 #endif
41 static const char rcsid[] =
42  "$FreeBSD: src/games/rogue/pack.c,v 1.8 1999/11/30 03:49:25 billf Exp $";
43 #endif /* not lint */
44
45 /*
46  * pack.c
47  *
48  * This source herein may be modified and/or distributed by anybody who
49  * so desires, with the following restrictions:
50  *    1.)  No portion of this notice shall be removed.
51  *    2.)  Credit shall not be taken for the creation of this source.
52  *    3.)  This code is not to be traded, sold, or used for personal
53  *         gain or profit.
54  *
55  */
56
57 #include "rogue.h"
58
59 const char *curse_message = "you can't, it appears to be cursed";
60
61 extern short levitate;
62
63 object *
64 add_to_pack(obj, pack, condense)
65 object *obj, *pack;
66 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 take_from_pack(obj, pack)
93 object *obj, *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(row, col, status)
107 int row, col;
108 short *status;
109 {
110         object *obj;
111
112         *status = 1;
113
114         if (levitate) {
115                 message("you're floating in the air!", 0);
116                 return((object *) 0);
117         }
118         obj = object_at(&level_objects, row, col);
119         if (!obj) {
120                 message("pick_up(): inconsistent", 1);
121                 return(obj);
122         }
123         if (    (obj->what_is == SCROL) &&
124                         (obj->which_kind == SCARE_MONSTER) &&
125                         obj->picked_up) {
126                 message("the scroll turns to dust as you pick it up", 0);
127                 dungeon[row][col] &= (~OBJECT);
128                 vanish(obj, 0, &level_objects);
129                 *status = 0;
130                 if (id_scrolls[SCARE_MONSTER].id_status == UNIDENTIFIED) {
131                         id_scrolls[SCARE_MONSTER].id_status = IDENTIFIED;
132                 }
133                 return((object *) 0);
134         }
135         if (obj->what_is == GOLD) {
136                 rogue.gold += obj->quantity;
137                 dungeon[row][col] &= ~(OBJECT);
138                 take_from_pack(obj, &level_objects);
139                 print_stats(STAT_GOLD);
140                 return(obj);    /* obj will be free_object()ed in caller */
141         }
142         if (pack_count(obj) >= MAX_PACK_COUNT) {
143                 message("pack too full", 1);
144                 return((object *) 0);
145         }
146         dungeon[row][col] &= ~(OBJECT);
147         take_from_pack(obj, &level_objects);
148         obj = add_to_pack(obj, &rogue.pack, 1);
149         obj->picked_up = 1;
150         return(obj);
151 }
152
153 drop()
154 {
155         object *obj, *new;
156         short ch;
157         char desc[DCOLS];
158
159         if (dungeon[rogue.row][rogue.col] & (OBJECT | STAIRS | TRAP)) {
160                 message("there's already something there", 0);
161                 return;
162         }
163         if (!rogue.pack.next_object) {
164                 message("you have nothing to drop", 0);
165                 return;
166         }
167         if ((ch = pack_letter("drop what?", ALL_OBJECTS)) == CANCEL) {
168                 return;
169         }
170         if (!(obj = get_letter_object(ch))) {
171                 message("no such item.", 0);
172                 return;
173         }
174         if (obj->in_use_flags & BEING_WIELDED) {
175                 if (obj->is_cursed) {
176                         message(curse_message, 0);
177                         return;
178                 }
179                 unwield(rogue.weapon);
180         } else if (obj->in_use_flags & BEING_WORN) {
181                 if (obj->is_cursed) {
182                         message(curse_message, 0);
183                         return;
184                 }
185                 mv_aquatars();
186                 unwear(rogue.armor);
187                 print_stats(STAT_ARMOR);
188         } else if (obj->in_use_flags & ON_EITHER_HAND) {
189                 if (obj->is_cursed) {
190                         message(curse_message, 0);
191                         return;
192                 }
193                 un_put_on(obj);
194         }
195         obj->row = rogue.row;
196         obj->col = rogue.col;
197
198         if ((obj->quantity > 1) && (obj->what_is != WEAPON)) {
199                 obj->quantity--;
200                 new = alloc_object();
201                 *new = *obj;
202                 new->quantity = 1;
203                 obj = new;
204         } else {
205                 obj->ichar = 'L';
206                 take_from_pack(obj, &rogue.pack);
207         }
208         place_at(obj, rogue.row, rogue.col);
209         (void) strcpy(desc, "dropped ");
210         get_desc(obj, desc+8);
211         message(desc, 0);
212         (void) reg_move();
213 }
214
215 object *
216 check_duplicate(obj, pack)
217 object *obj, *pack;
218 {
219         object *op;
220
221         if (!(obj->what_is & (WEAPON | FOOD | SCROL | POTION))) {
222                 return(0);
223         }
224         if ((obj->what_is == FOOD) && (obj->which_kind == FRUIT)) {
225                 return(0);
226         }
227         op = pack->next_object;
228
229         while (op) {
230                 if ((op->what_is == obj->what_is) &&
231                         (op->which_kind == obj->which_kind)) {
232
233                         if ((obj->what_is != WEAPON) ||
234                         ((obj->what_is == WEAPON) &&
235                         ((obj->which_kind == ARROW) ||
236                         (obj->which_kind == DAGGER) ||
237                         (obj->which_kind == DART) ||
238                         (obj->which_kind == SHURIKEN)) &&
239                         (obj->quiver == op->quiver))) {
240                                 op->quantity += obj->quantity;
241                                 return(op);
242                         }
243                 }
244                 op = op->next_object;
245         }
246         return(0);
247 }
248
249 next_avail_ichar()
250 {
251         object *obj;
252         int i;
253         boolean ichars[26];
254
255         for (i = 0; i < 26; i++) {
256                 ichars[i] = 0;
257         }
258         obj = rogue.pack.next_object;
259         while (obj) {
260                 ichars[(obj->ichar - 'a')] = 1;
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 wait_for_ack()
272 {
273         if (!isatty(0) || !isatty(1))
274             return;
275         while (rgetchar() != ' ') ;
276 }
277
278 pack_letter(prompt, mask)
279 const char *prompt;
280 unsigned short mask;
281 {
282         short ch;
283         unsigned short tmask = mask;
284
285         if (!mask_pack(&rogue.pack, mask)) {
286                 message("nothing appropriate", 0);
287                 return(CANCEL);
288         }
289         for (;;) {
290
291                 message(prompt, 0);
292
293                 for (;;) {
294                         ch = rgetchar();
295                         if (!is_pack_letter(&ch, &mask)) {
296                                 sound_bell();
297                         } else {
298                                 break;
299                         }
300                 }
301
302                 if (ch == LIST) {
303                         check_message();
304                         mask = tmask;
305                         inventory(&rogue.pack, mask);
306                 } else {
307                         break;
308                 }
309                 mask = tmask;
310         }
311         check_message();
312         return(ch);
313 }
314
315 take_off()
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                         (void) strcpy(desc, "was wearing ");
328                         get_desc(obj, desc+12);
329                         message(desc, 0);
330                         print_stats(STAT_ARMOR);
331                         (void) reg_move();
332                 }
333         } else {
334                 message("not wearing any", 0);
335         }
336 }
337
338 wear()
339 {
340         short ch;
341         object *obj;
342         char desc[DCOLS];
343
344         if (rogue.armor) {
345                 message("your already wearing some", 0);
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                 message("no such item.", 0);
355                 return;
356         }
357         if (obj->what_is != ARMOR) {
358                 message("you can't wear that", 0);
359                 return;
360         }
361         obj->identified = 1;
362         (void) strcpy(desc, "wearing ");
363         get_desc(obj, desc + 8);
364         message(desc, 0);
365         do_wear(obj);
366         print_stats(STAT_ARMOR);
367         (void) reg_move();
368 }
369
370 unwear(obj)
371 object *obj;
372 {
373         if (obj) {
374                 obj->in_use_flags &= (~BEING_WORN);
375         }
376         rogue.armor = (object *) 0;
377 }
378
379 do_wear(obj)
380 object *obj;
381 {
382         rogue.armor = obj;
383         obj->in_use_flags |= BEING_WORN;
384         obj->identified = 1;
385 }
386
387 wield()
388 {
389         short ch;
390         object *obj;
391         char desc[DCOLS];
392
393         if (rogue.weapon && rogue.weapon->is_cursed) {
394                 message(curse_message, 0);
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                 message("No such item.", 0);
404                 return;
405         }
406         if (obj->what_is & (ARMOR | RING)) {
407                 sprintf(desc, "you can't wield %s",
408                         ((obj->what_is == ARMOR) ? "armor" : "rings"));
409                 message(desc, 0);
410                 return;
411         }
412         if (obj->in_use_flags & BEING_WIELDED) {
413                 message("in use", 0);
414         } else {
415                 unwield(rogue.weapon);
416                 (void) strcpy(desc, "wielding ");
417                 get_desc(obj, desc + 9);
418                 message(desc, 0);
419                 do_wield(obj);
420                 (void) reg_move();
421         }
422 }
423
424 do_wield(obj)
425 object *obj;
426 {
427         rogue.weapon = obj;
428         obj->in_use_flags |= BEING_WIELDED;
429 }
430
431 unwield(obj)
432 object *obj;
433 {
434         if (obj) {
435                 obj->in_use_flags &= (~BEING_WIELDED);
436         }
437         rogue.weapon = (object *) 0;
438 }
439
440 call_it()
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                 (void) strcpy(id_table[obj->which_kind].title, buf);
465         }
466 }
467
468 pack_count(new_obj)
469 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 boolean
496 mask_pack(pack, mask)
497 const object *pack;
498 unsigned short mask;
499 {
500         while (pack->next_object) {
501                 pack = pack->next_object;
502                 if (pack->what_is & mask) {
503                         return(1);
504                 }
505         }
506         return(0);
507 }
508
509 is_pack_letter(c, mask)
510 short *c;
511 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 has_amulet()
548 {
549         return(mask_pack(&rogue.pack, AMULET));
550 }
551
552 kick_into_pack()
553 {
554         object *obj;
555         char desc[DCOLS];
556         short n, stat;
557
558         if (!(dungeon[rogue.row][rogue.col] & OBJECT)) {
559                 message("nothing here", 0);
560         } else {
561                 if (obj = pick_up(rogue.row, rogue.col, &stat)) {
562                         get_desc(obj, desc);
563                         if (obj->what_is == GOLD) {
564                                 message(desc, 0);
565                                 free_object(obj);
566                         } else {
567                                 n = strlen(desc);
568                                 desc[n] = '(';
569                                 desc[n+1] = obj->ichar;
570                                 desc[n+2] = ')';
571                                 desc[n+3] = 0;
572                                 message(desc, 0);
573                         }
574                 }
575                 if (obj || (!stat)) {
576                         (void) reg_move();
577                 }
578         }
579 }