Files
nethack/src/trap.c
nethack.rankin eca287ab89 feedback from exploding potions of acid (trunk only)
water_damage() gave "A potion explodes!" when destroying potions of
acid even when it was a stack of multiple potions exploding.  The vague
fixes entry "grammar, spelling and other typos" covers this one....
2007-08-25 00:32:10 +00:00

4759 lines
132 KiB
C

/* SCCS Id: @(#)trap.c 3.5 2007/08/24 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
#include "hack.h"
extern const char * const destroy_strings[][3]; /* from zap.c */
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(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,BOOLEAN_P));
STATIC_DCL void FDECL(launch_drop_spot, (struct obj *, XCHAR_P, XCHAR_P));
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 char *FDECL(trapnote, (struct trap *,BOOLEAN_P));
#if 0
STATIC_DCL void FDECL(join_adjacent_pits, (struct trap *));
#endif
STATIC_DCL void FDECL(clear_conjoined_pits, (struct trap *));
#ifdef STEED
STATIC_OVL int FDECL(steedintrap, (struct trap *, struct obj *));
STATIC_OVL boolean FDECL(keep_saddle_with_steedcorpse,
(unsigned, struct obj *, struct obj *));
#endif
/* mintrap() should take a flags argument, but for time being we use this */
STATIC_VAR int force_mintrap = 0;
STATIC_VAR const char * const a_your[2] = { "a", "your" };
STATIC_VAR const char * const A_Your[2] = { "A", "Your" };
STATIC_VAR const char tower_of_flame[] = "tower of flame";
STATIC_VAR const char * const A_gush_of_water_hits = "A gush of water hits";
STATIC_VAR const char * const blindgas[6] =
{"humid", "odorless", "pungent", "chilling", "acrid", "biting"};
/* 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 %s",
materialnm[mat_idx], helm_simple_name(item));
}
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 * const action[] = { "smoulder", "rust", "rot", "corrode" };
static NEARDATA const char * const msg[] = { "burnt", "rusted", "rotten", "corroded" };
boolean vulnerable = FALSE;
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;
if (!vulnerable) {
if (flags.verbose) {
if (victim == &youmonst)
Your("%s %s not affected.", ostr, vtense(ostr, "are"));
else if (vismon)
pline("%s's %s %s not affected.", Monnam(victim), ostr,
vtense(ostr, "are"));
}
} else if (erosion < MAX_ERODE) {
if (grprot && otmp->greased) {
grease_protect(otmp,ostr,victim);
} else if (otmp->oerodeproof || (otmp->blessed && !rnl(4))) {
if (flags.verbose) {
if (victim == &youmonst)
pline("Somehow, your %s %s not affected.",
ostr, vtense(ostr, "are"));
else if (vismon)
pline("Somehow, %s's %s %s not affected.",
mon_nam(victim), ostr, vtense(ostr, "are"));
}
} else {
if (victim == &youmonst)
Your("%s %s%s!", ostr,
vtense(ostr, action[type]),
erosion+1 == MAX_ERODE ? " completely" :
erosion ? " further" : "");
else if (vismon)
pline("%s's %s %s%s!", Monnam(victim), ostr,
vtense(ostr, action[type]),
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 completely %s.", ostr,
vtense(ostr, Blind ? "feel" : "look"),
msg[type]);
else if (vismon)
pline("%s's %s %s completely %s.",
Monnam(victim), ostr,
vtense(ostr, "look"), msg[type]);
}
}
return(TRUE);
}
void
grease_protect(otmp,ostr,victim)
register struct obj *otmp;
register const char *ostr;
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, vtense(ostr, "are"), txt);
else if (vismon)
pline("%s's %s %s %s", Monnam(victim),
ostr, vtense(ostr, "are"), txt);
} else if ((victim == &youmonst) || vismon) {
pline("%s %s", Yobjnam2(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 SQKY_BOARD:
{
int tavail[12], tpick[12], tcnt = 0, k;
struct trap *t;
for (k = 0; k < 12; ++k)
tavail[k] = 0;
for (t = ftrap; t; t = t->ntrap)
if (t->ttyp == SQKY_BOARD)
tavail[t->tnote] = 1;
/* Now populate tpick with the available indexes */
for (k = 0; k < 12; ++k) {
if (tavail[k] == 0)
tpick[tcnt++] = k;
}
if (tcnt > 0)
ttmp->tnote = (short)tpick[rn2(tcnt)];
else
ttmp->tnote = (short)rn2(12); /* all in use anyway */
break;
}
case STATUE_TRAP: /* create a "living" statue */
{ struct monst *mtmp;
struct obj *otmp, *statue;
struct permonst *mptr;
int trycount = 10;
do { /* avoid ultimately hostile co-aligned unicorn */
mptr = &mons[rndmonnum()];
} while (--trycount > 0 && is_unicorn(mptr) &&
sgn(u.ualign.type) == sgn(mptr->maligntyp));
statue = mkcorpstat(STATUE, (struct monst *)0,
mptr, x, y, CORPSTAT_NONE);
mtmp = makemon(&mons[statue->corpsenm], 0, 0, MM_NOCOUNTBIRTH);
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 PIT:
case SPIKED_PIT:
ttmp->conjoined = 0;
/* fall through */
case HOLE:
case TRAPDOOR:
lev = &levl[x][y];
if (*in_rooms(x, y, SHOPBASE) &&
((typ == HOLE || typ == TRAPDOOR) ||
IS_DOOR(lev->typ) || IS_WALL(lev->typ)))
add_damage(x, y, /* schedule repair */
((IS_DOOR(lev->typ) || IS_WALL(lev->typ))
&& !context.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;
int newlevel, bottom;
/* KMH -- You can't escape the Sokoban level traps */
if(Blind && Levitation && !In_sokoban(&u.uz)) return;
bottom = dunlevs_in_dungeon(&u.uz);
/* when in the upper half of the quest, don't fall past the
middle "quest locate" level if hero hasn't been there yet */
if (In_quest(&u.uz)) {
int qlocate_depth = qlocate_level.dlevel;
/* deepest reached < qlocate implies current < qlocate */
if (dunlev_reached(&u.uz) < qlocate_depth)
bottom = qlocate_depth; /* early cut-off */
}
newlevel = dunlev(&u.uz); /* current level */
do {
newlevel++;
} while (!rn2(4) && newlevel < bottom);
if(td) {
struct trap *t = t_at(u.ux,u.uy);
feeltrap(t);
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) && !levl[u.ux][u.uy].candig)
|| Flying || is_clinger(youmonst.data)
|| (Inhell && !u.uevent.invoked && newlevel == bottom)
) {
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... ???
*
* Sequencing matters:
* create monster; if it fails, give up with statue intact;
* give "statue comes to life" message;
* if statue belongs to shop, have shk give "you owe" message;
* transfer statue contents to monster (after stolen_value());
* delete statue.
* [This ordering means that if the statue ends up wearing a cloak of
* invisibility or a mummy wrapping, the visibility checks might be
* wrong, but to avoid that we'd have to clone the statue contents
* first in order to give them to the monster before checking their
* shop status--it's not worth the hassle.]
*/
struct monst *
animate_statue(statue, x, y, cause, fail_reason)
struct obj *statue;
xchar x, y;
int cause;
int *fail_reason;
{
int mnum = statue->corpsenm;
struct permonst *mptr = &mons[mnum];
struct monst *mon = 0, *shkp;
struct obj *item;
coord cc;
boolean historic = (Role_if(PM_ARCHEOLOGIST) &&
(statue->spe & STATUE_HISTORIC) != 0),
golem_xform = FALSE, use_saved_traits;
const char *comes_to_life;
char statuename[BUFSZ], tmpbuf[BUFSZ];
static const char historic_statue_is_gone[] =
"that the historic statue is now gone";
if (cant_revive(&mnum, TRUE, statue)) {
/* mnum has changed; we won't be animating this statue as itself */
if (mnum != PM_DOPPELGANGER) mptr = &mons[mnum];
use_saved_traits = FALSE;
} else if (is_golem(mptr) && cause == ANIMATE_SPELL) {
/* statue of any golem hit by stone-to-flesh becomes flesh golem */
golem_xform = (mptr != &mons[PM_FLESH_GOLEM]);
mnum = PM_FLESH_GOLEM;
mptr = &mons[PM_FLESH_GOLEM];
use_saved_traits = (has_omonst(statue) && !golem_xform);
} else {
use_saved_traits = has_omonst(statue);
}
if (use_saved_traits) {
/* restore a petrified monster */
cc.x = x, cc.y = y;
mon = montraits(statue, &cc);
if (mon && mon->mtame && !mon->isminion)
wary_dog(mon, TRUE);
} else {
/* statues of unique monsters from bones or wishing end
up here (cant_revive() sets mnum to be doppelganger;
mptr reflects the original form for use by newcham()) */
if ((mnum == PM_DOPPELGANGER &&
mptr != &mons[PM_DOPPELGANGER]) ||
/* block quest guards from other roles */
(mptr->msound == MS_GUARDIAN &&
quest_info(MS_GUARDIAN) != mnum)) {
mon = makemon(&mons[PM_DOPPELGANGER], x, y,
NO_MINVENT | MM_NOCOUNTBIRTH | MM_ADJACENTOK);
/* if hero has protection from shape changers, cham field will
be NON_PM; otherwise, set form to match the statue */
if (mon && mon->cham >= LOW_PM)
(void) newcham(mon, mptr, FALSE, FALSE);
} else
mon = makemon(mptr, x, y, (cause == ANIMATE_SPELL) ?
(NO_MINVENT | MM_ADJACENTOK) : NO_MINVENT);
}
if (!mon) {
if (fail_reason)
*fail_reason = unique_corpstat(&mons[statue->corpsenm]) ?
AS_MON_IS_UNIQUE : AS_NO_MON;
return (struct monst *)0;
}
/* a non-montraits() statue might specify gender */
if (statue->spe & STATUE_MALE)
mon->female = FALSE;
else if (statue->spe & STATUE_FEMALE)
mon->female = TRUE;
/* if statue has been named, give same name to the monster */
if (has_oname(statue))
mon = christen_monst(mon, ONAME(statue));
/* mimic statue becomes seen mimic; other hiders won't be hidden */
if (mon->m_ap_type) seemimic(mon);
else mon->mundetected = FALSE;
mon->msleeping = 0;
if (cause == ANIMATE_NORMAL || cause == ANIMATE_SHATTER) {
/* trap always releases hostile monster */
mon->mtame = 0; /* (might be petrified pet tossed onto trap) */
mon->mpeaceful = 0;
set_malign(mon);
}
comes_to_life = !canspotmon(mon) ? "disappears" :
golem_xform ? "turns into flesh" :
(nonliving(mon->data) || is_vampshifter(mon)) ?
"moves" : "comes to life";
if ((x == u.ux && y == u.uy) || cause == ANIMATE_SPELL) {
/* "the|your|Manlobbi's statue [of a wombat]" */
shkp = shop_keeper(*in_rooms(mon->mx, mon->my, SHOPBASE));
Sprintf(statuename, "%s%s", shk_your(tmpbuf, statue),
(cause == ANIMATE_SPELL &&
/* avoid "of a shopkeeper" if it's Manlobbi himself
(if carried, it can't be unpaid--hence won't be
described as "Manlobbi's statue"--because there
wasn't any living shk when statue was picked up) */
(mon != shkp || carried(statue))) ? xname(statue) :
"statue");
pline("%s %s!", upstart(statuename), comes_to_life);
} else if (Hallucination) { /* They don't know it's a statue */
pline_The("%s suddenly seems more animated.", rndmonnam());
} else if (cause == ANIMATE_SHATTER) {
if (cansee(x, y))
Sprintf(statuename, "%s%s", shk_your(tmpbuf, statue),
xname(statue));
else
Strcpy(statuename, "a statue");
pline("Instead of shattering, %s suddenly %s!",
statuename, comes_to_life);
} else { /* cause == ANIMATE_NORMAL */
You("find %s posing as a statue.",
canspotmon(mon) ? a_monnam(mon) : something);
if (!canspotmon(mon) && Blind)
map_invisible(x,y);
stop_occupation();
}
/* if this isn't caused by a monster using a wand of striking,
there might be consequences for the hero */
if (!context.mon_moving) {
/* if statue is owned by a shop, hero will have to pay for it;
stolen_value gives a message (about debt or use of credit)
which refers to "it" so needs to follow a message describing
the object ("the statue comes to life" one above) */
if (cause != ANIMATE_NORMAL && costly_spot(x, y) &&
(shkp = shop_keeper(*in_rooms(x, y, SHOPBASE))) != 0 &&
/* avoid charging for Manlobbi's statue of Manlobbi
if stone-to-flesh is used on petrified shopkeep */
mon != shkp)
(void) stolen_value(statue, x, y,
(boolean)shkp->mpeaceful, FALSE);
if (historic) {
You_feel("guilty %s.", historic_statue_is_gone);
adjalign(-1);
}
} else {
if (historic && cansee(x, y))
You_feel("regret %s.", historic_statue_is_gone);
/* no alignment penalty */
}
/* transfer any statue contents to monster's inventory */
while ((item = statue->cobj) != 0) {
obj_extract_self(item);
(void) mpickobj(mon, item);
}
m_dowear(mon, TRUE);
/* in case statue is wielded and hero zaps stone-to-flesh at self */
if (statue->owornmask) remove_worn_item(statue, TRUE);
/* statue no longer exists */
delobj(statue);
/* 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;
otmp = nxtobj(otmp, STATUE, TRUE);
}
if (Blind) feel_location(x, y);
else newsym(x, y);
return mtmp;
}
#ifdef STEED
STATIC_OVL boolean
keep_saddle_with_steedcorpse(steed_mid, objchn, saddle)
unsigned steed_mid;
struct obj *objchn, *saddle;
{
if (!saddle) return FALSE;
while(objchn) {
if(objchn->otyp == CORPSE && has_omonst(objchn)) {
struct monst *mtmp = OMONST(objchn);
if (mtmp->m_id == steed_mid) {
/* move saddle */
xchar x,y;
if (get_obj_location(objchn, &x, &y, 0)) {
obj_extract_self(saddle);
place_object(saddle, x, y);
stackobj(saddle);
}
return TRUE;
}
}
if (Has_contents(objchn) &&
keep_saddle_with_steedcorpse(steed_mid, objchn->cobj, saddle))
return TRUE;
objchn = objchn->nobj;
}
return FALSE;
}
#endif /*STEED*/
void
dotrap(trap, trflags)
register struct trap *trap;
unsigned trflags;
{
register int ttype = trap->ttyp;
register struct obj *otmp;
boolean already_seen = trap->tseen,
forcetrap = (trflags & FORCETRAP) != 0,
webmsgok = (trflags & NOWEBMSG) == 0,
forcebungle = (trflags & FORCEBUNGLE) != 0,
plunged = (trflags & TOOKPLUNGE) != 0,
adj_pit = conjoined_pits(trap, t_at(u.ux0,u.uy0), TRUE);
#ifdef STEED
int steed_article = ARTICLE_THE;
#endif
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 && !forcetrap) {
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 && !forcebungle && !plunged && !adj_pit &&
(!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;
}
}
#ifdef STEED
if (u.usteed) {
u.usteed->mtrapseen |= (1 << (ttype - 1));
/* suppress article in various steed messages when using its
name (which won't occur when hallucinating) */
if (has_mname(u.usteed) && !Hallucination)
steed_article = ARTICLE_NONE;
}
#endif
switch(ttype) {
case ARROW_TRAP:
if (trap->once && trap->tseen && !rn2(15)) {
You_hear("a loud click!");
deltrap(trap);
newsym(u.ux,u.uy);
break;
}
trap->once = 1;
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:
if (trap->once && trap->tseen && !rn2(15)) {
You_hear("a soft click.");
deltrap(trap);
newsym(u.ux,u.uy);
break;
}
trap->once = 1;
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, "little dart", 10, TRUE);
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:
if (trap->once && trap->tseen && !rn2(15)) {
pline("A trap door in %s opens, but nothing falls out!",
the(ceiling(u.ux,u.uy)));
deltrap(trap);
newsym(u.ux,u.uy);
} else {
int dmg = d(2,6); /* should be std ROCK dmg? */
trap->once = 1;
feeltrap(trap);
otmp = mksobj_at(ROCK, u.ux, u.uy, TRUE, FALSE);
otmp->quan = 1L;
otmp->owt = weight(otmp);
pline("A trap door in %s opens and %s falls on your %s!",
the(ceiling(u.ux,u.uy)),
an(xname(otmp)),
body_part(HEAD));
if (uarmh) {
if(is_metallic(uarmh)) {
pline("Fortunately, you are wearing a hard helmet.");
dmg = 2;
} else if (flags.verbose) {
pline("%s does not protect you.", Yname2(uarmh));
}
}
if (!Blind) otmp->dknown = 1;
stackobj(otmp);
newsym(u.ux,u.uy); /* map the rock */
losehp(Maybe_Half_Phys(dmg), "falling rock", KILLED_BY_AN);
exercise(A_STR, FALSE);
}
break;
case SQKY_BOARD: /* stepped on a squeaky board */
if ((Levitation || Flying) && !forcetrap) {
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 %s%s%s.",
Deaf ? "vibrates" : "squeaks ",
Deaf ? "" : trapnote(trap,0),
Deaf ? "" : " loudly");
wake_nearby();
}
break;
case BEAR_TRAP:
{
int dmg = d(2, 4);
if ((Levitation || Flying) && !forcetrap) break;
feeltrap(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));
if (thitm(0, u.usteed, (struct obj *)0, dmg, FALSE))
u.utrap = 0; /* steed died, hero not trapped */
} 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!");
losehp(Maybe_Half_Phys(dmg), "bear trap", KILLED_BY_AN);
}
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!");
} else {
pline("A cloud of gas puts you to sleep!");
fall_asleep(-rnd(25), TRUE);
}
#ifdef STEED
(void) steedintrap(trap, (struct obj *)0);
#endif
break;
case RUST_TRAP:
seetrap(trap);
if (u.umonnum == PM_IRON_GOLEM) {
int dam = u.mhmax;
pline("%s you!", A_gush_of_water_hits);
You("are covered with rust!");
losehp(Maybe_Half_Phys(dam), "rusting away", KILLED_BY);
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, helm_simple_name(uarmh),
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)))
(void) erode_obj(u.twoweap ? uswapwep : uwep,
1, TRUE, FALSE);
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));
(void) erode_obj(uwep, 1, TRUE, FALSE);
goto glovecheck;
default:
pline("%s you!", A_gush_of_water_hits);
for (otmp = invent; otmp; otmp = otmp->nobj)
if (otmp->lamplit && otmp != uwep &&
(otmp != uswapwep || !u.twoweap))
(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
}
update_inventory();
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;
feeltrap(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) {
if ((trflags & RECURSIVETRAP) != 0)
Sprintf(verbbuf, "and %s fall",
x_monnam(u.usteed, steed_article,
(char *)0, SUPPRESS_SADDLE, FALSE));
else
Sprintf(verbbuf, "lead %s",
x_monnam(u.usteed, steed_article,
"poor", SUPPRESS_SADDLE, FALSE));
} else
#endif
if (adj_pit) {
You("move into an adjacent pit.");
} else {
Strcpy(verbbuf, plunged ? "plunge" : "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) {
const char *predicament = "on a set of sharp iron spikes";
#ifdef STEED
if (u.usteed) {
pline("%s %s %s!",
upstart(x_monnam(u.usteed, steed_article,
"poor", SUPPRESS_SADDLE, FALSE)),
adj_pit ? "steps" : "lands",
predicament);
} else
#endif
You("%s %s!", adj_pit ? "step" : "land", predicament);
}
u.utrap = rn1(6,2);
u.utraptype = TT_PIT;
#ifdef STEED
if (!steedintrap(trap, (struct obj *)0)) {
#endif
if (ttype == SPIKED_PIT) {
losehp(Maybe_Half_Phys(rnd(adj_pit ? 6 : 10)),
plunged ? "deliberately plunged into a pit of iron spikes" :
adj_pit ? "stepped into a pit of iron spikes" :
"fell into a pit of iron spikes",
NO_KILLER_PREFIX);
if (!rn2(6))
poisoned("spikes", A_STR,
adj_pit ? "stepping on poison spikes" :
"fall onto poison spikes",
8, FALSE);
} else {
if (!adj_pit)
losehp(Maybe_Half_Phys(rnd(6)),
plunged ? "deliberately plunged into a pit" :
"fell into a pit",
NO_KILLER_PREFIX);
}
if (Punished && !carried(uball)) {
unplacebc();
ballfall();
placebc();
}
if (!adj_pit) 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:
if (!Can_fall_thru(&u.uz)) {
seetrap(trap); /* normally done in fall_through */
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. */
feeltrap(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];
if (forcetrap) {
Strcpy(verbbuf, "are caught by");
#ifdef STEED
} else if (u.usteed) {
Sprintf(verbbuf, "lead %s into",
x_monnam(u.usteed, steed_article,
"poor", SUPPRESS_SADDLE, FALSE));
#endif
} else {
Sprintf(verbbuf, "%s into",
Levitation ? (const char *)"float" :
locomotion(youmonst.data, "stumble"));
}
You("%s %s spider web!", verbbuf, a_your[trap->madeby_u]);
}
u.utraptype = TT_WEB;
/* Time stuck in the web depends on your/steed strength. */
{
register int str = ACURR(A_STR);
#ifdef STEED
/* If mounted, the steed gets trapped. Use mintrap
* to do all the work. If mtrapped is set as a result,
* unset it and set utrap instead. In the case of a
* strongmonst and mintrap said it's trapped, use a
* short but non-zero trap time. Otherwise, monsters
* have no specific strength, so use player strength.
* This gets skipped for webmsgok, which implies that
* the steed isn't a factor.
*/
if (u.usteed && webmsgok) {
/* mtmp location might not be up to date */
u.usteed->mx = u.ux;
u.usteed->my = u.uy;
/* mintrap currently does not return 2(died) for webs */
if (mintrap(u.usteed)) {
u.usteed->mtrapped = 0;
if (strongmonst(u.usteed->data)) str = 17;
} else {
break;
}
webmsgok = FALSE; /* mintrap printed the messages */
}
#endif
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);
/* hero without magic resistance loses spell energy,
hero with magic resistance takes damage instead;
possibly non-intuitive but useful for play balance */
if (!Antimagic) {
drain_en(rnd(u.ulevel) + 1);
} else {
int dmgval = rnd(4), hp = Upolyd ? u.mh : u.uhp;
/* Half_XXX_damage has opposite its usual effect (approx)
but isn't cumulative if hero has more than one */
if (Half_physical_damage || Half_spell_damage)
dmgval += rnd(4);
/* give Magicbane wielder dose of own medicine */
if (uwep && uwep->oartifact == ART_MAGICBANE)
dmgval += rnd(4);
/* having an artifact--other than own quest one--which
confers magic resistance simply by being carried
also increases the effect */
for (otmp = invent; otmp; otmp = otmp->nobj)
if (otmp->oartifact && !is_quest_artifact(otmp) &&
protects(AD_MAGM, otmp)) break;
if (otmp) dmgval += rnd(4);
if (Passes_walls) dmgval = (dmgval + 3) / 4;
You_feel((dmgval >= hp) ? "unbearably torpid!" :
(dmgval >= hp / 4) ? "very lethargic." :
"sluggish.");
/* opposite of magical explosion */
losehp(dmgval, "anti-magic implosion", KILLED_BY_AN);
}
break;
case POLY_TRAP: {
char verbbuf[BUFSZ];
seetrap(trap);
#ifdef STEED
if (u.usteed)
Sprintf(verbbuf, "lead %s",
x_monnam(u.usteed, steed_article,
(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(0);
}
break;
}
case LANDMINE: {
#ifdef STEED
unsigned steed_mid = 0;
struct obj *saddle = 0;
#endif
if ((Levitation || Flying) && !forcetrap) {
if (!already_seen && rn2(3)) break;
feeltrap(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!!! %s %s%s off!",
forcebungle ? "Your inept attempt sets" :
"The air currents set",
already_seen ? a_your[trap->madeby_u] : "",
already_seen ? " land mine" : "it");
} else {
#ifdef STEED
/* prevent landmine from killing steed, throwing you to
* the ground, and you being affected again by the same
* mine because it hasn't been deleted yet
*/
static boolean recursive_mine = FALSE;
if (recursive_mine) break;
#endif
feeltrap(trap);
pline("KAABLAMM!!! You triggered %s land mine!",
a_your[trap->madeby_u]);
#ifdef STEED
if (u.usteed) steed_mid = u.usteed->m_id;
recursive_mine = TRUE;
(void) steedintrap(trap, (struct obj *)0);
recursive_mine = FALSE;
saddle = sobj_at(SADDLE,u.ux, u.uy);
#endif
set_wounded_legs(LEFT_SIDE, rn1(35, 41));
set_wounded_legs(RIGHT_SIDE, rn1(35, 41));
exercise(A_DEX, FALSE);
}
blow_up_landmine(trap);
#ifdef STEED
if (steed_mid && saddle && !u.usteed)
(void)keep_saddle_with_steedcorpse(steed_mid, fobj, saddle);
#endif
newsym(u.ux,u.uy); /* update trap symbol */
losehp(Maybe_Half_Phys(rnd(16)), "land mine", KILLED_BY_AN);
/* fall recursively into the pit... */
if ((trap = t_at(u.ux, u.uy)) != 0) dotrap(trap, RECURSIVETRAP);
fill_pit(u.ux, u.uy);
break;
}
case ROLLING_BOULDER_TRAP: {
int style = ROLL | (trap->tseen ? LAUNCH_KNOWN : 0);
feeltrap(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, style)) {
deltrap(trap);
newsym(u.ux,u.uy); /* get rid of trap symbol */
pline("Fortunately for you, no boulder was released.");
}
break;
}
case MAGIC_PORTAL:
feeltrap(trap);
domagicportal(trap);
break;
default:
feeltrap(trap);
impossible("You hit a trap of type %u", trap->ttyp);
}
}
STATIC_OVL char *
trapnote(trap, noprefix)
struct trap *trap;
boolean noprefix;
{
static char tnbuf[12];
const char *tn, *tnnames[12] = {
"C note" , "D flat", "D note",
"E flat" , "E note", "F note",
"F sharp", "G note", "G sharp",
"A note" , "B flat", "B note"
};
tnbuf[0] = '\0';
tn = tnnames[trap->tnote];
if (!noprefix)
Sprintf(tnbuf, "%s ",
(*tn == 'A' || *tn == 'E' || *tn == 'F') ? "an" : "a");
Sprintf(eos(tnbuf), "%s", tn);
return tnbuf;
}
#ifdef STEED
STATIC_OVL int
steedintrap(trap, otmp)
struct trap *trap;
struct obj *otmp;
{
struct monst *steed = u.usteed;
int tt;
boolean in_sight, trapkilled, steedhit;
if (!steed || !trap) return 0;
tt = trap->ttyp;
steed->mx = u.ux;
steed->my = u.uy;
in_sight = !Blind;
trapkilled = steedhit = FALSE;
switch (tt) {
case ARROW_TRAP:
if (!otmp) {
impossible("steed hit by non-existant arrow?");
return 0;
}
trapkilled = thitm(8, steed, otmp, 0, FALSE);
steedhit = TRUE;
break;
case DART_TRAP:
if (!otmp) {
impossible("steed hit by non-existant dart?");
return 0;
}
trapkilled = thitm(7, steed, otmp, 0, FALSE);
steedhit = TRUE;
break;
case SLP_GAS_TRAP:
if (!resists_sleep(steed) && !breathless(steed->data) &&
!steed->msleeping && steed->mcanmove) {
if (sleep_monst(steed, rnd(25), -1))
/* no in_sight check here; you can feel it even if blind */
pline("%s suddenly falls asleep!", Monnam(steed));
}
steedhit = TRUE;
break;
case LANDMINE:
trapkilled = thitm(0, steed, (struct obj *)0, rnd(16), FALSE);
steedhit = TRUE;
break;
case PIT:
case SPIKED_PIT:
trapkilled = (steed->mhp <= 0 ||
thitm(0, steed, (struct obj *)0,
rnd((tt == PIT) ? 6 : 10), FALSE));
steedhit = TRUE;
break;
case POLY_TRAP:
if (!resists_magm(steed) &&
!resist(steed, WAND_CLASS, 0, NOTELL)) {
(void) newcham(steed, (struct permonst *)0, FALSE, FALSE);
if (!can_saddle(steed) || !can_ride(steed))
dismount_steed(DISMOUNT_POLY);
else
You("have to adjust yourself in the saddle on %s.",
x_monnam(steed, ARTICLE_A,
(char *)0, SUPPRESS_SADDLE, FALSE));
}
steedhit = TRUE;
break;
default:
break;
}
if (trapkilled) {
dismount_steed(DISMOUNT_POLY);
return 2;
}
return steedhit ? 1 : 0;
}
#endif /*STEED*/
/* some actions common to both player and monsters for triggered landmine */
void
blow_up_landmine(trap)
struct trap *trap;
{
int x = trap->tx, y = trap->ty, dbx, dby;
struct rm *lev = &levl[x][y];
(void)scatter(x, y, 4,
MAY_DESTROY | MAY_HIT | MAY_FRACTURE | VIS_EFFECTS,
(struct obj *)0);
del_engr_at(x, y);
wake_nearto(x, y, 400);
if (IS_DOOR(lev->typ))
lev->doormask = D_BROKEN;
/* destroy drawbridge if present */
if (lev->typ == DRAWBRIDGE_DOWN || is_drawbridge_wall(x, y) >= 0) {
dbx = x, dby = y;
/* if under the portcullis, the bridge is adjacent */
if (find_drawbridge(&dbx, &dby))
destroy_drawbridge(dbx, dby);
trap = t_at(x, y); /* expected to be null after destruction */
}
/* convert landmine into pit */
if (trap) {
if (Is_waterlevel(&u.uz) || Is_airlevel(&u.uz)) {
/* no pits here */
deltrap(trap);
} else {
trap->ttyp = PIT; /* explosion creates a pit */
trap->madeby_u = FALSE; /* resulting pit isn't yours */
seetrap(trap); /* and it isn't concealed */
}
}
}
/*
* The following are used to track launched objects to
* prevent them from vanishing if you are killed. They
* will reappear at the launchplace in bones files.
*/
static struct {
struct obj *obj;
xchar x,y;
} launchplace;
static void
launch_drop_spot(obj,x,y)
struct obj *obj;
xchar x,y;
{
if (!obj) {
launchplace.obj = (struct obj *)0;
launchplace.x = 0;
launchplace.y = 0;
} else {
launchplace.obj = obj;
launchplace.x = x;
launchplace.y = y;
}
}
boolean
launch_in_progress()
{
if (launchplace.obj) return TRUE;
return FALSE;
}
void
force_launch_placement()
{
if (launchplace.obj) {
launchplace.obj->otrapped = 0;
place_object(launchplace.obj, launchplace.x, launchplace.y);
}
}
/*
* 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, 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 == context.digging.pos.x &&
singleobj->oy == context.digging.pos.y)
(void) memset((genericptr_t)&context.digging, 0, sizeof(struct dig_info));
dist = distmin(x1,y1,x2,y2);
bhitpos.x = x1;
bhitpos.y = y1;
dx = sgn(x2 - x1);
dy = sgn(y2 - y1);
switch (style) {
case ROLL|LAUNCH_UNSEEN:
if (otyp == BOULDER) {
You_hear(Hallucination ?
"someone bowling." :
"rumbling in the distance.");
}
style &= ~LAUNCH_UNSEEN;
goto roll;
case ROLL|LAUNCH_KNOWN:
/* use otrapped as a flag to ohitmon */
singleobj->otrapped = 1;
style &= ~LAUNCH_KNOWN;
/* fall through */
roll:
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);
}
/* Mark a spot to place object in bones files to prevent
* loss of object. Use the starting spot to ensure that
* a rolling boulder will still launch, which it wouldn't
* do if left midstream. Unfortunately we can't use the
* target resting spot, because there are some things/situations
* that would prevent it from ever getting there (bars), and we
* can't tell that yet.
*/
launch_drop_spot(singleobj, 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));
singleobj->otrapped = 0;
(void) mpickobj(mtmp, singleobj);
used_up = TRUE;
launch_drop_spot((struct obj *)0, 0, 0);
break;
}
}
if (ohitmon(mtmp,singleobj,
(style==ROLL) ? -1 : dist, FALSE)) {
used_up = TRUE;
launch_drop_spot((struct obj *)0, 0, 0);
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;
launch_drop_spot((struct obj *)0, 0, 0);
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);
singleobj->otrapped = 0;
fracture_rock(singleobj);
(void)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;
launch_drop_spot((struct obj *)0, 0, 0);
}
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.");
singleobj->otrapped = 0;
if (t->ttyp == TELEP_TRAP)
(void)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;
launch_drop_spot((struct obj *)0, 0, 0);
break;
case PIT:
case SPIKED_PIT:
case HOLE:
case TRAPDOOR:
/* the boulder won't be used up if there is a
monster in the trap; stop rolling anyway */
x2 = bhitpos.x, y2 = bhitpos.y; /* stops here */
if (flooreffects(singleobj, x2, y2, "fall")) {
used_up = TRUE;
launch_drop_spot((struct obj *)0, 0, 0);
}
dist = -1; /* stop rolling immediately */
break;
}
if (used_up || dist == -1) break;
}
if (flooreffects(singleobj, bhitpos.x, bhitpos.y, "fall")) {
used_up = TRUE;
launch_drop_spot((struct obj *)0, 0, 0);
break;
}
if (otyp == BOULDER &&
(otmp2 = sobj_at(BOULDER, bhitpos.x, bhitpos.y)) != 0) {
const char *bmsg =
" as one boulder sets another in motion";
if (!isok(bhitpos.x + dx, bhitpos.y + dy) || !dist ||
IS_ROCK(levl[bhitpos.x + dx][bhitpos.y + dy].typ))
bmsg = " as one boulder hits another";
You_hear("a loud crash%s!",
cansee(bhitpos.x, bhitpos.y) ? bmsg : "");
obj_extract_self(otmp2);
/* pass off the otrapped flag to the next boulder */
otmp2->otrapped = singleobj->otrapped;
singleobj->otrapped = 0;
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;
if (dist) unblock_point(bhitpos.x, bhitpos.y);
}
/* if about to hit iron bars, do so now */
if (dist > 0 && isok(bhitpos.x + dx,bhitpos.y + dy) &&
levl[bhitpos.x + dx][bhitpos.y + dy].typ == IRONBARS) {
x2 = bhitpos.x, y2 = bhitpos.y; /* object stops here */
if (hits_bars(&singleobj, x2, y2, !rn2(20), 0)) {
if (!singleobj) {
used_up = TRUE;
launch_drop_spot((struct obj *)0, 0, 0);
}
break;
}
}
}
tmp_at(DISP_END, 0);
launch_drop_spot((struct obj *)0, 0, 0);
if (!used_up) {
singleobj->otrapped = 0;
place_object(singleobj, x2,y2);
newsym(x2,y2);
return 1;
} else
return 2;
}
void
seetrap(trap)
struct trap *trap;
{
if (!trap->tseen) {
trap->tseen = 1;
newsym(trap->tx, trap->ty);
}
}
/* like seetrap() but overrides vision */
void
feeltrap(trap)
struct trap *trap;
{
trap->tseen = 1;
map_trap(trap, 1);
/* in case it's beneath something, redisplay the something */
newsym(trap->tx, trap->ty);
}
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;
}
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 = force_mintrap ||
((tt == HOLE || tt == PIT) &&
In_sokoban(&u.uz) && !trap->madeby_u);
const char *fallverb;
#ifdef STEED
/* true when called from dotrap, inescapable is not an option */
if (mtmp == u.usteed) inescapable = TRUE;
#endif
if (!inescapable &&
((mtmp->mtrapseen & (1 << (tt-1))) != 0 ||
(tt == HOLE && !mindless(mptr)))) {
/* 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);
in_sight = canseemon(mtmp);
see_it = cansee(mtmp->mx, mtmp->my);
#ifdef STEED
/* assume hero can tell what's going on for the steed */
if (mtmp == u.usteed) in_sight = TRUE;
#endif
switch (tt) {
case ARROW_TRAP:
if (trap->once && trap->tseen && !rn2(15)) {
if (in_sight && see_it)
pline("%s triggers a trap but nothing happens.",
Monnam(mtmp));
deltrap(trap);
newsym(mtmp->mx, mtmp->my);
break;
}
trap->once = 1;
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, FALSE)) trapkilled = TRUE;
break;
case DART_TRAP:
if (trap->once && trap->tseen && !rn2(15)) {
if (in_sight && see_it)
pline("%s triggers a trap but nothing happens.",
Monnam(mtmp));
deltrap(trap);
newsym(mtmp->mx, mtmp->my);
break;
}
trap->once = 1;
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, FALSE)) trapkilled = TRUE;
break;
case ROCKTRAP:
if (trap->once && trap->tseen && !rn2(15)) {
if (in_sight && see_it)
pline("A trap door above %s opens, but nothing falls out!",
mon_nam(mtmp));
deltrap(trap);
newsym(mtmp->mx, mtmp->my);
break;
}
trap->once = 1;
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), FALSE))
trapkilled = TRUE;
break;
case SQKY_BOARD:
if(is_flyer(mptr)) break;
/* stepped on a squeaky board */
if (in_sight) {
if (!Deaf) {
pline("A board beneath %s squeaks %s loudly.",
mon_nam(mtmp), trapnote(trap,0));
seetrap(trap);
} else {
pline("%s stops momentarily and appears to cringe.",
Monnam(mtmp));
}
} else if (!Deaf) {
You_hear("a distant %s squeak.",
trapnote(trap,1));
}
/* 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])
&& !Deaf)
You_hear("the roaring of an angry bear!");
}
} else if (force_mintrap) {
if (in_sight) {
pline("%s evades %s bear trap!",
Monnam(mtmp), a_your[trap->madeby_u]);
seetrap(trap);
}
}
if (mtmp->mtrapped)
trapkilled = thitm(0, mtmp, (struct obj *)0,
d(2, 4), FALSE);
break;
case SLP_GAS_TRAP:
if (!resists_sleep(mtmp) && !breathless(mptr) &&
!mtmp->msleeping && mtmp->mcanmove) {
if (sleep_monst(mtmp, rnd(25), -1) && 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, helm_simple_name(target),
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))
(void) erode_obj(target, 1, TRUE, FALSE);
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));
(void) erode_obj(MON_WEP(mtmp), 1, TRUE, FALSE);
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)
if (otmp->lamplit &&
(otmp->owornmask & (W_WEP|W_SWAPWEP)) == 0)
(void) snuff_lit(otmp);
if ((target = which_armor(mtmp, W_ARMC)) != 0)
(void) rust_dmg(target,
cloak_simple_name(target),
1, TRUE, mtmp);
else if ((target = which_armor(mtmp, W_ARM)) != 0)
(void) rust_dmg(target, "armor", 1, TRUE, mtmp);
#ifdef TOURIST
else if ((target = which_armor(mtmp, W_ARMU)) != 0)
(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:
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), alt;
boolean immolate = FALSE;
/* paper burns very fast, assume straw is tightly
* packed and burns a bit slower */
switch (monsndx(mptr)) {
case PM_PAPER_GOLEM: immolate = TRUE;
alt = mtmp->mhpmax; break;
case PM_STRAW_GOLEM: alt = mtmp->mhpmax / 2; break;
case PM_WOOD_GOLEM: alt = mtmp->mhpmax / 4; break;
case PM_LEATHER_GOLEM: alt = mtmp->mhpmax / 8; break;
default: alt = 0; break;
}
if (alt > num) num = alt;
if (thitm(0, mtmp, (struct obj *)0, num, immolate))
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, (char *)0);
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 (force_mintrap && !In_sokoban(&u.uz)) {
/* openfallingtrap; not inescapable here */
if (in_sight) {
seetrap(trap);
pline("%s doesn't fall into the pit.",
Monnam(mtmp));
}
break; /* inescapable = FALSE; */
}
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), FALSE))
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 (force_mintrap && !In_sokoban(&u.uz)) {
/* openfallingtrap; not inescapable here */
if (in_sight) {
seetrap(trap);
if (tt == TRAPDOOR)
pline(
"A trap door opens, but %s doesn't fall through.",
mon_nam(mtmp));
else /* (tt == HOLE) */
pline(
"%s doesn't fall through the hole.",
Monnam(mtmp));
}
break; /* inescapable = FALSE; */
}
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:
case PM_ORION:
case PM_NORN:
case PM_CYCLOPS:
case PM_LORD_SURTUR:
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);
} else if (force_mintrap && !mtmp->mtrapped) {
if (in_sight) {
pline("%s avoids %s spider web!",
Monnam(mtmp), a_your[trap->madeby_u]);
seetrap(trap);
}
}
break;
case STATUE_TRAP:
break;
case MAGIC_TRAP:
/* A magic trap. Monsters usually immune. */
if (!rn2(21)) goto mfiretrap;
break;
case ANTI_MAGIC:
/* similar to hero's case, more or less */
if (!resists_magm(mtmp)) { /* lose spell energy */
if (!mtmp->mcan &&
(attacktype(mptr, AT_MAGC) ||
attacktype(mptr, AT_BREA))) {
mtmp->mspec_used += d(2, 2);
if (in_sight) {
seetrap(trap);
pline("%s seems lethargic.", Monnam(mtmp));
}
}
} else { /* take some damage */
int dmgval = rnd(4);
if ((otmp = MON_WEP(mtmp)) != 0 &&
otmp->oartifact == ART_MAGICBANE)
dmgval += rnd(4);
for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
if (otmp->oartifact && protects(AD_MAGM, otmp))
break;
if (otmp) dmgval += rnd(4);
if (passes_walls(mptr)) dmgval = (dmgval + 3) / 4;
if (in_sight) seetrap(trap);
if ((mtmp->mhp -= dmgval) <= 0)
monkilled(mtmp, in_sight ?
"compression from an anti-magic field" : 0,
-AD_MAGM);
if (mtmp->mhp <= 0) trapkilled = TRUE;
if (see_it) newsym(trap->tx, trap->ty);
}
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);
/* explosion might have destroyed a drawbridge; don't
dish out more damage if monster is already dead */
if (mtmp->mhp <= 0 ||
thitm(0, mtmp, (struct obj *)0, rnd(16), FALSE))
trapkilled = TRUE;
else {
/* monsters recursively fall into new pit */
if (mintrap(mtmp) == 2) trapkilled=TRUE;
}
/* a boulder may fill the new pit, crushing monster */
fill_pit(trap->tx, trap->ty);
if (mtmp->mhp <= 0) 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)) {
if (newcham(mtmp, (struct permonst *)0, FALSE, FALSE))
/* we're done with mptr but keep it up to date */
mptr = mtmp->data;
if (in_sight) seetrap(trap);
}
break;
case ROLLING_BOULDER_TRAP:
if (!is_flyer(mptr)) {
int style = ROLL | (in_sight ? 0 : LAUNCH_UNSEEN);
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, style)) {
if (in_sight) trap->tseen = TRUE;
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;
}
/* 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;
if (str != killer.name) Strcpy(killer.name, str ? str : "");
done(STONING);
}
void
minstapetrify(mon,byplayer)
struct monst *mon;
boolean byplayer;
{
if (resists_ston(mon)) return;
if (poly_when_stoned(mon->data)) {
mon_to_stone(mon);
return;
}
/* give a "<mon> is slowing down" message and also remove
intrinsic speed (comparable to similar effect on the hero) */
mon_adjust_speed(mon, -3, (struct obj *)0);
if (cansee(mon->mx, mon->my))
pline("%s turns to stone.", Monnam(mon));
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);
/* life-saved; unwield the corpse if we can't handle it */
if (!uarmg && !Stone_resistance) uwepgone();
}
/* Or your secondary weapon, if wielded [hypothetical; we don't
allow two-weapon combat when either weapon is a corpse] */
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);
/* life-saved; unwield the corpse */
if (!uarmg && !Stone_resistance) uswapwepgone();
}
}
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]) &&
!resists_ston(mon)) {
if (cansee(mon->mx, mon->my)) {
pline("%s%s touches %s.",
arg ? arg : "", arg ? mon_nam(mon) : Monnam(mon),
corpse_xname(mwep, (const char *)0, CXN_PFX_THE));
}
minstapetrify(mon, byplayer);
/* if life-saved, might not be able to continue wielding */
if (mon->mhp > 0 && !which_armor(mon, W_ARMG) &&
!resists_ston(mon))
mwepgone(mon);
}
}
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 */
nomul(0); /* stop running or resting */
if(u.uswallow) {
You("float down, but you are still %s.",
is_animal(u.ustuck->data) ? "swallowed" : "engulfed");
return(1);
}
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) {
if (!u.uswallow && u.ustuck) {
if (sticks(youmonst.data))
You("aren't able to maintain your hold on %s.",
mon_nam(u.ustuck));
else
pline("Startled, %s can no longer hold you!",
mon_nam(u.ustuck));
u.ustuck = 0;
}
/* 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
{
if (In_sokoban(&u.uz) && trap) {
/* 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.
*/
if (Hallucination)
pline("Bummer! You've crashed.");
else
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");
#ifdef STEED
} else if (u.usteed && (is_floater(u.usteed->data) ||
is_flyer(u.usteed->data))) {
You("settle more firmly in the saddle.");
#endif
} else if (Hallucination)
pline("Bummer! You've %s.",
is_pool(u.ux,u.uy) ? "splashed down" :
"hit the ground");
else
You("float gently to the %s.", surface(u.ux, u.uy));
}
}
}
/* 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(&current_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:
if (!u.utrap) /* not already in the trap */
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, &current_dungeon_level))
(void) pickup(1);
return 1;
}
/* shared code for climbing out of a pit */
void
climb_pit()
{
if (!u.utrap || u.utraptype != TT_PIT) return;
if (Passes_walls) {
/* marked as trapped so they can pick things up */
You("ascend from the pit.");
u.utrap = 0;
fill_pit(u.ux, u.uy);
vision_full_recalc = 1; /* vision limits change */
} else if (!rn2(2) && sobj_at(BOULDER, u.ux, u.uy)) {
Your("%s gets stuck in a crevice.", body_part(LEG));
display_nhwindow(WIN_MESSAGE, FALSE);
clear_nhwindow(WIN_MESSAGE);
You("free your %s.", body_part(LEG));
} else if (Flying && !In_sokoban(&u.uz)) {
/* eg fell in pit, poly'd to a flying monster */
You("fly from the pit.");
u.utrap = 0;
fill_pit(u.ux, u.uy);
vision_full_recalc = 1; /* vision limits change */
} else if (!(--u.utrap)) {
You("%s to the edge of the pit.",
(In_sokoban(&u.uz) && Levitation) ?
"struggle against the air currents and float" :
#ifdef STEED
u.usteed ? "ride" :
#endif
"crawl");
fill_pit(u.ux, u.uy);
vision_full_recalc = 1; /* vision limits change */
} else if (flags.verbose) {
#ifdef STEED
if (u.usteed)
Norep("%s is still in a pit.",
upstart(y_monnam(u.usteed)));
else
#endif
Norep( (Hallucination && !rn2(5)) ?
"You've fallen, and you can't get up." :
"You are still in a pit." );
}
}
STATIC_OVL void
dofiretrap(box)
struct obj *box; /* null for floor trap */
{
boolean see_it = !Blind;
int num, alt;
/* 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 if (Upolyd) {
num = d(2,4);
switch (u.umonnum) {
case PM_PAPER_GOLEM: alt = u.mhmax; break;
case PM_STRAW_GOLEM: alt = u.mhmax / 2; break;
case PM_WOOD_GOLEM: alt = u.mhmax / 4; break;
case PM_LEATHER_GOLEM: alt = u.mhmax / 8; break;
default: alt = 0; break;
}
if (alt > num) num = alt;
if (u.mhmax > mons[u.umonnum].mlevel)
u.mhmax -= rn2(min(u.mhmax,num + 1)), context.botl = 1;
} else {
num = d(2,4);
if (u.uhpmax > u.ulevel)
u.uhpmax -= rn2(min(u.uhpmax,num + 1)), context.botl = 1;
}
if (!num)
You("are uninjured.");
else
losehp(num, tower_of_flame, KILLED_BY_AN); /* fire damage */
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, (char *)0);
}
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 if (!Deaf) {
You_hear("a deafening roar!");
}
incr_itimeout(&HDeaf, rn1(20,30));
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,
and zero out oextra */
pseudo.otyp = SCR_REMOVE_CURSE;
HConfusion = 0L;
(void) seffects(&pseudo);
HConfusion = save_conf;
break;
}
default: break;
}
}
/*
* Scrolls, spellbooks, potions, and flammable items
* may get affected by the fire.
*
* Return number of objects destroyed. --ALI
*/
int
fire_damage(chain, force, here, x, y)
struct obj *chain;
boolean force, here;
xchar x, y;
{
int chance;
struct obj *obj, *otmp, *nobj, *ncobj;
int retval = 0;
int in_sight = !Blind && couldsee(x, y); /* Don't care if it's lit */
int dindx;
for (obj = chain; obj; obj = nobj) {
nobj = here ? obj->nexthere : obj->nobj;
/* object might light in a controlled manner */
if (catch_lit(obj))
continue;
if (Is_container(obj)) {
switch (obj->otyp) {
case ICE_BOX:
continue; /* Immune */
/*NOTREACHED*/
break;
case CHEST:
chance = 40;
break;
case LARGE_BOX:
chance = 30;
break;
default:
chance = 20;
break;
}
if ((!force && (Luck + 5) > rn2(chance)) ||
(is_flammable(obj) && obj->oerodeproof))
continue;
/* Container is burnt up - dump contents out */
if (in_sight) pline("%s catches fire and burns.", Yname2(obj));
if (Has_contents(obj)) {
if (in_sight) pline("Its contents fall out.");
for (otmp = obj->cobj; otmp; otmp = ncobj) {
ncobj = otmp->nobj;
obj_extract_self(otmp);
if (!flooreffects(otmp, x, y, ""))
place_object(otmp, x, y);
}
}
delobj(obj);
retval++;
} else if (!force && (Luck + 5) > rn2(20)) {
/* chance per item of sustaining damage:
* max luck (Luck==13): 10%
* avg luck (Luck==0): 75%
* awful luck (Luck<-4): 100%
*/
continue;
} else if (obj->oclass == SCROLL_CLASS || obj->oclass == SPBOOK_CLASS) {
if (obj->otyp == SCR_FIRE || obj->otyp == SPE_FIREBALL)
continue;
if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
if (in_sight) pline("Smoke rises from %s.", the(xname(obj)));
continue;
}
dindx = (obj->oclass == SCROLL_CLASS) ? 3 : 4;
if (in_sight)
pline("%s %s.", Yname2(obj),
destroy_strings[dindx][(obj->quan > 1L)]);
delobj(obj);
retval++;
} else if (obj->oclass == POTION_CLASS) {
dindx = (obj->otyp != POT_OIL) ? 1 : 2;
if (in_sight)
pline("%s %s.", Yname2(obj),
destroy_strings[dindx][(obj->quan > 1L)]);
delobj(obj);
retval++;
} else if (is_flammable(obj) && obj->oeroded < MAX_ERODE &&
!(obj->oerodeproof || (obj->blessed && !rnl(4)))) {
if (in_sight) {
pline("%s %s%s.", Yname2(obj), otense(obj, "burn"),
obj->oeroded+1 == MAX_ERODE ? " completely" :
obj->oeroded ? " further" : "");
}
obj->oeroded++;
}
}
if (retval && !in_sight)
You("smell smoke.");
return retval;
}
void
water_damage(objp, force, here)
struct obj **objp;
boolean force, here;
{
register struct obj *obj = *objp, *otmp;
boolean loose_obj = (obj && obj->where == OBJ_FREE), exploded = FALSE;
if (loose_obj && (obj->nobj || obj->nexthere)) {
/* [this should actually be a panic()] */
impossible("water_damage: loose object has%s%s%s list%s?",
obj->nobj ? " nobj" : "",
(obj->nobj && obj->nexthere) ? " and" : "",
obj->nexthere ? " nexthere" : "",
(obj->nobj && obj->nexthere) ? "s" : "");
}
/* Scrolls, spellbooks, potions, weapons and
pieces of armor may get affected by the water */
for (; obj; obj = otmp) {
otmp = 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->otyp == POT_ACID) {
char *bufp, buf[BUFSZ];
boolean one = (obj->quan == 1L);
bufp = strcpy(buf, "potion");
if (!one) bufp = makeplural(bufp);
/* [should we damage player/monster?] */
pline("%s %s %s!", /* "A potion explodes!" */
!exploded ? (one ? "A" : "Some") :
(one ? "Another" : "More"),
bufp, vtense(bufp, "explode"));
exploded = TRUE;
/* let caller know that obj has gone away
[when obj is part of a list, delobj()'s
obj_extract_self() takes care of this;
for loose_obj, obj should always equal
*objp and otmp should always be null] */
if (loose_obj && obj == *objp) *objp = otmp;
delobj(obj);
continue;
} else 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(TRUE);
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 */
obj->in_use = TRUE;
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) remove_worn_item(otmp, FALSE);
*lostsome = TRUE;
dropx(otmp);
invc--;
}
return(TRUE);
}
/*
* return(TRUE) == player relocated
*/
boolean
drown()
{
const char *pool_of_water;
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 = Maybe_Half_Phys(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)) &&
!Unaware && (Teleport_control || rn2(3) < Luck+2)) {
You("attempt a teleport spell."); /* utcsri!carroll */
if (!level.flags.noteleport) {
(void) dotele();
if(!is_pool(u.ux,u.uy))
return(TRUE);
} else pline_The("attempted teleport spell fails.");
}
#ifdef STEED
if (u.usteed) {
dismount_steed(DISMOUNT_GENERIC);
if(!is_pool(u.ux,u.uy))
return(TRUE);
}
#endif
crawl_ok = FALSE;
x = y = 0; /* lint suppression */
/* if sleeping, wake up now so that we don't crawl out of water
while still asleep; we can't do that the same way that waking
due to combat is handled; note unmul() clears u.usleep */
if (u.usleep) unmul("Suddenly you wake up!");
/* being doused will revive from fainting */
if (is_fainted()) reset_faint();
/* can't crawl if unable to move (crawl_ok flag stays false) */
if (multi < 0 || (Upolyd && !youmonst.data->mmove)) goto crawl;
/* 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, 0)) {
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, 0)) {
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,TRUE);
return(TRUE);
}
/* still too much weight */
pline("But in vain.");
}
u.uinwater = 1;
You("drown.");
for (;;) {
/* killer format and name are reconstructed every iteration
because lifesaving resets them */
pool_of_water = waterbody_name(u.ux, u.uy);
killer.format = KILLED_BY_AN;
/* avoid "drowned in [a] water" */
if (!strcmp(pool_of_water, "water"))
pool_of_water = "deep water", killer.format = KILLED_BY;
Strcpy(killer.name, pool_of_water);
done(DROWNING);
/* oops, we're still alive. better get out of the water. */
if (safe_teleds(TRUE)) break; /* successful life-save */
/* nowhere safe to land; repeat drowning loop... */
pline("You're still drowning.");
}
if (u.uinwater) {
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) {
You_feel("momentarily lethargic.");
} else {
You_feel("your magical energy drain away!");
u.uen -= n;
if(u.uen < 0) {
u.uenmax -= rnd(-u.uen);
if(u.uenmax < 0) u.uenmax = 0;
u.uen = 0;
}
context.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);
}
/* 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 */
void
cnv_trap_obj(otyp, cnt, ttmp, bury_it)
int otyp;
int cnt;
struct trap *ttmp;
boolean bury_it;
{
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);
if (bury_it) {
/* magical digging first disarms this trap, then will unearth it */
(void) bury_an_obj(otmp);
} else {
/* Sell your own traps only... */
if (ttmp->madeby_u) sellobj(otmp, ttmp->tx, ttmp->ty);
stackobj(otmp);
}
newsym(ttmp->tx, ttmp->ty);
if (u.utrap && ttmp->tx == u.ux && ttmp->ty == u.uy) u.utrap = 0;
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, TRUE)) {
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);
/* marking the trap unseen forces dotrap() to treat it like a new
discovery and prevents pickup() -> look_here() -> check_here()
from giving a redudant "there is a <trap> here" message when
there are objects covering this trap */
ttmp->tseen = 0; /* hack for check_here() */
/* trigger the trap */
spoteffects(TRUE); /* pickup() + 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;
}
/* duplicate tight-space checks from test_move */
if (u.dx && u.dy &&
bad_rock(youmonst.data,u.ux,ttmp->ty) &&
bad_rock(youmonst.data,ttmp->tx,u.uy)) {
if ((invent && (inv_weight() + weight_cap() > 600)) ||
bigmonst(youmonst.data)) {
/* don't allow untrap if they can't get thru to it */
You("are unable to reach the %s!",
defsyms[trap_to_defsym(ttype)].explanation);
return 0;
}
}
/* untrappable traps are located on the ground. */
if (!can_reach_floor(TRUE)) {
#ifdef STEED
if (u.usteed && P_SKILL(P_RIDING) < P_BASIC)
rider_cant_reach();
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);
#ifdef STEED
if (u.usteed && u.utrap) {
/* you, not steed, are trapped */
dismount_steed(DISMOUNT_FELL);
}
#endif
}
} 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 &&
!mtmp->msleeping && !mtmp->mfrozen &&
!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, FALSE);
} 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, FALSE);
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) {
consume_obj_charge(obj, TRUE);
} 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, FALSE);
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 int x,y;
int ch;
struct trap *ttmp;
struct monst *mtmp;
const char *trapdescr;
boolean here, useplural,
confused = (Confusion || Hallucination),
trap_skipped = FALSE,
deal_with_floor_trap;
int boxcnt = 0;
char the_trap[BUFSZ], qbuf[QBUFSZ];
if(!getdir((char *)0)) return(0);
x = u.ux + u.dx;
y = u.uy + u.dy;
if (!isok(x, y)) {
pline_The("perils lurking there are beyond your grasp.");
return 0;
}
ttmp = t_at(x, y);
if (ttmp && !ttmp->tseen) ttmp = 0;
trapdescr = ttmp ? defsyms[trap_to_defsym(ttmp->ttyp)].explanation : 0;
here = (x == u.ux && y == u.uy); /* !u.dx && !u.dy */
if (here) /* are there are one or more containers here? */
for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
if (Is_box(otmp)) {
if (++boxcnt > 1) break;
}
deal_with_floor_trap = can_reach_floor(FALSE);
if (!deal_with_floor_trap) {
*the_trap = '\0';
if (ttmp) Strcat(the_trap, an(trapdescr));
if (ttmp && boxcnt) Strcat(the_trap, " and ");
if (boxcnt) Strcat(the_trap,
(boxcnt == 1) ? "a container" : "containers");
useplural = ((ttmp && boxcnt > 0) || boxcnt > 1);
/* note: boxcnt and useplural will always be 0 for !here case */
if (ttmp || boxcnt)
There("%s %s %s but you can't reach %s%s.",
useplural ? "are" : "is",
the_trap, here ? "here" : "there",
useplural ? "them" : "it",
#ifdef STEED
u.usteed ? " while mounted" :
#endif
"");
trap_skipped = (ttmp != 0);
} else { /* deal_with_floor_trap */
if (ttmp) {
Strcpy(the_trap, the(trapdescr));
if (boxcnt) {
if (ttmp->ttyp == PIT || ttmp->ttyp == SPIKED_PIT) {
You_cant("do much about %s%s.",
the_trap, u.utrap ?
" that you're stuck in" :
" while standing on the edge of it");
trap_skipped = TRUE;
deal_with_floor_trap = FALSE;
} else {
Sprintf(qbuf, "There %s and %s here. %s %s?",
(boxcnt == 1) ? "is a container" :
"are containers",
an(trapdescr),
(ttmp->ttyp == WEB) ? "Remove" : "Disarm",
the_trap);
switch (ynq(qbuf)) {
case 'q': return(0);
case 'n': trap_skipped = TRUE;
deal_with_floor_trap = FALSE;
break;
}
}
}
if (deal_with_floor_trap) {
if (u.utrap) {
You("cannot deal with %s while trapped%s!", the_trap,
(x == u.ux && y == u.uy) ? " in it" : "");
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 (here) {
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.",
!here ? "that" : "this");
return 0;
}
}
} /* end if */
if (boxcnt) {
for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
if(Is_box(otmp)) {
(void)safe_qbuf(qbuf, "There is ",
" here. Check it for traps?",
otmp, doname, ansimpleoname, "a box");
switch (ynq(qbuf)) {
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)));
if (!confused) 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);
}
} /* deal_with_floor_trap */
/* doors can be manipulated even while levitating/unskilled riding */
if (!IS_DOOR(levl[x][y].typ)) {
if (!trap_skipped)
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);
}
}
/* for magic unlocking; returns true if targetted monster (which might
be hero) gets untrapped; the trap remains intact */
boolean
openholdingtrap(mon, noticed)
struct monst *mon;
boolean *noticed; /* set to true iff hero notices the effect; */
{ /* otherwise left with its previous value intact */
struct trap *t;
char buf[BUFSZ];
const char *trapdescr, *which;
boolean ishero = (mon == &youmonst);
#ifdef STEED
if (mon == u.usteed) ishero = TRUE;
#endif
t = t_at(ishero ? u.ux : mon->mx, ishero ? u.uy : mon->my);
/* if no trap here or it's not a holding trap, we're done */
if (!t || (t->ttyp != BEAR_TRAP && t->ttyp != WEB)) return FALSE;
trapdescr = defsyms[trap_to_defsym(t->ttyp)].explanation;
which = t->tseen ? the_your[t->madeby_u] :
index(vowels, *trapdescr) ? "an" : "a";
if (ishero) {
if (!u.utrap) return FALSE;
u.utrap = 0; /* released regardless of type */
*noticed = TRUE;
/* give message only if trap was the expected type */
if (u.utraptype == TT_BEARTRAP || u.utraptype == TT_WEB) {
#ifdef STEED
if (u.usteed)
Sprintf(buf, "%s is", noit_Monnam(u.usteed));
else
#endif
Strcpy(buf, "You are");
pline("%s released from %s %s.", buf, which, trapdescr);
}
} else {
if (!mon->mtrapped) return FALSE;
mon->mtrapped = 0;
if (canspotmon(mon)) {
*noticed = TRUE;
pline("%s is released from %s %s.",
Monnam(mon), which, trapdescr);
} else if (cansee(t->tx, t->ty) && t->tseen) {
*noticed = TRUE;
if (t->ttyp == WEB)
pline("%s is released from %s %s.",
Something, which, trapdescr);
else /* BEAR_TRAP */
pline("%s %s opens.", upstart(strcpy(buf, which)), trapdescr);
}
/* might pacify monster if adjacent */
if (rn2(2) && distu(mon->mx, mon->my) <= 2) reward_untrap(t, mon);
}
return TRUE;
}
/* for magic locking; returns true if targetted monster (which might
be hero) gets hit by a trap (might avoid actually becoming trapped) */
boolean
closeholdingtrap(mon, noticed)
struct monst *mon;
boolean *noticed; /* set to true iff hero notices the effect; */
{ /* otherwise left with its previous value intact */
struct trap *t;
unsigned dotrapflags;
boolean ishero = (mon == &youmonst), result;
#ifdef STEED
if (mon == u.usteed) ishero = TRUE;
#endif
t = t_at(ishero ? u.ux : mon->mx, ishero ? u.uy : mon->my);
/* if no trap here or it's not a holding trap, we're done */
if (!t || (t->ttyp != BEAR_TRAP && t->ttyp != WEB)) return FALSE;
if (ishero) {
if (u.utrap) return FALSE; /* already trapped */
*noticed = TRUE;
dotrapflags = FORCETRAP;
#ifdef STEED
/* dotrap calls mintrap when mounted hero encounters a web */
if (u.usteed) dotrapflags |= NOWEBMSG;
#endif
++force_mintrap;
dotrap(t, dotrapflags);
--force_mintrap;
result = (u.utrap != 0);
} else {
if (mon->mtrapped) return FALSE; /* already trapped */
/* you notice it if you see the trap close/tremble/whatever
or if you sense the monster who becomes trapped */
*noticed = cansee(t->tx, t->ty) || canspotmon(mon);
++force_mintrap;
result = (mintrap(mon) != 0);
--force_mintrap;
}
return result;
}
/* for magic unlocking; returns true if targetted monster (which might
be hero) gets hit by a trap (target might avoid its effect) */
boolean
openfallingtrap(mon, trapdoor_only, noticed)
struct monst *mon;
boolean trapdoor_only;
boolean *noticed; /* set to true iff hero notices the effect; */
{ /* otherwise left with its previous value intact */
struct trap *t;
boolean ishero = (mon == &youmonst), result;
#ifdef STEED
if (mon == u.usteed) ishero = TRUE;
#endif
t = t_at(ishero ? u.ux : mon->mx, ishero ? u.uy : mon->my);
/* if no trap here or it's not a falling trap, we're done
(note: falling rock traps have a trapdoor in the ceiling) */
if (!t || ((t->ttyp != TRAPDOOR && t->ttyp != ROCKTRAP) &&
(trapdoor_only ||
(t->ttyp != HOLE && t->ttyp != PIT && t->ttyp != SPIKED_PIT))))
return FALSE;
if (ishero) {
if (u.utrap) return FALSE; /* already trapped */
*noticed = TRUE;
dotrap(t, FORCETRAP);
result = (u.utrap != 0);
} else {
if (mon->mtrapped) return FALSE; /* already trapped */
/* you notice it if you see the trap close/tremble/whatever
or if you sense the monster who becomes trapped */
*noticed = cansee(t->tx, t->ty) || canspotmon(mon);
/* monster will be angered; mintrap doesn't handle that */
wakeup(mon);
++force_mintrap;
result = (mintrap(mon) != 0);
--force_mintrap;
/* mon might now be on the migrating monsters list */
}
return TRUE;
}
/* 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!", Tobjnam(obj, "explode"));
Sprintf(buf, "exploding %s", xname(obj));
if(costly)
loss += stolen_value(obj, ox, oy,
(boolean)shkp->mpeaceful, TRUE);
delete_contents(obj);
/* we're about to delete all things at this location,
* which could include the ball & chain.
* If we attempt to call unpunish() in the
* for-loop below we can end up with otmp2
* being invalid once the chain is gone.
* Deal with ball & chain right now instead.
*/
if (Punished && !carried(uball) &&
((uchain->ox == u.ux && uchain->oy == u.uy) ||
(uball->ox == u.ux && uball->oy == u.uy)))
unpunish();
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(Maybe_Half_Phys(d(6,6)), buf, KILLED_BY_AN);
exercise(A_STR, FALSE);
if(costly && loss) {
if(insider)
You("owe %ld %s for objects destroyed.",
loss, currency(loss));
else {
You("caused %ld %s worth of damage!",
loss, currency(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, FALSE);
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, FALSE);
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.",
Blind ? blindgas[rn2(SIZE(blindgas))] :
rndcolor(), the(xname(obj)));
if(!Stunned) {
if (Hallucination)
pline("What a groovy feeling!");
else
You("%s%s...",
stagger(youmonst.data, "stagger"),
Halluc_resistance ? "" :
Blind ? " and get dizzy" :
" and your vision blurs");
}
make_stunned(HStun + rn1(7, 16),FALSE);
(void) make_hallucinated(HHallucination + rn1(5, 16),FALSE,0L);
break;
default: impossible("bad chest trap");
break;
}
bot(); /* to get immediate botl re-display */
}
return FALSE;
}
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);
}
void
deltrap(trap)
register struct trap *trap;
{
register struct trap *ttmp;
clear_conjoined_pits(trap);
if(trap == ftrap)
ftrap = ftrap->ntrap;
else {
for(ttmp = ftrap; ttmp->ntrap != trap; ttmp = ttmp->ntrap) ;
ttmp->ntrap = trap->ntrap;
}
dealloc_trap(trap);
}
boolean
conjoined_pits(trap2, trap1, u_entering_trap2)
struct trap *trap2, *trap1;
boolean u_entering_trap2;
{
int dx, dy, diridx, adjidx;
if (!trap1 || !trap2) return FALSE;
if (!isok(trap2->tx,trap2->ty) || !isok(trap1->tx,trap1->ty) ||
!(trap2->ttyp == PIT || trap2->ttyp == SPIKED_PIT) ||
!(trap1->ttyp == PIT || trap1->ttyp == SPIKED_PIT) ||
(u_entering_trap2 && !(u.utrap && u.utraptype == TT_PIT)))
return FALSE;
dx = sgn(trap2->tx - trap1->tx);
dy = sgn(trap2->ty - trap1->ty);
for (diridx = 0; diridx < 8; diridx++)
if (xdir[diridx] == dx && ydir[diridx] == dy)
break;
/* diridx is valid if < 8 */
if (diridx < 8) {
adjidx = (diridx + 4) % 8;
if ((trap1->conjoined & (1 << diridx)) &&
(trap2->conjoined & (1 << adjidx)))
return TRUE;
}
return FALSE;
}
void
clear_conjoined_pits(trap)
struct trap *trap;
{
int diridx, adjidx, x, y;
struct trap *t;
if (trap && (trap->ttyp == PIT || trap->ttyp == SPIKED_PIT)) {
for(diridx = 0; diridx < 8; ++diridx) {
if (trap->conjoined & (1 << diridx)) {
x = trap->tx + xdir[diridx];
y = trap->ty + ydir[diridx];
t = t_at(x,y);
if (isok(x,y) && t &&
(t->ttyp == PIT || t->ttyp == SPIKED_PIT)) {
adjidx = (diridx + 4) % 8;
t->conjoined &= ~(1 << adjidx);
}
trap->conjoined &= ~(1 << diridx);
}
}
}
}
#if 0
/*
* Mark all neighboring pits as conjoined pits.
* (currently not called from anywhere)
*/
STATIC_OVL void
join_adjacent_pits(trap)
struct trap *trap;
{
struct trap *t;
int diridx, x, y;
if (!trap) return;
for(diridx = 0; diridx < 8; ++diridx) {
x = trap->tx + xdir[diridx];
y = trap->ty + ydir[diridx];
if (isok(x,y)) {
if (((t = t_at(x,y)) != 0) &&
(t->ttyp == PIT || t->ttyp == SPIKED_PIT)) {
trap->conjoined |= (1 << diridx);
join_adjacent_pits(t);
} else trap->conjoined &= ~(1 << diridx);
}
}
}
#endif
/*
* Returns TRUE if you escaped a pit and are standing on the precipice.
*/
boolean
uteetering_at_seen_pit(trap)
struct trap *trap;
{
if (trap && trap->tseen &&
(!u.utrap || u.utraptype != TT_PIT) &&
(trap->ttyp==PIT || trap->ttyp==SPIKED_PIT))
return TRUE;
else
return FALSE;
}
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(Maybe_Half_Phys(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, nocorpse)
int tlev;
struct monst *mon;
struct obj *obj;
int d_override;
boolean nocorpse;
{
int strike;
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 (obj && 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, "", nocorpse ? -AD_RBRE : 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 awake", 9) ||
!strncmp(nomovemsg,"You regain con", 14) ||
!strncmp(nomovemsg,"You are consci", 14))));
}
static const char lava_killer[] = "molten lava";
boolean
lava_effects()
{
register struct obj *obj, *obj2;
int dmg = d(6, 6); /* only applicable for water walking */
boolean usurvive, boil_away;
usurvive = Fire_resistance || (Wwalking && dmg < u.uhp);
/* a timely interrupt might manage to salvage your life
but not your gear; do this before messages */
if (!usurvive)
for (obj = invent; obj; obj = obj->nobj)
if (is_organic(obj) && !obj->oerodeproof) obj->in_use = TRUE;
burn_away_slime();
if (likes_lava(youmonst.data)) return FALSE;
if (!Fire_resistance) {
if(Wwalking) {
pline_The("lava here burns you!");
if (usurvive) {
losehp(dmg, lava_killer, KILLED_BY); /* lava damage */
goto burn_stuff;
}
} else
You("fall into the lava!");
usurvive = Lifesaved || discover;
#ifdef WIZARD
if (wizard) usurvive = TRUE;
#endif
/* prevent Boots_off() -> spoteffects() -> lava_effects() recursion
which would successfully delete (via useupall) the no-longer-worn
boots; once recursive call returned, we would try to delete them
again here in the outer call (access stale memory, probably panic) */
iflags.in_lava_effects++;
for(obj = invent; obj; obj = obj2) {
obj2 = obj->nobj;
if(is_organic(obj) && !obj->oerodeproof) {
if(obj->owornmask) {
if (usurvive)
pline("%s into flame!", Yobjnam2(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) (void) Shirt_off();
#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);
}
}
iflags.in_lava_effects--;
/* s/he died... */
boil_away = (u.umonnum == PM_WATER_ELEMENTAL ||
u.umonnum == PM_STEAM_VORTEX ||
u.umonnum == PM_FOG_CLOUD);
for (;;) {
u.uhp = -1;
/* killer format and name are reconstructed every iteration
because lifesaving resets them */
killer.format = KILLED_BY;
Strcpy(killer.name, lava_killer);
You("%s...", boil_away ? "boil away" : "burn to a crisp");
done(BURNING);
if (safe_teleds(TRUE)) break; /* successful life-save */
/* nowhere safe to land; repeat burning loop */
pline("You're still 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); /* lava damage */
}
/* 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;
pline("%s into flame!", Yobjnam2(obj, "burst"));
(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);
}
/* called each turn when trapped in lava */
void
sink_into_lava()
{
static const char sink_deeper[] = "You sink deeper into the lava.";
if (!u.utrap || u.utraptype != TT_LAVA) {
; /* do nothing; this shouldn't happen */
} else if (!is_lava(u.ux, u.uy)) {
u.utrap = 0; /* this shouldn't happen either */
} else if (!u.uinvulnerable) {
u.utrap -= (1 << 8);
if (u.utrap < (1 << 8)) {
killer.format = KILLED_BY;
Strcpy(killer.name, "molten lava");
You("sink below the surface and die.");
burn_away_slime(); /* add insult to injury? */
done(DISSOLVED);
} else if (!u.umoved) {
/* can't fully turn into slime while in lava, but might not
have it be burned away until you've come awfully close */
if (Slimed && rnd(10 - 1) >= (int)(Slimed & TIMEOUT)) {
pline(sink_deeper);
burn_away_slime();
} else {
Norep(sink_deeper);
}
u.utrap += rnd(4);
}
}
}
/*trap.c*/