ftp.nice.ch/pub/next/games/strategic/NetHack.s.tar.gz#/NetHackSource/src/eat.c

This is eat.c in view mode; [Download] [Up]

/*      SCCS Id: @(#)eat.c      3.0     89/11/21
/* 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 debug	if (wizard) pline
# else
#define debug	pline
# endif
#endif

static long FDECL(rounddiv, (long, long));
#ifndef OVERLAY
static int NDECL(Meatdone);
#endif
static void FDECL(choke, (struct obj *));
static void NDECL(recalc_wt);
static struct obj *FDECL(touchfood, (struct obj *));
static void NDECL(do_reset_eat);
#ifndef OVERLAY
static int NDECL(eatfood);
#endif
static void FDECL(done_eating, (BOOLEAN_P));
static void FDECL(cprefx, (int));
static void FDECL(cpostfx, (int));
#ifndef OVERLAY
static int NDECL(opentin);
#endif
static void FDECL(start_tin, (struct obj *));
#ifdef POLYSELF
static int FDECL(rottenfood, (struct obj *));
#else
static int NDECL(rottenfood);
#endif
static int FDECL(eatcorpse, (struct obj *));
static void FDECL(start_eating, (struct obj *));
static void FDECL(fprefx, (struct obj *));
#ifdef POLYSELF
static void NDECL(eatspecial);
static const char * FDECL(foodword, (struct obj *));
#endif
static void FDECL(fpostfx, (struct obj *));
static int NDECL(bite);
#ifndef OVERLAY
static int NDECL(unfaint);
#endif

#ifdef OVLB

char corpsename[60];
char msgbuf[BUFSZ];

#endif /* OVLB */

/* 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

#ifdef OVLB

const char *hu_stat[] = {
	"Satiated",
	"        ",
	"Hungry  ",
	"Weak    ",
	"Fainting",
	"Fainted ",
	"Starved "
};

static const char comestibles[] = { FOOD_SYM, 0 };
#ifdef POLYSELF
static const char everything[] = { GOLD_SYM, /* must come first */
	WEAPON_SYM, ARMOR_SYM, POTION_SYM, SCROLL_SYM, WAND_SYM,
#ifdef SPELLS
	SPBOOK_SYM,
#endif
	RING_SYM, WAND_SYM, AMULET_SYM, FOOD_SYM, TOOL_SYM, GEM_SYM,
	ROCK_SYM, BALL_SYM, CHAIN_SYM, 0 };

boolean
is_edible(obj)
register struct obj *obj;
{
	if (metallivorous(uasmon) && (obj->olet == GOLD_SYM ||
			(objects[obj->otyp].oc_material > WOOD &&
			objects[obj->otyp].oc_material < MINERAL)))
		return TRUE;
	if (u.umonnum == PM_GELATINOUS_CUBE &&
			objects[obj->otyp].oc_material <= WOOD)
		return TRUE;
	return !!index(comestibles, obj->olet);
}
#endif

/* calculate x/y, rounding as appropriate */

static long
rounddiv(x, y)
long x, y;
{
	int divsgn = 1;
	long r, m;

	if (y == 0)
		panic("division by zero in rounddiv");
	if (x < 0) {
		divsgn = -divsgn; x = -x;
	}
	if (y < 0) {
		divsgn = -divsgn; y = -y;
	}
	r = x/y;
	m = x%y;
	if (2*m >= y)
		r++;
	return divsgn*r;
}

void
init_uhunger(){
	u.uhunger = 900;
	u.uhs = NOT_HUNGRY;
}

const struct { const char *txt; int nut; } tintxts[] = {
	"deep fried",	60,
	"pickled",	40,
	"soup made from", 20,
	"pureed", 500,
	"rotten", -50,
	"",	0
};
#define	TTSZ	SIZE(tintxts)

static struct {
	struct	obj *tin;
	int	usedtime, reqtime;
} tin;

static struct {
	struct	obj *piece;	/* the thing being eaten, or last thing that
				 * was partially eaten, unless that thing was
				 * a tin, which uses the tin structure above */
	int	usedtime,	/* turns spent eating */
		reqtime;	/* turns required to eat */
	int	nmod;		/* coded nutrition per turn */
	Bitfield(canchoke,1);	/* was satiated at beginning */
	Bitfield(fullwarn,1);	/* have warned about being full */
	Bitfield(eating,1);	/* victual currently being eaten */
	Bitfield(doreset,1);	/* stop eating at end of turn */
} victual;

#ifndef OVERLAY
static 
#endif
int
Meatdone() {		/* called after mimicing is over */
	u.usym =
#ifdef POLYSELF
		u.mtimedone ? uasmon->mlet :
#endif
		S_HUMAN;
	prme();
	return 0;
}

/* 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.
 */
/*ARGSUSED*/
static 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) return;

	if (pl_character[0] == 'K' && u.ualigntyp == U_LAWFUL)
		u.ualign--;	/* gluttony is unchivalrous */

