Allow pets to use ranged attacks

This is the Pet ranged attack -patch by Darshan Shaligram,
with the spellcaster parts removed to keep it simpler.

Pets will now throw, spit and breathe at other monsters.
This commit is contained in:
Pasi Kallinen
2016-06-04 01:06:00 +03:00
parent 4d594f77c2
commit 5009a67264
6 changed files with 626 additions and 11 deletions

View File

@@ -436,6 +436,7 @@ Ray Chason's proper background tiles for lava and water
Ray Chason's MS-DOS port restored to functionality with credit to Reddit user
b_helyer for the fix to sys/share/pcmain.c
Ray Chason's MSDOS port support for some VESA modes
Darshan Shaligram's pet ranged attack
Code Cleanup and Reorganization

View File

@@ -1512,6 +1512,9 @@ E int FDECL(breamu, (struct monst *, struct attack *));
E boolean FDECL(linedup, (XCHAR_P, XCHAR_P, XCHAR_P, XCHAR_P, int));
E boolean FDECL(lined_up, (struct monst *));
E struct obj *FDECL(m_carrying, (struct monst *, int));
E int FDECL(thrwmm, (struct monst *, struct monst *));
E int FDECL(spitmm, (struct monst *, struct attack *, struct monst *));
E int FDECL(breamm, (struct monst *, struct attack *, struct monst *));
E void FDECL(m_useupall, (struct monst *, struct obj *));
E void FDECL(m_useup, (struct monst *, struct obj *));
E void FDECL(m_throw, (struct monst *, int, int, int, int, int, struct obj *));
@@ -2797,6 +2800,7 @@ E struct monst *FDECL(boomhit, (struct obj *, int, int));
E int FDECL(zhitm, (struct monst *, int, int, struct obj **));
E int FDECL(burn_floor_objects, (int, int, BOOLEAN_P, BOOLEAN_P));
E void FDECL(buzz, (int, int, XCHAR_P, XCHAR_P, int, int));
E void FDECL(dobuzz, (int, int, XCHAR_P, XCHAR_P, int, int, boolean));
E void FDECL(melt_ice, (XCHAR_P, XCHAR_P, const char *));
E void FDECL(start_melt_ice_timeout, (XCHAR_P, XCHAR_P, long));
E void FDECL(melt_ice_away, (ANY_P *, long));

View File

