xref: /netbsd-src/games/hack/hack.potion.c (revision 10ad5ffa714ce1a679dcc9dd8159648df2d67b5a)
1 /*	$NetBSD: hack.potion.c,v 1.7 2009/06/07 18:30:39 dholland Exp $	*/
2 
3 /*
4  * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica,
5  * Amsterdam
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are
10  * met:
11  *
12  * - Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * - 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  *
19  * - Neither the name of the Stichting Centrum voor Wiskunde en
20  * Informatica, nor the names of its contributors may be used to endorse or
21  * promote products derived from this software without specific prior
22  * written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
25  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
27  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
28  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  */
36 
37 /*
38  * Copyright (c) 1982 Jay Fenlason <hack@gnu.org>
39  * All rights reserved.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 3. The name of the author may not be used to endorse or promote products
50  *    derived from this software without specific prior written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
53  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
54  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
55  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
56  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
57  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
58  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
59  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
60  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
61  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62  */
63 
64 #include <sys/cdefs.h>
65 #ifndef lint
66 __RCSID("$NetBSD: hack.potion.c,v 1.7 2009/06/07 18:30:39 dholland Exp $");
67 #endif				/* not lint */
68 
69 #include "hack.h"
70 #include "extern.h"
71 
72 int
73 dodrink(void)
74 {
75 	struct obj     *otmp, *objs;
76 	struct monst   *mtmp;
77 	int             unkn = 0, nothing = 0;
78 
79 	otmp = getobj("!", "drink");
80 	if (!otmp)
81 		return (0);
82 	if (!strcmp(objects[otmp->otyp].oc_descr, "smoky") && !rn2(13)) {
83 		ghost_from_bottle();
84 		goto use_it;
85 	}
86 	switch (otmp->otyp) {
87 	case POT_RESTORE_STRENGTH:
88 		unkn++;
89 		pline("Wow!  This makes you feel great!");
90 		if (u.ustr < u.ustrmax) {
91 			u.ustr = u.ustrmax;
92 			flags.botl = 1;
93 		}
94 		break;
95 	case POT_BOOZE:
96 		unkn++;
97 		pline("Ooph!  This tastes like liquid fire!");
98 		Confusion += d(3, 8);
99 		/* the whiskey makes us feel better */
100 		if (u.uhp < u.uhpmax)
101 			losehp(-1, "bottle of whiskey");
102 		if (!rn2(4)) {
103 			pline("You pass out.");
104 			multi = -rnd(15);
105 			nomovemsg = "You awake with a headache.";
106 		}
107 		break;
108 	case POT_INVISIBILITY:
109 		if (Invis || See_invisible)
110 			nothing++;
111 		else {
112 			if (!Blind)
113 				pline("Gee!  All of a sudden, you can't see yourself.");
114 			else
115 				pline("You feel rather airy."), unkn++;
116 			newsym(u.ux, u.uy);
117 		}
118 		Invis += rn1(15, 31);
119 		break;
120 	case POT_FRUIT_JUICE:
121 		pline("This tastes like fruit juice.");
122 		lesshungry(20);
123 		break;
124 	case POT_HEALING:
125 		pline("You begin to feel better.");
126 		flags.botl = 1;
127 		u.uhp += rnd(10);
128 		if (u.uhp > u.uhpmax)
129 			u.uhp = ++u.uhpmax;
130 		if (Blind)
131 			Blind = 1;	/* see on next move */
132 		if (Sick)
133 			Sick = 0;
134 		break;
135 	case POT_PARALYSIS:
136 		if (Levitation)
137 			pline("You are motionlessly suspended.");
138 		else
139 			pline("Your feet are frozen to the floor!");
140 		nomul(-(rn1(10, 25)));
141 		break;
142 	case POT_MONSTER_DETECTION:
143 		if (!fmon) {
144 			strange_feeling(otmp, "You feel threatened.");
145 			return (1);
146 		} else {
147 			cls();
148 			for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
149 				if (mtmp->mx > 0)
150 					at(mtmp->mx, mtmp->my, mtmp->data->mlet);
151 			prme();
152 			pline("You sense the presence of monsters.");
153 			more();
154 			docrt();
155 		}
156 		break;
157 	case POT_OBJECT_DETECTION:
158 		if (!fobj) {
159 			strange_feeling(otmp, "You feel a pull downward.");
160 			return (1);
161 		} else {
162 			for (objs = fobj; objs; objs = objs->nobj)
163 				if (objs->ox != u.ux || objs->oy != u.uy)
164 					goto outobjmap;
165 			pline("You sense the presence of objects close nearby.");
166 			break;
167 	outobjmap:
168 			cls();
169 			for (objs = fobj; objs; objs = objs->nobj)
170 				at(objs->ox, objs->oy, objs->olet);
171 			prme();
172 			pline("You sense the presence of objects.");
173 			more();
174 			docrt();
175 		}
176 		break;
177 	case POT_SICKNESS:
178 		pline("Yech! This stuff tastes like poison.");
179 		if (Poison_resistance)
180 			pline("(But in fact it was biologically contaminated orange juice.)");
181 		losestr(rn1(4, 3));
182 		losehp(rnd(10), "contaminated potion");
183 		break;
184 	case POT_CONFUSION:
185 		if (!Confusion)
186 			pline("Huh, What?  Where am I?");
187 		else
188 			nothing++;
189 		Confusion += rn1(7, 16);
190 		break;
191 	case POT_GAIN_STRENGTH:
192 		pline("Wow do you feel strong!");
193 		if (u.ustr >= 118)
194 			break;	/* > 118 is impossible */
195 		if (u.ustr > 17)
196 			u.ustr += rnd(118 - u.ustr);
197 		else
198 			u.ustr++;
199 		if (u.ustr > u.ustrmax)
200 			u.ustrmax = u.ustr;
201 		flags.botl = 1;
202 		break;
203 	case POT_SPEED:
204 		if (Wounded_legs) {
205 			heal_legs();
206 			unkn++;
207 			break;
208 		}
209 		if (!(Fast & ~INTRINSIC))
210 			pline("You are suddenly moving much faster.");
211 		else
212 			pline("Your legs get new energy."), unkn++;
213 		Fast += rn1(10, 100);
214 		break;
215 	case POT_BLINDNESS:
216 		if (!Blind)
217 			pline("A cloud of darkness falls upon you.");
218 		else
219 			nothing++;
220 		Blind += rn1(100, 250);
221 		seeoff(0);
222 		break;
223 	case POT_GAIN_LEVEL:
224 		pluslvl();
225 		break;
226 	case POT_EXTRA_HEALING:
227 		pline("You feel much better.");
228 		flags.botl = 1;
229 		u.uhp += d(2, 20) + 1;
230 		if (u.uhp > u.uhpmax)
231 			u.uhp = (u.uhpmax += 2);
232 		if (Blind)
233 			Blind = 1;
234 		if (Sick)
235 			Sick = 0;
236 		break;
237 	case POT_LEVITATION:
238 		if (!Levitation)
239 			float_up();
240 		else
241 			nothing++;
242 		Levitation += rnd(100);
243 		u.uprops[PROP(RIN_LEVITATION)].p_tofn = float_down;
244 		break;
245 	default:
246 		impossible("What a funny potion! (%u)", otmp->otyp);
247 		return (0);
248 	}
249 	if (nothing) {
250 		unkn++;
251 		pline("You have a peculiar feeling for a moment, then it passes.");
252 	}
253 	if (otmp->dknown && !objects[otmp->otyp].oc_name_known) {
254 		if (!unkn) {
255 			objects[otmp->otyp].oc_name_known = 1;
256 			more_experienced(0, 10);
257 		} else if (!objects[otmp->otyp].oc_uname)
258 			docall(otmp);
259 	}
260 use_it:
261 	useup(otmp);
262 	return (1);
263 }
264 
265 void
266 pluslvl(void)
267 {
268 	int num;
269 
270 	pline("You feel more experienced.");
271 	num = rnd(10);
272 	u.uhpmax += num;
273 	u.uhp += num;
274 	if (u.ulevel < 14) {
275 		u.uexp = newuexp() + 1;
276 		pline("Welcome to experience level %u.", ++u.ulevel);
277 	}
278 	flags.botl = 1;
279 }
280 
281 void
282 strange_feeling(struct obj *obj, const char *txt)
283 {
284 	if (flags.beginner)
285 		pline("You have a strange feeling for a moment, then it passes.");
286 	else
287 		pline(txt);
288 	if (!objects[obj->otyp].oc_name_known && !objects[obj->otyp].oc_uname)
289 		docall(obj);
290 	useup(obj);
291 }
292 
293 const char           *const bottlenames[] = {
294 	"bottle", "phial", "flagon", "carafe", "flask", "jar", "vial"
295 };
296 
297 void
298 potionhit(struct monst *mon, struct obj *obj)
299 {
300 	const char           *botlnam = bottlenames[rn2(SIZE(bottlenames))];
301 	boolean         uclose, isyou = (mon == &youmonst);
302 
303 	if (isyou) {
304 		uclose = TRUE;
305 		pline("The %s crashes on your head and breaks into shivers.",
306 		      botlnam);
307 		losehp(rnd(2), "thrown potion");
308 	} else {
309 		uclose = (dist(mon->mx, mon->my) < 3);
310 		/* perhaps 'E' and 'a' have no head? */
311 		pline("The %s crashes on %s's head and breaks into shivers.",
312 		      botlnam, monnam(mon));
313 		if (rn2(5) && mon->mhp > 1)
314 			mon->mhp--;
315 	}
316 	pline("The %s evaporates.", xname(obj));
317 
318 	if (!isyou && !rn2(3))
319 		switch (obj->otyp) {
320 
321 		case POT_RESTORE_STRENGTH:
322 		case POT_GAIN_STRENGTH:
323 		case POT_HEALING:
324 		case POT_EXTRA_HEALING:
325 			if (mon->mhp < mon->mhpmax) {
326 				mon->mhp = mon->mhpmax;
327 				pline("%s looks sound and hale again!", Monnam(mon));
328 			}
329 			break;
330 		case POT_SICKNESS:
331 			if (mon->mhpmax > 3)
332 				mon->mhpmax /= 2;
333 			if (mon->mhp > 2)
334 				mon->mhp /= 2;
335 			break;
336 		case POT_CONFUSION:
337 		case POT_BOOZE:
338 			mon->mconf = 1;
339 			break;
340 		case POT_INVISIBILITY:
341 			unpmon(mon);
342 			mon->minvis = 1;
343 			pmon(mon);
344 			break;
345 		case POT_PARALYSIS:
346 			mon->mfroz = 1;
347 			break;
348 		case POT_SPEED:
349 			mon->mspeed = MFAST;
350 			break;
351 		case POT_BLINDNESS:
352 			mon->mblinded |= 64 + rn2(64);
353 			break;
354 			/*
355 			 * case POT_GAIN_LEVEL: case POT_LEVITATION: case
356 			 * POT_FRUIT_JUICE: case POT_MONSTER_DETECTION: case
357 			 * POT_OBJECT_DETECTION: break;
358 			 */
359 		}
360 	if (uclose && rn2(5))
361 		potionbreathe(obj);
362 	obfree(obj, Null(obj));
363 }
364 
365 void
366 potionbreathe(struct obj *obj)
367 {
368 	switch (obj->otyp) {
369 	case POT_RESTORE_STRENGTH:
370 	case POT_GAIN_STRENGTH:
371 		if (u.ustr < u.ustrmax)
372 			u.ustr++, flags.botl = 1;
373 		break;
374 	case POT_HEALING:
375 	case POT_EXTRA_HEALING:
376 		if (u.uhp < u.uhpmax)
377 			u.uhp++, flags.botl = 1;
378 		break;
379 	case POT_SICKNESS:
380 		if (u.uhp <= 5)
381 			u.uhp = 1;
382 		else
383 			u.uhp -= 5;
384 		flags.botl = 1;
385 		break;
386 	case POT_CONFUSION:
387 	case POT_BOOZE:
388 		if (!Confusion)
389 			pline("You feel somewhat dizzy.");
390 		Confusion += rnd(5);
391 		break;
392 	case POT_INVISIBILITY:
393 		pline("For an instant you couldn't see your right hand.");
394 		break;
395 	case POT_PARALYSIS:
396 		pline("Something seems to be holding you.");
397 		nomul(-rnd(5));
398 		break;
399 	case POT_SPEED:
400 		Fast += rnd(5);
401 		pline("Your knees seem more flexible now.");
402 		break;
403 	case POT_BLINDNESS:
404 		if (!Blind)
405 			pline("It suddenly gets dark.");
406 		Blind += rnd(5);
407 		seeoff(0);
408 		break;
409 		/*
410 		 * case POT_GAIN_LEVEL: case POT_LEVITATION: case
411 		 * POT_FRUIT_JUICE: case POT_MONSTER_DETECTION: case
412 		 * POT_OBJECT_DETECTION: break;
413 		 */
414 	}
415 	/* note: no obfree() */
416 }
417 
418 /*
419  * -- rudimentary -- to do this correctly requires much more work
420  * -- all sharp weapons get one or more qualities derived from the potions
421  * -- texts on scrolls may be (partially) wiped out; do they become blank?
422  * --   or does their effect change, like under Confusion?
423  * -- all objects may be made invisible by POT_INVISIBILITY
424  * -- If the flask is small, can one dip a large object? Does it magically
425  * --   become a jug? Etc.
426  */
427 int
428 dodip(void)
429 {
430 	struct obj     *potion, *obj;
431 
432 	if (!(obj = getobj("#", "dip")))
433 		return (0);
434 	if (!(potion = getobj("!", "dip into")))
435 		return (0);
436 	pline("Interesting...");
437 	if (obj->otyp == ARROW || obj->otyp == DART ||
438 	    obj->otyp == CROSSBOW_BOLT) {
439 		if (potion->otyp == POT_SICKNESS) {
440 			useup(potion);
441 			if (obj->spe < 7)
442 				obj->spe++;	/* %% */
443 		}
444 	}
445 	return (1);
446 }
447 
448 void
449 ghost_from_bottle(void)
450 {
451 	struct monst   *mtmp;
452 
453 	if (!(mtmp = makemon(PM_GHOST, u.ux, u.uy))) {
454 		pline("This bottle turns out to be empty.");
455 		return;
456 	}
457 	mnexto(mtmp);
458 	pline("As you open the bottle, an enormous ghost emerges!");
459 	pline("You are frightened to death, and unable to move.");
460 	nomul(-3);
461 }
462