#define touch_petrifies(ptr) \
((ptr) == &mons[PM_COCKATRICE] || (ptr) == &mons[PM_CHICKATRICE])
+/* missiles made of rocks don't harm these: xorns and earth elementals
+ (but not ghosts and shades because that would impact all missile use
+ and also require an exception for blessed rocks/gems/boulders) */
+#define passes_rocks(ptr) (passes_walls(ptr) && !unsolid(ptr))
+
#define is_mind_flayer(ptr) \
((ptr) == &mons[PM_MIND_FLAYER] || (ptr) == &mons[PM_MASTER_MIND_FLAYER])
#define uslinging() (uwep && objects[uwep->otyp].oc_skill == P_SLING)
/* 'is_quest_artifact()' only applies to the current role's artifact */
#define any_quest_artifact(o) ((o)->oartifact >= ART_ORB_OF_DETECTION)
+/* 'missile' aspect is up to the caller and does not imply is_missile();
+ rings might be launched as missiles when being scattered by an explosion */
+#define stone_missile(o) \
+ ((o) && (objects[(o)->otyp].oc_material == GEMSTONE \
+ || (objects[(o)->otyp].oc_material == MINERAL)) \
+ && (o)->oclass != RING_CLASS)
/* Armor */
#define is_shield(otmp) \
falseCap = (*pm_name != lowc(*pm_name));
char *bp;
+ if (mtmp == &g.youmonst)
+ return strcpy(buf, "you"); /* ignore article, "invisible", &c */
+
if (g.program_state.gameover)
suppress |= SUPPRESS_HALLUCINATION;
if (article == ARTICLE_YOUR && !mtmp->mtame)
/* Object hits floor at hero's feet.
Called from drop(), throwit(), hold_another_object(), litter(). */
void
-hitfloor(struct obj *obj,
- boolean verbosely) /* usually True; False if caller has given
- drop message */
+hitfloor(
+ struct obj *obj,
+ boolean verbosely) /* usually True; False if caller has given drop mesg */
{
if (IS_SOFT(levl[u.ux][u.uy].typ) || u.uinwater || u.uswallow) {
dropy(obj);
}
/* Will 'obj' cause damage if it falls on hero's head when thrown upward?
- Not used to handle things which break when they hit. */
+ Not used to handle things which break when they hit.
+ Stone missile hitting hero w/ Passes_walls attribute handled separately. */
static boolean
harmless_missile(struct obj *obj)
{
hitfloor(obj, FALSE);
g.thrownobj = 0;
} else { /* neither potion nor other breaking object */
- boolean is_silver = (objects[otyp].oc_material == SILVER),
+ int material = objects[otyp].oc_material;
+ boolean is_silver = (material == SILVER),
less_damage = (uarmh && is_metallic(uarmh)
&& (!is_silver || !Hate_silver)),
+ harmless = (stone_missile(obj)
+ && passes_rocks(g.youmonst.data)),
artimsg = FALSE;
int dmg = dmgval(obj, &g.youmonst);
- if (obj->oartifact)
+ if (obj->oartifact && !harmless)
/* need a fake die roll here; rn1(18,2) avoids 1 and 20 */
artimsg = artifact_hit((struct monst *) 0, &g.youmonst, obj, &dmg,
rn1(18, 2));
dmg = Maybe_Half_Phys(dmg);
if (uarmh) {
- if (less_damage && dmg < (Upolyd ? u.mh : u.uhp)) {
- if (!artimsg)
- pline("Fortunately, you are wearing a hard helmet.");
+ /* note: 'harmless' and 'petrifier' are mutually exclusive */
+ if ((less_damage && dmg < (Upolyd ? u.mh : u.uhp)) || harmless) {
+ if (!artimsg) {
+ if (!harmless) /* !harmless => less_damage here */
+ pline("Fortunately, you are wearing a hard helmet.");
+ else
+ pline("Unfortunately, you are wearing %s.",
+ an(helm_simple_name(uarmh))); /* helm or hat */
+ }
/* helmet definitely protects you when it blocks petrification */
} else if (!petrifier) {
if (flags.verbose)
Your("%s does not protect you.", helm_simple_name(uarmh));
}
+ /* stone missile against hero in xorn form would have been
+ harmless, but hitting a worn helmet negates that */
+ harmless = FALSE;
} else if (petrifier && !Stone_resistance
&& !(poly_when_stoned(g.youmonst.data)
&& polymon(PM_STONE_GOLEM))) {
}
if (is_silver && Hate_silver)
pline_The("silver sears you!");
+ if (harmless)
+ hit(thesimpleoname(obj), &g.youmonst, " but doesn't hurt.");
+
hitfloor(obj, TRUE);
g.thrownobj = 0;
- losehp(dmg, "falling object", KILLED_BY_AN);
+ if (!harmless)
+ losehp(dmg, "falling object", KILLED_BY_AN);
}
return TRUE;
}
* Also used for kicked objects and for polearms/grapnel applied at range.
*/
int
-thitmonst(register struct monst *mon,
- register struct obj *obj) /* g.thrownobj or g.kickedobj or uwep */
+thitmonst(
+ struct monst *mon,
+ struct obj *obj) /* g.thrownobj or g.kickedobj or uwep */
{
register int tmp; /* Base chance to hit */
register int disttmp; /* distance modifier */
return FALSE;
}
-/* hero is hit by something other than a monster */
+/* hero is hit by something other than a monster (though it could be a
+ missile thrown or shot by a monster) */
int
thitu(
- int tlev,
+ int tlev, /* pseudo-level used when deciding whether to hit hero's AC */
int dam,
struct obj **objp,
const char *name) /* if null, then format `*objp' */
{
struct obj *obj = objp ? *objp : 0;
const char *onm, *knm;
- boolean is_acid;
+ boolean is_acid, named = (name != 0);
int kprefix = KILLED_BY_AN, dieroll;
char onmbuf[BUFSZ], knmbuf[BUFSZ];
if (is_acid && Acid_resistance) {
pline("It doesn't seem to hurt you.");
monstseesu(M_SEEN_ACID);
+ } else if (stone_missile(obj) && passes_rocks(g.youmonst.data)) {
+ /* use 'named' as an approximation for "hitting from above";
+ we avoid "passes through you" for horizontal flight path
+ because missile stops and that wording would suggest that
+ it should keep going */
+ pline("It %s you.",
+ named ? "passes harmlessly through" : "doesn't harm");
} else if (obj && obj->oclass == POTION_CLASS) {
/* an explosion which scatters objects might hit hero with one
(potions deliberately thrown at hero are handled by m_throw) */
struct monst *mtmp, /* accidental target, located at <g.bhitpos.x,.y> */
struct obj *otmp, /* missile; might be destroyed by drop_throw */
int range, /* how much farther will object travel if it misses;
- use -1 to signify to keep going even after hit,
- unless it's gone (used for rolling_boulder_traps) */
+ * use -1 to signify to keep going even after hit,
+ * unless it's gone (used for rolling_boulder_traps) */
boolean verbose)/* give message(s) even when you can't see what happened */
{
int damage, tmp;
potionhit(mtmp, otmp, POTHIT_OTHER_THROW);
return 1;
} else {
+ int material = objects[otmp->otyp].oc_material;
+ boolean harmless = (stone_missile(otmp) && passes_rocks(mtmp->data));
+
damage = dmgval(otmp, mtmp);
if (otmp->otyp == ACID_VENOM && resists_acid(mtmp))
damage = 0;
seemimic(mtmp);
mtmp->msleeping = 0;
if (vis) {
- if (otmp->otyp == EGG)
+ if (otmp->otyp == EGG) {
pline("Splat! %s is hit with %s egg!", Monnam(mtmp),
otmp->known ? an(mons[otmp->corpsenm].pmnames[NEUTRAL])
: "an");
- else
- hit(distant_name(otmp, mshot_xname), mtmp, exclam(damage));
+ } else {
+ char how[BUFSZ];
+
+ if (!harmless)
+ Strcpy(how, exclam(damage)); /* "!" or "." */
+ else
+ Sprintf(how, " but passes harmlessly through %.9s.",
+ mhim(mtmp));
+ hit(distant_name(otmp, mshot_xname), mtmp, how);
+ }
} else if (verbose && !g.mtarget)
pline("%s%s is hit%s", (otmp->otyp == EGG) ? "Splat! " : "",
Monnam(mtmp), exclam(damage));
}
}
}
- if (objects[otmp->otyp].oc_material == SILVER
- && mon_hates_silver(mtmp)) {
+ if (material == SILVER && mon_hates_silver(mtmp)) {
boolean flesh = (!noncorporeal(mtmp->data)
&& !amorphous(mtmp->data));
damage = 0;
}
- if (!DEADMONSTER(mtmp)) { /* might already be dead (if petrified) */
+ /* might already be dead (if petrified) */
+ if (!harmless && !DEADMONSTER(mtmp)) {
mtmp->mhp -= damage;
if (DEADMONSTER(mtmp)) {
if (vis || (verbose && !g.mtarget))
static boolean keep_saddle_with_steedcorpse(unsigned, struct obj *,
struct obj *);
-static boolean mu_maybe_destroy_web(struct monst *, boolean,
- struct trap *);
+static boolean mu_maybe_destroy_web(struct monst *, boolean, struct trap *);
static struct obj *t_missile(int, struct trap *);
static int trapeffect_arrow_trap(struct monst *, struct trap *, unsigned);
static int trapeffect_dart_trap(struct monst *, struct trap *, unsigned);
unsigned trflags UNUSED)
{
struct obj *otmp;
+ boolean harmless = FALSE;
if (mtmp == &g.youmonst) {
if (trap->once && trap->tseen && !rn2(15)) {
pline("A trap door in %s opens and %s falls on your %s!",
the(ceiling(u.ux, u.uy)), an(xname(otmp)), body_part(HEAD));
if (uarmh) {
- if (is_metallic(uarmh)) {
+ /* normally passes_rocks() would protect againt a falling
+ rock, but not when wearing a helmet */
+ if (passes_rocks(g.youmonst.data)) {
+ pline("Unfortunately, you are wearing %s.",
+ an(helm_simple_name(uarmh))); /* helm or hat */
+ dmg = 2;
+ } else if (is_metallic(uarmh)) {
pline("Fortunately, you are wearing a hard helmet.");
dmg = 2;
} else if (flags.verbose) {
pline("%s does not protect you.", Yname2(uarmh));
}
+ } else if (passes_rocks(g.youmonst.data)) {
+ pline("It passes harmlessly through you.");
+ harmless = TRUE;
}
if (!Blind)
otmp->dknown = 1;
stackobj(otmp);
newsym(u.ux, u.uy); /* map the rock */
- losehp(Maybe_Half_Phys(dmg), "falling rock", KILLED_BY_AN);
- exercise(A_STR, FALSE);
+ if (!harmless) {
+ losehp(Maybe_Half_Phys(dmg), "falling rock", KILLED_BY_AN);
+ exercise(A_STR, FALSE);
+ }
}
} else {
boolean in_sight = canseemon(mtmp) || (mtmp == u.usteed);
int
launch_obj(
short otyp,
- register int x1,
- register int y1,
- register int x2,
- register int y2,
+ int x1, int y1,
+ int x2, int y2,
int style)
{
- register struct monst *mtmp;
- register struct obj *otmp, *otmp2;
- register int dx, dy;
+ struct monst *mtmp;
+ struct obj *otmp, *otmp2;
+ int dx, dy;
struct obj *singleobj;
boolean used_up = FALSE;
boolean otherside = FALSE;
singleobj->otrapped = 1;
style &= ~LAUNCH_KNOWN;
/*FALLTHRU*/
- roll:
case ROLL:
+ roll:
delaycnt = 2;
/*FALLTHRU*/
default:
}
/* Monster is hit by trap. */
-/* Note: doesn't work if both obj and d_override are null */
static boolean
thitm(
- int tlev,
- struct monst *mon,
- struct obj *obj,
- int d_override,
- boolean nocorpse)
+ int tlev, /* missile's attack level */
+ struct monst *mon, /* target */
+ struct obj *obj, /* missile; might be Null */
+ int d_override, /* non-zero: force hit for this amount of damage */
+ boolean nocorpse) /* True: a trap is completely burning up the target */
{
int strike;
boolean trapkilled = FALSE;
pline("%s is almost hit by %s!", Monnam(mon), doname(obj));
} else {
int dam = 1;
+ boolean harmless = (stone_missile(obj) && passes_rocks(mon->data));
if (obj && cansee(mon->mx, mon->my))
- pline("%s is hit by %s!", Monnam(mon), doname(obj));
+ pline("%s is hit by %s%s", Monnam(mon), doname(obj),
+ harmless ? " but is not harmed." : "!");
if (d_override) {
dam = d_override;
} else if (obj) {
if (dam < 1)
dam = 1;
}
- mon->mhp -= dam;
- if (mon->mhp <= 0) {
- int xx = mon->mx, yy = mon->my;
+ if (!harmless) {
+ mon->mhp -= dam;
+ if (mon->mhp <= 0) {
+ int xx = mon->mx, yy = mon->my;
- monkilled(mon, "", nocorpse ? -AD_RBRE : AD_PHYS);
- if (DEADMONSTER(mon)) {
- newsym(xx, yy);
- trapkilled = TRUE;
+ monkilled(mon, "", nocorpse ? -AD_RBRE : AD_PHYS);
+ if (DEADMONSTER(mon)) {
+ newsym(xx, yy);
+ trapkilled = TRUE;
+ }
}
+ } else {
+ strike = 0; /* harmless; don't use up the missile */
}
}
if (obj && (!strike || d_override)) {
using attack type aatyp and/or weapon.
larger value == easier to hit */
int
-find_roll_to_hit(struct monst *mtmp,
- uchar aatyp, /* usually AT_WEAP or AT_KICK */
- struct obj *weapon, /* uwep or uswapwep or NULL */
- int *attk_count,
- int *role_roll_penalty)
+find_roll_to_hit(
+ struct monst *mtmp,
+ uchar aatyp, /* usually AT_WEAP or AT_KICK */
+ struct obj *weapon, /* uwep or uswapwep or NULL */
+ int *attk_count,
+ int *role_roll_penalty)
{
int tmp, tmp2;
/* hit the monster next to you and the monsters to the left and right of it;
return False if the primary target is killed, True otherwise */
static boolean
-hitum_cleave(struct monst *target, /* non-Null; forcefight at nothing doesn't
- cleave... */
- struct attack *uattk) /* ... but we don't enforce that here; Null
- works ok */
+hitum_cleave(
+ struct monst *target, /* non-Null; forcefight at nothing doesn't cleave +*/
+ struct attack *uattk) /*+ but we don't enforce that here; Null works ok */
{
/* swings will be delivered in alternate directions; with consecutive
attacks it will simulate normal swing and backswing; when swings
DISABLE_WARNING_FORMAT_NONLITERAL
-/* guts of hmon() */
+/* guts of hmon(); returns True if 'mon' survives */
static boolean
-hmon_hitmon(struct monst *mon,
- struct obj *obj,
- int thrown, /* HMON_xxx (0 => hand-to-hand, other => ranged) */
- int dieroll)
+hmon_hitmon(
+ struct monst *mon,
+ struct obj *obj,
+ int thrown, /* HMON_xxx (0 => hand-to-hand, other => ranged) */
+ int dieroll)
{
int tmp;
struct permonst *mdat = mon->data;
/* not grapnels; applied implies uwep */
|| (thrown == HMON_APPLIED && is_pole(uwep)));
int jousting = 0;
- long silverhit = 0L;
+ int material = obj ? objects[obj->otyp].oc_material : 0;
int wtype;
struct obj *monwep;
char saved_oname[BUFSZ];
wakeup(mon, TRUE);
if (!obj) { /* attack with bare hands */
+ long silverhit = 0L; /* armor mask */
+
if (mdat == &mons[PM_SHADE]) {
tmp = 0;
} else {
silvermsg = TRUE;
} else {
+ /* stone missile does not hurt xorn or earth elemental, but doesn't
+ pass all the way through and continue on to some further target */
+ if ((thrown == HMON_THROWN || thrown == HMON_KICKED) /* not Applied */
+ && stone_missile(obj) && passes_rocks(mdat)) {
+ hit(mshot_xname(obj), mon, " but does no harm.");
+ return TRUE;
+ }
+ /* remember obj's name since it might end up being destroyed and
+ we'll want to use it after that */
if (!(artifact_light(obj) && obj->lamplit))
Strcpy(saved_oname, cxname(obj));
else
Strcpy(saved_oname, bare_artifactname(obj));
+
if (obj->oclass == WEAPON_CLASS || is_weptool(obj)
|| obj->oclass == GEM_CLASS) {
/* is it not a melee weapon? */
tmp = 0;
else
tmp = rnd(2);
- if (objects[obj->otyp].oc_material == SILVER
- && mon_hates_silver(mon)) {
- silvermsg = TRUE;
- silverobj = TRUE;
+ if (material == SILVER && mon_hates_silver(mon)) {
+ silvermsg = silverobj = TRUE;
/* if it will already inflict dmg, make it worse */
tmp += rnd((tmp) ? 20 : 10);
}
return TRUE;
hittxt = TRUE;
}
- if (objects[obj->otyp].oc_material == SILVER
- && mon_hates_silver(mon)) {
- silvermsg = TRUE;
- silverobj = TRUE;
+ if (material == SILVER && mon_hates_silver(mon)) {
+ silvermsg = silverobj = TRUE;
}
if (artifact_light(obj) && obj->lamplit
&& mon_hates_light(mon))
}
/* things like silver wands can arrive here so we
need another silver check; blessed check too */
- if (objects[obj->otyp].oc_material == SILVER
- && mon_hates_silver(mon)) {
+ if (material == SILVER && mon_hates_silver(mon)) {
tmp += rnd(20);
silvermsg = silverobj = TRUE;
}
/* iron weapon using melee or polearm hit [3.6.1: metal weapon too;
also allow either or both weapons to cause split when twoweap] */
&& obj && (obj == uwep || (u.twoweap && obj == uswapwep))
- && ((objects[obj->otyp].oc_material == IRON
+ && ((material == IRON
/* allow scalpel and tsurugi to split puddings */
- || objects[obj->otyp].oc_material == METAL)
+ || material == METAL)
/* but not bashing with darts, arrows or ya */
&& !(is_ammo(obj) || is_missile(obj)))
&& hand_to_hand) {
}
void
-hit(const char *str, struct monst *mtmp,
- const char *force) /* usually either "." or "!" */
+hit(const char *str, /* zap text or missile name */
+ struct monst *mtmp, /* target; for missile, might be hero */
+ const char *force) /* usually either "." or "!" via exclam() */
{
- if ((!cansee(g.bhitpos.x, g.bhitpos.y) && !canspotmon(mtmp)
- && !engulfing_u(mtmp)) || !flags.verbose)
+ boolean verbosely = (mtmp == &g.youmonst
+ || (flags.verbose
+ && (cansee(g.bhitpos.x, g.bhitpos.y)
+ || canspotmon(mtmp) || engulfing_u(mtmp))));
+
+ if (!verbosely)
pline("%s %s it.", The(str), vtense(str, "hit"));
else
pline("%s %s %s%s", The(str), vtense(str, "hit"),