autounlock:untrap

Implement 'untrap' as an 'autounlock' action.  Quite a bit more work
than anticipated.  The new documentation is rather clumsy; too many
if-this and if-not-that clauses have intruded.

I'll be astonished if all the return values are correct....

[A couple of places were checking for (rx != 0 && ry != 0) to decide
whether they were performating an autounlock action at <rx,ry> but
that erroneously excludes the top line of the map if the current
level extends that far.  Just check rx for zero/non-zero.]
This commit is contained in:
PatR
2022-05-06 14:44:57 -07:00
parent a31dd26d8a
commit 02207b967a
10 changed files with 268 additions and 126 deletions
+11 -5
View File
@@ -148,12 +148,18 @@ Compound options are written as option_name:option_value.
Compound options which can be set during the game are: Compound options which can be set during the game are:
autounlock when attempting to open a door or loot a [Apply-Key] autounlock when attempting to open a locked door or loot [Apply-Key]
container that is locked, specifies an action to take: a locked container, specifies an action to take: can be
can be None, or one or more of Apply-Key + Kick + Force; None, or one or more of Untrap + Apply-Key + Kick + Force;
if Untrap is included, it will be handled first; the
others apply if you answer "no" to "check for traps?";
for "yes", it won't necessary find anything even when a
trap happens to be present and will use up the rest of
the current move regardless of whether it finds anything;
Kick is only useful for doors and Force is only useful for Kick is only useful for doors and Force is only useful for
containers; either will only be attempted if Apply-Key is containers; either will only be attempted if Untrap is
omitted or you aren't carrying any unlocking tool omitted or skipped and Apply-Key is omitted or you aren't
carrying an unlocking tool or you decline to use one
boulder override the default boulder symbol [`] boulder override the default boulder symbol [`]
disclose the types of information you want [ni na nv ng nc no] disclose the types of information you want [ni na nv ng nc no]
offered at the end of the game offered at the end of the game
+20 -9
View File
@@ -3662,15 +3662,26 @@ Persistent.
Controls what action to take when attempting to walk into a locked door Controls what action to take when attempting to walk into a locked door
or to loot a locked container. or to loot a locked container.
Takes a plus-sign separated list of values: Takes a plus-sign separated list of values:
\fIapply-key\fP which will attempt to use a key or other unlocking tool .PS Apply-Key
if you have one; .PL Untrap
\fIkick\fP which will kick the door (if you lack a key or omit apply-key; prompt about whether to attempt to find a trap;
it might fail to find one even when present; if it does find one, it
will ask whether you want to try to disarm the trap; if you decline,
your character will forget that the door or box is trapped;
.PL Apply-Key
if carrying a key or other unlocking tool, prompt about using it;
.PL Kick
kick the door (if you omit untrap or decline to attempt untrap and
you omit apply-key or you lack a key or you decline to use the key;
has no effect on containers); has no effect on containers);
\fIforce\fP which will try to force a container's lid with your currently .PL Force
wielded weapon (if you lack a key or omit apply-key; has no effect on try to force a container's lid with your currently
doors); or wielded weapon (if you omit untrap or decline to attempt untrap and
\fInone\fP which can't be combined with the other choices. you omit apply-key or you lack a key or you decline to use the key;
.lp "" has no effect on doors);
.PL None
none of the above; can't be combined with the other choices.
.PE
Omitting the value is treated as if \f(CRautounlock:apply-key\fP. Omitting the value is treated as if \f(CRautounlock:apply-key\fP.
Preceding \f(CRautounlock\fP with \(oq!\(cq or \(lqno\(rq is treated as Preceding \f(CRautounlock\fP with \(oq!\(cq or \(lqno\(rq is treated as
\f(CRautounlock:none\fP. \f(CRautounlock:none\fP.
@@ -3680,7 +3691,7 @@ Successfully kicking a door will break it and wake up nearby monsters.
Successfully forcing a container open will break its lock and might also Successfully forcing a container open will break its lock and might also
destroy some of its contents or damage your weapon or both. destroy some of its contents or damage your weapon or both.
.lp "" .lp ""
The default is \fIapply-key\fP. The default is Apply-Key.
Persistent. Persistent.
.lp blind .lp blind
Start the character permanently blind (default false). Start the character permanently blind (default false).
+33 -10
View File
@@ -3970,19 +3970,42 @@ If no weapon is found or the option is
false, the `t' (throw) command is executed instead. Persistent. false, the `t' (throw) command is executed instead. Persistent.
%.lp %.lp
\item[\ib{autounlock}] \item[\ib{autounlock}]
%\hyphenation{apply\-key}%this needs to be tested...
Controls what action to take when attempting to walk into a locked door Controls what action to take when attempting to walk into a locked door
or to loot a locked container. or to loot a locked container.
Takes a plus-sign separated list of values: Takes a plus-sign separated list of values:
{\it apply-key\/} which will attempt to use a key or other unlocking tool % paranoid
if you have one; % au => autounlock
{\it kick\/} which will kick the door (if you lack a key or omit apply-key; \newlength{\auwidth}
%.PS Apply-Key
\settowidth{\auwidth}{\tt Apply-Key}
\addtolength{\auwidth}{\labelsep}
\blist{\leftmargin \auwidth \topsep 1mm \itemsep 0mm}
%.PL Untrap
\item[{\tt Untrap}]
prompt about whether to attempt to find a trap;
it might fail to find one even when present; if it does find one, it
will ask whether you want to try to disarm the trap; if you decline,
your character will forget that the door or box is trapped;
%.PL Apply-Key
\item[{\tt Apply-Key}]
if carrying a key or other unlocking tool, prompt about using it;
%.PL Kick
\item[{\tt Kick}]
kick the door (if you omit untrap or decline to attempt untrap and
you omit apply-key or you lack a key or you decline to use the key;
has no effect on containers); has no effect on containers);
{\it force\/} which will try to force a container's lid with your currently %.PL Force
wielded weapon (if you lack a key or omit apply-key; has no effect on \item[{\tt Force}]
doors); or try to force a container's lid with your currently
{\it none\/} which can't be combined with the other choices. wielded weapon (if you omit untrap or decline to attempt untrap and
\\ you omit apply-key or you lack a key or you decline to use the key;
%.lp "" has no effect on doors);
%.PL None
\item[{\tt None}]
none of the above; can't be combined with the other choices.
%.PE
\elist
Omitting the value is treated as if {\tt autounlock:apply-key}. Omitting the value is treated as if {\tt autounlock:apply-key}.
Preceding {\tt autounlock} with `{\tt !}' or ``{\tt no}'' is treated as Preceding {\tt autounlock} with `{\tt !}' or ``{\tt no}'' is treated as
{\tt autounlock:none}. {\tt autounlock:none}.
@@ -3994,7 +4017,7 @@ Successfully forcing a container open will break its lock and might also
destroy some of its contents or damage your weapon or both. destroy some of its contents or damage your weapon or both.
\\ \\
%.lp "" %.lp ""
The default is {\it apply-key\/}. The default is Apply-Key.
Persistent. Persistent.
%.lp %.lp
\item[\ib{blind}] \item[\ib{blind}]
+1
View File
@@ -1197,6 +1197,7 @@ using #wizmakemap on Plane of Water added a new set of air bubbles each time
it was run and eventually replaced just about all the water; likewise it was run and eventually replaced just about all the water; likewise
with clouds on Plane of Air with clouds on Plane of Air
avoid new "where are we?" panic if player quits during character selection avoid new "where are we?" panic if player quits during character selection
add Untrap as a potential 'autounlock' action
curses: 'msg_window' option wasn't functional for curses unless the binary curses: 'msg_window' option wasn't functional for curses unless the binary
also included tty support also included tty support
+2 -1
View File
@@ -2750,8 +2750,9 @@ extern void water_damage_chain(struct obj *, boolean);
extern boolean drown(void); extern boolean drown(void);
extern void drain_en(int); extern void drain_en(int);
extern int dountrap(void); extern int dountrap(void);
extern int could_untrap(boolean, boolean);
extern void cnv_trap_obj(int, int, struct trap *, boolean); extern void cnv_trap_obj(int, int, struct trap *, boolean);
extern int untrap(boolean); extern int untrap(boolean, int, int, struct obj *);
extern boolean openholdingtrap(struct monst *, boolean *); extern boolean openholdingtrap(struct monst *, boolean *);
extern boolean closeholdingtrap(struct monst *, boolean *); extern boolean closeholdingtrap(struct monst *, boolean *);
extern boolean openfallingtrap(struct monst *, boolean, boolean *); extern boolean openfallingtrap(struct monst *, boolean, boolean *);
+1 -1
View File
@@ -1720,7 +1720,7 @@ arti_invoke(struct obj *obj)
break; break;
} }
case UNTRAP: { case UNTRAP: {
if (!untrap(TRUE)) { if (!untrap(TRUE, 0, 0, (struct obj *) 0)) {
obj->age = 0; /* don't charge for changing their mind */ obj->age = 0; /* don't charge for changing their mind */
return ECMD_OK; return ECMD_OK;
} }
+42 -16
View File
@@ -356,14 +356,19 @@ pick_lock(
* does not prompt for direction if these are set */ * does not prompt for direction if these are set */
struct obj *container) /* container, for autounlock */ struct obj *container) /* container, for autounlock */
{ {
struct obj dummypick;
int picktyp, c, ch; int picktyp, c, ch;
coord cc; coord cc;
struct rm *door; struct rm *door;
struct obj *otmp; struct obj *otmp;
char qbuf[QBUFSZ]; char qbuf[QBUFSZ];
boolean autounlock = (((rx != 0 && ry != 0) || container != NULL) boolean autounlock = (rx != 0 || container != NULL);
&& (flags.autounlock & AUTOUNLOCK_APPLY_KEY) != 0);
/* 'pick' might be Null [called by do_loot_cont() for AUTOUNLOCK_UNTRAP] */
if (!pick) {
dummypick = cg.zeroobj;
pick = &dummypick; /* pick->otyp will be STRANGE_OBJECT */
}
picktyp = pick->otyp; picktyp = pick->otyp;
/* check whether we're resuming an interrupted previous attempt */ /* check whether we're resuming an interrupted previous attempt */
@@ -401,15 +406,14 @@ pick_lock(
return PICKLOCK_DID_NOTHING; return PICKLOCK_DID_NOTHING;
} }
if (picktyp != LOCK_PICK if (pick != &dummypick && picktyp != SKELETON_KEY
&& picktyp != CREDIT_CARD && picktyp != LOCK_PICK && picktyp != CREDIT_CARD) {
&& picktyp != SKELETON_KEY) {
impossible("picking lock with object %d?", picktyp); impossible("picking lock with object %d?", picktyp);
return PICKLOCK_DID_NOTHING; return PICKLOCK_DID_NOTHING;
} }
ch = 0; /* lint suppression */ ch = 0; /* lint suppression */
if (rx != 0 && ry != 0) { /* autounlock; caller has provided coordinates */ if (rx != 0) { /* autounlock; caller has provided coordinates */
cc.x = rx; cc.x = rx;
cc.y = ry; cc.y = ry;
} else if (!get_adjacent_loc((char *) 0, "Invalid location!", } else if (!get_adjacent_loc((char *) 0, "Invalid location!",
@@ -458,11 +462,25 @@ pick_lock(
else else
verb = "pick"; verb = "pick";
if (autounlock) { if (autounlock && (flags.autounlock & AUTOUNLOCK_UNTRAP) != 0
Sprintf(qbuf, "Unlock it with %s?", yname(pick)); && could_untrap(FALSE, TRUE)
c = yn(qbuf); && (c = ynq(safe_qbuf(qbuf, "Check ", " for a trap?",
if (c == 'n') otmp, yname, ysimple_name, "this")))
return 0; != 'n') {
if (c == 'q')
return PICKLOCK_DID_NOTHING; /* c == 'q' */
/* c == 'y' */
untrap(FALSE, 0, 0, otmp);
return PICKLOCK_DID_SOMETHING; /* even if no trap found */
} else if (autounlock
&& (flags.autounlock & AUTOUNLOCK_APPLY_KEY) != 0) {
c = 'q';
if (pick != &dummypick) {
Sprintf(qbuf, "Unlock it with %s?", yname(pick));
c = ynq(qbuf);
}
if (c != 'y')
return PICKLOCK_DID_NOTHING;
} else { } else {
/* "There is <a box> here; <verb> <it|its lock>?" */ /* "There is <a box> here; <verb> <it|its lock>?" */
Sprintf(qsfx, " here; %s %s?", Sprintf(qsfx, " here; %s %s?",
@@ -473,9 +491,9 @@ pick_lock(
c = ynq(qbuf); c = ynq(qbuf);
if (c == 'q') if (c == 'q')
return 0; return PICKLOCK_DID_NOTHING;
if (c == 'n') if (c == 'n')
continue; continue; /* try next box */
} }
if (otmp->obroken) { if (otmp->obroken) {
@@ -561,6 +579,15 @@ pick_lock(
pline("This door is broken."); pline("This door is broken.");
return PICKLOCK_LEARNED_SOMETHING; return PICKLOCK_LEARNED_SOMETHING;
default: default:
if ((flags.autounlock & AUTOUNLOCK_UNTRAP) != 0
&& could_untrap(FALSE, FALSE)
&& (c = ynq("Check this door for a trap?")) != 'n') {
if (c == 'q')
return PICKLOCK_DID_NOTHING;
/* c == 'y' */
untrap(FALSE, cc.x, cc.y, (struct obj *) 0);
return PICKLOCK_DID_SOMETHING; /* even if no trap found */
}
/* credit cards are only good for unlocking */ /* credit cards are only good for unlocking */
if (picktyp == CREDIT_CARD && !(door->doormask & D_LOCKED)) { if (picktyp == CREDIT_CARD && !(door->doormask & D_LOCKED)) {
You_cant("lock a door with a credit card."); You_cant("lock a door with a credit card.");
@@ -571,9 +598,8 @@ pick_lock(
(door->doormask & D_LOCKED) ? "Unlock" : "Lock", (door->doormask & D_LOCKED) ? "Unlock" : "Lock",
autounlock ? " with " : "", autounlock ? " with " : "",
autounlock ? yname(pick) : ""); autounlock ? yname(pick) : "");
c = ynq(qbuf);
c = yn(qbuf); if (c != 'y')
if (c == 'n')
return 0; return 0;
/* note: for !autounlock, 'apply' already did touch check */ /* note: for !autounlock, 'apply' already did touch check */
-2
View File
@@ -4534,8 +4534,6 @@ handler_autounlock(int optidx)
start_menu(tmpwin, MENU_BEHAVE_STANDARD); start_menu(tmpwin, MENU_BEHAVE_STANDARD);
any = cg.zeroany; any = cg.zeroany;
for (i = 0; i < SIZE(unlocktypes); ++i) { for (i = 0; i < SIZE(unlocktypes); ++i) {
if (i == 1) /*** suppress 'untrap' from the menu... ***/
continue; /*** until it actually gets implemented ***/
Sprintf(buf, "%-10.10s%c%.40s", Sprintf(buf, "%-10.10s%c%.40s",
unlocktypes[i][0], sep, unlocktypes[i][1]); unlocktypes[i][0], sep, unlocktypes[i][1]);
presel = !i ? !flags.autounlock : (flags.autounlock & (1 << (i - 1))); presel = !i ? !flags.autounlock : (flags.autounlock & (1 << (i - 1)));
+34 -13
View File
@@ -1863,43 +1863,64 @@ mon_beside(int x, int y)
} }
static int static int
do_loot_cont(struct obj **cobjp, do_loot_cont(
int cindex, /* index of this container (1..N)... */ struct obj **cobjp,
int ccount) /* ...number of them (N) */ int cindex, /* index of this container (1..N)... */
int ccount) /* ...number of them (N) */
{ {
struct obj *cobj = *cobjp; struct obj *cobj = *cobjp;
if (!cobj) if (!cobj)
return ECMD_OK; return ECMD_OK;
if (cobj->olocked) { if (cobj->olocked) {
int res = ECMD_OK;
#if 0
if (ccount < 2 && (g.level.objects[cobj->ox][cobj->oy] == cobj)) if (ccount < 2 && (g.level.objects[cobj->ox][cobj->oy] == cobj))
pline("%s locked.", pline("%s locked.",
cobj->lknown ? "It is" : "Hmmm, it turns out to be"); cobj->lknown ? "It is" : "Hmmm, it turns out to be");
else if (cobj->lknown) else
#endif
if (cobj->lknown)
pline("%s is locked.", The(xname(cobj))); pline("%s is locked.", The(xname(cobj)));
else else
pline("Hmmm, %s turns out to be locked.", the(xname(cobj))); pline("Hmmm, %s turns out to be locked.", the(xname(cobj)));
cobj->lknown = 1; cobj->lknown = 1;
if (flags.autounlock) { if (flags.autounlock) {
struct obj *unlocktool; struct obj *otmp, *unlocktool = 0;
xchar ox = cobj->ox, oy = cobj->oy;
/* TODO: handle AUTOUNLOCK_UNTRAP and maybe add kicking at
self when chest present to handle AUTOUNLOCK_KICK */
u.dz = 0; /* might be non-zero from previous command since u.dz = 0; /* might be non-zero from previous command since
* #loot isn't a move command; pick_lock() cares */ * #loot isn't a move command; pick_lock() cares */
if ((flags.autounlock & AUTOUNLOCK_APPLY_KEY) != 0 /* if both the untrap and apply_key bits are set, untrap
&& (unlocktool = autokey(TRUE)) != 0) { attempt will be performed first but we need to set up
unlocktool in case "check for trap?" is declined */
if (((flags.autounlock & AUTOUNLOCK_APPLY_KEY) != 0
&& (unlocktool = autokey(TRUE)) != 0)
|| (flags.autounlock & AUTOUNLOCK_UNTRAP) != 0) {
/* pass ox and oy to avoid direction prompt */ /* pass ox and oy to avoid direction prompt */
return (pick_lock(unlocktool, cobj->ox, cobj->oy, cobj) != 0); if (pick_lock(unlocktool, ox, oy, cobj))
} else if ((flags.autounlock & AUTOUNLOCK_FORCE) != 0 res = ECMD_TIME;
&& ccount == 1 && u_have_forceable_weapon()) { /* attempting to untrap or unlock might trigger a trap
which destroys 'cobj'; inform caller if that happens */
for (otmp = g.level.objects[ox][oy]; otmp;
otmp = otmp->nexthere)
if (otmp == cobj)
break;
if (!otmp)
*cobjp = (struct obj *) 0;
return res;
}
if ((flags.autounlock & AUTOUNLOCK_FORCE) != 0
&& res != ECMD_TIME
&& ccount == 1 && u_have_forceable_weapon()) {
/* single container, and we could #force it open... */ /* single container, and we could #force it open... */
cmdq_add_ec(doforce); /* doforce asks for confirmation */ cmdq_add_ec(doforce); /* doforce asks for confirmation */
g.abort_looting = TRUE; g.abort_looting = TRUE;
} }
} }
return ECMD_OK; return res;
} }
cobj->lknown = 1; /* floor container, so no need for update_inventory() */ cobj->lknown = 1; /* floor container, so no need for update_inventory() */
+124 -69
View File
@@ -61,6 +61,7 @@ static void clear_conjoined_pits(struct trap *);
static boolean adj_nonconjoined_pit(struct trap *); static boolean adj_nonconjoined_pit(struct trap *);
static int try_lift(struct monst *, struct trap *, int, boolean); static int try_lift(struct monst *, struct trap *, int, boolean);
static int help_monster_out(struct monst *, struct trap *); static int help_monster_out(struct monst *, struct trap *);
static void untrap_box(struct obj *, boolean, boolean);
#if 0 #if 0
static void join_adjacent_pits(struct trap *); static void join_adjacent_pits(struct trap *);
#endif #endif
@@ -4495,23 +4496,40 @@ drain_en(int n)
int int
dountrap(void) dountrap(void)
{ {
if (!could_untrap(TRUE, FALSE))
return ECMD_OK;
return untrap(FALSE, 0, 0, (struct obj *) 0) ? ECMD_TIME : ECMD_OK;
}
/* the #untrap command - disarm a trap */
int
could_untrap(boolean verbosely, boolean check_floor)
{
char buf[BUFSZ];
buf[0] = '\0';
if (near_capacity() >= HVY_ENCUMBER) { if (near_capacity() >= HVY_ENCUMBER) {
pline("You're too strained to do that."); Strcpy(buf, "You're too strained to do that.");
return ECMD_OK; } else if ((nohands(g.youmonst.data) && !webmaker(g.youmonst.data))
} || !g.youmonst.data->mmove) {
if ((nohands(g.youmonst.data) && !webmaker(g.youmonst.data)) Strcpy(buf, "And just how do you expect to do that?");
|| !g.youmonst.data->mmove) {
pline("And just how do you expect to do that?");
return ECMD_OK;
} else if (u.ustuck && sticks(g.youmonst.data)) { } else if (u.ustuck && sticks(g.youmonst.data)) {
pline("You'll have to let go of %s first.", mon_nam(u.ustuck)); Sprintf(buf, "You'll have to let go of %s first.", mon_nam(u.ustuck));
return ECMD_OK; } else if (u.ustuck || (welded(uwep) && bimanual(uwep))) {
Sprintf(buf, "Your %s seem to be too busy for that.",
makeplural(body_part(HAND)));
} else if (check_floor && !can_reach_floor(FALSE)) {
/* only checked here for autounlock of chest/box and that will
be !verbosely so precise details of the message don't matter */
Sprintf(buf, "You can't reach the %s.", surface(u.ux, u.uy));
} }
if (u.ustuck || (welded(uwep) && bimanual(uwep))) { if (buf[0]) {
Your("%s seem to be too busy for that.", makeplural(body_part(HAND))); if (verbosely)
return ECMD_OK; pline("%s", buf);
return 0;
} }
return untrap(FALSE) ? ECMD_TIME : ECMD_OK; return 1;
} }
/* Probability of disabling a trap. Helge Hafting */ /* Probability of disabling a trap. Helge Hafting */
@@ -4964,8 +4982,50 @@ help_monster_out(
return 1; return 1;
} }
/* check a particular container for a trap and optionally disarm it */
static void
untrap_box(
struct obj *box,
boolean force,
boolean confused)
{
if ((box->otrapped
&& (force || (!confused && rn2(MAXULEV + 1 - u.ulevel) < 10)))
|| (!force && confused && !rn2(3))) {
You("find a trap on %s!", the(xname(box)));
if (!confused)
exercise(A_WIS, TRUE);
if (ynq("Disarm it?") == 'y') {
if (box->otrapped) {
int ch = ACURR(A_DEX) + u.ulevel;
if (Role_if(PM_ROGUE))
ch *= 2;
if (!force && (confused || Fumbling
|| rnd(75 + level_difficulty() / 2) > ch)) {
(void) chest_trap(box, FINGER, TRUE);
/* 'box' might be gone now */
} else {
You("disarm it!");
box->otrapped = 0;
}
exercise(A_DEX, TRUE);
} else {
pline("That %s was not trapped.", xname(box));
}
}
} else {
You("find no traps on %s.", the(xname(box)));
}
}
/* hero is able to attempt untrap, so do so */
int int
untrap(boolean force) untrap(
boolean force,
int rx, int ry,
struct obj *container)
{ {
register struct obj *otmp; register struct obj *otmp;
register int x, y; register int x, y;
@@ -4975,22 +5035,35 @@ untrap(boolean force)
const char *trapdescr; const char *trapdescr;
boolean here, useplural, deal_with_floor_trap, boolean here, useplural, deal_with_floor_trap,
confused = (Confusion || Hallucination), confused = (Confusion || Hallucination),
trap_skipped = FALSE; trap_skipped = FALSE, autounlock_door = FALSE;
int boxcnt = 0; int boxcnt = 0;
char the_trap[BUFSZ], qbuf[QBUFSZ]; char the_trap[BUFSZ], qbuf[QBUFSZ];
if (!getdir((char *) 0)) /* 'force' is true for #invoke; if carrying MKoT, make it be true
return 0; for #untrap or autounlock */
x = u.ux + u.dx; if (!force && has_magic_key(&g.youmonst))
y = u.uy + u.dy; force = TRUE;
if (!rx && !container) {
/* usual case */
if (!getdir((char *) 0))
return 0;
x = u.ux + u.dx;
y = u.uy + u.dy;
} else {
/* autounlock's untrap; skip most prompting */
if (container) {
untrap_box(container, force, confused);
return 1;
}
/* levl[rx][ry] is a locked or trapped door */
x = rx, y = ry;
autounlock_door = TRUE;
}
if (!isok(x, y)) { if (!isok(x, y)) {
pline_The("perils lurking there are beyond your grasp."); pline_The("perils lurking there are beyond your grasp.");
return 0; return 0;
} }
/* 'force' is true for #invoke; make it be true for #untrap if
carrying MKoT */
if (!force && has_magic_key(&g.youmonst))
force = TRUE;
ttmp = t_at(x, y); ttmp = t_at(x, y);
if (ttmp && !ttmp->tseen) if (ttmp && !ttmp->tseen)
@@ -5006,7 +5079,9 @@ untrap(boolean force)
} }
deal_with_floor_trap = can_reach_floor(FALSE); deal_with_floor_trap = can_reach_floor(FALSE);
if (!deal_with_floor_trap) { if (autounlock_door) {
; /* skip a bunch */
} else if (!deal_with_floor_trap) {
*the_trap = '\0'; *the_trap = '\0';
if (ttmp) if (ttmp)
Strcat(the_trap, an(trapdescr)); Strcat(the_trap, an(trapdescr));
@@ -5095,6 +5170,11 @@ untrap(boolean force)
} /* end if */ } /* end if */
if (boxcnt) { if (boxcnt) {
/* 3.7: this used to allow searching for traps on multiple
containers on the same move and needed to keep track of
whether any had been found but not attempted to untrap;
now at most one per move may be checked and we only
continue on to door handling if they are all declined */
for (otmp = g.level.objects[x][y]; otmp; otmp = otmp->nexthere) for (otmp = g.level.objects[x][y]; otmp; otmp = otmp->nexthere)
if (Is_box(otmp)) { if (Is_box(otmp)) {
(void) safe_qbuf(qbuf, "There is ", (void) safe_qbuf(qbuf, "There is ",
@@ -5103,59 +5183,34 @@ untrap(boolean force)
switch (ynq(qbuf)) { switch (ynq(qbuf)) {
case 'q': case 'q':
return 0; return 0;
case 'n': case 'y':
continue; untrap_box(otmp, force, confused);
} return 1; /* even for 'no' at "Disarm it?" prompt */
if ((otmp->otrapped
&& (force || (!confused
&& rn2(MAXULEV + 1 - u.ulevel) < 10)))
|| (!force && confused && !rn2(3))) {
You("find a trap on %s!", the(xname(otmp)));
if (!confused)
exercise(A_WIS, TRUE);
switch (ynq("Disarm it?")) {
case 'q':
return 1;
case 'n':
trap_skipped = TRUE;
continue;
}
if (otmp->otrapped) {
exercise(A_DEX, TRUE);
ch = ACURR(A_DEX) + u.ulevel;
if (Role_if(PM_ROGUE))
ch *= 2;
if (!force && (confused || Fumbling
|| rnd(75 + level_difficulty() / 2)
> ch)) {
(void) chest_trap(otmp, FINGER, TRUE);
} else {
You("disarm it!");
otmp->otrapped = 0;
}
} else
pline("That %s was not trapped.", xname(otmp));
return 1;
} else {
You("find no traps on %s.", the(xname(otmp)));
return 1;
} }
/* 'n' => continue to next box */
} }
There("are no other chests or boxes here.");
You(trap_skipped ? "find no other traps here."
: "know of no traps here.");
return 0;
} }
if (stumble_on_door_mimic(x, y)) if (stumble_on_door_mimic(x, y))
return 1; return 1;
} /* deal_with_floor_trap */ } /* deal_with_floor_trap */
/* doors can be manipulated even while levitating/unskilled riding */
/*
* Doors can be manipulated even while levitating/unskilled riding.
*
* Ordinarily there won't be a closed or locked door at the same
* location as a floor trap or a container. However, there could
* be a container at a closed/locked door spot if it was dropped
* there by a monster or poly'd hero with Passes_walls capability,
* and poly'd hero could move onto that spot and attempt #untrap
* in direction '.' or '>'. We'll get here for that situation if
* player declines to check all containers for traps.
*
* The usual situation is #untrap toward an adjacent closed door.
* No floor trap would be present and any containers would be
* ignored because they're only checked when direction is '.'/'>'.
*/
if (!IS_DOOR(levl[x][y].typ)) { if (!IS_DOOR(levl[x][y].typ)) {
if (!trap_skipped) if (!trap_skipped)
You("know of no traps there."); You("know of no traps there.");