1    | /*	SCCS Id: @(#)fountain.c	3.3	1999/08/16	*/
2    | /*	Copyright Scott R. Turner, srt@ucla, 10/27/86 */
3    | /* NetHack may be freely redistributed.  See license for details. */
4    | 
5    | /* Code for drinking from fountains. */
6    | 
7    | #include "hack.h"
8    | 
9    | STATIC_DCL void NDECL(dowatersnakes);
10   | STATIC_DCL void NDECL(dowaterdemon);
11   | STATIC_DCL void NDECL(dowaternymph);
12   | STATIC_PTR void FDECL(gush, (int,int,genericptr_t));
13   | STATIC_DCL void NDECL(dofindgem);
14   | 
15   | void
16   | floating_above(what)
17   | const char *what;
18   | {
19   |     You("are floating high above the %s.", what);
20   | }
21   | 
22   | STATIC_OVL void
23   | dowatersnakes() /* Fountain of snakes! */
24   | {
25   |     register int num = rn1(5,2);
26   |     struct monst *mtmp;
27   | 
28   |     if (!(mvitals[PM_WATER_MOCCASIN].mvflags & G_GONE)) {
29   | 	if (!Blind)
30   | 	    pline("An endless stream of %s pours forth!",
31   | 		  Hallucination ? makeplural(rndmonnam()) : "snakes");
32   | 	else
33   | 	    You_hear("%s hissing!", something);
34   | 	while(num-- > 0)
35   | 	    if((mtmp = makemon(&mons[PM_WATER_MOCCASIN],
36   | 			u.ux, u.uy, NO_MM_FLAGS)) && t_at(mtmp->mx, mtmp->my))
37   | 		(void) mintrap(mtmp);
38   |     } else
39   | 	pline_The("fountain bubbles furiously for a moment, then calms.");
40   | }
41   | 
42   | STATIC_OVL
43   | void
44   | dowaterdemon() /* Water demon */
45   | {
46   | 	register struct monst *mtmp;
47   | 
48   | 	if(mvitals[PM_WATER_DEMON].mvflags & G_GONE) return;
49   | 	if((mtmp = makemon(&mons[PM_WATER_DEMON],u.ux,u.uy, NO_MM_FLAGS))) {
50   | 	    if (!Blind)
51   | 		You("unleash %s!", a_monnam(mtmp));
52   | 	    else
53   | 		You_feel("the presence of evil.");
54   | 
55   | 	/* Give those on low levels a (slightly) better chance of survival */
56   | 	    if (rnd(100) > (80 + level_difficulty())) {
57   | 		pline("Grateful for %s release, %s grants you a wish!",
58   | 		      his[pronoun_gender(mtmp)], he[pronoun_gender(mtmp)]);
59   | 		makewish();
60   | 		mongone(mtmp);
61   | 	    } else if (t_at(mtmp->mx, mtmp->my))
62   | 		(void) mintrap(mtmp);
63   | 	}
64   | }
65   | 
66   | STATIC_OVL void
67   | dowaternymph() /* Water Nymph */
68   | {
69   | 	register struct monst *mtmp;
70   | 
71   | 	if(mvitals[PM_WATER_NYMPH].mvflags & G_GONE) return;
72   | 	if((mtmp = makemon(&mons[PM_WATER_NYMPH],u.ux,u.uy, NO_MM_FLAGS))) {
73   | 		if (!Blind)
74   | 		   You("attract %s!", a_monnam(mtmp));
75   | 		else
76   | 		   You_hear("a seductive voice.");
77   | 		mtmp->msleeping = 0;
78   | 		if (t_at(mtmp->mx, mtmp->my))
79   | 		    (void) mintrap(mtmp);
80   | 	} else
81   | 		if (!Blind)
82   | 		   pline("A large bubble rises to the surface and pops.");
83   | 		else
84   | 		   You_hear("a loud pop.");
85   | }
86   | 
87   | void
88   | dogushforth(drinking) /* Gushing forth along LOS from (u.ux, u.uy) */
89   | int drinking;
90   | {
91   | 	int madepool = 0;
92   | 
93   | 	do_clear_area(u.ux, u.uy, 7, gush, (genericptr_t)&madepool);
94   | 	if (!madepool) {
95   | 	    if (drinking)
96   | 		Your("thirst is quenched.");
97   | 	    else
98   | 		pline("Water sprays all over you.");
99   | 	}
100  | }
101  | 
102  | STATIC_PTR void
103  | gush(x, y, poolcnt)
104  | int x, y;
105  | genericptr_t poolcnt;
106  | {
107  | 	register struct monst *mtmp;
108  | 	register struct trap *ttmp;
109  | 
110  | 	if (((x+y)%2) || (x == u.ux && y == u.uy) ||
111  | 	    (rn2(1 + distmin(u.ux, u.uy, x, y)))  ||
112  | 	    (levl[x][y].typ != ROOM) ||
113  | 	    (sobj_at(BOULDER, x, y)) || nexttodoor(x, y))
114  | 		return;
115  | 
116  | 	if ((ttmp = t_at(x, y)) != 0 && !delfloortrap(ttmp))
117  | 		return;
118  | 
119  | 	if (!((*(int *)poolcnt)++))
120  | 	    pline("Water gushes forth from the overflowing fountain!");
121  | 
122  | 	/* Put a pool at x, y */
123  | 	levl[x][y].typ = POOL;
124  | 	/* No kelp! */
125  | 	del_engr_at(x, y);
126  | 	water_damage(level.objects[x][y], FALSE, TRUE);
127  | 
128  | 	if ((mtmp = m_at(x, y)) != 0)
129  | 		(void) minwater(mtmp);
130  | 	else
131  | 		newsym(x,y);
132  | }
133  | 
134  | STATIC_OVL void
135  | dofindgem() /* Find a gem in the sparkling waters. */
136  | {
137  | 	if (!Blind) You("spot a gem in the sparkling waters!");
138  | 	(void) mksobj_at(rnd_class(DILITHIUM_CRYSTAL, LUCKSTONE-1),
139  | 						u.ux, u.uy, FALSE);
140  | 	levl[u.ux][u.uy].looted |= F_LOOTED;
141  | 	newsym(u.ux, u.uy);
142  | 	exercise(A_WIS, TRUE);			/* a discovery! */
143  | }
144  | 
145  | void
146  | dryup(x, y, isyou)
147  | xchar x, y;
148  | boolean isyou;
149  | {
150  | 	if (IS_FOUNTAIN(levl[x][y].typ) &&
151  | 	    (!rn2(3) || (levl[x][y].looted & F_WARNED))) {
152  | 		s_level *slev = Is_special(&u.uz);
153  | 		if(isyou && slev && slev->flags.town &&
154  | 		   !(levl[x][y].looted & F_WARNED)) {
155  | 			struct monst *mtmp;
156  | 			levl[x][y].looted |= F_WARNED;
157  | 			/* Warn about future fountain use. */
158  | 			for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
159  | 			    if (DEADMONSTER(mtmp)) continue;
160  | 			    if ((mtmp->data == &mons[PM_WATCHMAN] ||
161  | 				mtmp->data == &mons[PM_WATCH_CAPTAIN]) &&
162  | 			       couldsee(mtmp->mx, mtmp->my) &&
163  | 			       mtmp->mpeaceful) {
164  | 				pline("%s yells:", Amonnam(mtmp));
165  | 				verbalize("Hey, stop using that fountain!");
166  | 				break;
167  | 			    }
168  | 			}
169  | 			/* You can see or hear this effect */
170  | 			if(!mtmp) pline_The("flow reduces to a trickle.");
171  | 			return;
172  | 		}
173  | #ifdef WIZARD
174  | 		if (isyou && wizard) {
175  | 			if (yn("Dry up fountain?") == 'n')
176  | 				return;
177  | 		}
178  | #endif
179  | 		if (cansee(x,y)) pline_The("fountain dries up!");
180  | 		levl[x][y].typ = ROOM;
181  | 		levl[x][y].looted = 0;
182  | 		levl[x][y].blessedftn = 0;
183  | 		/* The location is seen if the hero/monster is invisible */
184  | 		/* or felt if the hero is blind.			 */
185  | 		newsym(x, y);
186  | 		level.flags.nfountains--;
187  | 		if(isyou && slev && slev->flags.town)
188  | 		    (void) angry_guards(FALSE);
189  | 	}
190  | }
191  | 
192  | void
193  | drinkfountain()
194  | {
195  | 	/* What happens when you drink from a fountain? */
196  | 	register boolean mgkftn = (levl[u.ux][u.uy].blessedftn == 1);
197  | 	register int fate = rnd(30);
198  | 
199  | 	if (Levitation) {
200  | 		floating_above("fountain");
201  | 		return;
202  | 	}
203  | 
204  | 	if (mgkftn && u.uluck >= 0 && fate >= 10) {
205  | 		int i, ii, littleluck = (u.uluck < 4);
206  | 
207  | 		pline("Wow!  This makes you feel great!");
208  | 		/* blessed restore ability */
209  | 		for (ii = 0; ii < A_MAX; ii++)
210  | 		    if (ABASE(ii) < AMAX(ii)) {
211  | 			ABASE(ii) = AMAX(ii);
212  | 			flags.botl = 1;
213  | 		    }
214  | 		/* gain ability, blessed if "natural" luck is high */
215  | 		i = rn2(A_MAX);		/* start at a random attribute */
216  | 		for (ii = 0; ii < A_MAX; ii++) {
217  | 		    if (adjattrib(i, 1, littleluck ? -1 : 0) && littleluck)
218  | 			break;
219  | 		    if (++i >= A_MAX) i = 0;
220  | 		}
221  | 		display_nhwindow(WIN_MESSAGE, FALSE);
222  | 		pline("A wisp of vapor escapes the fountain...");
223  | 		exercise(A_WIS, TRUE);
224  | 		levl[u.ux][u.uy].blessedftn = 0;
225  | 		return;
226  | 	}
227  | 
228  | 	if (fate < 10) {
229  | 		pline_The("cool draught refreshes you.");
230  | 		u.uhunger += rnd(10); /* don't choke on water */
231  | 		newuhs(FALSE);
232  | 		if(mgkftn) return;
233  | 	} else {
234  | 	    switch (fate) {
235  | 
236  | 		case 19: /* Self-knowledge */
237  | 
238  | 			You_feel("self-knowledgeable...");
239  | 			display_nhwindow(WIN_MESSAGE, FALSE);
240  | 			enlightenment(0);
241  | 			exercise(A_WIS, TRUE);
242  | 			pline_The("feeling subsides.");
243  | 			break;
244  | 
245  | 		case 20: /* Foul water */
246  | 
247  | 			pline_The("water is foul!  You gag and vomit.");
248  | 			morehungry(rn1(20, 11));
249  | 			vomit();
250  | 			break;
251  | 
252  | 		case 21: /* Poisonous */
253  | 
254  | 			pline_The("water is contaminated!");
255  | 			if (Poison_resistance) {
256  | 	   pline("Perhaps it is runoff from the nearby %s farm.", pl_fruit);
257  | 			   losehp(rnd(4),"unrefrigerated sip of juice",
258  | 				KILLED_BY_AN);
259  | 			   break;
260  | 			}
261  | 			losestr(rn1(4,3));
262  | 			losehp(rnd(10),"contaminated water", KILLED_BY);
263  | 			exercise(A_CON, FALSE);
264  | 			break;
265  | 
266  | 		case 22: /* Fountain of snakes! */
267  | 
268  | 			dowatersnakes();
269  | 			break;
270  | 
271  | 		case 23: /* Water demon */
272  | 			dowaterdemon();
273  | 			break;
274  | 
275  | 		case 24: /* Curse an item */ {
276  | 			register struct obj *obj;
277  | 
278  | 			pline("This water's no good!");
279  | 			morehungry(rn1(20, 11));
280  | 			exercise(A_CON, FALSE);
281  | 			for(obj = invent; obj ; obj = obj->nobj)
282  | 				if (!rn2(5))	curse(obj);
283  | 			break;
284  | 			}
285  | 
286  | 		case 25: /* See invisible */
287  | 
288  | 			if (Blind) {
289  | 			  if (Invisible) {
290  | 			    You("feel very self-conscious.");
291  | 			    pline("Then it passes.");
292  | 			  } else {
293  | 			    You("feel transparent.");
294  | 			  }
295  | 			} else {
296  | 			   You("see an image of someone stalking you.");
297  | 			   pline("But it disappears.");
298  | 			}
299  | 			HSee_invisible |= FROMOUTSIDE;
300  | 			newsym(u.ux,u.uy);
301  | 			exercise(A_WIS, TRUE);
302  | 			break;
303  | 
304  | 		case 26: /* See Monsters */
305  | 
306  | 			(void) monster_detect((struct obj *)0, 0);
307  | 			exercise(A_WIS, TRUE);
308  | 			break;
309  | 
310  | 		case 27: /* Find a gem in the sparkling waters. */
311  | 
312  | 			if (!levl[u.ux][u.uy].looted) {
313  | 				dofindgem();
314  | 				break;
315  | 			}
316  | 
317  | 		case 28: /* Water Nymph */
318  | 
319  | 			dowaternymph();
320  | 			break;
321  | 
322  | 		case 29: /* Scare */ {
323  | 			register struct monst *mtmp;
324  | 
325  | 			pline("This water gives you bad breath!");
326  | 			for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
327  | 			    if(!DEADMONSTER(mtmp))
328  | 				mtmp->mflee = 1;
329  | 			}
330  | 			break;
331  | 
332  | 		case 30: /* Gushing forth in this room */
333  | 
334  | 			dogushforth(TRUE);
335  | 			break;
336  | 
337  | 		default:
338  | 
339  | 			pline("This tepid water is tasteless.");
340  | 			break;
341  | 	    }
342  | 	}
343  | 	dryup(u.ux, u.uy, TRUE);
344  | }
345  | 
346  | void
347  | dipfountain(obj)
348  | register struct obj *obj;
349  | {
350  | 	if (Levitation) {
351  | 		floating_above("fountain");
352  | 		return;
353  | 	}
354  | 
355  | 	/* Don't grant Excalibur when there's more than one object.  */
356  | 	/* (quantity could be > 1 if merged daggers got polymorphed) */
357  | 	if (obj->otyp == LONG_SWORD && obj->quan == 1L
358  | 	    && u.ulevel >= 5 && !rn2(6)
359  | 	    && !obj->oartifact
360  | 	    && !exist_artifact(LONG_SWORD, artiname(ART_EXCALIBUR))) {
361  | 		s_level *slev = Is_special(&u.uz);
362  | 
363  | 		if (u.ualign.type != A_LAWFUL) {
364  | 			/* Ha!  Trying to cheat her. */
365  | 			pline("A freezing mist rises from the water and envelopes the sword.");
366  | 			pline_The("fountain disappears!");
367  | 			curse(obj);
368  | 			if (obj->spe > -6 && !rn2(3)) obj->spe--;
369  | 			obj->oerodeproof = FALSE;
370  | 			exercise(A_WIS, FALSE);
371  | 		} else {
372  | 			/* The lady of the lake acts! - Eric Backus */
373  | 			/* Be *REAL* nice */
374  | 	  pline("From the murky depths, a hand reaches up to bless the sword.");
375  | 			pline("As the hand retreats, the fountain disappears!");
376  | 			obj = oname(obj, artiname(ART_EXCALIBUR));
377  | 			discover_artifact(ART_EXCALIBUR);
378  | 			bless(obj);
379  | 			obj->oeroded = obj->oeroded2 = 0;
380  | 			obj->oerodeproof = TRUE;
381  | 			exercise(A_WIS, TRUE);
382  | 		}
383  | 		levl[u.ux][u.uy].typ = ROOM;
384  | 		levl[u.ux][u.uy].looted = 0;
385  | 		if(Invisible) newsym(u.ux, u.uy);
386  | 		level.flags.nfountains--;
387  | 		if(slev && slev->flags.town)
388  | 		    (void) angry_guards(FALSE);
389  | 		return;
390  | 	} else (void) get_wet(obj);
391  | 
392  | 	/* Acid and water don't mix */
393  | 	if (obj->otyp == POT_ACID) {
394  | 	    useup(obj);
395  | 	    return;
396  | 	}
397  | 
398  | 	switch (rnd(30)) {
399  | 		case 16: /* Curse the item */
400  | 			curse(obj);
401  | 			break;
402  | 		case 17:
403  | 		case 18:
404  | 		case 19:
405  | 		case 20: /* Uncurse the item */
406  | 			if(obj->cursed) {
407  | 			    if (!Blind)
408  | 				pline_The("water glows for a moment.");
409  | 			    uncurse(obj);
410  | 			} else {
411  | 			    pline("A feeling of loss comes over you.");
412  | 			}
413  | 			break;
414  | 		case 21: /* Water Demon */
415  | 			dowaterdemon();
416  | 			break;
417  | 		case 22: /* Water Nymph */
418  | 			dowaternymph();
419  | 			break;
420  | 		case 23: /* an Endless Stream of Snakes */
421  | 			dowatersnakes();
422  | 			break;
423  | 		case 24: /* Find a gem */
424  | 			if (!levl[u.ux][u.uy].looted) {
425  | 				dofindgem();
426  | 				break;
427  | 			}
428  | 		case 25: /* Water gushes forth */
429  | 			dogushforth(FALSE);
430  | 			break;
431  | 		case 26: /* Strange feeling */
432  | 			pline("A strange tingling runs up your %s.",
433  | 							body_part(ARM));
434  | 			break;
435  | 		case 27: /* Strange feeling */
436  | 			You_feel("a sudden chill.");
437  | 			break;
438  | 		case 28: /* Strange feeling */
439  | 			pline("An urge to take a bath overwhelms you.");
440  | 			if (u.ugold > 10) {
441  | 			    u.ugold -= somegold() / 10;
442  | 			    You("lost some of your gold in the fountain!");
443  | 			    levl[u.ux][u.uy].looted &= ~F_LOOTED;
444  | 			    exercise(A_WIS, FALSE);
445  | 			}
446  | 			break;
447  | 		case 29: /* You see coins */
448  | 
449  | 		/* We make fountains have more coins the closer you are to the
450  | 		 * surface.  After all, there will have been more people going
451  | 		 * by.	Just like a shopping mall!  Chris Woodbury  */
452  | 
453  | 		    if (levl[u.ux][u.uy].looted) break;
454  | 		    levl[u.ux][u.uy].looted |= F_LOOTED;
455  | 		    (void) mkgold((long)
456  | 			(rnd((dunlevs_in_dungeon(&u.uz)-dunlev(&u.uz)+1)*2)+5),
457  | 			u.ux, u.uy);
458  | 		    if (!Blind)
459  | 		pline("Far below you, you see coins glistening in the water.");
460  | 		    exercise(A_WIS, TRUE);
461  | 		    newsym(u.ux,u.uy);
462  | 		    break;
463  | 	}
464  | 	dryup(u.ux, u.uy, TRUE);
465  | }
466  | 
467  | #ifdef SINKS
468  | void
469  | breaksink(x,y)
470  | int x, y;
471  | {
472  |     if(cansee(x,y) || (x == u.ux && y == u.uy))
473  | 	pline_The("pipes break!  Water spurts out!");
474  |     level.flags.nsinks--;
475  |     levl[x][y].doormask = 0;
476  |     levl[x][y].typ = FOUNTAIN;
477  |     level.flags.nfountains++;
478  |     newsym(x,y);
479  | }
480  | 
481  | void
482  | drinksink()
483  | {
484  | 	struct obj *otmp;
485  | 	struct monst *mtmp;
486  | 
487  | 	if (Levitation) {
488  | 		floating_above("sink");
489  | 		return;
490  | 	}
491  | 	switch(rn2(20)) {
492  | 		case 0: You("take a sip of very cold water.");
493  | 			break;
494  | 		case 1: You("take a sip of very warm water.");
495  | 			break;
496  | 		case 2: You("take a sip of scalding hot water.");
497  | 			if (Fire_resistance)
498  | 				pline("It seems quite tasty.");
499  | 			else losehp(rnd(6), "sipping boiling water", KILLED_BY);
500  | 			break;
501  | 		case 3: if (mvitals[PM_SEWER_RAT].mvflags & G_GONE)
502  | 				pline_The("sink seems quite dirty.");
503  | 			else {
504  | 				mtmp = makemon(&mons[PM_SEWER_RAT],
505  | 						u.ux, u.uy, NO_MM_FLAGS);
506  | 				pline("Eek!  There's %s in the sink!",
507  | 					Blind ? "something squirmy" :
508  | 					a_monnam(mtmp));
509  | 			}
510  | 			break;
511  | 		case 4: do {
512  | 				otmp = mkobj(POTION_CLASS,FALSE);
513  | 				if (otmp->otyp == POT_WATER) {
514  | 					obfree(otmp, (struct obj *)0);
515  | 					otmp = (struct obj *) 0;
516  | 				}
517  | 			} while(!otmp);
518  | 			otmp->cursed = otmp->blessed = 0;
519  | 			pline("Some %s liquid flows from the faucet.",
520  | 			      Blind ? "odd" :
521  | 			      hcolor(OBJ_DESCR(objects[otmp->otyp])));
522  | 			otmp->dknown = !(Blind || Hallucination);
523  | 			otmp->quan++; /* Avoid panic upon useup() */
524  | 			otmp->corpsenm = 1; /* kludge for docall() */
525  | 			(void) dopotion(otmp);
526  | 			obfree(otmp, (struct obj *)0);
527  | 			break;
528  | 		case 5: if (!(levl[u.ux][u.uy].looted & S_LRING)) {
529  | 			    You("find a ring in the sink!");
530  | 			    (void) mkobj_at(RING_CLASS, u.ux, u.uy, TRUE);
531  | 			    levl[u.ux][u.uy].looted |= S_LRING;
532  | 			    exercise(A_WIS, TRUE);
533  | 			    newsym(u.ux,u.uy);
534  | 			} else pline("Some dirty water backs up in the drain.");
535  | 			break;
536  | 		case 6: breaksink(u.ux,u.uy);
537  | 			break;
538  | 		case 7: pline_The("water moves as though of its own will!");
539  | 			if ((mvitals[PM_WATER_ELEMENTAL].mvflags & G_GONE)
540  | 			    || !makemon(&mons[PM_WATER_ELEMENTAL],
541  | 					u.ux, u.uy, NO_MM_FLAGS))
542  | 				pline("But it quiets down.");
543  | 			break;
544  | 		case 8: pline("Yuk, this water tastes awful.");
545  | 			more_experienced(1,0);
546  | 			newexplevel();
547  | 			break;
548  | 		case 9: pline("Gaggg... this tastes like sewage!  You vomit.");
549  | 			morehungry(rn1(30-ACURR(A_CON), 11));
550  | 			vomit();
551  | 			break;
552  | 		case 10: pline("This water contains toxic wastes!");
553  | 			if (!Unchanging) {
554  | 				You("undergo a freakish metamorphosis!");
555  | 				polyself();
556  | 			}
557  | 			break;
558  | 		/* more odd messages --JJB */
559  | 		case 11: You_hear("clanking from the pipes...");
560  | 			break;
561  | 		case 12: You_hear("snatches of song from among the sewers...");
562  | 			break;
563  | 		case 19: if (Hallucination) {
564  | 		   pline("From the murky drain, a hand reaches up... --oops--");
565  | 				break;
566  | 			}
567  | 		default: You("take a sip of %s water.",
568  | 			rn2(3) ? (rn2(2) ? "cold" : "warm") : "hot");
569  | 	}
570  | }
571  | #endif /* SINKS */
572  | 
573  | /*fountain.c*/