1    | /*	SCCS Id: @(#)trap.c	3.3	2000/07/07	*/
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    | STATIC_DCL void FDECL(dofiretrap, (struct obj *));
8    | STATIC_DCL void NDECL(domagictrap);
9    | STATIC_DCL boolean FDECL(emergency_disrobe,(boolean *));
10   | STATIC_DCL int FDECL(untrap_prob, (struct trap *ttmp));
11   | STATIC_DCL void FDECL(cnv_trap_obj, (int, int, struct trap *));
12   | STATIC_DCL void FDECL(move_into_trap, (struct trap *));
13   | STATIC_DCL int FDECL(try_disarm, (struct trap *,BOOLEAN_P));
14   | STATIC_DCL void FDECL(reward_untrap, (struct trap *, struct monst *));
15   | STATIC_DCL int FDECL(disarm_beartrap, (struct trap *));
16   | STATIC_DCL int FDECL(disarm_landmine, (struct trap *));
17   | STATIC_DCL int FDECL(disarm_squeaky_board, (struct trap *));
18   | STATIC_DCL int FDECL(disarm_shooting_trap, (struct trap *, int));
19   | STATIC_DCL int FDECL(try_lift, (struct monst *, struct trap *, int, BOOLEAN_P));
20   | STATIC_DCL int FDECL(help_monster_out, (struct monst *, struct trap *));
21   | STATIC_DCL boolean FDECL(thitm, (int, struct monst *, struct obj *, int));
22   | STATIC_DCL int FDECL(mkroll_launch,
23   | 			(struct trap *,XCHAR_P,XCHAR_P,SHORT_P,long));
24   | STATIC_DCL boolean FDECL(isclearpath,(coord *, int, SCHAR_P, SCHAR_P));
25   | STATIC_DCL void FDECL(blow_up_landmine, (struct trap *));
26   | 
27   | #ifndef OVLB
28   | STATIC_VAR const char *a_your[2];
29   | STATIC_VAR const char *A_Your[2];
30   | STATIC_VAR const char *the_your[2];
31   | STATIC_VAR const char tower_of_flame[];
32   | STATIC_VAR const char *A_gush_of_water_hits;
33   | 
34   | #else
35   | 
36   | STATIC_VAR const char *a_your[2] = { "a", "your" };
37   | STATIC_VAR const char *A_Your[2] = { "A", "Your" };
38   | STATIC_VAR const char *the_your[2] = { "the", "your" };
39   | STATIC_VAR const char tower_of_flame[] = "tower of flame";
40   | STATIC_VAR const char *A_gush_of_water_hits = "A gush of water hits";
41   | 
42   | #endif /* OVLB */
43   | 
44   | #ifdef OVLB
45   | 
46   | /* called when you're hit by fire (dofiretrap,buzz,zapyourself,explode) */
47   | boolean			/* returns TRUE if hit on torso */
48   | burnarmor(victim)
49   | struct monst *victim;
50   | {
51   |     struct obj *item;
52   |     char buf[BUFSZ];
53   |     int mat_idx;
54   |     
55   |     if (!victim) return 0;
56   | #define burn_dmg(obj,descr) rust_dmg(obj, descr, 0, FALSE, victim)
57   |     while (1) {
58   | 	switch (rn2(5)) {
59   | 	case 0:
60   | 	    item = (victim == &youmonst) ? uarmh : which_armor(victim, W_ARMH);
61   | 	    if (item) {
62   | 		mat_idx = objects[item->otyp].oc_material;
63   | 	    	Sprintf(buf,"%s helmet", materialnm[mat_idx] );
64   | 	    }
65   | 	    if (!burn_dmg(item, item ? buf : "helmet")) continue;
66   | 	    break;
67   | 	case 1:
68   | 	    item = (victim == &youmonst) ? uarmc : which_armor(victim, W_ARMC);
69   | 	    if (item) {
70   | 		(void) burn_dmg(item, "cloak");
71   | 		return TRUE;
72   | 	    }
73   | 	    item = (victim == &youmonst) ? uarm : which_armor(victim, W_ARM);
74   | 	    if (item) {
75   | 		(void) burn_dmg(item, xname(item));
76   | 		return TRUE;
77   | 	    }
78   | #ifdef TOURIST
79   | 	    item = (victim == &youmonst) ? uarmu : which_armor(victim, W_ARMU);
80   | 	    if (item)
81   | 		(void) burn_dmg(item, "shirt");
82   | #endif
83   | 	    return TRUE;
84   | 	case 2:
85   | 	    item = (victim == &youmonst) ? uarms : which_armor(victim, W_ARMS);
86   | 	    if (!burn_dmg(item, "wooden shield")) continue;
87   | 	    break;
88   | 	case 3:
89   | 	    item = (victim == &youmonst) ? uarmg : which_armor(victim, W_ARMG);
90   | 	    if (!burn_dmg(item, "gloves")) continue;
91   | 	    break;
92   | 	case 4:
93   | 	    item = (victim == &youmonst) ? uarmf : which_armor(victim, W_ARMF);
94   | 	    if (!burn_dmg(item, "boots")) continue;
95   | 	    break;
96   | 	}
97   | 	break; /* Out of while loop */
98   |     }
99   |     return FALSE;
100  | #undef burn_dmg
101  | }
102  | 
103  | /* Generic rust-armor function.  Returns TRUE if a message was printed;
104  |  * "print", if set, means to print a message (and thus to return TRUE) even
105  |  * if the item could not be rusted; otherwise a message is printed and TRUE is
106  |  * returned only for rustable items.
107  |  */
108  | boolean
109  | rust_dmg(otmp, ostr, type, print, victim)
110  | register struct obj *otmp;
111  | register const char *ostr;
112  | int type;
113  | boolean print;
114  | struct monst *victim;
115  | {
116  | 	static NEARDATA const char *action[] = { "smoulder", "rust", "rot", "corrode" };
117  | 	static NEARDATA const char *msg[] =  { "burnt", "rusted", "rotten", "corroded" };
118  | 	boolean vulnerable = FALSE;
119  | 	boolean plural;
120  | 	boolean grprot = FALSE;
121  | 	boolean is_primary = TRUE;
122  | 	boolean vismon = (victim != &youmonst) && canseemon(victim);
123  | 	int erosion;
124  | 
125  | 	if (!otmp) return(FALSE);
126  | 	switch(type) {
127  | 		case 0: vulnerable = is_flammable(otmp);
128  | 			break;
129  | 		case 1: vulnerable = is_rustprone(otmp);
130  | 			grprot = TRUE;
131  | 			break;
132  | 		case 2: vulnerable = is_rottable(otmp);
133  | 			is_primary = FALSE;
134  | 			break;
135  | 		case 3: vulnerable = is_corrodeable(otmp);
136  | 			grprot = TRUE;
137  | 			is_primary = FALSE;
138  | 			break;
139  | 	}
140  | 	erosion = is_primary ? otmp->oeroded : otmp->oeroded2;
141  | 
142  | 	if (!print && (!vulnerable || otmp->oerodeproof || erosion == MAX_ERODE))
143  | 		return FALSE;
144  | 
145  | 	plural = (is_gloves(otmp) || is_boots(otmp)) &&
146  | 		!strstri(ostr, "pair of ");	/* "pair of *s" is singular */
147  | 
148  | 	if (!vulnerable) {
149  | 	    if (flags.verbose) {
150  | 		if (victim == &youmonst)
151  | 		    Your("%s %s not affected.", ostr, plural ? "are" : "is");
152  | 		else if (vismon)
153  | 		    pline("%s's %s %s not affected.", Monnam(victim), ostr,
154  | 			plural ? "are" : "is");
155  | 	    }
156  | 	} else if (erosion < MAX_ERODE) {
157  | 	    if (grprot && otmp->greased) {
158  | 		grease_protect(otmp,ostr,plural,victim);
159  | 	    } else if (otmp->oerodeproof || (otmp->blessed && !rnl(4))) {
160  | 		if (flags.verbose) {
161  | 		    if (victim == &youmonst)
162  | 			pline("Somehow, your %s %s not affected.",
163  | 				ostr, plural ? "are" : "is");
164  | 		    else if (vismon)
165  | 			pline("Somehow, %s's %s %s not affected.",
166  | 				mon_nam(victim), ostr, plural ? "are" : "is");
167  | 		}
168  | 	    } else {
169  | 		if (victim == &youmonst)
170  | 		    Your("%s %s%s%s!", ostr, action[type],
171  | 			plural ? "" : "s",
172  | 			erosion+1 == MAX_ERODE ? " completely" :
173  | 			erosion ? " further" : "");
174  | 		else if (vismon)
175  | 		    pline("%s's %s %s%s%s!", Monnam(victim), ostr, action[type],
176  | 			plural ? "" : "s",
177  | 			erosion+1 == MAX_ERODE ? " completely" :
178  | 			erosion ? " further" : "");
179  | 		if (is_primary)
180  | 		    otmp->oeroded++;
181  | 		else
182  | 		    otmp->oeroded2++;
183  | 	    }
184  | 	} else {
185  | 	    if (flags.verbose) {
186  | 		if (victim == &youmonst)
187  | 		    Your("%s %s%s completely %s.", ostr,
188  | 			Blind ? "feel" : "look",
189  | 			plural ? "" : "s", msg[type]);
190  | 		else if (vismon)
191  | 		    pline("%s's %s look%s completely %s.",
192  | 			Monnam(victim), ostr,
193  | 			plural ? "" : "s", msg[type]);
194  | 	    }
195  | 	}
196  | 	return(TRUE);
197  | }
198  | 
199  | void
200  | grease_protect(otmp,ostr,plu,victim)
201  | register struct obj *otmp;
202  | register const char *ostr;
203  | register boolean plu;
204  | struct monst *victim;
205  | {
206  | 	static const char txt[] = "protected by the layer of grease!";
207  | 	boolean vismon = (victim != &youmonst) && canseemon(victim);
208  | 
209  | 	if (ostr) {
210  | 	    if (victim == &youmonst)
211  | 		Your("%s %s %s",ostr,plu ? "are" : "is", txt);
212  | 	    else if (vismon)
213  | 		pline("%s's %s %s %s", Monnam(victim),
214  | 		    ostr, plu ? "are" : "is", txt);
215  | 	} else {
216  | 	    if (victim == &youmonst)
217  | 		Your("%s %s",aobjnam(otmp,"are"), txt);
218  | 	    else if (vismon)
219  | 		pline("%s's %s %s", Monnam(victim), aobjnam(otmp,"are"), txt);
220  | 	}
221  | 	if (!rn2(2)) {
222  | 	    pline_The("grease dissolves.");
223  | 	    otmp->greased = 0;
224  | 	    update_inventory();
225  | 	}
226  | }
227  | 
228  | struct trap *
229  | maketrap(x,y,typ)
230  | register int x, y, typ;
231  | {
232  | 	register struct trap *ttmp;
233  | 	register struct rm *lev;
234  | 	register boolean oldplace;
235  | 
236  | 	if ((ttmp = t_at(x,y)) != 0) {
237  | 	    if (ttmp->ttyp == MAGIC_PORTAL) return (struct trap *)0;
238  | 	    oldplace = TRUE;
239  | 	    if (u.utrap && (x == u.ux) && (y == u.uy) &&
240  | 	      ((u.utraptype == TT_BEARTRAP && typ != BEAR_TRAP) ||
241  | 	      (u.utraptype == TT_WEB && typ != WEB) ||
242  | 	      (u.utraptype == TT_PIT && typ != PIT && typ != SPIKED_PIT)))
243  | 		    u.utrap = 0;
244  | 	} else {
245  | 	    oldplace = FALSE;
246  | 	    ttmp = newtrap();
247  | 	    ttmp->tx = x;
248  | 	    ttmp->ty = y;
249  | 	    ttmp->launch.x = -1;	/* force error if used before set */
250  | 	    ttmp->launch.y = -1;
251  | 	}
252  | 	ttmp->ttyp = typ;
253  | 	switch(typ) {
254  | 	    case STATUE_TRAP:	    /* create a "living" statue */
255  | 	      { struct monst *mtmp;
256  | 		struct obj *otmp, *statue;
257  | 
258  | 		statue = mkcorpstat(STATUE, (struct monst *)0,
259  | 					&mons[rndmonnum()], x, y, FALSE);
260  | 		mtmp = makemon(&mons[statue->corpsenm], 0, 0, NO_MM_FLAGS);
261  | 		if (!mtmp) break; /* should never happen */
262  | 		while(mtmp->minvent) {
263  | 		    otmp = mtmp->minvent;
264  | 		    otmp->owornmask = 0;
265  | 		    obj_extract_self(otmp);
266  | 		    add_to_container(statue, otmp);
267  | 		}
268  | 		mongone(mtmp);
269  | 		break;
270  | 	      }
271  | 	    case ROLLING_BOULDER_TRAP:	/* boulder will roll towards trigger */
272  | 		(void) mkroll_launch(ttmp, x, y, BOULDER, 1L);
273  | 		break;
274  | 	    case HOLE:
275  | 	    case PIT:
276  | 	    case SPIKED_PIT:
277  | 	    case TRAPDOOR:
278  | 		lev = &levl[x][y];
279  | 		if (*in_rooms(x, y, SHOPBASE) &&
280  | 			((typ == HOLE || typ == TRAPDOOR) || IS_DOOR(lev->typ)))
281  | 		    add_damage(x, y,		/* schedule repair */
282  | 			(IS_DOOR(lev->typ) && !flags.mon_moving) ? 200L : 0L);
283  | 		lev->doormask = 0;	/* subsumes altarmask, icedpool... */
284  | 		if (IS_ROOM(lev->typ)) /* && !IS_AIR(lev->typ) */
285  | 		    lev->typ = ROOM;
286  | 
287  | 		/*
288  | 		 * some cases which can happen when digging
289  | 		 * down while phazing thru solid areas
290  | 		 */
291  | 		else if (lev->typ == STONE || lev->typ == SCORR)
292  | 		    lev->typ = CORR;
293  | 		else if (IS_WALL(lev->typ) || lev->typ == SDOOR)
294  | 		    lev->typ = level.flags.is_maze_lev ? ROOM :
295  | 			       level.flags.is_cavernous_lev ? CORR : DOOR;
296  | 
297  | 		unearth_objs(x, y);
298  | 		break;
299  | 	}
300  | 	if (ttmp->ttyp == HOLE) ttmp->tseen = 1;  /* You can't hide a hole */
301  | 	else ttmp->tseen = 0;
302  | 	ttmp->once = 0;
303  | 	ttmp->madeby_u = 0;
304  | 	ttmp->dst.dnum = -1;
305  | 	ttmp->dst.dlevel = -1;
306  | 	if (!oldplace) {
307  | 	    ttmp->ntrap = ftrap;
308  | 	    ftrap = ttmp;
309  | 	}
310  | 	return(ttmp);
311  | }
312  | 
313  | void
314  | fall_through(td)
315  | boolean td;	/* td == TRUE : trap door or hole */
316  | {
317  | 	d_level dtmp;
318  | 	char msgbuf[BUFSZ];
319  | 	const char *dont_fall = 0;
320  | 	register int newlevel = dunlev(&u.uz);
321  | 
322  | 	/* KMH -- You can't escape the Sokoban level traps */
323  | 	if(Blind && Levitation && !In_sokoban(&u.uz)) return;
324  | 
325  | 	do {
326  | 	    newlevel++;
327  | 	} while(!rn2(4) && newlevel < dunlevs_in_dungeon(&u.uz));
328  | 
329  | 	if(td) {
330  | 	    struct trap *t=t_at(u.ux,u.uy);
331  | 	    if (!In_sokoban(&u.uz)) {
332  | 		if (t->ttyp == TRAPDOOR)
333  | 			pline("A trap door opens up under you!");
334  | 		else 
335  | 			pline("There's a gaping hole under you!");
336  | 	    }
337  | 	} else pline_The("%s opens up under you!", surface(u.ux,u.uy));
338  | 
339  | 	if (In_sokoban(&u.uz) && Can_fall_thru(&u.uz))
340  | 	    ;	/* KMH -- You can't escape the Sokoban level traps */
341  | 	else if(Levitation || u.ustuck || !Can_fall_thru(&u.uz)
342  | 	   || Flying || is_clinger(youmonst.data)
343  | 	   || (Inhell && !u.uevent.invoked &&
344  | 					newlevel == dunlevs_in_dungeon(&u.uz))
345  | 		) {
346  | 	    dont_fall = "don't fall in.";
347  | 	} else if (youmonst.data->msize >= MZ_HUGE) {
348  | 	    dont_fall = "don't fit through.";
349  | 	} else if (!next_to_u()) {
350  | 	    dont_fall = "are jerked back by your pet!";
351  | 	}
352  | 	if (dont_fall) {
353  | 	    You(dont_fall);
354  | 	    /* hero didn't fall through, but any objects here might */
355  | 	    impact_drop((struct obj *)0, u.ux, u.uy, 0);
356  | 	    if (!td) {
357  | 		display_nhwindow(WIN_MESSAGE, FALSE);
358  | 		pline_The("opening under you closes up.");
359  | 	    }
360  | 	    return;
361  | 	}
362  | 
363  | 	if(*u.ushops) shopdig(1);
364  | 	if (Is_stronghold(&u.uz)) {
365  | 	    find_hell(&dtmp);
366  | 	} else {
367  | 	    dtmp.dnum = u.uz.dnum;
368  | 	    dtmp.dlevel = newlevel;
369  | 	}
370  | 	if (!td)
371  | 	    Sprintf(msgbuf, "The hole in the %s above you closes up.",
372  | 		    ceiling(u.ux,u.uy));
373  | 	schedule_goto(&dtmp, FALSE, TRUE, 0,
374  | 		      (char *)0, !td ? msgbuf : (char *)0);
375  | }
376  | 
377  | /*
378  |  * Animate the given statue.  May have been via shatter attempt, trap,
379  |  * or stone to flesh spell.  Return a monster if successfully animated.
380  |  * If the monster is animated, the object is deleted.  If fail_reason
381  |  * is non-null, then fill in the reason for failure (or success).
382  |  *
383  |  * The cause of animation is:
384  |  *
385  |  *	ANIMATE_NORMAL  - hero "finds" the monster
386  |  *	ANIMATE_SHATTER - hero tries to destroy the statue
387  |  *	ANIMATE_SPELL   - stone to flesh spell hits the statue
388  |  *
389  |  * Perhaps x, y is not needed if we can use get_obj_location() to find
390  |  * the statue's location... ???
391  |  */
392  | struct monst *
393  | animate_statue(statue, x, y, cause, fail_reason)
394  | struct obj *statue;
395  | xchar x, y;
396  | int cause;
397  | int *fail_reason;
398  | {
399  | 	struct permonst *mptr;
400  | 	struct monst *mon = 0;
401  | 	struct obj *item;
402  | 	coord cc;
403  | 
404  | 	if (statue->oxlth && statue->oattached == OATTACHED_MONST) {
405  | 	    cc.x = x,  cc.y = y;
406  | 	    mon = montraits(statue, &cc);
407  | 	    if (mon && mon->mtame && !mon->isminion)
408  | 		wary_dog(mon, TRUE);
409  | 	} else {
410  | 	    /*
411  | 	     * Guard against someone wishing for a statue of a unique monster
412  | 	     * (which is allowed in normal play) and then tossing it onto the
413  | 	     * [detected or guessed] location of a statue trap.  Normally the
414  | 	     * uppermost statue is the one which would be activated.
415  | 	     */
416  | 	    mptr = &mons[statue->corpsenm];
417  | 	    if (mptr->geno & G_UNIQ) {
418  | 	        if (fail_reason) *fail_reason = AS_MON_IS_UNIQUE;
419  | 	        return (struct monst *)0;
420  | 	    }
421  | 	    mon = makemon(mptr, x, y, NO_MINVENT);
422  | 	}
423  | 
424  | 	if (!mon) {
425  | 	    if (fail_reason) *fail_reason = AS_NO_MON;
426  | 	    return (struct monst *)0;
427  | 	}
428  | 
429  | 	/* if statue has been named, give same name to the monster */
430  | 	if (statue->onamelth)
431  | 	    mon = christen_monst(mon, ONAME(statue));
432  | 	/* transfer any statue contents to monster's inventory */
433  | 	while ((item = statue->cobj) != 0) {
434  | 	    obj_extract_self(item);
435  | 	    (void) add_to_minv(mon, item);
436  | 	}
437  | 	m_dowear(mon, TRUE);
438  | 	delobj(statue);
439  | 	/* mimic statue becomes seen mimic; other hiders won't be hidden */
440  | 	if (mon->m_ap_type) seemimic(mon);
441  | 	else mon->mundetected = FALSE;
442  | 	if ((x == u.ux && y == u.uy) || cause == ANIMATE_SPELL)
443  | 	    pline_The("statue comes to life!");
444  | 	else if (cause == ANIMATE_SHATTER)
445  | 	    pline("Instead of shattering, the statue suddenly comes alive!");
446  | 	else /* cause == ANIMATE_NORMAL */
447  | 	    You("find %s posing as a statue.", a_monnam(mon));
448  | 	/* avoid hiding under nothing */
449  | 	if (x == u.ux && y == u.uy &&
450  | 		Upolyd && hides_under(youmonst.data) && !OBJ_AT(x, y))
451  | 	    u.uundetected = 0;
452  | 
453  | 	if (fail_reason) *fail_reason = AS_OK;
454  | 	return mon;
455  | }
456  | 
457  | /*
458  |  * You've either stepped onto a statue trap's location or you've triggered a
459  |  * statue trap by searching next to it or by trying to break it with a wand
460  |  * or pick-axe.
461  |  */
462  | struct monst *
463  | activate_statue_trap(trap, x, y, shatter)
464  | struct trap *trap;
465  | xchar x, y;
466  | boolean shatter;
467  | {
468  | 	struct monst *mtmp = (struct monst *)0;
469  | 	struct obj *otmp = sobj_at(STATUE, x, y);
470  | 	int fail_reason;
471  | 
472  | 	/*
473  | 	 * Try to animate the first valid statue.  Stop the loop when we
474  | 	 * actually create something or the failure cause is not because
475  | 	 * the mon was unique.
476  | 	 */
477  | 	deltrap(trap);
478  | 	while (otmp) {
479  | 	    mtmp = animate_statue(otmp, x, y,
480  | 		    shatter ? ANIMATE_SHATTER : ANIMATE_NORMAL, &fail_reason);
481  | 	    if (mtmp || fail_reason != AS_MON_IS_UNIQUE) break;
482  | 
483  | 	    while ((otmp = otmp->nexthere) != 0)
484  | 		if (otmp->otyp == STATUE) break;
485  | 	}
486  | 
487  | 	if (Blind) feel_location(x, y);
488  | 	else newsym(x, y);
489  | 	return mtmp;
490  | }
491  | 
492  | void
493  | dotrap(trap)
494  | register struct trap *trap;
495  | {
496  | 	register int ttype = trap->ttyp;
497  | 	register struct obj *otmp;
498  | 	boolean already_seen = trap->tseen;
499  | 	
500  | 	nomul(0);
501  | 
502  | 	/* KMH -- You can't escape the Sokoban level traps */
503  | 	if (In_sokoban(&u.uz) &&
504  | 			(ttype == PIT || ttype == SPIKED_PIT || ttype == HOLE ||
505  | 			ttype == TRAPDOOR)) {
506  | 	    /* The "air currents" message is still appropriate -- even when
507  | 	     * the hero isn't flying or levitating -- because it conveys the
508  | 	     * reason why the player cannot escape the trap with a dexterity
509  | 	     * check, clinging to the ceiling, etc.
510  | 	     */
511  | 	    pline("Air currents pull you down into %s %s!",
512  | 	    	a_your[trap->madeby_u],
513  | 	    	defsyms[trap_to_defsym(ttype)].explanation);
514  | 	    /* then proceed to normal trap effect */
515  | 	} else if (already_seen) {
516  | 	    if ((Levitation || Flying) &&
517  | 		    (ttype == PIT || ttype == SPIKED_PIT || ttype == HOLE ||
518  | 		    ttype == BEAR_TRAP)) {
519  | 		You("%s over %s %s.",
520  | 		    Levitation ? "float" : "fly",
521  | 		    a_your[trap->madeby_u],
522  | 		    defsyms[trap_to_defsym(ttype)].explanation);
523  | 		return;
524  | 	    }
525  | 	    if(!Fumbling && ttype != MAGIC_PORTAL && ttype != ANTI_MAGIC &&
526  | 		(!rn2(5) ||
527  | 	    ((ttype == PIT || ttype == SPIKED_PIT) && is_clinger(youmonst.data)))) {
528  | 		You("escape %s %s.",
529  | 		    (ttype == ARROW_TRAP && !trap->madeby_u) ? "an" :
530  | 			a_your[trap->madeby_u],
531  | 		    defsyms[trap_to_defsym(ttype)].explanation);
532  | 		return;
533  | 	    }
534  | 	}
535  | 
536  | 	switch(ttype) {
537  | 	    case ARROW_TRAP:
538  | 		seetrap(trap);
539  | 		pline("An arrow shoots out at you!");
540  | 		otmp = mksobj(ARROW, TRUE, FALSE);
541  | 		otmp->quan = 1L;
542  | 		otmp->owt = weight(otmp);
543  | 		if (thitu(8, dmgval(otmp, &youmonst), otmp, "arrow")) {
544  | 		    obfree(otmp, (struct obj *)0);
545  | 		} else {
546  | 		    place_object(otmp, u.ux, u.uy);
547  | 		    if (!Blind) otmp->dknown = 1;
548  | 		    stackobj(otmp);
549  | 		    newsym(u.ux, u.uy);
550  | 		}
551  | 		break;
552  | 	    case DART_TRAP:
553  | 		seetrap(trap);
554  | 		pline("A little dart shoots out at you!");
555  | 		otmp = mksobj(DART, TRUE, FALSE);
556  | 		otmp->quan = 1L;
557  | 		otmp->owt = weight(otmp);
558  | 		if (!rn2(6)) otmp->opoisoned = 1;
559  | 		if (thitu(7, dmgval(otmp, &youmonst), otmp, "little dart")) {
560  | 		    if (otmp->opoisoned)
561  | 			poisoned("dart",A_CON,"poison dart",10);
562  | 		    obfree(otmp, (struct obj *)0);
563  | 		} else {
564  | 		    place_object(otmp, u.ux, u.uy);
565  | 		    if (!Blind) otmp->dknown = 1;
566  | 		    stackobj(otmp);
567  | 		    newsym(u.ux, u.uy);
568  | 		}
569  | 		break;
570  | 	    case ROCKTRAP:
571  | 		{
572  | 		    int dmg = d(2,6); /* should be std ROCK dmg? */
573  | 
574  | 		    seetrap(trap);
575  | 		    otmp = mksobj_at(ROCK, u.ux, u.uy, TRUE);
576  | 		    otmp->quan = 1L;
577  | 		    otmp->owt = weight(otmp);
578  | 
579  |     pline("A trap door in the %s opens and a rock falls on your %s!",
580  | 			    ceiling(u.ux,u.uy),
581  | 			    body_part(HEAD));
582  | 
583  | 		    if (uarmh) {
584  | 			if(is_metallic(uarmh)) {
585  | 			    pline("Fortunately, you are wearing a hard helmet.");
586  | 			    dmg = 2;
587  | 			} else if (flags.verbose) {
588  | 			    Your("%s does not protect you.", xname(uarmh));
589  | 			}
590  | 		    }
591  | 
592  | 		    if (!Blind) otmp->dknown = 1;
593  | 		    stackobj(otmp);
594  | 		    newsym(u.ux,u.uy);	/* map the rock */
595  | 
596  | 		    losehp(dmg, "falling rock", KILLED_BY_AN);
597  | 		    exercise(A_STR, FALSE);
598  | 		}
599  | 		break;
600  | 
601  | 	    case SQKY_BOARD:	    /* stepped on a squeaky board */
602  | 		if (Levitation || Flying) {
603  | 		    if (!Blind) {
604  | 			seetrap(trap);
605  | 			if (Hallucination)
606  | 				You("notice a crease in the linoleum.");
607  | 			else
608  | 				You("notice a loose board below you.");
609  | 		    }
610  | 		} else {
611  | 		    seetrap(trap);
612  | 		    pline("A board beneath you squeaks loudly.");
613  | 		    wake_nearby();
614  | 		}
615  | 		break;
616  | 
617  | 	    case BEAR_TRAP:
618  | 		if(Levitation || Flying) break;
619  | 		seetrap(trap);
620  | 		if(amorphous(youmonst.data) || is_whirly(youmonst.data) ||
621  | 						    unsolid(youmonst.data)) {
622  | 		    pline("%s bear trap closes harmlessly through you.",
623  | 			    A_Your[trap->madeby_u]);
624  | 		    break;
625  | 		}
626  | 		if(youmonst.data->msize <= MZ_SMALL) {
627  | 		    pline("%s bear trap closes harmlessly over you.",
628  | 			    A_Your[trap->madeby_u]);
629  | 		    break;
630  | 		}
631  | 		u.utrap = rn1(4, 4);
632  | 		u.utraptype = TT_BEARTRAP;
633  | #ifdef STEED
634  | 		if (u.usteed) {
635  | 		    pline("%s bear trap closes on %s %s!",
636  | 			A_Your[trap->madeby_u], s_suffix(mon_nam(u.usteed)),
637  | 			body_part(FOOT));
638  | 		} else
639  | #endif
640  | 		{
641  | 		    pline("%s bear trap closes on your %s!",
642  | 			    A_Your[trap->madeby_u], body_part(FOOT));
643  | 		    if(u.umonnum == PM_OWLBEAR || u.umonnum == PM_BUGBEAR)
644  | 			You("howl in anger!");
645  | 		}
646  | 		exercise(A_DEX, FALSE);
647  | 		break;
648  | 
649  | 	    case SLP_GAS_TRAP:
650  | 		seetrap(trap);
651  | 		if(Sleep_resistance) {
652  | 		    You("are enveloped in a cloud of gas!");
653  | 		    break;
654  | 		}
655  | 		pline("A cloud of gas puts you to sleep!");
656  | 		flags.soundok = 0;
657  | 		fall_asleep(-rnd(25), TRUE);
658  | 		afternmv = Hear_again;
659  | 		break;
660  | 
661  | 	    case RUST_TRAP:
662  | 		seetrap(trap);
663  | 		if (u.umonnum == PM_IRON_GOLEM) {
664  | 		    pline("%s you!", A_gush_of_water_hits);
665  | 		    You("are covered with rust!");
666  | 		    rehumanize();
667  | 		    break;
668  | 		} else if (u.umonnum == PM_GREMLIN && rn2(3)) {
669  | 		    pline("%s you!", A_gush_of_water_hits);
670  | 		    (void)split_mon(&youmonst, (struct monst *)0);
671  | 		    break;
672  | 		}
673  | 
674  | 	    /* Unlike monsters, traps cannot aim their rust attacks at
675  | 	     * you, so instead of looping through and taking either the
676  | 	     * first rustable one or the body, we take whatever we get,
677  | 	     * even if it is not rustable.
678  | 	     */
679  | 		switch (rn2(5)) {
680  | 		    case 0:
681  | 			pline("%s you on the %s!", A_gush_of_water_hits,
682  | 				    body_part(HEAD));
683  | 			(void) rust_dmg(uarmh, "helmet", 1, TRUE, &youmonst);
684  | 			break;
685  | 		    case 1:
686  | 			pline("%s your left %s!", A_gush_of_water_hits,
687  | 				    body_part(ARM));
688  | 			if (rust_dmg(uarms, "shield", 1, TRUE, &youmonst))
689  | 			    break;
690  | 			if (u.twoweap || (uwep && bimanual(uwep)))
691  | 			    erode_weapon(u.twoweap ? uswapwep : uwep, FALSE);
692  | glovecheck:		(void) rust_dmg(uarmg, "gauntlets", 1, TRUE, &youmonst);
693  | 			/* Not "metal gauntlets" since it gets called
694  | 			 * even if it's leather for the message
695  | 			 */
696  | 			break;
697  | 		    case 2:
698  | 			pline("%s your right %s!", A_gush_of_water_hits,
699  | 				    body_part(ARM));
700  | 			erode_weapon(uwep, FALSE);
701  | 			goto glovecheck;
702  | 		    default:
703  | 			pline("%s you!", A_gush_of_water_hits);
704  | 			for (otmp=invent; otmp; otmp = otmp->nobj)
705  | 				    (void) snuff_lit(otmp);
706  | 			if (uarmc)
707  | 			    (void) rust_dmg(uarmc, "cloak", 1, TRUE, &youmonst);
708  | 			else if (uarm)
709  | 			    (void) rust_dmg(uarm, "armor", 1, TRUE, &youmonst);
710  | #ifdef TOURIST
711  | 			else if (uarmu)
712  | 			    (void) rust_dmg(uarmu, "shirt", 1, TRUE, &youmonst);
713  | #endif
714  | 		}
715  | 		break;
716  | 
717  | 	    case FIRE_TRAP:
718  | 		seetrap(trap);
719  | 		dofiretrap((struct obj *)0);
720  | 		break;
721  | 
722  | 	    case PIT:
723  | 	    case SPIKED_PIT:
724  | 		/* KMH -- You can't escape the Sokoban level traps */
725  | 		if (!In_sokoban(&u.uz) && (Levitation || Flying)) break;
726  | 		seetrap(trap);
727  | 		if (is_clinger(youmonst.data)) {
728  | 		    if(trap->tseen) {
729  | 			You("see %s %spit below you.", a_your[trap->madeby_u],
730  | 			    ttype == SPIKED_PIT ? "spiked " : "");
731  | 		    } else {
732  | 			pline("%s pit %sopens up under you!",
733  | 			    A_Your[trap->madeby_u],
734  | 			    ttype == SPIKED_PIT ? "full of spikes " : "");
735  | 			You("don't fall in!");
736  | 		    }
737  | 		    break;
738  | 		}
739  | 		if (!In_sokoban(&u.uz))
740  | 		    You("fall into %s pit!", a_your[trap->madeby_u]);
741  | 		/* wumpus reference */
742  | 		if (Role_if(PM_RANGER) && !trap->madeby_u && !trap->once &&
743  | 			In_quest(&u.uz) && Is_qlocate(&u.uz)) {
744  | 		    pline("Fortunately it has a bottom after all...");
745  | 		    trap->once = 1;
746  | 		} else if (u.umonnum == PM_PIT_VIPER ||
747  | 			u.umonnum == PM_PIT_FIEND)
748  | 		    pline("How pitiful.  Isn't that the pits?");
749  | 		if (ttype == SPIKED_PIT)
750  | 		    You("land on a set of sharp iron spikes!");
751  | 		if (!Passes_walls)
752  | 		    u.utrap = rn1(6,2);
753  | 		u.utraptype = TT_PIT;
754  | 		if (ttype == SPIKED_PIT) {
755  | 		    losehp(rnd(10),"fell into a pit of iron spikes",
756  | 			NO_KILLER_PREFIX);
757  | 		    if (!rn2(6))
758  | 			poisoned("spikes", A_STR, "fall onto poison spikes", 8);
759  | 		} else
760  | 		    losehp(rnd(6),"fell into a pit", NO_KILLER_PREFIX);
761  | 		if (Punished && !carried(uball)) {
762  | 		    unplacebc();
763  | 		    ballfall();
764  | 		    placebc();
765  | 		}
766  | 		selftouch("Falling, you");
767  | 		vision_full_recalc = 1;	/* vision limits change */
768  | 		exercise(A_STR, FALSE);
769  | 		exercise(A_DEX, FALSE);
770  | 		break;
771  | 	    case HOLE:
772  | 	    case TRAPDOOR:
773  | 		seetrap(trap);
774  | 		if (!Can_fall_thru(&u.uz)) {
775  | 		    impossible("dotrap: %ss cannot exist on this level.",
776  | 			       defsyms[trap_to_defsym(ttype)].explanation);
777  | 		    break;		/* don't activate it after all */
778  | 		}
779  | 		fall_through(TRUE);
780  | 		break;
781  | 
782  | 	    case TELEP_TRAP:
783  | 		seetrap(trap);
784  | 		tele_trap(trap);
785  | 		break;
786  | 	    case LEVEL_TELEP:
787  | 		seetrap(trap);
788  | 		level_tele_trap(trap);
789  | 		break;
790  | 
791  | 	    case WEB: /* Our luckless player has stumbled into a web. */
792  | 		seetrap(trap);
793  | 		if (amorphous(youmonst.data) || is_whirly(youmonst.data) ||
794  | 						    unsolid(youmonst.data)) {
795  | 		    if (acidic(youmonst.data) || u.umonnum == PM_GELATINOUS_CUBE ||
796  | 			u.umonnum == PM_FIRE_ELEMENTAL) {
797  | 			You("%s %s spider web!",
798  | 			    (u.umonnum == PM_FIRE_ELEMENTAL) ? "burn" : "dissolve",
799  | 			    a_your[trap->madeby_u]);
800  | 			deltrap(trap);
801  | 			newsym(u.ux,u.uy);
802  | 			break;
803  | 		    }
804  | 		    You("flow through %s spider web.",
805  | 			    a_your[trap->madeby_u]);
806  | 		    break;
807  | 		}
808  | 		if (webmaker(youmonst.data)) {
809  | 		    pline(trap->madeby_u ? "You take a walk on your web."
810  | 					 : "There is a spider web here.");
811  | 		    break;
812  | 		}
813  | 		You("%s into %s spider web!",
814  | 		      Levitation ? (const char *)"float" :
815  | 		      locomotion(youmonst.data, "stumble"),
816  | 		      a_your[trap->madeby_u]);
817  | 		u.utraptype = TT_WEB;
818  | 
819  | 		/* Time stuck in the web depends on your strength. */
820  | 		{
821  | 		    register int str = ACURR(A_STR);
822  | 
823  | 		    if (str <= 3) u.utrap = rn1(6,6);
824  | 		    else if (str < 6) u.utrap = rn1(6,4);
825  | 		    else if (str < 9) u.utrap = rn1(4,4);
826  | 		    else if (str < 12) u.utrap = rn1(4,2);
827  | 		    else if (str < 15) u.utrap = rn1(2,2);
828  | 		    else if (str < 18) u.utrap = rnd(2);
829  | 		    else if (str < 69) u.utrap = 1;
830  | 		    else {
831  | 			u.utrap = 0;
832  | 			You("tear through %s web!", a_your[trap->madeby_u]);
833  | 			deltrap(trap);
834  | 			newsym(u.ux,u.uy);	/* get rid of trap symbol */
835  | 		    }
836  | 		}
837  | 		break;
838  | 
839  | 	    case STATUE_TRAP:
840  | 		(void) activate_statue_trap(trap, u.ux, u.uy, FALSE);
841  | 		break;
842  | 
843  | 	    case MAGIC_TRAP:	    /* A magic trap. */
844  | 		seetrap(trap);
845  | 		if (!rn2(30)) {
846  | 		    deltrap(trap);
847  | 		    newsym(u.ux,u.uy);	/* update position */
848  | 		    You("are caught in a magical explosion!");
849  | 		    losehp(rnd(10), "magical explosion", KILLED_BY_AN);
850  | 		    Your("body absorbs some of the magical energy!");
851  | 		    u.uen = (u.uenmax += 2);
852  | 		} else domagictrap();
853  | 		break;
854  | 
855  | 	    case ANTI_MAGIC:
856  | 		seetrap(trap);
857  | 		if(Antimagic) {
858  | 		    shieldeff(u.ux, u.uy);
859  | 		    You_feel("momentarily lethargic.");
860  | 		} else drain_en(rnd(u.ulevel) + 1);
861  | 		break;
862  | 
863  | 	    case POLY_TRAP:
864  | 		seetrap(trap);
865  | 		You("%s onto a polymorph trap!",
866  | 		    Levitation ? (const char *)"float" :
867  | 		    locomotion(youmonst.data, "step"));
868  | 		if(Antimagic || Unchanging) {
869  | 		    shieldeff(u.ux, u.uy);
870  | 		    You_feel("momentarily different.");
871  | 		    /* Trap did nothing; don't remove it --KAA */
872  | 		} else {
873  | 		    deltrap(trap);	/* delete trap before polymorph */
874  | 		    newsym(u.ux,u.uy);	/* get rid of trap symbol */
875  | 		    You_feel("a change coming over you.");
876  | 		    polyself();
877  | 		}
878  | 		break;
879  | 
880  | 	    case LANDMINE:
881  | 		if (Levitation || Flying) {
882  | 		    if (!already_seen && rn2(3)) break;
883  | 		    seetrap(trap);
884  | 		    pline("%s %s in a pile of soil below you.",
885  | 			    already_seen ? "There is" : "You discover",
886  | 			    trap->madeby_u ? "the trigger of your mine" :
887  | 					     "a trigger");
888  | 		    if (already_seen && rn2(3)) break;
889  | 		    pline("KAABLAMM!!!  The air currents set %s%s off!",
890  | 			    already_seen ? a_your[trap->madeby_u] : "",
891  | 			    already_seen ? " land mine" : "it");
892  | 		} else {
893  | 		    seetrap(trap);
894  | 		    pline("KAABLAMM!!!  You triggered %s land mine!",
895  | 					    a_your[trap->madeby_u]);
896  | 		    set_wounded_legs(LEFT_SIDE, rn1(35, 41));
897  | 		    set_wounded_legs(RIGHT_SIDE, rn1(35, 41));
898  | 		    exercise(A_DEX, FALSE);
899  | 		}
900  | 		blow_up_landmine(trap);
901  | 		newsym(u.ux,u.uy);		/* update trap symbol */
902  | 		losehp(rnd(16), "land mine", KILLED_BY_AN);
903  | 		/* fall recursively into the pit... */
904  | 		if ((trap = t_at(u.ux, u.uy)) != 0) dotrap(trap);
905  | 		break;
906  | 
907  | 	    case ROLLING_BOULDER_TRAP:
908  | 		seetrap(trap);
909  | 		pline("Click! You trigger a rolling boulder trap!");
910  | 		if(!launch_obj(BOULDER, trap->launch.x, trap->launch.y,
911  | 		      trap->launch2.x,trap->launch2.y, ROLL)) {
912  | 		    deltrap(trap);
913  | 		    newsym(u.ux,u.uy);	/* get rid of trap symbol */
914  | 		    pline("Fortunately for you, no boulder was released.");
915  | 		}
916  | 		break;
917  | 
918  | 	    case MAGIC_PORTAL:
919  | 		seetrap(trap);
920  | 		domagicportal(trap);
921  | 		break;
922  | 
923  | 	    default:
924  | 		seetrap(trap);
925  | 		impossible("You hit a trap of type %u", trap->ttyp);
926  | 	}
927  | }
928  | 
929  | /* some actions common to both player and monsters for triggered landmine */
930  | STATIC_OVL void
931  | blow_up_landmine(trap)
932  | struct trap *trap;
933  | {
934  | 	scatter(trap->tx, trap->ty, 4,
935  | 		MAY_DESTROY | MAY_HIT | MAY_FRACTURE | VIS_EFFECTS,
936  | 		(struct obj *)0);
937  | 	del_engr_at(trap->tx, trap->ty);
938  | 	wake_nearto(trap->tx, trap->ty, 400);
939  | 	if (IS_DOOR(levl[trap->tx][trap->ty].typ))
940  | 	    levl[trap->tx][trap->ty].doormask = D_BROKEN;
941  | 	/* TODO: destroy drawbridge if present;
942  | 		 sometimes delete trap instead of always leaving a pit */
943  | 	trap->ttyp = PIT;		/* explosion creates a pit */
944  | 	trap->madeby_u = FALSE;		/* resulting pit isn't yours */
945  | }
946  | 
947  | #endif /* OVLB */
948  | #ifdef OVL3
949  | 
950  | /*
951  |  * Move obj from (x1,y1) to (x2,y2)
952  |  *
953  |  * Return 0 if no object was launched.
954  |  *        1 if an object was launched and placed somewhere.
955  |  *        2 if an object was launched, but used up.
956  |  */
957  | int
958  | launch_obj(otyp, x1, y1, x2, y2, style)
959  | short otyp;
960  | register int x1,y1,x2,y2;
961  | int style;
962  | {
963  | 	register struct monst *mtmp;
964  | 	register struct obj *otmp;
965  | 	register int dx,dy;
966  | 	struct obj *singleobj;
967  | 	boolean used_up = FALSE;
968  | 	boolean otherside = FALSE;
969  | 	int dist;
970  | 	int tmp;
971  | 	int delaycnt = 0;
972  | 
973  | 	otmp = sobj_at(otyp, x1, y1);
974  | 	/* Try the other side too, for rolling boulder traps */
975  | 	if (!otmp && otyp == BOULDER) {
976  | 		otherside = TRUE;
977  | 		otmp = sobj_at(otyp, x2, y2);
978  | 	}
979  | 	if (!otmp) return 0;
980  | 	if (otherside) {	/* swap 'em */
981  | 		int tx, ty;
982  | 
983  | 		tx = x1; ty = y1;
984  | 		x1 = x2; y1 = y2;
985  | 		x2 = tx; y2 = ty;
986  | 	}
987  | 
988  | 	if (otmp->quan == 1L) {
989  | 	    obj_extract_self(otmp);
990  | 	    singleobj = otmp;
991  | 	    otmp = (struct obj *) 0;
992  | 	} else {
993  | 	    singleobj = splitobj(otmp, otmp->quan - 1L);
994  | 	    obj_extract_self(singleobj);
995  | 	}
996  | 	newsym(x1,y1);
997  | 	/* in case you're using a pick-axe to chop the boulder that's being
998  | 	   launched (perhaps a monster triggered it), destroy context so that
999  | 	   next dig attempt never thinks you're resuming previous effort */
1000 | 	if ((otyp == BOULDER || otyp == STATUE) &&
1001 | 	    singleobj->ox == digging.pos.x && singleobj->oy == digging.pos.y)
1002 | 	    (void) memset((genericptr_t)&digging, 0, sizeof digging);
1003 | 
1004 | 	dist = distmin(x1,y1,x2,y2);
1005 | 	bhitpos.x = x1;
1006 | 	bhitpos.y = y1;
1007 | 	dx = sgn(x2 - x1);
1008 | 	dy = sgn(y2 - y1);
1009 | 	switch (style) {
1010 | 	    case ROLL:
1011 | 			delaycnt = 2;
1012 | 			/* fall through */
1013 | 	    default:
1014 | 			if (!delaycnt) delaycnt = 1;
1015 | 			if (!cansee(bhitpos.x,bhitpos.y)) curs_on_u();
1016 | 			tmp_at(DISP_FLASH, obj_to_glyph(singleobj));
1017 | 			tmp_at(bhitpos.x, bhitpos.y);
1018 | 	}
1019 | 
1020 | 	/* Set the object in motion */
1021 | 	while(dist-- > 0 && !used_up) {
1022 | 		tmp_at(bhitpos.x, bhitpos.y);
1023 | 		tmp = delaycnt;
1024 | 
1025 | 		/* dstage@u.washington.edu -- Delay only if hero sees it */
1026 | 		if (cansee(bhitpos.x, bhitpos.y))
1027 | 			while (tmp-- > 0) delay_output();
1028 | 
1029 | 		bhitpos.x += dx;
1030 | 		bhitpos.y += dy;
1031 | 
1032 | 		if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) {
1033 | 			if (otyp == BOULDER && throws_rocks(mtmp->data)) {
1034 | 			    if (rn2(3)) {
1035 | 				pline("%s snatches the boulder.",
1036 | 					Monnam(mtmp));
1037 | 				(void) mpickobj(mtmp, singleobj);
1038 | 				used_up = TRUE;
1039 | 				break;
1040 | 			    }
1041 | 			}
1042 | 			if (ohitmon(mtmp,singleobj,
1043 | 					(style==ROLL) ? -1 : dist, FALSE)) {
1044 | 				used_up = TRUE;
1045 | 				break;
1046 | 			}
1047 | 		} else if (bhitpos.x == u.ux && bhitpos.y == u.uy) {
1048 | 			if (multi) nomul(0);
1049 | 			if (thitu(9 + singleobj->spe,
1050 | 				  dmgval(singleobj, &youmonst),
1051 | 				  singleobj, (char *)0))
1052 | 			    stop_occupation();
1053 | 		}
1054 | 		if (style == ROLL) {
1055 | 		    if (down_gate(bhitpos.x, bhitpos.y) != -1) {
1056 | 		       if (ship_object(singleobj, bhitpos.x, bhitpos.y, FALSE)){
1057 | 				used_up = TRUE;
1058 | 				break;
1059 | 			}
1060 | 		    }
1061 | 		    if (flooreffects(singleobj, bhitpos.x, bhitpos.y, "fall")) {
1062 | 			used_up = TRUE;
1063 | 			break;
1064 | 		    }
1065 | 		}
1066 | 		if (otyp == BOULDER && closed_door(bhitpos.x,bhitpos.y)) {
1067 | 			if (cansee(bhitpos.x, bhitpos.y))
1068 | 				pline_The("boulder crashes through a door.");
1069 | 			levl[bhitpos.x][bhitpos.y].doormask = D_BROKEN;
1070 | 		}
1071 | 	}
1072 | 	tmp_at(DISP_END, 0);
1073 | 	if (!used_up) {
1074 | 		place_object(singleobj, x2,y2);
1075 | 		newsym(x2,y2);
1076 | 		return 1;
1077 | 	} else
1078 | 		return 2;
1079 | }
1080 | #endif /* OVL3 */
1081 | #ifdef OVLB
1082 | 
1083 | void
1084 | seetrap(trap)
1085 | 	register struct trap *trap;
1086 | {
1087 | 	if(!trap->tseen) {
1088 | 	    trap->tseen = 1;
1089 | 	    newsym(trap->tx, trap->ty);
1090 | 	}
1091 | }
1092 | 
1093 | #endif /* OVLB */
1094 | #ifdef OVL3
1095 | 
1096 | STATIC_OVL int
1097 | mkroll_launch(ttmp, x, y, otyp, ocount)
1098 | struct trap *ttmp;
1099 | xchar x,y;
1100 | short otyp;
1101 | long ocount;
1102 | {
1103 | 	struct obj *otmp;
1104 | 	register int tmp;
1105 | 	schar dx,dy;
1106 | 	int distance;
1107 | 	coord cc;
1108 | 	coord bcc;
1109 | 	int trycount = 0;
1110 | 	boolean success = FALSE;
1111 | 	int mindist = 4;
1112 | 
1113 | 	if (ttmp->ttyp == ROLLING_BOULDER_TRAP) mindist = 2;
1114 | 	distance = rn1(5,4);    /* 4..8 away */
1115 | 	tmp = rn2(8);		/* randomly pick a direction to try first */
1116 | 	while (distance >= mindist) {
1117 | 		dx = xdir[tmp];
1118 | 		dy = ydir[tmp];
1119 | 		cc.x = x; cc.y = y;
1120 | 		/* Prevent boulder from being placed on water */
1121 | 		if (ttmp->ttyp == ROLLING_BOULDER_TRAP
1122 | 				&& is_pool(x+distance*dx,y+distance*dy))
1123 | 			success = FALSE;
1124 | 		else success = isclearpath(&cc, distance, dx, dy);
1125 | 		if (ttmp->ttyp == ROLLING_BOULDER_TRAP) {
1126 | 			boolean success_otherway;
1127 | 			bcc.x = x; bcc.y = y;
1128 | 			success_otherway = isclearpath(&bcc, distance,
1129 | 						-(dx), -(dy));
1130 | 			if (!success_otherway) success = FALSE;
1131 | 		}
1132 | 		if (success) break;
1133 | 		if (++tmp > 7) tmp = 0;
1134 | 		if ((++trycount % 8) == 0) --distance;
1135 | 	}
1136 | 	if (!success) {
1137 | 	    /* create the trap without any ammo, launch pt at trap location */
1138 | 		cc.x = bcc.x = x;
1139 | 		cc.y = bcc.y = y;
1140 | 	} else {
1141 | 		otmp = mksobj(otyp, TRUE, FALSE);
1142 | 		otmp->quan = ocount;
1143 | 		otmp->owt = weight(otmp);
1144 | 		place_object(otmp, cc.x, cc.y);
1145 | 		stackobj(otmp);
1146 | 	}
1147 | 	ttmp->launch.x = cc.x;
1148 | 	ttmp->launch.y = cc.y;
1149 | 	if (ttmp->ttyp == ROLLING_BOULDER_TRAP) {
1150 | 		ttmp->launch2.x = bcc.x;
1151 | 		ttmp->launch2.y = bcc.y;
1152 | 	} else
1153 | 		ttmp->launch_otyp = otyp;
1154 | 	newsym(ttmp->launch.x, ttmp->launch.y);
1155 | 	return 1;
1156 | }
1157 | 
1158 | STATIC_OVL boolean
1159 | isclearpath(cc,distance,dx,dy)
1160 | coord *cc;
1161 | int distance;
1162 | schar dx,dy;
1163 | {
1164 | 	uchar typ;
1165 | 	xchar x, y;
1166 | 
1167 | 	x = cc->x;
1168 | 	y = cc->y;
1169 | 	while (distance-- > 0) {
1170 | 		x += dx;
1171 | 		y += dy;
1172 | 		typ = levl[x][y].typ;
1173 | 		if (!isok(x,y) || !ZAP_POS(typ) || closed_door(x,y))
1174 | 			return FALSE;
1175 | 	}
1176 | 	cc->x = x;
1177 | 	cc->y = y;
1178 | 	return TRUE;
1179 | }
1180 | #endif /* OVL3 */
1181 | #ifdef OVL1
1182 | 
1183 | int
1184 | mintrap(mtmp)
1185 | register struct monst *mtmp;
1186 | {
1187 | 	register struct trap *trap = t_at(mtmp->mx, mtmp->my);
1188 | 	boolean trapkilled = FALSE;
1189 | 	struct permonst *mptr = mtmp->data;
1190 | 	struct obj *otmp;
1191 | 
1192 | 	if (!trap) {
1193 | 	    mtmp->mtrapped = 0;	/* perhaps teleported? */
1194 | 	} else if (mtmp->mtrapped) {	/* is currently in the trap */
1195 | 	    if (!rn2(40)) {
1196 | 		if (sobj_at(BOULDER, mtmp->mx, mtmp->my) &&
1197 | 			(trap->ttyp == PIT || trap->ttyp == SPIKED_PIT)) {
1198 | 		    if (!rn2(2)) {
1199 | 			mtmp->mtrapped = 0;
1200 | 			if (canseemon(mtmp))
1201 | 			    pline("%s pulls free...", Monnam(mtmp));
1202 | 			fill_pit(mtmp->mx, mtmp->my);
1203 | 		    }
1204 | 		} else {
1205 | 		    mtmp->mtrapped = 0;
1206 | 		}
1207 | 	    } else if (metallivorous(mptr)) {
1208 | 		if (trap->ttyp == BEAR_TRAP) {
1209 | 		    if (canseemon(mtmp))
1210 | 			pline("%s eats a bear trap!", Monnam(mtmp));
1211 | 		    deltrap(trap);
1212 | 		    mtmp->meating = 5;
1213 | 		    mtmp->mtrapped = 0;
1214 | 		} else if (trap->ttyp == SPIKED_PIT) {
1215 | 		    if (canseemon(mtmp))
1216 | 			pline("%s munches on some spikes!", Monnam(mtmp));
1217 | 		    trap->ttyp = PIT;
1218 | 		    mtmp->meating = 5;
1219 | 		}
1220 | 	    }
1221 | 	} else {
1222 | 	    register int tt = trap->ttyp;
1223 | 	    boolean in_sight, tear_web, see_it,
1224 | 		    inescapable = ((tt == HOLE || tt == PIT) &&
1225 | 				   In_sokoban(&u.uz) && !trap->madeby_u);
1226 | 	    const char *fallverb;
1227 | 
1228 | 	    if (!inescapable &&
1229 | 		    ((mtmp->mtrapseen & (1 << (tt-1))) != 0 ||
1230 | 			(tt == HOLE && !mindless(mtmp->data)))) {
1231 | 		/* it has been in such a trap - perhaps it escapes */
1232 | 		if(rn2(4)) return(0);
1233 | 	    } else {
1234 | 		mtmp->mtrapseen |= (1 << (tt-1));
1235 | 	    }
1236 | 	    /* Monster is aggravated by being trapped by you.
1237 | 	       Recognizing who made the trap isn't completely
1238 | 	       unreasonable; everybody has their own style. */
1239 | 	    if (trap->madeby_u && rnl(5)) setmangry(mtmp);
1240 | 
1241 | 	    /* bug?  `in_sight' ought to be split to distinguish between
1242 | 	       trap_in_sight and can_see_victim to handle invisible monsters */
1243 | 	    in_sight = canseemon(mtmp);
1244 | 	    switch (tt) {
1245 | 		case ARROW_TRAP:
1246 | 			otmp = mksobj(ARROW, TRUE, FALSE);
1247 | 			otmp->quan = 1L;
1248 | 			otmp->owt = weight(otmp);
1249 | 			if (in_sight) seetrap(trap);
1250 | 			if(thitm(8, mtmp, otmp, 0)) trapkilled = TRUE;
1251 | 			break;
1252 | 		case DART_TRAP:
1253 | 			otmp = mksobj(DART, TRUE, FALSE);
1254 | 			otmp->quan = 1L;
1255 | 			otmp->owt = weight(otmp);
1256 | 			if (!rn2(6)) otmp->opoisoned = 1;
1257 | 			if (in_sight) seetrap(trap);
1258 | 			if(thitm(7, mtmp, otmp, 0)) trapkilled = TRUE;
1259 | 			break;
1260 | 		case ROCKTRAP:
1261 | 			otmp = mksobj(ROCK, TRUE, FALSE);
1262 | 			otmp->quan = 1L;
1263 | 			otmp->owt = weight(otmp);
1264 | 			if (in_sight) seetrap(trap);
1265 | 			if (thitm(0, mtmp, otmp, d(2, 6)))
1266 | 			    trapkilled = TRUE;
1267 | 			break;
1268 | 
1269 | 		case SQKY_BOARD:
1270 | 			if(is_flyer(mptr)) break;
1271 | 			/* stepped on a squeaky board */
1272 | 			if (in_sight) {
1273 | 			    pline("A board beneath %s squeaks loudly.", mon_nam(mtmp));
1274 | 			    seetrap(trap);
1275 | 			} else
1276 | 			   You_hear("a distant squeak.");
1277 | 			/* wake up nearby monsters */
1278 | 			wake_nearto(mtmp->mx, mtmp->my, 40);
1279 | 			break;
1280 | 
1281 | 		case BEAR_TRAP:
1282 | 			if(mptr->msize > MZ_SMALL &&
1283 | 				!amorphous(mptr) && !is_flyer(mptr) &&
1284 | 				!is_whirly(mptr) && !unsolid(mptr)) {
1285 | 			    mtmp->mtrapped = 1;
1286 | 			    if(in_sight) {
1287 | 				pline("%s is caught in %s bear trap!",
1288 | 				      Monnam(mtmp), a_your[trap->madeby_u]);
1289 | 				seetrap(trap);
1290 | 			    } else {
1291 | 				if((mptr == &mons[PM_OWLBEAR]
1292 | 				    || mptr == &mons[PM_BUGBEAR])
1293 | 				   && flags.soundok)
1294 | 				    You_hear("the roaring of an angry bear!");
1295 | 			    }
1296 | 			}
1297 | 			break;
1298 | 
1299 | 		case SLP_GAS_TRAP:
1300 | 			if (!resists_sleep(mtmp) &&
1301 | 				!mtmp->msleeping && mtmp->mcanmove) {
1302 | 			    mtmp->mcanmove = 0;
1303 | 			    mtmp->mfrozen = rnd(25);
1304 | 			    if (in_sight) {
1305 | 				pline("%s suddenly falls asleep!",
1306 | 				      Monnam(mtmp));
1307 | 				seetrap(trap);
1308 | 			    }
1309 | 			}
1310 | 			break;
1311 | 
1312 | 		case RUST_TRAP:
1313 | 		    {
1314 | 			struct obj *target;
1315 | 
1316 | 			if (in_sight)
1317 | 			    seetrap(trap);
1318 | 			switch (rn2(5)) {
1319 | 			case 0:
1320 | 			    if (in_sight)
1321 | 				pline("%s %s on the %s!", A_gush_of_water_hits,
1322 | 				    mon_nam(mtmp), mbodypart(mtmp, HEAD));
1323 | 			    target = which_armor(mtmp, W_ARMH);
1324 | 			    (void) rust_dmg(target, "helmet", 1, TRUE, mtmp);
1325 | 			    break;
1326 | 			case 1:
1327 | 			    if (in_sight)
1328 | 				pline("%s %s's left %s!", A_gush_of_water_hits,
1329 | 				    mon_nam(mtmp), mbodypart(mtmp, ARM));
1330 | 			    target = which_armor(mtmp, W_ARMS);
1331 | 			    if (rust_dmg(target, "shield", 1, TRUE, mtmp))
1332 | 				break;
1333 | 			    target = MON_WEP(mtmp);
1334 | 			    if (target && bimanual(target))
1335 | 				erode_weapon(target, FALSE);
1336 | glovecheck:		    target = which_armor(mtmp, W_ARMG);
1337 | 			    (void) rust_dmg(target, "gauntlets", 1, TRUE, mtmp);
1338 | 			    break;
1339 | 			case 2:
1340 | 			    if (in_sight)
1341 | 				pline("%s %s's right %s!", A_gush_of_water_hits,
1342 | 				    mon_nam(mtmp), mbodypart(mtmp, ARM));
1343 | 			    erode_weapon(MON_WEP(mtmp), FALSE);
1344 | 			    goto glovecheck;
1345 | 			default:
1346 | 			    if (in_sight)
1347 | 				pline("%s %s!", A_gush_of_water_hits,
1348 | 				    mon_nam(mtmp));
1349 | 			    for (otmp=mtmp->minvent; otmp; otmp = otmp->nobj)
1350 | 				(void) snuff_lit(otmp);
1351 | 			    target = which_armor(mtmp, W_ARMC);
1352 | 			    if (target)
1353 | 				(void) rust_dmg(target, "cloak", 1, TRUE, mtmp);
1354 | 			    else {
1355 | 				target = which_armor(mtmp, W_ARM);
1356 | 				if (target)
1357 | 				    (void) rust_dmg(target, "armor", 1, TRUE, mtmp);
1358 | #ifdef TOURIST
1359 | 				else {
1360 | 				    target = which_armor(mtmp, W_ARMU);
1361 | 				    (void) rust_dmg(target, "shirt", 1, TRUE, mtmp);
1362 | 				}
1363 | #endif
1364 | 			    }
1365 | 			}
1366 | 			if (mptr == &mons[PM_IRON_GOLEM]) {
1367 | 				if (in_sight)
1368 | 				    pline("%s falls to pieces!", Monnam(mtmp));
1369 | 				else if(mtmp->mtame)
1370 | 				    pline("May %s rust in peace.",
1371 | 								mon_nam(mtmp));
1372 | 				mondied(mtmp);
1373 | 				if (mtmp->mhp <= 0)
1374 | 					trapkilled = TRUE;
1375 | 			} else if (mptr == &mons[PM_GREMLIN] && rn2(3)) {
1376 | 				(void)split_mon(mtmp, (struct monst *)0);
1377 | 			}
1378 | 			break;
1379 | 		    }
1380 | 		case FIRE_TRAP:
1381 |  mfiretrap:
1382 | 			see_it = cansee(mtmp->mx, mtmp->my);
1383 | 			if (in_sight)
1384 | 			    pline("A %s erupts from the %s under %s!",
1385 | 				  tower_of_flame,
1386 | 				  surface(mtmp->mx,mtmp->my), mon_nam(mtmp));
1387 | 			else if (see_it)  /* evidently `mtmp' is invisible */
1388 | 			    You("see a %s erupt from the %s!",
1389 | 				tower_of_flame, surface(mtmp->mx,mtmp->my));
1390 | 
1391 | 			if (resists_fire(mtmp)) {
1392 | 			    if (in_sight) {
1393 | 				shieldeff(mtmp->mx,mtmp->my);
1394 | 				pline("%s is uninjured.", Monnam(mtmp));
1395 | 			    }
1396 | 			} else {
1397 | 			    int num = d(2,4);
1398 | 
1399 | 			    if (thitm(0, mtmp, (struct obj *)0, num))
1400 | 				trapkilled = TRUE;
1401 | 			    else
1402 | 				/* we know mhp is at least `num' below mhpmax,
1403 | 				   so no (mhp > mhpmax) check is needed here */
1404 | 				mtmp->mhpmax -= rn2(num + 1);
1405 | 			}
1406 | 			if (burnarmor(mtmp) || rn2(3)) {
1407 | 			    (void) destroy_mitem(mtmp, SCROLL_CLASS, AD_FIRE);
1408 | 			    (void) destroy_mitem(mtmp, SPBOOK_CLASS, AD_FIRE);
1409 | 			    (void) destroy_mitem(mtmp, POTION_CLASS, AD_FIRE);
1410 | 			}
1411 | 			if (burn_floor_paper(mtmp->mx, mtmp->my, see_it) &&
1412 | 				!see_it && distu(mtmp->mx, mtmp->my) <= 3*3)
1413 | 			    You("smell smoke.");
1414 | 			if (is_ice(mtmp->mx,mtmp->my))
1415 | 			    melt_ice(mtmp->mx,mtmp->my);
1416 | 			if (see_it) seetrap(trap);
1417 | 			break;
1418 | 
1419 | 		case PIT:
1420 | 		case SPIKED_PIT:
1421 | 			fallverb = "falls";
1422 | 			if (is_flyer(mptr) || is_floater(mptr) ||
1423 | 				(mtmp->wormno && count_wsegs(mtmp) > 5) ||
1424 | 				is_clinger(mptr)) {
1425 | 			    if (!inescapable) break;	/* avoids trap */
1426 | 			    fallverb = "is dragged";	/* sokoban pit */
1427 | 			}
1428 | 			if (!passes_walls(mptr))
1429 | 			    mtmp->mtrapped = 1;
1430 | 			if (in_sight) {
1431 | 			    pline("%s %s into %s pit!",
1432 | 				  Monnam(mtmp), fallverb,
1433 | 				  a_your[trap->madeby_u]);
1434 | 			    seetrap(trap);
1435 | 			}
1436 | 			if (mptr == &mons[PM_PIT_VIPER] || mptr == &mons[PM_PIT_FIEND])
1437 | 			    pline("How pitiful.  Isn't that the pits?");
1438 | 			mselftouch(mtmp, "Falling, ", FALSE);
1439 | 			if (mtmp->mhp <= 0 ||
1440 | 				thitm(0, mtmp, (struct obj *)0,
1441 | 				      rnd((tt == PIT) ? 6 : 10)))
1442 | 			    trapkilled = TRUE;
1443 | 			break;
1444 | 		case HOLE:
1445 | 		case TRAPDOOR:
1446 | 			if (!Can_fall_thru(&u.uz)) {
1447 | 			 impossible("mintrap: %ss cannot exist on this level.",
1448 | 				    defsyms[trap_to_defsym(tt)].explanation);
1449 | 			    break;	/* don't activate it after all */
1450 | 			}
1451 | 			if (is_flyer(mptr) || is_floater(mptr) ||
1452 | 				mptr == &mons[PM_WUMPUS] ||
1453 | 				(mtmp->wormno && count_wsegs(mtmp) > 5) ||
1454 | 				mptr->msize >= MZ_HUGE) {
1455 | 			    if (inescapable) {	/* sokoban hole */
1456 | 				if (in_sight) {
1457 | 				    pline("%s seems to be yanked down!",
1458 | 					  Monnam(mtmp));
1459 | 				    /* suppress message in mlevel_tele_trap() */
1460 | 				    in_sight = FALSE;
1461 | 				    seetrap(trap);
1462 | 				}
1463 | 			    } else
1464 | 				break;
1465 | 			}
1466 | 			/* Fall through */
1467 | 		case LEVEL_TELEP:
1468 | 		case MAGIC_PORTAL:
1469 | 			{
1470 | 			    int mlev_res;
1471 | 			    mlev_res = mlevel_tele_trap(mtmp, trap,
1472 | 							inescapable, in_sight);
1473 | 			    if (mlev_res) return(mlev_res);
1474 | 			}
1475 | 			break;
1476 | 
1477 | 		case TELEP_TRAP:
1478 | 			mtele_trap(mtmp, trap, in_sight);
1479 | 			break;
1480 | 
1481 | 		case WEB:
1482 | 			/* Monster in a web. */
1483 | 			if (webmaker(mptr)) break;
1484 | 			if (amorphous(mptr) || is_whirly(mptr) || unsolid(mptr)){
1485 | 			    if(acidic(mptr) ||
1486 | 			       mptr == &mons[PM_GELATINOUS_CUBE] ||
1487 | 			       mptr == &mons[PM_FIRE_ELEMENTAL]) {
1488 | 				if (in_sight)
1489 | 				    pline("%s %s %s spider web!",
1490 | 					  Monnam(mtmp),
1491 | 					  (mptr == &mons[PM_FIRE_ELEMENTAL]) ?
1492 | 					    "burns" : "dissolves",
1493 | 					  a_your[trap->madeby_u]);
1494 | 				deltrap(trap);
1495 | 				newsym(mtmp->mx, mtmp->my);
1496 | 				break;
1497 | 			    }
1498 | 			    if (in_sight) {
1499 | 				pline("%s flows through %s spider web.",
1500 | 				      Monnam(mtmp),
1501 | 				      a_your[trap->madeby_u]);
1502 | 				seetrap(trap);
1503 | 			    }
1504 | 			    break;
1505 | 			}
1506 | 			tear_web = FALSE;
1507 | 			switch (monsndx(mptr)) {
1508 | 			    case PM_OWLBEAR: /* Eric Backus */
1509 | 			    case PM_BUGBEAR:
1510 | 				if (!in_sight) {
1511 | 				    You_hear("the roaring of a confused bear!");
1512 | 				    mtmp->mtrapped = 1;
1513 | 				    break;
1514 | 				}
1515 | 				/* fall though */
1516 | 			    default:
1517 | 				if (mptr->mlet == S_GIANT ||
1518 | 				    (mptr->mlet == S_DRAGON &&
1519 | 					extra_nasty(mptr)) || /* excl. babies */
1520 | 				    (mtmp->wormno && count_wsegs(mtmp) > 5)) {
1521 | 				    tear_web = TRUE;
1522 | 				} else if (in_sight) {
1523 | 				    pline("%s is caught in %s spider web.",
1524 | 					  Monnam(mtmp),
1525 | 					  a_your[trap->madeby_u]);
1526 | 				    seetrap(trap);
1527 | 				}
1528 | 				mtmp->mtrapped = tear_web ? 0 : 1;
1529 | 				break;
1530 | 			    /* this list is fairly arbitrary; it deliberately
1531 | 			       excludes wumpus & giant/ettin zombies/mummies */
1532 | 			    case PM_TITANOTHERE:
1533 | 			    case PM_BALUCHITHERIUM:
1534 | 			    case PM_PURPLE_WORM:
1535 | 			    case PM_JABBERWOCK:
1536 | 			    case PM_IRON_GOLEM:
1537 | 			    case PM_BALROG:
1538 | 			    case PM_KRAKEN:
1539 | 				tear_web = TRUE;
1540 | 				break;
1541 | 			}
1542 | 			if (tear_web) {
1543 | 			    if (in_sight)
1544 | 				pline("%s tears through %s spider web!",
1545 | 				      Monnam(mtmp), a_your[trap->madeby_u]);
1546 | 			    deltrap(trap);
1547 | 			    newsym(mtmp->mx, mtmp->my);
1548 | 			}
1549 | 			break;
1550 | 
1551 | 		case STATUE_TRAP:
1552 | 			break;
1553 | 
1554 | 		case MAGIC_TRAP:
1555 | 			/* A magic trap.  Monsters usually immune. */
1556 | 			if (!rn2(21)) goto mfiretrap;
1557 | 			break;
1558 | 		case ANTI_MAGIC:
1559 | 			break;
1560 | 
1561 | 		case LANDMINE:
1562 | 			if(rn2(3))
1563 | 				break; /* monsters usually don't set it off */
1564 | 			if(is_flyer(mptr)) {
1565 | 				boolean already_seen = trap->tseen;
1566 | 				if (in_sight && !already_seen) {
1567 | 	pline("A trigger appears in a pile of soil below %s.", mon_nam(mtmp));
1568 | 					seetrap(trap);
1569 | 				}
1570 | 				if (rn2(3)) break;
1571 | 				if (in_sight) {
1572 | 					newsym(mtmp->mx, mtmp->my);
1573 | 					pline_The("air currents set %s off!",
1574 | 					  already_seen ? "a land mine" : "it");
1575 | 				}
1576 | 			} else if(in_sight) {
1577 | 			    newsym(mtmp->mx, mtmp->my);
1578 | 			    pline("KAABLAMM!!!  %s triggers %s land mine!",
1579 | 				Monnam(mtmp), a_your[trap->madeby_u]);
1580 | 			}
1581 | 			if (!in_sight)
1582 | 				pline("Kaablamm!  You hear an explosion in the distance!");
1583 | 			blow_up_landmine(trap);
1584 | 			if(thitm(0, mtmp, (struct obj *)0, rnd(16)))
1585 | 				trapkilled = TRUE;
1586 | 			else {
1587 | 				/* monsters recursively fall into new pit */
1588 | 				if (mintrap(mtmp) == 2) trapkilled=TRUE;
1589 | 			}
1590 | 			if (unconscious()) {
1591 | 				multi = -1;
1592 | 				nomovemsg="The explosion awakens you!";
1593 | 			}
1594 | 			break;
1595 | 
1596 | 		case POLY_TRAP:
1597 | 		    if (resists_magm(mtmp)) {
1598 | 			shieldeff(mtmp->mx, mtmp->my);
1599 | 		    } else if (!resist(mtmp, WAND_CLASS, 0, NOTELL)) {
1600 | 			(void) newcham(mtmp, (struct permonst *)0);
1601 | 			if (in_sight) seetrap(trap);
1602 | 		    }
1603 | 		    break;
1604 | 
1605 | 		case ROLLING_BOULDER_TRAP:
1606 | 		    if (!is_flyer(mptr)) {
1607 | 		        newsym(mtmp->mx,mtmp->my);
1608 | 			if (in_sight)
1609 | 			  pline("Click! %s triggers %s.", Monnam(mtmp),
1610 | 				  trap->tseen ?
1611 | 				  "a rolling boulder trap" :
1612 | 				  something);
1613 | 			if (launch_obj(BOULDER, trap->launch.x, trap->launch.y,
1614 | 				       trap->launch2.x, trap->launch2.y, ROLL)) {
1615 | 			  if (in_sight) trap->tseen = TRUE;
1616 | 			  else You_hear(Hallucination ?
1617 | 					"someone bowling." :
1618 | 					"rumbling in the distance.");
1619 | 			  if (mtmp->mhp <= 0) trapkilled = TRUE;
1620 | 			} else {
1621 | 			  deltrap(trap);
1622 | 			  newsym(mtmp->mx,mtmp->my);
1623 | 			}
1624 | 		      }
1625 | 		    break;
1626 | 
1627 | 		default:
1628 | 			impossible("Some monster encountered a strange trap of type %d.", tt);
1629 | 	    }
1630 | 	}
1631 | 	if(trapkilled) return 2;
1632 | 	return mtmp->mtrapped;
1633 | }
1634 | 
1635 | #endif /* OVL1 */
1636 | #ifdef OVLB
1637 | 
1638 | /* Combine cockatrice checks into single functions to avoid repeating code. */
1639 | void
1640 | instapetrify(str)
1641 | const char *str;
1642 | {
1643 | 	if (Stone_resistance) return;
1644 | 	if (poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))
1645 | 	    return;
1646 | 	You("turn to stone...");
1647 | 	killer_format = KILLED_BY;
1648 | 	killer = str;
1649 | 	done(STONING);
1650 | }
1651 | 
1652 | void
1653 | minstapetrify(mon,byplayer)
1654 | struct monst *mon;
1655 | boolean byplayer;
1656 | {
1657 | 	if (resists_ston(mon)) return;
1658 | 	if (cansee(mon->mx, mon->my))
1659 | 		pline("%s turns to stone.", Monnam(mon));
1660 | 	if (poly_when_stoned(mon->data)) {
1661 | 		mon_to_stone(mon);
1662 | 		return;
1663 | 	}
1664 | 	if (byplayer) {
1665 | 		stoned = TRUE;
1666 | 		xkilled(mon,0);
1667 | 	} else monstone(mon);
1668 | }
1669 | 
1670 | void
1671 | selftouch(arg)
1672 | const char *arg;
1673 | {
1674 | 	char kbuf[BUFSZ];
1675 | 
1676 | 	if(uwep && uwep->otyp == CORPSE && touch_petrifies(&mons[uwep->corpsenm])
1677 | 			&& !Stone_resistance) {
1678 | 		pline("%s touch the %s corpse.", arg,
1679 | 		        mons[uwep->corpsenm].mname);
1680 | 		Sprintf(kbuf, "%s corpse", an(mons[uwep->corpsenm].mname));
1681 | 		instapetrify(kbuf);
1682 | 	}
1683 | 	/* Or your secondary weapon, if wielded */
1684 | 	if(u.twoweap && uswapwep && uswapwep->otyp == CORPSE &&
1685 | 			touch_petrifies(&mons[uswapwep->corpsenm]) && !Stone_resistance){
1686 | 		pline("%s touch the %s corpse.", arg,
1687 | 		        mons[uswapwep->corpsenm].mname);
1688 | 		Sprintf(kbuf, "%s corpse", an(mons[uswapwep->corpsenm].mname));
1689 | 		instapetrify(kbuf);
1690 | 	}
1691 | }
1692 | 
1693 | void
1694 | mselftouch(mon,arg,byplayer)
1695 | struct monst *mon;
1696 | const char *arg;
1697 | boolean byplayer;
1698 | {
1699 | 	struct obj *mwep = MON_WEP(mon);
1700 | 
1701 | 	if (mwep && mwep->otyp == CORPSE && touch_petrifies(&mons[mwep->corpsenm])) {
1702 | 		if (cansee(mon->mx, mon->my)) {
1703 | 			pline("%s%s touches the %s corpse.",
1704 | 			    arg ? arg : "", arg ? mon_nam(mon) : Monnam(mon),
1705 | 			    mons[mwep->corpsenm].mname);
1706 | 		}
1707 | 		minstapetrify(mon, byplayer);
1708 | 	}
1709 | }
1710 | 
1711 | void
1712 | float_up()
1713 | {
1714 | 	if(u.utrap) {
1715 | 		if(u.utraptype == TT_PIT) {
1716 | 			u.utrap = 0;
1717 | 			You("float up, out of the pit!");
1718 | 			vision_full_recalc = 1;	/* vision limits change */
1719 | 			fill_pit(u.ux, u.uy);
1720 | 		} else if (u.utraptype == TT_INFLOOR) {
1721 | 			Your("body pulls upward, but your %s are still stuck.",
1722 | 			     makeplural(body_part(LEG)));
1723 | 		} else {
1724 | 			You("float up, only your %s is still stuck.",
1725 | 				body_part(LEG));
1726 | 		}
1727 | 	}
1728 | 	else if(Is_waterlevel(&u.uz))
1729 | 		pline("It feels as though you've lost some weight.");
1730 | 	else if(u.uinwater)
1731 | 		spoteffects(TRUE);
1732 | 	else if(u.uswallow)
1733 | 		You(is_animal(u.ustuck->data) ?
1734 | 			"float away from the %s."  :
1735 | 			"spiral up into %s.",
1736 | 		    is_animal(u.ustuck->data) ?
1737 | 			surface(u.ux, u.uy) :
1738 | 			mon_nam(u.ustuck));
1739 | 	else if (Hallucination)
1740 | 		pline("Up, up, and awaaaay!  You're walking on air!");
1741 | 	else if(Is_airlevel(&u.uz))
1742 | 		You("gain control over your movements.");
1743 | 	else
1744 | 		You("start to float in the air!");
1745 | #ifdef STEED
1746 | 	if (u.usteed && !is_floater(u.usteed->data) &&
1747 | 						!is_flyer(u.usteed->data)) {
1748 | 	    if (Lev_at_will)
1749 | 	    	pline("%s magically floats up!", Monnam(u.usteed));
1750 | 	    else {
1751 | 	    	You("cannot stay on %s.", mon_nam(u.usteed));
1752 | 	    	dismount_steed(DISMOUNT_GENERIC);
1753 | 	    }
1754 | 	}
1755 | #endif
1756 | 	return;
1757 | }
1758 | 
1759 | void
1760 | fill_pit(x, y)
1761 | int x, y;
1762 | {
1763 | 	struct obj *otmp;
1764 | 	struct trap *t;
1765 | 
1766 | 	if ((t = t_at(x, y)) &&
1767 | 	    ((t->ttyp == PIT) || (t->ttyp == SPIKED_PIT)) &&
1768 | 	    (otmp = sobj_at(BOULDER, x, y))) {
1769 | 		obj_extract_self(otmp);
1770 | 		(void) flooreffects(otmp, x, y, "settle");
1771 | 	}
1772 | }
1773 | 
1774 | int
1775 | float_down(hmask, emask)
1776 | long hmask, emask;     /* might cancel timeout */
1777 | {
1778 | 	register struct trap *trap = (struct trap *)0;
1779 | 	d_level current_dungeon_level;
1780 | 	boolean no_msg = FALSE;
1781 | 
1782 | 	HLevitation &= ~hmask;
1783 | 	ELevitation &= ~emask;
1784 | 	if(Levitation) return(0); /* maybe another ring/potion/boots */
1785 | 
1786 | 	if (Punished && !carried(uball) &&
1787 | 	    (is_pool(uball->ox, uball->oy) ||
1788 | 	     ((trap = t_at(uball->ox, uball->oy)) &&
1789 | 	      ((trap->ttyp == PIT) || (trap->ttyp == SPIKED_PIT) ||
1790 | 	       (trap->ttyp == TRAPDOOR) || (trap->ttyp == HOLE))))) {
1791 | 			u.ux0 = u.ux;
1792 | 			u.uy0 = u.uy;
1793 | 			u.ux = uball->ox;
1794 | 			u.uy = uball->oy;
1795 | 			movobj(uchain, uball->ox, uball->oy);
1796 | 			newsym(u.ux0, u.uy0);
1797 | 			vision_full_recalc = 1;	/* in case the hero moved. */
1798 | 	}
1799 | 	/* check for falling into pool - added by GAN 10/20/86 */
1800 | 	if(!Flying) {
1801 | 		/* kludge alert:
1802 | 		 * drown() and lava_effects() print various messages almost
1803 | 		 * every time they're called which conflict with the "fall
1804 | 		 * into" message below.  Thus, we want to avoid printing
1805 | 		 * confusing, duplicate or out-of-order messages.
1806 | 		 * Use knowledge of the two routines as a hack -- this
1807 | 		 * should really be handled differently -dlc
1808 | 		 */
1809 | 		if(is_pool(u.ux,u.uy) && !Wwalking && !Swimming && !u.uinwater)
1810 | 			no_msg = drown();
1811 | 
1812 | 		if(is_lava(u.ux,u.uy)) {
1813 | 			(void) lava_effects();
1814 | 			no_msg = TRUE;
1815 | 		}
1816 | 	}
1817 | 	if (!trap) {
1818 | 		trap = t_at(u.ux,u.uy);
1819 | 		if(Is_airlevel(&u.uz))
1820 | 			You("begin to tumble in place.");
1821 | 		else if (Is_waterlevel(&u.uz) && !no_msg)
1822 | 			You_feel("heavier.");
1823 | 		/* u.uinwater msgs already in spoteffects()/drown() */
1824 | 		else if (!u.uinwater && !no_msg) {
1825 | #ifdef STEED
1826 | 		    if (!(emask & W_SADDLE))
1827 | #endif
1828 | 		    {
1829 | 			boolean sokoban_trap = (In_sokoban(&u.uz) && trap);
1830 | 			if (Hallucination)
1831 | 				pline("Bummer!  You've %s.",
1832 | 				      is_pool(u.ux,u.uy) ?
1833 | 					"splashed down" : sokoban_trap ? "crashed" :
1834 | 					"hit the ground");
1835 | 			else {
1836 | 				if (!sokoban_trap)
1837 | 					You("float gently to the %s.",
1838 | 					    surface(u.ux, u.uy));
1839 | 				else {
1840 | 					/* Justification elsewhere for Sokoban traps
1841 | 					 * is based on air currents. This is
1842 | 					 * consistent with that.
1843 | 					 * The unexpected additional force of the
1844 | 					 * air currents once leviation
1845 | 					 * ceases knocks you off your feet.
1846 | 					 */
1847 | 					You("fall over.");
1848 | 		    			losehp(rnd(2), "wind swept", KILLED_BY);
1849 | #ifdef STEED
1850 | 		    			if (u.usteed) dismount_steed(DISMOUNT_FELL);
1851 | #endif
1852 | 					selftouch("As you fall, you");
1853 | 				}
1854 | 			}
1855 | 		    }
1856 | 		}
1857 | 	}
1858 | 
1859 | 	/* can't rely on u.uz0 for detecting trap door-induced level change;
1860 | 	   it gets changed to reflect the new level before we can check it */
1861 | 	assign_level(&current_dungeon_level, &u.uz);
1862 | 
1863 | 	if(trap)
1864 | 		switch(trap->ttyp) {
1865 | 		case STATUE_TRAP:
1866 | 			break;
1867 | 		case HOLE:
1868 | 		case TRAPDOOR:
1869 | 			if(!Can_fall_thru(&u.uz) || u.ustuck)
1870 | 				break;
1871 | 			/* fall into next case */
1872 | 		default:
1873 | 			dotrap(trap);
1874 | 	}
1875 | 
1876 | 	if (!Is_airlevel(&u.uz) && !Is_waterlevel(&u.uz) && !u.uswallow &&
1877 | 		/* falling through trap door calls goto_level,
1878 | 		   and goto_level does its own pickup() call */
1879 | 		on_level(&u.uz, &current_dungeon_level))
1880 | 	    (void) pickup(1);
1881 | 	return 1;
1882 | }
1883 | 
1884 | STATIC_OVL void
1885 | dofiretrap(box)
1886 | struct obj *box;	/* null for floor trap */
1887 | {
1888 | 	boolean see_it = !Blind;
1889 | 	int num;
1890 | 
1891 | /* Bug: for box case, the equivalent of burn_floor_paper() ought
1892 |  * to be done upon its contents.
1893 |  */
1894 | 
1895 | 	if ((box && !carried(box)) ? is_pool(box->ox, box->oy) : Underwater) {
1896 | 	    pline("A cascade of steamy bubbles erupts from %s!",
1897 | 		    the(box ? xname(box) : surface(u.ux,u.uy)));
1898 | 	    if (Fire_resistance) You("are uninjured.");
1899 | 	    else losehp(rnd(3), "boiling water", KILLED_BY);
1900 | 	    return;
1901 | 	}
1902 | 	pline("A %s %s from %s!", tower_of_flame,
1903 | 	      box ? "bursts" : "erupts",
1904 | 	      the(box ? xname(box) : surface(u.ux,u.uy)));
1905 | 	if (Fire_resistance) {
1906 | 	    shieldeff(u.ux, u.uy);
1907 | 	    num = rn2(2);
1908 | 	} else {
1909 | 	    num = d(2,4);
1910 | 	    if (u.uhpmax > u.ulevel)
1911 | 		u.uhpmax -= rn2(min(u.uhpmax,num + 1)), flags.botl = 1;
1912 | 	}
1913 | 	if (!num)
1914 | 	    You("are uninjured.");
1915 | 	else
1916 | 	    losehp(num, tower_of_flame, KILLED_BY_AN);
1917 | 	burn_away_slime();
1918 | 
1919 | 	if (burnarmor(&youmonst) || rn2(3)) {
1920 | 	    destroy_item(SCROLL_CLASS, AD_FIRE);
1921 | 	    destroy_item(SPBOOK_CLASS, AD_FIRE);
1922 | 	    destroy_item(POTION_CLASS, AD_FIRE);
1923 | 	}
1924 | 	if (!box && burn_floor_paper(u.ux, u.uy, see_it) && !see_it)
1925 | 	    You("smell paper burning.");
1926 | 	if (is_ice(u.ux, u.uy))
1927 | 	    melt_ice(u.ux, u.uy);
1928 | }
1929 | 
1930 | STATIC_OVL void
1931 | domagictrap()
1932 | {
1933 | 	register int fate = rnd(20);
1934 | 
1935 | 	/* What happened to the poor sucker? */
1936 | 
1937 | 	if (fate < 10) {
1938 | 	  /* Most of the time, it creates some monsters. */
1939 | 	  register int cnt = rnd(4);
1940 | 
1941 | 	  if (!resists_blnd(&youmonst)) {
1942 | 		You("are momentarily blinded by a flash of light!");
1943 | 		make_blinded((long)rn1(5,10),FALSE);
1944 | 	  } else if (!Blind) {
1945 | 		You("see a flash of light!");
1946 | 	  }  else
1947 | 		You_hear("a deafening roar!");
1948 | 	  while(cnt--)
1949 | 		(void) makemon((struct permonst *) 0, u.ux, u.uy, NO_MM_FLAGS);
1950 | 	}
1951 | 	else
1952 | 	  switch (fate) {
1953 | 
1954 | 	     case 10:
1955 | 	     case 11:
1956 | 		      /* sometimes nothing happens */
1957 | 			break;
1958 | 	     case 12: /* a flash of fire */
1959 | 			dofiretrap((struct obj *)0);
1960 | 			break;
1961 | 
1962 | 	     /* odd feelings */
1963 | 	     case 13:	pline("A shiver runs up and down your %s!",
1964 | 			      body_part(SPINE));
1965 | 			break;
1966 | 	     case 14:	You_hear(Hallucination ?
1967 | 				"the moon howling at you." :
1968 | 				"distant howling.");
1969 | 			break;
1970 | 	     case 15:	if (on_level(&u.uz, &qstart_level))
1971 | 			    You_feel("%slike the prodigal son.",
1972 | 			      (flags.female || (Upolyd && is_neuter(youmonst.data))) ?
1973 | 				     "oddly " : "");
1974 | 			else
1975 | 			    You("suddenly yearn for %s.",
1976 | 				Hallucination ? "Cleveland" :
1977 | 			    (In_quest(&u.uz) || at_dgn_entrance("The Quest")) ?
1978 | 						"your nearby homeland" :
1979 | 						"your distant homeland");
1980 | 			break;
1981 | 	     case 16:   Your("pack shakes violently!");
1982 | 			break;
1983 | 	     case 17:	You(Hallucination ?
1984 | 				"smell hamburgers." :
1985 | 				"smell charred flesh.");
1986 | 			break;
1987 | 	     case 18:	You_feel("tired.");
1988 | 			break;
1989 | 
1990 | 	     /* very occasionally something nice happens. */
1991 | 
1992 | 	     case 19:
1993 | 		    /* tame nearby monsters */
1994 | 		   {   register int i,j;
1995 | 		       register struct monst *mtmp;
1996 | 
1997 | 		       (void) adjattrib(A_CHA,1,FALSE);
1998 | 		       for(i = -1; i <= 1; i++) for(j = -1; j <= 1; j++) {
1999 | 			   if(!isok(u.ux+i, u.uy+j)) continue;
2000 | 			   mtmp = m_at(u.ux+i, u.uy+j);
2001 | 			   if(mtmp)
2002 | 			       (void) tamedog(mtmp, (struct obj *)0);
2003 | 		       }
2004 | 		       break;
2005 | 		   }
2006 | 
2007 | 	     case 20:
2008 | 		    /* uncurse stuff */
2009 | 		   {  register struct obj *obj;
2010 | 
2011 | 			/* below plines added by GAN 10/30/86 */
2012 | 			You_feel(Hallucination ?
2013 | 				"in touch with the Universal Oneness." :
2014 | 				"like someone is helping you.");
2015 | 			for(obj = invent; obj ; obj = obj->nobj)
2016 | 			       if(obj->owornmask || obj->otyp == LOADSTONE)
2017 | 					uncurse(obj);
2018 | 		       if(Punished) unpunish();
2019 | 		       break;
2020 | 		   }
2021 | 	     default: break;
2022 | 	  }
2023 | }
2024 | 
2025 | void
2026 | water_damage(obj, force, here)
2027 | register struct obj *obj;
2028 | register boolean force, here;
2029 | {
2030 | 	/* Scrolls, spellbooks, potions, weapons and
2031 | 	   pieces of armor may get affected by the water */
2032 | 	for (; obj; obj = (here ? obj->nexthere : obj->nobj)) {
2033 | 
2034 | 		(void) snuff_lit(obj);
2035 | 
2036 | 		if(obj->otyp == CAN_OF_GREASE && obj->spe > 0) {
2037 | 			continue;
2038 | 		} else if(obj->greased) {
2039 | 			if (force || !rn2(2)) obj->greased = 0;
2040 | 		} else if(Is_container(obj) && !Is_box(obj) &&
2041 | 			(obj->otyp != OILSKIN_SACK || (obj->cursed && !rn2(3)))) {
2042 | 			water_damage(obj->cobj, force, FALSE);
2043 | 		} else if (!force && (Luck + 5) > rn2(20)) {
2044 | 			/*  chance per item of sustaining damage:
2045 | 			 *	max luck (full moon):	 5%
2046 | 			 *	max luck (elsewhen):	10%
2047 | 			 *	avg luck (Luck==0):	75%
2048 | 			 *	awful luck (Luck<-4):  100%
2049 | 			 */
2050 | 			continue;
2051 | 		} else if (obj->oclass == SCROLL_CLASS) {
2052 | #ifdef MAIL
2053 | 		    if (obj->otyp != SCR_MAIL)
2054 | #endif
2055 | 		    {
2056 | 			obj->otyp = SCR_BLANK_PAPER;
2057 | 			obj->spe = 0;
2058 | 		    }
2059 | 		} else if (obj->oclass == SPBOOK_CLASS) {
2060 | 			if (obj->otyp == SPE_BOOK_OF_THE_DEAD)
2061 | 				pline("Steam rises from %s.", the(xname(obj)));
2062 | 			else obj->otyp = SPE_BLANK_PAPER;
2063 | 		} else if (obj->oclass == POTION_CLASS) {
2064 | 			if (obj->odiluted) {
2065 | 				obj->otyp = POT_WATER;
2066 | 				obj->blessed = obj->cursed = 0;
2067 | 				obj->odiluted = 0;
2068 | 			} else if (obj->otyp != POT_WATER)
2069 | 				obj->odiluted++;
2070 | 		} else if (is_rustprone(obj) && obj->oeroded < MAX_ERODE &&
2071 | 			  !(obj->oerodeproof || (obj->blessed && !rnl(4)))) {
2072 | 			/* all metal stuff and armor except (body armor
2073 | 			   protected by oilskin cloak) */
2074 | 			if(obj->oclass != ARMOR_CLASS || obj != uarm ||
2075 | 			   !uarmc || uarmc->otyp != OILSKIN_CLOAK ||
2076 | 			   (uarmc->cursed && !rn2(3)))
2077 | 				obj->oeroded++;
2078 | 		}
2079 | 	}
2080 | }
2081 | 
2082 | /*
2083 |  * This function is potentially expensive - rolling
2084 |  * inventory list multiple times.  Luckily it's seldom needed.
2085 |  * Returns TRUE if disrobing made player unencumbered enough to
2086 |  * crawl out of the current predicament.
2087 |  */
2088 | STATIC_OVL boolean
2089 | emergency_disrobe(lostsome)
2090 | boolean *lostsome;
2091 | {
2092 | 	int invc = inv_cnt();
2093 | 
2094 | 
2095 | 	while (near_capacity() > (Punished ? UNENCUMBERED : SLT_ENCUMBER)) {
2096 | 		register struct obj *obj, *otmp = (struct obj *)0;
2097 | 		register int i;
2098 | 
2099 | 		/* Pick a random object */
2100 | 		if (invc > 0) {
2101 | 			i = rn2(invc);
2102 | 		for (obj = invent; obj; obj = obj->nobj) {
2103 | 			/*
2104 | 			 * Undroppables are: body armor, boots, gloves,
2105 | 			 * amulets, and rings because of the time and effort
2106 | 			 * in removing them + loadstone and other cursed stuff
2107 | 			 * for obvious reasons.
2108 | 			 */
2109 | 			if (!((obj->otyp == LOADSTONE && obj->cursed) ||
2110 | 			      obj == uamul || obj == uleft || obj == uright ||
2111 | 			      obj == ublindf || obj == uarm || obj == uarmc ||
2112 | 			      obj == uarmg || obj == uarmf ||
2113 | #ifdef TOURIST
2114 | 			      obj == uarmu ||
2115 | #endif
2116 | 			      (obj->cursed && (obj == uarmh || obj == uarms)) ||
2117 | 			      welded(obj)))
2118 | 				otmp = obj;
2119 | 			/* reached the mark and found some stuff to drop? */
2120 | 			if (--i < 0 && otmp) break;
2121 | 
2122 | 			/* else continue */
2123 | 		}
2124 | 		}
2125 | 
2126 | 		if (!otmp) {
2127 | 			/* Nothing available left to drop; try gold */
2128 | 			if (u.ugold) {
2129 | 				pline("In desperation, you drop your purse.");
2130 | 				/* Hack: gold is not in the inventory, so make a gold object
2131 | 				 * and put it at the head of the inventory list.
2132 | 				 */
2133 | 				obj = mkgoldobj(u.ugold);    /* removes from u.ugold */
2134 | 				u.ugold = obj->quan;         /* put the gold back */
2135 | 				assigninvlet(obj);           /* might end up as NOINVSYM */
2136 | 				obj->nobj = invent;
2137 | 				invent = obj;
2138 | 				dropx(obj);
2139 | 				continue;                    /* Try again */
2140 | 			}
2141 | 			/* We can't even drop gold! */
2142 | 			return (FALSE);
2143 | 		}
2144 | 
2145 | 		if (otmp == uarmh) (void) Helmet_off();
2146 | 		else if (otmp == uarms) (void) Shield_off();
2147 | 		else if (otmp == uwep) setuwep((struct obj *)0);
2148 | 		*lostsome = TRUE;
2149 | 		dropx(otmp);
2150 | 		invc--;
2151 | 	}
2152 | 	return(TRUE);
2153 | }
2154 | 
2155 | /*
2156 |  *  return(TRUE) == player relocated
2157 |  */
2158 | boolean
2159 | drown()
2160 | {
2161 | 	boolean inpool_ok = FALSE, crawl_ok;
2162 | 	int i, x, y;
2163 | 
2164 | 	/* happily wading in the same contiguous pool */
2165 | 	if (u.uinwater && is_pool(u.ux-u.dx,u.uy-u.dy) &&
2166 | 	    (Swimming || Amphibious)) {
2167 | 		/* water effects on objects every now and then */
2168 | 		if (!rn2(5)) inpool_ok = TRUE;
2169 | 		else return(FALSE);
2170 | 	}
2171 | 
2172 | 	if (!u.uinwater) {
2173 | 	    You("%s into the water%c",
2174 | 		Is_waterlevel(&u.uz) ? "plunge" : "fall",
2175 | 		Amphibious || Swimming ? '.' : '!');
2176 | 	    if (!Swimming && !Is_waterlevel(&u.uz))
2177 | 		    You("sink like %s.",
2178 | 			Hallucination ? "the Titanic" : "a rock");
2179 | 	}
2180 | 
2181 | 	water_damage(invent, FALSE, FALSE);
2182 | 
2183 | 	if (u.umonnum == PM_GREMLIN && rn2(3))
2184 | 	    (void)split_mon(&youmonst, (struct monst *)0);
2185 | 	else if (u.umonnum == PM_IRON_GOLEM) {
2186 | 	    You("rust!");
2187 | 	    i = d(2,6);
2188 | 	    if (u.mhmax > i) u.mhmax -= i;
2189 | 	    losehp(i, "rusting away", KILLED_BY);
2190 | 	}
2191 | 	if (inpool_ok) return(FALSE);
2192 | 
2193 | 	if ((i = number_leashed()) > 0) {
2194 | 		pline_The("leash%s slip%s loose.",
2195 | 			(i > 1) ? "es" : "",
2196 | 			(i > 1) ? "" : "s");
2197 | 		unleash_all();
2198 | 	}
2199 | 
2200 | 	if (Amphibious || Swimming) {
2201 | 		if (Amphibious) {
2202 | 			if (flags.verbose)
2203 | 				pline("But you aren't drowning.");
2204 | 			if (!Is_waterlevel(&u.uz)) {
2205 | 				if (Hallucination)
2206 | 					Your("keel hits the bottom.");
2207 | 				else
2208 | 					You("touch bottom.");
2209 | 			}
2210 | 		}
2211 | 		if (Punished) {
2212 | 			unplacebc();
2213 | 			placebc();
2214 | 		}
2215 | 		vision_recalc(2);	/* unsee old position */
2216 | 		u.uinwater = 1;
2217 | 		under_water(1);
2218 | 		vision_full_recalc = 1;
2219 | 		return(FALSE);
2220 | 	}
2221 | 	if((Teleportation || can_teleport(youmonst.data)) &&
2222 | 	   (Teleport_control || rn2(3) < Luck+2)) {
2223 | 		You("attempt a teleport spell.");	/* utcsri!carroll */
2224 | 		(void) dotele();
2225 | 		if(!is_pool(u.ux,u.uy))
2226 | 			return(TRUE);
2227 | 	}
2228 | #ifdef STEED
2229 | 	if (u.usteed) {
2230 | 		dismount_steed(DISMOUNT_GENERIC);
2231 | 		if(!is_pool(u.ux,u.uy))
2232 | 			return(TRUE);
2233 | 	}
2234 | #endif
2235 | 	crawl_ok = FALSE;
2236 | 	/* look around for a place to crawl to */
2237 | 	for (i = 0; i < 100; i++) {
2238 | 		x = rn1(3,u.ux - 1);
2239 | 		y = rn1(3,u.uy - 1);
2240 | 		if (goodpos(x, y, &youmonst)) {
2241 | 			crawl_ok = TRUE;
2242 | 			goto crawl;
2243 | 		}
2244 | 	}
2245 | 	/* one more scan */
2246 | 	for (x = u.ux - 1; x <= u.ux + 1; x++)
2247 | 		for (y = u.uy - 1; y <= u.uy + 1; y++)
2248 | 			if (goodpos(x, y, &youmonst)) {
2249 | 				crawl_ok = TRUE;
2250 | 				goto crawl;
2251 | 			}
2252 | crawl:;
2253 | 	if (crawl_ok) {
2254 | 		boolean lost = FALSE;
2255 | 		/* time to do some strip-tease... */
2256 | 		boolean succ = Is_waterlevel(&u.uz) ? TRUE :
2257 | 				emergency_disrobe(&lost);
2258 | 
2259 | 		You("try to crawl out of the water.");
2260 | 		if (lost)
2261 | 			You("dump some of your gear to lose weight...");
2262 | 		if (succ) {
2263 | 			pline("Pheew!  That was close.");
2264 | 			teleds(x,y);
2265 | 			return(TRUE);
2266 | 		}
2267 | 		/* still too much weight */
2268 | 		pline("But in vain.");
2269 | 	}
2270 | 	u.uinwater = 1;
2271 | 	You("drown.");
2272 | 	killer_format = KILLED_BY_AN;
2273 | 	killer = (levl[u.ux][u.uy].typ == POOL || Is_medusa_level(&u.uz)) ?
2274 | 	    "pool of water" : "moat";
2275 | 	done(DROWNING);
2276 | 	/* oops, we're still alive.  better get out of the water. */
2277 | 	while (!safe_teleds()) {
2278 | 		pline("You're still drowning.");
2279 | 		done(DROWNING);
2280 | 	}
2281 | 	u.uinwater = 0;
2282 | 	You("find yourself back %s.", Is_waterlevel(&u.uz) ?
2283 | 		"in an air bubble" : "on land");
2284 | 	return(TRUE);
2285 | }
2286 | 
2287 | void
2288 | drain_en(n)
2289 | register int n;
2290 | {
2291 | 	if (!u.uenmax) return;
2292 | 	You_feel("your magical energy drain away!");
2293 | 	u.uen -= n;
2294 | 	if(u.uen < 0)  {
2295 | 		u.uenmax += u.uen;
2296 | 		if(u.uenmax < 0) u.uenmax = 0;
2297 | 		u.uen = 0;
2298 | 	}
2299 | 	flags.botl = 1;
2300 | }
2301 | 
2302 | int
2303 | dountrap()	/* disarm a trap */
2304 | {
2305 | 	if (near_capacity() >= HVY_ENCUMBER) {
2306 | 	    pline("You're too strained to do that.");
2307 | 	    return 0;
2308 | 	}
2309 | 	if (nohands(youmonst.data) || !youmonst.data->mmove) {
2310 | 	    pline("And just how do you expect to do that?");
2311 | 	    return 0;
2312 | 	} else if (u.ustuck && sticks(youmonst.data)) {
2313 | 	    pline("You'll have to let go of %s first.", mon_nam(u.ustuck));
2314 | 	    return 0;
2315 | 	}
2316 | 	if (u.ustuck || (welded(uwep) && bimanual(uwep))) {
2317 | 	    Your("%s seem to be too busy for that.",
2318 | 		 makeplural(body_part(HAND)));
2319 | 	    return 0;
2320 | 	}
2321 | 	return untrap(FALSE);
2322 | }
2323 | #endif /* OVLB */
2324 | #ifdef OVL2
2325 | 
2326 | /* Probability of disabling a trap.  Helge Hafting */
2327 | STATIC_OVL int
2328 | untrap_prob(ttmp)
2329 | struct trap *ttmp;
2330 | {
2331 | 	int chance = 3;
2332 | 
2333 | 	if (Confusion || Hallucination) chance++;
2334 | 	if (Blind) chance++;
2335 | 	if (Stunned) chance += 2;
2336 | 	if (Fumbling) chance *= 2;
2337 | 	/* Your own traps are better known than others. */
2338 | 	if (ttmp && ttmp->madeby_u) chance--;
2339 | 	if (Role_if(PM_ROGUE)) {
2340 | 	    if (rn2(2 * MAXULEV) < u.ulevel) chance--;
2341 | 	    if (u.uhave.questart && chance > 1) chance--;
2342 | 	} else if (Role_if(PM_RANGER)) chance--;
2343 | 	return rn2(chance);
2344 | }
2345 | 
2346 | /* Replace trap with object(s).  Helge Hafting */
2347 | STATIC_OVL void
2348 | cnv_trap_obj(otyp, cnt, ttmp)
2349 | int otyp;
2350 | int cnt;
2351 | struct trap *ttmp;
2352 | {
2353 | 	struct obj *otmp = mksobj(otyp, TRUE, FALSE);
2354 | 	otmp->quan=cnt;
2355 | 	otmp->owt = weight(otmp);
2356 | 	place_object(otmp, ttmp->tx, ttmp->ty);
2357 | 	/* Sell your own traps only... */
2358 | 	if (ttmp->madeby_u) sellobj(otmp, ttmp->tx, ttmp->ty);
2359 | 	stackobj(otmp);
2360 | 	newsym(ttmp->tx, ttmp->ty);
2361 | 	deltrap(ttmp);
2362 | }
2363 | 
2364 | /* while attempting to disarm an adjacent trap, we've fallen into it */
2365 | STATIC_OVL void
2366 | move_into_trap(ttmp)
2367 | struct trap *ttmp;
2368 | {
2369 | 	int bc;
2370 | 	xchar x = ttmp->tx, y = ttmp->ty, bx, by, cx, cy;
2371 | 	boolean unused;
2372 | 
2373 | 	/* we know there's no monster in the way, and we're not trapped */
2374 | 	if (!Punished || drag_ball(x, y, &bc, &bx, &by, &cx, &cy, &unused)) {
2375 | 	    u.ux0 = u.ux,  u.uy0 = u.uy;
2376 | 	    u.ux = x,  u.uy = y;
2377 | 	    u.umoved = TRUE;
2378 | 	    newsym(u.ux0, u.uy0);
2379 | 	    vision_recalc(1);
2380 | 	    check_leash(u.ux0, u.uy0);
2381 | 	    if (Punished) move_bc(0, bc, bx, by, cx, cy);
2382 | 	    spoteffects(TRUE);	/* dotrap() */
2383 | 	    exercise(A_WIS, FALSE);
2384 | 	}
2385 | }
2386 | 
2387 | /* 0: doesn't even try
2388 |  * 1: tries and fails
2389 |  * 2: succeeds
2390 |  */
2391 | STATIC_OVL int
2392 | try_disarm(ttmp, force_failure)
2393 | struct trap *ttmp;
2394 | boolean force_failure;
2395 | {
2396 | 	struct monst *mtmp = m_at(ttmp->tx,ttmp->ty);
2397 | 	int ttype = ttmp->ttyp;
2398 | 	boolean under_u = (!u.dx && !u.dy);
2399 | 
2400 | 	/* Test for monster first, monsters are displayed instead of trap. */
2401 | 	if (mtmp && (!mtmp->mtrapped || ttype != BEAR_TRAP)) {
2402 | 		pline("%s is in the way.", Monnam(mtmp));
2403 | 		return 0;
2404 | 	}
2405 | 	/* We might be forced to move onto the trap's location. */
2406 | 	if (sobj_at(BOULDER, ttmp->tx, ttmp->ty)
2407 | 				&& !Passes_walls && !under_u) {
2408 | 		There("is a boulder in your way.");
2409 | 		return 0;
2410 | 	}
2411 | 	/* untrappable traps are located on the ground. */
2412 | 	if (!can_reach_floor()) {
2413 | #ifdef STEED
2414 | 		if (u.usteed && P_SKILL(P_RIDING) < P_BASIC)
2415 | 			You("aren't skilled enough to reach from %s.",
2416 | 				mon_nam(u.usteed));
2417 | 		else
2418 | #endif
2419 | 		You("are unable to reach the %s!",
2420 | 			defsyms[trap_to_defsym(ttype)].explanation);
2421 | 		return 0;
2422 | 	}
2423 | 
2424 | 	/* Will our hero succeed? */
2425 | 	if (force_failure || untrap_prob(ttmp)) {
2426 | 		if (rnl(5)) {
2427 | 		    pline("Whoops...");
2428 | 		    if (mtmp) {		/* must be a bear trap */
2429 | 			if (mtmp->mtame) abuse_dog(mtmp);
2430 | 			if ((mtmp->mhp -= rnd(4)) <= 0) killed(mtmp);
2431 | 		    } else if (under_u) {
2432 | 			dotrap(ttmp);
2433 | 		    } else {
2434 | 			move_into_trap(ttmp);
2435 | 		    }
2436 | 		} else {
2437 | 		    pline("%s %s is difficult to disarm.",
2438 | 			  ttmp->madeby_u ? "Your" : under_u ? "This" : "That",
2439 | 			  defsyms[trap_to_defsym(ttype)].explanation);
2440 | 		}
2441 | 		return 1;
2442 | 	}
2443 | 	return 2;
2444 | }
2445 | 
2446 | STATIC_OVL void
2447 | reward_untrap(ttmp, mtmp)
2448 | struct trap *ttmp;
2449 | struct monst *mtmp;
2450 | {
2451 | 	if (!ttmp->madeby_u) {
2452 | 		if (rnl(10)<8 && !mtmp->mpeaceful &&
2453 | 						mtmp->data->mlet != S_HUMAN) {
2454 | 			mtmp->mpeaceful = 1;
2455 | 			set_malign(mtmp);	/* reset alignment */
2456 | 			pline("%s is grateful.", Monnam(mtmp));
2457 | 		}
2458 | 		/* Helping someone out of a trap is a nice thing to do,
2459 | 		 * A lawful may be rewarded, but not too often.  */
2460 | 		if (!rn2(3) && !rnl(8) && u.ualign.type == A_LAWFUL) {
2461 | 			adjalign(1);
2462 | 			You_feel("that you did the right thing.");
2463 | 		}
2464 | 	}
2465 | }
2466 | 
2467 | STATIC_OVL int
2468 | disarm_beartrap(ttmp) /* Helge Hafting */
2469 | struct trap *ttmp;
2470 | {
2471 | 	struct monst *mtmp;
2472 | 	int fails = try_disarm(ttmp, FALSE);
2473 | 
2474 | 	if (fails < 2) return fails;
2475 | 
2476 | 	/* ok, disarm it. */
2477 | 
2478 | 	/* untrap the monster, if any.
2479 | 	   There's no need for a cockatrice test, only the trap is touched */
2480 | 	if ((mtmp = m_at(ttmp->tx,ttmp->ty)) != 0) {
2481 | 		mtmp->mtrapped = 0;
2482 | 		You("remove %s bear trap from %s.", the_your[ttmp->madeby_u],
2483 | 			mon_nam(mtmp));
2484 | 		reward_untrap(ttmp, mtmp);
2485 | 	} else You("disarm %s bear trap.", the_your[ttmp->madeby_u]);
2486 | 	cnv_trap_obj(BEARTRAP, 1, ttmp);
2487 | 	return 1;
2488 | }
2489 | 
2490 | STATIC_OVL int
2491 | disarm_landmine(ttmp) /* Helge Hafting */
2492 | struct trap *ttmp;
2493 | {
2494 | 	int fails = try_disarm(ttmp, FALSE);
2495 | 
2496 | 	if (fails < 2) return fails;
2497 | 	You("disarm %s land mine.", the_your[ttmp->madeby_u]);
2498 | 	cnv_trap_obj(LAND_MINE, 1, ttmp);
2499 | 	return 1;
2500 | }
2501 | 
2502 | /* getobj will filter down to cans of grease and known potions of oil */
2503 | static NEARDATA const char oil[] = { ALL_CLASSES, TOOL_CLASS, POTION_CLASS, 0 };
2504 | 
2505 | /* it may not make much sense to use grease on floor boards, but so what? */
2506 | STATIC_OVL int
2507 | disarm_squeaky_board(ttmp)
2508 | struct trap *ttmp;
2509 | {
2510 | 	struct obj *obj;
2511 | 	boolean bad_tool;
2512 | 	int fails;
2513 | 
2514 | 	obj = getobj(oil, "untrap with");
2515 | 	if (!obj) return 0;
2516 | 
2517 | 	bad_tool = (obj->cursed ||
2518 | 			((obj->otyp != POT_OIL || obj->lamplit) &&
2519 | 			 (obj->otyp != CAN_OF_GREASE || !obj->spe)));
2520 | 
2521 | 	fails = try_disarm(ttmp, bad_tool);
2522 | 	if (fails < 2) return fails;
2523 | 
2524 | 	/* successfully used oil or grease to fix squeaky board */
2525 | 	if (obj->otyp == CAN_OF_GREASE) {
2526 | 	    check_unpaid(obj);
2527 | 	    obj->spe--;
2528 | 	} else {
2529 | 	    useup(obj);	/* oil */
2530 | 	    makeknown(POT_OIL);
2531 | 	}
2532 | 	You("repair the squeaky board.");	/* no madeby_u */
2533 | 	deltrap(ttmp);
2534 | 	newsym(u.ux + u.dx, u.uy + u.dy);
2535 | 	more_experienced(1, 5);
2536 | 	return 1;
2537 | }
2538 | 
2539 | /* removes traps that shoot arrows, darts, etc. */
2540 | STATIC_OVL int
2541 | disarm_shooting_trap(ttmp, otyp)
2542 | struct trap *ttmp;
2543 | int otyp;
2544 | {
2545 | 	int fails = try_disarm(ttmp, FALSE);
2546 | 
2547 | 	if (fails < 2) return fails;
2548 | 	You("disarm %s trap.", the_your[ttmp->madeby_u]);
2549 | 	cnv_trap_obj(otyp, 50-rnl(50), ttmp);
2550 | 	return 1;
2551 | }
2552 | 
2553 | /* Is the weight too heavy?
2554 |  * Formula as in near_capacity() & check_capacity() */
2555 | STATIC_OVL int
2556 | try_lift(mtmp, ttmp, wt, stuff)
2557 | struct monst *mtmp;
2558 | struct trap *ttmp;
2559 | int wt;
2560 | boolean stuff;
2561 | {
2562 | 	int wc = weight_cap();
2563 | 
2564 | 	if ((((wt<<1) / wc)+1) >= EXT_ENCUMBER) {
2565 | 		pline("%s is %s for you to lift.", Monnam(mtmp),
2566 | 			stuff ? "carrying too much" : "too heavy");
2567 | 		if (!ttmp->madeby_u && !mtmp->mpeaceful
2568 | 			&& mtmp->data->mlet != S_HUMAN && rnl(10) < 3) {
2569 | 		    mtmp->mpeaceful = 1;
2570 | 		    set_malign(mtmp);		/* reset alignment */
2571 | 		    pline("%s thinks it was nice of you to try.", Monnam(mtmp));
2572 | 		}
2573 | 		return 0;
2574 | 	}
2575 | 	return 1;
2576 | }
2577 | 
2578 | /* Help trapped monster (out of a (spiked) pit) */
2579 | STATIC_OVL int
2580 | help_monster_out(mtmp, ttmp)
2581 | struct monst *mtmp;
2582 | struct trap *ttmp;
2583 | {
2584 | 	int wt;
2585 | 	struct obj *otmp;
2586 | 
2587 | 	/*
2588 | 	 * This works when levitating too -- consistent with the ability
2589 | 	 * to hit monsters while levitating.
2590 | 	 *
2591 | 	 * Should perhaps check that our hero has arms/hands at the
2592 | 	 * moment.  Helping can also be done by engulfing...
2593 | 	 *
2594 | 	 * Test the monster first - monsters are displayed before traps.
2595 | 	 */
2596 | 	if (!mtmp->mtrapped) {
2597 | 		pline("%s isn't trapped.", Monnam(mtmp));
2598 | 		return 0;
2599 | 	}
2600 | 	/* Do you have the necessary capacity to lift anything? */
2601 | 	if (check_capacity((char *)0)) return 1;
2602 | 
2603 | 	/* Will our hero succeed? */
2604 | 	if (untrap_prob(ttmp)) {
2605 | 		You("try to reach out your %s, but %s backs away skeptically.",
2606 | 			makeplural(body_part(ARM)),
2607 | 			mon_nam(mtmp));
2608 | 		return 1;
2609 | 	}
2610 | 
2611 | 
2612 | 	/* is it a cockatrice?... */
2613 | 	if (touch_petrifies(mtmp->data) && !uarmg && !Stone_resistance) {
2614 | 		You("grab the trapped %s using your bare %s.",
2615 | 				mtmp->data->mname, makeplural(body_part(HAND)));
2616 | 
2617 | 		if (poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))
2618 | 			display_nhwindow(WIN_MESSAGE, FALSE);
2619 | 		else {
2620 | 			char kbuf[BUFSZ];
2621 | 
2622 | 			Sprintf(kbuf, "trying to help %s out of a pit",
2623 | 					an(mtmp->data->mname));
2624 | 			instapetrify(kbuf);
2625 | 			return 1;
2626 | 		}
2627 | 	}
2628 | 	You("reach out your %s and grab %s.",
2629 | 	    makeplural(body_part(ARM)), mon_nam(mtmp));
2630 | 
2631 | 	/* is the monster too heavy? */
2632 | 	wt = inv_weight() + mtmp->data->cwt;
2633 | 	if (!try_lift(mtmp, ttmp, wt, FALSE)) return 1;
2634 | 
2635 | 	/* is the monster with inventory too heavy? */
2636 | 	for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
2637 | 		wt += otmp->owt;
2638 | 	if (!try_lift(mtmp, ttmp, wt, TRUE)) return 1;
2639 | 
2640 | 	You("pull %s out of the pit.", mon_nam(mtmp));
2641 | 	mtmp->mtrapped = 0;
2642 | 	fill_pit(mtmp->mx, mtmp->my);
2643 | 	reward_untrap(ttmp, mtmp);
2644 | 	return 1;
2645 | }
2646 | 
2647 | int
2648 | untrap(force)
2649 | boolean force;
2650 | {
2651 | 	register struct obj *otmp;
2652 | 	register boolean confused = (Confusion > 0 || Hallucination > 0);
2653 | 	register int x,y;
2654 | 	int ch;
2655 | 	struct trap *ttmp;
2656 | 	struct monst *mtmp;
2657 | 	boolean trap_skipped = FALSE;
2658 | 
2659 | 	if(!getdir((char *)0)) return(0);
2660 | 	x = u.ux + u.dx;
2661 | 	y = u.uy + u.dy;
2662 | 
2663 | 	if ((ttmp = t_at(x,y)) && ttmp->tseen) {
2664 | 		if (u.utrap) {
2665 | 			You("cannot deal with traps while trapped!");
2666 | 			return 1;
2667 | 		}
2668 | 		switch(ttmp->ttyp) {
2669 | 			case BEAR_TRAP:
2670 | 				return disarm_beartrap(ttmp);
2671 | 			case LANDMINE:
2672 | 				return disarm_landmine(ttmp);
2673 | 			case SQKY_BOARD:
2674 | 				return disarm_squeaky_board(ttmp);
2675 | 			case DART_TRAP:
2676 | 				return disarm_shooting_trap(ttmp, DART);
2677 | 			case ARROW_TRAP:
2678 | 				return disarm_shooting_trap(ttmp, ARROW);
2679 | 			case PIT:
2680 | 			case SPIKED_PIT:
2681 | 				if (!u.dx && !u.dy) {
2682 | 				    You("are already on the edge of the pit.");
2683 | 				    return 0;
2684 | 				}
2685 | 				if (!(mtmp = m_at(x,y))) {
2686 | 				    pline("Try filling the pit instead.");
2687 | 				    return 0;
2688 | 				}
2689 | 				return help_monster_out(mtmp, ttmp);
2690 | 			default:
2691 | 				You("cannot disable %s trap.", (u.dx || u.dy) ? "that" : "this");
2692 | 				return 0;
2693 | 		} /* end switch */
2694 | 	} /* end if */
2695 | 
2696 | 	if(!u.dx && !u.dy) {
2697 | 	    for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
2698 | 		if(Is_box(otmp)) {
2699 | 		    There("is %s here.", doname(otmp));
2700 | 
2701 | 		    switch (ynq("Check for traps?")) {
2702 | 			case 'q': return(0);
2703 | 			case 'n': continue;
2704 | 		    }
2705 | 
2706 | 		    if((otmp->otrapped && (force || (!confused
2707 | 				&& rn2(MAXULEV + 1 - u.ulevel) < 10)))
2708 | 		       || (!force && confused && !rn2(3))) {
2709 | 			You("find a trap on %s!", the(xname(otmp)));
2710 | 			exercise(A_WIS, TRUE);
2711 | 
2712 | 			switch (ynq("Disarm it?")) {
2713 | 			    case 'q': return(1);
2714 | 			    case 'n': trap_skipped = TRUE;  continue;
2715 | 			}
2716 | 
2717 | 			if(otmp->otrapped) {
2718 | 			    exercise(A_DEX, TRUE);
2719 | 			    ch = ACURR(A_DEX) + u.ulevel;
2720 | 			    if (Role_if(PM_ROGUE)) ch *= 2;
2721 | 			    if(!force && (confused || Fumbling ||
2722 | 				rnd(75+level_difficulty()/2) > ch)) {
2723 | 				(void) chest_trap(otmp, FINGER, TRUE);
2724 | 			    } else {
2725 | 				You("disarm it!");
2726 | 				otmp->otrapped = 0;
2727 | 			    }
2728 | 			} else pline("That %s was not trapped.", xname(otmp));
2729 | 			return(1);
2730 | 		    } else {
2731 | 			You("find no traps on %s.", the(xname(otmp)));
2732 | 			return(1);
2733 | 		    }
2734 | 		}
2735 | 
2736 | 	    You(trap_skipped ? "find no other traps here."
2737 | 			     : "know of no traps here.");
2738 | 	    return(0);
2739 | 	}
2740 | 
2741 | 	if ((mtmp = m_at(x,y))				&&
2742 | 		mtmp->m_ap_type == M_AP_FURNITURE	&&
2743 | 		(mtmp->mappearance == S_hcdoor ||
2744 | 			mtmp->mappearance == S_vcdoor)	&&
2745 | 		!Protection_from_shape_changers)	 {
2746 | 
2747 | 	    stumble_onto_mimic(mtmp);
2748 | 	    return(1);
2749 | 	}
2750 | 
2751 | 	if (!IS_DOOR(levl[x][y].typ)) {
2752 | 	    if ((ttmp = t_at(x,y)) && ttmp->tseen)
2753 | 		You("cannot disable that trap.");
2754 | 	    else
2755 | 		You("know of no traps there.");
2756 | 	    return(0);
2757 | 	}
2758 | 
2759 | 	switch (levl[x][y].doormask) {
2760 | 	    case D_NODOOR:
2761 | 		You("%s no door there.", Blind ? "feel" : "see");
2762 | 		return(0);
2763 | 	    case D_ISOPEN:
2764 | 		pline("This door is safely open.");
2765 | 		return(0);
2766 | 	    case D_BROKEN:
2767 | 		pline("This door is broken.");
2768 | 		return(0);
2769 | 	}
2770 | 
2771 | 	if ((levl[x][y].doormask & D_TRAPPED
2772 | 	     && (force ||
2773 | 		 (!confused && rn2(MAXULEV - u.ulevel + 11) < 10)))
2774 | 	    || (!force && confused && !rn2(3))) {
2775 | 		You("find a trap on the door!");
2776 | 		exercise(A_WIS, TRUE);
2777 | 		if (ynq("Disarm it?") != 'y') return(1);
2778 | 		if (levl[x][y].doormask & D_TRAPPED) {
2779 | 		    ch = 15 + (Role_if(PM_ROGUE) ? u.ulevel*3 : u.ulevel);
2780 | 		    exercise(A_DEX, TRUE);
2781 | 		    if(!force && (confused || Fumbling ||
2782 | 				     rnd(75+level_difficulty()/2) > ch)) {
2783 | 			You("set it off!");
2784 | 			b_trapped("door", FINGER);
2785 | 			levl[x][y].doormask = D_NODOOR;
2786 | 			unblock_point(x, y);
2787 | 			newsym(x, y);
2788 | 			/* (probably ought to charge for this damage...) */
2789 | 			if (*in_rooms(x, y, SHOPBASE)) add_damage(x, y, 0L);
2790 | 		    } else {
2791 | 			You("disarm it!");
2792 | 			levl[x][y].doormask &= ~D_TRAPPED;
2793 | 		    }
2794 | 		} else pline("This door was not trapped.");
2795 | 		return(1);
2796 | 	} else {
2797 | 		You("find no traps on the door.");
2798 | 		return(1);
2799 | 	}
2800 | }
2801 | #endif /* OVL2 */
2802 | #ifdef OVLB
2803 | 
2804 | /* only called when the player is doing something to the chest directly */
2805 | boolean
2806 | chest_trap(obj, bodypart, disarm)
2807 | register struct obj *obj;
2808 | register int bodypart;
2809 | boolean disarm;
2810 | {
2811 | 	register struct obj *otmp = obj, *otmp2;
2812 | 	char	buf[80];
2813 | 	const char *msg;
2814 | 	coord cc;
2815 | 
2816 | 	if (get_obj_location(obj, &cc.x, &cc.y, 0))	/* might be carried */
2817 | 	    obj->ox = cc.x,  obj->oy = cc.y;
2818 | 
2819 | 	otmp->otrapped = 0;	/* trap is one-shot; clear flag first in case
2820 | 				   chest kills you and ends up in bones file */
2821 | 	You(disarm ? "set it off!" : "trigger a trap!");
2822 | 	display_nhwindow(WIN_MESSAGE, FALSE);
2823 | 	if (Luck > -13 && rn2(13+Luck) > 7) {	/* saved by luck */
2824 | 	    /* trap went off, but good luck prevents damage */
2825 | 	    switch (rn2(13)) {
2826 | 		case 12:
2827 | 		case 11:  msg = "explosive charge is a dud";  break;
2828 | 		case 10:
2829 | 		case  9:  msg = "electric charge is grounded";  break;
2830 | 		case  8:
2831 | 		case  7:  msg = "flame fizzles out";  break;
2832 | 		case  6:
2833 | 		case  5:
2834 | 		case  4:  msg = "poisoned needle misses";  break;
2835 | 		case  3:
2836 | 		case  2:
2837 | 		case  1:
2838 | 		case  0:  msg = "gas cloud blows away";  break;
2839 | 		default:  impossible("chest disarm bug");  msg = (char *)0;
2840 | 			  break;
2841 | 	    }
2842 | 	    if (msg) pline("But luckily the %s!", msg);
2843 | 	} else {
2844 | 	    switch(rn2(20) ? ((Luck >= 13) ? 0 : rn2(13-Luck)) : rn2(26)) {
2845 | 		case 25:
2846 | 		case 24:
2847 | 		case 23:
2848 | 		case 22:
2849 | 		case 21: {
2850 | 			  struct monst *shkp = 0;
2851 | 			  long loss = 0L;
2852 | 			  boolean costly, insider;
2853 | 			  register xchar ox = obj->ox, oy = obj->oy;
2854 | 
2855 | 			  /* the obj location need not be that of player */
2856 | 			  costly = (costly_spot(ox, oy) &&
2857 | 				   (shkp = shop_keeper(*in_rooms(ox, oy,
2858 | 				    SHOPBASE))) != (struct monst *)0);
2859 | 			  insider = (*u.ushops && inside_shop(u.ux, u.uy) &&
2860 | 				    *in_rooms(ox, oy, SHOPBASE) == *u.ushops);
2861 | 
2862 | 			  pline("%s explodes!", The(xname(obj)));
2863 | 			  Sprintf(buf, "exploding %s", xname(obj));
2864 | 
2865 | 			  if(costly)
2866 | 			      loss += stolen_value(obj, ox, oy,
2867 | 						(boolean)shkp->mpeaceful, TRUE);
2868 | 			  delete_contents(obj);
2869 | 			  for(otmp = level.objects[u.ux][u.uy];
2870 | 							otmp; otmp = otmp2) {
2871 | 			      otmp2 = otmp->nexthere;
2872 | 			      if(costly)
2873 | 				  loss += stolen_value(otmp, otmp->ox,
2874 | 					  otmp->oy, (boolean)shkp->mpeaceful,
2875 | 					  TRUE);
2876 | 			      delobj(otmp);
2877 | 			  }
2878 | 			  wake_nearby();
2879 | 			  losehp(d(6,6), buf, KILLED_BY_AN);
2880 | 			  exercise(A_STR, FALSE);
2881 | 			  if(costly && loss) {
2882 | 			      if(insider)
2883 | 			      You("owe %ld zorkmids for objects destroyed.",
2884 | 							loss);
2885 | 			      else {
2886 | 				  You("caused %ld zorkmids worth of damage!",
2887 | 							loss);
2888 | 				  make_angry_shk(shkp, ox, oy);
2889 | 			      }
2890 | 			  }
2891 | 			  return TRUE;
2892 | 			}
2893 | 		case 20:
2894 | 		case 19:
2895 | 		case 18:
2896 | 		case 17:
2897 | 			pline("A cloud of noxious gas billows from %s.",
2898 | 							the(xname(obj)));
2899 | 			poisoned("gas cloud", A_STR, "cloud of poison gas",15);
2900 | 			exercise(A_CON, FALSE);
2901 | 			break;
2902 | 		case 16:
2903 | 		case 15:
2904 | 		case 14:
2905 | 		case 13:
2906 | 			You_feel("a needle prick your %s.",body_part(bodypart));
2907 | 			poisoned("needle", A_CON, "poisoned needle",10);
2908 | 			exercise(A_CON, FALSE);
2909 | 			break;
2910 | 		case 12:
2911 | 		case 11:
2912 | 		case 10:
2913 | 		case 9:
2914 | 			dofiretrap(obj);
2915 | 			break;
2916 | 		case 8:
2917 | 		case 7:
2918 | 		case 6: {
2919 | 			int dmg;
2920 | 
2921 | 			You("are jolted by a surge of electricity!");
2922 | 			if(Shock_resistance)  {
2923 | 			    shieldeff(u.ux, u.uy);
2924 | 			    You("don't seem to be affected.");
2925 | 			    dmg = 0;
2926 | 			} else
2927 | 			    dmg = d(4, 4);
2928 | 			destroy_item(RING_CLASS, AD_ELEC);
2929 | 			destroy_item(WAND_CLASS, AD_ELEC);
2930 | 			if (dmg) losehp(dmg, "electric shock", KILLED_BY_AN);
2931 | 			break;
2932 | 		      }
2933 | 		case 5:
2934 | 		case 4:
2935 | 		case 3:
2936 | 			if (!Free_action) {                        
2937 | 			pline("Suddenly you are frozen in place!");
2938 | 			nomul(-d(5, 6));
2939 | 			exercise(A_DEX, FALSE);
2940 | 			nomovemsg = You_can_move_again;
2941 | 			} else You("momentarily stiffen.");
2942 | 			break;
2943 | 		case 2:
2944 | 		case 1:
2945 | 		case 0:
2946 | 			pline("A cloud of %s gas billows from %s.",
2947 | 						hcolor((char *)0),
2948 | 						the(xname(obj)));
2949 | 			if(!Stunned) {
2950 | 			    if (Hallucination)
2951 | 				pline("What a groovy feeling!");
2952 | 			    else if (Blind)
2953 | 				You("stagger and get dizzy...");
2954 | 			    else
2955 | 				You("stagger and your vision blurs...");
2956 | 			}
2957 | 			make_stunned(HStun + rn1(7, 16),FALSE);
2958 | 			make_hallucinated(HHallucination + rn1(5, 16),FALSE,0L);
2959 | 			break;
2960 | 		default: impossible("bad chest trap");
2961 | 			break;
2962 | 	    }
2963 | 	    bot();			/* to get immediate botl re-display */
2964 | 	}
2965 | 
2966 | 	return FALSE;
2967 | }
2968 | 
2969 | #endif /* OVLB */
2970 | #ifdef OVL0
2971 | 
2972 | struct trap *
2973 | t_at(x,y)
2974 | register int x, y;
2975 | {
2976 | 	register struct trap *trap = ftrap;
2977 | 	while(trap) {
2978 | 		if(trap->tx == x && trap->ty == y) return(trap);
2979 | 		trap = trap->ntrap;
2980 | 	}
2981 | 	return((struct trap *)0);
2982 | }
2983 | 
2984 | #endif /* OVL0 */
2985 | #ifdef OVLB
2986 | 
2987 | void
2988 | deltrap(trap)
2989 | register struct trap *trap;
2990 | {
2991 | 	register struct trap *ttmp;
2992 | 
2993 | 	if(trap == ftrap)
2994 | 		ftrap = ftrap->ntrap;
2995 | 	else {
2996 | 		for(ttmp = ftrap; ttmp->ntrap != trap; ttmp = ttmp->ntrap) ;
2997 | 		ttmp->ntrap = trap->ntrap;
2998 | 	}
2999 | 	dealloc_trap(trap);
3000 | }
3001 | 
3002 | boolean delfloortrap(ttmp)
3003 | register struct trap *ttmp;
3004 | {
3005 | 	/* Destroy a trap that emanates from the floor. */
3006 | 	/* some of these are arbitrary -dlc */
3007 | 	if (ttmp && ((ttmp->ttyp == SQKY_BOARD) ||
3008 | 		     (ttmp->ttyp == BEAR_TRAP) ||
3009 | 		     (ttmp->ttyp == LANDMINE) ||
3010 | 		     (ttmp->ttyp == FIRE_TRAP) ||
3011 | 		     (ttmp->ttyp == PIT) ||
3012 | 		     (ttmp->ttyp == SPIKED_PIT) ||
3013 | 		     (ttmp->ttyp == HOLE) ||
3014 | 		     (ttmp->ttyp == TRAPDOOR) ||
3015 | 		     (ttmp->ttyp == TELEP_TRAP) ||
3016 | 		     (ttmp->ttyp == LEVEL_TELEP) ||
3017 | 		     (ttmp->ttyp == WEB) ||
3018 | 		     (ttmp->ttyp == MAGIC_TRAP) ||
3019 | 		     (ttmp->ttyp == ANTI_MAGIC))) {
3020 | 	    register struct monst *mtmp;
3021 | 
3022 | 	    if (ttmp->tx == u.ux && ttmp->ty == u.uy) {
3023 | 		u.utrap = 0;
3024 | 		u.utraptype = 0;
3025 | 	    } else if ((mtmp = m_at(ttmp->tx, ttmp->ty)) != 0) {
3026 | 		mtmp->mtrapped = 0;
3027 | 	    }
3028 | 	    deltrap(ttmp);
3029 | 	    return TRUE;
3030 | 	} else
3031 | 	    return FALSE;
3032 | }
3033 | 
3034 | /* used for doors (also tins).  can be used for anything else that opens. */
3035 | void
3036 | b_trapped(item, bodypart)
3037 | register const char *item;
3038 | register int bodypart;
3039 | {
3040 | 	register int lvl = level_difficulty();
3041 | 	int dmg = rnd(5 + (lvl < 5 ? lvl : 2+lvl/2));
3042 | 
3043 | 	pline("KABOOM!!  %s was booby-trapped!", The(item));
3044 | 	wake_nearby();
3045 | 	losehp(dmg, "explosion", KILLED_BY_AN);
3046 | 	exercise(A_STR, FALSE);
3047 | 	if (bodypart) exercise(A_CON, FALSE);
3048 | 	make_stunned(HStun + dmg, TRUE);
3049 | }
3050 | 
3051 | /* Monster is hit by trap. */
3052 | /* Note: doesn't work if both obj and d_override are null */
3053 | STATIC_OVL boolean
3054 | thitm(tlev, mon, obj, d_override)
3055 | register int tlev;
3056 | register struct monst *mon;
3057 | register struct obj *obj;
3058 | int d_override;
3059 | {
3060 | 	register int strike;
3061 | 	register boolean trapkilled = FALSE;
3062 | 
3063 | 	if (d_override) strike = 1;
3064 | 	else if (obj) strike = (find_mac(mon) + tlev + obj->spe <= rnd(20));
3065 | 	else strike = (find_mac(mon) + tlev <= rnd(20));
3066 | 
3067 | 	/* Actually more accurate than thitu, which doesn't take
3068 | 	 * obj->spe into account.
3069 | 	 */
3070 | 	if(!strike) {
3071 | 		if (cansee(mon->mx, mon->my))
3072 | 			pline("%s is almost hit by %s!", Monnam(mon),
3073 | 								doname(obj));
3074 | 	} else {
3075 | 		int dam = 1;
3076 | 
3077 | 		if (obj && cansee(mon->mx, mon->my))
3078 | 			pline("%s is hit by %s!", Monnam(mon), doname(obj));
3079 | 		if (d_override) dam = d_override;
3080 | 		else if (obj) {
3081 | 			dam = dmgval(obj, mon);
3082 | 			if (dam < 1) dam = 1;
3083 | 		}
3084 | 		if ((mon->mhp -= dam) <= 0) {
3085 | 			int xx = mon->mx;
3086 | 			int yy = mon->my;
3087 | 
3088 | 			monkilled(mon, "", AD_PHYS);
3089 | 			if (mon->mhp <= 0) {
3090 | 				newsym(xx, yy);
3091 | 				trapkilled = TRUE;
3092 | 			}
3093 | 		}
3094 | 	}
3095 | 	if (obj && (!strike || d_override)) {
3096 | 		place_object(obj, mon->mx, mon->my);
3097 | 		stackobj(obj);
3098 | 	} else if (obj) dealloc_obj(obj);
3099 | 
3100 | 	return trapkilled;
3101 | }
3102 | 
3103 | boolean
3104 | unconscious()
3105 | {
3106 | 	return((boolean)(multi < 0 && (!nomovemsg ||
3107 | 		u.usleep ||
3108 | 		!strncmp(nomovemsg,"You regain con", 15) ||
3109 | 		!strncmp(nomovemsg,"You are consci", 15))));
3110 | }
3111 | 
3112 | static char lava_killer[] = "molten lava";
3113 | 
3114 | boolean
3115 | lava_effects()
3116 | {
3117 |     register struct obj *obj, *obj2;
3118 |     int dmg;
3119 |     boolean usurvive;
3120 | 
3121 |     burn_away_slime();
3122 |     if (likes_lava(youmonst.data)) return FALSE;
3123 | 
3124 |     if (!Fire_resistance) {
3125 | 	if(Wwalking) {
3126 | 	    dmg = d(6,6);
3127 | 	    pline_The("lava here burns you!");
3128 | 	    if(dmg < u.uhp) {
3129 | 		losehp(dmg, lava_killer, KILLED_BY);
3130 | 		goto burn_stuff;
3131 | 	    }
3132 | 	} else
3133 | 	    You("fall into the lava!");
3134 | 
3135 | 	usurvive = Lifesaved || discover;
3136 | #ifdef WIZARD
3137 | 	if (wizard) usurvive = TRUE;
3138 | #endif
3139 | 	for(obj = invent; obj; obj = obj2) {
3140 | 	    obj2 = obj->nobj;
3141 | 	    if(is_organic(obj) && !obj->oerodeproof) {
3142 | 		if(obj->owornmask) {
3143 | 		    if (usurvive)
3144 | 			Your("%s into flame!", aobjnam(obj, "burst"));
3145 | 
3146 | 		    if(obj == uarm) (void) Armor_gone();
3147 | 		    else if(obj == uarmc) (void) Cloak_off();
3148 | 		    else if(obj == uarmh) (void) Helmet_off();
3149 | 		    else if(obj == uarms) (void) Shield_off();
3150 | 		    else if(obj == uarmg) (void) Gloves_off();
3151 | 		    else if(obj == uarmf) (void) Boots_off();
3152 | #ifdef TOURIST
3153 | 		    else if(obj == uarmu) setnotworn(obj);
3154 | #endif
3155 | 		    else if(obj == uleft) Ring_gone(obj);
3156 | 		    else if(obj == uright) Ring_gone(obj);
3157 | 		    else if(obj == ublindf) Blindf_off(obj);
3158 | 		    else if(obj == uamul) Amulet_off();
3159 | 		    else if(obj == uwep) uwepgone();
3160 | 		    else if (obj == uquiver) uqwepgone();
3161 | 		    else if (obj == uswapwep) uswapwepgone();
3162 | 		}
3163 | 		useupall(obj);
3164 | 	    }
3165 | 	}
3166 | 
3167 | 	/* s/he died... */
3168 | 	u.uhp = -1;
3169 | 	killer_format = KILLED_BY;
3170 | 	killer = lava_killer;
3171 | 	You("burn to a crisp...");
3172 | 	done(BURNING);
3173 | 	while (!safe_teleds()) {
3174 | 		pline("You're still burning.");
3175 | 		done(BURNING);
3176 | 	}
3177 | 	You("find yourself back on solid %s.", surface(u.ux, u.uy));
3178 | 	return(TRUE);
3179 |     }
3180 | 
3181 |     if (!Wwalking) {
3182 | 	u.utrap = rn1(4, 4) + (rn1(4, 12) << 8);
3183 | 	u.utraptype = TT_LAVA;
3184 | 	You("sink into the lava, but it only burns slightly!");
3185 | 	if (u.uhp > 1)
3186 | 	    losehp(1, lava_killer, KILLED_BY);
3187 |     }
3188 |     /* just want to burn boots, not all armor; destroy_item doesn't work on
3189 |        armor anyway */
3190 | burn_stuff:
3191 |     if(uarmf && !uarmf->oerodeproof && is_organic(uarmf)) {
3192 | 	/* save uarmf value because Boots_off() sets uarmf to null */
3193 | 	obj = uarmf;
3194 | 	Your("%s bursts into flame!", xname(obj));
3195 | 	(void) Boots_off();
3196 | 	useup(obj);
3197 |     }
3198 |     destroy_item(SCROLL_CLASS, AD_FIRE);
3199 |     destroy_item(SPBOOK_CLASS, AD_FIRE);
3200 |     destroy_item(POTION_CLASS, AD_FIRE);
3201 |     return(FALSE);
3202 | }
3203 | 
3204 | #endif /* OVLB */
3205 | 
3206 | /*trap.c*/