#ifdef HARD
	if (!rn2(20)) {
#else
	if (rn2(20)) {
#endif
		You("stuff yourself and then vomit voluminously.");
		morehungry(1000);	/* you just got *very* sick! */
		vomit();
	} else {
		killer_format = KILLED_BY_AN;
		if(food) {
#ifdef POLYSELF
			if (food->olet == GOLD_SYM) {
				killer_format = KILLED_BY;
				killer = "eating too rich a meal";
			} else
#endif
				killer = singular(food, xname);
		} else
			killer = "exuberant appetite";
		if (!food) You("choke over it.");
#ifdef POLYSELF
		else You("choke over your %s.", foodword(food));
#else
		else You("choke over your food.");
#endif
		You("die...");
		done(CHOKING);
	}
}

static void
recalc_wt() {	/* modify object wt. depending on time spent consuming it */
	register struct obj *piece = victual.piece;

#ifdef DEBUG
	debug("Old weight = %d", piece->owt);
	debug("Used time = %d, Req'd time = %d",
		victual.usedtime, victual.reqtime);
#endif
	/* weight(piece) = weight of full item */
  	if(victual.usedtime)
	    piece->owt = eaten_stat(weight(piece), piece);
#ifdef DEBUG
	debug("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(victual.eating && !victual.doreset) {
#ifdef DEBUG
	    debug("reset_eat...");
#endif
	    victual.doreset = TRUE;
	}
	return;
}

static struct obj *
touchfood(otmp)
register struct obj *otmp;
{
	if (otmp->quan > 1) {
	    otmp = splitobj(otmp, (int)otmp->quan-1);
#ifdef DEBUG
	    debug("split object,");
#endif
	}
	if (!otmp->oeaten)
	    otmp->oeaten = (otmp->otyp == CORPSE ?
				(int)mons[otmp->corpsenm].cnutrit :
				objects[otmp->otyp].nutrition);
	if (carried(otmp)) {
	    freeinv(otmp);
	    if(inv_cnt() >= 52)
		dropy(otmp);
	    else
		otmp = addinv(otmp); /* unlikely but a merge is possible */
	}
	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 == victual.piece) victual.piece = (struct obj *)0;
}

static void
do_reset_eat() {

#ifdef DEBUG
	debug("do_reset_eat...");
#endif
	if (victual.piece) {
		victual.piece = touchfood(victual.piece);
		recalc_wt();
	}
	victual.fullwarn = victual.eating = 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();
}

#ifndef OVERLAY
static 
#endif
int
eatfood() {		/* called each move during eating process */
	if(!carried(victual.piece) && !obj_here(victual.piece, u.ux, u.uy)) {
		/* maybe it was stolen? */
		do_reset_eat();
		return(0);
	}
	if(!victual.eating) return(0);

	if(++victual.usedtime < victual.reqtime) {
	    if(bite()) return(0);
	    return(1);	/* still busy */
	} else {	/* done */
	    done_eating(TRUE);
	    return(0);
	}
}

static void
done_eating(message)
boolean message;
{
#ifndef NO_SIGNAL
	victual.piece->in_use = TRUE;
#endif
	if (nomovemsg) {
		if (message) pline(nomovemsg);
		nomovemsg = 0;
	} else if (message)
		You("finish eating the %s.", singular(victual.piece, xname));

	if(victual.piece->otyp == CORPSE)
		cpostfx(victual.piece->corpsenm);
	else
		fpostfx(victual.piece);

	if (carried(victual.piece)) useup(victual.piece);
	else useupf(victual.piece);
	victual.piece = (struct obj *) 0;
	victual.fullwarn = victual.eating = victual.doreset = FALSE;
}

static void
cprefx(pm)		/* called at the "first bite" of a corpse */
register int pm;
{
	if ((pl_character[0]=='E') ? is_elf(&mons[pm]) : is_human(&mons[pm])) {
		You("cannibal!  You will regret this!");
		Aggravate_monster |= INTRINSIC;
	}

	switch(pm) {
	    case PM_LITTLE_DOG:
	    case PM_DOG:
	    case PM_LARGE_DOG:
	    case PM_KITTEN:
	    case PM_HOUSECAT:
	    case PM_LARGE_CAT:
		Aggravate_monster |= INTRINSIC;
		break;
	    case PM_COCKATRICE:
#ifdef MEDUSA
	    case PM_MEDUSA:
#endif
#ifdef POLYSELF
		if(!resists_ston(uasmon))
#endif
			{
			char *cruft;	/* killer is const char * */
			killer_format = KILLED_BY;
			killer = cruft = (char *) alloc(40);
			You("turn to stone.");
			Sprintf(cruft, "%s meat", mons[pm].mname);
			done(STONING);
			}
			break;
#ifdef POLYSELF
	    case PM_LIZARD:
		/* Relief from cockatrices -dgk */
		if (Stoned) {
			Stoned = 0;
			You("feel limber!");
		}
		break;
	    default:
		if(acidic(&mons[pm]) && Stoned) {
		    pline("What a pity - you just destroyed a future piece of art!");
		    Stoned = 0;
		}
#endif
	}
	return;
}

