From 9b0d78d26514651154caac27160bb04adfc717bf Mon Sep 17 00:00:00 2001 From: copperwater Date: Mon, 30 Sep 2019 20:56:03 -0400 Subject: [PATCH] Port the autounlock feature from UnNetHack This adds a boolean option, autounlock, defaulting to true. When this is set to TRUE, messages stating that some door or container is locked are automatically followed by a prompt asking if you would like to unlock it, if you are carrying an unlocking tool (key, lock pick, or credit card). Architecturally, this extends the pick_lock function to take three additional arguments (door coordinates or a box on the ground you are autounlocking). Because this adds a new field to struct flag, this is not a save-compatible change. I have not adjusted EDITLEVEL or VERSION_COMPATIBILITY, though. The code that selects an unlocking tool will always look first for a skeleton key, then a lock pick, then a credit card. Since curses, rust, and other attributes don't really have an effect on the viability of the unlocking device, it didn't seem to warrant making a more complex function for that. --- include/extern.h | 2 +- include/flag.h | 1 + src/apply.c | 2 +- src/lock.c | 59 ++++++++++++++++++++++++++++++++++++------------ src/options.c | 1 + src/pickup.c | 9 ++++++++ 6 files changed, 57 insertions(+), 17 deletions(-) diff --git a/include/extern.h b/include/extern.h index 7c37297b3..e408f4845 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1100,7 +1100,7 @@ E boolean FDECL(picking_at, (int, int)); E void FDECL(breakchestlock, (struct obj *, BOOLEAN_P)); E void NDECL(reset_pick); E void FDECL(maybe_reset_pick, (struct obj *)); -E int FDECL(pick_lock, (struct obj *)); +E int FDECL(pick_lock, (struct obj *, xchar, xchar, struct obj *)); E int NDECL(doforce); E boolean FDECL(boxlock, (struct obj *, struct obj *)); E boolean FDECL(doorlock, (struct obj *, int, int)); diff --git a/include/flag.h b/include/flag.h index daef72bc4..957981a57 100644 --- a/include/flag.h +++ b/include/flag.h @@ -20,6 +20,7 @@ struct flag { boolean autodig; /* MRKR: Automatically dig */ boolean autoquiver; /* Automatically fill quiver */ boolean autoopen; /* open doors by walking into them */ + boolean autounlock; /* automatically apply unlocking tools */ boolean beginner; boolean biff; /* enable checking for mail */ boolean bones; /* allow saving/loading bones */ diff --git a/src/apply.c b/src/apply.c index 11bd09d03..5bb06aef6 100644 --- a/src/apply.c +++ b/src/apply.c @@ -3606,7 +3606,7 @@ doapply() case LOCK_PICK: case CREDIT_CARD: case SKELETON_KEY: - res = (pick_lock(obj) != 0); + res = (pick_lock(obj, 0, 0, NULL) != 0); break; case PICK_AXE: case DWARVISH_MATTOCK: diff --git a/src/lock.c b/src/lock.c index 270ab1866..8645b66a1 100644 --- a/src/lock.c +++ b/src/lock.c @@ -303,14 +303,18 @@ struct obj *container; /* passed from obfree() */ /* player is applying a key, lock pick, or credit card */ int -pick_lock(pick) +pick_lock(pick, rx, ry, container) struct obj *pick; +xchar rx, ry; /* coordinates of doors/container, for autounlock: does not + prompt for direction if these are set */ +struct obj *container; /* container, for autounlock */ { int picktyp, c, ch; coord cc; struct rm *door; struct obj *otmp; char qbuf[QBUFSZ]; + boolean autounlock = (rx != 0 && ry != 0) || (container != NULL); picktyp = pick->otyp; @@ -357,8 +361,13 @@ struct obj *pick; } ch = 0; /* lint suppression */ - if (!get_adjacent_loc((char *) 0, "Invalid location!", u.ux, u.uy, &cc)) + if (rx != 0 && ry != 0) { /* autounlock; caller has provided coordinates */ + cc.x = rx; + cc.y = ry; + } + else 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; @@ -381,7 +390,9 @@ struct obj *pick; 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)) { + /* autounlock on boxes: only the one that just informed you it was + * locked. Don't include any other boxes which might be here. */ + if ((!autounlock && Is_box(otmp)) || (otmp == container)) { ++count; if (!can_reach_floor(TRUE)) { You_cant("reach %s from up here.", the(xname(otmp))); @@ -397,17 +408,24 @@ struct obj *pick; else verb = "pick"; - /* "There is here; ?" */ - 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; + if (autounlock) { + Sprintf(qbuf, "Unlock it with %s?", yname(pick)); + c = yn(qbuf); + if (c == 'n') + return 0; + } else { + /* "There is here; ?" */ + 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; + 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)); @@ -493,8 +511,10 @@ struct obj *pick; return PICKLOCK_LEARNED_SOMETHING; } - Sprintf(qbuf, "%s it?", - (door->doormask & D_LOCKED) ? "Unlock" : "Lock"); + Sprintf(qbuf, "%s it%s%s?", + (door->doormask & D_LOCKED) ? "Unlock" : "Lock", + autounlock ? " with " : "", + autounlock ? yname(pick) : ""); c = yn(qbuf); if (c == 'n') @@ -694,6 +714,8 @@ int x, y; if (!(door->doormask & D_CLOSED)) { const char *mesg; + boolean locked = FALSE; + struct obj* unlocktool; switch (door->doormask) { case D_BROKEN: @@ -707,9 +729,16 @@ int x, y; break; default: mesg = " is locked"; + locked = TRUE; break; } pline("This door%s.", mesg); + if (locked && flags.autounlock && + ((unlocktool = carrying(SKELETON_KEY)) || + (unlocktool = carrying(LOCK_PICK)) || + (unlocktool = carrying(CREDIT_CARD)))) { + pick_lock(unlocktool, cc.x, cc.y, (struct obj *) 0); + } return res; } diff --git a/src/options.c b/src/options.c index 50f92ca86..605e42b03 100644 --- a/src/options.c +++ b/src/options.c @@ -87,6 +87,7 @@ static struct Bool_Opt { { "autoopen", &flags.autoopen, TRUE, SET_IN_GAME }, { "autopickup", &flags.pickup, TRUE, SET_IN_GAME }, { "autoquiver", &flags.autoquiver, FALSE, SET_IN_GAME }, + { "autounlock", &flags.autounlock, TRUE, SET_IN_GAME }, #if defined(MICRO) && !defined(AMIGA) { "BIOS", &iflags.BIOS, FALSE, SET_IN_FILE }, #else diff --git a/src/pickup.c b/src/pickup.c index e47d989da..e3ab9ae11 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -1720,6 +1720,7 @@ int cindex, ccount; /* index of this container (1..N), number of them (N) */ if (!cobj) return 0; if (cobj->olocked) { + struct obj *unlocktool; if (ccount < 2) pline("%s locked.", cobj->lknown ? "It is" : "Hmmm, it turns out to be"); @@ -1728,6 +1729,14 @@ int cindex, ccount; /* index of this container (1..N), number of them (N) */ else pline("Hmmm, %s turns out to be locked.", the(xname(cobj))); cobj->lknown = 1; + + if (flags.autounlock && + ((unlocktool = carrying(SKELETON_KEY)) || + (unlocktool = carrying(LOCK_PICK)) || + (unlocktool = carrying(CREDIT_CARD)))) { + /* pass ox and oy to avoid direction prompt */ + pick_lock(unlocktool, cobj->ox, cobj->oy, cobj); + } return 0; } cobj->lknown = 1; /* floor container, so no need for update_inventory() */