1 /*        $NetBSD: use.c,v 1.11 2021/05/02 12:50:46 rillig Exp $      */
2 
3 /*
4  * Copyright (c) 1988, 1993
5  *        The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Timothy C. Stoehr.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 #include <sys/cdefs.h>
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)use.c       8.1 (Berkeley) 5/31/93";
39 #else
40 __RCSID("$NetBSD: use.c,v 1.11 2021/05/02 12:50:46 rillig Exp $");
41 #endif
42 #endif /* not lint */
43 
44 /*
45  * use.c
46  *
47  * This source herein may be modified and/or distributed by anybody who
48  * so desires, with the following restrictions:
49  *    1.)  No portion of this notice shall be removed.
50  *    2.)  Credit shall not be taken for the creation of this source.
51  *    3.)  This code is not to be traded, sold, or used for personal
52  *         gain or profit.
53  *
54  */
55 
56 #include "rogue.h"
57 
58 short halluc = 0;
59 short blind = 0;
60 short confused = 0;
61 short levitate = 0;
62 short haste_self = 0;
63 boolean see_invisible = 0;
64 short extra_hp = 0;
65 boolean detect_monster = 0;
66 boolean con_mon = 0;
67 
68 static const char strange_feeling[] =
69           "you have a strange feeling for a moment, then it passes";
70 
71 static const char *get_ench_color(void);
72 static void go_blind(void);
73 static void hold_monster(void);
74 static void idntfy(void);
75 static void potion_heal(int);
76 static void uncurse_all(void);
77 
78 void
quaff(void)79 quaff(void)
80 {
81           short ch;
82           object *obj;
83 
84           ch = pack_letter("quaff what?", POTION);
85 
86           if (ch == CANCEL) {
87                     return;
88           }
89           if (!(obj = get_letter_object(ch))) {
90                     messagef(0, "no such item.");
91                     return;
92           }
93           if (obj->what_is != POTION) {
94                     messagef(0, "you can't drink that");
95                     return;
96           }
97           switch(obj->which_kind) {
98                     case INCREASE_STRENGTH:
99                               messagef(0, "you feel stronger now, what bulging muscles!");
100                               rogue.str_current++;
101                               if (rogue.str_current > rogue.str_max) {
102                                         rogue.str_max = rogue.str_current;
103                               }
104                               break;
105                     case RESTORE_STRENGTH:
106                               rogue.str_current = rogue.str_max;
107                               messagef(0, "this tastes great, you feel warm all over");
108                               break;
109                     case HEALING:
110                               messagef(0, "you begin to feel better");
111                               potion_heal(0);
112                               break;
113                     case EXTRA_HEALING:
114                               messagef(0, "you begin to feel much better");
115                               potion_heal(1);
116                               break;
117                     case POISON:
118                               if (!sustain_strength) {
119                                         rogue.str_current -= get_rand(1, 3);
120                                         if (rogue.str_current < 1) {
121                                                   rogue.str_current = 1;
122                                         }
123                               }
124                               messagef(0, "you feel very sick now");
125                               if (halluc) {
126                                         unhallucinate();
127                               }
128                               break;
129                     case RAISE_LEVEL:
130                               rogue.exp_points = level_points[rogue.exp - 1];
131                               messagef(0, "you suddenly feel much more skillful");
132                               add_exp(1, 1);
133                               break;
134                     case BLINDNESS:
135                               go_blind();
136                               break;
137                     case HALLUCINATION:
138                               messagef(0, "oh wow, everything seems so cosmic");
139                               halluc += get_rand(500, 800);
140                               break;
141                     case DETECT_MONSTER:
142                               show_monsters();
143                               if (!(level_monsters.next_monster)) {
144                                         messagef(0, "%s", strange_feeling);
145                               }
146                               break;
147                     case DETECT_OBJECTS:
148                               if (level_objects.next_object) {
149                                         if (!blind) {
150                                                   show_objects();
151                                         }
152                               } else {
153                                         messagef(0, "%s", strange_feeling);
154                               }
155                               break;
156                     case CONFUSION:
157                               messagef(0, (halluc ? "what a trippy feeling" :
158                               "you feel confused"));
159                               cnfs();
160                               break;
161                     case LEVITATION:
162                               messagef(0, "you start to float in the air");
163                               levitate += get_rand(15, 30);
164                               being_held = bear_trap = 0;
165                               break;
166                     case HASTE_SELF:
167                               messagef(0, "you feel yourself moving much faster");
168                               haste_self += get_rand(11, 21);
169                               if (!(haste_self % 2)) {
170                                         haste_self++;
171                               }
172                               break;
173                     case SEE_INVISIBLE:
174                               messagef(0, "hmm, this potion tastes like %sjuice",
175                                          fruit);
176                               if (blind) {
177                                         unblind();
178                               }
179                               see_invisible = 1;
180                               relight();
181                               break;
182           }
183           print_stats((STAT_STRENGTH | STAT_HP));
184           if (id_potions[obj->which_kind].id_status != CALLED) {
185                     id_potions[obj->which_kind].id_status = IDENTIFIED;
186           }
187           vanish(obj, 1, &rogue.pack);
188 }
189 
190 void
read_scroll(void)191 read_scroll(void)
192 {
193           short ch;
194           object *obj;
195 
196           ch = pack_letter("read what?", SCROL);
197 
198           if (ch == CANCEL) {
199                     return;
200           }
201           if (!(obj = get_letter_object(ch))) {
202                     messagef(0, "no such item.");
203                     return;
204           }
205           if (obj->what_is != SCROL) {
206                     messagef(0, "you can't read that");
207                     return;
208           }
209           switch(obj->which_kind) {
210                     case SCARE_MONSTER:
211                               messagef(0, "you hear a maniacal laughter in the distance");
212                               break;
213                     case HOLD_MONSTER:
214                               hold_monster();
215                               break;
216                     case ENCH_WEAPON:
217                               if (rogue.weapon) {
218                                         if (rogue.weapon->what_is == WEAPON) {
219                                                   messagef(0, "your %sglow%s %sfor a moment",
220                                                             name_of(rogue.weapon),
221                                                             ((rogue.weapon->quantity <= 1) ? "s" : ""),
222                                                             get_ench_color());
223                                                   if (coin_toss()) {
224                                                             rogue.weapon->hit_enchant++;
225                                                   } else {
226                                                             rogue.weapon->d_enchant++;
227                                                   }
228                                         }
229                                         rogue.weapon->is_cursed = 0;
230                               } else {
231                                         messagef(0, "your hands tingle");
232                               }
233                               break;
234                     case ENCH_ARMOR:
235                               if (rogue.armor) {
236                                         messagef(0, "your armor glows %sfor a moment",
237                                                   get_ench_color());
238                                         rogue.armor->d_enchant++;
239                                         rogue.armor->is_cursed = 0;
240                                         print_stats(STAT_ARMOR);
241                               } else {
242                                         messagef(0, "your skin crawls");
243                               }
244                               break;
245                     case IDENTIFY:
246                               messagef(0, "this is a scroll of identify");
247                               obj->identified = 1;
248                               id_scrolls[obj->which_kind].id_status = IDENTIFIED;
249                               idntfy();
250                               break;
251                     case TELEPORT:
252                               tele();
253                               break;
254                     case SLEEP:
255                               messagef(0, "you fall asleep");
256                               take_a_nap();
257                               break;
258                     case PROTECT_ARMOR:
259                               if (rogue.armor) {
260                                         messagef(0, "your armor is covered by a shimmering gold shield");
261                                         rogue.armor->is_protected = 1;
262                                         rogue.armor->is_cursed = 0;
263                               } else {
264                                         messagef(0, "your acne seems to have disappeared");
265                               }
266                               break;
267                     case REMOVE_CURSE:
268                                         messagef(0, (!halluc) ?
269                                                   "you feel as though someone is watching over you" :
270                                                   "you feel in touch with the universal oneness");
271                               uncurse_all();
272                               break;
273                     case CREATE_MONSTER:
274                               create_monster();
275                               break;
276                     case AGGRAVATE_MONSTER:
277                               aggravate();
278                               break;
279                     case MAGIC_MAPPING:
280                               messagef(0, "this scroll seems to have a map on it");
281                               draw_magic_map();
282                               break;
283                     case CON_MON:
284                               con_mon = 1;
285                               messagef(0, "your hands glow %sfor a moment",
286                                          get_ench_color());
287                               break;
288           }
289           if (id_scrolls[obj->which_kind].id_status != CALLED) {
290                     id_scrolls[obj->which_kind].id_status = IDENTIFIED;
291           }
292           vanish(obj, (obj->which_kind != SLEEP), &rogue.pack);
293 }
294 
295 /* vanish() does NOT handle a quiver of weapons with more than one
296  *  arrow (or whatever) in the quiver.  It will only decrement the count.
297  */
298 
299 void
vanish(object * obj,short rm,object * pack)300 vanish(object *obj, short rm, object *pack)
301 {
302           if (obj->quantity > 1) {
303                     obj->quantity--;
304           } else {
305                     if (obj->in_use_flags & BEING_WIELDED) {
306                               unwield(obj);
307                     } else if (obj->in_use_flags & BEING_WORN) {
308                               unwear(obj);
309                     } else if (obj->in_use_flags & ON_EITHER_HAND) {
310                               un_put_on(obj);
311                     }
312                     take_from_pack(obj, pack);
313                     free_object(obj);
314           }
315           if (rm) {
316                     (void)reg_move();
317           }
318 }
319 
320 static void
potion_heal(int extra)321 potion_heal(int extra)
322 {
323           float ratio;
324           short add;
325 
326           rogue.hp_current += rogue.exp;
327 
328           ratio = ((float)rogue.hp_current) / rogue.hp_max;
329 
330           if (ratio >= 1.00) {
331                     rogue.hp_max += (extra ? 2 : 1);
332                     extra_hp += (extra ? 2 : 1);
333                     rogue.hp_current = rogue.hp_max;
334           } else if (ratio >= 0.90) {
335                     rogue.hp_max += (extra ? 1 : 0);
336                     extra_hp += (extra ? 1 : 0);
337                     rogue.hp_current = rogue.hp_max;
338           } else {
339                     if (ratio < 0.33) {
340                               ratio = 0.33;
341                     }
342                     if (extra) {
343                               ratio += ratio;
344                     }
345                     add = (short)(ratio * (rogue.hp_max - rogue.hp_current));
346                     rogue.hp_current += add;
347                     if (rogue.hp_current > rogue.hp_max) {
348                               rogue.hp_current = rogue.hp_max;
349                     }
350           }
351           if (blind) {
352                     unblind();
353           }
354           if (confused && extra) {
355                     unconfuse();
356           } else if (confused) {
357                     confused = (confused / 2) + 1;
358           }
359           if (halluc && extra) {
360                     unhallucinate();
361           } else if (halluc) {
362                     halluc = (halluc / 2) + 1;
363           }
364 }
365 
366 static void
idntfy(void)367 idntfy(void)
368 {
369           short ch;
370           object *obj;
371           struct id *id_table;
372           char desc[DCOLS];
373 AGAIN:
374           ch = pack_letter("what would you like to identify?", ALL_OBJECTS);
375 
376           if (ch == CANCEL) {
377                     return;
378           }
379           if (!(obj = get_letter_object(ch))) {
380                     messagef(0, "no such item, try again");
381                     messagef(0, "%s", "");        /* gcc objects to just "" */
382                     check_message();
383                     goto AGAIN;
384           }
385           obj->identified = 1;
386           if (obj->what_is & (SCROL | POTION | WEAPON | ARMOR | WAND | RING)) {
387                     id_table = get_id_table(obj);
388                     id_table[obj->which_kind].id_status = IDENTIFIED;
389           }
390           get_desc(obj, desc, sizeof(desc));
391           messagef(0, "%s", desc);
392 }
393 
394 void
eat(void)395 eat(void)
396 {
397           short ch;
398           short moves;
399           object *obj;
400 
401           ch = pack_letter("eat what?", FOOD);
402 
403           if (ch == CANCEL) {
404                     return;
405           }
406           if (!(obj = get_letter_object(ch))) {
407                     messagef(0, "no such item.");
408                     return;
409           }
410           if (obj->what_is != FOOD) {
411                     messagef(0, "you can't eat that");
412                     return;
413           }
414           if ((obj->which_kind == FRUIT) || rand_percent(60)) {
415                     moves = get_rand(950, 1150);
416                     if (obj->which_kind == RATION) {
417                               messagef(0, "yum, that tasted good");
418                     } else {
419                               messagef(0, "my, that was a yummy %s", fruit);
420                     }
421           } else {
422                     moves = get_rand(750, 950);
423                     messagef(0, "yuk, that food tasted awful");
424                     add_exp(2, 1);
425           }
426           rogue.moves_left /= 3;
427           rogue.moves_left += moves;
428           hunger_str[0] = 0;
429           print_stats(STAT_HUNGER);
430 
431           vanish(obj, 1, &rogue.pack);
432 }
433 
434 static void
hold_monster(void)435 hold_monster(void)
436 {
437           short i, j;
438           short mcount = 0;
439           object *monster;
440           short row, col;
441 
442           for (i = -2; i <= 2; i++) {
443                     for (j = -2; j <= 2; j++) {
444                               row = rogue.row + i;
445                               col = rogue.col + j;
446                               if ((row < MIN_ROW) || (row > (DROWS-2)) || (col < 0) ||
447                                          (col > (DCOLS-1))) {
448                                         continue;
449                               }
450                               if (dungeon[row][col] & MONSTER) {
451                                         monster = object_at(&level_monsters, row, col);
452                                         monster->m_flags |= ASLEEP;
453                                         monster->m_flags &= (~WAKENS);
454                                         mcount++;
455                               }
456                     }
457           }
458           if (mcount == 0) {
459                     messagef(0, "you feel a strange sense of loss");
460           } else if (mcount == 1) {
461                     messagef(0, "the monster freezes");
462           } else {
463                     messagef(0, "the monsters around you freeze");
464           }
465 }
466 
467 void
tele(void)468 tele(void)
469 {
470           mvaddch(rogue.row, rogue.col, get_dungeon_char(rogue.row, rogue.col));
471 
472           if (cur_room >= 0) {
473                     darken_room(cur_room);
474           }
475           put_player(get_room_number(rogue.row, rogue.col));
476           being_held = 0;
477           bear_trap = 0;
478 }
479 
480 void
hallucinate(void)481 hallucinate(void)
482 {
483           object *obj, *monster;
484           short ch;
485 
486           if (blind) return;
487 
488           obj = level_objects.next_object;
489 
490           while (obj) {
491                     ch = mvinch(obj->row, obj->col);
492                     if (((ch < 'A') || (ch > 'Z')) &&
493                               ((obj->row != rogue.row) || (obj->col != rogue.col)))
494                     if ((ch != ' ') && (ch != '.') && (ch != '#') && (ch != '+')) {
495                               addch(gr_obj_char());
496                     }
497                     obj = obj->next_object;
498           }
499           monster = level_monsters.next_monster;
500 
501           while (monster) {
502                     ch = mvinch(monster->row, monster->col);
503                     if ((ch >= 'A') && (ch <= 'Z')) {
504                               addch(get_rand('A', 'Z'));
505                     }
506                     monster = monster->next_monster;
507           }
508 }
509 
510 void
unhallucinate(void)511 unhallucinate(void)
512 {
513           halluc = 0;
514           relight();
515           messagef(1, "everything looks SO boring now");
516 }
517 
518 void
unblind(void)519 unblind(void)
520 {
521           blind = 0;
522           messagef(1, "the veil of darkness lifts");
523           relight();
524           if (halluc) {
525                     hallucinate();
526           }
527           if (detect_monster) {
528                     show_monsters();
529           }
530 }
531 
532 void
relight(void)533 relight(void)
534 {
535           if (cur_room == PASSAGE) {
536                     light_passage(rogue.row, rogue.col);
537           } else {
538                     light_up_room(cur_room);
539           }
540           mvaddch(rogue.row, rogue.col, rogue.fchar);
541 }
542 
543 void
take_a_nap(void)544 take_a_nap(void)
545 {
546           short i;
547 
548           i = get_rand(2, 5);
549           md_sleep(1);
550 
551           while (i--) {
552                     mv_mons();
553           }
554           md_sleep(1);
555           messagef(0, "%s", you_can_move_again);
556 }
557 
558 static void
go_blind(void)559 go_blind(void)
560 {
561           short i, j;
562 
563           if (!blind) {
564                     messagef(0, "a cloak of darkness falls around you");
565           }
566           blind += get_rand(500, 800);
567 
568           if (detect_monster) {
569                     object *monster;
570 
571                     monster = level_monsters.next_monster;
572 
573                     while (monster) {
574                               mvaddch(monster->row, monster->col, monster->trail_char);
575                               monster = monster->next_monster;
576                     }
577           }
578           if (cur_room >= 0) {
579                     for (i = rooms[cur_room].top_row + 1;
580                                i < rooms[cur_room].bottom_row; i++) {
581                               for (j = rooms[cur_room].left_col + 1;
582                                          j < rooms[cur_room].right_col; j++) {
583                                         mvaddch(i, j, ' ');
584                               }
585                     }
586           }
587           mvaddch(rogue.row, rogue.col, rogue.fchar);
588 }
589 
590 static const char *
get_ench_color(void)591 get_ench_color(void)
592 {
593           if (halluc) {
594                     return(id_potions[get_rand(0, POTIONS-1)].title);
595           } else if (con_mon) {
596                     return("red ");
597           }
598           return("blue ");
599 }
600 
601 void
cnfs(void)602 cnfs(void)
603 {
604           confused += get_rand(12, 22);
605 }
606 
607 void
unconfuse(void)608 unconfuse(void)
609 {
610           confused = 0;
611           messagef(1, "you feel less %s now", (halluc ? "trippy" : "confused"));
612 }
613 
614 static void
uncurse_all(void)615 uncurse_all(void)
616 {
617           object *obj;
618 
619           obj = rogue.pack.next_object;
620 
621           while (obj) {
622                     obj->is_cursed = 0;
623                     obj = obj->next_object;
624           }
625 }
626