From 05cf948007db95d080268068d876e1ef612d465a Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 23 Dec 2023 17:38:05 -0800 Subject: [PATCH] fix github issue #1186 - eating Medusa's corpse and having temporary stoning resistance timeout before finishing. Issue reported by Umbire: hero was able to finish eating Medusa's corpse safely after getting the message about no longer being protected against stoning that is given when temporary resistance times out. The eating code was extending temporary resistance--when eating something protected by such--to avoid just that. I thought this was probably a message sequencing situation but it turns out that the code was using touch_petrifies() to test the meal. It should use flesh_petrifies() instead; Medusa doesn't pass touch_petrifies(). I didn't figure that out until after rewriting how the duration is extended. The old way probably would have worked as desired with the revised petrify test but I'm checking in the new version anyway. Fixes #1186 --- doc/fixes3-7-0.txt | 5 +++++ include/extern.h | 4 +++- src/eat.c | 50 +++++++++++++++++++++++++++------------------- src/timeout.c | 24 +++++++++++++++++++--- 4 files changed, 58 insertions(+), 25 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index c2683835e..d92d0a25e 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1811,6 +1811,11 @@ autopickup while levitating shares code with 'mention_decor'; after being told about ice covered by an object, moving over some other object later when mention_decor is Off could give "you are back on solid ground" long after leaving the ice +duration of temporary stoning resistance would be extended while eating a + cockatrice corpse, as intended, but not while eating Medusa's corpse + so if it timed out during the meal, player got the message about hero + no longer being protected against stoning but was able to finish the + meal safely Fixes to 3.7.0-x Platform and/or Interface Problems Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index 49664ca9f..5538d1a86 100644 --- a/include/extern.h +++ b/include/extern.h @@ -863,8 +863,10 @@ extern int eaten_stat(int, struct obj *) NONNULLARG2; extern void food_disappears(struct obj *) NONNULLARG1; extern void food_substitution(struct obj *, struct obj *) NONNULLPTRS; extern long temp_resist(int); +extern boolean eating_dangerous_corpse(int); extern void eating_conducts(struct permonst *) NONNULLARG1; -extern int eat_brains(struct monst *, struct monst *, boolean, int *) NONNULLARG12; +extern int eat_brains(struct monst *, struct monst *, boolean, + int *) NONNULLARG12; extern void fix_petrification(void); extern int intrinsic_possible(int, struct permonst *) NONNULLARG2; extern boolean should_givit(int, struct permonst *) NONNULLARG2; diff --git a/src/eat.c b/src/eat.c index 8587b0338..b9b686961 100644 --- a/src/eat.c +++ b/src/eat.c @@ -16,7 +16,6 @@ static void choke(struct obj *); static void recalc_wt(void); static struct obj *touchfood(struct obj *) NONNULL; static void do_reset_eat(void); -static void maybe_extend_timed_resist(int); static void done_eating(boolean); static void cprefx(int); static boolean temp_givit(int, struct permonst *); @@ -428,7 +427,7 @@ do_reset_eat(void) /* if 'prop' is only set because of a timed value (so not an intrinsic attribute or because of polymorph shape or worn or carried gear), return - its timeout, otherwise return 0 */ + its timeout, otherwise return 0; used by enlightenment */ long temp_resist(int prop) { @@ -448,6 +447,33 @@ temp_resist(int prop) return 0L; } +/* if temporary acid or stoning resistance is timing out while eating + something which that resistance is protecting against, caller will + extend resistance's duration so that it times out after meal finishes */ +boolean +eating_dangerous_corpse(int res) +{ + struct obj *food; + int mnum; + + if (go.occupation == eatfood + && (food = gc.context.victual.piece) != 0 + && food->otyp == CORPSE + && (mnum = food->corpsenm) >= LOW_PM + && (carried(food) || obj_here(food, u.ux, u.uy))) { + + if (res == ACID_RES && acidic(&mons[mnum])) + return TRUE; + /* flesh_petrifies() includes Medusa as well as touch_petrifies() */ + if (res == STONE_RES && flesh_petrifies(&mons[mnum])) + return TRUE; + } + return FALSE; +} + +#if 0 /* no longer used */ +static void maybe_extend_timed_resist(int); + /* if temp resist against 'prop' is about to timeout, extend it slightly */ static void maybe_extend_timed_resist(int prop) @@ -465,6 +491,7 @@ maybe_extend_timed_resist(int prop) set_itimeout(&u.uprops[prop].intrinsic, 2L); } } +#endif /* called each move during eating process */ static int @@ -482,25 +509,6 @@ eatfood(void) if (!gc.context.victual.eating) return 0; - /* - * We don't want temporary acid resistance to timeout while eating - * an acidic corpse or temporary stoning resistance to do that while - * eating a cockatrice corpse. Protection is checked at the start - * of the meal and having it go away mid-meal with a message about - * increased vulnerability but no consequences is too obviously wrong, - * but also too nit-picky to deal with. - * - * (Tins aren't handled by eatfood() and wouldn't need this anyway - * because they're finished in one turn once they've been opened. - * Come to think of it, eggs are probably eaten in one turn too.) - */ - if ((food->otyp == CORPSE || food->otyp == EGG) - && food->corpsenm >= LOW_PM && acidic(&mons[food->corpsenm])) - maybe_extend_timed_resist(ACID_RES); - if ((food->otyp == CORPSE || food->otyp == EGG) - && food->corpsenm >= LOW_PM && touch_petrifies(&mons[food->corpsenm])) - maybe_extend_timed_resist(STONE_RES); - if (++gc.context.victual.usedtime <= gc.context.victual.reqtime) { if (bite()) return 0; diff --git a/src/timeout.c b/src/timeout.c index 1ae644098..7d8204dc7 100644 --- a/src/timeout.c +++ b/src/timeout.c @@ -775,15 +775,33 @@ nh_timeout(void) } break; case ACID_RES: - if (!Acid_resistance && !Unaware) - You("no longer feel safe from acid."); + if (!Acid_resistance) { + if (eating_dangerous_corpse(ACID_RES)) { + /* extend temporary acid resistance if in midst + of eating an acidic corpse; this will repeat + until eating is finished or interrupted */ + set_itimeout(&u.uprops[ACID_RES].intrinsic, 1L); + break; + } + if (!Unaware) + You("no longer feel safe from acid."); + } break; case STONE_RES: if (!Stone_resistance) { + if (eating_dangerous_corpse(STONE_RES)) { + /* extend temporary stoning resistance if in midst + of eating a stoning corpse; this will repeat + until eating is finished or interrupted */ + set_itimeout(&u.uprops[STONE_RES].intrinsic, 1L); + break; + } if (!Unaware) You("no longer feel secure from petrification."); /* no-op if not wielding a cockatrice corpse; - uswapwep case is always a no-op (see Gloves_off()) */ + uswapwep case is always a no-op because two-weapon + combat is only possible with two one-handed weapons + or weapon tools, not corpses */ wielding_corpse(uwep, (struct obj *) 0, FALSE); wielding_corpse(uswapwep, (struct obj *) 0, FALSE); }