diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index afa642959..7721bc578 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1145,7 +1145,7 @@ give feedback if monster holding onto the hero has to let go when hero polys into a form which can't be held prevent hug attacks and touch or engulf attacks for wrap, stick-to, and digestion damage from succeeding against unsolid targets (ghosts, - vortices, a few others) + vortices, a few others) or against worm tails wand of speed gives temporary speed, potion gives intrinsic some monsters (riders, shopkeepers, priests, quest leader) can break boulders corpse-eating monsters will go out of their way to eat corpses on the floor diff --git a/src/dogmove.c b/src/dogmove.c index ac19348a2..f373fbd00 100644 --- a/src/dogmove.c +++ b/src/dogmove.c @@ -660,8 +660,9 @@ find_targ( if ((targ = m_at(curx, cury)) != 0) { /* Is the monster visible to the pet? */ - if ((!targ->minvis || perceives(mtmp->data)) - && !targ->mundetected) + if ((!targ->minvis || perceives(mtmp->data)) && !targ->mundetected + /* if a long worm, only accept the head as a target */ + && targ->mx == curx && targ->my == cury) /* not tail */ break; /* If the pet can't see it, it assumes it aint there */ targ = 0; @@ -898,6 +899,8 @@ pet_ranged_attk(struct monst *mtmp) */ mstatus = M_ATTK_HIT; } else { + gb.bhitpos.x = mtmp->mx, gb.bhitpos.y = mtmp->my; + gn.notonhead = FALSE; mstatus = mattackm(mtmp, mtarg); /* Shouldn't happen, really */ @@ -917,8 +920,11 @@ pet_ranged_attk(struct monst *mtmp) * if it's blind or unseeing, it can't retaliate */ if (mtarg->mcansee && haseyes(mtarg->data)) { - int mresp = mattackm(mtarg, mtmp); + int mresp; + gb.bhitpos.x = mtmp->mx, gb.bhitpos.y = mtmp->my; + gn.notonhead = FALSE; + mresp = mattackm(mtarg, mtmp); if (mresp & M_ATTK_DEF_DIED) return MMOVE_DIED; } @@ -1107,7 +1113,8 @@ dog_move( if (after) return MMOVE_NOTHING; /* hit only once each move */ - gn.notonhead = 0; + gb.bhitpos.x = nx, gb.bhitpos.y = ny; + gn.notonhead = mtmp2->mx != nx || mtmp2->my != ny; mstatus = mattackm(mtmp, mtmp2); /* aggressor (pet) died */ @@ -1120,6 +1127,8 @@ dog_move( && !onscary(mtmp->mx, mtmp->my, mtmp2) /* monnear check needed: long worms hit on tail */ && monnear(mtmp2, mtmp->mx, mtmp->my)) { + gb.bhitpos.x = mtmp->mx, gb.bhitpos.y = mtmp->my; + gn.notonhead = FALSE; mstatus = mattackm(mtmp2, mtmp); /* return attack */ if (mstatus & M_ATTK_DEF_DIED) return MMOVE_DIED; diff --git a/src/hack.c b/src/hack.c index 17547ea31..7f7ba2eea 100644 --- a/src/hack.c +++ b/src/hack.c @@ -1748,9 +1748,9 @@ domove_attackmon_at( coordxy x, coordxy y, boolean *displaceu) { - /* only attack if we know it's there */ - /* or if we used the 'F' command to fight blindly */ - /* or if it hides_under, in which case we call do_attack() to print + /* only attack if we know it's there + * or if we used the 'F' command to fight blindly + * or if it hides_under, in which case we call do_attack() to print * the Wait! message. * This is different from ceiling hiders, who aren't handled in * do_attack(). diff --git a/src/mhitm.c b/src/mhitm.c index 0eee0e3d8..5bd612c1c 100644 --- a/src/mhitm.c +++ b/src/mhitm.c @@ -139,9 +139,8 @@ fightm(register struct monst *mtmp) } /* mtmp can be killed */ - gb.bhitpos.x = mon->mx; - gb.bhitpos.y = mon->my; - gn.notonhead = 0; + gb.bhitpos.x = mon->mx, gb.bhitpos.y = mon->my; + gn.notonhead = FALSE; result = mattackm(mtmp, mon); if (result & M_ATTK_AGR_DIED) @@ -161,7 +160,8 @@ fightm(register struct monst *mtmp) mon->movement -= NORMAL_SPEED; else mon->movement = 0; - gn.notonhead = 0; + gb.bhitpos.x = mtmp->mx, gb.bhitpos.y = mtmp->my; + gn.notonhead = FALSE; (void) mattackm(mon, mtmp); /* return attack */ } @@ -177,8 +177,10 @@ fightm(register struct monst *mtmp) * returns same results as mattackm(). */ int -mdisplacem(register struct monst *magr, register struct monst *mdef, - boolean quietly) +mdisplacem( + struct monst *magr, + struct monst *mdef, + boolean quietly) { struct permonst *pa, *pd; int tx, ty, fx, fy; @@ -286,7 +288,9 @@ mdisplacem(register struct monst *magr, register struct monst *mdef, * In the case of exploding monsters, the monster dies as well. */ int -mattackm(register struct monst *magr, register struct monst *mdef) +mattackm( + register struct monst *magr, + register struct monst *mdef) { int i, /* loop counter */ tmp, /* armor class difference */ @@ -569,14 +573,15 @@ mattackm(register struct monst *magr, register struct monst *mdef) return (struck ? M_ATTK_HIT : M_ATTK_MISS); } -/* can't hold an unsolid target (ghosts, lights, vortices, most elementals) */ +/* can't hold an unsolid target (ghosts, lights, vortices, most elementals) + or a long worm tail */ boolean failed_grab( struct monst *magr, struct monst *mdef, struct attack *mattk) { - if (unsolid(mdef->data) + if ((unsolid(mdef->data) || gn.notonhead) /* hug attack: most holders (owlbear, python, pit fiend, &c); wrap damage: eel grabbing, trapper/lurker-above engulfing; stick-to damage: mimic, lichen; @@ -586,21 +591,30 @@ failed_grab( if ((gv.vis && canspotmon(mdef)) /* mon-vs-mon */ || magr == &gy.youmonst || mdef == &gy.youmonst) { char magrnam[BUFSZ], mdefnam[BUFSZ]; + boolean tailmiss = gn.notonhead; const char *verb = (mattk->adtyp == AD_DGST) ? "gulp" : (mattk->adtyp == AD_STCK) ? "adhere" : "grab"; /* beware of "Foo's grab passes through Bar's ghost"; mon_nam(x_monnam) calls s_suffix() for named ghosts and - s_suffix() uses a single static buffer; make copies of - both names to overcome that */ + s_suffix() uses a single static buffer; make copies of both + names to overcome that [note: comment predates 'tailmiss'] */ Strcpy(magrnam, (magr == &gy.youmonst) ? "Your" : s_suffix(Monnam(magr))); - Strcpy(mdefnam, (mdef == &gy.youmonst) ? "you" : mon_nam(mdef)); - /* this is actually somewhat iffy--how come ordinary attacks - don't also pass right through? */ - pline("%.99s %s attempt passes right through %.99s!", - magrnam, verb, mdefnam); + if (!tailmiss) { + Strcpy(mdefnam, (mdef == &gy.youmonst) ? "you" + : mon_nam(mdef)); + } else { + /* hero poly'd into long worm can't grow tail + so no 'youmonst' handling is needed here */ + Sprintf(mdefnam, "%s tail", s_suffix(some_mon_nam(mdef))); + } + /* unsolid grab misses are actually somewhat iffy--how come + ordinary attacks don't also pass right through? */ + pline("%.99s %s attempt %s %.99s!", magrnam, verb, + !tailmiss ? "passes right through" : "fails to hold", + mdefnam); } return TRUE; } @@ -972,8 +986,12 @@ explmm(struct monst *magr, struct monst *mdef, struct attack *mattk) * See comment at top of mattackm(), for return values. */ static int -mdamagem(struct monst *magr, struct monst *mdef, - struct attack *mattk, struct obj *mwep, int dieroll) +mdamagem( + struct monst *magr, + struct monst *mdef, + struct attack *mattk, + struct obj *mwep, + int dieroll) { struct permonst *pa = magr->data, *pd = mdef->data; struct mhitm_data mhm; @@ -1247,11 +1265,15 @@ mswingsm( * handled above. Returns same values as mattackm. */ static int -passivemm(register struct monst *magr, register struct monst *mdef, - boolean mhitb, int mdead, struct obj *mwep) +passivemm( + struct monst *magr, + struct monst *mdef, + boolean mhitb, + int mdead, + struct obj *mwep) { - register struct permonst *mddat = mdef->data; - register struct permonst *madat = magr->data; + struct permonst *mddat = mdef->data; + struct permonst *madat = magr->data; char buf[BUFSZ]; int i, tmp; int mhit = mhitb ? M_ATTK_HIT : M_ATTK_MISS; diff --git a/src/mhitu.c b/src/mhitu.c index f17538a19..e79f6d47e 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -391,14 +391,21 @@ getmattk(struct monst *magr, struct monst *mdef, /* calc some variables needed for mattacku() */ static void -calc_mattacku_vars(struct monst *mtmp, - boolean *ranged, boolean *range2, - boolean *foundyou, boolean *youseeit) +calc_mattacku_vars( + struct monst *mtmp, + boolean *ranged, boolean *range2, + boolean *foundyou, boolean *youseeit) { *ranged = (mdistu(mtmp) > 3); *range2 = !monnear(mtmp, mtmp->mux, mtmp->muy); *foundyou = u_at(mtmp->mux, mtmp->muy); *youseeit = canseemon(mtmp); + + /* do_attack() uses bhitpos to set/clear notonhead; do likewise here */ + gb.bhitpos.x = u.ux, gb.bhitpos.y = u.uy; + /* hero poly'd into a long worm isn't allowed to grow a tail, so + hitting tail instead of head can't happen */ + gn.notonhead = FALSE; } /* @@ -453,17 +460,19 @@ mattacku(register struct monst *mtmp) /* Your steed won't attack you */ return 0; /* Orcs like to steal and eat horses and the like */ - if (!rn2(is_orc(mtmp->data) ? 2 : 4) - && next2u(mtmp->mx, mtmp->my)) { - /* Attack your steed instead */ + if (!rn2(is_orc(mtmp->data) ? 2 : 4) && next2u(mtmp->mx, mtmp->my)) { + /* attack your steed instead; 'bhitpos' and 'notonhead' are + already set from tagetting hero */ i = mattackm(mtmp, u.usteed); - if ((i & M_ATTK_AGR_DIED)) + if ((i & M_ATTK_AGR_DIED) != 0) return 1; /* make sure steed is still alive and within range */ - if ((i & M_ATTK_DEF_DIED) || !u.usteed + if ((i & M_ATTK_DEF_DIED) != 0 || !u.usteed || !next2u(mtmp->mx, mtmp->my)) return 0; /* Let your steed retaliate */ + gb.bhitpos.x = mtmp->mx, gb.bhitpos.y = mtmp->my; + gn.notonhead = FALSE; return !!(mattackm(u.usteed, mtmp) & M_ATTK_DEF_DIED); } } @@ -565,8 +574,10 @@ mattacku(register struct monst *mtmp) else pline( "Wait, %s! There's a %s named %s hiding under %s!", - m_monnam(mtmp), pmname(gy.youmonst.data, Ugender), - gp.plname, doname(gl.level.objects[u.ux][u.uy])); + m_monnam(mtmp), + pmname(gy.youmonst.data, Ugender), + gp.plname, + doname(gl.level.objects[u.ux][u.uy])); if (obj) obj->spe = save_spe; } else diff --git a/src/uhitm.c b/src/uhitm.c index 319da0081..5748c6598 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -646,6 +646,7 @@ hitum_cleave( static boolean clockwise = FALSE; int i; coord save_bhitpos; + boolean save_notonhead; int count, umort, x = u.ux, y = u.uy; /* find the direction toward primary target */ @@ -661,6 +662,7 @@ hitum_cleave( i = clockwise ? DIR_LEFT2(i) : DIR_RIGHT2(i); umort = u.umortality; /* used to detect life-saving */ save_bhitpos = gb.bhitpos; + save_notonhead = gn.notonhead; /* * Three attacks: adjacent to primary, primary, adjacent on other @@ -692,8 +694,8 @@ hitum_cleave( mon_maybe_unparalyze(mtmp); dieroll = rnd(20); mhit = (tmp > dieroll); - gb.bhitpos.x = tx, gb.bhitpos.y = ty; /* normally set up by - do_attack() */ + gb.bhitpos.x = tx, gb.bhitpos.y = ty; /* normally set by do_attack() */ + gn.notonhead = (mtmp->mx != tx || mtmp->my != ty); (void) known_hitum(mtmp, uwep, &mhit, tmp, armorpenalty, uattk, dieroll); (void) passive(mtmp, uwep, mhit, !DEADMONSTER(mtmp), AT_WEAP, !uwep); @@ -706,7 +708,8 @@ hitum_cleave( /* set up for next time */ clockwise = !clockwise; /* alternate */ gb.bhitpos = save_bhitpos; /* in case somebody relies on bhitpos - * designating the primary target */ + * designating the primary target */ + gn.notonhead = save_notonhead; /* return False if primary target died, True otherwise; note: if 'target' was nonNull upon entry then it's still nonNull even if *target died */ @@ -3116,7 +3119,9 @@ mhitm_ad_wrap( if (magr == &gy.youmonst) { /* uhitm */ if (!sticks(pd)) { - if (!u.ustuck && !rn2(10)) { + boolean tailmiss = !gn.notonhead; + + if (!u.ustuck && !tailmiss && !rn2(10)) { if (m_slips_free(mdef, mattk)) { mhm->damage = 0; } else { @@ -3124,7 +3129,7 @@ mhitm_ad_wrap( coil ? "coil" : "swing", mon_nam(mdef)); set_ustuck(mdef); } - } else if (u.ustuck == mdef) { + } else if (u.ustuck == mdef && !tailmiss) { /* Monsters don't wear amulets of magical breathing */ if (is_pool(u.ux, u.uy) && !cant_drown(pd)) { You("drown %s...", mon_nam(mdef)); @@ -3134,11 +3139,11 @@ mhitm_ad_wrap( } else { mhm->damage = 0; if (Verbose(4, mhitm_ad_wrap1)) { - if (coil) + if (coil && !tailmiss) You("brush against %s.", mon_nam(mdef)); else You("brush against %s %s.", s_suffix(mon_nam(mdef)), - mbodypart(mdef, LEG)); + tailmiss ? "tail" : mbodypart(mdef, LEG)); } } } else @@ -3187,6 +3192,11 @@ mhitm_ad_wrap( /* mhitm */ if (magr->mcan) mhm->damage = 0; + + if (!mhm->damage && (canseemon(magr) || canseemon(mdef))) { + pline("%s brushes against %s.", + Some_Monnam(magr), some_mon_nam(mdef)); + } } }