by forcing newman() if poly-target matches your_race()
add missing data.base entries for caveman, healer, monk, priest, and samurai
allow "grey spellbook" as alternative spelling of "gray spellbook"
+handle attacks by cancelled monsters more consistently
+armor worn by monsters might negate some magic attacks like it does for hero
Platform- and/or Interface-Specific Fixes
-/* SCCS Id: @(#)extern.h 3.4 2002/08/22 */
+/* SCCS Id: @(#)extern.h 3.4 2003/01/02 */
/* Copyright (c) Steve Creps, 1988. */
/* NetHack may be freely redistributed. See license for details. */
E void FDECL(expels, (struct monst *,struct permonst *,BOOLEAN_P));
E struct attack *FDECL(getmattk, (struct permonst *,int,int *,struct attack *));
E int FDECL(mattacku, (struct monst *));
+E int FDECL(magic_negation, (struct monst *));
E int FDECL(gazemu, (struct monst *,struct attack *));
E void FDECL(mdamageu, (struct monst *,int));
E int FDECL(could_seduce, (struct monst *,struct monst *,struct attack *));
-/* SCCS Id: @(#)mhitm.c 3.4 2002/12/09 */
+/* SCCS Id: @(#)mhitm.c 3.4 2003/01/02 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
struct obj *obj;
char buf[BUFSZ];
struct permonst *pa = magr->data, *pd = mdef->data;
- int num, tmp = d((int)mattk->damn, (int)mattk->damd);
+ int armpro, num, tmp = d((int)mattk->damn, (int)mattk->damd);
+ boolean cancelled;
if (touch_petrifies(pd) && !resists_ston(magr)) {
long protector = attk_protection(mattk->aatyp),
}
}
+ /* cancellation factor is the same as when attacking the hero */
+ armpro = magic_negation(mdef);
+ cancelled = magr->mcan || !((rn2(3) >= armpro) || !rn2(50));
+
switch(mattk->adtyp) {
case AD_DGST:
/* eating a Rider or its corpse is fatal */
pline("%s %s for a moment.", Monnam(mdef),
makeplural(stagger(mdef->data, "stagger")));
mdef->mstun = 1;
- /* fall through */
+ goto physical;
+ case AD_LEGS:
+ if (magr->mcan) {
+ tmp = 0;
+ break;
+ }
+ goto physical;
case AD_WERE:
case AD_HEAL:
- case AD_LEGS:
case AD_PHYS:
- if (mattk->aatyp == AT_KICK && thick_skinned(pd))
- tmp = 0;
- else if(mattk->aatyp == AT_WEAP) {
+ physical:
+ if (mattk->aatyp == AT_KICK && thick_skinned(pd)) {
+ tmp = 0;
+ } else if(mattk->aatyp == AT_WEAP) {
if(otmp) {
if (otmp->otyp == CORPSE &&
touch_petrifies(&mons[otmp->corpsenm]))
- goto do_stone_goto_label;
+ goto do_stone;
tmp += dmgval(otmp, mdef);
if (otmp->oartifact) {
(void)artifact_hit(magr,mdef, otmp, &tmp, dieroll);
}
break;
case AD_FIRE:
- if (magr->mcan) {
+ if (cancelled) {
tmp = 0;
break;
}
tmp += destroy_mitem(mdef, POTION_CLASS, AD_FIRE);
break;
case AD_COLD:
- if (magr->mcan) {
+ if (cancelled) {
tmp = 0;
break;
}
tmp += destroy_mitem(mdef, POTION_CLASS, AD_COLD);
break;
case AD_ELEC:
- if (magr->mcan) {
+ if (cancelled) {
tmp = 0;
break;
}
if (!rn2(6)) erode_obj(MON_WEP(mdef), TRUE, TRUE);
break;
case AD_RUST:
- if (!magr->mcan && pd == &mons[PM_IRON_GOLEM]) {
+ if (magr->mcan) break;
+ if (pd == &mons[PM_IRON_GOLEM]) {
if (vis) pline("%s falls to pieces!", Monnam(mdef));
mondied(mdef);
if (mdef->mhp > 0) return 0;
tmp = 0;
break;
case AD_CORR:
+ if (magr->mcan) break;
hurtmarmor(mdef, AD_CORR);
mdef->mstrategy &= ~STRAT_WAITFORU;
tmp = 0;
break;
case AD_DCAY:
- if (!magr->mcan && (pd == &mons[PM_WOOD_GOLEM] ||
- pd == &mons[PM_LEATHER_GOLEM])) {
+ if (magr->mcan) break;
+ if (pd == &mons[PM_WOOD_GOLEM] ||
+ pd == &mons[PM_LEATHER_GOLEM]) {
if (vis) pline("%s falls to pieces!", Monnam(mdef));
mondied(mdef);
if (mdef->mhp > 0) return 0;
tmp = 0;
break;
case AD_STON:
-do_stone_goto_label:
+ if (magr->mcan) break;
+ do_stone:
/* may die from the acid if it eats a stone-curing corpse */
- if (munstone(mdef, FALSE)) goto label2;
+ if (munstone(mdef, FALSE)) goto post_stone;
if (poly_when_stoned(pd)) {
mon_to_stone(mdef);
tmp = 0;
if (!resists_ston(mdef)) {
if (vis) pline("%s turns to stone!", Monnam(mdef));
monstone(mdef);
-label2: if (mdef->mhp > 0) return 0;
+ post_stone: if (mdef->mhp > 0) return 0;
else if (mdef->mtame && !vis)
You(brief_feeling, "peculiarly sad");
return (MM_DEF_DIED | (grow_up(magr,mdef) ?
tmp = (mattk->adtyp == AD_STON ? 0 : 1);
break;
case AD_TLPT:
- if (!magr->mcan && tmp < mdef->mhp && !tele_restrict(mdef)) {
+ if (!cancelled && tmp < mdef->mhp && !tele_restrict(mdef)) {
char mdef_Monnam[BUFSZ];
/* save the name before monster teleports, otherwise
we'll get "it" in the suddenly disappears message */
}
break;
case AD_SLEE:
- if (!magr->mcan && !mdef->msleeping &&
+ if (!cancelled && !mdef->msleeping &&
sleep_monst(mdef, rnd(10), -1)) {
if (vis) {
Strcpy(buf, Monnam(mdef));
}
break;
case AD_PLYS:
- if(!magr->mcan && mdef->mcanmove) {
+ if(!cancelled && mdef->mcanmove) {
if (vis) {
Strcpy(buf, Monnam(mdef));
pline("%s is frozen by %s.", buf, mon_nam(magr));
}
break;
case AD_SLOW:
- if (!magr->mcan && vis && mdef->mspeed != MSLOW) {
+ if (!cancelled && mdef->mspeed != MSLOW) {
unsigned int oldspeed = mdef->mspeed;
mon_adjust_speed(mdef, -1, (struct obj *)0);
}
break;
case AD_DRLI:
- if (rn2(2) && !resists_drli(mdef)) {
+ if (!cancelled && !rn2(3) && !resists_drli(mdef)) {
tmp = d(2,6);
if (vis)
pline("%s suddenly seems weaker!", Monnam(mdef));
#endif
case AD_SITM: /* for now these are the same */
case AD_SEDU:
+ if (magr->mcan) break;
/* find an object to steal, non-cursed if magr is tame */
- for (obj = mdef->minvent; obj; obj = obj->nobj) {
+ for (obj = mdef->minvent; obj; obj = obj->nobj)
if (!magr->mtame || !obj->cursed)
break;
- }
- if (!magr->mcan && obj) {
+ if (obj) {
char onambuf[BUFSZ], mdefnambuf[BUFSZ];
/* make a special x_monnam() call that never omits
case AD_DRST:
case AD_DRDX:
case AD_DRCO:
- if (!magr->mcan && !rn2(8)) {
+ if (!cancelled && !rn2(8)) {
if (vis)
pline("%s %s was poisoned!", s_suffix(Monnam(magr)),
mpoisons_subj(magr, mattk));
s_suffix(Monnam(mdef)));
break;
case AD_SLIM:
- if (!rn2(4) && mdef->data != &mons[PM_FIRE_VORTEX] &&
- mdef->data != &mons[PM_FIRE_ELEMENTAL] &&
- mdef->data != &mons[PM_SALAMANDER] &&
- mdef->data != &mons[PM_GREEN_SLIME]) {
- (void) newcham(mdef, &mons[PM_GREEN_SLIME], FALSE, vis);
+ if (cancelled) break; /* physical damage only */
+ if (!rn2(4) && mdef->data != &mons[PM_FIRE_VORTEX] &&
+ mdef->data != &mons[PM_FIRE_ELEMENTAL] &&
+ mdef->data != &mons[PM_SALAMANDER] &&
+ mdef->data != &mons[PM_GREEN_SLIME]) {
+ (void) newcham(mdef, &mons[PM_GREEN_SLIME], FALSE, vis);
mdef->mstrategy &= ~STRAT_WAITFORU;
- tmp = 0;
- }
- break;
+ tmp = 0;
+ }
+ break;
case AD_STCK:
+ if (cancelled) tmp = 0;
+ break;
case AD_WRAP: /* monsters cannot grab one another, it's too hard */
- case AD_ENCH: /* There's no msomearmor() function, so just do damage */
+ if (magr->mcan) tmp = 0;
+ break;
+ case AD_ENCH:
+ /* there's no msomearmor() function, so just do damage */
+ /* if (cancelled) break; */
break;
default: tmp = 0;
break;
-/* SCCS Id: @(#)mhitu.c 3.4 2002/12/09 */
+/* SCCS Id: @(#)mhitu.c 3.4 2003/01/02 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
return FALSE;
}
+/* armor that sufficiently covers the body might be able to block magic */
+int
+magic_negation(mon)
+struct monst *mon;
+{
+ struct obj *armor;
+ int armpro = 0;
+
+ armor = (mon == &youmonst) ? uarm : which_armor(mon, W_ARM);
+ if (armor && armpro < objects[armor->otyp].a_can)
+ armpro = objects[armor->otyp].a_can;
+ armor = (mon == &youmonst) ? uarmc : which_armor(mon, W_ARMC);
+ if (armor && armpro < objects[armor->otyp].a_can)
+ armpro = objects[armor->otyp].a_can;
+ armor = (mon == &youmonst) ? uarmh : which_armor(mon, W_ARMH);
+ if (armor && armpro < objects[armor->otyp].a_can)
+ armpro = objects[armor->otyp].a_can;
+
+ /* armor types for shirt, gloves, shoes, and shield don't currently
+ provide any magic cancellation but we might as well be complete */
+#ifdef TOURIST
+ armor = (mon == &youmonst) ? uarmu : which_armor(mon, W_ARMU);
+ if (armor && armpro < objects[armor->otyp].a_can)
+ armpro = objects[armor->otyp].a_can;
+#endif
+ armor = (mon == &youmonst) ? uarmg : which_armor(mon, W_ARMG);
+ if (armor && armpro < objects[armor->otyp].a_can)
+ armpro = objects[armor->otyp].a_can;
+ armor = (mon == &youmonst) ? uarmf : which_armor(mon, W_ARMF);
+ if (armor && armpro < objects[armor->otyp].a_can)
+ armpro = objects[armor->otyp].a_can;
+ armor = (mon == &youmonst) ? uarms : which_armor(mon, W_ARMS);
+ if (armor && armpro < objects[armor->otyp].a_can)
+ armpro = objects[armor->otyp].a_can;
+
+#ifdef STEED
+ /* this one is really a stretch... */
+ armor = (mon == &youmonst) ? 0 : which_armor(mon, W_SADDLE);
+ if (armor && armpro < objects[armor->otyp].a_can)
+ armpro = objects[armor->otyp].a_can;
+#endif
+
+ return armpro;
+}
+
/*
* hitmu: monster hits you
* returns 2 if monster dies (e.g. "yellow light"), 1 otherwise
/* Use uncancelled when the cancellation factor takes into account certain
* armor's special magic protection. Otherwise just use !mtmp->mcan.
*/
- armpro = 0;
- if (uarm && armpro < objects[uarm->otyp].a_can)
- armpro = objects[uarm->otyp].a_can;
- if (uarmc && armpro < objects[uarmc->otyp].a_can)
- armpro = objects[uarmc->otyp].a_can;
- if (uarmh && armpro < objects[uarmh->otyp].a_can)
- armpro = objects[uarmh->otyp].a_can;
+ armpro = magic_negation(&youmonst);
uncancelled = !mtmp->mcan && ((rn2(3) >= armpro) || !rn2(50));
permdmg = 0;
case AD_PLYS:
hitmsg(mtmp, mattk);
if (uncancelled && multi >= 0 && !rn2(3)) {
- if (Free_action) You("momentarily stiffen.");
- else {
- if (Blind) You("are frozen!");
- else You("are frozen by %s!", mon_nam(mtmp));
- nomovemsg = 0; /* default: "you can move again" */
- nomul(-rnd(10));
- exercise(A_DEX, FALSE);
- }
+ if (Free_action) {
+ You("momentarily stiffen.");
+ } else {
+ if (Blind) You("are frozen!");
+ else You("are frozen by %s!", mon_nam(mtmp));
+ nomovemsg = 0; /* default: "you can move again" */
+ nomul(-rnd(10));
+ exercise(A_DEX, FALSE);
+ }
}
break;
case AD_DRLI:
/* This case is too obvious to ignore, but Nethack is not in
* general very good at considering height--most short monsters
* still _can_ attack you when you're flying or mounted.
+ * [FIXME: why can't a flying attacker overcome this?]
*/
if (
#ifdef STEED
Levitation || Flying) {
pline("%s tries to reach your %s %s!", Monnam(mtmp),
sidestr, body_part(LEG));
+ dmg = 0;
} else if (mtmp->mcan) {
pline("%s nuzzles against your %s %s!", Monnam(mtmp),
sidestr, body_part(LEG));
+ dmg = 0;
} else {
if (uarmf) {
if (rn2(2) && (uarmf->otyp == LOW_BOOTS ||
else {
pline("%s scratches your %s boot!", Monnam(mtmp),
sidestr);
+ dmg = 0;
break;
}
} else pline("%s pricks your %s %s!", Monnam(mtmp),
You_hear("%s hissing!", s_suffix(mon_nam(mtmp)));
if(!rn2(10) ||
(flags.moonphase == NEW_MOON && !have_lizard())) {
-do_stone:
+ do_stone:
if (!Stoned && !Stone_resistance
&& !(poly_when_stoned(youmonst.data) &&
polymon(PM_STONE_GOLEM))) {
hurtarmor(AD_DCAY);
break;
case AD_HEAL:
+ /* a cancelled nurse is just an ordinary monster */
+ if (mtmp->mcan) {
+ hitmsg(mtmp, mattk);
+ break;
+ }
if(!uwep
#ifdef TOURIST
&& !uarmu
-/* SCCS Id: @(#)uhitm.c 3.4 2002/12/26 */
+/* SCCS Id: @(#)uhitm.c 3.4 2003/01/02 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
{
register struct permonst *pd = mdef->data;
register int tmp = d((int)mattk->damn, (int)mattk->damd);
+ int armpro;
+ boolean negated;
+
+ armpro = magic_negation(mdef);
+ /* since hero can't be cancelled, only defender's armor applies */
+ negated = !((rn2(3) >= armpro) || !rn2(50));
if (is_demon(youmonst.data) && !rn2(13) && !uwep
&& u.umonnum != PM_SUCCUBUS && u.umonnum != PM_INCUBUS
pline("%s %s for a moment.", Monnam(mdef),
makeplural(stagger(mdef->data, "stagger")));
mdef->mstun = 1;
- /* fall through to next case */
- case AD_WERE: /* no effect on monsters */
- case AD_HEAL:
+ goto physical;
case AD_LEGS:
+ /* if (u.ucancelled) { */
+ /* tmp = 0; */
+ /* break; */
+ /* } */
+ goto physical;
+ case AD_WERE: /* no special effect on monsters */
+ case AD_HEAL: /* likewise */
case AD_PHYS:
+ physical:
if(mattk->aatyp == AT_WEAP) {
if(uwep) tmp = 0;
} else if(mattk->aatyp == AT_KICK) {
}
break;
case AD_FIRE:
+ if (negated) {
+ tmp = 0;
+ break;
+ }
if (!Blind)
pline("%s is %s!", Monnam(mdef),
on_fire(mdef->data, mattk));
if (pd == &mons[PM_STRAW_GOLEM] ||
pd == &mons[PM_PAPER_GOLEM]) {
if (!Blind)
- pline("%s burns completely!", Monnam(mdef));
+ pline("%s burns completely!", Monnam(mdef));
xkilled(mdef,2);
tmp = 0;
break;
tmp += destroy_mitem(mdef, POTION_CLASS, AD_FIRE);
break;
case AD_COLD:
+ if (negated) {
+ tmp = 0;
+ break;
+ }
if (!Blind) pline("%s is covered in frost!", Monnam(mdef));
if (resists_cold(mdef)) {
shieldeff(mdef->mx, mdef->my);
tmp += destroy_mitem(mdef, POTION_CLASS, AD_COLD);
break;
case AD_ELEC:
+ if (negated) {
+ tmp = 0;
+ break;
+ }
if (!Blind) pline("%s is zapped!", Monnam(mdef));
tmp += destroy_mitem(mdef, WAND_CLASS, AD_ELEC);
if (resists_elec(mdef)) {
tmp = 0;
break;
case AD_TLPT:
- if(tmp <= 0) tmp = 1;
- if(tmp < mdef->mhp) {
+ if (tmp <= 0) tmp = 1;
+ if (!negated && tmp < mdef->mhp) {
char nambuf[BUFSZ];
boolean u_saw_mon = canseemon(mdef);
/* record the name before losing sight of monster */
tmp = 0;
break;
case AD_DRLI:
- if (rn2(2) && !resists_drli(mdef)) {
+ if (!negated && !rn2(3) && !resists_drli(mdef)) {
int xtmp = d(2,6);
pline("%s suddenly seems weaker!", Monnam(mdef));
mdef->mhpmax -= xtmp;
xkilled(mdef,0);
} else
mdef->m_lev--;
+ tmp = 0;
}
- tmp = 0;
break;
case AD_RUST:
if (pd == &mons[PM_IRON_GOLEM]) {
case AD_DRST:
case AD_DRDX:
case AD_DRCO:
- if (!rn2(8)) {
+ if (!negated && !rn2(8)) {
Your("%s was poisoned!", mpoisons_subj(&youmonst, mattk));
if (resists_poison(mdef))
pline_The("poison doesn't seem to affect %s.",
exercise(A_WIS, TRUE);
break;
case AD_STCK:
- if (!sticks(mdef->data))
+ if (!negated && !sticks(mdef->data))
u.ustuck = mdef; /* it's now stuck to you */
break;
case AD_WRAP:
} else tmp = 0;
break;
case AD_PLYS:
- if (mdef->mcanmove && !rn2(3) && tmp < mdef->mhp) {
+ if (!negated && mdef->mcanmove && !rn2(3) && tmp < mdef->mhp) {
if (!Blind) pline("%s is frozen by you!", Monnam(mdef));
mdef->mcanmove = 0;
mdef->mfrozen = rnd(10);
}
break;
case AD_SLEE:
- if (!mdef->msleeping && sleep_monst(mdef, rnd(10), -1)) {
+ if (!negated && !mdef->msleeping &&
+ sleep_monst(mdef, rnd(10), -1)) {
if (!Blind)
pline("%s is put to sleep by you!", Monnam(mdef));
slept_monst(mdef);
}
break;
case AD_SLIM:
- if (!rn2(4) && mdef->data != &mons[PM_FIRE_VORTEX] &&
- mdef->data != &mons[PM_FIRE_ELEMENTAL] &&
- mdef->data != &mons[PM_SALAMANDER] &&
- mdef->data != &mons[PM_GREEN_SLIME]) {
- You("turn %s into slime.", mon_nam(mdef));
- (void) newcham(mdef, &mons[PM_GREEN_SLIME], FALSE, FALSE);
- tmp = 0;
- }
- break;
+ if (negated) break; /* physical damage only */
+ if (!rn2(4) && mdef->data != &mons[PM_FIRE_VORTEX] &&
+ mdef->data != &mons[PM_FIRE_ELEMENTAL] &&
+ mdef->data != &mons[PM_SALAMANDER] &&
+ mdef->data != &mons[PM_GREEN_SLIME]) {
+ You("turn %s into slime.", mon_nam(mdef));
+ (void) newcham(mdef, &mons[PM_GREEN_SLIME], FALSE, FALSE);
+ tmp = 0;
+ }
+ break;
case AD_ENCH: /* KMH -- remove enchantment (disenchanter) */
- /* There's no msomearmor() function, so just do damage */
- break;
+ /* there's no msomearmor() function, so just do damage */
+ /* if (negated) break; */
+ break;
+ case AD_SLOW:
+ if (!negated && mdef->mspeed != MSLOW) {
+ unsigned int oldspeed = mdef->mspeed;
+
+ mon_adjust_speed(mdef, -1, (struct obj *)0);
+ if (mdef->mspeed != oldspeed && canseemon(mdef))
+ pline("%s slows down.", Monnam(mdef));
+ }
+ break;
+ case AD_CONF:
+ if (!mdef->mconf) {
+ if (canseemon(mdef))
+ pline("%s looks confused.", Monnam(mdef));
+ mdef->mconf = 1;
+ }
+ break;
default: tmp = 0;
- break;
+ break;
}
mdef->mstrategy &= ~STRAT_WAITFORU; /* in case player is very fast */