There was code to give feedback if you attempted to offer the Amulet on a regular altar instead of the final high altar, but that code was unreachable; getobj() yielded "that's a silly thing" whenever you picked an amulet while not on the Astral (or recently changed, Sanctum) level. This allows you to try to offer the real or fake Amulet of Yendor on any altar, but they'll only be listed as likely candidates when on the Astral level. Conversely, it no longer lists carried corpses as likely candidates at the Astral high altars; they're still acceptable but not what the hero is supposed to be fiddling with there. Also, allow corpses on the floor to be offered on high altars, fixing a complaint we've gotten a few times over the years. (Unfortunately there's no way to suppress them as likely candidates on the high altars while still allowing them to be sacrified.)
2986 lines
84 KiB
C
2986 lines
84 KiB
C
/* SCCS Id: @(#)eat.c 3.5 2006/12/07 */
|
|
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
|
|
/* NetHack may be freely redistributed. See license for details. */
|
|
|
|
#include "hack.h"
|
|
|
|
/* #define DEBUG */ /* uncomment to enable new eat code debugging */
|
|
|
|
#ifdef DEBUG
|
|
# ifdef WIZARD
|
|
#define debugpline if (wizard) pline
|
|
# else
|
|
#define debugpline pline
|
|
# endif
|
|
#endif
|
|
|
|
STATIC_PTR int NDECL(eatmdone);
|
|
STATIC_PTR int NDECL(eatfood);
|
|
STATIC_PTR void FDECL(costly_tin, (int));
|
|
STATIC_PTR int NDECL(opentin);
|
|
STATIC_PTR int NDECL(unfaint);
|
|
|
|
STATIC_DCL const char *FDECL(food_xname, (struct obj *,BOOLEAN_P));
|
|
STATIC_DCL void FDECL(choke, (struct obj *));
|
|
STATIC_DCL void NDECL(recalc_wt);
|
|
STATIC_DCL struct obj *FDECL(touchfood, (struct obj *));
|
|
STATIC_DCL void NDECL(do_reset_eat);
|
|
STATIC_DCL void FDECL(done_eating, (BOOLEAN_P));
|
|
STATIC_DCL void FDECL(cprefx, (int));
|
|
STATIC_DCL int FDECL(intrinsic_possible, (int,struct permonst *));
|
|
STATIC_DCL void FDECL(givit, (int,struct permonst *));
|
|
STATIC_DCL void FDECL(cpostfx, (int));
|
|
STATIC_DCL void FDECL(start_tin, (struct obj *));
|
|
STATIC_DCL int FDECL(eatcorpse, (struct obj *));
|
|
STATIC_DCL void FDECL(start_eating, (struct obj *));
|
|
STATIC_DCL void FDECL(fprefx, (struct obj *));
|
|
STATIC_DCL void FDECL(fpostfx, (struct obj *));
|
|
STATIC_DCL int NDECL(bite);
|
|
STATIC_DCL int FDECL(edibility_prompts, (struct obj *));
|
|
STATIC_DCL int FDECL(rottenfood, (struct obj *));
|
|
STATIC_DCL void NDECL(eatspecial);
|
|
STATIC_DCL int FDECL(bounded_increase, (int,int,int));
|
|
STATIC_DCL void FDECL(accessory_has_effect, (struct obj *));
|
|
STATIC_DCL void FDECL(eataccessory, (struct obj *));
|
|
STATIC_DCL const char *FDECL(foodword, (struct obj *));
|
|
STATIC_DCL int FDECL(tin_variety, (struct obj *,BOOLEAN_P));
|
|
STATIC_DCL boolean FDECL(maybe_cannibal, (int,BOOLEAN_P));
|
|
|
|
char msgbuf[BUFSZ];
|
|
|
|
/* hunger texts used on bottom line (each 8 chars long) */
|
|
#define SATIATED 0
|
|
#define NOT_HUNGRY 1
|
|
#define HUNGRY 2
|
|
#define WEAK 3
|
|
#define FAINTING 4
|
|
#define FAINTED 5
|
|
#define STARVED 6
|
|
|
|
/* also used to see if you're allowed to eat cats and dogs */
|
|
#define CANNIBAL_ALLOWED() (Role_if(PM_CAVEMAN) || Race_if(PM_ORC))
|
|
|
|
/* monster types that cause hero to be turned into stone if eaten */
|
|
#define flesh_petrifies(pm) (touch_petrifies(pm) || (pm) == &mons[PM_MEDUSA])
|
|
|
|
/* Rider corpses are treated as non-rotting so that attempting to eat one
|
|
will be sure to reach the stage of eating where that meal is fatal */
|
|
#define nonrotting_corpse(mnum) ((mnum) == PM_LIZARD || \
|
|
(mnum) == PM_LICHEN || \
|
|
is_rider(&mons[mnum]))
|
|
|
|
/* non-rotting non-corpses; unlike lizard corpses, these items will behave
|
|
as if rotten if they are cursed (fortune cookies handled elsewhere) */
|
|
#define nonrotting_food(otyp) ((otyp) == LEMBAS_WAFER || (otyp) == CRAM_RATION)
|
|
|
|
STATIC_OVL NEARDATA const char comestibles[] = { FOOD_CLASS, 0 };
|
|
STATIC_OVL NEARDATA const char offerfodder[] = { FOOD_CLASS, AMULET_CLASS, 0 };
|
|
|
|
/* Gold must come first for getobj(). */
|
|
STATIC_OVL NEARDATA const char allobj[] = {
|
|
COIN_CLASS, WEAPON_CLASS, ARMOR_CLASS, POTION_CLASS, SCROLL_CLASS,
|
|
WAND_CLASS, RING_CLASS, AMULET_CLASS, FOOD_CLASS, TOOL_CLASS,
|
|
GEM_CLASS, ROCK_CLASS, BALL_CLASS, CHAIN_CLASS, SPBOOK_CLASS, 0 };
|
|
|
|
STATIC_OVL boolean force_save_hs = FALSE;
|
|
|
|
const char *hu_stat[] = {
|
|
"Satiated",
|
|
" ",
|
|
"Hungry ",
|
|
"Weak ",
|
|
"Fainting",
|
|
"Fainted ",
|
|
"Starved "
|
|
};
|
|
|
|
/*
|
|
* Decide whether a particular object can be eaten by the possibly
|
|
* polymorphed character. Not used for monster checks.
|
|
*/
|
|
boolean
|
|
is_edible(obj)
|
|
register struct obj *obj;
|
|
{
|
|
/* protect invocation tools but not Rider corpses (handled elsewhere)*/
|
|
/* if (obj->oclass != FOOD_CLASS && obj_resists(obj, 0, 0)) */
|
|
if (objects[obj->otyp].oc_unique)
|
|
return FALSE;
|
|
/* above also prevents the Amulet from being eaten, so we must never
|
|
allow fake amulets to be eaten either [which is already the case] */
|
|
|
|
if (metallivorous(youmonst.data) && is_metallic(obj) &&
|
|
(youmonst.data != &mons[PM_RUST_MONSTER] || is_rustprone(obj)))
|
|
return TRUE;
|
|
if (u.umonnum == PM_GELATINOUS_CUBE && is_organic(obj) &&
|
|
/* [g.cubes can eat containers and retain all contents
|
|
as engulfed items, but poly'd player can't do that] */
|
|
!Has_contents(obj))
|
|
return TRUE;
|
|
|
|
/* return((boolean)(!!index(comestibles, obj->oclass))); */
|
|
return (boolean)(obj->oclass == FOOD_CLASS);
|
|
}
|
|
|
|
void
|
|
init_uhunger()
|
|
{
|
|
u.uhunger = 900;
|
|
u.uhs = NOT_HUNGRY;
|
|
}
|
|
|
|
/* tin types */
|
|
static const struct {
|
|
const char *txt; /* description */
|
|
int nut; /* nutrition */
|
|
Bitfield(fodder,1); /* stocked by health food shops */
|
|
Bitfield(greasy,1); /* causes slippery fingers */
|
|
} tintxts[] = {
|
|
{"rotten", -50, 0, 0}, /* ROTTEN_TIN = 0 */
|
|
{"homemade", 50, 1, 0}, /* HOMEMADE_TIN = 1 */
|
|
{"soup made from", 20, 1, 0},
|
|
{"french fried", 40, 0, 1},
|
|
{"pickled", 40, 1, 0},
|
|
{"boiled", 50, 1, 0},
|
|
{"smoked", 50, 1, 0},
|
|
{"dried", 55, 1, 0},
|
|
{"deep fried", 60, 0, 1},
|
|
{"szechuan", 70, 1, 0},
|
|
{"broiled", 80, 0, 0},
|
|
{"stir fried", 80, 0, 1},
|
|
{"sauteed", 95, 0, 0},
|
|
{"candied", 100, 1, 0},
|
|
{"pureed", 500, 1, 0},
|
|
{"", 0, 0, 0}
|
|
};
|
|
#define TTSZ SIZE(tintxts)
|
|
|
|
static char *eatmbuf = 0; /* set by cpostfx() */
|
|
|
|
STATIC_PTR
|
|
int
|
|
eatmdone(VOID_ARGS) /* called after mimicing is over */
|
|
{
|
|
/* release `eatmbuf' */
|
|
if (eatmbuf) {
|
|
if (nomovemsg == eatmbuf) nomovemsg = 0;
|
|
free((genericptr_t)eatmbuf), eatmbuf = 0;
|
|
}
|
|
/* update display */
|
|
if (youmonst.m_ap_type) {
|
|
youmonst.m_ap_type = M_AP_NOTHING;
|
|
newsym(u.ux,u.uy);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* called when hallucination is toggled */
|
|
void
|
|
eatmupdate()
|
|
{
|
|
const char *altmsg = 0;
|
|
int altapp = 0; /* lint suppression */
|
|
|
|
if (!eatmbuf || nomovemsg != eatmbuf) return;
|
|
|
|
if (youmonst.m_ap_type == M_AP_OBJECT &&
|
|
youmonst.mappearance == ORANGE &&
|
|
!Hallucination) {
|
|
/* revert from hallucinatory to "normal" mimicking */
|
|
altmsg = "You now prefer mimicking yourself.";
|
|
altapp = GOLD_PIECE;
|
|
} else if (youmonst.m_ap_type == M_AP_OBJECT &&
|
|
youmonst.mappearance == GOLD_PIECE &&
|
|
Hallucination) {
|
|
/* won't happen; anything which might make immobilized
|
|
hero begin hallucinating (black light attack, theft
|
|
of Grayswandir) will terminate the mimicry first */
|
|
altmsg = "Your rind escaped intact.";
|
|
altapp = ORANGE;
|
|
}
|
|
|
|
if (altmsg) {
|
|
/* replace end-of-mimicking message */
|
|
if (strlen(altmsg) > strlen(eatmbuf)) {
|
|
free((genericptr_t) eatmbuf);
|
|
eatmbuf = (char *) alloc(strlen(altmsg) + 1);
|
|
}
|
|
nomovemsg = strcpy(eatmbuf, altmsg);
|
|
/* update current image */
|
|
youmonst.mappearance = altapp;
|
|
newsym(u.ux, u.uy);
|
|
}
|
|
}
|
|
|
|
/* ``[the(] singular(food, xname) [)]'' */
|
|
STATIC_OVL const char *
|
|
food_xname(food, the_pfx)
|
|
struct obj *food;
|
|
boolean the_pfx;
|
|
{
|
|
const char *result;
|
|
|
|
if (food->otyp == CORPSE) {
|
|
result = corpse_xname(food, (const char *)0,
|
|
CXN_SINGULAR | (the_pfx ? CXN_PFX_THE : 0));
|
|
/* not strictly needed since pname values are capitalized
|
|
and the() is a no-op for them */
|
|
if (type_is_pname(&mons[food->corpsenm])) the_pfx = FALSE;
|
|
} else {
|
|
/* the ordinary case */
|
|
result = singular(food, xname);
|
|
}
|
|
if (the_pfx) result = the(result);
|
|
return result;
|
|
}
|
|
|
|
/* Created by GAN 01/28/87
|
|
* Amended by AKP 09/22/87: if not hard, don't choke, just vomit.
|
|
* Amended by 3. 06/12/89: if not hard, sometimes choke anyway, to keep risk.
|
|
* 11/10/89: if hard, rarely vomit anyway, for slim chance.
|
|
*/
|
|
STATIC_OVL void
|
|
choke(food) /* To a full belly all food is bad. (It.) */
|
|
register struct obj *food;
|
|
{
|
|
/* only happens if you were satiated */
|
|
if (u.uhs != SATIATED) {
|
|
if (!food || food->otyp != AMULET_OF_STRANGULATION)
|
|
return;
|
|
} else if (Role_if(PM_KNIGHT) && u.ualign.type == A_LAWFUL) {
|
|
adjalign(-1); /* gluttony is unchivalrous */
|
|
You_feel("like a glutton!");
|
|
}
|
|
|
|
exercise(A_CON, FALSE);
|
|
|
|
if (Breathless || (!Strangled && !rn2(20))) {
|
|
/* choking by eating AoS doesn't involve stuffing yourself */
|
|
if (food && food->otyp == AMULET_OF_STRANGULATION) {
|
|
You("choke, but recover your composure.");
|
|
return;
|
|
}
|
|
You("stuff yourself and then vomit voluminously.");
|
|
morehungry(1000); /* you just got *very* sick! */
|
|
vomit();
|
|
} else {
|
|
killer.format = KILLED_BY_AN;
|
|
/*
|
|
* Note all "killer"s below read "Choked on %s" on the
|
|
* high score list & tombstone. So plan accordingly.
|
|
*/
|
|
if(food) {
|
|
You("choke over your %s.", foodword(food));
|
|
if (food->oclass == COIN_CLASS) {
|
|
Strcpy(killer.name, "very rich meal");
|
|
} else {
|
|
killer.format = KILLED_BY;
|
|
Strcpy(killer.name, killer_xname(food));
|
|
}
|
|
} else {
|
|
You("choke over it.");
|
|
Strcpy(killer.name, "quick snack");
|
|
}
|
|
You("die...");
|
|
done(CHOKING);
|
|
}
|
|
}
|
|
|
|
/* modify object wt. depending on time spent consuming it */
|
|
STATIC_OVL void
|
|
recalc_wt()
|
|
{
|
|
struct obj *piece = context.victual.piece;
|
|
|
|
#ifdef DEBUG
|
|
debugpline("Old weight = %d", piece->owt);
|
|
debugpline("Used time = %d, Req'd time = %d",
|
|
context.victual.usedtime, context.victual.reqtime);
|
|
#endif
|
|
piece->owt = weight(piece);
|
|
#ifdef DEBUG
|
|
debugpline("New weight = %d", piece->owt);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
reset_eat() /* called when eating interrupted by an event */
|
|
{
|
|
/* we only set a flag here - the actual reset process is done after
|
|
* the round is spent eating.
|
|
*/
|
|
if(context.victual.eating && !context.victual.doreset) {
|
|
#ifdef DEBUG
|
|
debugpline("reset_eat...");
|
|
#endif
|
|
context.victual.doreset = TRUE;
|
|
}
|
|
return;
|
|
}
|
|
|
|
STATIC_OVL struct obj *
|
|
touchfood(otmp)
|
|
register struct obj *otmp;
|
|
{
|
|
if (otmp->quan > 1L) {
|
|
if(!carried(otmp))
|
|
(void) splitobj(otmp, otmp->quan - 1L);
|
|
else
|
|
otmp = splitobj(otmp, 1L);
|
|
#ifdef DEBUG
|
|
debugpline("split object,");
|
|
#endif
|
|
}
|
|
|
|
if (!otmp->oeaten) {
|
|
costly_alteration(otmp, COST_BITE);
|
|
otmp->oeaten = (otmp->otyp == CORPSE ?
|
|
mons[otmp->corpsenm].cnutrit :
|
|
objects[otmp->otyp].oc_nutrition);
|
|
}
|
|
|
|
if (carried(otmp)) {
|
|
freeinv(otmp);
|
|
if (inv_cnt() >= 52) {
|
|
sellobj_state(SELL_DONTSELL);
|
|
dropy(otmp);
|
|
sellobj_state(SELL_NORMAL);
|
|
} else {
|
|
otmp->nomerge = 1; /* used to prevent merge */
|
|
otmp = addinv(otmp);
|
|
otmp->nomerge = 0;
|
|
}
|
|
}
|
|
return(otmp);
|
|
}
|
|
|
|
/* When food decays, in the middle of your meal, we don't want to dereference
|
|
* any dangling pointers, so set it to null (which should still trigger
|
|
* do_reset_eat() at the beginning of eatfood()) and check for null pointers
|
|
* in do_reset_eat().
|
|
*/
|
|
void
|
|
food_disappears(obj)
|
|
register struct obj *obj;
|
|
{
|
|
if (obj == context.victual.piece) {
|
|
context.victual.piece = (struct obj *)0;
|
|
context.victual.o_id = 0;
|
|
}
|
|
if (obj->timed) obj_stop_timers(obj);
|
|
}
|
|
|
|
/* renaming an object usually results in it having a different address;
|
|
so the sequence start eating/opening, get interrupted, name the food,
|
|
resume eating/opening would restart from scratch */
|
|
void
|
|
food_substitution(old_obj, new_obj)
|
|
struct obj *old_obj, *new_obj;
|
|
{
|
|
if (old_obj == context.victual.piece) {
|
|
context.victual.piece = new_obj;
|
|
context.victual.o_id = new_obj->o_id;
|
|
}
|
|
if (old_obj == context.tin.tin) {
|
|
context.tin.tin = new_obj;
|
|
context.tin.o_id =new_obj->o_id;
|
|
}
|
|
}
|
|
|
|
STATIC_OVL void
|
|
do_reset_eat()
|
|
{
|
|
#ifdef DEBUG
|
|
debugpline("do_reset_eat...");
|
|
#endif
|
|
if (context.victual.piece) {
|
|
context.victual.o_id = 0;
|
|
context.victual.piece = touchfood(context.victual.piece);
|
|
if (context.victual.piece)
|
|
context.victual.o_id = context.victual.piece->o_id;
|
|
recalc_wt();
|
|
}
|
|
context.victual.fullwarn = context.victual.eating = context.victual.doreset = FALSE;
|
|
/* Do not set canchoke to FALSE; if we continue eating the same object
|
|
* we need to know if canchoke was set when they started eating it the
|
|
* previous time. And if we don't continue eating the same object
|
|
* canchoke always gets recalculated anyway.
|
|
*/
|
|
stop_occupation();
|
|
newuhs(FALSE);
|
|
}
|
|
|
|
STATIC_PTR
|
|
int
|
|
eatfood(VOID_ARGS) /* called each move during eating process */
|
|
{
|
|
if(!context.victual.piece ||
|
|
(!carried(context.victual.piece) &&
|
|
!obj_here(context.victual.piece, u.ux, u.uy))) {
|
|
/* maybe it was stolen? */
|
|
do_reset_eat();
|
|
return(0);
|
|
}
|
|
if(!context.victual.eating) return(0);
|
|
|
|
if(++context.victual.usedtime <= context.victual.reqtime) {
|
|
if(bite()) return(0);
|
|
return(1); /* still busy */
|
|
} else { /* done */
|
|
done_eating(TRUE);
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
STATIC_OVL void
|
|
done_eating(message)
|
|
boolean message;
|
|
{
|
|
context.victual.piece->in_use = TRUE;
|
|
occupation = 0; /* do this early, so newuhs() knows we're done */
|
|
newuhs(FALSE);
|
|
if (nomovemsg) {
|
|
if (message) pline(nomovemsg);
|
|
nomovemsg = 0;
|
|
} else if (message)
|
|
You("finish eating %s.", food_xname(context.victual.piece, TRUE));
|
|
|
|
if(context.victual.piece->otyp == CORPSE)
|
|
cpostfx(context.victual.piece->corpsenm);
|
|
else
|
|
fpostfx(context.victual.piece);
|
|
|
|
if (carried(context.victual.piece)) useup(context.victual.piece);
|
|
else useupf(context.victual.piece, 1L);
|
|
context.victual.piece = (struct obj *) 0;
|
|
context.victual.o_id = 0;
|
|
context.victual.fullwarn = context.victual.eating = context.victual.doreset = FALSE;
|
|
}
|
|
|
|
/* handle side-effects of mind flayer's tentacle attack */
|
|
int
|
|
eat_brains(magr, mdef, visflag, dmg_p)
|
|
struct monst *magr, *mdef;
|
|
boolean visflag;
|
|
int *dmg_p; /* for dishing out extra damage in lieu of Int loss */
|
|
{
|
|
struct permonst *pd = mdef->data;
|
|
boolean give_nutrit = FALSE;
|
|
int result = MM_HIT, xtra_dmg = rnd(10);
|
|
|
|
if (magr == &youmonst) {
|
|
You("eat %s brain!", s_suffix(mon_nam(mdef)));
|
|
} else if (mdef == &youmonst) {
|
|
Your("brain is eaten!");
|
|
} else { /* monster against monster */
|
|
if (visflag) pline("%s brain is eaten!", s_suffix(Monnam(mdef)));
|
|
}
|
|
|
|
if (flesh_petrifies(pd)) {
|
|
/* mind flayer has attempted to eat the brains of a petrification
|
|
inducing critter (most likely Medusa; attacking a cockatrice via
|
|
tentacle-touch should have been caught before reaching this far) */
|
|
if (magr == &youmonst) {
|
|
if (!Stone_resistance && !Stoned)
|
|
make_stoned(5L, (char *)0, KILLED_BY_AN, pd->mname);
|
|
} else {
|
|
/* no need to check for poly_when_stoned or Stone_resistance;
|
|
mind flayers don't have those capabilities */
|
|
if (visflag) pline("%s turns to stone!", Monnam(magr));
|
|
monstone(magr);
|
|
if (magr->mhp > 0) {
|
|
/* life-saved; don't continue eating the brains */
|
|
return MM_MISS;
|
|
} else {
|
|
if (magr->mtame && !visflag)
|
|
/* parallels mhitm.c's brief_feeling */
|
|
You("have a sad thought for a moment, then is passes.");
|
|
return MM_AGR_DIED;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (magr == &youmonst) {
|
|
/*
|
|
* player mind flayer is eating something's brain
|
|
*/
|
|
u.uconduct.food++;
|
|
if (!vegan(pd))
|
|
u.uconduct.unvegan++;
|
|
if (!vegetarian(pd))
|
|
violated_vegetarian();
|
|
if (mindless(pd)) { /* (cannibalism not possible here) */
|
|
pline("%s doesn't notice.", Monnam(mdef));
|
|
/* all done; no extra harm inflicted upon target */
|
|
return MM_MISS;
|
|
} else if (is_rider(pd)) {
|
|
pline("Ingesting that is fatal.");
|
|
Sprintf(killer.name, "unwisely ate the brain of %s", pd->mname);
|
|
killer.format = NO_KILLER_PREFIX;
|
|
done(DIED);
|
|
/* life-saving needed to reach here */
|
|
exercise(A_WIS, FALSE);
|
|
*dmg_p += xtra_dmg; /* Rider takes extra damage */
|
|
} else {
|
|
morehungry(-rnd(30)); /* cannot choke */
|
|
if (ABASE(A_INT) < AMAX(A_INT)) {
|
|
/* recover lost Int; won't increase current max */
|
|
ABASE(A_INT) += rnd(4);
|
|
if (ABASE(A_INT) > AMAX(A_INT)) ABASE(A_INT) = AMAX(A_INT);
|
|
context.botl = 1;
|
|
}
|
|
exercise(A_WIS, TRUE);
|
|
*dmg_p += xtra_dmg;
|
|
}
|
|
/* targetting another mind flayer or your own underlying species
|
|
is cannibalism */
|
|
(void) maybe_cannibal(monsndx(pd), TRUE);
|
|
|
|
} else if (mdef == &youmonst) {
|
|
/*
|
|
* monster mind flayer is eating hero's brain
|
|
*/
|
|
/* no such thing as mindless players */
|
|
if (ABASE(A_INT) <= ATTRMIN(A_INT)) {
|
|
static NEARDATA const char brainlessness[] = "brainlessness";
|
|
|
|
if (Lifesaved) {
|
|
Strcpy(killer.name, brainlessness);
|
|
killer.format = KILLED_BY;
|
|
done(DIED);
|
|
/* amulet of life saving has now been used up */
|
|
pline("Unfortunately your brain is still gone.");
|
|
} else {
|
|
Your("last thought fades away.");
|
|
}
|
|
Strcpy(killer.name, brainlessness);
|
|
killer.format = KILLED_BY;
|
|
done(DIED);
|
|
/* can only get here when in wizard or explore mode and user has
|
|
explicitly chosen not to die; arbitrarily boost intelligence */
|
|
ABASE(A_INT) = ATTRMIN(A_INT) + 2;
|
|
You_feel("like a scarecrow.");
|
|
}
|
|
give_nutrit = TRUE; /* in case a conflicted pet is doing this */
|
|
exercise(A_WIS, FALSE);
|
|
/* caller handles Int and memory loss */
|
|
|
|
} else { /* mhitm */
|
|
/*
|
|
* monster mind flayer is eating another monster's brain
|
|
*/
|
|
if (mindless(pd)) {
|
|
if (visflag) pline("%s doesn't notice.", Monnam(mdef));
|
|
return MM_MISS;
|
|
} else if (is_rider(pd)) {
|
|
mondied(magr);
|
|
if (magr->mhp <= 0) result = MM_AGR_DIED;
|
|
/* Rider takes extra damage regardless of whether attacker dies */
|
|
*dmg_p += xtra_dmg;
|
|
} else {
|
|
*dmg_p += xtra_dmg;
|
|
give_nutrit = TRUE;
|
|
if (*dmg_p >= mdef->mhp && visflag)
|
|
pline("%s last thought fades away...", s_suffix(Monnam(mdef)));
|
|
}
|
|
}
|
|
|
|
if (give_nutrit && magr->mtame && !magr->isminion) {
|
|
EDOG(magr)->hungrytime += rnd(60);
|
|
magr->mconf = 0;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/* eating a corpse or egg of one's own species is usually naughty */
|
|
STATIC_OVL boolean
|
|
maybe_cannibal(pm, allowmsg)
|
|
int pm;
|
|
boolean allowmsg;
|
|
{
|
|
static NEARDATA long ate_brains = 0L;
|
|
struct permonst *fptr = &mons[pm]; /* food type */
|
|
|
|
/* when poly'd into a mind flayer, multiple tentacle hits in one
|
|
turn cause multiple digestion checks to occur; avoid giving
|
|
multiple luck penalties for the same attack */
|
|
if (moves == ate_brains) return FALSE;
|
|
ate_brains = moves; /* ate_anything, not just brains... */
|
|
|
|
if (!CANNIBAL_ALLOWED() &&
|
|
/* non-cannibalistic heroes shouldn't eat own species ever
|
|
and also shouldn't eat current species when polymorphed
|
|
(even if having the form of something which doesn't care
|
|
about cannibalism--hero's innate traits aren't altered) */
|
|
(your_race(fptr) ||
|
|
(Upolyd && same_race(youmonst.data, fptr)))) {
|
|
if (allowmsg) {
|
|
if (Upolyd && your_race(fptr))
|
|
You("have a bad feeling deep inside.");
|
|
You("cannibal! You will regret this!");
|
|
}
|
|
HAggravate_monster |= FROMOUTSIDE;
|
|
change_luck(-rn1(4,2)); /* -5..-2 */
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
STATIC_OVL void
|
|
cprefx(pm)
|
|
register int pm;
|
|
{
|
|
(void) maybe_cannibal(pm,TRUE);
|
|
if (flesh_petrifies(&mons[pm])) {
|
|
if (!Stone_resistance &&
|
|
!(poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))) {
|
|
Sprintf(killer.name, "tasting %s meat", mons[pm].mname);
|
|
killer.format = KILLED_BY;
|
|
You("turn to stone.");
|
|
done(STONING);
|
|
if (context.victual.piece)
|
|
context.victual.eating = FALSE;
|
|
return; /* lifesaved */
|
|
}
|
|
}
|
|
|
|
switch(pm) {
|
|
case PM_LITTLE_DOG:
|
|
case PM_DOG:
|
|
case PM_LARGE_DOG:
|
|
case PM_KITTEN:
|
|
case PM_HOUSECAT:
|
|
case PM_LARGE_CAT:
|
|
if (!CANNIBAL_ALLOWED()) {
|
|
You_feel("that eating the %s was a bad idea.", mons[pm].mname);
|
|
HAggravate_monster |= FROMOUTSIDE;
|
|
}
|
|
break;
|
|
case PM_LIZARD:
|
|
if (Stoned) fix_petrification();
|
|
break;
|
|
case PM_DEATH:
|
|
case PM_PESTILENCE:
|
|
case PM_FAMINE:
|
|
{
|
|
pline("Eating that is instantly fatal.");
|
|
Sprintf(killer.name, "unwisely ate the body of %s",
|
|
mons[pm].mname);
|
|
killer.format = NO_KILLER_PREFIX;
|
|
done(DIED);
|
|
/* It so happens that since we know these monsters */
|
|
/* cannot appear in tins, context.victual.piece will always */
|
|
/* be what we want, which is not generally true. */
|
|
if (revive_corpse(context.victual.piece)) {
|
|
context.victual.piece = (struct obj *)0;
|
|
context.victual.o_id = 0;
|
|
}
|
|
return;
|
|
}
|
|
case PM_GREEN_SLIME:
|
|
if (!Slimed && !Unchanging && !flaming(youmonst.data) &&
|
|
youmonst.data != &mons[PM_GREEN_SLIME]) {
|
|
You("don't feel very well.");
|
|
make_slimed(10L, (char*) 0);
|
|
delayed_killer(SLIMED, KILLED_BY_AN, nul);
|
|
}
|
|
/* Fall through */
|
|
default:
|
|
if (acidic(&mons[pm]) && Stoned)
|
|
fix_petrification();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
fix_petrification()
|
|
{
|
|
char buf[BUFSZ];
|
|
|
|
if (Hallucination)
|
|
Sprintf(buf, "What a pity--you just ruined a future piece of %sart!",
|
|
ACURR(A_CHA) > 15 ? "fine " : "");
|
|
else
|
|
Strcpy(buf, "You feel limber!");
|
|
make_stoned(0L, buf, 0, (char *)0);
|
|
}
|
|
|
|
/*
|
|
* If you add an intrinsic that can be gotten by eating a monster, add it
|
|
* to intrinsic_possible() and givit(). (It must already be in prop.h to
|
|
* be an intrinsic property.)
|
|
* It would be very easy to make the intrinsics not try to give you one
|
|
* that you already had by checking to see if you have it in
|
|
* intrinsic_possible() instead of givit().
|
|
*/
|
|
|
|
/* intrinsic_possible() returns TRUE iff a monster can give an intrinsic. */
|
|
STATIC_OVL int
|
|
intrinsic_possible(type, ptr)
|
|
int type;
|
|
register struct permonst *ptr;
|
|
{
|
|
switch (type) {
|
|
case FIRE_RES:
|
|
#ifdef DEBUG
|
|
if (ptr->mconveys & MR_FIRE) {
|
|
debugpline("can get fire resistance");
|
|
return(TRUE);
|
|
} else return(FALSE);
|
|
#else
|
|
return(ptr->mconveys & MR_FIRE);
|
|
#endif
|
|
case SLEEP_RES:
|
|
#ifdef DEBUG
|
|
if (ptr->mconveys & MR_SLEEP) {
|
|
debugpline("can get sleep resistance");
|
|
return(TRUE);
|
|
} else return(FALSE);
|
|
#else
|
|
return(ptr->mconveys & MR_SLEEP);
|
|
#endif
|
|
case COLD_RES:
|
|
#ifdef DEBUG
|
|
if (ptr->mconveys & MR_COLD) {
|
|
debugpline("can get cold resistance");
|
|
return(TRUE);
|
|
} else return(FALSE);
|
|
#else
|
|
return(ptr->mconveys & MR_COLD);
|
|
#endif
|
|
case DISINT_RES:
|
|
#ifdef DEBUG
|
|
if (ptr->mconveys & MR_DISINT) {
|
|
debugpline("can get disintegration resistance");
|
|
return(TRUE);
|
|
} else return(FALSE);
|
|
#else
|
|
return(ptr->mconveys & MR_DISINT);
|
|
#endif
|
|
case SHOCK_RES: /* shock (electricity) resistance */
|
|
#ifdef DEBUG
|
|
if (ptr->mconveys & MR_ELEC) {
|
|
debugpline("can get shock resistance");
|
|
return(TRUE);
|
|
} else return(FALSE);
|
|
#else
|
|
return(ptr->mconveys & MR_ELEC);
|
|
#endif
|
|
case POISON_RES:
|
|
#ifdef DEBUG
|
|
if (ptr->mconveys & MR_POISON) {
|
|
debugpline("can get poison resistance");
|
|
return(TRUE);
|
|
} else return(FALSE);
|
|
#else
|
|
return(ptr->mconveys & MR_POISON);
|
|
#endif
|
|
case TELEPORT:
|
|
#ifdef DEBUG
|
|
if (can_teleport(ptr)) {
|
|
debugpline("can get teleport");
|
|
return(TRUE);
|
|
} else return(FALSE);
|
|
#else
|
|
return(can_teleport(ptr));
|
|
#endif
|
|
case TELEPORT_CONTROL:
|
|
#ifdef DEBUG
|
|
if (control_teleport(ptr)) {
|
|
debugpline("can get teleport control");
|
|
return(TRUE);
|
|
} else return(FALSE);
|
|
#else
|
|
return(control_teleport(ptr));
|
|
#endif
|
|
case TELEPAT:
|
|
#ifdef DEBUG
|
|
if (telepathic(ptr)) {
|
|
debugpline("can get telepathy");
|
|
return(TRUE);
|
|
} else return(FALSE);
|
|
#else
|
|
return(telepathic(ptr));
|
|
#endif
|
|
default:
|
|
return(FALSE);
|
|
}
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
/* givit() tries to give you an intrinsic based on the monster's level
|
|
* and what type of intrinsic it is trying to give you.
|
|
*/
|
|
STATIC_OVL void
|
|
givit(type, ptr)
|
|
int type;
|
|
register struct permonst *ptr;
|
|
{
|
|
register int chance;
|
|
|
|
#ifdef DEBUG
|
|
debugpline("Attempting to give intrinsic %d", type);
|
|
#endif
|
|
/* some intrinsics are easier to get than others */
|
|
switch (type) {
|
|
case POISON_RES:
|
|
if ((ptr == &mons[PM_KILLER_BEE] ||
|
|
ptr == &mons[PM_SCORPION]) && !rn2(4))
|
|
chance = 1;
|
|
else
|
|
chance = 15;
|
|
break;
|
|
case TELEPORT:
|
|
chance = 10;
|
|
break;
|
|
case TELEPORT_CONTROL:
|
|
chance = 12;
|
|
break;
|
|
case TELEPAT:
|
|
chance = 1;
|
|
break;
|
|
default:
|
|
chance = 15;
|
|
break;
|
|
}
|
|
|
|
if (ptr->mlevel <= rn2(chance))
|
|
return; /* failed die roll */
|
|
|
|
switch (type) {
|
|
case FIRE_RES:
|
|
#ifdef DEBUG
|
|
debugpline("Trying to give fire resistance");
|
|
#endif
|
|
if(!(HFire_resistance & FROMOUTSIDE)) {
|
|
You(Hallucination ? "be chillin'." :
|
|
"feel a momentary chill.");
|
|
HFire_resistance |= FROMOUTSIDE;
|
|
}
|
|
break;
|
|
case SLEEP_RES:
|
|
#ifdef DEBUG
|
|
debugpline("Trying to give sleep resistance");
|
|
#endif
|
|
if(!(HSleep_resistance & FROMOUTSIDE)) {
|
|
You_feel("wide awake.");
|
|
HSleep_resistance |= FROMOUTSIDE;
|
|
}
|
|
break;
|
|
case COLD_RES:
|
|
#ifdef DEBUG
|
|
debugpline("Trying to give cold resistance");
|
|
#endif
|
|
if(!(HCold_resistance & FROMOUTSIDE)) {
|
|
You_feel("full of hot air.");
|
|
HCold_resistance |= FROMOUTSIDE;
|
|
}
|
|
break;
|
|
case DISINT_RES:
|
|
#ifdef DEBUG
|
|
debugpline("Trying to give disintegration resistance");
|
|
#endif
|
|
if(!(HDisint_resistance & FROMOUTSIDE)) {
|
|
You_feel(Hallucination ?
|
|
"totally together, man." :
|
|
"very firm.");
|
|
HDisint_resistance |= FROMOUTSIDE;
|
|
}
|
|
break;
|
|
case SHOCK_RES: /* shock (electricity) resistance */
|
|
#ifdef DEBUG
|
|
debugpline("Trying to give shock resistance");
|
|
#endif
|
|
if(!(HShock_resistance & FROMOUTSIDE)) {
|
|
if (Hallucination)
|
|
You_feel("grounded in reality.");
|
|
else
|
|
Your("health currently feels amplified!");
|
|
HShock_resistance |= FROMOUTSIDE;
|
|
}
|
|
break;
|
|
case POISON_RES:
|
|
#ifdef DEBUG
|
|
debugpline("Trying to give poison resistance");
|
|
#endif
|
|
if(!(HPoison_resistance & FROMOUTSIDE)) {
|
|
You_feel(Poison_resistance ?
|
|
"especially healthy." : "healthy.");
|
|
HPoison_resistance |= FROMOUTSIDE;
|
|
}
|
|
break;
|
|
case TELEPORT:
|
|
#ifdef DEBUG
|
|
debugpline("Trying to give teleport");
|
|
#endif
|
|
if(!(HTeleportation & FROMOUTSIDE)) {
|
|
You_feel(Hallucination ? "diffuse." :
|
|
"very jumpy.");
|
|
HTeleportation |= FROMOUTSIDE;
|
|
}
|
|
break;
|
|
case TELEPORT_CONTROL:
|
|
#ifdef DEBUG
|
|
debugpline("Trying to give teleport control");
|
|
#endif
|
|
if(!(HTeleport_control & FROMOUTSIDE)) {
|
|
You_feel(Hallucination ?
|
|
"centered in your personal space." :
|
|
"in control of yourself.");
|
|
HTeleport_control |= FROMOUTSIDE;
|
|
}
|
|
break;
|
|
case TELEPAT:
|
|
#ifdef DEBUG
|
|
debugpline("Trying to give telepathy");
|
|
#endif
|
|
if(!(HTelepat & FROMOUTSIDE)) {
|
|
You_feel(Hallucination ?
|
|
"in touch with the cosmos." :
|
|
"a strange mental acuity.");
|
|
HTelepat |= FROMOUTSIDE;
|
|
/* If blind, make sure monsters show up. */
|
|
if (Blind) see_monsters();
|
|
}
|
|
break;
|
|
default:
|
|
#ifdef DEBUG
|
|
debugpline("Tried to give an impossible intrinsic");
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
STATIC_OVL void
|
|
cpostfx(pm) /* called after completely consuming a corpse */
|
|
register int pm;
|
|
{
|
|
register int tmp = 0;
|
|
boolean catch_lycanthropy = FALSE;
|
|
|
|
/* in case `afternmv' didn't get called for previously mimicking
|
|
gold, clean up now to avoid `eatmbuf' memory leak */
|
|
if (eatmbuf) (void)eatmdone();
|
|
|
|
switch(pm) {
|
|
case PM_NEWT:
|
|
/* MRKR: "eye of newt" may give small magical energy boost */
|
|
if (rn2(3) || 3 * u.uen <= 2 * u.uenmax) {
|
|
int old_uen = u.uen;
|
|
u.uen += rnd(3);
|
|
if (u.uen > u.uenmax) {
|
|
if (!rn2(3)) u.uenmax++;
|
|
u.uen = u.uenmax;
|
|
}
|
|
if (old_uen != u.uen) {
|
|
You_feel("a mild buzz.");
|
|
context.botl = 1;
|
|
}
|
|
}
|
|
break;
|
|
case PM_WRAITH:
|
|
pluslvl(FALSE);
|
|
break;
|
|
case PM_HUMAN_WERERAT:
|
|
catch_lycanthropy = TRUE;
|
|
u.ulycn = PM_WERERAT;
|
|
break;
|
|
case PM_HUMAN_WEREJACKAL:
|
|
catch_lycanthropy = TRUE;
|
|
u.ulycn = PM_WEREJACKAL;
|
|
break;
|
|
case PM_HUMAN_WEREWOLF:
|
|
catch_lycanthropy = TRUE;
|
|
u.ulycn = PM_WEREWOLF;
|
|
break;
|
|
case PM_NURSE:
|
|
if (Upolyd) u.mh = u.mhmax;
|
|
else u.uhp = u.uhpmax;
|
|
context.botl = 1;
|
|
break;
|
|
case PM_STALKER:
|
|
if(!Invis) {
|
|
set_itimeout(&HInvis, (long)rn1(100, 50));
|
|
if (!Blind && !BInvis) self_invis_message();
|
|
} else {
|
|
if (!(HInvis & INTRINSIC)) You_feel("hidden!");
|
|
HInvis |= FROMOUTSIDE;
|
|
HSee_invisible |= FROMOUTSIDE;
|
|
}
|
|
newsym(u.ux, u.uy);
|
|
/* fall into next case */
|
|
case PM_YELLOW_LIGHT:
|
|
/* fall into next case */
|
|
case PM_GIANT_BAT:
|
|
make_stunned(HStun + 30,FALSE);
|
|
/* fall into next case */
|
|
case PM_BAT:
|
|
make_stunned(HStun + 30,FALSE);
|
|
break;
|
|
case PM_GIANT_MIMIC:
|
|
tmp += 10;
|
|
/* fall into next case */
|
|
case PM_LARGE_MIMIC:
|
|
tmp += 20;
|
|
/* fall into next case */
|
|
case PM_SMALL_MIMIC:
|
|
tmp += 20;
|
|
if (youmonst.data->mlet != S_MIMIC && !Unchanging) {
|
|
char buf[BUFSZ];
|
|
|
|
u.uconduct.polyselfs++; /* you're changing form */
|
|
You_cant("resist the temptation to mimic %s.",
|
|
Hallucination ? "an orange" : "a pile of gold");
|
|
#ifdef STEED
|
|
/* A pile of gold can't ride. */
|
|
if (u.usteed) dismount_steed(DISMOUNT_FELL);
|
|
#endif
|
|
nomul(-tmp);
|
|
Sprintf(buf, Hallucination ?
|
|
"You suddenly dread being peeled and mimic %s again!" :
|
|
"You now prefer mimicking %s again.",
|
|
an(Upolyd ? youmonst.data->mname : urace.noun));
|
|
eatmbuf = strcpy((char *) alloc(strlen(buf) + 1), buf);
|
|
nomovemsg = eatmbuf;
|
|
afternmv = eatmdone;
|
|
/* ??? what if this was set before? */
|
|
youmonst.m_ap_type = M_AP_OBJECT;
|
|
youmonst.mappearance = Hallucination ? ORANGE : GOLD_PIECE;
|
|
newsym(u.ux,u.uy);
|
|
curs_on_u();
|
|
/* make gold symbol show up now */
|
|
display_nhwindow(WIN_MAP, TRUE);
|
|
}
|
|
break;
|
|
case PM_QUANTUM_MECHANIC:
|
|
Your("velocity suddenly seems very uncertain!");
|
|
if (HFast & INTRINSIC) {
|
|
HFast &= ~INTRINSIC;
|
|
You("seem slower.");
|
|
} else {
|
|
HFast |= FROMOUTSIDE;
|
|
You("seem faster.");
|
|
}
|
|
break;
|
|
case PM_LIZARD:
|
|
if (HStun > 2) make_stunned(2L,FALSE);
|
|
if (HConfusion > 2) make_confused(2L,FALSE);
|
|
break;
|
|
case PM_CHAMELEON:
|
|
case PM_DOPPELGANGER:
|
|
/* case PM_SANDESTIN: */
|
|
if (!Unchanging) {
|
|
You_feel("a change coming over you.");
|
|
polyself(0);
|
|
}
|
|
break;
|
|
case PM_MIND_FLAYER:
|
|
case PM_MASTER_MIND_FLAYER:
|
|
if (ABASE(A_INT) < ATTRMAX(A_INT)) {
|
|
if (!rn2(2)) {
|
|
pline("Yum! That was real brain food!");
|
|
(void) adjattrib(A_INT, 1, FALSE);
|
|
break; /* don't give them telepathy, too */
|
|
}
|
|
}
|
|
else {
|
|
pline("For some reason, that tasted bland.");
|
|
}
|
|
/* fall through to default case */
|
|
default: {
|
|
register struct permonst *ptr = &mons[pm];
|
|
int i, count;
|
|
|
|
if (dmgtype(ptr, AD_STUN) || dmgtype(ptr, AD_HALU) ||
|
|
pm == PM_VIOLET_FUNGUS) {
|
|
pline ("Oh wow! Great stuff!");
|
|
(void) make_hallucinated(HHallucination + 200,FALSE,0L);
|
|
}
|
|
if(is_giant(ptr)) gainstr((struct obj *)0, 0);
|
|
|
|
/* Check the monster for all of the intrinsics. If this
|
|
* monster can give more than one, pick one to try to give
|
|
* from among all it can give.
|
|
*
|
|
* If a monster can give 4 intrinsics then you have
|
|
* a 1/1 * 1/2 * 2/3 * 3/4 = 1/4 chance of getting the first,
|
|
* a 1/2 * 2/3 * 3/4 = 1/4 chance of getting the second,
|
|
* a 1/3 * 3/4 = 1/4 chance of getting the third,
|
|
* and a 1/4 chance of getting the fourth.
|
|
*
|
|
* And now a proof by induction:
|
|
* it works for 1 intrinsic (1 in 1 of getting it)
|
|
* for 2 you have a 1 in 2 chance of getting the second,
|
|
* otherwise you keep the first
|
|
* for 3 you have a 1 in 3 chance of getting the third,
|
|
* otherwise you keep the first or the second
|
|
* for n+1 you have a 1 in n+1 chance of getting the (n+1)st,
|
|
* otherwise you keep the previous one.
|
|
* Elliott Kleinrock, October 5, 1990
|
|
*/
|
|
|
|
count = 0; /* number of possible intrinsics */
|
|
tmp = 0; /* which one we will try to give */
|
|
for (i = 1; i <= LAST_PROP; i++) {
|
|
if (intrinsic_possible(i, ptr)) {
|
|
count++;
|
|
/* a 1 in count chance of replacing the old
|
|
* one with this one, and a count-1 in count
|
|
* chance of keeping the old one. (note
|
|
* that 1 in 1 and 0 in 1 are what we want
|
|
* for the first one
|
|
*/
|
|
if (!rn2(count)) {
|
|
#ifdef DEBUG
|
|
debugpline("Intrinsic %d replacing %d",
|
|
i, tmp);
|
|
#endif
|
|
tmp = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* if any found try to give them one */
|
|
if (count) givit(tmp, ptr);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (catch_lycanthropy && defends(AD_WERE, uwep)) {
|
|
if (!touch_artifact(uwep, &youmonst)) {
|
|
dropx(uwep);
|
|
uwepgone();
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
violated_vegetarian()
|
|
{
|
|
u.uconduct.unvegetarian++;
|
|
if (Role_if(PM_MONK)) {
|
|
You_feel("guilty.");
|
|
adjalign(-1);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* common code to check and possibly charge for 1 context.tin.tin,
|
|
* will split() context.tin.tin if necessary */
|
|
STATIC_PTR
|
|
void
|
|
costly_tin(alter_type)
|
|
int alter_type; /* COST_xxx */
|
|
{
|
|
struct obj *tin = context.tin.tin;
|
|
|
|
if (carried(tin) ? tin->unpaid :
|
|
(costly_spot(tin->ox, tin->oy) && !tin->no_charge)) {
|
|
if (tin->quan > 1L) {
|
|
tin = context.tin.tin = splitobj(tin, 1L);
|
|
context.tin.o_id = tin->o_id;
|
|
}
|
|
costly_alteration(tin, alter_type);
|
|
}
|
|
}
|
|
|
|
int
|
|
tin_variety_txt(s,tinvariety)
|
|
char *s;
|
|
int *tinvariety;
|
|
{
|
|
int k, l;
|
|
|
|
if (s && tinvariety) {
|
|
*tinvariety = -1;
|
|
for (k = 0; k < TTSZ-1; ++k) {
|
|
l = (int)strlen(tintxts[k].txt);
|
|
if (!strncmpi(s, tintxts[k].txt, l) &&
|
|
((int)strlen(s) > l) && s[l] == ' ') {
|
|
*tinvariety = k;
|
|
return (l + 1);
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* This assumes that buf already contains the word "tin",
|
|
* as is the case with caller xname().
|
|
*/
|
|
void
|
|
tin_details(obj, mnum, buf)
|
|
struct obj *obj;
|
|
int mnum;
|
|
char *buf;
|
|
{
|
|
char buf2[BUFSZ];
|
|
if (obj && buf) {
|
|
if(obj->spe > 0)
|
|
Strcat(buf, " of spinach");
|
|
else if (mnum == NON_PM)
|
|
Strcpy(buf, "empty tin");
|
|
else {
|
|
if ((obj->cknown || iflags.override_ID) && obj->spe < 0) {
|
|
int r = tin_variety(obj, TRUE);
|
|
if (r == ROTTEN_TIN || r == HOMEMADE_TIN) {
|
|
/* put these before the word tin */
|
|
Sprintf(buf2,"%s %s of ", tintxts[r].txt, buf);
|
|
Strcpy(buf, buf2);
|
|
} else {
|
|
Sprintf(eos(buf), " of %s ", tintxts[r].txt);
|
|
}
|
|
} else {
|
|
Strcpy(eos(buf), " of ");
|
|
}
|
|
if (vegetarian(&mons[mnum]))
|
|
Sprintf(eos(buf), "%s", mons[mnum].mname);
|
|
else
|
|
Sprintf(eos(buf), "%s meat", mons[mnum].mname);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
set_tin_variety(obj, forcetype)
|
|
struct obj *obj;
|
|
int forcetype;
|
|
{
|
|
register int r;
|
|
|
|
if (forcetype == SPINACH_TIN ||
|
|
(forcetype == HEALTHY_TIN &&
|
|
(obj->corpsenm == NON_PM || /* empty or already spinach */
|
|
!vegetarian(&mons[obj->corpsenm])))) { /* replace meat */
|
|
obj->corpsenm = NON_PM; /* not based on any monster */
|
|
obj->spe = 1; /* spinach */
|
|
return;
|
|
} else if (forcetype == HEALTHY_TIN) {
|
|
r = tin_variety(obj, FALSE);
|
|
if (r < 0 || r >= TTSZ) r = ROTTEN_TIN; /* shouldn't happen */
|
|
while ((r == ROTTEN_TIN && !obj->cursed) || !tintxts[r].fodder)
|
|
r = rn2(TTSZ-1);
|
|
} else if (forcetype >= 0 && forcetype < TTSZ-1) {
|
|
r = forcetype;
|
|
} else { /* RANDOM_TIN */
|
|
r = rn2(TTSZ-1); /* take your pick */
|
|
if (r == ROTTEN_TIN && nonrotting_corpse(obj->corpsenm))
|
|
r = HOMEMADE_TIN; /* lizards don't rot */
|
|
}
|
|
obj->spe = -(r+1); /* offset by 1 to allow index 0 */
|
|
}
|
|
|
|
STATIC_OVL int
|
|
tin_variety(obj, disp)
|
|
struct obj *obj;
|
|
boolean disp; /* we're just displaying so leave things alone */
|
|
{
|
|
register int r;
|
|
|
|
if (obj->spe == 1) {
|
|
r = SPINACH_TIN;
|
|
} else if (obj->cursed) {
|
|
r = ROTTEN_TIN; /* always rotten if cursed */
|
|
} else if (obj->spe < 0) {
|
|
r = -(obj->spe);
|
|
--r; /* get rid of the offset */
|
|
} else
|
|
r = rn2(TTSZ-1);
|
|
|
|
if (!disp && r == HOMEMADE_TIN &&
|
|
!obj->blessed && !rn2(7))
|
|
r = ROTTEN_TIN; /* some homemade tins go bad */
|
|
|
|
if (r == ROTTEN_TIN && nonrotting_corpse(obj->corpsenm))
|
|
r = HOMEMADE_TIN; /* lizards don't rot */
|
|
return r;
|
|
}
|
|
|
|
STATIC_PTR
|
|
int
|
|
opentin(VOID_ARGS) /* called during each move whilst opening a tin */
|
|
{
|
|
register int r;
|
|
const char *what;
|
|
int which, mnum;
|
|
|
|
if(!carried(context.tin.tin) && !obj_here(context.tin.tin, u.ux, u.uy))
|
|
/* perhaps it was stolen? */
|
|
return(0); /* %% probably we should use tinoid */
|
|
if(context.tin.usedtime++ >= 50) {
|
|
You("give up your attempt to open the tin.");
|
|
return(0);
|
|
}
|
|
if(context.tin.usedtime < context.tin.reqtime)
|
|
return(1); /* still busy */
|
|
if(context.tin.tin->otrapped ||
|
|
(context.tin.tin->cursed && context.tin.tin->spe != -1 && !rn2(8))) {
|
|
b_trapped("tin", 0);
|
|
costly_tin(COST_DSTROY);
|
|
goto use_me;
|
|
}
|
|
|
|
You("succeed in opening the tin.");
|
|
if(context.tin.tin->spe != 1) {
|
|
mnum = context.tin.tin->corpsenm;
|
|
if (mnum == NON_PM) {
|
|
pline("It turns out to be empty.");
|
|
context.tin.tin->dknown = context.tin.tin->known = TRUE;
|
|
costly_tin(COST_OPEN);
|
|
goto use_me;
|
|
}
|
|
r = tin_variety(context.tin.tin, FALSE);
|
|
which = 0; /* 0=>plural, 1=>as-is, 2=>"the" prefix */
|
|
if ((mnum == PM_COCKATRICE || mnum == PM_CHICKATRICE) &&
|
|
(Stone_resistance || Hallucination)) {
|
|
what = "chicken";
|
|
which = 1; /* suppress pluralization */
|
|
} else if (Hallucination) {
|
|
what = rndmonnam();
|
|
} else {
|
|
what = mons[mnum].mname;
|
|
if (the_unique_pm(&mons[mnum])) which = 2;
|
|
else if (type_is_pname(&mons[mnum])) which = 1;
|
|
}
|
|
if (which == 0) what = makeplural(what);
|
|
|
|
pline("It smells like %s%s.", (which == 2) ? "the " : "", what);
|
|
if (yn("Eat it?") == 'n') {
|
|
if (!Hallucination)
|
|
context.tin.tin->dknown = context.tin.tin->known = TRUE;
|
|
if (flags.verbose) You("discard the open tin.");
|
|
costly_tin(COST_OPEN);
|
|
goto use_me;
|
|
}
|
|
/* in case stop_occupation() was called on previous meal */
|
|
context.victual.piece = (struct obj *)0;
|
|
context.victual.o_id = 0;
|
|
context.victual.fullwarn = context.victual.eating =
|
|
context.victual.doreset = FALSE;
|
|
|
|
You("consume %s %s.", tintxts[r].txt, mons[mnum].mname);
|
|
|
|
/* KMH, conduct */
|
|
u.uconduct.food++;
|
|
if (!vegan(&mons[mnum]))
|
|
u.uconduct.unvegan++;
|
|
if (!vegetarian(&mons[mnum]))
|
|
violated_vegetarian();
|
|
|
|
context.tin.tin->dknown = context.tin.tin->known = TRUE;
|
|
cprefx(mnum);
|
|
cpostfx(mnum);
|
|
|
|
/* charge for one at pre-eating cost */
|
|
costly_tin(COST_OPEN);
|
|
|
|
/* check for vomiting added by GAN 01/16/87 */
|
|
if(tintxts[r].nut < 0) make_vomiting((long)rn1(15,10), FALSE);
|
|
else lesshungry(tintxts[r].nut);
|
|
|
|
if (tintxts[r].greasy) {
|
|
/* Assume !Glib, because you can't open tins when Glib. */
|
|
incr_itimeout(&Glib, rnd(15));
|
|
pline("Eating %s food made your %s very slippery.",
|
|
tintxts[r].txt, makeplural(body_part(FINGER)));
|
|
}
|
|
} else {
|
|
if (context.tin.tin->cursed)
|
|
pline("It contains some decaying%s%s substance.",
|
|
Blind ? "" : " ", Blind ? "" : hcolor(NH_GREEN));
|
|
else
|
|
pline("It contains spinach.");
|
|
|
|
if (yn("Eat it?") == 'n') {
|
|
if (!Hallucination && !context.tin.tin->cursed)
|
|
context.tin.tin->dknown = context.tin.tin->known = TRUE;
|
|
if (flags.verbose)
|
|
You("discard the open tin.");
|
|
costly_tin(COST_OPEN);
|
|
goto use_me;
|
|
}
|
|
|
|
context.tin.tin->dknown = context.tin.tin->known = TRUE;
|
|
costly_tin(COST_OPEN);
|
|
|
|
if (!context.tin.tin->cursed)
|
|
pline("This makes you feel like %s!",
|
|
Hallucination ? "Swee'pea" : "Popeye");
|
|
lesshungry(600);
|
|
gainstr(context.tin.tin, 0);
|
|
u.uconduct.food++;
|
|
}
|
|
use_me:
|
|
if (carried(context.tin.tin)) useup(context.tin.tin);
|
|
else useupf(context.tin.tin, 1L);
|
|
context.tin.tin = (struct obj *) 0;
|
|
context.tin.o_id = 0;
|
|
return(0);
|
|
}
|
|
|
|
STATIC_OVL void
|
|
start_tin(otmp) /* called when starting to open a tin */
|
|
register struct obj *otmp;
|
|
{
|
|
register int tmp;
|
|
|
|
if (metallivorous(youmonst.data)) {
|
|
You("bite right into the metal tin...");
|
|
tmp = 1;
|
|
} else if (nolimbs(youmonst.data)) {
|
|
You("cannot handle the tin properly to open it.");
|
|
return;
|
|
} else if (otmp->blessed) {
|
|
pline_The("tin opens like magic!");
|
|
tmp = 1;
|
|
} else if(uwep) {
|
|
switch(uwep->otyp) {
|
|
case TIN_OPENER:
|
|
tmp = 1;
|
|
break;
|
|
case DAGGER:
|
|
case SILVER_DAGGER:
|
|
case ELVEN_DAGGER:
|
|
case ORCISH_DAGGER:
|
|
case ATHAME:
|
|
case CRYSKNIFE:
|
|
tmp = 3;
|
|
break;
|
|
case PICK_AXE:
|
|
case AXE:
|
|
tmp = 6;
|
|
break;
|
|
default:
|
|
goto no_opener;
|
|
}
|
|
pline("Using %s you try to open the tin.",
|
|
yobjnam(uwep, (char *)0));
|
|
} else {
|
|
no_opener:
|
|
pline("It is not so easy to open this tin.");
|
|
if(Glib) {
|
|
pline_The("tin slips from your %s.",
|
|
makeplural(body_part(FINGER)));
|
|
if(otmp->quan > 1L) {
|
|
otmp = splitobj(otmp, 1L);
|
|
}
|
|
if (carried(otmp)) dropx(otmp);
|
|
else stackobj(otmp);
|
|
return;
|
|
}
|
|
tmp = rn1(1 + 500/((int)(ACURR(A_DEX) + ACURRSTR)), 10);
|
|
}
|
|
context.tin.reqtime = tmp;
|
|
context.tin.usedtime = 0;
|
|
context.tin.tin = otmp;
|
|
if (context.tin.tin)
|
|
context.tin.o_id = context.tin.tin->o_id;
|
|
set_occupation(opentin, "opening the tin", 0);
|
|
return;
|
|
}
|
|
|
|
int
|
|
Hear_again(VOID_ARGS) /* called when waking up after fainting */
|
|
{
|
|
/* Chance of deafness going away while fainted/sleepeing/etc. */
|
|
if (!rn2(2))
|
|
set_itimeout(&HDeaf, 0L);
|
|
return 0;
|
|
}
|
|
|
|
/* called on the "first bite" of rotten food */
|
|
STATIC_OVL int
|
|
rottenfood(obj)
|
|
struct obj *obj;
|
|
{
|
|
pline("Blecch! Rotten %s!", foodword(obj));
|
|
if(!rn2(4)) {
|
|
if (Hallucination) You_feel("rather trippy.");
|
|
else You_feel("rather %s.", body_part(LIGHT_HEADED));
|
|
make_confused(HConfusion + d(2,4),FALSE);
|
|
} else if(!rn2(4) && !Blind) {
|
|
pline("Everything suddenly goes dark.");
|
|
make_blinded((long)d(2,10),FALSE);
|
|
if (!Blind) Your(vision_clears);
|
|
} else if(!rn2(3)) {
|
|
const char *what, *where;
|
|
int duration = rnd(10);
|
|
|
|
if (!Blind)
|
|
what = "goes", where = "dark";
|
|
else if (Levitation || Is_airlevel(&u.uz) ||
|
|
Is_waterlevel(&u.uz))
|
|
what = "you lose control of", where = "yourself";
|
|
else
|
|
what = "you slap against the", where =
|
|
#ifdef STEED
|
|
(u.usteed) ? "saddle" :
|
|
#endif
|
|
surface(u.ux,u.uy);
|
|
pline_The("world spins and %s %s.", what, where);
|
|
incr_itimeout(&HDeaf, duration);
|
|
nomul(-duration);
|
|
nomovemsg = "You are conscious again.";
|
|
afternmv = Hear_again;
|
|
return(1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
STATIC_OVL int
|
|
eatcorpse(otmp) /* called when a corpse is selected as food */
|
|
register struct obj *otmp;
|
|
{
|
|
int tp = 0, mnum = otmp->corpsenm;
|
|
long rotted = 0L;
|
|
int retcode = 0;
|
|
boolean stoneable = (flesh_petrifies(&mons[mnum]) &&
|
|
!Stone_resistance &&
|
|
!poly_when_stoned(youmonst.data));
|
|
|
|
/* KMH, conduct */
|
|
if (!vegan(&mons[mnum])) u.uconduct.unvegan++;
|
|
if (!vegetarian(&mons[mnum])) violated_vegetarian();
|
|
|
|
if (!nonrotting_corpse(mnum)) {
|
|
long age = peek_at_iced_corpse_age(otmp);
|
|
|
|
rotted = (monstermoves - age)/(10L + rn2(20));
|
|
if (otmp->cursed) rotted += 2L;
|
|
else if (otmp->blessed) rotted -= 2L;
|
|
}
|
|
|
|
if (mnum != PM_ACID_BLOB && !stoneable && rotted > 5L) {
|
|
boolean cannibal = maybe_cannibal(mnum, FALSE);
|
|
pline("Ulch - that %s was tainted%s!",
|
|
mons[mnum].mlet == S_FUNGUS ? "fungoid vegetation" :
|
|
!vegetarian(&mons[mnum]) ? "meat" : "protoplasm",
|
|
cannibal ? ", you cannibal" : "");
|
|
if (Sick_resistance) {
|
|
pline("It doesn't seem at all sickening, though...");
|
|
} else {
|
|
long sick_time;
|
|
|
|
sick_time = (long) rn1(10, 10);
|
|
/* make sure new ill doesn't result in improvement */
|
|
if (Sick && (sick_time > Sick))
|
|
sick_time = (Sick > 1L) ? Sick - 1L : 1L;
|
|
make_sick(sick_time,
|
|
corpse_xname(otmp, "rotted", CXN_NORMAL),
|
|
TRUE, SICK_VOMITABLE);
|
|
}
|
|
if (carried(otmp)) useup(otmp);
|
|
else useupf(otmp, 1L);
|
|
return(2);
|
|
} else if (acidic(&mons[mnum]) && !Acid_resistance) {
|
|
tp++;
|
|
You("have a very bad case of stomach acid."); /* not body_part() */
|
|
losehp(rnd(15), "acidic corpse", KILLED_BY_AN); /* acid damage */
|
|
} else if (poisonous(&mons[mnum]) && rn2(5)) {
|
|
tp++;
|
|
pline("Ecch - that must have been poisonous!");
|
|
if(!Poison_resistance) {
|
|
losestr(rnd(4));
|
|
losehp(rnd(15), "poisonous corpse", KILLED_BY_AN);
|
|
} else You("seem unaffected by the poison.");
|
|
/* now any corpse left too long will make you mildly ill */
|
|
} else if ((rotted > 5L || (rotted > 3L && rn2(5)))
|
|
&& !Sick_resistance) {
|
|
tp++;
|
|
You_feel("%ssick.", (Sick) ? "very " : "");
|
|
losehp(rnd(8), "cadaver", KILLED_BY_AN);
|
|
}
|
|
|
|
/* delay is weight dependent */
|
|
context.victual.reqtime = 3 + (mons[mnum].cwt >> 6);
|
|
|
|
if (!tp && !nonrotting_corpse(mnum) && (otmp->orotten || !rn2(7))) {
|
|
if (rottenfood(otmp)) {
|
|
otmp->orotten = TRUE;
|
|
(void)touchfood(otmp);
|
|
retcode = 1;
|
|
}
|
|
|
|
if (!mons[otmp->corpsenm].cnutrit) {
|
|
/* no nutrution: rots away, no message if you passed out */
|
|
if (!retcode) pline_The("corpse rots away completely.");
|
|
if (carried(otmp)) useup(otmp);
|
|
else useupf(otmp, 1L);
|
|
retcode = 2;
|
|
}
|
|
|
|
if (!retcode) consume_oeaten(otmp, 2); /* oeaten >>= 2 */
|
|
} else if ((mnum == PM_COCKATRICE || mnum == PM_CHICKATRICE) &&
|
|
(Stone_resistance || Hallucination)) {
|
|
pline("This tastes just like chicken!");
|
|
} else {
|
|
pline("%s%s %s!",
|
|
type_is_pname(&mons[mnum]) ? "" :
|
|
the_unique_pm(&mons[mnum]) ? "The " : "This ",
|
|
food_xname(otmp, FALSE),
|
|
(vegan(&mons[mnum]) ?
|
|
(!carnivorous(youmonst.data) && herbivorous(youmonst.data)) :
|
|
(carnivorous(youmonst.data) && !herbivorous(youmonst.data)))
|
|
? "is delicious" : "tastes terrible");
|
|
}
|
|
|
|
return(retcode);
|
|
}
|
|
|
|
STATIC_OVL void
|
|
start_eating(otmp) /* called as you start to eat */
|
|
register struct obj *otmp;
|
|
{
|
|
#ifdef DEBUG
|
|
debugpline("start_eating: %lx (victual = %lx)", otmp, context.victual.piece);
|
|
debugpline("reqtime = %d", context.victual.reqtime);
|
|
debugpline("(original reqtime = %d)", objects[otmp->otyp].oc_delay);
|
|
debugpline("nmod = %d", context.victual.nmod);
|
|
debugpline("oeaten = %d", otmp->oeaten);
|
|
#endif
|
|
context.victual.fullwarn = context.victual.doreset = FALSE;
|
|
context.victual.eating = TRUE;
|
|
|
|
if (otmp->otyp == CORPSE) {
|
|
cprefx(context.victual.piece->corpsenm);
|
|
if (!context.victual.piece || !context.victual.eating) {
|
|
/* rider revived, or died and lifesaved */
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (bite()) return;
|
|
|
|
if (++context.victual.usedtime >= context.victual.reqtime) {
|
|
/* print "finish eating" message if they just resumed -dlc */
|
|
done_eating(context.victual.reqtime > 1 ? TRUE : FALSE);
|
|
return;
|
|
}
|
|
|
|
Sprintf(msgbuf, "eating %s", food_xname(otmp, TRUE));
|
|
set_occupation(eatfood, msgbuf, 0);
|
|
}
|
|
|
|
|
|
/*
|
|
* called on "first bite" of (non-corpse) food.
|
|
* used for non-rotten non-tin non-corpse food
|
|
*/
|
|
STATIC_OVL void
|
|
fprefx(otmp)
|
|
struct obj *otmp;
|
|
{
|
|
switch(otmp->otyp) {
|
|
case FOOD_RATION:
|
|
if(u.uhunger <= 200)
|
|
pline(Hallucination ? "Oh wow, like, superior, man!" :
|
|
"That food really hit the spot!");
|
|
else if(u.uhunger <= 700) pline("That satiated your %s!",
|
|
body_part(STOMACH));
|
|
break;
|
|
case TRIPE_RATION:
|
|
if (carnivorous(youmonst.data) && !humanoid(youmonst.data))
|
|
pline("That tripe ration was surprisingly good!");
|
|
else if (maybe_polyd(is_orc(youmonst.data), Race_if(PM_ORC)))
|
|
pline(Hallucination ? "Tastes great! Less filling!" :
|
|
"Mmm, tripe... not bad!");
|
|
else {
|
|
pline("Yak - dog food!");
|
|
more_experienced(1,0);
|
|
newexplevel();
|
|
/* not cannibalism, but we use similar criteria
|
|
for deciding whether to be sickened by this meal */
|
|
if (rn2(2) && !CANNIBAL_ALLOWED())
|
|
make_vomiting((long)rn1(context.victual.reqtime, 14), FALSE);
|
|
}
|
|
break;
|
|
case MEATBALL:
|
|
case MEAT_STICK:
|
|
case HUGE_CHUNK_OF_MEAT:
|
|
case MEAT_RING:
|
|
goto give_feedback;
|
|
/* break; */
|
|
case CLOVE_OF_GARLIC:
|
|
if (is_undead(youmonst.data)) {
|
|
make_vomiting((long)rn1(context.victual.reqtime, 5), FALSE);
|
|
break;
|
|
}
|
|
/* Fall through otherwise */
|
|
default:
|
|
if (otmp->otyp==SLIME_MOLD && !otmp->cursed
|
|
&& otmp->spe == current_fruit)
|
|
pline("My, that was a %s %s!",
|
|
Hallucination ? "primo" : "yummy",
|
|
singular(otmp, xname));
|
|
else
|
|
#ifdef UNIX
|
|
if (otmp->otyp == APPLE || otmp->otyp == PEAR) {
|
|
if (!Hallucination) pline("Core dumped.");
|
|
else {
|
|
/* This is based on an old Usenet joke, a fake a.out manual page */
|
|
int x = rnd(100);
|
|
if (x <= 75)
|
|
pline("Segmentation fault -- core dumped.");
|
|
else if (x <= 99)
|
|
pline("Bus error -- core dumped.");
|
|
else pline("Yo' mama -- core dumped.");
|
|
}
|
|
} else
|
|
#endif
|
|
#ifdef MAC /* KMH -- Why should Unix have all the fun? */
|
|
if (otmp->otyp == APPLE) {
|
|
pline("Delicious! Must be a Macintosh!");
|
|
} else
|
|
#endif
|
|
if (otmp->otyp == EGG && stale_egg(otmp)) {
|
|
pline("Ugh. Rotten egg."); /* perhaps others like it */
|
|
make_vomiting(Vomiting+d(10,4), TRUE);
|
|
} else
|
|
give_feedback:
|
|
pline("This %s is %s", singular(otmp, xname),
|
|
otmp->cursed ? (Hallucination ? "grody!" : "terrible!") :
|
|
(otmp->otyp == CRAM_RATION
|
|
|| otmp->otyp == K_RATION
|
|
|| otmp->otyp == C_RATION)
|
|
? "bland." :
|
|
Hallucination ? "gnarly!" : "delicious!");
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* increment a combat intrinsic with limits on its growth */
|
|
STATIC_OVL int
|
|
bounded_increase(old, inc, typ)
|
|
int old, inc, typ;
|
|
{
|
|
int absold, absinc, sgnold, sgninc;
|
|
|
|
/* don't include any amount coming from worn rings */
|
|
if (uright && uright->otyp == typ) old -= uright->spe;
|
|
if (uleft && uleft->otyp == typ) old -= uleft->spe;
|
|
absold = abs(old), absinc = abs(inc);
|
|
sgnold = sgn(old), sgninc = sgn(inc);
|
|
|
|
if (absinc == 0 || sgnold != sgninc || absold + absinc < 10) {
|
|
; /* use inc as-is */
|
|
} else if (absold + absinc < 20) {
|
|
absinc = rnd(absinc); /* 1..n */
|
|
if (absold + absinc < 10) absinc = 10 - absold;
|
|
inc = sgninc * absinc;
|
|
} else if (absold + absinc < 40) {
|
|
absinc = rn2(absinc) ? 1 : 0;
|
|
if (absold + absinc < 20) absinc = rnd(20 - absold);
|
|
inc = sgninc * absinc;
|
|
} else {
|
|
inc = 0; /* no further increase allowed via this method */
|
|
}
|
|
return old + inc;
|
|
}
|
|
|
|
STATIC_OVL void
|
|
accessory_has_effect(otmp)
|
|
struct obj *otmp;
|
|
{
|
|
pline("Magic spreads through your body as you digest the %s.",
|
|
otmp->oclass == RING_CLASS ? "ring" : "amulet");
|
|
}
|
|
|
|
STATIC_OVL void
|
|
eataccessory(otmp)
|
|
struct obj *otmp;
|
|
{
|
|
int typ = otmp->otyp;
|
|
long oldprop;
|
|
|
|
/* Note: rings are not so common that this is unbalancing. */
|
|
/* (How often do you even _find_ 3 rings of polymorph in a game?) */
|
|
oldprop = u.uprops[objects[typ].oc_oprop].intrinsic;
|
|
if (otmp == uleft || otmp == uright) {
|
|
Ring_gone(otmp);
|
|
if (u.uhp <= 0) return; /* died from sink fall */
|
|
}
|
|
otmp->known = otmp->dknown = 1; /* by taste */
|
|
if (!rn2(otmp->oclass == RING_CLASS ? 3 : 5)) {
|
|
switch (otmp->otyp) {
|
|
default:
|
|
if (!objects[typ].oc_oprop) break; /* should never happen */
|
|
|
|
if (!(u.uprops[objects[typ].oc_oprop].intrinsic & FROMOUTSIDE))
|
|
accessory_has_effect(otmp);
|
|
|
|
u.uprops[objects[typ].oc_oprop].intrinsic |= FROMOUTSIDE;
|
|
|
|
switch (typ) {
|
|
case RIN_SEE_INVISIBLE:
|
|
set_mimic_blocking();
|
|
see_monsters();
|
|
if (Invis && !oldprop && !ESee_invisible &&
|
|
!perceives(youmonst.data) && !Blind) {
|
|
newsym(u.ux,u.uy);
|
|
pline("Suddenly you can see yourself.");
|
|
makeknown(typ);
|
|
}
|
|
break;
|
|
case RIN_INVISIBILITY:
|
|
if (!oldprop && !EInvis && !BInvis &&
|
|
!See_invisible && !Blind) {
|
|
newsym(u.ux,u.uy);
|
|
Your("body takes on a %s transparency...",
|
|
Hallucination ? "normal" : "strange");
|
|
makeknown(typ);
|
|
}
|
|
break;
|
|
case RIN_PROTECTION_FROM_SHAPE_CHAN:
|
|
rescham();
|
|
break;
|
|
case RIN_LEVITATION:
|
|
/* undo the `.intrinsic |= FROMOUTSIDE' done above */
|
|
u.uprops[LEVITATION].intrinsic = oldprop;
|
|
if (!Levitation) {
|
|
float_up();
|
|
incr_itimeout(&HLevitation, d(10,20));
|
|
makeknown(typ);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case RIN_ADORNMENT:
|
|
accessory_has_effect(otmp);
|
|
if (adjattrib(A_CHA, otmp->spe, -1))
|
|
makeknown(typ);
|
|
break;
|
|
case RIN_GAIN_STRENGTH:
|
|
accessory_has_effect(otmp);
|
|
if (adjattrib(A_STR, otmp->spe, -1))
|
|
makeknown(typ);
|
|
break;
|
|
case RIN_GAIN_CONSTITUTION:
|
|
accessory_has_effect(otmp);
|
|
if (adjattrib(A_CON, otmp->spe, -1))
|
|
makeknown(typ);
|
|
break;
|
|
case RIN_INCREASE_ACCURACY:
|
|
accessory_has_effect(otmp);
|
|
u.uhitinc = (schar)bounded_increase((int)u.uhitinc, otmp->spe,
|
|
RIN_INCREASE_ACCURACY);
|
|
break;
|
|
case RIN_INCREASE_DAMAGE:
|
|
accessory_has_effect(otmp);
|
|
u.udaminc = (schar)bounded_increase((int)u.udaminc, otmp->spe,
|
|
RIN_INCREASE_DAMAGE);
|
|
break;
|
|
case RIN_PROTECTION:
|
|
accessory_has_effect(otmp);
|
|
HProtection |= FROMOUTSIDE;
|
|
u.ublessed = bounded_increase(u.ublessed, otmp->spe,
|
|
RIN_PROTECTION);
|
|
context.botl = 1;
|
|
break;
|
|
case RIN_FREE_ACTION:
|
|
/* Give sleep resistance instead */
|
|
if (!(HSleep_resistance & FROMOUTSIDE))
|
|
accessory_has_effect(otmp);
|
|
if (!Sleep_resistance)
|
|
You_feel("wide awake.");
|
|
HSleep_resistance |= FROMOUTSIDE;
|
|
break;
|
|
case AMULET_OF_CHANGE:
|
|
accessory_has_effect(otmp);
|
|
makeknown(typ);
|
|
change_sex();
|
|
You("are suddenly very %s!",
|
|
flags.female ? "feminine" : "masculine");
|
|
context.botl = 1;
|
|
break;
|
|
case AMULET_OF_UNCHANGING:
|
|
/* un-change: it's a pun */
|
|
if (!Unchanging && Upolyd) {
|
|
accessory_has_effect(otmp);
|
|
makeknown(typ);
|
|
rehumanize();
|
|
}
|
|
break;
|
|
case AMULET_OF_STRANGULATION: /* bad idea! */
|
|
/* no message--this gives no permanent effect */
|
|
choke(otmp);
|
|
break;
|
|
case AMULET_OF_RESTFUL_SLEEP: /* another bad idea! */
|
|
if (!(HSleeping & FROMOUTSIDE))
|
|
accessory_has_effect(otmp);
|
|
HSleeping = FROMOUTSIDE | rnd(100);
|
|
break;
|
|
case RIN_SUSTAIN_ABILITY:
|
|
case AMULET_OF_LIFE_SAVING:
|
|
case AMULET_OF_REFLECTION: /* nice try */
|
|
/* can't eat Amulet of Yendor or fakes,
|
|
* and no oc_prop even if you could -3.
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
STATIC_OVL void
|
|
eatspecial() /* called after eating non-food */
|
|
{
|
|
register struct obj *otmp = context.victual.piece;
|
|
|
|
/* lesshungry wants an occupation to handle choke messages correctly */
|
|
set_occupation(eatfood, "eating non-food", 0);
|
|
lesshungry(context.victual.nmod);
|
|
occupation = 0;
|
|
context.victual.piece = (struct obj *)0;
|
|
context.victual.o_id = 0;
|
|
context.victual.eating = 0;
|
|
if (otmp->oclass == COIN_CLASS) {
|
|
#ifdef GOLDOBJ
|
|
if (carried(otmp))
|
|
useupall(otmp);
|
|
#else
|
|
if (otmp->where == OBJ_FREE)
|
|
dealloc_obj(otmp);
|
|
#endif
|
|
else
|
|
useupf(otmp, otmp->quan);
|
|
vault_gd_watching(GD_EATGOLD);
|
|
return;
|
|
}
|
|
#ifdef MAIL
|
|
if (otmp->otyp == SCR_MAIL) {
|
|
/* no nutrition */
|
|
pline("This junk mail is less than satisfying.");
|
|
}
|
|
#endif
|
|
if (otmp->oclass == POTION_CLASS) {
|
|
otmp->quan++; /* dopotion() does a useup() */
|
|
(void)dopotion(otmp);
|
|
}
|
|
if (otmp->oclass == RING_CLASS || otmp->oclass == AMULET_CLASS)
|
|
eataccessory(otmp);
|
|
else if (otmp->otyp == LEASH && otmp->leashmon)
|
|
o_unleash(otmp);
|
|
|
|
/* KMH -- idea by "Tommy the Terrorist" */
|
|
if ((otmp->otyp == TRIDENT) && !otmp->cursed)
|
|
{
|
|
pline(Hallucination ? "Four out of five dentists agree." :
|
|
"That was pure chewing satisfaction!");
|
|
exercise(A_WIS, TRUE);
|
|
}
|
|
if ((otmp->otyp == FLINT) && !otmp->cursed)
|
|
{
|
|
pline("Yabba-dabba delicious!");
|
|
exercise(A_CON, TRUE);
|
|
}
|
|
|
|
if (otmp == uwep && otmp->quan == 1L) uwepgone();
|
|
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
|
|
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
|
|
|
|
if (otmp == uball) unpunish();
|
|
if (otmp == uchain) unpunish(); /* but no useup() */
|
|
else if (carried(otmp)) useup(otmp);
|
|
else useupf(otmp, 1L);
|
|
}
|
|
|
|
/* NOTE: the order of these words exactly corresponds to the
|
|
order of oc_material values #define'd in objclass.h. */
|
|
static const char *foodwords[] = {
|
|
"meal", "liquid", "wax", "food", "meat",
|
|
"paper", "cloth", "leather", "wood", "bone", "scale",
|
|
"metal", "metal", "metal", "silver", "gold", "platinum", "mithril",
|
|
"plastic", "glass", "rich food", "stone"
|
|
};
|
|
|
|
STATIC_OVL const char *
|
|
foodword(otmp)
|
|
register struct obj *otmp;
|
|
{
|
|
if (otmp->oclass == FOOD_CLASS) return "food";
|
|
if (otmp->oclass == GEM_CLASS &&
|
|
objects[otmp->otyp].oc_material == GLASS &&
|
|
otmp->dknown)
|
|
makeknown(otmp->otyp);
|
|
return foodwords[objects[otmp->otyp].oc_material];
|
|
}
|
|
|
|
STATIC_OVL void
|
|
fpostfx(otmp) /* called after consuming (non-corpse) food */
|
|
register struct obj *otmp;
|
|
{
|
|
switch(otmp->otyp) {
|
|
case SPRIG_OF_WOLFSBANE:
|
|
if (u.ulycn >= LOW_PM || is_were(youmonst.data))
|
|
you_unwere(TRUE);
|
|
break;
|
|
case CARROT:
|
|
if (!u.uswallow ||
|
|
!attacktype_fordmg(u.ustuck->data, AT_ENGL, AD_BLND))
|
|
make_blinded((long)u.ucreamed,TRUE);
|
|
break;
|
|
case FORTUNE_COOKIE:
|
|
outrumor(bcsign(otmp), BY_COOKIE);
|
|
if (!Blind) u.uconduct.literate++;
|
|
break;
|
|
case LUMP_OF_ROYAL_JELLY:
|
|
/* This stuff seems to be VERY healthy! */
|
|
gainstr(otmp, 1);
|
|
if (Upolyd) {
|
|
u.mh += otmp->cursed ? -rnd(20) : rnd(20);
|
|
if (u.mh > u.mhmax) {
|
|
if (!rn2(17)) u.mhmax++;
|
|
u.mh = u.mhmax;
|
|
} else if (u.mh <= 0) {
|
|
rehumanize();
|
|
}
|
|
} else {
|
|
u.uhp += otmp->cursed ? -rnd(20) : rnd(20);
|
|
if (u.uhp > u.uhpmax) {
|
|
if(!rn2(17)) u.uhpmax++;
|
|
u.uhp = u.uhpmax;
|
|
} else if (u.uhp <= 0) {
|
|
killer.format = KILLED_BY_AN;
|
|
Strcpy(killer.name, "rotten lump of royal jelly");
|
|
done(POISONING);
|
|
}
|
|
}
|
|
if(!otmp->cursed) heal_legs();
|
|
break;
|
|
case EGG:
|
|
if (flesh_petrifies(&mons[otmp->corpsenm])) {
|
|
if (!Stone_resistance &&
|
|
!(poly_when_stoned(youmonst.data) &&
|
|
polymon(PM_STONE_GOLEM))) {
|
|
if (!Stoned) {
|
|
Sprintf(killer.name,
|
|
"%s egg", mons[otmp->corpsenm].mname);
|
|
make_stoned(5L, (char *)0,
|
|
KILLED_BY_AN, killer.name);
|
|
}
|
|
}
|
|
/* note: no "tastes like chicken" message for eggs */
|
|
}
|
|
break;
|
|
case EUCALYPTUS_LEAF:
|
|
if (Sick && !otmp->cursed)
|
|
make_sick(0L, (char *)0, TRUE, SICK_ALL);
|
|
if (Vomiting && !otmp->cursed)
|
|
make_vomiting(0L, TRUE);
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
#if 0
|
|
/* intended for eating a spellbook while polymorphed, but not used;
|
|
"leather" applied to appearance, not composition, and has been
|
|
changed to "leathery" to reflect that */
|
|
STATIC_DCL boolean FDECL(leather_cover, (struct obj *));
|
|
|
|
STATIC_OVL boolean
|
|
leather_cover(otmp)
|
|
struct obj *otmp;
|
|
{
|
|
const char *odesc = OBJ_DESCR(objects[otmp->otyp]);
|
|
|
|
if (odesc && (otmp->oclass == SPBOOK_CLASS)) {
|
|
if (!strcmp(odesc, "leather"))
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* return 0 if the food was not dangerous.
|
|
* return 1 if the food was dangerous and you chose to stop.
|
|
* return 2 if the food was dangerous and you chose to eat it anyway.
|
|
*/
|
|
STATIC_OVL int
|
|
edibility_prompts(otmp)
|
|
struct obj *otmp;
|
|
{
|
|
/* blessed food detection granted you a one-use
|
|
ability to detect food that is unfit for consumption
|
|
or dangerous and avoid it. */
|
|
|
|
char buf[BUFSZ], foodsmell[BUFSZ],
|
|
it_or_they[QBUFSZ], eat_it_anyway[QBUFSZ];
|
|
boolean cadaver = (otmp->otyp == CORPSE),
|
|
stoneorslime = FALSE;
|
|
int material = objects[otmp->otyp].oc_material,
|
|
mnum = otmp->corpsenm;
|
|
long rotted = 0L;
|
|
|
|
Strcpy(foodsmell, Tobjnam(otmp, "smell"));
|
|
Strcpy(it_or_they, (otmp->quan == 1L) ? "it" : "they");
|
|
Sprintf(eat_it_anyway, "Eat %s anyway?",
|
|
(otmp->quan == 1L) ? "it" : "one");
|
|
|
|
if (cadaver || otmp->otyp == EGG || otmp->otyp == TIN) {
|
|
/* These checks must match those in eatcorpse() */
|
|
stoneorslime = (flesh_petrifies(&mons[mnum]) &&
|
|
!Stone_resistance &&
|
|
!poly_when_stoned(youmonst.data));
|
|
|
|
if (mnum == PM_GREEN_SLIME)
|
|
stoneorslime = (!Unchanging && !flaming(youmonst.data) &&
|
|
youmonst.data != &mons[PM_GREEN_SLIME]);
|
|
|
|
if (cadaver && !nonrotting_corpse(mnum)) {
|
|
long age = peek_at_iced_corpse_age(otmp);
|
|
/* worst case rather than random
|
|
in this calculation to force prompt */
|
|
rotted = (monstermoves - age)/(10L + 0 /* was rn2(20) */);
|
|
if (otmp->cursed) rotted += 2L;
|
|
else if (otmp->blessed) rotted -= 2L;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* These problems with food should be checked in
|
|
* order from most detrimental to least detrimental.
|
|
*/
|
|
|
|
if (cadaver && mnum != PM_ACID_BLOB && rotted > 5L && !Sick_resistance) {
|
|
/* Tainted meat */
|
|
Sprintf(buf, "%s like %s could be tainted! %s",
|
|
foodsmell, it_or_they, eat_it_anyway);
|
|
if (yn_function(buf,ynchars,'n')=='n') return 1;
|
|
else return 2;
|
|
}
|
|
if (stoneorslime) {
|
|
Sprintf(buf, "%s like %s could be something very dangerous! %s",
|
|
foodsmell, it_or_they, eat_it_anyway);
|
|
if (yn_function(buf,ynchars,'n')=='n') return 1;
|
|
else return 2;
|
|
}
|
|
if (otmp->orotten || (cadaver && rotted > 3L)) {
|
|
/* Rotten */
|
|
Sprintf(buf, "%s like %s could be rotten! %s",
|
|
foodsmell, it_or_they, eat_it_anyway);
|
|
if (yn_function(buf,ynchars,'n')=='n') return 1;
|
|
else return 2;
|
|
}
|
|
if (cadaver && poisonous(&mons[mnum]) && !Poison_resistance) {
|
|
/* poisonous */
|
|
Sprintf(buf, "%s like %s might be poisonous! %s",
|
|
foodsmell, it_or_they, eat_it_anyway);
|
|
if (yn_function(buf,ynchars,'n')=='n') return 1;
|
|
else return 2;
|
|
}
|
|
if (cadaver && !vegetarian(&mons[mnum]) &&
|
|
!u.uconduct.unvegetarian && Role_if(PM_MONK)) {
|
|
Sprintf(buf, "%s unhealthy. %s",
|
|
foodsmell, eat_it_anyway);
|
|
if (yn_function(buf,ynchars,'n')=='n') return 1;
|
|
else return 2;
|
|
}
|
|
if (cadaver && acidic(&mons[mnum]) && !Acid_resistance) {
|
|
Sprintf(buf, "%s rather acidic. %s",
|
|
foodsmell, eat_it_anyway);
|
|
if (yn_function(buf,ynchars,'n')=='n') return 1;
|
|
else return 2;
|
|
}
|
|
if (Upolyd && u.umonnum == PM_RUST_MONSTER &&
|
|
is_metallic(otmp) && otmp->oerodeproof) {
|
|
Sprintf(buf, "%s disgusting to you right now. %s",
|
|
foodsmell, eat_it_anyway);
|
|
if (yn_function(buf,ynchars,'n')=='n') return 1;
|
|
else return 2;
|
|
}
|
|
|
|
/*
|
|
* Breaks conduct, but otherwise safe.
|
|
*/
|
|
|
|
if (!u.uconduct.unvegan &&
|
|
((material == LEATHER || material == BONE ||
|
|
material == DRAGON_HIDE || material == WAX) ||
|
|
(cadaver && !vegan(&mons[mnum])))) {
|
|
Sprintf(buf, "%s foul and unfamiliar to you. %s",
|
|
foodsmell, eat_it_anyway);
|
|
if (yn_function(buf,ynchars,'n')=='n') return 1;
|
|
else return 2;
|
|
}
|
|
if (!u.uconduct.unvegetarian &&
|
|
((material == LEATHER || material == BONE ||
|
|
material == DRAGON_HIDE) ||
|
|
(cadaver && !vegetarian(&mons[mnum])))) {
|
|
Sprintf(buf, "%s unfamiliar to you. %s",
|
|
foodsmell, eat_it_anyway);
|
|
if (yn_function(buf,ynchars,'n')=='n') return 1;
|
|
else return 2;
|
|
}
|
|
|
|
if (cadaver && mnum != PM_ACID_BLOB && rotted > 5L && Sick_resistance) {
|
|
/* Tainted meat with Sick_resistance */
|
|
Sprintf(buf, "%s like %s could be tainted! %s",
|
|
foodsmell, it_or_they, eat_it_anyway);
|
|
if (yn_function(buf,ynchars,'n')=='n') return 1;
|
|
else return 2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
doeat() /* generic "eat" command funtion (see cmd.c) */
|
|
{
|
|
register struct obj *otmp;
|
|
int basenutrit; /* nutrition of full item */
|
|
boolean dont_start = FALSE, nodelicious = FALSE;
|
|
|
|
if (Strangled) {
|
|
pline("If you can't breathe air, how can you consume solids?");
|
|
return 0;
|
|
}
|
|
if (!(otmp = floorfood("eat", 0))) return 0;
|
|
if (check_capacity((char *)0)) return 0;
|
|
|
|
if (u.uedibility) {
|
|
int res = edibility_prompts(otmp);
|
|
if (res) {
|
|
Your("%s stops tingling and your sense of smell returns to normal.",
|
|
body_part(NOSE));
|
|
u.uedibility = 0;
|
|
if (res == 1) return 0;
|
|
}
|
|
}
|
|
|
|
/* We have to make non-foods take 1 move to eat, unless we want to
|
|
* do ridiculous amounts of coding to deal with partly eaten plate
|
|
* mails, players who polymorph back to human in the middle of their
|
|
* metallic meal, etc....
|
|
*/
|
|
if (!is_edible(otmp)) {
|
|
You("cannot eat that!");
|
|
return 0;
|
|
} else if ((otmp->owornmask & (W_ARMOR|W_TOOL|W_AMUL
|
|
#ifdef STEED
|
|
|W_SADDLE
|
|
#endif
|
|
)) != 0) {
|
|
/* let them eat rings */
|
|
You_cant("eat %s you're wearing.", something);
|
|
return 0;
|
|
}
|
|
if (is_metallic(otmp) &&
|
|
u.umonnum == PM_RUST_MONSTER && otmp->oerodeproof) {
|
|
otmp->rknown = TRUE;
|
|
if (otmp->quan > 1L) {
|
|
if(!carried(otmp))
|
|
(void) splitobj(otmp, otmp->quan - 1L);
|
|
else
|
|
otmp = splitobj(otmp, 1L);
|
|
}
|
|
pline("Ulch - that %s was rustproofed!", xname(otmp));
|
|
/* The regurgitated object's rustproofing is gone now */
|
|
otmp->oerodeproof = 0;
|
|
make_stunned(HStun + rn2(10), TRUE);
|
|
You("spit %s out onto the %s.", the(xname(otmp)),
|
|
surface(u.ux, u.uy));
|
|
if (carried(otmp)) {
|
|
freeinv(otmp);
|
|
dropy(otmp);
|
|
}
|
|
stackobj(otmp);
|
|
return 1;
|
|
}
|
|
/* KMH -- Slow digestion is... indigestible */
|
|
if (otmp->otyp == RIN_SLOW_DIGESTION) {
|
|
pline("This ring is indigestible!");
|
|
(void) rottenfood(otmp);
|
|
if (otmp->dknown && !objects[otmp->otyp].oc_name_known
|
|
&& !objects[otmp->otyp].oc_uname)
|
|
docall(otmp);
|
|
return (1);
|
|
}
|
|
if (otmp->oclass != FOOD_CLASS) {
|
|
int material;
|
|
context.victual.reqtime = 1;
|
|
context.victual.piece = otmp;
|
|
context.victual.o_id = otmp->o_id;
|
|
/* Don't split it, we don't need to if it's 1 move */
|
|
context.victual.usedtime = 0;
|
|
context.victual.canchoke = (u.uhs == SATIATED);
|
|
/* Note: gold weighs 1 pt. for each 1000 pieces (see */
|
|
/* pickup.c) so gold and non-gold is consistent. */
|
|
if (otmp->oclass == COIN_CLASS)
|
|
basenutrit = ((otmp->quan > 200000L) ? 2000
|
|
: (int)(otmp->quan/100L));
|
|
else if(otmp->oclass == BALL_CLASS || otmp->oclass == CHAIN_CLASS)
|
|
basenutrit = weight(otmp);
|
|
/* oc_nutrition is usually weight anyway */
|
|
else basenutrit = objects[otmp->otyp].oc_nutrition;
|
|
#ifdef MAIL
|
|
if (otmp->otyp == SCR_MAIL) {
|
|
basenutrit = 0;
|
|
nodelicious = TRUE;
|
|
}
|
|
#endif
|
|
context.victual.nmod = basenutrit;
|
|
context.victual.eating = TRUE; /* needed for lesshungry() */
|
|
|
|
material = objects[otmp->otyp].oc_material;
|
|
if (material == LEATHER ||
|
|
material == BONE || material == DRAGON_HIDE) {
|
|
u.uconduct.unvegan++;
|
|
violated_vegetarian();
|
|
} else if (material == WAX)
|
|
u.uconduct.unvegan++;
|
|
u.uconduct.food++;
|
|
|
|
if (otmp->cursed)
|
|
(void) rottenfood(otmp);
|
|
|
|
if (otmp->oclass == WEAPON_CLASS && otmp->opoisoned) {
|
|
pline("Ecch - that must have been poisonous!");
|
|
if(!Poison_resistance) {
|
|
losestr(rnd(4));
|
|
losehp(rnd(15), xname(otmp), KILLED_BY_AN);
|
|
} else
|
|
You("seem unaffected by the poison.");
|
|
} else if (!otmp->cursed && !nodelicious)
|
|
pline("%s%s is delicious!",
|
|
(obj_is_pname(otmp) &&
|
|
(otmp->oartifact < ART_ORB_OF_DETECTION)) ? "" : "This ",
|
|
otmp->oclass == COIN_CLASS ? foodword(otmp) :
|
|
singular(otmp, xname));
|
|
|
|
eatspecial();
|
|
return 1;
|
|
}
|
|
|
|
if(otmp == context.victual.piece) {
|
|
/* If they weren't able to choke, they don't suddenly become able to
|
|
* choke just because they were interrupted. On the other hand, if
|
|
* they were able to choke before, if they lost food it's possible
|
|
* they shouldn't be able to choke now.
|
|
*/
|
|
if (u.uhs != SATIATED) context.victual.canchoke = FALSE;
|
|
context.victual.o_id = 0;
|
|
context.victual.piece = touchfood(otmp);
|
|
if (context.victual.piece)
|
|
context.victual.o_id = context.victual.piece->o_id;
|
|
You("resume your meal.");
|
|
start_eating(context.victual.piece);
|
|
return(1);
|
|
}
|
|
|
|
/* nothing in progress - so try to find something. */
|
|
/* tins are a special case */
|
|
/* tins must also check conduct separately in case they're discarded */
|
|
if(otmp->otyp == TIN) {
|
|
start_tin(otmp);
|
|
return(1);
|
|
}
|
|
|
|
/* KMH, conduct */
|
|
u.uconduct.food++;
|
|
|
|
context.victual.o_id = 0;
|
|
context.victual.piece = otmp = touchfood(otmp);
|
|
if (context.victual.piece)
|
|
context.victual.o_id = context.victual.piece->o_id;
|
|
context.victual.usedtime = 0;
|
|
|
|
/* Now we need to calculate delay and nutritional info.
|
|
* The base nutrition calculated here and in eatcorpse() accounts
|
|
* for normal vs. rotten food. The reqtime and nutrit values are
|
|
* then adjusted in accordance with the amount of food left.
|
|
*/
|
|
if(otmp->otyp == CORPSE) {
|
|
int tmp = eatcorpse(otmp);
|
|
if (tmp == 2) {
|
|
/* used up */
|
|
context.victual.piece = (struct obj *)0;
|
|
context.victual.o_id = 0;
|
|
return(1);
|
|
} else if (tmp)
|
|
dont_start = TRUE;
|
|
/* if not used up, eatcorpse sets up reqtime and may modify
|
|
* oeaten */
|
|
} else {
|
|
/* No checks for WAX, LEATHER, BONE, DRAGON_HIDE. These are
|
|
* all handled in the != FOOD_CLASS case, above */
|
|
switch (objects[otmp->otyp].oc_material) {
|
|
case FLESH:
|
|
u.uconduct.unvegan++;
|
|
if (otmp->otyp != EGG) {
|
|
violated_vegetarian();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (otmp->otyp == PANCAKE ||
|
|
otmp->otyp == FORTUNE_COOKIE || /* eggs */
|
|
otmp->otyp == CREAM_PIE ||
|
|
otmp->otyp == CANDY_BAR || /* milk */
|
|
otmp->otyp == LUMP_OF_ROYAL_JELLY)
|
|
u.uconduct.unvegan++;
|
|
break;
|
|
}
|
|
|
|
context.victual.reqtime = objects[otmp->otyp].oc_delay;
|
|
if (otmp->otyp != FORTUNE_COOKIE &&
|
|
(otmp->cursed || (!nonrotting_food(otmp->otyp) &&
|
|
(monstermoves - otmp->age) > (otmp->blessed ? 50L : 30L) &&
|
|
(otmp->orotten || !rn2(7))))) {
|
|
|
|
if (rottenfood(otmp)) {
|
|
otmp->orotten = TRUE;
|
|
dont_start = TRUE;
|
|
}
|
|
consume_oeaten(otmp, 1); /* oeaten >>= 1 */
|
|
} else
|
|
fprefx(otmp);
|
|
}
|
|
|
|
/* re-calc the nutrition */
|
|
if (otmp->otyp == CORPSE) basenutrit = mons[otmp->corpsenm].cnutrit;
|
|
else basenutrit = objects[otmp->otyp].oc_nutrition;
|
|
|
|
#ifdef DEBUG
|
|
debugpline("before rounddiv: context.victual.reqtime == %d", context.victual.reqtime);
|
|
debugpline("oeaten == %d, basenutrit == %d", otmp->oeaten, basenutrit);
|
|
#endif
|
|
context.victual.reqtime = (basenutrit == 0 ? 0 :
|
|
rounddiv(context.victual.reqtime * (long)otmp->oeaten, basenutrit));
|
|
#ifdef DEBUG
|
|
debugpline("after rounddiv: context.victual.reqtime == %d", context.victual.reqtime);
|
|
#endif
|
|
/* calculate the modulo value (nutrit. units per round eating)
|
|
* note: this isn't exact - you actually lose a little nutrition
|
|
* due to this method.
|
|
* TODO: add in a "remainder" value to be given at the end of the
|
|
* meal.
|
|
*/
|
|
if (context.victual.reqtime == 0 || otmp->oeaten == 0)
|
|
/* possible if most has been eaten before */
|
|
context.victual.nmod = 0;
|
|
else if ((int)otmp->oeaten >= context.victual.reqtime)
|
|
context.victual.nmod = -((int)otmp->oeaten / context.victual.reqtime);
|
|
else
|
|
context.victual.nmod = context.victual.reqtime % otmp->oeaten;
|
|
context.victual.canchoke = (u.uhs == SATIATED);
|
|
|
|
if (!dont_start) start_eating(otmp);
|
|
return(1);
|
|
}
|
|
|
|
/* Take a single bite from a piece of food, checking for choking and
|
|
* modifying usedtime. Returns 1 if they choked and survived, 0 otherwise.
|
|
*/
|
|
STATIC_OVL int
|
|
bite()
|
|
{
|
|
if(context.victual.canchoke && u.uhunger >= 2000) {
|
|
choke(context.victual.piece);
|
|
return 1;
|
|
}
|
|
if (context.victual.doreset) {
|
|
do_reset_eat();
|
|
return 0;
|
|
}
|
|
force_save_hs = TRUE;
|
|
if(context.victual.nmod < 0) {
|
|
lesshungry(-context.victual.nmod);
|
|
consume_oeaten(context.victual.piece, context.victual.nmod); /* -= -nmod */
|
|
} else if(context.victual.nmod > 0 && (context.victual.usedtime % context.victual.nmod)) {
|
|
lesshungry(1);
|
|
consume_oeaten(context.victual.piece, -1); /* -= 1 */
|
|
}
|
|
force_save_hs = FALSE;
|
|
recalc_wt();
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
gethungry() /* as time goes by - called by moveloop() and domove() */
|
|
{
|
|
if (u.uinvulnerable) return; /* you don't feel hungrier */
|
|
|
|
if ((!u.usleep || !rn2(10)) /* slow metabolic rate while asleep */
|
|
&& (carnivorous(youmonst.data) || herbivorous(youmonst.data))
|
|
&& !Slow_digestion)
|
|
u.uhunger--; /* ordinary food consumption */
|
|
|
|
if (moves % 2) { /* odd turns */
|
|
/* Regeneration uses up food, unless due to an artifact */
|
|
if (HRegeneration || ((ERegeneration & (~W_ART)) &&
|
|
(ERegeneration != W_WEP || !uwep->oartifact)))
|
|
u.uhunger--;
|
|
if (near_capacity() > SLT_ENCUMBER) u.uhunger--;
|
|
} else { /* even turns */
|
|
if (Hunger) u.uhunger--;
|
|
/* Conflict uses up food too */
|
|
if (HConflict || (EConflict & (~W_ARTI))) u.uhunger--;
|
|
/* +0 charged rings don't do anything, so don't affect hunger */
|
|
/* Slow digestion still uses ring hunger */
|
|
switch ((int)(moves % 20)) { /* note: use even cases only */
|
|
case 4: if (uleft &&
|
|
(uleft->spe || !objects[uleft->otyp].oc_charged))
|
|
u.uhunger--;
|
|
break;
|
|
case 8: if (uamul) u.uhunger--;
|
|
break;
|
|
case 12: if (uright &&
|
|
(uright->spe || !objects[uright->otyp].oc_charged))
|
|
u.uhunger--;
|
|
break;
|
|
case 16: if (u.uhave.amulet) u.uhunger--;
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
newuhs(TRUE);
|
|
}
|
|
|
|
void
|
|
morehungry(num) /* called after vomiting and after performing feats of magic */
|
|
register int num;
|
|
{
|
|
u.uhunger -= num;
|
|
newuhs(TRUE);
|
|
}
|
|
|
|
|
|
void
|
|
lesshungry(num) /* called after eating (and after drinking fruit juice) */
|
|
register int num;
|
|
{
|
|
/* See comments in newuhs() for discussion on force_save_hs */
|
|
boolean iseating = (occupation == eatfood) || force_save_hs;
|
|
#ifdef DEBUG
|
|
debugpline("lesshungry(%d)", num);
|
|
#endif
|
|
u.uhunger += num;
|
|
if(u.uhunger >= 2000) {
|
|
if (!iseating || context.victual.canchoke) {
|
|
if (iseating) {
|
|
choke(context.victual.piece);
|
|
reset_eat();
|
|
} else
|
|
choke(occupation == opentin ? context.tin.tin : (struct obj *)0);
|
|
/* no reset_eat() */
|
|
}
|
|
} else {
|
|
/* Have lesshungry() report when you're nearly full so all eating
|
|
* warns when you're about to choke.
|
|
*/
|
|
if (u.uhunger >= 1500) {
|
|
if (!context.victual.eating || (context.victual.eating && !context.victual.fullwarn)) {
|
|
pline("You're having a hard time getting all of it down.");
|
|
nomovemsg = "You're finally finished.";
|
|
if (!context.victual.eating)
|
|
multi = -2;
|
|
else {
|
|
context.victual.fullwarn = TRUE;
|
|
if (context.victual.canchoke && context.victual.reqtime > 1) {
|
|
/* a one-gulp food will not survive a stop */
|
|
if (yn_function("Continue eating?",
|
|
ynchars,'n') != 'y') {
|
|
reset_eat();
|
|
nomovemsg = (char *)0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
newuhs(FALSE);
|
|
}
|
|
|
|
STATIC_PTR
|
|
int
|
|
unfaint(VOID_ARGS)
|
|
{
|
|
(void) Hear_again();
|
|
if(u.uhs > FAINTING)
|
|
u.uhs = FAINTING;
|
|
stop_occupation();
|
|
context.botl = 1;
|
|
return 0;
|
|
}
|
|
|
|
boolean
|
|
is_fainted()
|
|
{
|
|
return((boolean)(u.uhs == FAINTED));
|
|
}
|
|
|
|
void
|
|
reset_faint() /* call when a faint must be prematurely terminated */
|
|
{
|
|
if (afternmv == unfaint) unmul("You revive.");
|
|
}
|
|
|
|
void
|
|
newuhs(incr) /* compute and comment on your (new?) hunger status */
|
|
boolean incr;
|
|
{
|
|
unsigned newhs;
|
|
static unsigned save_hs;
|
|
static boolean saved_hs = FALSE;
|
|
int h = u.uhunger;
|
|
|
|
newhs = (h > 1000) ? SATIATED :
|
|
(h > 150) ? NOT_HUNGRY :
|
|
(h > 50) ? HUNGRY :
|
|
(h > 0) ? WEAK : FAINTING;
|
|
|
|
/* While you're eating, you may pass from WEAK to HUNGRY to NOT_HUNGRY.
|
|
* This should not produce the message "you only feel hungry now";
|
|
* that message should only appear if HUNGRY is an endpoint. Therefore
|
|
* we check to see if we're in the middle of eating. If so, we save
|
|
* the first hunger status, and at the end of eating we decide what
|
|
* message to print based on the _entire_ meal, not on each little bit.
|
|
*/
|
|
/* It is normally possible to check if you are in the middle of a meal
|
|
* by checking occupation == eatfood, but there is one special case:
|
|
* start_eating() can call bite() for your first bite before it
|
|
* sets the occupation.
|
|
* Anyone who wants to get that case to work _without_ an ugly static
|
|
* force_save_hs variable, feel free.
|
|
*/
|
|
/* Note: If you become a certain hunger status in the middle of the
|
|
* meal, and still have that same status at the end of the meal,
|
|
* this will incorrectly print the associated message at the end of
|
|
* the meal instead of the middle. Such a case is currently
|
|
* impossible, but could become possible if a message for SATIATED
|
|
* were added or if HUNGRY and WEAK were separated by a big enough
|
|
* gap to fit two bites.
|
|
*/
|
|
if (occupation == eatfood || force_save_hs) {
|
|
if (!saved_hs) {
|
|
save_hs = u.uhs;
|
|
saved_hs = TRUE;
|
|
}
|
|
u.uhs = newhs;
|
|
return;
|
|
} else {
|
|
if (saved_hs) {
|
|
u.uhs = save_hs;
|
|
saved_hs = FALSE;
|
|
}
|
|
}
|
|
|
|
if(newhs == FAINTING) {
|
|
if(is_fainted()) newhs = FAINTED;
|
|
if(u.uhs <= WEAK || rn2(20-u.uhunger/10) >= 19) {
|
|
if(!is_fainted() && multi >= 0 /* %% */) {
|
|
int duration = 10-(u.uhunger/10);
|
|
|
|
/* stop what you're doing, then faint */
|
|
stop_occupation();
|
|
You("faint from lack of food.");
|
|
incr_itimeout(&HDeaf, duration);
|
|
nomul(-duration);
|
|
nomovemsg = "You regain consciousness.";
|
|
afternmv = unfaint;
|
|
newhs = FAINTED;
|
|
}
|
|
} else
|
|
if(u.uhunger < -(int)(200 + 20*ACURR(A_CON))) {
|
|
u.uhs = STARVED;
|
|
context.botl = 1;
|
|
bot();
|
|
You("die from starvation.");
|
|
killer.format = KILLED_BY;
|
|
Strcpy(killer.name, "starvation");
|
|
done(STARVING);
|
|
/* if we return, we lifesaved, and that calls newuhs */
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(newhs != u.uhs) {
|
|
if(newhs >= WEAK && u.uhs < WEAK)
|
|
losestr(1); /* this may kill you -- see below */
|
|
else if(newhs < WEAK && u.uhs >= WEAK)
|
|
losestr(-1);
|
|
switch(newhs){
|
|
case HUNGRY:
|
|
if (Hallucination) {
|
|
You((!incr) ?
|
|
"now have a lesser case of the munchies." :
|
|
"are getting the munchies.");
|
|
} else
|
|
You((!incr) ? "only feel hungry now." :
|
|
(u.uhunger < 145) ? "feel hungry." :
|
|
"are beginning to feel hungry.");
|
|
if (incr && occupation &&
|
|
(occupation != eatfood && occupation != opentin))
|
|
stop_occupation();
|
|
break;
|
|
case WEAK:
|
|
if (Hallucination)
|
|
pline((!incr) ?
|
|
"You still have the munchies." :
|
|
"The munchies are interfering with your motor capabilities.");
|
|
else if (incr &&
|
|
(Role_if(PM_WIZARD) || Race_if(PM_ELF) ||
|
|
Role_if(PM_VALKYRIE)))
|
|
pline("%s needs food, badly!",
|
|
(Role_if(PM_WIZARD) || Role_if(PM_VALKYRIE)) ?
|
|
urole.name.m : "Elf");
|
|
else
|
|
You((!incr) ? "feel weak now." :
|
|
(u.uhunger < 45) ? "feel weak." :
|
|
"are beginning to feel weak.");
|
|
if (incr && occupation &&
|
|
(occupation != eatfood && occupation != opentin))
|
|
stop_occupation();
|
|
break;
|
|
}
|
|
u.uhs = newhs;
|
|
context.botl = 1;
|
|
bot();
|
|
if ((Upolyd ? u.mh : u.uhp) < 1) {
|
|
You("die from hunger and exhaustion.");
|
|
killer.format = KILLED_BY;
|
|
Strcpy(killer.name, "exhaustion");
|
|
done(STARVING);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Returns an object representing food. Object may be either on floor or
|
|
* in inventory.
|
|
*/
|
|
struct obj *
|
|
floorfood(verb,corpsecheck) /* get food from floor or pack */
|
|
const char *verb;
|
|
int corpsecheck; /* 0, no check, 1, corpses, 2, tinnable corpses */
|
|
{
|
|
register struct obj *otmp;
|
|
char qbuf[QBUFSZ];
|
|
char c;
|
|
boolean feeding = !strcmp(verb, "eat"), /* corpsecheck==0 */
|
|
offering = !strcmp(verb, "sacrifice"); /* corpsecheck==1 */
|
|
|
|
/* if we can't touch floor objects then use invent food only */
|
|
if (!can_reach_floor(TRUE) ||
|
|
#ifdef STEED
|
|
(feeding && u.usteed) || /* can't eat off floor while riding */
|
|
#endif
|
|
((is_pool(u.ux, u.uy) || is_lava(u.ux, u.uy)) &&
|
|
(Wwalking || is_clinger(youmonst.data) ||
|
|
(Flying && !Breathless))))
|
|
goto skipfloor;
|
|
|
|
if (feeding && metallivorous(youmonst.data)) {
|
|
struct obj *gold;
|
|
struct trap *ttmp = t_at(u.ux, u.uy);
|
|
|
|
if (ttmp && ttmp->tseen && ttmp->ttyp == BEAR_TRAP) {
|
|
/* If not already stuck in the trap, perhaps there should
|
|
be a chance to becoming trapped? Probably not, because
|
|
then the trap would just get eaten on the _next_ turn... */
|
|
Sprintf(qbuf, "There is a bear trap here (%s); eat it?",
|
|
(u.utrap && u.utraptype == TT_BEARTRAP) ?
|
|
"holding you" : "armed");
|
|
if ((c = yn_function(qbuf, ynqchars, 'n')) == 'y') {
|
|
u.utrap = u.utraptype = 0;
|
|
deltrap(ttmp);
|
|
return mksobj(BEARTRAP, TRUE, FALSE);
|
|
} else if (c == 'q') {
|
|
return (struct obj *)0;
|
|
}
|
|
}
|
|
|
|
if (youmonst.data != &mons[PM_RUST_MONSTER] &&
|
|
(gold = g_at(u.ux, u.uy)) != 0) {
|
|
if (gold->quan == 1L)
|
|
Sprintf(qbuf, "There is 1 gold piece here; eat it?");
|
|
else
|
|
Sprintf(qbuf, "There are %ld gold pieces here; eat them?",
|
|
gold->quan);
|
|
if ((c = yn_function(qbuf, ynqchars, 'n')) == 'y') {
|
|
return gold;
|
|
} else if (c == 'q') {
|
|
return (struct obj *)0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Is there some food (probably a heavy corpse) here on the ground? */
|
|
for (otmp = level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere) {
|
|
if (corpsecheck ?
|
|
(otmp->otyp==CORPSE && (corpsecheck == 1 || tinnable(otmp))) :
|
|
feeding ? (otmp->oclass != COIN_CLASS && is_edible(otmp)) :
|
|
otmp->oclass==FOOD_CLASS) {
|
|
char qsfx[QBUFSZ];
|
|
boolean one = (otmp->quan == 1L);
|
|
|
|
/* "There is <an object> here; <verb> it?" or
|
|
"There are <N objects> here; <verb> one?" */
|
|
Sprintf(qbuf, "There %s ", otense(otmp, "are"));
|
|
Sprintf(qsfx, " here; %s %s?", verb, one ? "it" : "one");
|
|
(void)safe_qbuf(qbuf, qbuf, qsfx,
|
|
otmp, doname, ansimpleoname,
|
|
one ? something : (const char *)"things");
|
|
if ((c = yn_function(qbuf,ynqchars,'n')) == 'y')
|
|
return(otmp);
|
|
else if (c == 'q')
|
|
return((struct obj *) 0);
|
|
}
|
|
}
|
|
|
|
skipfloor:
|
|
/* We cannot use ALL_CLASSES since that causes getobj() to skip its
|
|
* "ugly checks" and we need to check for inedible items.
|
|
*/
|
|
otmp = getobj(feeding ? allobj : offering ? offerfodder : comestibles,
|
|
verb);
|
|
if (corpsecheck && otmp && !(offering && otmp->oclass == AMULET_CLASS))
|
|
if (otmp->otyp != CORPSE || (corpsecheck == 2 && !tinnable(otmp))) {
|
|
You_cant("%s that!", verb);
|
|
return (struct obj *)0;
|
|
}
|
|
return otmp;
|
|
}
|
|
|
|
/* Side effects of vomiting */
|
|
/* added nomul (MRS) - it makes sense, you're too busy being sick! */
|
|
void
|
|
vomit() /* A good idea from David Neves */
|
|
{
|
|
make_sick(0L, (char *) 0, TRUE, SICK_VOMITABLE);
|
|
nomul(-2);
|
|
nomovemsg = You_can_move_again;
|
|
}
|
|
|
|
int
|
|
eaten_stat(base, obj)
|
|
register int base;
|
|
register struct obj *obj;
|
|
{
|
|
long uneaten_amt, full_amount;
|
|
|
|
uneaten_amt = (long)obj->oeaten;
|
|
full_amount = (obj->otyp == CORPSE) ? (long)mons[obj->corpsenm].cnutrit
|
|
: (long)objects[obj->otyp].oc_nutrition;
|
|
if (uneaten_amt > full_amount) {
|
|
impossible(
|
|
"partly eaten food (%ld) more nutritious than untouched food (%ld)",
|
|
uneaten_amt, full_amount);
|
|
uneaten_amt = full_amount;
|
|
}
|
|
|
|
base = (int)(full_amount ? (long)base * uneaten_amt / full_amount : 0L);
|
|
return (base < 1) ? 1 : base;
|
|
}
|
|
|
|
/* reduce obj's oeaten field, making sure it never hits or passes 0 */
|
|
void
|
|
consume_oeaten(obj, amt)
|
|
struct obj *obj;
|
|
int amt;
|
|
{
|
|
/*
|
|
* This is a hack to try to squelch several long standing mystery
|
|
* food bugs. A better solution would be to rewrite the entire
|
|
* victual handling mechanism from scratch using a less complex
|
|
* model. Alternatively, this routine could call done_eating()
|
|
* or food_disappears() but its callers would need revisions to
|
|
* cope with context.victual.piece unexpectedly going away.
|
|
*
|
|
* Multi-turn eating operates by setting the food's oeaten field
|
|
* to its full nutritional value and then running a counter which
|
|
* independently keeps track of whether there is any food left.
|
|
* The oeaten field can reach exactly zero on the last turn, and
|
|
* the object isn't removed from inventory until the next turn
|
|
* when the "you finish eating" message gets delivered, so the
|
|
* food would be restored to the status of untouched during that
|
|
* interval. This resulted in unexpected encumbrance messages
|
|
* at the end of a meal (if near enough to a threshold) and would
|
|
* yield full food if there was an interruption on the critical
|
|
* turn. Also, there have been reports over the years of food
|
|
* becoming massively heavy or producing unlimited satiation;
|
|
* this would occur if reducing oeaten via subtraction attempted
|
|
* to drop it below 0 since its unsigned type would produce a
|
|
* huge positive value instead. So far, no one has figured out
|
|
* _why_ that inappropriate subtraction might sometimes happen.
|
|
*/
|
|
|
|
if (amt > 0) {
|
|
/* bit shift to divide the remaining amount of food */
|
|
obj->oeaten >>= amt;
|
|
} else {
|
|
/* simple decrement; value is negative so we actually add it */
|
|
if ((int) obj->oeaten > -amt)
|
|
obj->oeaten += amt;
|
|
else
|
|
obj->oeaten = 0;
|
|
}
|
|
|
|
if (obj->oeaten == 0) {
|
|
if (obj == context.victual.piece) /* always true unless wishing... */
|
|
context.victual.reqtime = context.victual.usedtime; /* no bites left */
|
|
obj->oeaten = 1; /* smallest possible positive value */
|
|
}
|
|
}
|
|
|
|
/* called when eatfood occupation has been interrupted,
|
|
or in the case of theft, is about to be interrupted */
|
|
boolean
|
|
maybe_finished_meal(stopping)
|
|
boolean stopping;
|
|
{
|
|
/* in case consume_oeaten() has decided that the food is all gone */
|
|
if (occupation == eatfood && context.victual.usedtime >= context.victual.reqtime) {
|
|
if (stopping) occupation = 0; /* for do_reset_eat */
|
|
(void) eatfood(); /* calls done_eating() to use up context.victual.piece */
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* Tin of <something> to the rescue? Decide whether current occupation
|
|
is an attempt to eat a tin of something capable of saving hero's life.
|
|
We don't care about consumption of non-tinned food here because special
|
|
effects there take place on first bite rather than at end of occupation.
|
|
[Popeye the Sailor gets out of trouble by eating tins of spinach. :-] */
|
|
boolean
|
|
Popeye(threat)
|
|
int threat;
|
|
{
|
|
struct obj *otin;
|
|
int mndx;
|
|
|
|
if (occupation != opentin) return FALSE;
|
|
otin = context.tin.tin;
|
|
/* make sure hero still has access to tin */
|
|
if (!carried(otin) &&
|
|
(!obj_here(otin, u.ux, u.uy) || !can_reach_floor(TRUE)))
|
|
return FALSE;
|
|
/* unknown tin is assumed to be helpful */
|
|
if (!otin->known) return TRUE;
|
|
/* known tin is helpful if it will stop life-threatening problem */
|
|
mndx = otin->corpsenm;
|
|
switch (threat) {
|
|
/* note: not used; hunger code bypasses stop_occupation() when eating */
|
|
case HUNGER:
|
|
return (mndx != NON_PM || otin->spe == 1);
|
|
/* flesh from lizards and acidic critters stops petrification */
|
|
case STONED:
|
|
return (mndx >= LOW_PM && (mndx == PM_LIZARD || acidic(&mons[mndx])));
|
|
/* no tins can cure these (yet?) */
|
|
case SLIMED:
|
|
case SICK:
|
|
case VOMITING:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*eat.c*/
|