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. */
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
));
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
,
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.
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
)
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
)))
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
))
102 /* return (boolean) !!index(comestibles, obj->oclass); */
103 return (boolean
) (obj
->oclass
== FOOD_CLASS
);
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 },
135 #define TTSZ SIZE(tintxts)
137 static char *eatmbuf
= 0; /* set by cpostfx() */
139 /* called after mimicing is over */
143 /* release `eatmbuf' */
145 if (nomovemsg
== eatmbuf
)
147 free((genericptr_t
) eatmbuf
), eatmbuf
= 0;
150 if (youmonst
.m_ap_type
) {
151 youmonst
.m_ap_type
= M_AP_NOTHING
;
157 /* called when hallucination is toggled */
161 const char *altmsg
= 0;
162 int altapp
= 0; /* lint suppression */
164 if (!eatmbuf
|| nomovemsg
!= eatmbuf
)
167 if (is_obj_mappear(&youmonst
,ORANGE
) && !Hallucination
) {
168 /* revert from hallucinatory to "normal" mimicking */
169 altmsg
= "You now prefer mimicking yourself.";
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.";
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
;
192 /* ``[the(] singular(food, xname) [)]'' */
193 STATIC_OVL
const char *
194 food_xname(food
, the_pfx
)
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
]))
208 /* the ordinary case */
209 result
= singular(food
, xname
);
212 result
= the(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.)
227 /* only happens if you were satiated */
228 if (u
.uhs
!= SATIATED
) {
229 if (!food
|| food
->otyp
!= AMULET_OF_STRANGULATION
)
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.");
244 You("stuff yourself and then vomit voluminously.");
245 morehungry(1000); /* you just got *very* sick! */
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.
254 You("choke over your %s.", foodword(food
));
255 if (food
->oclass
== COIN_CLASS
) {
256 Strcpy(killer
.name
, "very rich meal");
258 killer
.format
= KILLED_BY
;
259 Strcpy(killer
.name
, killer_xname(food
));
262 You("choke over it.");
263 Strcpy(killer
.name
, "quick snack");
270 /* modify object wt. depending on time spent consuming it */
274 struct obj
*piece
= context
.victual
.piece
;
276 impossible("recalc_wt without piece");
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 */
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
;
300 /* base nutrition of a food-class object */
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
)
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 */
326 STATIC_OVL
struct obj
*
330 if (otmp
->quan
> 1L) {
332 (void) splitobj(otmp
, otmp
->quan
- 1L);
334 otmp
= splitobj(otmp
, 1L);
335 debugpline0("split object,");
339 costly_alteration(otmp
, COST_BITE
);
340 otmp
->oeaten
= obj_nutrition(otmp
);
345 if (inv_cnt(FALSE
) >= 52) {
346 sellobj_state(SELL_DONTSELL
);
348 sellobj_state(SELL_NORMAL
);
350 otmp
->nomerge
= 1; /* used to prevent merge */
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
367 if (obj
== context
.victual
.piece
) {
368 context
.victual
.piece
= (struct obj
*) 0;
369 context
.victual
.o_id
= 0;
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 */
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
;
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
;
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.
414 /* called each move during eating process */
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? */
425 if (!context
.victual
.eating
)
428 if (++context
.victual
.usedtime
<= context
.victual
.reqtime
) {
431 return 1; /* still busy */
442 struct obj
*piece
= context
.victual
.piece
;
444 piece
->in_use
= TRUE
;
445 occupation
= 0; /* do this early, so newuhs() knows we're done */
452 You("finish eating %s.", food_xname(piece
, TRUE
));
454 if (piece
->otyp
== CORPSE
|| piece
->globby
)
455 cpostfx(piece
->corpsenm
);
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
;
475 u
.uconduct
.unvegan
++;
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
;
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
)) {
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
);
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
));
519 /* life-saved; don't continue eating the brains */
522 if (magr
->mtame
&& !visflag
)
523 /* parallels mhitm.c's brief_feeling */
524 You("have a sad thought for a moment, then it passes.");
530 if (magr
== &youmonst
) {
532 * player mind flayer is eating something's brain
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 */
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
;
544 /* life-saving needed to reach here */
545 exercise(A_WIS
, FALSE
);
546 *dmg_p
+= xtra_dmg
; /* Rider takes extra damage */
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
);
556 exercise(A_WIS
, TRUE
);
559 /* targetting another mind flayer or your own underlying species
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";
572 Strcpy(killer
.name
, brainlessness
);
573 killer
.format
= KILLED_BY
;
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;
581 Your("last thought fades away.");
583 Strcpy(killer
.name
, brainlessness
);
584 killer
.format
= KILLED_BY
;
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 */
597 * monster mind flayer is eating another monster's brain
600 if (visflag
&& canspotmon(mdef
))
601 pline("%s doesn't notice.", Monnam(mdef
));
603 } else if (is_rider(pd
)) {
606 result
= MM_AGR_DIED
;
607 /* Rider takes extra damage regardless of whether attacker dies */
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);
626 /* eating a corpse or egg of one's own species is usually naughty */
628 maybe_cannibal(pm
, 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
)
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) */
648 || (Upolyd
&& same_race(youmonst
.data
, fptr
))
649 || (u
.ulycn
>= LOW_PM
&& were_beastie(pm
) == u
.ulycn
))) {
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 */
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.");
675 if (context
.victual
.piece
)
676 context
.victual
.eating
= FALSE
;
677 return; /* lifesaved */
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
;
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
;
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;
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
, "");
724 if (acidic(&mons
[pm
]) && Stoned
)
736 Sprintf(buf
, "What a pity--you just ruined a future piece of %sart!",
737 ACURR(A_CHA
) > 15 ? "fine " : "");
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. */
754 intrinsic_possible(type
, ptr
)
756 register struct permonst
*ptr
;
761 #define ifdebugresist(Msg) \
767 #define ifdebugresist(Msg) /*empty*/
771 res
= (ptr
->mconveys
& MR_FIRE
) != 0;
772 ifdebugresist("can get fire resistance");
775 res
= (ptr
->mconveys
& MR_SLEEP
) != 0;
776 ifdebugresist("can get sleep resistance");
779 res
= (ptr
->mconveys
& MR_COLD
) != 0;
780 ifdebugresist("can get cold resistance");
783 res
= (ptr
->mconveys
& MR_DISINT
) != 0;
784 ifdebugresist("can get disintegration resistance");
786 case SHOCK_RES
: /* shock (electricity) resistance */
787 res
= (ptr
->mconveys
& MR_ELEC
) != 0;
788 ifdebugresist("can get shock resistance");
791 res
= (ptr
->mconveys
& MR_POISON
) != 0;
792 ifdebugresist("can get poison resistance");
795 res
= can_teleport(ptr
);
796 ifdebugresist("can get teleport");
798 case TELEPORT_CONTROL
:
799 res
= control_teleport(ptr
);
800 ifdebugresist("can get teleport control");
803 res
= telepathic(ptr
);
804 ifdebugresist("can get telepathy");
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.
820 register struct permonst
*ptr
;
824 debugpline1("Attempting to give intrinsic %d", type
);
825 /* some intrinsics are easier to get than others */
828 if ((ptr
== &mons
[PM_KILLER_BEE
] || ptr
== &mons
[PM_SCORPION
])
837 case TELEPORT_CONTROL
:
848 if (ptr
->mlevel
<= rn2(chance
))
849 return; /* failed die roll */
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
;
860 debugpline0("Trying to give sleep resistance");
861 if (!(HSleep_resistance
& FROMOUTSIDE
)) {
862 You_feel("wide awake.");
863 HSleep_resistance
|= FROMOUTSIDE
;
867 debugpline0("Trying to give cold resistance");
868 if (!(HCold_resistance
& FROMOUTSIDE
)) {
869 You_feel("full of hot air.");
870 HCold_resistance
|= FROMOUTSIDE
;
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
;
880 case SHOCK_RES
: /* shock (electricity) resistance */
881 debugpline0("Trying to give shock resistance");
882 if (!(HShock_resistance
& FROMOUTSIDE
)) {
884 You_feel("grounded in reality.");
886 Your("health currently feels amplified!");
887 HShock_resistance
|= FROMOUTSIDE
;
891 debugpline0("Trying to give poison resistance");
892 if (!(HPoison_resistance
& FROMOUTSIDE
)) {
893 You_feel(Poison_resistance
? "especially healthy." : "healthy.");
894 HPoison_resistance
|= FROMOUTSIDE
;
898 debugpline0("Trying to give teleport");
899 if (!(HTeleportation
& FROMOUTSIDE
)) {
900 You_feel(Hallucination
? "diffuse." : "very jumpy.");
901 HTeleportation
|= FROMOUTSIDE
;
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
;
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. */
924 debugpline0("Tried to give an impossible intrinsic");
929 /* called after completely consuming a corpse */
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 */
944 /* MRKR: "eye of newt" may give small magical energy boost */
945 if (rn2(3) || 3 * u
.uen
<= 2 * u
.uenmax
) {
948 if (u
.uen
> u
.uenmax
) {
953 if (old_uen
!= u
.uen
) {
954 You_feel("a mild buzz.");
962 case PM_HUMAN_WERERAT
:
963 catch_lycanthropy
= PM_WERERAT
;
965 case PM_HUMAN_WEREJACKAL
:
966 catch_lycanthropy
= PM_WEREJACKAL
;
968 case PM_HUMAN_WEREWOLF
:
969 catch_lycanthropy
= PM_WEREWOLF
;
980 set_itimeout(&HInvis
, (long) rn1(100, 50));
981 if (!Blind
&& !BInvis
)
982 self_invis_message();
984 if (!(HInvis
& INTRINSIC
))
986 HInvis
|= FROMOUTSIDE
;
987 HSee_invisible
|= FROMOUTSIDE
;
991 case PM_YELLOW_LIGHT
:
993 make_stunned((HStun
& TIMEOUT
) + 30L, FALSE
);
996 make_stunned((HStun
& TIMEOUT
) + 30L, FALSE
);
1001 case PM_LARGE_MIMIC
:
1004 case PM_SMALL_MIMIC
:
1006 if (youmonst
.data
->mlet
!= S_MIMIC
&& !Unchanging
) {
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. */
1014 dismount_steed(DISMOUNT_FELL
);
1016 multi_reason
= "pretending to be a pile of gold";
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
;
1030 /* make gold symbol show up now */
1031 display_nhwindow(WIN_MAP
, TRUE
);
1034 case PM_QUANTUM_MECHANIC
:
1035 Your("velocity suddenly seems very uncertain!");
1036 if (HFast
& INTRINSIC
) {
1037 HFast
&= ~INTRINSIC
;
1038 You("seem slower.");
1040 HFast
|= FROMOUTSIDE
;
1041 You("seem faster.");
1045 if ((HStun
& TIMEOUT
) > 2)
1046 make_stunned(2L, FALSE
);
1047 if ((HConfusion
& TIMEOUT
) > 2)
1048 make_confused(2L, FALSE
);
1051 case PM_DOPPELGANGER
:
1052 case PM_SANDESTIN
: /* moot--they don't leave corpses */
1054 You_feel("momentarily different."); /* same as poly trap */
1056 You_feel("a change coming over you.");
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");
1066 case PM_MIND_FLAYER
:
1067 case PM_MASTER_MIND_FLAYER
:
1068 if (ABASE(A_INT
) < ATTRMAX(A_INT
)) {
1070 pline("Yum! That was real brain food!");
1071 (void) adjattrib(A_INT
, 1, FALSE
);
1072 break; /* don't give them telepathy, too */
1075 pline("For some reason, that tasted bland.");
1079 struct permonst
*ptr
= &mons
[pm
];
1080 boolean conveys_STR
= is_giant(ptr
);
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
,
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 */
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
))
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) */
1113 debugpline2("Intrinsic %d replacing %d", i
, tmp
);
1117 /* if strength is the only candidate, give it 50% chance */
1118 if (conveys_STR
&& count
== 1 && !rn2(2))
1120 /* if something was chosen, give it now (givit() might fail) */
1122 gainstr((struct obj
*) 0, 0, TRUE
);
1126 } /* default case */
1129 if (catch_lycanthropy
>= LOW_PM
) {
1130 set_ulycn(catch_lycanthropy
);
1131 retouch_equipment(2);
1137 violated_vegetarian()
1139 u
.uconduct
.unvegetarian
++;
1140 if (Role_if(PM_MONK
)) {
1141 You_feel("guilty.");
1147 /* common code to check and possibly charge for 1 context.tin.tin,
1148 * will split() context.tin.tin if necessary */
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
)
1172 if (s
&& tinvariety
) {
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
)
1187 * This assumes that buf already contains the word "tin",
1188 * as is the case with caller xname().
1191 tin_details(obj
, mnum
, buf
)
1197 int r
= tin_variety(obj
, TRUE
);
1200 if (r
== SPINACH_TIN
)
1201 Strcat(buf
, " of spinach");
1202 else if (mnum
== NON_PM
)
1203 Strcpy(buf
, "empty tin");
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
);
1211 Sprintf(eos(buf
), " of %s ", tintxts
[r
].txt
);
1214 Strcpy(eos(buf
), " of ");
1216 if (vegetarian(&mons
[mnum
]))
1217 Sprintf(eos(buf
), "%s", mons
[mnum
].mname
);
1219 Sprintf(eos(buf
), "%s meat", mons
[mnum
].mname
);
1225 set_tin_variety(obj
, forcetype
)
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 */
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
)
1244 } else if (forcetype
>= 0 && forcetype
< TTSZ
- 1) {
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 */
1255 tin_variety(obj
, disp
)
1257 boolean disp
; /* we're just displaying so leave things alone */
1261 if (obj
->spe
== 1) {
1263 } else if (obj
->cursed
) {
1264 r
= ROTTEN_TIN
; /* always rotten if cursed */
1265 } else if (obj
->spe
< 0) {
1267 --r
; /* get rid of the offset */
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 */
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
);
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
);
1305 which
= 0; /* 0=>plural, 1=>as-is, 2=>"the" prefix */
1306 if ((mnum
== PM_COCKATRICE
|| mnum
== PM_CHICKATRICE
)
1307 && (Stone_resistance
|| Hallucination
)) {
1309 which
= 1; /* suppress pluralization */
1310 } else if (Hallucination
) {
1311 what
= rndmonnam(NULL
);
1313 what
= mons
[mnum
].mname
;
1314 if (the_unique_pm(&mons
[mnum
]))
1316 else if (type_is_pname(&mons
[mnum
]))
1320 what
= makeplural(what
);
1321 else if (which
== 2)
1324 pline("It smells like %s.", what
);
1325 if (yn("Eat it?") == 'n') {
1327 You("discard the open tin.");
1329 tin
->dknown
= tin
->known
= 1;
1330 costly_tin(COST_OPEN
);
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;
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
);
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... */
1365 pline("It contains some decaying%s%s substance.",
1366 Blind
? "" : " ", Blind
? "" : hcolor(NH_GREEN
));
1368 pline("It contains spinach.");
1369 tin
->dknown
= tin
->known
= 1;
1372 if (yn("Eat it?") == 'n') {
1374 You("discard the open tin.");
1375 costly_tin(COST_OPEN
);
1380 * Same order as with non-spinach above:
1381 * conduct update, side-effects, shop handling, and nutrition.
1384 .food
++; /* don't need vegan/vegetarian checks for spinach */
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
1395 ? (400 + rnd(200)) /* uncursed */
1396 : (200 + rnd(400))); /* cursed */
1404 context
.tin
.tin
= (struct obj
*) 0;
1405 context
.tin
.o_id
= 0;
1408 /* called during each move whilst opening a tin */
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.");
1420 if (context
.tin
.usedtime
< context
.tin
.reqtime
)
1421 return 1; /* still busy */
1423 consume_tin("You succeed in opening the tin.");
1427 /* called when starting to open a tin */
1432 const char *mesg
= 0;
1435 if (metallivorous(youmonst
.data
)) {
1436 mesg
= "You bite right into the metal tin...";
1438 } else if (cantwield(youmonst
.data
)) { /* nohands || verysmall */
1439 You("cannot handle the tin properly to open it.");
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);
1449 mesg
= "The tin opens like magic!";
1451 pline_The("tin seems easy to open.");
1453 switch (uwep
->otyp
) {
1455 mesg
= "You easily open the tin."; /* iff tmp==0 */
1456 tmp
= rn2(uwep
->cursed
? 3 : !uwep
->blessed
? 2 : 1);
1475 pline("Using %s you try to open the tin.", yobjnam(uwep
, (char *) 0));
1478 pline("It is not so easy to open this tin.");
1480 pline_The("tin slips from your %s.",
1481 makeplural(body_part(FINGER
)));
1482 if (otmp
->quan
> 1L) {
1483 otmp
= splitobj(otmp
, 1L);
1491 tmp
= rn1(1 + 500 / ((int) (ACURR(A_DEX
) + ACURRSTR
)), 10);
1494 context
.tin
.tin
= otmp
;
1495 context
.tin
.o_id
= otmp
->o_id
;
1497 consume_tin(mesg
); /* begin immediately */
1499 context
.tin
.reqtime
= tmp
;
1500 context
.tin
.usedtime
= 0;
1501 set_occupation(opentin
, "opening the tin", 0);
1506 /* called when waking up after fainting */
1508 Hear_again(VOID_ARGS
)
1510 /* Chance of deafness going away while fainted/sleeping/etc. */
1512 make_deaf(0L, FALSE
);
1513 context
.botl
= TRUE
;
1518 /* called on the "first bite" of rotten food */
1523 pline("Blecch! Rotten %s!", foodword(obj
));
1526 You_feel("rather trippy.");
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
);
1536 Your1(vision_clears
);
1537 } else if (!rn2(3)) {
1538 const char *what
, *where
;
1539 int duration
= rnd(10);
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";
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
;
1552 multi_reason
= "unconscious from rotten food";
1553 nomovemsg
= "You are conscious again.";
1554 afternmv
= Hear_again
;
1560 /* called when a corpse is selected as food */
1565 int retcode
= 0, tp
= 0, mnum
= otmp
->corpsenm
;
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
;
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));
1585 else if (otmp
->blessed
)
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"
1595 : vegetarian(&mons
[mnum
]) ? "protoplasm"
1597 cannibal
? ", you cannibal" : "");
1598 if (Sick_resistance
) {
1599 pline("It doesn't seem at all sickening, though...");
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
);
1615 } else if (acidic(&mons
[mnum
]) && !Acid_resistance
) {
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)) {
1622 pline("Ecch - that must have been poisonous!");
1623 if (!Poison_resistance
) {
1625 losehp(rnd(15), !glob
? "poisonous corpse" : "posionous glob",
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
) {
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
);
1646 if (!mons
[otmp
->corpsenm
].cnutrit
) {
1647 /* no nutrition: rots away, no message if you passed out */
1649 pline_The("corpse rots away completely.");
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.");
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))
1678 pline("%s%s %s %s%c",
1679 type_is_pname(&mons
[mnum
])
1680 ? "" : the_unique_pm(&mons
[mnum
]) ? "The " : "This ",
1682 Hallucination
? "is" : "tastes",
1683 /* tiger reference is to TV ads for "Frosted Flakes",
1684 breakfast cereal targeted at kids by "Tony the tiger" */
1686 ? (yummy
? ((u
.umonnum
== PM_TIGER
) ? "gr-r-reat" : "gnarly")
1687 : palatable
? "copacetic" : "grody")
1688 : (yummy
? "delicious" : palatable
? "okay" : "terrible"),
1689 (yummy
|| !palatable
) ? '!' : '.');
1695 /* called as you start to eat */
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 */
1719 old_nomovemsg
= nomovemsg
;
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
;
1731 nomovemsg
= save_nomovemsg
;
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
);
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
1754 switch (otmp
->otyp
) {
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
));
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!");
1769 pline("Yak - dog food!");
1770 more_experienced(1, 0);
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
);
1779 if (maybe_polyd(is_orc(youmonst
.data
), Race_if(PM_ORC
))) {
1780 pline("%s", "!#?&* elf kibble!");
1782 } else if (maybe_polyd(is_elf(youmonst
.data
), Race_if(PM_ELF
))) {
1783 pline("A little goes a long way.");
1789 case HUGE_CHUNK_OF_MEAT
:
1792 case CLOVE_OF_GARLIC
:
1793 if (is_undead(youmonst
.data
)) {
1794 make_vomiting((long) rn1(context
.victual
.reqtime
, 5), FALSE
);
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!");
1816 } else if (otmp
->otyp
== APPLE
|| otmp
->otyp
== PEAR
) {
1817 if (!Hallucination
) {
1818 pline("Core dumped.");
1820 /* This is based on an old Usenet joke, a fake a.out manual
1825 pline("%s -- core dumped.",
1827 ? "Segmentation fault"
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
);
1841 pline("This %s is %s", singular(otmp
, xname
),
1843 ? (Hallucination
? "grody!" : "terrible!")
1844 : (otmp
->otyp
== CRAM_RATION
1845 || otmp
->otyp
== K_RATION
1846 || otmp
->otyp
== C_RATION
)
1848 : Hallucination
? "gnarly!" : "delicious!");
1850 break; /* default */
1854 /* increment a combat intrinsic with limits on its growth */
1856 bounded_increase(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
)
1864 if (uleft
&& uleft
->otyp
== typ
)
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
;
1882 inc
= 0; /* no further increase allowed via this method */
1888 accessory_has_effect(otmp
)
1891 pline("Magic spreads through your body as you digest the %s.",
1892 otmp
->oclass
== RING_CLASS
? "ring" : "amulet");
1899 int typ
= otmp
->otyp
;
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
) {
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
) {
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
;
1923 case RIN_SEE_INVISIBLE
:
1924 set_mimic_blocking();
1926 if (Invis
&& !oldprop
&& !ESee_invisible
1927 && !perceives(youmonst
.data
) && !Blind
) {
1929 pline("Suddenly you can see yourself.");
1933 case RIN_INVISIBILITY
:
1934 if (!oldprop
&& !EInvis
&& !BInvis
&& !See_invisible
1937 Your("body takes on a %s transparency...",
1938 Hallucination
? "normal" : "strange");
1942 case RIN_PROTECTION_FROM_SHAPE_CHAN
:
1945 case RIN_LEVITATION
:
1946 /* undo the `.intrinsic |= FROMOUTSIDE' done above */
1947 u
.uprops
[LEVITATION
].intrinsic
= oldprop
;
1950 incr_itimeout(&HLevitation
, d(10, 20));
1954 } /* inner switch */
1955 break; /* default case of outer switch */
1958 accessory_has_effect(otmp
);
1959 if (adjattrib(A_CHA
, otmp
->spe
, -1))
1962 case RIN_GAIN_STRENGTH
:
1963 accessory_has_effect(otmp
);
1964 if (adjattrib(A_STR
, otmp
->spe
, -1))
1967 case RIN_GAIN_CONSTITUTION
:
1968 accessory_has_effect(otmp
);
1969 if (adjattrib(A_CON
, otmp
->spe
, -1))
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
);
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
);
1982 case RIN_PROTECTION
:
1983 accessory_has_effect(otmp
);
1984 HProtection
|= FROMOUTSIDE
;
1985 u
.ublessed
= bounded_increase(u
.ublessed
, otmp
->spe
,
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
;
1997 case AMULET_OF_CHANGE
:
1998 accessory_has_effect(otmp
);
2001 You("are suddenly very %s!",
2002 flags
.female
? "feminine" : "masculine");
2005 case AMULET_OF_UNCHANGING
:
2006 /* un-change: it's a pun */
2007 if (!Unchanging
&& Upolyd
) {
2008 accessory_has_effect(otmp
);
2013 case AMULET_OF_STRANGULATION
: /* bad idea! */
2014 /* no message--this gives no permanent effect */
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
;
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.
2039 /* called after eating non-food */
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
);
2049 context
.victual
.piece
= (struct obj
*) 0;
2050 context
.victual
.o_id
= 0;
2051 context
.victual
.eating
= 0;
2052 if (otmp
->oclass
== COIN_CLASS
) {
2056 useupf(otmp
, otmp
->quan
);
2057 vault_gd_watching(GD_EATGOLD
);
2060 if (objects
[otmp
->otyp
].oc_material
== PAPER
) {
2062 if (otmp
->otyp
== SCR_MAIL
)
2064 pline("This junk mail is less than satisfying.");
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
? '!' : '.');
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
) {
2082 } else if (otmp
->otyp
== LEASH
&& otmp
->leashmon
) {
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)
2101 if (otmp
== uquiver
&& otmp
->quan
== 1L)
2103 if (otmp
== uswapwep
&& otmp
->quan
== 1L)
2109 unpunish(); /* but no useup() */
2110 else if (carried(otmp
))
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 *
2129 if (otmp
->oclass
== FOOD_CLASS
)
2131 if (otmp
->oclass
== GEM_CLASS
&& objects
[otmp
->otyp
].oc_material
== GLASS
2133 makeknown(otmp
->otyp
);
2134 return foodwords
[objects
[otmp
->otyp
].oc_material
];
2137 /* called after consuming (non-corpse) food */
2142 switch (otmp
->otyp
) {
2143 case SPRIG_OF_WOLFSBANE
:
2144 if (u
.ulycn
>= LOW_PM
|| is_were(youmonst
.data
))
2149 || !attacktype_fordmg(u
.ustuck
->data
, AT_ENGL
, AD_BLND
))
2150 make_blinded((long) u
.ucreamed
, TRUE
);
2152 case FORTUNE_COOKIE
:
2153 outrumor(bcsign(otmp
), BY_COOKIE
);
2155 u
.uconduct
.literate
++;
2157 case LUMP_OF_ROYAL_JELLY
:
2158 /* This stuff seems to be VERY healthy! */
2159 gainstr(otmp
, 1, TRUE
);
2161 u
.mh
+= otmp
->cursed
? -rnd(20) : rnd(20);
2162 if (u
.mh
> u
.mhmax
) {
2166 } else if (u
.mh
<= 0) {
2170 u
.uhp
+= otmp
->cursed
? -rnd(20) : rnd(20);
2171 if (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");
2185 if (flesh_petrifies(&mons
[otmp
->corpsenm
])) {
2186 if (!Stone_resistance
2187 && !(poly_when_stoned(youmonst
.data
)
2188 && polymon(PM_STONE_GOLEM
))) {
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 */
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
);
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.");
2214 You_hear("sinister laughter as you fall asleep...");
2215 fall_asleep(-rn1(11, 20), TRUE
);
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
*));
2232 const char *odesc
= OBJ_DESCR(objects
[otmp
->otyp
]);
2234 if (odesc
&& (otmp
->oclass
== SPBOOK_CLASS
)) {
2235 if (!strcmp(odesc
, "leather"))
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.
2248 edibility_prompts(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
;
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) */);
2283 else if (otmp
->blessed
)
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
) {
2294 Sprintf(buf
, "%s like %s could be tainted! %s", foodsmell
, it_or_they
,
2296 if (yn_function(buf
, ynchars
, 'n') == 'n')
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')
2309 if (otmp
->orotten
|| (cadaver
&& rotted
> 3L)) {
2311 Sprintf(buf
, "%s like %s could be rotten! %s", foodsmell
, it_or_they
,
2313 if (yn_function(buf
, ynchars
, 'n') == 'n')
2318 if (cadaver
&& poisonous(&mons
[mnum
]) && !Poison_resistance
) {
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')
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')
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')
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
,
2352 if (yn_function(buf
, ynchars
, 'n') == 'n')
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
,
2367 if (yn_function(buf
, ynchars
, 'n') == 'n')
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')
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
,
2387 if (yn_function(buf
, ynchars
, 'n') == 'n')
2400 int basenutrit
; /* nutrition of full item */
2401 boolean dont_start
= FALSE
, nodelicious
= FALSE
;
2404 pline("If you can't breathe air, how can you consume solids?");
2407 if (!(otmp
= floorfood("eat", 0)))
2409 if (check_capacity((char *) 0))
2413 int res
= edibility_prompts(otmp
);
2417 "%s stops tingling and your sense of smell returns to normal.",
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
))) {
2433 } else if (!is_edible(otmp
)) {
2434 You("cannot eat that!");
2436 } else if ((otmp
->owornmask
& (W_ARMOR
| W_TOOL
| W_AMUL
| W_SADDLE
))
2438 /* let them eat rings */
2439 You_cant("eat %s you're wearing.", something
);
2442 if (is_metallic(otmp
) && u
.umonnum
== PM_RUST_MONSTER
2443 && otmp
->oerodeproof
) {
2444 otmp
->rknown
= TRUE
;
2445 if (otmp
->quan
> 1L) {
2447 (void) splitobj(otmp
, otmp
->quan
- 1L);
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
)) {
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
)
2473 if (otmp
->oclass
!= FOOD_CLASS
) {
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)
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 */
2492 basenutrit
= objects
[otmp
->otyp
].oc_nutrition
;
2494 if (otmp
->otyp
== SCR_MAIL
) {
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
++;
2512 (void) rottenfood(otmp
);
2514 } else if (objects
[otmp
->otyp
].oc_material
== PAPER
)
2517 if (otmp
->oclass
== WEAPON_CLASS
&& otmp
->opoisoned
) {
2518 pline("Ecch - that must have been poisonous!");
2519 if (!Poison_resistance
) {
2521 losehp(rnd(15), xname(otmp
), KILLED_BY_AN
);
2523 You("seem unaffected by the poison.");
2524 } else if (!nodelicious
) {
2525 pline("%s%s is delicious!",
2527 && otmp
->oartifact
< ART_ORB_OF_DETECTION
)
2530 (otmp
->oclass
== COIN_CLASS
)
2532 : singular(otmp
, xname
));
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
);
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
) {
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
);
2582 context
.victual
.piece
= (struct obj
*) 0;
2583 context
.victual
.o_id
= 0;
2587 /* if not used up, eatcorpse sets up reqtime and may modify oeaten */
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
) {
2594 u
.uconduct
.unvegan
++;
2595 if (otmp
->otyp
!= EGG
) {
2596 violated_vegetarian();
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
++;
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
;
2618 consume_oeaten(otmp
, 1); /* oeaten >>= 1 */
2623 /* re-calc the nutrition */
2624 basenutrit
= (int) obj_nutrition(otmp
);
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
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
);
2648 context
.victual
.nmod
= context
.victual
.reqtime
% otmp
->oeaten
;
2649 context
.victual
.canchoke
= (u
.uhs
== SATIATED
);
2663 if (!carrying(TIN
)) {
2664 You("have no tin to open.");
2669 if (obj
->cursed
&& obj
->bknown
) {
2672 if (ynq(safe_qbuf(qbuf
, "Really wield ", "?",
2673 obj
, doname
, thesimpleoname
, "that")) != 'y')
2676 if (!wield_tool(obj
, "use"))
2681 otmp
= getobj(comestibles
, "open");
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.
2695 if (context
.victual
.canchoke
&& u
.uhunger
>= 2000) {
2696 choke(context
.victual
.piece
);
2699 if (context
.victual
.doreset
) {
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
)) {
2711 consume_oeaten(context
.victual
.piece
, -1); /* -= 1 */
2713 force_save_hs
= FALSE
;
2718 /* as time goes by - called by moveloop(every move) & domove(melee attack) */
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
))
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
)))
2741 if (near_capacity() > SLT_ENCUMBER
)
2743 } else { /* even turns */
2746 /* Conflict uses up food too */
2747 if (HConflict
|| (EConflict
& (~W_ARTI
)))
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 */
2753 if (uleft
&& (uleft
->spe
|| !objects
[uleft
->otyp
].oc_charged
))
2761 if (uright
&& (uright
->spe
|| !objects
[uright
->otyp
].oc_charged
))
2775 /* called after vomiting and after performing feats of magic */
2784 /* called after eating (and after drinking fruit juice) */
2789 /* See comments in newuhs() for discussion on force_save_hs */
2790 boolean iseating
= (occupation
== eatfood
) || force_save_hs
;
2792 debugpline1("lesshungry(%d)", num
);
2794 if (u
.uhunger
>= 2000) {
2795 if (!iseating
|| context
.victual
.canchoke
) {
2797 choke(context
.victual
.piece
);
2800 choke(occupation
== opentin
? context
.tin
.tin
2801 : (struct obj
*) 0);
2802 /* no reset_eat() */
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
) {
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')
2823 nomovemsg
= (char *) 0;
2837 (void) Hear_again();
2838 if (u
.uhs
> FAINTING
)
2848 return (boolean
) (u
.uhs
== FAINTED
);
2851 /* call when a faint must be prematurely terminated */
2855 if (afternmv
== unfaint
)
2856 unmul("You revive.");
2859 /* compute and comment on your (new?) hunger status */
2865 static unsigned save_hs
;
2866 static boolean saved_hs
= FALSE
;
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
) {
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);
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 */
2922 You("faint from lack of food.");
2923 incr_itimeout(&HDeaf
, duration
);
2924 context
.botl
= TRUE
;
2926 multi_reason
= "fainted from lack of food";
2927 nomovemsg
= "You regain consciousness.";
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
))) {
2941 You("die from starvation.");
2942 killer
.format
= KILLED_BY
;
2943 Strcpy(killer
.name
, "starvation");
2945 /* if we return, we lifesaved, and that calls newuhs */
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
)
2957 if (Hallucination
) {
2958 You((!incr
) ? "now have a lesser case of the munchies."
2959 : "are getting the munchies.");
2961 You((!incr
) ? "only feel hungry now."
2964 : "are beginning to feel hungry.");
2965 if (incr
&& occupation
2966 && (occupation
!= eatfood
&& occupation
!= opentin
))
2968 context
.travel
= context
.travel1
= context
.mv
= context
.run
= 0;
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
))
2983 : (u
.uhunger
< 45) ? "feel weak."
2984 : "are beginning to feel weak.");
2985 if (incr
&& occupation
2986 && (occupation
!= eatfood
&& occupation
!= opentin
))
2988 context
.travel
= context
.travel1
= context
.mv
= context
.run
= 0;
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");
3004 /* Returns an object representing food.
3005 * Object may be either on floor or in inventory.
3008 floorfood(verb
, corpsecheck
)
3010 int corpsecheck
; /* 0, no check, 1, corpses, 2, tinnable corpses */
3012 register struct obj
*otmp
;
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
))))
3026 if (feeding
&& metallivorous(youmonst
.data
)) {
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"
3037 if ((c
= yn_function(qbuf
, ynqchars
, 'n')) == 'y') {
3038 u
.utrap
= u
.utraptype
= 0;
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?");
3051 Sprintf(qbuf
, "There are %ld gold pieces here; eat them?",
3053 if ((c
= yn_function(qbuf
, ynqchars
, 'n')) == 'y') {
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
) {
3064 ? (otmp
->otyp
== CORPSE
3065 && (corpsecheck
== 1 || tinnable(otmp
)))
3066 : feeding
? (otmp
->oclass
!= COIN_CLASS
&& is_edible(otmp
))
3067 : otmp
->oclass
== FOOD_CLASS
) {
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')
3090 return (struct obj
*) 0;
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
,
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;
3108 /* Side effects of vomiting */
3109 /* added nomul (MRS) - it makes sense, you're too busy being sick! */
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.");
3118 make_sick(0L, (char *) 0, TRUE
, SICK_VOMITABLE
);
3120 multi_reason
= "vomiting";
3121 nomovemsg
= You_can_move_again
;
3125 eaten_stat(base
, 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
) {
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 */
3147 consume_oeaten(obj
, 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.
3178 /* bit shift to divide the remaining amount of food */
3179 obj
->oeaten
>>= amt
;
3181 /* simple decrement; value is negative so we actually add it */
3182 if ((int) obj
->oeaten
> -amt
)
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 */
3199 maybe_finished_meal(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
) {
3206 occupation
= 0; /* for do_reset_eat */
3207 (void) eatfood(); /* calls done_eating() to use up
3208 context.victual.piece */
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. :-] */
3226 if (occupation
!= opentin
)
3228 otin
= context
.tin
.tin
;
3229 /* make sure hero still has access to tin */
3231 && (!obj_here(otin
, u
.ux
, u
.uy
) || !can_reach_floor(TRUE
)))
3233 /* unknown tin is assumed to be helpful */
3236 /* known tin is helpful if it will stop life-threatening problem */
3237 mndx
= otin
->corpsenm
;
3239 /* note: not used; hunger code bypasses stop_occupation() when eating */
3241 return (boolean
) (mndx
!= NON_PM
|| otin
->spe
== 1);
3242 /* flesh from lizards and acidic critters stops petrification */
3244 return (boolean
) (mndx
>= LOW_PM
3245 && (mndx
== PM_LIZARD
|| acidic(&mons
[mndx
])));
3246 /* no tins can cure these (yet?) */