static void
cpostfx(pm)		/* called after completely consuming a corpse */
register int pm;
{
	register int tmp = 0;

	switch(pm) {
	    case PM_WRAITH:
		pluslvl();
		break;
#ifdef POLYSELF
	    case PM_WERERAT:
		u.ulycn = PM_RATWERE;
		break;
	    case PM_WEREJACKAL:
		u.ulycn = PM_JACKALWERE;
		break;
	    case PM_WEREWOLF:
		u.ulycn = PM_WOLFWERE;
		break;
#endif
	    case PM_NURSE:
		u.uhp = u.uhpmax;
		flags.botl = 1;
		break;
	    case PM_STALKER:
		if(!Invis) {
			HInvis = 50+rn2(100);
			if(!See_invisible)
				newsym(u.ux, u.uy);
		} else {
			if (!(HInvis & INTRINSIC)) You("feel hidden!");
			HInvis |= INTRINSIC;
			HSee_invisible |= INTRINSIC;
		}
		/* 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(u.usym == S_HUMAN) {
		    You("cannot resist the temptation to mimic a pile of gold.");
		    nomul(-tmp);
		    afternmv = Meatdone;
		    if (pl_character[0]=='E')
			nomovemsg = "You now again prefer mimicking an elf.";
		    else
			nomovemsg = "You now again prefer mimicking a human.";
		    u.usym = GOLD_SYM;
		    prme();
		}
		break;
	    case PM_FLOATING_EYE:
		if (!(HTelepat & INTRINSIC)) {
			HTelepat |= INTRINSIC;
			You("feel a %s mental acuity.",
			Hallucination ? "normal" : "strange");
		}
		break;
	    case PM_QUANTUM_MECHANIC:
		Your("velocity suddenly seems very uncertain!");
		if (Fast & INTRINSIC) {
			Fast &= ~INTRINSIC;
			You("seem slower.");
		} else {
			Fast |= INTRINSIC;
			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:
		You("feel a change coming over you.");
#ifdef POLYSELF
		polyself();
#else
		newman();
#endif
		break;
	    default: {
		register struct permonst *ptr = &mons[pm];
		if(dmgtype(ptr, AD_STUN) || ptr==&mons[PM_VIOLET_FUNGUS]) {
		    pline ("Oh wow!  Great stuff!");
		    make_hallucinated(Hallucination + 200,FALSE);
		}
		/* prevent polymorph abuse by killing/eating your offspring */
		if(ptr >= &mons[PM_BABY_GRAY_DRAGON] &&
		   ptr <= &mons[PM_BABY_YELLOW_DRAGON]) return;
		if(is_giant(ptr)) gainstr((struct obj *)0, 0);

		if(can_teleport(ptr) && ptr->mlevel > rn2(10)) {
		    if (!(HTeleportation & INTRINSIC)) {
			You("feel very jumpy.");
			HTeleportation |= INTRINSIC;
		    }
		} else if(control_teleport(ptr) && ptr->mlevel > rn2(15)) {
		    if (!(HTeleport_control & INTRINSIC)) {
			You("feel in control of yourself.");
			HTeleport_control |= INTRINSIC;
		    }
		} else if(resists_fire(ptr) && ptr->mlevel > rn2(15)) {
		    if (!(HFire_resistance & INTRINSIC)) {
			You("feel a momentary chill.");
			HFire_resistance |= INTRINSIC;
		    }
		} else if(resists_cold(ptr) && ptr->mlevel > rn2(15)) {
		    if (!(HCold_resistance & INTRINSIC)) {
			You("feel full of hot air.");
			HCold_resistance |= INTRINSIC;
		    }
		} else if(((ptr->mflags1 & M1_POIS_RES) && ptr->mlevel>rn2(15))
		   || ((pm == PM_KILLER_BEE || pm == PM_SCORPION) && !rn2(4))) {
		/* Monsters with only M1_POIS are poison resistant themselves,
		 * but do not confer resistance when eaten
		 */
		    if (!(HPoison_resistance & INTRINSIC)) {
			You("feel healthy.");
			HPoison_resistance |= INTRINSIC;
		    }
		} else if(resists_elec(ptr) && ptr->mlevel > rn2(15)) {
		    if (!(HShock_resistance & INTRINSIC)) {
			Your("health currently feels amplified!");
			HShock_resistance |= INTRINSIC;
		    }
		} else if((ptr->mflags1 & M1_SLEE_RES) && ptr->mlevel > rn2(15)) {
		/* Undead monsters never sleep,
		 * but also do not confer resistance when eaten
		 */
		    if (!(HSleep_resistance & INTRINSIC)) {
			You("feel wide awake.");
			HSleep_resistance |= INTRINSIC;
		    }
		} else if(resists_disint(ptr) && ptr->mlevel > rn2(15)) {
		    if (!(HDisint_resistance & INTRINSIC)) {
			You("feel very firm.");
			HDisint_resistance |= INTRINSIC;
		    }
		}
	    }
	    break;
	}
	return;
}

