From c48e1732d89e5a22b9ede83247a7898271f54359 Mon Sep 17 00:00:00 2001 From: PatR Date: Wed, 4 Nov 2015 02:27:59 -0800 Subject: [PATCH] spellcasting bug fix: confusion duration Fix the reported bug that attempting to cast an expired spell, which causes confusion and/or stun, was replacing the duration of any existing confusion or stun with the new amount rather than increasing it by that amount. Attempting to cast any spell while stunned will now fail immediately, and casting an expired spell while confused will increase confusion duration (and/or set stun duration) rather than override it. --- doc/fixes36.0 | 3 ++ src/spell.c | 79 ++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 65 insertions(+), 17 deletions(-) diff --git a/doc/fixes36.0 b/doc/fixes36.0 index 81ac18b62..07d92f96a 100644 --- a/doc/fixes36.0 +++ b/doc/fixes36.0 @@ -932,6 +932,9 @@ remaining monsters continued to move after hero conceptually left the level #turn for non-priest/non-knight attempts to cast "turn undead" spell, but was forcing the spell to target self rather than choose a direction potions of gain energy are more useful for recovering hero's spell energy +spellcasting attempt of any spell while stunned now always fails +spellcasting attempt of expired spell while confused will increase confusion + duration rather than replace it Platform- and/or Interface-Specific Fixes diff --git a/src/spell.c b/src/spell.c index 83054c7bf..cbe06065d 100644 --- a/src/spell.c +++ b/src/spell.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 spell.c $NHDT-Date: 1446191879 2015/10/30 07:57:59 $ $NHDT-Branch: master $:$NHDT-Revision: 1.67 $ */ +/* NetHack 3.6 spell.c $NHDT-Date: 1446632870 2015/11/04 10:27:50 $ $NHDT-Branch: master $:$NHDT-Revision: 1.68 $ */ /* Copyright (c) M. Stephenson 1988 */ /* NetHack may be freely redistributed. See license for details. */ @@ -30,9 +30,10 @@ STATIC_DCL boolean FDECL(cursed_book, (struct obj * bp)); STATIC_DCL boolean FDECL(confused_book, (struct obj *)); STATIC_DCL void FDECL(deadbook, (struct obj *)); STATIC_PTR int NDECL(learn); +STATIC_DCL boolean NDECL(rejectcasting); STATIC_DCL boolean FDECL(getspell, (int *)); -STATIC_PTR int FDECL(CFDECLSPEC spell_cmp, - (const genericptr, const genericptr)); +STATIC_PTR int FDECL(CFDECLSPEC spell_cmp, (const genericptr, + const genericptr)); STATIC_DCL void NDECL(sortspells); STATIC_DCL boolean NDECL(spellsortmenu); STATIC_DCL boolean FDECL(dospellmenu, (const char *, int, int *)); @@ -617,6 +618,29 @@ age_spells() return; } +/* return True if spellcasting is inhibited; + only covers a small subset of reasons why casting won't work */ +STATIC_OVL boolean +rejectcasting() +{ + /* rejections which take place before selecting a particular spell */ + if (Stunned) { + You("are too impaired to cast a spell."); + return TRUE; + } else if (!freehand()) { + /* Note: !freehand() occurs when weapon and shield (or two-handed + * weapon) are welded to hands, so "arms" probably doesn't need + * to be makeplural(bodypart(ARM)). + * + * But why isn't lack of free arms (for gesturing) an issue when + * poly'd hero has no limbs? + */ + Your("arms are not free to cast!"); + return TRUE; + } + return FALSE; +} + /* * Return TRUE if a spell was picked, with the spell index in the return * parameter. Otherwise return FALSE. @@ -632,6 +656,9 @@ int *spell_no; You("don't know any spells right now."); return FALSE; } + if (rejectcasting()) + return FALSE; /* no spell chosen */ + if (flags.menu_style == MENU_TRADITIONAL) { /* we know there is at least 1 known spell */ for (nspells = 1; nspells < MAXSPELL && spellid(nspells) != NO_SPELL; @@ -796,29 +823,37 @@ STATIC_OVL void spell_backfire(spell) int spell; { - long duration = (long) ((spellev(spell) + 1) * 3); /* 6..24 */ + long duration = (long) ((spellev(spell) + 1) * 3), /* 6..24 */ + old_stun = (HStun & TIMEOUT), old_conf = (HConfusion & TIMEOUT); - /* prior to 3.4.1, the only effect was confusion; it still predominates */ + /* Prior to 3.4.1, only effect was confusion; it still predominates. + * + * 3.6.0: this used to override pre-existing confusion duration + * (cases 0..8) and pre-existing stun duration (cases 4..9); + * increase them instead. (Hero can no longer cast spells while + * Stunned, so the potential increment to stun duration here is + * just hypothetical.) + */ switch (rn2(10)) { case 0: case 1: case 2: case 3: - make_confused(duration, FALSE); /* 40% */ + make_confused(old_conf + duration, FALSE); /* 40% */ break; case 4: case 5: case 6: - make_confused(2L * duration / 3L, FALSE); /* 30% */ - make_stunned(duration / 3L, FALSE); + make_confused(old_conf + 2L * duration / 3L, FALSE); /* 30% */ + make_stunned(old_stun + duration / 3L, FALSE); break; case 7: case 8: - make_stunned(2L * duration / 3L, FALSE); /* 20% */ - make_confused(duration / 3L, FALSE); + make_stunned(old_conf + 2L * duration / 3L, FALSE); /* 20% */ + make_confused(old_stun + duration / 3L, FALSE); break; case 9: - make_stunned(duration, FALSE); /* 10% */ + make_stunned(old_stun + duration, FALSE); /* 10% */ break; } return; @@ -836,6 +871,19 @@ boolean atme; struct obj *pseudo; coord cc; + /* + * Reject attempting to cast while stunned or with no free hands. + * Already done in getspell() to stop casting before choosing + * which spell, but duplicated here for cases where spelleffects() + * gets called directly for ^T without intrinsic teleport capability + * or #turn for non-priest/non-knight. + * (There's no duplication of messages; when the rejection takes + * place in getspell(), we don't get called.) + */ + if (rejectcasting()) { + return 0; /* no time elapses */ + } + /* * Spell casting no longer affects knowledge of the spell. A * decrement of spell knowledge is done every turn. @@ -844,7 +892,7 @@ boolean atme; Your("knowledge of this spell is twisted."); pline("It invokes nightmarish images in your mind..."); spell_backfire(spell); - return 0; + return 1; } else if (spellknow(spell) <= KEEN / 200) { /* 100 turns left */ You("strain to recall the spell."); } else if (spellknow(spell) <= KEEN / 40) { /* 500 turns left */ @@ -862,12 +910,9 @@ boolean atme; } else if (ACURR(A_STR) < 4 && spellid(spell) != SPE_RESTORE_ABILITY) { You("lack the strength to cast spells."); return 0; - } else if (check_capacity("Your concentration falters while carrying so " - "much stuff.")) { + } else if (check_capacity( + "Your concentration falters while carrying so much stuff.")) { return 1; - } else if (!freehand()) { - Your("arms are not free to cast!"); - return 0; } if (u.uhave.amulet) {