Initial import from FreeBSD RELENG_4:
[dragonfly.git] / games / rogue / object.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[] = "@(#)object.c    8.1 (Berkeley) 5/31/93";
40 #endif
41 static const char rcsid[] =
42  "$FreeBSD: src/games/rogue/object.c,v 1.5 1999/11/30 03:49:25 billf Exp $";
43 #endif /* not lint */
44
45 /*
46  * object.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 object level_objects;
60 unsigned short dungeon[DROWS][DCOLS];
61 short foods = 0;
62 object *free_list = (object *) 0;
63 char *fruit = (char *) 0;
64
65 fighter rogue = {
66         INIT_AW,        /* armor, weapon */
67         INIT_RINGS,     /* rings */
68         INIT_HP,        /* Hp current,max */
69         INIT_STR,       /* Str current,max */
70         INIT_PACK,      /* pack */
71         INIT_GOLD,      /* gold */
72         INIT_EXP,       /* exp level,points */
73         0, 0,           /* row, col */
74         INIT_CHAR,      /* char */
75         INIT_MOVES      /* moves */
76 };
77
78 struct id id_potions[POTIONS] = {
79 {100, "blue \0                           ", "of increase strength ", 0},
80 {250, "red \0                            ", "of restore strength ", 0},
81 {100, "green \0                          ", "of healing ", 0},
82 {200, "grey \0                           ", "of extra healing ", 0},
83  {10, "brown \0                          ", "of poison ", 0},
84 {300, "clear \0                          ", "of raise level ", 0},
85  {10, "pink \0                           ", "of blindness ", 0},
86  {25, "white \0                          ", "of hallucination ", 0},
87 {100, "purple \0                         ", "of detect monster ", 0},
88 {100, "black \0                          ", "of detect things ", 0},
89  {10, "yellow \0                         ", "of confusion ", 0},
90  {80, "plaid \0                          ", "of levitation ", 0},
91 {150, "burgundy \0                       ", "of haste self ", 0},
92 {145, "beige \0                          ", "of see invisible ", 0}
93 };
94
95 struct id id_scrolls[SCROLS] = {
96 {505, "                                   ", "of protect armor ", 0},
97 {200, "                                   ", "of hold monster ", 0},
98 {235, "                                   ", "of enchant weapon ", 0},
99 {235, "                                   ", "of enchant armor ", 0},
100 {175, "                                   ", "of identify ", 0},
101 {190, "                                   ", "of teleportation ", 0},
102  {25, "                                   ", "of sleep ", 0},
103 {610, "                                   ", "of scare monster ", 0},
104 {210, "                                   ", "of remove curse ", 0},
105  {80, "                                   ", "of create monster ",0},
106  {25, "                                   ", "of aggravate monster ",0},
107 {180, "                                   ", "of magic mapping ", 0},
108  {90, "                                   ", "of confuse monster ", 0}
109 };
110
111 struct id id_weapons[WEAPONS] = {
112         {150, "short bow ", "", 0},
113           {8, "darts ", "", 0},
114          {15, "arrows ", "", 0},
115          {27, "daggers ", "", 0},
116          {35, "shurikens ", "", 0},
117         {360, "mace ", "", 0},
118         {470, "long sword ", "", 0},
119         {580, "two-handed sword ", "", 0}
120 };
121
122 struct id id_armors[ARMORS] = {
123         {300, "leather armor ", "", (UNIDENTIFIED)},
124         {300, "ring mail ", "", (UNIDENTIFIED)},
125         {400, "scale mail ", "", (UNIDENTIFIED)},
126         {500, "chain mail ", "", (UNIDENTIFIED)},
127         {600, "banded mail ", "", (UNIDENTIFIED)},
128         {600, "splint mail ", "", (UNIDENTIFIED)},
129         {700, "plate mail ", "", (UNIDENTIFIED)}
130 };
131
132 struct id id_wands[WANDS] = {
133          {25, "                                 ", "of teleport away ",0},
134          {50, "                                 ", "of slow monster ", 0},
135           {8, "                                 ", "of invisibility ",0},
136          {55, "                                 ", "of polymorph ",0},
137           {2, "                                 ", "of haste monster ",0},
138          {20, "                                 ", "of magic missile ",0},
139          {20, "                                 ", "of cancellation ",0},
140           {0, "                                 ", "of do nothing ",0},
141          {35, "                                 ", "of drain life ",0},
142          {20, "                                 ", "of cold ",0},
143          {20, "                                 ", "of fire ",0}
144 };
145
146 struct id id_rings[RINGS] = {
147          {250, "                                 ", "of stealth ",0},
148          {100, "                                 ", "of teleportation ", 0},
149          {255, "                                 ", "of regeneration ",0},
150          {295, "                                 ", "of slow digestion ",0},
151          {200, "                                 ", "of add strength ",0},
152          {250, "                                 ", "of sustain strength ",0},
153          {250, "                                 ", "of dexterity ",0},
154           {25, "                                 ", "of adornment ",0},
155          {300, "                                 ", "of see invisible ",0},
156          {290, "                                 ", "of maintain armor ",0},
157          {270, "                                 ", "of searching ",0},
158 };
159
160 extern short cur_level, max_level;
161 extern short party_room;
162 extern char *error_file;
163 extern boolean is_wood[];
164
165 put_objects()
166 {
167         short i, n;
168         object *obj;
169
170         if (cur_level < max_level) {
171                 return;
172         }
173         n = coin_toss() ? get_rand(2, 4) : get_rand(3, 5);
174         while (rand_percent(33)) {
175                 n++;
176         }
177         if (party_room != NO_ROOM) {
178                 make_party();
179         }
180         for (i = 0; i < n; i++) {
181                 obj = gr_object();
182                 rand_place(obj);
183         }
184         put_gold();
185 }
186
187 put_gold()
188 {
189         short i, j;
190         short row,col;
191         boolean is_maze, is_room;
192
193         for (i = 0; i < MAXROOMS; i++) {
194                 is_maze = (rooms[i].is_room & R_MAZE) ? 1 : 0;
195                 is_room = (rooms[i].is_room & R_ROOM) ? 1 : 0;
196
197                 if (!(is_room || is_maze)) {
198                         continue;
199                 }
200                 if (is_maze || rand_percent(GOLD_PERCENT)) {
201                         for (j = 0; j < 50; j++) {
202                                 row = get_rand(rooms[i].top_row+1,
203                                 rooms[i].bottom_row-1);
204                                 col = get_rand(rooms[i].left_col+1,
205                                 rooms[i].right_col-1);
206                                 if ((dungeon[row][col] == FLOOR) ||
207                                         (dungeon[row][col] == TUNNEL)) {
208                                         plant_gold(row, col, is_maze);
209                                         break;
210                                 }
211                         }
212                 }
213         }
214 }
215
216 plant_gold(row, col, is_maze)
217 short row, col;
218 boolean is_maze;
219 {
220         object *obj;
221
222         obj = alloc_object();
223         obj->row = row; obj->col = col;
224         obj->what_is = GOLD;
225         obj->quantity = get_rand((2 * cur_level), (16 * cur_level));
226         if (is_maze) {
227                 obj->quantity += obj->quantity / 2;
228         }
229         dungeon[row][col] |= OBJECT;
230         (void) add_to_pack(obj, &level_objects, 0);
231 }
232
233 place_at(obj, row, col)
234 object *obj;
235 int row, col;
236 {
237         obj->row = row;
238         obj->col = col;
239         dungeon[row][col] |= OBJECT;
240         (void) add_to_pack(obj, &level_objects, 0);
241 }
242
243 object *
244 object_at(pack, row, col)
245 object *pack;
246 short row, col;
247 {
248         object *obj = (object *) 0;
249
250         if (dungeon[row][col] & (MONSTER | OBJECT)) {
251                 obj = pack->next_object;
252
253                 while (obj && ((obj->row != row) || (obj->col != col))) {
254                         obj = obj->next_object;
255                 }
256                 if (!obj) {
257                         message("object_at(): inconsistent", 1);
258                 }
259         }
260         return(obj);
261 }
262
263 object *
264 get_letter_object(ch)
265 int ch;
266 {
267         object *obj;
268
269         obj = rogue.pack.next_object;
270
271         while (obj && (obj->ichar != ch)) {
272                 obj = obj->next_object;
273         }
274         return(obj);
275 }
276
277 free_stuff(objlist)
278 object *objlist;
279 {
280         object *obj;
281
282         while (objlist->next_object) {
283                 obj = objlist->next_object;
284                 objlist->next_object =
285                         objlist->next_object->next_object;
286                 free_object(obj);
287         }
288 }
289
290 const char *
291 name_of(obj)
292 const object *obj;
293 {
294         const char *retstring;
295
296         switch(obj->what_is) {
297         case SCROL:
298                 retstring = obj->quantity > 1 ? "scrolls " : "scroll ";
299                 break;
300         case POTION:
301                 retstring = obj->quantity > 1 ? "potions " : "potion ";
302                 break;
303         case FOOD:
304                 if (obj->which_kind == RATION) {
305                         retstring = "food ";
306                 } else {
307                         retstring = fruit;
308                 }
309                 break;
310         case WAND:
311                 retstring = is_wood[obj->which_kind] ? "staff " : "wand ";
312                 break;
313         case WEAPON:
314                 switch(obj->which_kind) {
315                 case DART:
316                         retstring=obj->quantity > 1 ? "darts " : "dart ";
317                         break;
318                 case ARROW:
319                         retstring=obj->quantity > 1 ? "arrows " : "arrow ";
320                         break;
321                 case DAGGER:
322                         retstring=obj->quantity > 1 ? "daggers " : "dagger ";
323                         break;
324                 case SHURIKEN:
325                         retstring=obj->quantity > 1?"shurikens ":"shuriken ";
326                         break;
327                 default:
328                         retstring = id_weapons[obj->which_kind].title;
329                 }
330                 break;
331         case ARMOR:
332                 retstring = "armor ";
333                 break;
334         case RING:
335                         retstring = "ring ";
336                 break;
337         case AMULET:
338                 retstring = "amulet ";
339                 break;
340         default:
341                 retstring = "unknown ";
342                 break;
343         }
344         return(retstring);
345 }
346
347 object *
348 gr_object()
349 {
350         object *obj;
351
352         obj = alloc_object();
353
354         if (foods < (cur_level / 3)) {
355                 obj->what_is = FOOD;
356                 foods++;
357         } else {
358                 obj->what_is = gr_what_is();
359         }
360         switch(obj->what_is) {
361         case SCROL:
362                 gr_scroll(obj);
363                 break;
364         case POTION:
365                 gr_potion(obj);
366                 break;
367         case WEAPON:
368                 gr_weapon(obj, 1);
369                 break;
370         case ARMOR:
371                 gr_armor(obj);
372                 break;
373         case WAND:
374                 gr_wand(obj);
375                 break;
376         case FOOD:
377                 get_food(obj, 0);
378                 break;
379         case RING:
380                 gr_ring(obj, 1);
381                 break;
382         }
383         return(obj);
384 }
385
386 unsigned short
387 gr_what_is()
388 {
389         short percent;
390         unsigned short what_is;
391
392         percent = get_rand(1, 91);
393
394         if (percent <= 30) {
395                 what_is = SCROL;
396         } else if (percent <= 60) {
397                 what_is = POTION;
398         } else if (percent <= 64) {
399                 what_is = WAND;
400         } else if (percent <= 74) {
401                 what_is = WEAPON;
402         } else if (percent <= 83) {
403                 what_is = ARMOR;
404         } else if (percent <= 88) {
405                 what_is = FOOD;
406         } else {
407                 what_is = RING;
408         }
409         return(what_is);
410 }
411
412 gr_scroll(obj)
413 object *obj;
414 {
415         short percent;
416
417         percent = get_rand(0, 91);
418
419         obj->what_is = SCROL;
420
421         if (percent <= 5) {
422                 obj->which_kind = PROTECT_ARMOR;
423         } else if (percent <= 10) {
424                 obj->which_kind = HOLD_MONSTER;
425         } else if (percent <= 20) {
426                 obj->which_kind = CREATE_MONSTER;
427         } else if (percent <= 35) {
428                 obj->which_kind = IDENTIFY;
429         } else if (percent <= 43) {
430                 obj->which_kind = TELEPORT;
431         } else if (percent <= 50) {
432                 obj->which_kind = SLEEP;
433         } else if (percent <= 55) {
434                 obj->which_kind = SCARE_MONSTER;
435         } else if (percent <= 64) {
436                 obj->which_kind = REMOVE_CURSE;
437         } else if (percent <= 69) {
438                 obj->which_kind = ENCH_ARMOR;
439         } else if (percent <= 74) {
440                 obj->which_kind = ENCH_WEAPON;
441         } else if (percent <= 80) {
442                 obj->which_kind = AGGRAVATE_MONSTER;
443         } else if (percent <= 86) {
444                 obj->which_kind = CON_MON;
445         } else {
446                 obj->which_kind = MAGIC_MAPPING;
447         }
448 }
449
450 gr_potion(obj)
451 object *obj;
452 {
453         short percent;
454
455         percent = get_rand(1, 118);
456
457         obj->what_is = POTION;
458
459         if (percent <= 5) {
460                 obj->which_kind = RAISE_LEVEL;
461         } else if (percent <= 15) {
462                 obj->which_kind = DETECT_OBJECTS;
463         } else if (percent <= 25) {
464                 obj->which_kind = DETECT_MONSTER;
465         } else if (percent <= 35) {
466                 obj->which_kind = INCREASE_STRENGTH;
467         } else if (percent <= 45) {
468                 obj->which_kind = RESTORE_STRENGTH;
469         } else if (percent <= 55) {
470                 obj->which_kind = HEALING;
471         } else if (percent <= 65) {
472                 obj->which_kind = EXTRA_HEALING;
473         } else if (percent <= 75) {
474                 obj->which_kind = BLINDNESS;
475         } else if (percent <= 85) {
476                 obj->which_kind = HALLUCINATION;
477         } else if (percent <= 95) {
478                 obj->which_kind = CONFUSION;
479         } else if (percent <= 105) {
480                 obj->which_kind = POISON;
481         } else if (percent <= 110) {
482                 obj->which_kind = LEVITATION;
483         } else if (percent <= 114) {
484                 obj->which_kind = HASTE_SELF;
485         } else {
486                 obj->which_kind = SEE_INVISIBLE;
487         }
488 }
489
490 gr_weapon(obj, assign_wk)
491 object *obj;
492 int assign_wk;
493 {
494         short percent;
495         short i;
496         short blessing, increment;
497
498         obj->what_is = WEAPON;
499         if (assign_wk) {
500                 obj->which_kind = get_rand(0, (WEAPONS - 1));
501         }
502         if ((obj->which_kind == ARROW) || (obj->which_kind == DAGGER) ||
503                 (obj->which_kind == SHURIKEN) | (obj->which_kind == DART)) {
504                 obj->quantity = get_rand(3, 15);
505                 obj->quiver = get_rand(0, 126);
506         } else {
507                 obj->quantity = 1;
508         }
509         obj->hit_enchant = obj->d_enchant = 0;
510
511         percent = get_rand(1, 96);
512         blessing = get_rand(1, 3);
513
514         if (percent <= 16) {
515                 increment = 1;
516         } else if (percent <= 32) {
517                 increment = -1;
518                 obj->is_cursed = 1;
519         }
520         if (percent <= 32) {
521                 for (i = 0; i < blessing; i++) {
522                         if (coin_toss()) {
523                                 obj->hit_enchant += increment;
524                         } else {
525                                 obj->d_enchant += increment;
526                         }
527                 }
528         }
529         switch(obj->which_kind) {
530         case BOW:
531         case DART:
532                 obj->damage = "1d1";
533                 break;
534         case ARROW:
535                 obj->damage = "1d2";
536                 break;
537         case DAGGER:
538                 obj->damage = "1d3";
539                 break;
540         case SHURIKEN:
541                 obj->damage = "1d4";
542                 break;
543         case MACE:
544                 obj->damage = "2d3";
545                 break;
546         case LONG_SWORD:
547                 obj->damage = "3d4";
548                 break;
549         case TWO_HANDED_SWORD:
550                 obj->damage = "4d5";
551                 break;
552         }
553 }
554
555 gr_armor(obj)
556 object *obj;
557 {
558         short percent;
559         short blessing;
560
561         obj->what_is = ARMOR;
562         obj->which_kind = get_rand(0, (ARMORS - 1));
563         obj->class = obj->which_kind + 2;
564         if ((obj->which_kind == PLATE) || (obj->which_kind == SPLINT)) {
565                 obj->class--;
566         }
567         obj->is_protected = 0;
568         obj->d_enchant = 0;
569
570         percent = get_rand(1, 100);
571         blessing = get_rand(1, 3);
572
573         if (percent <= 16) {
574                 obj->is_cursed = 1;
575                 obj->d_enchant -= blessing;
576         } else if (percent <= 33) {
577                 obj->d_enchant += blessing;
578         }
579 }
580
581 gr_wand(obj)
582 object *obj;
583 {
584         obj->what_is = WAND;
585         obj->which_kind = get_rand(0, (WANDS - 1));
586         obj->class = get_rand(3, 7);
587 }
588
589 get_food(obj, force_ration)
590 object *obj;
591 boolean force_ration;
592 {
593         obj->what_is = FOOD;
594
595         if (force_ration || rand_percent(80)) {
596                 obj->which_kind = RATION;
597         } else {
598                 obj->which_kind = FRUIT;
599         }
600 }
601
602 put_stairs()
603 {
604         short row, col;
605
606         gr_row_col(&row, &col, (FLOOR | TUNNEL));
607         dungeon[row][col] |= STAIRS;
608 }
609
610 get_armor_class(obj)
611 const object *obj;
612 {
613         if (obj) {
614                 return(obj->class + obj->d_enchant);
615         }
616         return(0);
617 }
618
619 object *
620 alloc_object()
621 {
622         object *obj;
623
624         if (free_list) {
625                 obj = free_list;
626                 free_list = free_list->next_object;
627         } else if (!(obj = (object *) md_malloc(sizeof(object)))) {
628                         message("cannot allocate object, saving game", 0);
629                         save_into_file(error_file);
630         }
631         obj->quantity = 1;
632         obj->ichar = 'L';
633         obj->picked_up = obj->is_cursed = 0;
634         obj->in_use_flags = NOT_USED;
635         obj->identified = UNIDENTIFIED;
636         obj->damage = "1d1";
637         return(obj);
638 }
639
640 free_object(obj)
641 object *obj;
642 {
643         obj->next_object = free_list;
644         free_list = obj;
645 }
646
647 make_party()
648 {
649         short n;
650
651         party_room = gr_room();
652
653         n = rand_percent(99) ? party_objects(party_room) : 11;
654         if (rand_percent(99)) {
655                 party_monsters(party_room, n);
656         }
657 }
658
659 show_objects()
660 {
661         object *obj;
662         short mc, rc, row, col;
663         object *monster;
664
665         obj = level_objects.next_object;
666
667         while (obj) {
668                 row = obj->row;
669                 col = obj->col;
670
671                 rc = get_mask_char(obj->what_is);
672
673                 if (dungeon[row][col] & MONSTER) {
674                         if (monster = object_at(&level_monsters, row, col)) {
675                                 monster->trail_char = rc;
676                         }
677                 }
678                 mc = mvinch(row, col);
679                 if (((mc < 'A') || (mc > 'Z')) &&
680                         ((row != rogue.row) || (col != rogue.col))) {
681                         mvaddch(row, col, rc);
682                 }
683                 obj = obj->next_object;
684         }
685
686         monster = level_monsters.next_object;
687
688         while (monster) {
689                 if (monster->m_flags & IMITATES) {
690                         mvaddch(monster->row, monster->col, (int) monster->disguise);
691                 }
692                 monster = monster->next_monster;
693         }
694 }
695
696 put_amulet()
697 {
698         object *obj;
699
700         obj = alloc_object();
701         obj->what_is = AMULET;
702         rand_place(obj);
703 }
704
705 rand_place(obj)
706 object *obj;
707 {
708         short row, col;
709
710         gr_row_col(&row, &col, (FLOOR | TUNNEL));
711         place_at(obj, row, col);
712 }
713
714 c_object_for_wizard()
715 {
716         short ch, max, wk;
717         object *obj;
718         char buf[80];
719
720         if (pack_count((object *) 0) >= MAX_PACK_COUNT) {
721                 message("pack full", 0);
722                 return;
723         }
724         message("type of object?", 0);
725
726         while (r_index("!?:)]=/,\033", (ch = rgetchar()), 0) == -1) {
727                 sound_bell();
728         }
729         check_message();
730
731         if (ch == '\033') {
732                 return;
733         }
734         obj = alloc_object();
735
736         switch(ch) {
737         case '!':
738                 obj->what_is = POTION;
739                 max = POTIONS - 1;
740                 break;
741         case '?':
742                 obj->what_is = SCROL;
743                 max = SCROLS - 1;
744                 break;
745         case ',':
746                 obj->what_is = AMULET;
747                 break;
748         case ':':
749                 get_food(obj, 0);
750                 break;
751         case ')':
752                 gr_weapon(obj, 0);
753                 max = WEAPONS - 1;
754                 break;
755         case ']':
756                 gr_armor(obj);
757                 max = ARMORS - 1;
758                 break;
759         case '/':
760                 gr_wand(obj);
761                 max = WANDS - 1;
762                 break;
763         case '=':
764                 max = RINGS - 1;
765                 obj->what_is = RING;
766                 break;
767         }
768         if ((ch != ',') && (ch != ':')) {
769 GIL:
770                 if (get_input_line("which kind?", "", buf, "", 0, 1)) {
771                         wk = get_number(buf);
772                         if ((wk >= 0) && (wk <= max)) {
773                                 obj->which_kind = (unsigned short) wk;
774                                 if (obj->what_is == RING) {
775                                         gr_ring(obj, 0);
776                                 }
777                         } else {
778                                 sound_bell();
779                                 goto GIL;
780                         }
781                 } else {
782                         free_object(obj);
783                         return;
784                 }
785         }
786         get_desc(obj, buf);
787         message(buf, 0);
788         (void) add_to_pack(obj, &rogue.pack, 1);
789 }