#ifndef OVERLAY
static 
#endif
int
opentin()		/* called during each move whilst opening a tin */
{
	register int r;

	if(!carried(tin.tin) && !obj_here(tin.tin, u.ux, u.uy))
					/* perhaps it was stolen? */
		return(0);		/* %% probably we should use tinoid */
	if(tin.usedtime++ >= 50) {
		You("give up your attempt to open the tin.");
		return(0);
	}
	if(tin.usedtime < tin.reqtime)
		return(1);		/* still busy */
	if(tin.tin->cursed && !rn2(8)) {
		b_trapped("tin");
		goto use_me;
	}
	You("succeed in opening the tin.");
	if(!tin.tin->spe) {
	    if(tin.tin->corpsenm == -1) {
		pline("It turns out to be empty.");
		tin.tin->dknown = tin.tin->known = TRUE;
		goto use_me;
	    }
	    r = tin.tin->cursed ? 4 : rn2(TTSZ-1); /* Always rotten if cursed */
	    pline("It smells like %s.", makeplural(
		  Hallucination ? rndmonnam() : mons[tin.tin->corpsenm].mname));
	    pline("Eat it? ");
	    if (yn() == 'n') {
		if (!Hallucination) tin.tin->dknown = tin.tin->known = TRUE;
		if (flags.verbose) You("discard the open tin.");
		goto use_me;
	    }
	    You("consume %s %s.", tintxts[r].txt,
		  mons[tin.tin->corpsenm].mname);
	    tin.tin->dknown = tin.tin->known = TRUE;
	    cprefx(tin.tin->corpsenm); cpostfx(tin.tin->corpsenm);

	    /* 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(r == 0) {			/* Deep Fried */
		Glib = rnd(15);
		pline("Eating deep fried food made your %s very slippery.",
			makeplural(body_part(FINGER)));
	    }
	} else {
	    if (tin.tin->cursed)
		pline("It contains some decaying %s substance.",
			Hallucination ? hcolor() : green);
	    else
		pline("It contains spinach.");

	    pline("Eat it? ");
	    if (yn() == 'n') {
		if (!Hallucination && !tin.tin->cursed)
		    tin.tin->dknown = tin.tin->known = TRUE;
		if (flags.verbose)
		    You("discard the open tin.");
		goto use_me;
	    }
	    if (!tin.tin->cursed)
		pline("This makes you feel like %s!",
		      Hallucination ? "Swee'pea" : "Popeye");
	    lesshungry(600);
	    gainstr(tin.tin, 0);
	}
	tin.tin->dknown = tin.tin->known = TRUE;
use_me:
	if (carried(tin.tin)) useup(tin.tin);
	else useupf(tin.tin);
	return(0);
}

static void
start_tin(otmp)		/* called when starting to open a tin */
	register struct obj *otmp;
{
	register int tmp;

#ifdef POLYSELF
	if (metallivorous(uasmon)) {
		You("bite right into the metal can....");
		tmp = 1;
	} else
#endif
	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:
#ifdef TOLKIEN
		case ELVEN_DAGGER:
		case ORCISH_DAGGER:
#endif
		case ATHAME:
#ifdef WORM
		case CRYSKNIFE:
#endif
			tmp = 3;
			break;
		case PICK_AXE:
		case AXE:
			tmp = 6;
			break;
		default:
			goto no_opener;
		}
		pline("Using your %s you try to open the tin.",
			aobjnam(uwep, NULL));
	} else {
no_opener:
		pline("It is not so easy to open this tin.");
		if(Glib) {
			pline("The tin slips out of your hands.");
			if(otmp->quan > 1) {
				register struct obj *obj;
				obj = splitobj(otmp, 1);
				if(otmp == uwep) setuwep(obj);
			}
			if (carried(otmp)) dropx(otmp);
			else stackobj(otmp);
			return;
		}
		tmp = 10 + rn2(1 + 500/((int)(ACURR(A_DEX) + ACURR(A_STR))));
	}
	tin.reqtime = tmp;
	tin.usedtime = 0;
	tin.tin = otmp;
	set_occupation(opentin, "opening the tin", 0);
	return;
}

int
Hear_again() {		/* called when waking up after fainting */
	flags.soundok = 1;
	return 0;
}

static int
#ifdef POLYSELF
rottenfood(obj)
struct obj *obj;
#else
rottenfood()
#endif
{		/* called on the "first bite" of rotten food */
#ifdef POLYSELF
	pline("Blecch!  Rotten %s!", foodword(obj));
#else
	pline("Blecch!  Rotten food!");
#endif
	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);
	} else if(!rn2(3)) {
		if(Blind)
		  pline("The world spins and you slap against the floor.");
		else
		  pline("The world spins and goes dark.");
		flags.soundok = 0;
		nomul(-rnd(10));
		nomovemsg = "You are conscious again.";
		afternmv = Hear_again;
		return(1);
	}
	return(0);
}

