From: PatR Date: Sun, 8 Oct 2017 10:29:16 +0000 (-0700) Subject: still more Master Key of Thievery X-Git-Tag: NetHack-3.6.1_RC01~302 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5c77360023f84a9a709f52fac10cf43fd3167d5f;p=nethack still more Master Key of Thievery Make #untrap while carrying the non-cursed (for rogues) or blessed (for non-rogues) Key work the same as #invoke has been doing (without regard to its bless/curse state): when used on trapped door or chest, that trap will always be found and disarming it will always succeed. It should work when carried by monsters too: if they try to open a trapped door while carrying the Key (must be blessed since they're not rogues) the trap will be automatically disarmed. (Caveat: that hasn't been adequately tested.) TODO (maybe...): change the #invoke property to detect unseen/secret door detection instead of #untrap. The latter isn't completely redundant; it works when the Key is cursed. But quest artifacts strongly resist becoming cursed so that isn't a particularly useful distinction. Also, trap hints when wielding the Key without gloves didn't notice adjacent door and chest traps. Now it does. And the behavior is slightly different: known traps covered by objects or monsters are treated like unknown traps as far as the hot/cold hints go. --- diff --git a/include/artilist.h b/include/artilist.h index e06e99a7f..d5992bcef 100644 --- a/include/artilist.h +++ b/include/artilist.h @@ -208,6 +208,9 @@ A("The Palantir of Westernesse", CRYSTAL_BALL, PHYS(5, 0), NO_DFNS, NO_CARY, CREATE_AMMO, A_CHAOTIC, PM_RANGER, NON_PM, 4000L, NO_COLOR), + /* MKoT has an additional carry property if the Key is not cursed (for + rogues) or blessed (for non-rogues): #untrap of doors and chests + will always find any traps and disarming those will always succeed */ A("The Master Key of Thievery", SKELETON_KEY, (SPFX_NOGEN | SPFX_RESTR | SPFX_INTEL | SPFX_SPEAK), (SPFX_WARN | SPFX_TCTRL | SPFX_HPHDAM), 0, NO_ATTK, NO_DFNS, NO_CARY, diff --git a/include/extern.h b/include/extern.h index ca78d340f..cc2bf44dc 100644 --- a/include/extern.h +++ b/include/extern.h @@ -78,8 +78,8 @@ E int FDECL(spec_dbon, (struct obj *, struct monst *, int)); E void FDECL(discover_artifact, (XCHAR_P)); E boolean FDECL(undiscovered_artifact, (XCHAR_P)); E int FDECL(disp_artifact_discoveries, (winid)); -E boolean FDECL(artifact_hit, - (struct monst *, struct monst *, struct obj *, int *, int)); +E boolean FDECL(artifact_hit, (struct monst *, struct monst *, struct obj *, + int *, int)); E int NDECL(doinvoke); E boolean FDECL(finesse_ahriman, (struct obj *)); E void FDECL(arti_speak, (struct obj *)); @@ -93,6 +93,8 @@ E void FDECL(Sting_effects, (int)); E int FDECL(retouch_object, (struct obj **, BOOLEAN_P)); E void FDECL(retouch_equipment, (int)); E void NDECL(mkot_trap_warn); +E boolean FDECL(is_magic_key, (struct monst *, struct obj *)); +E struct obj *FDECL(has_magic_key, (struct monst *)); /* ### attrib.c ### */ diff --git a/src/artifact.c b/src/artifact.c index 4fdfc828a..25fc17118 100644 --- a/src/artifact.c +++ b/src/artifact.c @@ -2055,36 +2055,102 @@ int dropflag; /* 0==don't drop, 1==drop all, 2==drop weapon */ static int mkot_trap_warn_count = 0; STATIC_OVL int -count_surround_traps(x,y) -int x,y; +count_surround_traps(x, y) +int x, y; { - int dx, dy, ret = 0; + struct rm *levp; + struct obj *otmp; struct trap *ttmp; - - for (dx = x-1; dx < x+2; dx++) - for (dy = y-1; dy < y+2; dy++) - if (isok(dx,dy) && (ttmp = t_at(dx,dy)) - && !ttmp->tseen) - ret++; + int dx, dy, glyph, ret = 0; + + for (dx = x - 1; dx < x + 2; ++dx) + for (dy = y - 1; dy < y + 2; ++dy) { + if (!isok(dx, dy)) + continue; + /* If a trap is shown here, don't count it; the hero + * should be expecting it. But if there is a trap here + * that's not shown, either undiscovered or covered by + * something, do count it. + */ + glyph = glyph_at(dx, dy); + if (glyph_is_trap(glyph)) + continue; + if ((ttmp = t_at(dx, dy)) != 0) { + ++ret; + continue; + } + levp = &levl[dx][dy]; + if (IS_DOOR(levp->typ) && (levp->doormask & D_TRAPPED) != 0) { + ++ret; + continue; + } + for (otmp = level.objects[dx][dy]; otmp; otmp = otmp->nexthere) + if (Is_container(otmp) && otmp->otrapped) { + ++ret; /* we're counting locations, so just */ + break; /* count the first one in a pile */ + } + } + /* + * [Shouldn't we also check inventory for a trapped container? + * Even if its trap has already been found, there's no 'tknown' + * flag to help hero remember that so we have nothing comparable + * to a shown glyph to justify skipping it.] + */ return ret; } +/* sense adjacent traps if wielding MKoT without wearing gloves */ void mkot_trap_warn() { - if (!uarmg && uwep - && uwep->oartifact == ART_MASTER_KEY_OF_THIEVERY) { - const char *const heat[7] = { "cold", "slightly warm", "warm", - "very warm", "hot", "very hot", - "like fire" }; - int ntraps = count_surround_traps(u.ux, u.uy); - - if (ntraps != mkot_trap_warn_count) - pline_The("key feels %s%c", heat[(ntraps > 6) ? 6 : ntraps], - ntraps > 3 ? '!' : '.'); + static const char *const heat[7] = { + "cool", "slightly warm", "warm", "very warm", + "hot", "very hot", "like fire" + }; + + if (!uarmg && uwep && uwep->oartifact == ART_MASTER_KEY_OF_THIEVERY) { + int idx, ntraps = count_surround_traps(u.ux, u.uy); + + if (ntraps != mkot_trap_warn_count) { + idx = min(ntraps, SIZE(heat) - 1); + pline_The("Key feels %s%c", heat[idx], (ntraps > 3) ? '!' : '.'); + } mkot_trap_warn_count = ntraps; } else mkot_trap_warn_count = 0; } +/* Master Key is magic key if its bless/curse state meets our criteria: + not cursed for rogues or blessed for non-rogues */ +boolean +is_magic_key(mon, obj) +struct monst *mon; /* if null, non-rogue is assumed */ +struct obj *obj; +{ + if (((obj && obj->oartifact == ART_MASTER_KEY_OF_THIEVERY) + && ((mon == &youmonst) ? Role_if(PM_ROGUE) + : (mon && mon->data == &mons[PM_ROGUE]))) + ? !obj->cursed : obj->blessed) + return TRUE; + return FALSE; +} + +/* figure out whether 'mon' (usually youmonst) is carrying the magic key */ +struct obj * +has_magic_key(mon) +struct monst *mon; /* if null, hero assumed */ +{ + struct obj *o; + short key = artilist[ART_MASTER_KEY_OF_THIEVERY].otyp; + + if (!mon) + mon = &youmonst; + for (o = ((mon == &youmonst) ? invent : mon->minvent); o; + o = nxtobj(o, key, FALSE)) { + if (is_magic_key(mon, o)) + return o; + } + return (struct obj *) 0; +} + /*artifact.c*/ diff --git a/src/lock.c b/src/lock.c index 19dfabe46..426e8a35f 100644 --- a/src/lock.c +++ b/src/lock.c @@ -18,7 +18,6 @@ STATIC_PTR int NDECL(picklock); STATIC_PTR int NDECL(forcelock); STATIC_DCL const char *NDECL(lock_action); -STATIC_DCL boolean FDECL(is_magic_key, (struct monst *, struct obj *)); STATIC_DCL boolean FDECL(obstructed, (int, int, BOOLEAN_P)); STATIC_DCL void FDECL(chest_shatter_msg, (struct obj *)); @@ -275,21 +274,6 @@ maybe_reset_pick() reset_pick(); } -/* Master Key is magic key if its bless/curse state meets our criteria: - not cursed for rogues or blessed for non-rogues */ -STATIC_OVL boolean -is_magic_key(mon, obj) -struct monst *mon; /* if null, non-rogue is assumed */ -struct obj *obj; -{ - if (((obj && obj->oartifact == ART_MASTER_KEY_OF_THIEVERY) - && ((mon == &youmonst) ? Role_if(PM_ROGUE) - : (mon && mon->data == &mons[PM_ROGUE]))) - ? !obj->cursed : obj->blessed) - return TRUE; - return FALSE; -} - /* 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 diff --git a/src/monmove.c b/src/monmove.c index ab17709f4..26d7e2144 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -1246,6 +1246,16 @@ postmov: boolean btrapped = (here->doormask & D_TRAPPED) != 0, observeit = canseeit && canspotmon(mtmp); + /* if mon has MKoT, disarm door trap; no message given */ + if (btrapped && has_magic_key(mtmp)) { + /* BUG: this lets a vampire or blob or a doorbuster + holding the Key disarm the trap even though it isn't + using that Key when squeezing under or smashing the + door. Not significant enough to worry about; perhaps + the Key's magic is more powerful for monsters? */ + here->doormask &= ~D_TRAPPED; + btrapped = FALSE; + } if ((here->doormask & (D_LOCKED | D_CLOSED)) != 0 && (amorphous(ptr) || (can_fog(mtmp) diff --git a/src/trap.c b/src/trap.c index 53493d426..401d5a7fc 100644 --- a/src/trap.c +++ b/src/trap.c @@ -4256,6 +4256,11 @@ boolean force; 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(&youmonst)) + force = TRUE; + ttmp = t_at(x, y); if (ttmp && !ttmp->tseen) ttmp = 0;