From: PatR Date: Tue, 13 Apr 2021 20:51:57 +0000 (-0700) Subject: opening magic vs holding monster X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5d6ab55372471b04e3cae4e2bcaebced5b993ce4;p=nethack opening magic vs holding monster Zapping wand of opening or spell of knock at engulfer while swallowed would make the engulfer expel the hero; this change makes zapping other holders release their hold. Zapping self now achieves the same effect, as does breaking a non-empty wand of opening. When poly'd hero is holding a monster rather than being held, that monster will be released. Engulfers can't re-engulf for 1 or 2 turns after releasing the hero in order to prevent hero from being immediately re-engulfed. Impose the same limitation on other holders. --- diff --git a/doc/fixes37.0 b/doc/fixes37.0 index 97365e891..59452ef65 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -452,6 +452,8 @@ turn off input autocompletion for '#twoweapon' since simple 'X' invokes it; if a branch has only one level (Fort Ludios), prevent creation of any level teleporters there (level definition doesn't have any but wizard mode wishing could attempt to place one) +opening/unlocking magic zapped at monster holding the hero will release hold + (zap at engulfer already expels hero); zapping at self has same effect Fixes to 3.7.0-x Problems that Were Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index 3da9d738e..fc3a7a14e 100644 --- a/include/extern.h +++ b/include/extern.h @@ -3123,6 +3123,7 @@ extern int dowrite(struct obj *); extern void learnwand(struct obj *); extern int bhitm(struct monst *, struct obj *); +extern void release_hold(void); extern void probe_monster(struct monst *); extern boolean get_obj_location(struct obj *, xchar *, xchar *, int); extern boolean get_mon_location(struct monst *, xchar *, xchar *, int); diff --git a/src/apply.c b/src/apply.c index d6eeb80f5..7d27cd921 100644 --- a/src/apply.c +++ b/src/apply.c @@ -3480,12 +3480,19 @@ do_break_wand(struct obj *obj) affects_objects = FALSE; switch (obj->otyp) { + case WAN_OPENING: + if (u.ustuck) { + release_hold(); + if (obj->dknown) + makeknown(WAN_OPENING); + goto discard_broken_wand; + } + /*FALLTHRU*/ case WAN_WISHING: case WAN_NOTHING: case WAN_LOCKING: case WAN_PROBING: case WAN_ENLIGHTENMENT: - case WAN_OPENING: case WAN_SECRET_DOOR_DETECTION: pline(nothing_else_happens); goto discard_broken_wand; @@ -3502,7 +3509,7 @@ do_break_wand(struct obj *obj) dmg *= 2; /*FALLTHRU*/ case WAN_MAGIC_MISSILE: - wanexpl: + wanexpl: explode(u.ux, u.uy, -(obj->otyp), dmg, WAND_CLASS, expltype); makeknown(obj->otyp); /* explode describes the effect */ goto discard_broken_wand; @@ -3633,7 +3640,7 @@ do_break_wand(struct obj *obj) if (obj->otyp == WAN_LIGHT) litroom(TRUE, obj); /* only needs to be done once */ -discard_broken_wand: + discard_broken_wand: obj = g.current_wand; /* [see dozap() and destroy_item()] */ g.current_wand = 0; if (obj) diff --git a/src/mhitu.c b/src/mhitu.c index faf177388..f548dca0f 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -298,8 +298,14 @@ getmattk(struct monst *magr, struct monst *mdef, /* note: 3d9 is slightly higher than previous 4d6 */ } - } else if (attk->aatyp == AT_ENGL && magr->mspec_used) { - /* can't re-engulf yet; switch to simpler attack */ + /* holders/engulfers who release the hero have mspec_used set to rnd(2) + and can't re-hold/re-engulf until it has been decremented to zero */ + } else if (magr->mspec_used && (attk->aatyp == AT_ENGL + || attk->aatyp == AT_HUGS + || attk->adtyp == AD_STCK)) { + boolean wimpy = (attk->damd == 0); /* lichen, violet fungus */ + + /* can't re-engulf or re-grab yet; switch to simpler attack */ *alt_attk_buf = *attk; attk = alt_attk_buf; if (attk->adtyp == AD_ACID || attk->adtyp == AD_ELEC @@ -311,6 +317,10 @@ getmattk(struct monst *magr, struct monst *mdef, } attk->damn = 1; /* relatively weak: 1d6 */ attk->damd = 6; + if (wimpy && attk->aatyp == AT_CLAW) { + attk->aatyp = AT_TUCH; + attk->damn = attk->damd = 0; + } /* barrow wight, Nazgul, erinys have weapon attack for non-physical damage; force physical damage if attacker has been cancelled or diff --git a/src/mon.c b/src/mon.c index 7e740e696..0aef9dda5 100644 --- a/src/mon.c +++ b/src/mon.c @@ -2725,6 +2725,8 @@ void unstuck(struct monst* mtmp) { if (u.ustuck == mtmp) { + struct permonst *ptr = mtmp->data; + /* do this first so that docrt()'s botl update is accurate; safe to do as long as u.uswallow is also cleared before docrt() */ set_ustuck((struct monst *) 0); @@ -2738,11 +2740,16 @@ unstuck(struct monst* mtmp) placebc(); g.vision_full_recalc = 1; docrt(); - /* prevent swallower (mtmp might have just poly'd into something - without an engulf attack) from immediately re-engulfing */ - if (attacktype(mtmp->data, AT_ENGL) && !mtmp->mspec_used) - mtmp->mspec_used = rnd(2); } + + /* prevent holder/engulfer from immediately re-holding/re-engulfing + [note: this call to unstuck() might be because u.ustuck has just + changed shape and doesn't have a holding attack any more, hence + don't set mspec_used uncondtionally] */ + if (!mtmp->mspec_used && (dmgtype(ptr, AD_STCK) + || attacktype(ptr, AT_ENGL) + || attacktype(ptr, AT_HUGS))) + mtmp->mspec_used = rnd(2); } } diff --git a/src/uhitm.c b/src/uhitm.c index ee5d3dfa0..45ddb0450 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -2689,7 +2689,7 @@ mhitm_ad_stck(struct monst *magr, struct attack *mattk, struct monst *mdef, boolean negated = !(rn2(10) >= 3 * armpro); if (!negated && !sticks(pd) && distu(mdef->mx, mdef->my) <= 2) - u.ustuck = mdef; /* it's now stuck to you */ + set_ustuck(mdef); /* it's now stuck to you */ } else if (mdef == &g.youmonst) { /* mhitu */ int armpro = magic_negation(mdef); diff --git a/src/zap.c b/src/zap.c index ea78ea23d..a3fb0884a 100644 --- a/src/zap.c +++ b/src/zap.c @@ -318,17 +318,15 @@ bhitm(struct monst *mtmp, struct obj *otmp) case WAN_OPENING: case SPE_KNOCK: wake = FALSE; /* don't want immediate counterattack */ - if (u.uswallow && mtmp == u.ustuck) { - if (is_animal(mtmp->data)) { - if (Blind) - You_feel("a sudden rush of air!"); - else - pline("%s opens its mouth!", Monnam(mtmp)); - } - expels(mtmp, mtmp->data, TRUE); - /* zap which hits steed will only release saddle if it - doesn't hit a holding or falling trap; playability - here overrides the more logical target ordering */ + if (mtmp == u.ustuck) { + /* zapping either holder/holdee or self [zapyourself()] will + release hero from holder's grasp or holdee from hero's grasp */ + release_hold(); + learn_it = TRUE; + + /* zap which hits steed will only release saddle if it + doesn't hit a holding or falling trap; playability + here overrides the more logical target ordering */ } else if (openholdingtrap(mtmp, &learn_it)) { break; } else if (openfallingtrap(mtmp, TRUE, &learn_it)) { @@ -471,6 +469,42 @@ bhitm(struct monst *mtmp, struct obj *otmp) return 0; } +/* hero is held by a monster or engulfed or holding a monster and has zapped + opening/unlocking magic at holder/engulfer/holdee or at self */ +void +release_hold() +{ + struct monst *mtmp = u.ustuck; + + if (!mtmp) { + impossible("release_hold when not held?"); + } else if (sticks(g.youmonst.data)) { + /* order matters if 'holding' status condition is enabled; + set_ustuck() will set flag for botl update, You() pline will + trigger a status update with "UHold" removed */ + set_ustuck((struct monst *) 0); + You("release %s.", mon_nam(mtmp)); + } else if (u.uswallow) { + if (is_animal(mtmp->data)) { + if (!Blind) + pline("%s opens its mouth!", Monnam(mtmp)); + else + You_feel("a sudden rush of air!"); + } + /* gives "you get regurgitated" or "you get expelled from " */ + expels(mtmp, mtmp->data, TRUE); + } else { /* held but not swallowed */ + char relbuf[BUFSZ]; + + unstuck(u.ustuck); + if (!nohands(mtmp->data)) + Sprintf(relbuf, "from %s grasp", s_suffix(mon_nam(mtmp))); + else + Sprintf(relbuf, "by %s", mon_nam(mtmp)); + You("are released %s.", relbuf); + } +} + void probe_monster(struct monst *mtmp) { @@ -2577,6 +2611,12 @@ zapyourself(struct obj *obj, boolean ordinary) break; case WAN_OPENING: case SPE_KNOCK: + if (u.ustuck) { + /* zapping either self or holder/holdee [bhitm()] will release + holder's grasp from the hero or hero's grasp from holdee */ + release_hold(); + learn_it = TRUE; + } if (Punished) { learn_it = TRUE; unpunish();