static int
eatcorpse(otmp)		/* called when a corpse is selected as food */
	register struct obj *otmp;
{
	register const char *cname = mons[otmp->corpsenm].mname;
	register int tp, rotted = 0;

	tp = 0;

	if(otmp->corpsenm != PM_LIZARD) {
#ifndef LINT	/* problem if more than 320K moves before try to eat */
		rotted = (monstermoves - otmp->age)/((long)(10 + rn2(20)));
#endif

		if(otmp->cursed) rotted += 2;
		else if (otmp->blessed) rotted -= 2;
	}

	if(otmp->corpsenm != PM_ACID_BLOB && (rotted > 5)) {
		tp++;
		pline("Ulch - that %s was tainted!",
		      mons[otmp->corpsenm].mlet != S_FUNGUS ?
				"meat" : "fungoid vegetation");
#ifdef POLYSELF
		if (u.usym == S_FUNGUS)
			pline("It doesn't seem at all sickening, though...");
		else {
#endif
			make_sick(10L + rn2(10),FALSE);
			Sprintf(corpsename, "rotted %s corpse", cname);
			u.usick_cause = (const char *)corpsename;
			flags.botl = 1;
#ifdef POLYSELF
		}
#endif
		if (carried(otmp)) useup(otmp);
		else useupf(otmp);
		return(1);
	} else if(acidic(&mons[otmp->corpsenm])
#ifdef POLYSELF
		  && !resists_acid(uasmon)
#endif
		 ) {
		tp++;
		You("have a very bad case of stomach acid.");
		losehp(rnd(15), "acidic corpse", KILLED_BY_AN);
	} else if(poisonous(&mons[otmp->corpsenm]) && 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 > 5) || ((rotted > 3) && rn2(5)))
#ifdef POLYSELF
		&& u.usym != S_FUNGUS
#endif
							){
		tp++;
		You("feel%s sick.", (Sick) ? " very" : "");
		losehp(rnd(8), "cadaver", KILLED_BY_AN);
	}
	if(!tp && otmp->corpsenm != PM_LIZARD && (otmp->orotten || !rn2(7))) {
#ifdef POLYSELF
	    if(rottenfood(otmp)) {
#else
	    if(rottenfood()) {
#endif
		otmp->orotten = TRUE;
		(void)touchfood(otmp);
		return(1);
	    }
	    otmp->oeaten >>= 2;
	} else {
#ifdef POLYSELF
	    pline("This %s corpse %s!", cname,
		carnivorous(uasmon) ? "is delicious" : "tastes terrible");
#else
	    pline("This %s corpse tastes terrible!", cname);
#endif
	}

	/* delay is weight dependent */
	victual.reqtime = 3 + (mons[otmp->corpsenm].cwt >> 2);
	return(0);
}

static void
start_eating(otmp)		/* called as you start to eat */
	register struct obj *otmp;
{
#ifdef DEBUG
	debug("start_eating: %lx (victual = %lx)", otmp, victual.piece);
	debug("reqtime = %d", victual.reqtime);
	debug("(original reqtime = %d)", objects[otmp->otyp].oc_delay);
	debug("nmod = %d", victual.nmod);
	debug("oeaten = %d", otmp->oeaten);
#endif
	victual.fullwarn = victual.doreset = FALSE;
	victual.eating = TRUE;

	if (otmp->otyp == CORPSE)
	    cprefx(victual.piece->corpsenm);

	if (bite()) return;

	if(++victual.usedtime >= victual.reqtime) {
	    done_eating(FALSE);
	    return;
	}

	Sprintf(msgbuf, "eating the %s", singular(otmp, xname));
	set_occupation(eatfood, msgbuf, 0);
}


static void
fprefx(otmp)		/* called on "first bite" of (non-corpse) food */

	register struct obj *otmp;
{
	switch(otmp->otyp) {

	    case FOOD_RATION:
		if(u.uhunger <= 200)
		    if (Hallucination) pline("Oh wow, like, superior, man!");
		    else	       pline("That food really hit the spot!");
		else if(u.uhunger <= 700) pline("That satiated your stomach!");
		break;
	    case TRIPE_RATION:
#ifdef POLYSELF
		if (carnivorous(uasmon))
		    pline("That tripe ration was surprisingly good!");
		else {
#endif
		    pline("Yak - dog food!");
		    more_experienced(1,0);
		    flags.botl = 1;
#ifdef POLYSELF
		}
#endif
		if(rn2(2)
#ifdef POLYSELF
			&& !carnivorous(uasmon)
#endif
						) {
			make_vomiting((long)rn1(victual.reqtime, 10), FALSE);
		}
		break;
#ifdef POLYSELF
	    case CLOVE_OF_GARLIC:
		if (is_undead(uasmon)) {
			make_vomiting((long)rn1(victual.reqtime, 5), FALSE);
			break;
		}
		/* Fall through otherwise */
#endif
	    default:
#ifdef TUTTI_FRUTTI
		if (otmp->otyp==SLIME_MOLD && !otmp->cursed
			&& otmp->spe == current_fruit)
		    pline(!Hallucination ? "Mmm!  Your favorite!" :
			    		   "Yum!  Your fave fruit!");
		else
#endif
#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
		{
		    int oldquan = otmp->quan;
		    otmp->quan = 1;
		    pline("This %s is %s!", xname(otmp),
		      otmp->cursed ? (Hallucination ? "grody" : "terrible"):
		      Hallucination ? "gnarly" : (
#ifdef TOLKIEN
		       otmp->otyp==CRAM_RATION ? "bland":
#endif
		       "delicious"));
		    otmp->quan = oldquan;
		}
		break;
	}
}

