1
0
Fork 0
mirror of https://codeberg.org/noisytoot/notnotdnethack.git synced 2025-07-28 16:32:27 +01:00
notnotdnethack/src/eat.c
Ron Nazarov 7dd65d4260
New race: octopode
Based on http://crawl.chaosforge.org/Octopode to some extent.

-Can wear 8 rings.
--Extra rings fall off if polymorphed.
--Not a valid polymorph form, but it should work.
-Can be wizards, rogues, pirates, acus, or binders.
-Attribute caps:
 Str   Int Wis Dex Con Cha
 18/** 20  18  20  16  16
-Special spell: poison spray.
--Not very useful since they don't have a special way of learning it.
-Human HP and energy growth.
-Radius-2 low light vision.
-Medium sized.  Can wear barded helmets and soft hats, cloaks, dragon
 scales (not scale mail) and waistcloths, shields.
-Extra 1d8 tentacle wrap attack that can drown if over water.
-+level/10 bonus to natural DR and +level/2 bonus to natural AC, to
 compensate for being unable to wear most armour.
--Rings of protection can also compensate, but unlike in DCSS they
  need to be identified and charged first.
-Starts with stone resistance, poison resistance, stealth, swimming,
 magical breathing, waterproofing.
--Stone resistance because it would be unplayable otherwise (no
  gloves).
--Waterproof so swimming is usable.
-Immune to heart-piercing and bisection vorpality (doesn't have any
 special effect) and being ripped apart by Demogorgon (does double damage
 instead).
--Immunity to heart piercing vorpality could be explained by octopodes
  having 3 hearts, but the real reason for this is that they can't wear
  armour that would protect them.
-Weldproof because I didn't implement interactions of curses with
 being able to wear 8 rings.  Unlike the other intrinsics, this is
 only provided from form (you will lose it if you polymorph).
-Immune to glib because of their suckers (and because I didn't
 implement how being able to wear 8 rings interacts with it).
-Can't have rings put on by seduction because I didn't implement it.

Also:
-Parasitized androids are weldproof.
-Enlightenment message for white hlf waterproofing.
2024-04-12 14:30:52 +01:00

4958 lines
148 KiB
C

/* SCCS Id: @(#)eat.c 3.4 2003/02/13 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
#include "hack.h"
#include "artifact.h"
/* #define DEBUG */ /* uncomment to enable new eat code debugging */
# ifdef WIZARD
#define debugpline if (wizard) pline
#endif
STATIC_PTR int NDECL(eatmdone);
STATIC_PTR int NDECL(eatfood);
STATIC_PTR void FDECL(costly_tin, (const char*));
STATIC_PTR int NDECL(opentin);
STATIC_PTR int NDECL(windclock);
STATIC_PTR int FDECL(clockwork_eat_menu, (BOOLEAN_P,BOOLEAN_P));
#ifdef OVLB
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(givit, (int,struct permonst *,SHORT_P,BOOLEAN_P));
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(accessory_has_effect, (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 void FDECL(eataccessory, (struct obj *));
STATIC_DCL const char *FDECL(foodword, (struct obj *));
STATIC_DCL boolean FDECL(maybe_cannibal, (int,BOOLEAN_P));
char msgbuf[BUFSZ];
int etype; /* Clockwork's eat type */
#endif /* OVLB */
#ifndef OVLB
STATIC_DCL NEARDATA const char comestibles[];
STATIC_DCL NEARDATA const char allobj[];
STATIC_DCL boolean force_save_hs;
#else
STATIC_OVL NEARDATA const char comestibles[] = { FOOD_CLASS, 0 };
/* Gold must come first for getobj(). */
STATIC_OVL NEARDATA const char allobj[] = {
COIN_CLASS, WEAPON_CLASS, ARMOR_CLASS, POTION_CLASS, SCROLL_CLASS, TILE_CLASS,
WAND_CLASS, RING_CLASS, AMULET_CLASS, FOOD_CLASS, TOOL_CLASS,
GEM_CLASS, ROCK_CLASS, BALL_CLASS, CHAIN_CLASS, SPBOOK_CLASS, BED_CLASS, SCOIN_CLASS, 0 };
STATIC_OVL boolean force_save_hs = FALSE;
const char *hu_stat[] = {
"Satiated",
"",
"Hungry",
"Weak",
"Fainting",
"Fainted",
"Starved"
};
const char *ca_hu_stat[] = {
"OvrWound",
"",
"Waning",
"Unwound",
"Slipping",
"Slipped",
"Stopped"
};
#endif /* OVLB */
#ifdef OVL1
boolean
incantifier_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)) */
/* Repeating this check in the name of futureproofing */
if (objects[obj->otyp].oc_unique || obj->oartifact ||
(objects[obj->otyp].oc_merge &&
obj->otyp != CORPSE &&
obj->otyp != TIN &&
obj->oclass != SCROLL_CLASS)
) return FALSE;
if(obj->spe > 0){
if(obj->oclass == WEAPON_CLASS) return TRUE;
if(obj->oclass == ARMOR_CLASS) return TRUE;
if(obj->oclass == TOOL_CLASS && is_weptool(obj)) return TRUE;
}
if(obj->oclass == SCROLL_CLASS && objects[obj->otyp].oc_magic && !obj->oartifact) return TRUE;
if(obj->oclass == SPBOOK_CLASS && objects[obj->otyp].oc_magic && !obj->oartifact) return TRUE;
if(obj->oclass == AMULET_CLASS && obj->spe >= 0 && objects[obj->otyp].oc_magic && !obj->oartifact) return TRUE;
if(obj->oclass == RING_CLASS && obj->spe >= 0 && objects[obj->otyp].oc_magic && !obj->oartifact) return TRUE;
if(obj->oclass == WAND_CLASS && obj->spe > 0 && objects[obj->otyp].oc_magic) return TRUE;
if(obj->otyp == CORPSE && (obj->corpsenm == PM_AOA || obj->corpsenm == PM_AOA_DROPLET || obj->corpsenm == PM_NEWT)) return TRUE;
if(obj->otyp == TIN && (!obj->known || obj->corpsenm == PM_AOA || obj->corpsenm == PM_AOA_DROPLET || obj->corpsenm == PM_NEWT)) return TRUE;
if(obj->otyp == MAGIC_WHISTLE) return TRUE;
return FALSE;
}
boolean
uclockwork_edible(obj)
struct obj *obj;
{
/* protect invocation tools but not Rider corpses (handled elsewhere)*/
/* if (obj->oclass != FOOD_CLASS && obj_resists(obj, 0, 0)) */
/* Repeating this check in the name of futureproofing */
if (objects[obj->otyp].oc_unique)
return FALSE;
if(etype == SCRAP_MAW && is_metallic(obj) && is_corrodeable(obj) && !(obj->oartifact))
return TRUE;
if(etype == WOOD_STOVE && !(obj->oartifact)){
if(obj->otyp == EUCALYPTUS_LEAF ||
obj->otyp == SPRIG_OF_WOLFSBANE ||
obj->otyp == FORTUNE_COOKIE ||
obj->otyp == LEMBAS_WAFER ||
obj->otyp == SHEAF_OF_HAY ||
obj->otyp == SEDGE_HAT ||
is_wood(obj) ||
(is_paper(obj)
#ifdef MAIL
&& obj->otyp != SCR_MAIL
#endif
)
) return TRUE;
}
if(etype == HELLFIRE_FURNACE && !(obj->oartifact)){
if(obj->obj_material >= WAX && obj->obj_material <= CHITIN
#ifdef MAIL
&& obj->otyp != SCR_MAIL
#endif
)
return TRUE;
}
if(etype == MAGIC_FURNACE && !(objects[obj->otyp].oc_merge &&
obj->otyp != CORPSE &&
obj->otyp != TIN &&
obj->oclass != SCROLL_CLASS)
){
if(obj->spe > 0){
if(obj->oclass == WEAPON_CLASS) return TRUE;
if(obj->oclass == ARMOR_CLASS) return TRUE;
if(obj->oclass == TOOL_CLASS && is_weptool(obj)) return TRUE;
}
if(obj->oclass == SCROLL_CLASS && obj->otyp != SCR_BLANK_PAPER && obj->otyp != SCR_GOLD_SCROLL_OF_LAW &&
#ifdef MAIL
obj->otyp != SCR_MAIL &&
#endif
!obj->oartifact) return TRUE;
if(obj->oclass == SPBOOK_CLASS && obj->otyp != SPE_BLANK_PAPER && !obj->oartifact) return TRUE;
if(obj->oclass == AMULET_CLASS && obj->spe >= 0 && !obj->oartifact) return TRUE;
if(obj->oclass == RING_CLASS && obj->spe >= 0 && !obj->oartifact) return TRUE;
if(obj->oclass == WAND_CLASS && obj->spe > 0 && obj->otyp != WAN_NOTHING) return TRUE;
if(obj->otyp == CORPSE && (obj->corpsenm == PM_AOA || obj->corpsenm == PM_AOA_DROPLET || obj->corpsenm == PM_NEWT)) return TRUE;
}
return FALSE;
}
/*
* 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(Race_if(PM_INCANTIFIER)) return incantifier_edible(obj);
if(magivorous(youracedata)) return incantifier_edible(obj);
if(uclockwork) return uclockwork_edible(obj);
if(Role_if(PM_ANACHRONONAUT) && !(Upolyd || Race_if(PM_VAMPIRE)))
return ((obj->otyp >= K_RATION && obj->otyp <= TIN) || (obj->otyp >= SLIME_MOLD && obj->otyp <= TIN &&
(obj->obj_material == VEGGY || obj->obj_material == FLESH))); /*Processed foods only*/
if (metallivorous(youracedata) && is_metallic(obj) &&
(youracedata->mtyp != PM_RUST_MONSTER || is_rustprone(obj)))
return TRUE;
if ((u.umonnum == PM_GELATINOUS_CUBE || u.umonnum == PM_ANCIENT_OF_CORRUPTION) && 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;
if(obj->otyp == FEATHER) return FALSE;
/* a sheaf of straw is VEGGY, but only edible for herbivorous animals */
if ((obj->otyp == ROPE_OF_ENTANGLING || obj->otyp == SHEAF_OF_HAY || obj->otyp == SEDGE_HAT)
&& herbivorous(youracedata)
&& (obj->obj_material == VEGGY)
) return !carnivorous(youracedata);
if (herbivorous(youracedata) && is_veggy(obj))
return TRUE;
/* Ghouls only eat corpses */
if (u.umonnum == PM_GHOUL)
return (boolean)(obj->otyp == CORPSE && obj->obj_material == FLESH);
/* Vampires drink the blood of meaty corpses */
/* [ALI] (fully) drained food is not presented as an option,
* but partly eaten food is (even though you can't drain it).
*/
if (is_vampire(youracedata))
return (boolean)(obj->otyp == CORPSE && obj->obj_material == FLESH &&
has_blood(&mons[obj->corpsenm]) && (!obj->odrained ||
obj->oeaten > drainlevel(obj)));
if (carnivorous(youracedata) && (obj->obj_material == FLESH))
return TRUE;
/* return((boolean)(!!index(comestibles, obj->oclass))); */
return (boolean)(obj->oclass == FOOD_CLASS && (obj->obj_material == VEGGY || obj->obj_material == FLESH || obj->otyp == TIN));
}
#endif /* OVL1 */
#ifdef OVLB
void
init_uhunger()
{
if(Race_if(PM_INCANTIFIER)){
u.uenbonus += 1800;
calc_total_maxen();
u.uen = get_satiationlimit()*0.9;
} else {
u.uhungermax = DEFAULT_HMAX;
u.uhunger = get_satiationlimit()*.9;
}
u.uhs = NOT_HUNGRY;
}
void
reset_uhunger()
{
if(Race_if(PM_INCANTIFIER)){
u.uen = min(u.uen+400, get_satiationlimit()*.9);
newuhs(TRUE);
} else {
u.uhunger = get_satiationlimit()*.9;
u.uhs = NOT_HUNGRY;
}
}
boolean
satiate_uhunger()
{
if(Race_if(PM_INCANTIFIER)){
if(u.uen > min(u.uen+400, get_satiationlimit()*11/10))
return FALSE;
u.uen = min(u.uen+400, get_satiationlimit()*11/10);
newuhs(TRUE);
} else {
if(u.uhunger >= get_satiationlimit()*11/10)
return FALSE;
u.uhunger = get_satiationlimit()*11/10;
u.uhs = NOT_HUNGRY;
}
return TRUE;
}
double
get_uhungersizemod()
{
double sizemod = 1;
if(youracedata->msize > MZ_MEDIUM)
sizemod *= 1 + youracedata->msize - MZ_MEDIUM;
else if(youracedata->msize < MZ_MEDIUM)
sizemod /= 1 + MZ_MEDIUM - youracedata->msize;
return sizemod;
}
int
get_uhungermax()
{
int hungermax = u.uhungermax;
if(youracedata->msize > MZ_MEDIUM)
hungermax *= 1 + youracedata->msize - MZ_MEDIUM;
//For gameplay reasons, Gnomes et al can overeat by the same amount as humans before choking
return hungermax;
}
int
get_satiationlimit()
{
int hungermax = get_uhungermax();
/* Satiation is special-cased for Clockworks and Incantifiers,
* since both of their hunger mechanics strongly incentize staying
* near full.
* 3/4 max for incantifiers is around 1500/2000 to 3750/5000 (from xp1 to xp30)
* 3/4 max for clockworks is 1500/2000 to 15000/20000 (from no to max upgrades)
* This is applied AFTER any multiplicative changes to size, so polyselfed clockworks
* have something like 45,000/60,000 for huge forms or 90,000/120,000 for gigantic forms.
* Wild.
*/
if (Race_if(PM_INCANTIFIER)) return max((u.uenmax*4)/5, 200);
if (uclockwork) return (hungermax*3)/4;
return hungermax/2;
}
static const struct { const char *txt; int nut; } tintxts[] = {
{"deep fried", 60},
{"pickled", 40},
{"soup made from", 20},
{"pureed", 500},
#define ROTTEN_TIN 4
{"rotten", -50},
#define HOMEMADE_TIN 5
{"homemade", 50},
{"stir fried", 80},
{"candied", 100},
{"boiled", 50},
#define DRIED_TIN 9
{"dried", 55},
{"szechuan", 70},
#define FRENCH_FRIED_TIN 11
{"french fried", 40},
{"sauteed", 95},
{"broiled", 80},
{"smoked", 50},
{"", 0}
};
#define TTSZ SIZE(tintxts)
static NEARDATA struct {
struct obj *tin;
int usedtime, reqtime;
} tin;
static NEARDATA 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,
* in which case this should be 0 */
/* doeat() initializes these when piece is valid */
int usedtime, /* turns spent eating */
reqtime; /* turns required to eat */
int nmod; /* coded nutrition per turn */
struct monst *mon; /* monster associated with item */
Bitfield(canchoke,1); /* was satiated at beginning */
/* start_eating() initializes these */
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;
static char *eatmbuf = 0; /* set by cpostfx() */
STATIC_PTR
int
eatmdone() /* 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;
}
/* ``[the(] singular(food, xname) [)]'' with awareness of unique monsters */
STATIC_OVL const char *
food_xname(food, the_pfx)
struct obj *food;
boolean the_pfx;
{
const char *result;
int mtyp = food->corpsenm;
if (food->otyp == CORPSE && (mons[mtyp].geno & G_UNIQ)) {
/* grab xname()'s modifiable return buffer for our own use */
char *bufp = xname(food);
Sprintf(bufp, "%s%s corpse",
(the_pfx && !type_is_pname(&mons[mtyp])) ? "the " : "",
s_suffix(mons[mtyp].mname));
result = bufp;
} 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(-5); /* gluttony is unchivalrous */ //stiffer penalty
You_feel("like a glutton!");
}
exercise(A_CON, FALSE);
if ((Breathless || (!Strangled && !rn2(20))) && !Race_if(PM_INCANTIFIER) && !magivorous(youracedata)) {
/* 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*get_uhungersizemod()); /* you just got *very* sick! */
nomovemsg = 0;
vomit();
} else {
if(Race_if(PM_INCANTIFIER)){
killer_format = KILLED_BY;
You("absorb too much energy and explode.");
killer = Hallucination ? "amateur-hour horseshit" : "absorbing too much energy and exploding"; //8-bit theater
You("die...");
done(DISINTEGRATED);
explode(u.ux, u.uy, AD_MAGM, MON_EXPLODE, u.uhpmax/2, EXPL_MAGICAL, 1);
u.uhp = u.uhpmax/2;
pline("You reform!");
morehungry(u.uenmax/2); /* lifesaved */
} else if(magivorous(youracedata)){
You("absorb too much energy and then vomit up a rainbow!");
morehungry(1000*get_uhungersizemod()); /* you just got *very* sick! */
nomovemsg = 0;
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) {
delayed_killer = "a very rich meal";
} else {
delayed_killer = food_xname(food, FALSE);
if (food->otyp == CORPSE &&
(mons[food->corpsenm].geno & G_UNIQ)) {
if (!type_is_pname(&mons[food->corpsenm]))
delayed_killer = the(delayed_killer);
killer_format = KILLED_BY;
}
else
delayed_killer = an(delayed_killer);
}
} else {
You("choke over it.");
delayed_killer = "a quick snack";
}
u.divetimer = (u.divetimer+1)/2;
HStrangled |= rnd(u.divetimer) + 1;
do_reset_eat();
}
}
}
/* modify object wt. depending on time spent consuming it */
STATIC_OVL void
recalc_wt()
{
struct obj *piece = victual.piece;
//debugpline("Old weight = %d", piece->owt);
//debugpline("Used time = %d, Req'd time = %d",
// victual.usedtime, victual.reqtime);
piece->owt = weight(piece);
//debugpline("New weight = %d", piece->owt);
}
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) {
//debugpline("reset_eat...");
victual.doreset = TRUE;
}
return;
}
STATIC_OVL struct obj *
touchfood(otmp)
register struct obj *otmp;
{
struct obj *pseudo;
if (otmp->quan > 1L) {
if(!carried(otmp))
(void) splitobj(otmp, otmp->quan - 1L);
else
otmp = splitobj(otmp, 1L);
//debugpline("split object,");
}
if (!otmp->oeaten) {
if(((!carried(otmp) && costly_spot(otmp->ox, otmp->oy) &&
!otmp->no_charge)
|| otmp->unpaid)) {
/* create a dummy duplicate to put on bill */
verbalize("You bit it, you bought it!");
bill_dummy_object(otmp);
}
if((!(u.uconduct.unvegetarian) || Role_if(PM_MONK)) && (otmp->otyp == K_RATION || otmp->otyp == C_RATION)){
You("find that this military ration has a meat serving.");
if(yn("Eat the meat?") == 'n'){
You("discard the meat.");
otmp->oeaten = obj_nutrition(otmp) / 4;
}
else{
You("eat the meat.");
otmp->oeaten = obj_nutrition(otmp);
u.uconduct.unvegan++;
violated_vegetarian();
}
}
else if(otmp->otyp == CANDY_BAR){
if(!rn2(10)){
You("find this candy bar to be of rare quality!");
// otmp->oeaten = 10 * objects[otmp->otyp].oc_nutrition;
pluslvl(FALSE);/*first bite gives level up, also has a lot of nutrition*/
if(((!carried(otmp) && costly_spot(otmp->ox, otmp->oy) &&
!otmp->no_charge)
|| otmp->unpaid)) {
/* create a dummy duplicate to put on bill */
verbalize("Hey, that was really rare!");
pseudo = mksobj(POT_GAIN_LEVEL, MKOBJ_NOINIT);
pseudo->blessed = pseudo->cursed = 0;
bill_dummy_object(pseudo);
obfree(pseudo, (struct obj *)0); /* now, get rid of it */
}
}
else otmp->oeaten = obj_nutrition(otmp);
}
else otmp->oeaten = obj_nutrition(otmp);
}
if (carried(otmp)) {
freeinv(otmp);
if (inv_cnt() >= 52) {
sellobj_state(SELL_DONTSELL);
dropy(otmp);
sellobj_state(SELL_NORMAL);
} else {
otmp->nomerge = TRUE;
otmp = addinv(otmp);
otmp->nomerge = FALSE;
}
}
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;
if (obj->timed) stop_all_timers(obj->timed);
}
/* 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 == victual.piece) victual.piece = new_obj;
if (old_obj == tin.tin) tin.tin = new_obj;
}
STATIC_OVL void
do_reset_eat()
{
//debugpline("do_reset_eat...");
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();
newuhs(FALSE);
}
STATIC_PTR
int
eatfood() /* called each move during eating process */
{
if(!victual.piece ||
(!carried(victual.piece) && !obj_here(victual.piece, u.ux, u.uy))) {
/* maybe it was stolen? */
do_reset_eat();
return MOVE_CANCELLED;
}
if (is_vampire(youracedata) != victual.piece->odrained) {
/* Polymorphed while eating/draining */
do_reset_eat();
return MOVE_CANCELLED;
}
if(!victual.eating) return MOVE_CANCELLED;
if(++victual.usedtime <= victual.reqtime) {
if(bite()) return MOVE_CANCELLED;
return MOVE_ATE; /* still busy */
} else { /* done */
int crumbs = victual.piece->oeaten; /* The last crumbs */
if (victual.piece->odrained) crumbs -= drainlevel(victual.piece);
if (crumbs > 0) {
lesshungry(crumbs);
victual.piece->oeaten -= crumbs;
}
done_eating(TRUE);
return MOVE_FINISHED_OCCUPATION;
}
}
STATIC_OVL void
done_eating(message)
boolean message;
{
victual.piece->in_use = TRUE;
occupation = 0; /* do this early, so newuhs() knows we're done */
newuhs(FALSE);
if (nomovemsg) {
if (message) pline1(nomovemsg);
nomovemsg = 0;
} else if (message)
You("finish %s %s.", victual.piece->odrained ? "draining" :
"eating", food_xname(victual.piece, TRUE));
if(victual.piece->otyp == CORPSE) {
cpostfx(victual.piece->corpsenm, FALSE, FALSE, victual.piece->odrained);
} else
fpostfx(victual.piece);
if (victual.piece->odrained)
victual.piece->in_use = FALSE;
else
if (carried(victual.piece)) useup(victual.piece);
else useupf(victual.piece, 1L);
victual.piece = (struct obj *) 0;
victual.fullwarn = victual.eating = victual.doreset = FALSE;
}
STATIC_OVL boolean
maybe_cannibal(pm, allowmsg)
int pm;
boolean allowmsg;
{
if(your_race(&mons[pm]) && !is_animal(&mons[pm]) && !mindless(&mons[pm])){
if (!CANNIBAL_ALLOWED()) {
if (allowmsg) {
if (Upolyd)
You("have a bad feeling deep inside.");
You("cannibal! You will regret this!");
}
HAggravate_monster |= TIMEOUT_INF;
change_luck(-rn1(4,2)); /* -5..-2 */
} else if (Role_if(PM_CAVEMAN)) {
adjalign(sgn(u.ualign.type));
You("honor the dead.");
} else {
adjalign(-sgn(u.ualign.type));
You_feel("evil and fiendish!");
}
return TRUE;
}
return FALSE;
}
void
cprefx(pm,bld,nobadeffects)
int pm;
BOOLEAN_P bld, nobadeffects;
{
if(!nobadeffects) maybe_cannibal(pm,TRUE);
if (!nobadeffects && (touch_petrifies(&mons[pm]) || pm == PM_MEDUSA)) {
if (!Stone_resistance &&
!(poly_when_stoned(youracedata) && polymon(PM_STONE_GOLEM))) {
Sprintf(killer_buf, "tasting %s %s", mons[pm].mname, bld ? "blood" : "meat");
killer_format = KILLED_BY;
killer = killer_buf;
You("turn to stone.");
done(STONING);
if (victual.piece)
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 (!nobadeffects && !CANNIBAL_ALLOWED()) {
You_feel("that %s the %s%s%s was a bad idea.",
victual.eating ? "eating" : bld ? "drinking" : "biting",
occupation == opentin ? "tinned " : "", mons[pm].mname,
bld ? " blood" : "");
HAggravate_monster |= TIMEOUT_INF;
}
break;
case PM_LIZARD:
case PM_BABY_CAVE_LIZARD:
case PM_SMALL_CAVE_LIZARD:
case PM_CAVE_LIZARD:
case PM_LARGE_CAVE_LIZARD:
if (Stoned) fix_petrification();
if (Golded) fix_petrification();
break;
case PM_MANDRAKE:
if(!nobadeffects){
pline ("Oh wow! Great stuff!");
make_hallucinated(HHallucination + 200,FALSE,0L);
}
if (Stoned) fix_petrification();
if (Golded) fix_petrification();
make_sick(0L, (char *) 0, TRUE, SICK_ALL);
break;
case PM_GREEN_SLIME:
case PM_FLUX_SLIME:
if (!nobadeffects && !Slime_res(&youmonst)) {
You("don't feel very well.");
Slimed = 10L;
flags.botl = 1;
}
/* Fall through */
default:
if(is_deadly(&mons[pm])){
char buf[BUFSZ];
if(!nobadeffects){
pline("Eating that is instantly fatal.");
Sprintf(buf, "unwisely ate the body of %s",
mons[pm].mname);
killer = buf;
killer_format = NO_KILLER_PREFIX;
done(DIED);
}
}
if(is_rider(&mons[pm])){
/* It so happens that since we know these monsters */
/* cannot appear in tins, victual.piece will always */
/* be what we want, which is not generally true. */
if (revive_corpse(victual.piece, REVIVE_MONSTER))
victual.piece = (struct obj *)0;
return;
}
if (acidic(&mons[pm]) && (Stoned || Golded))
fix_petrification();
break;
}
}
/*
* Called when a vampire bites a monster.
* Returns TRUE if hero died and was lifesaved.
*/
boolean
bite_monster(mon)
struct monst *mon;
{
switch(monsndx(mon->data)) {
case PM_LIZARD:
case PM_BABY_CAVE_LIZARD:
case PM_SMALL_CAVE_LIZARD:
case PM_CAVE_LIZARD:
case PM_LARGE_CAVE_LIZARD:
if (Stoned) fix_petrification();
if (Golded) fix_petrification();
break;
// case PM_MANDRAKE: No blood
// if(!nobadeffects){
// pline ("Oh wow! Great stuff!");
// make_hallucinated(HHallucination + 200,FALSE,0L);
// }
// if (Stoned) fix_petrification();
// if (Golded) fix_petrification();
// make_sick(0L, (char *) 0, TRUE, SICK_ALL);
// return TRUE;
case PM_DEATH:
case PM_PESTILENCE:
case PM_FAMINE:
pline("Unfortunately, eating any of it is fatal.");
done_in_by(mon);
return TRUE; /* lifesaved */
case PM_GREEN_SLIME:
case PM_FLUX_SLIME:
if (!Slime_res(&youmonst)) {
You("don't feel very well.");
Slimed = 10L;
}
/* Fall through */
default:
if (acidic(mon->data) && (Stoned || Golded))
fix_petrification();
break;
}
return FALSE;
}
void
fix_petrification()
{
Stoned = 0;
Golded = 0;
delayed_killer = 0;
if (Hallucination)
pline("What a pity - you just ruined a future piece of %sart!",
ACURR(A_CHA) > 15 ? "fine " : "");
else
You_feel("limber!");
}
/*
* 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. */
int
intrinsic_possible(type, ptr)
int type;
register struct permonst *ptr;
{
switch (type) {
case FIRE_RES:
if (ptr->mconveys & MR_FIRE) {
debugpline("can get fire resistance");
return(TRUE);
} else return(FALSE);
case SLEEP_RES:
if (ptr->mconveys & MR_SLEEP) {
debugpline("can get sleep resistance");
return(TRUE);
} else
return(FALSE);
case COLD_RES:
if (ptr->mconveys & MR_COLD) {
debugpline("can get cold resistance");
return(TRUE);
} else
return(FALSE);
case ACID_RES:
if (ptr->mconveys & MR_ACID) {
debugpline("can get acid resistance");
return(TRUE);
} else
return(FALSE);
case DISINT_RES:
if (ptr->mconveys & MR_DISINT) {
debugpline("can get disintegration resistance");
return(TRUE);
} else
return(FALSE);
case SHOCK_RES: /* shock (electricity) resistance */
if (ptr->mconveys & MR_ELEC) {
debugpline("can get shock resistance");
return(TRUE);
} else
return(FALSE);
case POISON_RES:
if (ptr->mconveys & MR_POISON) {
debugpline("can get poison resistance");
return(TRUE);
} else
return(FALSE);
case TELEPORT:
if (species_teleports(ptr)) {
debugpline("can get teleport");
return(TRUE);
} else
return(FALSE);
case DISPLACED:
if (species_displaces(ptr)) {
debugpline("can displacement");
return(TRUE);
} else
return(FALSE);
case TELEPORT_CONTROL:
if (species_controls_teleports(ptr)) {
debugpline("can get teleport control");
return(TRUE);
} else
return(FALSE);
case TELEPAT:
if (species_is_telepathic(ptr)) {
debugpline("can get telepathy");
return(TRUE);
} else
return(FALSE);
default:
return(FALSE);
}
}
void
give_intrinsic(int type, long duration){
boolean permanent = (duration == -1);
boolean nullify = (duration == -2);
if (permanent){
u.uprops[type].intrinsic |= TIMEOUT_INF;
return;
}
if (nullify){
u.uprops[type].intrinsic &= ~TIMEOUT;
}
incr_itimeout(&u.uprops[type].intrinsic, duration);
}
/* givit() tries to give you an intrinsic based on the monster's level
* and what type of intrinsic it is trying to give you.
* Energy resistence intrinsics time out now - CM
*/
STATIC_OVL void
givit(type, ptr, nutval, drained)
int type;
register struct permonst *ptr;
short nutval;
boolean drained;
{
int chance = 0; //starts at 0. Changing it indicates a non-energy resistence
int multiplier = 1;
char permanent = 0;
#ifdef DEBUG
debugpline("Attempting to give intrinsic %d", type);
#endif
/* some intrinsics are easier to get than others */
switch (type) {
case FIRE_RES:
case SLEEP_RES:
case COLD_RES:
case SHOCK_RES:
case ACID_RES:
chance = 0;//skip roll, give atribute.
break;
case POISON_RES:
if ((ptr->mtyp == PM_KILLER_BEE ||
ptr->mtyp == PM_SCORPION) && !rn2(4))
chance = 1;
else
chance = 15;
break;
case DISPLACED:
chance = ptr->mtyp == PM_SHIMMERING_DRAGON ? 0 : 10;
break;
case TELEPORT:
chance = 10;
break;
case TELEPORT_CONTROL:
chance = 12;
break;
case TELEPAT:
chance = 1;
break;
case DISINT_RES:
chance = 15;
break;
default:
chance = 15;
break;
}
if(chance && drained && rn2(5))
return; /* drain chance */
if (chance && ptr->mlevel <= rn2(chance))
return; /* failed die roll */
if(ptr->mlevel < 5) multiplier = 5;
else if (ptr->mlevel < 10) multiplier = 10;
else if (ptr->mlevel < 15) multiplier = 15;
else multiplier = 20;
if(ptr->geno & G_UNIQ) multiplier = 20;
if(ptr->geno & G_UNIQ && ptr->mlevel > 14) permanent = 1;
long duration = nutval * multiplier;
duration /= get_uhungersizemod();
if (permanent)
duration = -1;
switch (type) {
case FIRE_RES:
debugpline("Trying to give fire resistance");
if (!(HFire_resistance))
You(Hallucination ? "be chillin'." :"feel a momentary chill.");
give_intrinsic(FIRE_RES, duration);
break;
case SLEEP_RES:
debugpline("Trying to give sleep resistance");
if (!(HSleep_resistance))
You_feel("wide awake.");
give_intrinsic(SLEEP_RES, duration);
break;
case COLD_RES:
debugpline("Trying to give cold resistance");
if(!(HCold_resistance))
You_feel("full of hot air.");
give_intrinsic(COLD_RES, duration);
break;
case DISINT_RES:
debugpline("Trying to give disintegration resistance");
if (!(HDisint_resistance & (TIMEOUT_INF | FROMOUTSIDE))) {
You_feel(Hallucination ? "totally together, man." : "very firm.");
give_intrinsic(DISINT_RES, -1);
}
break;
case SHOCK_RES: /* shock (electricity) resistance */
debugpline("Trying to give shock resistance");
if (!(HShock_resistance)) {
if (Hallucination)
rn2(2) ? You_feel("grounded in reality.") : Your("health currently feels amplified!");
else You_feel("well grounded.");
}
give_intrinsic(SHOCK_RES, duration);
break;
case ACID_RES: /* acid resistance */
debugpline("Trying to give acid resistance");
if (!(HAcid_resistance)) {
if (Hallucination)
rn2(2) ? You_feel("like you've really gotten back to basics!") : You_feel("insoluble.");
else Your("skin feels leathery.");
}
give_intrinsic(ACID_RES, duration);
break;
case POISON_RES:
debugpline("Trying to give poison resistance");
if (!(HPoison_resistance & (TIMEOUT_INF | FROMOUTSIDE))) {
You_feel(Poison_resistance ? "especially healthy." : "healthy.");
give_intrinsic(POISON_RES, -1);
}
break;
case DISPLACED: /* displacement resistance */
//Note that intrinsic displacemnt disregards the timeout multiplier and permanence setting.
debugpline("Trying to give intrinsic displacement");
if (!(HDisplaced)) {
if (Hallucination) You_feel("quite beside yourself!");
else Your("outline shimmers and shifts.");
}
give_intrinsic(DISPLACED, duration);
break;
case TELEPORT:
debugpline("Trying to give teleport");
if (!(HTeleportation & (TIMEOUT_INF | FROMOUTSIDE))) {
You_feel(Hallucination ? "diffuse." : "very jumpy.");
give_intrinsic(TELEPORT, -1);
}
break;
case TELEPORT_CONTROL:
debugpline("Trying to give teleport control");
if (!(HTeleport_control & (TIMEOUT_INF | FROMOUTSIDE))) {
You_feel(Hallucination ? "centered in your personal space." : "in control of yourself.");
give_intrinsic(TELEPORT_CONTROL, -1);
}
break;
case TELEPAT:
debugpline("Trying to give telepathy");
if (!(HTelepat & (TIMEOUT_INF | FROMOUTSIDE))) {
You_feel(Hallucination ? "in touch with the cosmos." : "a strange mental acuity.");
give_intrinsic(TELEPAT, -1);
if(Hallucination) change_uinsight(1);
/* If blind, make sure monsters show up. */
if (Blind) see_monsters();
}
break;
default:
debugpline("Tried to give an impossible intrinsic");
break;
}
}
void
cpostfx(pm, tin, nobadeffects, drained) /* called after completely consuming a corpse */
register int pm;
BOOLEAN_P tin, nobadeffects, drained;
{
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(!drained || !rn2(5)){
int old_uen = u.uen;
int amnt = d(2,10);
if(Race_if(PM_INCANTIFIER)) u.uen += amnt*10;
else u.uen += amnt;
flags.botl = 1;
if (u.uen > u.uenmax){
u.uenbonus++;
calc_total_maxen();
u.uen = u.uenmax;
}
if (old_uen != u.uen){
You_feel("a mild buzz.");
flags.botl = 1;
}
}
break;
case PM_AOA_DROPLET: //Aoas are drops of pure magic
if(!drained || !rn2(5)) if (rn2(3) || 3 * u.uen <= 2 * u.uenmax) {
int old_uen = u.uen;
int amnt = d(4,10);
if(Race_if(PM_INCANTIFIER)) u.uen += amnt*10;
else u.uen += amnt;
flags.botl = 1;
if (u.uen > u.uenmax) {
u.uenbonus += 4;
calc_total_maxen();
u.uen = u.uenmax;
}
if (old_uen != u.uen) {
You_feel("a strong buzzing sensation.");
flags.botl = 1;
}
}
break;
case PM_AOA: //Aoas are drops of pure magic
if(!drained || !rn2(5)) if (rn2(3) || 3 * u.uen <= 2 * u.uenmax) {
int old_uen = u.uen;
int amnt = d(6,10);
if(Race_if(PM_INCANTIFIER)) u.uen += amnt*10;
else u.uen += amnt;
flags.botl = 1;
u.uen = u.uen + 400;
u.uen += amnt;
if (u.uen > u.uenmax) {
u.uenbonus += 10;
calc_total_maxen();
u.uen = u.uenmax;
}
if (old_uen != u.uen) {
You_feel("a great jolt run through your mind!");
flags.botl = 1;
}
}
break;
case PM_WRAITH:
if(!drained || !rn2(5)) pluslvl(FALSE);
break;
case PM_DEEP_DRAGON:
if(!drained || !rn2(5)) pluslvl(FALSE);
break;
case PM_HUMAN_WERERAT:
case PM_HUMAN_WEREJACKAL:
case PM_HUMAN_WEREWOLF:
case PM_ANUBITE:
if(!nobadeffects){
catch_lycanthropy = TRUE;
u.ulycn = transmitted_were(pm);
}
break;
case PM_NURSE:
if (Upolyd) u.mh = u.mhmax;
else u.uhp = u.uhpmax;
flags.botl = 1;
break;
case PM_SEWER_RAT:
if(!nobadeffects && !drained){
if(d(1,10) > 9 && !Sick_resistance) make_vomiting(Vomiting+d(10,4), TRUE);
}
break;
case PM_RABID_RAT:
if(!nobadeffects){
if(!rn2(20)){
if (!Sick_resistance) {
char buf[BUFSZ];
long sick_time;
sick_time = (long) d(10, 100)+1000;
/* make sure new illness doesn't result in improvement */
if (Sick && (sick_time > Sick))
sick_time = (Sick > 1L) ? Sick - 1L : 1L;
Sprintf(buf, "%s%s diseased corpse",
!type_is_pname(&mons[pm]) ? "the " : "",
s_suffix(mons[pm].mname));
make_sick(sick_time, buf, FALSE, SICK_NONVOMITABLE);
}
}
}
break;
case PM_STALKER:
if(!drained || !rn2(5)) if(!nobadeffects){
if(!Invis) {
set_itimeout(&HInvis, (long)rn1(100, 50));
if (!Blind && !BInvis) self_invis_message();
} else {
if (!(HInvis & INTRINSIC)) You_feel("hidden!");
HInvis |= TIMEOUT_INF;
HSee_invisible |= TIMEOUT_INF;
}
newsym(u.ux, u.uy);
}
/* fall into next case */
case PM_YELLOW_LIGHT:
/* fall into next case */
case PM_GIANT_BAT:
if(!nobadeffects) make_stunned(HStun + 30,FALSE);
/* fall into next case */
case PM_BAT:
if(!nobadeffects) 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 (!nobadeffects && youracedata->mlet != S_MIMIC && !Unchanging) {
char buf[BUFSZ];
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, "pretending to be a pile of gold");
Sprintf(buf, Hallucination ?
"You suddenly dread being peeled and mimic %s again!" :
"You now prefer mimicking %s again.",
an(Upolyd ? youracedata->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:
if (HFast & INTRINSIC) {
if(!nobadeffects){
HFast &= ~INTRINSIC;
Your("velocity suddenly seems very uncertain!");
You("seem slower.");
}
} else {
HFast |= FROMOUTSIDE;
Your("velocity suddenly seems very uncertain!");
You("seem faster.");
}
break;
case PM_GUG:
if(!drained || !rn2(5)){
gainstr((struct obj *)0, 0);
}
break;
case PM_MANDRAKE:
case PM_LIZARD:
case PM_BABY_CAVE_LIZARD:
case PM_SMALL_CAVE_LIZARD:
case PM_CAVE_LIZARD:
case PM_LARGE_CAVE_LIZARD:
if (HStun > 2) make_stunned(2L,FALSE);
if (HConfusion > 2) make_confused(2L,FALSE);
break;
case PM_BEHOLDER:
if(!drained || !rn2(5)) {
You(Hallucination ? "can see your own insides!"
: "feel as though you can see the world from a whole new angle!");
if (!BClairvoyant)
do_vicinity_map(u.ux,u.uy);
/* at present, only one thing blocks clairvoyance */
else if (uarmh && uarmh->otyp == CORNUTHAUM)
You("sense a pointy hat on top of your %s.",
body_part(HEAD));
incr_itimeout(&HClairvoyant, rn1(500,500));
}
break;
case PM_CHAMELEON:
case PM_DOPPELGANGER:
/* case PM_SANDESTIN: */
if (!Unchanging && !nobadeffects) {
You_feel("a change coming over you.");
polyself(FALSE);
}
break;
case PM_FUNGAL_BRAIN:
case PM_MIND_FLAYER:
case PM_MASTER_MIND_FLAYER:
if(!drained || !rn2(5)) {
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.");
}
}
}
register struct permonst *ptr = &mons[pm];
int i, count;
if (!nobadeffects && hallucinogenic(ptr)) {
pline ("Oh wow! Great stuff!");
make_hallucinated(HHallucination + 200,FALSE,0L);
}
if(!drained || !rn2(5)) 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++;
if(u.sealsActive&SEAL_AHAZU) givit(i, ptr, (tin && ptr->cnutrit > 50) ? 45 : ptr->cnutrit*0.9, drained);
else givit(i, ptr, (tin && ptr->cnutrit > 50) ? 50 : ptr->cnutrit, drained);
}
}
/* if any found try to give them one */
if (catch_lycanthropy && arti_worn_prop(uwep, ARTP_NOWERE)) {
if (!touch_artifact(uwep, &youmonst, FALSE)) {
dropx(uwep);
uwepgone();
}
}
return;
}
void
violated_vegetarian()
{
u.uconduct.unvegetarian++;
if (Role_if(PM_MONK)) {
You_feel("guilty.");
adjalign(-1);
u.ualign.sins++;
if(u.uconduct.unvegetarian%2) change_hod(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(verb)
const char* verb; /* if 0, the verb is "open" */
{
if(((!carried(tin.tin) &&
costly_spot(tin.tin->ox, tin.tin->oy) &&
!tin.tin->no_charge)
|| tin.tin->unpaid)) {
verbalize("You %s it, you bought it!", verb ? verb : "open");
if(tin.tin->quan > 1L) tin.tin = splitobj(tin.tin, 1L);
bill_dummy_object(tin.tin);
}
}
STATIC_PTR
int
opentin() /* called during each move whilst opening a tin */
{
register int r;
const char *what;
int which;
if(!carried(tin.tin) && !obj_here(tin.tin, u.ux, u.uy))
/* perhaps it was stolen? */
return MOVE_CANCELLED; /* %% probably we should use tinoid */
if(tin.usedtime++ >= 50) {
You("give up your attempt to open the tin.");
return MOVE_FINISHED_OCCUPATION;
}
if(tin.usedtime < tin.reqtime)
return MOVE_STANDARD; /* still busy */
if(tin.tin->otrapped ||
(tin.tin->cursed && tin.tin->spe != -1 && !rn2(8))) {
b_trapped("tin", 0);
costly_tin("destroyed");
goto use_me;
}
You("succeed in opening the tin.");
if(tin.tin->spe != 1) {
if (tin.tin->corpsenm == NON_PM) {
pline("It turns out to be empty.");
tin.tin->dknown = tin.tin->known = TRUE;
costly_tin((const char*)0);
goto use_me;
}
r = tin.tin->cursed ? ROTTEN_TIN : /* always rotten if cursed */
(tin.tin->spe == -1) ? HOMEMADE_TIN : /* player made it */
rn2(TTSZ-1); /* else take your pick */
if (r == ROTTEN_TIN && (tin.tin->corpsenm == PM_LIZARD || tin.tin->corpsenm == PM_SMALL_CAVE_LIZARD ||
tin.tin->corpsenm == PM_CAVE_LIZARD || tin.tin->corpsenm == PM_BABY_CAVE_LIZARD ||
tin.tin->corpsenm == PM_LARGE_CAVE_LIZARD ||
tin.tin->corpsenm == PM_LICHEN || tin.tin->corpsenm == PM_BEHOLDER))
r = DRIED_TIN; /* lizards don't rot */
else if (tin.tin->spe == -1 && !tin.tin->blessed && !rn2(7))
r = ROTTEN_TIN; /* some homemade tins go bad */
which = 0; /* 0=>plural, 1=>as-is, 2=>"the" prefix */
if (Hallucination) {
what = rndmonnam();
} else {
what = mons[tin.tin->corpsenm].mname;
if (mons[tin.tin->corpsenm].geno & G_UNIQ)
which = type_is_pname(&mons[tin.tin->corpsenm]) ? 1 : 2;
}
if (which == 0) what = makeplural(what);
pline("It smells like %s%s.", (which == 2) ? "the " : "", what);
if (uclockwork){
int eatHow;
if(Hallucination && u.clockworkUpgrades&(WOOD_STOVE|MAGIC_FURNACE|HELLFIRE_FURNACE|SCRAP_MAW)){
You("try to put it in your intake hopper, but you hallucinate and dump it instead!");
costly_tin((const char*)0);
goto use_me;
} else if((eatHow = clockwork_eat_menu(r == DRIED_TIN || is_burnable(&mons[tin.tin->corpsenm]),
u.clockworkUpgrades&MAGIC_FURNACE &&
(tin.tin->corpsenm == PM_NEWT ||
tin.tin->corpsenm == PM_AOA_DROPLET ||
tin.tin->corpsenm == PM_AOA))
)){
if(eatHow == WOOD_STOVE){
/* KMH, conduct */
u.uconduct.food++;
if (!vegan(&mons[tin.tin->corpsenm]))
u.uconduct.unvegan++;
if (!vegetarian(&mons[tin.tin->corpsenm]))
violated_vegetarian();
tin.tin->dknown = tin.tin->known = TRUE;
u.ustove += tintxts[r].nut;
costly_tin((const char*)0);
goto use_me;
} else if(eatHow == MAGIC_FURNACE){
/* KMH, conduct */
u.uconduct.food++;
if (!vegan(&mons[tin.tin->corpsenm]))
u.uconduct.unvegan++;
if (!vegetarian(&mons[tin.tin->corpsenm]))
violated_vegetarian();
tin.tin->dknown = tin.tin->known = TRUE;
cpostfx(tin.tin->corpsenm, FALSE, FALSE, FALSE);
costly_tin((const char*)0);
goto use_me;
} else if(eatHow == HELLFIRE_FURNACE){
/* KMH, conduct */
u.uconduct.food++;
if (!vegan(&mons[tin.tin->corpsenm]))
u.uconduct.unvegan++;
if (!vegetarian(&mons[tin.tin->corpsenm]))
violated_vegetarian();
tin.tin->dknown = tin.tin->known = TRUE;
u.ustove += (tintxts[r].nut)/10;
costly_tin((const char*)0);
goto use_me;
} else if(eatHow == SCRAP_MAW){
You("crunch up the tin can.");
if(!Upolyd){
u.uhp += 5;
if (u.uhp > u.uhpmax) u.uhp = u.uhpmax;
} else {
u.mh += 5;
if (u.mh > u.mhmax) u.mh = u.mhmax;
}
tin.tin->dknown = tin.tin->known = TRUE;
costly_tin((const char*)0);
goto use_me;
}
} else {
You("discard the tin.");
tin.tin->dknown = tin.tin->known = TRUE;
costly_tin((const char*)0);
goto use_me;
}
} else if((Race_if(PM_INCANTIFIER) || magivorous(youracedata)) &&
((tin.tin->corpsenm != PM_NEWT &&
tin.tin->corpsenm != PM_AOA_DROPLET &&
tin.tin->corpsenm != PM_AOA) ||
Hallucination)
){
You("would not derive any nutrition from eating that, so you discard it instead.");
costly_tin((const char*)0);
goto use_me;
}
char buf[BUFSZ];
if(tin.tin->corpsenm > NON_PM && tin.tin->spe != 1 && your_race(&mons[tin.tin->corpsenm])
&& !is_animal(&mons[tin.tin->corpsenm]) && !mindless(&mons[tin.tin->corpsenm])
&& !CANNIBAL_ALLOWED()
/* Always give warning if the tin's contents is known. */
&& (tin.tin->known || !Hallucination
|| ((u.ualign.record >= 20 || ACURR(A_WIS) >= 20
|| u.ualign.record >= rnd(20-ACURR(A_WIS)))
&& !roll_madness(MAD_CANNIBALISM))))
Sprintf(buf, "You feel a deep sense of kinship to the tin! Eat it anyway?");
else
Sprintf(buf, "Eat it?");
if (yn_function(buf,ynchars,'n')=='n') {
if (!Hallucination) tin.tin->dknown = tin.tin->known = TRUE;
if (flags.verbose) You("discard the open tin.");
costly_tin((const char*)0);
goto use_me;
}
if(Race_if(PM_INCANTIFIER)){
u.uconduct.food++;
You("drain energy from %s %s.", tintxts[r].txt,
mons[tin.tin->corpsenm].mname);
cprefx(tin.tin->corpsenm, TRUE, FALSE);
cpostfx(tin.tin->corpsenm, FALSE, FALSE, FALSE);
costly_tin((const char*)0);
goto use_me;
}
/* in case stop_occupation() was called on previous meal */
victual.piece = (struct obj *)0;
victual.fullwarn = victual.eating = victual.doreset = FALSE;
if(tin.tin->corpsenm == PM_JUBJUB_BIRD && tin.tin->blessed){
You("consume symmetrical %s.", mons[tin.tin->corpsenm].mname);
} else {
You("consume %s %s.", tintxts[r].txt,
mons[tin.tin->corpsenm].mname);
}
/* KMH, conduct */
u.uconduct.food++;
if (!vegan(&mons[tin.tin->corpsenm]))
u.uconduct.unvegan++;
if (!vegetarian(&mons[tin.tin->corpsenm])){
violated_vegetarian();
} else {
if(roll_madness(MAD_CANNIBALISM)){
make_vomiting(6+rn1(15,10), TRUE);
}
}
tin.tin->dknown = tin.tin->known = TRUE;
cprefx(tin.tin->corpsenm, FALSE, FALSE);
cpostfx(tin.tin->corpsenm, TRUE, FALSE, FALSE);
/* charge for one at pre-eating cost */
costly_tin((const char*)0);
/* check for vomiting added by GAN 01/16/87 */
if(tintxts[r].nut < 0) make_vomiting((long)rn1(15,10), FALSE);
else if(tin.tin->corpsenm == PM_JUBJUB_BIRD && tin.tin->blessed){
lesshungry(600);
use_unicorn_horn(tin.tin);
} else lesshungry(tintxts[r].nut);
if(r == 0 || r == FRENCH_FRIED_TIN) {
/* Assume !Glib, because you can't open tins when Glib. */
incr_itimeout(&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%s substance.",
Blind ? "" : " ", Blind ? "" : hcolor(NH_GREEN));
else
pline("It contains spinach.");
if (uclockwork){
if(u.clockworkUpgrades&HELLFIRE_FURNACE &&
yn("Burn it in your hellfire furnace?") == 'y'
){
/* KMH, conduct */
u.uconduct.food++;
tin.tin->dknown = tin.tin->known = TRUE;
u.ustove += 60;
costly_tin((const char*)0);
goto use_me;
} else if(u.clockworkUpgrades&SCRAP_MAW &&
yn("Feed it to your scrap maw?") == 'y'
){
You("crunch up the tin can.");
if(!Upolyd){
u.uhp += 5;
if (u.uhp > u.uhpmax) u.uhp = u.uhpmax;
} else {
u.mh += 5;
if (u.mh > u.mhmax) u.mh = u.mhmax;
}
tin.tin->dknown = tin.tin->known = TRUE;
costly_tin((const char*)0);
goto use_me;
} else if(u.clockworkUpgrades&WOOD_STOVE){
/* KMH, conduct */
pline("Sadly, the spinach is much too damp to burn.");
You("reluctantly discard the spinach.");
tin.tin->dknown = tin.tin->known = TRUE;
costly_tin((const char*)0);
goto use_me;
} else {
You("have no way to eat%s.",
u.clockworkUpgrades&(WOOD_STOVE|OIL_STOVE|MAGIC_FURNACE|HELLFIRE_FURNACE|SCRAP_MAW) ? " that" : "");
You("reluctantly discard the spinach.");
tin.tin->dknown = tin.tin->known = TRUE;
costly_tin((const char*)0);
goto use_me;
}
} else if(Race_if(PM_INCANTIFIER) || magivorous(youracedata) ){
pline("Sadly, you would not derive any nutrition from eating it.");
You("reluctantly discard the spinach.");
costly_tin((const char*)0);
goto use_me;
}
if (yn("Eat it?") == 'n') {
if (!Hallucination && !tin.tin->cursed)
tin.tin->dknown = tin.tin->known = TRUE;
if (flags.verbose)
You("discard the open tin.");
costly_tin((const char*)0);
goto use_me;
}
tin.tin->dknown = tin.tin->known = TRUE;
costly_tin((const char*)0);
if (!tin.tin->cursed)
pline("This makes you feel like %s!",
Hallucination ? "Swee'pea" : "Popeye");
lesshungry(600);
gainstr(tin.tin, 0);
u.uconduct.food++;
}
use_me:
if (carried(tin.tin)) useup(tin.tin);
else useupf(tin.tin, 1L);
tin.tin = (struct obj *) 0;
return MOVE_FINISHED_OCCUPATION;
}
STATIC_OVL void
start_tin(otmp) /* called when starting to open a tin */
register struct obj *otmp;
{
register int tmp;
if (metallivorous(youracedata)) {
You("bite right into the metal tin...");
tmp = 1;
} else if (Upolyd && u.umonnum == PM_TOVE) {
You("gyre, gimbling right into the metal tin.");
tmp = 1;
} else if (nolimbs(youracedata)) {
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 GRAPPLING_HOOK:
if(uwep->oartifact != ART_HELPING_HAND) goto no_opener;
//else fall through
case TIN_OPENER:
tmp = 1;
break;
case 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 your %s you try to open the tin.",
aobjnam(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);
}
tin.reqtime = tmp;
tin.usedtime = 0;
tin.tin = otmp;
set_occupation(opentin, "opening the tin", 0);
return;
}
int
Hear_again(VOID_ARGS) /* called when waking up after fainting */
{
flags.soundok = 1;
return 0;
}
/* called on the "first bite" of rotten food */
STATIC_OVL int
rottenfood(obj)
struct obj *obj;
{
pline("Blecch! Rotten %s!",
is_vampire(youracedata) ? "blood" : 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("%s", vision_clears);
} else if(!rn2(3)) {
const char *what, *where;
if (!Blind)
what = "goes", where = "dark";
else if (Levitation || Weightless ||
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);
flags.soundok = 0;
nomul(-rnd(10), "unconscious from rotten food");
nomovemsg = "You are conscious again.";
afternmv = Hear_again;
return(1);
}
return(0);
}
static char *eatrat[] = {
"This tastes delicious!",
"This tastes quite good!",
"This tastes quite good (could use some ketchup, though).",
"This tastes ... good (but really needs some ketchup).",
"...You'd pay double for ketchup.",
"...What you wouldn't give for some ketchup....",
};
STATIC_OVL int
eatcorpse(otmp) /* called when a corpse is selected as food */
register struct obj *otmp;
{
int tp = 0, mtyp = otmp->corpsenm;
long rotted = 0L;
boolean uniq = !!(mons[mtyp].geno & G_UNIQ);
int retcode = 0;
boolean stoneable = (touch_petrifies(&mons[mtyp]) && !Stone_resistance &&
!poly_when_stoned(youracedata));
/* KMH, conduct */
if (!vegan(&mons[mtyp])) u.uconduct.unvegan++;
if (!vegetarian(&mons[mtyp])){
violated_vegetarian();
} else {
if(roll_madness(MAD_CANNIBALISM)){
make_vomiting((3 + (mons[mtyp].cwt >> 6))+rn1(15,10), TRUE);
}
}
if (mtyp != PM_LIZARD && mtyp != PM_SMALL_CAVE_LIZARD && mtyp != PM_CAVE_LIZARD
&& mtyp != PM_LARGE_CAVE_LIZARD && mtyp != PM_LICHEN && mtyp != PM_BEHOLDER
) {
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;
}
/* Vampires only drink the blood of very young, meaty corpses
* is_edible only allows meaty corpses here
* Blood is assumed to be 1/5 of the nutrition
* Thus happens before the conduct checks intentionally - should it be after?
* Blood is assumed to be meat and flesh.
*/
if (is_vampire(youracedata)) {
/* oeaten is set up by touchfood */
if (otmp->odrained ? otmp->oeaten <= drainlevel(otmp) :
otmp->oeaten < mons[otmp->corpsenm].cnutrit) {
pline("There is no blood left in this corpse!");
return 3;
} else if (rotted <= 0 &&
(peek_at_iced_corpse_age(otmp) + 5) >= monstermoves) {
char buf[BUFSZ];
/* Generate the name for the corpse */
if (!uniq || Hallucination)
Sprintf(buf, "%s", the(corpse_xname(otmp,TRUE)));
else
Sprintf(buf, "%s%s corpse",
!type_is_pname(&mons[mtyp]) ? "the " : "",
s_suffix(mons[mtyp].mname));
pline("You drain the blood from %s.", buf);
otmp->odrained = 1;
} else {
pline("The blood in this corpse has coagulated!");
return 3;
}
}
else
otmp->odrained = 0;
/* Very rotten corpse will make you sick unless you are a ghoul or a ghast or bound to Chupoclops */
if (mtyp != PM_ACID_BLOB && !stoneable && rotted > 5L) {
boolean cannibal = maybe_cannibal(mtyp, FALSE);
if (u.umonnum == PM_GHOUL) {
pline("Yum - that %s was well aged%s!",
(mons[mtyp].mlet == S_PLANT || mtyp == PM_WOOD_TROLL) ? "vegetation" :
mons[mtyp].mlet == S_FUNGUS ? "fungoid vegetation" :
!vegetarian(&mons[mtyp]) ? "meat" : "protoplasm",
cannibal ? ", cannibal" : "");
} else if(u.sealsActive&SEAL_CHUPOCLOPS){
boolean cannibal = maybe_cannibal(mtyp, FALSE);
pline("The corpse liquefies into a putrid broth, and you slurp it down%s!",
cannibal ? ", cannibal" : "");
} else {
pline("Ulch - that %s was tainted%s!",
(mons[mtyp].mlet == S_PLANT || mtyp == PM_WOOD_TROLL) ? "vegetation" :
mons[mtyp].mlet == S_FUNGUS ? "fungoid vegetation" :
!vegetarian(&mons[mtyp]) ? "meat" : "protoplasm",
cannibal ? ", cannibal" : "");
if (Sick_resistance) {
pline("It doesn't seem at all sickening, though...");
} else {
char buf[BUFSZ];
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;
if (!uniq)
Sprintf(buf, "rotted %s", corpse_xname(otmp,TRUE));
else
Sprintf(buf, "%s%s rotted corpse",
!type_is_pname(&mons[mtyp]) ? "the " : "",
s_suffix(mons[mtyp].mname));
make_sick(sick_time, buf, TRUE, SICK_VOMITABLE);
}
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
return(2);
}
} else if (youracedata->mtyp == PM_GHOUL) {
pline ("This corpse is too fresh!");
return 3;
} else if (acidic(&mons[mtyp]) && !Acid_resistance) {
tp++;
You("have a very bad case of stomach acid."); /* not body_part() */
losehp(rnd(15), "acidic corpse", KILLED_BY_AN);
} if (freezing(&mons[mtyp]) && !Cold_resistance) {
tp++;
You("feel your stomach freeze!"); /* not body_part() */
roll_frigophobia();
losehp(d(2, 12), "cryonic corpse", KILLED_BY_AN);
}
if (burning(&mons[mtyp]) && !Fire_resistance) {
tp++;
You("feel your stomach boil!"); /* not body_part() */
losehp(rnd(20), "boiling hot corpse", KILLED_BY_AN);
}
if (poisonous(&mons[mtyp]) && 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 */
} if ( !(poisonous(&mons[mtyp]) || burning(&mons[mtyp]) ||
freezing(&mons[mtyp]) || acidic(&mons[mtyp])) &&
(rotted > 5L || (rotted > 3L && rn2(5)))
&& !(Sick_resistance || u.sealsActive&SEAL_CHUPOCLOPS)) {
tp++;
You_feel("%ssick.", (Sick) ? "very " : "");
losehp(rnd(8), "cadaver", KILLED_BY_AN);
}
/* delay is weight dependent */
victual.reqtime = (u.sealsActive&SEAL_AHAZU) ? 1 : (3 + (mons[mtyp].cwt >> 6));
if (otmp->odrained) victual.reqtime = (u.sealsActive&SEAL_AHAZU) ? 1 : rounddiv(victual.reqtime, 5);
if (!tp && mtyp != PM_LIZARD && mtyp != PM_SMALL_CAVE_LIZARD && mtyp != PM_CAVE_LIZARD
&& mtyp != PM_LARGE_CAVE_LIZARD && mtyp != PM_LICHEN && mtyp != PM_BEHOLDER &&
(otmp->orotten || !rn2(7))) {
if ( (monstermoves - peek_at_iced_corpse_age(otmp)) > 5 && 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;
} else {
if (!retcode) consume_oeaten(otmp, 2); /* oeaten >>= 2 */
if (otmp->odrained && otmp->oeaten < drainlevel(otmp))
otmp->oeaten = drainlevel(otmp);
}
} else if (!is_vampire(youracedata)) {
if(is_rat(&mons[mtyp]) && Race_if(PM_DWARF)){
if(u.uconduct.ratseaten<SIZE(eatrat)) pline("%s", eatrat[u.uconduct.ratseaten++]);
else{
u.uconduct.ratseaten++;
pline("%s", eatrat[SIZE(eatrat)-1]);
}
} else if(mtyp == PM_LONG_WORM){
pline("This tastes spicy!");
} else if(mtyp == PM_CROW){
You("eat crow!");
} else {
pline("%s%s %s!",
!uniq ? "This " : !type_is_pname(&mons[mtyp]) ? "The " : "",
food_xname(otmp, FALSE),
(vegan(&mons[mtyp]) ?
(!(carnivorous(youracedata) || (uarmg && uarmg->oartifact == ART_GREAT_CLAWS_OF_URDLEN)) && herbivorous(youracedata)) :
((carnivorous(youracedata) || (uarmg && uarmg->oartifact == ART_GREAT_CLAWS_OF_URDLEN)) && !herbivorous(youracedata)))
? "is delicious" : "tastes terrible");
}
}
return(retcode);
}
STATIC_OVL void
start_eating(otmp) /* called as you start to eat */
register struct obj *otmp;
{
/*debugpline("start_eating: %lx (victual = %lx)", otmp, victual.piece);
debugpline("reqtime = %d", victual.reqtime);
debugpline("(original reqtime = %d)", objects[otmp->otyp].oc_delay);
debugpline("nmod = %d", victual.nmod);
debugpline("oeaten = %d", otmp->oeaten);*/
victual.fullwarn = victual.doreset = FALSE;
victual.eating = TRUE;
if (otmp->otyp == CORPSE) {
cprefx(victual.piece->corpsenm,FALSE,FALSE);
if (!victual.piece || !victual.eating) {
/* rider revived, or died and lifesaved */
return;
}
}
if (bite()) return;
if (++victual.usedtime >= victual.reqtime) {
/* print "finish eating" message if they just resumed -dlc */
done_eating(victual.reqtime > 1 ? TRUE : FALSE);
return;
}
Sprintf(msgbuf, "%s %s", otmp->odrained ? "draining" : "eating",
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(YouHunger <= 200*get_uhungersizemod())
pline(Hallucination ? "Oh wow, like, superior, man!" :
"That food really hit the spot!");
else if(YouHunger <= get_satiationlimit() - 300*get_uhungersizemod()) pline("That satiated your %s!",
body_part(STOMACH));
break;
case TRIPE_RATION:
if ((carnivorous(youracedata) && !humanoid(youracedata)) ||
(u.ulycn != NON_PM && carnivorous(&mons[u.ulycn]) &&
!humanoid(&mons[u.ulycn])))
/* Symptom of lycanthropy is starting to like your
* alternative form's food!
*/
pline("That tripe ration was surprisingly good!");
else if (maybe_polyd(is_orc(youmonst.data), Race_if(PM_ORC)) || Race_if(PM_HALF_DRAGON))
pline(Hallucination ? "Tastes great! Less filling!" :
"Mmm, tripe... not bad!");
else {
pline("Yak - dog food!");
#ifdef CONVICT
if (Role_if(PM_CONVICT))
pline("At least it's not prison food.");
#endif /* CONVICT */
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())
#ifdef CONVICT
if (!Role_if(PM_CONVICT))
#endif /* CONVICT */
make_vomiting((long)rn1(victual.reqtime, 14), FALSE);
}
break;
case MEATBALL:
case MEAT_STICK:
case MASSIVE_CHUNK_OF_MEAT:
case MEAT_RING:
goto give_feedback;
/* break; */
case CLOVE_OF_GARLIC:
if (is_undead(youracedata)) {
make_vomiting((long)rn1(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 */
#ifdef CONVICT
if (Role_if(PM_CONVICT) && (rn2(8) > u.ulevel)) {
You_feel("a slight stomach ache."); /* prisoners are used to bad food */
} else
#endif /* CONVICT */
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;
}
}
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[0]].intrinsic;
if (otmp->owornmask & W_RING) {
Ring_gone(otmp);
if (u.uhp <= 0) return; /* died from sink fall */
}
otmp->known = otmp->dknown = 1; /* by taste */
if (otmp->otyp == AMULET_OF_STRANGULATION || otmp->otyp == AMULET_OF_DRAIN_RESISTANCE || !rn2(otmp->oclass == RING_CLASS ? 3 : 5)) {
switch (otmp->otyp) {
default:
if (!objects[typ].oc_oprop[0]) break; /* should never happen */
if (!(u.uprops[objects[typ].oc_oprop[0]].intrinsic & FROMOUTSIDE))
accessory_has_effect(otmp);
u.uprops[objects[typ].oc_oprop[0]].intrinsic |= TIMEOUT_INF;
switch (typ) {
case RIN_SEE_INVISIBLE:
set_mimic_blocking();
see_monsters();
if (Invis && !oldprop && !ESee_invisible &&
!mon_resistance(&youmonst,SEE_INVIS) && !Blind) {
newsym(u.ux,u.uy);
pline("Suddenly you can see yourself.");
makeknown(typ);
}
break;
case RIN_INVISIBILITY:
if (!oldprop && !EInvis && !BInvis && !Blind) {
if(See_invisible(u.ux,u.uy)){
Your("body takes on a %s transparency...",
Hallucination ? "normal" : "strange");
makeknown(typ);
} else {
newsym(u.ux,u.uy);
Your("body fades out of view.");
makeknown(typ);
}
}
break;
case RIN_PROTECTION_FROM_SHAPE_CHAN:
rescham();
break;
case RIN_LEVITATION:
/* undo the `.intrinsic |= TIMEOUT_INF' 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 += otmp->spe;
break;
case RIN_INCREASE_DAMAGE:
accessory_has_effect(otmp);
u.udaminc += otmp->spe;
break;
case RIN_PROTECTION:
accessory_has_effect(otmp);
HProtection |= TIMEOUT_INF;
u.ublessed += otmp->spe;
flags.botl = 1;
break;
case RIN_FREE_ACTION:
/* Give sleep resistance instead */
if (!(HSleep_resistance & (FROMOUTSIDE | TIMEOUT_INF)))
accessory_has_effect(otmp);
if (!Sleep_resistance)
You_feel("wide awake.");
HSleep_resistance |= TIMEOUT_INF;
break;
case AMULET_OF_CHANGE:
accessory_has_effect(otmp);
makeknown(typ);
change_sex();
You("are suddenly very %s!",
flags.female ? "feminine" : "masculine");
flags.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! */
if(!Race_if(PM_INCANTIFIER)){
Your("throat swells shut!");
HStrangled = 6; //If you can survive without air for six turns, you live.
}
break;
case AMULET_OF_RESTFUL_SLEEP: /* another bad idea! */
if(!Race_if(PM_INCANTIFIER)){
if (!(HSleeping & FROMOUTSIDE))
accessory_has_effect(otmp);
HSleeping = FROMOUTSIDE | rnd(100);
}
break;
case AMULET_OF_NULLIFY_MAGIC:
debugpline("Trying to give null magic");
if (!(HNullmagic))
pline("A shimmering film forms around you!");
give_intrinsic(NULLMAGIC, 1000L);
break;
case AMULET_OF_DRAIN_RESISTANCE:
debugpline("Trying to give drain resistance");
if (!(HDrain_resistance))
You(Hallucination ? "are bouncing off the walls!" : "feel especially energetic.");
give_intrinsic(DRAIN_RES, 1000L);
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 = victual.piece;
/* lesshungry wants an occupation to handle choke messages correctly */
set_occupation(eatfood, "eating non-food", 0);
lesshungry(victual.nmod);
occupation = 0;
victual.piece = (struct obj *)0;
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);
return;
}
if (otmp->oclass == POTION_CLASS) {
otmp->quan++; /* dopotion() does a useup() */
(void)dopotion(otmp, TRUE);
}
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", "metal", "silver", "gold", "platinum", "lead", "mithril",
"plastic", "glass", "rich food", "stone", "obsidian", "shadow"
};
STATIC_OVL const char *
foodword(otmp)
register struct obj *otmp;
{
if (otmp->oclass == FOOD_CLASS) return "food";
if (otmp->oclass == GEM_CLASS &&
otmp->obj_material == GLASS &&
otmp->dknown)
makeknown(otmp->otyp);
return foodwords[otmp->obj_material];
}
STATIC_OVL void
fpostfx(otmp) /* called after consuming (non-corpse) food */
register struct obj *otmp;
{
if(u.sealsActive&SEAL_ENKI && u.uhp < u.uhpmax && otmp->otyp >= CREAM_PIE && otmp->otyp <= TIN){
pline("The fruits of civilization give you strength!");
if(!Upolyd){
u.uhp += obj_nutrition(otmp);
if (u.uhp > u.uhpmax) u.uhp = u.uhpmax;
} else {
u.mh += obj_nutrition(otmp);
if (u.mh > u.mhmax) u.mh = u.mhmax;
}
}
switch(otmp->otyp) {
case SPRIG_OF_WOLFSBANE:
if (u.ulycn >= LOW_PM || is_were(youracedata))
you_unwere(TRUE);
break;
case CARROT:
make_blinded((long)u.ucreamed,TRUE);
break;
case EYEBALL:
/* body parts -- now checks for artifact and name*/
if (!otmp->oartifact) break;
You("feel a burning inside!");
u.uhp -= rn1(50,150);
if (u.uhp <= 0) {
killer_format = KILLED_BY;
killer = food_xname(otmp, TRUE);
done(CHOKING);
}
break;
case SEVERED_HAND:
if (!otmp->oartifact) break;
You("feel the hand scrabbling around inside of you!");
u.uhp -= rn1(50,150);
if (u.uhp <= 0) {
killer_format = KILLED_BY;
killer = food_xname(otmp, TRUE);
done(CHOKING);
}
break;
case FORTUNE_COOKIE:
if (yn("Read the fortune?") == 'y') {
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();
change_gevurah(1); //cheated death.
}
} 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;
killer = "rotten lump of royal jelly";
done(POISONING);
}
}
if(!otmp->cursed) heal_legs();
break;
case LUMP_OF_SOLDIER_S_JELLY:
/* This stuff seems to be VERY healthy! */
adjattrib(A_CON, 1, 0);
if (Upolyd) {
u.mh += otmp->cursed ? -d(4,10) : d(4,10);
if (u.mh > u.mhmax) {
u.mh = u.mhmax;
} else if (u.mh <= 0) {
rehumanize();
change_gevurah(1); //cheated death.
}
} else {
u.uhp += otmp->cursed ? -d(4,10) : d(4,10);
if (u.uhp > u.uhpmax) {
u.uhp = u.uhpmax;
} else if (u.uhp <= 0) {
killer_format = KILLED_BY_AN;
killer = "rotten lump of soldier's jelly";
done(POISONING);
}
}
if(!otmp->cursed) heal_legs();
break;
case LUMP_OF_DANCER_S_JELLY:
/* This stuff seems to be VERY healthy! */
adjattrib(A_DEX, 1, 0);
if(!otmp->cursed){
if (!(HFast & (INTRINSIC))) {
if (!Fast) You("speed up.");
else Your("quickness feels more natural.");
HFast |= TIMEOUT_INF;
}
} else {
if ((HFast & TIMEOUT_INF)) {
HFast &= ~TIMEOUT_INF;
if (!Fast) You("slow down.");
else Your("quickness feels less natural.");
}
}
if(!otmp->cursed) heal_legs();
break;
case LUMP_OF_PHILOSOPHER_S_JELLY:
/* This stuff seems to be VERY healthy! */
adjattrib(A_INT, 1, 0);
if(!otmp->cursed){
if (HStun > 2) make_stunned(2L,FALSE);
if (HConfusion > 2) make_confused(2L,FALSE);
} else {
make_stunned(HStun + rnd(20),FALSE);
make_confused(HConfusion + d(10, 20),FALSE);
}
break;
case LUMP_OF_PRIESTESS_S_JELLY:
/* This stuff seems to be VERY healthy! */
adjattrib(A_WIS, 1, 0);
if(!otmp->cursed){
if (HStun > 2) make_stunned(2L,FALSE);
if (HConfusion > 2) make_confused(2L,FALSE);
} else {
make_stunned(HStun + rnd(20),FALSE);
make_confused(HConfusion + d(10, 20),FALSE);
}
break;
case LUMP_OF_RHETOR_S_JELLY:
/* This stuff seems to be VERY healthy! */
adjattrib(A_CHA, 1, 0);
if(!otmp->cursed){
if (HStun > 2) make_stunned(2L,FALSE);
if (HConfusion > 2) make_confused(2L,FALSE);
} else {
make_stunned(HStun + rnd(20),FALSE);
make_confused(HConfusion + d(10, 20),FALSE);
}
break;
case GILLYWEED:{
long timeLength = 200 + rnd(50);
if( !(HSwimming) || !(HMagical_breathing)) {
You_feel("gills develop on your neck.");
}
give_intrinsic(MAGICAL_BREATHING, timeLength);
give_intrinsic(SWIMMING, timeLength);
break;
}
case BRAINROOT:
if(mvitals[PM_BRAINBLOSSOM_PATCH].insight_gained <= 0){
pline("Alien impulses assault your mind!");
mvitals[PM_BRAINBLOSSOM_PATCH].insight_gained++;
change_uinsight(1);
make_hallucinated(HHallucination + 200,FALSE,0L);
}
else pline("Alien impulses intrude upon your mind.");
make_confused(HConfusion + d(10, 20),FALSE);
givit(TELEPAT, &mons[PM_BRAINBLOSSOM_PATCH], 600, FALSE);
if(!rn2(10)){
adjattrib(!rn2(3) ? A_INT : rn2(2) ? A_WIS : A_CHA, 1, 0);
}
break;
case EGG:
if (otmp->corpsenm != NON_PM && touch_petrifies(&mons[otmp->corpsenm])) {
if (!Stone_resistance &&
!(poly_when_stoned(youracedata) && polymon(PM_STONE_GOLEM))) {
if (!Stoned) Stoned = 5;
killer_format = KILLED_BY_AN;
Sprintf(killer_buf, "%s egg", mons[otmp->corpsenm].mname);
delayed_killer = killer_buf;
}
}
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;
}
/*
* 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.
*
* Now triggers on all eating, but gives only vague information and is
* overly cautious without edibility turned on. The general rule is that
* edibility can magically check things that would require knowledge of
* user-invisible information, without it it has to go on public info.
* (An exception's made for age, where it's overly cautious rather than
* leaving it out altogether to help avoid instadeath accidents.)
*/
STATIC_OVL int
edibility_prompts(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 = otmp->obj_material,
mtyp = otmp->corpsenm;
long rotted = 0L;
boolean edibility = u.uedibility || u.sealsActive&SEAL_BUER || goodsmeller(youracedata);
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");
/* edibility's needed to ID the contents of eggs (and unknown tins if hallucinating) */
if (cadaver || (otmp->otyp == EGG && edibility) ||
(otmp->otyp == TIN && (edibility || !Hallucination || otmp->known))) {
/* These checks must match those in eatcorpse() */
stoneorslime = (mtyp != NON_PM && (
touch_petrifies(&mons[mtyp]) &&
!Stone_resistance &&
!poly_when_stoned(youracedata)));
if (mtyp == PM_GREEN_SLIME || mtyp == PM_FLUX_SLIME)
stoneorslime = !Slime_res(&youmonst);
if (cadaver && mtyp != PM_LIZARD && mtyp != PM_SMALL_CAVE_LIZARD && mtyp != PM_CAVE_LIZARD
&& mtyp != PM_LARGE_CAVE_LIZARD && mtyp != PM_LICHEN && mtyp != PM_BEHOLDER ) {
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.
*/
/* without edibility, you don't know BCU if not bknown, so deduct 2 in case it's cursed */
if (cadaver && mtyp != PM_ACID_BLOB && rotted > ((edibility || otmp->bknown) ? 5L : 3L) &&
!(Sick_resistance || u.sealsActive&SEAL_CHUPOCLOPS)) {
/* Tainted meat */
if (edibility)
Sprintf(buf, "%s like %s could be tainted! %s",
foodsmell, it_or_they, eat_it_anyway);
else
Sprintf(buf, "%s too old for you to be certain %s %s "
"safe. %s", foodsmell, it_or_they,
(otmp->quan == 1L ? "is" : "are"), 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 (edibility && (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 && polyfodder(otmp) && !Unchanging) {
/* polymorphous */
Sprintf(buf, "%s strangely variable! %s",
foodsmell, eat_it_anyway);
if (yn_function(buf,ynchars,'n')=='n') return 1;
else return 2;
}
if (cadaver && poisonous(&mons[mtyp]) && !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[mtyp]) &&
!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[mtyp]) && !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 && !(Race_if(PM_INCANTIFIER) || magivorous(youracedata)) &&
((material == LEATHER || material == BONE || material == CHITIN ||
material == EYEBALL || material == SEVERED_HAND ||
material == DRAGON_HIDE || material == WAX) ||
(cadaver && !vegan(&mons[mtyp])))) {
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 && !(Race_if(PM_INCANTIFIER) || magivorous(youracedata)) &&
((material == LEATHER || material == BONE || material == CHITIN ||
material == EYEBALL || material == SEVERED_HAND ||
material == DRAGON_HIDE) ||
(cadaver && !vegetarian(&mons[mtyp])))) {
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 && mtyp != PM_ACID_BLOB && rotted > 5L && edibility &&
(Sick_resistance || u.sealsActive&SEAL_CHUPOCLOPS)) {
/* 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 */
int nutrit; /* nutrition available */
etype = 0;
char qbuf[QBUFSZ];
char c;
struct obj *obj2;
boolean dont_start = FALSE;
if(Race_if(PM_ETHEREALOID)){
pline("As an etherealoid, you are incapable of eating.");
return 0;
}
if(uandroid && !Race_if(PM_INCANTIFIER)){
pline("Though you may look human, you run on magical energy, not food.");
pline("Use #monster to rest and recover.");
return MOVE_CANCELLED;
}
if(uclockwork && !Race_if(PM_INCANTIFIER)){
long uUpgrades = (u.clockworkUpgrades&(WOOD_STOVE|MAGIC_FURNACE|HELLFIRE_FURNACE|SCRAP_MAW));
if(!uUpgrades){
pline("You are metal and springs, not flesh and blood. You cannot eat.");
return MOVE_CANCELLED;
} else if(uUpgrades == WOOD_STOVE ||
uUpgrades == MAGIC_FURNACE ||
uUpgrades == HELLFIRE_FURNACE ||
uUpgrades == SCRAP_MAW
) etype = uUpgrades;
else etype = clockwork_eat_menu(TRUE,TRUE);
}
if (Strangled) {
pline("If you can't breathe air, how can you consume solids?");
return MOVE_CANCELLED;
}
if (uarmh && FacelessHelm(uarmh) && ((uarmh->cursed && !Weldproof) || !freehand())){
pline("The %s covers your whole face.", xname(uarmh));
display_nhwindow(WIN_MESSAGE, TRUE); /* --More-- */
return MOVE_CANCELLED;
}
if (uarmc && FacelessCloak(uarmc) && ((uarmc->cursed && !Weldproof) || !freehand())){
pline("The %s covers your whole face.", xname(uarmc));
display_nhwindow(WIN_MESSAGE, TRUE); /* --More-- */
return MOVE_CANCELLED;
}
if(herbivorous(youracedata) && !carnivorous(youracedata) && !Race_if(PM_INCANTIFIER)
&& levl[u.ux][u.uy].typ == GRASS && can_reach_floor() &&
/* if we can't touch floor objects then use invent food only */
#ifdef STEED
!u.usteed /* can't eat off floor while riding */
#endif
){
if(yn("Eat some of the grass growing here?") == 'y'){
You("eat some grass.");
if(u.uhunger < (uclockwork ? ((get_uhungermax()*7)/8) : ((get_uhungermax()*3)/4)) || yn_function("You feel awfully full, continue eating?",ynchars,'n') == 'y'){
lesshungry(objects[FOOD_RATION].oc_nutrition/objects[FOOD_RATION].oc_delay);
}
return MOVE_ATE;
}
}
if (!(otmp = floorfood("eat", 0))) return MOVE_CANCELLED;
if (check_capacity((char *)0)) return MOVE_CANCELLED;
/* 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 MOVE_CANCELLED;
} 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 MOVE_CANCELLED;
}
if(otmp->otyp == CORPSE && your_race(&mons[otmp->corpsenm])
&& !is_animal(&mons[otmp->corpsenm]) && !mindless(&mons[otmp->corpsenm])
&& !CANNIBAL_ALLOWED())
{
char buf[BUFSZ];
Sprintf(buf, "You feel a deep sense of kinship to %s! Eat %s anyway?",
the(xname(otmp)), (otmp->quan == 1L) ? "it" : "one");
if (yn_function(buf,ynchars,'n')=='n') return 0;
}
{
int res = edibility_prompts(otmp);
if (res) {
if(u.uedibility && !(u.sealsActive&SEAL_BUER || goodsmeller(youracedata))){
Your("%s stops tingling and your sense of smell returns to normal.",
body_part(NOSE));
u.uedibility = 0;
}
if (res == 1) return MOVE_INSTANT;
}
}
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 MOVE_ATE;
}
if(Race_if(PM_INCANTIFIER) || magivorous(youracedata)){
int curspe;
if(objects[otmp->otyp].oc_unique) return 1;//redundant check against unique
if((Race_if(PM_INCANTIFIER) || magivorous(youracedata)) && u.uen >= u.uenmax * 3/4 && yn("You feel overcharged. Continue eating?") != 'y'){
return MOVE_INSTANT;
}
if (otmp->quan > 1L) {
if(!carried(otmp))
(void) splitobj(otmp, otmp->quan - 1L);
else
otmp = splitobj(otmp, 1L);
if (carried(otmp)) {
freeinv(otmp);
if (inv_cnt() >= 52) {
sellobj_state(SELL_DONTSELL);
dropy(otmp);
sellobj_state(SELL_NORMAL);
} else {
otmp->nomerge = TRUE;
otmp = addinv(otmp);
otmp->nomerge = FALSE;
}
}
}
if(!is_tipped_spear(otmp)){
/*These cases destroy the object, rescue its contents*/
while((obj2 = otmp->cobj)){
obj_extract_self(obj2);
/* Compartmentalize tip() */
if(carried(otmp)){
sellobj_state(SELL_DONTSELL);
dropy(obj2);
sellobj_state(SELL_NORMAL);
}
else {
place_object(obj2, u.ux, u.uy);
stackobj(obj2);
}
}
}
switch(otmp->oclass){
case WEAPON_CLASS:
u.uconduct.food++;
curspe = otmp->spe;
(void) drain_item(otmp);
if(curspe > otmp->spe){
You("drain the %s%s.", xname(otmp),otmp->spe!=0 ? "":" dry");
lesshungry(5*INC_BASE_NUTRITION);
flags.botl = 1;
} else {
pline("The %s resists your attempt to drain its magic.", xname(otmp));
}
break;
case RING_CLASS:
if(otmp->oartifact) break; //redundant check
u.uconduct.food++;
pline("The %s turns to dust as you drain it dry.", xname(otmp));
eataccessory(otmp);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
lesshungry(5*INC_BASE_NUTRITION);
flags.botl = 1;
break;
case AMULET_CLASS:
if(otmp->oartifact || objects[otmp->otyp].oc_unique) break; //redundant check
u.uconduct.food++;
pline("The %s crumbles to dust as you drain it dry.", xname(otmp));
eataccessory(otmp);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
lesshungry(5*INC_BASE_NUTRITION);
flags.botl = 1;
break;
case ARMOR_CLASS:
u.uconduct.food++;
curspe = otmp->spe;
(void) drain_item(otmp);
if(curspe > otmp->spe){
You("drain the %s%s.", xname(otmp),otmp->spe!=0 ? "":" dry");
lesshungry(5*INC_BASE_NUTRITION);
flags.botl = 1;
} else {
pline("The %s resists your attempt to drain its magic.", xname(otmp));
}
break;
case TOOL_CLASS:
u.uconduct.food++;
if (otmp->otyp == MAGIC_WHISTLE){
otmp = poly_obj(otmp, WHISTLE);
You("drain the %s of its magic.", xname(otmp));
} else {
curspe = otmp->spe;
(void) drain_item(otmp);
if(curspe > otmp->spe){
You("drain the %s%s.", xname(otmp),otmp->spe!=0 ? "":" dry");
} else {
pline("The %s resists your attempt to drain its magic.", xname(otmp));
}
}
lesshungry(5*INC_BASE_NUTRITION);
flags.botl = 1;
break;
case SCROLL_CLASS:
if(otmp->oartifact) break; //redundant check
u.uconduct.food++;
You("drain the ink from the %s.", xname(otmp));
costly_cancel(otmp);
otmp->otyp = SCR_BLANK_PAPER;
remove_oprop(otmp, OPROP_TACTB);
otmp->spe = 0;
otmp->oward = 0;
lesshungry(5*INC_BASE_NUTRITION);
flags.botl = 1;
break;
case SPBOOK_CLASS:
if(otmp->oartifact) break; //redundant check
u.uconduct.food++;
You("drain the %smagic from the %s.",
(otmp->spestudied == MAX_SPELL_STUDY) ? "last of the " : "",
xname(otmp));
otmp->spestudied++;
costly_cancel(otmp);
if(otmp->spestudied > MAX_SPELL_STUDY){
otmp->otyp = SPE_BLANK_PAPER;
otmp->obj_color = objects[SPE_BLANK_PAPER].oc_color;
remove_oprop(otmp, OPROP_TACTB);
}
lesshungry(5*INC_BASE_NUTRITION);
flags.botl = 1;
break;
case WAND_CLASS:
u.uconduct.food++;
curspe = otmp->spe;
(void) drain_item(otmp);
if(curspe > otmp->spe){
You("drain the %s%s.", xname(otmp),otmp->spe!=0 ? "":" dry");
lesshungry(1*INC_BASE_NUTRITION);
flags.botl = 1;
} else {
pline("The %s resists your attempt to drain its magic.", xname(otmp));
}
break;
case FOOD_CLASS:
if(otmp->otyp == CORPSE){
u.uconduct.food++;
You("drain the %s.", xname(otmp));
cprefx(otmp->corpsenm, TRUE, FALSE);
cpostfx(otmp->corpsenm, FALSE, FALSE, otmp->odrained);
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
}
else if(otmp->otyp == TIN) {
start_tin(otmp);
return(1);
}
break;
}
return MOVE_ATE;
}
if(uclockwork){
if(etype == MAGIC_FURNACE && !otmp->oartifact){
int curspe;
if(objects[otmp->otyp].oc_unique) return 1;//redundant check against unique
You("place the %s in your magic furnace.", xname(otmp));
/*These cases destroy the object, rescue its contents*/
while((obj2 = otmp->cobj)){
obj_extract_self(obj2);
/* Compartmentalize tip() */
if(carried(otmp)){
sellobj_state(SELL_DONTSELL);
dropy(obj2);
sellobj_state(SELL_NORMAL);
}
else {
place_object(obj2, u.ux, u.uy);
stackobj(obj2);
}
}
if (otmp->quan > 1L) {
if(!carried(otmp))
(void) splitobj(otmp, otmp->quan - 1L);
else
otmp = splitobj(otmp, 1L);
if (carried(otmp)) {
freeinv(otmp);
if (inv_cnt() >= 52) {
sellobj_state(SELL_DONTSELL);
dropy(otmp);
sellobj_state(SELL_NORMAL);
} else {
otmp->nomerge = TRUE;
otmp = addinv(otmp);
otmp->nomerge = FALSE;
}
}
}
/*These cases destroy the object, rescue its contents*/
while((obj2 = otmp->cobj)){
obj_extract_self(obj2);
/* Compartmentalize tip() */
if(carried(otmp)){
sellobj_state(SELL_DONTSELL);
dropy(obj2);
sellobj_state(SELL_NORMAL);
}
else {
place_object(obj2, u.ux, u.uy);
stackobj(obj2);
}
}
switch(otmp->oclass){
case WEAPON_CLASS:
u.uconduct.food++;
u.uen = min(u.uenmax, u.uen + otmp->spe*50);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case RING_CLASS:
u.uconduct.food++;
u.uen = min(u.uenmax, u.uen + 50);
eataccessory(otmp);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case AMULET_CLASS:
u.uconduct.food++;
u.uen = min(u.uenmax, u.uen + 50);
eataccessory(otmp);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case ARMOR_CLASS:
u.uconduct.food++;
u.uen = min(u.uenmax, u.uen + otmp->spe*50);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case TOOL_CLASS:
u.uconduct.food++;
u.uen = min(u.uenmax, u.uen + otmp->spe*50);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case SCROLL_CLASS:
u.uconduct.food++;
u.uen = min(u.uenmax, u.uen + 50);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case SPBOOK_CLASS:
u.uconduct.food++;
u.uen = min(u.uenmax, u.uen + 150);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case WAND_CLASS:
u.uconduct.food++;
u.uen = min(u.uenmax, u.uen + otmp->spe*10);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case FOOD_CLASS:
if(otmp->otyp == CORPSE){
u.uconduct.food++;
cpostfx(otmp->corpsenm, FALSE, FALSE, otmp->odrained);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
}
break;
}
return MOVE_ATE;
}
if(etype == WOOD_STOVE && !otmp->oartifact){
if(objects[otmp->otyp].oc_unique) return 1;//redundant check against unique
You("place the %s in your wood stove.", xname(otmp));
/*These cases destroy the object, rescue its contents*/
while((obj2 = otmp->cobj)){
obj_extract_self(obj2);
/* Compartmentalize tip() */
if(carried(otmp)){
sellobj_state(SELL_DONTSELL);
dropy(obj2);
sellobj_state(SELL_NORMAL);
}
else {
place_object(obj2, u.ux, u.uy);
stackobj(obj2);
}
}
if (otmp->quan > 1L) {
if(!carried(otmp))
(void) splitobj(otmp, otmp->quan - 1L);
else
otmp = splitobj(otmp, 1L);
if (carried(otmp)) {
freeinv(otmp);
if (inv_cnt() >= 52) {
sellobj_state(SELL_DONTSELL);
dropy(otmp);
sellobj_state(SELL_NORMAL);
} else {
otmp->nomerge = TRUE;
otmp = addinv(otmp);
otmp->nomerge = FALSE;
}
}
}
switch(otmp->oclass){
case WEAPON_CLASS:
u.uconduct.food++;
u.ustove += obj_nutrition(otmp);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case RING_CLASS:
u.uconduct.food++;
u.ustove += obj_nutrition(otmp);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case AMULET_CLASS:
u.uconduct.food++;
u.ustove += obj_nutrition(otmp);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case ARMOR_CLASS:
u.uconduct.food++;
u.ustove += obj_nutrition(otmp);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case TOOL_CLASS:
u.uconduct.food++;
u.ustove += obj_nutrition(otmp);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case SCROLL_CLASS:
u.uconduct.food++;
u.ustove += obj_nutrition(otmp);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case SPBOOK_CLASS:
u.uconduct.food++;
u.ustove += obj_nutrition(otmp);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case WAND_CLASS:
u.uconduct.food++;
u.ustove += obj_nutrition(otmp);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case FOOD_CLASS:
if(otmp->otyp == CORPSE){
u.uconduct.food++;
if (!vegan(&mons[otmp->corpsenm])) u.uconduct.unvegan++;
if (!vegetarian(&mons[otmp->corpsenm])) violated_vegetarian();
u.ustove += otmp->oeaten ? otmp->oeaten : obj_nutrition(otmp);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
} else {
u.uconduct.food++;
u.ustove += obj_nutrition(otmp);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
}
break;
}
return MOVE_ATE;
}
if(etype == HELLFIRE_FURNACE && !otmp->oartifact){
if(objects[otmp->otyp].oc_unique) return 1;//redundant check against unique
You("place the %s in your hellfire furnace.", xname(otmp));
/*These cases destroy the object, rescue its contents*/
while((obj2 = otmp->cobj)){
obj_extract_self(obj2);
/* Compartmentalize tip() */
if(carried(otmp)){
sellobj_state(SELL_DONTSELL);
dropy(obj2);
sellobj_state(SELL_NORMAL);
}
else {
place_object(obj2, u.ux, u.uy);
stackobj(obj2);
}
}
if (otmp->quan > 1L) {
if(!carried(otmp))
(void) splitobj(otmp, otmp->quan - 1L);
else
otmp = splitobj(otmp, 1L);
if (carried(otmp)) {
freeinv(otmp);
if (inv_cnt() >= 52) {
sellobj_state(SELL_DONTSELL);
dropy(otmp);
sellobj_state(SELL_NORMAL);
} else {
otmp->nomerge = TRUE;
otmp = addinv(otmp);
otmp->nomerge = FALSE;
}
}
}
switch(otmp->oclass){
case WEAPON_CLASS:
u.uconduct.food++;
u.ustove += obj_nutrition(otmp)/10+1;
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case RING_CLASS:
u.uconduct.food++;
u.ustove += obj_nutrition(otmp)/10+1;
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case AMULET_CLASS:
u.uconduct.food++;
u.ustove += obj_nutrition(otmp)/10+1;
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case ARMOR_CLASS:
u.uconduct.food++;
u.ustove += obj_nutrition(otmp)/10+1;
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case TOOL_CLASS:
u.uconduct.food++;
u.ustove += obj_nutrition(otmp)/10+1;
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case SCROLL_CLASS:
u.uconduct.food++;
u.ustove += obj_nutrition(otmp)/10+1;
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case SPBOOK_CLASS:
u.uconduct.food++;
u.ustove += obj_nutrition(otmp)/10+1;
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case WAND_CLASS:
u.uconduct.food++;
u.ustove += obj_nutrition(otmp)/10+1;
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case FOOD_CLASS:
if(otmp->otyp == CORPSE){
u.uconduct.food++;
if (!vegan(&mons[otmp->corpsenm])) u.uconduct.unvegan++;
if (!vegetarian(&mons[otmp->corpsenm])) violated_vegetarian();
u.ustove += otmp->oeaten ? ((otmp->oeaten)/10+1) : (obj_nutrition(otmp)/10+1);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
} else {
u.uconduct.food++;
u.ustove += obj_nutrition(otmp)/10+1;
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
}
break;
}
return MOVE_ATE;
}
if(etype == SCRAP_MAW && !otmp->oartifact){
if(objects[otmp->otyp].oc_unique) return 1;//redundant check against unique
You("crunch up the %s with your scrap maw.", xname(otmp));
/*These cases destroy the object, rescue its contents*/
while((obj2 = otmp->cobj)){
obj_extract_self(obj2);
/* Compartmentalize tip() */
if(carried(otmp)){
sellobj_state(SELL_DONTSELL);
dropy(obj2);
sellobj_state(SELL_NORMAL);
}
else {
place_object(obj2, u.ux, u.uy);
stackobj(obj2);
}
}
if (otmp->quan > 1L) {
if(!carried(otmp))
(void) splitobj(otmp, otmp->quan - 1L);
else
otmp = splitobj(otmp, 1L);
if (carried(otmp)) {
freeinv(otmp);
if (inv_cnt() >= 52) {
sellobj_state(SELL_DONTSELL);
dropy(otmp);
sellobj_state(SELL_NORMAL);
} else {
otmp->nomerge = TRUE;
otmp = addinv(otmp);
otmp->nomerge = FALSE;
}
}
}
switch(otmp->oclass){
case WEAPON_CLASS:
u.uconduct.food++;
if(Upolyd) u.mh = min(u.mhmax, u.mh+obj_nutrition(otmp));
else u.uhp = min(u.uhpmax, u.uhp+obj_nutrition(otmp));
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case RING_CLASS:
u.uconduct.food++;
if(Upolyd) u.mh = min(u.mhmax, u.mh+obj_nutrition(otmp));
else u.uhp = min(u.uhpmax, u.uhp+obj_nutrition(otmp));
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case AMULET_CLASS:
u.uconduct.food++;
if(Upolyd) u.mh = min(u.mhmax, u.mh+obj_nutrition(otmp));
else u.uhp = min(u.uhpmax, u.uhp+obj_nutrition(otmp));
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case ARMOR_CLASS:
u.uconduct.food++;
if(Upolyd) u.mh = min(u.mhmax, u.mh+obj_nutrition(otmp));
else u.uhp = min(u.uhpmax, u.uhp+obj_nutrition(otmp));
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case TOOL_CLASS:
u.uconduct.food++;
if(Upolyd) u.mh = min(u.mhmax, u.mh+obj_nutrition(otmp));
else u.uhp = min(u.uhpmax, u.uhp+obj_nutrition(otmp));
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case SCROLL_CLASS:
u.uconduct.food++;
if(Upolyd) u.mh = min(u.mhmax, u.mh+obj_nutrition(otmp));
else u.uhp = min(u.uhpmax, u.uhp+obj_nutrition(otmp));
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case SPBOOK_CLASS:
u.uconduct.food++;
if(Upolyd) u.mh = min(u.mhmax, u.mh+obj_nutrition(otmp));
else u.uhp = min(u.uhpmax, u.uhp+obj_nutrition(otmp));
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case WAND_CLASS:
u.uconduct.food++;
if(Upolyd) u.mh = min(u.mhmax, u.mh+obj_nutrition(otmp));
else u.uhp = min(u.uhpmax, u.uhp+obj_nutrition(otmp));
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
break;
case FOOD_CLASS:
if(otmp->otyp == CORPSE){
u.uconduct.food++;
if (!vegan(&mons[otmp->corpsenm])) u.uconduct.unvegan++;
if (!vegetarian(&mons[otmp->corpsenm])) violated_vegetarian();
if(Upolyd) u.mh += otmp->oeaten ? ((otmp->oeaten)/10+1) : (obj_nutrition(otmp)/10+1);
else u.uhp += otmp->oeaten ? ((otmp->oeaten)/10+1) : (obj_nutrition(otmp)/10+1);
if(Upolyd) u.mh = min(u.mhmax, u.mh);
else u.uhp = min(u.uhpmax, u.uhp);
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
} else {
u.uconduct.food++;
if(Upolyd) u.mh = min(u.mhmax, u.mh+obj_nutrition(otmp));
else u.uhp = min(u.uhpmax, u.uhp+obj_nutrition(otmp));
if (otmp == uwep && otmp->quan == 1L) uwepgone();
if (otmp == uquiver && otmp->quan == 1L) uqwepgone();
if (otmp == uswapwep && otmp->quan == 1L) uswapwepgone();
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
}
break;
}
return MOVE_ATE;
}
}
if ((otmp->otyp == EYEBALL || otmp->otyp == SEVERED_HAND) && otmp->oartifact) {
Strcpy(qbuf,"Are you sure you want to eat that?");
if ((c = yn_function(qbuf, ynqchars, 'n')) != 'y') return MOVE_CANCELLED;
}
/* 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 MOVE_ATE;
}
if (otmp->oclass != FOOD_CLASS) {
int material;
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);
/* 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 > ((long)get_uhungermax())*100L) ? get_uhungermax() : (int)(otmp->quan/100L));
else if(otmp->oclass == BALL_CLASS || (otmp->oclass == CHAIN_CLASS && weight(otmp) > objects[otmp->otyp].oc_nutrition))
basenutrit = weight(otmp);
/* oc_nutrition is usually weight anyway */
else
basenutrit = obj_nutrition(otmp);
victual.nmod = basenutrit;
victual.eating = TRUE; /* needed for lesshungry() */
material = otmp->obj_material;
if (material == LEATHER || material == BONE || material == CHITIN || material == DRAGON_HIDE) {
u.uconduct.unvegan++;
violated_vegetarian();
} else {
if(roll_madness(MAD_CANNIBALISM)){
make_vomiting(victual.reqtime+rn1(15,10), TRUE);
}
if (material == WAX)
u.uconduct.unvegan++;
}
u.uconduct.food++;
if (otmp->cursed)
(void) rottenfood(otmp);
/*These cases destroy the object, rescue its contents*/
while((obj2 = otmp->cobj)){
obj_extract_self(obj2);
/* Compartmentalize tip() */
if(carried(otmp)){
sellobj_state(SELL_DONTSELL);
dropy(obj2);
sellobj_state(SELL_NORMAL);
}
else {
place_object(obj2, u.ux, u.uy);
stackobj(obj2);
}
}
if (otmp->oclass == WEAPON_CLASS && otmp->opoisoned) {
if(otmp->opoisoned & OPOISON_BASIC){
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.");
}
if(otmp->opoisoned & OPOISON_FILTH){
pline("Ulch - that was tainted with filth!");
if (Sick_resistance) {
pline("It doesn't seem at all sickening, though...");
} else {
char buf[BUFSZ];
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;
Sprintf(buf, "filth-crusted %s", xname(otmp));
make_sick(sick_time, buf, TRUE, SICK_VOMITABLE);
}
}
if(otmp->opoisoned & OPOISON_SLEEP){
pline("Ecch - that must have been drugged!");
if(Poison_resistance || Sleep_resistance) {
You("suddenly fall asleep!");
fall_asleep(-rn1(10, 25 - 12*bcsign(otmp)), TRUE);
} else
You("seem unaffected by the drugs.");
}
if(otmp->opoisoned & OPOISON_BLIND){
pline("Ecch - that must have been poisoned!");
make_blinded(rn1(200, 250 - 125 * bcsign(otmp)),
(boolean)!Blind);
}
if(otmp->opoisoned & OPOISON_HALLU){
pline ("Oh wow! Great stuff!");
make_hallucinated(HHallucination + 200,FALSE,0L);
}
if(otmp->opoisoned & OPOISON_PARAL){
if (Free_action)
You("stiffen momentarily.");
else {
if (Levitation || Weightless||Is_waterlevel(&u.uz))
You("are motionlessly suspended.");
#ifdef STEED
else if (u.usteed)
You("are frozen in place!");
#endif
else
Your("%s are frozen to the %s!",
makeplural(body_part(FOOT)), surface(u.ux, u.uy));
nomul(-(rn1(10, 25 - 12*bcsign(otmp))), "paralyzed from eating an envenomed weapon");
nomovemsg = You_can_move_again;
exercise(A_DEX, FALSE);
}
}
if(otmp->opoisoned & OPOISON_AMNES){
forget(otmp->cursed ? 25 : otmp->blessed ? 0 : 10);
if (Hallucination)
pline("Hakuna matata!");
else
You_feel("your memories dissolve.");
/* Blessed amnesia makes you forget lycanthropy, sickness */
if (otmp->blessed) {
if (u.ulycn >= LOW_PM && !Race_if(PM_HUMAN_WEREWOLF)) {
You("forget your affinity to %s!",
makeplural(mons[u.ulycn].mname));
if (youracedata->mtyp == u.ulycn)
you_unwere(FALSE);
u.ulycn = NON_PM; /* cure lycanthropy */
}
make_sick(0L, (char *) 0, TRUE, SICK_ALL);
/* You feel refreshed */
if(Race_if(PM_INCANTIFIER)){
int engain = 50 + rnd(50);
u.uen += engain;
} else u.uhunger += 50 + rnd(50);
newuhs(FALSE);
} else {
if(Role_if(PM_MADMAN)){
You_feel("ashamed of wiping your own memory.");
change_hod(otmp->cursed ? 5 : 2);
}
exercise(A_WIS, FALSE);
}
}
if((otmp->opoisoned & OPOISON_ACID) && !Acid_resistance){
You("have a very bad case of stomach acid."); /* not body_part() */
losehp(rnd(15), "acidic meal", KILLED_BY_AN);
}
if((otmp->opoisoned & OPOISON_SILVER) && hates_silver(youracedata)){
pline("The silver sears the inside of your mouth!"); /* not body_part() */
losehp(rnd(20), "silvery meal", KILLED_BY_AN);
}
} else if (!otmp->cursed)
pline("This %s is delicious!",
otmp->oclass == COIN_CLASS ? foodword(otmp) :
singular(otmp, xname));
eatspecial();
return MOVE_ATE;
}
/* [ALI] Hero polymorphed in the meantime.
*/
if (otmp == victual.piece &&
is_vampire(youracedata) != otmp->odrained)
victual.piece = (struct obj *)0; /* Can't resume */
/* [ALI] Blood can coagulate during the interruption
* but not during the draining process.
*/
if(otmp == victual.piece && otmp->odrained &&
(peek_at_iced_corpse_age(otmp) + victual.usedtime + 5) < monstermoves)
victual.piece = (struct obj *)0; /* Can't resume */
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;
victual.piece = touchfood(otmp);
You("resume your meal.");
start_eating(victual.piece);
return MOVE_ATE;
}
/* 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 MOVE_STANDARD;
}
/* KMH, conduct */
u.uconduct.food++;
if(otmp->ostolen && u.sealsActive&SEAL_ANDROMALIUS) unbind(SEAL_ANDROMALIUS,TRUE);
if(otmp->otyp == EGG && u.sealsActive&SEAL_ECHIDNA) unbind(SEAL_ECHIDNA,TRUE);
if(otmp->otyp >= APPLE && otmp->otyp <= BANANA && u.sealsActive&SEAL_EVE) unbind(SEAL_EVE,TRUE);
if(otmp->corpsenm != NON_PM && is_giant(&mons[otmp->corpsenm]) && u.sealsActive&SEAL_YMIR) unbind(SEAL_YMIR,TRUE);
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) {
int tmp = eatcorpse(otmp);
if (tmp == 3) {
/* inedible */
victual.piece = (struct obj *)0;
/*
* The combination of odrained == TRUE and oeaten == cnutrit
* represents the case of starting to drain a corpse but not
* getting any further (eg., loosing consciousness due to
* rotten food). We must preserve this case to avoid corpses
* changing appearance after a failed attempt to eat.
*/
if (!otmp->odrained &&
otmp->oeaten == mons[otmp->corpsenm].cnutrit)
otmp->oeaten = 0;
/* ALI, conduct: didn't eat it after all */
u.uconduct.food--;
return MOVE_INSTANT;
} else if (tmp == 2) {
/* used up */
victual.piece = (struct obj *)0;
return MOVE_ATE;
} 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, etc. These are
* all handled in the != FOOD_CLASS case, above */
switch (otmp->obj_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 == HONEYCOMB ||
otmp->otyp == LUMP_OF_ROYAL_JELLY ||
otmp->otyp == LUMP_OF_SOLDIER_S_JELLY ||
otmp->otyp == LUMP_OF_DANCER_S_JELLY ||
otmp->otyp == LUMP_OF_PHILOSOPHER_S_JELLY ||
otmp->otyp == LUMP_OF_PRIESTESS_S_JELLY ||
otmp->otyp == LUMP_OF_RHETOR_S_JELLY
) u.uconduct.unvegan++;
if(roll_madness(MAD_CANNIBALISM)){
make_vomiting(objects[otmp->otyp].oc_delay+rn1(15,10), TRUE);
}
break;
}
victual.reqtime = (u.sealsActive&SEAL_AHAZU) ? 1 : objects[otmp->otyp].oc_delay;
if (otmp->otyp != FORTUNE_COOKIE && otmp->otyp != PROTEIN_PILL && otmp->otyp != LEMBAS_WAFER &&
(otmp->cursed ||
(((monstermoves - otmp->age) > (otmp->blessed ? 50:30)) &&
(otmp->orotten || !rn2(7))))) {
if (rottenfood(otmp)) {
otmp->orotten = TRUE;
dont_start = TRUE;
}
if (otmp->oeaten < 2) {
victual.piece = (struct obj *)0;
if (carried(otmp)) useup(otmp);
else useupf(otmp, 1L);
return MOVE_ATE;
} else
consume_oeaten(otmp, 1); /* oeaten >>= 1 */
} else fprefx(otmp);
}
/* re-calc the nutrition */
basenutrit = obj_nutrition(otmp);
nutrit = otmp->oeaten;
if (otmp->otyp == CORPSE && otmp->odrained) {
nutrit -= drainlevel(otmp);
basenutrit -= drainlevel(otmp);
}
/*debugpline("before rounddiv: victual.reqtime == %d", victual.reqtime);
debugpline("oeaten == %d, basenutrit == %d", otmp->oeaten, basenutrit);
debugpline("nutrit == %d, cnutrit == %d", nutrit, otmp->otyp == CORPSE ?*
mons[otmp->corpsenm].cnutrit : objects[otmp->otyp].oc_nutrition);*/
victual.reqtime = (basenutrit == 0 ? 0 : (u.sealsActive&SEAL_AHAZU) ? 1 :
rounddiv(victual.reqtime * (long)nutrit, basenutrit));
//debugpline("after rounddiv: victual.reqtime == %d", victual.reqtime);
/* calculate the modulo value (nutrit. units per round eating)
* [ALI] Note: although this is not exact, the remainder is
* now dealt with in done_eating().
*/
if (victual.reqtime == 0 || nutrit == 0)
/* possible if most has been eaten before */
victual.nmod = 0;
else if (nutrit >= victual.reqtime)
victual.nmod = -(nutrit / victual.reqtime);
else
victual.nmod = victual.reqtime % nutrit;
victual.canchoke = (u.uhs == SATIATED);
if (!dont_start) start_eating(otmp);
return MOVE_ATE;
}
/* 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(victual.canchoke &&
((Race_if(PM_INCANTIFIER) && u.uen >= u.uenmax) ||
(!Race_if(PM_INCANTIFIER)&& u.uhunger >= get_uhungermax()) )
) {
choke(victual.piece);
return 1;
}
if (victual.doreset) {
do_reset_eat();
return 0;
}
force_save_hs = TRUE;
if(victual.nmod < 0) {
lesshungry(-victual.nmod);
consume_oeaten(victual.piece, victual.nmod); /* -= -nmod */
} else if(victual.nmod > 0 && (victual.usedtime % victual.nmod)) {
lesshungry(1);
consume_oeaten(victual.piece, -1); /* -= 1 */
}
force_save_hs = FALSE;
recalc_wt();
return 0;
}
#endif /* OVLB */
#ifdef OVL0
void
gethungry() /* as time goes by - called by moveloop() and domove() */
{
int hungermod = 1;
if (Invulnerable) return; /* you don't feel hungrier */
if (Race_if(PM_ETHEREALOID)) return;
if(inediate(youracedata) && !uclockwork && !Race_if(PM_INCANTIFIER)){
//Gradually return to normal if you departed from normal as a result of polymorph.
if(u.uhunger < get_satiationlimit()*0.9)
u.uhunger++;
else if(u.uhunger > get_satiationlimit()*0.9)
u.uhunger--;
newuhs(TRUE);
return;
}
if(u.usleep) hungermod *= 10; /* slow metabolic rate while asleep */
/* Monks and Convicts can last twice as long at hungry and below. Convicts last 5x as long at weak or lower. */
if(Role_if(PM_CONVICT)){
if(u.uhs == HUNGRY) hungermod *= 2;
else if(u.uhs > HUNGRY) hungermod *=5; /* WEAK or hungrier */
}
if(Role_if(PM_MONK)){
if(u.uhs >= HUNGRY) hungermod *= 2; /* HUNGRY or hungrier */
}
if(Role_if(PM_MONK) && u.unull){
hungermod *= 2;
}
//Elder vampires can go for longer without blood
if(is_vampire(youracedata))
hungermod *= (maybe_polyd(youmonst.data->mlevel, u.ulevel)/14 + 1);
//Incantifiers get reduced hunger
if(Race_if(PM_INCANTIFIER))
hungermod *= 10;
//Unusually-sized creatures have more or less hunger
if(get_uhungersizemod() < 1){
hungermod /= get_uhungersizemod();
}
if ((!inediate(youracedata) || Race_if(PM_INCANTIFIER))
&& !(moves % hungermod)
&& !( (Slow_digestion && !Race_if(PM_INCANTIFIER)) ||
(uclockwork) )
){
int hunger = 1;
if(get_uhungersizemod() > 1){
hunger *= get_uhungersizemod();
}
/* ordinary food consumption */
if(Race_if(PM_INCANTIFIER))
u.uen -= hunger;
else u.uhunger -= hunger;
if(uwep && (
uwep->oartifact == ART_GARNET_ROD || (uwep->oartifact == ART_TENSA_ZANGETSU && !is_undead(youracedata)))
){
if(Race_if(PM_INCANTIFIER)) losepw(9);
else u.uhunger -= 9;
}
}
if ((!inediate(youracedata) || Race_if(PM_INCANTIFIER))
&& !uclockwork
&& NightmareAware_Sanity < 100
&& !BlockableClearThoughts
&& u.umadness&MAD_GLUTTONY
){
int delta = NightmareAware_Insanity;
if(Race_if(PM_INCANTIFIER)) losepw(delta/25);
else u.uhunger -= delta/25;
//remainder
delta = delta%25;
if(delta){
if (((moves)*(delta)) / 25 > ((moves - 1)*(delta)) / 25){
if(Race_if(PM_INCANTIFIER)) u.uen--;
else u.uhunger--;
}
}
}
if(uclockwork){
if(u.ucspeed == SLOW_CLOCKSPEED){
if(!(moves%(10+u.slowclock))){
if(Race_if(PM_INCANTIFIER)) u.uen--;
else u.uhunger--;
}
} else {
if(u.slowclock < 10){
if(moves%10 >= u.slowclock) (Race_if(PM_INCANTIFIER) ? u.uen-- : u.uhunger--);
} else if(!(moves%u.slowclock)) (Race_if(PM_INCANTIFIER) ? u.uen-- : u.uhunger--);
}
}
if (moves % 2) { /* odd turns */
/* Regeneration uses up food, unless due to an artifact */
if ( (HRegeneration && uhp() < uhpmax()) || ((ERegeneration & (~W_ART)) &&
(ERegeneration != W_WEP || !uwep->oartifact) &&
(ERegeneration != W_ARMS || !uarms->oartifact)
))
(Race_if(PM_INCANTIFIER) ? u.uen-- : u.uhunger--);
if (near_capacity() > SLT_ENCUMBER) (Race_if(PM_INCANTIFIER) ? u.uen-- : u.uhunger--);
} else { /* even turns */
if (Hunger) (Race_if(PM_INCANTIFIER) ? u.uen-- : u.uhunger--);
if (u.sealsActive&SEAL_AHAZU && (moves%10 == 1)) (Race_if(PM_INCANTIFIER) ? u.uen-- : u.uhunger--);
/* Conflict uses up food too */
if (HConflict || (EConflict & (~W_ARTI))) (Race_if(PM_INCANTIFIER) ? u.uen-- : u.uhunger--);
/* Alacrity uses up food too */
if (EFast & W_RING) (Race_if(PM_INCANTIFIER) ? u.uen-- : u.uhunger--);
/* +0 charged rings don't do anything, so don't affect hunger */
/* Slow digestion still uses ring hunger */
#define RING_HUNGER(ring) \
if (ring && \
(ring->spe || !objects[ring->otyp].oc_charged)) \
(Race_if(PM_INCANTIFIER) ? u.uen-- : u.uhunger--)
/* TODO clean this up */
switch ((int)(moves % 20)) { /* note: use even cases only */
case 2: RING_HUNGER(urings[2]); break;
case 4: RING_HUNGER(uleft); break;
case 6: RING_HUNGER(urings[3]); break;
case 8: if (uamul) (Race_if(PM_INCANTIFIER) ? u.uen-- : u.uhunger--);
break;
case 10: RING_HUNGER(urings[4]); break;
case 12: RING_HUNGER(uright); break;
case 14: RING_HUNGER(urings[5]); break;
case 16: if (u.uhave.amulet) (Race_if(PM_INCANTIFIER) ? u.uen-- : u.uhunger--);
break;
case 18: RING_HUNGER(urings[6]); break;
case 20: RING_HUNGER(urings[7]); break;
default: break;
}
#undef RING_HUNGER
}
newuhs(TRUE);
}
#endif /* OVL0 */
#ifdef OVLB
void
morehungry(num) /* called after vomiting and after performing feats of magic */
register int num;
{
if(inediate(youracedata) && !uclockwork && !Race_if(PM_INCANTIFIER)) return;
if(Race_if(PM_INCANTIFIER)) losepw(num);
else u.uhunger -= num;
newuhs(TRUE);
}
void
lesshungry(num) /* called after eating (and after drinking fruit juice) */
register int num;
{
if(Race_if(PM_ETHEREALOID)) return;
/* See comments in newuhs() for discussion on force_save_hs */
boolean iseating = (occupation == eatfood) || force_save_hs;
//debugpline("lesshungry(%d)", num);
if(Race_if(PM_INCANTIFIER)) u.uen += num;
else u.uhunger += num;
if(((Race_if(PM_INCANTIFIER) && u.uen > u.uenmax) ||
(!Race_if(PM_INCANTIFIER)&& u.uhunger >= get_uhungermax()) )
) {
if (!iseating || victual.canchoke) {
if (iseating) {
choke(victual.piece);
reset_eat();
} else
if(!uclockwork) choke(occupation == opentin ? tin.tin : (struct obj *)0);
else{
Your("mainspring is wound too tight!");
Your("clockwork breaks apart!");
if (!Unchanging && Upolyd) {
rehumanize();
change_gevurah(1); //cheated death.
} else {
killer_format = KILLED_BY;
killer = "overwinding";
done(OVERWOUND);
}
victual.piece = 0;
victual.mon = 0;
return;
}
/* no reset_eat() */
}
} else {
/* Have lesshungry() report when you're nearly full so all eating
* warns when you're about to choke.
*/
if ((Race_if(PM_INCANTIFIER) && u.uen >= (get_uhungermax()*7)/8) ||
(uclockwork && u.uhunger >= (get_uhungermax()*7)/8) ||
(!Race_if(PM_INCANTIFIER) && !uclockwork && u.uhunger >= (get_uhungermax()*3)/4)) {
if (!victual.eating || (victual.eating && !victual.fullwarn)) {
if(!uclockwork){
pline("You're having a hard time getting all of it down.");
nomovemsg = "You're finally finished.";
}
else{
Your("mainspring is being overwound!");
nomovemsg = "The winding finally stopped.";
}
if (!victual.eating)
multi = -2;
else {
victual.fullwarn = TRUE;
if (victual.canchoke && victual.reqtime > 1) {
/* a one-gulp food will not survive a stop */
if (yn_function("Continue eating?",ynchars,'n')=='n') {
reset_eat();
nomovemsg = (char *)0;
}
}
}
}
}
}
newuhs(FALSE);
}
int
unfaint(VOID_ARGS)
{
(void) Hear_again();
if(u.uhs > FAINTING)
u.uhs = FAINTING;
stop_occupation();
flags.botl = 1;
return 0;
}
int
ask_turns(mon, flatrate, perturn)
struct monst *mon;
int flatrate;
int perturn;
{
int ret, turns;
long price;
char buf[BUFSZ], qbuf[QBUFSZ];
boolean pay;
Sprintf(qbuf, "How many turns? (80 turns = 1 food ration)");
getlin(qbuf, buf);
(void)mungspaces(buf);
if (buf[0] == '\033' || buf[0] == '\0') ret = 0;
else ret = sscanf(buf, "%d", &turns);
if (ret != 1 || turns <= 0){
return 0;
}
if(turns > 100){
pline("\"That sounds really boring.\"");
return 0;
}
price = (long)(turns*perturn + flatrate);
if(price < 0){
You("couldn't have enough %s to pay for that!", currency(price));
return 0;
}
if(price){
Sprintf(qbuf, "That will be %ld %s. (Pay?)", price, currency(price));
if(yn(qbuf)=='y'){
struct eshk *eshkp = 0;
int credit = 0;
if(mon->isshk)
eshkp = ESHK(mon);
if(eshkp && eshkp->credit > 0)
credit = eshkp->credit;
if (credit >= price) {
Your("credit of %d is used to cover the bill.", credit);
eshkp->credit -= price;
}
#ifndef GOLDOBJ
else if (price > u.ugold+credit) {
You("don't have enough %s!", currency(price));
return 0;
} else {
if(credit){
Your("credit of %d is used to cover part of the bill.", credit);
price -= credit;
}
You("give %s %ld %s.", mon_nam(mon), price, currency(price));
u.ugold -= price;
mon->mgold += price;
}
#else
if (price > umoney) {
You("don't have enough %s!", currency(price));
return 0;
} else {
if(credit){
Your("credit of %d is used to cover part of the bill.", credit);
price -= credit;
}
You("give %s %ld %s.", mon_nam(mon), price, currency(price));
(void) money2mon(mon, price);
}
#endif
return turns;
} else{
return 0;
}
}
else return turns;
}
int
ask_cp(mon, flatrate)
struct monst *mon;
int flatrate;
{
int ret, howmany;
long price;
char buf[BUFSZ], qbuf[QBUFSZ];
boolean pay;
Sprintf(qbuf, "How many components?");
getlin(qbuf, buf);
(void)mungspaces(buf);
if (buf[0] == '\033' || buf[0] == '\0') ret = 0;
else ret = sscanf(buf, "%d", &howmany);
if (ret != 1 || howmany <= 0){
return 0;
}
if(howmany > 100){
pline("\"That's too many all at once.\"");
return 0;
}
price = (long)(howmany*flatrate);
if(price < 0){
You("couldn't have enough %s to pay for that!", currency(price));
return 0;
}
if(price){
Sprintf(qbuf, "That will be %ld %s. (Pay?)", price, currency(price));
if(yn(qbuf)=='y'){
#ifndef GOLDOBJ
if (price > u.ugold) {
You("don't have enough %s!", currency(price));
return 0;
} else {
You("give %s %ld %s.", mon_nam(mon), price, currency(price));
}
u.ugold -= price;
mon->mgold += price;
#else
if (price > umoney) {
You("don't have enough %s!", currency(price));
return 0;
} else {
You("give %s %ld %s.", mon_nam(mon), price, currency(price));
}
(void) money2mon(mon, price);
#endif
return howmany;
} else{
return 0;
}
}
else return howmany;
}
int
start_clockwinding(key, mon, turns)
struct obj * key;
struct monst *mon;
int turns;
{
// int ret;
// char buf[BUFSZ], qbuf[QBUFSZ];
// Sprintf(qbuf, "How many turns?");
// getlin(qbuf, buf);
// (void)mungspaces(buf);
// if (buf[0] == '\033' || buf[0] == '\0') ret = 0;
// else ret = sscanf(buf, "%d", &turns);
// if (ret != 1 || turns <= 0){
// pline(Never_mind);
// return 0;
// }
if (key->otyp != SKELETON_KEY)
return 0;
pline("%s uses the key to wind up your clockwork.", Monnam(mon));
victual.piece = key;
victual.canchoke = TRUE;
victual.usedtime = 0;
victual.fullwarn = FALSE;
victual.reqtime = turns;
victual.mon = mon;
mon->moccupation = 1;
mon->mcanmove = 0;
Sprintf(msgbuf, "being wound");
set_occupation(windclock, msgbuf, 0);
return 1;
}
STATIC_PTR
int
windclock()
{
if (victual.reqtime == victual.usedtime){
occupation = 0;
pline("%s finishes winding up your clockwork.", Monnam(victual.mon));
victual.mon->moccupation = 0;
victual.mon->mcanmove = 1;
newuhs(FALSE);
victual.piece = 0;
victual.mon = 0;
}else if (!carried(victual.piece)){
newuhs(FALSE);
stop_occupation();
victual.piece = 0;
victual.mon = 0;
return MOVE_CANCELLED;
}else if(victual.canchoke && u.uhunger >= get_uhungermax() && !Race_if(PM_INCANTIFIER)) {
Your("mainspring is wound too tight!");
Your("clockwork breaks apart!");
killer_format = KILLED_BY;
killer = "overwinding";
done(OVERWOUND);
victual.piece = 0;
victual.mon = 0;
return MOVE_FINISHED_OCCUPATION;
}
else if (u.uhunger >= (get_uhungermax()*3)/4 && !victual.fullwarn && !Race_if(PM_INCANTIFIER)) {
pline("%s is having a hard time cranking the key.",Monnam(victual.mon));
victual.fullwarn = TRUE;
}
else {
if(victual.reqtime - victual.usedtime >= 10){
if(!Race_if(PM_INCANTIFIER)) u.uhunger += 100;
victual.usedtime += 10;
} else {
if(!Race_if(PM_INCANTIFIER)) u.uhunger += (victual.reqtime - victual.usedtime) * 10;
victual.usedtime += (victual.reqtime - victual.usedtime);
}
}
flags.botl = 1;
return MOVE_STANDARD;
}
#endif /* OVLB */
#ifdef OVL0
boolean
is_fainted()
{
return((boolean)(u.uhs == FAINTED));
}
void
reset_faint() /* call when a faint must be prematurely terminated */
{
if(is_fainted()) nomul(0, NULL);
}
#if 0
void
sync_hunger()
{
if(is_fainted()) {
flags.soundok = 0;
if(get_uhungersizemod() > 1){
if(Role_if(PM_CONVICT)) nomul(-1+( YouHunger/(20*get_uhungersizemod())), "fainted from lack of food");
else nomul(-10+( YouHunger/(10*get_uhungersizemod())), "fainted from lack of food");
}
else {
if(Role_if(PM_CONVICT)) nomul(-1+( YouHunger/20), "fainted from lack of food");
else nomul(-10+( YouHunger/10), "fainted from lack of food");
}
nomovemsg = "You regain consciousness.";
afternmv = unfaint;
}
}
#endif
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 = YouHunger;
boolean clockwork = uclockwork;
newhs = (h > get_satiationlimit()) ? SATIATED :
(h > 150*get_uhungersizemod()) ? NOT_HUNGRY :
(h > 50*get_uhungersizemod()) ? 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-YouHunger/10) >= 19) {
if(!is_fainted() && multi >= 0 && occupation != windclock) {
/* stop what you're doing, then faint */
stop_occupation();
if (clockwork){
Your("clockwork slips.");
flags.soundok=0;
nomul(-2, "immobilized by slipping gears.");
nomovemsg = "Your clockwork catches again.";
afternmv = unfaint;
} else {
You("faint from lack of food.");
flags.soundok = 0;
if(Role_if(PM_CONVICT)) nomul(-1+YouHunger/20, "fainted from lack of food");
else nomul(-10+YouHunger/10, "fainted from lack of food");
nomovemsg = "You regain consciousness.";
afternmv = unfaint;
}
newhs = FAINTED;
}
} else
if(YouHunger < -(int)(200 + 20*ACURR(A_CON))) {
if (clockwork){
Your("clockwork comes to a complete stop.");
killer_format = KILLED_BY_AN;
killer = "unwound spring";
done(DIED);
} else {
u.uhs = STARVED;
flags.botl = 1;
bot();
You("die from starvation.");
killer_format = KILLED_BY;
killer = "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 (clockwork){
if (Hallucination)
Your((!incr)? "cuckoo only feels hungry now.":
"cuckoo is feeling hungry.");
else
You_feel((!incr) ? "your mainspring tightening." :
"the power of your mainspring waning.");
if (incr && occupation && occupation != windclock)
stop_occupation();
break;
}
if (Hallucination) {
You((!incr) ?
"now have a lesser case of the munchies." :
"are getting the munchies.");
} else
You((!incr) ? "only feel hungry now." :
(YouHunger < 145) ? "feel hungry." :
"are beginning to feel hungry.");
if (incr && occupation &&
(occupation != eatfood && occupation != opentin))
stop_occupation();
break;
case WEAK:
if (clockwork){
You_feel("your mainspring %s and your gears %s.",
(Hallucination)?"sprunging":"unwinding",
(!incr)?"still slipping":
(YouHunger < 45 ) ? "slipping":
"starting to slip");
if (incr && occupation && occupation != windclock)
stop_occupation();
if(u.ucspeed == HIGH_CLOCKSPEED){
pline("There is no longer sufficient tension in your mainspring to maintain a high clock-speed.");
u.ucspeed = NORM_CLOCKSPEED;
}
break;
}
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." :
(YouHunger < 45) ? "feel weak." :
"are beginning to feel weak.");
if (incr && occupation &&
(occupation != eatfood && occupation != opentin))
stop_occupation();
break;
case SATIATED:
if (clockwork){
if(u.ucspeed == SLOW_CLOCKSPEED){
pline("There is now too much tension in your mainspring to maintain a slow clock-speed.");
u.ucspeed = NORM_CLOCKSPEED;
}
}
break;
case FAINTING:
if (clockwork){
if(u.ucspeed == HIGH_CLOCKSPEED){
pline("There is no longer sufficient tension in your mainspring to maintain a high clock-speed.");
u.ucspeed = NORM_CLOCKSPEED;
}
}
break;
}
u.uhs = newhs;
flags.botl = 1;
bot();
if ((Upolyd ? u.mh : u.uhp) < 1) {
You("die from hunger and exhaustion.");
killer_format = KILLED_BY;
killer = "exhaustion";
done(STARVING);
return;
}
}
}
#endif /* OVL0 */
#ifdef OVLB
/* 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"));
/* if we can't touch floor objects then use invent food only */
if (!can_reach_floor() ||
#ifdef STEED
(feeding && u.usteed) || /* can't eat off floor while riding */
#endif
((is_pool(u.ux, u.uy, FALSE) || is_lava(u.ux, u.uy)) &&
(Wwalking || is_clinger(youracedata) ||
(Flying && !Breathless))))
goto skipfloor;
if (feeding && (metallivorous(youracedata) || (uclockwork && u.clockworkUpgrades&SCRAP_MAW))) {
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, NO_MKOBJ_FLAGS);
} else if (c == 'q') {
return (struct obj *)0;
}
}
if (youracedata->mtyp != PM_RUST_MONSTER &&
!(uclockwork && u.clockworkUpgrades&SCRAP_MAW) &&
(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) {
Sprintf(qbuf, "There %s %s here; %s %s?",
otense(otmp, "are"),
doname(otmp), verb,
(otmp->quan == 1L) ? "it" : "one");
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 ? (const char *)allobj :
(const char *)comestibles, verb);
if (corpsecheck && otmp)
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);
if (!Free_action) nomul(-2, "vomiting");
}
int
eaten_stat(base, obj)
register int base;
register struct obj *obj;
{
long uneaten_amt, full_amount;
full_amount = (long)obj_nutrition(obj);
uneaten_amt = (long)obj->oeaten;
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;
}
int
obj_nutrition(struct obj *otmp)
{
int nut = (otmp->otyp == CORPSE) ? mons[otmp->corpsenm].cnutrit : (int) objects[otmp->otyp].oc_nutrition;
if (otmp->otyp == LEMBAS_WAFER) {
if (maybe_polyd(is_elf(youmonst.data), Race_if(PM_ELF)))
nut += nut / 4; /* 800 -> 1000 */
else if (maybe_polyd(is_orc(youmonst.data), Race_if(PM_ORC)))
nut -= nut / 4; /* 800 -> 600 */
/* prevent polymorph making a partly eaten wafer
become more nutritious than an untouched one */
if (otmp->oeaten >= nut)
otmp->oeaten = (otmp->oeaten < objects[LEMBAS_WAFER].oc_nutrition)
? (nut - 1) : nut;
} else if (otmp->otyp == CRAM_RATION) {
if (maybe_polyd(is_dwarf(youmonst.data), Race_if(PM_DWARF)))
nut += nut / 6; /* 600 -> 700 */
}
return nut;
}
/* reduce obj's oeaten field, making sure it never hits or passes 0 */
void
consume_oeaten(obj, amt)
struct obj *obj;
int amt;
{
if (!obj_nutrition(obj)) {
char itembuf[40];
int otyp = obj->otyp;
if (otyp == CORPSE || otyp == EGG || otyp == TIN) {
Strcpy(itembuf, (otyp == CORPSE) ? "corpse"
: (otyp == EGG) ? "egg"
: (otyp == TIN) ? "tin" : "other?");
Sprintf(eos(itembuf), " [%d]", obj->corpsenm);
} else {
Sprintf(itembuf, "%d", otyp);
}
impossible("oeaten: attempting to set 0 nutrition food (%s) partially eaten", itembuf);
return;
}
/*
* 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 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 == victual.piece) /* always true unless wishing... */
victual.reqtime = victual.usedtime; /* no bites left */
obj->oeaten = 1; /* smallest possible positive value */
}
}
STATIC_PTR int
clockwork_eat_menu(dry,mgc)
boolean dry;
boolean mgc;
{
winid tmpwin;
int n, how;
char buf[BUFSZ];
char incntlet = 'a';
menu_item *selected;
anything any;
if(!(dry||mgc||u.clockworkUpgrades&(SCRAP_MAW|HELLFIRE_FURNACE))) return 0;
tmpwin = create_nhwindow(NHW_MENU);
start_menu(tmpwin);
any.a_void = 0; /* zero out all bits */
Sprintf(buf, "Installed Upgrades");
add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_BOLD, buf, MENU_UNSELECTED);
if(u.clockworkUpgrades & WOOD_STOVE && dry){
Sprintf(buf, "wood stove");
any.a_int = (int) WOOD_STOVE; /* must be non-zero */
add_menu(tmpwin, NO_GLYPH, &any,
incntlet, 0, ATR_NONE, buf,
MENU_UNSELECTED);
incntlet = (incntlet != 'z') ? (incntlet+1) : 'A';
}
if(u.clockworkUpgrades & HELLFIRE_FURNACE){
Sprintf(buf, "hellfire furnace");
any.a_int = (int) HELLFIRE_FURNACE; /* must be non-zero */
add_menu(tmpwin, NO_GLYPH, &any,
incntlet, 0, ATR_NONE, buf,
MENU_UNSELECTED);
incntlet = (incntlet != 'z') ? (incntlet+1) : 'A';
}
if(u.clockworkUpgrades & MAGIC_FURNACE && mgc){
Sprintf(buf, "magic furnace");
any.a_int = (int) MAGIC_FURNACE; /* must be non-zero */
add_menu(tmpwin, NO_GLYPH, &any,
incntlet, 0, ATR_NONE, buf,
MENU_UNSELECTED);
incntlet = (incntlet != 'z') ? (incntlet+1) : 'A';
}
if(u.clockworkUpgrades & SCRAP_MAW){
Sprintf(buf, "scrap maw");
any.a_int = (int) SCRAP_MAW; /* must be non-zero */
add_menu(tmpwin, NO_GLYPH, &any,
incntlet, 0, ATR_NONE, buf,
MENU_UNSELECTED);
incntlet = (incntlet != 'z') ? (incntlet+1) : 'A';
}
end_menu(tmpwin, "Choose upgrade:");
how = PICK_ONE;
n = select_menu(tmpwin, how, &selected);
destroy_nhwindow(tmpwin);
if(n > 0){
int picked = selected[0].item.a_int;
free(selected);
return picked;
}
return 0;
}
#endif /* OVLB */
#ifdef OVL1
/* 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 && victual.usedtime >= victual.reqtime) {
if (stopping) occupation = 0; /* for do_reset_eat */
(void) eatfood(); /* calls done_eating() to use up victual.piece */
return TRUE;
}
return FALSE;
}
#endif /* OVL1 */
/*eat.c*/