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 <creature>" message and "Eat it? [yn]"
prompt.  (The message while eating it also reports <creature>, so
skipping the 'smells' one doesn't end up hiding anything.)
This commit is contained in:
PatR
2024-11-26 18:01:19 -08:00
parent d93704a154
commit 1c5b295097
2 changed files with 89 additions and 35 deletions

View File

@@ -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
<monster>" 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

119
src/eat.c
View File

@@ -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 */