diff --git a/src/fountain.c b/src/fountain.c new file mode 100644 index 000000000..b0563d263 --- /dev/null +++ b/src/fountain.c @@ -0,0 +1,598 @@ +/* SCCS Id: @(#)fountain.c 3.3 2001/09/06 */ +/* Copyright Scott R. Turner, srt@ucla, 10/27/86 */ +/* NetHack may be freely redistributed. See license for details. */ + +/* Code for drinking from fountains. */ + +#include "hack.h" + +STATIC_DCL void NDECL(dowatersnakes); +STATIC_DCL void NDECL(dowaterdemon); +STATIC_DCL void NDECL(dowaternymph); +STATIC_PTR void FDECL(gush, (int,int,genericptr_t)); +STATIC_DCL void NDECL(dofindgem); + +void +floating_above(what) +const char *what; +{ + You("are floating high above the %s.", what); +} + +STATIC_OVL void +dowatersnakes() /* Fountain of snakes! */ +{ + register int num = rn1(5,2); + struct monst *mtmp; + + if (!(mvitals[PM_WATER_MOCCASIN].mvflags & G_GONE)) { + if (!Blind) + pline("An endless stream of %s pours forth!", + Hallucination ? makeplural(rndmonnam()) : "snakes"); + else + You_hear("%s hissing!", something); + while(num-- > 0) + if((mtmp = makemon(&mons[PM_WATER_MOCCASIN], + u.ux, u.uy, NO_MM_FLAGS)) && t_at(mtmp->mx, mtmp->my)) + (void) mintrap(mtmp); + } else + pline_The("fountain bubbles furiously for a moment, then calms."); +} + +STATIC_OVL +void +dowaterdemon() /* Water demon */ +{ + register struct monst *mtmp; + + if(mvitals[PM_WATER_DEMON].mvflags & G_GONE) return; + if((mtmp = makemon(&mons[PM_WATER_DEMON],u.ux,u.uy, NO_MM_FLAGS))) { + if (!Blind) + You("unleash %s!", a_monnam(mtmp)); + else + You_feel("the presence of evil."); + + /* Give those on low levels a (slightly) better chance of survival */ + if (rnd(100) > (80 + level_difficulty())) { + pline("Grateful for %s release, %s grants you a wish!", + mhis(mtmp), mhe(mtmp)); + makewish(); + mongone(mtmp); + } else if (t_at(mtmp->mx, mtmp->my)) + (void) mintrap(mtmp); + } +} + +STATIC_OVL void +dowaternymph() /* Water Nymph */ +{ + register struct monst *mtmp; + + if(mvitals[PM_WATER_NYMPH].mvflags & G_GONE) return; + if((mtmp = makemon(&mons[PM_WATER_NYMPH],u.ux,u.uy, NO_MM_FLAGS))) { + if (!Blind) + You("attract %s!", a_monnam(mtmp)); + else + You_hear("a seductive voice."); + mtmp->msleeping = 0; + if (t_at(mtmp->mx, mtmp->my)) + (void) mintrap(mtmp); + } else + if (!Blind) + pline("A large bubble rises to the surface and pops."); + else + You_hear("a loud pop."); +} + +void +dogushforth(drinking) /* Gushing forth along LOS from (u.ux, u.uy) */ +int drinking; +{ + int madepool = 0; + + do_clear_area(u.ux, u.uy, 7, gush, (genericptr_t)&madepool); + if (!madepool) { + if (drinking) + Your("thirst is quenched."); + else + pline("Water sprays all over you."); + } +} + +STATIC_PTR void +gush(x, y, poolcnt) +int x, y; +genericptr_t poolcnt; +{ + register struct monst *mtmp; + register struct trap *ttmp; + + if (((x+y)%2) || (x == u.ux && y == u.uy) || + (rn2(1 + distmin(u.ux, u.uy, x, y))) || + (levl[x][y].typ != ROOM) || + (sobj_at(BOULDER, x, y)) || nexttodoor(x, y)) + return; + + if ((ttmp = t_at(x, y)) != 0 && !delfloortrap(ttmp)) + return; + + if (!((*(int *)poolcnt)++)) + pline("Water gushes forth from the overflowing fountain!"); + + /* Put a pool at x, y */ + levl[x][y].typ = POOL; + /* No kelp! */ + del_engr_at(x, y); + water_damage(level.objects[x][y], FALSE, TRUE); + + if ((mtmp = m_at(x, y)) != 0) + (void) minwater(mtmp); + else + newsym(x,y); +} + +STATIC_OVL void +dofindgem() /* Find a gem in the sparkling waters. */ +{ + if (!Blind) You("spot a gem in the sparkling waters!"); + else You_feel("a gem here!"); + (void) mksobj_at(rnd_class(DILITHIUM_CRYSTAL, LUCKSTONE-1), + u.ux, u.uy, FALSE, FALSE); + levl[u.ux][u.uy].looted |= F_LOOTED; + newsym(u.ux, u.uy); + exercise(A_WIS, TRUE); /* a discovery! */ +} + +void +dryup(x, y, isyou) +xchar x, y; +boolean isyou; +{ + if (IS_FOUNTAIN(levl[x][y].typ) && + (!rn2(3) || (levl[x][y].looted & F_WARNED))) { + s_level *slev = Is_special(&u.uz); + if(isyou && slev && slev->flags.town && + !(levl[x][y].looted & F_WARNED)) { + struct monst *mtmp; + levl[x][y].looted |= F_WARNED; + /* Warn about future fountain use. */ + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) { + if (DEADMONSTER(mtmp)) continue; + if ((mtmp->data == &mons[PM_WATCHMAN] || + mtmp->data == &mons[PM_WATCH_CAPTAIN]) && + couldsee(mtmp->mx, mtmp->my) && + mtmp->mpeaceful) { + pline("%s yells:", Amonnam(mtmp)); + verbalize("Hey, stop using that fountain!"); + break; + } + } + /* You can see or hear this effect */ + if(!mtmp) pline_The("flow reduces to a trickle."); + return; + } +#ifdef WIZARD + if (isyou && wizard) { + if (yn("Dry up fountain?") == 'n') + return; + } +#endif + if (cansee(x,y)) pline_The("fountain dries up!"); + levl[x][y].typ = ROOM; + levl[x][y].looted = 0; + levl[x][y].blessedftn = 0; + /* The location is seen if the hero/monster is invisible */ + /* or felt if the hero is blind. */ + newsym(x, y); + level.flags.nfountains--; + if(isyou && slev && slev->flags.town) + (void) angry_guards(FALSE); + } +} + +void +drinkfountain() +{ + /* What happens when you drink from a fountain? */ + register boolean mgkftn = (levl[u.ux][u.uy].blessedftn == 1); + register int fate = rnd(30); + + if (Levitation) { + floating_above("fountain"); + return; + } + + if (mgkftn && u.uluck >= 0 && fate >= 10) { + int i, ii, littleluck = (u.uluck < 4); + + pline("Wow! This makes you feel great!"); + /* blessed restore ability */ + for (ii = 0; ii < A_MAX; ii++) + if (ABASE(ii) < AMAX(ii)) { + ABASE(ii) = AMAX(ii); + flags.botl = 1; + } + /* gain ability, blessed if "natural" luck is high */ + i = rn2(A_MAX); /* start at a random attribute */ + for (ii = 0; ii < A_MAX; ii++) { + if (adjattrib(i, 1, littleluck ? -1 : 0) && littleluck) + break; + if (++i >= A_MAX) i = 0; + } + display_nhwindow(WIN_MESSAGE, FALSE); + pline("A wisp of vapor escapes the fountain..."); + exercise(A_WIS, TRUE); + levl[u.ux][u.uy].blessedftn = 0; + return; + } + + if (fate < 10) { + pline_The("cool draught refreshes you."); + u.uhunger += rnd(10); /* don't choke on water */ + newuhs(FALSE); + if(mgkftn) return; + } else { + switch (fate) { + + case 19: /* Self-knowledge */ + + You_feel("self-knowledgeable..."); + display_nhwindow(WIN_MESSAGE, FALSE); + enlightenment(0); + exercise(A_WIS, TRUE); + pline_The("feeling subsides."); + break; + + case 20: /* Foul water */ + + pline_The("water is foul! You gag and vomit."); + morehungry(rn1(20, 11)); + vomit(); + break; + + case 21: /* Poisonous */ + + pline_The("water is contaminated!"); + if (Poison_resistance) { + pline("Perhaps it is runoff from the nearby %s farm.", pl_fruit); + losehp(rnd(4),"unrefrigerated sip of juice", + KILLED_BY_AN); + break; + } + losestr(rn1(4,3)); + losehp(rnd(10),"contaminated water", KILLED_BY); + exercise(A_CON, FALSE); + break; + + case 22: /* Fountain of snakes! */ + + dowatersnakes(); + break; + + case 23: /* Water demon */ + dowaterdemon(); + break; + + case 24: /* Curse an item */ { + register struct obj *obj; + + pline("This water's no good!"); + morehungry(rn1(20, 11)); + exercise(A_CON, FALSE); + for(obj = invent; obj ; obj = obj->nobj) + if (!rn2(5)) curse(obj); + break; + } + + case 25: /* See invisible */ + + if (Blind) { + if (Invisible) { + You("feel very self-conscious."); + pline("Then it passes."); + } else { + You("feel transparent."); + } + } else { + You("see an image of someone stalking you."); + pline("But it disappears."); + } + HSee_invisible |= FROMOUTSIDE; + newsym(u.ux,u.uy); + exercise(A_WIS, TRUE); + break; + + case 26: /* See Monsters */ + + (void) monster_detect((struct obj *)0, 0); + exercise(A_WIS, TRUE); + break; + + case 27: /* Find a gem in the sparkling waters. */ + + if (!levl[u.ux][u.uy].looted) { + dofindgem(); + break; + } + + case 28: /* Water Nymph */ + + dowaternymph(); + break; + + case 29: /* Scare */ { + register struct monst *mtmp; + + pline("This water gives you bad breath!"); + for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) + if(!DEADMONSTER(mtmp)) + monflee(mtmp, 0, FALSE, FALSE); + } + break; + + case 30: /* Gushing forth in this room */ + + dogushforth(TRUE); + break; + + default: + + pline("This tepid water is tasteless."); + break; + } + } + dryup(u.ux, u.uy, TRUE); +} + +void +dipfountain(obj) +register struct obj *obj; +{ + if (Levitation) { + floating_above("fountain"); + return; + } + + /* Don't grant Excalibur when there's more than one object. */ + /* (quantity could be > 1 if merged daggers got polymorphed) */ + if (obj->otyp == LONG_SWORD && obj->quan == 1L + && u.ulevel >= 5 && !rn2(6) + && !obj->oartifact + && !exist_artifact(LONG_SWORD, artiname(ART_EXCALIBUR))) { + s_level *slev = Is_special(&u.uz); + + if (u.ualign.type != A_LAWFUL) { + /* Ha! Trying to cheat her. */ + pline("A freezing mist rises from the water and envelopes the sword."); + pline_The("fountain disappears!"); + curse(obj); + if (obj->spe > -6 && !rn2(3)) obj->spe--; + obj->oerodeproof = FALSE; + exercise(A_WIS, FALSE); + } else { + /* The lady of the lake acts! - Eric Backus */ + /* Be *REAL* nice */ + pline("From the murky depths, a hand reaches up to bless the sword."); + pline("As the hand retreats, the fountain disappears!"); + obj = oname(obj, artiname(ART_EXCALIBUR)); + discover_artifact(ART_EXCALIBUR); + bless(obj); + obj->oeroded = obj->oeroded2 = 0; + obj->oerodeproof = TRUE; + exercise(A_WIS, TRUE); + } + update_inventory(); + levl[u.ux][u.uy].typ = ROOM; + levl[u.ux][u.uy].looted = 0; + if(Invisible) newsym(u.ux, u.uy); + level.flags.nfountains--; + if(slev && slev->flags.town) + (void) angry_guards(FALSE); + return; + } else (void) get_wet(obj); + + /* Acid and water don't mix */ + if (obj->otyp == POT_ACID) { + useup(obj); + return; + } + + switch (rnd(30)) { + case 16: /* Curse the item */ + curse(obj); + break; + case 17: + case 18: + case 19: + case 20: /* Uncurse the item */ + if(obj->cursed) { + if (!Blind) + pline_The("water glows for a moment."); + uncurse(obj); + } else { + pline("A feeling of loss comes over you."); + } + break; + case 21: /* Water Demon */ + dowaterdemon(); + break; + case 22: /* Water Nymph */ + dowaternymph(); + break; + case 23: /* an Endless Stream of Snakes */ + dowatersnakes(); + break; + case 24: /* Find a gem */ + if (!levl[u.ux][u.uy].looted) { + dofindgem(); + break; + } + case 25: /* Water gushes forth */ + dogushforth(FALSE); + break; + case 26: /* Strange feeling */ + pline("A strange tingling runs up your %s.", + body_part(ARM)); + break; + case 27: /* Strange feeling */ + You_feel("a sudden chill."); + break; + case 28: /* Strange feeling */ + pline("An urge to take a bath overwhelms you."); +#ifndef GOLDOBJ + if (u.ugold > 10) { + u.ugold -= somegold() / 10; + You("lost some of your gold in the fountain!"); + levl[u.ux][u.uy].looted &= ~F_LOOTED; + exercise(A_WIS, FALSE); + } +#else + { + long money = money_cnt(invent); + struct obj *otmp; + if (money > 10) { + /* Amount to loose. Might get rounded up as fountains don't pay change... */ + money = somegold(money) / 10; + for (otmp = invent; otmp && money > 0; otmp = otmp->nobj) if (otmp->oclass == GOLD_CLASS) { + int denomination = objects[otmp->otyp].oc_cost; + long coin_loss = (money + denomination - 1) / denomination; + coin_loss = min(coin_loss, otmp->quan); + otmp->quan -= coin_loss; + money -= coin_loss * denomination; + if (!otmp->quan) delobj(otmp); + } + You("lost some of your money in the fountain!"); + levl[u.ux][u.uy].looted &= ~F_LOOTED; + exercise(A_WIS, FALSE); + } + } +#endif + break; + case 29: /* You see coins */ + + /* We make fountains have more coins the closer you are to the + * surface. After all, there will have been more people going + * by. Just like a shopping mall! Chris Woodbury */ + + if (levl[u.ux][u.uy].looted) break; + levl[u.ux][u.uy].looted |= F_LOOTED; + (void) mkgold((long) + (rnd((dunlevs_in_dungeon(&u.uz)-dunlev(&u.uz)+1)*2)+5), + u.ux, u.uy); + if (!Blind) + pline("Far below you, you see coins glistening in the water."); + exercise(A_WIS, TRUE); + newsym(u.ux,u.uy); + break; + } + update_inventory(); + dryup(u.ux, u.uy, TRUE); +} + +#ifdef SINKS +void +breaksink(x,y) +int x, y; +{ + if(cansee(x,y) || (x == u.ux && y == u.uy)) + pline_The("pipes break! Water spurts out!"); + level.flags.nsinks--; + levl[x][y].doormask = 0; + levl[x][y].typ = FOUNTAIN; + level.flags.nfountains++; + newsym(x,y); +} + +void +drinksink() +{ + struct obj *otmp; + struct monst *mtmp; + + if (Levitation) { + floating_above("sink"); + return; + } + switch(rn2(20)) { + case 0: You("take a sip of very cold water."); + break; + case 1: You("take a sip of very warm water."); + break; + case 2: You("take a sip of scalding hot water."); + if (Fire_resistance) + pline("It seems quite tasty."); + else losehp(rnd(6), "sipping boiling water", KILLED_BY); + break; + case 3: if (mvitals[PM_SEWER_RAT].mvflags & G_GONE) + pline_The("sink seems quite dirty."); + else { + mtmp = makemon(&mons[PM_SEWER_RAT], + u.ux, u.uy, NO_MM_FLAGS); + pline("Eek! There's %s in the sink!", + Blind ? "something squirmy" : + a_monnam(mtmp)); + } + break; + case 4: do { + otmp = mkobj(POTION_CLASS,FALSE); + if (otmp->otyp == POT_WATER) { + obfree(otmp, (struct obj *)0); + otmp = (struct obj *) 0; + } + } while(!otmp); + otmp->cursed = otmp->blessed = 0; + pline("Some %s liquid flows from the faucet.", + Blind ? "odd" : + hcolor(OBJ_DESCR(objects[otmp->otyp]))); + otmp->dknown = !(Blind || Hallucination); + otmp->quan++; /* Avoid panic upon useup() */ + otmp->corpsenm = 1; /* kludge for docall() */ + (void) dopotion(otmp); + obfree(otmp, (struct obj *)0); + break; + case 5: if (!(levl[u.ux][u.uy].looted & S_LRING)) { + You("find a ring in the sink!"); + (void) mkobj_at(RING_CLASS, u.ux, u.uy, TRUE); + levl[u.ux][u.uy].looted |= S_LRING; + exercise(A_WIS, TRUE); + newsym(u.ux,u.uy); + } else pline("Some dirty water backs up in the drain."); + break; + case 6: breaksink(u.ux,u.uy); + break; + case 7: pline_The("water moves as though of its own will!"); + if ((mvitals[PM_WATER_ELEMENTAL].mvflags & G_GONE) + || !makemon(&mons[PM_WATER_ELEMENTAL], + u.ux, u.uy, NO_MM_FLAGS)) + pline("But it quiets down."); + break; + case 8: pline("Yuk, this water tastes awful."); + more_experienced(1,0); + newexplevel(); + break; + case 9: pline("Gaggg... this tastes like sewage! You vomit."); + morehungry(rn1(30-ACURR(A_CON), 11)); + vomit(); + break; + case 10: pline("This water contains toxic wastes!"); + if (!Unchanging) { + You("undergo a freakish metamorphosis!"); + polyself(); + } + break; + /* more odd messages --JJB */ + case 11: You_hear("clanking from the pipes..."); + break; + case 12: You_hear("snatches of song from among the sewers..."); + break; + case 19: if (Hallucination) { + pline("From the murky drain, a hand reaches up... --oops--"); + break; + } + default: You("take a sip of %s water.", + rn2(3) ? (rn2(2) ? "cold" : "warm") : "hot"); + } +} +#endif /* SINKS */ + +/*fountain.c*/