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
38static 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
58short halluc = 0;
59short blind = 0;
60short confused = 0;
61short levitate = 0;
62short haste_self = 0;
63boolean see_invisible = 0;
64short extra_hp = 0;
65boolean detect_monster = 0;
66boolean con_mon = 0;
67
68static const char strange_feeling[] =
69	"you have a strange feeling for a moment, then it passes";
70
71static const char *get_ench_color(void);
72static void go_blind(void);
73static void hold_monster(void);
74static void idntfy(void);
75static void potion_heal(int);
76static void uncurse_all(void);
77
78void
79quaff(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
190void
191read_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
299void
300vanish(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
320static void
321potion_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
366static void
367idntfy(void)
368{
369	short ch;
370	object *obj;
371	struct id *id_table;
372	char desc[DCOLS];
373AGAIN:
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
394void
395eat(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
434static void
435hold_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
467void
468tele(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
480void
481hallucinate(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
510void
511unhallucinate(void)
512{
513	halluc = 0;
514	relight();
515	messagef(1, "everything looks SO boring now");
516}
517
518void
519unblind(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
532void
533relight(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
543void
544take_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
558static void
559go_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
590static const char *
591get_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
601void
602cnfs(void)
603{
604	confused += get_rand(12, 22);
605}
606
607void
608unconfuse(void)
609{
610	confused = 0;
611	messagef(1, "you feel less %s now", (halluc ? "trippy" : "confused"));
612}
613
614static void
615uncurse_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