@@ -11,6 +11,10 @@ extern boolean notonhead;
STATIC_DCL boolean FDECL(dog_hunger, (struct monst *, struct edog *));
STATIC_DCL int FDECL(dog_invent, (struct monst *, struct edog *, int));
STATIC_DCL int FDECL(dog_goal, (struct monst *, struct edog *, int, int, int));
STATIC_DCL struct monst* FDECL(find_targ, (struct monst *, int, int, int));
STATIC_OVL int FDECL(find_friends, (struct monst *, struct monst *, int));
STATIC_DCL struct monst* FDECL(best_target, (struct monst *));
STATIC_DCL long FDECL(score_targ, (struct monst *, struct monst *));
STATIC_DCL boolean FDECL(can_reach_location, (struct monst *, XCHAR_P,
XCHAR_P, XCHAR_P, XCHAR_P));
STATIC_DCL boolean FDECL(could_reach_item, (struct monst *, XCHAR_P, XCHAR_P));
@@ -610,6 +614,249 @@ int after, udist, whappr;
return appr;
}
STATIC_OVL struct monst *
find_targ(mtmp, dx, dy, maxdist)
register struct monst *mtmp;
int dx, dy;
int maxdist;
{
struct monst *targ = 0;
int curx = mtmp->mx, cury = mtmp->my;
int dist = 0;
/* Walk outwards */
for ( ; dist < maxdist; ++dist) {
curx += dx;
cury += dy;
if (!isok(curx, cury))
break;
/* FIXME: Check if we hit a wall/door/boulder to
* short-circuit unnecessary subsequent checks
*/
/* If we can't see up to here, forget it - will this
* mean pets in corridors don't breathe at monsters
* in rooms? If so, is that necessarily bad?
*/
if (!m_cansee(mtmp, curx, cury))
break;
targ = m_at(curx, cury);
if (curx == mtmp->mux && cury == mtmp->muy)
return &youmonst;
if (targ) {
/* Is the monster visible to the pet? */
if ((!targ->minvis || perceives(mtmp->data)) &&
!targ->mundetected)
break;
/* If the pet can't see it, it assumes it aint there */
targ = 0;
}
}
return targ;
}
STATIC_OVL int
find_friends(mtmp, mtarg, maxdist)
struct monst *mtmp, *mtarg;
int maxdist;
{
struct monst *pal;
int dx = sgn(mtarg->mx - mtmp->mx),
dy = sgn(mtarg->my - mtmp->my);
int curx = mtarg->mx, cury = mtarg->my;
int dist = distmin(mtarg->mx, mtarg->my, mtmp->mx, mtmp->my);
for ( ; dist <= maxdist; ++dist) {
curx += dx;
cury += dy;
if (!isok(curx, cury))
return 0;
/* If the pet can't see beyond this point, don't
* check any farther
*/
if (!m_cansee(mtmp, curx, cury))
return 0;
/* Does pet think you're here? */
if (mtmp->mux == curx && mtmp->muy == cury)
return 1;
pal = m_at(curx, cury);
if (pal) {
if (pal->mtame) {
/* Pet won't notice invisible pets */
if (!pal->minvis || perceives(mtmp->data))
return 1;
} else {
/* Quest leaders and guardians are always seen */
if (pal->data->msound == MS_LEADER
|| pal->data->msound == MS_GUARDIAN)
return 1;
}
}
}
return 0;
}
STATIC_OVL long
score_targ(mtmp, mtarg)
struct monst *mtmp, *mtarg;
{
long score = 0L;
/* If the monster is confused, normal scoring is disrupted -
* anything may happen
*/
/* Give 1 in 3 chance of safe breathing even if pet is confused or
* if you're on the quest start level */
if (!mtmp->mconf || !rn2(3) || Is_qstart(&u.uz)) {
aligntyp align1, align2; /* For priests, minions */
boolean faith1 = TRUE, faith2 = TRUE;
if (mtmp->isminion) align1 = EMIN(mtmp)->min_align;
else if (mtmp->ispriest) align1 = EPRI(mtmp)->shralign;
else faith1 = FALSE;
if (mtarg->isminion) align2 = EMIN(mtarg)->min_align; /* MAR */
else if (mtarg->ispriest) align2 = EPRI(mtarg)->shralign; /* MAR */
else faith2 = FALSE;
/* Never target quest friendlies */
if (mtarg->data->msound == MS_LEADER
|| mtarg->data->msound == MS_GUARDIAN)
return -5000L;
/* D: Fixed angelic beings using gaze attacks on coaligned priests */
if (faith1 && faith2 && align1 == align2 && mtarg->mpeaceful) {
score -= 5000L;
return score;
}
/* Is monster adjacent? */
if (distmin(mtmp->mx, mtmp->my, mtarg->mx, mtarg->my) <= 1) {
score -= 3000L;
return score;
}
/* Is the monster peaceful or tame? */
if (/*mtarg->mpeaceful ||*/ mtarg->mtame || mtarg == &youmonst) {
/* Pets will never be targeted */
score -= 3000L;
return score;
}
/* Is master/pet behind monster? Check up to 15 squares beyond
* pet.
*/
if (find_friends(mtmp, mtarg, 15)) {
score -= 3000L;
return score;
}
/* Target hostile monsters in preference to peaceful ones */
if (!mtarg->mpeaceful)
score += 10;
/* Is the monster passive? Don't waste energy on it, if so */
if (mtarg->data->mattk[0].aatyp == AT_NONE)
score -= 1000;
/* Even weak pets with breath attacks shouldn't take on very
* low-level monsters. Wasting breath on lichens is ridiculous.
*/
if ((mtarg->m_lev < 2 && mtmp->m_lev > 5)
|| (mtmp->m_lev > 12 && mtarg->m_lev < mtmp->m_lev - 9
&& u.ulevel > 8 && mtarg->m_lev < u.ulevel - 7))
score -= 25;
/* And pets will hesitate to attack vastly stronger foes.
* This penalty will be discarded if master's in trouble.
*/
if (mtarg->m_lev > mtmp->m_lev + 4L)
score -= (mtarg->m_lev - mtmp->m_lev) * 20L;
/* All things being the same, go for the beefiest monster. This
* bonus should not be large enough to override the pet's aversion
* to attacking much stronger monsters.
*/
score += mtarg->m_lev * 2 + mtarg->mhp / 3;
}
/* Fuzz factor to make things less predictable when very
* similar targets are abundant
*/
score += rnd(5);
/* Pet may decide not to use ranged attack when confused */
if (mtmp->mconf && !rn2(3))
score -= 1000;
return score;
}
STATIC_OVL struct monst *
best_target(mtmp)
struct monst *mtmp; /* Pet */
{
int dx, dy;
long bestscore = -40000L, currscore;
struct monst *best_targ = 0, *temp_targ = 0;
/* Help! */
if (!mtmp)
return 0;
/* If the pet is blind, it's not going to see any target */
if (!mtmp->mcansee)
return 0;
/* Search for any monsters lined up with the pet, within an arbitrary
* distance from the pet (7 squares, even along diagonals). Monsters
* are assigned scores and the best score is chosen.
*/
for (dy = -1; dy < 2; ++dy) {
for (dx = -1; dx < 2; ++dx) {
if (!dx && !dy)
continue;
/* Traverse the line to find the first monster within 7
* squares. Invisible monsters are skipped (if the
* pet doesn't have see invisible).
*/
temp_targ = find_targ(mtmp, dx, dy, 7);
/* Nothing in this line? */
if (!temp_targ)
continue;
/* Decide how attractive the target is */
currscore = score_targ(mtmp, temp_targ);
if (currscore > bestscore) {
bestscore = currscore;
best_targ = temp_targ;
}
}
}
/* Filter out targets the pet doesn't like */
if (bestscore < 0L)
best_targ = 0;
return best_targ;
}
/* return 0 (no move), 1 (move) or 2 (dead) */
int
dog_move(mtmp, after)
@@ -872,6 +1119,62 @@ int after; /* this is extra fast monster movement */
nxti:
;
}
/* Pet hasn't attacked anything but is considering moving -
* now's the time for ranged attacks. Note that the pet can
* move after it performs its ranged attack. Should this be
* changed?
*/
{
struct monst *mtarg;
int hungry = 0;
/* How hungry is the pet? */
if (!mtmp->isminion) {
struct edog *dog = EDOG(mtmp);
hungry = (monstermoves > (dog->hungrytime + 300));
}
/* Identify the best target in a straight line from the pet;
* if there is such a target, we'll let the pet attempt an
* attack.
*/
mtarg = best_target(mtmp);
/* Hungry pets are unlikely to use breath/spit attacks */
if (mtarg && (!hungry || !rn2(5))) {
int mstatus;
if (mtarg == &youmonst) {
if (mattacku(mtmp))
return 2;
} else {
mstatus = mattackm(mtmp, mtarg);
/* Shouldn't happen, really */
if (mstatus & MM_AGR_DIED) return 2;
/* Allow the targeted nasty to strike back - if
* the targeted beast doesn't have a ranged attack,
* nothing will happen.
*/
if ((mstatus & MM_HIT) && !(mstatus & MM_DEF_DIED)
&& rn2(4) && mtarg != &youmonst) {
/* Can monster see? If it can, it can retaliate
* even if the pet is invisible, since it'll see
* the direction from which the ranged attack came;
* if it's blind or unseeing, it can't retaliate
*/
if (mtarg->mcansee && haseyes(mtarg->data)) {
mstatus = mattackm(mtarg, mtmp);
if (mstatus & MM_DEF_DIED) return 2;
}
}
}
}
}
newdogpos:
if (nix != omx || niy != omy) {
boolean wasseen;

View File

@@ -356,6 +356,17 @@ register struct monst *magr, *mdef;
attk = 1;
switch (mattk->aatyp) {
case AT_WEAP: /* "hand to hand" attacks */
if (distmin(magr->mx,magr->my,mdef->mx,mdef->my) > 1) {
/* D: Do a ranged attack here! */
strike = thrwmm(magr, mdef);
if (DEADMONSTER(mdef))
res[i] = MM_DEF_DIED;
if (DEADMONSTER(magr))
res[i] |= MM_AGR_DIED;
break;
}
if (magr->weapon_check == NEED_WEAPON || !MON_WEP(magr)) {
magr->weapon_check = NEED_HTH_WEAPON;
if (mon_wield_item(magr) != 0)
@@ -379,7 +390,9 @@ register struct monst *magr, *mdef;
case AT_TENT:
/* Nymph that teleported away on first attack? */
if (distmin(magr->mx, magr->my, mdef->mx, mdef->my) > 1)
return MM_MISS;
/* Continue because the monster may have a ranged
* attack */
continue;
/* Monsters won't attack cockatrices physically if they
* have a weapon instead. This instinct doesn't work for
* players, or under conflict or confusion.
@@ -428,6 +441,10 @@ register struct monst *magr, *mdef;
break;
case AT_EXPL:
/* D: Prevent explosions from a distance */
if (distmin(magr->mx,magr->my,mdef->mx,mdef->my) > 1)
continue;
res[i] = explmm(magr, mdef, mattk);
if (res[i] == MM_MISS) { /* cancelled--no attack */
strike = 0;
@@ -441,6 +458,9 @@ register struct monst *magr, *mdef;
strike = 0;
break;
}
/* D: Prevent engulf from a distance */
if (distmin(magr->mx,magr->my,mdef->mx,mdef->my) > 1)
continue;
/* Engulfing attacks are directed at the hero if
* possible. -dlc
*/
@@ -454,13 +474,38 @@ register struct monst *magr, *mdef;
}
break;
case AT_BREA:
if (!monnear(magr, mdef->mx, mdef->my)) {
strike = breamm(magr, mattk, mdef);
/* We don't really know if we hit or not, but pretend
* we did */
if (strike) res[i] |= MM_HIT;
if (DEADMONSTER(mdef)) res[i] = MM_DEF_DIED;
if (DEADMONSTER(magr)) res[i] |= MM_AGR_DIED;
}
else
strike = 0;
break;
case AT_SPIT:
if (!monnear(magr, mdef->mx, mdef->my)) {
strike = spitmm(magr, mattk, mdef);
/* We don't really know if we hit or not, but pretend
* we did */
if (strike) res[i] |= MM_HIT;
if (DEADMONSTER(mdef)) res[i] = MM_DEF_DIED;
if (DEADMONSTER(magr)) res[i] |= MM_AGR_DIED;
}
break;
default: /* no attack */
strike = 0;
attk = 0;
break;
}
if (attk && !(res[i] & MM_AGR_DIED))
if (attk && !(res[i] & MM_AGR_DIED)
&& distmin(magr->mx,magr->my,mdef->mx,mdef->my) <= 1)
res[i] = passivemm(magr, mdef, strike, res[i] & MM_DEF_DIED);
if (res[i] & MM_DEF_DIED)

View File

@@ -5,12 +5,15 @@
#include "hack.h"
STATIC_DCL int FDECL(drop_throw, (struct obj *, BOOLEAN_P, int, int));
STATIC_DCL boolean FDECL(m_lined_up, (struct monst *, struct monst *));
#define URETREATING(x, y) \
(distmin(u.ux, u.uy, x, y) > distmin(u.ux0, u.uy0, x, y))
#define POLE_LIM 5 /* How far monsters can use pole-weapons */
#define PET_MISSILE_RANGE2 36 /* Square of distance within which pets shoot */
/*
* Keep consistent with breath weapons in zap.c, and AD_* in monattk.h.
*/
@@ -129,6 +132,11 @@ int x, y;
return retvalu;
}
/* The monster that's being shot at when one monster shoots at another */
STATIC_OVL struct monst *target = 0;
/* The monster that's doing the shooting/throwing */
STATIC_OVL struct monst *archer = 0;
/* an object launched by someone/thing other than player attacks a monster;
return 1 if the object has stopped moving (hit or its range used up) */
int
@@ -143,17 +151,27 @@ boolean verbose; /* give message(s) even when you can't see what happened */
int damage, tmp;
boolean vis, ismimic;
int objgone = 1;
struct obj *mon_launcher = archer ? MON_WEP(archer) : NULL;
notonhead = (bhitpos.x != mtmp->mx || bhitpos.y != mtmp->my);
ismimic = mtmp->m_ap_type && mtmp->m_ap_type != M_AP_MONSTER;
vis = cansee(bhitpos.x, bhitpos.y);
tmp = 5 + find_mac(mtmp) + omon_adj(mtmp, otmp, FALSE);
/* High level monsters will be more likely to hit */
/* This check applies only if this monster is the target
* the archer was aiming at. */
if (archer && target == mtmp) {
if (archer->m_lev > 5)
tmp += archer->m_lev - 5;
if (mon_launcher && mon_launcher->oartifact)
tmp += spec_abon(mon_launcher, mtmp);
}
if (tmp < rnd(20)) {
if (!ismimic) {
if (vis)
miss(distant_name(otmp, mshot_xname), mtmp);
else if (verbose)
else if (verbose && !target)
pline("It is missed.");
}
if (!range) { /* Last position; object drops */
@@ -177,7 +195,7 @@ boolean verbose; /* give message(s) even when you can't see what happened */
mtmp->msleeping = 0;
if (vis)
hit(distant_name(otmp, mshot_xname), mtmp, exclam(damage));
else if (verbose)
else if (verbose && !target)
pline("%s is hit%s", Monnam(mtmp), exclam(damage));
if (otmp->opoisoned && is_poisonable(otmp)) {
@@ -199,24 +217,24 @@ boolean verbose; /* give message(s) even when you can't see what happened */
&& mon_hates_silver(mtmp)) {
if (vis)
pline_The("silver sears %s flesh!", s_suffix(mon_nam(mtmp)));
else if (verbose)
else if (verbose && !target)
pline("Its flesh is seared!");
}
if (otmp->otyp == ACID_VENOM && cansee(mtmp->mx, mtmp->my)) {
if (resists_acid(mtmp)) {
if (vis || verbose)
if (vis || (verbose && !target))
pline("%s is unaffected.", Monnam(mtmp));
damage = 0;
} else {
if (vis)
pline_The("acid burns %s!", mon_nam(mtmp));
else if (verbose)
else if (verbose && !target)
pline("It is burned!");
}
}
mtmp->mhp -= damage;
if (mtmp->mhp < 1) {
if (vis || verbose)
if (vis || (verbose && !target))
pline("%s is %s!", Monnam(mtmp),
(nonliving(mtmp->data) || is_vampshifter(mtmp)
|| !canspotmon(mtmp))
@@ -482,6 +500,230 @@ struct obj *obj; /* missile (or stack providing it) */
}
}
int
thrwmm(mtmp, mtarg) /* Monster throws item at monster */
struct monst *mtmp, *mtarg;
{
struct obj *otmp, *mwep;
register xchar x, y;
boolean ispole;
schar skill;
int multishot = 1;
/* Polearms won't be applied by monsters against other monsters */
if (mtmp->weapon_check == NEED_WEAPON || !MON_WEP(mtmp)) {
mtmp->weapon_check = NEED_RANGED_WEAPON;
/* mon_wield_item resets weapon_check as appropriate */
if(mon_wield_item(mtmp) != 0) return 0;
}
/* Pick a weapon */
otmp = select_rwep(mtmp);
if (!otmp) return 0;
ispole = is_pole(otmp);
skill = objects[otmp->otyp].oc_skill;
x = mtmp->mx;
y = mtmp->my;
mwep = MON_WEP(mtmp); /* wielded weapon */
if(!ispole && m_lined_up(mtarg, mtmp)) {
/* WAC Catch this since rn2(0) is illegal */
int chance = (BOLT_LIM-distmin(x,y,mtarg->mx,mtarg->my) > 0) ?
BOLT_LIM-distmin(x,y,mtarg->mx,mtarg->my) : 1;
if(!mtarg->mflee || !rn2(chance)) {
const char *verb = "throws";
if (otmp->otyp == ARROW
|| otmp->otyp == ELVEN_ARROW
|| otmp->otyp == ORCISH_ARROW
|| otmp->otyp == YA
|| otmp->otyp == CROSSBOW_BOLT) verb = "shoots";
if (ammo_and_launcher(otmp, mwep) && is_launcher(mwep)) {
if (dist2(mtmp->mx, mtmp->my, mtarg->mx, mtarg->my) >
PET_MISSILE_RANGE2)
return 0; /* Out of range */
}
if (canseemon(mtmp)) {
pline("%s %s %s!", Monnam(mtmp), verb,
obj_is_pname(otmp) ?
the(singular(otmp, xname)) :
an(singular(otmp, xname)));
}
/* Multishot calculations */
if ((ammo_and_launcher(otmp, mwep) || skill == P_DAGGER
|| skill == -P_DART || skill == -P_SHURIKEN)
&& !mtmp->mconf) {
/* Assumes lords are skilled, princes are expert */
if (is_lord(mtmp->data)) multishot++;
if (is_prince(mtmp->data)) multishot += 2;
/* Elven Craftsmanship makes for light, quick bows */
if (otmp->otyp == ELVEN_ARROW && !otmp->cursed)
multishot++;
if (mwep && mwep->otyp == ELVEN_BOW &&
!mwep->cursed) multishot++;
/* 1/3 of object enchantment */
if (mwep && mwep->spe > 1)
multishot += (long) rounddiv(mwep->spe,3);
/* Some randomness */
if (multishot > 1L)
multishot = (long) rnd((int) multishot);
switch (monsndx(mtmp->data)) {
case PM_RANGER:
multishot++;
break;
case PM_ROGUE:
if (skill == P_DAGGER) multishot++;
break;
case PM_SAMURAI:
if (otmp->otyp == YA && mwep &&
mwep->otyp == YUMI) multishot++;
break;
default:
break;
}
{ /* racial bonus */
if (is_elf(mtmp->data) &&
otmp->otyp == ELVEN_ARROW &&
mwep && mwep->otyp == ELVEN_BOW)
multishot++;
else if (is_orc(mtmp->data) &&
otmp->otyp == ORCISH_ARROW &&
mwep && mwep->otyp == ORCISH_BOW)
multishot++;
}
}
if (otmp->quan < multishot) multishot = (int)otmp->quan;
if (multishot < 1) multishot = 1;
/* Set target monster */
target = mtarg;
archer = mtmp;
while (multishot-- > 0)
m_throw(mtmp, mtmp->mx, mtmp->my,
sgn(tbx), sgn(tby),
distmin(mtmp->mx, mtmp->my,
mtarg->mx, mtarg->my),
otmp);
archer = (struct monst *)0;
target = (struct monst *)0;
nomul(0);
return 1;
}
}
return 0;
}
/* monster spits substance at monster */
int
spitmm(mtmp, mattk, mtarg)
struct monst *mtmp, *mtarg;
struct attack *mattk;
{
struct obj *otmp;
if (mtmp->mcan) {
if (!Deaf)
pline("A dry rattle comes from %s throat.",
s_suffix(mon_nam(mtmp)));
return 0;
}
if (m_lined_up(mtarg, mtmp)) {
switch (mattk->adtyp) {
case AD_BLND:
case AD_DRST:
otmp = mksobj(BLINDING_VENOM, TRUE, FALSE);
break;
default:
impossible("bad attack type in spitmu");
/* fall through */
case AD_ACID:
otmp = mksobj(ACID_VENOM, TRUE, FALSE);
break;
}
if (!rn2(BOLT_LIM-distmin(mtmp->mx,mtmp->my,mtarg->mx,mtarg->my))) {
if (canseemon(mtmp))
pline("%s spits venom!", Monnam(mtmp));
target = mtarg;
m_throw(mtmp, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby),
distmin(mtmp->mx,mtmp->my,mtarg->mx,mtarg->my), otmp);
target = (struct monst *)0;
nomul(0);
/* If this is a pet, it'll get hungry. Minions and
* spell beings won't hunger */
if (mtmp->mtame && !mtmp->isminion) {
struct edog *dog = EDOG(mtmp);
/* Hunger effects will catch up next move */
if (dog->hungrytime > 1)
dog->hungrytime -= 5;
}
return 1;
}
}
return 0;
}
int
breamm(mtmp, mattk, mtarg) /* monster breathes at monster (ranged) */
struct monst *mtmp, *mtarg;
struct attack *mattk;
{
/* if new breath types are added, change AD_ACID to max type */
int typ = (mattk->adtyp == AD_RBRE) ? rnd(AD_ACID) : mattk->adtyp ;
if (m_lined_up(mtarg, mtmp)) {
if (mtmp->mcan) {
if (!Deaf) {
if (canseemon(mtmp))
pline("%s coughs.", Monnam(mtmp));
else
You_hear("a cough.");
}
return(0);
}
if (!mtmp->mspec_used && rn2(3)) {
if ((typ >= AD_MAGM) && (typ <= AD_ACID)) {
if (canseemon(mtmp))
pline("%s breathes %s!", Monnam(mtmp),
breathwep[typ-1]);
dobuzz((int) (-20 - (typ-1)), (int)mattk->damn,
mtmp->mx, mtmp->my, sgn(tbx), sgn(tby), FALSE);
nomul(0);
/* breath runs out sometimes. Also, give monster some
* cunning; don't breath if the target fell asleep.
*/
mtmp->mspec_used = 6+rn2(18);
/* If this is a pet, it'll get hungry. Minions and
* spell beings won't hunger */
if (mtmp->mtame && !mtmp->isminion) {
struct edog *dog = EDOG(mtmp);
/* Hunger effects will catch up next move */
if (dog->hungrytime >= 10)
dog->hungrytime -= 10;
}
} else impossible("Breath weapon %d used", typ-1);
} else
return (0);
}
return(1);
}
/* remove an entire item from a monster's inventory; destroy that item */
void
m_useupall(mon, obj)
@@ -803,6 +1045,14 @@ int boulderhandling; /* 0=block, 1=ignore, 2=conditionally block */
return FALSE;
}
STATIC_OVL boolean
m_lined_up(mtarg, mtmp)
struct monst *mtarg, *mtmp;
{
return (linedup(mtarg->mx, mtarg->my, mtmp->mx, mtmp->my, 0));
}
/* is mtmp in position to use ranged attack? */
boolean
lined_up(mtmp)

