182
src/mthrowu.c
182
src/mthrowu.c
@@ -12,6 +12,7 @@ staticfn const char *breathwep_name(int);
|
||||
staticfn boolean drop_throw(struct obj *, boolean, coordxy, coordxy);
|
||||
staticfn boolean blocking_terrain(coordxy, coordxy);
|
||||
staticfn int m_lined_up(struct monst *, struct monst *) NONNULLARG12;
|
||||
staticfn void return_from_mtoss(struct monst *, struct obj *, boolean);
|
||||
|
||||
#define URETREATING(x, y) \
|
||||
(distmin(u.ux, u.uy, x, y) > distmin(u.ux0, u.uy0, x, y))
|
||||
@@ -558,6 +559,10 @@ m_throw(
|
||||
boolean forcehit;
|
||||
char sym = obj->oclass;
|
||||
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.y = y;
|
||||
@@ -619,8 +624,30 @@ m_throw(
|
||||
* early to avoid the dagger bug, anyone who modifies this code should
|
||||
* be careful not to use either one after it's been freed.
|
||||
*/
|
||||
if (sym)
|
||||
tmp_at(DISP_FLASH, obj_to_glyph(singleobj, rn2_on_display_rng));
|
||||
if (sym) {
|
||||
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 */
|
||||
singleobj->ox = gb.bhitpos.x += dx;
|
||||
singleobj->oy = gb.bhitpos.y += dy;
|
||||
@@ -730,7 +757,12 @@ m_throw(
|
||||
}
|
||||
stop_occupation();
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -751,8 +783,12 @@ m_throw(
|
||||
&& (cansee(gb.bhitpos.x, gb.bhitpos.y)
|
||||
|| (gm.marcher && canseemon(gm.marcher))))
|
||||
pline("%s misses.", The(mshot_xname(singleobj)));
|
||||
|
||||
(void) drop_throw(singleobj, 0, gb.bhitpos.x, gb.bhitpos.y);
|
||||
if (!tethered_weapon) {
|
||||
(void) drop_throw(singleobj, 0, gb.bhitpos.x, gb.bhitpos.y);
|
||||
} else {
|
||||
/*ready for return journey */
|
||||
return_flightpath = TRUE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -761,7 +797,11 @@ m_throw(
|
||||
}
|
||||
tmp_at(gb.bhitpos.x, gb.bhitpos.y);
|
||||
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 */
|
||||
|
||||
if (blindinc) {
|
||||
@@ -777,6 +817,121 @@ m_throw(
|
||||
|
||||
#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 */
|
||||
int
|
||||
thrwmm(struct monst *mtmp, struct monst *mtarg)
|
||||
@@ -988,6 +1143,9 @@ thrwmu(struct monst *mtmp)
|
||||
struct obj *otmp, *mwep;
|
||||
coordxy x, y;
|
||||
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 */
|
||||
if (mtmp->weapon_check == NEED_WEAPON || !MON_WEP(mtmp)) {
|
||||
@@ -1003,10 +1161,10 @@ thrwmu(struct monst *mtmp)
|
||||
return;
|
||||
|
||||
if (is_pole(otmp)) {
|
||||
int dam, hitv, rang;
|
||||
int dam, hitv;
|
||||
|
||||
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
|
||||
@@ -1047,6 +1205,11 @@ thrwmu(struct monst *mtmp)
|
||||
(void) thitu(hitv, dam, &otmp, (char *) 0);
|
||||
stop_occupation();
|
||||
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;
|
||||
@@ -1058,7 +1221,8 @@ thrwmu(struct monst *mtmp)
|
||||
*/
|
||||
if (!lined_up(mtmp)
|
||||
|| (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;
|
||||
|
||||
mwep = MON_WEP(mtmp); /* wielded weapon */
|
||||
|
||||
Reference in New Issue
Block a user