OSDN Git Service

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