From 54325de339c540f4db298008e819267f70862699 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 31 Jan 2016 18:22:31 -0800 Subject: [PATCH] fix bz16 - book becoming cursed while being read Attempting to read a cursed spellbook fails with a nasty effect. But a non-cursed book can become cursed while being read (malignant aura after Wizard has been killed). Assuming no interruption for other reasons, the read would finish, the spell be learned, and then the nasty effect would be given. This changes things so that if the book being read becomes cursed and the hero notices (book's bknown flag is set), the read-in-progress will be interrupted. Resuming will take the attempting-to-read-a-cursed-book path. Unfortunately, if the hero doesn't notice, the old behavior still applies. Maybe the new behavior should happen even if bknown isn't set (but then player won't be told why the interruption occurred). --- doc/fixes36.1 | 5 +++++ include/extern.h | 1 + src/mkobj.c | 12 +++++++++--- src/spell.c | 31 ++++++++++++++++++++++--------- 4 files changed, 37 insertions(+), 12 deletions(-) diff --git a/doc/fixes36.1 b/doc/fixes36.1 index bf9cc0be9..051820f47 100644 --- a/doc/fixes36.1 +++ b/doc/fixes36.1 @@ -139,6 +139,11 @@ monsters fleeing up the upstairs on level 1 were supposed to escape the monster hitting fire-dealing monster with weapon triggered passive damage to weapon every time, when hero doing so only had 1/6 chance per hit hitting steam vortex with flammable weapon would damage the weapon with fire +if a non-cursed spellbook being read becomes cursed and hero recognizes that + (bknown is set), interrupt reading +if a non-cursed spellbook being read becomes cursed and hero doesn't notice, + don't leave it flagged as in-use (hence to be deleted during the + next save/restore cycle) if contact-poison cursed book effect occurs Platform- and/or Interface-Specific Fixes diff --git a/include/extern.h b/include/extern.h index 3c0abfaab..7b6fe3d0c 100644 --- a/include/extern.h +++ b/include/extern.h @@ -2197,6 +2197,7 @@ E boolean FDECL(load_special, (const char *)); /* ### spell.c ### */ +E void FDECL(book_cursed, (struct obj *)); #ifdef USE_TRAMPOLI E int NDECL(learn); #endif diff --git a/src/mkobj.c b/src/mkobj.c index 277fe8b46..ef0af3eec 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -1247,12 +1247,14 @@ void curse(otmp) register struct obj *otmp; { + unsigned already_cursed; int old_light = 0; if (otmp->oclass == COIN_CLASS) return; if (otmp->lamplit) old_light = arti_light_radius(otmp); + already_cursed = otmp->cursed; otmp->blessed = 0; otmp->cursed = 1; /* welded two-handed weapon interferes with some armor removal */ @@ -1263,14 +1265,18 @@ register struct obj *otmp; if (otmp == uswapwep && u.twoweap) drop_uswapwep(); /* some cursed items need immediate updating */ - if (carried(otmp) && confers_luck(otmp)) + if (carried(otmp) && confers_luck(otmp)) { set_moreluck(); - else if (otmp->otyp == BAG_OF_HOLDING) + } else if (otmp->otyp == BAG_OF_HOLDING) { otmp->owt = weight(otmp); - else if (otmp->otyp == FIGURINE) { + } else if (otmp->otyp == FIGURINE) { if (otmp->corpsenm != NON_PM && !dead_species(otmp->corpsenm, TRUE) && (carried(otmp) || mcarried(otmp))) attach_fig_transform_timeout(otmp); + } else if (otmp->oclass == SPBOOK_CLASS) { + /* if book hero is reading becomes cursed, interrupt */ + if (!already_cursed) + book_cursed(otmp); } if (otmp->lamplit) maybe_adjust_light(otmp, old_light); diff --git a/src/spell.c b/src/spell.c index 436919276..6110a8472 100644 --- a/src/spell.c +++ b/src/spell.c @@ -120,6 +120,7 @@ STATIC_OVL boolean cursed_book(bp) struct obj *bp; { + boolean was_in_use; int lev = objects[bp->otyp].oc_level; int dmg = 0; @@ -149,11 +150,12 @@ struct obj *bp; break; } /* temp disable in_use; death should not destroy the book */ + was_in_use = bp->in_use; bp->in_use = FALSE; losestr(Poison_resistance ? rn1(2, 1) : rn1(4, 3)); losehp(rnd(Poison_resistance ? 6 : 10), "contact-poisoned spellbook", KILLED_BY_AN); - bp->in_use = TRUE; + bp->in_use = was_in_use; break; case 6: if (Antimagic) { @@ -322,6 +324,17 @@ struct obj *book2; return; } +/* 'book' has just become cursed; if we're reading it and realize it is + now cursed, interrupt */ +void +book_cursed(book) +struct obj *book; +{ + if (occupation == learn && context.spbook.book == book + && book->cursed && book->bknown && multi >= 0) + stop_occupation(); +} + STATIC_PTR int learn(VOID_ARGS) { @@ -344,9 +357,8 @@ learn(VOID_ARGS) context.spbook.delay = 0; return 0; } - if (context - .spbook.delay) { /* not if (context.spbook.delay++), so at end - delay == 0 */ + if (context.spbook.delay) { + /* not if (context.spbook.delay++), so at end delay == 0 */ context.spbook.delay++; return 1; /* still busy */ } @@ -516,15 +528,16 @@ register struct obj *spellbook; too_hard = TRUE; } else { /* uncursed - chance to fail */ - int read_ability = - ACURR(A_INT) + 4 + u.ulevel / 2 - - 2 * objects[booktype].oc_level - + ((ublindf && ublindf->otyp == LENSES) ? 2 : 0); + int read_ability = ACURR(A_INT) + 4 + u.ulevel / 2 + - 2 * objects[booktype].oc_level + + ((ublindf && ublindf->otyp == LENSES) ? 2 : 0); + /* only wizards know if a spell is too difficult */ if (Role_if(PM_WIZARD) && read_ability < 20 && !confused) { char qbuf[QBUFSZ]; + Sprintf(qbuf, - "This spellbook is %sdifficult to comprehend. Continue?", + "This spellbook is %sdifficult to comprehend. Continue?", (read_ability < 12 ? "very " : "")); if (yn(qbuf) != 'y') { spellbook->in_use = FALSE;