OSDN Git Service

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