From 1c5b29509753e64fe972f906b2d1f20875eef82e Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 26 Nov 2024 18:01:19 -0800 Subject: [PATCH] tin consumption edge cases If eating a tin killed the hero (choked, turned to stone, poly'd into a new man with new Xp too low to survive) and bones were saved, the tin remained intact in them. When hero who is poly'd into metallivore form eats a tin, give a little extra nutrition for the tin itself. Also, eat it immediately by skipping the "It smells like " message and "Eat it? [yn]" prompt. (The message while eating it also reports , so skipping the 'smells' one doesn't end up hiding anything.) --- doc/fixes3-7-0.txt | 5 ++ src/eat.c | 119 ++++++++++++++++++++++++++++++++------------- 2 files changed, 89 insertions(+), 35 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index cf880e758..605bc49d4 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1476,6 +1476,11 @@ a pet with the hides-under attribute could "move reluctantly over" a cursed prevent monster generation in the sokoban trap hallway change MSGHANDLER from compile-time to sysconf option allow changing extended command autocompletions via #optionsfull +if eating a tin's contents caused the hero to choke to death or turn to stone, + resulting bones would contain the tin still intact +when hero who is poly'd into metallivore form eats a tin, bypass "smells like + " feedback and the "Eat it?" prompt; just eat the contents + along with the tin without asking Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/src/eat.c b/src/eat.c index 9b992e1c7..e2c369ae8 100644 --- a/src/eat.c +++ b/src/eat.c @@ -23,6 +23,7 @@ staticfn boolean temp_givit(int, struct permonst *); staticfn void givit(int, struct permonst *); staticfn void eye_of_newt_buzz(void); staticfn void cpostfx(int); +staticfn void use_up_tin(struct obj *) NONNULLARG1; staticfn void consume_tin(const char *); staticfn void start_tin(struct obj *); staticfn int eatcorpse(struct obj *); @@ -788,6 +789,10 @@ cprefx(int pm) if (!Stone_resistance && !(poly_when_stoned(gy.youmonst.data) && polymon(PM_STONE_GOLEM))) { + /* if food was a tin, use it up early to keep it out of bones */ + if (svc.context.tin.tin) + use_up_tin(svc.context.tin.tin); + Sprintf(svk.killer.name, "tasting %s meat", mons[pm].pmnames[NEUTRAL]); svk.killer.format = KILLED_BY; @@ -1232,6 +1237,14 @@ cpostfx(int pm) if (Unchanging) { You_feel("momentarily different."); /* same as poly trap */ } else { + /* polyself() is potentially fatal; if food is a tin, use it up + early to keep it out of bones */ + if (svc.context.tin.tin) { + use_up_tin(svc.context.tin.tin); + /* most tin effects end up being skipped */ + lesshungry(200 + (metallivorous(gy.youmonst.data) ? 5 : 0)); + } + You("%s.", (pm == PM_GENETIC_ENGINEER) ? "undergo a freakish metamorphosis" : "feel a change coming over you"); @@ -1486,18 +1499,34 @@ tin_variety( return r; } +/* finish consume_tin(); also potentially used by cprefx() and cpostfx() */ +staticfn void +use_up_tin(struct obj *tin) +{ + if (carried(tin)) + useup(tin); + else + useupf(tin, 1L); + /* reset tin context */ + svc.context.tin.tin = (struct obj *) NULL; + svc.context.tin.o_id = 0; +} + staticfn void consume_tin(const char *mesg) { const char *what; - int which, mnum, r; + int which, mnum, r, nutamt; + /* if you've eaten tin itself, chance to not eat contents gets bypassed */ + boolean always_eat = metallivorous(gy.youmonst.data); struct obj *tin = svc.context.tin.tin; r = tin_variety(tin, FALSE); if (tin->otrapped || (tin->cursed && r != HOMEMADE_TIN && !rn2(8))) { b_trapped("tin", NO_PART); tin = costly_tin(COST_DSTROY); - goto use_up_tin; + use_up_tin(tin); + return; } pline1(mesg); /* "You succeed in opening the tin." */ @@ -1508,7 +1537,10 @@ consume_tin(const char *mesg) pline("It turns out to be empty."); tin->dknown = tin->known = 1; tin = costly_tin(COST_OPEN); - goto use_up_tin; + use_up_tin(tin); + if (always_eat) + lesshungry(5); /* metallivorous hero ate the tin itself */ + return; } which = 0; /* 0=>plural, 1=>as-is, 2=>"the" prefix */ @@ -1530,14 +1562,17 @@ consume_tin(const char *mesg) else if (which == 2) what = the(what); - pline("It smells like %s.", what); - if (y_n("Eat it?") == 'n') { - if (flags.verbose) - You("discard the open tin."); - if (!Hallucination) - tin->dknown = tin->known = 1; - tin = costly_tin(COST_OPEN); - goto use_up_tin; + if (!always_eat) { + pline("It smells like %s.", what); + if (y_n("Eat it?") == 'n') { + if (flags.verbose) + You("discard the open tin."); + if (!Hallucination) + tin->dknown = tin->known = 1; + tin = costly_tin(COST_OPEN); + use_up_tin(tin); + return; + } } /* in case stop_occupation() was called on previous meal */ @@ -1548,31 +1583,44 @@ consume_tin(const char *mesg) eating_conducts(&mons[mnum]); tin->dknown = tin->known = 1; - cprefx(mnum); - cpostfx(mnum); - /* charge for one at pre-eating cost */ - tin = costly_tin(COST_OPEN); + tin = svc.context.tin.tin = costly_tin(COST_OPEN); + + /* cprefx() or cpostfx() might use up tin to keep it out of bones */ + cprefx(mnum); + if (svc.context.tin.tin) + cpostfx(mnum); + if (!svc.context.tin.tin) + return; if (tintxts[r].nut < 0) { /* rotten */ make_vomiting((long) rn1(15, 10), FALSE); } else { - int nutamt = tintxts[r].nut; - + nutamt = tintxts[r].nut; /* nutrition from a homemade tin (made from a single corpse) shouldn't be more than nutrition from the corresponding corpse; other tinning modes might use more than one corpse or add extra ingredients so aren't similarly restricted */ if (r == HOMEMADE_TIN && nutamt > mons[mnum].cnutrit) nutamt = mons[mnum].cnutrit; + if (always_eat) + nutamt += 5; /* metallivorous hero ate the tin itself */ + /* use up tin now; lesshungry() could be fatal and produce bones */ + use_up_tin(tin), tin = NULL; lesshungry(nutamt); } if (tintxts[r].greasy) { - /* Assume !Glib, because you can't open tins when Glib. */ - make_glib(rn1(11, 5)); /* 5..15 */ - pline("Eating %s food made your %s very slippery.", - tintxts[r].txt, fingers_or_gloves(TRUE)); + /* normal hero is !Glib, because you can't open tins when Glib, + but one poly'd into metallivorous form might already be Glib; + it's debatable whether a rock mole should have its paws made + slippery when eating a greasy tin, but we'll go with that... */ + int alreadyglib = (int) (Glib & TIMEOUT); + + make_glib(alreadyglib + rn1(11, 5)); /* 5..15 */ + pline("Eating %s food made your %s %s slippery.", + tintxts[r].txt, fingers_or_gloves(TRUE), + alreadyglib ? "even more" : "very"); } } else { /* spinach... */ @@ -1584,11 +1632,12 @@ consume_tin(const char *mesg) tin->dknown = tin->known = 1; } - if (y_n("Eat it?") == 'n') { + if (!always_eat && y_n("Eat it?") == 'n') { if (flags.verbose) You("discard the open tin."); tin = costly_tin(COST_OPEN); - goto use_up_tin; + use_up_tin(tin); + return; } /* @@ -1612,19 +1661,19 @@ consume_tin(const char *mesg) : (flags.female ? "Olive Oyl" : "Bluto")); gainstr(tin, 0, FALSE); - tin = costly_tin(COST_OPEN); - lesshungry(tin->blessed ? 600 /* blessed */ - : !tin->cursed ? (400 + rnd(200)) /* uncursed */ - : (200 + rnd(400))); /* cursed */ + tin = svc.context.tin.tin = costly_tin(COST_OPEN); + nutamt = (tin->blessed ? 600 /* blessed */ + : !tin->cursed ? (400 + rnd(200)) /* uncursed */ + : (200 + rnd(400))); /* cursed */ + if (always_eat) + nutamt += 5; /* metallivorous hero also eats the tin itself */ + /* use up tin first; lesshungry() could be fatal and produce bones */ + use_up_tin(tin), tin = NULL; + lesshungry(nutamt); } - - use_up_tin: - if (carried(tin)) - useup(tin); - else - useupf(tin, 1L); - svc.context.tin.tin = (struct obj *) 0; - svc.context.tin.o_id = 0; + if (tin) + use_up_tin(tin); + return; } /* called during each move whilst opening a tin */