2330 lines
80 KiB
C
2330 lines
80 KiB
C
/* NetHack 3.7 dig.c $NHDT-Date: 1740629713 2025/02/26 20:15:13 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.227 $ */
|
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
|
/*-Copyright (c) Michael Allison, 2012. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
#include "hack.h"
|
|
|
|
staticfn boolean rm_waslit(void);
|
|
staticfn void mkcavepos(coordxy, coordxy, int, boolean, boolean);
|
|
staticfn void mkcavearea(boolean);
|
|
staticfn boolean pick_can_reach(struct obj *, coordxy, coordxy) NONNULLARG1;
|
|
staticfn int dig(void);
|
|
staticfn void dig_up_grave(coord *);
|
|
staticfn boolean watchman_canseeu(struct monst *) NONNULLARG1;
|
|
staticfn int adj_pit_checks(coord *, char *) NONNULLARG2;
|
|
staticfn void pit_flow(struct trap *, schar);
|
|
staticfn boolean furniture_handled(coordxy, coordxy, boolean);
|
|
|
|
/* Indices returned by dig_typ() */
|
|
enum dig_types {
|
|
DIGTYP_UNDIGGABLE = 0,
|
|
DIGTYP_ROCK,
|
|
DIGTYP_STATUE,
|
|
DIGTYP_BOULDER,
|
|
DIGTYP_DOOR,
|
|
DIGTYP_TREE
|
|
};
|
|
|
|
staticfn boolean
|
|
rm_waslit(void)
|
|
{
|
|
coordxy x, y;
|
|
|
|
if (levl[u.ux][u.uy].typ == ROOM && levl[u.ux][u.uy].waslit)
|
|
return TRUE;
|
|
for (x = u.ux - 2; x < u.ux + 3; x++)
|
|
for (y = u.uy - 1; y < u.uy + 2; y++)
|
|
if (isok(x, y) && levl[x][y].waslit)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
/* Change level topology. Messes with vision tables and ignores things like
|
|
* boulders in the name of a nice effect. Vision will get fixed up again
|
|
* immediately after the effect is complete.
|
|
*/
|
|
staticfn void
|
|
mkcavepos(coordxy x, coordxy y, int dist, boolean waslit, boolean rockit)
|
|
{
|
|
struct rm *lev;
|
|
|
|
if (!isok(x, y))
|
|
return;
|
|
lev = &levl[x][y];
|
|
|
|
if (rockit) {
|
|
struct monst *mtmp;
|
|
|
|
if (IS_OBSTRUCTED(lev->typ))
|
|
return;
|
|
if (t_at(x, y))
|
|
return; /* don't cover the portal */
|
|
if ((mtmp = m_at(x, y)) != 0) /* make sure crucial monsters survive */
|
|
if (!passes_walls(mtmp->data))
|
|
(void) rloc(mtmp, RLOC_NOMSG);
|
|
} else if (lev->typ == ROOM)
|
|
return;
|
|
|
|
unblock_point(x, y); /* make sure vision knows this location is open */
|
|
|
|
/* fake out saved state */
|
|
lev->seenv = 0;
|
|
lev->doormask = 0;
|
|
if (dist < 3)
|
|
lev->lit = (rockit ? FALSE : TRUE);
|
|
if (waslit)
|
|
lev->waslit = (rockit ? FALSE : TRUE);
|
|
lev->horizontal = FALSE;
|
|
/* short-circuit vision recalc */
|
|
gv.viz_array[y][x] = (dist < 3) ? (IN_SIGHT | COULD_SEE) : COULD_SEE;
|
|
lev->typ = (rockit ? STONE : ROOM); /* flags set via doormask above */
|
|
if (dist >= 3)
|
|
impossible("mkcavepos called with dist %d", dist);
|
|
feel_newsym(x, y);
|
|
}
|
|
|
|
staticfn void
|
|
mkcavearea(boolean rockit)
|
|
{
|
|
int dist;
|
|
coordxy xmin = u.ux, xmax = u.ux;
|
|
coordxy ymin = u.uy, ymax = u.uy;
|
|
coordxy i;
|
|
boolean waslit = rm_waslit();
|
|
|
|
if (rockit) {
|
|
Soundeffect(se_crashing_rock, 100);
|
|
pline("Crash! The ceiling collapses around you!");
|
|
} else {
|
|
pline("A mysterious force %s cave around you!",
|
|
(levl[u.ux][u.uy].typ == CORR) ? "creates a" : "extends the");
|
|
}
|
|
display_nhwindow(WIN_MESSAGE, TRUE);
|
|
|
|
for (dist = 1; dist <= 2; dist++) {
|
|
xmin--;
|
|
xmax++;
|
|
|
|
/* top and bottom */
|
|
if (dist < 2) { /* the area is wider that it is high */
|
|
ymin--;
|
|
ymax++;
|
|
for (i = xmin + 1; i < xmax; i++) {
|
|
mkcavepos(i, ymin, dist, waslit, rockit);
|
|
mkcavepos(i, ymax, dist, waslit, rockit);
|
|
}
|
|
}
|
|
|
|
/* left and right */
|
|
for (i = ymin; i <= ymax; i++) {
|
|
mkcavepos(xmin, i, dist, waslit, rockit);
|
|
mkcavepos(xmax, i, dist, waslit, rockit);
|
|
}
|
|
|
|
flush_screen(1); /* make sure the new glyphs shows up */
|
|
nh_delay_output();
|
|
}
|
|
|
|
if (!rockit && levl[u.ux][u.uy].typ == CORR) {
|
|
levl[u.ux][u.uy].typ = ROOM; /* flags for CORR already 0 */
|
|
if (waslit)
|
|
levl[u.ux][u.uy].waslit = TRUE;
|
|
newsym(u.ux, u.uy); /* in case player is invisible */
|
|
}
|
|
|
|
gv.vision_full_recalc = 1; /* everything changed */
|
|
}
|
|
|
|
/* called when attempting to break a statue or boulder with a pick */
|
|
staticfn boolean
|
|
pick_can_reach(struct obj *pick, coordxy x, coordxy y)
|
|
{
|
|
struct trap *t = t_at(x, y);
|
|
/* tseen: pit only affects item positioning when it is known */
|
|
boolean target_in_pit = t && is_pit(t->ttyp) && t->tseen;
|
|
|
|
/* if hero is in a pit, pick can only reach if the statue is too and
|
|
the two pits are conjoined or the statue isn't and pick is two-handed;
|
|
this applies to hero in pit trying to reach an adjcacent boulder too */
|
|
if (u.utrap && u.utraptype == TT_PIT) {
|
|
if (target_in_pit)
|
|
return conjoined_pits(t, t_at(u.ux, u.uy), FALSE);
|
|
return bimanual(pick);
|
|
}
|
|
|
|
/* when hero isn't in a pit, a mattock or flying hero w/ pick can reach
|
|
whether or not the statue is in a pit */
|
|
if (bimanual(pick) || Flying)
|
|
return TRUE;
|
|
/* one-handed pick-axe can reach if statue isn't in a pit */
|
|
if (!target_in_pit)
|
|
return TRUE;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* When digging into location <x,y>, what are you actually digging into? */
|
|
int
|
|
dig_typ(struct obj *otmp, coordxy x, coordxy y)
|
|
{
|
|
int ltyp;
|
|
|
|
if (!isok(x, y) || !otmp || (!is_pick(otmp) && !is_axe(otmp)))
|
|
return DIGTYP_UNDIGGABLE;
|
|
|
|
ltyp = levl[x][y].typ;
|
|
if (is_axe(otmp))
|
|
return closed_door(x, y) ? DIGTYP_DOOR
|
|
: IS_TREE(ltyp) ? DIGTYP_TREE /* axe vs tree */
|
|
: DIGTYP_UNDIGGABLE;
|
|
/*assert(is_pick(otmp));*/
|
|
return (sobj_at(STATUE, x, y) && pick_can_reach(otmp, x, y))
|
|
? DIGTYP_STATUE
|
|
: (sobj_at(BOULDER, x, y) && pick_can_reach(otmp, x, y))
|
|
? DIGTYP_BOULDER
|
|
: closed_door(x, y) ? DIGTYP_DOOR
|
|
: IS_TREE(ltyp) ? DIGTYP_UNDIGGABLE /* pick vs tree */
|
|
: (IS_OBSTRUCTED(ltyp)
|
|
&& (!svl.level.flags.arboreal || IS_WALL(ltyp)))
|
|
? DIGTYP_ROCK
|
|
: DIGTYP_UNDIGGABLE;
|
|
}
|
|
|
|
boolean
|
|
is_digging(void)
|
|
{
|
|
if (go.occupation == dig) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
#define BY_YOU (&gy.youmonst)
|
|
#define BY_OBJECT ((struct monst *) 0)
|
|
|
|
enum digcheck_result
|
|
dig_check(struct monst *madeby, coordxy x, coordxy y)
|
|
{
|
|
struct trap *ttmp = t_at(x, y);
|
|
|
|
if (On_stairs(x, y)) {
|
|
stairway *stway = stairway_at(x, y);
|
|
if (stway->isladder) {
|
|
return DIGCHECK_FAIL_ONLADDER;
|
|
} else {
|
|
return DIGCHECK_FAIL_ONSTAIRS;
|
|
}
|
|
} else if (IS_THRONE(levl[x][y].typ) && madeby != BY_OBJECT) {
|
|
return DIGCHECK_FAIL_THRONE;
|
|
} else if (IS_ALTAR(levl[x][y].typ)
|
|
&& (madeby != BY_OBJECT
|
|
|| (altarmask_at(x, y) & AM_SANCTUM) != 0)) {
|
|
return DIGCHECK_FAIL_ALTAR;
|
|
} else if (Is_airlevel(&u.uz)) {
|
|
return DIGCHECK_FAIL_AIRLEVEL;
|
|
} else if (Is_waterlevel(&u.uz)) {
|
|
return DIGCHECK_FAIL_WATERLEVEL;
|
|
} else if ((IS_OBSTRUCTED(levl[x][y].typ) && levl[x][y].typ != SDOOR
|
|
&& (levl[x][y].wall_info & W_NONDIGGABLE) != 0)) {
|
|
return DIGCHECK_FAIL_TOOHARD;
|
|
} else if (ttmp && undestroyable_trap(ttmp->ttyp)) {
|
|
return DIGCHECK_FAIL_UNDESTROYABLETRAP;
|
|
} else if (!Can_dig_down(&u.uz) && !levl[x][y].candig) {
|
|
if (ttmp) {
|
|
if (!is_hole(ttmp->ttyp) && !is_pit(ttmp->ttyp))
|
|
return DIGCHECK_PASSED_DESTROY_TRAP;
|
|
else
|
|
return DIGCHECK_FAIL_CANTDIG;
|
|
} else {
|
|
return DIGCHECK_PASSED_PITONLY;
|
|
}
|
|
} else if (sobj_at(BOULDER, x, y)) {
|
|
return DIGCHECK_FAIL_BOULDER;
|
|
} else if (madeby == BY_OBJECT
|
|
/* the block against existing traps is mainly to
|
|
prevent broken wands from turning holes into pits */
|
|
&& (ttmp || is_pool_or_lava(x, y))) {
|
|
/* digging by player handles pools separately */
|
|
return DIGCHECK_FAIL_OBJ_POOL_OR_TRAP;
|
|
}
|
|
return DIGCHECK_PASSED;
|
|
}
|
|
|
|
void
|
|
digcheck_fail_message(enum digcheck_result digresult, struct monst *madeby,
|
|
coordxy x, coordxy y)
|
|
{
|
|
const char *verb =
|
|
(madeby == BY_YOU && uwep && is_axe(uwep)) ? "chop" : "dig in";
|
|
|
|
if (digresult < DIGCHECK_FAILED)
|
|
return;
|
|
|
|
switch (digresult) {
|
|
case DIGCHECK_FAIL_AIRLEVEL:
|
|
You("cannot %s thin air.", verb);
|
|
break;
|
|
case DIGCHECK_FAIL_ALTAR:
|
|
pline_The("altar is too hard to break apart.");
|
|
break;
|
|
case DIGCHECK_FAIL_BOULDER:
|
|
There("isn't enough room to %s here.", verb);
|
|
break;
|
|
case DIGCHECK_FAIL_ONLADDER:
|
|
pline_The("ladder resists your effort.");
|
|
break;
|
|
case DIGCHECK_FAIL_ONSTAIRS:
|
|
pline_The("stairs are too hard to %s.", verb);
|
|
break;
|
|
case DIGCHECK_FAIL_THRONE:
|
|
pline_The("throne is too hard to break apart.");
|
|
break;
|
|
case DIGCHECK_FAIL_CANTDIG:
|
|
case DIGCHECK_FAIL_TOOHARD:
|
|
case DIGCHECK_FAIL_UNDESTROYABLETRAP:
|
|
pline_The("%s here is too hard to %s.", surface(x, y), verb);
|
|
break;
|
|
case DIGCHECK_FAIL_WATERLEVEL:
|
|
pline_The("%s splashes and subsides.", hliquid("water"));
|
|
break;
|
|
case DIGCHECK_FAIL_OBJ_POOL_OR_TRAP:
|
|
case DIGCHECK_PASSED:
|
|
case DIGCHECK_PASSED_PITONLY:
|
|
case DIGCHECK_PASSED_DESTROY_TRAP:
|
|
break;
|
|
}
|
|
}
|
|
|
|
staticfn int
|
|
dig(void)
|
|
{
|
|
struct rm *lev;
|
|
coordxy dpx = svc.context.digging.pos.x, dpy = svc.context.digging.pos.y;
|
|
boolean ispick = uwep && is_pick(uwep);
|
|
const char *verb = (!uwep || is_pick(uwep)) ? "dig into" : "chop through";
|
|
enum digcheck_result dcresult = DIGCHECK_PASSED;
|
|
|
|
lev = &levl[dpx][dpy];
|
|
/* perhaps a nymph stole your pick-axe while you were busy digging */
|
|
/* or perhaps you teleported away */
|
|
if (u.uswallow || !uwep || (!ispick && !is_axe(uwep))
|
|
|| !on_level(&svc.context.digging.level, &u.uz)
|
|
|| ((svc.context.digging.down ? (dpx != u.ux || dpy != u.uy)
|
|
: !next2u(dpx, dpy))))
|
|
return 0;
|
|
|
|
if (svc.context.digging.down) {
|
|
dcresult = dig_check(BY_YOU, u.ux, u.uy);
|
|
if (dcresult >= DIGCHECK_FAILED) {
|
|
digcheck_fail_message(dcresult, BY_YOU, u.ux, u.uy);
|
|
return 0;
|
|
}
|
|
} else { /* !svc.context.digging.down */
|
|
if (IS_TREE(lev->typ) && !may_dig(dpx, dpy)
|
|
&& dig_typ(uwep, dpx, dpy) == DIGTYP_TREE) {
|
|
pline("This tree seems to be petrified.");
|
|
return 0;
|
|
}
|
|
if (IS_OBSTRUCTED(lev->typ) && !may_dig(dpx, dpy)
|
|
&& dig_typ(uwep, dpx, dpy) == DIGTYP_ROCK) {
|
|
pline("This %s is too hard to %s.",
|
|
is_db_wall(dpx, dpy) ? "drawbridge" : "wall", verb);
|
|
return 0;
|
|
}
|
|
}
|
|
if (Fumbling && !rn2(3)) {
|
|
switch (rn2(3)) {
|
|
case 0:
|
|
if (!welded(uwep)) {
|
|
You("fumble and drop %s.", yname(uwep));
|
|
dropx(uwep);
|
|
} else {
|
|
if (u.usteed)
|
|
pline("%s and %s %s!", Yobjnam2(uwep, "bounce"),
|
|
otense(uwep, "hit"), mon_nam(u.usteed));
|
|
else
|
|
pline("Ouch! %s and %s you!", Yobjnam2(uwep, "bounce"),
|
|
otense(uwep, "hit"));
|
|
set_wounded_legs(RIGHT_SIDE, 5 + rnd(5));
|
|
}
|
|
break;
|
|
case 1:
|
|
Soundeffect(se_bang_weapon_side, 100);
|
|
pline("Bang! You hit with the broad side of %s!",
|
|
the(xname(uwep)));
|
|
wake_nearby(FALSE);
|
|
break;
|
|
default:
|
|
Your("swing misses its mark.");
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
svc.context.digging.effort +=
|
|
10 + rn2(5) + abon() + uwep->spe - greatest_erosion(uwep) + u.udaminc;
|
|
if (Race_if(PM_DWARF))
|
|
svc.context.digging.effort *= 2;
|
|
if (svc.context.digging.down) {
|
|
struct trap *ttmp = t_at(dpx, dpy);
|
|
|
|
if (svc.context.digging.effort > 250
|
|
|| (ttmp && ttmp->ttyp == HOLE)) {
|
|
(void) dighole(FALSE, FALSE, (coord *) 0);
|
|
(void) memset((genericptr_t) &svc.context.digging, 0,
|
|
sizeof svc.context.digging);
|
|
return 0; /* done with digging */
|
|
}
|
|
|
|
if (svc.context.digging.effort <= 50
|
|
|| (ttmp && (ttmp->ttyp == TRAPDOOR || is_pit(ttmp->ttyp)))) {
|
|
return 1;
|
|
} else if (ttmp && (ttmp->ttyp == LANDMINE
|
|
|| (ttmp->ttyp == BEAR_TRAP && !u.utrap))) {
|
|
/* digging onto a set object trap triggers it;
|
|
hero should have used #untrap first */
|
|
dotrap(ttmp, FORCETRAP);
|
|
/* restart completely from scratch if we resume digging */
|
|
(void) memset((genericptr_t) &svc.context.digging, 0,
|
|
sizeof svc.context.digging);
|
|
return 0;
|
|
} else if (ttmp && ttmp->ttyp == BEAR_TRAP && u.utrap) {
|
|
if (rnl(7) > (Fumbling ? 1 : 4)) {
|
|
char kbuf[BUFSZ];
|
|
int dmg = dmgval(uwep, &gy.youmonst) + dbon();
|
|
|
|
if (dmg < 1)
|
|
dmg = 1;
|
|
else if (uarmf)
|
|
dmg = (dmg + 1) / 2;
|
|
You("hit yourself in the %s.", body_part(FOOT));
|
|
Sprintf(kbuf, "chopping off %s own %s", uhis(),
|
|
body_part(FOOT));
|
|
losehp(Maybe_Half_Phys(dmg), kbuf, KILLED_BY);
|
|
} else {
|
|
You("destroy the bear trap with %s.",
|
|
yobjnam(uwep, (const char *) 0));
|
|
deltrap(ttmp);
|
|
reset_utrap(TRUE); /* release from trap, maybe Lev or Fly */
|
|
}
|
|
/* we haven't made any progress toward a pit yet */
|
|
svc.context.digging.effort = 0;
|
|
return 0;
|
|
} else if (ttmp && dcresult == DIGCHECK_PASSED_DESTROY_TRAP) {
|
|
const char *ttmpname = trapname(ttmp->ttyp, FALSE);
|
|
|
|
if (ispick)
|
|
You("destroy %s with %s.",
|
|
ttmp->tseen ? the(ttmpname) : an(ttmpname),
|
|
yobjnam(uwep, (const char *) 0));
|
|
deltrap(ttmp);
|
|
/* we haven't made any progress toward a pit yet */
|
|
svc.context.digging.effort = 0;
|
|
return 0;
|
|
}
|
|
|
|
if (IS_ALTAR(lev->typ)) {
|
|
altar_wrath(dpx, dpy);
|
|
angry_priest();
|
|
}
|
|
|
|
/* make pit at <u.ux,u.uy> */
|
|
if (dighole(TRUE, FALSE, (coord *) 0)) {
|
|
svc.context.digging.level.dnum = 0;
|
|
svc.context.digging.level.dlevel = -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (svc.context.digging.effort > 100) {
|
|
char digbuf[BUFSZ];
|
|
const char *digtxt, *dmgtxt = (const char *) 0;
|
|
struct obj *obj, *bobj;
|
|
boolean shopedge = *in_rooms(dpx, dpy, SHOPBASE);
|
|
int digtyp = dig_typ(uwep, dpx, dpy);
|
|
|
|
if (digtyp == DIGTYP_STATUE
|
|
&& (obj = sobj_at(STATUE, dpx, dpy)) != 0) {
|
|
if (break_statue(obj))
|
|
digtxt = "The statue shatters.";
|
|
else
|
|
/* it was a statue trap; break_statue()
|
|
printed a message and updated the screen */
|
|
digtxt = (char *) 0;
|
|
} else if (digtyp == DIGTYP_BOULDER
|
|
&& (obj = sobj_at(BOULDER, dpx, dpy)) != 0) {
|
|
fracture_rock(obj);
|
|
/*[3.7: this probably isn't necessary anymore]*/
|
|
if ((bobj = sobj_at(BOULDER, dpx, dpy)) != 0) {
|
|
/* another boulder here, restack it to the top */
|
|
obj_extract_self(bobj);
|
|
place_object(bobj, dpx, dpy);
|
|
}
|
|
digtxt = "The boulder falls apart.";
|
|
} else if (lev->typ == STONE || lev->typ == SCORR
|
|
|| IS_TREE(lev->typ)) {
|
|
if (Is_earthlevel(&u.uz)) {
|
|
if (uwep->blessed && !rn2(3)) {
|
|
mkcavearea(FALSE);
|
|
goto cleanup;
|
|
} else if ((uwep->cursed && !rn2(4))
|
|
|| (!uwep->blessed && !rn2(6))) {
|
|
mkcavearea(TRUE);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
if (digtyp == DIGTYP_TREE) {
|
|
digtxt = "You cut down the tree.";
|
|
lev->typ = ROOM, lev->flags = 0;
|
|
if (!rn2(5))
|
|
(void) rnd_treefruit_at(dpx, dpy);
|
|
if (Race_if(PM_ELF) || Role_if(PM_RANGER))
|
|
adjalign(-1);
|
|
} else {
|
|
digtxt = "You succeed in cutting away some rock.";
|
|
lev->typ = CORR, lev->flags = 0;
|
|
}
|
|
} else if (IS_WALL(lev->typ)) {
|
|
if (shopedge) {
|
|
add_damage(dpx, dpy, SHOP_WALL_DMG);
|
|
dmgtxt = "damage";
|
|
}
|
|
if (svl.level.flags.is_maze_lev) {
|
|
lev->typ = ROOM, lev->flags = 0;
|
|
} else if (svl.level.flags.is_cavernous_lev
|
|
&& !in_town(dpx, dpy)) {
|
|
lev->typ = CORR, lev->flags = 0;
|
|
} else {
|
|
lev->typ = DOOR, lev->doormask = D_NODOOR;
|
|
}
|
|
digtxt = "You make an opening in the wall.";
|
|
} else if (lev->typ == SDOOR) {
|
|
cvt_sdoor_to_door(lev); /* ->typ = DOOR */
|
|
digtxt = "You break through a secret door!";
|
|
if (!(lev->doormask & D_TRAPPED))
|
|
lev->doormask = D_BROKEN;
|
|
} else if (closed_door(dpx, dpy)) {
|
|
Sprintf(digbuf, "You break through the door with your %s.",
|
|
simpleonames(uwep));
|
|
digtxt = digbuf;
|
|
if (shopedge) {
|
|
add_damage(dpx, dpy, SHOP_DOOR_COST);
|
|
dmgtxt = "break";
|
|
}
|
|
if (!(lev->doormask & D_TRAPPED))
|
|
lev->doormask = D_BROKEN;
|
|
} else
|
|
return 0; /* statue or boulder got taken */
|
|
|
|
if (!does_block(dpx, dpy, &levl[dpx][dpy]))
|
|
unblock_point(dpx, dpy); /* vision: can see through */
|
|
feel_newsym(dpx, dpy);
|
|
if (digtxt && !svc.context.digging.quiet)
|
|
pline1(digtxt); /* after newsym */
|
|
if (dmgtxt)
|
|
pay_for_damage(dmgtxt, FALSE);
|
|
|
|
if (Is_earthlevel(&u.uz) && !rn2(3)) {
|
|
int mndx = rn2(2) ? PM_EARTH_ELEMENTAL : PM_XORN;
|
|
|
|
if (makemon(&mons[mndx], dpx, dpy, MM_NOMSG))
|
|
pline_The("debris from your digging comes to life!");
|
|
}
|
|
if (IS_DOOR(lev->typ) && (lev->doormask & D_TRAPPED)) {
|
|
lev->doormask = D_NODOOR;
|
|
b_trapped("door", NO_PART);
|
|
recalc_block_point(dpx, dpy);
|
|
newsym(dpx, dpy);
|
|
}
|
|
cleanup:
|
|
svc.context.digging.lastdigtime = svm.moves;
|
|
svc.context.digging.quiet = FALSE;
|
|
svc.context.digging.level.dnum = 0;
|
|
svc.context.digging.level.dlevel = -1;
|
|
return 0;
|
|
} else { /* not enough effort has been spent yet */
|
|
static const char *const d_target[6] = { "", "rock", "statue",
|
|
"boulder", "door", "tree" };
|
|
int dig_target = dig_typ(uwep, dpx, dpy);
|
|
|
|
if (IS_WALL(lev->typ) || dig_target == DIGTYP_DOOR) {
|
|
if (*in_rooms(dpx, dpy, SHOPBASE)) {
|
|
pline("This %s seems too hard to %s.",
|
|
IS_DOOR(lev->typ) ? "door" : "wall", verb);
|
|
return 0;
|
|
}
|
|
} else if (dig_target == DIGTYP_UNDIGGABLE
|
|
|| (dig_target == DIGTYP_ROCK && !IS_OBSTRUCTED(lev->typ)))
|
|
return 0; /* statue or boulder got taken */
|
|
|
|
if (!gd.did_dig_msg) {
|
|
You("hit the %s with all your might.", d_target[dig_target]);
|
|
wake_nearby(FALSE);
|
|
gd.did_dig_msg = TRUE;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
staticfn boolean
|
|
furniture_handled(coordxy x, coordxy y, boolean madeby_u)
|
|
{
|
|
struct rm *lev = &levl[x][y];
|
|
|
|
if (IS_FOUNTAIN(lev->typ)) {
|
|
dogushforth(FALSE);
|
|
SET_FOUNTAIN_WARNED(x, y); /* force dryup */
|
|
dryup(x, y, madeby_u);
|
|
} else if (IS_SINK(lev->typ)) {
|
|
breaksink(x, y);
|
|
} else if (lev->typ == DRAWBRIDGE_DOWN
|
|
|| (is_drawbridge_wall(x, y) >= 0)) {
|
|
coordxy bx = x, by = y;
|
|
|
|
/* if under the portcullis, the bridge is adjacent */
|
|
(void) find_drawbridge(&bx, &by);
|
|
destroy_drawbridge(bx, by);
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* When will hole be finished? Very rough indication used by shopkeeper. */
|
|
int
|
|
holetime(void)
|
|
{
|
|
if (go.occupation != dig || !*u.ushops)
|
|
return -1;
|
|
return ((250 - svc.context.digging.effort) / 20);
|
|
}
|
|
|
|
/* Return typ of liquid to fill a hole with, or ROOM, if no liquid nearby */
|
|
schar
|
|
fillholetyp(coordxy x, coordxy y,
|
|
boolean fill_if_any) /* force filling if it exists at all */
|
|
{
|
|
coordxy x1, y1;
|
|
coordxy lo_x = max(1, x - 1), hi_x = min(x + 1, COLNO - 1),
|
|
lo_y = max(0, y - 1), hi_y = min(y + 1, ROWNO - 1);
|
|
int pool_cnt = 0, moat_cnt = 0, lava_cnt = 0;
|
|
|
|
for (x1 = lo_x; x1 <= hi_x; x1++)
|
|
for (y1 = lo_y; y1 <= hi_y; y1++)
|
|
if (is_moat(x1, y1))
|
|
moat_cnt++;
|
|
else if (is_pool(x1, y1))
|
|
/* This must come after is_moat since moats are pools
|
|
* but not vice-versa. */
|
|
pool_cnt++;
|
|
else if (is_lava(x1, y1))
|
|
lava_cnt++;
|
|
|
|
if (!fill_if_any)
|
|
pool_cnt /= 3; /* not as much liquid as the others */
|
|
|
|
if ((lava_cnt > moat_cnt + pool_cnt && rn2(lava_cnt + 1))
|
|
|| (lava_cnt && fill_if_any))
|
|
return LAVAPOOL;
|
|
else if ((moat_cnt > 0 && rn2(moat_cnt + 1)) || (moat_cnt && fill_if_any))
|
|
return MOAT;
|
|
else if ((pool_cnt > 0 && rn2(pool_cnt + 1)) || (pool_cnt && fill_if_any))
|
|
return POOL;
|
|
else
|
|
return ROOM;
|
|
}
|
|
|
|
void
|
|
digactualhole(coordxy x, coordxy y, struct monst *madeby, int ttyp)
|
|
{
|
|
struct obj *oldobjs, *newobjs;
|
|
struct trap *ttmp;
|
|
const char *surface_type, *tname, *in_thru;
|
|
char furniture[BUFSZ];
|
|
struct rm *lev = &levl[x][y];
|
|
struct monst *mtmp = m_at(x, y); /* may be madeby */
|
|
boolean madeby_u = (madeby == BY_YOU), madeby_obj = (madeby == BY_OBJECT),
|
|
/* BY_OBJECT means the hero broke a wand, so blame her for it */
|
|
heros_fault = (madeby_u || madeby_obj);
|
|
boolean shopdoor, at_u = u_at(x, y), wont_fall = Levitation || Flying;
|
|
int old_typ, old_aligntyp = A_NONE;
|
|
|
|
if (at_u && u.utrap) {
|
|
if (u.utraptype == TT_BURIEDBALL)
|
|
buried_ball_to_punishment();
|
|
else if (u.utraptype == TT_INFLOOR)
|
|
reset_utrap(FALSE);
|
|
}
|
|
|
|
if (furniture_handled(x, y, madeby_u))
|
|
return;
|
|
|
|
if (ttyp != PIT && (!Can_dig_down(&u.uz) && !lev->candig)) {
|
|
impossible("digactualhole: can't dig %s on this level.",
|
|
trapname(ttyp, TRUE));
|
|
ttyp = PIT;
|
|
}
|
|
|
|
/* maketrap() might change terrain type but we deliver messages after
|
|
that, so prepare in advance */
|
|
old_typ = lev->typ;
|
|
furniture[0] = '\0';
|
|
if (IS_FURNITURE(lev->typ)) {
|
|
/* should mirror the word used by surface() for normal floor */
|
|
surface_type = (IS_ROOM(lev->typ) && !Is_earthlevel(&u.uz)
|
|
? "floor" : "ground");
|
|
if (IS_ALTAR(lev->typ)) {
|
|
old_aligntyp = Amask2align(levl[x][y].altarmask & AM_MASK);
|
|
Strcpy(furniture, align_str(old_aligntyp));
|
|
Strcat(furniture, " ");
|
|
}
|
|
Strcat(furniture, surface(x, y));
|
|
} else {
|
|
surface_type = surface(x, y);
|
|
}
|
|
shopdoor = IS_DOOR(lev->typ) && *in_rooms(x, y, SHOPBASE);
|
|
oldobjs = svl.level.objects[x][y];
|
|
|
|
ttmp = maketrap(x, y, ttyp);
|
|
if (!ttmp)
|
|
return;
|
|
newobjs = svl.level.objects[x][y];
|
|
ttmp->madeby_u = heros_fault;
|
|
ttmp->tseen = 0;
|
|
if (cansee(x, y))
|
|
seetrap(ttmp);
|
|
else if (madeby_u)
|
|
feeltrap(ttmp);
|
|
|
|
tname = trapname(ttyp, TRUE);
|
|
in_thru = (ttyp == HOLE ? "through" : "in");
|
|
if (madeby_u) {
|
|
if (x != u.ux || y != u.uy)
|
|
You("dig an adjacent %s.", tname);
|
|
else
|
|
You("dig %s %s the %s.", an(tname), in_thru, surface_type);
|
|
} else if (!madeby_obj && canseemon(madeby)) {
|
|
pline("%s digs %s %s the %s.", Monnam(madeby), an(tname), in_thru,
|
|
surface_type);
|
|
} else if (cansee(x, y) && flags.verbose) {
|
|
if (IS_STWALL(old_typ))
|
|
pline_The("%s crumbles into %s.", surface_type, an(tname));
|
|
else
|
|
pline("%s appears in the %s.", An(tname), surface_type);
|
|
}
|
|
if (IS_FURNITURE(old_typ) && cansee(x, y))
|
|
pline_The("%s falls into the %s!", furniture, tname);
|
|
/* wrath should immediately follow altar destruction message */
|
|
if (heros_fault && old_typ == ALTAR)
|
|
desecrate_altar(FALSE, old_aligntyp);
|
|
|
|
/* now deal with actual post-trap creation effects */
|
|
if (ttyp == PIT) {
|
|
if (shopdoor && heros_fault)
|
|
pay_for_damage("ruin", FALSE);
|
|
else
|
|
add_damage(x, y, heros_fault ? SHOP_PIT_COST : 0L);
|
|
if (madeby_u)
|
|
wake_nearby(FALSE);
|
|
/* in case we're digging down while encased in solid rock
|
|
which is blocking levitation or flight */
|
|
switch_terrain();
|
|
if (Levitation || Flying)
|
|
wont_fall = TRUE;
|
|
|
|
if (at_u) {
|
|
if (!wont_fall) {
|
|
set_utrap(rn1(4, 2), TT_PIT);
|
|
gv.vision_full_recalc = 1; /* vision limits change */
|
|
} else
|
|
reset_utrap(TRUE);
|
|
if (oldobjs != newobjs) /* something unearthed */
|
|
(void) pickup(1); /* detects pit */
|
|
} else if (mtmp) {
|
|
if (is_flyer(mtmp->data) || is_floater(mtmp->data)) {
|
|
if (canseemon(mtmp))
|
|
pline("%s %s over the pit.", Monnam(mtmp),
|
|
(is_flyer(mtmp->data)) ? "flies" : "floats");
|
|
} else if (mtmp != madeby)
|
|
(void) mintrap(mtmp, NO_TRAP_FLAGS);
|
|
}
|
|
} else { /* was TRAPDOOR now a HOLE*/
|
|
if (at_u) {
|
|
/* in case we're digging down while encased in solid rock
|
|
which is blocking levitation or flight */
|
|
switch_terrain();
|
|
if (Levitation || Flying)
|
|
wont_fall = TRUE;
|
|
|
|
/* check for leashed pet that can't fall right now */
|
|
if (!u.ustuck && !wont_fall && !next_to_u()) {
|
|
You("are jerked back by your pet!");
|
|
wont_fall = TRUE;
|
|
}
|
|
|
|
/* Floor objects get a chance of falling down. The case where
|
|
* the hero does NOT fall down is treated here. The case
|
|
* where the hero does fall down is treated in goto_level().
|
|
*/
|
|
if (u.ustuck || wont_fall) {
|
|
if (newobjs)
|
|
impact_drop((struct obj *) 0, x, y, 0);
|
|
if (oldobjs != newobjs)
|
|
(void) pickup(1);
|
|
if (shopdoor && heros_fault)
|
|
pay_for_damage("ruin", FALSE);
|
|
} else {
|
|
d_level newlevel;
|
|
|
|
if (*u.ushops && heros_fault)
|
|
shopdig(1); /* shk might snatch pack */
|
|
else /* handle any earlier hero-caused damage */
|
|
pay_for_damage("dig into", TRUE);
|
|
You("fall through...");
|
|
/* Earlier checks must ensure that the destination
|
|
* level exists and is in the present dungeon.
|
|
*/
|
|
newlevel.dnum = u.uz.dnum;
|
|
newlevel.dlevel = u.uz.dlevel + 1;
|
|
goto_level(&newlevel, FALSE, TRUE, FALSE);
|
|
/* messages for arriving in special rooms */
|
|
spoteffects(FALSE);
|
|
}
|
|
} else {
|
|
if (shopdoor && heros_fault)
|
|
pay_for_damage("ruin", FALSE);
|
|
if (newobjs)
|
|
impact_drop((struct obj *) 0, x, y, 0);
|
|
if (mtmp) {
|
|
/*[don't we need special sokoban handling here?]*/
|
|
if (!grounded(mtmp->data)
|
|
|| (mtmp->wormno && count_wsegs(mtmp) > 5)
|
|
|| mtmp->data->msize >= MZ_HUGE)
|
|
return;
|
|
if (mtmp == u.ustuck) /* probably a vortex */
|
|
return; /* temporary? kludge */
|
|
|
|
if (teleport_pet(mtmp, FALSE)) {
|
|
d_level tolevel;
|
|
|
|
if (Is_stronghold(&u.uz)) {
|
|
assign_level(&tolevel, &valley_level);
|
|
} else if (Is_botlevel(&u.uz)) {
|
|
if (canseemon(mtmp))
|
|
pline("%s avoids the trap.", Monnam(mtmp));
|
|
return;
|
|
} else {
|
|
get_level(&tolevel, depth(&u.uz) + 1);
|
|
}
|
|
if (mtmp->isshk)
|
|
make_angry_shk(mtmp, 0, 0);
|
|
migrate_to_level(mtmp, ledger_no(&tolevel),
|
|
MIGR_RANDOM, (coord *) 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DISABLE_WARNING_FORMAT_NONLITERAL
|
|
|
|
/*
|
|
* Called from dighole(); also from do_break_wand() in apply.c
|
|
* and do_earthquake() in music.c.
|
|
*/
|
|
void
|
|
liquid_flow(
|
|
coordxy x, coordxy y,
|
|
schar typ,
|
|
struct trap *ttmp,
|
|
const char *fillmsg)
|
|
{
|
|
struct obj *objchain;
|
|
struct monst *mon;
|
|
boolean u_spot = u_at(x, y);
|
|
|
|
/* caller should have changed levl[x][y].typ to POOL, MOAT, or LAVA */
|
|
if (!is_pool_or_lava(x, y)) {
|
|
if (iflags.sanity_check) {
|
|
impossible("Insane liquid_flow(%d,%d,%s,%s).", x, y,
|
|
ttmp ? trapname(ttmp->ttyp, TRUE) : "no trap",
|
|
fillmsg ? fillmsg : "no mesg");
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (ttmp)
|
|
(void) delfloortrap(ttmp); /* will untrap monster if one is here */
|
|
/* if any objects were frozen here, they're released now */
|
|
obj_ice_effects(x, y, TRUE);
|
|
unearth_objs(x, y);
|
|
|
|
if (fillmsg)
|
|
pline(fillmsg, hliquid(typ == LAVAPOOL ? "lava" : "water"));
|
|
/* handle object damage before hero damage; affects potential bones */
|
|
if ((objchain = svl.level.objects[x][y]) != 0) {
|
|
if (typ == LAVAPOOL)
|
|
fire_damage_chain(objchain, TRUE, TRUE, x, y);
|
|
else
|
|
water_damage_chain(objchain, TRUE);
|
|
}
|
|
/* damage to the hero */
|
|
if (u_spot) {
|
|
(void) pooleffects(FALSE);
|
|
} else if ((mon = m_at(x, y)) != 0) {
|
|
(void) minliquid(mon);
|
|
}
|
|
}
|
|
|
|
RESTORE_WARNING_FORMAT_NONLITERAL
|
|
|
|
/* return TRUE if digging succeeded, FALSE otherwise */
|
|
boolean
|
|
dighole(boolean pit_only, boolean by_magic, coord *cc)
|
|
{
|
|
struct trap *ttmp;
|
|
struct rm *lev;
|
|
struct obj *boulder_here;
|
|
schar typ, old_typ;
|
|
coordxy dig_x, dig_y;
|
|
boolean nohole, retval = FALSE;
|
|
enum digcheck_result dig_check_result;
|
|
|
|
if (!cc) {
|
|
dig_x = u.ux;
|
|
dig_y = u.uy;
|
|
} else {
|
|
dig_x = cc->x;
|
|
dig_y = cc->y;
|
|
if (!isok(dig_x, dig_y))
|
|
return FALSE;
|
|
}
|
|
|
|
ttmp = t_at(dig_x, dig_y);
|
|
lev = &levl[dig_x][dig_y];
|
|
dig_check_result = dig_check(BY_YOU, dig_x, dig_y);
|
|
/* nohole = (!Can_dig_down(&u.uz) && !lev->candig); */
|
|
nohole = (dig_check_result == DIGCHECK_FAIL_CANTDIG
|
|
|| dig_check_result == DIGCHECK_FAIL_TOOHARD);
|
|
old_typ = lev->typ;
|
|
|
|
if ((ttmp && (undestroyable_trap(ttmp->ttyp) || nohole))
|
|
|| (IS_OBSTRUCTED(old_typ) && old_typ != SDOOR
|
|
&& (lev->wall_info & W_NONDIGGABLE) != 0)) {
|
|
pline_The("%s %shere is too hard to dig in.", surface(dig_x, dig_y),
|
|
(dig_x != u.ux || dig_y != u.uy) ? "t" : "");
|
|
} else if (ttmp && is_magical_trap(ttmp->ttyp)) {
|
|
explode(dig_x, dig_y, 0, 20 + d(3, 6), TRAP_EXPLODE, EXPL_MAGICAL);
|
|
deltrap(ttmp);
|
|
newsym(dig_x, dig_y);
|
|
} else if (is_pool_or_lava(dig_x, dig_y)) {
|
|
pline_The("%s sloshes furiously for a moment, then subsides.",
|
|
hliquid(is_lava(dig_x, dig_y) ? "lava" : "water"));
|
|
wake_nearby(FALSE); /* splashing */
|
|
|
|
} else if (old_typ == DRAWBRIDGE_DOWN
|
|
|| (is_drawbridge_wall(dig_x, dig_y) >= 0)) {
|
|
/* drawbridge_down is the platform crossing the moat when the
|
|
bridge is extended; drawbridge_wall is the open "doorway" or
|
|
closed "door" where the portcullis/mechanism is located */
|
|
if (pit_only) {
|
|
pline_The("drawbridge seems too hard to dig through.");
|
|
} else {
|
|
coordxy x = dig_x, y = dig_y;
|
|
/* if under the portcullis, the bridge is adjacent */
|
|
(void) find_drawbridge(&x, &y);
|
|
destroy_drawbridge(x, y);
|
|
retval = TRUE;
|
|
}
|
|
|
|
} else if ((boulder_here = sobj_at(BOULDER, dig_x, dig_y)) != 0) {
|
|
if (ttmp && is_pit(ttmp->ttyp)
|
|
&& rn2(2)) {
|
|
pline_The("boulder settles into the %spit.",
|
|
(dig_x != u.ux || dig_y != u.uy) ? "adjacent " : "");
|
|
ttmp->ttyp = PIT; /* crush spikes */
|
|
} else {
|
|
/*
|
|
* digging makes a hole, but the boulder immediately
|
|
* fills it. Final outcome: no hole, no boulder.
|
|
*/
|
|
Soundeffect(se_kadoom_boulder_falls_in, 60);
|
|
pline("KADOOM! The boulder falls in!");
|
|
wake_nearby(FALSE);
|
|
(void) delfloortrap(ttmp);
|
|
}
|
|
delobj(boulder_here);
|
|
} else if (IS_GRAVE(old_typ)) {
|
|
digactualhole(dig_x, dig_y, BY_YOU, PIT);
|
|
dig_up_grave(cc);
|
|
retval = TRUE;
|
|
} else if (old_typ == DRAWBRIDGE_UP) {
|
|
/* must be floor or ice, other cases handled above */
|
|
/* dig "pit" and let fluid flow in (if possible) */
|
|
typ = fillholetyp(dig_x, dig_y, FALSE);
|
|
|
|
if (typ == ROOM) {
|
|
/*
|
|
* We can't dig a hole here since that will destroy
|
|
* the drawbridge. The following is a cop-out. --dlc
|
|
*/
|
|
pline_The("%s %shere is too hard to dig in.",
|
|
surface(dig_x, dig_y),
|
|
(dig_x != u.ux || dig_y != u.uy) ? "t" : "");
|
|
} else {
|
|
lev->drawbridgemask &= ~DB_UNDER;
|
|
lev->drawbridgemask |= (typ == LAVAPOOL) ? DB_LAVA : DB_MOAT;
|
|
liquid_flow(dig_x, dig_y, typ, ttmp,
|
|
"As you dig, the hole fills with %s!");
|
|
retval = TRUE;
|
|
}
|
|
|
|
/* the following two are here for the wand of digging */
|
|
} else if (IS_THRONE(old_typ)) {
|
|
pline_The("throne is too hard to break apart.");
|
|
|
|
} else if (IS_ALTAR(old_typ)) {
|
|
pline_The("altar is too hard to break apart.");
|
|
|
|
} else {
|
|
typ = fillholetyp(dig_x, dig_y, FALSE);
|
|
|
|
lev->flags = 0;
|
|
if (typ != ROOM) {
|
|
if (!furniture_handled((int) dig_x, (int) dig_y, TRUE)) {
|
|
lev->typ = typ;
|
|
liquid_flow(dig_x, dig_y, typ, ttmp,
|
|
"As you dig, the hole fills with %s!");
|
|
}
|
|
retval = TRUE;
|
|
} else {
|
|
/* magical digging disarms settable traps */
|
|
if (by_magic && ttmp
|
|
&& (ttmp->ttyp == LANDMINE || ttmp->ttyp == BEAR_TRAP)) {
|
|
int otyp = (ttmp->ttyp == LANDMINE) ? LAND_MINE : BEARTRAP;
|
|
|
|
/* convert trap into buried object (deletes trap) */
|
|
cnv_trap_obj(otyp, 1, ttmp, TRUE);
|
|
}
|
|
|
|
/* finally we get to make a hole */
|
|
if (nohole || pit_only
|
|
|| dig_check_result == DIGCHECK_PASSED_DESTROY_TRAP
|
|
|| dig_check_result == DIGCHECK_PASSED_PITONLY)
|
|
digactualhole(dig_x, dig_y, BY_YOU, PIT);
|
|
else
|
|
digactualhole(dig_x, dig_y, BY_YOU, HOLE);
|
|
retval = TRUE;
|
|
}
|
|
}
|
|
spot_checks(dig_x, dig_y, old_typ);
|
|
return retval;
|
|
}
|
|
|
|
staticfn void
|
|
dig_up_grave(coord *cc)
|
|
{
|
|
struct obj *otmp;
|
|
int what_happens;
|
|
coordxy dig_x, dig_y;
|
|
|
|
if (!cc) {
|
|
dig_x = u.ux;
|
|
dig_y = u.uy;
|
|
} else {
|
|
dig_x = cc->x;
|
|
dig_y = cc->y;
|
|
if (!isok(dig_x, dig_y))
|
|
return;
|
|
}
|
|
|
|
/* Grave-robbing is frowned upon... */
|
|
exercise(A_WIS, FALSE);
|
|
if (Role_if(PM_ARCHEOLOGIST)) {
|
|
adjalign(-sgn(u.ualign.type) * 3);
|
|
You_feel("like a despicable grave-robber!");
|
|
} else if (Role_if(PM_SAMURAI)) {
|
|
adjalign(-sgn(u.ualign.type));
|
|
You("disturb the honorable dead!");
|
|
} else if (u.ualign.type == A_LAWFUL) {
|
|
if (u.ualign.record > -10)
|
|
adjalign(-1);
|
|
You("have violated the sanctity of this grave!");
|
|
}
|
|
|
|
/* -1: force default case for empty grave */
|
|
what_happens = levl[dig_x][dig_y].emptygrave ? -1 : rn2(5);
|
|
switch (what_happens) {
|
|
case 0:
|
|
case 1:
|
|
You("unearth a corpse.");
|
|
if ((otmp = mk_tt_object(CORPSE, dig_x, dig_y)) != 0)
|
|
otmp->age -= (TAINT_AGE + 1); /* this is an *OLD* corpse */
|
|
break;
|
|
case 2:
|
|
if (!Blind)
|
|
pline("%s!", Hallucination ? "Dude! The living dead"
|
|
: "The grave's owner is very upset");
|
|
(void) makemon(mkclass(S_ZOMBIE, 0), dig_x, dig_y, MM_NOMSG);
|
|
break;
|
|
case 3:
|
|
if (!Blind)
|
|
pline("%s!", Hallucination ? "I want my mummy"
|
|
: "You've disturbed a tomb");
|
|
(void) makemon(mkclass(S_MUMMY, 0), dig_x, dig_y, MM_NOMSG);
|
|
break;
|
|
default:
|
|
/* No corpse */
|
|
pline_The("grave is unoccupied. Strange...");
|
|
break;
|
|
}
|
|
levl[dig_x][dig_y].typ = ROOM;
|
|
levl[dig_x][dig_y].emptygrave = 0; /* clear 'flags' */
|
|
levl[dig_x][dig_y].disturbed = 0; /* clear 'horizontal' */
|
|
del_engr_at(dig_x, dig_y);
|
|
newsym(dig_x, dig_y);
|
|
return;
|
|
}
|
|
|
|
int
|
|
use_pick_axe(struct obj *obj)
|
|
{
|
|
const char *verb;
|
|
char *dsp, dirsyms[12], qbuf[BUFSZ];
|
|
boolean ispick;
|
|
int rx, ry, downok, res = ECMD_OK;
|
|
int dir;
|
|
|
|
/* Check tool */
|
|
if (obj != uwep) {
|
|
if (wield_tool(obj, "swing")) {
|
|
/* we're now wielding it. next turn, apply to dig. */
|
|
cmdq_add_ec(CQ_CANNED, doapply);
|
|
cmdq_add_key(CQ_CANNED, obj->invlet);
|
|
return ECMD_TIME;
|
|
}
|
|
return ECMD_OK;
|
|
}
|
|
ispick = is_pick(obj);
|
|
verb = ispick ? "dig" : "chop";
|
|
|
|
if (u.utrap && u.utraptype == TT_WEB) {
|
|
pline("%s you can't %s while entangled in a web.",
|
|
/* res==0 => no prior message;
|
|
res==1 => just got "You now wield a pick-axe." message */
|
|
!res ? "Unfortunately," : "But", verb);
|
|
return res;
|
|
}
|
|
|
|
/* construct list of directions to show player for likely choices */
|
|
downok = !!can_reach_floor(FALSE);
|
|
dsp = dirsyms;
|
|
for (dir = 0; dir < N_DIRS_Z; dir++) {
|
|
char dirch = cmd_from_dir(dir, MV_WALK);
|
|
|
|
/* filter out useless directions */
|
|
if (u.uswallow) {
|
|
; /* all directions are viable when swallowed */
|
|
} else if (movecmd(dirch, MV_WALK)) {
|
|
/* normal direction, within plane of the level map */
|
|
if (!dxdy_moveok())
|
|
continue; /* handle NODIAG */
|
|
rx = u.ux + u.dx;
|
|
ry = u.uy + u.dy;
|
|
if (!isok(rx, ry) || dig_typ(obj, rx, ry) == DIGTYP_UNDIGGABLE)
|
|
continue;
|
|
} else {
|
|
/* up or down; we used to always include down, so that
|
|
there would always be at least one choice shown, but
|
|
it shouldn't be a likely candidate when floating high
|
|
above the floor; include up instead in that situation
|
|
(as a silly candidate rather than a likely one...) */
|
|
if ((u.dz > 0) ^ downok)
|
|
continue;
|
|
}
|
|
/* include this direction */
|
|
*dsp++ = dirch;
|
|
}
|
|
*dsp = 0;
|
|
Sprintf(qbuf, "In what direction do you want to %s? [%s]", verb, dirsyms);
|
|
if (!getdir(qbuf))
|
|
return (res|ECMD_CANCEL);
|
|
|
|
return use_pick_axe2(obj);
|
|
}
|
|
|
|
/* MRKR: use_pick_axe() is split in two to allow autodig to bypass */
|
|
/* the "In what direction do you want to dig?" query. */
|
|
/* use_pick_axe2() uses the existing u.dx, u.dy and u.dz */
|
|
int
|
|
use_pick_axe2(struct obj *obj)
|
|
{
|
|
coordxy rx, ry;
|
|
struct rm *lev;
|
|
struct trap *trap, *trap_with_u;
|
|
int dig_target;
|
|
boolean ispick = is_pick(obj);
|
|
const char *verbing = ispick ? "digging" : "chopping";
|
|
|
|
if (u.uswallow && do_attack(u.ustuck)) {
|
|
; /* return 1 */
|
|
} else if (Underwater) {
|
|
pline("Turbulence torpedoes your %s attempts.", verbing);
|
|
} else if (u.dz < 0) {
|
|
if (Levitation)
|
|
You("don't have enough leverage.");
|
|
else
|
|
You_cant("reach the %s.", ceiling(u.ux, u.uy));
|
|
} else if (!u.dx && !u.dy && !u.dz) {
|
|
char buf[BUFSZ];
|
|
int dam;
|
|
|
|
dam = rnd(2) + dbon() + obj->spe;
|
|
if (dam <= 0)
|
|
dam = 1;
|
|
You("hit yourself with %s.", yname(uwep));
|
|
Sprintf(buf, "%s own %s", uhis(), OBJ_NAME(objects[obj->otyp]));
|
|
losehp(Maybe_Half_Phys(dam), buf, KILLED_BY);
|
|
disp.botl = TRUE;
|
|
return ECMD_TIME;
|
|
} else if (u.dz == 0) {
|
|
confdir(FALSE);
|
|
rx = u.ux + u.dx;
|
|
ry = u.uy + u.dy;
|
|
if (!isok(rx, ry)) {
|
|
Soundeffect(se_clash, 40);
|
|
pline("Clash!");
|
|
return ECMD_TIME;
|
|
}
|
|
lev = &levl[rx][ry];
|
|
if (MON_AT(rx, ry) && do_attack(m_at(rx, ry)))
|
|
return ECMD_TIME;
|
|
dig_target = dig_typ(obj, rx, ry);
|
|
if (dig_target == DIGTYP_UNDIGGABLE) {
|
|
struct obj *boulder;
|
|
|
|
/* ACCESSIBLE or POOL */
|
|
trap = t_at(rx, ry);
|
|
if (trap && trap->ttyp == WEB) {
|
|
if (!trap->tseen) {
|
|
seetrap(trap);
|
|
There("is a spider web there!");
|
|
}
|
|
pline("%s entangled in the web.", Yobjnam2(obj, "become"));
|
|
/* you ought to be able to let go; tough luck */
|
|
/* (maybe `move_into_trap()' would be better) */
|
|
nomul(-d(2, 2));
|
|
gm.multi_reason = "stuck in a spider web";
|
|
gn.nomovemsg = "You pull free.";
|
|
} else if (lev->typ == IRONBARS) {
|
|
pline("Clang!");
|
|
wake_nearby(FALSE);
|
|
} else if (IS_WATERWALL(lev->typ)) {
|
|
pline("Splash!");
|
|
} else if (lev->typ == LAVAWALL) {
|
|
pline("Splash!");
|
|
(void) fire_damage(uwep, FALSE, rx, ry);
|
|
} else if (IS_TREE(lev->typ)) {
|
|
You("need an axe to cut down a tree.");
|
|
} else if (IS_OBSTRUCTED(lev->typ)) {
|
|
You("need a pick to dig rock.");
|
|
} else if ((boulder = sobj_at(BOULDER, rx, ry)) != 0
|
|
|| sobj_at(STATUE, rx, ry)) {
|
|
/* if both boulders and statues are present, the topmost
|
|
boulder will be shown on the map so treat it as target */
|
|
const char *what = boulder ? "boulder" : "statue";
|
|
|
|
if (!ispick) {
|
|
boolean vibrate = !rn2(3);
|
|
|
|
pline("Sparks fly as you whack the %s.%s", what,
|
|
vibrate ? " The axe-handle vibrates violently!"
|
|
: "");
|
|
if (vibrate)
|
|
losehp(Maybe_Half_Phys(2), "axing a hard object",
|
|
KILLED_BY);
|
|
wake_nearby(FALSE);
|
|
} else {
|
|
/* using a pick but dig_target is DIGTYPE_UNDIGGABLE
|
|
and there is at least one boulder or statue or both
|
|
present; pick_can_reach() returned false */
|
|
You_cant("reach the %s.", what);
|
|
}
|
|
} else if (u.utrap && u.utraptype == TT_PIT && trap
|
|
&& (trap_with_u = t_at(u.ux, u.uy))
|
|
&& is_pit(trap->ttyp)
|
|
&& !conjoined_pits(trap, trap_with_u, FALSE)) {
|
|
int idx = xytod(u.dx, u.dy);
|
|
|
|
if (idx != DIR_ERR) {
|
|
int adjidx = DIR_180(idx);
|
|
|
|
trap_with_u->conjoined |= (1 << idx);
|
|
trap->conjoined |= (1 << adjidx);
|
|
You("clear some debris from between the pits.");
|
|
}
|
|
} else if (u.utrap && u.utraptype == TT_PIT
|
|
&& (trap_with_u = t_at(u.ux, u.uy)) != 0) {
|
|
You("swing %s, but the rubble has no place to go.",
|
|
yobjnam(obj, (char *) 0));
|
|
} else {
|
|
You("swing %s through thin air.", yobjnam(obj, (char *) 0));
|
|
}
|
|
} else {
|
|
static const char *const d_action[6] = { "swinging", "digging",
|
|
"chipping the statue",
|
|
"hitting the boulder",
|
|
"chopping at the door",
|
|
"cutting the tree" };
|
|
|
|
gd.did_dig_msg = FALSE;
|
|
svc.context.digging.quiet = FALSE;
|
|
if (svc.context.digging.pos.x != rx
|
|
|| svc.context.digging.pos.y != ry
|
|
|| !on_level(&svc.context.digging.level, &u.uz)
|
|
|| svc.context.digging.down) {
|
|
if (flags.autodig && dig_target == DIGTYP_ROCK
|
|
&& !svc.context.digging.down
|
|
&& u_at(svc.context.digging.pos.x,
|
|
svc.context.digging.pos.y)
|
|
&& (svm.moves <= svc.context.digging.lastdigtime + 2
|
|
&& svm.moves >= svc.context.digging.lastdigtime)) {
|
|
/* avoid messages if repeated autodigging */
|
|
gd.did_dig_msg = TRUE;
|
|
svc.context.digging.quiet = TRUE;
|
|
}
|
|
svc.context.digging.down = svc.context.digging.chew = FALSE;
|
|
svc.context.digging.warned = FALSE;
|
|
svc.context.digging.pos.x = rx;
|
|
svc.context.digging.pos.y = ry;
|
|
assign_level(&svc.context.digging.level, &u.uz);
|
|
svc.context.digging.effort = 0;
|
|
if (!svc.context.digging.quiet)
|
|
You("start %s.", d_action[dig_target]);
|
|
} else {
|
|
You("%s %s.", svc.context.digging.chew ? "begin" : "continue",
|
|
d_action[dig_target]);
|
|
svc.context.digging.chew = FALSE;
|
|
}
|
|
set_occupation(dig, verbing, 0);
|
|
}
|
|
} else if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz)) {
|
|
/* it must be air -- water checked above */
|
|
You("swing %s through thin air.", yobjnam(obj, (char *) 0));
|
|
} else if (!can_reach_floor(FALSE)) {
|
|
cant_reach_floor(u.ux, u.uy, FALSE, FALSE, FALSE);
|
|
} else if (is_pool_or_lava(u.ux, u.uy)) {
|
|
/* Monsters which swim also happen not to be able to dig */
|
|
You("cannot stay under%s long enough.",
|
|
is_pool(u.ux, u.uy) ? "water" : " the lava");
|
|
} else if ((trap = t_at(u.ux, u.uy)) != 0
|
|
&& (uteetering_at_seen_pit(trap) || uescaped_shaft(trap))) {
|
|
dotrap(trap, FORCEBUNGLE);
|
|
/* might escape trap and still be teetering at brink */
|
|
if (!u.utrap)
|
|
cant_reach_floor(u.ux, u.uy, FALSE, TRUE, FALSE);
|
|
} else if (!ispick
|
|
/* can only dig down with an axe when doing so will
|
|
trigger or disarm a trap here */
|
|
&& (!trap || (trap->ttyp != LANDMINE
|
|
&& trap->ttyp != BEAR_TRAP))) {
|
|
pline("%s merely scratches the %s.", Yobjnam2(obj, (char *) 0),
|
|
surface(u.ux, u.uy));
|
|
u_wipe_engr(3);
|
|
} else {
|
|
if (svc.context.digging.pos.x != u.ux
|
|
|| svc.context.digging.pos.y != u.uy
|
|
|| !on_level(&svc.context.digging.level, &u.uz)
|
|
|| !svc.context.digging.down) {
|
|
svc.context.digging.chew = FALSE;
|
|
svc.context.digging.down = TRUE;
|
|
svc.context.digging.warned = FALSE;
|
|
svc.context.digging.pos.x = u.ux;
|
|
svc.context.digging.pos.y = u.uy;
|
|
assign_level(&svc.context.digging.level, &u.uz);
|
|
svc.context.digging.effort = 0;
|
|
You("start %s downward.", verbing);
|
|
if (*u.ushops) {
|
|
shopdig(0);
|
|
add_damage(u.ux, u.uy, SHOP_PIT_COST);
|
|
}
|
|
} else
|
|
You("continue %s downward.", verbing);
|
|
gd.did_dig_msg = FALSE;
|
|
set_occupation(dig, verbing, 0);
|
|
}
|
|
return ECMD_TIME;
|
|
}
|
|
|
|
staticfn boolean
|
|
watchman_canseeu(struct monst *mtmp)
|
|
{
|
|
if (is_watch(mtmp->data) && mtmp->mcansee && m_canseeu(mtmp)
|
|
&& mtmp->mpeaceful)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Town Watchmen frown on damage to the town walls, trees or fountains.
|
|
* It's OK to dig holes in the ground, however.
|
|
* If mtmp is assumed to be a watchman, a watchman is found if mtmp == 0
|
|
* zap == TRUE if wand/spell of digging, FALSE otherwise (chewing)
|
|
*/
|
|
void
|
|
watch_dig(struct monst *mtmp, coordxy x, coordxy y, boolean zap)
|
|
{
|
|
struct rm *lev = &levl[x][y];
|
|
|
|
if (in_town(x, y)
|
|
&& (closed_door(x, y) || lev->typ == SDOOR || IS_WALL(lev->typ)
|
|
|| IS_FOUNTAIN(lev->typ) || IS_TREE(lev->typ))) {
|
|
if (!mtmp)
|
|
mtmp = get_iter_mons(watchman_canseeu);
|
|
|
|
if (mtmp) {
|
|
SetVoice(mtmp, 0, 80, 0);
|
|
if (zap || svc.context.digging.warned) {
|
|
verbalize("Halt, vandal! You're under arrest!");
|
|
(void) angry_guards(!!Deaf);
|
|
} else {
|
|
const char *str;
|
|
|
|
if (IS_DOOR(lev->typ))
|
|
str = "door";
|
|
else if (IS_TREE(lev->typ))
|
|
str = "tree";
|
|
else if (IS_OBSTRUCTED(lev->typ))
|
|
str = "wall";
|
|
else
|
|
str = "fountain";
|
|
verbalize("Hey, stop damaging that %s!", str);
|
|
svc.context.digging.warned = TRUE;
|
|
}
|
|
if (is_digging())
|
|
stop_occupation();
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Return TRUE if monster died, FALSE otherwise. Called from m_move(). */
|
|
boolean
|
|
mdig_tunnel(struct monst *mtmp)
|
|
{
|
|
struct rm *here;
|
|
boolean sawit, seeit, trapped;
|
|
int pile = rnd(12);
|
|
|
|
here = &levl[mtmp->mx][mtmp->my];
|
|
if (here->typ == SDOOR)
|
|
cvt_sdoor_to_door(here); /* ->typ = DOOR */
|
|
|
|
/* Eats away door if present & closed or locked */
|
|
if (closed_door(mtmp->mx, mtmp->my)) {
|
|
if (*in_rooms(mtmp->mx, mtmp->my, SHOPBASE))
|
|
add_damage(mtmp->mx, mtmp->my, 0L);
|
|
/* sawit: closed door location is more visible than an open one */
|
|
sawit = canseemon(mtmp); /* before door state change and unblock_pt */
|
|
trapped = (here->doormask & D_TRAPPED) ? TRUE : FALSE;
|
|
here->doormask = trapped ? D_NODOOR : D_BROKEN;
|
|
recalc_block_point(mtmp->mx, mtmp->my); /* vision */
|
|
newsym(mtmp->mx, mtmp->my);
|
|
if (trapped) {
|
|
seeit = canseemon(mtmp);
|
|
if (mb_trapped(mtmp, sawit || seeit)) { /* mtmp is killed */
|
|
newsym(mtmp->mx, mtmp->my);
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
if (flags.verbose) {
|
|
if (!Unaware && !rn2(3)) /* not too often.. */
|
|
draft_message(TRUE); /* "You feel an unexpected draft." */
|
|
}
|
|
}
|
|
return FALSE;
|
|
} else if (here->typ == SCORR) {
|
|
here->typ = CORR, here->flags = 0;
|
|
unblock_point(mtmp->mx, mtmp->my);
|
|
newsym(mtmp->mx, mtmp->my);
|
|
draft_message(FALSE); /* "You feel a draft." */
|
|
return FALSE;
|
|
} else if (!IS_OBSTRUCTED(here->typ) && !IS_TREE(here->typ)) { /* no dig */
|
|
return FALSE;
|
|
}
|
|
|
|
/* Only rock, trees, and walls fall through to this point. */
|
|
if ((here->wall_info & W_NONDIGGABLE) != 0) {
|
|
impossible("mdig_tunnel: %s at (%d,%d) is undiggable",
|
|
(IS_WALL(here->typ) ? "wall"
|
|
: IS_TREE(here->typ) ? "tree" : "stone"),
|
|
(int) mtmp->mx, (int) mtmp->my);
|
|
return FALSE; /* still alive */
|
|
}
|
|
|
|
if (IS_WALL(here->typ)) {
|
|
/* KMH -- Okay on arboreal levels (room walls are still stone) */
|
|
if (flags.verbose && !rn2(5)) {
|
|
Soundeffect(se_crashing_rock, 75);
|
|
You_hear("crashing rock.");
|
|
}
|
|
if (*in_rooms(mtmp->mx, mtmp->my, SHOPBASE))
|
|
add_damage(mtmp->mx, mtmp->my, 0L);
|
|
if (svl.level.flags.is_maze_lev) {
|
|
here->typ = ROOM, here->flags = 0;
|
|
} else if (svl.level.flags.is_cavernous_lev
|
|
&& !in_town(mtmp->mx, mtmp->my)) {
|
|
here->typ = CORR, here->flags = 0;
|
|
} else {
|
|
here->typ = DOOR, here->doormask = D_NODOOR;
|
|
}
|
|
} else if (IS_TREE(here->typ)) {
|
|
here->typ = ROOM, here->flags = 0;
|
|
if (pile && pile < 5)
|
|
(void) rnd_treefruit_at(mtmp->mx, mtmp->my);
|
|
} else {
|
|
here->typ = CORR, here->flags = 0;
|
|
if (pile && pile < 5)
|
|
(void) mksobj_at((pile == 1) ? BOULDER : ROCK, mtmp->mx, mtmp->my,
|
|
TRUE, FALSE);
|
|
}
|
|
newsym(mtmp->mx, mtmp->my);
|
|
if (!sobj_at(BOULDER, mtmp->mx, mtmp->my))
|
|
unblock_point(mtmp->mx, mtmp->my); /* vision */
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#define STRIDENT 4 /* from pray.c */
|
|
|
|
/* draft refers to air currents, but can be a pun on "draft" as conscription
|
|
for military service (probably not a good pun if it has to be explained) */
|
|
void
|
|
draft_message(boolean unexpected)
|
|
{
|
|
/*
|
|
* [Bug or TODO? Have caller pass coordinates and use the travel
|
|
* mechanism to determine whether there is a path between
|
|
* destroyed door (or exposed secret corridor) and hero's location.
|
|
* When there is no such path, no draft should be felt.]
|
|
*/
|
|
|
|
if (unexpected) {
|
|
if (!Hallucination)
|
|
You_feel("an unexpected draft.");
|
|
else
|
|
/* U.S. classification system uses 1-A for eligible to serve
|
|
and 4-F for ineligible due to physical or mental defect;
|
|
some intermediate values exist but are rarely seen */
|
|
You_feel("like you are %s.",
|
|
(ACURR(A_STR) < 6 || ACURR(A_DEX) < 6
|
|
|| ACURR(A_CON) < 6 || ACURR(A_CHA) < 6
|
|
|| ACURR(A_INT) < 6 || ACURR(A_WIS) < 6) ? "4-F"
|
|
: "1-A");
|
|
} else {
|
|
if (!Hallucination) {
|
|
You_feel("a draft.");
|
|
} else {
|
|
/* "marching" is deliberately ambiguous; it might mean drills
|
|
after entering military service or mean engaging in protests */
|
|
static const char *const draft_reaction[] = {
|
|
"enlisting", "marching", "protesting", "fleeing",
|
|
};
|
|
int dridx;
|
|
|
|
/* Lawful: 0..1, Neutral: 1..2, Chaotic: 2..3 */
|
|
dridx = rn1(2, 1 - sgn(u.ualign.type));
|
|
if (u.ualign.record < STRIDENT)
|
|
/* L: +(0..2), N: +(-1..1), C: +(-2..0); all: 0..3 */
|
|
dridx += rn1(3, sgn(u.ualign.type) - 1);
|
|
You_feel("like %s.", draft_reaction[dridx]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* digging via wand zap or spell cast */
|
|
void
|
|
zap_dig(void)
|
|
{
|
|
struct rm *room;
|
|
struct monst *mtmp;
|
|
struct obj *otmp;
|
|
struct trap *trap_with_u = (struct trap *) 0;
|
|
coordxy zx, zy, flow_x = -1, flow_y = -1;
|
|
int diridx = 8, digdepth;
|
|
boolean shopdoor, shopwall, maze_dig, pitdig = FALSE, pitflow = FALSE;
|
|
|
|
/*
|
|
* Original effect (approximately):
|
|
* from CORR: dig until we pierce a wall
|
|
* from ROOM: pierce wall and dig until we reach
|
|
* an ACCESSIBLE place.
|
|
* Currently: dig for digdepth positions;
|
|
* also down on request of Lennart Augustsson.
|
|
* 3.6.0: from a PIT: dig one adjacent pit.
|
|
*/
|
|
|
|
if (u.uswallow) {
|
|
mtmp = u.ustuck;
|
|
|
|
if (!is_whirly(mtmp->data)) {
|
|
if (digests(mtmp->data))
|
|
You("pierce %s %s wall!", s_suffix(mon_nam(mtmp)),
|
|
mbodypart(mtmp, STOMACH));
|
|
if (unique_corpstat(mtmp->data))
|
|
mtmp->mhp = (mtmp->mhp + 1) / 2;
|
|
else
|
|
mtmp->mhp = 1; /* almost dead */
|
|
expels(mtmp, mtmp->data, !digests(mtmp->data));
|
|
}
|
|
return;
|
|
} /* swallowed */
|
|
|
|
if (u.dz) {
|
|
if (!Is_airlevel(&u.uz) && !Is_waterlevel(&u.uz) && !Underwater) {
|
|
if (u.dz < 0 || On_stairs(u.ux, u.uy)) {
|
|
int dmg;
|
|
if (On_stairs(u.ux, u.uy)) {
|
|
stairway *stway = stairway_at(u.ux, u.uy);
|
|
pline_The("beam bounces off the %s and hits the %s.",
|
|
stway->isladder ? "ladder" : "stairs",
|
|
ceiling(u.ux, u.uy));
|
|
}
|
|
You("loosen a rock from the %s.", ceiling(u.ux, u.uy));
|
|
pline("It falls on your %s!", body_part(HEAD));
|
|
dmg = rnd(hard_helmet(uarmh) ? 2 : 6);
|
|
losehp(Maybe_Half_Phys(dmg), "falling rock", KILLED_BY_AN);
|
|
otmp = mksobj_at(ROCK, u.ux, u.uy, FALSE, FALSE);
|
|
if (otmp) {
|
|
(void) xname(otmp); /* set dknown, maybe bknown */
|
|
stackobj(otmp);
|
|
}
|
|
newsym(u.ux, u.uy);
|
|
} else {
|
|
watch_dig((struct monst *) 0, u.ux, u.uy, TRUE);
|
|
(void) dighole(FALSE, TRUE, (coord *) 0);
|
|
}
|
|
}
|
|
return;
|
|
} /* up or down */
|
|
|
|
/* normal case: digging across the level */
|
|
shopdoor = shopwall = FALSE;
|
|
maze_dig = svl.level.flags.is_maze_lev && !Is_earthlevel(&u.uz);
|
|
zx = u.ux + u.dx;
|
|
zy = u.uy + u.dy;
|
|
if (u.utrap && u.utraptype == TT_PIT
|
|
&& (trap_with_u = t_at(u.ux, u.uy))) {
|
|
pitdig = TRUE;
|
|
diridx = xytod(u.dx, u.dy);
|
|
}
|
|
digdepth = rn1(18, 8);
|
|
tmp_at(DISP_BEAM, cmap_to_glyph(S_digbeam));
|
|
while (--digdepth >= 0) {
|
|
if (!isok(zx, zy))
|
|
break;
|
|
room = &levl[zx][zy];
|
|
tmp_at(zx, zy);
|
|
nh_delay_output(); /* wait a little bit */
|
|
|
|
if (pitdig) { /* we are already in a pit if this is true */
|
|
coord cc;
|
|
struct trap *adjpit = t_at(zx, zy);
|
|
|
|
if (diridx != DIR_ERR
|
|
&& !conjoined_pits(adjpit, trap_with_u, FALSE)) {
|
|
digdepth = 0; /* limited to the adjacent location only */
|
|
nhUse(digdepth);
|
|
if (!(adjpit && is_pit(adjpit->ttyp))) {
|
|
char buf[BUFSZ];
|
|
|
|
cc.x = zx;
|
|
cc.y = zy;
|
|
if (!adj_pit_checks(&cc, buf)) {
|
|
if (buf[0])
|
|
pline1(buf);
|
|
} else {
|
|
/* this can also result in a pool at zx,zy */
|
|
dighole(TRUE, TRUE, &cc);
|
|
adjpit = t_at(zx, zy);
|
|
}
|
|
}
|
|
if (adjpit && is_pit(adjpit->ttyp)) {
|
|
int adjidx = DIR_180(diridx);
|
|
|
|
trap_with_u->conjoined |= (1 << diridx);
|
|
adjpit->conjoined |= (1 << adjidx);
|
|
flow_x = zx;
|
|
flow_y = zy;
|
|
pitflow = TRUE;
|
|
}
|
|
if (is_pool(zx, zy) || is_lava(zx, zy)) {
|
|
flow_x = zx - u.dx;
|
|
flow_y = zy - u.dy;
|
|
pitflow = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
} else if (closed_door(zx, zy) || room->typ == SDOOR) {
|
|
if (*in_rooms(zx, zy, SHOPBASE)) {
|
|
add_damage(zx, zy, SHOP_DOOR_COST);
|
|
shopdoor = TRUE;
|
|
}
|
|
if (room->typ == SDOOR)
|
|
room->typ = DOOR; /* doormask set below */
|
|
else if (cansee(zx, zy))
|
|
pline_The("door is razed!");
|
|
watch_dig((struct monst *) 0, zx, zy, TRUE);
|
|
room->doormask = D_NODOOR;
|
|
recalc_block_point(zx, zy); /* vision */
|
|
digdepth -= 2;
|
|
if (maze_dig)
|
|
break;
|
|
} else if (maze_dig) {
|
|
if (IS_WALL(room->typ)) {
|
|
if (!(room->wall_info & W_NONDIGGABLE)) {
|
|
if (*in_rooms(zx, zy, SHOPBASE)) {
|
|
add_damage(zx, zy, SHOP_WALL_COST);
|
|
shopwall = TRUE;
|
|
}
|
|
room->typ = ROOM, room->flags = 0;
|
|
unblock_point(zx, zy); /* vision */
|
|
} else if (!Blind)
|
|
pline_The("wall glows then fades.");
|
|
break;
|
|
} else if (IS_TREE(room->typ)) { /* check trees before stone */
|
|
if (!(room->wall_info & W_NONDIGGABLE)) {
|
|
room->typ = ROOM, room->flags = 0;
|
|
unblock_point(zx, zy); /* vision */
|
|
} else if (!Blind)
|
|
pline_The("tree shudders but is unharmed.");
|
|
break;
|
|
} else if (room->typ == STONE || room->typ == SCORR) {
|
|
if (!(room->wall_info & W_NONDIGGABLE)) {
|
|
room->typ = CORR, room->flags = 0;
|
|
unblock_point(zx, zy); /* vision */
|
|
} else if (!Blind)
|
|
pline_The("rock glows then fades.");
|
|
break;
|
|
}
|
|
} else if (IS_OBSTRUCTED(room->typ)) {
|
|
if (!may_dig(zx, zy))
|
|
break;
|
|
if (IS_WALL(room->typ) || room->typ == SDOOR) {
|
|
if (*in_rooms(zx, zy, SHOPBASE)) {
|
|
add_damage(zx, zy, SHOP_WALL_COST);
|
|
shopwall = TRUE;
|
|
}
|
|
watch_dig((struct monst *) 0, zx, zy, TRUE);
|
|
if (svl.level.flags.is_cavernous_lev && !in_town(zx, zy)) {
|
|
room->typ = CORR, room->flags = 0;
|
|
} else {
|
|
room->typ = DOOR, room->doormask = D_NODOOR;
|
|
}
|
|
digdepth -= 2;
|
|
} else if (IS_TREE(room->typ)) {
|
|
room->typ = ROOM, room->flags = 0;
|
|
digdepth -= 2;
|
|
} else { /* IS_OBSTRUCTED but not IS_WALL or SDOOR */
|
|
room->typ = CORR, room->flags = 0;
|
|
digdepth--;
|
|
}
|
|
unblock_point(zx, zy); /* vision */
|
|
}
|
|
zx += u.dx;
|
|
zy += u.dy;
|
|
} /* while */
|
|
tmp_at(DISP_END, 0); /* closing call */
|
|
|
|
if (pitflow && isok(flow_x, flow_y)) {
|
|
struct trap *ttmp = t_at(flow_x, flow_y);
|
|
|
|
if (ttmp && is_pit(ttmp->ttyp)) {
|
|
schar filltyp = fillholetyp(ttmp->tx, ttmp->ty, TRUE);
|
|
|
|
if (filltyp != ROOM)
|
|
pit_flow(ttmp, filltyp);
|
|
}
|
|
}
|
|
|
|
if (shopdoor || shopwall)
|
|
pay_for_damage(shopdoor ? "destroy" : "dig into", FALSE);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* This checks what is on the surface above the
|
|
* location where an adjacent pit might be created if
|
|
* you're zapping a wand of digging laterally while
|
|
* down in the pit.
|
|
*/
|
|
staticfn int
|
|
adj_pit_checks(coord *cc, char *msg)
|
|
{
|
|
int ltyp;
|
|
struct rm *room;
|
|
const char *foundation_msg =
|
|
"The foundation is too hard to dig through from this angle.";
|
|
|
|
if (!cc)
|
|
return FALSE;
|
|
if (!isok(cc->x, cc->y))
|
|
return FALSE;
|
|
*msg = '\0';
|
|
room = &levl[cc->x][cc->y];
|
|
ltyp = room->typ, room->flags = 0;
|
|
|
|
if (is_pool(cc->x, cc->y) || is_lava(cc->x, cc->y)) {
|
|
/* this is handled by the caller after we return FALSE */
|
|
return FALSE;
|
|
} else if (closed_door(cc->x, cc->y) || room->typ == SDOOR) {
|
|
/* We reject this here because dighole() isn't
|
|
prepared to deal with this case */
|
|
Strcpy(msg, foundation_msg);
|
|
return FALSE;
|
|
} else if (IS_WALL(ltyp)) {
|
|
/* if (room->wall_info & W_NONDIGGABLE) */
|
|
Strcpy(msg, foundation_msg);
|
|
return FALSE;
|
|
} else if (IS_TREE(ltyp)) { /* check trees before stone */
|
|
/* if (room->wall_info & W_NONDIGGABLE) */
|
|
Strcpy(msg, "The tree's roots glow then fade.");
|
|
return FALSE;
|
|
} else if (ltyp == STONE || ltyp == SCORR) {
|
|
if (room->wall_info & W_NONDIGGABLE) {
|
|
Strcpy(msg, "The rock glows then fades.");
|
|
return FALSE;
|
|
}
|
|
} else if (ltyp == IRONBARS) {
|
|
/* "set of iron bars" */
|
|
Strcpy(msg, "The bars go much deeper than your pit.");
|
|
return FALSE;
|
|
#if 0
|
|
} else if (is_lava(cc->x, cc->y)) {
|
|
} else if (is_ice(cc->x, cc->y)) {
|
|
} else if (is_pool(cc->x, cc->y)) {
|
|
} else if (IS_GRAVE(ltyp)) {
|
|
#endif
|
|
} else if (IS_SINK(ltyp)) {
|
|
Strcpy(msg, "A tangled mass of plumbing remains below the sink.");
|
|
return FALSE;
|
|
} else if (On_ladder(cc->x, cc->y)) {
|
|
Strcpy(msg, "The ladder is unaffected.");
|
|
return FALSE;
|
|
} else {
|
|
const char *supporting = (const char *) 0;
|
|
|
|
if (IS_FOUNTAIN(ltyp))
|
|
supporting = "fountain";
|
|
else if (IS_THRONE(ltyp))
|
|
supporting = "throne";
|
|
else if (IS_ALTAR(ltyp))
|
|
supporting = "altar";
|
|
else if (On_stairs(cc->x, cc->y))
|
|
/* staircase up or down. On_ladder handled above. */
|
|
supporting = "stairs";
|
|
else if (ltyp == DRAWBRIDGE_DOWN /* "lowered drawbridge" */
|
|
|| ltyp == DBWALL) /* "raised drawbridge" */
|
|
supporting = "drawbridge";
|
|
|
|
if (supporting) {
|
|
Sprintf(msg, "The %s supporting structures remain intact.",
|
|
s_suffix(supporting));
|
|
return FALSE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Ensure that all conjoined pits fill up.
|
|
*/
|
|
staticfn void
|
|
pit_flow(struct trap *trap, schar filltyp)
|
|
{
|
|
/*
|
|
* FIXME?
|
|
* liquid_flow() -> pooleffects() -> {drown(),lava_effects()}
|
|
* might kill the hero; the game will end and if that leaves bones,
|
|
* remaining conjoined pits will be left unprocessed.
|
|
*/
|
|
if (trap && filltyp != ROOM && is_pit(trap->ttyp)) {
|
|
struct trap t;
|
|
int idx;
|
|
|
|
t = *trap;
|
|
levl[t.tx][t.ty].typ = filltyp, levl[t.tx][t.ty].flags = 0;
|
|
liquid_flow(t.tx, t.ty, filltyp, trap,
|
|
u_at(t.tx, t.ty)
|
|
? "Suddenly %s flows in from the adjacent pit!"
|
|
: (char *) 0);
|
|
for (idx = 0; idx < N_DIRS; ++idx) {
|
|
if (t.conjoined & (1 << idx)) {
|
|
coordxy x, y;
|
|
struct trap *t2;
|
|
|
|
x = t.tx + xdir[idx];
|
|
y = t.ty + ydir[idx];
|
|
t2 = t_at(x, y);
|
|
#if 0
|
|
/* cannot do this back-check; liquid_flow()
|
|
* called deltrap() which cleaned up the
|
|
* conjoined fields on both pits.
|
|
*/
|
|
if (t2 && (t2->conjoined & (1 << DIR_180(idx))))
|
|
#endif
|
|
/* recursion */
|
|
pit_flow(t2, filltyp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct obj *
|
|
buried_ball(coord *cc)
|
|
{
|
|
int odist, bdist = COLNO;
|
|
struct obj *otmp, *ball = 0;
|
|
|
|
/* FIXME:
|
|
* This is just approximate; if multiple buried balls meet the
|
|
* criterium (within 2 steps of tethered hero's present location)
|
|
* it will find an arbitrary one rather than the one which used
|
|
* to be uball. Once 3.6.{0,1} save file compatibility is broken,
|
|
* we should add svc.context.buriedball_oid and then we can find the
|
|
* actual former uball, which might be extra heavy or christened
|
|
* or not the one buried directly underneath the target spot.
|
|
*
|
|
* [Why does this search within a radius of two when trapmove()
|
|
* only lets hero get one step away from the buried ball?]
|
|
*/
|
|
|
|
/* u.utrap might have already been cleared, in which case the value
|
|
of u.utraptype is no longer meaningful; if u.utrap is still set
|
|
then u.utraptype needs to be for buried ball */
|
|
if (!u.utrap || u.utraptype == TT_BURIEDBALL) {
|
|
for (otmp = svl.level.buriedobjlist; otmp; otmp = otmp->nobj) {
|
|
if (otmp->otyp != HEAVY_IRON_BALL)
|
|
continue;
|
|
/* if found at the target spot, we're done */
|
|
if (otmp->ox == cc->x && otmp->oy == cc->y)
|
|
return otmp;
|
|
/* find nearest within allowable vicinity: +/-2
|
|
* 4 5 8
|
|
* 1 2 5
|
|
* 0 1 4
|
|
*/
|
|
odist = dist2(otmp->ox, otmp->oy, cc->x, cc->y);
|
|
if (odist <= 8 && (!ball || odist < bdist)) {
|
|
/* remember nearest buried ball but keep checking others */
|
|
ball = otmp;
|
|
bdist = odist;
|
|
}
|
|
}
|
|
}
|
|
if (ball) {
|
|
/* found, but not at < cc->x, cc->y > */
|
|
cc->x = ball->ox;
|
|
cc->y = ball->oy;
|
|
}
|
|
return ball;
|
|
}
|
|
|
|
void
|
|
buried_ball_to_punishment(void)
|
|
{
|
|
coord cc;
|
|
struct obj *ball;
|
|
|
|
cc.x = u.ux;
|
|
cc.y = u.uy;
|
|
ball = buried_ball(&cc);
|
|
if (ball) {
|
|
obj_extract_self(ball);
|
|
#if 0
|
|
/* rusting buried metallic objects is not implemented yet */
|
|
if (ball->timed)
|
|
(void) stop_timer(RUST_METAL, obj_to_any(ball));
|
|
#endif
|
|
punish(ball); /* use ball as flag for unearthed buried ball */
|
|
reset_utrap(FALSE);
|
|
del_engr_at(cc.x, cc.y);
|
|
newsym(cc.x, cc.y);
|
|
}
|
|
}
|
|
|
|
void
|
|
buried_ball_to_freedom(void)
|
|
{
|
|
coord cc;
|
|
struct obj *ball;
|
|
|
|
cc.x = u.ux;
|
|
cc.y = u.uy;
|
|
ball = buried_ball(&cc);
|
|
if (ball) {
|
|
obj_extract_self(ball);
|
|
#if 0
|
|
/* rusting buried metallic objects is not implemented yet */
|
|
if (ball->timed)
|
|
(void) stop_timer(RUST_METAL, obj_to_any(ball));
|
|
#endif
|
|
place_object(ball, cc.x, cc.y);
|
|
stackobj(ball);
|
|
reset_utrap(TRUE);
|
|
del_engr_at(cc.x, cc.y);
|
|
newsym(cc.x, cc.y);
|
|
}
|
|
}
|
|
|
|
/* move objects from fobj/nexthere lists to buriedobjlist, keeping position
|
|
information */
|
|
struct obj *
|
|
bury_an_obj(struct obj *otmp, boolean *dealloced)
|
|
{
|
|
struct obj *otmp2;
|
|
boolean under_ice;
|
|
|
|
debugpline1("bury_an_obj: %s", xname(otmp));
|
|
if (dealloced)
|
|
*dealloced = FALSE;
|
|
if (otmp == uball) {
|
|
unpunish();
|
|
set_utrap((unsigned) rn1(50, 20), TT_BURIEDBALL);
|
|
pline_The("iron ball gets buried!");
|
|
}
|
|
/* after unpunish(), or might get deallocated chain */
|
|
otmp2 = otmp->nexthere;
|
|
/*
|
|
* obj_resists(,0,0) prevents Rider corpses from being buried.
|
|
* It also prevents The Amulet and invocation tools from being
|
|
* buried. Since they can't be confined to bags and statues,
|
|
* it makes sense that they can't be buried either, even though
|
|
* the real reason there (direct accessibility when carried) is
|
|
* completely different.
|
|
*/
|
|
if (otmp == uchain || obj_resists(otmp, 0, 0))
|
|
return otmp2;
|
|
|
|
if (otmp->otyp == LEASH && otmp->leashmon != 0)
|
|
o_unleash(otmp);
|
|
|
|
if (otmp->lamplit && otmp->otyp != POT_OIL)
|
|
end_burn(otmp, TRUE);
|
|
|
|
obj_extract_self(otmp);
|
|
|
|
under_ice = is_ice(otmp->ox, otmp->oy);
|
|
if ((otmp->otyp == ROCK && !under_ice) || otmp->otyp == BOULDER) {
|
|
/* merges into burying material; boulder removal is for #wizbury */
|
|
if (dealloced)
|
|
*dealloced = TRUE;
|
|
obfree(otmp, (struct obj *) 0);
|
|
return otmp2;
|
|
}
|
|
/*
|
|
* Start a rot on organic material. Not corpses -- they
|
|
* are already handled.
|
|
*/
|
|
if (otmp->otyp == CORPSE) {
|
|
; /* should cancel timer if under_ice */
|
|
} else if ((under_ice ? (otmp->oclass == POTION_CLASS) : is_organic(otmp))
|
|
&& !obj_resists(otmp, 5, 95)) {
|
|
(void) start_timer((under_ice ? 0L : 250L) + (long) rnd(250),
|
|
TIMER_OBJECT, ROT_ORGANIC, obj_to_any(otmp));
|
|
#if 0
|
|
/* rusting of buried metal not yet implemented */
|
|
} else if (is_rustprone(otmp)) {
|
|
(void) start_timer((long) rnd((otmp->otyp == HEAVY_IRON_BALL)
|
|
? 1500
|
|
: 250),
|
|
TIMER_OBJECT, RUST_METAL, obj_to_any(otmp));
|
|
#endif
|
|
}
|
|
add_to_buried(otmp);
|
|
return otmp2;
|
|
}
|
|
|
|
void
|
|
bury_objs(int x, int y)
|
|
{
|
|
struct obj *otmp, *otmp2;
|
|
struct monst *shkp;
|
|
long loss = 0L;
|
|
boolean costly;
|
|
|
|
costly = ((shkp = shop_keeper(*in_rooms(x, y, SHOPBASE)))
|
|
&& costly_spot(x, y));
|
|
|
|
if (svl.level.objects[x][y] != (struct obj *) 0) {
|
|
debugpline2("bury_objs: at <%d,%d>", x, y);
|
|
}
|
|
for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp2) {
|
|
if (costly && !svc.context.mon_moving) {
|
|
loss += stolen_value(otmp, x, y, (boolean) shkp->mpeaceful, TRUE);
|
|
if (otmp->oclass != COIN_CLASS)
|
|
otmp->no_charge = 1;
|
|
}
|
|
otmp2 = bury_an_obj(otmp, (boolean *) 0);
|
|
}
|
|
|
|
/* don't expect any engravings here, but just in case */
|
|
del_engr_at(x, y);
|
|
newsym(x, y);
|
|
maybe_unhide_at(x, y);
|
|
|
|
if (costly && loss) {
|
|
You("owe %s %ld %s for burying merchandise.", shkname(shkp), loss,
|
|
currency(loss));
|
|
}
|
|
}
|
|
|
|
/* move objects from buriedobjlist to fobj/nexthere lists; if caller
|
|
converts terrain from ice to something, it should call obj_ice_effects() */
|
|
void
|
|
unearth_objs(int x, int y)
|
|
{
|
|
struct obj *otmp, *otmp2, *bball;
|
|
coord cc;
|
|
|
|
debugpline2("unearth_objs: at <%d,%d>", x, y);
|
|
cc.x = x;
|
|
cc.y = y;
|
|
bball = buried_ball(&cc);
|
|
for (otmp = svl.level.buriedobjlist; otmp; otmp = otmp2) {
|
|
otmp2 = otmp->nobj;
|
|
if (otmp->ox == x && otmp->oy == y) {
|
|
if (bball && otmp == bball
|
|
&& u.utrap && u.utraptype == TT_BURIEDBALL) {
|
|
buried_ball_to_punishment();
|
|
} else {
|
|
obj_extract_self(otmp);
|
|
if (otmp->timed)
|
|
(void) stop_timer(ROT_ORGANIC, obj_to_any(otmp));
|
|
place_object(otmp, x, y);
|
|
stackobj(otmp);
|
|
}
|
|
}
|
|
}
|
|
del_engr_at(x, y);
|
|
newsym(x, y);
|
|
}
|
|
|
|
/*
|
|
* The organic material has rotted away while buried. As an expansion,
|
|
* we could add partial damage. A damage count is kept in the object
|
|
* and every time we are called we increment the count and reschedule another
|
|
* timeout. Eventually the object rots away.
|
|
*
|
|
* This is used by buried objects other than corpses. When a container rots
|
|
* away, any contents become newly buried objects.
|
|
*/
|
|
/* ARGSUSED */
|
|
void
|
|
rot_organic(anything *arg, long timeout UNUSED)
|
|
{
|
|
struct obj *obj = arg->a_obj;
|
|
|
|
while (Has_contents(obj)) {
|
|
/* We don't need to place contained object on the floor
|
|
first, but we do need to update its map coordinates. */
|
|
obj->cobj->ox = obj->ox, obj->cobj->oy = obj->oy;
|
|
/* Everything which can be held in a container can also be
|
|
buried, so bury_an_obj's use of obj_extract_self insures
|
|
that Has_contents(obj) will eventually become false. */
|
|
(void) bury_an_obj(obj->cobj, (boolean *) 0);
|
|
}
|
|
obj_extract_self(obj);
|
|
obfree(obj, (struct obj *) 0);
|
|
}
|
|
|
|
/*
|
|
* Called when a corpse has rotted completely away.
|
|
*/
|
|
void
|
|
rot_corpse(anything *arg, long timeout)
|
|
{
|
|
coordxy x = 0, y = 0;
|
|
struct obj *obj = arg->a_obj;
|
|
boolean on_floor = obj->where == OBJ_FLOOR,
|
|
in_invent = obj->where == OBJ_INVENT;
|
|
|
|
if (on_floor) {
|
|
x = obj->ox;
|
|
y = obj->oy;
|
|
} else if (in_invent) {
|
|
if (flags.verbose) {
|
|
char *cname = corpse_xname(obj, (const char *) 0, CXN_NO_PFX);
|
|
|
|
Your("%s%s %s away%c", obj == uwep ? "wielded " : "", cname,
|
|
otense(obj, "rot"), obj == uwep ? '!' : '.');
|
|
}
|
|
if (obj->owornmask) {
|
|
remove_worn_item(obj, TRUE);
|
|
stop_occupation();
|
|
}
|
|
} else if (obj->where == OBJ_MINVENT) {
|
|
if (obj->owornmask && obj == MON_WEP(obj->ocarry))
|
|
setmnotwielded(obj->ocarry, obj); /* clears owornmask */
|
|
} else if (obj->where == OBJ_MIGRATING) {
|
|
/* clear destination flag so that obfree()'s check for
|
|
freeing a worn object doesn't get a false hit */
|
|
obj->owornmask = 0L;
|
|
}
|
|
rot_organic(arg, timeout);
|
|
if (on_floor) {
|
|
struct monst *mtmp = m_at(x, y);
|
|
|
|
/* a hiding monster may be exposed */
|
|
if (mtmp && !OBJ_AT(x, y) && mtmp->mundetected
|
|
&& hides_under(mtmp->data)) {
|
|
mtmp->mundetected = 0;
|
|
} else if (u_at(x, y)
|
|
&& u.uundetected && hides_under(gy.youmonst.data))
|
|
(void) hideunder(&gy.youmonst);
|
|
newsym(x, y);
|
|
} else if (in_invent)
|
|
update_inventory();
|
|
}
|
|
|
|
#if 0
|
|
void
|
|
bury_monst(struct monst *mtmp)
|
|
{
|
|
debugpline1("bury_monst: %s", mon_nam(mtmp));
|
|
if (canseemon(mtmp)) {
|
|
if (is_flyer(mtmp->data) || is_floater(mtmp->data)) {
|
|
pline_The("%s opens up, but %s is not swallowed!",
|
|
surface(mtmp->mx, mtmp->my), mon_nam(mtmp));
|
|
return;
|
|
} else
|
|
pline_The("%s opens up and swallows %s!",
|
|
surface(mtmp->mx, mtmp->my), mon_nam(mtmp));
|
|
}
|
|
|
|
mtmp->mburied = TRUE;
|
|
wakeup(mtmp, FALSE); /* at least give it a chance :-) */
|
|
newsym(mtmp->mx, mtmp->my);
|
|
}
|
|
|
|
void
|
|
bury_you(void)
|
|
{
|
|
debugpline0("bury_you");
|
|
if (!Levitation && !Flying) {
|
|
if (u.uswallow)
|
|
You_feel("a sensation like falling into a trap!");
|
|
else
|
|
pline_The("%s opens beneath you and you fall in!",
|
|
surface(u.ux, u.uy));
|
|
|
|
u.uburied = TRUE;
|
|
if (!Strangled && !Breathless)
|
|
Strangled = 6;
|
|
under_ground(1);
|
|
}
|
|
}
|
|
|
|
void
|
|
unearth_you(void)
|
|
{
|
|
debugpline0("unearth_you");
|
|
u.uburied = FALSE;
|
|
under_ground(0);
|
|
if (!uamul || uamul->otyp != AMULET_OF_STRANGULATION)
|
|
Strangled = 0;
|
|
vision_recalc(0);
|
|
}
|
|
|
|
void
|
|
escape_tomb(void)
|
|
{
|
|
debugpline0("escape_tomb");
|
|
if ((Teleportation || can_teleport(gy.youmonst.data))
|
|
&& (Teleport_control || rn2(3) < Luck+2)) {
|
|
You("attempt a teleport spell.");
|
|
(void) dotele(FALSE); /* calls unearth_you() */
|
|
} else if (u.uburied) { /* still buried after 'port attempt */
|
|
boolean good;
|
|
|
|
if (amorphous(gy.youmonst.data) || Passes_walls
|
|
|| noncorporeal(gy.youmonst.data)
|
|
|| (unsolid(gy.youmonst.data)
|
|
&& gy.youmonst.data != &mons[PM_WATER_ELEMENTAL])
|
|
|| (tunnels(gy.youmonst.data) && !needspick(gy.youmonst.data))) {
|
|
You("%s up through the %s.",
|
|
(tunnels(gy.youmonst.data) && !needspick(gy.youmonst.data))
|
|
? "try to tunnel"
|
|
: (amorphous(gy.youmonst.data))
|
|
? "ooze"
|
|
: "phase",
|
|
surface(u.ux, u.uy));
|
|
|
|
good = (tunnels(gy.youmonst.data) && !needspick(gy.youmonst.data))
|
|
? dighole(TRUE, FALSE, (coord *) 0) : TRUE;
|
|
if (good)
|
|
unearth_you();
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
bury_obj(otmp)
|
|
struct obj *otmp;
|
|
{
|
|
debugpline0("bury_obj");
|
|
if (cansee(otmp->ox, otmp->oy))
|
|
pline_The("objects on the %s tumble into a hole!",
|
|
surface(otmp->ox, otmp->oy));
|
|
|
|
bury_objs(otmp->ox, otmp->oy);
|
|
}
|
|
#endif /*0*/
|
|
|
|
#ifdef DEBUG
|
|
/* the #wizbury command - bury everything at your loc and around */
|
|
int
|
|
wiz_debug_cmd_bury(void)
|
|
{
|
|
struct obj *otmp;
|
|
int x, y, before = 0, after = 0, diff;
|
|
|
|
for (x = u.ux - 1; x <= u.ux + 1; x++)
|
|
for (y = u.uy - 1; y <= u.uy + 1; y++) {
|
|
if (!isok(x, y))
|
|
continue;
|
|
for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp->nexthere)
|
|
++before;
|
|
|
|
bury_objs(x, y);
|
|
|
|
for (otmp = svl.level.objects[x][y]; otmp; otmp = otmp->nexthere)
|
|
++after;
|
|
}
|
|
|
|
diff = before - after;
|
|
if (before == 0)
|
|
/* there was nothing here */
|
|
pline("No objects here or adjacent to bury.");
|
|
else if (diff == 0)
|
|
/* before and after will be the same if only unburiable objects are
|
|
present (The Amulet, invocation items, Rider corpses, uchain when
|
|
uball doesn't get buried: carried or floor beyond burial range) */
|
|
pline("No objects buried.");
|
|
else
|
|
/* usual case; if uball got buried, uchain went away and won't be
|
|
counted as buried */
|
|
pline("%d object%s buried.", diff, plur(diff));
|
|
return ECMD_OK;
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
#undef BY_YOU
|
|
#undef BY_OBJECT
|
|
/* for 'onefile' testing, leave STRIDENT defined so that the other instance
|
|
of it in pray.c will trigger a complaint if someone changes its value */
|
|
/*#undef STRIDENT*/
|
|
|
|
/*dig.c*/
|