From bd9456b0f8c684e04269d3dbd07ce4fb5808bf27 Mon Sep 17 00:00:00 2001 From: jwalz Date: Sat, 5 Jan 2002 21:05:53 +0000 Subject: [PATCH] *** empty log message *** --- src/trap.c | 3506 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3506 insertions(+) create mode 100644 src/trap.c diff --git a/src/trap.c b/src/trap.c new file mode 100644 index 000000000..9150c062e --- /dev/null +++ b/src/trap.c @@ -0,0 +1,3506 @@ +/* SCCS Id: @(#)trap.c 3.3 2001/09/06 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" + +STATIC_DCL void FDECL(dofiretrap, (struct obj *)); +STATIC_DCL void NDECL(domagictrap); +STATIC_DCL boolean FDECL(emergency_disrobe,(boolean *)); +STATIC_DCL int FDECL(untrap_prob, (struct trap *ttmp)); +STATIC_DCL void FDECL(cnv_trap_obj, (int, int, struct trap *)); +STATIC_DCL void FDECL(move_into_trap, (struct trap *)); +STATIC_DCL int FDECL(try_disarm, (struct trap *,BOOLEAN_P)); +STATIC_DCL void FDECL(reward_untrap, (struct trap *, struct monst *)); +STATIC_DCL int FDECL(disarm_holdingtrap, (struct trap *)); +STATIC_DCL int FDECL(disarm_landmine, (struct trap *)); +STATIC_DCL int FDECL(disarm_squeaky_board, (struct trap *)); +STATIC_DCL int FDECL(disarm_shooting_trap, (struct trap *, int)); +STATIC_DCL int FDECL(try_lift, (struct monst *, struct trap *, int, BOOLEAN_P)); +STATIC_DCL int FDECL(help_monster_out, (struct monst *, struct trap *)); +STATIC_DCL boolean FDECL(thitm, (int, struct monst *, struct obj *, int)); +STATIC_DCL int FDECL(mkroll_launch, + (struct trap *,XCHAR_P,XCHAR_P,SHORT_P,long)); +STATIC_DCL boolean FDECL(isclearpath,(coord *, int, SCHAR_P, SCHAR_P)); +STATIC_DCL void FDECL(blow_up_landmine, (struct trap *)); +#ifdef STEED +STATIC_OVL int FDECL(steedintrap, (struct trap *, struct obj *)); +#endif + +#ifndef OVLB +STATIC_VAR const char *a_your[2]; +STATIC_VAR const char *A_Your[2]; +STATIC_VAR const char *the_your[2]; +STATIC_VAR const char tower_of_flame[]; +STATIC_VAR const char *A_gush_of_water_hits; + +#else + +STATIC_VAR const char *a_your[2] = { "a", "your" }; +STATIC_VAR const char *A_Your[2] = { "A", "Your" }; +STATIC_VAR const char *the_your[2] = { "the", "your" }; +STATIC_VAR const char tower_of_flame[] = "tower of flame"; +STATIC_VAR const char *A_gush_of_water_hits = "A gush of water hits"; + +#endif /* OVLB */ + +#ifdef OVLB + +/* called when you're hit by fire (dofiretrap,buzz,zapyourself,explode) */ +boolean /* returns TRUE if hit on torso */ +burnarmor(victim) +struct monst *victim; +{ + struct obj *item; + char buf[BUFSZ]; + int mat_idx; + + if (!victim) return 0; +#define burn_dmg(obj,descr) rust_dmg(obj, descr, 0, FALSE, victim) + while (1) { + switch (rn2(5)) { + case 0: + item = (victim == &youmonst) ? uarmh : which_armor(victim, W_ARMH); + if (item) { + mat_idx = objects[item->otyp].oc_material; + Sprintf(buf,"%s helmet", materialnm[mat_idx] ); + } + if (!burn_dmg(item, item ? buf : "helmet")) continue; + break; + case 1: + item = (victim == &youmonst) ? uarmc : which_armor(victim, W_ARMC); + if (item) { + (void) burn_dmg(item, cloak_simple_name(item)); + return TRUE; + } + item = (victim == &youmonst) ? uarm : which_armor(victim, W_ARM); + if (item) { + (void) burn_dmg(item, xname(item)); + return TRUE; + } +#ifdef TOURIST + item = (victim == &youmonst) ? uarmu : which_armor(victim, W_ARMU); + if (item) + (void) burn_dmg(item, "shirt"); +#endif + return TRUE; + case 2: + item = (victim == &youmonst) ? uarms : which_armor(victim, W_ARMS); + if (!burn_dmg(item, "wooden shield")) continue; + break; + case 3: + item = (victim == &youmonst) ? uarmg : which_armor(victim, W_ARMG); + if (!burn_dmg(item, "gloves")) continue; + break; + case 4: + item = (victim == &youmonst) ? uarmf : which_armor(victim, W_ARMF); + if (!burn_dmg(item, "boots")) continue; + break; + } + break; /* Out of while loop */ + } + return FALSE; +#undef burn_dmg +} + +/* Generic rust-armor function. Returns TRUE if a message was printed; + * "print", if set, means to print a message (and thus to return TRUE) even + * if the item could not be rusted; otherwise a message is printed and TRUE is + * returned only for rustable items. + */ +boolean +rust_dmg(otmp, ostr, type, print, victim) +register struct obj *otmp; +register const char *ostr; +int type; +boolean print; +struct monst *victim; +{ + static NEARDATA const char *action[] = { "smoulder", "rust", "rot", "corrode" }; + static NEARDATA const char *msg[] = { "burnt", "rusted", "rotten", "corroded" }; + boolean vulnerable = FALSE; + boolean plural; + boolean grprot = FALSE; + boolean is_primary = TRUE; + boolean vismon = (victim != &youmonst) && canseemon(victim); + int erosion; + + if (!otmp) return(FALSE); + switch(type) { + case 0: vulnerable = is_flammable(otmp); + break; + case 1: vulnerable = is_rustprone(otmp); + grprot = TRUE; + break; + case 2: vulnerable = is_rottable(otmp); + is_primary = FALSE; + break; + case 3: vulnerable = is_corrodeable(otmp); + grprot = TRUE; + is_primary = FALSE; + break; + } + erosion = is_primary ? otmp->oeroded : otmp->oeroded2; + + if (!print && (!vulnerable || otmp->oerodeproof || erosion == MAX_ERODE)) + return FALSE; + + plural = (is_gloves(otmp) || is_boots(otmp)) && + !strstri(ostr, "pair of "); /* "pair of *s" is singular */ + + if (!vulnerable) { + if (flags.verbose) { + if (victim == &youmonst) + Your("%s %s not affected.", ostr, plural ? "are" : "is"); + else if (vismon) + pline("%s's %s %s not affected.", Monnam(victim), ostr, + plural ? "are" : "is"); + } + } else if (erosion < MAX_ERODE) { + if (grprot && otmp->greased) { + grease_protect(otmp,ostr,plural,victim); + } else if (otmp->oerodeproof || (otmp->blessed && !rnl(4))) { + if (flags.verbose) { + if (victim == &youmonst) + pline("Somehow, your %s %s not affected.", + ostr, plural ? "are" : "is"); + else if (vismon) + pline("Somehow, %s's %s %s not affected.", + mon_nam(victim), ostr, plural ? "are" : "is"); + } + } else { + if (victim == &youmonst) + Your("%s %s%s%s!", ostr, action[type], + plural ? "" : "s", + erosion+1 == MAX_ERODE ? " completely" : + erosion ? " further" : ""); + else if (vismon) + pline("%s's %s %s%s%s!", Monnam(victim), ostr, action[type], + plural ? "" : "s", + erosion+1 == MAX_ERODE ? " completely" : + erosion ? " further" : ""); + if (is_primary) + otmp->oeroded++; + else + otmp->oeroded2++; + update_inventory(); + } + } else { + if (flags.verbose) { + if (victim == &youmonst) + Your("%s %s%s completely %s.", ostr, + Blind ? "feel" : "look", + plural ? "" : "s", msg[type]); + else if (vismon) + pline("%s's %s look%s completely %s.", + Monnam(victim), ostr, + plural ? "" : "s", msg[type]); + } + } + return(TRUE); +} + +void +grease_protect(otmp,ostr,plu,victim) +register struct obj *otmp; +register const char *ostr; +register boolean plu; +struct monst *victim; +{ + static const char txt[] = "protected by the layer of grease!"; + boolean vismon = victim && (victim != &youmonst) && canseemon(victim); + + if (ostr) { + if (victim == &youmonst) + Your("%s %s %s",ostr,plu ? "are" : "is", txt); + else if (vismon) + pline("%s's %s %s %s", Monnam(victim), + ostr, plu ? "are" : "is", txt); + } else { + if (victim == &youmonst) + Your("%s %s",aobjnam(otmp,"are"), txt); + else if (vismon) + pline("%s's %s %s", Monnam(victim), aobjnam(otmp,"are"), txt); + } + if (!rn2(2)) { + otmp->greased = 0; + if (carried(otmp)) { + pline_The("grease dissolves."); + update_inventory(); + } + } +} + +struct trap * +maketrap(x,y,typ) +register int x, y, typ; +{ + register struct trap *ttmp; + register struct rm *lev; + register boolean oldplace; + + if ((ttmp = t_at(x,y)) != 0) { + if (ttmp->ttyp == MAGIC_PORTAL) return (struct trap *)0; + oldplace = TRUE; + if (u.utrap && (x == u.ux) && (y == u.uy) && + ((u.utraptype == TT_BEARTRAP && typ != BEAR_TRAP) || + (u.utraptype == TT_WEB && typ != WEB) || + (u.utraptype == TT_PIT && typ != PIT && typ != SPIKED_PIT))) + u.utrap = 0; + } else { + oldplace = FALSE; + ttmp = newtrap(); + ttmp->tx = x; + ttmp->ty = y; + ttmp->launch.x = -1; /* force error if used before set */ + ttmp->launch.y = -1; + } + ttmp->ttyp = typ; + switch(typ) { + case STATUE_TRAP: /* create a "living" statue */ + { struct monst *mtmp; + struct obj *otmp, *statue; + + statue = mkcorpstat(STATUE, (struct monst *)0, + &mons[rndmonnum()], x, y, FALSE); + mtmp = makemon(&mons[statue->corpsenm], 0, 0, NO_MM_FLAGS); + if (!mtmp) break; /* should never happen */ + while(mtmp->minvent) { + otmp = mtmp->minvent; + otmp->owornmask = 0; + obj_extract_self(otmp); + (void) add_to_container(statue, otmp); + } + statue->owt = weight(statue); + mongone(mtmp); + break; + } + case ROLLING_BOULDER_TRAP: /* boulder will roll towards trigger */ + (void) mkroll_launch(ttmp, x, y, BOULDER, 1L); + break; + case HOLE: + case PIT: + case SPIKED_PIT: + case TRAPDOOR: + lev = &levl[x][y]; + if (*in_rooms(x, y, SHOPBASE) && + ((typ == HOLE || typ == TRAPDOOR) || IS_DOOR(lev->typ))) + add_damage(x, y, /* schedule repair */ + (IS_DOOR(lev->typ) && !flags.mon_moving) ? 200L : 0L); + lev->doormask = 0; /* subsumes altarmask, icedpool... */ + if (IS_ROOM(lev->typ)) /* && !IS_AIR(lev->typ) */ + lev->typ = ROOM; + + /* + * some cases which can happen when digging + * down while phazing thru solid areas + */ + else if (lev->typ == STONE || lev->typ == SCORR) + lev->typ = CORR; + else if (IS_WALL(lev->typ) || lev->typ == SDOOR) + lev->typ = level.flags.is_maze_lev ? ROOM : + level.flags.is_cavernous_lev ? CORR : DOOR; + + unearth_objs(x, y); + break; + } + if (ttmp->ttyp == HOLE) ttmp->tseen = 1; /* You can't hide a hole */ + else ttmp->tseen = 0; + ttmp->once = 0; + ttmp->madeby_u = 0; + ttmp->dst.dnum = -1; + ttmp->dst.dlevel = -1; + if (!oldplace) { + ttmp->ntrap = ftrap; + ftrap = ttmp; + } + return(ttmp); +} + +void +fall_through(td) +boolean td; /* td == TRUE : trap door or hole */ +{ + d_level dtmp; + char msgbuf[BUFSZ]; + const char *dont_fall = 0; + register int newlevel = dunlev(&u.uz); + + /* KMH -- You can't escape the Sokoban level traps */ + if(Blind && Levitation && !In_sokoban(&u.uz)) return; + + do { + newlevel++; + } while(!rn2(4) && newlevel < dunlevs_in_dungeon(&u.uz)); + + if(td) { + struct trap *t=t_at(u.ux,u.uy); + if (!In_sokoban(&u.uz)) { + if (t->ttyp == TRAPDOOR) + pline("A trap door opens up under you!"); + else + pline("There's a gaping hole under you!"); + } + } else pline_The("%s opens up under you!", surface(u.ux,u.uy)); + + if (In_sokoban(&u.uz) && Can_fall_thru(&u.uz)) + ; /* KMH -- You can't escape the Sokoban level traps */ + else if(Levitation || u.ustuck || !Can_fall_thru(&u.uz) + || Flying || is_clinger(youmonst.data) + || (Inhell && !u.uevent.invoked && + newlevel == dunlevs_in_dungeon(&u.uz)) + ) { + dont_fall = "don't fall in."; + } else if (youmonst.data->msize >= MZ_HUGE) { + dont_fall = "don't fit through."; + } else if (!next_to_u()) { + dont_fall = "are jerked back by your pet!"; + } + if (dont_fall) { + You(dont_fall); + /* hero didn't fall through, but any objects here might */ + impact_drop((struct obj *)0, u.ux, u.uy, 0); + if (!td) { + display_nhwindow(WIN_MESSAGE, FALSE); + pline_The("opening under you closes up."); + } + return; + } + + if(*u.ushops) shopdig(1); + if (Is_stronghold(&u.uz)) { + find_hell(&dtmp); + } else { + dtmp.dnum = u.uz.dnum; + dtmp.dlevel = newlevel; + } + if (!td) + Sprintf(msgbuf, "The hole in the %s above you closes up.", + ceiling(u.ux,u.uy)); + schedule_goto(&dtmp, FALSE, TRUE, 0, + (char *)0, !td ? msgbuf : (char *)0); +} + +/* + * Animate the given statue. May have been via shatter attempt, trap, + * or stone to flesh spell. Return a monster if successfully animated. + * If the monster is animated, the object is deleted. If fail_reason + * is non-null, then fill in the reason for failure (or success). + * + * The cause of animation is: + * + * ANIMATE_NORMAL - hero "finds" the monster + * ANIMATE_SHATTER - hero tries to destroy the statue + * ANIMATE_SPELL - stone to flesh spell hits the statue + * + * Perhaps x, y is not needed if we can use get_obj_location() to find + * the statue's location... ??? + */ +struct monst * +animate_statue(statue, x, y, cause, fail_reason) +struct obj *statue; +xchar x, y; +int cause; +int *fail_reason; +{ + struct permonst *mptr; + struct monst *mon = 0; + struct obj *item; + coord cc; + + if (statue->oxlth && statue->oattached == OATTACHED_MONST) { + cc.x = x, cc.y = y; + mon = montraits(statue, &cc); + if (mon && mon->mtame && !mon->isminion) + wary_dog(mon, TRUE); + } else { + /* + * Guard against someone wishing for a statue of a unique monster + * (which is allowed in normal play) and then tossing it onto the + * [detected or guessed] location of a statue trap. Normally the + * uppermost statue is the one which would be activated. + */ + mptr = &mons[statue->corpsenm]; + if (mptr->geno & G_UNIQ) { + if (fail_reason) *fail_reason = AS_MON_IS_UNIQUE; + return (struct monst *)0; + } + mon = makemon(mptr, x, y, NO_MINVENT); + } + + if (!mon) { + if (fail_reason) *fail_reason = AS_NO_MON; + return (struct monst *)0; + } + + /* if statue has been named, give same name to the monster */ + if (statue->onamelth) + mon = christen_monst(mon, ONAME(statue)); + /* transfer any statue contents to monster's inventory */ + while ((item = statue->cobj) != 0) { + obj_extract_self(item); + (void) add_to_minv(mon, item); + } + m_dowear(mon, TRUE); + delobj(statue); + /* mimic statue becomes seen mimic; other hiders won't be hidden */ + if (mon->m_ap_type) seemimic(mon); + else mon->mundetected = FALSE; + if ((x == u.ux && y == u.uy) || cause == ANIMATE_SPELL) + pline_The("statue comes to life!"); + else if (cause == ANIMATE_SHATTER) + pline("Instead of shattering, the statue suddenly comes alive!"); + else /* cause == ANIMATE_NORMAL */ + You("find %s posing as a statue.", a_monnam(mon)); + /* avoid hiding under nothing */ + if (x == u.ux && y == u.uy && + Upolyd && hides_under(youmonst.data) && !OBJ_AT(x, y)) + u.uundetected = 0; + + if (fail_reason) *fail_reason = AS_OK; + return mon; +} + +/* + * You've either stepped onto a statue trap's location or you've triggered a + * statue trap by searching next to it or by trying to break it with a wand + * or pick-axe. + */ +struct monst * +activate_statue_trap(trap, x, y, shatter) +struct trap *trap; +xchar x, y; +boolean shatter; +{ + struct monst *mtmp = (struct monst *)0; + struct obj *otmp = sobj_at(STATUE, x, y); + int fail_reason; + + /* + * Try to animate the first valid statue. Stop the loop when we + * actually create something or the failure cause is not because + * the mon was unique. + */ + deltrap(trap); + while (otmp) { + mtmp = animate_statue(otmp, x, y, + shatter ? ANIMATE_SHATTER : ANIMATE_NORMAL, &fail_reason); + if (mtmp || fail_reason != AS_MON_IS_UNIQUE) break; + + while ((otmp = otmp->nexthere) != 0) + if (otmp->otyp == STATUE) break; + } + + if (Blind) feel_location(x, y); + else newsym(x, y); + return mtmp; +} + +void +dotrap(trap, trflags) +register struct trap *trap; +unsigned trflags; +{ + register int ttype = trap->ttyp; + register struct obj *otmp; + boolean already_seen = trap->tseen; + boolean webmsgok = (!(trflags & NOWEBMSG)); + + nomul(0); + + /* KMH -- You can't escape the Sokoban level traps */ + if (In_sokoban(&u.uz) && + (ttype == PIT || ttype == SPIKED_PIT || ttype == HOLE || + ttype == TRAPDOOR)) { + /* The "air currents" message is still appropriate -- even when + * the hero isn't flying or levitating -- because it conveys the + * reason why the player cannot escape the trap with a dexterity + * check, clinging to the ceiling, etc. + */ + pline("Air currents pull you down into %s %s!", + a_your[trap->madeby_u], + defsyms[trap_to_defsym(ttype)].explanation); + /* then proceed to normal trap effect */ + } else if (already_seen) { + if ((Levitation || Flying) && + (ttype == PIT || ttype == SPIKED_PIT || ttype == HOLE || + ttype == BEAR_TRAP)) { + You("%s over %s %s.", + Levitation ? "float" : "fly", + a_your[trap->madeby_u], + defsyms[trap_to_defsym(ttype)].explanation); + return; + } + if(!Fumbling && ttype != MAGIC_PORTAL && ttype != ANTI_MAGIC && + (!rn2(5) || + ((ttype == PIT || ttype == SPIKED_PIT) && is_clinger(youmonst.data)))) { + You("escape %s %s.", + (ttype == ARROW_TRAP && !trap->madeby_u) ? "an" : + a_your[trap->madeby_u], + defsyms[trap_to_defsym(ttype)].explanation); + return; + } + } + + switch(ttype) { + case ARROW_TRAP: + seetrap(trap); + pline("An arrow shoots out at you!"); + otmp = mksobj(ARROW, TRUE, FALSE); + otmp->quan = 1L; + otmp->owt = weight(otmp); + otmp->opoisoned = 0; +#ifdef STEED + if (u.usteed && !rn2(2) && steedintrap(trap, otmp)) /* nothing */; + else +#endif + if (thitu(8, dmgval(otmp, &youmonst), otmp, "arrow")) { + obfree(otmp, (struct obj *)0); + } else { + place_object(otmp, u.ux, u.uy); + if (!Blind) otmp->dknown = 1; + stackobj(otmp); + newsym(u.ux, u.uy); + } + break; + case DART_TRAP: + seetrap(trap); + pline("A little dart shoots out at you!"); + otmp = mksobj(DART, TRUE, FALSE); + otmp->quan = 1L; + otmp->owt = weight(otmp); + if (!rn2(6)) otmp->opoisoned = 1; +#ifdef STEED + if (u.usteed && !rn2(2) && steedintrap(trap, otmp)) /* nothing */; + else +#endif + if (thitu(7, dmgval(otmp, &youmonst), otmp, "little dart")) { + if (otmp->opoisoned) + poisoned("dart",A_CON,"poison dart",10); + obfree(otmp, (struct obj *)0); + } else { + place_object(otmp, u.ux, u.uy); + if (!Blind) otmp->dknown = 1; + stackobj(otmp); + newsym(u.ux, u.uy); + } + break; + case ROCKTRAP: + { + int dmg = d(2,6); /* should be std ROCK dmg? */ + + seetrap(trap); + otmp = mksobj_at(ROCK, u.ux, u.uy, TRUE, FALSE); + otmp->quan = 1L; + otmp->owt = weight(otmp); + + pline("A trap door in the %s opens and a rock falls on your %s!", + ceiling(u.ux,u.uy), + body_part(HEAD)); + + if (uarmh) { + if(is_metallic(uarmh)) { + pline("Fortunately, you are wearing a hard helmet."); + dmg = 2; + } else if (flags.verbose) { + Your("%s does not protect you.", xname(uarmh)); + } + } + + if (!Blind) otmp->dknown = 1; + stackobj(otmp); + newsym(u.ux,u.uy); /* map the rock */ + + losehp(dmg, "falling rock", KILLED_BY_AN); + exercise(A_STR, FALSE); + } + break; + + case SQKY_BOARD: /* stepped on a squeaky board */ + if (Levitation || Flying) { + if (!Blind) { + seetrap(trap); + if (Hallucination) + You("notice a crease in the linoleum."); + else + You("notice a loose board below you."); + } + } else { + seetrap(trap); + pline("A board beneath you squeaks loudly."); + wake_nearby(); + } + break; + + case BEAR_TRAP: + if(Levitation || Flying) break; + seetrap(trap); + if(amorphous(youmonst.data) || is_whirly(youmonst.data) || + unsolid(youmonst.data)) { + pline("%s bear trap closes harmlessly through you.", + A_Your[trap->madeby_u]); + break; + } + if( +#ifdef STEED + !u.usteed && +#endif + youmonst.data->msize <= MZ_SMALL) { + pline("%s bear trap closes harmlessly over you.", + A_Your[trap->madeby_u]); + break; + } + u.utrap = rn1(4, 4); + u.utraptype = TT_BEARTRAP; +#ifdef STEED + if (u.usteed) { + pline("%s bear trap closes on %s %s!", + A_Your[trap->madeby_u], s_suffix(mon_nam(u.usteed)), + mbodypart(u.usteed, FOOT)); + } else +#endif + { + pline("%s bear trap closes on your %s!", + A_Your[trap->madeby_u], body_part(FOOT)); + if(u.umonnum == PM_OWLBEAR || u.umonnum == PM_BUGBEAR) + You("howl in anger!"); + } + exercise(A_DEX, FALSE); + break; + + case SLP_GAS_TRAP: + seetrap(trap); + if(Sleep_resistance || breathless(youmonst.data)) { + You("are enveloped in a cloud of gas!"); + break; + } + pline("A cloud of gas puts you to sleep!"); + flags.soundok = 0; + fall_asleep(-rnd(25), TRUE); + afternmv = Hear_again; +#ifdef STEED + (void) steedintrap(trap, (struct obj *)0); +#endif + break; + + case RUST_TRAP: + seetrap(trap); + if (u.umonnum == PM_IRON_GOLEM) { + pline("%s you!", A_gush_of_water_hits); + You("are covered with rust!"); + rehumanize(); + break; + } else if (u.umonnum == PM_GREMLIN && rn2(3)) { + pline("%s you!", A_gush_of_water_hits); + (void)split_mon(&youmonst, (struct monst *)0); + break; + } + + /* Unlike monsters, traps cannot aim their rust attacks at + * you, so instead of looping through and taking either the + * first rustable one or the body, we take whatever we get, + * even if it is not rustable. + */ + switch (rn2(5)) { + case 0: + pline("%s you on the %s!", A_gush_of_water_hits, + body_part(HEAD)); + (void) rust_dmg(uarmh, "helmet", 1, TRUE, &youmonst); + break; + case 1: + pline("%s your left %s!", A_gush_of_water_hits, + body_part(ARM)); + if (rust_dmg(uarms, "shield", 1, TRUE, &youmonst)) + break; + if (u.twoweap || (uwep && bimanual(uwep))) + erode_obj(u.twoweap ? uswapwep : uwep, FALSE, TRUE); +glovecheck: (void) rust_dmg(uarmg, "gauntlets", 1, TRUE, &youmonst); + /* Not "metal gauntlets" since it gets called + * even if it's leather for the message + */ + break; + case 2: + pline("%s your right %s!", A_gush_of_water_hits, + body_part(ARM)); + erode_obj(uwep, FALSE, TRUE); + goto glovecheck; + default: + pline("%s you!", A_gush_of_water_hits); + for (otmp=invent; otmp; otmp = otmp->nobj) + (void) snuff_lit(otmp); + if (uarmc) + (void) rust_dmg(uarmc, cloak_simple_name(uarmc), + 1, TRUE, &youmonst); + else if (uarm) + (void) rust_dmg(uarm, "armor", 1, TRUE, &youmonst); +#ifdef TOURIST + else if (uarmu) + (void) rust_dmg(uarmu, "shirt", 1, TRUE, &youmonst); +#endif + } + break; + + case FIRE_TRAP: + seetrap(trap); + dofiretrap((struct obj *)0); + break; + + case PIT: + case SPIKED_PIT: + /* KMH -- You can't escape the Sokoban level traps */ + if (!In_sokoban(&u.uz) && (Levitation || Flying)) break; + seetrap(trap); + if (!In_sokoban(&u.uz) && is_clinger(youmonst.data)) { + if(trap->tseen) { + You("see %s %spit below you.", a_your[trap->madeby_u], + ttype == SPIKED_PIT ? "spiked " : ""); + } else { + pline("%s pit %sopens up under you!", + A_Your[trap->madeby_u], + ttype == SPIKED_PIT ? "full of spikes " : ""); + You("don't fall in!"); + } + break; + } + if (!In_sokoban(&u.uz)) { + char verbbuf[BUFSZ]; +#ifdef STEED + if (u.usteed) + Sprintf(verbbuf,"lead %s", + x_monnam(u.usteed, + u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE, + "poor", SUPPRESS_SADDLE, FALSE)); + else +#endif + Strcpy(verbbuf,"fall"); + You("%s into %s pit!", verbbuf, a_your[trap->madeby_u]); + } + /* wumpus reference */ + if (Role_if(PM_RANGER) && !trap->madeby_u && !trap->once && + In_quest(&u.uz) && Is_qlocate(&u.uz)) { + pline("Fortunately it has a bottom after all..."); + trap->once = 1; + } else if (u.umonnum == PM_PIT_VIPER || + u.umonnum == PM_PIT_FIEND) + pline("How pitiful. Isn't that the pits?"); + if (ttype == SPIKED_PIT) { + char *predicament = "on a set of sharp iron spikes"; +#ifdef STEED + if (u.usteed) { + pline("%s lands %s!", + upstart(x_monnam(u.usteed, + u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE, + "poor", SUPPRESS_SADDLE, FALSE)), predicament); + } else +#endif + You("land %s!", predicament); + } + if (!Passes_walls) + u.utrap = rn1(6,2); + u.utraptype = TT_PIT; +#ifdef STEED + if (!steedintrap(trap, (struct obj *)0)) { +#endif + if (ttype == SPIKED_PIT) { + losehp(rnd(10),"fell into a pit of iron spikes", + NO_KILLER_PREFIX); + if (!rn2(6)) + poisoned("spikes", A_STR, "fall onto poison spikes", 8); + } else + losehp(rnd(6),"fell into a pit", NO_KILLER_PREFIX); + if (Punished && !carried(uball)) { + unplacebc(); + ballfall(); + placebc(); + } + selftouch("Falling, you"); + vision_full_recalc = 1; /* vision limits change */ + exercise(A_STR, FALSE); + exercise(A_DEX, FALSE); +#ifdef STEED + } +#endif + break; + case HOLE: + case TRAPDOOR: + seetrap(trap); + if (!Can_fall_thru(&u.uz)) { + impossible("dotrap: %ss cannot exist on this level.", + defsyms[trap_to_defsym(ttype)].explanation); + break; /* don't activate it after all */ + } + fall_through(TRUE); + break; + + case TELEP_TRAP: + seetrap(trap); + tele_trap(trap); + break; + case LEVEL_TELEP: + seetrap(trap); + level_tele_trap(trap); + break; + + case WEB: /* Our luckless player has stumbled into a web. */ + seetrap(trap); + if (amorphous(youmonst.data) || is_whirly(youmonst.data) || + unsolid(youmonst.data)) { + if (acidic(youmonst.data) || u.umonnum == PM_GELATINOUS_CUBE || + u.umonnum == PM_FIRE_ELEMENTAL) { + if (webmsgok) + You("%s %s spider web!", + (u.umonnum == PM_FIRE_ELEMENTAL) ? "burn" : "dissolve", + a_your[trap->madeby_u]); + deltrap(trap); + newsym(u.ux,u.uy); + break; + } + if (webmsgok) You("flow through %s spider web.", + a_your[trap->madeby_u]); + break; + } + if (webmaker(youmonst.data)) { + if (webmsgok) + pline(trap->madeby_u ? "You take a walk on your web." + : "There is a spider web here."); + break; + } + if (webmsgok) { + char verbbuf[BUFSZ]; + verbbuf[0] = '\0'; +#ifdef STEED + if (u.usteed) + Sprintf(verbbuf,"lead %s", + x_monnam(u.usteed, + u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE, + "poor", SUPPRESS_SADDLE, FALSE)); + else +#endif + + Sprintf(verbbuf, "%s", Levitation ? (const char *)"float" : + locomotion(youmonst.data, "stumble")); + You("%s into %s spider web!", verbbuf, a_your[trap->madeby_u]); + } + u.utraptype = TT_WEB; + + /* Time stuck in the web depends on your strength. */ + { + register int str = ACURR(A_STR); + + if (str <= 3) u.utrap = rn1(6,6); + else if (str < 6) u.utrap = rn1(6,4); + else if (str < 9) u.utrap = rn1(4,4); + else if (str < 12) u.utrap = rn1(4,2); + else if (str < 15) u.utrap = rn1(2,2); + else if (str < 18) u.utrap = rnd(2); + else if (str < 69) u.utrap = 1; + else { + u.utrap = 0; + if (webmsgok) + You("tear through %s web!", a_your[trap->madeby_u]); + deltrap(trap); + newsym(u.ux,u.uy); /* get rid of trap symbol */ + } + } + break; + + case STATUE_TRAP: + (void) activate_statue_trap(trap, u.ux, u.uy, FALSE); + break; + + case MAGIC_TRAP: /* A magic trap. */ + seetrap(trap); + if (!rn2(30)) { + deltrap(trap); + newsym(u.ux,u.uy); /* update position */ + You("are caught in a magical explosion!"); + losehp(rnd(10), "magical explosion", KILLED_BY_AN); + Your("body absorbs some of the magical energy!"); + u.uen = (u.uenmax += 2); + } else domagictrap(); +#ifdef STEED + (void) steedintrap(trap, (struct obj *)0); +#endif + break; + + case ANTI_MAGIC: + seetrap(trap); + if(Antimagic) { + shieldeff(u.ux, u.uy); + You_feel("momentarily lethargic."); + } else drain_en(rnd(u.ulevel) + 1); + break; + + case POLY_TRAP: { + char verbbuf[BUFSZ]; + seetrap(trap); +#ifdef STEED + if (u.usteed) + Sprintf(verbbuf, "lead %s", + x_monnam(u.usteed, + u.usteed->mnamelth ? ARTICLE_NONE : ARTICLE_THE, + (char *)0, SUPPRESS_SADDLE, FALSE)); + else +#endif + Sprintf(verbbuf,"%s", + Levitation ? (const char *)"float" : + locomotion(youmonst.data, "step")); + You("%s onto a polymorph trap!", verbbuf); + if(Antimagic || Unchanging) { + shieldeff(u.ux, u.uy); + You_feel("momentarily different."); + /* Trap did nothing; don't remove it --KAA */ + } else { +#ifdef STEED + (void) steedintrap(trap, (struct obj *)0); +#endif + deltrap(trap); /* delete trap before polymorph */ + newsym(u.ux,u.uy); /* get rid of trap symbol */ + You_feel("a change coming over you."); + polyself(); + } + break; + } + case LANDMINE: + if (Levitation || Flying) { + if (!already_seen && rn2(3)) break; + seetrap(trap); + pline("%s %s in a pile of soil below you.", + already_seen ? "There is" : "You discover", + trap->madeby_u ? "the trigger of your mine" : + "a trigger"); + if (already_seen && rn2(3)) break; + pline("KAABLAMM!!! The air currents set %s%s off!", + already_seen ? a_your[trap->madeby_u] : "", + already_seen ? " land mine" : "it"); + } else { + seetrap(trap); + pline("KAABLAMM!!! You triggered %s land mine!", + a_your[trap->madeby_u]); + set_wounded_legs(LEFT_SIDE, rn1(35, 41)); + set_wounded_legs(RIGHT_SIDE, rn1(35, 41)); + exercise(A_DEX, FALSE); + } + blow_up_landmine(trap); + newsym(u.ux,u.uy); /* update trap symbol */ + losehp(rnd(16), "land mine", KILLED_BY_AN); + /* fall recursively into the pit... */ + if ((trap = t_at(u.ux, u.uy)) != 0) dotrap(trap, 0); + break; + + case ROLLING_BOULDER_TRAP: + seetrap(trap); + pline("Click! You trigger a rolling boulder trap!"); + if(!launch_obj(BOULDER, trap->launch.x, trap->launch.y, + trap->launch2.x,trap->launch2.y, ROLL)) { + deltrap(trap); + newsym(u.ux,u.uy); /* get rid of trap symbol */ + pline("Fortunately for you, no boulder was released."); + } + break; + + case MAGIC_PORTAL: + seetrap(trap); + domagicportal(trap); + break; + + default: + seetrap(trap); + impossible("You hit a trap of type %u", trap->ttyp); + } +} + +#ifdef STEED +STATIC_OVL int +steedintrap(trap, otmp) +struct trap *trap; +struct obj *otmp; +{ + struct monst *mtmp = u.usteed; + struct permonst *mptr; + int tt; + boolean in_sight; + boolean trapkilled = FALSE; + boolean steedhit = FALSE; + + if (!u.usteed || !trap) return 0; + mptr = mtmp->data; + tt = trap->ttyp; + mtmp->mx = u.ux; + mtmp->my = u.uy; + + in_sight = !Blind; + switch (tt) { + case ARROW_TRAP: + if(!otmp) { + impossible("steed hit by non-existant arrow?"); + return 0; + } + if(thitm(8, mtmp, otmp, 0)) trapkilled = TRUE; + steedhit = TRUE; + break; + case DART_TRAP: + if(!otmp) { + impossible("steed hit by non-existant dart?"); + return 0; + } + if(thitm(7, mtmp, otmp, 0)) trapkilled = TRUE; + steedhit = TRUE; + break; + case SLP_GAS_TRAP: + if (!resists_sleep(mtmp) && !breathless(mptr) && + !mtmp->msleeping && mtmp->mcanmove) { + mtmp->mcanmove = 0; + mtmp->mfrozen = rnd(25); + if (in_sight) { + pline("%s suddenly falls asleep!", + Monnam(mtmp)); + } + } + steedhit = TRUE; + break; + case PIT: + case SPIKED_PIT: + if (mtmp->mhp <= 0 || + thitm(0, mtmp, (struct obj *)0, + rnd((tt == PIT) ? 6 : 10))) + trapkilled = TRUE; + steedhit = TRUE; + break; + case POLY_TRAP: + if (!resists_magm(mtmp)) { + if (!resist(mtmp, WAND_CLASS, 0, NOTELL)) { + (void) newcham(mtmp, (struct permonst *)0, FALSE); + if (!can_saddle(mtmp) || !can_ride(mtmp)) { + dismount_steed(DISMOUNT_POLY); + } else { + You("have to adjust yourself in the saddle on %s.", + x_monnam(mtmp, + mtmp->mnamelth ? ARTICLE_NONE : ARTICLE_A, + (char *)0, SUPPRESS_SADDLE, FALSE)); + } + + } + steedhit = TRUE; + break; + default: + return 0; + } + } + if(trapkilled) { + dismount_steed(DISMOUNT_POLY); + return 2; + } + else if(steedhit) return 1; + else return 0; +} +#endif /*STEED*/ + +/* some actions common to both player and monsters for triggered landmine */ +STATIC_OVL void +blow_up_landmine(trap) +struct trap *trap; +{ + scatter(trap->tx, trap->ty, 4, + MAY_DESTROY | MAY_HIT | MAY_FRACTURE | VIS_EFFECTS, + (struct obj *)0); + del_engr_at(trap->tx, trap->ty); + wake_nearto(trap->tx, trap->ty, 400); + if (IS_DOOR(levl[trap->tx][trap->ty].typ)) + levl[trap->tx][trap->ty].doormask = D_BROKEN; + /* TODO: destroy drawbridge if present; + sometimes delete trap instead of always leaving a pit */ + trap->ttyp = PIT; /* explosion creates a pit */ + trap->madeby_u = FALSE; /* resulting pit isn't yours */ +} + +#endif /* OVLB */ +#ifdef OVL3 + +/* + * Move obj from (x1,y1) to (x2,y2) + * + * Return 0 if no object was launched. + * 1 if an object was launched and placed somewhere. + * 2 if an object was launched, but used up. + */ +int +launch_obj(otyp, x1, y1, x2, y2, style) +short otyp; +register int x1,y1,x2,y2; +int style; +{ + register struct monst *mtmp; + register struct obj *otmp, *otmp2; + register int dx,dy; + struct obj *singleobj; + boolean used_up = FALSE; + boolean otherside = FALSE; + int dist; + int tmp; + int delaycnt = 0; + + otmp = sobj_at(otyp, x1, y1); + /* Try the other side too, for rolling boulder traps */ + if (!otmp && otyp == BOULDER) { + otherside = TRUE; + otmp = sobj_at(otyp, x2, y2); + } + if (!otmp) return 0; + if (otherside) { /* swap 'em */ + int tx, ty; + + tx = x1; ty = y1; + x1 = x2; y1 = y2; + x2 = tx; y2 = ty; + } + + if (otmp->quan == 1L) { + obj_extract_self(otmp); + singleobj = otmp; + otmp = (struct obj *) 0; + } else { + singleobj = splitobj(otmp, otmp->quan - 1L); + obj_extract_self(singleobj); + } + newsym(x1,y1); + /* in case you're using a pick-axe to chop the boulder that's being + launched (perhaps a monster triggered it), destroy context so that + next dig attempt never thinks you're resuming previous effort */ + if ((otyp == BOULDER || otyp == STATUE) && + singleobj->ox == digging.pos.x && singleobj->oy == digging.pos.y) + (void) memset((genericptr_t)&digging, 0, sizeof digging); + + dist = distmin(x1,y1,x2,y2); + bhitpos.x = x1; + bhitpos.y = y1; + dx = sgn(x2 - x1); + dy = sgn(y2 - y1); + switch (style) { + case ROLL: + delaycnt = 2; + /* fall through */ + default: + if (!delaycnt) delaycnt = 1; + if (!cansee(bhitpos.x,bhitpos.y)) curs_on_u(); + tmp_at(DISP_FLASH, obj_to_glyph(singleobj)); + tmp_at(bhitpos.x, bhitpos.y); + } + + /* Set the object in motion */ + while(dist-- > 0 && !used_up) { + struct trap *t; + tmp_at(bhitpos.x, bhitpos.y); + tmp = delaycnt; + + /* dstage@u.washington.edu -- Delay only if hero sees it */ + if (cansee(bhitpos.x, bhitpos.y)) + while (tmp-- > 0) delay_output(); + + bhitpos.x += dx; + bhitpos.y += dy; + t = t_at(bhitpos.x, bhitpos.y); + + if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) { + if (otyp == BOULDER && throws_rocks(mtmp->data)) { + if (rn2(3)) { + pline("%s snatches the boulder.", + Monnam(mtmp)); + (void) mpickobj(mtmp, singleobj); + used_up = TRUE; + break; + } + } + if (ohitmon(mtmp,singleobj, + (style==ROLL) ? -1 : dist, FALSE)) { + used_up = TRUE; + break; + } + } else if (bhitpos.x == u.ux && bhitpos.y == u.uy) { + if (multi) nomul(0); + if (thitu(9 + singleobj->spe, + dmgval(singleobj, &youmonst), + singleobj, (char *)0)) + stop_occupation(); + } + if (style == ROLL) { + if (down_gate(bhitpos.x, bhitpos.y) != -1) { + if (ship_object(singleobj, bhitpos.x, bhitpos.y, FALSE)){ + used_up = TRUE; + break; + } + } + if (t && otyp == BOULDER) { + switch(t->ttyp) { + case LANDMINE: + if (rn2(10) > 2) { + pline( + "KAABLAMM!!!%s", + cansee(bhitpos.x, bhitpos.y) ? + " The rolling boulder triggers a land mine." : ""); + deltrap(t); + del_engr_at(bhitpos.x,bhitpos.y); + place_object(singleobj, bhitpos.x, bhitpos.y); + fracture_rock(singleobj); + scatter(bhitpos.x,bhitpos.y, 4, + MAY_DESTROY|MAY_HIT|MAY_FRACTURE|VIS_EFFECTS, + (struct obj *)0); + if (cansee(bhitpos.x,bhitpos.y)) + newsym(bhitpos.x,bhitpos.y); + used_up = TRUE; + } + break; + case LEVEL_TELEP: + case TELEP_TRAP: + if (cansee(bhitpos.x, bhitpos.y)) + pline("Suddenly the rolling boulder disappears!"); + else + You_hear("a rumbling stop abruptly."); + if (t->ttyp == TELEP_TRAP) + rloco(singleobj); + else { + int newlev = random_teleport_level(); + d_level dest; + + if (newlev == depth(&u.uz) || In_endgame(&u.uz)) + continue; + add_to_migration(singleobj); + get_level(&dest, newlev); + singleobj->ox = dest.dnum; + singleobj->oy = dest.dlevel; + singleobj->owornmask = (long)MIGR_RANDOM; + } + seetrap(t); + used_up = TRUE; + break; + } + if (used_up) break; + } + if (flooreffects(singleobj, bhitpos.x, bhitpos.y, "fall")) { + used_up = TRUE; + break; + } + if (otyp == BOULDER && + (otmp2 = sobj_at(BOULDER, bhitpos.x, bhitpos.y)) != 0) { + char *bmsg = + " as one boulder sets another in motion"; + You_hear("a loud crash%s!", + cansee(bhitpos.x, bhitpos.y) ? bmsg : ""); + obj_extract_self(otmp2); + place_object(singleobj, bhitpos.x, bhitpos.y); + singleobj = otmp2; + otmp2 = (struct obj *)0; + wake_nearto(bhitpos.x, bhitpos.y, 10*10); + } + } + if (otyp == BOULDER && closed_door(bhitpos.x,bhitpos.y)) { + if (cansee(bhitpos.x, bhitpos.y)) + pline_The("boulder crashes through a door."); + levl[bhitpos.x][bhitpos.y].doormask = D_BROKEN; + } + } + tmp_at(DISP_END, 0); + if (!used_up) { + place_object(singleobj, x2,y2); + newsym(x2,y2); + return 1; + } else + return 2; +} +#endif /* OVL3 */ +#ifdef OVLB + +void +seetrap(trap) + register struct trap *trap; +{ + if(!trap->tseen) { + trap->tseen = 1; + newsym(trap->tx, trap->ty); + } +} + +#endif /* OVLB */ +#ifdef OVL3 + +STATIC_OVL int +mkroll_launch(ttmp, x, y, otyp, ocount) +struct trap *ttmp; +xchar x,y; +short otyp; +long ocount; +{ + struct obj *otmp; + register int tmp; + schar dx,dy; + int distance; + coord cc; + coord bcc; + int trycount = 0; + boolean success = FALSE; + int mindist = 4; + + if (ttmp->ttyp == ROLLING_BOULDER_TRAP) mindist = 2; + distance = rn1(5,4); /* 4..8 away */ + tmp = rn2(8); /* randomly pick a direction to try first */ + while (distance >= mindist) { + dx = xdir[tmp]; + dy = ydir[tmp]; + cc.x = x; cc.y = y; + /* Prevent boulder from being placed on water */ + if (ttmp->ttyp == ROLLING_BOULDER_TRAP + && is_pool(x+distance*dx,y+distance*dy)) + success = FALSE; + else success = isclearpath(&cc, distance, dx, dy); + if (ttmp->ttyp == ROLLING_BOULDER_TRAP) { + boolean success_otherway; + bcc.x = x; bcc.y = y; + success_otherway = isclearpath(&bcc, distance, + -(dx), -(dy)); + if (!success_otherway) success = FALSE; + } + if (success) break; + if (++tmp > 7) tmp = 0; + if ((++trycount % 8) == 0) --distance; + } + if (!success) { + /* create the trap without any ammo, launch pt at trap location */ + cc.x = bcc.x = x; + cc.y = bcc.y = y; + } else { + otmp = mksobj(otyp, TRUE, FALSE); + otmp->quan = ocount; + otmp->owt = weight(otmp); + place_object(otmp, cc.x, cc.y); + stackobj(otmp); + } + ttmp->launch.x = cc.x; + ttmp->launch.y = cc.y; + if (ttmp->ttyp == ROLLING_BOULDER_TRAP) { + ttmp->launch2.x = bcc.x; + ttmp->launch2.y = bcc.y; + } else + ttmp->launch_otyp = otyp; + newsym(ttmp->launch.x, ttmp->launch.y); + return 1; +} + +STATIC_OVL boolean +isclearpath(cc,distance,dx,dy) +coord *cc; +int distance; +schar dx,dy; +{ + uchar typ; + xchar x, y; + + x = cc->x; + y = cc->y; + while (distance-- > 0) { + x += dx; + y += dy; + typ = levl[x][y].typ; + if (!isok(x,y) || !ZAP_POS(typ) || closed_door(x,y)) + return FALSE; + } + cc->x = x; + cc->y = y; + return TRUE; +} +#endif /* OVL3 */ +#ifdef OVL1 + +int +mintrap(mtmp) +register struct monst *mtmp; +{ + register struct trap *trap = t_at(mtmp->mx, mtmp->my); + boolean trapkilled = FALSE; + struct permonst *mptr = mtmp->data; + struct obj *otmp; + + if (!trap) { + mtmp->mtrapped = 0; /* perhaps teleported? */ + } else if (mtmp->mtrapped) { /* is currently in the trap */ + if (!trap->tseen && + cansee(mtmp->mx, mtmp->my) && canseemon(mtmp) && + (trap->ttyp == SPIKED_PIT || trap->ttyp == BEAR_TRAP || + trap->ttyp == HOLE || trap->ttyp == PIT || + trap->ttyp == WEB)) { + /* If you come upon an obviously trapped monster, then + * you must be able to see the trap it's in too. + */ + seetrap(trap); + } + + if (!rn2(40)) { + if (sobj_at(BOULDER, mtmp->mx, mtmp->my) && + (trap->ttyp == PIT || trap->ttyp == SPIKED_PIT)) { + if (!rn2(2)) { + mtmp->mtrapped = 0; + if (canseemon(mtmp)) + pline("%s pulls free...", Monnam(mtmp)); + fill_pit(mtmp->mx, mtmp->my); + } + } else { + mtmp->mtrapped = 0; + } + } else if (metallivorous(mptr)) { + if (trap->ttyp == BEAR_TRAP) { + if (canseemon(mtmp)) + pline("%s eats a bear trap!", Monnam(mtmp)); + deltrap(trap); + mtmp->meating = 5; + mtmp->mtrapped = 0; + } else if (trap->ttyp == SPIKED_PIT) { + if (canseemon(mtmp)) + pline("%s munches on some spikes!", Monnam(mtmp)); + trap->ttyp = PIT; + mtmp->meating = 5; + } + } + } else { + register int tt = trap->ttyp; + boolean in_sight, tear_web, see_it, + inescapable = ((tt == HOLE || tt == PIT) && + In_sokoban(&u.uz) && !trap->madeby_u); + const char *fallverb; + + if (!inescapable && + ((mtmp->mtrapseen & (1 << (tt-1))) != 0 || + (tt == HOLE && !mindless(mtmp->data)))) { + /* it has been in such a trap - perhaps it escapes */ + if(rn2(4)) return(0); + } else { + mtmp->mtrapseen |= (1 << (tt-1)); + } + /* Monster is aggravated by being trapped by you. + Recognizing who made the trap isn't completely + unreasonable; everybody has their own style. */ + if (trap->madeby_u && rnl(5)) setmangry(mtmp); + + /* bug? `in_sight' ought to be split to distinguish between + trap_in_sight and can_see_victim to handle invisible monsters */ + in_sight = canseemon(mtmp); + switch (tt) { + case ARROW_TRAP: + otmp = mksobj(ARROW, TRUE, FALSE); + otmp->quan = 1L; + otmp->owt = weight(otmp); + otmp->opoisoned = 0; + if (in_sight) seetrap(trap); + if(thitm(8, mtmp, otmp, 0)) trapkilled = TRUE; + break; + case DART_TRAP: + otmp = mksobj(DART, TRUE, FALSE); + otmp->quan = 1L; + otmp->owt = weight(otmp); + if (!rn2(6)) otmp->opoisoned = 1; + if (in_sight) seetrap(trap); + if(thitm(7, mtmp, otmp, 0)) trapkilled = TRUE; + break; + case ROCKTRAP: + otmp = mksobj(ROCK, TRUE, FALSE); + otmp->quan = 1L; + otmp->owt = weight(otmp); + if (in_sight) seetrap(trap); + if (thitm(0, mtmp, otmp, d(2, 6))) + trapkilled = TRUE; + break; + + case SQKY_BOARD: + if(is_flyer(mptr)) break; + /* stepped on a squeaky board */ + if (in_sight) { + pline("A board beneath %s squeaks loudly.", mon_nam(mtmp)); + seetrap(trap); + } else + You_hear("a distant squeak."); + /* wake up nearby monsters */ + wake_nearto(mtmp->mx, mtmp->my, 40); + break; + + case BEAR_TRAP: + if(mptr->msize > MZ_SMALL && + !amorphous(mptr) && !is_flyer(mptr) && + !is_whirly(mptr) && !unsolid(mptr)) { + mtmp->mtrapped = 1; + if(in_sight) { + pline("%s is caught in %s bear trap!", + Monnam(mtmp), a_your[trap->madeby_u]); + seetrap(trap); + } else { + if((mptr == &mons[PM_OWLBEAR] + || mptr == &mons[PM_BUGBEAR]) + && flags.soundok) + You_hear("the roaring of an angry bear!"); + } + } + break; + + case SLP_GAS_TRAP: + if (!resists_sleep(mtmp) && !breathless(mptr) && + !mtmp->msleeping && mtmp->mcanmove) { + mtmp->mcanmove = 0; + mtmp->mfrozen = rnd(25); + if (in_sight) { + pline("%s suddenly falls asleep!", + Monnam(mtmp)); + seetrap(trap); + } + } + break; + + case RUST_TRAP: + { + struct obj *target; + + if (in_sight) + seetrap(trap); + switch (rn2(5)) { + case 0: + if (in_sight) + pline("%s %s on the %s!", A_gush_of_water_hits, + mon_nam(mtmp), mbodypart(mtmp, HEAD)); + target = which_armor(mtmp, W_ARMH); + (void) rust_dmg(target, "helmet", 1, TRUE, mtmp); + break; + case 1: + if (in_sight) + pline("%s %s's left %s!", A_gush_of_water_hits, + mon_nam(mtmp), mbodypart(mtmp, ARM)); + target = which_armor(mtmp, W_ARMS); + if (rust_dmg(target, "shield", 1, TRUE, mtmp)) + break; + target = MON_WEP(mtmp); + if (target && bimanual(target)) + erode_obj(target, FALSE, TRUE); +glovecheck: target = which_armor(mtmp, W_ARMG); + (void) rust_dmg(target, "gauntlets", 1, TRUE, mtmp); + break; + case 2: + if (in_sight) + pline("%s %s's right %s!", A_gush_of_water_hits, + mon_nam(mtmp), mbodypart(mtmp, ARM)); + erode_obj(MON_WEP(mtmp), FALSE, TRUE); + goto glovecheck; + default: + if (in_sight) + pline("%s %s!", A_gush_of_water_hits, + mon_nam(mtmp)); + for (otmp=mtmp->minvent; otmp; otmp = otmp->nobj) + (void) snuff_lit(otmp); + target = which_armor(mtmp, W_ARMC); + if (target) + (void) rust_dmg(target, cloak_simple_name(target), + 1, TRUE, mtmp); + else { + target = which_armor(mtmp, W_ARM); + if (target) + (void) rust_dmg(target, "armor", 1, TRUE, mtmp); +#ifdef TOURIST + else { + target = which_armor(mtmp, W_ARMU); + (void) rust_dmg(target, "shirt", 1, TRUE, mtmp); + } +#endif + } + } + if (mptr == &mons[PM_IRON_GOLEM]) { + if (in_sight) + pline("%s falls to pieces!", Monnam(mtmp)); + else if(mtmp->mtame) + pline("May %s rust in peace.", + mon_nam(mtmp)); + mondied(mtmp); + if (mtmp->mhp <= 0) + trapkilled = TRUE; + } else if (mptr == &mons[PM_GREMLIN] && rn2(3)) { + (void)split_mon(mtmp, (struct monst *)0); + } + break; + } + case FIRE_TRAP: + mfiretrap: + see_it = cansee(mtmp->mx, mtmp->my); + if (in_sight) + pline("A %s erupts from the %s under %s!", + tower_of_flame, + surface(mtmp->mx,mtmp->my), mon_nam(mtmp)); + else if (see_it) /* evidently `mtmp' is invisible */ + You("see a %s erupt from the %s!", + tower_of_flame, surface(mtmp->mx,mtmp->my)); + + if (resists_fire(mtmp)) { + if (in_sight) { + shieldeff(mtmp->mx,mtmp->my); + pline("%s is uninjured.", Monnam(mtmp)); + } + } else { + int num = d(2,4); + + if (thitm(0, mtmp, (struct obj *)0, num)) + trapkilled = TRUE; + else + /* we know mhp is at least `num' below mhpmax, + so no (mhp > mhpmax) check is needed here */ + mtmp->mhpmax -= rn2(num + 1); + } + if (burnarmor(mtmp) || rn2(3)) { + (void) destroy_mitem(mtmp, SCROLL_CLASS, AD_FIRE); + (void) destroy_mitem(mtmp, SPBOOK_CLASS, AD_FIRE); + (void) destroy_mitem(mtmp, POTION_CLASS, AD_FIRE); + } + if (burn_floor_paper(mtmp->mx, mtmp->my, see_it, FALSE) && + !see_it && distu(mtmp->mx, mtmp->my) <= 3*3) + You("smell smoke."); + if (is_ice(mtmp->mx,mtmp->my)) + melt_ice(mtmp->mx,mtmp->my); + if (see_it) seetrap(trap); + break; + + case PIT: + case SPIKED_PIT: + fallverb = "falls"; + if (is_flyer(mptr) || is_floater(mptr) || + (mtmp->wormno && count_wsegs(mtmp) > 5) || + is_clinger(mptr)) { + if (!inescapable) break; /* avoids trap */ + fallverb = "is dragged"; /* sokoban pit */ + } + if (!passes_walls(mptr)) + mtmp->mtrapped = 1; + if (in_sight) { + pline("%s %s into %s pit!", + Monnam(mtmp), fallverb, + a_your[trap->madeby_u]); + if (mptr == &mons[PM_PIT_VIPER] || mptr == &mons[PM_PIT_FIEND]) + pline("How pitiful. Isn't that the pits?"); + seetrap(trap); + } + mselftouch(mtmp, "Falling, ", FALSE); + if (mtmp->mhp <= 0 || + thitm(0, mtmp, (struct obj *)0, + rnd((tt == PIT) ? 6 : 10))) + trapkilled = TRUE; + break; + case HOLE: + case TRAPDOOR: + if (!Can_fall_thru(&u.uz)) { + impossible("mintrap: %ss cannot exist on this level.", + defsyms[trap_to_defsym(tt)].explanation); + break; /* don't activate it after all */ + } + if (is_flyer(mptr) || is_floater(mptr) || + mptr == &mons[PM_WUMPUS] || + (mtmp->wormno && count_wsegs(mtmp) > 5) || + mptr->msize >= MZ_HUGE) { + if (inescapable) { /* sokoban hole */ + if (in_sight) { + pline("%s seems to be yanked down!", + Monnam(mtmp)); + /* suppress message in mlevel_tele_trap() */ + in_sight = FALSE; + seetrap(trap); + } + } else + break; + } + /* Fall through */ + case LEVEL_TELEP: + case MAGIC_PORTAL: + { + int mlev_res; + mlev_res = mlevel_tele_trap(mtmp, trap, + inescapable, in_sight); + if (mlev_res) return(mlev_res); + } + break; + + case TELEP_TRAP: + mtele_trap(mtmp, trap, in_sight); + break; + + case WEB: + /* Monster in a web. */ + if (webmaker(mptr)) break; + if (amorphous(mptr) || is_whirly(mptr) || unsolid(mptr)){ + if(acidic(mptr) || + mptr == &mons[PM_GELATINOUS_CUBE] || + mptr == &mons[PM_FIRE_ELEMENTAL]) { + if (in_sight) + pline("%s %s %s spider web!", + Monnam(mtmp), + (mptr == &mons[PM_FIRE_ELEMENTAL]) ? + "burns" : "dissolves", + a_your[trap->madeby_u]); + deltrap(trap); + newsym(mtmp->mx, mtmp->my); + break; + } + if (in_sight) { + pline("%s flows through %s spider web.", + Monnam(mtmp), + a_your[trap->madeby_u]); + seetrap(trap); + } + break; + } + tear_web = FALSE; + switch (monsndx(mptr)) { + case PM_OWLBEAR: /* Eric Backus */ + case PM_BUGBEAR: + if (!in_sight) { + You_hear("the roaring of a confused bear!"); + mtmp->mtrapped = 1; + break; + } + /* fall though */ + default: + if (mptr->mlet == S_GIANT || + (mptr->mlet == S_DRAGON && + extra_nasty(mptr)) || /* excl. babies */ + (mtmp->wormno && count_wsegs(mtmp) > 5)) { + tear_web = TRUE; + } else if (in_sight) { + pline("%s is caught in %s spider web.", + Monnam(mtmp), + a_your[trap->madeby_u]); + seetrap(trap); + } + mtmp->mtrapped = tear_web ? 0 : 1; + break; + /* this list is fairly arbitrary; it deliberately + excludes wumpus & giant/ettin zombies/mummies */ + case PM_TITANOTHERE: + case PM_BALUCHITHERIUM: + case PM_PURPLE_WORM: + case PM_JABBERWOCK: + case PM_IRON_GOLEM: + case PM_BALROG: + case PM_KRAKEN: + case PM_MASTODON: + tear_web = TRUE; + break; + } + if (tear_web) { + if (in_sight) + pline("%s tears through %s spider web!", + Monnam(mtmp), a_your[trap->madeby_u]); + deltrap(trap); + newsym(mtmp->mx, mtmp->my); + } + break; + + case STATUE_TRAP: + break; + + case MAGIC_TRAP: + /* A magic trap. Monsters usually immune. */ + if (!rn2(21)) goto mfiretrap; + break; + case ANTI_MAGIC: + break; + + case LANDMINE: + if(rn2(3)) + break; /* monsters usually don't set it off */ + if(is_flyer(mptr)) { + boolean already_seen = trap->tseen; + if (in_sight && !already_seen) { + pline("A trigger appears in a pile of soil below %s.", mon_nam(mtmp)); + seetrap(trap); + } + if (rn2(3)) break; + if (in_sight) { + newsym(mtmp->mx, mtmp->my); + pline_The("air currents set %s off!", + already_seen ? "a land mine" : "it"); + } + } else if(in_sight) { + newsym(mtmp->mx, mtmp->my); + pline("KAABLAMM!!! %s triggers %s land mine!", + Monnam(mtmp), a_your[trap->madeby_u]); + } + if (!in_sight) + pline("Kaablamm! You hear an explosion in the distance!"); + blow_up_landmine(trap); + if(thitm(0, mtmp, (struct obj *)0, rnd(16))) + trapkilled = TRUE; + else { + /* monsters recursively fall into new pit */ + if (mintrap(mtmp) == 2) trapkilled=TRUE; + } + if (unconscious()) { + multi = -1; + nomovemsg="The explosion awakens you!"; + } + break; + + case POLY_TRAP: + if (resists_magm(mtmp)) { + shieldeff(mtmp->mx, mtmp->my); + } else if (!resist(mtmp, WAND_CLASS, 0, NOTELL)) { + (void) newcham(mtmp, (struct permonst *)0, FALSE); + if (in_sight) seetrap(trap); + } + break; + + case ROLLING_BOULDER_TRAP: + if (!is_flyer(mptr)) { + newsym(mtmp->mx,mtmp->my); + if (in_sight) + pline("Click! %s triggers %s.", Monnam(mtmp), + trap->tseen ? + "a rolling boulder trap" : + something); + if (launch_obj(BOULDER, trap->launch.x, trap->launch.y, + trap->launch2.x, trap->launch2.y, ROLL)) { + if (in_sight) trap->tseen = TRUE; + else You_hear(Hallucination ? + "someone bowling." : + "rumbling in the distance."); + if (mtmp->mhp <= 0) trapkilled = TRUE; + } else { + deltrap(trap); + newsym(mtmp->mx,mtmp->my); + } + } + break; + + default: + impossible("Some monster encountered a strange trap of type %d.", tt); + } + } + if(trapkilled) return 2; + return mtmp->mtrapped; +} + +#endif /* OVL1 */ +#ifdef OVLB + +/* Combine cockatrice checks into single functions to avoid repeating code. */ +void +instapetrify(str) +const char *str; +{ + if (Stone_resistance) return; + if (poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM)) + return; + You("turn to stone..."); + killer_format = KILLED_BY; + killer = str; + done(STONING); +} + +void +minstapetrify(mon,byplayer) +struct monst *mon; +boolean byplayer; +{ + if (resists_ston(mon)) return; + if (cansee(mon->mx, mon->my)) + pline("%s turns to stone.", Monnam(mon)); + if (poly_when_stoned(mon->data)) { + mon_to_stone(mon); + return; + } + if (byplayer) { + stoned = TRUE; + xkilled(mon,0); + } else monstone(mon); +} + +void +selftouch(arg) +const char *arg; +{ + char kbuf[BUFSZ]; + + if(uwep && uwep->otyp == CORPSE && touch_petrifies(&mons[uwep->corpsenm]) + && !Stone_resistance) { + pline("%s touch the %s corpse.", arg, + mons[uwep->corpsenm].mname); + Sprintf(kbuf, "%s corpse", an(mons[uwep->corpsenm].mname)); + instapetrify(kbuf); + } + /* Or your secondary weapon, if wielded */ + if(u.twoweap && uswapwep && uswapwep->otyp == CORPSE && + touch_petrifies(&mons[uswapwep->corpsenm]) && !Stone_resistance){ + pline("%s touch the %s corpse.", arg, + mons[uswapwep->corpsenm].mname); + Sprintf(kbuf, "%s corpse", an(mons[uswapwep->corpsenm].mname)); + instapetrify(kbuf); + } +} + +void +mselftouch(mon,arg,byplayer) +struct monst *mon; +const char *arg; +boolean byplayer; +{ + struct obj *mwep = MON_WEP(mon); + + if (mwep && mwep->otyp == CORPSE && touch_petrifies(&mons[mwep->corpsenm])) { + if (cansee(mon->mx, mon->my)) { + pline("%s%s touches the %s corpse.", + arg ? arg : "", arg ? mon_nam(mon) : Monnam(mon), + mons[mwep->corpsenm].mname); + } + minstapetrify(mon, byplayer); + } +} + +void +float_up() +{ + if(u.utrap) { + if(u.utraptype == TT_PIT) { + u.utrap = 0; + You("float up, out of the pit!"); + vision_full_recalc = 1; /* vision limits change */ + fill_pit(u.ux, u.uy); + } else if (u.utraptype == TT_INFLOOR) { + Your("body pulls upward, but your %s are still stuck.", + makeplural(body_part(LEG))); + } else { + You("float up, only your %s is still stuck.", + body_part(LEG)); + } + } + else if(Is_waterlevel(&u.uz)) + pline("It feels as though you've lost some weight."); + else if(u.uinwater) + spoteffects(TRUE); + else if(u.uswallow) + You(is_animal(u.ustuck->data) ? + "float away from the %s." : + "spiral up into %s.", + is_animal(u.ustuck->data) ? + surface(u.ux, u.uy) : + mon_nam(u.ustuck)); + else if (Hallucination) + pline("Up, up, and awaaaay! You're walking on air!"); + else if(Is_airlevel(&u.uz)) + You("gain control over your movements."); + else + You("start to float in the air!"); +#ifdef STEED + if (u.usteed && !is_floater(u.usteed->data) && + !is_flyer(u.usteed->data)) { + if (Lev_at_will) + pline("%s magically floats up!", Monnam(u.usteed)); + else { + You("cannot stay on %s.", mon_nam(u.usteed)); + dismount_steed(DISMOUNT_GENERIC); + } + } +#endif + return; +} + +void +fill_pit(x, y) +int x, y; +{ + struct obj *otmp; + struct trap *t; + + if ((t = t_at(x, y)) && + ((t->ttyp == PIT) || (t->ttyp == SPIKED_PIT)) && + (otmp = sobj_at(BOULDER, x, y))) { + obj_extract_self(otmp); + (void) flooreffects(otmp, x, y, "settle"); + } +} + +int +float_down(hmask, emask) +long hmask, emask; /* might cancel timeout */ +{ + register struct trap *trap = (struct trap *)0; + d_level current_dungeon_level; + boolean no_msg = FALSE; + + HLevitation &= ~hmask; + ELevitation &= ~emask; + if(Levitation) return(0); /* maybe another ring/potion/boots */ + + if (Punished && !carried(uball) && + (is_pool(uball->ox, uball->oy) || + ((trap = t_at(uball->ox, uball->oy)) && + ((trap->ttyp == PIT) || (trap->ttyp == SPIKED_PIT) || + (trap->ttyp == TRAPDOOR) || (trap->ttyp == HOLE))))) { + u.ux0 = u.ux; + u.uy0 = u.uy; + u.ux = uball->ox; + u.uy = uball->oy; + movobj(uchain, uball->ox, uball->oy); + newsym(u.ux0, u.uy0); + vision_full_recalc = 1; /* in case the hero moved. */ + } + /* check for falling into pool - added by GAN 10/20/86 */ + if(!Flying) { + /* kludge alert: + * drown() and lava_effects() print various messages almost + * every time they're called which conflict with the "fall + * into" message below. Thus, we want to avoid printing + * confusing, duplicate or out-of-order messages. + * Use knowledge of the two routines as a hack -- this + * should really be handled differently -dlc + */ + if(is_pool(u.ux,u.uy) && !Wwalking && !Swimming && !u.uinwater) + no_msg = drown(); + + if(is_lava(u.ux,u.uy)) { + (void) lava_effects(); + no_msg = TRUE; + } + } + if (!trap) { + trap = t_at(u.ux,u.uy); + if(Is_airlevel(&u.uz)) + You("begin to tumble in place."); + else if (Is_waterlevel(&u.uz) && !no_msg) + You_feel("heavier."); + /* u.uinwater msgs already in spoteffects()/drown() */ + else if (!u.uinwater && !no_msg) { +#ifdef STEED + if (!(emask & W_SADDLE)) +#endif + { + boolean sokoban_trap = (In_sokoban(&u.uz) && trap); + if (Hallucination) + pline("Bummer! You've %s.", + is_pool(u.ux,u.uy) ? + "splashed down" : sokoban_trap ? "crashed" : + "hit the ground"); + else { + if (!sokoban_trap) + You("float gently to the %s.", + surface(u.ux, u.uy)); + else { + /* Justification elsewhere for Sokoban traps + * is based on air currents. This is + * consistent with that. + * The unexpected additional force of the + * air currents once leviation + * ceases knocks you off your feet. + */ + You("fall over."); + losehp(rnd(2), "dangerous winds", KILLED_BY); +#ifdef STEED + if (u.usteed) dismount_steed(DISMOUNT_FELL); +#endif + selftouch("As you fall, you"); + } + } + } + } + } + + /* can't rely on u.uz0 for detecting trap door-induced level change; + it gets changed to reflect the new level before we can check it */ + assign_level(¤t_dungeon_level, &u.uz); + + if(trap) + switch(trap->ttyp) { + case STATUE_TRAP: + break; + case HOLE: + case TRAPDOOR: + if(!Can_fall_thru(&u.uz) || u.ustuck) + break; + /* fall into next case */ + default: + dotrap(trap, 0); + } + + if (!Is_airlevel(&u.uz) && !Is_waterlevel(&u.uz) && !u.uswallow && + /* falling through trap door calls goto_level, + and goto_level does its own pickup() call */ + on_level(&u.uz, ¤t_dungeon_level)) + (void) pickup(1); + return 1; +} + +STATIC_OVL void +dofiretrap(box) +struct obj *box; /* null for floor trap */ +{ + boolean see_it = !Blind; + int num; + +/* Bug: for box case, the equivalent of burn_floor_paper() ought + * to be done upon its contents. + */ + + if ((box && !carried(box)) ? is_pool(box->ox, box->oy) : Underwater) { + pline("A cascade of steamy bubbles erupts from %s!", + the(box ? xname(box) : surface(u.ux,u.uy))); + if (Fire_resistance) You("are uninjured."); + else losehp(rnd(3), "boiling water", KILLED_BY); + return; + } + pline("A %s %s from %s!", tower_of_flame, + box ? "bursts" : "erupts", + the(box ? xname(box) : surface(u.ux,u.uy))); + if (Fire_resistance) { + shieldeff(u.ux, u.uy); + num = rn2(2); + } else { + num = d(2,4); + if (u.uhpmax > u.ulevel) + u.uhpmax -= rn2(min(u.uhpmax,num + 1)), flags.botl = 1; + } + if (!num) + You("are uninjured."); + else + losehp(num, tower_of_flame, KILLED_BY_AN); + burn_away_slime(); + + if (burnarmor(&youmonst) || rn2(3)) { + destroy_item(SCROLL_CLASS, AD_FIRE); + destroy_item(SPBOOK_CLASS, AD_FIRE); + destroy_item(POTION_CLASS, AD_FIRE); + } + if (!box && burn_floor_paper(u.ux, u.uy, see_it, TRUE) && !see_it) + You("smell paper burning."); + if (is_ice(u.ux, u.uy)) + melt_ice(u.ux, u.uy); +} + +STATIC_OVL void +domagictrap() +{ + register int fate = rnd(20); + + /* What happened to the poor sucker? */ + + if (fate < 10) { + /* Most of the time, it creates some monsters. */ + register int cnt = rnd(4); + + if (!resists_blnd(&youmonst)) { + You("are momentarily blinded by a flash of light!"); + make_blinded((long)rn1(5,10),FALSE); + if (!Blind) Your(vision_clears); + } else if (!Blind) { + You("see a flash of light!"); + } else + You_hear("a deafening roar!"); + while(cnt--) + (void) makemon((struct permonst *) 0, u.ux, u.uy, NO_MM_FLAGS); + } + else + switch (fate) { + + case 10: + case 11: + /* sometimes nothing happens */ + break; + case 12: /* a flash of fire */ + dofiretrap((struct obj *)0); + break; + + /* odd feelings */ + case 13: pline("A shiver runs up and down your %s!", + body_part(SPINE)); + break; + case 14: You_hear(Hallucination ? + "the moon howling at you." : + "distant howling."); + break; + case 15: if (on_level(&u.uz, &qstart_level)) + You_feel("%slike the prodigal son.", + (flags.female || (Upolyd && is_neuter(youmonst.data))) ? + "oddly " : ""); + else + You("suddenly yearn for %s.", + Hallucination ? "Cleveland" : + (In_quest(&u.uz) || at_dgn_entrance("The Quest")) ? + "your nearby homeland" : + "your distant homeland"); + break; + case 16: Your("pack shakes violently!"); + break; + case 17: You(Hallucination ? + "smell hamburgers." : + "smell charred flesh."); + break; + case 18: You_feel("tired."); + break; + + /* very occasionally something nice happens. */ + + case 19: + /* tame nearby monsters */ + { register int i,j; + register struct monst *mtmp; + + (void) adjattrib(A_CHA,1,FALSE); + for(i = -1; i <= 1; i++) for(j = -1; j <= 1; j++) { + if(!isok(u.ux+i, u.uy+j)) continue; + mtmp = m_at(u.ux+i, u.uy+j); + if(mtmp) + (void) tamedog(mtmp, (struct obj *)0); + } + break; + } + + case 20: + /* uncurse stuff */ + { struct obj pseudo; + long save_conf = HConfusion; + + pseudo = zeroobj; /* neither cursed nor blessed */ + pseudo.otyp = SCR_REMOVE_CURSE; + HConfusion = 0L; + (void) seffects(&pseudo); + HConfusion = save_conf; + break; + } + default: break; + } +} + +void +water_damage(obj, force, here) +register struct obj *obj; +register boolean force, here; +{ + /* Scrolls, spellbooks, potions, weapons and + pieces of armor may get affected by the water */ + for (; obj; obj = (here ? obj->nexthere : obj->nobj)) { + + (void) snuff_lit(obj); + + if(obj->otyp == CAN_OF_GREASE && obj->spe > 0) { + continue; + } else if(obj->greased) { + if (force || !rn2(2)) obj->greased = 0; + } else if(Is_container(obj) && !Is_box(obj) && + (obj->otyp != OILSKIN_SACK || (obj->cursed && !rn2(3)))) { + water_damage(obj->cobj, force, FALSE); + } else if (!force && (Luck + 5) > rn2(20)) { + /* chance per item of sustaining damage: + * max luck (full moon): 5% + * max luck (elsewhen): 10% + * avg luck (Luck==0): 75% + * awful luck (Luck<-4): 100% + */ + continue; + } else if (obj->oclass == SCROLL_CLASS) { +#ifdef MAIL + if (obj->otyp != SCR_MAIL) +#endif + { + obj->otyp = SCR_BLANK_PAPER; + obj->spe = 0; + } + } else if (obj->oclass == SPBOOK_CLASS) { + if (obj->otyp == SPE_BOOK_OF_THE_DEAD) + pline("Steam rises from %s.", the(xname(obj))); + else obj->otyp = SPE_BLANK_PAPER; + } else if (obj->oclass == POTION_CLASS) { + if (obj->odiluted) { + obj->otyp = POT_WATER; + obj->blessed = obj->cursed = 0; + obj->odiluted = 0; + } else if (obj->otyp != POT_WATER) + obj->odiluted++; + } else if (is_rustprone(obj) && obj->oeroded < MAX_ERODE && + !(obj->oerodeproof || (obj->blessed && !rnl(4)))) { + /* all metal stuff and armor except (body armor + protected by oilskin cloak) */ + if(obj->oclass != ARMOR_CLASS || obj != uarm || + !uarmc || uarmc->otyp != OILSKIN_CLOAK || + (uarmc->cursed && !rn2(3))) + obj->oeroded++; + } + } +} + +/* + * This function is potentially expensive - rolling + * inventory list multiple times. Luckily it's seldom needed. + * Returns TRUE if disrobing made player unencumbered enough to + * crawl out of the current predicament. + */ +STATIC_OVL boolean +emergency_disrobe(lostsome) +boolean *lostsome; +{ + int invc = inv_cnt(); + + + while (near_capacity() > (Punished ? UNENCUMBERED : SLT_ENCUMBER)) { + register struct obj *obj, *otmp = (struct obj *)0; + register int i; + + /* Pick a random object */ + if (invc > 0) { + i = rn2(invc); + for (obj = invent; obj; obj = obj->nobj) { + /* + * Undroppables are: body armor, boots, gloves, + * amulets, and rings because of the time and effort + * in removing them + loadstone and other cursed stuff + * for obvious reasons. + */ + if (!((obj->otyp == LOADSTONE && obj->cursed) || + obj == uamul || obj == uleft || obj == uright || + obj == ublindf || obj == uarm || obj == uarmc || + obj == uarmg || obj == uarmf || +#ifdef TOURIST + obj == uarmu || +#endif + (obj->cursed && (obj == uarmh || obj == uarms)) || + welded(obj))) + otmp = obj; + /* reached the mark and found some stuff to drop? */ + if (--i < 0 && otmp) break; + + /* else continue */ + } + } +#ifndef GOLDOBJ + if (!otmp) { + /* Nothing available left to drop; try gold */ + if (u.ugold) { + pline("In desperation, you drop your purse."); + /* Hack: gold is not in the inventory, so make a gold object + * and put it at the head of the inventory list. + */ + obj = mkgoldobj(u.ugold); /* removes from u.ugold */ + u.ugold = obj->quan; /* put the gold back */ + assigninvlet(obj); /* might end up as NOINVSYM */ + obj->nobj = invent; + invent = obj; + *lostsome = TRUE; + dropx(obj); + continue; /* Try again */ + } + /* We can't even drop gold! */ + return (FALSE); + } +#else + if (!otmp) return (FALSE); /* nothing to drop! */ +#endif + if (otmp->owornmask && otmp != uball) remove_worn_item(otmp); + *lostsome = TRUE; + dropx(otmp); + invc--; + } + return(TRUE); +} + +/* + * return(TRUE) == player relocated + */ +boolean +drown() +{ + boolean inpool_ok = FALSE, crawl_ok; + int i, x, y; + + /* happily wading in the same contiguous pool */ + if (u.uinwater && is_pool(u.ux-u.dx,u.uy-u.dy) && + (Swimming || Amphibious)) { + /* water effects on objects every now and then */ + if (!rn2(5)) inpool_ok = TRUE; + else return(FALSE); + } + + if (!u.uinwater) { + You("%s into the water%c", + Is_waterlevel(&u.uz) ? "plunge" : "fall", + Amphibious || Swimming ? '.' : '!'); + if (!Swimming && !Is_waterlevel(&u.uz)) + You("sink like %s.", + Hallucination ? "the Titanic" : "a rock"); + } + + water_damage(invent, FALSE, FALSE); + + if (u.umonnum == PM_GREMLIN && rn2(3)) + (void)split_mon(&youmonst, (struct monst *)0); + else if (u.umonnum == PM_IRON_GOLEM) { + You("rust!"); + i = d(2,6); + if (u.mhmax > i) u.mhmax -= i; + losehp(i, "rusting away", KILLED_BY); + } + if (inpool_ok) return(FALSE); + + if ((i = number_leashed()) > 0) { + pline_The("leash%s slip%s loose.", + (i > 1) ? "es" : "", + (i > 1) ? "" : "s"); + unleash_all(); + } + + if (Amphibious || Swimming) { + if (Amphibious) { + if (flags.verbose) + pline("But you aren't drowning."); + if (!Is_waterlevel(&u.uz)) { + if (Hallucination) + Your("keel hits the bottom."); + else + You("touch bottom."); + } + } + if (Punished) { + unplacebc(); + placebc(); + } + vision_recalc(2); /* unsee old position */ + u.uinwater = 1; + under_water(1); + vision_full_recalc = 1; + return(FALSE); + } + if((Teleportation || can_teleport(youmonst.data)) && + (Teleport_control || rn2(3) < Luck+2)) { + You("attempt a teleport spell."); /* utcsri!carroll */ + (void) dotele(); + if(!is_pool(u.ux,u.uy)) + return(TRUE); + } +#ifdef STEED + if (u.usteed) { + dismount_steed(DISMOUNT_GENERIC); + if(!is_pool(u.ux,u.uy)) + return(TRUE); + } +#endif + crawl_ok = FALSE; + /* look around for a place to crawl to */ + for (i = 0; i < 100; i++) { + x = rn1(3,u.ux - 1); + y = rn1(3,u.uy - 1); + if (goodpos(x, y, &youmonst)) { + crawl_ok = TRUE; + goto crawl; + } + } + /* one more scan */ + for (x = u.ux - 1; x <= u.ux + 1; x++) + for (y = u.uy - 1; y <= u.uy + 1; y++) + if (goodpos(x, y, &youmonst)) { + crawl_ok = TRUE; + goto crawl; + } +crawl:; + if (crawl_ok) { + boolean lost = FALSE; + /* time to do some strip-tease... */ + boolean succ = Is_waterlevel(&u.uz) ? TRUE : + emergency_disrobe(&lost); + + You("try to crawl out of the water."); + if (lost) + You("dump some of your gear to lose weight..."); + if (succ) { + pline("Pheew! That was close."); + teleds(x,y); + return(TRUE); + } + /* still too much weight */ + pline("But in vain."); + } + u.uinwater = 1; + You("drown."); + killer_format = KILLED_BY_AN; + killer = (levl[u.ux][u.uy].typ == POOL || Is_medusa_level(&u.uz)) ? + "pool of water" : "moat"; + done(DROWNING); + /* oops, we're still alive. better get out of the water. */ + while (!safe_teleds()) { + pline("You're still drowning."); + done(DROWNING); + } + u.uinwater = 0; + You("find yourself back %s.", Is_waterlevel(&u.uz) ? + "in an air bubble" : "on land"); + return(TRUE); +} + +void +drain_en(n) +register int n; +{ + if (!u.uenmax) return; + You_feel("your magical energy drain away!"); + u.uen -= n; + if(u.uen < 0) { + u.uenmax += u.uen; + if(u.uenmax < 0) u.uenmax = 0; + u.uen = 0; + } + flags.botl = 1; +} + +int +dountrap() /* disarm a trap */ +{ + if (near_capacity() >= HVY_ENCUMBER) { + pline("You're too strained to do that."); + return 0; + } + if ((nohands(youmonst.data) && !webmaker(youmonst.data)) || !youmonst.data->mmove) { + pline("And just how do you expect to do that?"); + return 0; + } else if (u.ustuck && sticks(youmonst.data)) { + pline("You'll have to let go of %s first.", mon_nam(u.ustuck)); + return 0; + } + if (u.ustuck || (welded(uwep) && bimanual(uwep))) { + Your("%s seem to be too busy for that.", + makeplural(body_part(HAND))); + return 0; + } + return untrap(FALSE); +} +#endif /* OVLB */ +#ifdef OVL2 + +/* Probability of disabling a trap. Helge Hafting */ +STATIC_OVL int +untrap_prob(ttmp) +struct trap *ttmp; +{ + int chance = 3; + + /* Only spiders know how to deal with webs reliably */ + if (ttmp->ttyp == WEB && !webmaker(youmonst.data)) + chance = 30; + if (Confusion || Hallucination) chance++; + if (Blind) chance++; + if (Stunned) chance += 2; + if (Fumbling) chance *= 2; + /* Your own traps are better known than others. */ + if (ttmp && ttmp->madeby_u) chance--; + if (Role_if(PM_ROGUE)) { + if (rn2(2 * MAXULEV) < u.ulevel) chance--; + if (u.uhave.questart && chance > 1) chance--; + } else if (Role_if(PM_RANGER) && chance > 1) chance--; + return rn2(chance); +} + +/* Replace trap with object(s). Helge Hafting */ +STATIC_OVL void +cnv_trap_obj(otyp, cnt, ttmp) +int otyp; +int cnt; +struct trap *ttmp; +{ + struct obj *otmp = mksobj(otyp, TRUE, FALSE); + otmp->quan=cnt; + otmp->owt = weight(otmp); + /* Only dart traps are capable of being poisonous */ + if (otyp != DART) + otmp->opoisoned = 0; + place_object(otmp, ttmp->tx, ttmp->ty); + /* Sell your own traps only... */ + if (ttmp->madeby_u) sellobj(otmp, ttmp->tx, ttmp->ty); + stackobj(otmp); + newsym(ttmp->tx, ttmp->ty); + deltrap(ttmp); +} + +/* while attempting to disarm an adjacent trap, we've fallen into it */ +STATIC_OVL void +move_into_trap(ttmp) +struct trap *ttmp; +{ + int bc; + xchar x = ttmp->tx, y = ttmp->ty, bx, by, cx, cy; + boolean unused; + + /* we know there's no monster in the way, and we're not trapped */ + if (!Punished || drag_ball(x, y, &bc, &bx, &by, &cx, &cy, &unused)) { + u.ux0 = u.ux, u.uy0 = u.uy; + u.ux = x, u.uy = y; + u.umoved = TRUE; + newsym(u.ux0, u.uy0); + vision_recalc(1); + check_leash(u.ux0, u.uy0); + if (Punished) move_bc(0, bc, bx, by, cx, cy); + spoteffects(TRUE); /* dotrap() */ + exercise(A_WIS, FALSE); + } +} + +/* 0: doesn't even try + * 1: tries and fails + * 2: succeeds + */ +STATIC_OVL int +try_disarm(ttmp, force_failure) +struct trap *ttmp; +boolean force_failure; +{ + struct monst *mtmp = m_at(ttmp->tx,ttmp->ty); + int ttype = ttmp->ttyp; + boolean under_u = (!u.dx && !u.dy); + boolean holdingtrap = (ttype == BEAR_TRAP || ttype == WEB); + + /* Test for monster first, monsters are displayed instead of trap. */ + if (mtmp && (!mtmp->mtrapped || !holdingtrap)) { + pline("%s is in the way.", Monnam(mtmp)); + return 0; + } + /* We might be forced to move onto the trap's location. */ + if (sobj_at(BOULDER, ttmp->tx, ttmp->ty) + && !Passes_walls && !under_u) { + There("is a boulder in your way."); + return 0; + } + /* untrappable traps are located on the ground. */ + if (!can_reach_floor()) { +#ifdef STEED + if (u.usteed && P_SKILL(P_RIDING) < P_BASIC) + You("aren't skilled enough to reach from %s.", + mon_nam(u.usteed)); + else +#endif + You("are unable to reach the %s!", + defsyms[trap_to_defsym(ttype)].explanation); + return 0; + } + + /* Will our hero succeed? */ + if (force_failure || untrap_prob(ttmp)) { + if (rnl(5)) { + pline("Whoops..."); + if (mtmp) { /* must be a trap that holds monsters */ + if (ttype == BEAR_TRAP) { + if (mtmp->mtame) abuse_dog(mtmp); + if ((mtmp->mhp -= rnd(4)) <= 0) killed(mtmp); + } else if (ttype == WEB) { + if (!webmaker(youmonst.data)) { + struct trap *ttmp2 = maketrap(u.ux, u.uy, WEB); + if (ttmp2) { + pline_The("webbing sticks to you. You're caught too!"); + dotrap(ttmp2, NOWEBMSG); + } + } else + pline("%s remains entangled.", Monnam(mtmp)); + } + } else if (under_u) { + dotrap(ttmp, 0); + } else { + move_into_trap(ttmp); + } + } else { + pline("%s %s is difficult to %s.", + ttmp->madeby_u ? "Your" : under_u ? "This" : "That", + defsyms[trap_to_defsym(ttype)].explanation, + (ttype == WEB) ? "remove" : "disarm"); + } + return 1; + } + return 2; +} + +STATIC_OVL void +reward_untrap(ttmp, mtmp) +struct trap *ttmp; +struct monst *mtmp; +{ + if (!ttmp->madeby_u) { + if (rnl(10) < 8 && !mtmp->mpeaceful && + !mindless(mtmp->data) && + mtmp->data->mlet != S_HUMAN) { + mtmp->mpeaceful = 1; + set_malign(mtmp); /* reset alignment */ + pline("%s is grateful.", Monnam(mtmp)); + } + /* Helping someone out of a trap is a nice thing to do, + * A lawful may be rewarded, but not too often. */ + if (!rn2(3) && !rnl(8) && u.ualign.type == A_LAWFUL) { + adjalign(1); + You_feel("that you did the right thing."); + } + } +} + +STATIC_OVL int +disarm_holdingtrap(ttmp) /* Helge Hafting */ +struct trap *ttmp; +{ + struct monst *mtmp; + int fails = try_disarm(ttmp, FALSE); + + if (fails < 2) return fails; + + /* ok, disarm it. */ + + /* untrap the monster, if any. + There's no need for a cockatrice test, only the trap is touched */ + if ((mtmp = m_at(ttmp->tx,ttmp->ty)) != 0) { + mtmp->mtrapped = 0; + You("remove %s %s from %s.", the_your[ttmp->madeby_u], + (ttmp->ttyp == BEAR_TRAP) ? "bear trap" : "webbing", + mon_nam(mtmp)); + reward_untrap(ttmp, mtmp); + } else { + if (ttmp->ttyp == BEAR_TRAP) { + You("disarm %s bear trap.", the_your[ttmp->madeby_u]); + cnv_trap_obj(BEARTRAP, 1, ttmp); + } else /* if (ttmp->ttyp == WEB) */ { + You("succeed in removing %s web.", the_your[ttmp->madeby_u]); + deltrap(ttmp); + } + } + newsym(u.ux + u.dx, u.uy + u.dy); + return 1; +} + +STATIC_OVL int +disarm_landmine(ttmp) /* Helge Hafting */ +struct trap *ttmp; +{ + int fails = try_disarm(ttmp, FALSE); + + if (fails < 2) return fails; + You("disarm %s land mine.", the_your[ttmp->madeby_u]); + cnv_trap_obj(LAND_MINE, 1, ttmp); + return 1; +} + +/* getobj will filter down to cans of grease and known potions of oil */ +static NEARDATA const char oil[] = { ALL_CLASSES, TOOL_CLASS, POTION_CLASS, 0 }; + +/* it may not make much sense to use grease on floor boards, but so what? */ +STATIC_OVL int +disarm_squeaky_board(ttmp) +struct trap *ttmp; +{ + struct obj *obj; + boolean bad_tool; + int fails; + + obj = getobj(oil, "untrap with"); + if (!obj) return 0; + + bad_tool = (obj->cursed || + ((obj->otyp != POT_OIL || obj->lamplit) && + (obj->otyp != CAN_OF_GREASE || !obj->spe))); + + fails = try_disarm(ttmp, bad_tool); + if (fails < 2) return fails; + + /* successfully used oil or grease to fix squeaky board */ + if (obj->otyp == CAN_OF_GREASE) { + check_unpaid(obj); + obj->spe--; + } else { + useup(obj); /* oil */ + makeknown(POT_OIL); + } + You("repair the squeaky board."); /* no madeby_u */ + deltrap(ttmp); + newsym(u.ux + u.dx, u.uy + u.dy); + more_experienced(1, 5); + newexplevel(); + return 1; +} + +/* removes traps that shoot arrows, darts, etc. */ +STATIC_OVL int +disarm_shooting_trap(ttmp, otyp) +struct trap *ttmp; +int otyp; +{ + int fails = try_disarm(ttmp, FALSE); + + if (fails < 2) return fails; + You("disarm %s trap.", the_your[ttmp->madeby_u]); + cnv_trap_obj(otyp, 50-rnl(50), ttmp); + return 1; +} + +/* Is the weight too heavy? + * Formula as in near_capacity() & check_capacity() */ +STATIC_OVL int +try_lift(mtmp, ttmp, wt, stuff) +struct monst *mtmp; +struct trap *ttmp; +int wt; +boolean stuff; +{ + int wc = weight_cap(); + + if (((wt * 2) / wc) >= HVY_ENCUMBER) { + pline("%s is %s for you to lift.", Monnam(mtmp), + stuff ? "carrying too much" : "too heavy"); + if (!ttmp->madeby_u && !mtmp->mpeaceful && mtmp->mcanmove && + !mindless(mtmp->data) && + mtmp->data->mlet != S_HUMAN && rnl(10) < 3) { + mtmp->mpeaceful = 1; + set_malign(mtmp); /* reset alignment */ + pline("%s thinks it was nice of you to try.", Monnam(mtmp)); + } + return 0; + } + return 1; +} + +/* Help trapped monster (out of a (spiked) pit) */ +STATIC_OVL int +help_monster_out(mtmp, ttmp) +struct monst *mtmp; +struct trap *ttmp; +{ + int wt; + struct obj *otmp; + boolean uprob; + + /* + * This works when levitating too -- consistent with the ability + * to hit monsters while levitating. + * + * Should perhaps check that our hero has arms/hands at the + * moment. Helping can also be done by engulfing... + * + * Test the monster first - monsters are displayed before traps. + */ + if (!mtmp->mtrapped) { + pline("%s isn't trapped.", Monnam(mtmp)); + return 0; + } + /* Do you have the necessary capacity to lift anything? */ + if (check_capacity((char *)0)) return 1; + + /* Will our hero succeed? */ + if ((uprob = untrap_prob(ttmp)) && !mtmp->msleeping && mtmp->mcanmove) { + You("try to reach out your %s, but %s backs away skeptically.", + makeplural(body_part(ARM)), + mon_nam(mtmp)); + return 1; + } + + + /* is it a cockatrice?... */ + if (touch_petrifies(mtmp->data) && !uarmg && !Stone_resistance) { + You("grab the trapped %s using your bare %s.", + mtmp->data->mname, makeplural(body_part(HAND))); + + if (poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM)) + display_nhwindow(WIN_MESSAGE, FALSE); + else { + char kbuf[BUFSZ]; + + Sprintf(kbuf, "trying to help %s out of a pit", + an(mtmp->data->mname)); + instapetrify(kbuf); + return 1; + } + } + /* need to do cockatrice check first if sleeping or paralyzed */ + if (uprob) { + You("try to grab %s, but cannot get a firm grasp.", + mon_nam(mtmp)); + if (mtmp->msleeping) { + mtmp->msleeping = 0; + pline("%s awakens.", Monnam(mtmp)); + } + return 1; + } + + You("reach out your %s and grab %s.", + makeplural(body_part(ARM)), mon_nam(mtmp)); + + if (mtmp->msleeping) { + mtmp->msleeping = 0; + pline("%s awakens.", Monnam(mtmp)); + } else if (mtmp->mfrozen && !rn2(mtmp->mfrozen)) { + /* After such manhandling, perhaps the effect wears off */ + mtmp->mcanmove = 1; + mtmp->mfrozen = 0; + pline("%s stirs.", Monnam(mtmp)); + } + + /* is the monster too heavy? */ + wt = inv_weight() + mtmp->data->cwt; + if (!try_lift(mtmp, ttmp, wt, FALSE)) return 1; + + /* is the monster with inventory too heavy? */ + for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj) + wt += otmp->owt; + if (!try_lift(mtmp, ttmp, wt, TRUE)) return 1; + + You("pull %s out of the pit.", mon_nam(mtmp)); + mtmp->mtrapped = 0; + fill_pit(mtmp->mx, mtmp->my); + reward_untrap(ttmp, mtmp); + return 1; +} + +int +untrap(force) +boolean force; +{ + register struct obj *otmp; + register boolean confused = (Confusion > 0 || Hallucination > 0); + register int x,y; + int ch; + struct trap *ttmp; + struct monst *mtmp; + boolean trap_skipped = FALSE; + + if(!getdir((char *)0)) return(0); + x = u.ux + u.dx; + y = u.uy + u.dy; + + if ((ttmp = t_at(x,y)) && ttmp->tseen) { + if (u.utrap) { + You("cannot deal with traps while trapped!"); + return 1; + } + switch(ttmp->ttyp) { + case BEAR_TRAP: + case WEB: + return disarm_holdingtrap(ttmp); + case LANDMINE: + return disarm_landmine(ttmp); + case SQKY_BOARD: + return disarm_squeaky_board(ttmp); + case DART_TRAP: + return disarm_shooting_trap(ttmp, DART); + case ARROW_TRAP: + return disarm_shooting_trap(ttmp, ARROW); + case PIT: + case SPIKED_PIT: + if (!u.dx && !u.dy) { + You("are already on the edge of the pit."); + return 0; + } + if (!(mtmp = m_at(x,y))) { + pline("Try filling the pit instead."); + return 0; + } + return help_monster_out(mtmp, ttmp); + default: + You("cannot disable %s trap.", (u.dx || u.dy) ? "that" : "this"); + return 0; + } /* end switch */ + } /* end if */ + + if(!u.dx && !u.dy) { + for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere) + if(Is_box(otmp)) { + There("is %s here.", doname(otmp)); + + switch (ynq("Check for traps?")) { + case 'q': return(0); + case 'n': continue; + } + + if((otmp->otrapped && (force || (!confused + && rn2(MAXULEV + 1 - u.ulevel) < 10))) + || (!force && confused && !rn2(3))) { + You("find a trap on %s!", the(xname(otmp))); + exercise(A_WIS, TRUE); + + switch (ynq("Disarm it?")) { + case 'q': return(1); + case 'n': trap_skipped = TRUE; continue; + } + + if(otmp->otrapped) { + exercise(A_DEX, TRUE); + ch = ACURR(A_DEX) + u.ulevel; + if (Role_if(PM_ROGUE)) ch *= 2; + if(!force && (confused || Fumbling || + rnd(75+level_difficulty()/2) > ch)) { + (void) chest_trap(otmp, FINGER, TRUE); + } else { + You("disarm it!"); + otmp->otrapped = 0; + } + } else pline("That %s was not trapped.", xname(otmp)); + return(1); + } else { + You("find no traps on %s.", the(xname(otmp))); + return(1); + } + } + + You(trap_skipped ? "find no other traps here." + : "know of no traps here."); + return(0); + } + + if ((mtmp = m_at(x,y)) && + mtmp->m_ap_type == M_AP_FURNITURE && + (mtmp->mappearance == S_hcdoor || + mtmp->mappearance == S_vcdoor) && + !Protection_from_shape_changers) { + + stumble_onto_mimic(mtmp); + return(1); + } + + if (!IS_DOOR(levl[x][y].typ)) { + if ((ttmp = t_at(x,y)) && ttmp->tseen) + You("cannot disable that trap."); + else + You("know of no traps there."); + return(0); + } + + switch (levl[x][y].doormask) { + case D_NODOOR: + You("%s no door there.", Blind ? "feel" : "see"); + return(0); + case D_ISOPEN: + pline("This door is safely open."); + return(0); + case D_BROKEN: + pline("This door is broken."); + return(0); + } + + if ((levl[x][y].doormask & D_TRAPPED + && (force || + (!confused && rn2(MAXULEV - u.ulevel + 11) < 10))) + || (!force && confused && !rn2(3))) { + You("find a trap on the door!"); + exercise(A_WIS, TRUE); + if (ynq("Disarm it?") != 'y') return(1); + if (levl[x][y].doormask & D_TRAPPED) { + ch = 15 + (Role_if(PM_ROGUE) ? u.ulevel*3 : u.ulevel); + exercise(A_DEX, TRUE); + if(!force && (confused || Fumbling || + rnd(75+level_difficulty()/2) > ch)) { + You("set it off!"); + b_trapped("door", FINGER); + levl[x][y].doormask = D_NODOOR; + unblock_point(x, y); + newsym(x, y); + /* (probably ought to charge for this damage...) */ + if (*in_rooms(x, y, SHOPBASE)) add_damage(x, y, 0L); + } else { + You("disarm it!"); + levl[x][y].doormask &= ~D_TRAPPED; + } + } else pline("This door was not trapped."); + return(1); + } else { + You("find no traps on the door."); + return(1); + } +} +#endif /* OVL2 */ +#ifdef OVLB + +/* only called when the player is doing something to the chest directly */ +boolean +chest_trap(obj, bodypart, disarm) +register struct obj *obj; +register int bodypart; +boolean disarm; +{ + register struct obj *otmp = obj, *otmp2; + char buf[80]; + const char *msg; + coord cc; + + if (get_obj_location(obj, &cc.x, &cc.y, 0)) /* might be carried */ + obj->ox = cc.x, obj->oy = cc.y; + + otmp->otrapped = 0; /* trap is one-shot; clear flag first in case + chest kills you and ends up in bones file */ + You(disarm ? "set it off!" : "trigger a trap!"); + display_nhwindow(WIN_MESSAGE, FALSE); + if (Luck > -13 && rn2(13+Luck) > 7) { /* saved by luck */ + /* trap went off, but good luck prevents damage */ + switch (rn2(13)) { + case 12: + case 11: msg = "explosive charge is a dud"; break; + case 10: + case 9: msg = "electric charge is grounded"; break; + case 8: + case 7: msg = "flame fizzles out"; break; + case 6: + case 5: + case 4: msg = "poisoned needle misses"; break; + case 3: + case 2: + case 1: + case 0: msg = "gas cloud blows away"; break; + default: impossible("chest disarm bug"); msg = (char *)0; + break; + } + if (msg) pline("But luckily the %s!", msg); + } else { + switch(rn2(20) ? ((Luck >= 13) ? 0 : rn2(13-Luck)) : rn2(26)) { + case 25: + case 24: + case 23: + case 22: + case 21: { + struct monst *shkp = 0; + long loss = 0L; + boolean costly, insider; + register xchar ox = obj->ox, oy = obj->oy; + + /* the obj location need not be that of player */ + costly = (costly_spot(ox, oy) && + (shkp = shop_keeper(*in_rooms(ox, oy, + SHOPBASE))) != (struct monst *)0); + insider = (*u.ushops && inside_shop(u.ux, u.uy) && + *in_rooms(ox, oy, SHOPBASE) == *u.ushops); + + pline("%s explodes!", The(xname(obj))); + Sprintf(buf, "exploding %s", xname(obj)); + + if(costly) + loss += stolen_value(obj, ox, oy, + (boolean)shkp->mpeaceful, TRUE); + delete_contents(obj); + for(otmp = level.objects[u.ux][u.uy]; + otmp; otmp = otmp2) { + otmp2 = otmp->nexthere; + if(costly) + loss += stolen_value(otmp, otmp->ox, + otmp->oy, (boolean)shkp->mpeaceful, + TRUE); + delobj(otmp); + } + wake_nearby(); + losehp(d(6,6), buf, KILLED_BY_AN); + exercise(A_STR, FALSE); + if(costly && loss) { + if(insider) + You("owe %ld zorkmids for objects destroyed.", + loss); + else { + You("caused %ld zorkmids worth of damage!", + loss); + make_angry_shk(shkp, ox, oy); + } + } + return TRUE; + } + case 20: + case 19: + case 18: + case 17: + pline("A cloud of noxious gas billows from %s.", + the(xname(obj))); + poisoned("gas cloud", A_STR, "cloud of poison gas",15); + exercise(A_CON, FALSE); + break; + case 16: + case 15: + case 14: + case 13: + You_feel("a needle prick your %s.",body_part(bodypart)); + poisoned("needle", A_CON, "poisoned needle",10); + exercise(A_CON, FALSE); + break; + case 12: + case 11: + case 10: + case 9: + dofiretrap(obj); + break; + case 8: + case 7: + case 6: { + int dmg; + + You("are jolted by a surge of electricity!"); + if(Shock_resistance) { + shieldeff(u.ux, u.uy); + You("don't seem to be affected."); + dmg = 0; + } else + dmg = d(4, 4); + destroy_item(RING_CLASS, AD_ELEC); + destroy_item(WAND_CLASS, AD_ELEC); + if (dmg) losehp(dmg, "electric shock", KILLED_BY_AN); + break; + } + case 5: + case 4: + case 3: + if (!Free_action) { + pline("Suddenly you are frozen in place!"); + nomul(-d(5, 6)); + exercise(A_DEX, FALSE); + nomovemsg = You_can_move_again; + } else You("momentarily stiffen."); + break; + case 2: + case 1: + case 0: + pline("A cloud of %s gas billows from %s.", + hcolor((char *)0), + the(xname(obj))); + if(!Stunned) { + if (Hallucination) + pline("What a groovy feeling!"); + else if (Blind) + You("%s and get dizzy...", + stagger(youmonst.data, "stagger")); + else + You("%s and your vision blurs...", + stagger(youmonst.data, "stagger")); + } + make_stunned(HStun + rn1(7, 16),FALSE); + make_hallucinated(HHallucination + rn1(5, 16),FALSE,0L); + break; + default: impossible("bad chest trap"); + break; + } + bot(); /* to get immediate botl re-display */ + } + + return FALSE; +} + +#endif /* OVLB */ +#ifdef OVL0 + +struct trap * +t_at(x,y) +register int x, y; +{ + register struct trap *trap = ftrap; + while(trap) { + if(trap->tx == x && trap->ty == y) return(trap); + trap = trap->ntrap; + } + return((struct trap *)0); +} + +#endif /* OVL0 */ +#ifdef OVLB + +void +deltrap(trap) +register struct trap *trap; +{ + register struct trap *ttmp; + + if(trap == ftrap) + ftrap = ftrap->ntrap; + else { + for(ttmp = ftrap; ttmp->ntrap != trap; ttmp = ttmp->ntrap) ; + ttmp->ntrap = trap->ntrap; + } + dealloc_trap(trap); +} + +boolean delfloortrap(ttmp) +register struct trap *ttmp; +{ + /* Destroy a trap that emanates from the floor. */ + /* some of these are arbitrary -dlc */ + if (ttmp && ((ttmp->ttyp == SQKY_BOARD) || + (ttmp->ttyp == BEAR_TRAP) || + (ttmp->ttyp == LANDMINE) || + (ttmp->ttyp == FIRE_TRAP) || + (ttmp->ttyp == PIT) || + (ttmp->ttyp == SPIKED_PIT) || + (ttmp->ttyp == HOLE) || + (ttmp->ttyp == TRAPDOOR) || + (ttmp->ttyp == TELEP_TRAP) || + (ttmp->ttyp == LEVEL_TELEP) || + (ttmp->ttyp == WEB) || + (ttmp->ttyp == MAGIC_TRAP) || + (ttmp->ttyp == ANTI_MAGIC))) { + register struct monst *mtmp; + + if (ttmp->tx == u.ux && ttmp->ty == u.uy) { + u.utrap = 0; + u.utraptype = 0; + } else if ((mtmp = m_at(ttmp->tx, ttmp->ty)) != 0) { + mtmp->mtrapped = 0; + } + deltrap(ttmp); + return TRUE; + } else + return FALSE; +} + +/* used for doors (also tins). can be used for anything else that opens. */ +void +b_trapped(item, bodypart) +register const char *item; +register int bodypart; +{ + register int lvl = level_difficulty(); + int dmg = rnd(5 + (lvl < 5 ? lvl : 2+lvl/2)); + + pline("KABOOM!! %s was booby-trapped!", The(item)); + wake_nearby(); + losehp(dmg, "explosion", KILLED_BY_AN); + exercise(A_STR, FALSE); + if (bodypart) exercise(A_CON, FALSE); + make_stunned(HStun + dmg, TRUE); +} + +/* Monster is hit by trap. */ +/* Note: doesn't work if both obj and d_override are null */ +STATIC_OVL boolean +thitm(tlev, mon, obj, d_override) +register int tlev; +register struct monst *mon; +register struct obj *obj; +int d_override; +{ + register int strike; + register boolean trapkilled = FALSE; + + if (d_override) strike = 1; + else if (obj) strike = (find_mac(mon) + tlev + obj->spe <= rnd(20)); + else strike = (find_mac(mon) + tlev <= rnd(20)); + + /* Actually more accurate than thitu, which doesn't take + * obj->spe into account. + */ + if(!strike) { + if (cansee(mon->mx, mon->my)) + pline("%s is almost hit by %s!", Monnam(mon), + doname(obj)); + } else { + int dam = 1; + + if (obj && cansee(mon->mx, mon->my)) + pline("%s is hit by %s!", Monnam(mon), doname(obj)); + if (d_override) dam = d_override; + else if (obj) { + dam = dmgval(obj, mon); + if (dam < 1) dam = 1; + } + if ((mon->mhp -= dam) <= 0) { + int xx = mon->mx; + int yy = mon->my; + + monkilled(mon, "", AD_PHYS); + if (mon->mhp <= 0) { + newsym(xx, yy); + trapkilled = TRUE; + } + } + } + if (obj && (!strike || d_override)) { + place_object(obj, mon->mx, mon->my); + stackobj(obj); + } else if (obj) dealloc_obj(obj); + + return trapkilled; +} + +boolean +unconscious() +{ + return((boolean)(multi < 0 && (!nomovemsg || + u.usleep || + !strncmp(nomovemsg,"You regain con", 14) || + !strncmp(nomovemsg,"You are consci", 14)))); +} + +static char lava_killer[] = "molten lava"; + +boolean +lava_effects() +{ + register struct obj *obj, *obj2; + int dmg; + boolean usurvive; + + burn_away_slime(); + if (likes_lava(youmonst.data)) return FALSE; + + if (!Fire_resistance) { + if(Wwalking) { + dmg = d(6,6); + pline_The("lava here burns you!"); + if(dmg < u.uhp) { + losehp(dmg, lava_killer, KILLED_BY); + goto burn_stuff; + } + } else + You("fall into the lava!"); + + usurvive = Lifesaved || discover; +#ifdef WIZARD + if (wizard) usurvive = TRUE; +#endif + for(obj = invent; obj; obj = obj2) { + obj2 = obj->nobj; + if(is_organic(obj) && !obj->oerodeproof) { + if(obj->owornmask) { + if (usurvive) + Your("%s into flame!", aobjnam(obj, "burst")); + + if(obj == uarm) (void) Armor_gone(); + else if(obj == uarmc) (void) Cloak_off(); + else if(obj == uarmh) (void) Helmet_off(); + else if(obj == uarms) (void) Shield_off(); + else if(obj == uarmg) (void) Gloves_off(); + else if(obj == uarmf) (void) Boots_off(); +#ifdef TOURIST + else if(obj == uarmu) setnotworn(obj); +#endif + else if(obj == uleft) Ring_gone(obj); + else if(obj == uright) Ring_gone(obj); + else if(obj == ublindf) Blindf_off(obj); + else if(obj == uamul) Amulet_off(); + else if(obj == uwep) uwepgone(); + else if (obj == uquiver) uqwepgone(); + else if (obj == uswapwep) uswapwepgone(); + } + useupall(obj); + } + } + + /* s/he died... */ + u.uhp = -1; + killer_format = KILLED_BY; + killer = lava_killer; + You("burn to a crisp..."); + done(BURNING); + while (!safe_teleds()) { + pline("You're still burning."); + done(BURNING); + } + You("find yourself back on solid %s.", surface(u.ux, u.uy)); + return(TRUE); + } + + if (!Wwalking) { + u.utrap = rn1(4, 4) + (rn1(4, 12) << 8); + u.utraptype = TT_LAVA; + You("sink into the lava, but it only burns slightly!"); + if (u.uhp > 1) + losehp(1, lava_killer, KILLED_BY); + } + /* just want to burn boots, not all armor; destroy_item doesn't work on + armor anyway */ +burn_stuff: + if(uarmf && !uarmf->oerodeproof && is_organic(uarmf)) { + /* save uarmf value because Boots_off() sets uarmf to null */ + obj = uarmf; + Your("%s bursts into flame!", xname(obj)); + (void) Boots_off(); + useup(obj); + } + destroy_item(SCROLL_CLASS, AD_FIRE); + destroy_item(SPBOOK_CLASS, AD_FIRE); + destroy_item(POTION_CLASS, AD_FIRE); + return(FALSE); +} + +#endif /* OVLB */ + +/*trap.c*/