#ifdef POLYSELF
static void
eatspecial() /* called after eating non-food */
{
	register struct obj *otmp = victual.piece;

	lesshungry(victual.nmod);
	victual.piece = (struct obj *)0;
	victual.eating = 0;
	if (otmp->olet == GOLD_SYM) { /* temporary gold object */
		free ((genericptr_t)otmp);
		return;
	}
	if (otmp->olet == POTION_SYM) {
		otmp->quan++; /* dopotion() does a useup() */
		(void)dopotion(otmp);
	}
	if (otmp == uball) unpunish();
	if (otmp == uchain) unpunish(); /* but no useup() */
	else if (carried(otmp)) useup(otmp);
	else useupf(otmp);
}

static const char *
foodword(otmp)
register struct obj *otmp;
{
	if (otmp->olet == FOOD_SYM) return "food";
	if (otmp->olet == GOLD_SYM) return "gold";
	if (objects[otmp->otyp].oc_material == GLASS) {
		if (otmp->olet == GEM_SYM && otmp->dknown)
			makeknown(otmp->otyp);
		return "glass";
	}
	if (objects[otmp->otyp].oc_material > WOOD &&
			objects[otmp->otyp].oc_material < MINERAL)
		return "metal";
	if (objects[otmp->otyp].oc_material == MINERAL)
		return (otmp->otyp <= LAST_GEM && otmp->olet == GEM_SYM)
			? "rich food" : "stone";
	if (objects[otmp->otyp].oc_material == WOOD) return "wood";
	if (otmp->olet == ARMOR_SYM) return "armor";
	return "stuff";
}
#endif

static void
fpostfx(otmp)		/* called after consuming (non-corpse) food */

	register struct obj *otmp;
{
	switch(otmp->otyp) {
#ifdef POLYSELF
	    case CLOVE_OF_GARLIC:
		if (u.ulycn != -1) {
		    u.ulycn = -1;
		    You("feel purified.");
		    if(uasmon == &mons[u.ulycn] && !Polymorph_control)
			rehumanize();
		}
		break;
#endif
	    case CARROT:
		make_blinded(0L,TRUE);
		break;
	    case FORTUNE_COOKIE:
		outrumor(bcsign(otmp), TRUE);
		break;
	    case LUMP_OF_ROYAL_JELLY:
		/* This stuff seems to be VERY healthy! */
		gainstr(otmp, 1);
		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;
			killer = "rotten lump of royal jelly";
			done(POISONING);
		}
		if(!otmp->cursed) heal_legs();
		break;
	    case EGG:
		if(otmp->corpsenm == PM_COCKATRICE) {
#ifdef POLYSELF
		    if(!resists_ston(uasmon)) {
#endif
			if (!Stoned) Stoned = 5;
			killer_format = KILLED_BY_AN;
			killer = "cockatrice egg";
#ifdef POLYSELF
		    }
#endif
		}
		break;
	}
	return;
}

int
doeat() {		/* generic "eat" command funtion (see cmd.c) */
	register struct obj *otmp;
	int basenutrit; 		/* nutrition of full item */

	if (Strangled) {
		pline("If you can't breathe air, how can you consume solids?");
		return 0;
	}
	if (!(otmp = floorfood("eat", 0))) return 0;
#ifdef POLYSELF
	/* We have to make non-foods take no time 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!");
	    if (otmp->olet == GOLD_SYM) { /* temp gold object */
		if (otmp->ox) mkgold(OGOLD(otmp), u.ux, u.uy);
		else u.ugold += OGOLD(otmp);
		free((genericptr_t) otmp);
	    }
	    return 0;
	}
	if (otmp->olet != FOOD_SYM) {
	    victual.reqtime = 1;
	    victual.piece = otmp;
		/* Don't split it, we don't need to if it's 1 move */
	    victual.usedtime = 0;
	    victual.canchoke = (u.uhs == SATIATED);
	    if (otmp->olet == GOLD_SYM)
		basenutrit = ((OGOLD(otmp) > 5000L) ? 5000 : (int)OGOLD(otmp));
	    else basenutrit = otmp->owt * 10 / otmp->quan;
	    victual.nmod = basenutrit;
	    victual.eating = TRUE; /* needed for lesshungry() */
		
	    if (otmp->cursed) 
		(void) rottenfood(otmp);

	    if (otmp->olet == WEAPON_SYM && 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)
		pline("This %s is delicious!",
		    otmp->olet == GOLD_SYM ? "gold" : xname(otmp));
	    eatspecial();
	    return 1;
	}