View File

@@ -3869,6 +3869,15 @@ const char *fltxt;
xkilled(mon, XKILL_NOMSG | XKILL_NOCORPSE);
}
void
buzz(type,nd,sx,sy,dx,dy)
int type, nd;
xchar sx,sy;
int dx,dy;
{
dobuzz(type, nd, sx, sy, dx, dy, TRUE);
}
/*
* type == 0 to 9 : you shooting a wand
* type == 10 to 19 : you casting a spell
@@ -3879,10 +3888,11 @@ const char *fltxt;
* called with dx = dy = 0 with vertical bolts
*/
void
buzz(type, nd, sx, sy, dx, dy)
dobuzz(type, nd, sx, sy, dx, dy,say)
register int type, nd;
register xchar sx, sy;
register int dx, dy;
boolean say; /* Announce out of sight hit/miss events if true */
{
int range, abstype = abs(type) % 10;
register xchar lsx, lsy;
@@ -4009,7 +4019,8 @@ register int dx, dy;
} else {
if (!otmp) {
/* normal non-fatal hit */
hit(fltxt, mon, exclam(tmp));
if (say || canseemon(mon))
hit(fltxt, mon, exclam(tmp));
} else {
/* some armor was destroyed; no damage done */
if (canseemon(mon))
@@ -4024,7 +4035,8 @@ register int dx, dy;
}
range -= 2;
} else {
miss(fltxt, mon);
if (say || canseemon(mon))
miss(fltxt, mon);
}
} else if (sx == u.ux && sy == u.uy && range >= 0) {
nomul(0);