Compound options which can be set during the game are:
-autounlock when attempting to open a door or loot a [Apply-Key]
- container that is locked, specifies an action to take:
- can be None, or one or more of Apply-Key + Kick + Force;
+autounlock when attempting to open a locked door or loot [Apply-Key]
+ a locked container, specifies an action to take: can be
+ 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
- containers; either will only be attempted if Apply-Key is
- omitted or you aren't carrying any unlocking tool
+ containers; either will only be attempted if Untrap is
+ 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 [`]
disclose the types of information you want [ni na nv ng nc no]
offered at the end of the game
Controls what action to take when attempting to walk into a locked door
or to loot a locked container.
Takes a plus-sign separated list of values:
-\fIapply-key\fP which will attempt to use a key or other unlocking tool
-if you have one;
-\fIkick\fP which will kick the door (if you lack a key or omit apply-key;
+.PS Apply-Key
+.PL 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
+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);
-\fIforce\fP which will try to force a container's lid with your currently
-wielded weapon (if you lack a key or omit apply-key; has no effect on
-doors); or
-\fInone\fP which can't be combined with the other choices.
-.lp ""
+.PL Force
+try to force a container's lid with your currently
+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;
+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.
Preceding \f(CRautounlock\fP with \(oq!\(cq or \(lqno\(rq is treated as
\f(CRautounlock:none\fP.
Successfully forcing a container open will break its lock and might also
destroy some of its contents or damage your weapon or both.
.lp ""
-The default is \fIapply-key\fP.
+The default is Apply-Key.
Persistent.
.lp blind
Start the character permanently blind (default false).
false, the `t' (throw) command is executed instead. Persistent.
%.lp
\item[\ib{autounlock}]
+%\hyphenation{apply\-key}%this needs to be tested...
Controls what action to take when attempting to walk into a locked door
or to loot a locked container.
Takes a plus-sign separated list of values:
-{\it apply-key\/} which will attempt to use a key or other unlocking tool
-if you have one;
-{\it kick\/} which will kick the door (if you lack a key or omit apply-key;
+% paranoid
+% au => autounlock
+\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);
-{\it force\/} which will try to force a container's lid with your currently
-wielded weapon (if you lack a key or omit apply-key; has no effect on
-doors); or
-{\it none\/} which can't be combined with the other choices.
-\\
-%.lp ""
+%.PL Force
+\item[{\tt Force}]
+try to force a container's lid with your currently
+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;
+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}.
Preceding {\tt autounlock} with `{\tt !}' or ``{\tt no}'' is treated as
{\tt autounlock:none}.
destroy some of its contents or damage your weapon or both.
\\
%.lp ""
-The default is {\it apply-key\/}.
+The default is Apply-Key.
Persistent.
%.lp
\item[\ib{blind}]
it was run and eventually replaced just about all the water; likewise
with clouds on Plane of Air
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
also included tty support
extern boolean drown(void);
extern void drain_en(int);
extern int dountrap(void);
+extern int could_untrap(boolean, 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 closeholdingtrap(struct monst *, boolean *);
extern boolean openfallingtrap(struct monst *, boolean, boolean *);
break;
}
case UNTRAP: {
- if (!untrap(TRUE)) {
+ if (!untrap(TRUE, 0, 0, (struct obj *) 0)) {
obj->age = 0; /* don't charge for changing their mind */
return ECMD_OK;
}
* does not prompt for direction if these are set */
struct obj *container) /* container, for autounlock */
{
+ struct obj dummypick;
int picktyp, c, ch;
coord cc;
struct rm *door;
struct obj *otmp;
char qbuf[QBUFSZ];
- boolean autounlock = (((rx != 0 && ry != 0) || container != NULL)
- && (flags.autounlock & AUTOUNLOCK_APPLY_KEY) != 0);
+ boolean autounlock = (rx != 0 || container != NULL);
+ /* '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;
/* check whether we're resuming an interrupted previous attempt */
return PICKLOCK_DID_NOTHING;
}
- if (picktyp != LOCK_PICK
- && picktyp != CREDIT_CARD
- && picktyp != SKELETON_KEY) {
+ if (pick != &dummypick && picktyp != SKELETON_KEY
+ && picktyp != LOCK_PICK && picktyp != CREDIT_CARD) {
impossible("picking lock with object %d?", picktyp);
return PICKLOCK_DID_NOTHING;
}
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.y = ry;
} else if (!get_adjacent_loc((char *) 0, "Invalid location!",
else
verb = "pick";
- if (autounlock) {
- Sprintf(qbuf, "Unlock it with %s?", yname(pick));
- c = yn(qbuf);
- if (c == 'n')
- return 0;
+ if (autounlock && (flags.autounlock & AUTOUNLOCK_UNTRAP) != 0
+ && could_untrap(FALSE, TRUE)
+ && (c = ynq(safe_qbuf(qbuf, "Check ", " for a trap?",
+ otmp, yname, ysimple_name, "this")))
+ != '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 {
/* "There is <a box> here; <verb> <it|its lock>?" */
Sprintf(qsfx, " here; %s %s?",
c = ynq(qbuf);
if (c == 'q')
- return 0;
+ return PICKLOCK_DID_NOTHING;
if (c == 'n')
- continue;
+ continue; /* try next box */
}
if (otmp->obroken) {
pline("This door is broken.");
return PICKLOCK_LEARNED_SOMETHING;
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 */
if (picktyp == CREDIT_CARD && !(door->doormask & D_LOCKED)) {
You_cant("lock a door with a credit card.");
(door->doormask & D_LOCKED) ? "Unlock" : "Lock",
autounlock ? " with " : "",
autounlock ? yname(pick) : "");
-
- c = yn(qbuf);
- if (c == 'n')
+ c = ynq(qbuf);
+ if (c != 'y')
return 0;
/* note: for !autounlock, 'apply' already did touch check */
start_menu(tmpwin, MENU_BEHAVE_STANDARD);
any = cg.zeroany;
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",
unlocktypes[i][0], sep, unlocktypes[i][1]);
presel = !i ? !flags.autounlock : (flags.autounlock & (1 << (i - 1)));
}
static int
-do_loot_cont(struct obj **cobjp,
- int cindex, /* index of this container (1..N)... */
- int ccount) /* ...number of them (N) */
+do_loot_cont(
+ struct obj **cobjp,
+ int cindex, /* index of this container (1..N)... */
+ int ccount) /* ...number of them (N) */
{
struct obj *cobj = *cobjp;
if (!cobj)
return ECMD_OK;
if (cobj->olocked) {
+ int res = ECMD_OK;
+
+#if 0
if (ccount < 2 && (g.level.objects[cobj->ox][cobj->oy] == cobj))
pline("%s locked.",
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)));
else
pline("Hmmm, %s turns out to be locked.", the(xname(cobj)));
cobj->lknown = 1;
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
* #loot isn't a move command; pick_lock() cares */
- if ((flags.autounlock & AUTOUNLOCK_APPLY_KEY) != 0
- && (unlocktool = autokey(TRUE)) != 0) {
+ /* if both the untrap and apply_key bits are set, untrap
+ 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 */
- return (pick_lock(unlocktool, cobj->ox, cobj->oy, cobj) != 0);
- } else if ((flags.autounlock & AUTOUNLOCK_FORCE) != 0
- && ccount == 1 && u_have_forceable_weapon()) {
+ if (pick_lock(unlocktool, ox, oy, cobj))
+ res = ECMD_TIME;
+ /* 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... */
cmdq_add_ec(doforce); /* doforce asks for confirmation */
g.abort_looting = TRUE;
}
}
- return ECMD_OK;
+ return res;
}
cobj->lknown = 1; /* floor container, so no need for update_inventory() */
static boolean adj_nonconjoined_pit(struct trap *);
static int try_lift(struct monst *, struct trap *, int, boolean);
static int help_monster_out(struct monst *, struct trap *);
+static void untrap_box(struct obj *, boolean, boolean);
#if 0
static void join_adjacent_pits(struct trap *);
#endif
int
dountrap(void)
{
- if (near_capacity() >= HVY_ENCUMBER) {
- pline("You're too strained to do that.");
- return ECMD_OK;
- }
- if ((nohands(g.youmonst.data) && !webmaker(g.youmonst.data))
- || !g.youmonst.data->mmove) {
- pline("And just how do you expect to do that?");
+ 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) {
+ Strcpy(buf, "You're too strained to do that.");
+ } else if ((nohands(g.youmonst.data) && !webmaker(g.youmonst.data))
+ || !g.youmonst.data->mmove) {
+ Strcpy(buf, "And just how do you expect to do that?");
} else if (u.ustuck && sticks(g.youmonst.data)) {
- pline("You'll have to let go of %s first.", mon_nam(u.ustuck));
- return ECMD_OK;
- }
- if (u.ustuck || (welded(uwep) && bimanual(uwep))) {
- Your("%s seem to be too busy for that.", makeplural(body_part(HAND)));
- return ECMD_OK;
+ Sprintf(buf, "You'll have to let go of %s first.", mon_nam(u.ustuck));
+ } 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 (buf[0]) {
+ if (verbosely)
+ pline("%s", buf);
+ return 0;
}
- return untrap(FALSE) ? ECMD_TIME : ECMD_OK;
+ return 1;
}
/* Probability of disabling a trap. Helge Hafting */
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
-untrap(boolean force)
+untrap(
+ boolean force,
+ int rx, int ry,
+ struct obj *container)
{
register struct obj *otmp;
register int x, y;
const char *trapdescr;
boolean here, useplural, deal_with_floor_trap,
confused = (Confusion || Hallucination),
- trap_skipped = FALSE;
+ trap_skipped = FALSE, autounlock_door = FALSE;
int boxcnt = 0;
char the_trap[BUFSZ], qbuf[QBUFSZ];
- if (!getdir((char *) 0))
- return 0;
- x = u.ux + u.dx;
- y = u.uy + u.dy;
+ /* 'force' is true for #invoke; if carrying MKoT, make it be true
+ for #untrap or autounlock */
+ if (!force && has_magic_key(&g.youmonst))
+ 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)) {
pline_The("perils lurking there are beyond your grasp.");
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);
if (ttmp && !ttmp->tseen)
}
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';
if (ttmp)
Strcat(the_trap, an(trapdescr));
} /* end if */
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)
if (Is_box(otmp)) {
(void) safe_qbuf(qbuf, "There is ",
switch (ynq(qbuf)) {
case 'q':
return 0;
- case 'n':
- continue;
- }
-
- if ((otmp->otrapped
- && (force || (!confused
- && rn2(MAXULEV + 1 - u.ulevel) < 10)))
- || (!force && confused && !rn2(3))) {
- You("find a trap on %s!", the(xname(otmp)));
- if (!confused)
- exercise(A_WIS, TRUE);
-
- switch (ynq("Disarm it?")) {
- case 'q':
- return 1;
- case 'n':
- trap_skipped = TRUE;
- continue;
- }
-
- if (otmp->otrapped) {
- exercise(A_DEX, TRUE);
- ch = ACURR(A_DEX) + u.ulevel;
- if (Role_if(PM_ROGUE))
- ch *= 2;
- if (!force && (confused || Fumbling
- || rnd(75 + level_difficulty() / 2)
- > ch)) {
- (void) chest_trap(otmp, FINGER, TRUE);
- } else {
- You("disarm it!");
- otmp->otrapped = 0;
- }
- } else
- pline("That %s was not trapped.", xname(otmp));
- return 1;
- } else {
- You("find no traps on %s.", the(xname(otmp)));
- return 1;
+ case 'y':
+ untrap_box(otmp, force, confused);
+ return 1; /* even for 'no' at "Disarm it?" prompt */
}
+ /* 'n' => continue to next box */
}
-
- You(trap_skipped ? "find no other traps here."
- : "know of no traps here.");
- return 0;
+ There("are no other chests or boxes here.");
}
if (stumble_on_door_mimic(x, y))
return 1;
-
} /* 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 (!trap_skipped)
You("know of no traps there.");