]> granicus.if.org Git - nethack/commitdiff
intelligent pets vs cannibalism (trunk only)
authornethack.rankin <nethack.rankin>
Sun, 30 Jan 2005 04:19:01 +0000 (04:19 +0000)
committernethack.rankin <nethack.rankin>
Sun, 30 Jan 2005 04:19:01 +0000 (04:19 +0000)
     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).

doc/fixes35.0
include/extern.h
src/dog.c
src/eat.c
src/mondata.c

index b99dcd2e98fa32e0415d0915f6ac60677e9fccdb..0d8520f9af904701279875b4ec353e03fd60fc1b 100644 (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
index 34b3e2643c7b4b80bd591204912ce89aa23ee3c0..d42b34ae89a57584281de59f54048e018546f328 100644 (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 *));
index c3f3f2f6d03a5f5f50a0afa3328cab3baf52abdb..f908fcf9944346cf9dcf763889942539850a55f5 100644 (file)
--- 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);
index 3ef56ab9a8346f97818a173461a80d895d370984..9790ba9005978927e9badf2d563efc96dc88fcb3 100644 (file)
--- 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!");
                }
index a433d22ea0df4d5b674727bc9a83ab7ce7fdca1b..0b58b45cc39cf9c07fa893c079b25c1679c2aecd 100644 (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;