1054 lines
32 KiB
C
1054 lines
32 KiB
C
/* NetHack 3.6 lock.c $NHDT-Date: 1446955300 2015/11/08 04:01:40 $ $NHDT-Branch: master $:$NHDT-Revision: 1.67 $ */
|
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
#include "hack.h"
|
|
|
|
STATIC_PTR int NDECL(picklock);
|
|
STATIC_PTR int NDECL(forcelock);
|
|
|
|
/* at most one of `door' and `box' should be non-null at any given time */
|
|
STATIC_VAR NEARDATA struct xlock_s {
|
|
struct rm *door;
|
|
struct obj *box;
|
|
int picktyp, /* key|pick|card for unlock, sharp vs blunt for #force */
|
|
chance, usedtime;
|
|
} xlock;
|
|
|
|
STATIC_DCL const char *NDECL(lock_action);
|
|
STATIC_DCL boolean FDECL(obstructed, (int, int, BOOLEAN_P));
|
|
STATIC_DCL void FDECL(chest_shatter_msg, (struct obj *));
|
|
|
|
boolean
|
|
picking_lock(x, y)
|
|
int *x, *y;
|
|
{
|
|
if (occupation == picklock) {
|
|
*x = u.ux + u.dx;
|
|
*y = u.uy + u.dy;
|
|
return TRUE;
|
|
} else {
|
|
*x = *y = 0;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
boolean
|
|
picking_at(x, y)
|
|
int x, y;
|
|
{
|
|
return (boolean) (occupation == picklock && xlock.door == &levl[x][y]);
|
|
}
|
|
|
|
/* produce an occupation string appropriate for the current activity */
|
|
STATIC_OVL const char *
|
|
lock_action()
|
|
{
|
|
/* "unlocking"+2 == "locking" */
|
|
static const char *actions[] = {
|
|
"unlocking the door", /* [0] */
|
|
"unlocking the chest", /* [1] */
|
|
"unlocking the box", /* [2] */
|
|
"picking the lock" /* [3] */
|
|
};
|
|
|
|
/* if the target is currently unlocked, we're trying to lock it now */
|
|
if (xlock.door && !(xlock.door->doormask & D_LOCKED))
|
|
return actions[0] + 2; /* "locking the door" */
|
|
else if (xlock.box && !xlock.box->olocked)
|
|
return xlock.box->otyp == CHEST ? actions[1] + 2 : actions[2] + 2;
|
|
/* otherwise we're trying to unlock it */
|
|
else if (xlock.picktyp == LOCK_PICK)
|
|
return actions[3]; /* "picking the lock" */
|
|
else if (xlock.picktyp == CREDIT_CARD)
|
|
return actions[3]; /* same as lock_pick */
|
|
else if (xlock.door)
|
|
return actions[0]; /* "unlocking the door" */
|
|
else if (xlock.box)
|
|
return xlock.box->otyp == CHEST ? actions[1] : actions[2];
|
|
else
|
|
return actions[3];
|
|
}
|
|
|
|
/* try to open/close a lock */
|
|
STATIC_PTR int
|
|
picklock(VOID_ARGS)
|
|
{
|
|
if (xlock.box) {
|
|
if (xlock.box->where != OBJ_FLOOR
|
|
|| xlock.box->ox != u.ux || xlock.box->oy != u.uy) {
|
|
return ((xlock.usedtime = 0)); /* you or it moved */
|
|
}
|
|
} else { /* door */
|
|
if (xlock.door != &(levl[u.ux + u.dx][u.uy + u.dy])) {
|
|
return ((xlock.usedtime = 0)); /* you moved */
|
|
}
|
|
switch (xlock.door->doormask) {
|
|
case D_NODOOR:
|
|
pline("This doorway has no door.");
|
|
return ((xlock.usedtime = 0));
|
|
case D_ISOPEN:
|
|
You("cannot lock an open door.");
|
|
return ((xlock.usedtime = 0));
|
|
case D_BROKEN:
|
|
pline("This door is broken.");
|
|
return ((xlock.usedtime = 0));
|
|
}
|
|
}
|
|
|
|
if (xlock.usedtime++ >= 50 || nohands(youmonst.data)) {
|
|
You("give up your attempt at %s.", lock_action());
|
|
exercise(A_DEX, TRUE); /* even if you don't succeed */
|
|
return ((xlock.usedtime = 0));
|
|
}
|
|
|
|
if (rn2(100) >= xlock.chance)
|
|
return 1; /* still busy */
|
|
|
|
You("succeed in %s.", lock_action());
|
|
if (xlock.door) {
|
|
if (xlock.door->doormask & D_TRAPPED) {
|
|
b_trapped("door", FINGER);
|
|
xlock.door->doormask = D_NODOOR;
|
|
unblock_point(u.ux + u.dx, u.uy + u.dy);
|
|
if (*in_rooms(u.ux + u.dx, u.uy + u.dy, SHOPBASE))
|
|
add_damage(u.ux + u.dx, u.uy + u.dy, 0L);
|
|
newsym(u.ux + u.dx, u.uy + u.dy);
|
|
} else if (xlock.door->doormask & D_LOCKED)
|
|
xlock.door->doormask = D_CLOSED;
|
|
else
|
|
xlock.door->doormask = D_LOCKED;
|
|
} else {
|
|
xlock.box->olocked = !xlock.box->olocked;
|
|
xlock.box->lknown = 1;
|
|
if (xlock.box->otrapped)
|
|
(void) chest_trap(xlock.box, FINGER, FALSE);
|
|
}
|
|
exercise(A_DEX, TRUE);
|
|
return ((xlock.usedtime = 0));
|
|
}
|
|
|
|
void
|
|
breakchestlock(box, destroyit)
|
|
struct obj *box;
|
|
boolean destroyit;
|
|
{
|
|
if (!destroyit) { /* bill for the box but not for its contents */
|
|
struct obj *hide_contents = box->cobj;
|
|
|
|
box->cobj = 0;
|
|
costly_alteration(box, COST_BRKLCK);
|
|
box->cobj = hide_contents;
|
|
box->olocked = 0;
|
|
box->obroken = 1;
|
|
box->lknown = 1;
|
|
} else { /* #force has destroyed this box (at <u.ux,u.uy>) */
|
|
struct obj *otmp;
|
|
struct monst *shkp = (*u.ushops && costly_spot(u.ux, u.uy))
|
|
? shop_keeper(*u.ushops)
|
|
: 0;
|
|
boolean costly = (boolean) (shkp != 0),
|
|
peaceful_shk = costly && (boolean) shkp->mpeaceful;
|
|
long loss = 0L;
|
|
|
|
pline("In fact, you've totally destroyed %s.", the(xname(box)));
|
|
/* Put the contents on ground at the hero's feet. */
|
|
while ((otmp = box->cobj) != 0) {
|
|
obj_extract_self(otmp);
|
|
if (!rn2(3) || otmp->oclass == POTION_CLASS) {
|
|
chest_shatter_msg(otmp);
|
|
if (costly)
|
|
loss +=
|
|
stolen_value(otmp, u.ux, u.uy, peaceful_shk, TRUE);
|
|
if (otmp->quan == 1L) {
|
|
obfree(otmp, (struct obj *) 0);
|
|
continue;
|
|
}
|
|
useup(otmp);
|
|
}
|
|
if (box->otyp == ICE_BOX && otmp->otyp == CORPSE) {
|
|
otmp->age = monstermoves - otmp->age; /* actual age */
|
|
start_corpse_timeout(otmp);
|
|
}
|
|
place_object(otmp, u.ux, u.uy);
|
|
stackobj(otmp);
|
|
}
|
|
if (costly)
|
|
loss += stolen_value(box, u.ux, u.uy, peaceful_shk, TRUE);
|
|
if (loss)
|
|
You("owe %ld %s for objects destroyed.", loss, currency(loss));
|
|
delobj(box);
|
|
}
|
|
}
|
|
|
|
/* try to force a locked chest */
|
|
STATIC_PTR int
|
|
forcelock(VOID_ARGS)
|
|
{
|
|
if ((xlock.box->ox != u.ux) || (xlock.box->oy != u.uy))
|
|
return ((xlock.usedtime = 0)); /* you or it moved */
|
|
|
|
if (xlock.usedtime++ >= 50 || !uwep || nohands(youmonst.data)) {
|
|
You("give up your attempt to force the lock.");
|
|
if (xlock.usedtime >= 50) /* you made the effort */
|
|
exercise((xlock.picktyp) ? A_DEX : A_STR, TRUE);
|
|
return ((xlock.usedtime = 0));
|
|
}
|
|
|
|
if (xlock.picktyp) { /* blade */
|
|
if (rn2(1000 - (int) uwep->spe) > (992 - greatest_erosion(uwep) * 10)
|
|
&& !uwep->cursed && !obj_resists(uwep, 0, 99)) {
|
|
/* for a +0 weapon, probability that it survives an unsuccessful
|
|
* attempt to force the lock is (.992)^50 = .67
|
|
*/
|
|
pline("%sour %s broke!", (uwep->quan > 1L) ? "One of y" : "Y",
|
|
xname(uwep));
|
|
useup(uwep);
|
|
You("give up your attempt to force the lock.");
|
|
exercise(A_DEX, TRUE);
|
|
return ((xlock.usedtime = 0));
|
|
}
|
|
} else /* blunt */
|
|
wake_nearby(); /* due to hammering on the container */
|
|
|
|
if (rn2(100) >= xlock.chance)
|
|
return 1; /* still busy */
|
|
|
|
You("succeed in forcing the lock.");
|
|
breakchestlock(xlock.box, (boolean) (!xlock.picktyp && !rn2(3)));
|
|
|
|
exercise((xlock.picktyp) ? A_DEX : A_STR, TRUE);
|
|
return ((xlock.usedtime = 0));
|
|
}
|
|
|
|
void
|
|
reset_pick()
|
|
{
|
|
xlock.usedtime = xlock.chance = xlock.picktyp = 0;
|
|
xlock.door = 0;
|
|
xlock.box = 0;
|
|
}
|
|
|
|
/* level change; don't reset if hero is carrying xlock.box with him/her */
|
|
void
|
|
maybe_reset_pick()
|
|
{
|
|
if (!xlock.box || !carried(xlock.box))
|
|
reset_pick();
|
|
}
|
|
|
|
/* for doapply(); if player gives a direction or resumes an interrupted
|
|
previous attempt then it costs hero a move even if nothing ultimately
|
|
happens; when told "can't do that" before being asked for direction
|
|
or player cancels with ESC while giving direction, it doesn't */
|
|
#define PICKLOCK_LEARNED_SOMETHING (-1) /* time passes */
|
|
#define PICKLOCK_DID_NOTHING 0 /* no time passes */
|
|
#define PICKLOCK_DID_SOMETHING 1
|
|
|
|
/* player is applying a key, lock pick, or credit card */
|
|
int
|
|
pick_lock(pick)
|
|
struct obj *pick;
|
|
{
|
|
int picktyp, c, ch;
|
|
coord cc;
|
|
struct rm *door;
|
|
struct obj *otmp;
|
|
char qbuf[QBUFSZ];
|
|
|
|
picktyp = pick->otyp;
|
|
|
|
/* check whether we're resuming an interrupted previous attempt */
|
|
if (xlock.usedtime && picktyp == xlock.picktyp) {
|
|
static char no_longer[] = "Unfortunately, you can no longer %s %s.";
|
|
|
|
if (nohands(youmonst.data)) {
|
|
const char *what = (picktyp == LOCK_PICK) ? "pick" : "key";
|
|
if (picktyp == CREDIT_CARD)
|
|
what = "card";
|
|
pline(no_longer, "hold the", what);
|
|
reset_pick();
|
|
return PICKLOCK_LEARNED_SOMETHING;
|
|
} else if (u.uswallow || (xlock.box && !can_reach_floor(TRUE))) {
|
|
pline(no_longer, "reach the", "lock");
|
|
reset_pick();
|
|
return PICKLOCK_LEARNED_SOMETHING;
|
|
} else {
|
|
const char *action = lock_action();
|
|
|
|
You("resume your attempt at %s.", action);
|
|
set_occupation(picklock, action, 0);
|
|
return PICKLOCK_DID_SOMETHING;
|
|
}
|
|
}
|
|
|
|
if (nohands(youmonst.data)) {
|
|
You_cant("hold %s -- you have no hands!", doname(pick));
|
|
return PICKLOCK_DID_NOTHING;
|
|
} else if (u.uswallow) {
|
|
You_cant("%sunlock %s.", (picktyp == CREDIT_CARD) ? "" : "lock or ",
|
|
mon_nam(u.ustuck));
|
|
return PICKLOCK_DID_NOTHING;
|
|
}
|
|
|
|
if ((picktyp != LOCK_PICK && picktyp != CREDIT_CARD
|
|
&& picktyp != SKELETON_KEY)) {
|
|
impossible("picking lock with object %d?", picktyp);
|
|
return PICKLOCK_DID_NOTHING;
|
|
}
|
|
ch = 0; /* lint suppression */
|
|
|
|
if (!get_adjacent_loc((char *) 0, "Invalid location!", u.ux, u.uy, &cc))
|
|
return PICKLOCK_DID_NOTHING;
|
|
|
|
if (cc.x == u.ux && cc.y == u.uy) { /* pick lock on a container */
|
|
const char *verb;
|
|
char qsfx[QBUFSZ];
|
|
boolean it;
|
|
int count;
|
|
|
|
if (u.dz < 0) {
|
|
There("isn't any sort of lock up %s.",
|
|
Levitation ? "here" : "there");
|
|
return PICKLOCK_LEARNED_SOMETHING;
|
|
} else if (is_lava(u.ux, u.uy)) {
|
|
pline("Doing that would probably melt %s.", yname(pick));
|
|
return PICKLOCK_LEARNED_SOMETHING;
|
|
} else if (is_pool(u.ux, u.uy) && !Underwater) {
|
|
pline_The("%s has no lock.", hliquid("water"));
|
|
return PICKLOCK_LEARNED_SOMETHING;
|
|
}
|
|
|
|
count = 0;
|
|
c = 'n'; /* in case there are no boxes here */
|
|
for (otmp = level.objects[cc.x][cc.y]; otmp; otmp = otmp->nexthere)
|
|
if (Is_box(otmp)) {
|
|
++count;
|
|
if (!can_reach_floor(TRUE)) {
|
|
You_cant("reach %s from up here.", the(xname(otmp)));
|
|
return PICKLOCK_LEARNED_SOMETHING;
|
|
}
|
|
it = 0;
|
|
if (otmp->obroken)
|
|
verb = "fix";
|
|
else if (!otmp->olocked)
|
|
verb = "lock", it = 1;
|
|
else if (picktyp != LOCK_PICK)
|
|
verb = "unlock", it = 1;
|
|
else
|
|
verb = "pick";
|
|
|
|
/* "There is <a box> here; <verb> <it|its lock>?" */
|
|
Sprintf(qsfx, " here; %s %s?", verb, it ? "it" : "its lock");
|
|
(void) safe_qbuf(qbuf, "There is ", qsfx, otmp, doname,
|
|
ansimpleoname, "a box");
|
|
otmp->lknown = 1;
|
|
|
|
c = ynq(qbuf);
|
|
if (c == 'q')
|
|
return 0;
|
|
if (c == 'n')
|
|
continue;
|
|
|
|
if (otmp->obroken) {
|
|
You_cant("fix its broken lock with %s.", doname(pick));
|
|
return PICKLOCK_LEARNED_SOMETHING;
|
|
} else if (picktyp == CREDIT_CARD && !otmp->olocked) {
|
|
/* credit cards are only good for unlocking */
|
|
You_cant("do that with %s.",
|
|
an(simple_typename(picktyp)));
|
|
return PICKLOCK_LEARNED_SOMETHING;
|
|
}
|
|
switch (picktyp) {
|
|
case CREDIT_CARD:
|
|
ch = ACURR(A_DEX) + 20 * Role_if(PM_ROGUE);
|
|
break;
|
|
case LOCK_PICK:
|
|
ch = 4 * ACURR(A_DEX) + 25 * Role_if(PM_ROGUE);
|
|
break;
|
|
case SKELETON_KEY:
|
|
ch = 75 + ACURR(A_DEX);
|
|
break;
|
|
default:
|
|
ch = 0;
|
|
}
|
|
if (otmp->cursed)
|
|
ch /= 2;
|
|
|
|
xlock.picktyp = picktyp;
|
|
xlock.box = otmp;
|
|
xlock.door = 0;
|
|
break;
|
|
}
|
|
if (c != 'y') {
|
|
if (!count)
|
|
There("doesn't seem to be any sort of lock here.");
|
|
return PICKLOCK_LEARNED_SOMETHING; /* decided against all boxes */
|
|
}
|
|
} else { /* pick the lock in a door */
|
|
struct monst *mtmp;
|
|
|
|
if (u.utrap && u.utraptype == TT_PIT) {
|
|
You_cant("reach over the edge of the pit.");
|
|
return PICKLOCK_LEARNED_SOMETHING;
|
|
}
|
|
|
|
door = &levl[cc.x][cc.y];
|
|
mtmp = m_at(cc.x, cc.y);
|
|
if (mtmp && canseemon(mtmp) && mtmp->m_ap_type != M_AP_FURNITURE
|
|
&& mtmp->m_ap_type != M_AP_OBJECT) {
|
|
if (picktyp == CREDIT_CARD
|
|
&& (mtmp->isshk || mtmp->data == &mons[PM_ORACLE]))
|
|
verbalize("No checks, no credit, no problem.");
|
|
else
|
|
pline("I don't think %s would appreciate that.",
|
|
mon_nam(mtmp));
|
|
return PICKLOCK_LEARNED_SOMETHING;
|
|
} else if (mtmp && is_door_mappear(mtmp)) {
|
|
/* "The door actually was a <mimic>!" */
|
|
stumble_onto_mimic(mtmp);
|
|
/* mimic might keep the key (50% chance, 10% for PYEC) */
|
|
maybe_absorb_item(mtmp, pick, 50, 10);
|
|
return PICKLOCK_LEARNED_SOMETHING;
|
|
}
|
|
if (!IS_DOOR(door->typ)) {
|
|
if (is_drawbridge_wall(cc.x, cc.y) >= 0)
|
|
You("%s no lock on the drawbridge.", Blind ? "feel" : "see");
|
|
else
|
|
You("%s no door there.", Blind ? "feel" : "see");
|
|
return PICKLOCK_LEARNED_SOMETHING;
|
|
}
|
|
switch (door->doormask) {
|
|
case D_NODOOR:
|
|
pline("This doorway has no door.");
|
|
return PICKLOCK_LEARNED_SOMETHING;
|
|
case D_ISOPEN:
|
|
You("cannot lock an open door.");
|
|
return PICKLOCK_LEARNED_SOMETHING;
|
|
case D_BROKEN:
|
|
pline("This door is broken.");
|
|
return PICKLOCK_LEARNED_SOMETHING;
|
|
default:
|
|
/* credit cards are only good for unlocking */
|
|
if (picktyp == CREDIT_CARD && !(door->doormask & D_LOCKED)) {
|
|
You_cant("lock a door with a credit card.");
|
|
return PICKLOCK_LEARNED_SOMETHING;
|
|
}
|
|
|
|
Sprintf(qbuf, "%s it?",
|
|
(door->doormask & D_LOCKED) ? "Unlock" : "Lock");
|
|
|
|
c = yn(qbuf);
|
|
if (c == 'n')
|
|
return 0;
|
|
|
|
switch (picktyp) {
|
|
case CREDIT_CARD:
|
|
ch = 2 * ACURR(A_DEX) + 20 * Role_if(PM_ROGUE);
|
|
break;
|
|
case LOCK_PICK:
|
|
ch = 3 * ACURR(A_DEX) + 30 * Role_if(PM_ROGUE);
|
|
break;
|
|
case SKELETON_KEY:
|
|
ch = 70 + ACURR(A_DEX);
|
|
break;
|
|
default:
|
|
ch = 0;
|
|
}
|
|
xlock.door = door;
|
|
xlock.box = 0;
|
|
}
|
|
}
|
|
context.move = 0;
|
|
xlock.chance = ch;
|
|
xlock.picktyp = picktyp;
|
|
xlock.usedtime = 0;
|
|
set_occupation(picklock, lock_action(), 0);
|
|
return PICKLOCK_DID_SOMETHING;
|
|
}
|
|
|
|
/* try to force a chest with your weapon */
|
|
int
|
|
doforce()
|
|
{
|
|
register struct obj *otmp;
|
|
register int c, picktyp;
|
|
char qbuf[QBUFSZ];
|
|
|
|
if (u.uswallow) {
|
|
You_cant("force anything from inside here.");
|
|
return 0;
|
|
}
|
|
if (!uwep /* proper type test */
|
|
|| ((uwep->oclass == WEAPON_CLASS || is_weptool(uwep))
|
|
? (objects[uwep->otyp].oc_skill < P_DAGGER
|
|
|| objects[uwep->otyp].oc_skill == P_FLAIL
|
|
|| objects[uwep->otyp].oc_skill > P_LANCE)
|
|
: uwep->oclass != ROCK_CLASS)) {
|
|
You_cant("force anything %s weapon.",
|
|
!uwep ? "when not wielding a"
|
|
: (uwep->oclass != WEAPON_CLASS && !is_weptool(uwep))
|
|
? "without a proper"
|
|
: "with that");
|
|
return 0;
|
|
}
|
|
if (!can_reach_floor(TRUE)) {
|
|
cant_reach_floor(u.ux, u.uy, FALSE, TRUE);
|
|
return 0;
|
|
}
|
|
|
|
picktyp = is_blade(uwep) && !is_pick(uwep);
|
|
if (xlock.usedtime && xlock.box && picktyp == xlock.picktyp) {
|
|
You("resume your attempt to force the lock.");
|
|
set_occupation(forcelock, "forcing the lock", 0);
|
|
return 1;
|
|
}
|
|
|
|
/* A lock is made only for the honest man, the thief will break it. */
|
|
xlock.box = (struct obj *) 0;
|
|
for (otmp = level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere)
|
|
if (Is_box(otmp)) {
|
|
if (otmp->obroken || !otmp->olocked) {
|
|
There("is %s here, but its lock is already %s.", doname(otmp),
|
|
otmp->obroken ? "broken" : "unlocked");
|
|
otmp->lknown = 1;
|
|
continue;
|
|
}
|
|
(void) safe_qbuf(qbuf, "There is ", " here; force its lock?",
|
|
otmp, doname, ansimpleoname, "a box");
|
|
otmp->lknown = 1;
|
|
|
|
c = ynq(qbuf);
|
|
if (c == 'q')
|
|
return 0;
|
|
if (c == 'n')
|
|
continue;
|
|
|
|
if (picktyp)
|
|
You("force %s into a crack and pry.", yname(uwep));
|
|
else
|
|
You("start bashing it with %s.", yname(uwep));
|
|
xlock.box = otmp;
|
|
xlock.chance = objects[uwep->otyp].oc_wldam * 2;
|
|
xlock.picktyp = picktyp;
|
|
xlock.usedtime = 0;
|
|
break;
|
|
}
|
|
|
|
if (xlock.box)
|
|
set_occupation(forcelock, "forcing the lock", 0);
|
|
else
|
|
You("decide not to force the issue.");
|
|
return 1;
|
|
}
|
|
|
|
boolean
|
|
stumble_on_door_mimic(x, y)
|
|
int x, y;
|
|
{
|
|
struct monst *mtmp;
|
|
|
|
if ((mtmp = m_at(x, y)) && is_door_mappear(mtmp)
|
|
&& !Protection_from_shape_changers) {
|
|
stumble_onto_mimic(mtmp);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* the 'O' command - try to open a door */
|
|
int
|
|
doopen()
|
|
{
|
|
return doopen_indir(0, 0);
|
|
}
|
|
|
|
/* try to open a door in direction u.dx/u.dy */
|
|
int
|
|
doopen_indir(x, y)
|
|
int x, y;
|
|
{
|
|
coord cc;
|
|
register struct rm *door;
|
|
boolean portcullis;
|
|
int res = 0;
|
|
|
|
if (nohands(youmonst.data)) {
|
|
You_cant("open anything -- you have no hands!");
|
|
return 0;
|
|
}
|
|
|
|
if (u.utrap && u.utraptype == TT_PIT) {
|
|
You_cant("reach over the edge of the pit.");
|
|
return 0;
|
|
}
|
|
|
|
if (x > 0 && y > 0) {
|
|
cc.x = x;
|
|
cc.y = y;
|
|
} else if (!get_adjacent_loc((char *) 0, (char *) 0, u.ux, u.uy, &cc))
|
|
return 0;
|
|
|
|
if ((cc.x == u.ux) && (cc.y == u.uy))
|
|
return 0;
|
|
|
|
if (stumble_on_door_mimic(cc.x, cc.y))
|
|
return 1;
|
|
|
|
/* when choosing a direction is impaired, use a turn
|
|
regardless of whether a door is successfully targetted */
|
|
if (Confusion || Stunned)
|
|
res = 1;
|
|
|
|
door = &levl[cc.x][cc.y];
|
|
portcullis = (is_drawbridge_wall(cc.x, cc.y) >= 0);
|
|
if (Blind) {
|
|
int oldglyph = door->glyph;
|
|
schar oldlastseentyp = lastseentyp[cc.x][cc.y];
|
|
|
|
feel_location(cc.x, cc.y);
|
|
if (door->glyph != oldglyph
|
|
|| lastseentyp[cc.x][cc.y] != oldlastseentyp)
|
|
res = 1; /* learned something */
|
|
}
|
|
|
|
if (portcullis || !IS_DOOR(door->typ)) {
|
|
/* closed portcullis or spot that opened bridge would span */
|
|
if (is_db_wall(cc.x, cc.y) || door->typ == DRAWBRIDGE_UP)
|
|
There("is no obvious way to open the drawbridge.");
|
|
else if (portcullis || door->typ == DRAWBRIDGE_DOWN)
|
|
pline_The("drawbridge is already open.");
|
|
else if (container_at(cc.x, cc.y, TRUE))
|
|
pline("%s like something lootable over there.",
|
|
Blind ? "Feels" : "Seems");
|
|
else
|
|
You("%s no door there.", Blind ? "feel" : "see");
|
|
return res;
|
|
}
|
|
|
|
if (!(door->doormask & D_CLOSED)) {
|
|
const char *mesg;
|
|
|
|
switch (door->doormask) {
|
|
case D_BROKEN:
|
|
mesg = " is broken";
|
|
break;
|
|
case D_NODOOR:
|
|
mesg = "way has no door";
|
|
break;
|
|
case D_ISOPEN:
|
|
mesg = " is already open";
|
|
break;
|
|
default:
|
|
mesg = " is locked";
|
|
break;
|
|
}
|
|
pline("This door%s.", mesg);
|
|
return res;
|
|
}
|
|
|
|
if (verysmall(youmonst.data)) {
|
|
pline("You're too small to pull the door open.");
|
|
return res;
|
|
}
|
|
|
|
/* door is known to be CLOSED */
|
|
if (rnl(20) < (ACURRSTR + ACURR(A_DEX) + ACURR(A_CON)) / 3) {
|
|
pline_The("door opens.");
|
|
if (door->doormask & D_TRAPPED) {
|
|
b_trapped("door", FINGER);
|
|
door->doormask = D_NODOOR;
|
|
if (*in_rooms(cc.x, cc.y, SHOPBASE))
|
|
add_damage(cc.x, cc.y, 0L);
|
|
} else
|
|
door->doormask = D_ISOPEN;
|
|
feel_newsym(cc.x, cc.y); /* the hero knows she opened it */
|
|
unblock_point(cc.x, cc.y); /* vision: new see through there */
|
|
} else {
|
|
exercise(A_STR, TRUE);
|
|
pline_The("door resists!");
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
STATIC_OVL boolean
|
|
obstructed(x, y, quietly)
|
|
register int x, y;
|
|
boolean quietly;
|
|
{
|
|
register struct monst *mtmp = m_at(x, y);
|
|
|
|
if (mtmp && mtmp->m_ap_type != M_AP_FURNITURE) {
|
|
if (mtmp->m_ap_type == M_AP_OBJECT)
|
|
goto objhere;
|
|
if (!quietly) {
|
|
if ((mtmp->mx != x) || (mtmp->my != y)) {
|
|
/* worm tail */
|
|
pline("%s%s blocks the way!",
|
|
!canspotmon(mtmp) ? Something : s_suffix(Monnam(mtmp)),
|
|
!canspotmon(mtmp) ? "" : " tail");
|
|
} else {
|
|
pline("%s blocks the way!",
|
|
!canspotmon(mtmp) ? "Some creature" : Monnam(mtmp));
|
|
}
|
|
}
|
|
if (!canspotmon(mtmp))
|
|
map_invisible(x, y);
|
|
return TRUE;
|
|
}
|
|
if (OBJ_AT(x, y)) {
|
|
objhere:
|
|
if (!quietly)
|
|
pline("%s's in the way.", Something);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* the 'C' command - try to close a door */
|
|
int
|
|
doclose()
|
|
{
|
|
register int x, y;
|
|
register struct rm *door;
|
|
boolean portcullis;
|
|
int res = 0;
|
|
|
|
if (nohands(youmonst.data)) {
|
|
You_cant("close anything -- you have no hands!");
|
|
return 0;
|
|
}
|
|
|
|
if (u.utrap && u.utraptype == TT_PIT) {
|
|
You_cant("reach over the edge of the pit.");
|
|
return 0;
|
|
}
|
|
|
|
if (!getdir((char *) 0))
|
|
return 0;
|
|
|
|
x = u.ux + u.dx;
|
|
y = u.uy + u.dy;
|
|
if ((x == u.ux) && (y == u.uy)) {
|
|
You("are in the way!");
|
|
return 1;
|
|
}
|
|
|
|
if (!isok(x, y))
|
|
goto nodoor;
|
|
|
|
if (stumble_on_door_mimic(x, y))
|
|
return 1;
|
|
|
|
/* when choosing a direction is impaired, use a turn
|
|
regardless of whether a door is successfully targetted */
|
|
if (Confusion || Stunned)
|
|
res = 1;
|
|
|
|
door = &levl[x][y];
|
|
portcullis = (is_drawbridge_wall(x, y) >= 0);
|
|
if (Blind) {
|
|
int oldglyph = door->glyph;
|
|
schar oldlastseentyp = lastseentyp[x][y];
|
|
|
|
feel_location(x, y);
|
|
if (door->glyph != oldglyph || lastseentyp[x][y] != oldlastseentyp)
|
|
res = 1; /* learned something */
|
|
}
|
|
|
|
if (portcullis || !IS_DOOR(door->typ)) {
|
|
/* is_db_wall: closed portcullis */
|
|
if (is_db_wall(x, y) || door->typ == DRAWBRIDGE_UP)
|
|
pline_The("drawbridge is already closed.");
|
|
else if (portcullis || door->typ == DRAWBRIDGE_DOWN)
|
|
There("is no obvious way to close the drawbridge.");
|
|
else {
|
|
nodoor:
|
|
You("%s no door there.", Blind ? "feel" : "see");
|
|
}
|
|
return res;
|
|
}
|
|
|
|
if (door->doormask == D_NODOOR) {
|
|
pline("This doorway has no door.");
|
|
return res;
|
|
} else if (obstructed(x, y, FALSE)) {
|
|
return res;
|
|
} else if (door->doormask == D_BROKEN) {
|
|
pline("This door is broken.");
|
|
return res;
|
|
} else if (door->doormask & (D_CLOSED | D_LOCKED)) {
|
|
pline("This door is already closed.");
|
|
return res;
|
|
}
|
|
|
|
if (door->doormask == D_ISOPEN) {
|
|
if (verysmall(youmonst.data) && !u.usteed) {
|
|
pline("You're too small to push the door closed.");
|
|
return res;
|
|
}
|
|
if (u.usteed
|
|
|| rn2(25) < (ACURRSTR + ACURR(A_DEX) + ACURR(A_CON)) / 3) {
|
|
pline_The("door closes.");
|
|
door->doormask = D_CLOSED;
|
|
feel_newsym(x, y); /* the hero knows she closed it */
|
|
block_point(x, y); /* vision: no longer see there */
|
|
} else {
|
|
exercise(A_STR, TRUE);
|
|
pline_The("door resists!");
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* box obj was hit with spell or wand effect otmp;
|
|
returns true if something happened */
|
|
boolean
|
|
boxlock(obj, otmp)
|
|
struct obj *obj, *otmp; /* obj *is* a box */
|
|
{
|
|
boolean res = 0;
|
|
|
|
switch (otmp->otyp) {
|
|
case WAN_LOCKING:
|
|
case SPE_WIZARD_LOCK:
|
|
if (!obj->olocked) { /* lock it; fix if broken */
|
|
pline("Klunk!");
|
|
obj->olocked = 1;
|
|
obj->obroken = 0;
|
|
if (Role_if(PM_WIZARD))
|
|
obj->lknown = 1;
|
|
else
|
|
obj->lknown = 0;
|
|
res = 1;
|
|
} /* else already closed and locked */
|
|
break;
|
|
case WAN_OPENING:
|
|
case SPE_KNOCK:
|
|
if (obj->olocked) { /* unlock; couldn't be broken */
|
|
pline("Klick!");
|
|
obj->olocked = 0;
|
|
res = 1;
|
|
if (Role_if(PM_WIZARD))
|
|
obj->lknown = 1;
|
|
else
|
|
obj->lknown = 0;
|
|
} else /* silently fix if broken */
|
|
obj->obroken = 0;
|
|
break;
|
|
case WAN_POLYMORPH:
|
|
case SPE_POLYMORPH:
|
|
/* maybe start unlocking chest, get interrupted, then zap it;
|
|
we must avoid any attempt to resume unlocking it */
|
|
if (xlock.box == obj)
|
|
reset_pick();
|
|
break;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* Door/secret door was hit with spell or wand effect otmp;
|
|
returns true if something happened */
|
|
boolean
|
|
doorlock(otmp, x, y)
|
|
struct obj *otmp;
|
|
int x, y;
|
|
{
|
|
register struct rm *door = &levl[x][y];
|
|
boolean res = TRUE;
|
|
int loudness = 0;
|
|
const char *msg = (const char *) 0;
|
|
const char *dustcloud = "A cloud of dust";
|
|
const char *quickly_dissipates = "quickly dissipates";
|
|
boolean mysterywand = (otmp->oclass == WAND_CLASS && !otmp->dknown);
|
|
|
|
if (door->typ == SDOOR) {
|
|
switch (otmp->otyp) {
|
|
case WAN_OPENING:
|
|
case SPE_KNOCK:
|
|
case WAN_STRIKING:
|
|
case SPE_FORCE_BOLT:
|
|
door->typ = DOOR;
|
|
door->doormask = D_CLOSED | (door->doormask & D_TRAPPED);
|
|
newsym(x, y);
|
|
if (cansee(x, y))
|
|
pline("A door appears in the wall!");
|
|
if (otmp->otyp == WAN_OPENING || otmp->otyp == SPE_KNOCK)
|
|
return TRUE;
|
|
break; /* striking: continue door handling below */
|
|
case WAN_LOCKING:
|
|
case SPE_WIZARD_LOCK:
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
switch (otmp->otyp) {
|
|
case WAN_LOCKING:
|
|
case SPE_WIZARD_LOCK:
|
|
if (Is_rogue_level(&u.uz)) {
|
|
boolean vis = cansee(x, y);
|
|
/* Can't have real locking in Rogue, so just hide doorway */
|
|
if (vis)
|
|
pline("%s springs up in the older, more primitive doorway.",
|
|
dustcloud);
|
|
else
|
|
You_hear("a swoosh.");
|
|
if (obstructed(x, y, mysterywand)) {
|
|
if (vis)
|
|
pline_The("cloud %s.", quickly_dissipates);
|
|
return FALSE;
|
|
}
|
|
block_point(x, y);
|
|
door->typ = SDOOR;
|
|
if (vis)
|
|
pline_The("doorway vanishes!");
|
|
newsym(x, y);
|
|
return TRUE;
|
|
}
|
|
if (obstructed(x, y, mysterywand))
|
|
return FALSE;
|
|
/* Don't allow doors to close over traps. This is for pits */
|
|
/* & trap doors, but is it ever OK for anything else? */
|
|
if (t_at(x, y)) {
|
|
/* maketrap() clears doormask, so it should be NODOOR */
|
|
pline("%s springs up in the doorway, but %s.", dustcloud,
|
|
quickly_dissipates);
|
|
return FALSE;
|
|
}
|
|
|
|
switch (door->doormask & ~D_TRAPPED) {
|
|
case D_CLOSED:
|
|
msg = "The door locks!";
|
|
break;
|
|
case D_ISOPEN:
|
|
msg = "The door swings shut, and locks!";
|
|
break;
|
|
case D_BROKEN:
|
|
msg = "The broken door reassembles and locks!";
|
|
break;
|
|
case D_NODOOR:
|
|
msg =
|
|
"A cloud of dust springs up and assembles itself into a door!";
|
|
break;
|
|
default:
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
block_point(x, y);
|
|
door->doormask = D_LOCKED | (door->doormask & D_TRAPPED);
|
|
newsym(x, y);
|
|
break;
|
|
case WAN_OPENING:
|
|
case SPE_KNOCK:
|
|
if (door->doormask & D_LOCKED) {
|
|
msg = "The door unlocks!";
|
|
door->doormask = D_CLOSED | (door->doormask & D_TRAPPED);
|
|
} else
|
|
res = FALSE;
|
|
break;
|
|
case WAN_STRIKING:
|
|
case SPE_FORCE_BOLT:
|
|
if (door->doormask & (D_LOCKED | D_CLOSED)) {
|
|
if (door->doormask & D_TRAPPED) {
|
|
if (MON_AT(x, y))
|
|
(void) mb_trapped(m_at(x, y));
|
|
else if (flags.verbose) {
|
|
if (cansee(x, y))
|
|
pline("KABOOM!! You see a door explode.");
|
|
else
|
|
You_hear("a distant explosion.");
|
|
}
|
|
door->doormask = D_NODOOR;
|
|
unblock_point(x, y);
|
|
newsym(x, y);
|
|
loudness = 40;
|
|
break;
|
|
}
|
|
door->doormask = D_BROKEN;
|
|
if (flags.verbose) {
|
|
if (cansee(x, y))
|
|
pline_The("door crashes open!");
|
|
else
|
|
You_hear("a crashing sound.");
|
|
}
|
|
unblock_point(x, y);
|
|
newsym(x, y);
|
|
/* force vision recalc before printing more messages */
|
|
if (vision_full_recalc)
|
|
vision_recalc(0);
|
|
loudness = 20;
|
|
} else
|
|
res = FALSE;
|
|
break;
|
|
default:
|
|
impossible("magic (%d) attempted on door.", otmp->otyp);
|
|
break;
|
|
}
|
|
if (msg && cansee(x, y))
|
|
pline1(msg);
|
|
if (loudness > 0) {
|
|
/* door was destroyed */
|
|
wake_nearto(x, y, loudness);
|
|
if (*in_rooms(x, y, SHOPBASE))
|
|
add_damage(x, y, 0L);
|
|
}
|
|
|
|
if (res && picking_at(x, y)) {
|
|
/* maybe unseen monster zaps door you're unlocking */
|
|
stop_occupation();
|
|
reset_pick();
|
|
}
|
|
return res;
|
|
}
|
|
|
|
STATIC_OVL void
|
|
chest_shatter_msg(otmp)
|
|
struct obj *otmp;
|
|
{
|
|
const char *disposition;
|
|
const char *thing;
|
|
long save_Blinded;
|
|
|
|
if (otmp->oclass == POTION_CLASS) {
|
|
You("%s %s shatter!", Blind ? "hear" : "see", an(bottlename()));
|
|
if (!breathless(youmonst.data) || haseyes(youmonst.data))
|
|
potionbreathe(otmp);
|
|
return;
|
|
}
|
|
/* We have functions for distant and singular names, but not one */
|
|
/* which does _both_... */
|
|
save_Blinded = Blinded;
|
|
Blinded = 1;
|
|
thing = singular(otmp, xname);
|
|
Blinded = save_Blinded;
|
|
switch (objects[otmp->otyp].oc_material) {
|
|
case PAPER:
|
|
disposition = "is torn to shreds";
|
|
break;
|
|
case WAX:
|
|
disposition = "is crushed";
|
|
break;
|
|
case VEGGY:
|
|
disposition = "is pulped";
|
|
break;
|
|
case FLESH:
|
|
disposition = "is mashed";
|
|
break;
|
|
case GLASS:
|
|
disposition = "shatters";
|
|
break;
|
|
case WOOD:
|
|
disposition = "splinters to fragments";
|
|
break;
|
|
default:
|
|
disposition = "is destroyed";
|
|
break;
|
|
}
|
|
pline("%s %s!", An(thing), disposition);
|
|
}
|
|
|
|
/*lock.c*/
|