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