#endif

	if(otmp == 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) victual.canchoke = FALSE;
	    if(!carried(victual.piece)) {
		if(victual.piece->quan != 1)
			(void) splitobj(victual.piece, 1);
	    }
	    You("resume your meal.");
	    start_eating(victual.piece);
	    return(1);
	}

	/* nothing in progress - so try to find something. */
	/* tins are a special case */
	if(otmp->otyp == TIN) {
	    start_tin(otmp);
	    return(1);
	}

	victual.piece = otmp = touchfood(otmp);
	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) {
	    if(eatcorpse(otmp)) return(1);
	    /* else eatcorpse sets up reqtime and oeaten */
	} else {
	    victual.reqtime = objects[otmp->otyp].oc_delay;
	    if (otmp->otyp != FORTUNE_COOKIE &&
		(otmp->cursed ||
		 (((monstermoves - otmp->age) > otmp->blessed ? 50 : 30) &&
		(otmp->orotten || !rn2(7))))) {

#ifdef POLYSELF
		if(rottenfood(otmp)) {
#else
  		if(rottenfood()) {
#endif
		    otmp->orotten = TRUE;
		    return(1);
		}
		otmp->oeaten >>= 1;
	    } else fprefx(otmp);
	}

	/* re-calc the nutrition */
	if (otmp->otyp == CORPSE) basenutrit = mons[otmp->corpsenm].cnutrit;
	else basenutrit = objects[otmp->otyp].nutrition;

#ifdef DEBUG
	debug("before rounddiv: victual.reqtime == %d", victual.reqtime);
	debug("oeaten == %d, basenutrit == %d", otmp->oeaten, basenutrit);
#endif
	victual.reqtime = (basenutrit == 0 ? 0 :
	  (int)rounddiv(victual.reqtime * (long)otmp->oeaten,(long)basenutrit));
#ifdef DEBUG
	debug("after rounddiv: victual.reqtime == %d", 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(victual.reqtime == 0)
	    /* possible if most has been eaten before */
	    victual.nmod = 0;
	else if (otmp->oeaten > victual.reqtime)
	    victual.nmod = -(otmp->oeaten / victual.reqtime);
	else
	    victual.nmod = victual.reqtime % otmp->oeaten;
	victual.canchoke = (u.uhs == SATIATED);

	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 int
bite()
{
	if(victual.canchoke && u.uhunger >= 2000) {
		choke(victual.piece);
		return 1;
	}
	if (victual.doreset) {
		do_reset_eat();
		return 0;
	}
	if(victual.nmod < 0) {
		lesshungry(-victual.nmod);
		victual.piece->oeaten -= -victual.nmod;
	} else if(victual.nmod > 0 && (victual.usedtime % victual.nmod)) {
		lesshungry(1);
		victual.piece->oeaten--;
	}
	recalc_wt();
	return 0;
}

#endif /* OVLB */
#ifdef OVL0

void
gethungry() {		/* as time goes by - called in main.c */
	--u.uhunger;
	if(moves % 2) {
		if(HRegeneration) u.uhunger--;
		if(Hunger) u.uhunger--;
		/* a3:  if(Hunger & LEFT_RING) u.uhunger--;
			if(Hunger & RIGHT_RING) u.uhunger--;
		   etc. */
	}
	if(moves % 20 == 0) {			/* jimt@asgb */
		/* +0 rings don't do anything, so don't affect hunger */
		if(uleft && uleft->otyp && (!objects[uleft->otyp].oc_charged
			|| uleft->spe)) u.uhunger--;
		if(uright && uright->otyp && (!objects[uright->otyp].oc_charged
			|| uright->spe)) u.uhunger--;
		if(uamul) u.uhunger--;
		if(u.uhave_amulet) u.uhunger--;
	}
	newuhs(TRUE);
}

#endif /* OVL0 */
#ifdef OVLB

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;
{
#ifdef DEBUG
	debug("lesshungry(%d)", num);
#endif
	u.uhunger += num;
	if(u.uhunger >= 2000) {
	    if (!victual.eating || victual.canchoke)
		if (victual.eating) {
			choke(victual.piece);
			reset_eat();
		} else
			choke((struct obj *) 0);
			/* no reset_eat(); it was a non-food such as juice */
	} else {
	    /* Have lesshungry() report when you're nearly full so all eating
	     * warns when you're about to choke.
	     */
	    if (u.uhunger >= 1500) {
	      if(!victual.eating || (victual.eating && !victual.fullwarn)) {
		pline("You're having a hard time getting all of it down.");
		nomovemsg = "You're finally finished.";
		if(!victual.eating)
		    multi = -2;
		else {
		    victual.fullwarn = TRUE;
		    if (victual.canchoke) {
			pline("Stop eating? ");
			if(yn() == 'y') reset_eat();
		    }
		}
	      }
	    }
	}
	newuhs(FALSE);
}

#ifndef OVERLAY
static 
#endif
int
unfaint() {
	(void) Hear_again();
	u.uhs = FAINTING;
	flags.botl = 1;
	return 0;
}

#endif /* OVLB */
#ifdef OVL0

boolean
is_fainted() {
	return(u.uhs == FAINTED);
}

void
reset_faint() {	/* call when a faint must be prematurely terminated */
	if(is_fainted()) nomul(0);
}

void
sync_hunger() {

	if(is_fainted()) {

		flags.soundok = 0;
		nomul(-10+(u.uhunger/10));
		nomovemsg = "You regain consciousness.";
		afternmv = unfaint;
	}
}

void
newuhs(incr)		/* compute and comment on your (new?) hunger status */
	boolean incr;
{
	register int newhs, h = u.uhunger;

	newhs = (h > 1000) ? SATIATED :
		(h > 150) ? NOT_HUNGRY :
		(h > 50) ? HUNGRY :
		(h > 0) ? WEAK : FAINTING;

	if(newhs == FAINTING) {
		if(is_fainted()) newhs = FAINTED;
		if(u.uhs <= WEAK || rn2(20-u.uhunger/10) >= 19) {
			if(!is_fainted() && multi >= 0 /* %% */) {
				You("faint from lack of food.");
				flags.soundok = 0;
				nomul(-10+(u.uhunger/10));
				nomovemsg = "You regain consciousness.";
				afternmv = unfaint;
				newhs = FAINTED;
			}
		} else
		if(u.uhunger < -(int)(200 + 20*ACURR(A_CON))) {
			u.uhs = STARVED;
			flags.botl = 1;
			bot();
			You("die from starvation.");
			killer_format = KILLED_BY;
			killer = "starvation";
			done(STARVING);
		}
	}

	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) {
			    pline((!incr) ?
				"You now have a lesser case of the munchies." :
				"You are getting the munchies.");
			} else
			    You((!incr) ? "only feel hungry now." :
				  (u.uhunger < 145) ? "feel hungry." :
				   "are beginning to feel hungry.");
			break;
		case WEAK:
			if (Hallucination)
			    pline((!incr) ?
				  "You still have the munchies." :
      "The munchies are starting to interfere with your motor capabilities.");
			else
			    You((!incr) ? "feel weak now." :
				  (u.uhunger < 45) ? "feel weak." :
				   "are beginning to feel weak.");
			break;
		}
		u.uhs = newhs;
		flags.botl = 1;
		if(u.uhp < 1) {
			You("die from hunger and exhaustion.");
			killer_format = KILLED_BY;
			killer = "exhaustion";
			done(STARVING);
		}
	}
}

