1 | /* SCCS Id: @(#)potion.c 3.3 2000/06/02 */
2 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 | /* NetHack may be freely redistributed. See license for details. */
4 |
5 | #include "hack.h"
6 |
7 | #ifdef OVLB
8 | boolean notonhead = FALSE;
9 |
10 | static NEARDATA int nothing, unkn;
11 | static NEARDATA const char beverages[] = { POTION_CLASS, 0 };
12 |
13 | STATIC_DCL long FDECL(itimeout, (long));
14 | STATIC_DCL long FDECL(itimeout_incr, (long,int));
15 | STATIC_DCL void NDECL(ghost_from_bottle);
16 | STATIC_DCL short FDECL(mixtype, (struct obj *,struct obj *));
17 |
18 | /* force `val' to be within valid range for intrinsic timeout value */
19 | STATIC_OVL long
20 | itimeout(val)
21 | long val;
22 | {
23 | if (val >= TIMEOUT) val = TIMEOUT;
24 | else if (val < 1) val = 0;
25 |
26 | return val;
27 | }
28 |
29 | /* increment `old' by `incr' and force result to be valid intrinsic timeout */
30 | STATIC_OVL long
31 | itimeout_incr(old, incr)
32 | long old;
33 | int incr;
34 | {
35 | return itimeout((old & TIMEOUT) + (long)incr);
36 | }
37 |
38 | /* set the timeout field of intrinsic `which' */
39 | void
40 | set_itimeout(which, val)
41 | long *which, val;
42 | {
43 | *which &= ~TIMEOUT;
44 | *which |= itimeout(val);
45 | }
46 |
47 | /* increment the timeout field of intrinsic `which' */
48 | void
49 | incr_itimeout(which, incr)
50 | long *which;
51 | int incr;
52 | {
53 | set_itimeout(which, itimeout_incr(*which, incr));
54 | }
55 |
56 | void
57 | make_confused(xtime,talk)
58 | long xtime;
59 | boolean talk;
60 | {
61 | long old = HConfusion;
62 |
63 | if (!xtime && old) {
64 | if (talk)
65 | You_feel("less %s now.",
66 | Hallucination ? "trippy" : "confused");
67 | }
68 | if ((xtime && !old) || (!xtime && old)) flags.botl = TRUE;
69 |
70 | set_itimeout(&HConfusion, xtime);
71 | }
72 |
73 | void
74 | make_stunned(xtime,talk)
75 | long xtime;
76 | boolean talk;
77 | {
78 | long old = HStun;
79 |
80 | if (!xtime && old) {
81 | if (talk)
82 | You_feel("%s now.",
83 | Hallucination ? "less wobbly" : "a bit steadier");
84 | }
85 | if (xtime && !old) {
86 | if (talk) You("stagger...");
87 | }
88 | if ((!xtime && old) || (xtime && !old)) flags.botl = TRUE;
89 |
90 | set_itimeout(&HStun, xtime);
91 | }
92 |
93 | void
94 | make_sick(xtime, cause, talk, type)
95 | long xtime;
96 | const char *cause; /* sickness cause */
97 | boolean talk;
98 | int type;
99 | {
100 | long old = Sick;
101 |
102 | if (xtime > 0L) {
103 | if (Sick_resistance) return;
104 | if (!old) {
105 | /* newly sick */
106 | You_feel("deathly sick.");
107 | } else {
108 | /* already sick */
109 | if (talk) You_feel("%s worse.",
110 | xtime <= Sick/2L ? "much" : "even");
111 | }
112 | set_itimeout(&Sick, xtime);
113 | u.usick_type |= type;
114 | flags.botl = TRUE;
115 | } else if (old && (type & u.usick_type)) {
116 | /* was sick, now not */
117 | u.usick_type &= ~type;
118 | if (u.usick_type) { /* only partly cured */
119 | if (talk) You_feel("somewhat better.");
120 | set_itimeout(&Sick, Sick * 2); /* approximation */
121 | } else {
122 | if (talk) pline("What a relief!");
123 | Sick = 0L; /* set_itimeout(&Sick, 0L) */
124 | }
125 | flags.botl = TRUE;
126 | }
127 |
128 | if (Sick) {
129 | exercise(A_CON, FALSE);
130 | if (cause) {
131 | (void) strncpy(u.usick_cause, cause, sizeof(u.usick_cause));
132 | u.usick_cause[sizeof(u.usick_cause)-1] = 0;
133 | }
134 | else
135 | u.usick_cause[0] = 0;
136 | } else
137 | u.usick_cause[0] = 0;
138 | }
139 |
140 | void
141 | make_vomiting(xtime, talk)
142 | long xtime;
143 | boolean talk;
144 | {
145 | long old = Vomiting;
146 |
147 | if(!xtime && old)
148 | if(talk) You_feel("much less nauseous now.");
149 |
150 | set_itimeout(&Vomiting, xtime);
151 | }
152 |
153 |
154 | void
155 | make_blinded(xtime, talk)
156 | long xtime;
157 | boolean talk;
158 | {
159 | long old = Blinded;
160 | boolean changed = FALSE;
161 |
162 | if (u.usleep) talk = FALSE;
163 |
164 | if (!xtime && old && !Blindfolded && haseyes(youmonst.data)) {
165 | if (talk) {
166 | if (Hallucination)
167 | pline("Far out! Everything is all cosmic again!");
168 | else You("can see again.");
169 | }
170 | changed = TRUE;
171 | }
172 | if (xtime && !old && !Blindfolded && haseyes(youmonst.data)) {
173 | if (talk) {
174 | if (Hallucination)
175 | pline("Oh, bummer! Everything is dark! Help!");
176 | else
177 | pline("A cloud of darkness falls upon you.");
178 | }
179 | changed = TRUE;
180 |
181 | /* Before the hero goes blind, set the ball&chain variables. */
182 | if (Punished) set_bc(0);
183 | }
184 | set_itimeout(&Blinded, xtime);
185 | if (changed) {
186 | flags.botl = 1;
187 | vision_full_recalc = 1;
188 | if (Blind_telepat || Infravision) see_monsters();
189 | }
190 | }
191 |
192 | void
193 | make_hallucinated(xtime, talk, mask)
194 | long xtime; /* nonzero if this is an attempt to turn on hallucination */
195 | boolean talk;
196 | long mask; /* nonzero if resistance status should change by mask */
197 | {
198 | boolean changed = 0;
199 | #ifdef LINT
200 | const char *message = 0;
201 | #else
202 | const char *message;
203 | #endif
204 |
205 | if (!xtime)
206 | message = "Everything looks SO boring now.";
207 | else
208 | message = "Oh wow! Everything seems so cosmic!";
209 |
210 | if (mask) {
211 | if (HHallucination) changed = TRUE;
212 |
213 | if (!xtime) EHalluc_resistance |= mask;
214 | else EHalluc_resistance &= ~mask;
215 | } else {
216 | if (!EHalluc_resistance && (!!HHallucination != !!xtime))
217 | changed = TRUE;
218 | set_itimeout(&HHallucination, xtime);
219 | }
220 |
221 | if (changed) {
222 | if (u.uswallow) {
223 | swallowed(0); /* redraw swallow display */
224 | } else {
225 | /* The see_* routines should be called *before* the pline. */
226 | see_monsters();
227 | see_objects();
228 | see_traps();
229 | }
230 | flags.botl = 1;
231 | if (!Blind && talk) pline(message);
232 | }
233 | }
234 |
235 | STATIC_OVL void
236 | ghost_from_bottle()
237 | {
238 | struct monst *mtmp = makemon(&mons[PM_GHOST], u.ux, u.uy, NO_MM_FLAGS);
239 |
240 | if (!mtmp) {
241 | pline("This bottle turns out to be empty.");
242 | return;
243 | }
244 | if (Blind) {
245 | pline("As you open the bottle, %s emerges.", something);
246 | return;
247 | }
248 | pline("As you open the bottle, an enormous %s emerges!",
249 | Hallucination ? rndmonnam() : (const char *)"ghost");
250 | if(flags.verbose)
251 | You("are frightened to death, and unable to move.");
252 | nomul(-3);
253 | nomovemsg = "You regain your composure.";
254 | }
255 |
256 | int
257 | dodrink() {
258 | register struct obj *otmp;
259 | const char *potion_descr;
260 |
261 | if (Strangled) {
262 | pline("If you can't breathe air, how can you drink liquid?");
263 | return 0;
264 | }
265 | /* Is there a fountain to drink from here? */
266 | if (IS_FOUNTAIN(levl[u.ux][u.uy].typ) && !Levitation) {
267 | if(yn("Drink from the fountain?") == 'y') {
268 | drinkfountain();
269 | return 1;
270 | }
271 | }
272 | #ifdef SINKS
273 | /* Or a kitchen sink? */
274 | if (IS_SINK(levl[u.ux][u.uy].typ)) {
275 | if (yn("Drink from the sink?") == 'y') {
276 | drinksink();
277 | return 1;
278 | }
279 | }
280 | #endif
281 |
282 | /* Or are you surrounded by water? */
283 | if (Underwater) {
284 | if (yn("Drink the water around you?") == 'y') {
285 | pline("Do you know what lives in this water!");
286 | return 1;
287 | }
288 | }
289 |
290 | otmp = getobj(beverages, "drink");
291 | if(!otmp) return(0);
292 | otmp->in_use = TRUE; /* you've opened the stopper */
293 |
294 | #define POTION_OCCUPANT_CHANCE(n) (13 + 2*(n)) /* also in muse.c */
295 |
296 | potion_descr = OBJ_DESCR(objects[otmp->otyp]);
297 | if (potion_descr) {
298 | if (!strcmp(potion_descr, "milky") &&
299 | flags.ghost_count < MAXMONNO &&
300 | !rn2(POTION_OCCUPANT_CHANCE(flags.ghost_count))) {
301 | ghost_from_bottle();
302 | useup(otmp);
303 | return(1);
304 | } else if (!strcmp(potion_descr, "smoky") &&
305 | flags.djinni_count < MAXMONNO &&
306 | !rn2(POTION_OCCUPANT_CHANCE(flags.djinni_count))) {
307 | djinni_from_bottle(otmp);
308 | useup(otmp);
309 | return(1);
310 | }
311 | }
312 | return dopotion(otmp);
313 | }
314 |
315 | int
316 | dopotion(otmp)
317 | register struct obj *otmp;
318 | {
319 | int retval;
320 |
321 | nothing = unkn = 0;
322 | if((retval = peffects(otmp)) >= 0) return(retval);
323 |
324 | if(nothing) {
325 | unkn++;
326 | You("have a %s feeling for a moment, then it passes.",
327 | Hallucination ? "normal" : "peculiar");
328 | }
329 | if(otmp->dknown && !objects[otmp->otyp].oc_name_known) {
330 | if(!unkn) {
331 | makeknown(otmp->otyp);
332 | more_experienced(0,10);
333 | } else if(!objects[otmp->otyp].oc_uname)
334 | docall(otmp);
335 | }
336 | useup(otmp);
337 | return(1);
338 | }
339 |
340 | int
341 | peffects(otmp)
342 | register struct obj *otmp;
343 | {
344 | register int i, ii, lim;
345 |
346 | switch(otmp->otyp){
347 | case POT_RESTORE_ABILITY:
348 | case SPE_RESTORE_ABILITY:
349 | unkn++;
350 | if(otmp->cursed) {
351 | pline("Ulch! This makes you feel mediocre!");
352 | break;
353 | } else {
354 | pline("Wow! This makes you feel %s!",
355 | (otmp->blessed) ? "great" : "good");
356 | i = rn2(A_MAX); /* start at a random point */
357 | for (ii = 0; ii < A_MAX; ii++) {
358 | lim = AMAX(i);
359 | if (i == A_STR && u.uhs >= 3) --lim; /* WEAK */
360 | if (ABASE(i) < lim) {
361 | ABASE(i) = lim;
362 | flags.botl = 1;
363 | /* only first found if not blessed */
364 | if (!otmp->blessed) break;
365 | }
366 | if(++i >= A_MAX) i = 0;
367 | }
368 | }
369 | break;
370 | case POT_HALLUCINATION:
371 | if (Hallucination || Halluc_resistance) nothing++;
372 | make_hallucinated(itimeout_incr(HHallucination,
373 | rn1(200, 600 - 300 * bcsign(otmp))),
374 | TRUE, 0L);
375 | break;
376 | case POT_WATER:
377 | if(!otmp->blessed && !otmp->cursed) {
378 | pline("This tastes like water.");
379 | u.uhunger += rnd(10);
380 | newuhs(FALSE);
381 | break;
382 | }
383 | unkn++;
384 | if(is_undead(youmonst.data) || is_demon(youmonst.data) ||
385 | u.ualign.type == A_CHAOTIC) {
386 | if(otmp->blessed) {
387 | pline("This burns like acid!");
388 | exercise(A_CON, FALSE);
389 | if (u.ulycn >= LOW_PM) {
390 | Your("affinity to %s disappears!",
391 | makeplural(mons[u.ulycn].mname));
392 | if (youmonst.data == &mons[u.ulycn])
393 | you_unwere(FALSE);
394 | u.ulycn = NON_PM; /* cure lycanthropy */
395 | }
396 | losehp(d(2,6), "potion of holy water", KILLED_BY_AN);
397 | } else if(otmp->cursed) {
398 | You_feel("quite proud of yourself.");
399 | healup(d(2,6),0,0,0);
400 | if (u.ulycn >= LOW_PM && !Upolyd) you_were();
401 | exercise(A_CON, TRUE);
402 | }
403 | } else {
404 | if(otmp->blessed) {
405 | You_feel("full of awe.");
406 | make_sick(0L, (char *) 0, TRUE, SICK_ALL);
407 | exercise(A_WIS, TRUE);
408 | exercise(A_CON, TRUE);
409 | if (u.ulycn >= LOW_PM)
410 | you_unwere(TRUE); /* "Purified" */
411 | /* make_confused(0L,TRUE); */
412 | } else {
413 | if(u.ualign.type == A_LAWFUL) {
414 | pline("This burns like acid!");
415 | losehp(d(2,6), "potion of unholy water",
416 | KILLED_BY_AN);
417 | } else
418 | You_feel("full of dread.");
419 | if (u.ulycn >= LOW_PM && !Upolyd) you_were();
420 | exercise(A_CON, FALSE);
421 | }
422 | }
423 | break;
424 | case POT_BOOZE:
425 | unkn++;
426 | pline("Ooph! This tastes like %s%s!",
427 | otmp->odiluted ? "watered down " : "",
428 | Hallucination ? "dandelion wine" : "liquid fire");
429 | if (!otmp->blessed)
430 | make_confused(itimeout_incr(HConfusion, d(3,8)), FALSE);
431 | /* the whiskey makes us feel better */
432 | if (!otmp->odiluted) healup(1, 0, FALSE, FALSE);
433 | u.uhunger += 10 * (2 + bcsign(otmp));
434 | newuhs(FALSE);
435 | exercise(A_WIS, FALSE);
436 | if(otmp->cursed) {
437 | You("pass out.");
438 | multi = -rnd(15);
439 | nomovemsg = "You awake with a headache.";
440 | }
441 | break;
442 | case POT_ENLIGHTENMENT:
443 | if(otmp->cursed) {
444 | unkn++;
445 | You("have an uneasy feeling...");
446 | exercise(A_WIS, FALSE);
447 | } else {
448 | if (otmp->blessed) {
449 | (void) adjattrib(A_INT, 1, FALSE);
450 | (void) adjattrib(A_WIS, 1, FALSE);
451 | }
452 | You_feel("self-knowledgeable...");
453 | display_nhwindow(WIN_MESSAGE, FALSE);
454 | enlightenment(0);
455 | pline_The("feeling subsides.");
456 | exercise(A_WIS, TRUE);
457 | }
458 | break;
459 | case SPE_INVISIBILITY:
460 | /* spell cannot penetrate mummy wrapping */
461 | if (BInvis && uarmc->otyp == MUMMY_WRAPPING) {
462 | You_feel("rather itchy under your %s.", xname(uarmc));
463 | break;
464 | }
465 | /* FALLTHRU */
466 | case POT_INVISIBILITY:
467 | if (Invis || Blind || BInvis) {
468 | nothing++;
469 | } else {
470 | self_invis_message();
471 | }
472 | if (otmp->blessed) HInvis |= FROMOUTSIDE;
473 | else incr_itimeout(&HInvis, rn1(15,31));
474 | newsym(u.ux,u.uy); /* update position */
475 | if(otmp->cursed) {
476 | pline("For some reason, you feel your presence is known.");
477 | aggravate();
478 | }
479 | break;
480 | case POT_SEE_INVISIBLE:
481 | /* tastes like fruit juice in Rogue */
482 | case POT_FRUIT_JUICE:
483 | {
484 | int msg = Invisible && !Blind;
485 |
486 | unkn++;
487 | if (otmp->cursed)
488 | pline("Yecch! This tastes %s.",
489 | Hallucination ? "overripe" : "rotten");
490 | else pline(Hallucination ?
491 | "This tastes like 10%% real %s%s juice all-natural beverage." :
492 | "This tastes like %s%s juice.",
493 | otmp->odiluted ? "reconstituted " : "", pl_fruit);
494 | if (otmp->otyp == POT_FRUIT_JUICE) {
495 | u.uhunger += (otmp->odiluted ? 5 : 10) * (2 + bcsign(otmp));
496 | newuhs(FALSE);
497 | break;
498 | }
499 | if (!otmp->cursed) {
500 | /* Tell them they can see again immediately, which
501 | * will help them identify the potion...
502 | */
503 | make_blinded(0L,TRUE);
504 | }
505 | if (otmp->blessed)
506 | HSee_invisible |= FROMOUTSIDE;
507 | else
508 | incr_itimeout(&HSee_invisible, rn1(100,750));
509 | set_mimic_blocking(); /* do special mimic handling */
510 | see_monsters(); /* see invisible monsters */
511 | newsym(u.ux,u.uy); /* see yourself! */
512 | if (msg && !Blind) { /* Blind possible if polymorphed */
513 | You("can see through yourself, but you are visible!");
514 | unkn--;
515 | }
516 | break;
517 | }
518 | case POT_PARALYSIS:
519 | if (Free_action)
520 | You("stiffen momentarily.");
521 | else {
522 | if (Levitation||Is_airlevel(&u.uz)||Is_waterlevel(&u.uz))
523 | You("are motionlessly suspended.");
524 | #ifdef STEED
525 | else if (u.usteed)
526 | You("are frozen in place!");
527 | #endif
528 | else
529 | Your("%s are frozen to the %s!",
530 | makeplural(body_part(FOOT)), surface(u.ux, u.uy));
531 | nomul(-(rn1(10, 25 - 12*bcsign(otmp))));
532 | nomovemsg = You_can_move_again;
533 | exercise(A_DEX, FALSE);
534 | }
535 | break;
536 | case POT_SLEEPING:
537 | if(Sleep_resistance || Free_action)
538 | You("yawn.");
539 | else {
540 | You("suddenly fall asleep!");
541 | fall_asleep(-rn1(10, 25 - 12*bcsign(otmp)), TRUE);
542 | }
543 | break;
544 | case POT_MONSTER_DETECTION:
545 | case SPE_DETECT_MONSTERS:
546 | if (otmp->blessed) {
547 | int x, y;
548 |
549 | if (Detect_monsters) nothing++;
550 | unkn++;
551 | incr_itimeout(&HDetect_monsters, 20+rnd(40));
552 | for (x = 1; x < COLNO; x++) {
553 | for (y = 0; y < ROWNO; y++) {
554 | if (levl[x][y].glyph == GLYPH_INVISIBLE) {
555 | unmap_object(x, y);
556 | newsym(x,y);
557 | }
558 | if (MON_AT(x,y)) unkn = 0;
559 | }
560 | }
561 | see_monsters();
562 | if (unkn) You_feel("lonely.");
563 | break;
564 | }
565 | if (monster_detect(otmp, 0))
566 | return(1); /* nothing detected */
567 | exercise(A_WIS, TRUE);
568 | break;
569 | case POT_OBJECT_DETECTION:
570 | case SPE_DETECT_TREASURE:
571 | if (object_detect(otmp, 0))
572 | return(1); /* nothing detected */
573 | exercise(A_WIS, TRUE);
574 | break;
575 | case POT_SICKNESS:
576 | pline("Yecch! This stuff tastes like poison.");
577 | if (otmp->blessed) {
578 | pline("(But in fact it was mildly stale %s juice.)",
579 | pl_fruit);
580 | if (!Role_if(PM_HEALER))
581 | losehp(1, "mildly contaminated potion", KILLED_BY_AN);
582 | } else {
583 | if(Poison_resistance)
584 | pline(
585 | "(But in fact it was biologically contaminated %s juice.)",
586 | pl_fruit);
587 | if (Role_if(PM_HEALER))
588 | pline("Fortunately, you have been immunized.");
589 | else {
590 | int typ = rn2(A_MAX);
591 |
592 | if (!Fixed_abil) {
593 | poisontell(typ);
594 | (void) adjattrib(typ,
595 | Poison_resistance ? -1 : -rn1(4,3),
596 | TRUE);
597 | }
598 | if(!Poison_resistance)
599 | losehp(rnd(10)+5*!!(otmp->cursed),
600 | "contaminated potion", KILLED_BY_AN);
601 | exercise(A_CON, FALSE);
602 | }
603 | }
604 | if(Hallucination) {
605 | You("are shocked back to your senses!");
606 | make_hallucinated(0L,FALSE,0L);
607 | }
608 | break;
609 | case POT_CONFUSION:
610 | if(!Confusion)
611 | if (Hallucination) {
612 | pline("What a trippy feeling!");
613 | unkn++;
614 | } else
615 | pline("Huh, What? Where am I?");
616 | else nothing++;
617 | make_confused(itimeout_incr(HConfusion,
618 | rn1(7, 16 - 8 * bcsign(otmp))),
619 | FALSE);
620 | break;
621 | case POT_GAIN_ABILITY:
622 | if(otmp->cursed) {
623 | pline("Ulch! That potion tasted foul!");
624 | unkn++;
625 | } else { /* If blessed, increase all; if not, try up to */
626 | int itmp; /* 6 times to find one which can be increased. */
627 | i = -1; /* increment to 0 */
628 | for (ii = A_MAX; ii > 0; ii--) {
629 | i = (otmp->blessed ? i + 1 : rn2(A_MAX));
630 | /* only give "your X is already as high as it can get"
631 | message on last attempt (except blessed potions) */
632 | itmp = (otmp->blessed || ii == 1) ? 0 : -1;
633 | if (adjattrib(i, 1, itmp) && !otmp->blessed)
634 | break;
635 | }
636 | }
637 | break;
638 | case POT_SPEED:
639 | if(Wounded_legs && !otmp->cursed) {
640 | heal_legs();
641 | unkn++;
642 | break;
643 | } /* and fall through */
644 | case SPE_HASTE_SELF:
645 | if(!Very_fast) /* wwf@doe.carleton.ca */
646 | You("are suddenly moving %sfaster.",
647 | Fast ? "" : "much ");
648 | else {
649 | Your("%s get new energy.",
650 | makeplural(body_part(LEG)));
651 | unkn++;
652 | }
653 | exercise(A_DEX, TRUE);
654 | incr_itimeout(&HFast, rn1(10, 100 + 60 * bcsign(otmp)));
655 | break;
656 | case POT_BLINDNESS:
657 | if(Blind) nothing++;
658 | make_blinded(itimeout_incr(Blinded,
659 | rn1(200, 250 - 125 * bcsign(otmp))),
660 | TRUE);
661 | break;
662 | case POT_GAIN_LEVEL:
663 | if (otmp->cursed) {
664 | unkn++;
665 | /* they went up a level */
666 | if((ledger_no(&u.uz) == 1 && u.uhave.amulet) ||
667 | Can_rise_up(u.ux, u.uy, &u.uz)) {
668 | const char *riseup ="rise up, through the %s!";
669 | if(ledger_no(&u.uz) == 1) {
670 | You(riseup, ceiling(u.ux,u.uy));
671 | goto_level(&earth_level, FALSE, FALSE, FALSE);
672 | } else {
673 | register int newlev = depth(&u.uz)-1;
674 | d_level newlevel;
675 |
676 | get_level(&newlevel, newlev);
677 | if(on_level(&newlevel, &u.uz)) {
678 | pline("It tasted bad.");
679 | break;
680 | } else You(riseup, ceiling(u.ux,u.uy));
681 | goto_level(&newlevel, FALSE, FALSE, FALSE);
682 | }
683 | }
684 | else You("have an uneasy feeling.");
685 | break;
686 | }
687 | pluslvl(FALSE);
688 | if (otmp->blessed)
689 | /* blessed potions place you at a random spot in the
690 | * middle of the new level instead of the low point
691 | */
692 | u.uexp = rndexp();
693 | break;
694 | case POT_HEALING:
695 | You_feel("better.");
696 | healup(d(6 + 2 * bcsign(otmp), 4),
697 | !otmp->cursed ? 1 : 0, !!otmp->blessed, !otmp->cursed);
698 | exercise(A_CON, TRUE);
699 | break;
700 | case POT_EXTRA_HEALING:
701 | You_feel("much better.");
702 | healup(d(6 + 2 * bcsign(otmp), 8),
703 | otmp->blessed ? 5 : !otmp->cursed ? 2 : 0,
704 | !otmp->cursed, TRUE);
705 | make_hallucinated(0L,TRUE,0L);
706 | exercise(A_CON, TRUE);
707 | exercise(A_STR, TRUE);
708 | break;
709 | case POT_FULL_HEALING:
710 | You_feel("completely healed.");
711 | healup(400, 4+4*bcsign(otmp), !otmp->cursed, TRUE);
712 | /* Restore one lost level if blessed */
713 | if (otmp->blessed && (u.ulevel < u.ulevelmax))
714 | pluslvl(FALSE);
715 | make_hallucinated(0L,TRUE,0L);
716 | exercise(A_STR, TRUE);
717 | exercise(A_CON, TRUE);
718 | break;
719 | case POT_LEVITATION:
720 | case SPE_LEVITATION:
721 | if (otmp->cursed) HLevitation &= ~I_SPECIAL;
722 | if(!Levitation) {
723 | /* kludge to ensure proper operation of float_up() */
724 | HLevitation = 1;
725 | float_up();
726 | /* reverse kludge */
727 | HLevitation = 0;
728 | if (otmp->cursed && !Is_waterlevel(&u.uz)) {
729 | if((u.ux != xupstair || u.uy != yupstair)
730 | && (u.ux != sstairs.sx || u.uy != sstairs.sy || !sstairs.up)
731 | && (!xupladder || u.ux != xupladder || u.uy != yupladder)
732 | ) {
733 | You("hit your %s on the %s.",
734 | body_part(HEAD),
735 | ceiling(u.ux,u.uy));
736 | losehp(uarmh ? 1 : rnd(10),
737 | "colliding with the ceiling",
738 | KILLED_BY);
739 | } else (void) doup();
740 | }
741 | } else
742 | nothing++;
743 | if (otmp->blessed) {
744 | incr_itimeout(&HLevitation, rn1(50,250));
745 | HLevitation |= I_SPECIAL;
746 | } else incr_itimeout(&HLevitation, rn1(140,10));
747 | break;
748 | case POT_GAIN_ENERGY: /* M. Stephenson */
749 | { register int num;
750 | if(otmp->cursed)
751 | You_feel("lackluster.");
752 | else
753 | pline("Magical energies course through your body.");
754 | num = rnd(5) + 5 * otmp->blessed + 1;
755 | u.uenmax += (otmp->cursed) ? -num : num;
756 | u.uen += (otmp->cursed) ? -num : num;
757 | if(u.uenmax <= 0) u.uenmax = 0;
758 | if(u.uen <= 0) u.uen = 0;
759 | flags.botl = 1;
760 | exercise(A_WIS, TRUE);
761 | }
762 | break;
763 | case POT_OIL: /* P. Winner */
764 | {
765 | boolean good_for_you = FALSE;
766 |
767 | if (otmp->lamplit) {
768 | if (likes_fire(youmonst.data)) {
769 | pline("Ahh, a refreshing drink.");
770 | good_for_you = TRUE;
771 | } else {
772 | You("burn your %s.", body_part(FACE));
773 | losehp(d(Fire_resistance ? 1 : 3, 4),
774 | "burning potion of oil", KILLED_BY_AN);
775 | }
776 | } else if(otmp->cursed)
777 | pline("This tastes like castor oil.");
778 | else
779 | pline("That was smooth!");
780 | exercise(A_WIS, good_for_you);
781 | }
782 | break;
783 | case POT_ACID:
784 | if (Acid_resistance)
785 | /* Not necessarily a creature who _likes_ acid */
786 | pline("This tastes %s.", Hallucination ? "tangy" : "sour");
787 | else {
788 | pline("This burns%s!", otmp->blessed ? " a little" :
789 | otmp->cursed ? " a lot" : "");
790 | losehp(d(otmp->cursed ? 2 : 1, otmp->blessed ? 4 : 8),
791 | "potion of acid", KILLED_BY_AN);
792 | exercise(A_CON, FALSE);
793 | }
794 | if (Stoned) fix_petrification();
795 | break;
796 | case POT_POLYMORPH:
797 | You_feel("a little %s.", Hallucination ? "normal" : "strange");
798 | if (!Unchanging) polyself();
799 | break;
800 | default:
801 | impossible("What a funny potion! (%u)", otmp->otyp);
802 | return(0);
803 | }
804 | return(-1);
805 | }
806 |
807 | void
808 | healup(nhp, nxtra, curesick, cureblind)
809 | int nhp, nxtra;
810 | register boolean curesick, cureblind;
811 | {
812 | if (nhp) {
813 | if (Upolyd) {
814 | u.mh += nhp;
815 | if (u.mh > u.mhmax) u.mh = (u.mhmax += nxtra);
816 | } else {
817 | u.uhp += nhp;
818 | if(u.uhp > u.uhpmax) u.uhp = (u.uhpmax += nxtra);
819 | }
820 | }
821 | if(cureblind) make_blinded(0L,TRUE);
822 | if(curesick) make_sick(0L, (char *) 0, TRUE, SICK_ALL);
823 | flags.botl = 1;
824 | return;
825 | }
826 |
827 | void
828 | strange_feeling(obj,txt)
829 | register struct obj *obj;
830 | register const char *txt;
831 | {
832 | if(flags.beginner)
833 | You("have a %s feeling for a moment, then it passes.",
834 | Hallucination ? "normal" : "strange");
835 | else
836 | pline(txt);
837 |
838 | if(!obj) /* e.g., crystal ball finds no traps */
839 | return;
840 |
841 | if(obj->dknown && !objects[obj->otyp].oc_name_known &&
842 | !objects[obj->otyp].oc_uname)
843 | docall(obj);
844 | useup(obj);
845 | }
846 |
847 | const char *bottlenames[] = {
848 | "bottle", "phial", "flagon", "carafe", "flask", "jar", "vial"
849 | };
850 |
851 | void
852 | potionhit(mon, obj, your_fault)
853 | register struct monst *mon;
854 | register struct obj *obj;
855 | boolean your_fault;
856 | {
857 | register const char *botlnam = bottlenames[rn2(SIZE(bottlenames))];
858 | boolean isyou = (mon == &youmonst);
859 | int distance;
860 |
861 | if(isyou) {
862 | distance = 0;
863 | pline_The("%s crashes on your %s and breaks into shards.",
864 | botlnam, body_part(HEAD));
865 | losehp(rnd(2), "thrown potion", KILLED_BY_AN);
866 | } else {
867 | distance = distu(mon->mx,mon->my);
868 | if (!cansee(mon->mx,mon->my)) pline("Crash!");
869 | else {
870 | char *mnam = mon_nam(mon);
871 | char buf[BUFSZ];
872 |
873 | if(has_head(mon->data)) {
874 | Sprintf(buf, "%s %s",
875 | s_suffix(mnam),
876 | (notonhead ? "body" : "head"));
877 | } else {
878 | Strcpy(buf, mnam);
879 | }
880 | pline_The("%s crashes on %s and breaks into shards.",
881 | botlnam, buf);
882 | }
883 | if(rn2(5) && mon->mhp > 1)
884 | mon->mhp--;
885 | }
886 |
887 | /* oil doesn't instantly evaporate */
888 | if (obj->otyp != POT_OIL && cansee(mon->mx,mon->my))
889 | pline("%s evaporates.", The(xname(obj)));
890 |
891 | if (isyou) {
892 | switch (obj->otyp) {
893 | case POT_OIL:
894 | if (obj->lamplit)
895 | splatter_burning_oil(u.ux, u.uy);
896 | break;
897 | case POT_POLYMORPH:
898 | You_feel("a little %s.", Hallucination ? "normal" : "strange");
899 | if (!Unchanging && !Antimagic) polyself();
900 | break;
901 | case POT_ACID:
902 | if (!Acid_resistance) {
903 | pline("This burns%s!", obj->blessed ? " a little" :
904 | obj->cursed ? " a lot" : "");
905 | losehp(d(obj->cursed ? 2 : 1, obj->blessed ? 4 : 8),
906 | "potion of acid", KILLED_BY_AN);
907 | }
908 | break;
909 | }
910 | } else {
911 | boolean angermon = TRUE;
912 |
913 | if (!your_fault) angermon = FALSE;
914 | switch (obj->otyp) {
915 | case POT_HEALING:
916 | case POT_EXTRA_HEALING:
917 | case POT_FULL_HEALING:
918 | if (mon->data == &mons[PM_PESTILENCE]) goto do_illness;
919 | /*FALLTHRU*/
920 | case POT_RESTORE_ABILITY:
921 | case POT_GAIN_ABILITY:
922 | do_healing:
923 | angermon = FALSE;
924 | if(mon->mhp < mon->mhpmax) {
925 | mon->mhp = mon->mhpmax;
926 | if (canseemon(mon))
927 | pline("%s looks sound and hale again.", Monnam(mon));
928 | }
929 | break;
930 | case POT_SICKNESS:
931 | if (mon->data == &mons[PM_PESTILENCE]) goto do_healing;
932 | if (dmgtype(mon->data, AD_DISE) ||
933 | dmgtype(mon->data, AD_PEST) ||
934 | resists_poison(mon)) {
935 | if (canseemon(mon))
936 | pline("%s looks unharmed.", Monnam(mon));
937 | break;
938 | }
939 | do_illness:
940 | if((mon->mhpmax > 3) && !resist(mon, POTION_CLASS, 0, NOTELL))
941 | mon->mhpmax /= 2;
942 | if((mon->mhp > 2) && !resist(mon, POTION_CLASS, 0, NOTELL))
943 | mon->mhp /= 2;
944 | if (mon->mhp > mon->mhpmax) mon->mhp = mon->mhpmax;
945 | if (canseemon(mon))
946 | pline("%s looks rather ill.", Monnam(mon));
947 | break;
948 | case POT_CONFUSION:
949 | case POT_BOOZE:
950 | if(!resist(mon, POTION_CLASS, 0, NOTELL)) mon->mconf = TRUE;
951 | break;
952 | case POT_INVISIBILITY:
953 | angermon = FALSE;
954 | mon_set_minvis(mon);
955 | break;
956 | case POT_SLEEPING:
957 | /* wakeup() doesn't rouse victims of temporary sleep */
958 | if (sleep_monst(mon, rnd(12), POTION_CLASS)) {
959 | pline("%s falls asleep.", Monnam(mon));
960 | slept_monst(mon);
961 | }
962 | break;
963 | case POT_PARALYSIS:
964 | if (mon->mcanmove) {
965 | mon->mcanmove = 0;
966 | /* really should be rnd(5) for consistency with players
967 | * breathing potions, but...
968 | */
969 | mon->mfrozen = rnd(25);
970 | }
971 | break;
972 | case POT_SPEED:
973 | angermon = FALSE;
974 | mon_adjust_speed(mon, 1);
975 | break;
976 | case POT_BLINDNESS:
977 | if(haseyes(mon->data)) {
978 | register int btmp = 64 + rn2(32) +
979 | rn2(32) * !resist(mon, POTION_CLASS, 0, NOTELL);
980 | btmp += mon->mblinded;
981 | mon->mblinded = min(btmp,127);
982 | mon->mcansee = 0;
983 | }
984 | break;
985 | case POT_WATER:
986 | if (is_undead(mon->data) || is_demon(mon->data) ||
987 | is_were(mon->data)) {
988 | if (obj->blessed) {
989 | pline("%s shrieks in pain!", Monnam(mon));
990 | mon->mhp -= d(2,6);
991 | /* should only be by you */
992 | if (mon->mhp < 1) killed(mon);
993 | else if (is_were(mon->data) && !is_human(mon->data))
994 | new_were(mon); /* revert to human */
995 | } else if (obj->cursed) {
996 | angermon = FALSE;
997 | if (canseemon(mon))
998 | pline("%s looks healthier.", Monnam(mon));
999 | mon->mhp += d(2,6);
1000 | if (mon->mhp > mon->mhpmax) mon->mhp = mon->mhpmax;
1001 | if (is_were(mon->data) && is_human(mon->data) &&
1002 | !Protection_from_shape_changers)
1003 | new_were(mon); /* transform into beast */
1004 | }
1005 | } else if(mon->data == &mons[PM_GREMLIN]) {
1006 | angermon = FALSE;
1007 | (void)split_mon(mon, (struct monst *)0);
1008 | } else if(mon->data == &mons[PM_IRON_GOLEM]) {
1009 | if (canseemon(mon))
1010 | pline("%s rusts.", Monnam(mon));
1011 | mon->mhp -= d(1,6);
1012 | /* should only be by you */
1013 | if (mon->mhp < 1) killed(mon);
1014 | }
1015 | break;
1016 | case POT_OIL:
1017 | if (obj->lamplit)
1018 | splatter_burning_oil(mon->mx, mon->my);
1019 | break;
1020 | case POT_ACID:
1021 | if (!resists_acid(mon) && !resist(mon, POTION_CLASS, 0, NOTELL)) {
1022 | pline("%s shrieks in pain!", Monnam(mon));
1023 | mon->mhp -= d(obj->cursed ? 2 : 1, obj->blessed ? 4 : 8);
1024 | if (mon->mhp < 1) {
1025 | if (your_fault)
1026 | killed(mon);
1027 | else
1028 | monkilled(mon, "", AD_ACID);
1029 | }
1030 | }
1031 | break;
1032 | case POT_POLYMORPH:
1033 | (void) bhitm(mon, obj);
1034 | break;
1035 | /*
1036 | case POT_GAIN_LEVEL:
1037 | case POT_LEVITATION:
1038 | case POT_FRUIT_JUICE:
1039 | case POT_MONSTER_DETECTION:
1040 | case POT_OBJECT_DETECTION:
1041 | break;
1042 | */
1043 | }
1044 | if (angermon)
1045 | wakeup(mon);
1046 | else
1047 | mon->msleeping = 0;
1048 | }
1049 |
1050 | /* Note: potionbreathe() does its own docall() */
1051 | if (distance==0 || ((distance < 3) && rn2(5)))
1052 | potionbreathe(obj);
1053 | else if (obj->dknown && !objects[obj->otyp].oc_name_known &&
1054 | !objects[obj->otyp].oc_uname && cansee(mon->mx,mon->my))
1055 | docall(obj);
1056 | if(*u.ushops && obj->unpaid) {
1057 | register struct monst *shkp =
1058 | shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE));
1059 |
1060 | if(!shkp)
1061 | obj->unpaid = 0;
1062 | else {
1063 | (void)stolen_value(obj, u.ux, u.uy,
1064 | (boolean)shkp->mpeaceful, FALSE);
1065 | subfrombill(obj, shkp);
1066 | }
1067 | }
1068 | obfree(obj, (struct obj *)0);
1069 | }
1070 |
1071 | void
1072 | potionbreathe(obj)
1073 | register struct obj *obj;
1074 | {
1075 | register int i, ii, isdone, kn = 0;
1076 |
1077 | switch(obj->otyp) {
1078 | case POT_RESTORE_ABILITY:
1079 | case POT_GAIN_ABILITY:
1080 | if(obj->cursed) {
1081 | pline("Ulch! That potion smells terrible!");
1082 | break;
1083 | } else {
1084 | i = rn2(A_MAX); /* start at a random point */
1085 | for(isdone = ii = 0; !isdone && ii < A_MAX; ii++) {
1086 | if(ABASE(i) < AMAX(i)) {
1087 | ABASE(i)++;
1088 | /* only first found if not blessed */
1089 | isdone = !(obj->blessed);
1090 | flags.botl = 1;
1091 | }
1092 | if(++i >= A_MAX) i = 0;
1093 | }
1094 | }
1095 | break;
1096 | case POT_FULL_HEALING:
1097 | if (Upolyd && u.mh < u.mhmax) u.mh++, flags.botl = 1;
1098 | if (u.uhp < u.uhpmax) u.uhp++, flags.botl = 1;
1099 | /*FALL THROUGH*/
1100 | case POT_EXTRA_HEALING:
1101 | if (Upolyd && u.mh < u.mhmax) u.mh++, flags.botl = 1;
1102 | if (u.uhp < u.uhpmax) u.uhp++, flags.botl = 1;
1103 | /*FALL THROUGH*/
1104 | case POT_HEALING:
1105 | if (Upolyd && u.mh < u.mhmax) u.mh++, flags.botl = 1;
1106 | if (u.uhp < u.uhpmax) u.uhp++, flags.botl = 1;
1107 | exercise(A_CON, TRUE);
1108 | break;
1109 | case POT_SICKNESS:
1110 | if (!Role_if(PM_HEALER)) {
1111 | if (Upolyd) {
1112 | if (u.mh <= 5) u.mh = 1; else u.mh -= 5;
1113 | } else {
1114 | if (u.uhp <= 5) u.uhp = 1; else u.uhp -= 5;
1115 | }
1116 | flags.botl = 1;
1117 | exercise(A_CON, FALSE);
1118 | }
1119 | break;
1120 | case POT_HALLUCINATION:
1121 | You("have a momentary vision.");
1122 | break;
1123 | case POT_CONFUSION:
1124 | case POT_BOOZE:
1125 | if(!Confusion)
1126 | You_feel("somewhat dizzy.");
1127 | make_confused(itimeout_incr(HConfusion, rnd(5)), FALSE);
1128 | break;
1129 | case POT_INVISIBILITY:
1130 | if (!Blind && !Invis) {
1131 | kn++;
1132 | pline("For an instant you %s!",
1133 | See_invisible ? "could see right through yourself"
1134 | : "couldn't see yourself");
1135 | }
1136 | break;
1137 | case POT_PARALYSIS:
1138 | kn++;
1139 | if (!Free_action) {
1140 | pline("%s seems to be holding you.", Something);
1141 | nomul(-rnd(5));
1142 | nomovemsg = You_can_move_again;
1143 | exercise(A_DEX, FALSE);
1144 | } else You("stiffen momentarily.");
1145 | break;
1146 | case POT_SLEEPING:
1147 | kn++;
1148 | if (!Free_action && !Sleep_resistance) {
1149 | You_feel("rather tired.");
1150 | nomul(-rnd(5));
1151 | nomovemsg = You_can_move_again;
1152 | exercise(A_DEX, FALSE);
1153 | } else You("yawn.");
1154 | break;
1155 | case POT_SPEED:
1156 | if (!Fast) Your("knees seem more flexible now.");
1157 | incr_itimeout(&HFast, rnd(5));
1158 | exercise(A_DEX, TRUE);
1159 | break;
1160 | case POT_BLINDNESS:
1161 | if (!Blind && !u.usleep) {
1162 | kn++;
1163 | pline("It suddenly gets dark.");
1164 | }
1165 | make_blinded(itimeout_incr(Blinded, rnd(5)), FALSE);
1166 | break;
1167 | case POT_WATER:
1168 | if(u.umonnum == PM_GREMLIN) {
1169 | (void)split_mon(&youmonst, (struct monst *)0);
1170 | } else if (u.ulycn >= LOW_PM) {
1171 | /* vapor from [un]holy water will trigger
1172 | transformation but won't cure lycanthropy */
1173 | if (obj->blessed && youmonst.data == &mons[u.ulycn])
1174 | you_unwere(FALSE);
1175 | else if (obj->cursed && !Upolyd)
1176 | you_were();
1177 | }
1178 | case POT_ACID:
1179 | case POT_POLYMORPH:
1180 | exercise(A_CON, FALSE);
1181 | break;
1182 | /*
1183 | case POT_GAIN_LEVEL:
1184 | case POT_LEVITATION:
1185 | case POT_FRUIT_JUICE:
1186 | case POT_MONSTER_DETECTION:
1187 | case POT_OBJECT_DETECTION:
1188 | case POT_OIL:
1189 | break;
1190 | */
1191 | }
1192 | /* note: no obfree() */
1193 | if (obj->dknown) {
1194 | if (kn)
1195 | makeknown(obj->otyp);
1196 | else if (!objects[obj->otyp].oc_name_known &&
1197 | !objects[obj->otyp].oc_uname)
1198 | docall(obj);
1199 | }
1200 | }
1201 |
1202 | STATIC_OVL short
1203 | mixtype(o1, o2)
1204 | register struct obj *o1, *o2;
1205 | /* returns the potion type when o1 is dipped in o2 */
1206 | {
1207 | /* cut down on the number of cases below */
1208 | if (o1->oclass == POTION_CLASS &&
1209 | (o2->otyp == POT_GAIN_LEVEL ||
1210 | o2->otyp == POT_GAIN_ENERGY ||
1211 | o2->otyp == POT_HEALING ||
1212 | o2->otyp == POT_EXTRA_HEALING ||
1213 | o2->otyp == POT_FULL_HEALING ||
1214 | o2->otyp == POT_ENLIGHTENMENT ||
1215 | o2->otyp == POT_FRUIT_JUICE)) {
1216 | struct obj *swp;
1217 |
1218 | swp = o1; o1 = o2; o2 = swp;
1219 | }
1220 |
1221 | switch (o1->otyp) {
1222 | case POT_HEALING:
1223 | switch (o2->otyp) {
1224 | case POT_SPEED:
1225 | case POT_GAIN_LEVEL:
1226 | case POT_GAIN_ENERGY:
1227 | return POT_EXTRA_HEALING;
1228 | }
1229 | case POT_EXTRA_HEALING:
1230 | switch (o2->otyp) {
1231 | case POT_GAIN_LEVEL:
1232 | case POT_GAIN_ENERGY:
1233 | return POT_FULL_HEALING;
1234 | }
1235 | case POT_FULL_HEALING:
1236 | switch (o2->otyp) {
1237 | case POT_GAIN_LEVEL:
1238 | case POT_GAIN_ENERGY:
1239 | return POT_GAIN_ABILITY;
1240 | }
1241 | case UNICORN_HORN:
1242 | switch (o2->otyp) {
1243 | case POT_SICKNESS:
1244 | return POT_FRUIT_JUICE;
1245 | case POT_HALLUCINATION:
1246 | case POT_BLINDNESS:
1247 | case POT_CONFUSION:
1248 | return POT_WATER;
1249 | }
1250 | break;
1251 | case AMETHYST: /* "a-methyst" == "not intoxicated" */
1252 | if (o2->otyp == POT_BOOZE)
1253 | return POT_FRUIT_JUICE;
1254 | break;
1255 | case POT_GAIN_LEVEL:
1256 | case POT_GAIN_ENERGY:
1257 | switch (o2->otyp) {
1258 | case POT_CONFUSION:
1259 | return (rn2(3) ? POT_BOOZE : POT_ENLIGHTENMENT);
1260 | case POT_HEALING:
1261 | return POT_EXTRA_HEALING;
1262 | case POT_EXTRA_HEALING:
1263 | return POT_FULL_HEALING;
1264 | case POT_FULL_HEALING:
1265 | return POT_GAIN_ABILITY;
1266 | case POT_FRUIT_JUICE:
1267 | return POT_SEE_INVISIBLE;
1268 | case POT_BOOZE:
1269 | return POT_HALLUCINATION;
1270 | }
1271 | break;
1272 | case POT_FRUIT_JUICE:
1273 | switch (o2->otyp) {
1274 | case POT_SICKNESS:
1275 | return POT_SICKNESS;
1276 | case POT_SPEED:
1277 | return POT_BOOZE;
1278 | case POT_GAIN_LEVEL:
1279 | case POT_GAIN_ENERGY:
1280 | return POT_SEE_INVISIBLE;
1281 | }
1282 | break;
1283 | case POT_ENLIGHTENMENT:
1284 | switch (o2->otyp) {
1285 | case POT_LEVITATION:
1286 | if (rn2(3)) return POT_GAIN_LEVEL;
1287 | break;
1288 | case POT_FRUIT_JUICE:
1289 | return POT_BOOZE;
1290 | case POT_BOOZE:
1291 | return POT_CONFUSION;
1292 | }
1293 | break;
1294 | }
1295 |
1296 | return 0;
1297 | }
1298 |
1299 |
1300 | boolean
1301 | get_wet(obj)
1302 | register struct obj *obj;
1303 | /* returns TRUE if something happened (potion should be used up) */
1304 | {
1305 | char Your_buf[BUFSZ];
1306 |
1307 | if (snuff_lit(obj)) return(TRUE);
1308 |
1309 | if (obj->greased) {
1310 | grease_protect(obj,(char *)0,FALSE,&youmonst);
1311 | return(FALSE);
1312 | }
1313 | (void) Shk_Your(Your_buf, obj);
1314 | /* (Rusting shop goods ought to be charged for.) */
1315 | switch (obj->oclass) {
1316 | case WEAPON_CLASS:
1317 | if (!obj->oerodeproof && is_rustprone(obj) &&
1318 | (obj->oeroded < MAX_ERODE) && !rn2(2)) {
1319 | pline("%s %s some%s.",
1320 | Your_buf, aobjnam(obj, "rust"),
1321 | obj->oeroded ? " more" : "what");
1322 | obj->oeroded++;
1323 | update_inventory();
1324 | return TRUE;
1325 | } else break;
1326 | case POTION_CLASS:
1327 | if (obj->otyp == POT_WATER) return FALSE;
1328 | /* KMH -- Water into acid causes an explosion */
1329 | if (obj->otyp == POT_ACID) {
1330 | pline("It boils vigorously!");
1331 | losehp(rnd(10), "elementary chemistry", KILLED_BY);
1332 | makeknown(obj->otyp);
1333 | update_inventory();
1334 | return (TRUE);
1335 | }
1336 | pline("%s %s%s.", Your_buf, aobjnam(obj,"dilute"),
1337 | obj->odiluted ? " further" : "");
1338 | if(obj->unpaid && costly_spot(u.ux, u.uy)) {
1339 | You("dilute it, you pay for it.");
1340 | bill_dummy_object(obj);
1341 | }
1342 | if (obj->odiluted) {
1343 | obj->odiluted = 0;
1344 | #ifdef UNIXPC
1345 | obj->blessed = FALSE;
1346 | obj->cursed = FALSE;
1347 | #else
1348 | obj->blessed = obj->cursed = FALSE;
1349 | #endif
1350 | obj->otyp = POT_WATER;
1351 | } else obj->odiluted++;
1352 | update_inventory();
1353 | return TRUE;
1354 | case SCROLL_CLASS:
1355 | if (obj->otyp != SCR_BLANK_PAPER
1356 | #ifdef MAIL
1357 | && obj->otyp != SCR_MAIL
1358 | #endif
1359 | ) {
1360 | if (!Blind) {
1361 | boolean oq1 = obj->quan == 1L;
1362 | pline_The("scroll%s fade%s.",
1363 | oq1 ? "" : "s",
1364 | oq1 ? "s" : "");
1365 | }
1366 | if(obj->unpaid && costly_spot(u.ux, u.uy)) {
1367 | You("erase it, you pay for it.");
1368 | bill_dummy_object(obj);
1369 | }
1370 | obj->otyp = SCR_BLANK_PAPER;
1371 | obj->spe = 0;
1372 | update_inventory();
1373 | return TRUE;
1374 | } else break;
1375 | case SPBOOK_CLASS:
1376 | if (obj->otyp != SPE_BLANK_PAPER) {
1377 |
1378 | if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
1379 | pline("%s suddenly heats up; steam rises and it remains dry.",
1380 | The(xname(obj)));
1381 | } else {
1382 | if (!Blind) {
1383 | boolean oq1 = obj->quan == 1L;
1384 | pline_The("spellbook%s fade%s.",
1385 | oq1 ? "" : "s", oq1 ? "s" : "");
1386 | }
1387 | if(obj->unpaid && costly_spot(u.ux, u.uy)) {
1388 | You("erase it, you pay for it.");
1389 | bill_dummy_object(obj);
1390 | }
1391 | obj->otyp = SPE_BLANK_PAPER;
1392 | update_inventory();
1393 | }
1394 | return TRUE;
1395 | }
1396 | }
1397 | pline("%s %s wet.", Your_buf, aobjnam(obj,"get"));
1398 | return FALSE;
1399 | }
1400 |
1401 | int
1402 | dodip()
1403 | {
1404 | register struct obj *potion, *obj;
1405 | const char *tmp;
1406 | uchar here;
1407 | char allowall[2];
1408 | short mixture;
1409 | char qbuf[QBUFSZ], Your_buf[BUFSZ];
1410 |
1411 | allowall[0] = ALL_CLASSES; allowall[1] = '\0';
1412 | if(!(obj = getobj(allowall, "dip")))
1413 | return(0);
1414 |
1415 | here = levl[u.ux][u.uy].typ;
1416 | /* Is there a fountain to dip into here? */
1417 | if (IS_FOUNTAIN(here)) {
1418 | if(yn("Dip it into the fountain?") == 'y') {
1419 | dipfountain(obj);
1420 | return(1);
1421 | }
1422 | } else if (is_pool(u.ux,u.uy)) {
1423 | tmp = (here == POOL) ? "pool" : "moat";
1424 | Sprintf(qbuf, "Dip it into the %s?", tmp);
1425 | if (yn(qbuf) == 'y') {
1426 | if (Levitation)
1427 | floating_above(tmp);
1428 | else {
1429 | (void) get_wet(obj);
1430 | if (obj->otyp == POT_ACID) useup(obj);
1431 | }
1432 | return 1;
1433 | }
1434 | }
1435 |
1436 | if(!(potion = getobj(beverages, "dip into")))
1437 | return(0);
1438 | if (potion == obj && potion->quan == 1L) {
1439 | pline("That is a potion bottle, not a Klein bottle!");
1440 | return 0;
1441 | }
1442 | if(potion->otyp == POT_WATER) {
1443 | boolean useeit = !Blind;
1444 | if (useeit) (void) Shk_Your(Your_buf, obj);
1445 | if (potion->blessed) {
1446 | if (obj->cursed) {
1447 | if (useeit)
1448 | pline("%s %s %s.",
1449 | Your_buf,
1450 | aobjnam(obj, "softly glow"),
1451 | hcolor(amber));
1452 | uncurse(obj);
1453 | obj->bknown=1;
1454 | poof:
1455 | if(!(objects[potion->otyp].oc_name_known) &&
1456 | !(objects[potion->otyp].oc_uname))
1457 | docall(potion);
1458 | useup(potion);
1459 | return(1);
1460 | } else if(!obj->blessed) {
1461 | if (useeit) {
1462 | tmp = hcolor(light_blue);
1463 | pline("%s %s with a%s %s aura.",
1464 | Your_buf,
1465 | aobjnam(obj, "softly glow"),
1466 | index(vowels, *tmp) ? "n" : "", tmp);
1467 | }
1468 | bless(obj);
1469 | obj->bknown=1;
1470 | goto poof;
1471 | }
1472 | } else if (potion->cursed) {
1473 | if (obj->blessed) {
1474 | if (useeit)
1475 | pline("%s %s %s.",
1476 | Your_buf,
1477 | aobjnam(obj, "glow"),
1478 | hcolor((const char *)"brown"));
1479 | unbless(obj);
1480 | obj->bknown=1;
1481 | goto poof;
1482 | } else if(!obj->cursed) {
1483 | if (useeit) {
1484 | tmp = hcolor(Black);
1485 | pline("%s %s with a%s %s aura.",
1486 | Your_buf,
1487 | aobjnam(obj, "glow"),
1488 | index(vowels, *tmp) ? "n" : "", tmp);
1489 | }
1490 | curse(obj);
1491 | obj->bknown=1;
1492 | goto poof;
1493 | }
1494 | } else
1495 | if (get_wet(obj))
1496 | goto poof;
1497 | } else if (obj->otyp == POT_POLYMORPH ||
1498 | potion->otyp == POT_POLYMORPH) {
1499 | /* some objects can't be polymorphed */
1500 | if (obj->otyp == potion->otyp || /* both POT_POLY */
1501 | obj->otyp == WAN_POLYMORPH ||
1502 | obj->otyp == SPE_POLYMORPH ||
1503 | obj_resists(obj->otyp == POT_POLYMORPH ?
1504 | potion : obj, 5, 95)) {
1505 | pline(nothing_happens);
1506 | } else {
1507 | /* KMH, conduct */
1508 | u.uconduct.polypiles++;
1509 |
1510 | poly_obj(obj, STRANGE_OBJECT);
1511 | makeknown(POT_POLYMORPH);
1512 | useup(potion);
1513 | }
1514 | return(1);
1515 | } else if(obj->oclass == POTION_CLASS && obj->otyp != potion->otyp) {
1516 | /* Mixing potions is dangerous... */
1517 | pline_The("potions mix...");
1518 | /* KMH, balance patch -- acid is particularly unstable */
1519 | if (obj->cursed || obj->otyp == POT_ACID || !rn2(10)) {
1520 | pline("BOOM! They explode!");
1521 | exercise(A_STR, FALSE);
1522 | potionbreathe(obj);
1523 | useup(obj);
1524 | useup(potion);
1525 | losehp(rnd(10), "alchemic blast", KILLED_BY_AN);
1526 | return(1);
1527 | }
1528 |
1529 | obj->blessed = obj->cursed = obj->bknown = 0;
1530 | if (Blind || Hallucination) obj->dknown = 0;
1531 |
1532 | if ((mixture = mixtype(obj, potion)) != 0) {
1533 | obj->otyp = mixture;
1534 | } else {
1535 | switch (obj->odiluted ? 1 : rnd(8)) {
1536 | case 1:
1537 | obj->otyp = POT_WATER;
1538 | break;
1539 | case 2:
1540 | case 3:
1541 | obj->otyp = POT_SICKNESS;
1542 | break;
1543 | case 4:
1544 | {
1545 | struct obj *otmp;
1546 | otmp = mkobj(POTION_CLASS,FALSE);
1547 | obj->otyp = otmp->otyp;
1548 | obfree(otmp, (struct obj *)0);
1549 | }
1550 | break;
1551 | default:
1552 | if (!Blind)
1553 | pline_The("mixture glows brightly and evaporates.");
1554 | useup(obj);
1555 | useup(potion);
1556 | return(1);
1557 | }
1558 | }
1559 |
1560 | obj->odiluted = (obj->otyp != POT_WATER);
1561 |
1562 | if (obj->otyp == POT_WATER && !Hallucination) {
1563 | pline_The("mixture bubbles%s.",
1564 | Blind ? "" : ", then clears");
1565 | } else if (!Blind) {
1566 | pline_The("mixture looks %s.",
1567 | hcolor(OBJ_DESCR(objects[obj->otyp])));
1568 | }
1569 |
1570 | useup(potion);
1571 | return(1);
1572 | }
1573 |
1574 | #ifdef INVISIBLE_OBJECTS
1575 | if (potion->otyp == POT_INVISIBILITY && !obj->oinvis) {
1576 | obj->oinvis = TRUE;
1577 | if (!Blind) {
1578 | if (!See_invisible) pline("Where did %s go?",
1579 | the(xname(obj)));
1580 | else You("notice a little haziness around %s.",
1581 | the(xname(obj)));
1582 | }
1583 | goto poof;
1584 | } else if (potion->otyp == POT_SEE_INVISIBLE && obj->oinvis) {
1585 | obj->oinvis = FALSE;
1586 | if (!Blind) {
1587 | if (!See_invisible) pline("So that's where %s went!",
1588 | the(xname(obj)));
1589 | else pline_The("haziness around %s disappears.",
1590 | the(xname(obj)));
1591 | }
1592 | goto poof;
1593 | }
1594 | #endif
1595 |
1596 | if(is_poisonable(obj)) {
1597 | if(potion->otyp == POT_SICKNESS && !obj->opoisoned) {
1598 | char buf[BUFSZ];
1599 | Strcpy(buf, The(xname(potion)));
1600 | pline("%s form%s a coating on %s.",
1601 | buf, potion->quan == 1L ? "s" : "", the(xname(obj)));
1602 | obj->opoisoned = TRUE;
1603 | goto poof;
1604 | } else if(obj->opoisoned &&
1605 | (potion->otyp == POT_HEALING ||
1606 | potion->otyp == POT_EXTRA_HEALING ||
1607 | potion->otyp == POT_FULL_HEALING)) {
1608 | pline("A coating wears off %s.", the(xname(obj)));
1609 | obj->opoisoned = 0;
1610 | goto poof;
1611 | }
1612 | }
1613 |
1614 | if (potion->otyp == POT_OIL &&
1615 | (obj->oclass == WEAPON_CLASS || is_weptool(obj))) {
1616 | boolean wisx = FALSE;
1617 | if (potion->lamplit) { /* burning */
1618 | int omat = objects[obj->otyp].oc_material;
1619 | if (obj->oerodeproof || obj_resists(obj, 5, 95) ||
1620 | /* `METAL' should not be confused with is_metallic() */
1621 | omat == METAL || omat == MITHRIL || omat == BONE) {
1622 | pline("%s seem%s to burn for a moment.",
1623 | Yname2(obj),
1624 | (obj->quan > 1L) ? "" : "s");
1625 | } else {
1626 | if (omat == PLASTIC) obj->oeroded = MAX_ERODE;
1627 | pline_The("burning oil %s %s.",
1628 | obj->oeroded == MAX_ERODE ? "destroys" : "damages",
1629 | yname(obj));
1630 | if (obj->oeroded == MAX_ERODE) {
1631 | obj_extract_self(obj);
1632 | obfree(obj, (struct obj *)0);
1633 | obj = (struct obj *) 0;
1634 | } else {
1635 | /* should check for and do something about
1636 | damaging unpaid shop goods here */
1637 | obj->oeroded++;
1638 | }
1639 | }
1640 | } else if (potion->cursed) {
1641 | pline_The("potion spills and covers your %s with oil.",
1642 | makeplural(body_part(FINGER)));
1643 | incr_itimeout(&Glib, d(2,10));
1644 | /* Oil removes rust and corrosion, but doesn't unburn.
1645 | * Arrows, etc are classed as metallic due to arrowhead
1646 | * material, but dipping in oil shouldn't repair them.
1647 | */
1648 | } else if ((!is_rustprone(obj) && !is_corrodeable(obj)) ||
1649 | is_ammo(obj) || (!obj->oeroded && !obj->oeroded2)) {
1650 | /* uses up potion, doesn't set obj->greased */
1651 | pline("%s gleam%s with an oily sheen.",
1652 | Yname2(obj),
1653 | (obj->quan > 1L) ? "" : "s");
1654 | } else {
1655 | pline("%s %s less %s.",
1656 | Yname2(obj),
1657 | (obj->quan > 1L) ? "are" : "is",
1658 | (obj->oeroded && obj->oeroded2) ? "corroded and rusty" :
1659 | obj->oeroded ? "rusty" : "corroded");
1660 | if (obj->oeroded > 0) obj->oeroded--;
1661 | if (obj->oeroded2 > 0) obj->oeroded2--;
1662 | wisx = TRUE;
1663 | }
1664 | exercise(A_WIS, wisx);
1665 | makeknown(potion->otyp);
1666 | useup(potion);
1667 | return 1;
1668 | }
1669 |
1670 | /* Allow filling of MAGIC_LAMPs to prevent identification by player */
1671 | if ((obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP) &&
1672 | (potion->otyp == POT_OIL)) {
1673 | /* Turn off engine before fueling, turn off fuel too :-) */
1674 | if (obj->lamplit || potion->lamplit) {
1675 | useup(potion);
1676 | explode(u.ux, u.uy, 11, d(6,6), 0);
1677 | exercise(A_WIS, FALSE);
1678 | return 1;
1679 | }
1680 | /* Adding oil to an empty magic lamp renders it into an oil lamp */
1681 | if ((obj->otyp == MAGIC_LAMP) && obj->spe == 0) {
1682 | obj->otyp = OIL_LAMP;
1683 | obj->age = 0;
1684 | }
1685 | if (obj->age > 1000L) {
1686 | pline("%s is full.", Yname2(obj));
1687 | } else {
1688 | You("fill %s with oil.", yname(obj));
1689 | check_unpaid(potion); /* Yendorian Fuel Tax */
1690 | obj->age += 2*potion->age; /* burns more efficiently */
1691 | if (obj->age > 1500L) obj->age = 1500L;
1692 | useup(potion);
1693 | exercise(A_WIS, TRUE);
1694 | }
1695 | obj->spe = 1;
1696 | update_inventory();
1697 | return 1;
1698 | }
1699 |
1700 | if ((obj->otyp == UNICORN_HORN || obj->otyp == AMETHYST) &&
1701 | (mixture = mixtype(obj, potion)) != 0) {
1702 | /* with multiple merged potions, we should split off one and
1703 | just clear it, but clearing them all together is easier */
1704 | boolean more_than_one = potion->quan > 1L;
1705 | potion->otyp = mixture;
1706 | potion->blessed = 0;
1707 | if (mixture == POT_WATER)
1708 | potion->cursed = potion->odiluted = 0;
1709 | else
1710 | potion->cursed = obj->cursed; /* odiluted left as-is */
1711 | if (Blind)
1712 | potion->dknown = FALSE;
1713 | else {
1714 | if (mixture == POT_WATER &&
1715 | #ifdef DCC30_BUG
1716 | (potion->dknown = !Hallucination,
1717 | potion->dknown != 0))
1718 | #else
1719 | (potion->dknown = !Hallucination) != 0)
1720 | #endif
1721 | pline_The("potion%s clear%s.",
1722 | more_than_one ? "s" : "",
1723 | more_than_one ? "" : "s");
1724 | else
1725 | pline_The("potion%s turn%s %s.",
1726 | more_than_one ? "s" : "",
1727 | more_than_one ? "" : "s",
1728 | hcolor(OBJ_DESCR(objects[mixture])));
1729 | }
1730 | update_inventory();
1731 | return(1);
1732 | }
1733 |
1734 | pline("Interesting...");
1735 | return(1);
1736 | }
1737 |
1738 |
1739 | void
1740 | djinni_from_bottle(obj)
1741 | register struct obj *obj;
1742 | {
1743 | struct monst *mtmp;
1744 | int chance;
1745 |
1746 | if(!(mtmp = makemon(&mons[PM_DJINNI], u.ux, u.uy, NO_MM_FLAGS))){
1747 | pline("It turns out to be empty.");
1748 | return;
1749 | }
1750 |
1751 | if (!Blind) {
1752 | pline("In a cloud of smoke, %s emerges!", a_monnam(mtmp));
1753 | pline("%s speaks.", Monnam(mtmp));
1754 | } else {
1755 | You("smell acrid fumes.");
1756 | pline("%s speaks.", Something);
1757 | }
1758 |
1759 | chance = rn2(5);
1760 | if (obj->blessed) chance = (chance == 4) ? rnd(4) : 0;
1761 | else if (obj->cursed) chance = (chance == 0) ? rn2(4) : 4;
1762 | /* 0,1,2,3,4: b=80%,5,5,5,5; nc=20%,20,20,20,20; c=5%,5,5,5,80 */
1763 |
1764 | switch (chance) {
1765 | case 0 : verbalize("I am in your debt. I will grant one wish!");
1766 | makewish();
1767 | mongone(mtmp);
1768 | break;
1769 | case 1 : verbalize("Thank you for freeing me!");
1770 | (void) tamedog(mtmp, (struct obj *)0);
1771 | break;
1772 | case 2 : verbalize("You freed me!");
1773 | mtmp->mpeaceful = TRUE;
1774 | set_malign(mtmp);
1775 | break;
1776 | case 3 : verbalize("It is about time!");
1777 | pline("%s vanishes.", Monnam(mtmp));
1778 | mongone(mtmp);
1779 | break;
1780 | default: verbalize("You disturbed me, fool!");
1781 | break;
1782 | }
1783 | }
1784 |
1785 | /* clone a gremlin or mold (2nd arg non-null implies heat as the trigger);
1786 | hit points are cut in half (odd HP stays with original) */
1787 | struct monst *
1788 | split_mon(mon, mtmp)
1789 | struct monst *mon, /* monster being split */
1790 | *mtmp; /* optional attacker whose heat triggered it */
1791 | {
1792 | struct monst *mtmp2;
1793 | char reason[BUFSZ];
1794 |
1795 | reason[0] = '\0';
1796 | if (mtmp) Sprintf(reason, " from %s heat",
1797 | (mtmp == &youmonst) ? (const char *)"your" :
1798 | (const char *)s_suffix(mon_nam(mtmp)));
1799 |
1800 | if (mon == &youmonst) {
1801 | mtmp2 = cloneu();
1802 | if (mtmp2) {
1803 | mtmp2->mhpmax = u.mhmax / 2;
1804 | u.mhmax -= mtmp2->mhpmax;
1805 | flags.botl = 1;
1806 | You("multiply%s!", reason);
1807 | }
1808 | } else {
1809 | mtmp2 = clone_mon(mon);
1810 | if (mtmp2) {
1811 | mtmp2->mhpmax = mon->mhpmax / 2;
1812 | mon->mhpmax -= mtmp2->mhpmax;
1813 | if (canspotmon(mon))
1814 | pline("%s multiplies%s!", Monnam(mon), reason);
1815 | }
1816 | }
1817 | return mtmp2;
1818 | }
1819 |
1820 | #endif /* OVLB */
1821 |
1822 | /*potion.c*/