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