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