1 /* NetHack 3.7	eat.c	$NHDT-Date: 1620348708 2021/05/07 00:51:48 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.242 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Robert Patrick Rankin, 2012. */
4 /* NetHack may be freely redistributed.  See license for details. */
5 
6 #include "hack.h"
7 
8 static int eatmdone(void);
9 static int eatfood(void);
10 static struct obj *costly_tin(int);
11 static int opentin(void);
12 static int unfaint(void);
13 
14 static const char *food_xname(struct obj *, boolean);
15 static void choke(struct obj *);
16 static void recalc_wt(void);
17 static struct obj *touchfood(struct obj *);
18 static void do_reset_eat(void);
19 static void done_eating(boolean);
20 static void cprefx(int);
21 static void givit(struct permonst *);
22 static void cpostfx(int);
23 static void consume_tin(const char *);
24 static void start_tin(struct obj *);
25 static int eatcorpse(struct obj *);
26 static void start_eating(struct obj *, boolean);
27 static void fprefx(struct obj *);
28 static void fpostfx(struct obj *);
29 static int bite(void);
30 static int edibility_prompts(struct obj *);
31 static int tinopen_ok(struct obj *);
32 static int rottenfood(struct obj *);
33 static void eatspecial(void);
34 static int bounded_increase(int, int, int);
35 static void accessory_has_effect(struct obj *);
36 static void eataccessory(struct obj *);
37 static const char *foodword(struct obj *);
38 static boolean maybe_cannibal(int, boolean);
39 static int eat_ok(struct obj *);
40 static int offer_ok(struct obj *);
41 static int tin_ok(struct obj *);
42 
43 /* also used to see if you're allowed to eat cats and dogs */
44 #define CANNIBAL_ALLOWED() (Role_if(PM_CAVE_DWELLER) || Race_if(PM_ORC))
45 
46 /* monster types that cause hero to be turned into stone if eaten */
47 #define flesh_petrifies(pm) (touch_petrifies(pm) || (pm) == &mons[PM_MEDUSA])
48 
49 /* Rider corpses are treated as non-rotting so that attempting to eat one
50    will be sure to reach the stage of eating where that meal is fatal */
51 #define nonrotting_corpse(mnum) \
52     ((mnum) == PM_LIZARD || (mnum) == PM_LICHEN || is_rider(&mons[mnum]))
53 
54 /* non-rotting non-corpses; unlike lizard corpses, these items will behave
55    as if rotten if they are cursed (fortune cookies handled elsewhere) */
56 #define nonrotting_food(otyp) \
57     ((otyp) == LEMBAS_WAFER || (otyp) == CRAM_RATION)
58 
59 /* see hunger states in hack.h - texts used on bottom line */
60 const char *hu_stat[] = { "Satiated", "        ", "Hungry  ", "Weak    ",
61                           "Fainting", "Fainted ", "Starved " };
62 
63 /*
64  * Decide whether a particular object can be eaten by the possibly
65  * polymorphed character.  Not used for monster checks.
66  */
67 boolean
is_edible(register struct obj * obj)68 is_edible(register struct obj *obj)
69 {
70     /* protect invocation tools but not Rider corpses (handled elsewhere)*/
71     /* if (obj->oclass != FOOD_CLASS && obj_resists(obj, 0, 0)) */
72     if (objects[obj->otyp].oc_unique)
73         return FALSE;
74     /* above also prevents the Amulet from being eaten, so we must never
75        allow fake amulets to be eaten either [which is already the case] */
76 
77     /* [g-cubes can eat containers and retain all contents
78         as engulfed items, but poly'd player can't do that] */
79     if (Has_contents(obj))
80         return FALSE;
81 
82     if (can_eat_material(g.youmonst.data, obj->material))
83         return TRUE;
84 
85     /* Ghouls only eat non-veggy corpses or eggs (see dogfood()) */
86     if (u.umonnum == PM_GHOUL)
87         return (boolean)((obj->otyp == CORPSE
88                           && !vegan(&mons[obj->corpsenm]))
89                          || (obj->otyp == EGG));
90 
91     return (boolean) (obj->oclass == FOOD_CLASS);
92 }
93 
94 /* used for hero init, life saving (if choking), and prayer results of fix
95    starving, fix weak from hunger, or golden glow boon (if u.uhunger < 900) */
96 void
init_uhunger(void)97 init_uhunger(void)
98 {
99     g.context.botl = (u.uhs != NOT_HUNGRY || ATEMP(A_STR) < 0);
100     u.uhunger = 900;
101     u.uhs = NOT_HUNGRY;
102     if (ATEMP(A_STR) < 0) {
103         ATEMP(A_STR) = 0;
104         (void) encumber_msg();
105     }
106 }
107 
108 /* tin types [SPINACH_TIN = -1, overrides corpsenm, nut==600] */
109 static const struct {
110     const char *txt;                      /* description */
111     int nut;                              /* nutrition */
112     Bitfield(fodder, 1);                  /* stocked by health food shops */
113     Bitfield(greasy, 1);                  /* causes slippery fingers */
114 } tintxts[] = { { "rotten", -50, 0, 0 },  /* ROTTEN_TIN = 0 */
115                 { "homemade", 50, 1, 0 }, /* HOMEMADE_TIN = 1 */
116                 { "soup made from", 20, 1, 0 },
117                 { "french fried", 80, 0, 1 },
118                 { "pickled", 40, 1, 0 },
119                 { "boiled", 50, 1, 0 },
120                 { "smoked", 50, 1, 0 },
121                 { "dried", 55, 1, 0 },
122                 { "deep fried", 100, 0, 1 },
123                 { "szechuan", 70, 1, 0 },
124                 { "broiled", 80, 0, 0 },
125                 { "stir fried", 80, 0, 1 },
126                 { "sauteed", 95, 0, 0 },
127                 { "candied", 100, 1, 0 },
128                 { "gourmet", 350, 1, 0 },
129                 { "pureed", 500, 1, 0 },
130                 { "stale", 25, 1, 0 },
131                 { "", 0, 0, 0 } };
132 #define TTSZ SIZE(tintxts)
133 
134 /* called after mimicing is over */
135 static int
eatmdone(void)136 eatmdone(void)
137 {
138     /* release `eatmbuf' */
139     if (g.eatmbuf) {
140         if (g.nomovemsg == g.eatmbuf)
141             g.nomovemsg = 0;
142         free((genericptr_t) g.eatmbuf), g.eatmbuf = 0;
143     }
144     /* update display */
145     if (U_AP_TYPE) {
146         g.youmonst.m_ap_type = M_AP_NOTHING;
147         newsym(u.ux, u.uy);
148     }
149     return 0;
150 }
151 
152 /* called when hallucination is toggled */
153 void
eatmupdate(void)154 eatmupdate(void)
155 {
156     const char *altmsg = 0;
157     int altapp = 0; /* lint suppression */
158 
159     if (!g.eatmbuf || g.nomovemsg != g.eatmbuf)
160         return;
161 
162     if (is_obj_mappear(&g.youmonst,ORANGE) && !Hallucination) {
163         /* revert from hallucinatory to "normal" mimicking */
164         altmsg = "You now prefer mimicking yourself.";
165         altapp = GOLD_PIECE;
166     } else if (is_obj_mappear(&g.youmonst,GOLD_PIECE) && Hallucination) {
167         /* won't happen; anything which might make immobilized
168            hero begin hallucinating (black light attack, theft
169            of Grayswandir) will terminate the mimicry first */
170         altmsg = "Your rind escaped intact.";
171         altapp = ORANGE;
172     }
173 
174     if (altmsg) {
175         /* replace end-of-mimicking message */
176         if (strlen(altmsg) > strlen(g.eatmbuf)) {
177             free((genericptr_t) g.eatmbuf);
178             g.eatmbuf = (char *) alloc(strlen(altmsg) + 1);
179         }
180         g.nomovemsg = strcpy(g.eatmbuf, altmsg);
181         /* update current image */
182         g.youmonst.mappearance = altapp;
183         newsym(u.ux, u.uy);
184     }
185 }
186 
187 /* ``[the(] singular(food, xname) [)]'' */
188 static const char *
food_xname(struct obj * food,boolean the_pfx)189 food_xname(struct obj *food, boolean the_pfx)
190 {
191     const char *result;
192 
193     if (food->otyp == CORPSE) {
194         result = corpse_xname(food, (const char *) 0,
195                               CXN_SINGULAR | (the_pfx ? CXN_PFX_THE : 0));
196         /* not strictly needed since pname values are capitalized
197            and the() is a no-op for them */
198         if (type_is_pname(&mons[food->corpsenm]))
199             the_pfx = FALSE;
200     } else {
201         /* the ordinary case */
202         result = singular(food, xname);
203     }
204     if (the_pfx)
205         result = the(result);
206     return result;
207 }
208 
209 /* Created by GAN 01/28/87
210  * Amended by AKP 09/22/87: if not hard, don't choke, just vomit.
211  * Amended by 3.  06/12/89: if not hard, sometimes choke anyway, to keep risk.
212  *                11/10/89: if hard, rarely vomit anyway, for slim chance.
213  *
214  * To a full belly all food is bad. (It.)
215  */
216 static void
choke(struct obj * food)217 choke(struct obj *food)
218 {
219     /* only happens if you were satiated */
220     if (u.uhs != SATIATED) {
221         if (!food || food->otyp != AMULET_OF_STRANGULATION)
222             return;
223     } else if (Role_if(PM_KNIGHT) && u.ualign.type == A_LAWFUL) {
224         adjalign(-1); /* gluttony is unchivalrous */
225         You_feel("like a glutton!");
226     }
227 
228     exercise(A_CON, FALSE);
229 
230     if (Breathless || (!Strangled && !rn2(20))) {
231         /* choking by eating AoS doesn't involve stuffing yourself */
232         if (food && food->otyp == AMULET_OF_STRANGULATION) {
233             You("choke, but recover your composure.");
234             return;
235         }
236         You("stuff yourself and then vomit voluminously.");
237         morehungry(1000); /* you just got *very* sick! */
238         vomit();
239     } else {
240         g.killer.format = KILLED_BY_AN;
241         /*
242          * Note all "killer"s below read "Choked on %s" on the
243          * high score list & tombstone.  So plan accordingly.
244          */
245         if (food) {
246             You("choke over your %s.", foodword(food));
247             if (food->oclass == COIN_CLASS) {
248                 Strcpy(g.killer.name, "very rich meal");
249             } else {
250                 g.killer.format = KILLED_BY;
251                 Strcpy(g.killer.name, killer_xname(food));
252             }
253         } else {
254             You("choke over it.");
255             Strcpy(g.killer.name, "quick snack");
256         }
257         You("die...");
258         done(CHOKING);
259     }
260 }
261 
262 /* modify object wt. depending on time spent consuming it */
263 static void
recalc_wt(void)264 recalc_wt(void)
265 {
266     struct obj *piece = g.context.victual.piece;
267     if (!piece) {
268         impossible("recalc_wt without piece");
269         return;
270     }
271     debugpline1("Old weight = %d", piece->owt);
272     debugpline2("Used time = %d, Req'd time = %d", g.context.victual.usedtime,
273                 g.context.victual.reqtime);
274     piece->owt = weight(piece);
275     debugpline1("New weight = %d", piece->owt);
276 }
277 
278 /* called when eating interrupted by an event */
279 void
reset_eat(void)280 reset_eat(void)
281 {
282     /* we only set a flag here - the actual reset process is done after
283      * the round is spent eating.
284      */
285     if (g.context.victual.eating && !g.context.victual.doreset) {
286         debugpline0("reset_eat...");
287         g.context.victual.doreset = TRUE;
288     }
289     return;
290 }
291 
292 /* base nutrition of a food-class object */
293 unsigned
obj_nutrition(struct obj * otmp)294 obj_nutrition(struct obj *otmp)
295 {
296     unsigned nut = (otmp->otyp == CORPSE) ? mons[otmp->corpsenm].cnutrit
297                       : otmp->globby ? otmp->owt
298                          : (unsigned) objects[otmp->otyp].oc_nutrition;
299 
300     if (otmp->otyp == LEMBAS_WAFER) {
301         if (maybe_polyd(is_elf(g.youmonst.data), Race_if(PM_ELF)))
302             nut += nut / 4; /* 800 -> 1000 */
303         else if (maybe_polyd(is_orc(g.youmonst.data), Race_if(PM_ORC)))
304             nut -= nut / 4; /* 800 -> 600 */
305         /* prevent polymorph making a partly eaten wafer
306            become more nutritious than an untouched one */
307         if (otmp->oeaten >= nut)
308             otmp->oeaten = (otmp->oeaten < objects[LEMBAS_WAFER].oc_nutrition)
309                               ? (nut - 1) : nut;
310     } else if (otmp->otyp == CRAM_RATION) {
311         if (maybe_polyd(is_dwarf(g.youmonst.data), Race_if(PM_DWARF)))
312             nut += nut / 6; /* 600 -> 700 */
313     }
314     return nut;
315 }
316 
317 static struct obj *
touchfood(struct obj * otmp)318 touchfood(struct obj *otmp)
319 {
320     if (otmp->quan > 1L) {
321         if (!carried(otmp))
322             (void) splitobj(otmp, otmp->quan - 1L);
323         else
324             otmp = splitobj(otmp, 1L);
325         debugpline0("split object,");
326     }
327 
328     if (!otmp->oeaten) {
329         costly_alteration(otmp, COST_BITE);
330         otmp->oeaten = obj_nutrition(otmp);
331     }
332 
333     if (carried(otmp)) {
334         freeinv(otmp);
335         if (inv_cnt(FALSE) >= 52) {
336             sellobj_state(SELL_DONTSELL);
337             dropy(otmp);
338             sellobj_state(SELL_NORMAL);
339         } else {
340             otmp->nomerge = 1; /* used to prevent merge */
341             otmp = addinv(otmp);
342             otmp->nomerge = 0;
343         }
344     }
345     return otmp;
346 }
347 
348 /* When food decays, in the middle of your meal, we don't want to dereference
349  * any dangling pointers, so set it to null (which should still trigger
350  * do_reset_eat() at the beginning of eatfood()) and check for null pointers
351  * in do_reset_eat().
352  */
353 void
food_disappears(struct obj * obj)354 food_disappears(struct obj *obj)
355 {
356     if (obj == g.context.victual.piece) {
357         g.context.victual.piece = (struct obj *) 0;
358         g.context.victual.o_id = 0;
359     }
360     if (obj->timed)
361         obj_stop_timers(obj);
362 }
363 
364 /* renaming an object used to result in it having a different address,
365    so the sequence start eating/opening, get interrupted, name the food,
366    resume eating/opening would restart from scratch */
367 void
food_substitution(struct obj * old_obj,struct obj * new_obj)368 food_substitution(struct obj *old_obj, struct obj *new_obj)
369 {
370     if (old_obj == g.context.victual.piece) {
371         g.context.victual.piece = new_obj;
372         g.context.victual.o_id = new_obj->o_id;
373     }
374     if (old_obj == g.context.tin.tin) {
375         g.context.tin.tin = new_obj;
376         g.context.tin.o_id = new_obj->o_id;
377     }
378 }
379 
380 static void
do_reset_eat(void)381 do_reset_eat(void)
382 {
383     debugpline0("do_reset_eat...");
384     if (g.context.victual.piece) {
385         g.context.victual.o_id = 0;
386         g.context.victual.piece = touchfood(g.context.victual.piece);
387         if (g.context.victual.piece)
388             g.context.victual.o_id = g.context.victual.piece->o_id;
389         recalc_wt();
390     }
391     g.context.victual.fullwarn = g.context.victual.eating =
392         g.context.victual.doreset = FALSE;
393     /* Do not set canchoke to FALSE; if we continue eating the same object
394      * we need to know if canchoke was set when they started eating it the
395      * previous time.  And if we don't continue eating the same object
396      * canchoke always gets recalculated anyway.
397      */
398     stop_occupation();
399     newuhs(FALSE);
400 }
401 
402 /* called each move during eating process */
403 static int
eatfood(void)404 eatfood(void)
405 {
406     if (!g.context.victual.piece
407         || (!carried(g.context.victual.piece)
408             && !obj_here(g.context.victual.piece, u.ux, u.uy))) {
409         /* maybe it was stolen? */
410         do_reset_eat();
411         return 0;
412     }
413     if (!g.context.victual.eating)
414         return 0;
415 
416     if (++g.context.victual.usedtime <= g.context.victual.reqtime) {
417         if (bite())
418             return 0;
419         return 1; /* still busy */
420     } else {        /* done */
421         done_eating(TRUE);
422         return 0;
423     }
424 }
425 
426 static void
done_eating(boolean message)427 done_eating(boolean message)
428 {
429     struct obj *piece = g.context.victual.piece;
430 
431     piece->in_use = TRUE;
432     g.occupation = 0; /* do this early, so newuhs() knows we're done */
433     newuhs(FALSE);
434     if (g.nomovemsg) {
435         if (message)
436             pline1(g.nomovemsg);
437         g.nomovemsg = 0;
438     } else if (message)
439         You("finish eating %s.", food_xname(piece, TRUE));
440 
441     if (piece->otyp == CORPSE || piece->globby)
442         cpostfx(piece->corpsenm);
443     else
444         fpostfx(piece);
445 
446     if (carried(piece))
447         useup(piece);
448     else
449         useupf(piece, 1L);
450     g.context.victual.piece = (struct obj *) 0;
451     g.context.victual.o_id = 0;
452     g.context.victual.fullwarn = g.context.victual.eating =
453         g.context.victual.doreset = FALSE;
454 }
455 
456 void
eating_conducts(struct permonst * pd)457 eating_conducts(struct permonst *pd)
458 {
459     int ll_conduct = 0;
460     if (!u.uconduct.food++) {
461         livelog_printf(LL_CONDUCT, "ate for the first time - %s",
462                        pd->pmnames[NEUTRAL]);
463         ll_conduct++;
464     }
465     if (!vegan(pd))
466         if (!u.uconduct.unvegan++ && !ll_conduct) {
467             livelog_printf(LL_CONDUCT,
468                            "consumed animal products (%s) for the first time",
469                            pd->pmnames[NEUTRAL]);
470             ll_conduct++;
471         }
472     if (!vegetarian(pd)) {
473         if (!u.uconduct.unvegetarian && !ll_conduct)
474             livelog_printf(LL_CONDUCT,
475                            "tasted meat (%s) for the first time",
476                            pd->pmnames[NEUTRAL]);
477         violated_vegetarian();
478     }
479 }
480 
481 /* handle side-effects of mind flayer's tentacle attack */
482 int
eat_brains(struct monst * magr,struct monst * mdef,boolean visflag,int * dmg_p)483 eat_brains(struct monst *magr, struct monst *mdef,
484            boolean visflag,
485            int *dmg_p) /* for dishing out extra damage in lieu of Int loss */
486 {
487     struct permonst *pd = mdef->data;
488     boolean give_nutrit = FALSE;
489     int result = MM_HIT, xtra_dmg = rnd(10);
490 
491     if (noncorporeal(pd)) {
492         if (visflag)
493             pline("%s brain is unharmed.",
494                   (mdef == &g.youmonst) ? "Your" : s_suffix(Monnam(mdef)));
495         return MM_MISS; /* side-effects can't occur */
496     } else if (magr == &g.youmonst) {
497         You("eat %s brain!", s_suffix(mon_nam(mdef)));
498     } else if (mdef == &g.youmonst) {
499         if (Hallucination)
500             pline("Oh no! Brain drain!");
501         else
502             Your("brain is eaten!");
503     } else { /* monster against monster */
504         if (visflag && canspotmon(mdef))
505             pline("%s brain is eaten!", s_suffix(Monnam(mdef)));
506     }
507 
508     if (flesh_petrifies(pd)) {
509         /* mind flayer has attempted to eat the brains of a petrification
510            inducing critter (most likely Medusa; attacking a cockatrice via
511            tentacle-touch should have been caught before reaching this far) */
512         if (magr == &g.youmonst) {
513             if (!Stone_resistance && !Stoned)
514                 make_stoned(5L, (char *) 0, KILLED_BY_AN,
515                             pmname(pd, Mgender(mdef)));
516         } else {
517             /* no need to check for poly_when_stoned or Stone_resistance;
518                mind flayers don't have those capabilities */
519             if (visflag && canseemon(magr))
520                 pline("%s turns to stone!", Monnam(magr));
521             monstone(magr);
522             if (!DEADMONSTER(magr)) {
523                 /* life-saved; don't continue eating the brains */
524                 return MM_MISS;
525             } else {
526                 if (magr->mtame && !visflag)
527                     /* parallels mhitm.c's brief_feeling */
528                     You("have a sad thought for a moment, then it passes.");
529                 return MM_AGR_DIED;
530             }
531         }
532     }
533 
534     if (magr == &g.youmonst) {
535         /*
536          * player mind flayer is eating something's brain
537          */
538         eating_conducts(pd);
539         if (mindless(pd)) { /* (cannibalism not possible here) */
540             pline("%s doesn't notice.", Monnam(mdef));
541             /* all done; no extra harm inflicted upon target */
542             return MM_MISS;
543         } else if (is_rider(pd)) {
544             pline("Ingesting that is fatal.");
545             Sprintf(g.killer.name, "unwisely ate the brain of %s",
546                     pmname(pd, Mgender(mdef)));
547             g.killer.format = NO_KILLER_PREFIX;
548             done(DIED);
549             /* life-saving needed to reach here */
550             exercise(A_WIS, FALSE);
551             *dmg_p += xtra_dmg; /* Rider takes extra damage */
552         } else {
553             morehungry(-rnd(30)); /* cannot choke */
554             if (ABASE(A_INT) < AMAX(A_INT)) {
555                 /* recover lost Int; won't increase current max */
556                 ABASE(A_INT) += rnd(4);
557                 if (ABASE(A_INT) > AMAX(A_INT))
558                     ABASE(A_INT) = AMAX(A_INT);
559                 g.context.botl = 1;
560             }
561             exercise(A_WIS, TRUE);
562             *dmg_p += xtra_dmg;
563         }
564         /* targetting another mind flayer or your own underlying species
565            is cannibalism */
566         (void) maybe_cannibal(monsndx(pd), TRUE);
567 
568     } else if (mdef == &g.youmonst) {
569         /*
570          * monster mind flayer is eating hero's brain
571          */
572         /* no such thing as mindless players */
573         if (ABASE(A_INT) <= ATTRMIN(A_INT)) {
574             static NEARDATA const char brainlessness[] = "brainlessness";
575 
576             if (Lifesaved) {
577                 Strcpy(g.killer.name, brainlessness);
578                 g.killer.format = KILLED_BY;
579                 done(DIED);
580                 /* amulet of life saving has now been used up */
581                 pline("Unfortunately your brain is still gone.");
582                 /* sanity check against adding other forms of life-saving */
583                 u.uprops[LIFESAVED].extrinsic =
584                     u.uprops[LIFESAVED].intrinsic = 0L;
585             } else {
586                 Your("last thought fades away.");
587             }
588             Strcpy(g.killer.name, brainlessness);
589             g.killer.format = KILLED_BY;
590             done(DIED);
591             /* can only get here when in wizard or explore mode and user has
592                explicitly chosen not to die; arbitrarily boost intelligence */
593             ABASE(A_INT) = ATTRMIN(A_INT) + 2;
594             You_feel("like a scarecrow.");
595         }
596         give_nutrit = TRUE; /* in case a conflicted pet is doing this */
597         exercise(A_WIS, FALSE);
598         /* caller handles Int and memory loss */
599 
600     } else { /* mhitm */
601         /*
602          * monster mind flayer is eating another monster's brain
603          */
604         if (mindless(pd)) {
605             if (visflag && canspotmon(mdef))
606                 pline("%s doesn't notice.", Monnam(mdef));
607             return MM_MISS;
608         } else if (is_rider(pd)) {
609             mondied(magr);
610             if (DEADMONSTER(magr))
611                 result = MM_AGR_DIED;
612             /* Rider takes extra damage regardless of whether attacker dies */
613             *dmg_p += xtra_dmg;
614         } else {
615             *dmg_p += xtra_dmg;
616             give_nutrit = TRUE;
617             if (*dmg_p >= mdef->mhp && visflag && canspotmon(mdef))
618                 pline("%s last thought fades away...",
619                       s_suffix(Monnam(mdef)));
620         }
621     }
622 
623     if (give_nutrit && magr->mtame && !magr->isminion) {
624         EDOG(magr)->hungrytime += rnd(60);
625         magr->mconf = 0;
626     }
627 
628     return result;
629 }
630 
631 /* eating a corpse or egg of one's own species is usually naughty */
632 static boolean
maybe_cannibal(int pm,boolean allowmsg)633 maybe_cannibal(int pm, boolean allowmsg)
634 {
635     static NEARDATA long ate_brains = 0L;
636     struct permonst *fptr = &mons[pm]; /* food type */
637 
638     /* when poly'd into a mind flayer, multiple tentacle hits in one
639        turn cause multiple digestion checks to occur; avoid giving
640        multiple luck penalties for the same attack */
641     if (g.moves == ate_brains)
642         return FALSE;
643     ate_brains = g.moves; /* ate_anything, not just brains... */
644 
645     if (!CANNIBAL_ALLOWED()
646         /* non-cannibalistic heroes shouldn't eat own species ever
647            and also shouldn't eat current species when polymorphed
648            (even if having the form of something which doesn't care
649            about cannibalism--hero's innate traits aren't altered) */
650         && (your_race(fptr)
651             || (Upolyd && same_race(g.youmonst.data, fptr))
652             || (u.ulycn >= LOW_PM && were_beastie(pm) == u.ulycn))) {
653         if (allowmsg) {
654             if (Upolyd && your_race(fptr))
655                 You("have a bad feeling deep inside.");
656             You("cannibal!  You will regret this!");
657         }
658         HAggravate_monster |= FROMOUTSIDE;
659         change_luck(-rn1(4, 2)); /* -5..-2 */
660         return TRUE;
661     }
662     return FALSE;
663 }
664 
665 static void
cprefx(register int pm)666 cprefx(register int pm)
667 {
668     (void) maybe_cannibal(pm, TRUE);
669     if (flesh_petrifies(&mons[pm])) {
670         if (!Stone_resistance
671             && !(poly_when_stoned(g.youmonst.data)
672                  && polymon(PM_STONE_GOLEM, POLYMON_ALL_MSGS))) {
673             Sprintf(g.killer.name, "tasting %s meat",
674                     mons[pm].pmnames[NEUTRAL]);
675             g.killer.format = KILLED_BY;
676             You("turn to stone.");
677             done(STONING);
678             if (g.context.victual.piece)
679                 g.context.victual.eating = FALSE;
680             return; /* lifesaved */
681         }
682     }
683 
684     switch (pm) {
685     case PM_LITTLE_DOG:
686     case PM_DOG:
687     case PM_LARGE_DOG:
688     case PM_KITTEN:
689     case PM_HOUSECAT:
690     case PM_LARGE_CAT:
691         /* cannibals are allowed to eat domestic animals without penalty */
692         if (!CANNIBAL_ALLOWED()) {
693             You_feel("that eating the %s was a bad idea.",
694                      mons[pm].pmnames[NEUTRAL]);
695             HAggravate_monster |= FROMOUTSIDE;
696         }
697         break;
698     case PM_LIZARD:
699         if (Stoned)
700             fix_petrification();
701         break;
702     case PM_DEATH:
703     case PM_PESTILENCE:
704     case PM_FAMINE: {
705         pline("Eating that is instantly fatal.");
706         Sprintf(g.killer.name, "unwisely ate the body of %s",
707                 mons[pm].pmnames[NEUTRAL]);
708         g.killer.format = NO_KILLER_PREFIX;
709         done(DIED);
710         /* life-saving needed to reach here */
711         exercise(A_WIS, FALSE);
712         /* revive an actual corpse; can't do that if it was a tin;
713            3.7: this used to assume that such tins were impossible but
714            they can be wished for in wizard mode; they can't make it
715            to normal play though because bones creation empties them */
716         if (g.context.victual.piece /* Null for tins */
717             && g.context.victual.piece->otyp == CORPSE /* paranoia */
718             && revive_corpse(g.context.victual.piece, FALSE)) {
719             g.context.victual.piece = (struct obj *) 0;
720             g.context.victual.o_id = 0;
721         }
722         return;
723     }
724     case PM_GREEN_SLIME:
725         if (!Slimed && (!Unchanging || can_slime_with_unchanging())
726             && !slimeproof(g.youmonst.data)) {
727             You("don't feel very well.");
728             make_slimed(10L, (char *) 0);
729             delayed_killer(SLIMED, KILLED_BY_AN, "");
730         }
731     /* Fall through */
732     default:
733         if (acidic(&mons[pm]) && Stoned)
734             fix_petrification();
735         break;
736     }
737 }
738 
739 void
fix_petrification(void)740 fix_petrification(void)
741 {
742     char buf[BUFSZ];
743 
744     if (Hallucination)
745         Sprintf(buf, "What a pity--you just ruined a future piece of %sart!",
746                 ACURR(A_CHA) > 15 ? "fine " : "");
747     else
748         Strcpy(buf, "You feel limber!");
749     make_stoned(0L, buf, 0, (char *) 0);
750 }
751 
752 /*
753  * Relevant functions for gaining intrinsics through food:
754  *   intrinsic_possible()
755  *   corpse_intrinsic()
756  *   givit()
757  *   should_givit()
758  *   mon_givit()
759  * If you add an intrinsic that can be gotten by eating a monster, add it
760  * to these functions.  (It must already be in prop.h to be an intrinsic
761  * property.)
762  * If it's not an intrinsic but you want it to be treated as one, add a constant
763  * for it in psuedo_intrinsics in hack.h before adding it to these functions.
764  *
765  * It would be very easy to make the intrinsics not try to give you one
766  * that you already had by checking to see if you have it in
767  * intrinsic_possible() instead of givit(), but we're not that nice.
768  */
769 
770 /* intrinsic_possible() returns TRUE iff a monster can give an intrinsic.
771  * Has no side effects other than debugplines. */
772 int
intrinsic_possible(int type,register struct permonst * ptr)773 intrinsic_possible(int type, register struct permonst *ptr)
774 {
775     int res = 0;
776 
777 #ifdef DEBUG
778 #define ifdebugresist(Msg)      \
779     do {                        \
780         if (res)                \
781             debugpline0(Msg);   \
782     } while (0)
783 #else
784 #define ifdebugresist(Msg) /*empty*/
785 #endif
786     switch (type) {
787     case FIRE_RES:
788         res = (ptr->mconveys & MR_FIRE) != 0;
789         ifdebugresist("can get fire resistance");
790         break;
791     case SLEEP_RES:
792         res = (ptr->mconveys & MR_SLEEP) != 0;
793         ifdebugresist("can get sleep resistance");
794         break;
795     case COLD_RES:
796         res = (ptr->mconveys & MR_COLD) != 0;
797         ifdebugresist("can get cold resistance");
798         break;
799     case DISINT_RES:
800         res = (ptr->mconveys & MR_DISINT) != 0;
801         ifdebugresist("can get disintegration resistance");
802         break;
803     case SHOCK_RES: /* shock (electricity) resistance */
804         res = (ptr->mconveys & MR_ELEC) != 0;
805         ifdebugresist("can get shock resistance");
806         break;
807     case POISON_RES:
808         res = (ptr->mconveys & MR_POISON) != 0;
809         ifdebugresist("can get poison resistance");
810         break;
811     case TELEPORT:
812         res = can_teleport(ptr);
813         ifdebugresist("can get teleport");
814         break;
815     case TELEPORT_CONTROL:
816         res = control_teleport(ptr);
817         ifdebugresist("can get teleport control");
818         break;
819     case TELEPAT:
820         res = telepathic(ptr);
821         ifdebugresist("can get telepathy");
822         break;
823     case INTRINSIC_GAIN_STR:
824         res = is_giant(ptr);
825         ifdebugresist("can gain strength");
826         break;
827     case INTRINSIC_GAIN_EN:
828         /* Eating magical monsters can give you some magical energy. */
829         /* MRKR: "eye of newt" may give small magical energy boost */
830         res = (attacktype(ptr, AT_MAGC) || ptr == &mons[PM_NEWT]);
831         ifdebugresist("can gain magic energy");
832         break;
833     default:
834         /* res stays 0 */
835         break;
836     }
837 #undef ifdebugresist
838     return res;
839 }
840 
841 /* The "do we or do we not give the intrinsic" logic from givit(), extracted
842  * into its own function. Depends on the monster's level and the type of
843  * intrinsic it is trying to give you.
844  */
845 boolean
should_givit(int type,struct permonst * ptr)846 should_givit(int type, struct permonst *ptr)
847 {
848     int chance;
849     /* some intrinsics are easier to get than others */
850     switch (type) {
851     case POISON_RES:
852         if ((ptr == &mons[PM_KILLER_BEE] || ptr == &mons[PM_SCORPION])
853             && !rn2(4))
854             chance = 1;
855         else
856             chance = 15;
857         break;
858     case TELEPORT:
859         chance = 10;
860         break;
861     case TELEPORT_CONTROL:
862         chance = 12;
863         break;
864     case TELEPAT:
865         if (ptr == &mons[PM_FLOATING_EYE] || ptr == &mons[PM_MIND_FLAYER]
866             || ptr == &mons[PM_MASTER_MIND_FLAYER])
867             chance = 1;
868         else
869             chance = 20;
870         break;
871     case INTRINSIC_GAIN_STR:
872     case INTRINSIC_GAIN_EN:
873         /* gain str has special handling in corpse_intrinsic(); gain energy has
874          * special handling in givit(). Neither should be blocked by this check.
875          */
876         return TRUE;
877     default:
878         chance = 15;
879         break;
880     }
881 
882     return (ptr->mlevel > rn2(chance));
883 }
884 
885 /* givit() tries to give you an intrinsic based on the monster's level
886  * and what type of intrinsic it is trying to give you.
887  */
888 static void
givit(register struct permonst * ptr)889 givit(register struct permonst *ptr)
890 {
891     int type = corpse_intrinsic(ptr);
892 
893     if (type == 0) {
894         return; /* no intrinsic from this corpse */
895     }
896     debugpline1("Attempting to give intrinsic %d", type);
897 
898     if (!should_givit(type, ptr))
899         return; /* failed die roll */
900 
901     switch (type) {
902     case FIRE_RES:
903         debugpline0("Trying to give fire resistance");
904         if (!(HFire_resistance & FROMOUTSIDE)) {
905             You(Hallucination ? "be chillin'." : "feel a momentary chill.");
906             HFire_resistance |= FROMOUTSIDE;
907         }
908         break;
909     case SLEEP_RES:
910         debugpline0("Trying to give sleep resistance");
911         if (!(HSleep_resistance & FROMOUTSIDE)) {
912             You_feel("wide awake.");
913             HSleep_resistance |= FROMOUTSIDE;
914         }
915         break;
916     case COLD_RES:
917         debugpline0("Trying to give cold resistance");
918         if (!(HCold_resistance & FROMOUTSIDE)) {
919             You_feel("full of hot air.");
920             HCold_resistance |= FROMOUTSIDE;
921         }
922         break;
923     case DISINT_RES:
924         debugpline0("Trying to give disintegration resistance");
925         if (!(HDisint_resistance & FROMOUTSIDE)) {
926             You_feel(Hallucination ? "totally together, man." : "very firm.");
927             HDisint_resistance |= FROMOUTSIDE;
928         }
929         break;
930     case SHOCK_RES: /* shock (electricity) resistance */
931         debugpline0("Trying to give shock resistance");
932         if (!(HShock_resistance & FROMOUTSIDE)) {
933             if (Hallucination)
934                 You_feel("grounded in reality.");
935             else
936                 Your("health currently feels amplified!");
937             HShock_resistance |= FROMOUTSIDE;
938         }
939         break;
940     case POISON_RES:
941         debugpline0("Trying to give poison resistance");
942         if (!(HPoison_resistance & FROMOUTSIDE)) {
943             You_feel(Poison_resistance ? "especially healthy." : "healthy.");
944             HPoison_resistance |= FROMOUTSIDE;
945         }
946         break;
947     case TELEPORT:
948         debugpline0("Trying to give teleport");
949         if (!(HTeleportation & FROMOUTSIDE)) {
950             You_feel(Hallucination ? "diffuse." : "very jumpy.");
951             HTeleportation |= FROMOUTSIDE;
952         }
953         break;
954     case TELEPORT_CONTROL:
955         debugpline0("Trying to give teleport control");
956         if (!(HTeleport_control & FROMOUTSIDE)) {
957             You_feel(Hallucination ? "centered in your personal space."
958                                    : "in control of yourself.");
959             HTeleport_control |= FROMOUTSIDE;
960         }
961         break;
962     case TELEPAT:
963         debugpline0("Trying to give telepathy");
964         if (!(HTelepat & FROMOUTSIDE)) {
965             You_feel(Hallucination ? "in touch with the cosmos."
966                                    : "a strange mental acuity.");
967             HTelepat |= FROMOUTSIDE;
968             /* If blind, make sure monsters show up. */
969             if (Blind)
970                 see_monsters();
971         }
972         break;
973     case INTRINSIC_GAIN_STR:
974         gainstr((struct obj *) 0, 0, TRUE);
975         break;
976     case INTRINSIC_GAIN_EN:
977         if (rn2(3) || 3 * u.uen <= 2 * u.uenmax) {
978             int old_uen = u.uen, old_uenmax = u.uenmax;
979             u.uen += rnd(max(ptr->mlevel, 3));
980             if (u.uen > u.uenmax) {
981                 if (!rn2(3))
982                     u.uenmax++;
983                 u.uen = u.uenmax;
984             }
985             if (old_uen != u.uen) {
986                 You_feel("a %s buzz.",
987                         old_uenmax != u.uenmax ? "moderate" : "mild");
988                 g.context.botl = 1;
989             }
990         }
991         break;
992     default:
993         impossible("Tried to give an impossible intrinsic %d", type);
994         break;
995     }
996 }
997 
998 /* Choose (one of) the intrinsics granted by a corpse, and return it.
999  * If this corpse gives no intrinsics, return 0.
1000  * Non-deterministic; should only be called once per corpse.
1001  */
1002 int
corpse_intrinsic(struct permonst * ptr)1003 corpse_intrinsic(struct permonst *ptr)
1004 {
1005     int i;
1006     int count = 0;
1007     int prop = 0;
1008 
1009     /* Check the monster for all of the intrinsics.  If this
1010      * monster can give more than one, pick one to try to give
1011      * from among all it can give.
1012      *
1013      * Strength from giants is now treated like an intrinsic
1014      * rather than being given unconditionally.
1015      */
1016 
1017     for (i = FIRST_FAKE_PROP; i <= LAST_PROP; i++) {
1018         /* FIRST_FAKE_PROP is negative and 0 isn't a real property so skip it */
1019         if (i == 0) {
1020             continue;
1021         }
1022         if (!intrinsic_possible(i, ptr))
1023             continue;
1024         count++;
1025         /* a 1 in count chance of replacing the old choice
1026            with this one, and a count-1 in count chance
1027            of keeping the old choice (note that 1 in 1 and
1028            0 in 1 are what we want for the first candidate) */
1029         if (!rn2(count)) {
1030             debugpline2("Intrinsic %d replacing %d", i, prop);
1031             prop = i;
1032         }
1033     }
1034 
1035     /* if strength is the only candidate, give it 50% chance */
1036     if (prop == INTRINSIC_GAIN_STR && count == 1 && !rn2(2))
1037         prop = 0;
1038 
1039     return prop;
1040 }
1041 
1042 DISABLE_WARNING_FORMAT_NONLITERAL
1043 
1044 /* called after completely consuming a corpse */
1045 static void
cpostfx(int pm)1046 cpostfx(int pm)
1047 {
1048     int tmp = 0;
1049     int catch_lycanthropy = NON_PM;
1050     boolean check_intrinsics = FALSE;
1051 
1052     /* in case `afternmv' didn't get called for previously mimicking
1053        gold, clean up now to avoid `eatmbuf' memory leak */
1054     if (g.eatmbuf)
1055         (void) eatmdone();
1056 
1057     switch (pm) {
1058     case PM_WRAITH:
1059         pluslvl(FALSE);
1060         break;
1061     case PM_HUMAN_WERERAT:
1062         catch_lycanthropy = PM_WERERAT;
1063         break;
1064     case PM_HUMAN_WEREJACKAL:
1065         catch_lycanthropy = PM_WEREJACKAL;
1066         break;
1067     case PM_HUMAN_WEREWOLF:
1068         catch_lycanthropy = PM_WEREWOLF;
1069         break;
1070     case PM_NURSE:
1071         if (Upolyd)
1072             u.mh = u.mhmax;
1073         else
1074             u.uhp = u.uhpmax;
1075         make_blinded(0L, !u.ucreamed);
1076         g.context.botl = 1;
1077         check_intrinsics = TRUE; /* might also convey poison resistance */
1078         break;
1079     case PM_STALKER: {
1080         boolean was_invis = !!Invis;
1081         incr_itimeout(&HInvis, (long) rn1(200, 200));
1082         if (!was_invis && !Blind && !BInvis) {
1083             self_invis_message();
1084         }
1085         if (was_invis) {
1086             incr_itimeout(&HSee_invisible, (long) rn1(200, 600));
1087         }
1088         newsym(u.ux, u.uy);
1089     }
1090         /*FALLTHRU*/
1091     case PM_YELLOW_LIGHT:
1092     case PM_GIANT_BAT:
1093         make_stunned((HStun & TIMEOUT) + 30L, FALSE);
1094         /*FALLTHRU*/
1095     case PM_BAT:
1096         make_stunned((HStun & TIMEOUT) + 30L, FALSE);
1097         break;
1098     case PM_GIANT_MIMIC:
1099         tmp += 10;
1100         /*FALLTHRU*/
1101     case PM_LARGE_MIMIC:
1102         tmp += 20;
1103         /*FALLTHRU*/
1104     case PM_SMALL_MIMIC:
1105         tmp += 20;
1106         if (g.youmonst.data->mlet != S_MIMIC && !Unchanging) {
1107             char buf[BUFSZ];
1108 
1109             /* Long time debate on whether this should break polyselfless
1110              * conduct. In vanilla, it does, but in xNetHack it does not.  The
1111              * reasoning is that the mimics don't _actually_ change form, they
1112              * are just supernaturally good at looking like something else. So
1113              * the player upon eating them doesn't turn into a pile of gold
1114              * either; they just temporarily get very good at pretending to
1115              * look like one.
1116              * An additional argument is that the point of polyselfless conduct
1117              * is to abandon the benefits of turning into other monsters
1118              * (second HP bar, many powerful abilities, etc), and mimicking
1119              * an object provides none of these. */
1120             You_cant("resist the temptation to mimic %s.",
1121                      Hallucination ? "an orange" : "a pile of gold");
1122             /* A pile of gold can't ride. */
1123             if (u.usteed)
1124                 dismount_steed(DISMOUNT_FELL);
1125             nomul(-tmp);
1126             g.multi_reason = "pretending to be a pile of gold";
1127             Sprintf(buf,
1128                     Hallucination
1129                        ? "You suddenly dread being peeled and mimic %s again!"
1130                        : "You now prefer mimicking %s again.",
1131                     an(Upolyd ? pmname(g.youmonst.data, Ugender)
1132                               : g.urace.noun));
1133             g.eatmbuf = dupstr(buf);
1134             g.nomovemsg = g.eatmbuf;
1135             g.afternmv = eatmdone;
1136             /* ??? what if this was set before? */
1137             g.youmonst.m_ap_type = M_AP_OBJECT;
1138             g.youmonst.mappearance = Hallucination ? ORANGE : GOLD_PIECE;
1139             newsym(u.ux, u.uy);
1140             curs_on_u();
1141             /* make gold symbol show up now */
1142             display_nhwindow(WIN_MAP, TRUE);
1143         }
1144         break;
1145     case PM_QUANTUM_MECHANIC:
1146         Your("velocity suddenly seems very uncertain!");
1147         if (HFast & INTRINSIC) {
1148             HFast &= ~INTRINSIC;
1149             You("seem slower.");
1150         } else {
1151             HFast |= FROMOUTSIDE;
1152             You("seem faster.");
1153         }
1154         break;
1155     case PM_LIZARD:
1156         if ((HStun & TIMEOUT) > 2)
1157             make_stunned(2L, FALSE);
1158         if ((HConfusion & TIMEOUT) > 2)
1159             make_confused(2L, FALSE);
1160         break;
1161     case PM_CHAMELEON:
1162     case PM_DOPPELGANGER:
1163     case PM_SANDESTIN: /* moot--they don't leave corpses */
1164     case PM_GENETIC_ENGINEER:
1165         if (Unchanging) {
1166             You_feel("momentarily different."); /* same as poly trap */
1167         } else {
1168             You("%s.", (pm == PM_GENETIC_ENGINEER)
1169                           ? "undergo a freakish metamorphosis"
1170                           : "feel a change coming over you");
1171             polyself(0);
1172         }
1173         break;
1174     case PM_DISPLACER_BEAST:
1175         if (!Displaced) /* give a message (before setting the timeout) */
1176             toggle_displacement((struct obj *) 0, 0L, TRUE);
1177         incr_itimeout(&HDisplaced, d(6, 6));
1178         break;
1179     case PM_DISENCHANTER:
1180         /* picks an intrinsic at random and removes it; there's
1181            no feedback if hero already lacks the chosen ability */
1182         debugpline0("using attrcurse to strip an intrinsic");
1183         attrcurse();
1184         break;
1185     case PM_DEATH:
1186     case PM_PESTILENCE:
1187     case PM_FAMINE:
1188         /* life-saved; don't attempt to confer any intrinsics */
1189         break;
1190     case PM_MIND_FLAYER:
1191     case PM_MASTER_MIND_FLAYER:
1192         if (ABASE(A_INT) < ATTRMAX(A_INT)) {
1193             if (!rn2(2)) {
1194                 pline("Yum!  That was real brain food!");
1195                 (void) adjattrib(A_INT, 1, AA_YESMSG);
1196                 break; /* don't give them telepathy, too */
1197             }
1198         } else {
1199             pline("For some reason, that tasted bland.");
1200         }
1201     /*FALLTHRU*/
1202     default:
1203         check_intrinsics = TRUE;
1204         break;
1205     }
1206 
1207     /* possibly convey an intrinsic */
1208     if (check_intrinsics) {
1209         struct permonst *ptr = &mons[pm];
1210         if (dmgtype(ptr, AD_STUN) || dmgtype(ptr, AD_HALU)
1211             || pm == PM_VIOLET_FUNGUS) {
1212             pline("Oh wow!  Great stuff!");
1213             (void) make_hallucinated((HHallucination & TIMEOUT) + 200L, FALSE,
1214                                      0L);
1215         }
1216 
1217         /* give an intrinsic now (givit() might fail) */
1218         givit(ptr);
1219     }
1220 
1221     if (catch_lycanthropy >= LOW_PM) {
1222         set_ulycn(catch_lycanthropy);
1223         retouch_equipment(2);
1224     }
1225     return;
1226 }
1227 
1228 RESTORE_WARNING_FORMAT_NONLITERAL
1229 
1230 void
violated_vegetarian(void)1231 violated_vegetarian(void)
1232 {
1233     u.uconduct.unvegetarian++;
1234     if (Role_if(PM_MONK)) {
1235         if (u.uconduct.unvegetarian <= 10) {
1236             You_feel("guilty.");
1237         }
1238         adjalign(-1);
1239     }
1240     return;
1241 }
1242 
1243 /* common code to check and possibly charge for 1 g.context.tin.tin,
1244  * will split() g.context.tin.tin if necessary */
1245 static struct obj *
costly_tin(int alter_type)1246 costly_tin(int alter_type) /* COST_xxx */
1247 {
1248     struct obj *tin = g.context.tin.tin;
1249 
1250     if (carried(tin) ? tin->unpaid
1251                      : (costly_spot(tin->ox, tin->oy) && !tin->no_charge)) {
1252         if (tin->quan > 1L) {
1253             tin = g.context.tin.tin = splitobj(tin, 1L);
1254             g.context.tin.o_id = tin->o_id;
1255         }
1256         costly_alteration(tin, alter_type);
1257     }
1258     return tin;
1259 }
1260 
1261 int
tin_variety_txt(char * s,int * tinvariety)1262 tin_variety_txt(char *s, int *tinvariety)
1263 {
1264     int k, l;
1265 
1266     if (s && tinvariety) {
1267         *tinvariety = -1;
1268         for (k = 0; k < TTSZ - 1; ++k) {
1269             l = (int) strlen(tintxts[k].txt);
1270             if (!strncmpi(s, tintxts[k].txt, l) && ((int) strlen(s) > l)
1271                 && s[l] == ' ') {
1272                 *tinvariety = k;
1273                 return (l + 1);
1274             }
1275         }
1276     }
1277     return 0;
1278 }
1279 
1280 /*
1281  * This assumes that buf already contains the word "tin",
1282  * as is the case with caller xname().
1283  */
1284 void
tin_details(struct obj * obj,int mnum,char * buf)1285 tin_details(struct obj *obj, int mnum, char *buf)
1286 {
1287     char buf2[BUFSZ];
1288     int r = tin_variety(obj, TRUE);
1289 
1290     if (obj && buf) {
1291         if (r == SPINACH_TIN)
1292             Strcat(buf, " of spinach");
1293         else if (mnum == NON_PM)
1294             Strcpy(buf, "empty tin");
1295         else {
1296             if ((obj->cknown || iflags.override_ID) && obj->spe < 0) {
1297                 if (r == ROTTEN_TIN || r == HOMEMADE_TIN) {
1298                     /* put these before the word tin */
1299                     Sprintf(buf2, "%s %s of ", tintxts[r].txt, buf);
1300                     Strcpy(buf, buf2);
1301                 } else {
1302                     Sprintf(eos(buf), " of %s ", tintxts[r].txt);
1303                 }
1304             } else {
1305                 Strcpy(eos(buf), " of ");
1306             }
1307             if (vegetarian(&mons[mnum]))
1308                 Sprintf(eos(buf), "%s", mons[mnum].pmnames[NEUTRAL]);
1309             else
1310                 Sprintf(eos(buf), "%s meat", mons[mnum].pmnames[NEUTRAL]);
1311         }
1312     }
1313 }
1314 
1315 void
set_tin_variety(struct obj * obj,int forcetype)1316 set_tin_variety(struct obj *obj, int forcetype)
1317 {
1318     register int r;
1319 
1320     if (forcetype == SPINACH_TIN
1321         || (forcetype == HEALTHY_TIN
1322             && (obj->corpsenm == NON_PM /* empty or already spinach */
1323                 || !vegetarian(&mons[obj->corpsenm])))) { /* replace meat */
1324         obj->corpsenm = NON_PM; /* not based on any monster */
1325         obj->spe = 1;           /* spinach */
1326         return;
1327     } else if (forcetype == HEALTHY_TIN) {
1328         r = tin_variety(obj, FALSE);
1329         if (r < 0 || r >= TTSZ)
1330             r = ROTTEN_TIN; /* shouldn't happen */
1331         while ((r == ROTTEN_TIN && !obj->cursed) || !tintxts[r].fodder)
1332             r = rn2(TTSZ - 1);
1333     } else if (forcetype >= 0 && forcetype < TTSZ - 1) {
1334         r = forcetype;
1335     } else {               /* RANDOM_TIN */
1336         r = rn2(TTSZ - 1); /* take your pick */
1337         if (r == ROTTEN_TIN && nonrotting_corpse(obj->corpsenm))
1338             r = HOMEMADE_TIN; /* lizards don't rot */
1339     }
1340     obj->spe = -(r + 1); /* offset by 1 to allow index 0 */
1341 }
1342 
1343 int
tin_variety(struct obj * obj,boolean disp)1344 tin_variety(struct obj *obj,
1345             boolean disp) /* we're just displaying so leave things alone */
1346 {
1347     register int r;
1348 
1349     if (obj->spe == 1) {
1350         r = SPINACH_TIN;
1351     } else if (obj->cursed) {
1352         r = ROTTEN_TIN; /* always rotten if cursed */
1353     } else if (obj->spe < 0) {
1354         r = -(obj->spe);
1355         --r; /* get rid of the offset */
1356     } else
1357         r = rn2(TTSZ - 1);
1358 
1359     if (!disp && r == HOMEMADE_TIN && !obj->blessed && !rn2(7))
1360         r = ROTTEN_TIN; /* some homemade tins go bad */
1361 
1362     if (r == ROTTEN_TIN && nonrotting_corpse(obj->corpsenm))
1363         r = HOMEMADE_TIN; /* lizards don't rot */
1364     return r;
1365 }
1366 
1367 static void
consume_tin(const char * mesg)1368 consume_tin(const char *mesg)
1369 {
1370     const char *what;
1371     int which, mnum, r;
1372     struct obj *tin = g.context.tin.tin;
1373 
1374     r = tin_variety(tin, FALSE);
1375     if (tin->otrapped || (tin->cursed && r != HOMEMADE_TIN && !rn2(8))) {
1376         int lvl = level_difficulty(),
1377             dmg = rnd(5 + (lvl < 5 ? lvl : 2 + lvl / 2));
1378         pline("KABOOM!!  The tin was booby-trapped!");
1379         wake_nearby();
1380         losehp(Maybe_Half_Phys(dmg), "exploding tin", KILLED_BY_AN);
1381         exercise(A_STR, FALSE);
1382         make_stunned((HStun & TIMEOUT) + (long) dmg, TRUE);
1383         tin = costly_tin(COST_DSTROY);
1384         goto use_up_tin;
1385     }
1386 
1387     pline1(mesg); /* "You succeed in opening the tin." */
1388 
1389     if (r != SPINACH_TIN) {
1390         mnum = tin->corpsenm;
1391         if (mnum == NON_PM) {
1392             pline("It turns out to be empty.");
1393             tin->dknown = tin->known = 1;
1394             tin = costly_tin(COST_OPEN);
1395             goto use_up_tin;
1396         }
1397 
1398         which = 0; /* 0=>plural, 1=>as-is, 2=>"the" prefix */
1399         if ((mnum == PM_COCKATRICE || mnum == PM_CHICKATRICE)
1400             && (Stone_resistance || Hallucination)) {
1401             what = "chicken";
1402             which = 1; /* suppress pluralization */
1403         } else if (Hallucination) {
1404             what = rndmonnam(NULL);
1405         } else {
1406             what = mons[mnum].pmnames[NEUTRAL];
1407             if (the_unique_pm(&mons[mnum]))
1408                 which = 2;
1409             else if (type_is_pname(&mons[mnum]))
1410                 which = 1;
1411         }
1412         if (which == 0)
1413             what = makeplural(what);
1414         else if (which == 2)
1415             what = the(what);
1416 
1417         pline("It smells like %s.", what);
1418         if (yn("Eat it?") == 'n') {
1419             if (flags.verbose)
1420                 You("discard the open tin.");
1421             if (!Hallucination)
1422                 tin->dknown = tin->known = 1;
1423             tin = costly_tin(COST_OPEN);
1424             goto use_up_tin;
1425         }
1426 
1427         /* in case stop_occupation() was called on previous meal */
1428         g.context.victual.piece = (struct obj *) 0;
1429         g.context.victual.o_id = 0;
1430         g.context.victual.fullwarn = g.context.victual.eating =
1431             g.context.victual.doreset = FALSE;
1432 
1433         You("consume %s %s.", tintxts[r].txt, mons[mnum].pmnames[NEUTRAL]);
1434 
1435         eating_conducts(&mons[mnum]);
1436 
1437         tin->dknown = tin->known = 1;
1438         cprefx(mnum);
1439         cpostfx(mnum);
1440 
1441         /* charge for one at pre-eating cost */
1442         tin = costly_tin(COST_OPEN);
1443 
1444         if (tintxts[r].nut < 0) { /* rotten */
1445             make_vomiting((long) rn1(15, 10), FALSE);
1446         } else {
1447             int nutamt = tintxts[r].nut;
1448 
1449             /* nutrition from a homemade tin (made from a single corpse)
1450                shouldn't be more than nutrition from the corresponding
1451                corpse; other tinning modes might use more than one corpse
1452                or add extra ingredients so aren't similarly restricted */
1453             if (r == HOMEMADE_TIN && nutamt > mons[mnum].cnutrit)
1454                 nutamt = mons[mnum].cnutrit;
1455             lesshungry(nutamt);
1456         }
1457 
1458         if (tintxts[r].greasy) {
1459             /* Assume !Glib, because you can't open tins when Glib. */
1460             make_glib(rn1(11, 5)); /* 5..15 */
1461             pline("Eating %s food made your %s very slippery.",
1462                   tintxts[r].txt, fingers_or_gloves(TRUE));
1463         }
1464 
1465         if (!strcmp(tintxts[r].txt, "szechuan") && rn2(2)) {
1466             struct obj* cookie = mksobj(FORTUNE_COOKIE, TRUE, FALSE);
1467             cookie->blessed = tin->blessed;
1468             cookie->cursed = tin->cursed;
1469             pline("There is a free fortune cookie inside!");
1470             hold_another_object(cookie, "It falls to the floor.", NULL, NULL);
1471         }
1472     } else { /* spinach... */
1473         if (tin->cursed) {
1474             pline("It contains some decaying%s%s substance.",
1475                   Blind ? "" : " ", Blind ? "" : hcolor(NH_GREEN));
1476         } else {
1477             pline("It contains spinach.");
1478             tin->dknown = tin->known = 1;
1479         }
1480 
1481         if (yn("Eat it?") == 'n') {
1482             if (flags.verbose)
1483                 You("discard the open tin.");
1484             tin = costly_tin(COST_OPEN);
1485             goto use_up_tin;
1486         }
1487 
1488         /*
1489          * Same order as with non-spinach above:
1490          * conduct update, side-effects, shop handling, and nutrition.
1491          */
1492         /* don't need vegan/vegetarian checks for spinach */
1493         if (!u.uconduct.food++)
1494             livelog_write_string(LL_CONDUCT, "ate for the first time (spinach)");
1495         if (!tin->cursed)
1496             pline("This makes you feel like %s!",
1497                   /* "Swee'pea" is a character from the Popeye cartoons */
1498                   Hallucination ? "Swee'pea"
1499                   /* "feel like Popeye" unless sustain ability suppresses
1500                      any attribute change; this slightly oversimplifies
1501                      things:  we want "Popeye" if no strength increase
1502                      occurs due to already being at maximum, but we won't
1503                      get it if at-maximum and fixed-abil both apply */
1504                   : !Fixed_abil ? "Popeye"
1505                   /* no gain, feel like another character from Popeye */
1506                   : (flags.female ? "Olive Oyl" : "Bluto"));
1507         gainstr(tin, 0, FALSE);
1508 
1509         tin = costly_tin(COST_OPEN);
1510         lesshungry(tin->blessed ? 600                   /* blessed */
1511                    : !tin->cursed ? (400 + rnd(200))    /* uncursed */
1512                      : (200 + rnd(400)));               /* cursed */
1513     }
1514 
1515  use_up_tin:
1516     if (carried(tin))
1517         useup(tin);
1518     else
1519         useupf(tin, 1L);
1520     g.context.tin.tin = (struct obj *) 0;
1521     g.context.tin.o_id = 0;
1522 }
1523 
1524 /* called during each move whilst opening a tin */
1525 static int
opentin(void)1526 opentin(void)
1527 {
1528     /* perhaps it was stolen (although that should cause interruption) */
1529     if (!carried(g.context.tin.tin)
1530         && (!obj_here(g.context.tin.tin, u.ux, u.uy)
1531             || !can_reach_floor(TRUE)))
1532         return 0; /* %% probably we should use tinoid */
1533     if (g.context.tin.usedtime++ >= 50) {
1534         You("give up your attempt to open the tin.");
1535         return 0;
1536     }
1537     if (g.context.tin.usedtime < g.context.tin.reqtime)
1538         return 1; /* still busy */
1539 
1540     consume_tin("You succeed in opening the tin.");
1541     return 0;
1542 }
1543 
1544 /* called when starting to open a tin */
1545 static void
start_tin(struct obj * otmp)1546 start_tin(struct obj *otmp)
1547 {
1548     const char *mesg = 0;
1549     register int tmp;
1550 
1551     if (metallivorous(g.youmonst.data)) {
1552         mesg = "You bite right into the metal tin...";
1553         tmp = 0;
1554     } else if (cantwield(g.youmonst.data)) { /* nohands || verysmall */
1555         You("cannot handle the tin properly to open it.");
1556         return;
1557     } else if (otmp->blessed) {
1558         /* 50/50 chance for immediate access vs 1 turn delay (unless
1559            wielding blessed tin opener which always yields immediate
1560            access); 1 turn delay case is non-deterministic:  getting
1561            interrupted and retrying might yield another 1 turn delay
1562            or might open immediately on 2nd (or 3rd, 4th, ...) try */
1563         tmp = (uwep && uwep->blessed && uwep->otyp == TIN_OPENER) ? 0 : rn2(2);
1564         if (!tmp)
1565             mesg = "The tin opens like magic!";
1566         else
1567             pline_The("tin seems easy to open.");
1568     } else if (uwep) {
1569         switch (uwep->otyp) {
1570         case TIN_OPENER:
1571             mesg = "You easily open the tin."; /* iff tmp==0 */
1572             tmp = rn2(uwep->cursed ? 3 : !uwep->blessed ? 2 : 1);
1573             break;
1574         case DAGGER:
1575         case ELVEN_DAGGER:
1576         case ORCISH_DAGGER:
1577         case ATHAME:
1578         case KNIFE:
1579         case CRYSKNIFE:
1580             tmp = 3;
1581             break;
1582         case PICK_AXE:
1583         case AXE:
1584             tmp = 6;
1585             break;
1586         default:
1587             goto no_opener;
1588         }
1589         pline("Using %s you try to open the tin.", yobjnam(uwep, (char *) 0));
1590     } else {
1591  no_opener:
1592         pline("It is not so easy to open this tin.");
1593         if (Glib) {
1594             pline_The("tin slips from your %s.", fingers_or_gloves(FALSE));
1595             if (otmp->quan > 1L) {
1596                 otmp = splitobj(otmp, 1L);
1597             }
1598             if (carried(otmp))
1599                 dropx(otmp);
1600             else
1601                 stackobj(otmp);
1602             return;
1603         }
1604         tmp = rn1(1 + 500 / ((int) (ACURR(A_DEX) + ACURRSTR)), 10);
1605     }
1606 
1607     g.context.tin.tin = otmp;
1608     g.context.tin.o_id = otmp->o_id;
1609     if (!tmp) {
1610         consume_tin(mesg); /* begin immediately */
1611     } else {
1612         g.context.tin.reqtime = tmp;
1613         g.context.tin.usedtime = 0;
1614         set_occupation(opentin, "opening the tin", 0);
1615     }
1616     return;
1617 }
1618 
1619 /* called when waking up after fainting */
1620 int
Hear_again(void)1621 Hear_again(void)
1622 {
1623     /* Chance of deafness going away while fainted/sleeping/etc. */
1624     if (!rn2(2)) {
1625         make_deaf(0L, FALSE);
1626         g.context.botl = TRUE;
1627     }
1628     return 0;
1629 }
1630 
1631 /* called on the "first bite" of rotten food */
1632 static int
rottenfood(struct obj * obj)1633 rottenfood(struct obj *obj)
1634 {
1635     pline("Blecch!  Rotten %s!", foodword(obj));
1636     if (!rn2(4)) {
1637         if (Hallucination)
1638             You_feel("rather trippy.");
1639         else
1640             You_feel("rather %s.", body_part(LIGHT_HEADED));
1641         make_confused(HConfusion + d(2, 4), FALSE);
1642     } else if (!rn2(4) && !Blind) {
1643         pline("Everything suddenly goes dark.");
1644         /* hero is not Blind, but Blinded timer might be nonzero if
1645            blindness is being overridden by the Eyes of the Overworld */
1646         make_blinded((Blinded & TIMEOUT) + (long) d(2, 10), FALSE);
1647         if (!Blind)
1648             Your1(vision_clears);
1649     } else if (!rn2(3)) {
1650         const char *what, *where;
1651         int duration = rnd(10);
1652 
1653         if (!Blind)
1654             what = "goes", where = "dark";
1655         else if (Levitation || Is_airlevel(&u.uz) || Is_waterlevel(&u.uz))
1656             what = "you lose control of", where = "yourself";
1657         else
1658             what = "you slap against the",
1659             where = (u.usteed) ? "saddle" : surface(u.ux, u.uy);
1660         pline_The("world spins and %s %s.", what, where);
1661         incr_itimeout(&HDeaf, duration);
1662         g.context.botl = TRUE;
1663         nomul(-duration);
1664         g.multi_reason = "unconscious from rotten food";
1665         g.nomovemsg = "You are conscious again.";
1666         g.afternmv = Hear_again;
1667         return 1;
1668     }
1669     return 0;
1670 }
1671 
1672 /* called when a corpse is selected as food */
1673 static int
eatcorpse(struct obj * otmp)1674 eatcorpse(struct obj *otmp)
1675 {
1676     int retcode = 0, tp = 0, mnum = otmp->corpsenm;
1677     long rotted = 0L;
1678     int ll_conduct = 0;
1679     boolean stoneable = (flesh_petrifies(&mons[mnum]) && !Stone_resistance
1680                          && !poly_when_stoned(g.youmonst.data)),
1681             slimeable = (mnum == PM_GREEN_SLIME && !Slimed && !Unchanging
1682                          && !slimeproof(g.youmonst.data)),
1683             glob = otmp->globby ? TRUE : FALSE;
1684 
1685     /* KMH, conduct */
1686     if (!vegan(&mons[mnum]))
1687         if (!u.uconduct.unvegan++) {
1688             livelog_printf(LL_CONDUCT, "consumed animal products for the first time, by eating %s",
1689                            an(food_xname(otmp, FALSE)));
1690             ll_conduct++;
1691         }
1692     if (!vegetarian(&mons[mnum])) {
1693         if (!u.uconduct.unvegetarian && !ll_conduct)
1694             livelog_printf(LL_CONDUCT, "tasted meat for the first time, by eating %s",
1695                            an(food_xname(otmp, FALSE)));
1696         violated_vegetarian();
1697     }
1698 
1699     if (!nonrotting_corpse(mnum)) {
1700         long age = peek_at_iced_corpse_age(otmp);
1701 
1702         rotted = (g.monstermoves - age) / (10L + rn2(20));
1703         if (otmp->cursed)
1704             rotted += 2L;
1705         else if (otmp->blessed)
1706             rotted -= 2L;
1707     }
1708 
1709     if (mnum != PM_ACID_BLOB && !stoneable && !slimeable && rotted > 5L) {
1710         boolean cannibal = maybe_cannibal(mnum, FALSE);
1711 
1712         pline("Ulch - that %s was tainted%s!",
1713               (mons[mnum].mlet == S_FUNGUS) ? "fungoid vegetation"
1714                   : glob ? "glob"
1715                       : vegetarian(&mons[mnum]) ? "protoplasm"
1716                           : "meat",
1717               cannibal ? ", you cannibal" : "");
1718         if (Sick_resistance) {
1719             pline("It doesn't seem at all sickening, though...");
1720         } else {
1721             long sick_time;
1722 
1723             sick_time = (long) rn1(10, 10);
1724             /* make sure new ill doesn't result in improvement */
1725             if (Sick && (sick_time > Sick))
1726                 sick_time = (Sick > 1L) ? Sick - 1L : 1L;
1727             make_sick(sick_time, corpse_xname(otmp, "rotted", CXN_NORMAL),
1728                       TRUE, SICK_VOMITABLE);
1729 
1730             pline("(It must have died too long ago to be safe to eat.)");
1731         }
1732         if (carried(otmp))
1733             useup(otmp);
1734         else
1735             useupf(otmp, 1L);
1736         return 2;
1737     } else if (acidic(&mons[mnum]) && !Acid_resistance) {
1738         tp++;
1739         You("have a very bad case of stomach acid.");   /* not body_part() */
1740         losehp(rnd(15), !glob ? "acidic corpse" : "acidic glob",
1741                KILLED_BY_AN); /* acid damage */
1742     } else if (poisonous(&mons[mnum]) && rn2(5)) {
1743         tp++;
1744         pline("Ecch - that must have been poisonous!");
1745         if (!Poison_resistance) {
1746             losestr(rnd(4));
1747             losehp(rnd(15), !glob ? "poisonous corpse" : "poisonous glob",
1748                    KILLED_BY_AN);
1749         } else
1750             You("seem unaffected by the poison.");
1751     /* now any corpse left too long will make you mildly ill */
1752     } else if ((rotted > 5L || (rotted > 3L && rn2(5))) && !Sick_resistance) {
1753         tp++;
1754         You_feel("%ssick.", (Sick) ? "very " : "");
1755         losehp(rnd(8), !glob ? "cadaver" : "rotted glob", KILLED_BY_AN);
1756     }
1757 
1758     /* delay is weight dependent */
1759     g.context.victual.reqtime
1760         = 3 + ((!glob ? mons[mnum].cwt : otmp->owt) >> 6);
1761 
1762     if (!tp && !nonrotting_corpse(mnum) && (otmp->orotten || !rn2(7))) {
1763         if (rottenfood(otmp)) {
1764             otmp->orotten = TRUE;
1765             (void) touchfood(otmp);
1766             retcode = 1;
1767         }
1768 
1769         if (!mons[otmp->corpsenm].cnutrit) {
1770             /* no nutrition: rots away, no message if you passed out */
1771             if (!retcode)
1772                 pline_The("corpse rots away completely.");
1773             if (carried(otmp))
1774                 useup(otmp);
1775             else
1776                 useupf(otmp, 1L);
1777             retcode = 2;
1778         }
1779 
1780         if (!retcode)
1781             consume_oeaten(otmp, 2); /* oeaten >>= 2 */
1782     } else if ((mnum == PM_COCKATRICE || mnum == PM_CHICKATRICE)
1783                && (Stone_resistance || Hallucination)) {
1784         pline("This tastes just like chicken!");
1785     } else if (mnum == PM_FLOATING_EYE && u.umonnum == PM_RAVEN) {
1786         You("peck the eyeball with delight.");
1787     } else {
1788         /* yummy is always False for omnivores, palatable always True */
1789         boolean yummy = (vegan(&mons[mnum])
1790                             ? (!carnivorous(g.youmonst.data)
1791                                && herbivorous(g.youmonst.data))
1792                             : (carnivorous(g.youmonst.data)
1793                                && !herbivorous(g.youmonst.data))),
1794             palatable = ((vegetarian(&mons[mnum])
1795                           ? herbivorous(g.youmonst.data)
1796                           : carnivorous(g.youmonst.data))
1797                          && rn2(10)
1798                          && ((rotted < 1) ? TRUE : !rn2(rotted+1)));
1799         const char *pmxnam = food_xname(otmp, FALSE);
1800         const char* taste;
1801 
1802         if (Hallucination) {
1803             /* if hallu, "This food is X"; if not, "This food tastes X" */
1804             if (yummy)
1805                 /* tiger reference is to TV ads for "Frosted Flakes",
1806                    breakfast cereal targeted at kids by "Tony the tiger" */
1807                 taste = (u.umonnum == PM_TIGER) ? "gr-r-reat" : "gnarly";
1808             else if (palatable)
1809                 taste = "copacetic";
1810             else
1811                 taste = "grody";
1812         }
1813         else {
1814             if (yummy)
1815                 taste = "delicious";
1816             else if (palatable)
1817                 taste = "okay";
1818             else
1819                 taste = "terrible";
1820         }
1821 
1822         /* special cases for funny messages or combinations go here */
1823         if (maybe_polyd(is_dwarf(g.youmonst.data), Race_if(PM_DWARF))
1824             && mons[mnum].mlet == S_RODENT) {
1825             yummy = palatable = TRUE;
1826         }
1827         if (mnum == PM_LONG_WORM || mnum == PM_BABY_LONG_WORM) {
1828             taste = "spicy";
1829         }
1830         if ((yummy || palatable) && mnum == PM_BROWN_PUDDING) {
1831             taste = "like chocolate";
1832         }
1833 
1834         if (!strncmpi(pmxnam, "the ", 4))
1835             pmxnam += 4;
1836         pline("%s%s %s %s%c",
1837               type_is_pname(&mons[mnum])
1838                  ? "" : the_unique_pm(&mons[mnum]) ? "The " : "This ",
1839               pmxnam,
1840               Hallucination ? "is" : "tastes", taste,
1841               (yummy || !palatable) ? '!' : '.');
1842     }
1843 
1844     return retcode;
1845 }
1846 
1847 /* called as you start to eat */
1848 static void
start_eating(struct obj * otmp,boolean already_partly_eaten)1849 start_eating(struct obj *otmp, boolean already_partly_eaten)
1850 {
1851     const char *old_nomovemsg, *save_nomovemsg;
1852     static char msgbuf[BUFSZ];
1853 
1854     debugpline2("start_eating: %s (victual = %s)",
1855                 /* note: fmt_ptr() returns a static buffer but supports
1856                    several such so we don't need to copy the first result
1857                    before calling it a second time */
1858                 fmt_ptr((genericptr_t) otmp),
1859                 fmt_ptr((genericptr_t) g.context.victual.piece));
1860     debugpline1("reqtime = %d", g.context.victual.reqtime);
1861     debugpline1("(original reqtime = %d)", objects[otmp->otyp].oc_delay);
1862     debugpline1("nmod = %d", g.context.victual.nmod);
1863     debugpline1("oeaten = %d", otmp->oeaten);
1864     g.context.victual.fullwarn = g.context.victual.doreset = FALSE;
1865     g.context.victual.eating = TRUE;
1866 
1867     if (otmp->otyp == CORPSE || otmp->globby) {
1868         cprefx(g.context.victual.piece->corpsenm);
1869         if (!g.context.victual.piece || !g.context.victual.eating) {
1870             /* rider revived, or died and lifesaved */
1871             return;
1872         }
1873     }
1874 
1875     old_nomovemsg = g.nomovemsg;
1876     if (bite()) {
1877         /* survived choking, finish off food that's nearly done;
1878            need this to handle cockatrice eggs, fortune cookies, etc */
1879         if (++g.context.victual.usedtime >= g.context.victual.reqtime) {
1880             /* don't want done_eating() to issue g.nomovemsg if it
1881                is due to vomit() called by bite() */
1882             save_nomovemsg = g.nomovemsg;
1883             if (!old_nomovemsg)
1884                 g.nomovemsg = 0;
1885             done_eating(FALSE);
1886             if (!old_nomovemsg)
1887                 g.nomovemsg = save_nomovemsg;
1888         }
1889         return;
1890     }
1891 
1892     if (++g.context.victual.usedtime >= g.context.victual.reqtime) {
1893         /* print "finish eating" message if they just resumed -dlc */
1894         done_eating((g.context.victual.reqtime > 1
1895                      || already_partly_eaten) ? TRUE : FALSE);
1896         return;
1897     }
1898 
1899     Sprintf(msgbuf, "eating %s", food_xname(otmp, TRUE));
1900     set_occupation(eatfood, msgbuf, 0);
1901 }
1902 
1903 /*
1904  * Called on "first bite" of (non-corpse) food, after touchfood() has
1905  * marked it 'partly eaten'.  Used for non-rotten non-tin non-corpse food.
1906  * Messages should use present tense since multi-turn food won't be
1907  * finishing at the time they're issued.
1908  */
1909 static void
fprefx(struct obj * otmp)1910 fprefx(struct obj *otmp)
1911 {
1912     switch (otmp->otyp) {
1913     case FOOD_RATION: /* nutrition 800 */
1914         /* 200+800 remains below 1000+1, the satiation threshold */
1915         if (u.uhunger <= 200)
1916             pline("%s!", Hallucination ? "Oh wow, like, superior, man"
1917                                        : "This food really hits the spot");
1918 
1919         /* 700-1+800 remains below 1500, the choking threshold which
1920            triggers "you're having a hard time getting it down" feedback */
1921         else if (u.uhunger < 700)
1922             pline("This satiates your %s!", body_part(STOMACH));
1923         /* [satiation message may be inaccurate if eating gets interrupted] */
1924         break;
1925     case TRIPE_RATION:
1926         if (carnivorous(g.youmonst.data) && !humanoid(g.youmonst.data)) {
1927             pline("This tripe ration is surprisingly good!");
1928         } else if (maybe_polyd(is_orc(g.youmonst.data), Race_if(PM_ORC))) {
1929             pline(Hallucination ? "Tastes great!  Less filling!"
1930                                 : "Mmm, tripe... not bad!");
1931         } else {
1932             pline("Yak - dog food!");
1933             more_experienced(1, 0);
1934             newexplevel();
1935             /* not cannibalism, but we use similar criteria
1936                for deciding whether to be sickened by this meal */
1937             if (rn2(2) && !CANNIBAL_ALLOWED())
1938                 make_vomiting((long) rn1(g.context.victual.reqtime, 14),
1939                               FALSE);
1940         }
1941         break;
1942     case LEMBAS_WAFER:
1943         if (maybe_polyd(is_orc(g.youmonst.data), Race_if(PM_ORC))) {
1944             pline("%s", "!#?&* elf kibble!");
1945             break;
1946         } else if (maybe_polyd(is_elf(g.youmonst.data), Race_if(PM_ELF))) {
1947             pline("A little goes a long way.");
1948             break;
1949         }
1950         goto give_feedback;
1951     case MEATBALL:
1952     case MEAT_STICK:
1953     case HUGE_CHUNK_OF_MEAT:
1954     case MEAT_RING:
1955         goto give_feedback;
1956     case CLOVE_OF_GARLIC:
1957         if (is_undead(g.youmonst.data)) {
1958             make_vomiting((long) rn1(g.context.victual.reqtime, 5), FALSE);
1959             break;
1960         }
1961         /*FALLTHRU*/
1962     default:
1963         if (otmp->otyp == SLIME_MOLD && !otmp->cursed
1964             && otmp->spe == g.context.current_fruit) {
1965             pline("My, this is a %s %s!",
1966                   Hallucination ? "primo" : "yummy",
1967                   singular(otmp, xname));
1968         } else if (otmp->otyp == APPLE && otmp->cursed && !Sleep_resistance) {
1969             ; /* skip core joke; feedback deferred til fpostfx() */
1970 
1971 #if defined(MAC) || defined(MACOSX)
1972         /* KMH -- Why should Unix have all the fun?
1973            We check MACOSX before UNIX to get the Apple-specific apple
1974            message; the '#if UNIX' code will still kick in for pear. */
1975         } else if (otmp->otyp == APPLE) {
1976             pline("Delicious!  Must be a Macintosh!");
1977 #endif
1978 
1979 #ifdef UNIX
1980         } else if (otmp->otyp == APPLE || otmp->otyp == PEAR) {
1981             if (!Hallucination) {
1982                 pline("Core dumped.");
1983             } else {
1984                 /* based on an old Usenet joke, a fake a.out manual page */
1985                 int x = rnd(100);
1986 
1987                 pline("%s -- core dumped.",
1988                       (x <= 75)
1989                          ? "Segmentation fault"
1990                          : (x <= 99)
1991                             ? "Bus error"
1992                             : "Yo' mama");
1993             }
1994 #endif
1995         } else if (otmp->otyp == EGG && stale_egg(otmp)) {
1996             pline("Ugh.  Rotten egg."); /* perhaps others like it */
1997             /* increasing existing nausea means that it will take longer
1998                before eventual vomit, but also means that constitution
1999                will be abused more times before illness completes */
2000             make_vomiting((Vomiting & TIMEOUT) + (long) d(10, 4), TRUE);
2001         } else {
2002  give_feedback:
2003             pline("This %s is %s", singular(otmp, xname),
2004                   otmp->cursed
2005                      ? (Hallucination ? "grody!" : "terrible!")
2006                      : (otmp->otyp == CRAM_RATION
2007                         || otmp->otyp == K_RATION
2008                         || otmp->otyp == C_RATION)
2009                         ? "bland."
2010                         : Hallucination ? "gnarly!" : "delicious!");
2011         }
2012         break; /* default */
2013     } /* switch */
2014 }
2015 
2016 /* increment a combat intrinsic with limits on its growth */
2017 static int
bounded_increase(int old,int inc,int typ)2018 bounded_increase(int old, int inc, int typ)
2019 {
2020     int absold, absinc, sgnold, sgninc;
2021 
2022     /* don't include any amount coming from worn rings (caller handles
2023        'protection' differently) */
2024     if (uright && uright->otyp == typ && typ != RIN_PROTECTION)
2025         old -= uright->spe;
2026     if (uleft && uleft->otyp == typ && typ != RIN_PROTECTION)
2027         old -= uleft->spe;
2028     absold = abs(old), absinc = abs(inc);
2029     sgnold = sgn(old), sgninc = sgn(inc);
2030 
2031     if (absinc == 0 || sgnold != sgninc || absold + absinc < 10) {
2032         ; /* use inc as-is */
2033     } else if (absold + absinc < 20) {
2034         absinc = rnd(absinc); /* 1..n */
2035         if (absold + absinc < 10)
2036             absinc = 10 - absold;
2037         inc = sgninc * absinc;
2038     } else if (absold + absinc < 40) {
2039         absinc = rn2(absinc) ? 1 : 0;
2040         if (absold + absinc < 20)
2041             absinc = rnd(20 - absold);
2042         inc = sgninc * absinc;
2043     } else {
2044         inc = 0; /* no further increase allowed via this method */
2045     }
2046     /* put amount from worn rings back */
2047     if (uright && uright->otyp == typ && typ != RIN_PROTECTION)
2048         old += uright->spe;
2049     if (uleft && uleft->otyp == typ && typ != RIN_PROTECTION)
2050         old += uleft->spe;
2051     return old + inc;
2052 }
2053 
2054 static void
accessory_has_effect(struct obj * otmp)2055 accessory_has_effect(struct obj *otmp)
2056 {
2057     pline("Magic spreads through your body as you digest the %s.",
2058           (otmp->oclass == RING_CLASS) ? "ring" : "amulet");
2059 }
2060 
2061 static void
eataccessory(struct obj * otmp)2062 eataccessory(struct obj *otmp)
2063 {
2064     int typ = otmp->otyp;
2065     long oldprop;
2066 
2067     /* Note: rings are not so common that this is unbalancing. */
2068     /* (How often do you even _find_ 3 rings of polymorph in a game?) */
2069     oldprop = u.uprops[objects[typ].oc_oprop].intrinsic;
2070     if (otmp == uleft || otmp == uright) {
2071         Ring_gone(otmp);
2072         if (u.uhp <= 0)
2073             return; /* died from sink fall */
2074     }
2075     otmp->known = otmp->dknown = 1; /* by taste */
2076     if (!rn2(otmp->oclass == RING_CLASS ? 3 : 5)) {
2077         switch (otmp->otyp) {
2078         default:
2079             if (!objects[typ].oc_oprop)
2080                 break; /* should never happen */
2081 
2082             if (!(u.uprops[objects[typ].oc_oprop].intrinsic & FROMOUTSIDE))
2083                 accessory_has_effect(otmp);
2084 
2085             /* cases where we want to have effects but not grant a permanent
2086              * intrinsic */
2087             if (typ != RIN_SEE_INVISIBLE && typ != RIN_INVISIBILITY) {
2088                 u.uprops[objects[typ].oc_oprop].intrinsic |= FROMOUTSIDE;
2089             }
2090 
2091             switch (typ) {
2092             case RIN_SEE_INVISIBLE:
2093                 incr_itimeout(&HSee_invisible, rnd(200) + 600);
2094                 set_mimic_blocking();
2095                 see_monsters();
2096                 if (Invis && !oldprop && !ESee_invisible
2097                     && !perceives(g.youmonst.data) && !Blind) {
2098                     newsym(u.ux, u.uy);
2099                     pline("Suddenly you can see yourself.");
2100                     makeknown(typ);
2101                 }
2102                 break;
2103             case RIN_INVISIBILITY:
2104                 incr_itimeout(&HInvis, rnd(200) + 600);
2105                 if (!oldprop && !EInvis && !BInvis && !See_invisible
2106                     && !Blind) {
2107                     newsym(u.ux, u.uy);
2108                     Your("body takes on a %s transparency...",
2109                          Hallucination ? "normal" : "strange");
2110                     makeknown(typ);
2111                 }
2112                 break;
2113             case RIN_PROTECTION_FROM_SHAPE_CHAN:
2114                 rescham();
2115                 break;
2116             case RIN_LEVITATION:
2117                 /* undo the `.intrinsic |= FROMOUTSIDE' done above */
2118                 u.uprops[LEVITATION].intrinsic = oldprop;
2119                 if (!Levitation) {
2120                     float_up();
2121                     incr_itimeout(&HLevitation, d(10, 20));
2122                     makeknown(typ);
2123                 }
2124                 break;
2125             } /* inner switch */
2126             break; /* default case of outer switch */
2127 
2128         case RIN_ADORNMENT:
2129             accessory_has_effect(otmp);
2130             if (adjattrib(A_CHA, otmp->spe, AA_CONDMSG) == AA_CURRCHNG)
2131                 makeknown(typ);
2132             break;
2133         case RIN_GAIN_STRENGTH:
2134             accessory_has_effect(otmp);
2135             if (adjattrib(A_STR, otmp->spe, AA_CONDMSG) == AA_CURRCHNG)
2136                 makeknown(typ);
2137             break;
2138         case RIN_GAIN_CONSTITUTION:
2139             accessory_has_effect(otmp);
2140             if (adjattrib(A_CON, otmp->spe, AA_CONDMSG) == AA_CURRCHNG)
2141                 makeknown(typ);
2142             break;
2143         case RIN_INCREASE_ACCURACY:
2144             accessory_has_effect(otmp);
2145             u.uhitinc = (schar) bounded_increase((int) u.uhitinc, otmp->spe,
2146                                                  RIN_INCREASE_ACCURACY);
2147             break;
2148         case RIN_INCREASE_DAMAGE:
2149             accessory_has_effect(otmp);
2150             u.udaminc = (schar) bounded_increase((int) u.udaminc, otmp->spe,
2151                                                  RIN_INCREASE_DAMAGE);
2152             break;
2153         case RIN_PROTECTION:
2154         case AMULET_OF_GUARDING:
2155             accessory_has_effect(otmp);
2156             HProtection |= FROMOUTSIDE;
2157             u.ublessed = bounded_increase(u.ublessed,
2158                                           (typ == RIN_PROTECTION) ? otmp->spe
2159                                            : 2, /* fixed amount for amulet */
2160                                           typ);
2161             g.context.botl = 1;
2162             break;
2163         case RIN_FREE_ACTION:
2164             /* Give sleep resistance instead */
2165             if (!(HSleep_resistance & FROMOUTSIDE))
2166                 accessory_has_effect(otmp);
2167             if (!Sleep_resistance)
2168                 You_feel("wide awake.");
2169             HSleep_resistance |= FROMOUTSIDE;
2170             break;
2171         case AMULET_OF_CHANGE:
2172             accessory_has_effect(otmp);
2173             makeknown(typ);
2174             change_sex();
2175             You("are suddenly very %s!",
2176                 flags.female ? "feminine" : "masculine");
2177             g.context.botl = 1;
2178             break;
2179         case AMULET_OF_UNCHANGING:
2180             /* un-change: it's a pun */
2181             if (!Unchanging && Upolyd) {
2182                 accessory_has_effect(otmp);
2183                 makeknown(typ);
2184                 rehumanize();
2185             }
2186             break;
2187         case AMULET_OF_STRANGULATION: /* bad idea! */
2188             /* no message--this gives no permanent effect */
2189             choke(otmp);
2190             break;
2191         case AMULET_OF_RESTFUL_SLEEP: { /* another bad idea! */
2192             long newnap = (long) rnd(100), oldnap = (HSleepy & TIMEOUT);
2193 
2194             if (!(HSleepy & FROMOUTSIDE))
2195                 accessory_has_effect(otmp);
2196             HSleepy |= FROMOUTSIDE;
2197             /* might also be wearing one; use shorter of two timeouts */
2198             if (newnap < oldnap || oldnap == 0L)
2199                 HSleepy = (HSleepy & ~TIMEOUT) | newnap;
2200             break;
2201         }
2202         case RIN_SUSTAIN_ABILITY:
2203         case AMULET_OF_LIFE_SAVING:
2204         case AMULET_OF_FLYING:
2205         case AMULET_OF_REFLECTION: /* nice try */
2206             /* can't eat Amulet of Yendor or fakes,
2207              * and no oc_prop even if you could -3.
2208              */
2209             break;
2210         }
2211     }
2212 }
2213 
2214 /* called after eating non-food */
2215 static void
eatspecial(void)2216 eatspecial(void)
2217 {
2218     struct obj *otmp = g.context.victual.piece;
2219 
2220     /* lesshungry wants an occupation to handle choke messages correctly */
2221     set_occupation(eatfood, "eating non-food", 0);
2222     lesshungry(g.context.victual.nmod);
2223     g.occupation = 0;
2224     g.context.victual.piece = (struct obj *) 0;
2225     g.context.victual.o_id = 0;
2226     g.context.victual.eating = 0;
2227     if (otmp->oclass == COIN_CLASS) {
2228         if (carried(otmp))
2229             useupall(otmp);
2230         else
2231             useupf(otmp, otmp->quan);
2232         vault_gd_watching(GD_EATGOLD);
2233         return;
2234     }
2235     if (otmp->material == PAPER) {
2236 #ifdef MAIL_STRUCTURES
2237         if (otmp->otyp == SCR_MAIL)
2238             /* no nutrition */
2239             pline("This junk mail is less than satisfying.");
2240         else
2241 #endif
2242         if (otmp->otyp == SCR_SCARE_MONSTER)
2243             /* to eat scroll, hero is currently polymorphed into a monster */
2244             pline("Yuck%c", otmp->blessed ? '!' : '.');
2245         else if (otmp->oclass == SCROLL_CLASS && objdescr_is(otmp, "YUM YUM"))
2246                  /* check description after checking for specific scrolls */
2247             pline("Yum%c", otmp->blessed ? '!' : '.');
2248         else
2249             pline("Needs salt...");
2250     }
2251     if (otmp->oclass == POTION_CLASS) {
2252         otmp->quan++; /* dopotion() does a useup() */
2253         (void) dopotion(otmp);
2254     } else if (otmp->oclass == RING_CLASS || otmp->oclass == AMULET_CLASS) {
2255         eataccessory(otmp);
2256     } else if (otmp->otyp == LEASH && otmp->leashmon) {
2257         o_unleash(otmp);
2258     }
2259 
2260     /* KMH -- idea by "Tommy the Terrorist" */
2261     if (otmp->otyp == TRIDENT && !otmp->cursed) {
2262         /* sugarless chewing gum which used to be heavily advertised on TV */
2263         pline(Hallucination ? "Four out of five dentists agree."
2264                             : "That was pure chewing satisfaction!");
2265         exercise(A_WIS, TRUE);
2266     }
2267     if (otmp->otyp == FLINT && !otmp->cursed) {
2268         /* chewable vitamin for kids based on "The Flintstones" TV cartoon */
2269         pline("Yabba-dabba delicious!");
2270         exercise(A_CON, TRUE);
2271     }
2272 
2273     if (otmp == uwep && otmp->quan == 1L)
2274         uwepgone();
2275     if (otmp == uquiver && otmp->quan == 1L)
2276         uqwepgone();
2277     if (otmp == uswapwep && otmp->quan == 1L)
2278         uswapwepgone();
2279 
2280     if (otmp == uball)
2281         unpunish();
2282     if (otmp == uchain)
2283         unpunish(); /* but no useup() */
2284     else if (carried(otmp))
2285         useup(otmp);
2286     else
2287         useupf(otmp, 1L);
2288 }
2289 
2290 /* NOTE: the order of these words exactly corresponds to the
2291    order of oc_material values #define'd in objclass.h. */
2292 static const char *foodwords[] = {
2293     "meal",    "liquid",  "wax",       "food", "meat",     "paper",
2294     "cloth",   "leather", "wood",      "bone", "scale",    "metal",
2295     "metal",   "metal",   "silver",    "gold", "platinum", "mithril",
2296     "plastic", "glass",   "rich food", "stone"
2297 };
2298 
2299 static const char *
foodword(struct obj * otmp)2300 foodword(struct obj *otmp)
2301 {
2302     if (otmp->oclass == FOOD_CLASS)
2303         return "food";
2304     if (is_worthless_glass(otmp) && otmp->dknown)
2305         makeknown(otmp->otyp);
2306     return foodwords[otmp->material];
2307 }
2308 
2309 /* called after consuming (non-corpse) food */
2310 static void
fpostfx(struct obj * otmp)2311 fpostfx(struct obj *otmp)
2312 {
2313     switch (otmp->otyp) {
2314     case SPRIG_OF_WOLFSBANE:
2315         if (u.ulycn >= LOW_PM || is_were(g.youmonst.data))
2316             you_unwere(TRUE);
2317         break;
2318     case CARROT:
2319         if (!u.uswallow
2320             || !attacktype_fordmg(u.ustuck->data, AT_ENGL, AD_BLND))
2321             make_blinded((long) u.ucreamed, TRUE);
2322         break;
2323     case FORTUNE_COOKIE:
2324         outrumor(bcsign(otmp), BY_COOKIE);
2325         if (!Blind)
2326             if (!u.uconduct.literate++)
2327                 livelog_write_string(LL_CONDUCT, "became literate by reading the fortune inside a cookie");
2328         break;
2329     case LUMP_OF_ROYAL_JELLY: {
2330         /* This stuff seems to be VERY healthy! */
2331         gainstr(otmp, 1, TRUE); /* will -1 if cursed */
2332         if (otmp->cursed) {
2333             losehp(rnd(20), "rotten lump of royal jelly", KILLED_BY_AN);
2334         }
2335         else {
2336             int *hp = Upolyd ? &u.mh : &u.uhp;
2337             int *hpmax = Upolyd ? &u.mhmax : &u.uhpmax;
2338             *hp += rnd(20);
2339             if (*hp > *hpmax) {
2340                 if (!rn2(17))
2341                     (*hpmax)++;
2342                 *hp = *hpmax;
2343             }
2344             heal_legs(0);
2345         }
2346         break;
2347     }
2348     case EGG:
2349         if (flesh_petrifies(&mons[otmp->corpsenm])) {
2350             if (!Stone_resistance
2351                 && !(poly_when_stoned(g.youmonst.data)
2352                      && polymon(PM_STONE_GOLEM, POLYMON_ALL_MSGS))) {
2353                 if (!Stoned) {
2354                     Sprintf(g.killer.name, "%s egg",
2355                             mons[otmp->corpsenm].pmnames[NEUTRAL]);
2356                     make_stoned(5L, (char *) 0, KILLED_BY_AN, g.killer.name);
2357                 }
2358             }
2359             /* note: no "tastes like chicken" message for eggs */
2360         }
2361         break;
2362     case EUCALYPTUS_LEAF:
2363         if (Sick && !otmp->cursed)
2364             make_sick(0L, (char *) 0, TRUE, SICK_ALL);
2365         if (Vomiting && !otmp->cursed)
2366             make_vomiting(0L, TRUE);
2367         break;
2368     case APPLE:
2369         if (otmp->cursed && !Sleep_resistance) {
2370             /* Snow White; 'poisoned' applies to [a subset of] weapons,
2371                not food, so we substitute cursed; fortunately our hero
2372                won't have to wait for a prince to be rescued/revived */
2373             if (Race_if(PM_DWARF) && Hallucination)
2374                 verbalize("Heigh-ho, ho-hum, I think I'll skip work today.");
2375             else if (Deaf || !flags.acoustics)
2376                 You("fall asleep.");
2377             else
2378                 You_hear("sinister laughter as you fall asleep...");
2379             fall_asleep(-rn1(11, 20), TRUE);
2380         }
2381         break;
2382     case CANDY_BAR:
2383         /* "rare candy" - Pokemon */
2384         if (!rn2(100)) {
2385             pluslvl(FALSE);
2386         }
2387     }
2388     return;
2389 }
2390 
2391 #if 0
2392 /* intended for eating a spellbook while polymorphed, but not used;
2393    "leather" applied to appearance, not composition, and has been
2394    changed to "leathery" to reflect that */
2395 static boolean leather_cover(struct obj *);
2396 
2397 static boolean
2398 leather_cover(struct obj *otmp)
2399 {
2400     const char *odesc = OBJ_DESCR(objects[otmp->otyp]);
2401 
2402     if (odesc && (otmp->oclass == SPBOOK_CLASS)) {
2403         if (!strcmp(odesc, "leather"))
2404             return TRUE;
2405     }
2406     return FALSE;
2407 }
2408 #endif
2409 
2410 /*
2411  * return 0 if the food was not dangerous.
2412  * return 1 if the food was dangerous and you chose to stop.
2413  * return 2 if the food was dangerous and you chose to eat it anyway.
2414  */
2415 static int
edibility_prompts(struct obj * otmp)2416 edibility_prompts(struct obj *otmp)
2417 {
2418     /* Blessed food detection grants hero a one-use
2419      * ability to detect food that is unfit for consumption
2420      * or dangerous and avoid it.
2421      */
2422     char buf[BUFSZ], foodsmell[BUFSZ],
2423          it_or_they[QBUFSZ], eat_it_anyway[QBUFSZ];
2424     boolean cadaver = (otmp->otyp == CORPSE || otmp->globby),
2425             stoneorslime = FALSE;
2426     int material = otmp->material, mnum = otmp->corpsenm;
2427     long rotted = 0L;
2428 
2429     Strcpy(foodsmell, Tobjnam(otmp, "smell"));
2430     Strcpy(it_or_they, (otmp->quan == 1L) ? "it" : "they");
2431     Sprintf(eat_it_anyway, "Eat %s anyway?",
2432             (otmp->quan == 1L) ? "it" : "one");
2433 
2434     if (cadaver || otmp->otyp == EGG || otmp->otyp == TIN) {
2435         /* These checks must match those in eatcorpse() */
2436         stoneorslime = (flesh_petrifies(&mons[mnum]) && !Stone_resistance
2437                         && !poly_when_stoned(g.youmonst.data));
2438 
2439         if (mnum == PM_GREEN_SLIME || otmp->otyp == GLOB_OF_GREEN_SLIME)
2440             stoneorslime = (!Unchanging && !slimeproof(g.youmonst.data));
2441 
2442         if (cadaver && !nonrotting_corpse(mnum)) {
2443             long age = peek_at_iced_corpse_age(otmp);
2444 
2445             /* worst case rather than random
2446                in this calculation to force prompt */
2447             rotted = (g.monstermoves - age) / (10L + 0 /* was rn2(20) */);
2448             if (otmp->cursed)
2449                 rotted += 2L;
2450             else if (otmp->blessed)
2451                 rotted -= 2L;
2452         }
2453     }
2454 
2455     /*
2456      * These problems with food should be checked in
2457      * order from most detrimental to least detrimental.
2458      */
2459     if (cadaver && mnum != PM_ACID_BLOB && rotted > 5L && !Sick_resistance) {
2460         /* Tainted meat */
2461         Snprintf(buf, sizeof(buf), "%s like %s could be tainted!  %s",
2462                  foodsmell, it_or_they, eat_it_anyway);
2463         if (yn_function(buf, ynchars, 'n') == 'n')
2464             return 1;
2465         else
2466             return 2;
2467     }
2468     if (stoneorslime) {
2469         Snprintf(buf, sizeof(buf),
2470                  "%s like %s could be something very dangerous!  %s",
2471                 foodsmell, it_or_they, eat_it_anyway);
2472         if (yn_function(buf, ynchars, 'n') == 'n')
2473             return 1;
2474         else
2475             return 2;
2476     }
2477     if (otmp->orotten || (cadaver && rotted > 3L)) {
2478         /* Rotten */
2479         Snprintf(buf, sizeof(buf), "%s like %s could be rotten! %s",
2480                  foodsmell, it_or_they, eat_it_anyway);
2481         if (yn_function(buf, ynchars, 'n') == 'n')
2482             return 1;
2483         else
2484             return 2;
2485     }
2486     if (cadaver && poisonous(&mons[mnum]) && !Poison_resistance) {
2487         /* poisonous */
2488         Snprintf(buf, sizeof(buf), "%s like %s might be poisonous!  %s",
2489                  foodsmell, it_or_they, eat_it_anyway);
2490         if (yn_function(buf, ynchars, 'n') == 'n')
2491             return 1;
2492         else
2493             return 2;
2494     }
2495     if (otmp->otyp == APPLE && otmp->cursed && !Sleep_resistance) {
2496         /* causes sleep, for long enough to be dangerous */
2497         Snprintf(buf, sizeof(buf), "%s like %s might have been poisoned.  %s",
2498                  foodsmell, it_or_they, eat_it_anyway);
2499         return (yn_function(buf, ynchars, 'n') == 'n') ? 1 : 2;
2500     }
2501     if (cadaver && !vegetarian(&mons[mnum]) && !u.uconduct.unvegetarian
2502         && Role_if(PM_MONK)) {
2503         Snprintf(buf, sizeof(buf), "%s unhealthy.  %s", foodsmell,
2504                  eat_it_anyway);
2505         if (yn_function(buf, ynchars, 'n') == 'n')
2506             return 1;
2507         else
2508             return 2;
2509     }
2510     if (cadaver && acidic(&mons[mnum]) && !Acid_resistance) {
2511         Snprintf(buf, sizeof(buf), "%s rather acidic.  %s",
2512                  foodsmell, eat_it_anyway);
2513         if (yn_function(buf, ynchars, 'n') == 'n')
2514             return 1;
2515         else
2516             return 2;
2517     }
2518     if (Upolyd && u.umonnum == PM_RUST_MONSTER && is_metallic(otmp)
2519         && otmp->oerodeproof) {
2520         Snprintf(buf, sizeof(buf), "%s disgusting to you right now.  %s",
2521                  foodsmell, eat_it_anyway);
2522         if (yn_function(buf, ynchars, 'n') == 'n')
2523             return 1;
2524         else
2525             return 2;
2526     }
2527 
2528     /*
2529      * Breaks conduct, but otherwise safe.
2530      */
2531     if (!u.uconduct.unvegan
2532         && ((material == LEATHER || material == BONE
2533              || material == DRAGON_HIDE || material == WAX)
2534             || (cadaver && !vegan(&mons[mnum])))) {
2535         Snprintf(buf, sizeof(buf), "%s foul and unfamiliar to you.  %s",
2536                  foodsmell, eat_it_anyway);
2537         if (yn_function(buf, ynchars, 'n') == 'n')
2538             return 1;
2539         else
2540             return 2;
2541     }
2542     if (!u.uconduct.unvegetarian
2543         && ((material == LEATHER || material == BONE
2544              || material == DRAGON_HIDE)
2545             || (cadaver && !vegetarian(&mons[mnum])))) {
2546         Snprintf(buf, sizeof(buf), "%s unfamiliar to you.  %s",
2547                  foodsmell, eat_it_anyway);
2548         if (yn_function(buf, ynchars, 'n') == 'n')
2549             return 1;
2550         else
2551             return 2;
2552     }
2553 
2554     if (cadaver && mnum != PM_ACID_BLOB && rotted > 5L && Sick_resistance) {
2555         /* Tainted meat with Sick_resistance */
2556         Snprintf(buf, sizeof(buf), "%s like %s could be tainted!  %s",
2557                 foodsmell, it_or_they, eat_it_anyway);
2558         if (yn_function(buf, ynchars, 'n') == 'n')
2559             return 1;
2560         else
2561             return 2;
2562     }
2563     return 0;
2564 }
2565 
2566 /* 'e' command */
2567 int
doeat(void)2568 doeat(void)
2569 {
2570     struct obj *otmp;
2571     int basenutrit; /* nutrition of full item */
2572     boolean dont_start = FALSE, nodelicious = FALSE,
2573             already_partly_eaten;
2574     int ll_conduct = 0; /* livelog hardest conduct food>vegn>vegt */
2575 
2576     if (Strangled) {
2577         pline("If you can't breathe air, how can you consume solids?");
2578         return 0;
2579     }
2580     if (!(otmp = floorfood("eat", 0)))
2581         return 0;
2582     if (check_capacity((char *) 0))
2583         return 0;
2584 
2585     if (u.uedibility) {
2586         int res = edibility_prompts(otmp);
2587 
2588         if (res) {
2589             Your(
2590                "%s stops tingling and your sense of smell returns to normal.",
2591                  body_part(NOSE));
2592             u.uedibility = 0;
2593             if (res == 1)
2594                 return 0;
2595         }
2596     }
2597 
2598     /* from floorfood(), &zeroobj means iron bars at current spot */
2599     if (otmp == &cg.zeroobj) {
2600         /* hero in metallivore form is eating [diggable] iron bars
2601            at current location so skip the other assorted checks;
2602            operates as if digging rather than via the eat occupation */
2603         if (still_chewing(u.ux, u.uy) && levl[u.ux][u.uy].typ == IRONBARS) {
2604             /* this is verbose, but player will see the hero rather than the
2605                bars so wouldn't know that more turns of eating are required */
2606             You("pause to swallow.");
2607         }
2608         return 1;
2609     }
2610     /* We have to make non-foods take 1 move to eat, unless we want to
2611      * do ridiculous amounts of coding to deal with partly eaten plate
2612      * mails, players who polymorph back to human in the middle of their
2613      * metallic meal, etc....
2614      */
2615     if (!is_edible(otmp)) {
2616         You("cannot eat that!");
2617         return 0;
2618     } else if ((otmp->owornmask & (W_ARMOR | W_TOOL | W_AMUL | W_SADDLE))
2619                != 0) {
2620         /* let them eat rings */
2621         You_cant("eat %s you're wearing.", something);
2622         return 0;
2623     } else if (!(carried(otmp) ? retouch_object(&otmp, FALSE, FALSE)
2624                                : touch_artifact(otmp, &g.youmonst))) {
2625         return 1; /* got blasted so use a turn */
2626     }
2627     if (is_metallic(otmp) && u.umonnum == PM_RUST_MONSTER
2628         && otmp->oerodeproof) {
2629         otmp->rknown = TRUE;
2630         if (otmp->quan > 1L) {
2631             if (!carried(otmp))
2632                 (void) splitobj(otmp, otmp->quan - 1L);
2633             else
2634                 otmp = splitobj(otmp, 1L);
2635         }
2636         pline("Ulch - that %s was rustproofed!", xname(otmp));
2637         /* The regurgitated object's rustproofing is gone now */
2638         otmp->oerodeproof = 0;
2639         make_stunned((HStun & TIMEOUT) + (long) rn2(10), TRUE);
2640         /*
2641          * We don't expect rust monsters to be wielding welded weapons
2642          * or wearing cursed rings which were rustproofed, but guard
2643          * against the possibility just in case.
2644          */
2645         if (welded(otmp) || (otmp->cursed && (otmp->owornmask & W_RING))) {
2646             set_bknown(otmp, 1); /* for ring; welded() does this for weapon */
2647             You("spit out %s.", the(xname(otmp)));
2648         } else {
2649             You("spit %s out onto the %s.", the(xname(otmp)),
2650                 surface(u.ux, u.uy));
2651             if (carried(otmp)) {
2652                 /* no need to check for leash in use; it's not metallic */
2653                 if (otmp->owornmask)
2654                     remove_worn_item(otmp, FALSE);
2655                 freeinv(otmp);
2656                 dropy(otmp);
2657             }
2658             stackobj(otmp);
2659         }
2660         return 1;
2661     }
2662     /* KMH -- Slow digestion is... indigestible */
2663     if (otmp->otyp == RIN_SLOW_DIGESTION) {
2664         pline("This ring is indigestible!");
2665         if (otmp->dknown && !objects[otmp->otyp].oc_name_known
2666             && !objects[otmp->otyp].oc_uname)
2667             docall(otmp);
2668         return 1;
2669     }
2670     if (otmp->oclass != FOOD_CLASS) {
2671         int material;
2672 
2673         g.context.victual.reqtime = 1;
2674         g.context.victual.piece = otmp;
2675         g.context.victual.o_id = otmp->o_id;
2676         /* Don't split it, we don't need to if it's 1 move */
2677         g.context.victual.usedtime = 0;
2678         g.context.victual.canchoke = (u.uhs == SATIATED);
2679         /* Note: gold weighs 1 pt. for each 1000 pieces (see
2680            pickup.c) so gold and non-gold is consistent. */
2681         if (otmp->oclass == COIN_CLASS)
2682             basenutrit = ((otmp->quan > 200000L)
2683                              ? 2000
2684                              : (int) (otmp->quan / 100L));
2685         else if (otmp->oclass == BALL_CLASS || otmp->oclass == CHAIN_CLASS)
2686             basenutrit = weight(otmp);
2687         /* oc_nutrition is usually weight anyway */
2688         else
2689             basenutrit = objects[otmp->otyp].oc_nutrition;
2690 #ifdef MAIL_STRUCTURES
2691         if (otmp->otyp == SCR_MAIL) {
2692             basenutrit = 0;
2693             nodelicious = TRUE;
2694         }
2695 #endif
2696         g.context.victual.nmod = basenutrit;
2697         g.context.victual.eating = TRUE; /* needed for lesshungry() */
2698 
2699         if (!u.uconduct.food++) {
2700             ll_conduct++;
2701             livelog_printf(LL_CONDUCT, "ate for the first time (%s)",
2702                            food_xname(otmp, FALSE));
2703         }
2704         material = otmp->material;
2705         if (material == LEATHER || material == BONE || material == WAX
2706             || material == DRAGON_HIDE) {
2707             if (!u.uconduct.unvegan++ && !ll_conduct) {
2708                 livelog_printf(LL_CONDUCT, "consumed animal products for the first time, by eating %s",
2709                                an(food_xname(otmp, FALSE)));
2710                 ll_conduct++;
2711             }
2712             if (material != WAX && !u.uconduct.unvegetarian && !ll_conduct)
2713                 livelog_printf(LL_CONDUCT, "tasted meat for the first time, by eating %s",
2714                                an(food_xname(otmp, FALSE)));
2715             violated_vegetarian();
2716         }
2717 
2718         if (otmp->cursed) {
2719             (void) rottenfood(otmp);
2720             nodelicious = TRUE;
2721         } else if (otmp->material == PAPER)
2722             nodelicious = TRUE;
2723 
2724         if (otmp->oclass == WEAPON_CLASS && otmp->opoisoned) {
2725             pline("Ecch - that must have been poisonous!");
2726             if (!Poison_resistance) {
2727                 losestr(rnd(4));
2728                 losehp(rnd(15), xname(otmp), KILLED_BY_AN);
2729             } else
2730                 You("seem unaffected by the poison.");
2731         } else if (!nodelicious) {
2732             pline("%s%s is delicious!",
2733                   (obj_is_pname(otmp) && !arti_starts_with_the(otmp))
2734                       ? ""
2735                       : "This ",
2736                   (otmp->oclass == COIN_CLASS)
2737                       ? foodword(otmp)
2738                       : singular(otmp, xname));
2739         }
2740         eatspecial();
2741         return 1;
2742     }
2743 
2744     if (otmp == g.context.victual.piece) {
2745         boolean one_bite_left
2746             = (g.context.victual.usedtime + 1 >= g.context.victual.reqtime);
2747 
2748         /* If they weren't able to choke, they don't suddenly become able to
2749          * choke just because they were interrupted.  On the other hand, if
2750          * they were able to choke before, if they lost food it's possible
2751          * they shouldn't be able to choke now.
2752          */
2753         if (u.uhs != SATIATED)
2754             g.context.victual.canchoke = FALSE;
2755         g.context.victual.o_id = 0;
2756         g.context.victual.piece = touchfood(otmp);
2757         if (g.context.victual.piece)
2758             g.context.victual.o_id = g.context.victual.piece->o_id;
2759         /* if there's only one bite left, there sometimes won't be any
2760            "you finish eating" message when done; use different wording
2761            for resuming with one bite remaining instead of trying to
2762            determine whether or not "you finish" is going to be given */
2763         You("%s your meal.",
2764             !one_bite_left ? "resume" : "consume the last bite of");
2765         start_eating(g.context.victual.piece, FALSE);
2766         return 1;
2767     }
2768 
2769     /* nothing in progress - so try to find something. */
2770     /* tins are a special case */
2771     /* tins must also check conduct separately in case they're discarded */
2772     if (otmp->otyp == TIN) {
2773         start_tin(otmp);
2774         return 1;
2775     }
2776 
2777     /* KMH, conduct */
2778     if (!u.uconduct.food++) {
2779         livelog_printf(LL_CONDUCT, "ate for the first time - %s", food_xname(otmp, FALSE));
2780         ll_conduct++;
2781     }
2782 
2783     already_partly_eaten = otmp->oeaten ? TRUE : FALSE;
2784     g.context.victual.piece = otmp = touchfood(otmp);
2785     if (g.context.victual.piece)
2786         g.context.victual.o_id = g.context.victual.piece->o_id;
2787     g.context.victual.usedtime = 0;
2788 
2789     /* Now we need to calculate delay and nutritional info.
2790      * The base nutrition calculated here and in eatcorpse() accounts
2791      * for normal vs. rotten food.  The reqtime and nutrit values are
2792      * then adjusted in accordance with the amount of food left.
2793      */
2794     if (otmp->otyp == CORPSE || otmp->globby) {
2795         int tmp = eatcorpse(otmp);
2796 
2797         if (tmp == 2) {
2798             /* used up */
2799             g.context.victual.piece = (struct obj *) 0;
2800             g.context.victual.o_id = 0;
2801             return 1;
2802         } else if (tmp)
2803             dont_start = TRUE;
2804         /* if not used up, eatcorpse sets up reqtime and may modify oeaten */
2805     } else {
2806         /* No checks for WAX, LEATHER, BONE, DRAGON_HIDE.  These are
2807          * all handled in the != FOOD_CLASS case, above.
2808          */
2809         if (otmp->material == FLESH) {
2810             if (!u.uconduct.unvegan++ && !ll_conduct) {
2811                 ll_conduct++;
2812                 livelog_printf(LL_CONDUCT,
2813                                "consumed animal products for the first time, by eating %s",
2814                                an(food_xname(otmp,FALSE)));
2815             }
2816             if (otmp->otyp != EGG) {
2817                 if (!u.uconduct.unvegetarian && !ll_conduct)
2818                     livelog_printf(LL_CONDUCT,
2819                                    "tasted meat for the first time, by eating %s",
2820                                    an(food_xname(otmp,FALSE)));
2821                 violated_vegetarian();
2822             }
2823         }
2824         else if (otmp->otyp == PANCAKE || otmp->otyp == FORTUNE_COOKIE /*eggs*/
2825                  || otmp->otyp == CREAM_PIE || otmp->otyp == CANDY_BAR /*milk*/
2826                  || otmp->otyp == LUMP_OF_ROYAL_JELLY) {
2827             if (!u.uconduct.unvegan++ && !ll_conduct)
2828                 livelog_printf(LL_CONDUCT,
2829                                "consumed animal products (%s) for the first time",
2830                                food_xname(otmp,FALSE));
2831         }
2832 
2833         g.context.victual.reqtime = objects[otmp->otyp].oc_delay;
2834         if (otmp->otyp != FORTUNE_COOKIE && otmp->cursed
2835             && !nonrotting_food(otmp->otyp)) {
2836             if (rottenfood(otmp)) {
2837                 otmp->orotten = TRUE;
2838                 dont_start = TRUE;
2839             }
2840             consume_oeaten(otmp, 1); /* oeaten >>= 1 */
2841         } else if (!already_partly_eaten) {
2842             fprefx(otmp);
2843         } else {
2844             You("%s %s.",
2845                 (g.context.victual.reqtime == 1) ? "eat" : "begin eating",
2846                 doname(otmp));
2847         }
2848     }
2849 
2850     /* re-calc the nutrition */
2851     basenutrit = (int) obj_nutrition(otmp);
2852 
2853     debugpline3(
2854      "before rounddiv: victual.reqtime == %d, oeaten == %d, basenutrit == %d",
2855                 g.context.victual.reqtime, otmp->oeaten, basenutrit);
2856 
2857     g.context.victual.reqtime = (basenutrit == 0) ? 0
2858         : rounddiv(g.context.victual.reqtime * (long) otmp->oeaten, basenutrit);
2859 
2860     debugpline1("after rounddiv: victual.reqtime == %d",
2861                 g.context.victual.reqtime);
2862     /*
2863      * calculate the modulo value (nutrit. units per round eating)
2864      * note: this isn't exact - you actually lose a little nutrition due
2865      *       to this method.
2866      * TODO: add in a "remainder" value to be given at the end of the meal.
2867      */
2868     if (g.context.victual.reqtime == 0 || otmp->oeaten == 0)
2869         /* possible if most has been eaten before */
2870         g.context.victual.nmod = 0;
2871     else if ((int) otmp->oeaten >= g.context.victual.reqtime)
2872         g.context.victual.nmod = -((int) otmp->oeaten
2873                                  / g.context.victual.reqtime);
2874     else
2875         g.context.victual.nmod = g.context.victual.reqtime % otmp->oeaten;
2876     g.context.victual.canchoke = (u.uhs == SATIATED);
2877 
2878     if (!dont_start)
2879         start_eating(otmp, already_partly_eaten);
2880     return 1;
2881 }
2882 
2883 /* getobj callback for object to be opened with a tin opener */
2884 static int
tinopen_ok(struct obj * obj)2885 tinopen_ok(struct obj *obj)
2886 {
2887     if (obj && obj->otyp == TIN)
2888         return GETOBJ_SUGGEST;
2889 
2890     return GETOBJ_EXCLUDE;
2891 }
2892 
2893 
2894 int
use_tin_opener(struct obj * obj)2895 use_tin_opener(struct obj *obj)
2896 {
2897     struct obj *otmp;
2898     int res = 0;
2899 
2900     if (!carrying(TIN)) {
2901         You("have no tin to open.");
2902         return 0;
2903     }
2904 
2905     if (obj != uwep) {
2906         if (obj->cursed && obj->bknown) {
2907             char qbuf[QBUFSZ];
2908 
2909             if (ynq(safe_qbuf(qbuf, "Really wield ", "?",
2910                               obj, doname, thesimpleoname, "that")) != 'y')
2911                 return 0;
2912         }
2913         if (!wield_tool(obj, "use"))
2914             return 0;
2915         res = 1;
2916     }
2917 
2918     otmp = getobj("open", tinopen_ok, GETOBJ_NOFLAGS);
2919     if (!otmp)
2920         return res;
2921 
2922     start_tin(otmp);
2923     return 1;
2924 }
2925 
2926 /* Take a single bite from a piece of food, checking for choking and
2927  * modifying usedtime.  Returns 1 if they choked and survived, 0 otherwise.
2928  */
2929 static int
bite(void)2930 bite(void)
2931 {
2932     if (g.context.victual.canchoke && u.uhunger >= 2000) {
2933         choke(g.context.victual.piece);
2934         return 1;
2935     }
2936     if (g.context.victual.doreset) {
2937         do_reset_eat();
2938         return 0;
2939     }
2940     g.force_save_hs = TRUE;
2941     if (g.context.victual.nmod < 0) {
2942         lesshungry(-g.context.victual.nmod);
2943         consume_oeaten(g.context.victual.piece,
2944                        g.context.victual.nmod); /* -= -nmod */
2945     } else if (g.context.victual.nmod > 0
2946                && (g.context.victual.usedtime % g.context.victual.nmod)) {
2947         lesshungry(1);
2948         consume_oeaten(g.context.victual.piece, -1); /* -= 1 */
2949     }
2950     g.force_save_hs = FALSE;
2951     recalc_wt();
2952     return 0;
2953 }
2954 
2955 /* as time goes by - called by moveloop(every move) & domove(melee attack) */
2956 void
gethungry(void)2957 gethungry(void)
2958 {
2959     int accessorytime;
2960 
2961     if (u.uinvulnerable)
2962         return; /* you don't feel hungrier */
2963 
2964     /* being polymorphed into a creature which doesn't eat prevents
2965        this first uhunger decrement, but to stay in such form the hero
2966        will need to wear an Amulet of Unchanging so still burn a small
2967        amount of nutrition in the 'moves % 20' ring/amulet check below */
2968     if ((!Unaware || !rn2(10)) /* slow metabolic rate while asleep */
2969         && (carnivorous(g.youmonst.data)
2970             || herbivorous(g.youmonst.data)
2971             || metallivorous(g.youmonst.data))
2972         && !Slow_digestion)
2973         u.uhunger--; /* ordinary food consumption */
2974 
2975     /*
2976      * 3.7:  trigger is randomized instead of (moves % N).  Makes
2977      * ring juggling (using the 'time' option to see the turn counter
2978      * in order to time swapping of a pair of rings of slow digestion,
2979      * wearing one on one hand, then putting on the other and taking
2980      * off the first, then vice versa, over and over and over and ...
2981      * to avoid any hunger from wearing a ring) become ineffective.
2982      * Also causes melee-induced hunger to vary from turn-based hunger
2983      * instead of just replicating that.
2984      */
2985     accessorytime = rn2(20); /* rn2(20) replaces (int) (g.moves % 20L) */
2986     if (accessorytime % 2) { /* odd */
2987         /* Regeneration uses up food, unless due to an artifact */
2988         if ((HRegeneration & ~FROMFORM)
2989             || (ERegeneration & ~(W_ARTI | W_WEP)))
2990             u.uhunger--;
2991         if (near_capacity() > SLT_ENCUMBER)
2992             u.uhunger--;
2993     } else { /* even */
2994         if (Hunger)
2995             u.uhunger--;
2996         /* Conflict uses up food too */
2997         if (HConflict || (EConflict & (~W_ARTI)))
2998             u.uhunger--;
2999         /*
3000          * +0 charged rings don't do anything, so don't affect hunger.
3001          * Slow digestion cancels movement and melee hunger but still
3002          * causes ring hunger.
3003          * Possessing the real Amulet imposes a separate hunger penalty
3004          * from wearing an amulet (so gets a double penalty when worn).
3005          *
3006          * 3.7.0:  Worn meat rings don't affect hunger.
3007          * Same with worn cheap plastic imitation of the Amulet.
3008          * +0 ring of protection might do something (enhanced "magical
3009          * cancellation") if hero doesn't have protection from some
3010          * other source (cloak or second ring).
3011          */
3012         switch (accessorytime) { /* note: use even cases among 0..19 only */
3013         case 4:
3014             if (uleft && uleft->otyp != MEAT_RING
3015                 /* more hungry if +/- is nonzero or +/- doesn't apply or
3016                    +0 ring of protection is only source of protection;
3017                    need to check whether both rings are +0 protection or
3018                    they'd both slip by the "is there another source?" test,
3019                    but don't do that for both rings or they will both be
3020                    treated as supplying "MC" when only one matters;
3021                    note: amulet of guarding overrides both +0 rings and
3022                    is caught by the (EProtection & ~W_RINGx) == 0L tests */
3023                 && (uleft->spe
3024                     || !objects[uleft->otyp].oc_charged
3025                     || (uleft->otyp == RIN_PROTECTION
3026                         && ((EProtection & ~W_RINGL) == 0L
3027                             || ((EProtection & ~W_RINGL) == W_RINGR
3028                                 && uright && uright->otyp == RIN_PROTECTION
3029                                 && !uright->spe)))))
3030                 u.uhunger--;
3031             break;
3032         case 8:
3033             if (uamul && uamul->otyp != FAKE_AMULET_OF_YENDOR)
3034                 u.uhunger--;
3035             break;
3036         case 12:
3037             if (uright && uright->otyp != MEAT_RING
3038                 && (uright->spe
3039                     || !objects[uright->otyp].oc_charged
3040                     || (uright->otyp == RIN_PROTECTION
3041                         && (EProtection & ~W_RINGR) == 0L)))
3042                 u.uhunger--;
3043             break;
3044         case 16:
3045             if (u.uhave.amulet)
3046                 u.uhunger--;
3047             break;
3048         default:
3049             break;
3050         }
3051     }
3052     newuhs(TRUE);
3053 }
3054 
3055 /* called after vomiting and after performing feats of magic */
3056 void
morehungry(int num)3057 morehungry(int num)
3058 {
3059     u.uhunger -= num;
3060     newuhs(TRUE);
3061 }
3062 
3063 /* called after eating (and after drinking fruit juice) */
3064 void
lesshungry(int num)3065 lesshungry(int num)
3066 {
3067     /* See comments in newuhs() for discussion on force_save_hs */
3068     boolean iseating = (g.occupation == eatfood) || g.force_save_hs;
3069 
3070     debugpline1("lesshungry(%d)", num);
3071     u.uhunger += num;
3072     if (u.uhunger >= 2000) {
3073         if (!iseating || g.context.victual.canchoke) {
3074             if (iseating) {
3075                 choke(g.context.victual.piece);
3076                 reset_eat();
3077             } else
3078                 choke(g.occupation == opentin ? g.context.tin.tin
3079                                             : (struct obj *) 0);
3080             /* no reset_eat() */
3081         }
3082     } else {
3083         /* Have lesshungry() report when you're nearly full so all eating
3084          * warns when you're about to choke.
3085          */
3086         if (u.uhunger >= 1500
3087             && (!g.context.victual.eating
3088                 || (g.context.victual.eating
3089                     && !g.context.victual.fullwarn))) {
3090             pline("You're having a hard time getting all of it down.");
3091             g.nomovemsg = "You're finally finished.";
3092             if (!g.context.victual.eating) {
3093                 g.multi = -2;
3094             } else {
3095                 g.context.victual.fullwarn = TRUE;
3096                 if (g.context.victual.canchoke
3097                     && g.context.victual.reqtime > 1) {
3098                     /* a one-gulp food will not survive a stop */
3099                     if (!paranoid_query(ParanoidEating, "Continue eating?")) {
3100                         reset_eat();
3101                         g.nomovemsg = (char *) 0;
3102                     }
3103                 }
3104             }
3105         }
3106     }
3107     newuhs(FALSE);
3108 }
3109 
3110 static int
unfaint(void)3111 unfaint(void)
3112 {
3113     (void) Hear_again();
3114     if (u.uhs > FAINTING)
3115         u.uhs = FAINTING;
3116     stop_occupation();
3117     g.context.botl = 1;
3118     return 0;
3119 }
3120 
3121 boolean
is_fainted(void)3122 is_fainted(void)
3123 {
3124     return (boolean) (u.uhs == FAINTED);
3125 }
3126 
3127 /* call when a faint must be prematurely terminated */
3128 void
reset_faint(void)3129 reset_faint(void)
3130 {
3131     if (g.afternmv == unfaint)
3132         unmul("You revive.");
3133 }
3134 
3135 /* compute and comment on your (new?) hunger status */
3136 void
newuhs(boolean incr)3137 newuhs(boolean incr)
3138 {
3139     unsigned newhs;
3140     static unsigned save_hs;
3141     static boolean saved_hs = FALSE;
3142     int h = u.uhunger;
3143 
3144     newhs = (h > 1000)
3145                 ? SATIATED
3146                 : (h > 150) ? NOT_HUNGRY
3147                             : (h > 50) ? HUNGRY : (h > 0) ? WEAK : FAINTING;
3148 
3149     /* While you're eating, you may pass from WEAK to HUNGRY to NOT_HUNGRY.
3150      * This should not produce the message "you only feel hungry now";
3151      * that message should only appear if HUNGRY is an endpoint.  Therefore
3152      * we check to see if we're in the middle of eating.  If so, we save
3153      * the first hunger status, and at the end of eating we decide what
3154      * message to print based on the _entire_ meal, not on each little bit.
3155      */
3156     /* It is normally possible to check if you are in the middle of a meal
3157      * by checking occupation == eatfood, but there is one special case:
3158      * start_eating() can call bite() for your first bite before it
3159      * sets the occupation.
3160      * Anyone who wants to get that case to work _without_ an ugly static
3161      * force_save_hs variable, feel free.
3162      */
3163     /* Note: If you become a certain hunger status in the middle of the
3164      * meal, and still have that same status at the end of the meal,
3165      * this will incorrectly print the associated message at the end of
3166      * the meal instead of the middle.  Such a case is currently
3167      * impossible, but could become possible if a message for SATIATED
3168      * were added or if HUNGRY and WEAK were separated by a big enough
3169      * gap to fit two bites.
3170      */
3171     if (g.occupation == eatfood || g.force_save_hs) {
3172         if (!saved_hs) {
3173             save_hs = u.uhs;
3174             saved_hs = TRUE;
3175         }
3176         u.uhs = newhs;
3177         return;
3178     } else {
3179         if (saved_hs) {
3180             u.uhs = save_hs;
3181             saved_hs = FALSE;
3182         }
3183     }
3184 
3185     if (newhs == FAINTING) {
3186         /* u,uhunger is likely to be negative at this point */
3187         int uhunger_div_by_10 = sgn(u.uhunger) * ((abs(u.uhunger) + 5) / 10);
3188 
3189         if (is_fainted())
3190             newhs = FAINTED;
3191         if (u.uhs <= WEAK || rn2(20 - uhunger_div_by_10) >= 19) {
3192             if (!is_fainted() && g.multi >= 0 /* %% */) {
3193                 int duration = 10 - uhunger_div_by_10;
3194 
3195                 /* stop what you're doing, then faint */
3196                 stop_occupation();
3197                 You("faint from lack of food.");
3198                 incr_itimeout(&HDeaf, duration);
3199                 g.context.botl = TRUE;
3200                 nomul(-duration);
3201                 g.multi_reason = "fainted from lack of food";
3202                 g.nomovemsg = "You regain consciousness.";
3203                 g.afternmv = unfaint;
3204                 newhs = FAINTED;
3205                 if (!Levitation)
3206                     selftouch("Falling, you");
3207             }
3208 
3209         /* this used to be -(200 + 20 * Con) but that was when being asleep
3210            suppressed per-turn uhunger decrement but being fainted didn't;
3211            now uhunger becomes more negative at a slower rate */
3212         } else if (u.uhunger < -(100 + 10 * (int) ACURR(A_CON))) {
3213             u.uhs = STARVED;
3214             g.context.botl = 1;
3215             bot();
3216             You("die from starvation.");
3217             g.killer.format = KILLED_BY;
3218             Strcpy(g.killer.name, "starvation");
3219             done(STARVING);
3220             /* if we return, we lifesaved, and that calls newuhs */
3221             return;
3222         }
3223     }
3224 
3225     if (newhs != u.uhs) {
3226         if (newhs >= WEAK && u.uhs < WEAK) {
3227             /* this used to be losestr(1) which had the potential to
3228                be fatal (still handled below) by reducing HP if it
3229                tried to take base strength below minimum of 3 */
3230             ATEMP(A_STR) = -1; /* temporary loss overrides Fixed_abil */
3231             /* defer g.context.botl status update until after hunger message */
3232         } else if (newhs < WEAK && u.uhs >= WEAK) {
3233             /* this used to be losestr(-1) which could be abused by
3234                becoming weak while wearing ring of sustain ability,
3235                removing ring, eating to 'restore' strength which boosted
3236                strength by a point each time the cycle was performed;
3237                substituting "while polymorphed" for sustain ability and
3238                "rehumanize" for ring removal might have done that too */
3239             ATEMP(A_STR) = 0; /* repair of loss also overrides Fixed_abil */
3240             /* defer g.context.botl status update until after hunger message */
3241         }
3242 
3243         switch (newhs) {
3244         case HUNGRY:
3245             if (Hallucination) {
3246                 You(!incr ? "now have a lesser case of the munchies."
3247                     : "are getting the munchies.");
3248             } else
3249                 You("%s.", !incr ? "only feel hungry now"
3250                            : (u.uhunger < 145) ? "feel hungry"
3251                              : "are beginning to feel hungry");
3252             if (incr && g.occupation
3253                 && (g.occupation != eatfood && g.occupation != opentin))
3254                 stop_occupation();
3255             end_running(TRUE);
3256             break;
3257         case WEAK:
3258             if (Hallucination)
3259                 pline(!incr ? "You still have the munchies."
3260               : "The munchies are interfering with your motor capabilities.");
3261             else if (incr && (Role_if(PM_WIZARD) || Race_if(PM_ELF)
3262                               || Role_if(PM_VALKYRIE)))
3263                 pline("%s needs food, badly!",
3264                       (Role_if(PM_WIZARD) || Role_if(PM_VALKYRIE))
3265                           ? g.urole.name.m
3266                           : "Elf");
3267             else
3268                 You("%s weak from hunger.",
3269                     !incr ? "are still"
3270                           : (u.uhunger < 45) ? "feel"
3271                                              : "are beginning to feel");
3272             if (incr && g.occupation
3273                 && (g.occupation != eatfood && g.occupation != opentin))
3274                 stop_occupation();
3275             end_running(TRUE);
3276             break;
3277         case NOT_HUNGRY:
3278             if (flags.verbose && !incr) {
3279                 if (Hallucination)
3280                     pline("The munchies have receded.");
3281                 else
3282                     You("no longer feel hungry.");
3283             }
3284             break;
3285         }
3286         u.uhs = newhs;
3287         g.context.botl = 1;
3288         bot();
3289         if ((Upolyd ? u.mh : u.uhp) < 1) {
3290             You("die from hunger and exhaustion.");
3291             g.killer.format = KILLED_BY;
3292             Strcpy(g.killer.name, "exhaustion");
3293             done(STARVING);
3294             return;
3295         }
3296     }
3297 }
3298 
3299 /* getobj callback for object to eat - effectively just wraps is_edible() */
3300 static int
eat_ok(struct obj * obj)3301 eat_ok(struct obj *obj)
3302 {
3303     if (!obj)
3304         return GETOBJ_EXCLUDE;
3305 
3306     if (is_edible(obj))
3307         return GETOBJ_SUGGEST;
3308 
3309     /* make sure to exclude, not downplay, gold (if not is_edible) in order to
3310      * produce the "You cannot eat gold" message in getobj */
3311     if (obj->oclass == COIN_CLASS)
3312         return GETOBJ_EXCLUDE;
3313 
3314     return GETOBJ_EXCLUDE_SELECTABLE;
3315 }
3316 
3317 /* getobj callback for object to be offered (corpses and things that look like
3318  * the Amulet only */
3319 static int
offer_ok(struct obj * obj)3320 offer_ok(struct obj *obj)
3321 {
3322     if (!obj || (obj->oclass != FOOD_CLASS && obj->oclass != AMULET_CLASS))
3323         return GETOBJ_EXCLUDE;
3324 
3325     if (obj->otyp != CORPSE && obj->otyp != AMULET_OF_YENDOR
3326         && obj->otyp != FAKE_AMULET_OF_YENDOR)
3327         return GETOBJ_EXCLUDE_SELECTABLE;
3328 
3329     /* suppress corpses on astral, amulets elsewhere
3330      * (!astral && amulet) || (astral && !amulet) */
3331     if (Is_astralevel(&u.uz) ^ (obj->oclass == AMULET_CLASS))
3332         return GETOBJ_DOWNPLAY;
3333 
3334     return GETOBJ_SUGGEST;
3335 }
3336 
3337 /* getobj callback for object to be tinned */
3338 static int
tin_ok(struct obj * obj)3339 tin_ok(struct obj *obj)
3340 {
3341     if (!obj || obj->oclass != FOOD_CLASS)
3342         return GETOBJ_EXCLUDE;
3343 
3344     if (obj->otyp != CORPSE || !tinnable(obj))
3345         return GETOBJ_EXCLUDE_SELECTABLE;
3346 
3347     return GETOBJ_SUGGEST;
3348 }
3349 
3350 /* Returns an object representing food.
3351  * Object may be either on floor or in inventory.
3352  */
3353 struct obj *
floorfood(const char * verb,int corpsecheck)3354 floorfood(const char *verb,
3355           int corpsecheck) /* 0, no check, 1, corpses, 2, tinnable corpses */
3356 {
3357     register struct obj *otmp;
3358     char qbuf[QBUFSZ];
3359     char c;
3360     struct permonst *uptr = g.youmonst.data;
3361     boolean feeding = !strcmp(verb, "eat"),        /* corpsecheck==0 */
3362             offering = !strcmp(verb, "sacrifice"); /* corpsecheck==1 */
3363 
3364     /* if we can't touch floor objects then use invent food only */
3365     if (iflags.menu_requested /* command was preceded by 'm' prefix */
3366         || !can_reach_floor(TRUE) || (feeding && u.usteed)
3367         || (is_pool_or_lava(u.ux, u.uy)
3368             && (Wwalking || is_clinger(uptr) || (Flying && !Breathless))))
3369         goto skipfloor;
3370 
3371     if (feeding && metallivorous(uptr)) {
3372         struct obj *gold;
3373         struct trap *ttmp = t_at(u.ux, u.uy);
3374 
3375         if (ttmp && ttmp->tseen && ttmp->ttyp == BEAR_TRAP) {
3376             boolean u_in_beartrap = (u.utrap && u.utraptype == TT_BEARTRAP);
3377 
3378             /* If not already stuck in the trap, perhaps there should
3379                be a chance to becoming trapped?  Probably not, because
3380                then the trap would just get eaten on the _next_ turn... */
3381             Sprintf(qbuf, "There is a bear trap here (%s)",
3382                     u_in_beartrap ? "holding you" : "armed");
3383             if (!can_eat_material(uptr, ttmp->ammo->material)) {
3384                 pline("%s, but you cannot eat it.", qbuf);
3385                 return (struct obj *) 0;
3386             }
3387             Strcat(qbuf, "; eat it?");
3388             if ((c = yn_function(qbuf, ynqchars, 'n')) == 'y') {
3389                 if (u_in_beartrap)
3390                     reset_utrap(TRUE);
3391                 return deltrap_with_ammo(ttmp, DELTRAP_RETURN_AMMO);
3392             } else if (c == 'q') {
3393                 return (struct obj *) 0;
3394             }
3395         }
3396         if (levl[u.ux][u.uy].typ == IRONBARS) {
3397             /* already verified that hero is metallivorous above */
3398             boolean nodig = (levl[u.ux][u.uy].wall_info & W_NONDIGGABLE) != 0;
3399 
3400             c = 'n';
3401             Strcpy(qbuf, "There are iron bars here");
3402             if (nodig || u.uhunger > 1500) {
3403                 pline("%s but you %s eat them.", qbuf,
3404                       nodig ? "cannot" : "are too full to");
3405             } else {
3406                 Strcat(qbuf, ((!g.context.digging.chew
3407                                || g.context.digging.pos.x != u.ux
3408                                || g.context.digging.pos.y != u.uy
3409                                || !on_level(&g.context.digging.level, &u.uz))
3410                               ? "; eat them?"
3411                               : "; resume eating them?"));
3412                 c = yn_function(qbuf, ynqchars, 'n');
3413             }
3414             if (c == 'y')
3415                 return (struct obj *) &cg.zeroobj; /* csst away 'const' */
3416             else if (c == 'q')
3417                 return (struct obj *) 0;
3418         }
3419         if (can_eat_material(uptr, GOLD) && (gold = g_at(u.ux, u.uy)) != 0) {
3420             if (gold->quan == 1L)
3421                 Sprintf(qbuf, "There is 1 gold piece here; eat it?");
3422             else
3423                 Sprintf(qbuf, "There are %ld gold pieces here; eat them?",
3424                         gold->quan);
3425             if ((c = yn_function(qbuf, ynqchars, 'n')) == 'y') {
3426                 return gold;
3427             } else if (c == 'q') {
3428                 return (struct obj *) 0;
3429             }
3430         }
3431     }
3432 
3433     /* Is there some food (probably a heavy corpse) here on the ground? */
3434     for (otmp = g.level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere) {
3435         if (corpsecheck
3436                 ? (otmp->otyp == CORPSE
3437                    && (corpsecheck == 1 || tinnable(otmp)))
3438                 : feeding ? (otmp->oclass != COIN_CLASS && is_edible(otmp))
3439                           : otmp->oclass == FOOD_CLASS) {
3440             char qsfx[QBUFSZ];
3441             boolean one = (otmp->quan == 1L);
3442 
3443             /* if blind and without gloves, attempting to eat (or tin or
3444                offer) a cockatrice corpse is fatal before asking whether
3445                or not to use it; otherwise, 'm<dir>' followed by 'e' could
3446                be used to locate cockatrice corpses without touching them */
3447             if (otmp->otyp == CORPSE && will_feel_cockatrice(otmp, FALSE)) {
3448                 feel_cockatrice(otmp, FALSE);
3449                 /* if life-saved (or poly'd into stone golem), terminate
3450                    attempt to eat off floor */
3451                 return (struct obj *) 0;
3452             }
3453             /* "There is <an object> here; <verb> it?" or
3454                "There are <N objects> here; <verb> one?" */
3455             Sprintf(qbuf, "There %s ", otense(otmp, "are"));
3456             Sprintf(qsfx, " here; %s %s?", verb, one ? "it" : "one");
3457             (void) safe_qbuf(qbuf, qbuf, qsfx, otmp, doname, ansimpleoname,
3458                              one ? something : (const char *) "things");
3459             if ((c = yn_function(qbuf, ynqchars, 'n')) == 'y')
3460                 return  otmp;
3461             else if (c == 'q')
3462                 return (struct obj *) 0;
3463         }
3464     }
3465 
3466  skipfloor:
3467     /* We cannot use GETOBJ_PROMPT since we don't want a prompt in the case
3468      * where nothing edible is being carried. */
3469     if (feeding)
3470         otmp = getobj("eat", eat_ok, GETOBJ_NOFLAGS);
3471     else if (offering)
3472         otmp = getobj("sacrifice", offer_ok, GETOBJ_NOFLAGS);
3473     else if (corpsecheck == 2)
3474         otmp = getobj(verb, tin_ok, GETOBJ_NOFLAGS);
3475     else {
3476         impossible("floorfood: unknown request (%s)", verb);
3477         return (struct obj *) 0;
3478     }
3479     if (corpsecheck && otmp && !(offering && otmp->oclass == AMULET_CLASS))
3480         if (otmp->otyp != CORPSE || (corpsecheck == 2 && !tinnable(otmp))) {
3481             You_cant("%s that!", verb);
3482             return (struct obj *) 0;
3483         }
3484     return otmp;
3485 }
3486 
3487 /* Side effects of vomiting */
3488 /* added nomul (MRS) - it makes sense, you're too busy being sick! */
3489 void
vomit(void)3490 vomit(void) /* A good idea from David Neves */
3491 {
3492     if (cantvomit(g.youmonst.data)) {
3493         /* doesn't cure food poisoning; message assumes that we aren't
3494            dealing with some esoteric body_part() */
3495         Your("jaw gapes convulsively.");
3496     } else {
3497         if (Sick && (u.usick_type & SICK_VOMITABLE) != 0)
3498             make_sick(0L, (char *) 0, TRUE, SICK_VOMITABLE);
3499         /* if not enough in stomach to actually vomit then dry heave;
3500            vomiting_dialog() gives a vomit message when its countdown
3501            reaches 0, but only if u.uhs < FAINTING (and !cantvomit()) */
3502         if (u.uhs >= FAINTING)
3503             Your("%s heaves convulsively!", body_part(STOMACH));
3504     }
3505 
3506     /* nomul()/You_can_move_again used to be unconditional, which was
3507        viable while eating but not for Vomiting countdown where hero might
3508        be immobilized for some other reason at the time vomit() is called */
3509     if (g.multi >= -2) {
3510         nomul(-2);
3511         g.multi_reason = "vomiting";
3512         g.nomovemsg = You_can_move_again;
3513     }
3514 }
3515 
3516 int
eaten_stat(int base,struct obj * obj)3517 eaten_stat(int base, struct obj *obj)
3518 {
3519     long uneaten_amt, full_amount;
3520 
3521     /* get full_amount first; obj_nutrition() might modify obj->oeaten */
3522     full_amount = (long) obj_nutrition(obj);
3523     uneaten_amt = (long) obj->oeaten;
3524     if (uneaten_amt > full_amount) {
3525         impossible(
3526           "partly eaten food (%ld) more nutritious than untouched food (%ld)",
3527                    uneaten_amt, full_amount);
3528         uneaten_amt = full_amount;
3529     }
3530 
3531     base = (int) (full_amount ? (long) base * uneaten_amt / full_amount : 0L);
3532     return (base < 1) ? 1 : base;
3533 }
3534 
3535 /* reduce obj's oeaten field, making sure it never hits or passes 0 */
3536 void
consume_oeaten(struct obj * obj,int amt)3537 consume_oeaten(struct obj *obj, int amt)
3538 {
3539     if (!obj_nutrition(obj)) {
3540         char itembuf[40];
3541         int otyp = obj->otyp;
3542 
3543         if (otyp == CORPSE || otyp == EGG || otyp == TIN) {
3544             Strcpy(itembuf, (otyp == CORPSE) ? "corpse"
3545                             : (otyp == EGG) ? "egg"
3546                               : (otyp == TIN) ? "tin" : "other?");
3547             Sprintf(eos(itembuf), " [%d]", obj->corpsenm);
3548         } else {
3549             Sprintf(itembuf, "%d", otyp);
3550         }
3551         impossible(
3552             "oeaten: attempting to set 0 nutrition food (%s) partially eaten",
3553                    itembuf);
3554         return;
3555     }
3556 
3557     /*
3558      * This is a hack to try to squelch several long standing mystery
3559      * food bugs.  A better solution would be to rewrite the entire
3560      * victual handling mechanism from scratch using a less complex
3561      * model.  Alternatively, this routine could call done_eating()
3562      * or food_disappears() but its callers would need revisions to
3563      * cope with g.context.victual.piece unexpectedly going away.
3564      *
3565      * Multi-turn eating operates by setting the food's oeaten field
3566      * to its full nutritional value and then running a counter which
3567      * independently keeps track of whether there is any food left.
3568      * The oeaten field can reach exactly zero on the last turn, and
3569      * the object isn't removed from inventory until the next turn
3570      * when the "you finish eating" message gets delivered, so the
3571      * food would be restored to the status of untouched during that
3572      * interval.  This resulted in unexpected encumbrance messages
3573      * at the end of a meal (if near enough to a threshold) and would
3574      * yield full food if there was an interruption on the critical
3575      * turn.  Also, there have been reports over the years of food
3576      * becoming massively heavy or producing unlimited satiation;
3577      * this would occur if reducing oeaten via subtraction attempted
3578      * to drop it below 0 since its unsigned type would produce a
3579      * huge positive value instead.  So far, no one has figured out
3580      * _why_ that inappropriate subtraction might sometimes happen.
3581      */
3582 
3583     if (amt > 0) {
3584         /* bit shift to divide the remaining amount of food */
3585         obj->oeaten >>= amt;
3586     } else {
3587         /* simple decrement; value is negative so we actually add it */
3588         if ((int) obj->oeaten > -amt)
3589             obj->oeaten += amt;
3590         else
3591             obj->oeaten = 0;
3592     }
3593 
3594     if (obj->oeaten == 0) {
3595         if (obj == g.context.victual.piece) /* always true unless wishing... */
3596             g.context.victual.reqtime =
3597                 g.context.victual.usedtime; /* no bites left */
3598         obj->oeaten = 1; /* smallest possible positive value */
3599     }
3600 }
3601 
3602 /* called when eatfood occupation has been interrupted,
3603    or in the case of theft, is about to be interrupted */
3604 boolean
maybe_finished_meal(boolean stopping)3605 maybe_finished_meal(boolean stopping)
3606 {
3607     /* in case consume_oeaten() has decided that the food is all gone */
3608     if (g.occupation == eatfood
3609         && g.context.victual.usedtime >= g.context.victual.reqtime) {
3610         if (stopping)
3611             g.occupation = 0; /* for do_reset_eat */
3612         (void) eatfood();   /* calls done_eating() to use up
3613                                g.context.victual.piece */
3614         return TRUE;
3615     }
3616     return FALSE;
3617 }
3618 
3619 /* Tin of <something> to the rescue?  Decide whether current occupation
3620    is an attempt to eat a tin of something capable of saving hero's life.
3621    We don't care about consumption of non-tinned food here because special
3622    effects there take place on first bite rather than at end of occupation.
3623    [Popeye the Sailor gets out of trouble by eating tins of spinach. :-] */
3624 boolean
Popeye(int threat)3625 Popeye(int threat)
3626 {
3627     struct obj *otin;
3628     int mndx;
3629 
3630     if (g.occupation != opentin)
3631         return FALSE;
3632     otin = g.context.tin.tin;
3633     /* make sure hero still has access to tin */
3634     if (!carried(otin)
3635         && (!obj_here(otin, u.ux, u.uy) || !can_reach_floor(TRUE)))
3636         return FALSE;
3637     /* unknown tin is assumed to be helpful */
3638     if (!otin->known)
3639         return TRUE;
3640     /* known tin is helpful if it will stop life-threatening problem */
3641     mndx = otin->corpsenm;
3642     switch (threat) {
3643     /* note: not used; hunger code bypasses stop_occupation() when eating */
3644     case HUNGER:
3645         return (boolean) (mndx != NON_PM || otin->spe == 1);
3646     /* flesh from lizards and acidic critters stops petrification */
3647     case STONED:
3648         return (boolean) (mndx >= LOW_PM
3649                           && (mndx == PM_LIZARD || acidic(&mons[mndx])));
3650     /* polymorph into a fiery monster */
3651     case SLIMED:
3652         return (boolean) (mndx == PM_CHAMELEON);
3653     /* no tins can cure these (yet?) */
3654     case SICK:
3655     case VOMITING:
3656         break;
3657     default:
3658         break;
3659     }
3660     return FALSE;
3661 }
3662 
3663 /* Return true if the given monster species can eat objects (and terrain) made
3664  * of the given material.
3665  * This is only for eating unusual materials; things like inediate monsters
3666  * eating VEGGY/FLESH are not covered. */
3667 boolean
can_eat_material(struct permonst * pm,int material)3668 can_eat_material(struct permonst *pm, int material)
3669 {
3670     /* Use a fake object so that we can call the is_foo() functions on object
3671      * materials. */
3672     struct obj psuedo;
3673     psuedo.material = material;
3674 
3675     if (pm == &mons[PM_GELATINOUS_CUBE]) {
3676         return is_organic(&psuedo);
3677     }
3678     else if (pm == &mons[PM_RUST_MONSTER]) {
3679         /* rust monsters can ONLY eat rustprone items */
3680         return is_rustprone(&psuedo);
3681     }
3682     else if (metallivorous(pm)) {
3683         return is_metallic(&psuedo);
3684     }
3685     return FALSE;
3686 }
3687 
3688 /*eat.c*/
3689