#endif /* OVL0 */
#ifdef OVLB

/* Returns an object representing food.  Object may be either on floor or
 * in inventory.
 */
struct obj *
floorfood(verb,corpseonly)	/* get food from floor or pack */
	const char *verb;
	boolean corpseonly;
{
	register struct obj *otmp;
#ifdef POLYSELF
	struct gold *gold = g_at(u.ux, u.uy);
	boolean feeding = (!strcmp(verb, "eat"));
#endif
#ifdef POLYSELF
	if (feeding && gold && metallivorous(uasmon)) {
	    if (gold->amount == 1)
		pline("There is 1 gold piece here; eat it? ");
	    else pline("There are %ld gold pieces here; eat them? ",
								gold->amount);
	    if (yn() == 'y') {
		otmp = newobj(0);
		otmp->olet = GOLD_SYM;
		otmp->ox = u.ux;
		otmp->oy = u.uy;
		OGOLD(otmp) = gold->amount;
		freegold(gold);
		return otmp;
	    }
	}
#endif
	/* Is there some food (probably a heavy corpse) here on the ground? */
	if(!Levitation && !u.uswallow) {
	    for(otmp = level.objects[u.ux][u.uy]; otmp; otmp = otmp->nexthere) {
		if(corpseonly ? otmp->otyp==CORPSE : 
#ifdef POLYSELF
		    feeding ? is_edible(otmp) :
#endif
						otmp->olet==FOOD_SYM) {
			pline("There %s %s here; %s %s? ",
				(otmp->quan == 1) ? "is" : "are",
				doname(otmp), verb,
				(otmp->quan == 1) ? "it" : "one");
			if(yn() == 'y')
				return(otmp);
		}
	    }
	}
#ifdef POLYSELF
	/* We cannot use "#" since that causes getobj() to skip its
	 * "ugly checks" and we need to check for inedible items.
	 */
	return getobj (feeding ? (const char *)everything :
				 (const char *)comestibles, verb);
#else
	return getobj(comestibles, verb);
#endif
}

/* Side effects of vomiting */
/* added nomul (MRS) - it makes sense, you're too busy being sick! */
/* TO DO: regurgitate swallowed monsters when poly'd */
void
vomit() {		/* A good idea from David Neves */
	make_sick(0L,TRUE);
	nomul(-2);
}

int
eaten_stat(base, obj)
register int base;
register struct obj *obj;
{
	base *= obj->oeaten;

	if (obj->otyp == CORPSE) 
	    base = mons[obj->corpsenm].cnutrit ?
				base / mons[obj->corpsenm].cnutrit : 0;
	else base = objects[obj->otyp].nutrition ?
				base / objects[obj->otyp].nutrition : 0;
	return (base < 1) ? 1 : base;
}

#endif /* OVLB */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.