xref: /openbsd/games/hack/hack.potion.c (revision 7b36286a)
1 /*	$OpenBSD: hack.potion.c,v 1.4 2003/05/19 06:30:56 pjanzen 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 #ifndef lint
65 static const char rcsid[] = "$OpenBSD: hack.potion.c,v 1.4 2003/05/19 06:30:56 pjanzen Exp $";
66 #endif /* not lint */
67 
68 #include "hack.h"
69 extern char *nomovemsg;
70 extern struct monst youmonst;
71 extern struct monst *makemon();
72 
73 static void ghost_from_bottle(void);
74 
75 int
76 dodrink()
77 {
78 	struct obj *otmp,*objs;
79 	struct monst *mtmp;
80 	int unkn = 0, nothing = 0;
81 
82 	otmp = getobj("!", "drink");
83 	if(!otmp) return(0);
84 	if(!strcmp(objects[otmp->otyp].oc_descr, "smoky") && !rn2(13)) {
85 		ghost_from_bottle();
86 		goto use_it;
87 	}
88 	switch(otmp->otyp){
89 	case POT_RESTORE_STRENGTH:
90 		unkn++;
91 		pline("Wow!  This makes you feel great!");
92 		if(u.ustr < u.ustrmax) {
93 			u.ustr = u.ustrmax;
94 			flags.botl = 1;
95 		}
96 		break;
97 	case POT_BOOZE:
98 		unkn++;
99 		pline("Ooph!  This tastes like liquid fire!");
100 		Confusion += d(3,8);
101 		/* the whiskey makes us feel better */
102 		if(u.uhp < u.uhpmax) losehp(-1, "bottle of whiskey");
103 		if(!rn2(4)) {
104 			pline("You pass out.");
105 			multi = -rnd(15);
106 			nomovemsg = "You awake with a headache.";
107 		}
108 		break;
109 	case POT_INVISIBILITY:
110 		if(Invis || See_invisible)
111 		  nothing++;
112 		else {
113 		  if(!Blind)
114 		    pline("Gee!  All of a sudden, you can't see yourself.");
115 		  else
116 		    pline("You feel rather airy."), unkn++;
117 		  newsym(u.ux,u.uy);
118 		}
119 		Invis += rn1(15,31);
120 		break;
121 	case POT_FRUIT_JUICE:
122 		pline("This tastes like fruit juice.");
123 		lesshungry(20);
124 		break;
125 	case POT_HEALING:
126 		pline("You begin to feel better.");
127 		flags.botl = 1;
128 		u.uhp += rnd(10);
129 		if(u.uhp > u.uhpmax)
130 			u.uhp = ++u.uhpmax;
131 		if(Blind) Blind = 1;	/* see on next move */
132 		if(Sick) Sick = 0;
133 		break;
134 	case POT_PARALYSIS:
135 		if(Levitation)
136 			pline("You are motionlessly suspended.");
137 		else
138 			pline("Your feet are frozen to the floor!");
139 		nomul(-(rn1(10,25)));
140 		break;
141 	case POT_MONSTER_DETECTION:
142 		if(!fmon) {
143 			strange_feeling(otmp, "You feel threatened.");
144 			return(1);
145 		} else {
146 			cls();
147 			for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
148 				if(mtmp->mx > 0)
149 				at(mtmp->mx,mtmp->my,mtmp->data->mlet);
150 			prme();
151 			pline("You sense the presence of monsters.");
152 			more();
153 			docrt();
154 		}
155 		break;
156 	case POT_OBJECT_DETECTION:
157 		if(!fobj) {
158 			strange_feeling(otmp, "You feel a pull downward.");
159 			return(1);
160 		} else {
161 		    for(objs = fobj; objs; objs = objs->nobj)
162 			if(objs->ox != u.ux || objs->oy != u.uy)
163 				goto outobjmap;
164 		    pline("You sense the presence of objects close nearby.");
165 		    break;
166 		outobjmap:
167 			cls();
168 			for(objs = fobj; objs; objs = objs->nobj)
169 				at(objs->ox,objs->oy,objs->olet);
170 			prme();
171 			pline("You sense the presence of objects.");
172 			more();
173 			docrt();
174 		}
175 		break;
176 	case POT_SICKNESS:
177 		pline("Yech! This stuff tastes like poison.");
178 		if(Poison_resistance)
179     pline("(But in fact it was biologically contaminated orange juice.)");
180 		losestr(rn1(4,3));
181 		losehp(rnd(10), "contaminated potion");
182 		break;
183 	case POT_CONFUSION:
184 		if(!Confusion)
185 			pline("Huh, What?  Where am I?");
186 		else
187 			nothing++;
188 		Confusion += rn1(7,16);
189 		break;
190 	case POT_GAIN_STRENGTH:
191 		pline("Wow do you feel strong!");
192 		if(u.ustr >= 118) break;	/* > 118 is impossible */
193 		if(u.ustr > 17) u.ustr += rnd(118-u.ustr);
194 		else u.ustr++;
195 		if(u.ustr > u.ustrmax) u.ustrmax = u.ustr;
196 		flags.botl = 1;
197 		break;
198 	case POT_SPEED:
199 		if(Wounded_legs) {
200 			heal_legs();
201 			unkn++;
202 			break;
203 		}
204 		if(!(Fast & ~INTRINSIC))
205 			pline("You are suddenly moving much faster.");
206 		else
207 			pline("Your legs get new energy."), unkn++;
208 		Fast += rn1(10,100);
209 		break;
210 	case POT_BLINDNESS:
211 		if(!Blind)
212 			pline("A cloud of darkness falls upon you.");
213 		else
214 			nothing++;
215 		Blind += rn1(100,250);
216 		seeoff(0);
217 		break;
218 	case POT_GAIN_LEVEL:
219 		pluslvl();
220 		break;
221 	case POT_EXTRA_HEALING:
222 		pline("You feel much better.");
223 		flags.botl = 1;
224 		u.uhp += d(2,20)+1;
225 		if(u.uhp > u.uhpmax)
226 			u.uhp = (u.uhpmax += 2);
227 		if(Blind) Blind = 1;
228 		if(Sick) Sick = 0;
229 		break;
230 	case POT_LEVITATION:
231 		if(!Levitation)
232 			float_up();
233 		else
234 			nothing++;
235 		Levitation += rnd(100);
236 		u.uprops[PROP(RIN_LEVITATION)].p_tofn = float_down;
237 		break;
238 	default:
239 		impossible("What a funny potion! (%u)", otmp->otyp);
240 		return(0);
241 	}
242 	if(nothing) {
243 	    unkn++;
244 	    pline("You have a peculiar feeling for a moment, then it passes.");
245 	}
246 	if(otmp->dknown && !objects[otmp->otyp].oc_name_known) {
247 		if(!unkn) {
248 			objects[otmp->otyp].oc_name_known = 1;
249 			more_experienced(0,10);
250 		} else if(!objects[otmp->otyp].oc_uname)
251 			docall(otmp);
252 	}
253 use_it:
254 	useup(otmp);
255 	return(1);
256 }
257 
258 void
259 pluslvl()
260 {
261 	int num;
262 
263 	pline("You feel more experienced.");
264 	num = rnd(10);
265 	u.uhpmax += num;
266 	u.uhp += num;
267 	if(u.ulevel < 14) {
268 		u.uexp = newuexp()+1;
269 		pline("Welcome to experience level %u.", ++u.ulevel);
270 	}
271 	flags.botl = 1;
272 }
273 
274 void
275 strange_feeling(struct obj *obj, char *txt)
276 {
277 	if(flags.beginner)
278 	    pline("You have a strange feeling for a moment, then it passes.");
279 	else
280 	    pline(txt);
281 	if(!objects[obj->otyp].oc_name_known && !objects[obj->otyp].oc_uname)
282 		docall(obj);
283 	useup(obj);
284 }
285 
286 char *bottlenames[] = {
287 	"bottle", "phial", "flagon", "carafe", "flask", "jar", "vial"
288 };
289 
290 void
291 potionhit(struct monst *mon, struct obj *obj)
292 {
293 	char *botlnam = bottlenames[rn2(SIZE(bottlenames))];
294 	boolean uclose, isyou = (mon == &youmonst);
295 
296 	if(isyou) {
297 		uclose = TRUE;
298 		pline("The %s crashes on your head and breaks into shivers.",
299 			botlnam);
300 		losehp(rnd(2), "thrown potion");
301 	} else {
302 		uclose = (dist(mon->mx,mon->my) < 3);
303 		/* perhaps 'E' and 'a' have no head? */
304 		pline("The %s crashes on %s's head and breaks into shivers.",
305 			botlnam, monnam(mon));
306 		if(rn2(5) && mon->mhp > 1)
307 			mon->mhp--;
308 	}
309 	pline("The %s evaporates.", xname(obj));
310 
311 	if(!isyou && !rn2(3)) switch(obj->otyp) {
312 
313 	case POT_RESTORE_STRENGTH:
314 	case POT_GAIN_STRENGTH:
315 	case POT_HEALING:
316 	case POT_EXTRA_HEALING:
317 		if(mon->mhp < mon->mhpmax) {
318 			mon->mhp = mon->mhpmax;
319 			pline("%s looks sound and hale again!", Monnam(mon));
320 		}
321 		break;
322 	case POT_SICKNESS:
323 		if(mon->mhpmax > 3)
324 			mon->mhpmax /= 2;
325 		if(mon->mhp > 2)
326 			mon->mhp /= 2;
327 		break;
328 	case POT_CONFUSION:
329 	case POT_BOOZE:
330 		mon->mconf = 1;
331 		break;
332 	case POT_INVISIBILITY:
333 		unpmon(mon);
334 		mon->minvis = 1;
335 		pmon(mon);
336 		break;
337 	case POT_PARALYSIS:
338 		mon->mfroz = 1;
339 		break;
340 	case POT_SPEED:
341 		mon->mspeed = MFAST;
342 		break;
343 	case POT_BLINDNESS:
344 		mon->mblinded |= 64 + rn2(64);
345 		break;
346 /*
347 	case POT_GAIN_LEVEL:
348 	case POT_LEVITATION:
349 	case POT_FRUIT_JUICE:
350 	case POT_MONSTER_DETECTION:
351 	case POT_OBJECT_DETECTION:
352 		break;
353 */
354 	}
355 	if(uclose && rn2(5))
356 		potionbreathe(obj);
357 	obfree(obj, Null(obj));
358 }
359 
360 void
361 potionbreathe(struct obj *obj)
362 {
363 	switch(obj->otyp) {
364 	case POT_RESTORE_STRENGTH:
365 	case POT_GAIN_STRENGTH:
366 		if(u.ustr < u.ustrmax) u.ustr++, flags.botl = 1;
367 		break;
368 	case POT_HEALING:
369 	case POT_EXTRA_HEALING:
370 		if(u.uhp < u.uhpmax) u.uhp++, flags.botl = 1;
371 		break;
372 	case POT_SICKNESS:
373 		if(u.uhp <= 5) u.uhp = 1; else u.uhp -= 5;
374 		flags.botl = 1;
375 		break;
376 	case POT_CONFUSION:
377 	case POT_BOOZE:
378 		if(!Confusion)
379 			pline("You feel somewhat dizzy.");
380 		Confusion += rnd(5);
381 		break;
382 	case POT_INVISIBILITY:
383 		pline("For an instant you couldn't see your right hand.");
384 		break;
385 	case POT_PARALYSIS:
386 		pline("Something seems to be holding you.");
387 		nomul(-rnd(5));
388 		break;
389 	case POT_SPEED:
390 		Fast += rnd(5);
391 		pline("Your knees seem more flexible now.");
392 		break;
393 	case POT_BLINDNESS:
394 		if(!Blind) pline("It suddenly gets dark.");
395 		Blind += rnd(5);
396 		seeoff(0);
397 		break;
398 /*
399 	case POT_GAIN_LEVEL:
400 	case POT_LEVITATION:
401 	case POT_FRUIT_JUICE:
402 	case POT_MONSTER_DETECTION:
403 	case POT_OBJECT_DETECTION:
404 		break;
405 */
406 	}
407 	/* note: no obfree() */
408 }
409 
410 /*
411  * -- rudimentary -- to do this correctly requires much more work
412  * -- all sharp weapons get one or more qualities derived from the potions
413  * -- texts on scrolls may be (partially) wiped out; do they become blank?
414  * --   or does their effect change, like under Confusion?
415  * -- all objects may be made invisible by POT_INVISIBILITY
416  * -- If the flask is small, can one dip a large object? Does it magically
417  * --   become a jug? Etc.
418  */
419 int
420 dodip()
421 {
422 	struct obj *potion, *obj;
423 
424 	if(!(obj = getobj("#", "dip")))
425 		return(0);
426 	if(!(potion = getobj("!", "dip into")))
427 		return(0);
428 	pline("Interesting...");
429 	if(obj->otyp == ARROW || obj->otyp == DART ||
430 	   obj->otyp == CROSSBOW_BOLT) {
431 		if(potion->otyp == POT_SICKNESS) {
432 			useup(potion);
433 			if(obj->spe < 7) obj->spe++;	/* %% */
434 		}
435 	}
436 	return(1);
437 }
438 
439 static void
440 ghost_from_bottle()
441 {
442 	extern struct permonst pm_ghost;
443 	struct monst *mtmp;
444 
445 	if(!(mtmp = makemon(PM_GHOST,u.ux,u.uy))){
446 		pline("This bottle turns out to be empty.");
447 		return;
448 	}
449 	mnexto(mtmp);
450 	pline("As you open the bottle, an enormous ghost emerges!");
451 	pline("You are frightened to death, and unable to move.");
452 	nomul(-3);
453 }
454