magicbane fixes

Revamp the Magicbane code so that it won't result in "<monster>
turns to flee" followed by "the magic-absorbing blade scares <monster>".
In the process I noticed that resistance checks for its scare and purge
effects were based on the character's experience level regardless of who
was wielding it or who its target was.

     I didn't try to retain the exact behavior it had before, but the
new behavior is pretty close.  The main differences are that the "purge"
effect is now called "cancel" and that the stun effect will be less
common now and always gives feedback when it occurs.  It used to combine
stun with scare and/or purge in some cases, now it won't; and it used to
always scare when purging, now it will pick one or the other.
This commit is contained in:
nethack.rankin
2003-02-10 11:08:29 +00:00
parent 00d320c3d3
commit 6c1d28753c
2 changed files with 188 additions and 159 deletions

View File

@@ -378,6 +378,7 @@ see_monsters() wasn't called when you lost the innate warning intrinsic due
to level loss
xorns sink if the drawbridge they're standing on is raised
applying figurines to an adjacent spot over water does drowning checks
fix sequencing of Magicbane's hit messages
Platform- and/or Interface-Specific Fixes

View File

@@ -1,4 +1,4 @@
/* SCCS Id: @(#)artifact.c 3.4 2002/12/18 */
/* SCCS Id: @(#)artifact.c 3.4 2003/02/08 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
@@ -20,8 +20,11 @@ extern boolean notonhead; /* for long worms */
#define get_artifact(o) \
(((o)&&(o)->oartifact) ? &artilist[(int) (o)->oartifact] : 0)
STATIC_DCL int FDECL(spec_applies, (const struct artifact *,struct monst *));
STATIC_DCL int FDECL(arti_invoke, (struct obj*));
STATIC_DCL boolean FDECL(Mb_hit, (struct monst *magr,struct monst *mdef,
struct obj *,int *,int,BOOLEAN_P,char *));
/* The amount added to the victim's total hit points to insure that the
victim will be killed even after damage bonus/penalty adjustments.
@@ -717,6 +720,184 @@ winid tmpwin; /* supplied by dodiscover() */
#ifdef OVLB
/*
* Magicbane's intrinsic magic is incompatible with normal
* enchantment magic. Thus, its effects have a negative
* dependence on spe. Against low mr victims, it typically
* does "double athame" damage, 2d4. Occasionally, it will
* cast unbalancing magic which effectively averages out to
* 4d4 damage (2.5d4 against high mr victims), for spe = 0.
*/
#define MB_MAX_DIEROLL 8 /* rolls above this aren't magical */
static const char * const mb_verb[4] = {
"probe", "stun", "scare", "cancel"
};
#define MB_INDEX_PROBE 0
#define MB_INDEX_STUN 1
#define MB_INDEX_SCARE 2
#define MB_INDEX_CANCEL 3
/* called when someone is being hit by Magicbane */
STATIC_OVL boolean
Mb_hit(magr, mdef, mb, dmgptr, dieroll, vis, hittee)
struct monst *magr, *mdef; /* attacker and defender */
struct obj *mb; /* Magicbane */
int *dmgptr; /* extra damage target will suffer */
int dieroll; /* d20 that has already scored a hit */
boolean vis; /* whether the action can be seen */
char *hittee; /* target's name: "you" or mon_nam(mdef) */
{
struct permonst *old_uasmon;
boolean youattack = (magr == &youmonst),
youdefend = (mdef == &youmonst),
resisted = FALSE, do_stun, do_confuse, result;
int attack_indx, scare_dieroll = MB_MAX_DIEROLL / 2;
result = FALSE; /* no message given yet */
/* the most severe effects are less likely at higher enchantment */
if (mb->spe >= 3)
scare_dieroll /= (1 << (mb->spe / 3));
/* might stun even when attempting a more severe effect, but
in that case it will only happen if the other effect fails;
extra damage will apply regardless */
do_stun = (mb->spe <= rn2(10));
/* the special effects also boost physical damage (base 2d4 assumed);
increments are generally cumulative, but since the stun effect is
based on a different criterium its damage might not be included */
attack_indx = MB_INDEX_PROBE;
*dmgptr += rnd(4); /* 3d4 */
if (do_stun) {
attack_indx = MB_INDEX_STUN;
*dmgptr += rnd(4); /* 4d4 */
}
if (dieroll <= scare_dieroll) {
attack_indx = MB_INDEX_SCARE;
*dmgptr += rnd(4); /* 5d4; actually (4 or 5)d4 */
}
if (dieroll <= (scare_dieroll / 2)) {
attack_indx = MB_INDEX_CANCEL;
*dmgptr += rnd(4); /* 6d4; actually (5 or 6)d4 */
}
/* give the hit message prior to inflicting the effects */
if (youattack || youdefend || vis) {
result = TRUE;
pline_The("magic-absorbing blade %s %s!",
vtense((const char *)0, mb_verb[attack_indx]), hittee);
/* assume probing has some sort of noticeable feedback
even if it is being done by one monster to another */
if (attack_indx == MB_INDEX_PROBE && !canspotmon(mdef))
map_invisible(mdef->mx, mdef->my);
}
/* now perform special effects */
switch (attack_indx) {
case MB_INDEX_CANCEL:
old_uasmon = youmonst.data;
if (!cancel_monst(mdef, mb, youattack, FALSE, FALSE)) {
resisted = TRUE;
} else {
do_stun = FALSE;
if (youdefend) {
if (youmonst.data != old_uasmon)
*dmgptr = 0; /* rehumanized, so no more damage */
if (u.uenmax > 0) {
You("lose magical energy!");
u.uenmax--;
if (u.uen > 0) u.uen--;
flags.botl = 1;
}
} else {
if (mdef->data == &mons[PM_CLAY_GOLEM])
mdef->mhp = 1; /* cancelled clay golems will die */
if (youattack && attacktype(mdef->data, AT_MAGC)) {
You("absorb magical energy!");
u.uenmax++;
u.uen++;
flags.botl = 1;
}
}
}
break;
case MB_INDEX_SCARE:
if (youdefend) {
if (Antimagic) {
resisted = TRUE;
} else {
nomul(-3);
nomovemsg = "";
if (magr && magr == u.ustuck && sticks(youmonst.data)) {
u.ustuck = (struct monst *)0;
You("release %s!", mon_nam(magr));
}
}
} else {
if (rn2(2) && resist(mdef, WEAPON_CLASS, 0, NOTELL))
resisted = TRUE;
else
monflee(mdef, 3, FALSE, (mdef->mhp > *dmgptr));
}
if (!resisted) do_stun = FALSE;
break;
case MB_INDEX_STUN:
do_stun = TRUE; /* (this is redundant...) */
break;
case MB_INDEX_PROBE:
/* note: can't get probe result unless mb->spe >= 1, but
guard against passing bad argument to rn2() anyway */
if (youattack && (mb->spe < 1 || !rn2(4 * mb->spe))) {
pline_The("probe is insightful.");
/* pre-damage status */
probe_monster(mdef);
}
break;
}
/* stun if that was selected and a worse effect didn't occur */
if (do_stun) {
if (youdefend)
make_stunned((HStun + 3), FALSE);
else
mdef->mstun = 1;
/* avoid extra stun message below if we used mb_verb["stun"] above */
if (attack_indx == MB_INDEX_STUN) do_stun = FALSE;
}
/* lastly, all this magic can be confusing... */
do_confuse = !rn2(12);
if (do_confuse) {
if (youdefend)
make_confused(HConfusion + 4, FALSE);
else
mdef->mconf = 1;
}
if (youattack || youdefend || vis) {
(void) upstart(hittee); /* capitalize */
if (resisted) {
pline("%s %s!", hittee, vtense(hittee, "resist"));
shieldeff(youdefend ? u.ux : mdef->mx,
youdefend ? u.uy : mdef->my);
}
if ((do_stun || do_confuse) && flags.verbose) {
char buf[BUFSZ];
buf[0] = '\0';
if (do_stun) Strcat(buf, "stunned");
if (do_stun && do_confuse) Strcat(buf, " and ");
if (do_confuse) Strcat(buf, "confused");
pline("%s %s %s%c", hittee, vtense(hittee, "are"),
buf, (do_stun && do_confuse) ? '!' : '.');
}
}
return result;
}
/* Function used when someone attacks someone else with an artifact
* weapon. Only adds the special (artifact) damage, and returns a 1 if it
* did something special (in which case the caller won't print the normal
@@ -798,170 +979,17 @@ int dieroll; /* needed for Magicbane and vorpal blades */
return realizes_damage;
}
if (attacks(AD_STUN, otmp) && dieroll <= MB_MAX_DIEROLL) {
/* Magicbane's special attacks (possibly modifies hittee[]) */
return Mb_hit(magr, mdef, otmp, dmgptr, dieroll, vis, hittee);
}
if (!spec_dbon_applies) {
/* since damage bonus didn't apply, nothing more to do;
no further attacks have side-effects on inventory */
return FALSE;
}
/*
* Magicbane's intrinsic magic is incompatible with normal
* enchantment magic. Thus, its effects have a negative
* dependence on spe. Against low mr victims, it typically
* does "double athame" damage, 2d4. Occasionally, it will
* cast unbalancing magic which effectively averages out to
* 4d4 damage (2.5d4 against high mr victims), for spe = 0.
*/
#define MB_MAX_DIEROLL 8 /* rolls above this aren't magical */
#define MB_INDEX_INIT (-1)
#define MB_INDEX_PROBE 0
#define MB_INDEX_STUN 1
#define MB_INDEX_SCARE 2
#define MB_INDEX_PURGE 3
#define MB_RESIST_ATTACK (resist_index = attack_index)
#define MB_RESISTED_ATTACK (resist_index == attack_index)
#define MB_UWEP_ATTACK (youattack && (otmp == uwep))
if (attacks(AD_STUN, otmp) && (dieroll <= MB_MAX_DIEROLL)) {
int attack_index = MB_INDEX_INIT;
int resist_index = MB_INDEX_INIT;
int scare_dieroll = MB_MAX_DIEROLL / 2;
if (otmp->spe >= 3)
scare_dieroll /= (1 << (otmp->spe / 3));
*dmgptr += rnd(4); /* 3d4 */
if (otmp->spe > rn2(10)) /* probe */
attack_index = MB_INDEX_PROBE;
else { /* stun */
attack_index = MB_INDEX_STUN;
*dmgptr += rnd(4); /* 4d4 */
if (youdefend)
make_stunned((HStun + 3), FALSE);
else
mdef->mstun = 1;
}
if (dieroll <= scare_dieroll) { /* scare */
attack_index = MB_INDEX_SCARE;
*dmgptr += rnd(4); /* 5d4 */
if (youdefend) {
if (Antimagic)
MB_RESIST_ATTACK;
else {
nomul(-3);
nomovemsg = "";
if (magr && magr == u.ustuck
&& sticks(youmonst.data)) {
u.ustuck = (struct monst *)0;
You("release %s!", mon_nam(magr));
}
}
} else if (youattack) {
if (rn2(2) && resist(mdef,SPBOOK_CLASS,0,0)) {
MB_RESIST_ATTACK;
} else {
monflee(mdef, 3, FALSE, TRUE);
}
}
}
if (dieroll <= (scare_dieroll / 2)) { /* purge */
struct obj *ospell;
struct permonst *old_uasmon = youmonst.data;
attack_index = MB_INDEX_PURGE;
*dmgptr += rnd(4); /* 6d4 */
/* Create a fake spell object, ala spell.c */
ospell = mksobj(SPE_CANCELLATION, FALSE, FALSE);
ospell->blessed = ospell->cursed = 0;
ospell->quan = 20L;
cancel_monst(mdef, ospell, youattack, FALSE, FALSE);
if (youdefend) {
if (old_uasmon != youmonst.data)
/* rehumanized, no more damage */
*dmgptr = 0;
if (Antimagic)
MB_RESIST_ATTACK;
} else {
if (!mdef->mcan)
MB_RESIST_ATTACK;
/* cancelled clay golems will die ... */
else if (mdef->data == &mons[PM_CLAY_GOLEM])
mdef->mhp = 1;
}
obfree(ospell, (struct obj *)0);
}
if (youdefend || mdef->mhp > 0) { /* ??? -dkh- */
static const char * const mb_verb[4] =
{"probe", "stun", "scare", "purge"};
if (youattack || youdefend || vis) {
pline_The("magic-absorbing blade %ss %s!",
mb_verb[attack_index], hittee);
if (MB_RESISTED_ATTACK) {
pline("%s resist%s!",
youdefend ? "You" : Monnam(mdef),
youdefend ? "" : "s");
shieldeff(youdefend ? u.ux : mdef->mx,
youdefend ? u.uy : mdef->my);
}
}
/* Much ado about nothing. More magic fanfare! */
if (MB_UWEP_ATTACK) {
if (attack_index == MB_INDEX_PURGE) {
if (!MB_RESISTED_ATTACK &&
attacktype(mdef->data, AT_MAGC)) {
You("absorb magical energy!");
u.uenmax++;
u.uen++;
flags.botl = 1;
}
} else if (attack_index == MB_INDEX_PROBE) {
if (!rn2(4 * otmp->spe)) {
pline_The("probe is insightful!");
if (!canspotmon(mdef))
map_invisible(u.ux+u.dx,u.uy+u.dy);
/* pre-damage status */
probe_monster(mdef);
}
}
} else if (youdefend && !MB_RESISTED_ATTACK
&& (attack_index == MB_INDEX_PURGE)) {
You("lose magical energy!");
if (u.uenmax > 0) u.uenmax--;
if (u.uen > 0) u.uen--;
flags.botl = 1;
}
/* all this magic is confusing ... */
if (!rn2(12)) {
if (youdefend)
make_confused((HConfusion + 4), FALSE);
else
mdef->mconf = 1;
if (youattack || youdefend || vis)
pline("%s %s confused.",
youdefend ? "You" : Monnam(mdef),
youdefend ? "are" : "is");
}
}
return TRUE;
}
/* end of Magicbane code */
/* We really want "on a natural 20" but Nethack does it in */
/* reverse from AD&D. */
if (spec_ability(otmp, SPFX_BEHEAD)) {