From: nethack.rankin Date: Sun, 30 Jan 2005 04:19:01 +0000 (+0000) Subject: intelligent pets vs cannibalism (trunk only) X-Git-Tag: MOVE2GIT~1336 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=d1732e1e8c829d305dea4644778a4d480990f067;p=nethack 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). --- diff --git a/doc/fixes35.0 b/doc/fixes35.0 index b99dcd2e9..0d8520f9a 100644 --- a/doc/fixes35.0 +++ b/doc/fixes35.0 @@ -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 diff --git a/include/extern.h b/include/extern.h index 34b3e2643..d42b34ae8 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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 *)); diff --git a/src/dog.c b/src/dog.c index c3f3f2f6d..f908fcf99 100644 --- a/src/dog.c +++ b/src/dog.c @@ -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); diff --git a/src/eat.c b/src/eat.c index 3ef56ab9a..9790ba900 100644 --- a/src/eat.c +++ b/src/eat.c @@ -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!"); } diff --git a/src/mondata.c b/src/mondata.c index a433d22ea..0b58b45cc 100644 --- a/src/mondata.c +++ b/src/mondata.c @@ -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;