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.
This commit is contained in:
PatR
2021-04-13 13:51:57 -07:00
parent 8bd08ebb71
commit 5d6ab55372
7 changed files with 88 additions and 21 deletions

View File

@@ -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 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 teleporters there (level definition doesn't have any but wizard mode
wishing could attempt to place one) 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 Fixes to 3.7.0-x Problems that Were Exposed Via git Repository

View File

@@ -3123,6 +3123,7 @@ extern int dowrite(struct obj *);
extern void learnwand(struct obj *); extern void learnwand(struct obj *);
extern int bhitm(struct monst *, struct obj *); extern int bhitm(struct monst *, struct obj *);
extern void release_hold(void);
extern void probe_monster(struct monst *); extern void probe_monster(struct monst *);
extern boolean get_obj_location(struct obj *, xchar *, xchar *, int); extern boolean get_obj_location(struct obj *, xchar *, xchar *, int);
extern boolean get_mon_location(struct monst *, xchar *, xchar *, int); extern boolean get_mon_location(struct monst *, xchar *, xchar *, int);

View File

@@ -3480,12 +3480,19 @@ do_break_wand(struct obj *obj)
affects_objects = FALSE; affects_objects = FALSE;
switch (obj->otyp) { 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_WISHING:
case WAN_NOTHING: case WAN_NOTHING:
case WAN_LOCKING: case WAN_LOCKING:
case WAN_PROBING: case WAN_PROBING:
case WAN_ENLIGHTENMENT: case WAN_ENLIGHTENMENT:
case WAN_OPENING:
case WAN_SECRET_DOOR_DETECTION: case WAN_SECRET_DOOR_DETECTION:
pline(nothing_else_happens); pline(nothing_else_happens);
goto discard_broken_wand; goto discard_broken_wand;
@@ -3502,7 +3509,7 @@ do_break_wand(struct obj *obj)
dmg *= 2; dmg *= 2;
/*FALLTHRU*/ /*FALLTHRU*/
case WAN_MAGIC_MISSILE: case WAN_MAGIC_MISSILE:
wanexpl: wanexpl:
explode(u.ux, u.uy, -(obj->otyp), dmg, WAND_CLASS, expltype); explode(u.ux, u.uy, -(obj->otyp), dmg, WAND_CLASS, expltype);
makeknown(obj->otyp); /* explode describes the effect */ makeknown(obj->otyp); /* explode describes the effect */
goto discard_broken_wand; goto discard_broken_wand;
@@ -3633,7 +3640,7 @@ do_break_wand(struct obj *obj)
if (obj->otyp == WAN_LIGHT) if (obj->otyp == WAN_LIGHT)
litroom(TRUE, obj); /* only needs to be done once */ litroom(TRUE, obj); /* only needs to be done once */
discard_broken_wand: discard_broken_wand:
obj = g.current_wand; /* [see dozap() and destroy_item()] */ obj = g.current_wand; /* [see dozap() and destroy_item()] */
g.current_wand = 0; g.current_wand = 0;
if (obj) if (obj)

View File

@@ -298,8 +298,14 @@ getmattk(struct monst *magr, struct monst *mdef,
/* note: 3d9 is slightly higher than previous 4d6 */ /* note: 3d9 is slightly higher than previous 4d6 */
} }
} else if (attk->aatyp == AT_ENGL && magr->mspec_used) { /* holders/engulfers who release the hero have mspec_used set to rnd(2)
/* can't re-engulf yet; switch to simpler attack */ 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; *alt_attk_buf = *attk;
attk = alt_attk_buf; attk = alt_attk_buf;
if (attk->adtyp == AD_ACID || attk->adtyp == AD_ELEC 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->damn = 1; /* relatively weak: 1d6 */
attk->damd = 6; 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 /* barrow wight, Nazgul, erinys have weapon attack for non-physical
damage; force physical damage if attacker has been cancelled or damage; force physical damage if attacker has been cancelled or

View File

@@ -2725,6 +2725,8 @@ void
unstuck(struct monst* mtmp) unstuck(struct monst* mtmp)
{ {
if (u.ustuck == mtmp) { if (u.ustuck == mtmp) {
struct permonst *ptr = mtmp->data;
/* do this first so that docrt()'s botl update is accurate; /* do this first so that docrt()'s botl update is accurate;
safe to do as long as u.uswallow is also cleared before docrt() */ safe to do as long as u.uswallow is also cleared before docrt() */
set_ustuck((struct monst *) 0); set_ustuck((struct monst *) 0);
@@ -2738,11 +2740,16 @@ unstuck(struct monst* mtmp)
placebc(); placebc();
g.vision_full_recalc = 1; g.vision_full_recalc = 1;
docrt(); 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);
} }
} }

View File

@@ -2689,7 +2689,7 @@ mhitm_ad_stck(struct monst *magr, struct attack *mattk, struct monst *mdef,
boolean negated = !(rn2(10) >= 3 * armpro); boolean negated = !(rn2(10) >= 3 * armpro);
if (!negated && !sticks(pd) && distu(mdef->mx, mdef->my) <= 2) 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) { } else if (mdef == &g.youmonst) {
/* mhitu */ /* mhitu */
int armpro = magic_negation(mdef); int armpro = magic_negation(mdef);

View File

@@ -318,17 +318,15 @@ bhitm(struct monst *mtmp, struct obj *otmp)
case WAN_OPENING: case WAN_OPENING:
case SPE_KNOCK: case SPE_KNOCK:
wake = FALSE; /* don't want immediate counterattack */ wake = FALSE; /* don't want immediate counterattack */
if (u.uswallow && mtmp == u.ustuck) { if (mtmp == u.ustuck) {
if (is_animal(mtmp->data)) { /* zapping either holder/holdee or self [zapyourself()] will
if (Blind) release hero from holder's grasp or holdee from hero's grasp */
You_feel("a sudden rush of air!"); release_hold();
else learn_it = TRUE;
pline("%s opens its mouth!", Monnam(mtmp));
} /* zap which hits steed will only release saddle if it
expels(mtmp, mtmp->data, TRUE); doesn't hit a holding or falling trap; playability
/* zap which hits steed will only release saddle if it here overrides the more logical target ordering */
doesn't hit a holding or falling trap; playability
here overrides the more logical target ordering */
} else if (openholdingtrap(mtmp, &learn_it)) { } else if (openholdingtrap(mtmp, &learn_it)) {
break; break;
} else if (openfallingtrap(mtmp, TRUE, &learn_it)) { } else if (openfallingtrap(mtmp, TRUE, &learn_it)) {
@@ -471,6 +469,42 @@ bhitm(struct monst *mtmp, struct obj *otmp)
return 0; 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 <mon>" */
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 void
probe_monster(struct monst *mtmp) probe_monster(struct monst *mtmp)
{ {
@@ -2577,6 +2611,12 @@ zapyourself(struct obj *obj, boolean ordinary)
break; break;
case WAN_OPENING: case WAN_OPENING:
case SPE_KNOCK: 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) { if (Punished) {
learn_it = TRUE; learn_it = TRUE;
unpunish(); unpunish();