OSDN Git Service

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