multishot by monsters, plus reformatting

Some reformatting of the recently added pet ranged attack code.

The redundant--but different--multishot volley code has been replaced
so that there are only two versions (hero and monster) instead of
three (hero and monster vs hero and pet vs other monster).  The monst
version was out of date relative to post-3.4.3 changes to the hero one.
The pet version was way out of date and had some bugs:  wielding an
elven bow gave a +1 multishot increment to volley count for fast weapon
even when throwing something rather than shooting arrows, wielding any
weapon which had at least +2 enchantment gave 1/3 enchantment bonus to
volley count when throwing instead of shooting shoot ammo, and a pet
which got killed in the midst of a multishot volley--perhaps by a gas
spore explosion or some other passive counterattack--would keep on
shooting/throwing until the volley count was exhausted.

Pet use of ranged weapons is not ready for prime-time.  Pets don't
hang on to missiles or launchers+ammo, they just drop them if there is
no target immediately available.
This commit is contained in:
PatR
2016-06-06 17:42:58 -07:00
parent e276540230
commit 8c0810f687
5 changed files with 333 additions and 367 deletions
+40 -55
View File
@@ -11,9 +11,9 @@ extern boolean notonhead;
STATIC_DCL boolean FDECL(dog_hunger, (struct monst *, struct edog *)); 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_invent, (struct monst *, struct edog *, int));
STATIC_DCL int FDECL(dog_goal, (struct monst *, struct edog *, int, int, 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_DCL struct monst *FDECL(find_targ, (struct monst *, int, int, int));
STATIC_OVL int FDECL(find_friends, (struct monst *, struct monst *, int)); STATIC_OVL int FDECL(find_friends, (struct monst *, struct monst *, int));
STATIC_DCL struct monst* FDECL(best_target, (struct monst *)); STATIC_DCL struct monst *FDECL(best_target, (struct monst *));
STATIC_DCL long FDECL(score_targ, (struct monst *, struct monst *)); STATIC_DCL long FDECL(score_targ, (struct monst *, struct monst *));
STATIC_DCL boolean FDECL(can_reach_location, (struct monst *, XCHAR_P, STATIC_DCL boolean FDECL(can_reach_location, (struct monst *, XCHAR_P,
XCHAR_P, XCHAR_P, XCHAR_P)); XCHAR_P, XCHAR_P, XCHAR_P));
@@ -284,8 +284,8 @@ boolean devour;
Strcpy(objnambuf, xname(obj)); Strcpy(objnambuf, xname(obj));
iflags.suppress_price--; iflags.suppress_price--;
} }
/* It's a reward if it's DOGFOOD and the player dropped/threw it. */ /* It's a reward if it's DOGFOOD and the player dropped/threw it.
/* We know the player had it if invlet is set -dlc */ We know the player had it if invlet is set. -dlc */
if (dogfood(mtmp, obj) == DOGFOOD && obj->invlet) if (dogfood(mtmp, obj) == DOGFOOD && obj->invlet)
#ifdef LINT #ifdef LINT
edog->apport = 0; edog->apport = 0;
@@ -351,8 +351,8 @@ boolean devour;
/* hunger effects -- returns TRUE on starvation */ /* hunger effects -- returns TRUE on starvation */
STATIC_OVL boolean STATIC_OVL boolean
dog_hunger(mtmp, edog) dog_hunger(mtmp, edog)
register struct monst *mtmp; struct monst *mtmp;
register struct edog *edog; struct edog *edog;
{ {
if (monstermoves > edog->hungrytime + 500) { if (monstermoves > edog->hungrytime + 500) {
if (!carnivorous(mtmp->data) && !herbivorous(mtmp->data)) { if (!carnivorous(mtmp->data) && !herbivorous(mtmp->data)) {
@@ -423,7 +423,8 @@ int udist;
edog->droptime = monstermoves; edog->droptime = monstermoves;
} }
} else { } else {
if ((obj = level.objects[omx][omy]) && !index(nofetch, obj->oclass) if ((obj = level.objects[omx][omy]) != 0
&& !index(nofetch, obj->oclass)
#ifdef MAIL #ifdef MAIL
&& obj->otyp != SCR_MAIL && obj->otyp != SCR_MAIL
#endif #endif
@@ -561,16 +562,12 @@ int after, udist, whappr;
appr = 1; appr = 1;
} }
/* if you have dog food it'll follow you more closely */ /* if you have dog food it'll follow you more closely */
if (appr == 0) { if (appr == 0)
obj = invent; for (obj = invent; obj; obj = obj->nobj)
while (obj) {
if (dogfood(mtmp, obj) == DOGFOOD) { if (dogfood(mtmp, obj) == DOGFOOD) {
appr = 1; appr = 1;
break; break;
} }
obj = obj->nobj;
}
}
} else } else
appr = 1; /* gtyp != UNDEF */ appr = 1; /* gtyp != UNDEF */
if (mtmp->mconf) if (mtmp->mconf)
@@ -589,7 +586,7 @@ int after, udist, whappr;
} else { } else {
/* assume master hasn't moved far, and reuse previous goal */ /* assume master hasn't moved far, and reuse previous goal */
if (edog && edog->ogoal.x if (edog && edog->ogoal.x
&& ((edog->ogoal.x != omx) || (edog->ogoal.y != omy))) { && (edog->ogoal.x != omx || edog->ogoal.y != omy)) {
gx = edog->ogoal.x; gx = edog->ogoal.x;
gy = edog->ogoal.y; gy = edog->ogoal.y;
edog->ogoal.x = 0; edog->ogoal.x = 0;
@@ -707,7 +704,6 @@ int maxdist;
return 0; return 0;
} }
STATIC_OVL long STATIC_OVL long
score_targ(mtmp, mtarg) score_targ(mtmp, mtarg)
struct monst *mtmp, *mtarg; struct monst *mtmp, *mtarg;
@@ -721,86 +717,74 @@ struct monst *mtmp, *mtarg;
/* Give 1 in 3 chance of safe breathing even if pet is confused or /* Give 1 in 3 chance of safe breathing even if pet is confused or
* if you're on the quest start level */ * if you're on the quest start level */
if (!mtmp->mconf || !rn2(3) || Is_qstart(&u.uz)) { if (!mtmp->mconf || !rn2(3) || Is_qstart(&u.uz)) {
aligntyp align1, align2; /* For priests, minions */ aligntyp align1 = A_NONE, align2 = A_NONE; /* For priests, minions */
boolean faith1 = TRUE, faith2 = TRUE; boolean faith1 = TRUE, faith2 = TRUE;
if (mtmp->isminion) align1 = EMIN(mtmp)->min_align; if (mtmp->isminion)
else if (mtmp->ispriest) align1 = EPRI(mtmp)->shralign; align1 = EMIN(mtmp)->min_align;
else faith1 = FALSE; else if (mtmp->ispriest)
if (mtarg->isminion) align2 = EMIN(mtarg)->min_align; /* MAR */ align1 = EPRI(mtmp)->shralign;
else if (mtarg->ispriest) align2 = EPRI(mtarg)->shralign; /* MAR */ else
else faith2 = FALSE; 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 */ /* Never target quest friendlies */
if (mtarg->data->msound == MS_LEADER if (mtarg->data->msound == MS_LEADER
|| mtarg->data->msound == MS_GUARDIAN) || mtarg->data->msound == MS_GUARDIAN)
return -5000L; return -5000L;
/* D: Fixed angelic beings using gaze attacks on coaligned priests */ /* D: Fixed angelic beings using gaze attacks on coaligned priests */
if (faith1 && faith2 && align1 == align2 && mtarg->mpeaceful) { if (faith1 && faith2 && align1 == align2 && mtarg->mpeaceful) {
score -= 5000L; score -= 5000L;
return score; return score;
} }
/* Is monster adjacent? */ /* Is monster adjacent? */
if (distmin(mtmp->mx, mtmp->my, mtarg->mx, mtarg->my) <= 1) { if (distmin(mtmp->mx, mtmp->my, mtarg->mx, mtarg->my) <= 1) {
score -= 3000L; score -= 3000L;
return score; return score;
} }
/* Is the monster peaceful or tame? */ /* Is the monster peaceful or tame? */
if (/*mtarg->mpeaceful ||*/ mtarg->mtame || mtarg == &youmonst) { if (/*mtarg->mpeaceful ||*/ mtarg->mtame || mtarg == &youmonst) {
/* Pets will never be targeted */ /* Pets will never be targeted */
score -= 3000L; score -= 3000L;
return score; return score;
} }
/* Is master/pet behind monster? Check up to 15 squares beyond pet. */
/* Is master/pet behind monster? Check up to 15 squares beyond
* pet.
*/
if (find_friends(mtmp, mtarg, 15)) { if (find_friends(mtmp, mtarg, 15)) {
score -= 3000L; score -= 3000L;
return score; return score;
} }
/* Target hostile monsters in preference to peaceful ones */ /* Target hostile monsters in preference to peaceful ones */
if (!mtarg->mpeaceful) if (!mtarg->mpeaceful)
score += 10; score += 10;
/* Is the monster passive? Don't waste energy on it, if so */ /* Is the monster passive? Don't waste energy on it, if so */
if (mtarg->data->mattk[0].aatyp == AT_NONE) if (mtarg->data->mattk[0].aatyp == AT_NONE)
score -= 1000; score -= 1000;
/* Even weak pets with breath attacks shouldn't take on very /* Even weak pets with breath attacks shouldn't take on very
* low-level monsters. Wasting breath on lichens is ridiculous. low-level monsters. Wasting breath on lichens is ridiculous. */
*/
if ((mtarg->m_lev < 2 && mtmp->m_lev > 5) if ((mtarg->m_lev < 2 && mtmp->m_lev > 5)
|| (mtmp->m_lev > 12 && mtarg->m_lev < mtmp->m_lev - 9 || (mtmp->m_lev > 12 && mtarg->m_lev < mtmp->m_lev - 9
&& u.ulevel > 8 && mtarg->m_lev < u.ulevel - 7)) && u.ulevel > 8 && mtarg->m_lev < u.ulevel - 7))
score -= 25; score -= 25;
/* And pets will hesitate to attack vastly stronger foes. /* And pets will hesitate to attack vastly stronger foes.
* This penalty will be discarded if master's in trouble. This penalty will be discarded if master's in trouble. */
*/
if (mtarg->m_lev > mtmp->m_lev + 4L) if (mtarg->m_lev > mtmp->m_lev + 4L)
score -= (mtarg->m_lev - mtmp->m_lev) * 20L; score -= (mtarg->m_lev - mtmp->m_lev) * 20L;
/* All things being the same, go for the beefiest monster. This /* All things being the same, go for the beefiest monster. This
* bonus should not be large enough to override the pet's aversion bonus should not be large enough to override the pet's aversion
* to attacking much stronger monsters. to attacking much stronger monsters. */
*/
score += mtarg->m_lev * 2 + mtarg->mhp / 3; score += mtarg->m_lev * 2 + mtarg->mhp / 3;
} }
/* Fuzz factor to make things less predictable when very /* Fuzz factor to make things less predictable when very
* similar targets are abundant similar targets are abundant. */
*/
score += rnd(5); score += rnd(5);
/* Pet may decide not to use ranged attack when confused */ /* Pet may decide not to use ranged attack when confused */
if (mtmp->mconf && !rn2(3)) if (mtmp->mconf && !rn2(3))
score -= 1000; score -= 1000;
return score; return score;
} }
@@ -1121,9 +1105,8 @@ int after; /* this is extra fast monster movement */
} }
/* Pet hasn't attacked anything but is considering moving - /* Pet hasn't attacked anything but is considering moving -
* now's the time for ranged attacks. Note that the pet can * now's the time for ranged attacks. Note that the pet can move
* move after it performs its ranged attack. Should this be * after it performs its ranged attack. Should this be changed?
* changed?
*/ */
{ {
struct monst *mtarg; struct monst *mtarg;
@@ -1152,7 +1135,8 @@ int after; /* this is extra fast monster movement */
mstatus = mattackm(mtmp, mtarg); mstatus = mattackm(mtmp, mtarg);
/* Shouldn't happen, really */ /* Shouldn't happen, really */
if (mstatus & MM_AGR_DIED) return 2; if (mstatus & MM_AGR_DIED)
return 2;
/* Allow the targeted nasty to strike back - if /* Allow the targeted nasty to strike back - if
* the targeted beast doesn't have a ranged attack, * the targeted beast doesn't have a ranged attack,
@@ -1168,7 +1152,8 @@ int after; /* this is extra fast monster movement */
*/ */
if (mtarg->mcansee && haseyes(mtarg->data)) { if (mtarg->mcansee && haseyes(mtarg->data)) {
mstatus = mattackm(mtarg, mtmp); mstatus = mattackm(mtarg, mtmp);
if (mstatus & MM_DEF_DIED) return 2; if (mstatus & MM_DEF_DIED)
return 2;
} }
} }
} }
@@ -1201,7 +1186,7 @@ newdogpos:
/* describe top item of pile, not necessarily cursed item itself; /* describe top item of pile, not necessarily cursed item itself;
don't use glyph_at() here--it would return the pet but we want don't use glyph_at() here--it would return the pet but we want
to know whether an object is remembered at this map location */ to know whether an object is remembered at this map location */
struct obj *o = (!Hallucination struct obj *o = (!Hallucination && level.flags.hero_memory
&& glyph_is_object(levl[nix][niy].glyph)) && glyph_is_object(levl[nix][niy].glyph))
? vobj_at(nix, niy) : 0; ? vobj_at(nix, niy) : 0;
const char *what = o ? distant_name(o, doname) : something; const char *what = o ? distant_name(o, doname) : something;
@@ -1319,12 +1304,12 @@ wantdoor(x, y, distance)
int x, y; int x, y;
genericptr_t distance; genericptr_t distance;
{ {
int ndist; int ndist, *dist_ptr = (int *) distance;
if (*(int *) distance > (ndist = distu(x, y))) { if (*dist_ptr > (ndist = distu(x, y))) {
gx = x; gx = x;
gy = y; gy = y;
*(int *) distance = ndist; *dist_ptr = ndist;
} }
} }
+2
View File
@@ -781,6 +781,8 @@ int how;
u.ugrave_arise = NON_PM; u.ugrave_arise = NON_PM;
HUnchanging = 0L; HUnchanging = 0L;
curs_on_u(); curs_on_u();
if (!context.mon_moving)
endmultishot(FALSE);
} }
/* /*
+32 -34
View File
@@ -356,15 +356,13 @@ register struct monst *magr, *mdef;
attk = 1; attk = 1;
switch (mattk->aatyp) { switch (mattk->aatyp) {
case AT_WEAP: /* "hand to hand" attacks */ case AT_WEAP: /* "hand to hand" attacks */
if (distmin(magr->mx,magr->my,mdef->mx,mdef->my) > 1) { if (distmin(magr->mx, magr->my, mdef->mx, mdef->my) > 1) {
/* D: Do a ranged attack here! */ /* D: Do a ranged attack here! */
strike = thrwmm(magr, mdef); strike = thrwmm(magr, mdef);
if (DEADMONSTER(mdef)) if (DEADMONSTER(mdef))
res[i] = MM_DEF_DIED; res[i] = MM_DEF_DIED;
if (DEADMONSTER(magr)) if (DEADMONSTER(magr))
res[i] |= MM_AGR_DIED; res[i] |= MM_AGR_DIED;
break; break;
} }
if (magr->weapon_check == NEED_WEAPON || !MON_WEP(magr)) { if (magr->weapon_check == NEED_WEAPON || !MON_WEP(magr)) {
@@ -390,8 +388,7 @@ register struct monst *magr, *mdef;
case AT_TENT: case AT_TENT:
/* Nymph that teleported away on first attack? */ /* Nymph that teleported away on first attack? */
if (distmin(magr->mx, magr->my, mdef->mx, mdef->my) > 1) if (distmin(magr->mx, magr->my, mdef->mx, mdef->my) > 1)
/* Continue because the monster may have a ranged /* Continue because the monster may have a ranged attack. */
* attack */
continue; continue;
/* Monsters won't attack cockatrices physically if they /* Monsters won't attack cockatrices physically if they
* have a weapon instead. This instinct doesn't work for * have a weapon instead. This instinct doesn't work for
@@ -454,50 +451,52 @@ register struct monst *magr, *mdef;
break; break;
case AT_ENGL: case AT_ENGL:
if (u.usteed && (mdef == u.usteed)) { if (u.usteed && mdef == u.usteed) {
strike = 0; strike = 0;
break; break;
} }
/* D: Prevent engulf from a distance */ /* D: Prevent engulf from a distance */
if (distmin(magr->mx,magr->my,mdef->mx,mdef->my) > 1) if (distmin(magr->mx, magr->my, mdef->mx, mdef->my) > 1)
continue; continue;
/* Engulfing attacks are directed at the hero if /* Engulfing attacks are directed at the hero if possible. -dlc */
* possible. -dlc
*/
if (u.uswallow && magr == u.ustuck) if (u.uswallow && magr == u.ustuck)
strike = 0; strike = 0;
else { else if ((strike = (tmp > rnd(20 + i))) != 0)
if ((strike = (tmp > rnd(20 + i)))) res[i] = gulpmm(magr, mdef, mattk);
res[i] = gulpmm(magr, mdef, mattk); else
else missmm(magr, mdef, mattk);
missmm(magr, mdef, mattk);
}
break; break;
case AT_BREA: case AT_BREA:
if (!monnear(magr, mdef->mx, mdef->my)) { if (!monnear(magr, mdef->mx, mdef->my)) {
strike = breamm(magr, mattk, mdef); strike = breamm(magr, mattk, mdef);
/* We don't really know if we hit or not, but pretend /* We don't really know if we hit or not; pretend we did. */
* we did */ if (strike)
if (strike) res[i] |= MM_HIT; res[i] |= MM_HIT;
if (DEADMONSTER(mdef)) res[i] = MM_DEF_DIED; if (DEADMONSTER(mdef))
if (DEADMONSTER(magr)) res[i] |= MM_AGR_DIED; res[i] = MM_DEF_DIED;
if (DEADMONSTER(magr))
res[i] |= MM_AGR_DIED;
} }
else else
strike = 0; strike = 0;
break; break;
case AT_SPIT: case AT_SPIT:
if (!monnear(magr, mdef->mx, mdef->my)) { if (!monnear(magr, mdef->mx, mdef->my)) {
strike = spitmm(magr, mattk, mdef); strike = spitmm(magr, mattk, mdef);
/* We don't really know if we hit or not, but pretend /* We don't really know if we hit or not; pretend we did. */
* we did */ if (strike)
if (strike) res[i] |= MM_HIT; res[i] |= MM_HIT;
if (DEADMONSTER(mdef)) res[i] = MM_DEF_DIED; if (DEADMONSTER(mdef))
if (DEADMONSTER(magr)) res[i] |= MM_AGR_DIED; res[i] = MM_DEF_DIED;
if (DEADMONSTER(magr))
res[i] |= MM_AGR_DIED;
} }
break; break;
default: /* no attack */ default: /* no attack */
strike = 0; strike = 0;
attk = 0; attk = 0;
@@ -505,12 +504,11 @@ register struct monst *magr, *mdef;
} }
if (attk && !(res[i] & MM_AGR_DIED) if (attk && !(res[i] & MM_AGR_DIED)
&& distmin(magr->mx,magr->my,mdef->mx,mdef->my) <= 1) && distmin(magr->mx, magr->my, mdef->mx, mdef->my) <= 1)
res[i] = passivemm(magr, mdef, strike, res[i] & MM_DEF_DIED); res[i] = passivemm(magr, mdef, strike, res[i] & MM_DEF_DIED);
if (res[i] & MM_DEF_DIED) if (res[i] & MM_DEF_DIED)
return res[i]; return res[i];
if (res[i] & MM_AGR_DIED) if (res[i] & MM_AGR_DIED)
return res[i]; return res[i];
/* return if aggressor can no longer attack */ /* return if aggressor can no longer attack */
@@ -595,9 +593,8 @@ struct attack *mattk;
pline("%s %s...", buf, mon_nam(mdef)); pline("%s %s...", buf, mon_nam(mdef));
} }
if (magr->mcan || !magr->mcansee if (magr->mcan || !magr->mcansee || !mdef->mcansee
|| (magr->minvis && !perceives(mdef->data)) || !mdef->mcansee || (magr->minvis && !perceives(mdef->data)) || mdef->msleeping) {
|| mdef->msleeping) {
if (vis) if (vis)
pline("but nothing happens."); pline("but nothing happens.");
return MM_MISS; return MM_MISS;
@@ -609,8 +606,8 @@ struct attack *mattk;
if (mdef->mcansee) { if (mdef->mcansee) {
if (mon_reflects(magr, (char *) 0)) { if (mon_reflects(magr, (char *) 0)) {
if (canseemon(magr)) if (canseemon(magr))
(void) mon_reflects( (void) mon_reflects(magr,
magr, "The gaze is reflected away by %s %s."); "The gaze is reflected away by %s %s.");
return MM_MISS; return MM_MISS;
} }
if (mdef->minvis && !perceives(magr->data)) { if (mdef->minvis && !perceives(magr->data)) {
@@ -723,7 +720,7 @@ register struct attack *mattk;
} else if (status & MM_AGR_DIED) { /* aggressor died */ } else if (status & MM_AGR_DIED) { /* aggressor died */
place_monster(mdef, dx, dy); place_monster(mdef, dx, dy);
newsym(dx, dy); newsym(dx, dy);
} else { /* both alive, put them back */ } else { /* both alive, put them back */
if (cansee(dx, dy)) if (cansee(dx, dy))
pline("%s is regurgitated!", Monnam(mdef)); pline("%s is regurgitated!", Monnam(mdef));
@@ -1174,6 +1171,7 @@ register struct attack *mattk;
*/ */
{ {
struct obj *gold = findgold(mdef->minvent); struct obj *gold = findgold(mdef->minvent);
if (!gold) if (!gold)
break; break;
obj_extract_self(gold); obj_extract_self(gold);
+67 -64
View File
@@ -8,11 +8,9 @@
STATIC_VAR NEARDATA struct obj *otmp; STATIC_VAR NEARDATA struct obj *otmp;
STATIC_DCL boolean FDECL(u_slip_free, (struct monst *, struct attack *)); STATIC_DCL boolean FDECL(u_slip_free, (struct monst *, struct attack *));
STATIC_DCL int FDECL(passiveum, STATIC_DCL int FDECL(passiveum, (struct permonst *, struct monst *,
(struct permonst *, struct monst *, struct attack *)); struct attack *));
STATIC_DCL void FDECL(mayberem, (struct obj *, const char *)); STATIC_DCL void FDECL(mayberem, (struct obj *, const char *));
STATIC_DCL boolean FDECL(diseasemu, (struct permonst *)); STATIC_DCL boolean FDECL(diseasemu, (struct permonst *));
STATIC_DCL int FDECL(hitmu, (struct monst *, struct attack *)); STATIC_DCL int FDECL(hitmu, (struct monst *, struct attack *));
STATIC_DCL int FDECL(gulpmu, (struct monst *, struct attack *)); STATIC_DCL int FDECL(gulpmu, (struct monst *, struct attack *));
@@ -20,7 +18,6 @@ STATIC_DCL int FDECL(explmu, (struct monst *, struct attack *, BOOLEAN_P));
STATIC_DCL void FDECL(missmu, (struct monst *, BOOLEAN_P, struct attack *)); STATIC_DCL void FDECL(missmu, (struct monst *, BOOLEAN_P, struct attack *));
STATIC_DCL void FDECL(mswings, (struct monst *, struct obj *)); STATIC_DCL void FDECL(mswings, (struct monst *, struct obj *));
STATIC_DCL void FDECL(wildmiss, (struct monst *, struct attack *)); STATIC_DCL void FDECL(wildmiss, (struct monst *, struct attack *));
STATIC_DCL void FDECL(hitmsg, (struct monst *, struct attack *)); STATIC_DCL void FDECL(hitmsg, (struct monst *, struct attack *));
/* See comment in mhitm.c. If we use this a lot it probably should be */ /* See comment in mhitm.c. If we use this a lot it probably should be */
@@ -33,41 +30,48 @@ register struct monst *mtmp;
register struct attack *mattk; register struct attack *mattk;
{ {
int compat; int compat;
const char *pfmt = 0;
char *Monst_name = Monnam(mtmp);
/* Note: if opposite gender, "seductively" */ /* Note: if opposite gender, "seductively" */
/* If same gender, "engagingly" for nymph, normal msg for others */ /* If same gender, "engagingly" for nymph, normal msg for others */
if ((compat = could_seduce(mtmp, &youmonst, mattk)) && !mtmp->mcan if ((compat = could_seduce(mtmp, &youmonst, mattk)) != 0
&& !mtmp->mspec_used) { && !mtmp->mcan && !mtmp->mspec_used) {
pline("%s %s you %s.", Monnam(mtmp), Blind ? "talks to" : "smiles at", pline("%s %s you %s.", Monst_name,
compat == 2 ? "engagingly" : "seductively"); Blind ? "talks to" : "smiles at",
} else (compat == 2) ? "engagingly" : "seductively");
} else {
switch (mattk->aatyp) { switch (mattk->aatyp) {
case AT_BITE: case AT_BITE:
pline("%s bites!", Monnam(mtmp)); pfmt = "%s bites!";
break; break;
case AT_KICK: case AT_KICK:
pline("%s kicks%c", Monnam(mtmp), pline("%s kicks%c", Monst_name,
thick_skinned(youmonst.data) ? '.' : '!'); thick_skinned(youmonst.data) ? '.' : '!');
break; break;
case AT_STNG: case AT_STNG:
pline("%s stings!", Monnam(mtmp)); pfmt = "%s stings!";
break; break;
case AT_BUTT: case AT_BUTT:
pline("%s butts!", Monnam(mtmp)); pfmt = "%s butts!";
break; break;
case AT_TUCH: case AT_TUCH:
pline("%s touches you!", Monnam(mtmp)); pfmt = "%s touches you!";
break; break;
case AT_TENT: case AT_TENT:
pline("%s tentacles suck you!", s_suffix(Monnam(mtmp))); pfmt = "%s tentacles suck you!";
Monst_name = s_suffix(Monst_name);
break; break;
case AT_EXPL: case AT_EXPL:
case AT_BOOM: case AT_BOOM:
pline("%s explodes!", Monnam(mtmp)); pfmt = "%s explodes!";
break; break;
default: default:
pline("%s hits!", Monnam(mtmp)); pfmt = "%s hits!";
} }
if (pfmt)
pline(pfmt, Monst_name);
}
} }
/* monster missed you */ /* monster missed you */
@@ -139,6 +143,7 @@ register struct monst *mtmp;
register struct attack *mattk; register struct attack *mattk;
{ {
int compat; int compat;
const char *Monst_name; /* Monnam(mtmp) */
/* no map_invisible() -- no way to tell where _this_ is coming from */ /* no map_invisible() -- no way to tell where _this_ is coming from */
@@ -150,42 +155,41 @@ register struct attack *mattk;
compat = ((mattk->adtyp == AD_SEDU || mattk->adtyp == AD_SSEX) compat = ((mattk->adtyp == AD_SEDU || mattk->adtyp == AD_SSEX)
&& could_seduce(mtmp, &youmonst, (struct attack *) 0)); && could_seduce(mtmp, &youmonst, (struct attack *) 0));
Monst_name = Monnam(mtmp);
if (!mtmp->mcansee || (Invis && !perceives(mtmp->data))) { if (!mtmp->mcansee || (Invis && !perceives(mtmp->data))) {
const char *swings = const char *swings = (mattk->aatyp == AT_BITE) ? "snaps"
mattk->aatyp == AT_BITE : (mattk->aatyp == AT_KICK) ? "kicks"
? "snaps" : (mattk->aatyp == AT_STNG
: mattk->aatyp == AT_KICK || mattk->aatyp == AT_BUTT
? "kicks" || nolimbs(mtmp->data)) ? "lunges"
: (mattk->aatyp == AT_STNG || mattk->aatyp == AT_BUTT : "swings";
|| nolimbs(mtmp->data))
? "lunges"
: "swings";
if (compat) if (compat)
pline("%s tries to touch you and misses!", Monnam(mtmp)); pline("%s tries to touch you and misses!", Monst_name);
else else
switch (rn2(3)) { switch (rn2(3)) {
case 0: case 0:
pline("%s %s wildly and misses!", Monnam(mtmp), swings); pline("%s %s wildly and misses!", Monst_name, swings);
break; break;
case 1: case 1:
pline("%s attacks a spot beside you.", Monnam(mtmp)); pline("%s attacks a spot beside you.", Monst_name);
break; break;
case 2: case 2:
pline("%s strikes at %s!", Monnam(mtmp), pline("%s strikes at %s!", Monst_name,
levl[mtmp->mux][mtmp->muy].typ == WATER ? "empty water" (levl[mtmp->mux][mtmp->muy].typ == WATER)
: "thin air"); ? "empty water"
: "thin air");
break; break;
default: default:
pline("%s %s wildly!", Monnam(mtmp), swings); pline("%s %s wildly!", Monst_name, swings);
break; break;
} }
} else if (Displaced) { } else if (Displaced) {
if (compat) if (compat)
pline("%s smiles %s at your %sdisplaced image...", Monnam(mtmp), pline("%s smiles %s at your %sdisplaced image...", Monst_name,
compat == 2 ? "engagingly" : "seductively", (compat == 2) ? "engagingly" : "seductively",
Invis ? "invisible " : ""); Invis ? "invisible " : "");
else else
pline("%s strikes at your %sdisplaced image and misses you!", pline("%s strikes at your %sdisplaced image and misses you!",
@@ -194,20 +198,20 @@ register struct attack *mattk;
* displaced image, since the displaced image is also * displaced image, since the displaced image is also
* invisible. * invisible.
*/ */
Monnam(mtmp), Invis ? "invisible " : ""); Monst_name, Invis ? "invisible " : "");
} else if (Underwater) { } else if (Underwater) {
/* monsters may miss especially on water level where /* monsters may miss especially on water level where
bubbles shake the player here and there */ bubbles shake the player here and there */
if (compat) if (compat)
pline("%s reaches towards your distorted image.", Monnam(mtmp)); pline("%s reaches towards your distorted image.", Monst_name);
else else
pline("%s is fooled by water reflections and misses!", pline("%s is fooled by water reflections and misses!",
Monnam(mtmp)); Monst_name);
} else } else
impossible("%s attacks you without knowing your location?", impossible("%s attacks you without knowing your location?",
Monnam(mtmp)); Monst_name);
} }
void void
@@ -217,9 +221,9 @@ struct permonst *mdat; /* if mtmp is polymorphed, mdat != mtmp->data */
boolean message; boolean message;
{ {
if (message) { if (message) {
if (is_animal(mdat)) if (is_animal(mdat)) {
You("get regurgitated!"); You("get regurgitated!");
else { } else {
char blast[40]; char blast[40];
register int i; register int i;
@@ -227,9 +231,9 @@ boolean message;
for (i = 0; i < NATTK; i++) for (i = 0; i < NATTK; i++)
if (mdat->mattk[i].aatyp == AT_ENGL) if (mdat->mattk[i].aatyp == AT_ENGL)
break; break;
if (mdat->mattk[i].aatyp != AT_ENGL) if (mdat->mattk[i].aatyp != AT_ENGL) {
impossible("Swallower has no engulfing attack?"); impossible("Swallower has no engulfing attack?");
else { } else {
if (is_whirly(mdat)) { if (is_whirly(mdat)) {
switch (mdat->mattk[i].adtyp) { switch (mdat->mattk[i].adtyp) {
case AD_ELEC: case AD_ELEC:
@@ -306,11 +310,10 @@ struct attack *alt_attk_buf;
&& !(mptr->mattk[1].aatyp == AT_WEAP && !(mptr->mattk[1].aatyp == AT_WEAP
&& mptr->mattk[1].adtyp == AD_PHYS) && mptr->mattk[1].adtyp == AD_PHYS)
&& (magr->mcan && (magr->mcan
|| (weap || (weap && ((weap->otyp == CORPSE
&& ((weap->otyp == CORPSE && touch_petrifies(&mons[weap->corpsenm]))
&& touch_petrifies(&mons[weap->corpsenm])) || weap->oartifact == ART_STORMBRINGER
|| weap->oartifact == ART_STORMBRINGER || weap->oartifact == ART_VORPAL_BLADE)))) {
|| weap->oartifact == ART_VORPAL_BLADE)))) {
*alt_attk_buf = *attk; *alt_attk_buf = *attk;
attk = alt_attk_buf; attk = alt_attk_buf;
attk->adtyp = AD_PHYS; attk->adtyp = AD_PHYS;
@@ -1157,7 +1160,8 @@ register struct attack *mattk;
break; break;
case AD_LEGS: { case AD_LEGS: {
register long side = rn2(2) ? RIGHT_SIDE : LEFT_SIDE; register long side = rn2(2) ? RIGHT_SIDE : LEFT_SIDE;
const char *sidestr = (side == RIGHT_SIDE) ? "right" : "left"; const char *sidestr = (side == RIGHT_SIDE) ? "right" : "left",
*Monst_name = Monnam(mtmp), *leg = body_part(LEG);
/* This case is too obvious to ignore, but Nethack is not in /* This case is too obvious to ignore, but Nethack is not in
* general very good at considering height--most short monsters * general very good at considering height--most short monsters
@@ -1165,31 +1169,30 @@ register struct attack *mattk;
* [FIXME: why can't a flying attacker overcome this?] * [FIXME: why can't a flying attacker overcome this?]
*/ */
if (u.usteed || Levitation || Flying) { if (u.usteed || Levitation || Flying) {
pline("%s tries to reach your %s %s!", Monnam(mtmp), sidestr, pline("%s tries to reach your %s %s!", Monst_name, sidestr, leg);
body_part(LEG));
dmg = 0; dmg = 0;
} else if (mtmp->mcan) { } else if (mtmp->mcan) {
pline("%s nuzzles against your %s %s!", Monnam(mtmp), sidestr, pline("%s nuzzles against your %s %s!", Monnam(mtmp),
body_part(LEG)); sidestr, leg);
dmg = 0; dmg = 0;
} else { } else {
if (uarmf) { if (uarmf) {
if (rn2(2) && (uarmf->otyp == LOW_BOOTS if (rn2(2) && (uarmf->otyp == LOW_BOOTS
|| uarmf->otyp == IRON_SHOES)) || uarmf->otyp == IRON_SHOES)) {
pline("%s pricks the exposed part of your %s %s!", pline("%s pricks the exposed part of your %s %s!",
Monnam(mtmp), sidestr, body_part(LEG)); Monst_name, sidestr, leg);
else if (!rn2(5)) } else if (!rn2(5)) {
pline("%s pricks through your %s boot!", Monnam(mtmp), pline("%s pricks through your %s boot!", Monst_name,
sidestr); sidestr);
else { } else {
pline("%s scratches your %s boot!", Monnam(mtmp), pline("%s scratches your %s boot!", Monst_name,
sidestr); sidestr);
dmg = 0; dmg = 0;
break; break;
} }
} else } else
pline("%s pricks your %s %s!", Monnam(mtmp), sidestr, pline("%s pricks your %s %s!", Monst_name, sidestr, leg);
body_part(LEG));
set_wounded_legs(side, rnd(60 - ACURR(A_DEX))); set_wounded_legs(side, rnd(60 - ACURR(A_DEX)));
exercise(A_STR, FALSE); exercise(A_STR, FALSE);
exercise(A_DEX, FALSE); exercise(A_DEX, FALSE);
@@ -1308,12 +1311,11 @@ register struct attack *mattk;
(void) rloc(mtmp, TRUE); (void) rloc(mtmp, TRUE);
return 3; return 3;
} else if (mtmp->mcan) { } else if (mtmp->mcan) {
if (!Blind) { if (!Blind)
pline("%s tries to %s you, but you seem %s.", pline("%s tries to %s you, but you seem %s.",
Adjmonnam(mtmp, "plain"), Adjmonnam(mtmp, "plain"),
flags.female ? "charm" : "seduce", flags.female ? "charm" : "seduce",
flags.female ? "unaffected" : "uninterested"); flags.female ? "unaffected" : "uninterested");
}
if (rn2(3)) { if (rn2(3)) {
if (!tele_restrict(mtmp)) if (!tele_restrict(mtmp))
(void) rloc(mtmp, TRUE); (void) rloc(mtmp, TRUE);
@@ -1705,6 +1707,7 @@ register struct attack *mattk;
newsym(mtmp->mx, mtmp->my); newsym(mtmp->mx, mtmp->my);
if (is_animal(mtmp->data) && u.usteed) { if (is_animal(mtmp->data) && u.usteed) {
char buf[BUFSZ]; char buf[BUFSZ];
/* Too many quirks presently if hero and steed /* Too many quirks presently if hero and steed
* are swallowed. Pretend purple worms don't * are swallowed. Pretend purple worms don't
* like horses for now :-) * like horses for now :-)
+192 -214
View File
@@ -4,6 +4,8 @@
#include "hack.h" #include "hack.h"
STATIC_DCL int FDECL(monmulti, (struct monst *, struct obj *, struct obj *));
STATIC_DCL void FDECL(monshoot, (struct monst *, struct obj *, struct obj *));
STATIC_DCL int FDECL(drop_throw, (struct obj *, BOOLEAN_P, int, int)); STATIC_DCL int FDECL(drop_throw, (struct obj *, BOOLEAN_P, int, int));
STATIC_DCL boolean FDECL(m_lined_up, (struct monst *, struct monst *)); STATIC_DCL boolean FDECL(m_lined_up, (struct monst *, struct monst *));
@@ -51,9 +53,9 @@ const char *name; /* if null, then format `obj' */
|| !strncmpi(name, "a ", 2)) || !strncmpi(name, "a ", 2))
kprefix = KILLED_BY; kprefix = KILLED_BY;
} }
onm = (obj && obj_is_pname(obj)) ? the(name) : (obj && obj->quan > 1L) onm = (obj && obj_is_pname(obj)) ? the(name)
? name : (obj && obj->quan > 1L) ? name
: an(name); : an(name);
is_acid = (obj && obj->otyp == ACID_VENOM); is_acid = (obj && obj->otyp == ACID_VENOM);
if (u.uac + tlev <= rnd(20)) { if (u.uac + tlev <= rnd(20)) {
@@ -73,9 +75,9 @@ const char *name; /* if null, then format `obj' */
pline_The("silver sears your flesh!"); pline_The("silver sears your flesh!");
exercise(A_CON, FALSE); exercise(A_CON, FALSE);
} }
if (is_acid && Acid_resistance) if (is_acid && Acid_resistance) {
pline("It doesn't seem to hurt you."); pline("It doesn't seem to hurt you.");
else { } else {
if (is_acid) if (is_acid)
pline("It burns!"); pline("It burns!");
losehp(dam, knm, kprefix); /* acid damage */ losehp(dam, knm, kprefix); /* acid damage */
@@ -116,8 +118,7 @@ int x, y;
if (down_gate(x, y) != -1) if (down_gate(x, y) != -1)
objgone = ship_object(obj, x, y, FALSE); objgone = ship_object(obj, x, y, FALSE);
if (!objgone) { if (!objgone) {
if (!flooreffects(obj, x, y, if (!flooreffects(obj, x, y, "fall")) {
"fall")) { /* don't double-dip on damage */
place_object(obj, x, y); place_object(obj, x, y);
if (!mtmp && x == u.ux && y == u.uy) if (!mtmp && x == u.ux && y == u.uy)
mtmp = &youmonst; mtmp = &youmonst;
@@ -137,6 +138,150 @@ STATIC_OVL struct monst *target = 0;
/* The monster that's doing the shooting/throwing */ /* The monster that's doing the shooting/throwing */
STATIC_OVL struct monst *archer = 0; STATIC_OVL struct monst *archer = 0;
/* calculate multishot volley count for mtmp throwing otmp (if not ammo) or
shooting otmp with mwep (if otmp is ammo and mwep appropriate launcher) */
STATIC_OVL int
monmulti(mtmp, otmp, mwep)
struct monst *mtmp;
struct obj *otmp, *mwep;
{
int skill = (int) objects[otmp->otyp].oc_skill;
int multishot = 1;
if (otmp->quan > 1L /* no point checking if there's only 1 */
/* ammo requires corresponding launcher be wielded */
&& (is_ammo(otmp)
? matching_launcher(otmp, mwep)
/* otherwise any stackable (non-ammo) weapon */
: otmp->oclass == WEAPON_CLASS)
&& !mtmp->mconf) {
/* Assumes lords are skilled, princes are expert */
if (is_prince(mtmp->data))
multishot += 2;
else if (is_lord(mtmp->data))
multishot++;
/* fake players treated as skilled (regardless of role limits) */
else if (is_mplayer(mtmp->data))
multishot++;
/* this portion is different from hero multishot; from slash'em?
*/
/* Elven Craftsmanship makes for light, quick bows */
if (otmp->otyp == ELVEN_ARROW && !otmp->cursed)
multishot++;
if (ammo_and_launcher(otmp, uwep) && mwep->otyp == ELVEN_BOW
&& !mwep->cursed)
multishot++;
/* 1/3 of launcher enchantment */
if (ammo_and_launcher(otmp, mwep) && mwep->spe > 1)
multishot += (long) rounddiv(mwep->spe, 3);
/* Some randomness */
multishot = (long) rnd((int) multishot);
/* class bonus */
switch (monsndx(mtmp->data)) {
case PM_CAVEMAN: /* give bonus for low-tech gear */
if (skill == -P_SLING || skill == P_SPEAR)
multishot++;
break;
case PM_MONK: /* allow higher volley count */
if (skill == -P_SHURIKEN)
multishot++;
break;
case PM_RANGER:
if (skill != P_DAGGER)
multishot++;
break;
case PM_ROGUE:
if (skill == P_DAGGER)
multishot++;
break;
case PM_NINJA:
if (skill == -P_SHURIKEN || skill == -P_DART)
multishot++;
/*FALLTHRU*/
case PM_SAMURAI:
if (otmp->otyp == YA && mwep->otyp == YUMI)
multishot++;
break;
default:
break;
}
/* racial bonus */
if ((is_elf(mtmp->data) && otmp->otyp == ELVEN_ARROW
&& mwep->otyp == ELVEN_BOW)
|| (is_orc(mtmp->data) && otmp->otyp == ORCISH_ARROW
&& mwep->otyp == ORCISH_BOW)
|| (is_gnome(mtmp->data) && otmp->otyp == CROSSBOW_BOLT
&& mwep->otyp == CROSSBOW))
multishot++;
}
if (otmp->quan < multishot)
multishot = (int) otmp->quan;
if (multishot < 1)
multishot = 1;
return multishot;
}
/* mtmp throws otmp, or shoots otmp with mwep, at hero or at monster mtarg */
STATIC_OVL void
monshoot(mtmp, otmp, mwep)
struct monst *mtmp;
struct obj *otmp, *mwep;
{
struct monst *mtarg = target;
int dm = distmin(mtmp->mx, mtmp->my,
mtarg ? mtarg->mx : mtmp->mux,
mtarg ? mtarg->my : mtmp->muy),
multishot = monmulti(mtmp, otmp, mwep);
/*
* Caller must have called linedup() to set up tbx, tby.
*/
if (canseemon(mtmp)) {
const char *onm;
char onmbuf[BUFSZ], trgbuf[BUFSZ];
if (multishot > 1) {
/* "N arrows"; multishot > 1 implies otmp->quan > 1, so
xname()'s result will already be pluralized */
Sprintf(onmbuf, "%d %s", multishot, xname(otmp));
onm = onmbuf;
} else {
/* "an arrow" */
onm = singular(otmp, xname);
onm = obj_is_pname(otmp) ? the(onm) : an(onm);
}
m_shot.s = ammo_and_launcher(otmp, mwep) ? TRUE : FALSE;
Strcpy(trgbuf, mtarg ? mon_nam(mtarg) : "");
if (!strcmp(trgbuf, "it"))
Strcpy(trgbuf, humanoid(mtmp->data) ? "someone" : something);
pline("%s %s %s%s%s!", Monnam(mtmp),
m_shot.s ? "shoots" : "throws", onm,
mtarg ? " at " : "", trgbuf);
m_shot.o = otmp->otyp;
} else {
m_shot.o = STRANGE_OBJECT; /* don't give multishot feedback */
}
m_shot.n = multishot;
for (m_shot.i = 1; m_shot.i <= m_shot.n; m_shot.i++) {
m_throw(mtmp, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby), dm, otmp);
/* conceptually all N missiles are in flight at once, but
if mtmp gets killed (shot kills adjacent gas spore and
triggers explosion, perhaps), inventory will be dropped
and otmp might go away via merging into another stack */
if (mtmp->mhp <= 0 && m_shot.i < m_shot.n)
/* cancel pending shots (perhaps ought to give a message here
since we gave one above about throwing/shooting N missiles) */
break; /* endmultishot(FALSE); */
}
/* reset 'm_shot' */
m_shot.n = m_shot.i = 0;
m_shot.o = STRANGE_OBJECT;
m_shot.s = FALSE;
}
/* an object launched by someone/thing other than player attacks a monster; /* 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) */ return 1 if the object has stopped moving (hit or its range used up) */
int int
@@ -237,9 +382,7 @@ boolean verbose; /* give message(s) even when you can't see what happened */
if (vis || (verbose && !target)) if (vis || (verbose && !target))
pline("%s is %s!", Monnam(mtmp), pline("%s is %s!", Monnam(mtmp),
(nonliving(mtmp->data) || is_vampshifter(mtmp) (nonliving(mtmp->data) || is_vampshifter(mtmp)
|| !canspotmon(mtmp)) || !canspotmon(mtmp)) ? "destroyed" : "killed");
? "destroyed"
: "killed");
/* don't blame hero for unknown rolling boulder trap */ /* don't blame hero for unknown rolling boulder trap */
if (!context.mon_moving if (!context.mon_moving
&& (otmp->otyp != BOULDER || range >= 0 || otmp->otrapped)) && (otmp->otyp != BOULDER || range >= 0 || otmp->otrapped))
@@ -249,8 +392,8 @@ boolean verbose; /* give message(s) even when you can't see what happened */
} }
if (can_blnd((struct monst *) 0, mtmp, if (can_blnd((struct monst *) 0, mtmp,
(uchar) (otmp->otyp == BLINDING_VENOM ? AT_SPIT (uchar) ((otmp->otyp == BLINDING_VENOM) ? AT_SPIT
: AT_WEAP), : AT_WEAP),
otmp)) { otmp)) {
if (vis && mtmp->mcansee) if (vis && mtmp->mcansee)
pline("%s is blinded by %s.", Monnam(mtmp), the(xname(otmp))); pline("%s is blinded by %s.", Monnam(mtmp), the(xname(otmp)));
@@ -280,7 +423,7 @@ struct obj *obj; /* missile (or stack providing it) */
struct monst *mtmp; struct monst *mtmp;
struct obj *singleobj; struct obj *singleobj;
char sym = obj->oclass; char sym = obj->oclass;
int hitu, oldumort, blindinc = 0; int hitu = 0, oldumort, blindinc = 0;
bhitpos.x = x; bhitpos.x = x;
bhitpos.y = y; bhitpos.y = y;
@@ -372,9 +515,10 @@ struct obj *obj; /* missile (or stack providing it) */
You( You(
"accept %s gift in the spirit in which it was intended.", "accept %s gift in the spirit in which it was intended.",
s_suffix(mon_nam(mon))); s_suffix(mon_nam(mon)));
(void) hold_another_object( (void) hold_another_object(singleobj,
singleobj, "You catch, but drop, %s.", "You catch, but drop, %s.",
xname(singleobj), "You catch:"); xname(singleobj),
"You catch:");
} }
break; break;
} }
@@ -430,7 +574,7 @@ struct obj *obj; /* missile (or stack providing it) */
(u.umortality > oldumort) ? 0 : 10, TRUE); (u.umortality > oldumort) ? 0 : 10, TRUE);
} }
if (hitu && can_blnd((struct monst *) 0, &youmonst, if (hitu && can_blnd((struct monst *) 0, &youmonst,
(uchar) (singleobj->otyp == BLINDING_VENOM (uchar) ((singleobj->otyp == BLINDING_VENOM)
? AT_SPIT ? AT_SPIT
: AT_WEAP), : AT_WEAP),
singleobj)) { singleobj)) {
@@ -461,8 +605,8 @@ struct obj *obj; /* missile (or stack providing it) */
} }
} }
stop_occupation(); stop_occupation();
if (hitu || !range) { if (hitu) {
(void) drop_throw(singleobj, hitu, u.ux, u.uy); range = 0;
break; break;
} }
} }
@@ -481,8 +625,12 @@ struct obj *obj; /* missile (or stack providing it) */
!rn2(5), 0)) !rn2(5), 0))
/* Thrown objects "sink" */ /* Thrown objects "sink" */
|| IS_SINK(levl[bhitpos.x][bhitpos.y].typ)) { || IS_SINK(levl[bhitpos.x][bhitpos.y].typ)) {
if (singleobj) /* hits_bars might have destroyed it */ if (singleobj) { /* hits_bars might have destroyed it */
if (m_shot.n > 1 && (cansee(bhitpos.x, bhitpos.y)
|| (archer && canseemon(archer))))
pline("%s misses.", The(mshot_xname(singleobj)));
(void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y); (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
}
break; break;
} }
tmp_at(bhitpos.x, bhitpos.y); tmp_at(bhitpos.x, bhitpos.y);
@@ -500,121 +648,47 @@ struct obj *obj; /* missile (or stack providing it) */
} }
} }
/* Monster throws item at another monster */
int int
thrwmm(mtmp, mtarg) /* Monster throws item at monster */ thrwmm(mtmp, mtarg)
struct monst *mtmp, *mtarg; struct monst *mtmp, *mtarg;
{ {
struct obj *otmp, *mwep; struct obj *otmp, *mwep;
register xchar x, y; register xchar x, y;
boolean ispole; boolean ispole;
schar skill;
int multishot = 1;
/* Polearms won't be applied by monsters against other monsters */ /* Polearms won't be applied by monsters against other monsters */
if (mtmp->weapon_check == NEED_WEAPON || !MON_WEP(mtmp)) { if (mtmp->weapon_check == NEED_WEAPON || !MON_WEP(mtmp)) {
mtmp->weapon_check = NEED_RANGED_WEAPON; mtmp->weapon_check = NEED_RANGED_WEAPON;
/* mon_wield_item resets weapon_check as appropriate */ /* mon_wield_item resets weapon_check as appropriate */
if(mon_wield_item(mtmp) != 0) return 0; if (mon_wield_item(mtmp) != 0)
return 0;
} }
/* Pick a weapon */ /* Pick a weapon */
otmp = select_rwep(mtmp); otmp = select_rwep(mtmp);
if (!otmp) return 0; if (!otmp)
return 0;
ispole = is_pole(otmp); ispole = is_pole(otmp);
skill = objects[otmp->otyp].oc_skill;
x = mtmp->mx; x = mtmp->mx;
y = mtmp->my; y = mtmp->my;
mwep = MON_WEP(mtmp); /* wielded weapon */ mwep = MON_WEP(mtmp); /* wielded weapon */
if(!ispole && m_lined_up(mtarg, mtmp)) { if (!ispole && m_lined_up(mtarg, mtmp)) {
/* WAC Catch this since rn2(0) is illegal */ int chance = max(BOLT_LIM - distmin(x, y, mtarg->mx, mtarg->my), 1);
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;
if (!mtarg->mflee || !rn2(chance)) {
if (ammo_and_launcher(otmp, mwep)
&& dist2(mtmp->mx, mtmp->my, mtarg->mx, mtarg->my)
> PET_MISSILE_RANGE2)
return 0; /* Out of range */
/* Set target monster */ /* Set target monster */
target = mtarg; target = mtarg;
archer = mtmp; archer = mtmp;
while (multishot-- > 0) monshoot(mtmp, otmp, mwep); /* multishot shooting or throwing */
m_throw(mtmp, mtmp->mx, mtmp->my, archer = target = (struct monst *) 0;
sgn(tbx), sgn(tby),
distmin(mtmp->mx, mtmp->my,
mtarg->mx, mtarg->my),
otmp);
archer = (struct monst *)0;
target = (struct monst *)0;
nomul(0); nomul(0);
return 1; return 1;
} }
@@ -622,7 +696,6 @@ struct monst *mtmp, *mtarg;
return 0; return 0;
} }
/* monster spits substance at monster */ /* monster spits substance at monster */
int int
spitmm(mtmp, mattk, mtarg) spitmm(mtmp, mattk, mtarg)
@@ -675,8 +748,9 @@ struct attack *mattk;
return 0; return 0;
} }
/* monster breathes at monster (ranged) */
int int
breamm(mtmp, mattk, mtarg) /* monster breathes at monster (ranged) */ breamm(mtmp, mattk, mtarg)
struct monst *mtmp, *mtarg; struct monst *mtmp, *mtarg;
struct attack *mattk; struct attack *mattk;
{ {
@@ -691,20 +765,19 @@ struct attack *mattk;
else else
You_hear("a cough."); You_hear("a cough.");
} }
return(0); return 0;
} }
if (!mtmp->mspec_used && rn2(3)) { if (!mtmp->mspec_used && rn2(3)) {
if ((typ >= AD_MAGM) && (typ <= AD_ACID)) { if ((typ >= AD_MAGM) && (typ <= AD_ACID)) {
if (canseemon(mtmp)) if (canseemon(mtmp))
pline("%s breathes %s!", Monnam(mtmp), pline("%s breathes %s!", Monnam(mtmp), breathwep[typ - 1]);
breathwep[typ-1]); dobuzz((int) (-20 - (typ - 1)), (int)mattk->damn,
dobuzz((int) (-20 - (typ-1)), (int)mattk->damn,
mtmp->mx, mtmp->my, sgn(tbx), sgn(tby), FALSE); mtmp->mx, mtmp->my, sgn(tbx), sgn(tby), FALSE);
nomul(0); nomul(0);
/* breath runs out sometimes. Also, give monster some /* breath runs out sometimes. Also, give monster some
* cunning; don't breath if the target fell asleep. * cunning; don't breath if the target fell asleep.
*/ */
mtmp->mspec_used = 6+rn2(18); mtmp->mspec_used = 6 + rn2(18);
/* If this is a pet, it'll get hungry. Minions and /* If this is a pet, it'll get hungry. Minions and
* spell beings won't hunger */ * spell beings won't hunger */
@@ -717,9 +790,9 @@ struct attack *mattk;
} }
} else impossible("Breath weapon %d used", typ-1); } else impossible("Breath weapon %d used", typ-1);
} else } else
return (0); return 0;
} }
return(1); return 1;
} }
@@ -762,7 +835,6 @@ struct monst *mtmp;
{ {
struct obj *otmp, *mwep; struct obj *otmp, *mwep;
xchar x, y; xchar x, y;
int multishot;
const char *onm; const char *onm;
/* Rearranged beginning so monsters can use polearms not in a line */ /* Rearranged beginning so monsters can use polearms not in a line */
@@ -821,101 +893,7 @@ struct monst *mtmp;
return; return;
mwep = MON_WEP(mtmp); /* wielded weapon */ mwep = MON_WEP(mtmp); /* wielded weapon */
monshoot(mtmp, otmp, mwep); /* multishot shooting or throwing */
/* Multishot calculations */
multishot = 1;
if (otmp->quan > 1L /* no point checking if there's only 1 */
/* ammo requires corresponding launcher be wielded */
&& (is_ammo(otmp)
? matching_launcher(otmp, mwep)
/* otherwise any stackable (non-ammo) weapon */
: otmp->oclass == WEAPON_CLASS)
&& !mtmp->mconf) {
int skill = (int) objects[otmp->otyp].oc_skill;
/* Assumes lords are skilled, princes are expert */
if (is_prince(mtmp->data))
multishot += 2;
else if (is_lord(mtmp->data))
multishot++;
/* fake players treated as skilled (regardless of role limits) */
else if (is_mplayer(mtmp->data))
multishot++;
/* class bonus */
switch (monsndx(mtmp->data)) {
case PM_MONK:
if (skill == -P_SHURIKEN)
multishot++;
break;
case PM_RANGER:
multishot++;
break;
case PM_ROGUE:
if (skill == P_DAGGER)
multishot++;
break;
case PM_NINJA:
if (skill == -P_SHURIKEN || skill == -P_DART)
multishot++;
/*FALLTHRU*/
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)
|| (is_orc(mtmp->data) && otmp->otyp == ORCISH_ARROW && mwep
&& mwep->otyp == ORCISH_BOW))
multishot++;
multishot = rnd(multishot);
if ((long) multishot > otmp->quan)
multishot = (int) otmp->quan;
}
if (canseemon(mtmp)) {
char onmbuf[BUFSZ];
if (multishot > 1) {
/* "N arrows"; multishot > 1 implies otmp->quan > 1, so
xname()'s result will already be pluralized */
Sprintf(onmbuf, "%d %s", multishot, xname(otmp));
onm = onmbuf;
} else {
/* "an arrow" */
onm = singular(otmp, xname);
onm = obj_is_pname(otmp) ? the(onm) : an(onm);
}
m_shot.s = ammo_and_launcher(otmp, mwep) ? TRUE : FALSE;
pline("%s %s %s!", Monnam(mtmp), m_shot.s ? "shoots" : "throws", onm);
m_shot.o = otmp->otyp;
} else {
m_shot.o = STRANGE_OBJECT; /* don't give multishot feedback */
}
m_shot.n = multishot;
for (m_shot.i = 1; m_shot.i <= m_shot.n; m_shot.i++) {
m_throw(mtmp, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby),
distmin(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy), otmp);
/* conceptually all N missiles are in flight at once, but
if mtmp gets killed (shot kills adjacent gas spore and
triggers explosion, perhaps), inventory will be dropped
and otmp might go away via merging into another stack */
if (mtmp->mhp <= 0 && m_shot.i < m_shot.n) {
/* cancel pending shots (ought to give a message here since
we gave one above about throwing/shooting N missiles) */
break; /* endmultishot(FALSE); */
}
}
m_shot.n = m_shot.i = 0;
m_shot.o = STRANGE_OBJECT;
m_shot.s = FALSE;
nomul(0); nomul(0);
} }