/* SCCS Id: @(#)potion.c 3.3 2001/12/07 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ #include "hack.h" #ifdef OVLB boolean notonhead = FALSE; static NEARDATA int nothing, unkn; static NEARDATA const char beverages[] = { POTION_CLASS, 0 }; STATIC_DCL long FDECL(itimeout, (long)); STATIC_DCL long FDECL(itimeout_incr, (long,int)); STATIC_DCL void NDECL(ghost_from_bottle); STATIC_DCL short FDECL(mixtype, (struct obj *,struct obj *)); /* force `val' to be within valid range for intrinsic timeout value */ STATIC_OVL long itimeout(val) long val; { if (val >= TIMEOUT) val = TIMEOUT; else if (val < 1) val = 0; return val; } /* increment `old' by `incr' and force result to be valid intrinsic timeout */ STATIC_OVL long itimeout_incr(old, incr) long old; int incr; { return itimeout((old & TIMEOUT) + (long)incr); } /* set the timeout field of intrinsic `which' */ void set_itimeout(which, val) long *which, val; { *which &= ~TIMEOUT; *which |= itimeout(val); } /* increment the timeout field of intrinsic `which' */ void incr_itimeout(which, incr) long *which; int incr; { set_itimeout(which, itimeout_incr(*which, incr)); } void make_confused(xtime,talk) long xtime; boolean talk; { long old = HConfusion; if (!xtime && old) { if (talk) You_feel("less %s now.", Hallucination ? "trippy" : "confused"); } if ((xtime && !old) || (!xtime && old)) flags.botl = TRUE; set_itimeout(&HConfusion, xtime); } void make_stunned(xtime,talk) long xtime; boolean talk; { long old = HStun; if (!xtime && old) { if (talk) You_feel("%s now.", Hallucination ? "less wobbly" : "a bit steadier"); } if (xtime && !old) { if (talk) { #ifdef STEED if (u.usteed) You("wobble in the saddle."); else #endif You("%s...", stagger(youmonst.data, "stagger")); } } if ((!xtime && old) || (xtime && !old)) flags.botl = TRUE; set_itimeout(&HStun, xtime); } void make_sick(xtime, cause, talk, type) long xtime; const char *cause; /* sickness cause */ boolean talk; int type; { long old = Sick; if (xtime > 0L) { if (Sick_resistance) return; if (!old) { /* newly sick */ You_feel("deathly sick."); } else { /* already sick */ if (talk) You_feel("%s worse.", xtime <= Sick/2L ? "much" : "even"); } set_itimeout(&Sick, xtime); u.usick_type |= type; flags.botl = TRUE; } else if (old && (type & u.usick_type)) { /* was sick, now not */ u.usick_type &= ~type; if (u.usick_type) { /* only partly cured */ if (talk) You_feel("somewhat better."); set_itimeout(&Sick, Sick * 2); /* approximation */ } else { if (talk) pline("What a relief!"); Sick = 0L; /* set_itimeout(&Sick, 0L) */ } flags.botl = TRUE; } if (Sick) { exercise(A_CON, FALSE); if (cause) { (void) strncpy(u.usick_cause, cause, sizeof(u.usick_cause)); u.usick_cause[sizeof(u.usick_cause)-1] = 0; } else u.usick_cause[0] = 0; } else u.usick_cause[0] = 0; } void make_vomiting(xtime, talk) long xtime; boolean talk; { long old = Vomiting; if(!xtime && old) if(talk) You_feel("much less nauseous now."); set_itimeout(&Vomiting, xtime); } void make_blinded(xtime, talk) long xtime; boolean talk; { long old = Blinded; boolean u_could_see, can_see_now; int eyecnt; char buf[BUFSZ]; static const char vismsg[] = "vision seems to %s for a moment but is %s now.", eyemsg[] = "%s momentarily %s."; /* we need to probe ahead in case the Eyes of the Overworld are or will be overriding blindness */ u_could_see = !Blind; Blinded = xtime ? 1L : 0L; can_see_now = !Blind; Blinded = old; /* restore */ if (u.usleep) talk = FALSE; if (can_see_now && !u_could_see) { /* regaining sight */ if (talk) { if (Hallucination) pline("Far out! Everything is all cosmic again!"); else You("can see again."); } } else if (old && !xtime) { /* clearing temporary blindness without toggling blindness */ if (talk) { if (!haseyes(youmonst.data)) { strange_feeling((struct obj *)0, (char *)0); } else if (Blindfolded) { Strcpy(buf, body_part(EYE)); eyecnt = eyecount(youmonst.data); Your(eyemsg, (eyecnt == 1) ? buf : makeplural(buf), (eyecnt == 1) ? "itches" : "itch"); } else { /* Eyes of the Overworld */ Your(vismsg, "brighten", Hallucination ? "sadder" : "normal"); } } } if (u_could_see && !can_see_now) { /* losing sight */ if (talk) { if (Hallucination) pline("Oh, bummer! Everything is dark! Help!"); else pline("A cloud of darkness falls upon you."); } /* Before the hero goes blind, set the ball&chain variables. */ if (Punished) set_bc(0); } else if (!old && xtime) { /* setting temporary blindness without toggling blindness */ if (talk) { if (!haseyes(youmonst.data)) { strange_feeling((struct obj *)0, (char *)0); } else if (Blindfolded) { Strcpy(buf, body_part(EYE)); eyecnt = eyecount(youmonst.data); Your(eyemsg, (eyecnt == 1) ? buf : makeplural(buf), (eyecnt == 1) ? "twitches" : "twitch"); } else { /* Eyes of the Overworld */ Your(vismsg, "dim", Hallucination ? "happier" : "normal"); } } } set_itimeout(&Blinded, xtime); if (u_could_see ^ can_see_now) { /* one or the other but not both */ flags.botl = 1; vision_full_recalc = 1; /* blindness just got toggled */ if (Blind_telepat || Infravision) see_monsters(); } } void make_hallucinated(xtime, talk, mask) long xtime; /* nonzero if this is an attempt to turn on hallucination */ boolean talk; long mask; /* nonzero if resistance status should change by mask */ { boolean changed = 0; #ifdef LINT const char *message = 0; #else const char *message; #endif if (!xtime) message = "Everything looks SO boring now."; else message = "Oh wow! Everything seems so cosmic!"; if (mask) { if (HHallucination) changed = TRUE; if (!xtime) EHalluc_resistance |= mask; else EHalluc_resistance &= ~mask; } else { if (!EHalluc_resistance && (!!HHallucination != !!xtime)) changed = TRUE; set_itimeout(&HHallucination, xtime); } if (changed) { if (u.uswallow) { swallowed(0); /* redraw swallow display */ } else { /* The see_* routines should be called *before* the pline. */ see_monsters(); see_objects(); see_traps(); } flags.botl = 1; if (!Blind && talk) pline(message); } } STATIC_OVL void ghost_from_bottle() { struct monst *mtmp = makemon(&mons[PM_GHOST], u.ux, u.uy, NO_MM_FLAGS); if (!mtmp) { pline("This bottle turns out to be empty."); return; } if (Blind) { pline("As you open the bottle, %s emerges.", something); return; } pline("As you open the bottle, an enormous %s emerges!", Hallucination ? rndmonnam() : (const char *)"ghost"); if(flags.verbose) You("are frightened to death, and unable to move."); nomul(-3); nomovemsg = "You regain your composure."; } int dodrink() { register struct obj *otmp; const char *potion_descr; if (Strangled) { pline("If you can't breathe air, how can you drink liquid?"); return 0; } /* Is there a fountain to drink from here? */ if (IS_FOUNTAIN(levl[u.ux][u.uy].typ) && !Levitation) { if(yn("Drink from the fountain?") == 'y') { drinkfountain(); return 1; } } #ifdef SINKS /* Or a kitchen sink? */ if (IS_SINK(levl[u.ux][u.uy].typ)) { if (yn("Drink from the sink?") == 'y') { drinksink(); return 1; } } #endif /* Or are you surrounded by water? */ if (Underwater) { if (yn("Drink the water around you?") == 'y') { pline("Do you know what lives in this water!"); return 1; } } otmp = getobj(beverages, "drink"); if(!otmp) return(0); otmp->in_use = TRUE; /* you've opened the stopper */ #define POTION_OCCUPANT_CHANCE(n) (13 + 2*(n)) /* also in muse.c */ potion_descr = OBJ_DESCR(objects[otmp->otyp]); if (potion_descr) { if (!strcmp(potion_descr, "milky") && flags.ghost_count < MAXMONNO && !rn2(POTION_OCCUPANT_CHANCE(flags.ghost_count))) { ghost_from_bottle(); useup(otmp); return(1); } else if (!strcmp(potion_descr, "smoky") && flags.djinni_count < MAXMONNO && !rn2(POTION_OCCUPANT_CHANCE(flags.djinni_count))) { djinni_from_bottle(otmp); useup(otmp); return(1); } } return dopotion(otmp); } int dopotion(otmp) register struct obj *otmp; { int retval; nothing = unkn = 0; if((retval = peffects(otmp)) >= 0) return(retval); if(nothing) { unkn++; You("have a %s feeling for a moment, then it passes.", Hallucination ? "normal" : "peculiar"); } if(otmp->dknown && !objects[otmp->otyp].oc_name_known) { if(!unkn) { makeknown(otmp->otyp); more_experienced(0,10); } else if(!objects[otmp->otyp].oc_uname) docall(otmp); } useup(otmp); return(1); } int peffects(otmp) register struct obj *otmp; { register int i, ii, lim; switch(otmp->otyp){ case POT_RESTORE_ABILITY: case SPE_RESTORE_ABILITY: unkn++; if(otmp->cursed) { pline("Ulch! This makes you feel mediocre!"); break; } else { pline("Wow! This makes you feel %s!", (otmp->blessed) ? (unfixable_trouble_count(FALSE) ? "better" : "great") : "good"); i = rn2(A_MAX); /* start at a random point */ for (ii = 0; ii < A_MAX; ii++) { lim = AMAX(i); if (i == A_STR && u.uhs >= 3) --lim; /* WEAK */ if (ABASE(i) < lim) { ABASE(i) = lim; flags.botl = 1; /* only first found if not blessed */ if (!otmp->blessed) break; } if(++i >= A_MAX) i = 0; } } break; case POT_HALLUCINATION: if (Hallucination || Halluc_resistance) nothing++; make_hallucinated(itimeout_incr(HHallucination, rn1(200, 600 - 300 * bcsign(otmp))), TRUE, 0L); break; case POT_WATER: if(!otmp->blessed && !otmp->cursed) { pline("This tastes like water."); u.uhunger += rnd(10); newuhs(FALSE); break; } unkn++; if(is_undead(youmonst.data) || is_demon(youmonst.data) || u.ualign.type == A_CHAOTIC) { if(otmp->blessed) { pline("This burns like acid!"); exercise(A_CON, FALSE); if (u.ulycn >= LOW_PM) { Your("affinity to %s disappears!", makeplural(mons[u.ulycn].mname)); if (youmonst.data == &mons[u.ulycn]) you_unwere(FALSE); u.ulycn = NON_PM; /* cure lycanthropy */ } losehp(d(2,6), "potion of holy water", KILLED_BY_AN); } else if(otmp->cursed) { You_feel("quite proud of yourself."); healup(d(2,6),0,0,0); if (u.ulycn >= LOW_PM && !Upolyd) you_were(); exercise(A_CON, TRUE); } } else { if(otmp->blessed) { You_feel("full of awe."); make_sick(0L, (char *) 0, TRUE, SICK_ALL); exercise(A_WIS, TRUE); exercise(A_CON, TRUE); if (u.ulycn >= LOW_PM) you_unwere(TRUE); /* "Purified" */ /* make_confused(0L,TRUE); */ } else { if(u.ualign.type == A_LAWFUL) { pline("This burns like acid!"); losehp(d(2,6), "potion of unholy water", KILLED_BY_AN); } else You_feel("full of dread."); if (u.ulycn >= LOW_PM && !Upolyd) you_were(); exercise(A_CON, FALSE); } } break; case POT_BOOZE: unkn++; pline("Ooph! This tastes like %s%s!", otmp->odiluted ? "watered down " : "", Hallucination ? "dandelion wine" : "liquid fire"); if (!otmp->blessed) make_confused(itimeout_incr(HConfusion, d(3,8)), FALSE); /* the whiskey makes us feel better */ if (!otmp->odiluted) healup(1, 0, FALSE, FALSE); u.uhunger += 10 * (2 + bcsign(otmp)); newuhs(FALSE); exercise(A_WIS, FALSE); if(otmp->cursed) { You("pass out."); multi = -rnd(15); nomovemsg = "You awake with a headache."; } break; case POT_ENLIGHTENMENT: if(otmp->cursed) { unkn++; You("have an uneasy feeling..."); exercise(A_WIS, FALSE); } else { if (otmp->blessed) { (void) adjattrib(A_INT, 1, FALSE); (void) adjattrib(A_WIS, 1, FALSE); } You_feel("self-knowledgeable..."); display_nhwindow(WIN_MESSAGE, FALSE); enlightenment(0); pline_The("feeling subsides."); exercise(A_WIS, TRUE); } break; case SPE_INVISIBILITY: /* spell cannot penetrate mummy wrapping */ if (BInvis && uarmc->otyp == MUMMY_WRAPPING) { You_feel("rather itchy under your %s.", xname(uarmc)); break; } /* FALLTHRU */ case POT_INVISIBILITY: if (Invis || Blind || BInvis) { nothing++; } else { self_invis_message(); } if (otmp->blessed) HInvis |= FROMOUTSIDE; else incr_itimeout(&HInvis, rn1(15,31)); newsym(u.ux,u.uy); /* update position */ if(otmp->cursed) { pline("For some reason, you feel your presence is known."); aggravate(); } break; case POT_SEE_INVISIBLE: /* tastes like fruit juice in Rogue */ case POT_FRUIT_JUICE: { int msg = Invisible && !Blind; unkn++; if (otmp->cursed) pline("Yecch! This tastes %s.", Hallucination ? "overripe" : "rotten"); else pline(Hallucination ? "This tastes like 10%% real %s%s juice all-natural beverage." : "This tastes like %s%s juice.", otmp->odiluted ? "reconstituted " : "", pl_fruit); if (otmp->otyp == POT_FRUIT_JUICE) { u.uhunger += (otmp->odiluted ? 5 : 10) * (2 + bcsign(otmp)); newuhs(FALSE); break; } if (!otmp->cursed) { /* Tell them they can see again immediately, which * will help them identify the potion... */ make_blinded(0L,TRUE); } if (otmp->blessed) HSee_invisible |= FROMOUTSIDE; else incr_itimeout(&HSee_invisible, rn1(100,750)); set_mimic_blocking(); /* do special mimic handling */ see_monsters(); /* see invisible monsters */ newsym(u.ux,u.uy); /* see yourself! */ if (msg && !Blind) { /* Blind possible if polymorphed */ You("can see through yourself, but you are visible!"); unkn--; } break; } case POT_PARALYSIS: if (Free_action) You("stiffen momentarily."); else { if (Levitation||Is_airlevel(&u.uz)||Is_waterlevel(&u.uz)) You("are motionlessly suspended."); #ifdef STEED else if (u.usteed) You("are frozen in place!"); #endif else Your("%s are frozen to the %s!", makeplural(body_part(FOOT)), surface(u.ux, u.uy)); nomul(-(rn1(10, 25 - 12*bcsign(otmp)))); nomovemsg = You_can_move_again; exercise(A_DEX, FALSE); } break; case POT_SLEEPING: if(Sleep_resistance || Free_action) You("yawn."); else { You("suddenly fall asleep!"); fall_asleep(-rn1(10, 25 - 12*bcsign(otmp)), TRUE); } break; case POT_MONSTER_DETECTION: case SPE_DETECT_MONSTERS: if (otmp->blessed) { int x, y; if (Detect_monsters) nothing++; unkn++; /* after a while, repeated uses become less effective */ if (HDetect_monsters >= 300L) i = 1; else i = rn1(40,21); incr_itimeout(&HDetect_monsters, i); for (x = 1; x < COLNO; x++) { for (y = 0; y < ROWNO; y++) { if (levl[x][y].glyph == GLYPH_INVISIBLE) { unmap_object(x, y); newsym(x,y); } if (MON_AT(x,y)) unkn = 0; } } see_monsters(); if (unkn) You_feel("lonely."); break; } if (monster_detect(otmp, 0)) return(1); /* nothing detected */ exercise(A_WIS, TRUE); break; case POT_OBJECT_DETECTION: case SPE_DETECT_TREASURE: if (object_detect(otmp, 0)) return(1); /* nothing detected */ exercise(A_WIS, TRUE); break; case POT_SICKNESS: pline("Yecch! This stuff tastes like poison."); if (otmp->blessed) { pline("(But in fact it was mildly stale %s juice.)", pl_fruit); if (!Role_if(PM_HEALER)) { if (otmp->corpsenm) losehp(1, "mildly contaminated tap water", KILLED_BY); else losehp(1, "mildly contaminated potion", KILLED_BY_AN); } } else { if(Poison_resistance) pline( "(But in fact it was biologically contaminated %s juice.)", pl_fruit); if (Role_if(PM_HEALER)) pline("Fortunately, you have been immunized."); else { int typ = rn2(A_MAX); if (!Fixed_abil) { poisontell(typ); (void) adjattrib(typ, Poison_resistance ? -1 : -rn1(4,3), TRUE); } if(!Poison_resistance) { if (otmp->corpsenm) losehp(rnd(10)+5*!!(otmp->cursed), "contaminated tap water", KILLED_BY); else losehp(rnd(10)+5*!!(otmp->cursed), "contaminated potion", KILLED_BY_AN); } exercise(A_CON, FALSE); } } if(Hallucination) { You("are shocked back to your senses!"); make_hallucinated(0L,FALSE,0L); } break; case POT_CONFUSION: if(!Confusion) if (Hallucination) { pline("What a trippy feeling!"); unkn++; } else pline("Huh, What? Where am I?"); else nothing++; make_confused(itimeout_incr(HConfusion, rn1(7, 16 - 8 * bcsign(otmp))), FALSE); break; case POT_GAIN_ABILITY: if(otmp->cursed) { pline("Ulch! That potion tasted foul!"); unkn++; } else if (Fixed_abil) { nothing++; } else { /* If blessed, increase all; if not, try up to */ int itmp; /* 6 times to find one which can be increased. */ i = -1; /* increment to 0 */ for (ii = A_MAX; ii > 0; ii--) { i = (otmp->blessed ? i + 1 : rn2(A_MAX)); /* only give "your X is already as high as it can get" message on last attempt (except blessed potions) */ itmp = (otmp->blessed || ii == 1) ? 0 : -1; if (adjattrib(i, 1, itmp) && !otmp->blessed) break; } } break; case POT_SPEED: if(Wounded_legs && !otmp->cursed #ifdef STEED && !u.usteed /* heal_legs() would heal steeds legs */ #endif ) { heal_legs(); unkn++; break; } /* and fall through */ case SPE_HASTE_SELF: if(!Very_fast) /* wwf@doe.carleton.ca */ You("are suddenly moving %sfaster.", Fast ? "" : "much "); else { Your("%s get new energy.", makeplural(body_part(LEG))); unkn++; } exercise(A_DEX, TRUE); incr_itimeout(&HFast, rn1(10, 100 + 60 * bcsign(otmp))); break; case POT_BLINDNESS: if(Blind) nothing++; make_blinded(itimeout_incr(Blinded, rn1(200, 250 - 125 * bcsign(otmp))), (boolean)!Blind); break; case POT_GAIN_LEVEL: if (otmp->cursed) { unkn++; /* they went up a level */ if((ledger_no(&u.uz) == 1 && u.uhave.amulet) || Can_rise_up(u.ux, u.uy, &u.uz)) { const char *riseup ="rise up, through the %s!"; if(ledger_no(&u.uz) == 1) { You(riseup, ceiling(u.ux,u.uy)); goto_level(&earth_level, FALSE, FALSE, FALSE); } else { register int newlev = depth(&u.uz)-1; d_level newlevel; get_level(&newlevel, newlev); if(on_level(&newlevel, &u.uz)) { pline("It tasted bad."); break; } else You(riseup, ceiling(u.ux,u.uy)); goto_level(&newlevel, FALSE, FALSE, FALSE); } } else You("have an uneasy feeling."); break; } pluslvl(FALSE); if (otmp->blessed) /* blessed potions place you at a random spot in the * middle of the new level instead of the low point */ u.uexp = rndexp(); break; case POT_HEALING: You_feel("better."); healup(d(6 + 2 * bcsign(otmp), 4), !otmp->cursed ? 1 : 0, !!otmp->blessed, !otmp->cursed); exercise(A_CON, TRUE); break; case POT_EXTRA_HEALING: You_feel("much better."); healup(d(6 + 2 * bcsign(otmp), 8), otmp->blessed ? 5 : !otmp->cursed ? 2 : 0, !otmp->cursed, TRUE); make_hallucinated(0L,TRUE,0L); exercise(A_CON, TRUE); exercise(A_STR, TRUE); break; case POT_FULL_HEALING: You_feel("completely healed."); healup(400, 4+4*bcsign(otmp), !otmp->cursed, TRUE); /* Restore one lost level if blessed */ if (otmp->blessed && (u.ulevel < u.ulevelmax)) pluslvl(FALSE); make_hallucinated(0L,TRUE,0L); exercise(A_STR, TRUE); exercise(A_CON, TRUE); break; case POT_LEVITATION: case SPE_LEVITATION: if (otmp->cursed) HLevitation &= ~I_SPECIAL; if(!Levitation) { /* kludge to ensure proper operation of float_up() */ HLevitation = 1; float_up(); /* reverse kludge */ HLevitation = 0; if (otmp->cursed && !Is_waterlevel(&u.uz)) { if((u.ux != xupstair || u.uy != yupstair) && (u.ux != sstairs.sx || u.uy != sstairs.sy || !sstairs.up) && (!xupladder || u.ux != xupladder || u.uy != yupladder) ) { You("hit your %s on the %s.", body_part(HEAD), ceiling(u.ux,u.uy)); losehp(uarmh ? 1 : rnd(10), "colliding with the ceiling", KILLED_BY); } else (void) doup(); } } else nothing++; if (otmp->blessed) { incr_itimeout(&HLevitation, rn1(50,250)); HLevitation |= I_SPECIAL; } else incr_itimeout(&HLevitation, rn1(140,10)); spoteffects(FALSE); /* for sinks */ break; case POT_GAIN_ENERGY: /* M. Stephenson */ { register int num; if(otmp->cursed) You_feel("lackluster."); else pline("Magical energies course through your body."); num = rnd(5) + 5 * otmp->blessed + 1; u.uenmax += (otmp->cursed) ? -num : num; u.uen += (otmp->cursed) ? -num : num; if(u.uenmax <= 0) u.uenmax = 0; if(u.uen <= 0) u.uen = 0; flags.botl = 1; exercise(A_WIS, TRUE); } break; case POT_OIL: /* P. Winner */ { boolean good_for_you = FALSE; if (otmp->lamplit) { if (likes_fire(youmonst.data)) { pline("Ahh, a refreshing drink."); good_for_you = TRUE; } else { You("burn your %s.", body_part(FACE)); losehp(d(Fire_resistance ? 1 : 3, 4), "burning potion of oil", KILLED_BY_AN); } } else if(otmp->cursed) pline("This tastes like castor oil."); else pline("That was smooth!"); exercise(A_WIS, good_for_you); } break; case POT_ACID: if (Acid_resistance) /* Not necessarily a creature who _likes_ acid */ pline("This tastes %s.", Hallucination ? "tangy" : "sour"); else { pline("This burns%s!", otmp->blessed ? " a little" : otmp->cursed ? " a lot" : " like acid"); losehp(d(otmp->cursed ? 2 : 1, otmp->blessed ? 4 : 8), "potion of acid", KILLED_BY_AN); exercise(A_CON, FALSE); } if (Stoned) fix_petrification(); unkn++; /* holy/unholy water can burn like acid too */ break; case POT_POLYMORPH: You_feel("a little %s.", Hallucination ? "normal" : "strange"); if (!Unchanging) polyself(); break; default: impossible("What a funny potion! (%u)", otmp->otyp); return(0); } return(-1); } void healup(nhp, nxtra, curesick, cureblind) int nhp, nxtra; register boolean curesick, cureblind; { if (nhp) { if (Upolyd) { u.mh += nhp; if (u.mh > u.mhmax) u.mh = (u.mhmax += nxtra); } else { u.uhp += nhp; if(u.uhp > u.uhpmax) u.uhp = (u.uhpmax += nxtra); } } if(cureblind) make_blinded(0L,TRUE); if(curesick) make_sick(0L, (char *) 0, TRUE, SICK_ALL); flags.botl = 1; return; } void strange_feeling(obj,txt) register struct obj *obj; register const char *txt; { if (flags.beginner || !txt) You("have a %s feeling for a moment, then it passes.", Hallucination ? "normal" : "strange"); else pline(txt); if(!obj) /* e.g., crystal ball finds no traps */ return; if(obj->dknown && !objects[obj->otyp].oc_name_known && !objects[obj->otyp].oc_uname) docall(obj); useup(obj); } const char *bottlenames[] = { "bottle", "phial", "flagon", "carafe", "flask", "jar", "vial" }; const char * bottlename() { return bottlenames[rn2(SIZE(bottlenames))]; } void potionhit(mon, obj, your_fault) register struct monst *mon; register struct obj *obj; boolean your_fault; { register const char *botlnam = bottlename(); boolean isyou = (mon == &youmonst); int distance; if(isyou) { distance = 0; pline_The("%s crashes on your %s and breaks into shards.", botlnam, body_part(HEAD)); losehp(rnd(2), "thrown potion", KILLED_BY_AN); } else { distance = distu(mon->mx,mon->my); if (!cansee(mon->mx,mon->my)) pline("Crash!"); else { char *mnam = mon_nam(mon); char buf[BUFSZ]; if(has_head(mon->data)) { Sprintf(buf, "%s %s", s_suffix(mnam), (notonhead ? "body" : "head")); } else { Strcpy(buf, mnam); } pline_The("%s crashes on %s and breaks into shards.", botlnam, buf); } if(rn2(5) && mon->mhp > 1) mon->mhp--; } /* oil doesn't instantly evaporate */ if (obj->otyp != POT_OIL && cansee(mon->mx,mon->my)) pline("%s evaporates.", The(xname(obj))); if (isyou) { switch (obj->otyp) { case POT_OIL: if (obj->lamplit) splatter_burning_oil(u.ux, u.uy); break; case POT_POLYMORPH: You_feel("a little %s.", Hallucination ? "normal" : "strange"); if (!Unchanging && !Antimagic) polyself(); break; case POT_ACID: if (!Acid_resistance) { pline("This burns%s!", obj->blessed ? " a little" : obj->cursed ? " a lot" : ""); losehp(d(obj->cursed ? 2 : 1, obj->blessed ? 4 : 8), "potion of acid", KILLED_BY_AN); } break; } } else { boolean angermon = TRUE; if (!your_fault) angermon = FALSE; switch (obj->otyp) { case POT_HEALING: case POT_EXTRA_HEALING: case POT_FULL_HEALING: if (mon->data == &mons[PM_PESTILENCE]) goto do_illness; /*FALLTHRU*/ case POT_RESTORE_ABILITY: case POT_GAIN_ABILITY: do_healing: angermon = FALSE; if(mon->mhp < mon->mhpmax) { mon->mhp = mon->mhpmax; if (canseemon(mon)) pline("%s looks sound and hale again.", Monnam(mon)); } break; case POT_SICKNESS: if (mon->data == &mons[PM_PESTILENCE]) goto do_healing; if (dmgtype(mon->data, AD_DISE) || dmgtype(mon->data, AD_PEST) || /* won't happen, see prior goto */ resists_poison(mon)) { if (canseemon(mon)) pline("%s looks unharmed.", Monnam(mon)); break; } do_illness: if((mon->mhpmax > 3) && !resist(mon, POTION_CLASS, 0, NOTELL)) mon->mhpmax /= 2; if((mon->mhp > 2) && !resist(mon, POTION_CLASS, 0, NOTELL)) mon->mhp /= 2; if (mon->mhp > mon->mhpmax) mon->mhp = mon->mhpmax; if (canseemon(mon)) pline("%s looks rather ill.", Monnam(mon)); break; case POT_CONFUSION: case POT_BOOZE: if(!resist(mon, POTION_CLASS, 0, NOTELL)) mon->mconf = TRUE; break; case POT_INVISIBILITY: angermon = FALSE; mon_set_minvis(mon); break; case POT_SLEEPING: /* wakeup() doesn't rouse victims of temporary sleep */ if (sleep_monst(mon, rnd(12), POTION_CLASS)) { pline("%s falls asleep.", Monnam(mon)); slept_monst(mon); } break; case POT_PARALYSIS: if (mon->mcanmove) { mon->mcanmove = 0; /* really should be rnd(5) for consistency with players * breathing potions, but... */ mon->mfrozen = rnd(25); } break; case POT_SPEED: angermon = FALSE; mon_adjust_speed(mon, 1); break; case POT_BLINDNESS: if(haseyes(mon->data)) { register int btmp = 64 + rn2(32) + rn2(32) * !resist(mon, POTION_CLASS, 0, NOTELL); btmp += mon->mblinded; mon->mblinded = min(btmp,127); mon->mcansee = 0; } break; case POT_WATER: if (is_undead(mon->data) || is_demon(mon->data) || is_were(mon->data)) { if (obj->blessed) { pline("%s %s in pain!", Monnam(mon), is_silent(mon->data) ? "writhes" : "shrieks"); mon->mhp -= d(2,6); /* should only be by you */ if (mon->mhp < 1) killed(mon); else if (is_were(mon->data) && !is_human(mon->data)) new_were(mon); /* revert to human */ } else if (obj->cursed) { angermon = FALSE; if (canseemon(mon)) pline("%s looks healthier.", Monnam(mon)); mon->mhp += d(2,6); if (mon->mhp > mon->mhpmax) mon->mhp = mon->mhpmax; if (is_were(mon->data) && is_human(mon->data) && !Protection_from_shape_changers) new_were(mon); /* transform into beast */ } } else if(mon->data == &mons[PM_GREMLIN]) { angermon = FALSE; (void)split_mon(mon, (struct monst *)0); } else if(mon->data == &mons[PM_IRON_GOLEM]) { if (canseemon(mon)) pline("%s rusts.", Monnam(mon)); mon->mhp -= d(1,6); /* should only be by you */ if (mon->mhp < 1) killed(mon); } break; case POT_OIL: if (obj->lamplit) splatter_burning_oil(mon->mx, mon->my); break; case POT_ACID: if (!resists_acid(mon) && !resist(mon, POTION_CLASS, 0, NOTELL)) { pline("%s %s in pain!", Monnam(mon), is_silent(mon->data) ? "writhes" : "shrieks"); mon->mhp -= d(obj->cursed ? 2 : 1, obj->blessed ? 4 : 8); if (mon->mhp < 1) { if (your_fault) killed(mon); else monkilled(mon, "", AD_ACID); } } break; case POT_POLYMORPH: (void) bhitm(mon, obj); break; /* case POT_GAIN_LEVEL: case POT_LEVITATION: case POT_FRUIT_JUICE: case POT_MONSTER_DETECTION: case POT_OBJECT_DETECTION: break; */ } if (angermon) wakeup(mon); else mon->msleeping = 0; } /* Note: potionbreathe() does its own docall() */ if ((distance==0 || ((distance < 3) && rn2(5))) && (!breathless(youmonst.data) || haseyes(youmonst.data))) potionbreathe(obj); else if (obj->dknown && !objects[obj->otyp].oc_name_known && !objects[obj->otyp].oc_uname && cansee(mon->mx,mon->my)) docall(obj); if(*u.ushops && obj->unpaid) { register struct monst *shkp = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE)); if(!shkp) obj->unpaid = 0; else { (void)stolen_value(obj, u.ux, u.uy, (boolean)shkp->mpeaceful, FALSE); subfrombill(obj, shkp); } } obfree(obj, (struct obj *)0); } /* vapors are inhaled or get in your eyes */ void potionbreathe(obj) register struct obj *obj; { register int i, ii, isdone, kn = 0; switch(obj->otyp) { case POT_RESTORE_ABILITY: case POT_GAIN_ABILITY: if(obj->cursed) { if (!breathless(youmonst.data)) pline("Ulch! That potion smells terrible!"); else if (haseyes(youmonst.data)) { int numeyes = eyecount(youmonst.data); Your("%s sting%s!", (numeyes == 1) ? body_part(EYE) : makeplural(body_part(EYE)), (numeyes == 1) ? "s" : ""); } break; } else { i = rn2(A_MAX); /* start at a random point */ for(isdone = ii = 0; !isdone && ii < A_MAX; ii++) { if(ABASE(i) < AMAX(i)) { ABASE(i)++; /* only first found if not blessed */ isdone = !(obj->blessed); flags.botl = 1; } if(++i >= A_MAX) i = 0; } } break; case POT_FULL_HEALING: if (Upolyd && u.mh < u.mhmax) u.mh++, flags.botl = 1; if (u.uhp < u.uhpmax) u.uhp++, flags.botl = 1; /*FALL THROUGH*/ case POT_EXTRA_HEALING: if (Upolyd && u.mh < u.mhmax) u.mh++, flags.botl = 1; if (u.uhp < u.uhpmax) u.uhp++, flags.botl = 1; /*FALL THROUGH*/ case POT_HEALING: if (Upolyd && u.mh < u.mhmax) u.mh++, flags.botl = 1; if (u.uhp < u.uhpmax) u.uhp++, flags.botl = 1; exercise(A_CON, TRUE); break; case POT_SICKNESS: if (!Role_if(PM_HEALER)) { if (Upolyd) { if (u.mh <= 5) u.mh = 1; else u.mh -= 5; } else { if (u.uhp <= 5) u.uhp = 1; else u.uhp -= 5; } flags.botl = 1; exercise(A_CON, FALSE); } break; case POT_HALLUCINATION: You("have a momentary vision."); break; case POT_CONFUSION: case POT_BOOZE: if(!Confusion) You_feel("somewhat dizzy."); make_confused(itimeout_incr(HConfusion, rnd(5)), FALSE); break; case POT_INVISIBILITY: if (!Blind && !Invis) { kn++; pline("For an instant you %s!", See_invisible ? "could see right through yourself" : "couldn't see yourself"); } break; case POT_PARALYSIS: kn++; if (!Free_action) { pline("%s seems to be holding you.", Something); nomul(-rnd(5)); nomovemsg = You_can_move_again; exercise(A_DEX, FALSE); } else You("stiffen momentarily."); break; case POT_SLEEPING: kn++; if (!Free_action && !Sleep_resistance) { You_feel("rather tired."); nomul(-rnd(5)); nomovemsg = You_can_move_again; exercise(A_DEX, FALSE); } else You("yawn."); break; case POT_SPEED: if (!Fast) Your("knees seem more flexible now."); incr_itimeout(&HFast, rnd(5)); exercise(A_DEX, TRUE); break; case POT_BLINDNESS: if (!Blind && !u.usleep) { kn++; pline("It suddenly gets dark."); } make_blinded(itimeout_incr(Blinded, rnd(5)), FALSE); if (!Blind && !u.usleep) Your(vision_clears); break; case POT_WATER: if(u.umonnum == PM_GREMLIN) { (void)split_mon(&youmonst, (struct monst *)0); } else if (u.ulycn >= LOW_PM) { /* vapor from [un]holy water will trigger transformation but won't cure lycanthropy */ if (obj->blessed && youmonst.data == &mons[u.ulycn]) you_unwere(FALSE); else if (obj->cursed && !Upolyd) you_were(); } break; case POT_ACID: case POT_POLYMORPH: exercise(A_CON, FALSE); break; /* case POT_GAIN_LEVEL: case POT_LEVITATION: case POT_FRUIT_JUICE: case POT_MONSTER_DETECTION: case POT_OBJECT_DETECTION: case POT_OIL: break; */ } /* note: no obfree() */ if (obj->dknown) { if (kn) makeknown(obj->otyp); else if (!objects[obj->otyp].oc_name_known && !objects[obj->otyp].oc_uname) docall(obj); } } STATIC_OVL short mixtype(o1, o2) register struct obj *o1, *o2; /* returns the potion type when o1 is dipped in o2 */ { /* cut down on the number of cases below */ if (o1->oclass == POTION_CLASS && (o2->otyp == POT_GAIN_LEVEL || o2->otyp == POT_GAIN_ENERGY || o2->otyp == POT_HEALING || o2->otyp == POT_EXTRA_HEALING || o2->otyp == POT_FULL_HEALING || o2->otyp == POT_ENLIGHTENMENT || o2->otyp == POT_FRUIT_JUICE)) { struct obj *swp; swp = o1; o1 = o2; o2 = swp; } switch (o1->otyp) { case POT_HEALING: switch (o2->otyp) { case POT_SPEED: case POT_GAIN_LEVEL: case POT_GAIN_ENERGY: return POT_EXTRA_HEALING; } case POT_EXTRA_HEALING: switch (o2->otyp) { case POT_GAIN_LEVEL: case POT_GAIN_ENERGY: return POT_FULL_HEALING; } case POT_FULL_HEALING: switch (o2->otyp) { case POT_GAIN_LEVEL: case POT_GAIN_ENERGY: return POT_GAIN_ABILITY; } case UNICORN_HORN: switch (o2->otyp) { case POT_SICKNESS: return POT_FRUIT_JUICE; case POT_HALLUCINATION: case POT_BLINDNESS: case POT_CONFUSION: return POT_WATER; } break; case AMETHYST: /* "a-methyst" == "not intoxicated" */ if (o2->otyp == POT_BOOZE) return POT_FRUIT_JUICE; break; case POT_GAIN_LEVEL: case POT_GAIN_ENERGY: switch (o2->otyp) { case POT_CONFUSION: return (rn2(3) ? POT_BOOZE : POT_ENLIGHTENMENT); case POT_HEALING: return POT_EXTRA_HEALING; case POT_EXTRA_HEALING: return POT_FULL_HEALING; case POT_FULL_HEALING: return POT_GAIN_ABILITY; case POT_FRUIT_JUICE: return POT_SEE_INVISIBLE; case POT_BOOZE: return POT_HALLUCINATION; } break; case POT_FRUIT_JUICE: switch (o2->otyp) { case POT_SICKNESS: return POT_SICKNESS; case POT_SPEED: return POT_BOOZE; case POT_GAIN_LEVEL: case POT_GAIN_ENERGY: return POT_SEE_INVISIBLE; } break; case POT_ENLIGHTENMENT: switch (o2->otyp) { case POT_LEVITATION: if (rn2(3)) return POT_GAIN_LEVEL; break; case POT_FRUIT_JUICE: return POT_BOOZE; case POT_BOOZE: return POT_CONFUSION; } break; } return 0; } boolean get_wet(obj) register struct obj *obj; /* returns TRUE if something happened (potion should be used up) */ { char Your_buf[BUFSZ]; if (snuff_lit(obj)) return(TRUE); if (obj->greased) { grease_protect(obj,(char *)0,FALSE,&youmonst); return(FALSE); } (void) Shk_Your(Your_buf, obj); /* (Rusting shop goods ought to be charged for.) */ switch (obj->oclass) { case WEAPON_CLASS: if (!obj->oerodeproof && is_rustprone(obj) && (obj->oeroded < MAX_ERODE) && !rn2(2)) { pline("%s %s some%s.", Your_buf, aobjnam(obj, "rust"), obj->oeroded ? " more" : "what"); obj->oeroded++; update_inventory(); return TRUE; } else break; case POTION_CLASS: if (obj->otyp == POT_WATER) return FALSE; /* KMH -- Water into acid causes an explosion */ if (obj->otyp == POT_ACID) { pline("It boils vigorously!"); losehp(rnd(10), "elementary chemistry", KILLED_BY); makeknown(obj->otyp); update_inventory(); return (TRUE); } pline("%s %s%s.", Your_buf, aobjnam(obj,"dilute"), obj->odiluted ? " further" : ""); if(obj->unpaid && costly_spot(u.ux, u.uy)) { You("dilute it, you pay for it."); bill_dummy_object(obj); } if (obj->odiluted) { obj->odiluted = 0; #ifdef UNIXPC obj->blessed = FALSE; obj->cursed = FALSE; #else obj->blessed = obj->cursed = FALSE; #endif obj->otyp = POT_WATER; } else obj->odiluted++; update_inventory(); return TRUE; case SCROLL_CLASS: if (obj->otyp != SCR_BLANK_PAPER #ifdef MAIL && obj->otyp != SCR_MAIL #endif ) { if (!Blind) { boolean oq1 = obj->quan == 1L; pline_The("scroll%s fade%s.", oq1 ? "" : "s", oq1 ? "s" : ""); } if(obj->unpaid && costly_spot(u.ux, u.uy)) { You("erase it, you pay for it."); bill_dummy_object(obj); } obj->otyp = SCR_BLANK_PAPER; obj->spe = 0; update_inventory(); return TRUE; } else break; case SPBOOK_CLASS: if (obj->otyp != SPE_BLANK_PAPER) { if (obj->otyp == SPE_BOOK_OF_THE_DEAD) { pline("%s suddenly heats up; steam rises and it remains dry.", The(xname(obj))); } else { if (!Blind) { boolean oq1 = obj->quan == 1L; pline_The("spellbook%s fade%s.", oq1 ? "" : "s", oq1 ? "s" : ""); } if(obj->unpaid && costly_spot(u.ux, u.uy)) { You("erase it, you pay for it."); bill_dummy_object(obj); } obj->otyp = SPE_BLANK_PAPER; update_inventory(); } return TRUE; } } pline("%s %s wet.", Your_buf, aobjnam(obj,"get")); return FALSE; } int dodip() { register struct obj *potion, *obj; struct obj *singlepotion; const char *tmp; uchar here; char allowall[2]; short mixture; char qbuf[QBUFSZ], Your_buf[BUFSZ]; allowall[0] = ALL_CLASSES; allowall[1] = '\0'; if(!(obj = getobj(allowall, "dip"))) return(0); here = levl[u.ux][u.uy].typ; /* Is there a fountain to dip into here? */ if (IS_FOUNTAIN(here)) { if(yn("Dip it into the fountain?") == 'y') { dipfountain(obj); return(1); } } else if (is_pool(u.ux,u.uy)) { tmp = (here == POOL) ? "pool" : "moat"; Sprintf(qbuf, "Dip it into the %s?", tmp); if (yn(qbuf) == 'y') { if (Levitation) floating_above(tmp); else { (void) get_wet(obj); if (obj->otyp == POT_ACID) useup(obj); } return 1; } } if(!(potion = getobj(beverages, "dip into"))) return(0); if (potion == obj && potion->quan == 1L) { pline("That is a potion bottle, not a Klein bottle!"); return 0; } if(potion->otyp == POT_WATER) { boolean useeit = !Blind; if (useeit) (void) Shk_Your(Your_buf, obj); if (potion->blessed) { if (obj->cursed) { if (useeit) pline("%s %s %s.", Your_buf, aobjnam(obj, "softly glow"), hcolor(amber)); uncurse(obj); obj->bknown=1; poof: if(!(objects[potion->otyp].oc_name_known) && !(objects[potion->otyp].oc_uname)) docall(potion); useup(potion); return(1); } else if(!obj->blessed) { if (useeit) { tmp = hcolor(light_blue); pline("%s %s with a%s %s aura.", Your_buf, aobjnam(obj, "softly glow"), index(vowels, *tmp) ? "n" : "", tmp); } bless(obj); obj->bknown=1; goto poof; } } else if (potion->cursed) { if (obj->blessed) { if (useeit) pline("%s %s %s.", Your_buf, aobjnam(obj, "glow"), hcolor((const char *)"brown")); unbless(obj); obj->bknown=1; goto poof; } else if(!obj->cursed) { if (useeit) { tmp = hcolor(Black); pline("%s %s with a%s %s aura.", Your_buf, aobjnam(obj, "glow"), index(vowels, *tmp) ? "n" : "", tmp); } curse(obj); obj->bknown=1; goto poof; } } else if (get_wet(obj)) goto poof; } else if (obj->otyp == POT_POLYMORPH || potion->otyp == POT_POLYMORPH) { /* some objects can't be polymorphed */ if (obj->otyp == potion->otyp || /* both POT_POLY */ obj->otyp == WAN_POLYMORPH || obj->otyp == SPE_POLYMORPH || obj == uball || obj == uskin || obj_resists(obj->otyp == POT_POLYMORPH ? potion : obj, 5, 95)) { pline(nothing_happens); } else { boolean was_wep = FALSE, was_swapwep = FALSE, was_quiver = FALSE; /* KMH, conduct */ u.uconduct.polypiles++; if (obj == uwep) was_wep = TRUE; else if (obj == uswapwep) was_swapwep = TRUE; else if (obj == uquiver) was_quiver = TRUE; obj = poly_obj(obj, STRANGE_OBJECT); if (was_wep) setuwep(obj); else if (was_swapwep) setuswapwep(obj); else if (was_quiver) setuqwep(obj); makeknown(POT_POLYMORPH); useup(potion); prinv((char *)0, obj, 0L); } return(1); } else if(obj->oclass == POTION_CLASS && obj->otyp != potion->otyp) { /* Mixing potions is dangerous... */ pline_The("potions mix..."); /* KMH, balance patch -- acid is particularly unstable */ if (obj->cursed || obj->otyp == POT_ACID || !rn2(10)) { pline("BOOM! They explode!"); exercise(A_STR, FALSE); if (!breathless(youmonst.data) || haseyes(youmonst.data)) potionbreathe(obj); useup(obj); useup(potion); losehp(rnd(10), "alchemic blast", KILLED_BY_AN); return(1); } obj->blessed = obj->cursed = obj->bknown = 0; if (Blind || Hallucination) obj->dknown = 0; if ((mixture = mixtype(obj, potion)) != 0) { obj->otyp = mixture; } else { switch (obj->odiluted ? 1 : rnd(8)) { case 1: obj->otyp = POT_WATER; break; case 2: case 3: obj->otyp = POT_SICKNESS; break; case 4: { struct obj *otmp; otmp = mkobj(POTION_CLASS,FALSE); obj->otyp = otmp->otyp; obfree(otmp, (struct obj *)0); } break; default: if (!Blind) pline_The("mixture glows brightly and evaporates."); useup(obj); useup(potion); return(1); } } obj->odiluted = (obj->otyp != POT_WATER); if (obj->otyp == POT_WATER && !Hallucination) { pline_The("mixture bubbles%s.", Blind ? "" : ", then clears"); } else if (!Blind) { pline_The("mixture looks %s.", hcolor(OBJ_DESCR(objects[obj->otyp]))); } useup(potion); return(1); } #ifdef INVISIBLE_OBJECTS if (potion->otyp == POT_INVISIBILITY && !obj->oinvis) { obj->oinvis = TRUE; if (!Blind) { if (!See_invisible) pline("Where did %s go?", the(xname(obj))); else You("notice a little haziness around %s.", the(xname(obj))); } goto poof; } else if (potion->otyp == POT_SEE_INVISIBLE && obj->oinvis) { obj->oinvis = FALSE; if (!Blind) { if (!See_invisible) pline("So that's where %s went!", the(xname(obj))); else pline_The("haziness around %s disappears.", the(xname(obj))); } goto poof; } #endif if(is_poisonable(obj)) { if(potion->otyp == POT_SICKNESS && !obj->opoisoned) { char buf[BUFSZ]; if (potion->quan > 1L) Sprintf(buf, "One of %s", the(xname(potion))); else Strcpy(buf, The(xname(potion))); pline("%s forms a coating on %s.", buf, the(xname(obj))); obj->opoisoned = TRUE; goto poof; } else if(obj->opoisoned && (potion->otyp == POT_HEALING || potion->otyp == POT_EXTRA_HEALING || potion->otyp == POT_FULL_HEALING)) { pline("A coating wears off %s.", the(xname(obj))); obj->opoisoned = 0; goto poof; } } if (potion->otyp == POT_OIL && (obj->oclass == WEAPON_CLASS || is_weptool(obj))) { boolean wisx = FALSE; if (potion->lamplit) { /* burning */ int omat = objects[obj->otyp].oc_material; if (obj->oerodeproof || obj_resists(obj, 5, 95) || /* `METAL' should not be confused with is_metallic() */ omat == METAL || omat == MITHRIL || omat == BONE) { pline("%s seem%s to burn for a moment.", Yname2(obj), (obj->quan > 1L) ? "" : "s"); } else { if (omat == PLASTIC) obj->oeroded = MAX_ERODE; pline_The("burning oil %s %s.", obj->oeroded == MAX_ERODE ? "destroys" : "damages", yname(obj)); if (obj->oeroded == MAX_ERODE) { obj_extract_self(obj); obfree(obj, (struct obj *)0); obj = (struct obj *) 0; } else { /* should check for and do something about damaging unpaid shop goods here */ obj->oeroded++; } } } else if (potion->cursed) { pline_The("potion spills and covers your %s with oil.", makeplural(body_part(FINGER))); incr_itimeout(&Glib, d(2,10)); /* Oil removes rust and corrosion, but doesn't unburn. * Arrows, etc are classed as metallic due to arrowhead * material, but dipping in oil shouldn't repair them. */ } else if ((!is_rustprone(obj) && !is_corrodeable(obj)) || is_ammo(obj) || (!obj->oeroded && !obj->oeroded2)) { /* uses up potion, doesn't set obj->greased */ pline("%s gleam%s with an oily sheen.", Yname2(obj), (obj->quan > 1L) ? "" : "s"); } else { pline("%s %s less %s.", Yname2(obj), (obj->quan > 1L) ? "are" : "is", (obj->oeroded && obj->oeroded2) ? "corroded and rusty" : obj->oeroded ? "rusty" : "corroded"); if (obj->oeroded > 0) obj->oeroded--; if (obj->oeroded2 > 0) obj->oeroded2--; wisx = TRUE; } exercise(A_WIS, wisx); makeknown(potion->otyp); useup(potion); return 1; } /* Allow filling of MAGIC_LAMPs to prevent identification by player */ if ((obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP) && (potion->otyp == POT_OIL)) { /* Turn off engine before fueling, turn off fuel too :-) */ if (obj->lamplit || potion->lamplit) { useup(potion); explode(u.ux, u.uy, 11, d(6,6), 0, EXPL_FIERY); exercise(A_WIS, FALSE); return 1; } /* Adding oil to an empty magic lamp renders it into an oil lamp */ if ((obj->otyp == MAGIC_LAMP) && obj->spe == 0) { obj->otyp = OIL_LAMP; obj->age = 0; } if (obj->age > 1000L) { pline("%s is full.", Yname2(obj)); } else { You("fill %s with oil.", yname(obj)); check_unpaid(potion); /* Yendorian Fuel Tax */ obj->age += 2*potion->age; /* burns more efficiently */ if (obj->age > 1500L) obj->age = 1500L; useup(potion); exercise(A_WIS, TRUE); } makeknown(POT_OIL); obj->spe = 1; update_inventory(); return 1; } if ((obj->otyp == UNICORN_HORN || obj->otyp == AMETHYST) && (mixture = mixtype(obj, potion)) != 0) { boolean more_than_one = potion->quan > 1; /* with multiple merged potions, split off one and just clear it */ if (potion->quan > 1L) { singlepotion = splitobj(potion, potion->quan - 1L); } else singlepotion = potion; if(singlepotion->unpaid && costly_spot(u.ux, u.uy)) { You("use it, you pay for it."); bill_dummy_object(singlepotion); } singlepotion->otyp = mixture; singlepotion->blessed = 0; if (mixture == POT_WATER) singlepotion->cursed = singlepotion->odiluted = 0; else singlepotion->cursed = obj->cursed; /* odiluted left as-is */ if (Blind) singlepotion->dknown = FALSE; else { if (mixture == POT_WATER && #ifdef DCC30_BUG (singlepotion->dknown = !Hallucination, singlepotion->dknown != 0)) #else (singlepotion->dknown = !Hallucination) != 0) #endif pline_The("potion%s clears.", more_than_one ? " that you dipped into" : ""); else pline_The("potion%s turns %s.", more_than_one ? " that you dipped into" : "", hcolor(OBJ_DESCR(objects[mixture]))); } obj_extract_self(singlepotion); singlepotion = hold_another_object(singlepotion, "You juggle and drop %s!", doname(singlepotion), (const char *)0); update_inventory(); return(1); } pline("Interesting..."); return(1); } void djinni_from_bottle(obj) register struct obj *obj; { struct monst *mtmp; int chance; if(!(mtmp = makemon(&mons[PM_DJINNI], u.ux, u.uy, NO_MM_FLAGS))){ pline("It turns out to be empty."); return; } if (!Blind) { pline("In a cloud of smoke, %s emerges!", a_monnam(mtmp)); pline("%s speaks.", Monnam(mtmp)); } else { You("smell acrid fumes."); pline("%s speaks.", Something); } chance = rn2(5); if (obj->blessed) chance = (chance == 4) ? rnd(4) : 0; else if (obj->cursed) chance = (chance == 0) ? rn2(4) : 4; /* 0,1,2,3,4: b=80%,5,5,5,5; nc=20%,20,20,20,20; c=5%,5,5,5,80 */ switch (chance) { case 0 : verbalize("I am in your debt. I will grant one wish!"); makewish(); mongone(mtmp); break; case 1 : verbalize("Thank you for freeing me!"); (void) tamedog(mtmp, (struct obj *)0); break; case 2 : verbalize("You freed me!"); mtmp->mpeaceful = TRUE; set_malign(mtmp); break; case 3 : verbalize("It is about time!"); pline("%s vanishes.", Monnam(mtmp)); mongone(mtmp); break; default: verbalize("You disturbed me, fool!"); break; } } /* clone a gremlin or mold (2nd arg non-null implies heat as the trigger); hit points are cut in half (odd HP stays with original) */ struct monst * split_mon(mon, mtmp) struct monst *mon, /* monster being split */ *mtmp; /* optional attacker whose heat triggered it */ { struct monst *mtmp2; char reason[BUFSZ]; reason[0] = '\0'; if (mtmp) Sprintf(reason, " from %s heat", (mtmp == &youmonst) ? (const char *)"your" : (const char *)s_suffix(mon_nam(mtmp))); if (mon == &youmonst) { mtmp2 = cloneu(); if (mtmp2) { mtmp2->mhpmax = u.mhmax / 2; u.mhmax -= mtmp2->mhpmax; flags.botl = 1; You("multiply%s!", reason); } } else { mtmp2 = clone_mon(mon); if (mtmp2) { mtmp2->mhpmax = mon->mhpmax / 2; mon->mhpmax -= mtmp2->mhpmax; if (canspotmon(mon)) pline("%s multiplies%s!", Monnam(mon), reason); } } return mtmp2; } #endif /* OVLB */ /*potion.c*/