@@ -3648,6 +3648,8 @@ extern int weapon_hit_bonus(struct obj *) NO_NNARGS;
|
|||||||
extern int weapon_dam_bonus(struct obj *) NO_NNARGS;
|
extern int weapon_dam_bonus(struct obj *) NO_NNARGS;
|
||||||
extern void skill_init(const struct def_skill *) NONNULLARG1;
|
extern void skill_init(const struct def_skill *) NONNULLARG1;
|
||||||
extern void setmnotwielded(struct monst *, struct obj *) NONNULLARG1;
|
extern void setmnotwielded(struct monst *, struct obj *) NONNULLARG1;
|
||||||
|
extern const struct throw_and_return_weapon *autoreturn_weapon(struct obj *)
|
||||||
|
NONNULLARG1;
|
||||||
|
|
||||||
/* ### were.c ### */
|
/* ### were.c ### */
|
||||||
|
|
||||||
|
|||||||
@@ -868,6 +868,12 @@ enum stoning_checks {
|
|||||||
st_all = (st_gloves | st_corpse | st_petrifies | st_resists)
|
st_all = (st_gloves | st_corpse | st_petrifies | st_resists)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct throw_and_return_weapon {
|
||||||
|
short otyp;
|
||||||
|
int range;
|
||||||
|
Bitfield(tethered, 1);
|
||||||
|
};
|
||||||
|
|
||||||
struct trapinfo {
|
struct trapinfo {
|
||||||
struct obj *tobj;
|
struct obj *tobj;
|
||||||
coordxy tx, ty;
|
coordxy tx, ty;
|
||||||
|
|||||||
+19
-17
@@ -6,26 +6,28 @@
|
|||||||
#ifndef MFNDPOS_H
|
#ifndef MFNDPOS_H
|
||||||
#define MFNDPOS_H
|
#define MFNDPOS_H
|
||||||
|
|
||||||
#define ALLOW_MDISP 0x00001000L /* can displace a monster out of its way */
|
/* clang-format off */
|
||||||
#define ALLOW_TRAPS 0x00020000L /* can enter traps */
|
#define ALLOW_MDISP 0x00001000L /* can displace a monster out of its way */
|
||||||
#define ALLOW_U 0x00040000L /* can attack you */
|
#define ALLOW_TRAPS 0x00020000L /* can enter traps */
|
||||||
#define ALLOW_M 0x00080000L /* can attack other monsters */
|
#define ALLOW_U 0x00040000L /* can attack you */
|
||||||
#define ALLOW_TM 0x00100000L /* can attack tame monsters */
|
#define ALLOW_M 0x00080000L /* can attack other monsters */
|
||||||
|
#define ALLOW_TM 0x00100000L /* can attack tame monsters */
|
||||||
#define ALLOW_ALL (ALLOW_U | ALLOW_M | ALLOW_TM | ALLOW_TRAPS)
|
#define ALLOW_ALL (ALLOW_U | ALLOW_M | ALLOW_TM | ALLOW_TRAPS)
|
||||||
#define NOTONL 0x00200000L /* avoids direct line to player */
|
#define NOTONL 0x00200000L /* avoids direct line to player */
|
||||||
#define OPENDOOR 0x00400000L /* opens closed doors */
|
#define OPENDOOR 0x00400000L /* opens closed doors */
|
||||||
#define UNLOCKDOOR 0x00800000L /* unlocks locked doors */
|
#define UNLOCKDOOR 0x00800000L /* unlocks locked doors */
|
||||||
#define BUSTDOOR 0x01000000L /* breaks any doors */
|
#define BUSTDOOR 0x01000000L /* breaks any doors */
|
||||||
#define ALLOW_ROCK 0x02000000L /* pushes rocks */
|
#define ALLOW_ROCK 0x02000000L /* pushes rocks */
|
||||||
#define ALLOW_WALL 0x04000000L /* walks thru walls */
|
#define ALLOW_WALL 0x04000000L /* walks thru walls */
|
||||||
#define ALLOW_DIG 0x08000000L /* digs */
|
#define ALLOW_DIG 0x08000000L /* digs */
|
||||||
#define ALLOW_BARS 0x10000000L /* may pass thru iron bars */
|
#define ALLOW_BARS 0x10000000L /* may pass thru iron bars */
|
||||||
#define ALLOW_SANCT 0x20000000L /* enters temples */
|
#define ALLOW_SANCT 0x20000000L /* enters temples */
|
||||||
#define ALLOW_SSM 0x40000000L /* ignores scare monster */
|
#define ALLOW_SSM 0x40000000L /* ignores scare monster */
|
||||||
#ifdef NHSTDC
|
#ifdef NHSTDC
|
||||||
#define NOGARLIC 0x80000000UL /* hates garlic */
|
#define NOGARLIC 0x80000000UL /* hates garlic */
|
||||||
#else
|
#else
|
||||||
#define NOGARLIC 0x80000000L /* hates garlic */
|
#define NOGARLIC 0x80000000L /* hates garlic */
|
||||||
#endif
|
#endif
|
||||||
|
/* clang-format on */
|
||||||
|
|
||||||
#endif /* MFNDPOS_H */
|
#endif /* MFNDPOS_H */
|
||||||
|
|||||||
+4
-3
@@ -1476,10 +1476,11 @@ throwit(struct obj *obj,
|
|||||||
{
|
{
|
||||||
struct monst *mon;
|
struct monst *mon;
|
||||||
int range, urange;
|
int range, urange;
|
||||||
|
const struct throw_and_return_weapon *arw = autoreturn_weapon(obj);
|
||||||
boolean crossbowing,
|
boolean crossbowing,
|
||||||
impaired = (Confusion || Stunned || Blind
|
impaired = (Confusion || Stunned || Blind
|
||||||
|| Hallucination || Fumbling),
|
|| Hallucination || Fumbling),
|
||||||
tethered_weapon = (obj->otyp == AKLYS && (wep_mask & W_WEP) != 0);
|
tethered_weapon = (arw && arw->tethered && (wep_mask & W_WEP) != 0);
|
||||||
|
|
||||||
gn.notonhead = FALSE; /* reset potentially stale value */
|
gn.notonhead = FALSE; /* reset potentially stale value */
|
||||||
if ((obj->cursed || obj->greased) && (u.dx || u.dy) && !rn2(7)) {
|
if ((obj->cursed || obj->greased) && (u.dx || u.dy) && !rn2(7)) {
|
||||||
@@ -1616,9 +1617,9 @@ throwit(struct obj *obj,
|
|||||||
else if (is_art(obj, ART_MJOLLNIR))
|
else if (is_art(obj, ART_MJOLLNIR))
|
||||||
range = (range + 1) / 2; /* it's heavy */
|
range = (range + 1) / 2; /* it's heavy */
|
||||||
else if (tethered_weapon) /* primary weapon is aklys */
|
else if (tethered_weapon) /* primary weapon is aklys */
|
||||||
/* if an aklys is going to return, range is limited by the
|
/* range of a tethered_weapon is limited by the
|
||||||
length of the attached cord [implicit aspect of item] */
|
length of the attached cord [implicit aspect of item] */
|
||||||
range = min(range, BOLT_LIM / 2);
|
range = min(range, arw->range);
|
||||||
else if (obj == uball && u.utrap && u.utraptype == TT_INFLOOR)
|
else if (obj == uball && u.utrap && u.utraptype == TT_INFLOOR)
|
||||||
range = 1;
|
range = 1;
|
||||||
|
|
||||||
|
|||||||
+3
-4
@@ -470,7 +470,7 @@ mattacku(struct monst *mtmp)
|
|||||||
struct permonst *mdat = mtmp->data;
|
struct permonst *mdat = mtmp->data;
|
||||||
/*
|
/*
|
||||||
* ranged: Is it near you? Affects your actions.
|
* ranged: Is it near you? Affects your actions.
|
||||||
* ranged2: Does it think it's near you? Affects its actions.
|
* range2: Does it think it's near you? Affects its actions.
|
||||||
* foundyou: Is it attacking you or your image?
|
* foundyou: Is it attacking you or your image?
|
||||||
* youseeit: Can you observe the attack? It might be attacking your
|
* youseeit: Can you observe the attack? It might be attacking your
|
||||||
* image around the corner, or invisible, or you might be blind.
|
* image around the corner, or invisible, or you might be blind.
|
||||||
@@ -497,11 +497,10 @@ mattacku(struct monst *mtmp)
|
|||||||
return 0;
|
return 0;
|
||||||
u.ustuck->mux = u.ux;
|
u.ustuck->mux = u.ux;
|
||||||
u.ustuck->muy = u.uy;
|
u.ustuck->muy = u.uy;
|
||||||
range2 = 0;
|
|
||||||
foundyou = 1;
|
|
||||||
if (u.uinvulnerable)
|
if (u.uinvulnerable)
|
||||||
return 0; /* stomachs can't hurt you! */
|
return 0; /* stomachs can't hurt you! */
|
||||||
|
range2 = 0;
|
||||||
|
foundyou = 1;
|
||||||
} else if (u.usteed) {
|
} else if (u.usteed) {
|
||||||
if (mtmp == u.usteed)
|
if (mtmp == u.usteed)
|
||||||
/* Your steed won't attack you */
|
/* Your steed won't attack you */
|
||||||
|
|||||||
+46
-17
@@ -23,7 +23,7 @@ staticfn int postmov(struct monst *, struct permonst *, coordxy, coordxy, int,
|
|||||||
unsigned, boolean, boolean, boolean) NONNULLPTRS;
|
unsigned, boolean, boolean, boolean) NONNULLPTRS;
|
||||||
staticfn boolean leppie_avoidance(struct monst *);
|
staticfn boolean leppie_avoidance(struct monst *);
|
||||||
staticfn void leppie_stash(struct monst *);
|
staticfn void leppie_stash(struct monst *);
|
||||||
staticfn boolean m_balks_at_approaching(struct monst *);
|
staticfn int m_balks_at_approaching(int, struct monst *, int *, int *);
|
||||||
staticfn boolean stuff_prevents_passage(struct monst *);
|
staticfn boolean stuff_prevents_passage(struct monst *);
|
||||||
staticfn int vamp_shift(struct monst *, struct permonst *, boolean);
|
staticfn int vamp_shift(struct monst *, struct permonst *, boolean);
|
||||||
staticfn void maybe_spin_web(struct monst *);
|
staticfn void maybe_spin_web(struct monst *);
|
||||||
@@ -1170,32 +1170,57 @@ leppie_stash(struct monst *mtmp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* does monster want to avoid you? */
|
/* does monster want to avoid you?
|
||||||
staticfn boolean
|
* returns the original value of appr if not.
|
||||||
m_balks_at_approaching(struct monst *mtmp)
|
* returns -1 if so.
|
||||||
|
* returns -2 if monster wants to adhere to a particular range,
|
||||||
|
* which may actually be further away,
|
||||||
|
* and sets *pdistmin and *pdistmax to describe that range
|
||||||
|
*/
|
||||||
|
staticfn int
|
||||||
|
m_balks_at_approaching(int oldappr, struct monst *mtmp, int *pdistmin,
|
||||||
|
int *pdistmax)
|
||||||
{
|
{
|
||||||
|
struct obj *mwep = MON_WEP(mtmp);
|
||||||
|
coordxy x = mtmp->mx, y = mtmp->my, ux = mtmp->mux, uy = mtmp->muy;
|
||||||
|
int edist = dist2(x, y, ux, uy);
|
||||||
|
const struct throw_and_return_weapon *arw;
|
||||||
|
|
||||||
|
if (pdistmin)
|
||||||
|
*pdistmin = 0;
|
||||||
|
if (pdistmax)
|
||||||
|
*pdistmax = 0;
|
||||||
|
|
||||||
/* peaceful, far away, or can't see you */
|
/* peaceful, far away, or can't see you */
|
||||||
if (mtmp->mpeaceful
|
if (mtmp->mpeaceful || (edist >= 5 * 5) || !m_canseeu(mtmp))
|
||||||
|| (dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) >= 5*5)
|
return oldappr;
|
||||||
|| !m_canseeu(mtmp))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
/* has ammo+launcher */
|
/* has ammo+launcher */
|
||||||
if (m_has_launcher_and_ammo(mtmp))
|
if (m_has_launcher_and_ammo(mtmp))
|
||||||
return TRUE;
|
return -1;
|
||||||
|
|
||||||
/* is using a polearm and in range */
|
/* is using a polearm and in range */
|
||||||
if (MON_WEP(mtmp) && is_pole(MON_WEP(mtmp))
|
if (MON_WEP(mtmp) && is_pole(MON_WEP(mtmp))
|
||||||
&& dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <= MON_POLE_DIST)
|
&& edist <= MON_POLE_DIST)
|
||||||
return TRUE;
|
return -1;
|
||||||
|
|
||||||
|
/* is using a throw-and-return weapon; provide min and max preferred range
|
||||||
|
*/
|
||||||
|
if (mwep && (arw = autoreturn_weapon(mwep)) != 0) {
|
||||||
|
if (pdistmin)
|
||||||
|
*pdistmin = 2 * 2;
|
||||||
|
if (pdistmax)
|
||||||
|
*pdistmax = arw->range * arw->range;
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
/* can attack from distance, and hp loss or attack not used */
|
/* can attack from distance, and hp loss or attack not used */
|
||||||
if (ranged_attk_available(mtmp)
|
if (ranged_attk_available(mtmp)
|
||||||
&& ((mtmp->mhp < (mtmp->mhpmax+1) / 3)
|
&& ((mtmp->mhp < (mtmp->mhpmax+1) / 3)
|
||||||
|| !mtmp->mspec_used))
|
|| !mtmp->mspec_used))
|
||||||
return TRUE;
|
return -1;
|
||||||
|
|
||||||
return FALSE;
|
return oldappr; /* leaves appr unchanged */
|
||||||
}
|
}
|
||||||
|
|
||||||
staticfn boolean
|
staticfn boolean
|
||||||
@@ -1697,7 +1722,8 @@ m_move(struct monst *mtmp, int after)
|
|||||||
boolean better_with_displacing = FALSE;
|
boolean better_with_displacing = FALSE;
|
||||||
unsigned seenflgs;
|
unsigned seenflgs;
|
||||||
struct permonst *ptr;
|
struct permonst *ptr;
|
||||||
int chi, mmoved = MMOVE_NOTHING; /* not strictly nec.: chi >= 0 will do */
|
int chi, mmoved = MMOVE_NOTHING, /* not strictly nec.: chi >= 0 will do */
|
||||||
|
preferredrange_min = 0, preferredrange_max = 0;
|
||||||
long info[9];
|
long info[9];
|
||||||
long flag;
|
long flag;
|
||||||
coordxy omx = mtmp->mx, omy = mtmp->my;
|
coordxy omx = mtmp->mx, omy = mtmp->my;
|
||||||
@@ -1848,8 +1874,7 @@ m_move(struct monst *mtmp, int after)
|
|||||||
appr = -1;
|
appr = -1;
|
||||||
|
|
||||||
/* hostiles with ranged weapon or attack try to stay away */
|
/* hostiles with ranged weapon or attack try to stay away */
|
||||||
if (m_balks_at_approaching(mtmp))
|
appr = m_balks_at_approaching(appr, mtmp, &preferredrange_min, &preferredrange_max);
|
||||||
appr = -1;
|
|
||||||
|
|
||||||
if (!should_see && can_track(ptr)) {
|
if (!should_see && can_track(ptr)) {
|
||||||
coord *cp;
|
coord *cp;
|
||||||
@@ -1942,7 +1967,11 @@ m_move(struct monst *mtmp, int after)
|
|||||||
nearer = ((ndist = dist2(nx, ny, ggx, ggy)) < nidist);
|
nearer = ((ndist = dist2(nx, ny, ggx, ggy)) < nidist);
|
||||||
|
|
||||||
if ((appr == 1 && nearer) || (appr == -1 && !nearer)
|
if ((appr == 1 && nearer) || (appr == -1 && !nearer)
|
||||||
|| (!appr && !rn2(++chcnt)) || (mmoved == MMOVE_NOTHING)) {
|
|| (!appr && !rn2(++chcnt))
|
||||||
|
|| (appr == -2
|
||||||
|
&& ((ndist <= preferredrange_min && !nearer)
|
||||||
|
|| (ndist >= preferredrange_max && nearer)))
|
||||||
|
|| (mmoved == MMOVE_NOTHING)) {
|
||||||
nix = nx;
|
nix = nx;
|
||||||
niy = ny;
|
niy = ny;
|
||||||
nidist = ndist;
|
nidist = ndist;
|
||||||
|
|||||||
+173
-9
@@ -12,6 +12,7 @@ staticfn const char *breathwep_name(int);
|
|||||||
staticfn boolean drop_throw(struct obj *, boolean, coordxy, coordxy);
|
staticfn boolean drop_throw(struct obj *, boolean, coordxy, coordxy);
|
||||||
staticfn boolean blocking_terrain(coordxy, coordxy);
|
staticfn boolean blocking_terrain(coordxy, coordxy);
|
||||||
staticfn int m_lined_up(struct monst *, struct monst *) NONNULLARG12;
|
staticfn int m_lined_up(struct monst *, struct monst *) NONNULLARG12;
|
||||||
|
staticfn void return_from_mtoss(struct monst *, struct obj *, boolean);
|
||||||
|
|
||||||
#define URETREATING(x, y) \
|
#define URETREATING(x, y) \
|
||||||
(distmin(u.ux, u.uy, x, y) > distmin(u.ux0, u.uy0, x, y))
|
(distmin(u.ux, u.uy, x, y) > distmin(u.ux0, u.uy0, x, y))
|
||||||
@@ -558,6 +559,10 @@ m_throw(
|
|||||||
boolean forcehit;
|
boolean forcehit;
|
||||||
char sym = obj->oclass;
|
char sym = obj->oclass;
|
||||||
int hitu = 0, oldumort, blindinc = 0;
|
int hitu = 0, oldumort, blindinc = 0;
|
||||||
|
const struct throw_and_return_weapon *arw = autoreturn_weapon(obj);
|
||||||
|
boolean tethered_weapon =
|
||||||
|
(obj == MON_WEP(mon) && arw && arw->tethered != 0),
|
||||||
|
return_flightpath = FALSE;
|
||||||
|
|
||||||
gb.bhitpos.x = x;
|
gb.bhitpos.x = x;
|
||||||
gb.bhitpos.y = y;
|
gb.bhitpos.y = y;
|
||||||
@@ -619,8 +624,30 @@ m_throw(
|
|||||||
* early to avoid the dagger bug, anyone who modifies this code should
|
* early to avoid the dagger bug, anyone who modifies this code should
|
||||||
* be careful not to use either one after it's been freed.
|
* be careful not to use either one after it's been freed.
|
||||||
*/
|
*/
|
||||||
if (sym)
|
if (sym) {
|
||||||
tmp_at(DISP_FLASH, obj_to_glyph(singleobj, rn2_on_display_rng));
|
if (!tethered_weapon) {
|
||||||
|
tmp_at(DISP_FLASH, obj_to_glyph(singleobj, rn2_on_display_rng));
|
||||||
|
} else {
|
||||||
|
tmp_at(DISP_TETHER, obj_to_glyph(singleobj, rn2_on_display_rng));
|
||||||
|
/*
|
||||||
|
* Considerations for a tethered object based on in throwit()/bhit() :
|
||||||
|
* - wall of water/lava will stop items, and triggers return.
|
||||||
|
* - iron bars will stop items, and triggers return.
|
||||||
|
* - pass harmlessly through shades.
|
||||||
|
* X stops forward motion at hit monster/hero, triggers return.
|
||||||
|
* - closed door will stop item's forward motion, triggers return.
|
||||||
|
* - sinks stop forward motion, triggers fall, then return.
|
||||||
|
* - object can get tangled in a web, no return (tether snaps?).
|
||||||
|
* On return:
|
||||||
|
* X rn2(100) chance of returning to thrower's location.
|
||||||
|
* X if impaired and rn2(100) == 0,
|
||||||
|
* -50/50 chance of landing on the ground.
|
||||||
|
* -50/50 chance of hitting the thrower and causing
|
||||||
|
* rnd(3) damage.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
while (range-- > 0) { /* Actually the loop is always exited by break */
|
while (range-- > 0) { /* Actually the loop is always exited by break */
|
||||||
singleobj->ox = gb.bhitpos.x += dx;
|
singleobj->ox = gb.bhitpos.x += dx;
|
||||||
singleobj->oy = gb.bhitpos.y += dy;
|
singleobj->oy = gb.bhitpos.y += dy;
|
||||||
@@ -730,7 +757,12 @@ m_throw(
|
|||||||
}
|
}
|
||||||
stop_occupation();
|
stop_occupation();
|
||||||
if (hitu) {
|
if (hitu) {
|
||||||
(void) drop_throw(singleobj, hitu, u.ux, u.uy);
|
if (!tethered_weapon) {
|
||||||
|
(void) drop_throw(singleobj, hitu, u.ux, u.uy);
|
||||||
|
} else {
|
||||||
|
/* ready for return journey */
|
||||||
|
return_flightpath = TRUE;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -751,8 +783,12 @@ m_throw(
|
|||||||
&& (cansee(gb.bhitpos.x, gb.bhitpos.y)
|
&& (cansee(gb.bhitpos.x, gb.bhitpos.y)
|
||||||
|| (gm.marcher && canseemon(gm.marcher))))
|
|| (gm.marcher && canseemon(gm.marcher))))
|
||||||
pline("%s misses.", The(mshot_xname(singleobj)));
|
pline("%s misses.", The(mshot_xname(singleobj)));
|
||||||
|
if (!tethered_weapon) {
|
||||||
(void) drop_throw(singleobj, 0, gb.bhitpos.x, gb.bhitpos.y);
|
(void) drop_throw(singleobj, 0, gb.bhitpos.x, gb.bhitpos.y);
|
||||||
|
} else {
|
||||||
|
/*ready for return journey */
|
||||||
|
return_flightpath = TRUE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -761,7 +797,11 @@ m_throw(
|
|||||||
}
|
}
|
||||||
tmp_at(gb.bhitpos.x, gb.bhitpos.y);
|
tmp_at(gb.bhitpos.x, gb.bhitpos.y);
|
||||||
nh_delay_output();
|
nh_delay_output();
|
||||||
tmp_at(DISP_END, 0);
|
if (arw && return_flightpath)
|
||||||
|
return_from_mtoss(mon, singleobj, tethered_weapon);
|
||||||
|
/* mon could be DEADMONSTER now */
|
||||||
|
else
|
||||||
|
tmp_at(DISP_END, 0);
|
||||||
gm.mesg_given = 0; /* reset */
|
gm.mesg_given = 0; /* reset */
|
||||||
|
|
||||||
if (blindinc) {
|
if (blindinc) {
|
||||||
@@ -777,6 +817,121 @@ m_throw(
|
|||||||
|
|
||||||
#undef MT_FLIGHTCHECK
|
#undef MT_FLIGHTCHECK
|
||||||
|
|
||||||
|
staticfn void
|
||||||
|
return_from_mtoss(struct monst *magr, struct obj *otmp, boolean tethered_weapon)
|
||||||
|
{
|
||||||
|
boolean impaired = (magr->mconf || magr->mstun || magr->mblinded),
|
||||||
|
notcaught = FALSE, hits_thrower = FALSE;
|
||||||
|
coordxy x = gb.bhitpos.x, y = gb.bhitpos.y;
|
||||||
|
int made_it_back = rn2(100), dmg = 0;
|
||||||
|
|
||||||
|
if (otmp && made_it_back) {
|
||||||
|
/* it made it back to thrower's location */
|
||||||
|
if (tethered_weapon) {
|
||||||
|
tmp_at(DISP_END, BACKTRACK);
|
||||||
|
} else {
|
||||||
|
int dx = sgn(x - magr->mx),
|
||||||
|
dy = sgn(y - magr->my);
|
||||||
|
|
||||||
|
if (x != magr->mx || y != magr->my) {
|
||||||
|
tmp_at(DISP_FLASH, obj_to_glyph(otmp, rn2_on_display_rng));
|
||||||
|
while (isok(x, y) && (x != magr->mx || y != magr->my)) {
|
||||||
|
tmp_at(x, y);
|
||||||
|
nh_delay_output();
|
||||||
|
x -= dx;
|
||||||
|
y -= dy;
|
||||||
|
}
|
||||||
|
tmp_at(DISP_END, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x = magr->mx;
|
||||||
|
y = magr->my;
|
||||||
|
if (!impaired && rn2(100)) {
|
||||||
|
static long do_not_annoy = 0;
|
||||||
|
|
||||||
|
if (!do_not_annoy || (svm.moves - do_not_annoy) > 500) {
|
||||||
|
pline("%s to %s %s!", Tobjnam(otmp, "return"),
|
||||||
|
s_suffix(mon_nam(magr)), mbodypart(magr, HAND));
|
||||||
|
do_not_annoy = svm.moves;
|
||||||
|
}
|
||||||
|
if (otmp) {
|
||||||
|
add_to_minv(magr, otmp);
|
||||||
|
if (tethered_weapon) {
|
||||||
|
magr->mw = otmp;
|
||||||
|
otmp->owornmask |= W_WEP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cansee(x, y))
|
||||||
|
newsym(x, y);
|
||||||
|
} else {
|
||||||
|
boolean mlevitating = FALSE; /* msg future-proofing only */
|
||||||
|
|
||||||
|
dmg = rn2(2);
|
||||||
|
if (!dmg) {
|
||||||
|
if (!Blind) {
|
||||||
|
pline("%s back to %s, landing %s %s %s.",
|
||||||
|
Tobjnam(otmp, "return"), mon_nam(magr),
|
||||||
|
mlevitating ? "beneath" : "at", mhis(magr),
|
||||||
|
makeplural(mbodypart(magr, FOOT)));
|
||||||
|
} else if (!Deaf) {
|
||||||
|
You_hear("%s land near %s.", Something, mon_nam(magr));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dmg += rnd(3);
|
||||||
|
if (!Blind) {
|
||||||
|
pline("%s back toward %s, hitting %s %s!",
|
||||||
|
Tobjnam(otmp, "fly"),
|
||||||
|
mon_nam(magr),
|
||||||
|
mhis(magr),
|
||||||
|
body_part(ARM));
|
||||||
|
} else if (!Deaf) {
|
||||||
|
You_hear("%s hit %s with a thud!", something,
|
||||||
|
mon_nam(magr));
|
||||||
|
}
|
||||||
|
hits_thrower = TRUE;
|
||||||
|
}
|
||||||
|
notcaught = TRUE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* it didn't make it back to thrower's location */
|
||||||
|
if (tethered_weapon)
|
||||||
|
tmp_at(DISP_END, 0);
|
||||||
|
You_hear("a loud snap!");
|
||||||
|
notcaught = TRUE;
|
||||||
|
}
|
||||||
|
if (otmp) {
|
||||||
|
if (hits_thrower) {
|
||||||
|
if (otmp->oartifact)
|
||||||
|
(void) artifact_hit((struct monst *) 0, magr, otmp, &dmg, 0);
|
||||||
|
magr->mhp -= dmg;
|
||||||
|
/* magr could be a DEADMONSTER now */
|
||||||
|
}
|
||||||
|
if (notcaught) {
|
||||||
|
(void) snuff_candle(otmp);
|
||||||
|
if (!ship_object(otmp, x, y, FALSE)) {
|
||||||
|
if (flooreffects(otmp, x, y, "drop")) {
|
||||||
|
if (cansee(x, y))
|
||||||
|
newsym(x, y);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
place_object(otmp, x, y);
|
||||||
|
stackobj(otmp);
|
||||||
|
}
|
||||||
|
if (!Deaf && !Underwater) {
|
||||||
|
/* Some sound effects when item lands in water or lava */
|
||||||
|
if (is_pool(x, y) || (is_lava(x, y) && !is_flammable(otmp))) {
|
||||||
|
Soundeffect(se_splash, 50);
|
||||||
|
pline((weight(otmp) > 9) ? "Splash!" : "Plop!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (obj_sheds_light(otmp))
|
||||||
|
gv.vision_full_recalc = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cansee(x, y))
|
||||||
|
newsym(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
/* Monster throws item at another monster */
|
/* Monster throws item at another monster */
|
||||||
int
|
int
|
||||||
thrwmm(struct monst *mtmp, struct monst *mtarg)
|
thrwmm(struct monst *mtmp, struct monst *mtarg)
|
||||||
@@ -988,6 +1143,9 @@ thrwmu(struct monst *mtmp)
|
|||||||
struct obj *otmp, *mwep;
|
struct obj *otmp, *mwep;
|
||||||
coordxy x, y;
|
coordxy x, y;
|
||||||
const char *onm;
|
const char *onm;
|
||||||
|
int rang;
|
||||||
|
const struct throw_and_return_weapon *arw;
|
||||||
|
boolean always_toss = FALSE;
|
||||||
|
|
||||||
/* Rearranged beginning so monsters can use polearms not in a line */
|
/* Rearranged beginning so monsters can use polearms not in a line */
|
||||||
if (mtmp->weapon_check == NEED_WEAPON || !MON_WEP(mtmp)) {
|
if (mtmp->weapon_check == NEED_WEAPON || !MON_WEP(mtmp)) {
|
||||||
@@ -1003,10 +1161,10 @@ thrwmu(struct monst *mtmp)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (is_pole(otmp)) {
|
if (is_pole(otmp)) {
|
||||||
int dam, hitv, rang;
|
int dam, hitv;
|
||||||
|
|
||||||
if (otmp != MON_WEP(mtmp))
|
if (otmp != MON_WEP(mtmp))
|
||||||
return; /* polearm must be wielded */
|
return; /* polearm, aklys must be wielded */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MON_POLE_DIST encompasses knight's move range (5): two spots
|
* MON_POLE_DIST encompasses knight's move range (5): two spots
|
||||||
@@ -1047,6 +1205,11 @@ thrwmu(struct monst *mtmp)
|
|||||||
(void) thitu(hitv, dam, &otmp, (char *) 0);
|
(void) thitu(hitv, dam, &otmp, (char *) 0);
|
||||||
stop_occupation();
|
stop_occupation();
|
||||||
return;
|
return;
|
||||||
|
} else if ((arw = autoreturn_weapon(otmp)) != 0 && !mwelded(otmp)) {
|
||||||
|
rang = dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy);
|
||||||
|
if (rang > arw->range || !couldsee(mtmp->mx, mtmp->my))
|
||||||
|
return; /* Out of range, or intervening wall */
|
||||||
|
always_toss = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
x = mtmp->mx;
|
x = mtmp->mx;
|
||||||
@@ -1058,7 +1221,8 @@ thrwmu(struct monst *mtmp)
|
|||||||
*/
|
*/
|
||||||
if (!lined_up(mtmp)
|
if (!lined_up(mtmp)
|
||||||
|| (URETREATING(x, y)
|
|| (URETREATING(x, y)
|
||||||
&& rn2(BOLT_LIM - distmin(x, y, mtmp->mux, mtmp->muy))))
|
&& (!always_toss
|
||||||
|
&& rn2(BOLT_LIM - distmin(x, y, mtmp->mux, mtmp->muy)))))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mwep = MON_WEP(mtmp); /* wielded weapon */
|
mwep = MON_WEP(mtmp); /* wielded weapon */
|
||||||
|
|||||||
+48
-4
@@ -493,21 +493,38 @@ oselect(struct monst *mtmp, int type)
|
|||||||
return (struct obj *) 0;
|
return (struct obj *) 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: have monsters use aklys' throw-and-return */
|
|
||||||
static NEARDATA const int rwep[] = {
|
static NEARDATA const int rwep[] = {
|
||||||
DWARVISH_SPEAR, SILVER_SPEAR, ELVEN_SPEAR, SPEAR, ORCISH_SPEAR, JAVELIN,
|
DWARVISH_SPEAR, SILVER_SPEAR, ELVEN_SPEAR, SPEAR, ORCISH_SPEAR, JAVELIN,
|
||||||
SHURIKEN, YA, SILVER_ARROW, ELVEN_ARROW, ARROW, ORCISH_ARROW,
|
SHURIKEN, YA, SILVER_ARROW, ELVEN_ARROW, ARROW, ORCISH_ARROW,
|
||||||
CROSSBOW_BOLT, SILVER_DAGGER, ELVEN_DAGGER, DAGGER, ORCISH_DAGGER, KNIFE,
|
CROSSBOW_BOLT, SILVER_DAGGER, ELVEN_DAGGER, DAGGER, ORCISH_DAGGER, KNIFE,
|
||||||
FLINT, ROCK, LOADSTONE, LUCKSTONE, DART,
|
FLINT, ROCK, LOADSTONE, LUCKSTONE, DART, CREAM_PIE,
|
||||||
/* BOOMERANG, */ CREAM_PIE
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* polearms */
|
||||||
static NEARDATA const int pwep[] = { HALBERD, BARDICHE, SPETUM,
|
static NEARDATA const int pwep[] = { HALBERD, BARDICHE, SPETUM,
|
||||||
BILL_GUISARME, VOULGE, RANSEUR,
|
BILL_GUISARME, VOULGE, RANSEUR,
|
||||||
GUISARME, GLAIVE, LUCERN_HAMMER,
|
GUISARME, GLAIVE, LUCERN_HAMMER,
|
||||||
BEC_DE_CORBIN, FAUCHARD, PARTISAN,
|
BEC_DE_CORBIN, FAUCHARD, PARTISAN,
|
||||||
LANCE };
|
LANCE };
|
||||||
|
|
||||||
|
/* throw-and-return weapons */
|
||||||
|
static NEARDATA const struct throw_and_return_weapon arwep[] = {
|
||||||
|
/* { BOOMERANG, 5, 0 }, */
|
||||||
|
{ AKLYS, (BOLT_LIM / 2), 1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct throw_and_return_weapon *
|
||||||
|
autoreturn_weapon(struct obj *otmp)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < SIZE(arwep); i++) {
|
||||||
|
if (otmp->otyp == arwep[i].otyp)
|
||||||
|
return &arwep[i];
|
||||||
|
}
|
||||||
|
return (struct throw_and_return_weapon *) 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* select a ranged weapon for the monster */
|
/* select a ranged weapon for the monster */
|
||||||
struct obj *
|
struct obj *
|
||||||
select_rwep(struct monst *mtmp)
|
select_rwep(struct monst *mtmp)
|
||||||
@@ -557,9 +574,31 @@ select_rwep(struct monst *mtmp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* Next, try to select a throw-and-return weapon, since they are
|
||||||
|
* also not as expendable. Again, don't pick one if monster's
|
||||||
|
* weapon is welded.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < SIZE(arwep); i++) {
|
||||||
|
const struct throw_and_return_weapon *arw = &arwep[i];
|
||||||
|
|
||||||
|
if (!mindless(mtmp->data) && !is_animal(mtmp->data) && !mweponly
|
||||||
|
&& dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <= arw->range
|
||||||
|
&& couldsee(mtmp->mx, mtmp->my)) {
|
||||||
|
if ((((mtmp->misc_worn_check & W_ARMS) == 0)
|
||||||
|
|| !objects[arw->otyp].oc_bimanual)
|
||||||
|
&& (objects[arw->otyp].oc_material != SILVER
|
||||||
|
|| !mon_hates_silver(mtmp))) {
|
||||||
|
if ((otmp = oselect(mtmp, arw->otyp)) != 0
|
||||||
|
&& (otmp == mwep || !mweponly)) {
|
||||||
|
gp.propellor = otmp; /* force the monster to wield it */
|
||||||
|
return otmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* other than these two specific cases, always select the
|
* other than the specific cases above, always select the
|
||||||
* most potent ranged weapon to hand.
|
* most potent ranged weapon to hand.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < SIZE(rwep); i++) {
|
for (i = 0; i < SIZE(rwep); i++) {
|
||||||
@@ -840,10 +879,15 @@ mon_wield_item(struct monst *mon)
|
|||||||
mon->weapon_check = NEED_WEAPON;
|
mon->weapon_check = NEED_WEAPON;
|
||||||
if (canseemon(mon)) {
|
if (canseemon(mon)) {
|
||||||
boolean newly_welded;
|
boolean newly_welded;
|
||||||
|
const struct throw_and_return_weapon *arw;
|
||||||
|
|
||||||
pline_mon(mon, "%s wields %s%c",
|
pline_mon(mon, "%s wields %s%c",
|
||||||
Monnam(mon), doname(obj),
|
Monnam(mon), doname(obj),
|
||||||
exclaim ? '!' : '.');
|
exclaim ? '!' : '.');
|
||||||
|
if ((arw = autoreturn_weapon(obj)) != 0 && arw->tethered != 0)
|
||||||
|
pline_mon(mon, "%s secures the tether on %s.", Monnam(mon),
|
||||||
|
the(xname(obj)));
|
||||||
|
|
||||||
/* 3.6.3: mwelded() predicate expects the object to have its
|
/* 3.6.3: mwelded() predicate expects the object to have its
|
||||||
W_WEP bit set in owormmask, but the pline here and for
|
W_WEP bit set in owormmask, but the pline here and for
|
||||||
artifact_light don't want that because they'd have '(weapon
|
artifact_light don't want that because they'd have '(weapon
|
||||||
|
|||||||
Reference in New Issue
Block a user