intelligent pets vs cannibalism (trunk only)

Implement a user suggestion that tame humanoids should avoid eating
corpses of their own species.  Prevent them--except for kobolds, orcs, and
ogres--from doing so unless starving.  Arbitrary:  tame elves won't eat
other elves even when starving.  A polymorphed character will incur the
effects of cannibalism when eating either his/her underlying race _or_
the current one (player orcs and cavemen aren't affected though).
This commit is contained in:
nethack.rankin
2005-01-30 04:19:01 +00:00
parent ce1bf539ac
commit d1732e1e8c
5 changed files with 115 additions and 26 deletions

View File

@@ -128,6 +128,7 @@ container cknown flag for container content awareness
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

View File

@@ -1221,6 +1221,7 @@ E int FDECL(num_horns, (struct permonst *));
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 *));

View File

@@ -1,4 +1,4 @@
/* 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. */
@@ -655,10 +655,9 @@ dogfood(mon,obj)
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);
@@ -666,12 +665,12 @@ register struct obj *obj;
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;
@@ -691,31 +690,38 @@ register struct obj *obj;
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;
@@ -730,9 +736,10 @@ register struct obj *obj;
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);

View File

@@ -1,4 +1,4 @@
/* 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. */
@@ -415,14 +415,23 @@ boolean message;
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!");
}

View File

@@ -1,4 +1,4 @@
/* 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. */
@@ -252,14 +252,14 @@ struct monst *mon;
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 */
@@ -392,6 +392,77 @@ max_passive_dmg(mdef, magr)
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;