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:
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
303
src/dogmove.c
303
src/dogmove.c
@@ -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;
|
||||
|
||||
49
src/mhitm.c
49
src/mhitm.c
@@ -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)
|
||||
|
||||
262
src/mthrowu.c
262
src/mthrowu.c
@@ -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)
|
||||
|
||||
18
src/zap.c
18
src/zap.c
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user