in src/zap.c [3221:3513]
int FDECL((*fhitm), (MONST_P, OBJ_P)), /* fns called when mon/obj hit */
FDECL((*fhito), (OBJ_P, OBJ_P));
struct obj **pobj; /* object tossed/used, set to NULL
* if object is destroyed */
{
struct monst *mtmp, *result = (struct monst *) 0;
struct obj *obj = *pobj;
uchar typ;
boolean shopdoor = FALSE, point_blank = TRUE;
boolean in_skip = FALSE, allow_skip = FALSE;
boolean tethered_weapon = FALSE;
int skiprange_start = 0, skiprange_end = 0, skipcount = 0;
if (weapon == KICKED_WEAPON) {
/* object starts one square in front of player */
bhitpos.x = u.ux + ddx;
bhitpos.y = u.uy + ddy;
range--;
} else {
bhitpos.x = u.ux;
bhitpos.y = u.uy;
}
if (weapon == THROWN_WEAPON && obj && obj->otyp == ROCK) {
skiprange(range, &skiprange_start, &skiprange_end);
allow_skip = !rn2(3);
}
if (weapon == FLASHED_LIGHT) {
tmp_at(DISP_BEAM, cmap_to_glyph(S_flashbeam));
} else if (weapon == THROWN_TETHERED_WEAPON && obj) {
tethered_weapon = TRUE;
weapon = THROWN_WEAPON; /* simplify 'if's that follow below */
tmp_at(DISP_TETHER, obj_to_glyph(obj, rn2_on_display_rng));
} else if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM)
tmp_at(DISP_FLASH, obj_to_glyph(obj, rn2_on_display_rng));
while (range-- > 0) {
int x, y;
bhitpos.x += ddx;
bhitpos.y += ddy;
x = bhitpos.x;
y = bhitpos.y;
if (!isok(x, y)) {
bhitpos.x -= ddx;
bhitpos.y -= ddy;
break;
}
if (is_pick(obj) && inside_shop(x, y)
&& (mtmp = shkcatch(obj, x, y)) != 0) {
tmp_at(DISP_END, 0);
result = mtmp;
goto bhit_done;
}
typ = levl[bhitpos.x][bhitpos.y].typ;
/* iron bars will block anything big enough and break some things */
if (weapon == THROWN_WEAPON || weapon == KICKED_WEAPON) {
if (typ == IRONBARS
&& hits_bars(pobj, x - ddx, y - ddy, bhitpos.x, bhitpos.y,
point_blank ? 0 : !rn2(5), 1)) {
/* caveat: obj might now be null... */
obj = *pobj;
bhitpos.x -= ddx;
bhitpos.y -= ddy;
break;
} else if (obj->lamplit && !Blind) {
show_transient_light(obj, bhitpos.x, bhitpos.y);
}
}
if (weapon == ZAPPED_WAND && find_drawbridge(&x, &y)) {
boolean learn_it = FALSE;
switch (obj->otyp) {
case WAN_OPENING:
case SPE_KNOCK:
if (is_db_wall(bhitpos.x, bhitpos.y)) {
if (cansee(x, y) || cansee(bhitpos.x, bhitpos.y))
learn_it = TRUE;
open_drawbridge(x, y);
}
break;
case WAN_LOCKING:
case SPE_WIZARD_LOCK:
if ((cansee(x, y) || cansee(bhitpos.x, bhitpos.y))
&& levl[x][y].typ == DRAWBRIDGE_DOWN)
learn_it = TRUE;
close_drawbridge(x, y);
break;
case WAN_STRIKING:
case SPE_FORCE_BOLT:
if (typ != DRAWBRIDGE_UP)
destroy_drawbridge(x, y);
learn_it = TRUE;
break;
}
if (learn_it)
learnwand(obj);
}
mtmp = m_at(bhitpos.x, bhitpos.y);
/*
* skipping rocks
*
* skiprange_start is only set if this is a thrown rock
*/
if (skiprange_start && (range == skiprange_start) && allow_skip) {
if (is_pool(bhitpos.x, bhitpos.y) && !mtmp) {
in_skip = TRUE;
if (!Blind)
pline("%s %s%s.", Yname2(obj), otense(obj, "skip"),
skipcount ? " again" : "");
else
You_hear("%s skip.", yname(obj));
skipcount++;
} else if (skiprange_start > skiprange_end + 1) {
--skiprange_start;
}
}
if (in_skip) {
if (range <= skiprange_end) {
in_skip = FALSE;
if (range > 3) /* another bounce? */
skiprange(range, &skiprange_start, &skiprange_end);
} else if (mtmp && M_IN_WATER(mtmp->data)) {
if (!Blind && canspotmon(mtmp))
pline("%s %s over %s.", Yname2(obj), otense(obj, "pass"),
mon_nam(mtmp));
mtmp = (struct monst *) 0;
}
}
/* if mtmp is a shade and missile passes harmlessly through it,
give message and skip it in order to keep going */
if (mtmp && (weapon == THROWN_WEAPON || weapon == KICKED_WEAPON)
&& shade_miss(&youmonst, mtmp, obj, TRUE, TRUE))
mtmp = (struct monst *) 0;
if (mtmp) {
notonhead = (bhitpos.x != mtmp->mx || bhitpos.y != mtmp->my);
if (weapon == FLASHED_LIGHT) {
/* FLASHED_LIGHT hitting invisible monster should
pass through instead of stop so we call
flash_hits_mon() directly rather than returning
mtmp back to caller. That allows the flash to
keep on going. Note that we use mtmp->minvis
not canspotmon() because it makes no difference
whether the hero can see the monster or not. */
if (mtmp->minvis) {
obj->ox = u.ux, obj->oy = u.uy;
(void) flash_hits_mon(mtmp, obj);
} else {
tmp_at(DISP_END, 0);
result = mtmp; /* caller will call flash_hits_mon */
goto bhit_done;
}
} else if (weapon == INVIS_BEAM) {
/* Like FLASHED_LIGHT, INVIS_BEAM should continue
through invisible targets; unlike it, we aren't
prepared for multiple hits so just get first one
that's either visible or could see its invisible
self. [No tmp_at() cleanup is needed here.] */
if (!mtmp->minvis || perceives(mtmp->data)) {
result = mtmp;
goto bhit_done;
}
} else if (weapon != ZAPPED_WAND) {
/* THROWN_WEAPON, KICKED_WEAPON */
if (!tethered_weapon)
tmp_at(DISP_END, 0);
if (cansee(bhitpos.x, bhitpos.y) && !canspotmon(mtmp))
map_invisible(bhitpos.x, bhitpos.y);
result = mtmp;
goto bhit_done;
} else {
/* ZAPPED_WAND */
(*fhitm)(mtmp, obj);
range -= 3;
}
} else {
if (weapon == ZAPPED_WAND && obj->otyp == WAN_PROBING
&& glyph_is_invisible(levl[bhitpos.x][bhitpos.y].glyph)) {
unmap_object(bhitpos.x, bhitpos.y);
newsym(x, y);
}
}
if (fhito) {
if (bhitpile(obj, fhito, bhitpos.x, bhitpos.y, 0))
range--;
} else {
if (weapon == KICKED_WEAPON
&& ((obj->oclass == COIN_CLASS
&& OBJ_AT(bhitpos.x, bhitpos.y))
|| ship_object(obj, bhitpos.x, bhitpos.y,
costly_spot(bhitpos.x, bhitpos.y)))) {
tmp_at(DISP_END, 0);
goto bhit_done; /* result == (struct monst *) 0 */
}
}
if (weapon == ZAPPED_WAND && (IS_DOOR(typ) || typ == SDOOR)) {
switch (obj->otyp) {
case WAN_OPENING:
case WAN_LOCKING:
case WAN_STRIKING:
case SPE_KNOCK:
case SPE_WIZARD_LOCK:
case SPE_FORCE_BOLT:
if (doorlock(obj, bhitpos.x, bhitpos.y)) {
if (cansee(bhitpos.x, bhitpos.y)
|| (obj->otyp == WAN_STRIKING && !Deaf))
learnwand(obj);
if (levl[bhitpos.x][bhitpos.y].doormask == D_BROKEN
&& *in_rooms(bhitpos.x, bhitpos.y, SHOPBASE)) {
shopdoor = TRUE;
add_damage(bhitpos.x, bhitpos.y, SHOP_DOOR_COST);
}
}
break;
}
}
if (!ZAP_POS(typ) || closed_door(bhitpos.x, bhitpos.y)) {
bhitpos.x -= ddx;
bhitpos.y -= ddy;
break;
}
if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM) {
/* 'I' present but no monster: erase */
/* do this before the tmp_at() */
if (glyph_is_invisible(levl[bhitpos.x][bhitpos.y].glyph)
&& cansee(x, y)) {
unmap_object(bhitpos.x, bhitpos.y);
newsym(x, y);
}
tmp_at(bhitpos.x, bhitpos.y);
delay_output();
/* kicked objects fall in pools */
if ((weapon == KICKED_WEAPON)
&& (is_pool(bhitpos.x, bhitpos.y)
|| is_lava(bhitpos.x, bhitpos.y)))
break;
if (IS_SINK(typ) && weapon != FLASHED_LIGHT)
break; /* physical objects fall onto sink */
}
/* limit range of ball so hero won't make an invalid move */
if (weapon == THROWN_WEAPON && range > 0
&& obj->otyp == HEAVY_IRON_BALL) {
struct obj *bobj;
struct trap *t;
if ((bobj = sobj_at(BOULDER, x, y)) != 0) {
if (cansee(x, y))
pline("%s hits %s.", The(distant_name(obj, xname)),
an(xname(bobj)));
range = 0;
} else if (obj == uball) {
if (!test_move(x - ddx, y - ddy, ddx, ddy, TEST_MOVE)) {
/* nb: it didn't hit anything directly */
if (cansee(x, y))
pline("%s jerks to an abrupt halt.",
The(distant_name(obj, xname))); /* lame */
range = 0;
} else if (Sokoban && (t = t_at(x, y)) != 0
&& (is_pit(t->ttyp) || is_hole(t->ttyp))) {
/* hero falls into the trap, so ball stops */
range = 0;
}
}
}
/* thrown/kicked missile has moved away from its starting spot */
point_blank = FALSE; /* affects passing through iron bars */
}
if (weapon != ZAPPED_WAND && weapon != INVIS_BEAM && !tethered_weapon)
tmp_at(DISP_END, 0);
if (shopdoor)
pay_for_damage("destroy", FALSE);
bhit_done:
if (weapon == THROWN_WEAPON || weapon == KICKED_WEAPON)
transient_light_cleanup();
return result;
}