1 /* SCCS Id: @(#)potion.c 3.4 2002/10/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 boolean notonhead = FALSE;
8
9 static NEARDATA int nothing, unkn;
10 static NEARDATA const char beverages[] = { POTION_CLASS, 0 };
11
12 STATIC_DCL long FDECL(itimeout, (long));
13 STATIC_DCL long FDECL(itimeout_incr, (long,int));
14 STATIC_DCL void NDECL(ghost_from_bottle);
15 STATIC_OVL void NDECL(alchemy_init);
16 STATIC_DCL short FDECL(mixtype, (struct obj *,struct obj *));
17
18 #ifndef TESTING
19 STATIC_DCL int FDECL(dip, (struct obj *,struct obj *));
20 #endif
21
22 /* force `val' to be within valid range for intrinsic timeout value */
23 STATIC_OVL long
itimeout(val)24 itimeout(val)
25 long val;
26 {
27 if (val >= TIMEOUT) val = TIMEOUT;
28 else if (val < 1) val = 0;
29
30 return val;
31 }
32
33 /* increment `old' by `incr' and force result to be valid intrinsic timeout */
34 STATIC_OVL long
itimeout_incr(old,incr)35 itimeout_incr(old, incr)
36 long old;
37 int incr;
38 {
39 return itimeout((old & TIMEOUT) + (long)incr);
40 }
41
42 /* set the timeout field of intrinsic `which' */
43 void
set_itimeout(which,val)44 set_itimeout(which, val)
45 long *which, val;
46 {
47 *which &= ~TIMEOUT;
48 *which |= itimeout(val);
49 }
50
51 /* increment the timeout field of intrinsic `which' */
52 void
incr_itimeout(which,incr)53 incr_itimeout(which, incr)
54 long *which;
55 int incr;
56 {
57 set_itimeout(which, itimeout_incr(*which, incr));
58 }
59
60 void
make_confused(xtime,talk)61 make_confused(xtime,talk)
62 long xtime;
63 boolean talk;
64 {
65 long old = HConfusion;
66
67 if (!xtime && old) {
68 if (talk)
69 You_feel("less %s now.",
70 Hallucination ? "trippy" : "confused");
71 }
72 if ((xtime && !old) || (!xtime && old)) flags.botl = TRUE;
73
74 set_itimeout(&HConfusion, xtime);
75 }
76
77 void
make_stunned(xtime,talk)78 make_stunned(xtime,talk)
79 long xtime;
80 boolean talk;
81 {
82 long old = HStun;
83
84 if (!xtime && old) {
85 if (talk)
86 You_feel("%s now.",
87 Hallucination ? "less wobbly" : "a bit steadier");
88 }
89 if (xtime && !old) {
90 if (talk) {
91 #ifdef STEED
92 if (u.usteed)
93 You("wobble in the saddle.");
94 else
95 #endif
96 You("%s...", stagger(youmonst.data, "stagger"));
97 }
98 }
99 if ((!xtime && old) || (xtime && !old)) flags.botl = TRUE;
100
101 set_itimeout(&HStun, xtime);
102 }
103
104 void
make_sick(xtime,cause,talk,type)105 make_sick(xtime, cause, talk, type)
106 long xtime;
107 const char *cause; /* sickness cause */
108 boolean talk;
109 int type;
110 {
111 long old = Sick;
112
113 if (xtime > 0L) {
114 if (Sick_resistance) return;
115 if (!old) {
116 /* newly sick */
117 You_feel("deathly sick.");
118 } else {
119 /* already sick */
120 if (talk) You_feel("%s worse.",
121 xtime <= Sick/2L ? "much" : "even");
122 }
123 set_itimeout(&Sick, xtime);
124 u.usick_type |= type;
125 flags.botl = TRUE;
126 } else if (old && (type & u.usick_type)) {
127 /* was sick, now not */
128 u.usick_type &= ~type;
129 if (u.usick_type) { /* only partly cured */
130 if (talk) You_feel("somewhat better.");
131 set_itimeout(&Sick, Sick * 2); /* approximation */
132 } else {
133 if (talk) pline("What a relief!");
134 Sick = 0L; /* set_itimeout(&Sick, 0L) */
135 }
136 flags.botl = TRUE;
137 }
138
139 if (Sick) {
140 exercise(A_CON, FALSE);
141 if (cause) {
142 (void) strncpy(u.usick_cause, cause, sizeof(u.usick_cause));
143 u.usick_cause[sizeof(u.usick_cause)-1] = 0;
144 }
145 else
146 u.usick_cause[0] = 0;
147 } else
148 u.usick_cause[0] = 0;
149 }
150
151 void
make_vomiting(xtime,talk)152 make_vomiting(xtime, talk)
153 long xtime;
154 boolean talk;
155 {
156 long old = Vomiting;
157
158 if(!xtime && old)
159 if(talk) You_feel("much less nauseated now.");
160
161 set_itimeout(&Vomiting, xtime);
162 }
163
164 static const char vismsg[] = "vision seems to %s for a moment but is %s now.";
165 static const char eyemsg[] = "%s momentarily %s.";
166
167 void
make_blinded(xtime,talk)168 make_blinded(xtime, talk)
169 long xtime;
170 boolean talk;
171 {
172 long old = Blinded;
173 boolean u_could_see, can_see_now;
174 int eyecnt;
175 char buf[BUFSZ];
176
177 /* we need to probe ahead in case the Eyes of the Overworld
178 are or will be overriding blindness */
179 u_could_see = !Blind;
180 Blinded = xtime ? 1L : 0L;
181 can_see_now = !Blind;
182 Blinded = old; /* restore */
183
184 if (u.usleep) talk = FALSE;
185
186 if (can_see_now && !u_could_see) { /* regaining sight */
187 if (talk) {
188 if (Hallucination)
189 pline("Far out! Everything is all cosmic again!");
190 else
191 You("can see again.");
192 }
193 } else if (old && !xtime) {
194 /* clearing temporary blindness without toggling blindness */
195 if (talk) {
196 if (!haseyes(youmonst.data)) {
197 strange_feeling((struct obj *)0, (char *)0);
198 } else if (Blindfolded) {
199 Strcpy(buf, body_part(EYE));
200 eyecnt = eyecount(youmonst.data);
201 Your(eyemsg, (eyecnt == 1) ? buf : makeplural(buf),
202 (eyecnt == 1) ? "itches" : "itch");
203 } else { /* Eyes of the Overworld */
204 Your(vismsg, "brighten",
205 Hallucination ? "sadder" : "normal");
206 }
207 }
208 }
209
210 if (u_could_see && !can_see_now) { /* losing sight */
211 if (talk) {
212 if (Hallucination)
213 pline("Oh, bummer! Everything is dark! Help!");
214 else
215 pline("A cloud of darkness falls upon you.");
216 }
217 /* Before the hero goes blind, set the ball&chain variables. */
218 if (Punished) set_bc(0);
219 } else if (!old && xtime) {
220 /* setting temporary blindness without toggling blindness */
221 if (talk) {
222 if (!haseyes(youmonst.data)) {
223 strange_feeling((struct obj *)0, (char *)0);
224 } else if (Blindfolded) {
225 Strcpy(buf, body_part(EYE));
226 eyecnt = eyecount(youmonst.data);
227 Your(eyemsg, (eyecnt == 1) ? buf : makeplural(buf),
228 (eyecnt == 1) ? "twitches" : "twitch");
229 } else { /* Eyes of the Overworld */
230 Your(vismsg, "dim",
231 Hallucination ? "happier" : "normal");
232 }
233 }
234 }
235
236 set_itimeout(&Blinded, xtime);
237
238 if (u_could_see ^ can_see_now) { /* one or the other but not both */
239 flags.botl = 1;
240 vision_full_recalc = 1; /* blindness just got toggled */
241 if (Blind_telepat || Infravision) see_monsters();
242 }
243 }
244
245 boolean
make_hallucinated(xtime,talk,mask)246 make_hallucinated(xtime, talk, mask)
247 long xtime; /* nonzero if this is an attempt to turn on hallucination */
248 boolean talk;
249 long mask; /* nonzero if resistance status should change by mask */
250 {
251 long old = HHallucination;
252 boolean changed = 0;
253 const char *message, *verb;
254
255 if (flags.perma_hallu) {
256 if (xtime > 0) {
257 pline("Oh wow! You have a yet another vision!");
258 }
259 return 0;
260 }
261
262 message = (!xtime) ? "Everything %s SO boring now." :
263 "Oh wow! Everything %s so cosmic!";
264 verb = (!Blind) ? "looks" : "feels";
265
266 if (mask) {
267 if (HHallucination) changed = TRUE;
268
269 if (!xtime) EHalluc_resistance |= mask;
270 else EHalluc_resistance &= ~mask;
271 } else {
272 if (!EHalluc_resistance && (!!HHallucination != !!xtime))
273 changed = TRUE;
274 set_itimeout(&HHallucination, xtime);
275
276 /* clearing temporary hallucination without toggling vision */
277 if (!changed && !HHallucination && old && talk) {
278 if (!haseyes(youmonst.data)) {
279 strange_feeling((struct obj *)0, (char *)0);
280 } else if (Blind) {
281 char buf[BUFSZ];
282 int eyecnt = eyecount(youmonst.data);
283
284 Strcpy(buf, body_part(EYE));
285 Your(eyemsg, (eyecnt == 1) ? buf : makeplural(buf),
286 (eyecnt == 1) ? "itches" : "itch");
287 } else { /* Grayswandir */
288 Your(vismsg, "flatten", "normal");
289 }
290 }
291 }
292
293 if (changed) {
294 if (u.uswallow) {
295 swallowed(0); /* redraw swallow display */
296 } else {
297 /* The see_* routines should be called *before* the pline. */
298 see_monsters();
299 see_objects();
300 see_traps();
301 }
302
303 /* for perm_inv and anything similar
304 (eg. Qt windowport's equipped items display) */
305 update_inventory();
306
307 flags.botl = 1;
308 if (talk) pline(message, verb);
309 }
310 return changed;
311 }
312
313 STATIC_OVL void
ghost_from_bottle()314 ghost_from_bottle()
315 {
316 struct monst *mtmp = makemon(&mons[PM_GHOST], u.ux, u.uy, NO_MM_FLAGS);
317
318 if (!mtmp) {
319 pline("This bottle turns out to be empty.");
320 return;
321 }
322 if (Blind) {
323 pline("As you open the bottle, %s emerges.", something);
324 return;
325 }
326 pline("As you open the bottle, an enormous %s emerges!",
327 Hallucination ? rndmonnam() : (const char *)"ghost");
328 if(flags.verbose)
329 You("are frightened to death, and unable to move.");
330 nomul(-3, "being frightened to death");
331 nomovemsg = "You regain your composure.";
332 }
333
334 /* "Quaffing is like drinking, except you spill more." -- Terry Pratchett
335 */
336 int
dodrink()337 dodrink()
338 {
339 register struct obj *otmp;
340 const char *potion_descr;
341
342 if (Strangled) {
343 pline("If you can't breathe air, how can you drink liquid?");
344 return 0;
345 }
346 /* Is there a fountain to drink from here? */
347 if (IS_FOUNTAIN(levl[u.ux][u.uy].typ) && !Levitation) {
348 if(yn("Drink from the fountain?") == 'y') {
349 drinkfountain();
350 return 1;
351 }
352 }
353 #ifdef SINKS
354 /* Or a kitchen sink? */
355 if (IS_SINK(levl[u.ux][u.uy].typ)) {
356 if (yn("Drink from the sink?") == 'y') {
357 drinksink();
358 return 1;
359 }
360 }
361 #endif
362
363 /* Or are you surrounded by water? */
364 if (Underwater) {
365 if (yn("Drink the water around you?") == 'y') {
366
367 if (Role_if(PM_ARCHEOLOGIST)) {
368 pline("No thank you, fish make love in it!"); /* Indiana Jones 3 */
369 } else {
370 pline("Do you know what lives in this water!");
371 }
372 return 1;
373 }
374 }
375
376 otmp = getobj(beverages, "drink");
377 if(!otmp) return(0);
378 otmp->in_use = TRUE; /* you've opened the stopper */
379
380 #define POTION_OCCUPANT_CHANCE(n) (13 + 2*(n)) /* also in muse.c */
381
382 potion_descr = OBJ_DESCR(objects[otmp->otyp]);
383 if (potion_descr) {
384 if (!strcmp(potion_descr, "milky") &&
385 flags.ghost_count < MAXMONNO &&
386 !rn2(POTION_OCCUPANT_CHANCE(flags.ghost_count))) {
387 ghost_from_bottle();
388 useup(otmp);
389 return(1);
390 } else if (!strcmp(potion_descr, "smoky") &&
391 flags.djinni_count < MAXMONNO &&
392 !rn2(POTION_OCCUPANT_CHANCE(flags.djinni_count))) {
393 djinni_from_bottle(otmp);
394 useup(otmp);
395 return(1);
396 }
397 }
398 return dopotion(otmp);
399 }
400
401 int
dopotion(otmp)402 dopotion(otmp)
403 register struct obj *otmp;
404 {
405 int retval;
406
407 otmp->in_use = TRUE;
408 nothing = unkn = 0;
409 if((retval = peffects(otmp)) >= 0) return(retval);
410
411 if(nothing) {
412 unkn++;
413 You("have a %s feeling for a moment, then it passes.",
414 Hallucination ? "normal" : "peculiar");
415 }
416 if(otmp->dknown && !objects[otmp->otyp].oc_name_known) {
417 if(!unkn) {
418 makeknown(otmp->otyp);
419 more_experienced(0,0,10);
420 } else if(!objects[otmp->otyp].oc_uname)
421 docall(otmp);
422 }
423 useup(otmp);
424 return(1);
425 }
426
427 int
peffects(otmp)428 peffects(otmp)
429 register struct obj *otmp;
430 {
431 register int i, ii, lim;
432
433 switch(otmp->otyp){
434 case POT_RESTORE_ABILITY:
435 case SPE_RESTORE_ABILITY:
436 unkn++;
437 if(otmp->cursed) {
438 pline("Ulch! This makes you feel mediocre!");
439 break;
440 } else {
441 pline("Wow! This makes you feel %s!",
442 (otmp->blessed) ?
443 (unfixable_trouble_count(FALSE) ? "better" : "great")
444 : "good");
445 i = rn2(A_MAX); /* start at a random point */
446 for (ii = 0; ii < A_MAX; ii++) {
447 lim = AMAX(i);
448 if (i == A_STR && u.uhs >= 3) --lim; /* WEAK */
449 if (ABASE(i) < lim) {
450 ABASE(i) = lim;
451 flags.botl = 1;
452 /* only first found if not blessed */
453 if (!otmp->blessed) break;
454 }
455 if(++i >= A_MAX) i = 0;
456 }
457 }
458 break;
459 case POT_HALLUCINATION:
460 if (Hallucination || Halluc_resistance) nothing++;
461 (void) make_hallucinated(itimeout_incr(HHallucination,
462 rn1(200, 600 - 300 * bcsign(otmp))),
463 TRUE, 0L);
464 break;
465 case POT_WATER:
466 if(!otmp->blessed && !otmp->cursed) {
467 pline("This tastes like water.");
468 u.uhunger += rnd(10);
469 newuhs(FALSE);
470 break;
471 }
472 unkn++;
473 if(is_undead(youmonst.data) || is_demon(youmonst.data) ||
474 u.ualign.type == A_CHAOTIC) {
475 if(otmp->blessed) {
476 pline("This burns like acid!");
477 exercise(A_CON, FALSE);
478 if (u.ulycn >= LOW_PM) {
479 Your("affinity to %s disappears!",
480 makeplural(mons[u.ulycn].mname));
481 if (youmonst.data == &mons[u.ulycn])
482 you_unwere(FALSE);
483 u.ulycn = NON_PM; /* cure lycanthropy */
484 }
485 losehp(d(2,6), "potion of holy water", KILLED_BY_AN);
486 } else if(otmp->cursed) {
487 You_feel("quite proud of yourself.");
488 healup(d(2,6),0,0,0);
489 if (u.ulycn >= LOW_PM && !Upolyd) you_were();
490 exercise(A_CON, TRUE);
491 }
492 } else {
493 if(otmp->blessed) {
494 You_feel("full of awe.");
495 make_sick(0L, (char *) 0, TRUE, SICK_ALL);
496 exercise(A_WIS, TRUE);
497 exercise(A_CON, TRUE);
498 if (u.ulycn >= LOW_PM)
499 you_unwere(TRUE); /* "Purified" */
500 /* make_confused(0L,TRUE); */
501 } else {
502 if(u.ualign.type == A_LAWFUL) {
503 pline("This burns like acid!");
504 losehp(d(2,6), "potion of unholy water",
505 KILLED_BY_AN);
506 } else
507 You_feel("full of dread.");
508 if (u.ulycn >= LOW_PM && !Upolyd) you_were();
509 exercise(A_CON, FALSE);
510 }
511 }
512 break;
513 case POT_BOOZE:
514 unkn++;
515 pline("Ooph! This tastes like %s%s!",
516 otmp->odiluted ? "watered down " : "",
517 Hallucination ? "dandelion wine" : "liquid fire");
518 if (!otmp->blessed)
519 make_confused(itimeout_incr(HConfusion, d(3,8)), FALSE);
520 /* the whiskey makes us feel better */
521 if (!otmp->odiluted) healup(1, 0, FALSE, FALSE);
522 u.uhunger += 10 * (2 + bcsign(otmp));
523 newuhs(FALSE);
524 exercise(A_WIS, FALSE);
525 if(otmp->cursed) {
526 You("pass out.");
527 multi = -rnd(15);
528 nomovemsg = "You awake with a headache.";
529 }
530 break;
531 case POT_ENLIGHTENMENT:
532 if(otmp->cursed) {
533 unkn++;
534 You("have an uneasy feeling...");
535 exercise(A_WIS, FALSE);
536 } else {
537 if (otmp->blessed) {
538 (void) adjattrib(A_INT, 1, FALSE);
539 (void) adjattrib(A_WIS, 1, FALSE);
540 }
541 You_feel("self-knowledgeable...");
542 display_nhwindow(WIN_MESSAGE, FALSE);
543 enlightenment(0, TRUE);
544 pline_The("feeling subsides.");
545 exercise(A_WIS, TRUE);
546 }
547 break;
548 case SPE_INVISIBILITY:
549 /* spell cannot penetrate mummy wrapping */
550 if (BInvis && uarmc->otyp == MUMMY_WRAPPING) {
551 You_feel("rather itchy under your %s.", xname(uarmc));
552 break;
553 }
554 /* FALLTHRU */
555 case POT_INVISIBILITY:
556 if (Invis || Blind || BInvis) {
557 nothing++;
558 } else {
559 self_invis_message();
560 }
561 if (otmp->blessed) HInvis |= FROMOUTSIDE;
562 else incr_itimeout(&HInvis, rn1(15,31));
563 newsym(u.ux,u.uy); /* update position */
564 if(otmp->cursed) {
565 pline("For some reason, you feel your presence is known.");
566 aggravate();
567 }
568 break;
569 case POT_SEE_INVISIBLE:
570 /* tastes like fruit juice in Rogue */
571 case POT_FRUIT_JUICE:
572 {
573 int msg = Invisible && !Blind;
574
575 unkn++;
576 if (otmp->cursed)
577 pline("Yecch! This tastes %s.",
578 Hallucination ? "overripe" : "rotten");
579 else
580 pline(Hallucination ?
581 "This tastes like 10%% real %s%s all-natural beverage." :
582 "This tastes like %s%s.",
583 otmp->odiluted ? "reconstituted " : "",
584 fruitname(TRUE));
585 if (otmp->otyp == POT_FRUIT_JUICE) {
586 u.uhunger += (otmp->odiluted ? 5 : 10) * (2 + bcsign(otmp));
587 newuhs(FALSE);
588 break;
589 }
590 if (!otmp->cursed) {
591 /* Tell them they can see again immediately, which
592 * will help them identify the potion...
593 */
594 make_blinded(0L,TRUE);
595 }
596 if (otmp->blessed)
597 HSee_invisible |= FROMOUTSIDE;
598 else
599 incr_itimeout(&HSee_invisible, rn1(100,750));
600 set_mimic_blocking(); /* do special mimic handling */
601 see_monsters(); /* see invisible monsters */
602 newsym(u.ux,u.uy); /* see yourself! */
603 if (msg && !Blind) { /* Blind possible if polymorphed */
604 You("can see through yourself, but you are visible!");
605 unkn--;
606 }
607 break;
608 }
609 case POT_PARALYSIS:
610 if (Free_action)
611 You("stiffen momentarily.");
612 else {
613 if (Levitation || Is_airlevel(&u.uz)||Is_waterlevel(&u.uz))
614 You("are motionlessly suspended.");
615 #ifdef STEED
616 else if (u.usteed)
617 You("are frozen in place!");
618 #endif
619 else
620 Your("%s are frozen to the %s!",
621 makeplural(body_part(FOOT)), surface(u.ux, u.uy));
622 nomul(-(rn1(10, 25 - 12*bcsign(otmp))), "frozen by a potion");
623 nomovemsg = You_can_move_again;
624 exercise(A_DEX, FALSE);
625 }
626 break;
627 case POT_SLEEPING:
628 if(Sleep_resistance || Free_action)
629 You("yawn.");
630 else {
631 You("suddenly fall asleep!");
632 fall_asleep(-rn1(10, 25 - 12*bcsign(otmp)), TRUE);
633 }
634 break;
635 case POT_MONSTER_DETECTION:
636 case SPE_DETECT_MONSTERS:
637 if (otmp->blessed) {
638 int x, y;
639
640 if (Detect_monsters) nothing++;
641 unkn++;
642 /* after a while, repeated uses become less effective */
643 if (HDetect_monsters >= 300L)
644 i = 1;
645 else
646 i = rn1(40,21);
647 incr_itimeout(&HDetect_monsters, i);
648 for (x = 1; x < COLNO; x++) {
649 for (y = 0; y < ROWNO; y++) {
650 if (levl[x][y].glyph == GLYPH_INVISIBLE) {
651 unmap_object(x, y);
652 newsym(x,y);
653 }
654 if (MON_AT(x,y)) unkn = 0;
655 }
656 }
657 see_monsters();
658 if (unkn) You_feel("lonely.");
659 break;
660 }
661 if (monster_detect(otmp, 0))
662 return(1); /* nothing detected */
663 exercise(A_WIS, TRUE);
664 break;
665 case POT_OBJECT_DETECTION:
666 case SPE_DETECT_TREASURE:
667 if (object_detect(otmp, 0, FALSE))
668 return(1); /* nothing detected */
669 exercise(A_WIS, TRUE);
670 break;
671 case POT_SICKNESS:
672 pline("Yecch! This stuff tastes like poison.");
673 if (otmp->blessed) {
674 pline("(But in fact it was mildly stale %s.)",
675 fruitname(TRUE));
676 if (!Role_if(PM_HEALER)) {
677 /* NB: blessed otmp->fromsink is not possible */
678 losehp(1, "mildly contaminated potion", KILLED_BY_AN);
679 }
680 } else {
681 if(Poison_resistance)
682 pline(
683 "(But in fact it was biologically contaminated %s.)",
684 fruitname(TRUE));
685 if (Role_if(PM_HEALER))
686 pline("Fortunately, you have been immunized.");
687 else {
688 int typ = rn2(A_MAX);
689
690 if (!Fixed_abil) {
691 poisontell(typ);
692 (void) adjattrib(typ,
693 Poison_resistance ? -1 : -rn1(4,3),
694 TRUE);
695 }
696 if(!Poison_resistance) {
697 if (otmp->fromsink)
698 losehp(rnd(10)+5*!!(otmp->cursed),
699 "contaminated tap water", KILLED_BY);
700 else
701 losehp(rnd(10)+5*!!(otmp->cursed),
702 "contaminated potion", KILLED_BY_AN);
703 }
704 exercise(A_CON, FALSE);
705 }
706 }
707 if(Hallucination) {
708 You("are shocked back to your senses!");
709 (void) make_hallucinated(0L,FALSE,0L);
710 }
711 break;
712 case POT_CONFUSION:
713 if(!Confusion)
714 if (Hallucination) {
715 pline("What a trippy feeling!");
716 unkn++;
717 } else
718 pline("Huh, What? Where am I?");
719 else nothing++;
720 make_confused(itimeout_incr(HConfusion,
721 rn1(7, 16 - 8 * bcsign(otmp))),
722 FALSE);
723 break;
724 case POT_GAIN_ABILITY:
725 if(otmp->cursed) {
726 pline("Ulch! That potion tasted foul!");
727 unkn++;
728 } else if (Fixed_abil) {
729 nothing++;
730 } else { /* If blessed, try very hard to find an ability */
731 /* that can be increased; if not, try up to */
732 int itmp; /* 3 times to find one which can be increased. */
733 i = -1; /* increment to 0 */
734 for (ii = (otmp->blessed ? 1000 : A_MAX/2); ii > 0; ii--) {
735 i = rn2(A_MAX);
736 /* only give "your X is already as high as it can get"
737 message on last attempt */
738 itmp = (ii == 1) ? 0 : -1;
739 if (adjattrib(i, 1, itmp))
740 break;
741 }
742 }
743 break;
744 case POT_SPEED:
745 if(Wounded_legs && !otmp->cursed
746 #ifdef STEED
747 && !u.usteed /* heal_legs() would heal steeds legs */
748 #endif
749 ) {
750 heal_legs();
751 unkn++;
752 break;
753 } /* and fall through */
754 case SPE_HASTE_SELF:
755 if(!Very_fast) /* wwf@doe.carleton.ca */
756 You("are suddenly moving %sfaster.",
757 Fast ? "" : "much ");
758 else {
759 Your("%s get new energy.",
760 makeplural(body_part(LEG)));
761 unkn++;
762 }
763 exercise(A_DEX, TRUE);
764 incr_itimeout(&HFast, rn1(10, 100 + 60 * bcsign(otmp)));
765 break;
766 case POT_BLINDNESS:
767 if(Blind) nothing++;
768 make_blinded(itimeout_incr(Blinded,
769 rn1(200, 250 - 125 * bcsign(otmp))),
770 (boolean)!Blind);
771 break;
772 case POT_GAIN_LEVEL:
773 if (otmp->cursed) {
774 unkn++;
775 /* they went up a level */
776 if((ledger_no(&u.uz) == 1 && u.uhave.amulet) ||
777 Can_rise_up(u.ux, u.uy, &u.uz)) {
778 const char *riseup ="rise up, through the %s!";
779 if(ledger_no(&u.uz) == 1) {
780 You(riseup, ceiling(u.ux,u.uy));
781 #ifdef RANDOMIZED_PLANES
782 goto_level(get_first_elemental_plane(), FALSE, FALSE, FALSE);
783 #else
784 goto_level(&earth_level, FALSE, FALSE, FALSE);
785 #endif
786 } else {
787 register int newlev = depth(&u.uz)-1;
788 d_level newlevel;
789
790 get_level(&newlevel, newlev);
791 if(on_level(&newlevel, &u.uz)) {
792 pline("It tasted bad.");
793 break;
794 } else You(riseup, ceiling(u.ux,u.uy));
795 goto_level(&newlevel, FALSE, FALSE, FALSE);
796 }
797 }
798 else You("have an uneasy feeling.");
799 break;
800 }
801 pluslvl(FALSE);
802 if (otmp->blessed)
803 /* blessed potions place you at a random spot in the
804 * middle of the new level instead of the low point
805 */
806 u.uexp = rndexp(TRUE);
807 break;
808 case POT_HEALING:
809 You_feel("better.");
810 healup(d(6 + 2 * bcsign(otmp), 4),
811 !otmp->cursed ? 1 : 0, !!otmp->blessed, !otmp->cursed);
812 exercise(A_CON, TRUE);
813 break;
814 case POT_EXTRA_HEALING:
815 You_feel("much better.");
816 healup(d(6 + 2 * bcsign(otmp), 8),
817 otmp->blessed ? 5 : !otmp->cursed ? 2 : 0,
818 !otmp->cursed, TRUE);
819 (void) make_hallucinated(0L,TRUE,0L);
820 exercise(A_CON, TRUE);
821 exercise(A_STR, TRUE);
822 break;
823 case POT_FULL_HEALING:
824 You_feel("completely healed.");
825 healup(400, 4+4*bcsign(otmp), !otmp->cursed, TRUE);
826 /* Restore one lost level if blessed */
827 if (otmp->blessed && u.ulevel < u.ulevelmax) {
828 /* when multiple levels have been lost, drinking
829 multiple potions will only get half of them back */
830 u.ulevelmax -= 1;
831 pluslvl(FALSE);
832 }
833 (void) make_hallucinated(0L,TRUE,0L);
834 exercise(A_STR, TRUE);
835 exercise(A_CON, TRUE);
836 break;
837 case POT_LEVITATION:
838 case SPE_LEVITATION:
839 if (otmp->cursed) HLevitation &= ~I_SPECIAL;
840 if(!Levitation) {
841 /* kludge to ensure proper operation of float_up() */
842 HLevitation = 1;
843 float_up();
844 /* reverse kludge */
845 HLevitation = 0;
846 if (otmp->cursed && !Is_waterlevel(&u.uz)) {
847 if((u.ux != xupstair || u.uy != yupstair)
848 && (u.ux != sstairs.sx || u.uy != sstairs.sy || !sstairs.up)
849 && (!xupladder || u.ux != xupladder || u.uy != yupladder)
850 ) {
851 You("hit your %s on the %s.",
852 body_part(HEAD),
853 ceiling(u.ux,u.uy));
854 losehp(uarmh ? 1 : rnd(10),
855 "colliding with the ceiling",
856 KILLED_BY);
857 } else (void) doup();
858 }
859 } else
860 nothing++;
861 if (otmp->blessed) {
862 incr_itimeout(&HLevitation, rn1(50,250));
863 HLevitation |= I_SPECIAL;
864 } else incr_itimeout(&HLevitation, rn1(140,10));
865 spoteffects(FALSE); /* for sinks */
866 break;
867 case POT_GAIN_ENERGY: /* M. Stephenson */
868 { register int num;
869 if(otmp->cursed)
870 You_feel("lackluster.");
871 else
872 pline("Magical energies course through your body.");
873 num = rnd(5) + 5 * otmp->blessed + 1;
874 u.uenmax += (otmp->cursed) ? -num : num;
875 u.uen += (otmp->cursed) ? -num : num;
876 if(u.uenmax <= 0) u.uenmax = 0;
877 if(u.uen <= 0) u.uen = 0;
878 flags.botl = 1;
879 exercise(A_WIS, TRUE);
880 }
881 break;
882 case POT_OIL: /* P. Winner */
883 {
884 boolean good_for_you = FALSE;
885
886 if (otmp->lamplit) {
887 if (likes_fire(youmonst.data)) {
888 pline("Ahh, a refreshing drink.");
889 good_for_you = TRUE;
890 } else {
891 You("burn your %s.", body_part(FACE));
892 losehp(d(Fire_resistance ? 1 : 3, 4),
893 "burning potion of oil", KILLED_BY_AN);
894 }
895 } else if(otmp->cursed)
896 pline("This tastes like castor oil.");
897 else
898 pline("That was smooth!");
899 exercise(A_WIS, good_for_you);
900 }
901 break;
902 case POT_ACID:
903 if (Acid_resistance)
904 /* Not necessarily a creature who _likes_ acid */
905 pline("This tastes %s.", Hallucination ? "tangy" : "sour");
906 else {
907 pline("This burns%s!", otmp->blessed ? " a little" :
908 otmp->cursed ? " a lot" : " like acid");
909 losehp(d(otmp->cursed ? 2 : 1, otmp->blessed ? 4 : 8),
910 "potion of acid", KILLED_BY_AN);
911 exercise(A_CON, FALSE);
912 }
913 if (Stoned) fix_petrification();
914 unkn++; /* holy/unholy water can burn like acid too */
915 break;
916 case POT_POLYMORPH:
917 You_feel("a little %s.", Hallucination ? "normal" : "strange");
918 if (!Unchanging) polyself(FALSE);
919 break;
920 case POT_BLOOD:
921 case POT_VAMPIRE_BLOOD:
922 unkn++;
923 u.uconduct.unvegan++;
924 if (maybe_polyd(is_vampire(youmonst.data), Race_if(PM_VAMPIRE))) {
925 violated_vegetarian();
926 if (otmp->cursed)
927 pline("Yecch! This %s.", Hallucination ?
928 "liquid could do with a good stir" : "blood has congealed");
929 else pline(Hallucination ?
930 "The %s liquid stirs memories of home." :
931 "The %s blood tastes delicious.",
932 otmp->odiluted ? "watery" : "thick");
933 if (!otmp->cursed)
934 lesshungry((otmp->odiluted ? 1 : 2) *
935 (otmp->otyp == POT_VAMPIRE_BLOOD ? 400 :
936 otmp->blessed ? 15 : 10));
937 if (otmp->otyp == POT_VAMPIRE_BLOOD && otmp->blessed) {
938 int num = newhp();
939 if (Upolyd) {
940 u.mhmax += num;
941 u.mh += num;
942 } else {
943 u.uhpmax += num;
944 u.uhp += num;
945 }
946 }
947 } else if (otmp->otyp == POT_VAMPIRE_BLOOD) {
948 /* [CWC] fix conducts for potions of (vampire) blood -
949 doesn't use violated_vegetarian() to prevent
950 duplicated "you feel guilty" messages */
951 u.uconduct.unvegetarian++;
952 if (u.ualign.type == A_LAWFUL || Role_if(PM_MONK)) {
953 You_feel("%sguilty about drinking such a vile liquid.",
954 Role_if(PM_MONK) ? "especially " : "");
955 u.ugangr++;
956 adjalign(-15);
957 } else if (u.ualign.type == A_NEUTRAL)
958 adjalign(-3);
959 exercise(A_CON, FALSE);
960 if (!Unchanging) {
961 int successful_polymorph = FALSE;
962 if (otmp->blessed)
963 successful_polymorph = polymon(PM_VAMPIRE_LORD);
964 else if (otmp->cursed)
965 successful_polymorph = polymon(PM_VAMPIRE_BAT);
966 else
967 successful_polymorph = polymon(PM_VAMPIRE);
968 if (successful_polymorph)
969 u.mtimedone = 0; /* "Permament" change */
970 }
971 } else {
972 violated_vegetarian();
973 pline("Ugh. That was vile.");
974 make_vomiting(Vomiting+d(10,8), TRUE);
975 }
976 break;
977 default:
978 impossible("What a funny potion! (%u)", otmp->otyp);
979 return(0);
980 }
981 return(-1);
982 }
983
984 void
healup(nhp,nxtra,curesick,cureblind)985 healup(nhp, nxtra, curesick, cureblind)
986 int nhp, nxtra;
987 register boolean curesick, cureblind;
988 {
989 if (nhp) {
990 if (Upolyd) {
991 u.mh += nhp;
992 if (u.mh > u.mhmax) u.mh = (u.mhmax += nxtra);
993 } else {
994 u.uhp += nhp;
995 if(u.uhp > u.uhpmax) u.uhp = (u.uhpmax += nxtra);
996 }
997 }
998 check_uhpmax();
999
1000 if(cureblind) make_blinded(0L,TRUE);
1001 if(curesick) make_sick(0L, (char *) 0, TRUE, SICK_ALL);
1002 flags.botl = 1;
1003 return;
1004 }
1005
1006 void
strange_feeling(obj,txt)1007 strange_feeling(obj,txt)
1008 register struct obj *obj;
1009 register const char *txt;
1010 {
1011 if (flags.beginner || !txt)
1012 You("have a %s feeling for a moment, then it passes.",
1013 Hallucination ? "normal" : "strange");
1014 else
1015 pline("%s", txt);
1016
1017 if(!obj) /* e.g., crystal ball finds no traps */
1018 return;
1019
1020 if(obj->dknown && !objects[obj->otyp].oc_name_known &&
1021 !objects[obj->otyp].oc_uname)
1022 docall(obj);
1023 useup(obj);
1024 }
1025
1026 const char *bottlenames[] = {
1027 "bottle", "phial", "flagon", "carafe", "flask", "jar", "vial"
1028 };
1029
1030
1031 const char *
bottlename()1032 bottlename()
1033 {
1034 return bottlenames[rn2(SIZE(bottlenames))];
1035 }
1036
1037 void
potionhit(mon,obj,your_fault)1038 potionhit(mon, obj, your_fault)
1039 register struct monst *mon;
1040 register struct obj *obj;
1041 boolean your_fault;
1042 {
1043 register const char *botlnam = bottlename();
1044 boolean isyou = (mon == &youmonst);
1045 int distance;
1046 #ifdef WEBB_DISINT
1047 boolean disint = (touch_disintegrates(mon->data) &&
1048 !oresist_disintegration(obj) &&
1049 !mon->mcan &&
1050 mon->mhp>6);
1051 #endif
1052
1053 if (isyou) {
1054 distance = 0;
1055 pline_The("%s crashes on your %s and breaks into shards.",
1056 botlnam, body_part(HEAD));
1057 if (!heaven_or_hell_mode) {
1058 losehp(rnd(2), "thrown potion", KILLED_BY_AN);
1059 } else
1060 You_feel("as if something protected you.");
1061 } else {
1062 distance = distu(mon->mx,mon->my);
1063 #ifdef WEBB_DISINT
1064 if (!cansee(mon->mx,mon->my)) pline(disint?"Vip!":"Crash!");
1065 #else
1066 if (!cansee(mon->mx,mon->my)) pline("Crash!");
1067 #endif
1068 else {
1069 char *mnam = mon_nam(mon);
1070 char buf[BUFSZ];
1071
1072 if(has_head(mon->data)) {
1073 Sprintf(buf, "%s %s",
1074 s_suffix(mnam),
1075 (notonhead ? "body" : "head"));
1076 } else {
1077 Strcpy(buf, mnam);
1078 }
1079 #ifdef WEBB_DISINT
1080 pline_The("%s crashes on %s and %s.",
1081 botlnam, buf, disint?"disintegrates":"breaks into shards");
1082 #else
1083 pline_The("%s crashes on %s breaks into shards.",
1084 botlnam, buf);
1085 #endif
1086 }
1087 if(rn2(5) && mon->mhp > 1)
1088 mon->mhp--;
1089 }
1090
1091 /* oil doesn't instantly evaporate */
1092 if (obj->otyp != POT_OIL && cansee(mon->mx,mon->my)
1093 #ifdef WEBB_DISINT
1094 && !disint
1095 #endif
1096 )
1097 pline("%s.", Tobjnam(obj, "evaporate"));
1098
1099 if (isyou) {
1100 switch (obj->otyp) {
1101 case POT_OIL:
1102 if (obj->lamplit)
1103 splatter_burning_oil(u.ux, u.uy);
1104 break;
1105 case POT_POLYMORPH:
1106 You_feel("a little %s.", Hallucination ? "normal" : "strange");
1107 if (!Unchanging && !Antimagic) polyself(FALSE);
1108 break;
1109 case POT_ACID:
1110 if (!Acid_resistance) {
1111 pline("This burns%s!", obj->blessed ? " a little" :
1112 obj->cursed ? " a lot" : "");
1113 losehp(d(obj->cursed ? 2 : 1, obj->blessed ? 4 : 8),
1114 "potion of acid", KILLED_BY_AN);
1115 }
1116 break;
1117 }
1118 } else {
1119 boolean angermon = TRUE;
1120
1121 if (!your_fault) angermon = FALSE;
1122 #ifdef WEBB_DISINT
1123 if (!disint)
1124 #endif
1125 {
1126 switch (obj->otyp) {
1127 case POT_HEALING:
1128 case POT_EXTRA_HEALING:
1129 case POT_FULL_HEALING:
1130 if (mon->data == &mons[PM_PESTILENCE]) goto do_illness;
1131 /*FALLTHRU*/
1132 case POT_RESTORE_ABILITY:
1133 case POT_GAIN_ABILITY:
1134 do_healing:
1135 angermon = FALSE;
1136 if(mon->mhp < mon->mhpmax) {
1137 mon->mhp = mon->mhpmax;
1138 if (canseemon(mon))
1139 pline("%s looks sound and hale again.", Monnam(mon));
1140 }
1141 break;
1142 case POT_SICKNESS:
1143 if (mon->data == &mons[PM_PESTILENCE]) goto do_healing;
1144 if (dmgtype(mon->data, AD_DISE) ||
1145 dmgtype(mon->data, AD_PEST) || /* won't happen, see prior goto */
1146 resists_poison(mon)) {
1147 if (canseemon(mon))
1148 pline("%s looks unharmed.", Monnam(mon));
1149 break;
1150 }
1151 do_illness:
1152 if((mon->mhpmax > 3) && !resist(mon, POTION_CLASS, 0, NOTELL))
1153 mon->mhpmax /= 2;
1154 if((mon->mhp > 2) && !resist(mon, POTION_CLASS, 0, NOTELL))
1155 mon->mhp /= 2;
1156 if (mon->mhp > mon->mhpmax) mon->mhp = mon->mhpmax;
1157 if (canseemon(mon))
1158 pline("%s looks rather ill.", Monnam(mon));
1159 break;
1160 case POT_CONFUSION:
1161 case POT_BOOZE:
1162 if(!resist(mon, POTION_CLASS, 0, NOTELL)) mon->mconf = TRUE;
1163 break;
1164 case POT_INVISIBILITY:
1165 angermon = FALSE;
1166 mon_set_minvis(mon);
1167 break;
1168 case POT_SLEEPING:
1169 /* wakeup() doesn't rouse victims of temporary sleep */
1170 if (sleep_monst(mon, rnd(12), POTION_CLASS)) {
1171 pline("%s falls asleep.", Monnam(mon));
1172 slept_monst(mon);
1173 }
1174 break;
1175 case POT_PARALYSIS:
1176 if (mon->mcanmove) {
1177 mon->mcanmove = 0;
1178 /* really should be rnd(5) for consistency with players
1179 * breathing potions, but...
1180 */
1181 mon->mfrozen = rnd(25);
1182 }
1183 break;
1184 case POT_SPEED:
1185 angermon = FALSE;
1186 mon_adjust_speed(mon, 1, obj);
1187 break;
1188 case POT_BLINDNESS:
1189 if(haseyes(mon->data)) {
1190 register int btmp = 64 + rn2(32) +
1191 rn2(32) * !resist(mon, POTION_CLASS, 0, NOTELL);
1192 btmp += mon->mblinded;
1193 mon->mblinded = min(btmp,127);
1194 mon->mcansee = 0;
1195 }
1196 break;
1197 case POT_WATER:
1198 if (is_undead(mon->data) || is_demon(mon->data) ||
1199 is_were(mon->data)) {
1200 if (obj->blessed) {
1201 pline("%s %s in pain!", Monnam(mon),
1202 is_silent(mon->data) ? "writhes" : "shrieks");
1203 mon->mhp -= d(2,6);
1204 /* should only be by you */
1205 if (mon->mhp < 1) killed(mon);
1206 else if (is_were(mon->data) && !is_human(mon->data))
1207 new_were(mon); /* revert to human */
1208 } else if (obj->cursed) {
1209 angermon = FALSE;
1210 if (canseemon(mon))
1211 pline("%s looks healthier.", Monnam(mon));
1212 mon->mhp += d(2,6);
1213 if (mon->mhp > mon->mhpmax) mon->mhp = mon->mhpmax;
1214 if (is_were(mon->data) && is_human(mon->data) &&
1215 !Protection_from_shape_changers)
1216 new_were(mon); /* transform into beast */
1217 }
1218 } else if(mon->data == &mons[PM_GREMLIN]) {
1219 angermon = FALSE;
1220 (void)split_mon(mon, (struct monst *)0);
1221 } else if(mon->data == &mons[PM_IRON_GOLEM]) {
1222 if (canseemon(mon))
1223 pline("%s rusts.", Monnam(mon));
1224 mon->mhp -= d(1,6);
1225 /* should only be by you */
1226 if (mon->mhp < 1) killed(mon);
1227 }
1228 break;
1229 case POT_OIL:
1230 if (obj->lamplit)
1231 splatter_burning_oil(mon->mx, mon->my);
1232 break;
1233 case POT_ACID:
1234 if (!resists_acid(mon) && !resist(mon, POTION_CLASS, 0, NOTELL)) {
1235 pline("%s %s in pain!", Monnam(mon),
1236 is_silent(mon->data) ? "writhes" : "shrieks");
1237 mon->mhp -= d(obj->cursed ? 2 : 1, obj->blessed ? 4 : 8);
1238 if (mon->mhp < 1) {
1239 if (your_fault)
1240 killed(mon);
1241 else
1242 monkilled(mon, "", AD_ACID);
1243 }
1244 }
1245 break;
1246 case POT_POLYMORPH:
1247 (void) bhitm(mon, obj);
1248 break;
1249 /*
1250 case POT_GAIN_LEVEL:
1251 case POT_LEVITATION:
1252 case POT_FRUIT_JUICE:
1253 case POT_MONSTER_DETECTION:
1254 case POT_OBJECT_DETECTION:
1255 break;
1256 */
1257 }
1258 }
1259
1260 if (angermon)
1261 wakeup(mon);
1262 else
1263 mon->msleeping = 0;
1264 }
1265 #ifdef WEBB_DISINT
1266 if (!disint)
1267 #endif
1268 {
1269
1270 /* Note: potionbreathe() does its own docall() */
1271 if ((distance==0 || ((distance < 3) && rn2(5))) &&
1272 (!breathless(youmonst.data) || haseyes(youmonst.data)))
1273 potionbreathe(obj);
1274 else if (obj->dknown && !objects[obj->otyp].oc_name_known &&
1275 !objects[obj->otyp].oc_uname && cansee(mon->mx,mon->my))
1276 docall(obj);
1277 }
1278 if(*u.ushops && obj->unpaid) {
1279 register struct monst *shkp =
1280 shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE));
1281
1282 if(!shkp)
1283 obj->unpaid = 0;
1284 else {
1285 (void)stolen_value(obj, u.ux, u.uy,
1286 (boolean)shkp->mpeaceful, FALSE);
1287 subfrombill(obj, shkp);
1288 }
1289 }
1290 obfree(obj, (struct obj *)0);
1291 }
1292
1293 /* vapors are inhaled or get in your eyes */
1294 void
potionbreathe(obj)1295 potionbreathe(obj)
1296 register struct obj *obj;
1297 {
1298 register int i, ii, kn = 0;
1299
1300 switch(obj->otyp) {
1301 case POT_RESTORE_ABILITY:
1302 case POT_GAIN_ABILITY:
1303 if(obj->cursed) {
1304 if (!breathless(youmonst.data))
1305 pline("Ulch! That potion smells terrible!");
1306 else if (haseyes(youmonst.data)) {
1307 int numeyes = eyecount(youmonst.data);
1308 Your("%s sting%s!",
1309 (numeyes == 1) ? body_part(EYE) : makeplural(body_part(EYE)),
1310 (numeyes == 1) ? "s" : "");
1311 }
1312 break;
1313 } else {
1314 i = rn2(A_MAX); /* start at a random point */
1315 for(ii = 0; ii < A_MAX; ii++) {
1316 if(ABASE(i) < AMAX(i)) {
1317 ABASE(i)++;
1318 flags.botl = 1;
1319 }
1320 if(++i >= A_MAX) i = 0;
1321 }
1322 }
1323 break;
1324 case POT_FULL_HEALING:
1325 if (Upolyd && u.mh < u.mhmax) u.mh++, flags.botl = 1;
1326 if (u.uhp < u.uhpmax) u.uhp++, flags.botl = 1;
1327 /*FALL THROUGH*/
1328 case POT_EXTRA_HEALING:
1329 if (Upolyd && u.mh < u.mhmax) u.mh++, flags.botl = 1;
1330 if (u.uhp < u.uhpmax) u.uhp++, flags.botl = 1;
1331 /*FALL THROUGH*/
1332 case POT_HEALING:
1333 if (Upolyd && u.mh < u.mhmax) u.mh++, flags.botl = 1;
1334 if (u.uhp < u.uhpmax) u.uhp++, flags.botl = 1;
1335 exercise(A_CON, TRUE);
1336 break;
1337 case POT_SICKNESS:
1338 kn++;
1339 if (!Role_if(PM_HEALER)) {
1340 if (Upolyd) {
1341 if (u.mh <= 5) u.mh = 1; else u.mh -= 5;
1342 } else {
1343 if (u.uhp <= 5) u.uhp = 1; else u.uhp -= 5;
1344 }
1345 flags.botl = 1;
1346 exercise(A_CON, FALSE);
1347 }
1348 break;
1349 case POT_HALLUCINATION:
1350 kn++;
1351 You("have a momentary vision.");
1352 break;
1353 case POT_CONFUSION:
1354 case POT_BOOZE:
1355 if(!Confusion)
1356 You_feel("somewhat dizzy.");
1357 make_confused(itimeout_incr(HConfusion, rnd(5)), FALSE);
1358 break;
1359 case POT_INVISIBILITY:
1360 if (!Blind && !Invis) {
1361 kn++;
1362 pline("For an instant you %s!",
1363 See_invisible ? "could see right through yourself"
1364 : "couldn't see yourself");
1365 }
1366 break;
1367 case POT_PARALYSIS:
1368 kn++;
1369 if (!Free_action) {
1370 pline("%s seems to be holding you.", Something);
1371 nomul(-rnd(5), "frozen by a potion");
1372 nomovemsg = You_can_move_again;
1373 exercise(A_DEX, FALSE);
1374 } else You("stiffen momentarily.");
1375 break;
1376 case POT_SLEEPING:
1377 kn++;
1378 if (!Free_action && !Sleep_resistance) {
1379 You_feel("rather tired.");
1380 nomul(-rnd(5), "sleeping off a magical draught");
1381 nomovemsg = You_can_move_again;
1382 exercise(A_DEX, FALSE);
1383 } else You("yawn.");
1384 break;
1385 case POT_SPEED:
1386 if (!Fast) {
1387 kn++;
1388 Your("knees seem more flexible now.");
1389 }
1390 incr_itimeout(&HFast, rnd(5));
1391 exercise(A_DEX, TRUE);
1392 break;
1393 case POT_BLINDNESS:
1394 if (!Blind && !u.usleep) {
1395 kn++;
1396 pline("It suddenly gets dark.");
1397 }
1398 make_blinded(itimeout_incr(Blinded, rnd(5)), FALSE);
1399 if (!Blind && !u.usleep) Your("%s", vision_clears);
1400 break;
1401 case POT_WATER:
1402 if(u.umonnum == PM_GREMLIN) {
1403 (void)split_mon(&youmonst, (struct monst *)0);
1404 } else if (u.ulycn >= LOW_PM) {
1405 /* vapor from [un]holy water will trigger
1406 transformation but won't cure lycanthropy */
1407 if (obj->blessed && youmonst.data == &mons[u.ulycn])
1408 you_unwere(FALSE);
1409 else if (obj->cursed && !Upolyd)
1410 you_were();
1411 }
1412 break;
1413 case POT_ACID:
1414 case POT_POLYMORPH:
1415 exercise(A_CON, FALSE);
1416 break;
1417 case POT_BLOOD:
1418 case POT_VAMPIRE_BLOOD:
1419 if (maybe_polyd(is_vampire(youmonst.data), Race_if(PM_VAMPIRE))) {
1420 exercise(A_WIS, FALSE);
1421 You_feel("a %ssense of loss.",
1422 obj->otyp == POT_VAMPIRE_BLOOD ? "terrible " : "");
1423 } else
1424 exercise(A_CON, FALSE);
1425 break;
1426 /*
1427 case POT_GAIN_LEVEL:
1428 case POT_LEVITATION:
1429 case POT_FRUIT_JUICE:
1430 case POT_MONSTER_DETECTION:
1431 case POT_OBJECT_DETECTION:
1432 case POT_OIL:
1433 break;
1434 */
1435 }
1436 /* note: no obfree() */
1437 if (obj->dknown) {
1438 if (kn)
1439 makeknown(obj->otyp);
1440 else if (!objects[obj->otyp].oc_name_known &&
1441 !objects[obj->otyp].oc_uname)
1442 docall(obj);
1443 }
1444 }
1445
1446 /* new alchemy scheme based on color mixing
1447 * YANI by Graham Cox <aca00gac@shef.ac.uk>
1448 * Implemented by Nephi Allred <zindorsky@hotmail.com> on 15 Apr 2003
1449 *
1450 * Alchemical tables are based on 4 bits describing dark/light level, yellow, blue and red
1451 *
1452 * DYBR
1453 * 0000 white
1454 * 0001 pink
1455 * 0010 sky-blue
1456 * 0011 puce
1457 * 0100 yellow
1458 * 0101 orange
1459 * 0110 emerald
1460 * 0111 ochre
1461 * 1000 black
1462 * 1001 ruby
1463 * 1010 indigo
1464 * 1011 magenta
1465 * 1100 golden
1466 * 1101 amber
1467 * 1110 dark green
1468 * 1111 brown
1469 */
1470
1471 /* Assumes gain ability is first potion and vampire blood is last */
1472 char alchemy_table1[POT_VAMPIRE_BLOOD - POT_GAIN_ABILITY];
1473 short alchemy_table2[17];
1474
1475 #define ALCHEMY_WHITE 0
1476 #define ALCHEMY_BLACK 8
1477 #define ALCHEMY_GRAY (alchemy_table2[16])
1478 #define IS_PRIMARY_COLOR(x) (((x)&7)==1 || ((x)&7)==2 || ((x)&7)==4)
1479 #define IS_SECONDARY_COLOR(x) (((x)&7)==3 || ((x)&7)==5 || ((x)&7)==6)
1480 #define IS_LIGHT_COLOR(x) (((x)&8)==0)
1481 #define IS_DARK_COLOR(x) ((x)&8)
1482
1483 /** Does a one-time set up of alchemical tables. */
1484 STATIC_OVL void
alchemy_init()1485 alchemy_init()
1486 {
1487 static boolean init = FALSE;
1488
1489 if (!init) {
1490 short i;
1491 const char* potion_desc;
1492
1493 for(i=POT_GAIN_ABILITY; i<=POT_WATER; i++) {
1494 potion_desc = OBJ_DESCR(objects[i]);
1495 if (0==strcmp(potion_desc,"white")) {
1496 alchemy_table1[i-POT_GAIN_ABILITY]=0;
1497 alchemy_table2[0]=i;
1498 } else if (0==strcmp(potion_desc,"pink")) {
1499 alchemy_table1[i-POT_GAIN_ABILITY]=1;
1500 alchemy_table2[1]=i;
1501 } else if (0==strcmp(potion_desc,"sky blue")) {
1502 alchemy_table1[i-POT_GAIN_ABILITY]=2;
1503 alchemy_table2[2]=i;
1504 } else if (0==strcmp(potion_desc,"puce")) {
1505 alchemy_table1[i-POT_GAIN_ABILITY]=3;
1506 alchemy_table2[3]=i;
1507 } else if (0==strcmp(potion_desc,"yellow")) {
1508 alchemy_table1[i-POT_GAIN_ABILITY]=4;
1509 alchemy_table2[4]=i;
1510 } else if (0==strcmp(potion_desc,"orange")) {
1511 alchemy_table1[i-POT_GAIN_ABILITY]=5;
1512 alchemy_table2[5]=i;
1513 } else if (0==strcmp(potion_desc,"emerald")) {
1514 alchemy_table1[i-POT_GAIN_ABILITY]=6;
1515 alchemy_table2[6]=i;
1516 } else if (0==strcmp(potion_desc,"ochre")) {
1517 alchemy_table1[i-POT_GAIN_ABILITY]=7;
1518 alchemy_table2[7]=i;
1519 } else if (0==strcmp(potion_desc,"black")) {
1520 alchemy_table1[i-POT_GAIN_ABILITY]=8;
1521 alchemy_table2[8]=i;
1522 } else if (0==strcmp(potion_desc,"ruby")) {
1523 alchemy_table1[i-POT_GAIN_ABILITY]=9;
1524 alchemy_table2[9]=i;
1525 } else if (0==strcmp(potion_desc,"indigo")) {
1526 alchemy_table1[i-POT_GAIN_ABILITY]=10;
1527 alchemy_table2[10]=i;
1528 } else if (0==strcmp(potion_desc,"magenta")) {
1529 alchemy_table1[i-POT_GAIN_ABILITY]=11;
1530 alchemy_table2[11]=i;
1531 } else if (0==strcmp(potion_desc,"golden")) {
1532 alchemy_table1[i-POT_GAIN_ABILITY]=12;
1533 alchemy_table2[12]=i;
1534 } else if (0==strcmp(potion_desc,"amber")) {
1535 alchemy_table1[i-POT_GAIN_ABILITY]=13;
1536 alchemy_table2[13]=i;
1537 } else if (0==strcmp(potion_desc,"dark green")) {
1538 alchemy_table1[i-POT_GAIN_ABILITY]=14;
1539 alchemy_table2[14]=i;
1540 } else if (0==strcmp(potion_desc,"brown")) {
1541 alchemy_table1[i-POT_GAIN_ABILITY]=15;
1542 alchemy_table2[15]=i;
1543 } else if (0==strcmp(potion_desc,"silver")) {
1544 alchemy_table1[i-POT_GAIN_ABILITY]=-1;
1545 alchemy_table2[16]=i;
1546 } else {
1547 alchemy_table1[i-POT_GAIN_ABILITY]=-1;
1548 }
1549 }
1550 init = TRUE;
1551 }
1552 }
1553
1554 /** Returns the potion type when object o1 is dipped into object o2 */
1555 STATIC_OVL short
mixtype(o1,o2)1556 mixtype(o1, o2)
1557 register struct obj *o1, *o2;
1558 {
1559 /* cut down on the number of cases below */
1560 if (o1->oclass == POTION_CLASS &&
1561 (o2->otyp == POT_FRUIT_JUICE)) {
1562 struct obj *swp;
1563
1564 swp = o1; o1 = o2; o2 = swp;
1565 }
1566 switch (o1->otyp) {
1567 case POT_FRUIT_JUICE:
1568 switch (o2->otyp) {
1569 case POT_BLOOD:
1570 return POT_BLOOD;
1571 case POT_VAMPIRE_BLOOD:
1572 return POT_VAMPIRE_BLOOD;
1573 }
1574 break;
1575 }
1576
1577 if (o1->oclass == POTION_CLASS) {
1578 int i1,i2,result;
1579
1580 alchemy_init();
1581 i1 = alchemy_table1[o1->otyp-POT_GAIN_ABILITY];
1582 i2 = alchemy_table1[o2->otyp-POT_GAIN_ABILITY];
1583
1584 /* check that both potions are of mixable types */
1585 if (i1<0 || i2<0)
1586 return 0;
1587
1588 /* swap for simplified checks */
1589 if (i2==ALCHEMY_WHITE || (i2==ALCHEMY_BLACK && i1!=ALCHEMY_WHITE)) {
1590 result = i1;
1591 i1 = i2;
1592 i2 = result;
1593 }
1594
1595 if (i1==ALCHEMY_WHITE && i2==ALCHEMY_BLACK) {
1596 return ALCHEMY_GRAY;
1597 } else if ( (IS_PRIMARY_COLOR(i1) && IS_PRIMARY_COLOR(i2))
1598 || (IS_SECONDARY_COLOR(i1) && IS_SECONDARY_COLOR(i2)) ) {
1599 /* bitwise OR simulates pigment addition */
1600 result = i1 | i2;
1601 /* adjust light/dark level if necessary */
1602 if ((i1^i2)&8) {
1603 if (o1->odiluted==o2->odiluted) {
1604 /* same dilution level, randomly toggle */
1605 result ^= (rn2(2)<<3);
1606 } else {
1607 /* use dark/light level of undiluted potion */
1608 result ^= (o1->odiluted ? i1:i2)&8;
1609 }
1610 }
1611 } else if ((i1==ALCHEMY_WHITE && IS_DARK_COLOR(i2)) ||
1612 (i1==ALCHEMY_BLACK && IS_LIGHT_COLOR(i2))) {
1613 /* toggle light/dark bit */
1614 result = i2 ^ 8;
1615 } else {
1616 return 0;
1617 }
1618 if (OBJ_NAME(objects[alchemy_table2[result]]) == 0) {
1619 /* mixed potion doesn't exist in this game */
1620 return 0;
1621 } else {
1622 return alchemy_table2[result];
1623 }
1624 } else {
1625 switch (o1->otyp) {
1626 case UNICORN_HORN:
1627 switch (o2->otyp) {
1628 case POT_SICKNESS:
1629 return POT_FRUIT_JUICE;
1630 case POT_HALLUCINATION:
1631 case POT_BLINDNESS:
1632 case POT_CONFUSION:
1633 case POT_BLOOD:
1634 case POT_VAMPIRE_BLOOD:
1635 return POT_WATER;
1636 }
1637 break;
1638 case AMETHYST: /* "a-methyst" == "not intoxicated" */
1639 if (o2->otyp == POT_BOOZE)
1640 return POT_FRUIT_JUICE;
1641 break;
1642 }
1643 }
1644
1645 return 0;
1646 }
1647
1648
1649 boolean
get_wet(obj)1650 get_wet(obj)
1651 register struct obj *obj;
1652 /* returns TRUE if something happened (potion should be used up) */
1653 {
1654 char Your_buf[BUFSZ];
1655
1656 if (snuff_lit(obj)) return(TRUE);
1657
1658 if (obj->greased) {
1659 grease_protect(obj,(char *)0,&youmonst);
1660 return(FALSE);
1661 }
1662 (void) Shk_Your(Your_buf, obj);
1663 /* (Rusting shop goods ought to be charged for.) */
1664 switch (obj->oclass) {
1665 case POTION_CLASS:
1666 if (obj->otyp == POT_WATER) return FALSE;
1667 /* KMH -- Water into acid causes an explosion */
1668 if (obj->otyp == POT_ACID) {
1669 pline("It boils vigorously!");
1670 You("are caught in the explosion!");
1671 losehp(Acid_resistance ? rnd(5) : rnd(10),
1672 "elementary chemistry", KILLED_BY);
1673 makeknown(obj->otyp);
1674 update_inventory();
1675 return (TRUE);
1676 }
1677 pline("%s %s%s.", Your_buf, aobjnam(obj,"dilute"),
1678 obj->odiluted ? " further" : "");
1679 if(obj->unpaid && costly_spot(u.ux, u.uy)) {
1680 You("dilute it, you pay for it.");
1681 bill_dummy_object(obj);
1682 }
1683 if (obj->odiluted) {
1684 obj->odiluted = 0;
1685 #ifdef UNIXPC
1686 obj->blessed = FALSE;
1687 obj->cursed = FALSE;
1688 #else
1689 obj->blessed = obj->cursed = FALSE;
1690 #endif
1691 obj->otyp = POT_WATER;
1692 } else obj->odiluted++;
1693 update_inventory();
1694 return TRUE;
1695 case SCROLL_CLASS:
1696 if (obj->otyp != SCR_BLANK_PAPER && obj->otyp != SCR_FLOOD
1697 #ifdef MAIL
1698 && obj->otyp != SCR_MAIL
1699 #endif
1700 ) {
1701 if (!Blind) {
1702 boolean oq1 = obj->quan == 1L;
1703 pline_The("scroll%s %s.",
1704 oq1 ? "" : "s", otense(obj, "fade"));
1705 }
1706 if(obj->unpaid && costly_spot(u.ux, u.uy)) {
1707 You("erase it, you pay for it.");
1708 bill_dummy_object(obj);
1709 }
1710 obj->otyp = SCR_BLANK_PAPER;
1711 obj->spe = 0;
1712 update_inventory();
1713 return TRUE;
1714 } else break;
1715 case SPBOOK_CLASS:
1716 if (obj->otyp != SPE_BLANK_PAPER) {
1717
1718 if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
1719 pline("%s suddenly heats up; steam rises and it remains dry.",
1720 The(xname(obj)));
1721 } else {
1722 if (!Blind) {
1723 boolean oq1 = obj->quan == 1L;
1724 pline_The("spellbook%s %s.",
1725 oq1 ? "" : "s", otense(obj, "fade"));
1726 }
1727 if(obj->unpaid && costly_spot(u.ux, u.uy)) {
1728 You("erase it, you pay for it.");
1729 bill_dummy_object(obj);
1730 }
1731 obj->otyp = SPE_BLANK_PAPER;
1732 update_inventory();
1733 }
1734 return TRUE;
1735 }
1736 break;
1737 case WEAPON_CLASS:
1738 /* Just "fall through" to generic rustprone check for now. */
1739 /* fall through */
1740 default:
1741 if (!obj->oerodeproof && is_rustprone(obj) &&
1742 (obj->oeroded < MAX_ERODE) && !rn2(2)) {
1743 pline("%s %s some%s.",
1744 Your_buf, aobjnam(obj, "rust"),
1745 obj->oeroded ? " more" : "what");
1746 obj->oeroded++;
1747 update_inventory();
1748 return TRUE;
1749 } else break;
1750 }
1751 pline("%s %s wet.", Your_buf, aobjnam(obj,"get"));
1752 return FALSE;
1753 }
1754
1755 /** User command for dipping objects. */
1756 int
dodip()1757 dodip()
1758 {
1759 register struct obj *potion, *obj;
1760 const char *tmp;
1761 uchar here;
1762 char allowall[2];
1763 char qbuf[QBUFSZ];
1764
1765 allowall[0] = ALL_CLASSES; allowall[1] = '\0';
1766 if(!(obj = getobj(allowall, "dip")))
1767 return(0);
1768
1769 here = levl[u.ux][u.uy].typ;
1770 /* Is there a fountain to dip into here? */
1771 if (IS_FOUNTAIN(here)) {
1772 Sprintf(qbuf, "Dip %s into the fountain?",
1773 safe_qbuf("", sizeof("Dip into the fountain?"),
1774 the(xname(obj)), the(simple_typename(obj->otyp)),
1775 "this item"));
1776 if(yn(qbuf) == 'y') {
1777 dipfountain(obj);
1778 return(1);
1779 }
1780 } else if (is_pool(u.ux,u.uy) || is_swamp(u.ux,u.uy)) {
1781 tmp = waterbody_name(u.ux,u.uy);
1782 Sprintf(qbuf, "Dip %s into the %s?",
1783 safe_qbuf("", sizeof("Dip into the pool of water?"),
1784 the(xname(obj)), the(simple_typename(obj->otyp)),
1785 "this item"), tmp);
1786 if (yn(qbuf) == 'y') {
1787 if (Levitation) {
1788 floating_above(tmp);
1789 #ifdef STEED
1790 } else if (u.usteed && !is_swimmer(u.usteed->data) &&
1791 P_SKILL(P_RIDING) < P_BASIC) {
1792 rider_cant_reach(); /* not skilled enough to reach */
1793 #endif
1794 } else {
1795 (void) get_wet(obj);
1796 if (obj->otyp == POT_ACID) useup(obj);
1797 }
1798 return 1;
1799 }
1800 }
1801
1802 Sprintf(qbuf, "dip %s into",
1803 safe_qbuf("", sizeof("dip into"),
1804 the(xname(obj)), the(simple_typename(obj->otyp)),
1805 "this item"));
1806 if(!(potion = getobj(beverages, qbuf)))
1807 return(0);
1808 if (potion == obj && potion->quan == 1L) {
1809 pline("That is a potion bottle, not a Klein bottle!");
1810 return 0;
1811 }
1812
1813 return dip(potion, obj);
1814 }
1815
1816 /** Dip an arbitrary object into a potion. */
1817 int
dip(potion,obj)1818 dip(potion, obj)
1819 struct obj *potion, *obj;
1820 {
1821 struct obj *singlepotion;
1822 const char *tmp;
1823 short mixture;
1824 char Your_buf[BUFSZ];
1825
1826 potion->in_use = TRUE; /* assume it will be used up */
1827 if(potion->otyp == POT_WATER) {
1828 boolean useeit = !Blind;
1829 if (useeit) (void) Shk_Your(Your_buf, obj);
1830 if (potion->blessed) {
1831 if (obj->cursed) {
1832 if (useeit)
1833 pline("%s %s %s.",
1834 Your_buf,
1835 aobjnam(obj, "softly glow"),
1836 hcolor(NH_AMBER));
1837 uncurse(obj);
1838 obj->bknown=1;
1839 poof:
1840 if(!(objects[potion->otyp].oc_name_known) &&
1841 !(objects[potion->otyp].oc_uname))
1842 docall(potion);
1843 useup(potion);
1844 return(1);
1845 } else if(!obj->blessed) {
1846 if (useeit) {
1847 tmp = hcolor(NH_LIGHT_BLUE);
1848 pline("%s %s with a%s %s aura.",
1849 Your_buf,
1850 aobjnam(obj, "softly glow"),
1851 index(vowels, *tmp) ? "n" : "", tmp);
1852 }
1853 bless(obj);
1854 obj->bknown=1;
1855 goto poof;
1856 }
1857 } else if (potion->cursed) {
1858 if (obj->blessed) {
1859 if (useeit)
1860 pline("%s %s %s.",
1861 Your_buf,
1862 aobjnam(obj, "glow"),
1863 hcolor((const char *)"brown"));
1864 unbless(obj);
1865 obj->bknown=1;
1866 goto poof;
1867 } else if(!obj->cursed) {
1868 if (useeit) {
1869 tmp = hcolor(NH_BLACK);
1870 pline("%s %s with a%s %s aura.",
1871 Your_buf,
1872 aobjnam(obj, "glow"),
1873 index(vowels, *tmp) ? "n" : "", tmp);
1874 }
1875 curse(obj);
1876 obj->bknown=1;
1877 goto poof;
1878 }
1879 } else
1880 if (get_wet(obj))
1881 goto poof;
1882 } else if (obj->otyp == POT_POLYMORPH ||
1883 potion->otyp == POT_POLYMORPH) {
1884 /* some objects can't be polymorphed */
1885 if (obj->otyp == potion->otyp || /* both POT_POLY */
1886 obj->otyp == WAN_POLYMORPH ||
1887 obj->otyp == SPE_POLYMORPH ||
1888 obj->otyp == AMULET_OF_UNCHANGING ||
1889 obj == uball || obj == uskin ||
1890 obj_resists(obj->otyp == POT_POLYMORPH ?
1891 potion : obj, 5, 95)) {
1892 pline("%s", nothing_happens);
1893 } else {
1894 boolean was_wep = FALSE, was_swapwep = FALSE, was_quiver = FALSE;
1895 short save_otyp = obj->otyp;
1896 /* KMH, conduct */
1897 u.uconduct.polypiles++;
1898
1899 if (obj == uwep) was_wep = TRUE;
1900 else if (obj == uswapwep) was_swapwep = TRUE;
1901 else if (obj == uquiver) was_quiver = TRUE;
1902
1903 obj = poly_obj(obj, STRANGE_OBJECT);
1904
1905 if (was_wep) setuwep(obj);
1906 else if (was_swapwep) setuswapwep(obj);
1907 else if (was_quiver) setuqwep(obj);
1908
1909 if (obj->otyp != save_otyp) {
1910 makeknown(POT_POLYMORPH);
1911 useup(potion);
1912 prinv((char *)0, obj, 0L);
1913 return 1;
1914 } else {
1915 pline("Nothing seems to happen.");
1916 goto poof;
1917 }
1918 }
1919 potion->in_use = FALSE; /* didn't go poof */
1920 return(1);
1921 } else if(obj->oclass == POTION_CLASS && obj->otyp != potion->otyp) {
1922 /* Mixing potions is dangerous... */
1923 /* Give a clue to what's going on ... */
1924 if(potion->dknown && obj->dknown) {
1925 You("mix the %s potion with the %s one ...",
1926 OBJ_DESCR(objects[potion->otyp]),
1927 OBJ_DESCR(objects[obj->otyp]));
1928 } else
1929 pline_The("potions mix...");
1930 /* KMH, balance patch -- acid is particularly unstable */
1931 if (obj->cursed || obj->otyp == POT_ACID ||
1932 potion->otyp == POT_ACID ||
1933 !rn2(10)) {
1934 pline("BOOM! They explode!");
1935 exercise(A_STR, FALSE);
1936 if (!breathless(youmonst.data) || haseyes(youmonst.data))
1937 potionbreathe(obj);
1938 useup(obj);
1939 useup(potion);
1940 losehp(Acid_resistance ? rnd(5) : rnd(10),
1941 "alchemic blast", KILLED_BY_AN);
1942 return(1);
1943 }
1944
1945 obj->blessed = obj->cursed = obj->bknown = 0;
1946 if (Blind || Hallucination) obj->dknown = 0;
1947
1948 if ((mixture = mixtype(obj, potion)) != 0) {
1949 obj->otyp = mixture;
1950 } else {
1951 switch (obj->odiluted ? 1 : rnd(8)) {
1952 case 1:
1953 obj->otyp = POT_WATER;
1954 break;
1955 case 2:
1956 case 3:
1957 obj->otyp = POT_SICKNESS;
1958 break;
1959 case 4:
1960 {
1961 struct obj *otmp;
1962 otmp = mkobj(POTION_CLASS,FALSE);
1963 obj->otyp = otmp->otyp;
1964 obfree(otmp, (struct obj *)0);
1965 }
1966 break;
1967 default:
1968 if (!Blind)
1969 pline_The("mixture glows brightly and evaporates.");
1970 useup(obj);
1971 useup(potion);
1972 return(1);
1973 }
1974 }
1975
1976 obj->odiluted = (obj->otyp != POT_WATER);
1977
1978 if (OBJ_NAME(objects[obj->otyp]) == 0) {
1979 panic("dipping created an inexistant potion (%d)", obj->otyp);
1980 }
1981
1982 if (obj->otyp == POT_WATER && !Hallucination) {
1983 pline_The("mixture bubbles%s.",
1984 Blind ? "" : ", then clears");
1985 } else if (!Blind) {
1986 pline_The("mixture looks %s.",
1987 hcolor(OBJ_DESCR(objects[obj->otyp])));
1988 }
1989
1990 useup(potion);
1991 return(1);
1992 }
1993
1994 #ifdef INVISIBLE_OBJECTS
1995 if (potion->otyp == POT_INVISIBILITY && !obj->oinvis) {
1996 obj->oinvis = TRUE;
1997 if (!Blind) {
1998 if (!See_invisible) pline("Where did %s go?",
1999 the(xname(obj)));
2000 else You("notice a little haziness around %s.",
2001 the(xname(obj)));
2002 }
2003 goto poof;
2004 } else if (potion->otyp == POT_SEE_INVISIBLE && obj->oinvis) {
2005 obj->oinvis = FALSE;
2006 if (!Blind) {
2007 if (!See_invisible) pline("So that's where %s went!",
2008 the(xname(obj)));
2009 else pline_The("haziness around %s disappears.",
2010 the(xname(obj)));
2011 }
2012 goto poof;
2013 }
2014 #endif
2015 if (potion->otyp == POT_ACID && obj->otyp == CORPSE &&
2016 obj->corpsenm == PM_LICHEN && !Blind) {
2017 pline("%s %s %s around the edges.", The(cxname(obj)),
2018 otense(obj, "turn"),
2019 potion->odiluted ? hcolor(NH_ORANGE) : hcolor(NH_RED));
2020 makeknown(POT_ACID);
2021 potion->in_use = FALSE; /* didn't go poof */
2022 return(1);
2023 }
2024
2025 if(is_poisonable(obj)) {
2026 if(potion->otyp == POT_SICKNESS && !obj->opoisoned) {
2027 char buf[BUFSZ];
2028 if (potion->quan > 1L)
2029 Sprintf(buf, "One of %s", the(xname(potion)));
2030 else
2031 Strcpy(buf, The(xname(potion)));
2032 pline("%s forms a coating on %s.",
2033 buf, the(xname(obj)));
2034 obj->opoisoned = TRUE;
2035 goto poof;
2036 } else if(obj->opoisoned &&
2037 (potion->otyp == POT_HEALING ||
2038 potion->otyp == POT_EXTRA_HEALING ||
2039 potion->otyp == POT_FULL_HEALING)) {
2040 pline("A coating wears off %s.", the(xname(obj)));
2041 obj->opoisoned = 0;
2042 goto poof;
2043 }
2044 }
2045
2046 if (potion->otyp == POT_OIL) {
2047 boolean wisx = FALSE;
2048 if (potion->lamplit) { /* burning */
2049 int omat = objects[obj->otyp].oc_material;
2050 /* the code here should be merged with fire_damage */
2051 if (catch_lit(obj)) {
2052 /* catch_lit does all the work if true */
2053 } else if (obj->oerodeproof || obj_resists(obj, 5, 95) ||
2054 !is_flammable(obj) || obj->oclass == FOOD_CLASS) {
2055 pline("%s %s to burn for a moment.",
2056 Yname2(obj), otense(obj, "seem"));
2057 } else {
2058 if ((omat == PLASTIC || omat == PAPER) && !obj->oartifact)
2059 obj->oeroded = MAX_ERODE;
2060 pline_The("burning oil %s %s.",
2061 obj->oeroded == MAX_ERODE ? "destroys" : "damages",
2062 yname(obj));
2063 if (obj->oeroded == MAX_ERODE) {
2064 setnotworn(obj);
2065 obj_extract_self(obj);
2066 obfree(obj, (struct obj *)0);
2067 obj = (struct obj *) 0;
2068 } else {
2069 /* we know it's carried */
2070 if (obj->unpaid) {
2071 /* create a dummy duplicate to put on bill */
2072 verbalize("You burnt it, you bought it!");
2073 bill_dummy_object(obj);
2074 }
2075 obj->oeroded++;
2076 }
2077 }
2078 } else if (potion->cursed) {
2079 pline_The("potion spills and covers your %s with oil.",
2080 makeplural(body_part(FINGER)));
2081 incr_itimeout(&Glib, d(2,10));
2082 } else if (obj->oclass != WEAPON_CLASS && !is_weptool(obj)) {
2083 /* the following cases apply only to weapons */
2084 goto more_dips;
2085 /* Oil removes rust and corrosion, but doesn't unburn.
2086 * Arrows, etc are classed as metallic due to arrowhead
2087 * material, but dipping in oil shouldn't repair them.
2088 */
2089 } else if ((!is_rustprone(obj) && !is_corrodeable(obj)) ||
2090 is_ammo(obj) || (!obj->oeroded && !obj->oeroded2)) {
2091 /* uses up potion, doesn't set obj->greased */
2092 pline("%s %s with an oily sheen.",
2093 Yname2(obj), otense(obj, "gleam"));
2094 } else {
2095 pline("%s %s less %s.",
2096 Yname2(obj), otense(obj, "are"),
2097 (obj->oeroded && obj->oeroded2) ? "corroded and rusty" :
2098 obj->oeroded ? "rusty" : "corroded");
2099 if (obj->oeroded > 0) obj->oeroded--;
2100 if (obj->oeroded2 > 0) obj->oeroded2--;
2101 wisx = TRUE;
2102 }
2103 exercise(A_WIS, wisx);
2104 makeknown(potion->otyp);
2105 useup(potion);
2106 return 1;
2107 }
2108 more_dips:
2109
2110 /* Allow filling of MAGIC_LAMPs to prevent identification by player */
2111 if ((obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP) &&
2112 (potion->otyp == POT_OIL)) {
2113 /* Turn off engine before fueling, turn off fuel too :-) */
2114 if (obj->lamplit || potion->lamplit) {
2115 useup(potion);
2116 explode(u.ux, u.uy, 11, d(6,6), 0, EXPL_FIERY);
2117 exercise(A_WIS, FALSE);
2118 return 1;
2119 }
2120 /* Adding oil to an empty magic lamp renders it into an oil lamp */
2121 if ((obj->otyp == MAGIC_LAMP) && obj->spe == 0) {
2122 obj->otyp = OIL_LAMP;
2123 obj->age = 0;
2124 }
2125 if (obj->age > 1000L) {
2126 pline("%s %s full.", Yname2(obj), otense(obj, "are"));
2127 potion->in_use = FALSE; /* didn't go poof */
2128 } else {
2129 You("fill %s with oil.", yname(obj));
2130 check_unpaid(potion); /* Yendorian Fuel Tax */
2131 obj->age += 2*potion->age; /* burns more efficiently */
2132 if (obj->age > 1500L) obj->age = 1500L;
2133 useup(potion);
2134 exercise(A_WIS, TRUE);
2135 }
2136 makeknown(POT_OIL);
2137 obj->spe = 1;
2138 update_inventory();
2139 return 1;
2140 }
2141
2142 potion->in_use = FALSE; /* didn't go poof */
2143 if ((obj->otyp == UNICORN_HORN || obj->otyp == AMETHYST) &&
2144 (mixture = mixtype(obj, potion)) != 0) {
2145 char oldbuf[BUFSZ], newbuf[BUFSZ];
2146 short old_otyp = potion->otyp;
2147 boolean old_dknown = FALSE;
2148 boolean more_than_one = potion->quan > 1;
2149
2150 oldbuf[0] = '\0';
2151 if (potion->dknown) {
2152 old_dknown = TRUE;
2153 Sprintf(oldbuf, "%s ",
2154 hcolor(OBJ_DESCR(objects[potion->otyp])));
2155 }
2156 /* with multiple merged potions, split off one and
2157 just clear it */
2158 if (potion->quan > 1L) {
2159 singlepotion = splitobj(potion, 1L);
2160 } else singlepotion = potion;
2161
2162 if(singlepotion->unpaid && costly_spot(u.ux, u.uy)) {
2163 You("use it, you pay for it.");
2164 bill_dummy_object(singlepotion);
2165 }
2166 singlepotion->otyp = mixture;
2167 singlepotion->blessed = 0;
2168 if (mixture == POT_WATER)
2169 singlepotion->cursed = singlepotion->odiluted = 0;
2170 else
2171 singlepotion->cursed = obj->cursed; /* odiluted left as-is */
2172 singlepotion->bknown = FALSE;
2173 if (Blind) {
2174 singlepotion->dknown = FALSE;
2175 } else {
2176 singlepotion->dknown = !Hallucination;
2177 if (mixture == POT_WATER && singlepotion->dknown)
2178 Sprintf(newbuf, "clears");
2179 else
2180 Sprintf(newbuf, "turns %s",
2181 hcolor(OBJ_DESCR(objects[mixture])));
2182 pline_The("%spotion%s %s.", oldbuf,
2183 more_than_one ? " that you dipped into" : "",
2184 newbuf);
2185 if(!objects[old_otyp].oc_uname &&
2186 !objects[old_otyp].oc_name_known && old_dknown) {
2187 struct obj fakeobj;
2188 fakeobj = zeroobj;
2189 fakeobj.dknown = 1;
2190 fakeobj.otyp = old_otyp;
2191 fakeobj.oclass = POTION_CLASS;
2192 docall(&fakeobj);
2193 }
2194 }
2195 obj_extract_self(singlepotion);
2196 singlepotion = hold_another_object(singlepotion,
2197 "You juggle and drop %s!",
2198 doname(singlepotion), (const char *)0);
2199 update_inventory();
2200 return(1);
2201 }
2202
2203 pline("Interesting...");
2204 return(1);
2205 }
2206
2207
2208 void
djinni_from_bottle(obj)2209 djinni_from_bottle(obj)
2210 register struct obj *obj;
2211 {
2212 struct monst *mtmp;
2213 int chance;
2214
2215 if(!(mtmp = makemon(&mons[PM_DJINNI], u.ux, u.uy, NO_MM_FLAGS))){
2216 pline("It turns out to be empty.");
2217 return;
2218 }
2219
2220 if (!Blind) {
2221 pline("In a cloud of smoke, %s emerges!", a_monnam(mtmp));
2222 pline("%s speaks.", Monnam(mtmp));
2223 } else {
2224 You("smell acrid fumes.");
2225 pline("%s speaks.", Something);
2226 }
2227
2228 chance = rn2(5);
2229 if (obj->blessed) chance = (chance == 4) ? rnd(4) : 0;
2230 else if (obj->cursed) chance = (chance == 0) ? rn2(4) : 4;
2231 /* 0,1,2,3,4: b=80%,5,5,5,5; nc=20%,20,20,20,20; c=5%,5,5,5,80 */
2232
2233 switch (chance) {
2234 case 0 : verbalize("I am in your debt. I will grant one wish!");
2235 makewish(FALSE);
2236 mongone(mtmp);
2237 break;
2238 case 1 : verbalize("Thank you for freeing me!");
2239 (void) tamedog(mtmp, (struct obj *)0);
2240 break;
2241 case 2 : verbalize("You freed me!");
2242 mtmp->mpeaceful = TRUE;
2243 set_malign(mtmp);
2244 break;
2245 case 3 : verbalize("It is about time!");
2246 pline("%s vanishes.", Monnam(mtmp));
2247 mongone(mtmp);
2248 break;
2249 default: verbalize("You disturbed me, fool!");
2250 break;
2251 }
2252 }
2253
2254 /* clone a gremlin or mold (2nd arg non-null implies heat as the trigger);
2255 hit points are cut in half (odd HP stays with original) */
2256 struct monst *
split_mon(mon,mtmp)2257 split_mon(mon, mtmp)
2258 struct monst *mon, /* monster being split */
2259 *mtmp; /* optional attacker whose heat triggered it */
2260 {
2261 struct monst *mtmp2;
2262 char reason[BUFSZ];
2263
2264 reason[0] = '\0';
2265 if (mtmp) Sprintf(reason, " from %s heat",
2266 (mtmp == &youmonst) ? (const char *)"your" :
2267 (const char *)s_suffix(mon_nam(mtmp)));
2268
2269 if (mon == &youmonst) {
2270 mtmp2 = cloneu();
2271 if (mtmp2) {
2272 mtmp2->mhpmax = u.mhmax / 2;
2273 u.mhmax -= mtmp2->mhpmax;
2274 flags.botl = 1;
2275 You("multiply%s!", reason);
2276 }
2277 } else {
2278 mtmp2 = clone_mon(mon, 0, 0);
2279 if (mtmp2) {
2280 mtmp2->mhpmax = mon->mhpmax / 2;
2281 mon->mhpmax -= mtmp2->mhpmax;
2282 if (canspotmon(mon))
2283 pline("%s multiplies%s!", Monnam(mon), reason);
2284 }
2285 }
2286 return mtmp2;
2287 }
2288
2289 /*potion.c*/
2290