plname is stored in the save file on all platforms now
introduce support for negation of role, race, align, gender values to eliminate
them from random selection and the pick list of startup choices
+some intelligent pets will avoid cannibalism
Platform- and/or Interface-Specific New Features
E struct attack *FDECL(dmgtype_fromattack, (struct permonst *,int,int));
E boolean FDECL(dmgtype, (struct permonst *,int));
E int FDECL(max_passive_dmg, (struct monst *,struct monst *));
+E boolean FDECL(same_race, (struct permonst *,struct permonst *));
E int FDECL(monsndx, (struct permonst *));
E int FDECL(name_to_mon, (const char *));
E int FDECL(gender, (struct monst *));
-/* SCCS Id: @(#)dog.c 3.5 2004/11/26 */
+/* SCCS Id: @(#)dog.c 3.5 2005/01/29 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
struct monst *mon;
register struct obj *obj;
{
- boolean carni = carnivorous(mon->data);
- boolean herbi = herbivorous(mon->data);
- struct permonst *fptr = &mons[obj->corpsenm];
- boolean starving;
+ struct permonst *mptr = mon->data, *fptr = &mons[obj->corpsenm];
+ boolean carni = carnivorous(mptr), herbi = herbivorous(mptr),
+ starving;
if (is_quest_artifact(obj) || obj_resists(obj, 0, 95))
return (obj->cursed ? TABU : APPORT);
switch(obj->oclass) {
case FOOD_CLASS:
if (obj->otyp == CORPSE &&
- ((touch_petrifies(&mons[obj->corpsenm]) && !resists_ston(mon))
+ ((touch_petrifies(fptr) && !resists_ston(mon))
|| is_rider(fptr)))
return TABU;
/* Ghouls only eat old corpses... yum! */
- if (mon->data == &mons[PM_GHOUL])
+ if (mptr == &mons[PM_GHOUL])
return (obj->otyp == CORPSE &&
peek_at_iced_corpse_age(obj) + 50L <= monstermoves) ?
DOGFOOD : TABU;
case HUGE_CHUNK_OF_MEAT:
return (carni ? DOGFOOD : MANFOOD);
case EGG:
- if (touch_petrifies(&mons[obj->corpsenm]) && !resists_ston(mon))
+ if (touch_petrifies(fptr) && !resists_ston(mon))
return POISON;
return (carni ? CADAVER : MANFOOD);
case CORPSE:
if ((peek_at_iced_corpse_age(obj) + 50L <= monstermoves
&& obj->corpsenm != PM_LIZARD
&& obj->corpsenm != PM_LICHEN
- && mon->data->mlet != S_FUNGUS) ||
- (acidic(&mons[obj->corpsenm]) && !resists_acid(mon)) ||
- (poisonous(&mons[obj->corpsenm]) &&
- !resists_poison(mon)))
+ && mptr->mlet != S_FUNGUS) ||
+ (acidic(fptr) && !resists_acid(mon)) ||
+ (poisonous(fptr) && !resists_poison(mon)))
return POISON;
else if (vegan(fptr))
return (herbi ? CADAVER : MANFOOD);
- else return (carni ? CADAVER : MANFOOD);
+ /* most humanoids will avoid cannibalism unless starving;
+ arbitrary: elves won't eat other elves even then */
+ else if (humanoid(mptr) && same_race(mptr, fptr) &&
+ (!is_undead(mptr) && fptr->mlet != S_KOBOLD &&
+ fptr->mlet != S_ORC && fptr->mlet != S_OGRE))
+ return ((starving && carni && !is_elf(mptr)) ?
+ ACCFOOD : TABU);
+ else
+ return (carni ? CADAVER : MANFOOD);
case CLOVE_OF_GARLIC:
- return ((is_undead(mon->data) || is_vampshifter(mon)) ? TABU :
+ return ((is_undead(mptr) || is_vampshifter(mon)) ? TABU :
((herbi || starving) ? ACCFOOD : MANFOOD));
case TIN:
- return (metallivorous(mon->data) ? ACCFOOD : MANFOOD);
+ return (metallivorous(mptr) ? ACCFOOD : MANFOOD);
case APPLE:
case CARROT:
return (herbi ? DOGFOOD : starving ? ACCFOOD : MANFOOD);
case BANANA:
- return ((mon->data->mlet == S_YETI) ? DOGFOOD :
+ return ((mptr->mlet == S_YETI) ? DOGFOOD :
((herbi || starving) ? ACCFOOD : MANFOOD));
default:
if (starving) return ACCFOOD;
if (mon_hates_silver(mon) &&
objects[obj->otyp].oc_material == SILVER)
return(TABU);
- if (mon->data == &mons[PM_GELATINOUS_CUBE] && is_organic(obj))
+ if (mptr == &mons[PM_GELATINOUS_CUBE] && is_organic(obj))
return(ACCFOOD);
- if (metallivorous(mon->data) && is_metallic(obj) && (is_rustprone(obj) || mon->data != &mons[PM_RUST_MONSTER])) {
+ if (metallivorous(mptr) && is_metallic(obj) &&
+ (is_rustprone(obj) || mptr != &mons[PM_RUST_MONSTER])) {
/* Non-rustproofed ferrous based metals are preferred. */
return((is_rustprone(obj) && !obj->oerodeproof) ? DOGFOOD :
ACCFOOD);
-/* SCCS Id: @(#)eat.c 3.5 2004/11/17 */
+/* SCCS Id: @(#)eat.c 3.5 2005/01/29 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
context.victual.fullwarn = context.victual.eating = context.victual.doreset = FALSE;
}
+/* eating a corpse or egg of one's own species is usually naughty */
STATIC_OVL boolean
maybe_cannibal(pm, allowmsg)
int pm;
boolean allowmsg;
{
- if (!CANNIBAL_ALLOWED() && your_race(&mons[pm])) {
+ struct permonst *fptr = &mons[pm]; /* food type */
+
+ if (!CANNIBAL_ALLOWED() &&
+ /* non-cannibalistic heroes shouldn't eat own species ever
+ and also shouldn't eat current species when polymorphed
+ (even if having the form of something which doesn't care
+ about cannibalism--hero's innate traits aren't altered) */
+ (your_race(fptr) ||
+ (Upolyd && same_race(youmonst.data, fptr)))) {
if (allowmsg) {
- if (Upolyd)
+ if (Upolyd && your_race(fptr))
You("have a bad feeling deep inside.");
You("cannibal! You will regret this!");
}
-/* SCCS Id: @(#)mondata.c 3.5 2004/10/20 */
+/* SCCS Id: @(#)mondata.c 3.5 2005/01/29 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
return (is_vampshifter(mon) || hates_silver(mon->data));
}
+/* TRUE if monster is especially affected by silver weapons */
boolean
hates_silver(ptr)
register struct permonst *ptr;
-/* returns TRUE if monster is especially affected by silver weapons */
{
- return((boolean)(is_were(ptr) || ptr->mlet==S_VAMPIRE || is_demon(ptr) ||
- ptr == &mons[PM_SHADE] ||
- (ptr->mlet==S_IMP && ptr != &mons[PM_TENGU])));
+ return (boolean)(is_were(ptr) || ptr->mlet == S_VAMPIRE ||
+ is_demon(ptr) || ptr == &mons[PM_SHADE] ||
+ (ptr->mlet == S_IMP && ptr != &mons[PM_TENGU]));
}
/* true iff the type of monster pass through iron bars */
return 0;
}
+/* determine whether two monster types are from the same species */
+boolean
+same_race(pm1, pm2)
+struct permonst *pm1, *pm2;
+{
+ char let1 = pm1->mlet, let2 = pm2->mlet;
+
+ if (pm1 == pm2) return TRUE; /* exact match */
+ /* player races have their own predicates */
+ if (is_human(pm1)) return is_human(pm2);
+ if (is_elf(pm1)) return is_elf(pm2);
+ if (is_dwarf(pm1)) return is_dwarf(pm2);
+ if (is_gnome(pm1)) return is_gnome(pm2);
+ if (is_orc(pm1)) return is_orc(pm2);
+ /* other creatures are less precise */
+ if (is_giant(pm1)) return is_giant(pm2); /* open to quibbling here */
+ if (is_golem(pm1)) return is_golem(pm2); /* even moreso... */
+ if (is_mind_flayer(pm1)) return is_mind_flayer(pm2);
+ if (let1 == S_KOBOLD ||
+ pm1 == &mons[PM_KOBOLD_ZOMBIE] ||
+ pm1 == &mons[PM_KOBOLD_MUMMY])
+ return (let2 == S_KOBOLD ||
+ pm2 == &mons[PM_KOBOLD_ZOMBIE] ||
+ pm2 == &mons[PM_KOBOLD_MUMMY]);
+ if (let1 == S_OGRE) return (let2 == S_OGRE);
+ if (let1 == S_NYMPH) return (let2 == S_NYMPH);
+ if (let1 == S_CENTAUR) return (let2 == S_CENTAUR);
+ if (is_unicorn(pm1)) return is_unicorn(pm2);
+ if (let1 == S_DRAGON) return (let2 == S_DRAGON);
+ if (let1 == S_NAGA) return (let2 == S_NAGA);
+ /* other critters get steadily messier */
+ if (is_rider(pm1)) return is_rider(pm2); /* debatable */
+ if (is_minion(pm1)) return is_minion(pm2); /* [needs work?] */
+ /* tengu don't match imps (first test handled case of both being tengu) */
+ if (pm1 == &mons[PM_TENGU] || pm2 == &mons[PM_TENGU]) return FALSE;
+ if (let1 == S_IMP) return (let2 == S_IMP);
+ /* and minor demons (imps) don't match major demons */
+ else if (let2 == S_IMP) return FALSE;
+ if (is_demon(pm1)) return is_demon(pm2);
+ if (is_undead(pm1)) {
+ if (let1 == S_ZOMBIE) return (let2 == S_ZOMBIE);
+ if (let1 == S_MUMMY) return (let2 == S_MUMMY);
+ if (let1 == S_VAMPIRE) return (let2 == S_VAMPIRE);
+ if (let1 == S_LICH) return (let2 == S_LICH);
+ if (let1 == S_WRAITH) return (let2 == S_WRAITH);
+ if (let1 == S_GHOST) return (let2 == S_GHOST);
+ } else if (is_undead(pm2)) return FALSE;
+ /* check for monsters--mainly animals--which grow into more mature forms */
+ if (let1 == let2) {
+ int m1 = monsndx(pm1), m2 = monsndx(pm2), prv, nxt;
+
+ /* we know m1 != m2 (very first check above); test all smaller
+ forms of m1 against m2, then all larger ones; don't need to
+ make the corresponding tests for variants of m2 against m1 */
+ for (prv = m1, nxt = big_to_little(m1); nxt != prv;
+ prv = nxt, nxt = big_to_little(nxt)) if (nxt == m2) return TRUE;
+ for (prv = m1, nxt = little_to_big(m1); nxt != prv;
+ prv = nxt, nxt = little_to_big(nxt)) if (nxt == m2) return TRUE;
+ }
+ /* not caught by little/big handling */
+ if (pm1 == &mons[PM_GARGOYLE] || pm1 == &mons[PM_WINGED_GARGOYLE])
+ return (pm2 == &mons[PM_GARGOYLE] || pm2 == &mons[PM_WINGED_GARGOYLE]);
+ if (pm1 == &mons[PM_KILLER_BEE] || pm1 == &mons[PM_QUEEN_BEE])
+ return (pm2 == &mons[PM_KILLER_BEE] || pm2 == &mons[PM_QUEEN_BEE]);
+ if (is_longworm(pm1)) return is_longworm(pm2); /* handles tail */
+ /* [currently there's no reason to bother matching up
+ assorted bugs and blobs with their closest variants] */
+ /* didn't match */
+ return FALSE;
+}
+
int
monsndx(ptr) /* return an index into the mons array */
struct permonst *ptr;