]> granicus.if.org Git - nethack/commitdiff
monsters defending against slime (trunk only)
authornethack.rankin <nethack.rankin>
Mon, 30 Apr 2007 02:47:11 +0000 (02:47 +0000)
committernethack.rankin <nethack.rankin>
Mon, 30 Apr 2007 02:47:11 +0000 (02:47 +0000)
     Like their use of lizard corpses to defeat being turned into stone,
let monsters use wands of fire, fire horns, and scrolls of fire to try to
defeat being turned into slime.  If the scroll is read while confused, it
won't succeed.  Otherwise, monsters who don't resist fire will take some
damage in the process and might end up killing themselves (although with
the testing I've gone I've yet to see that happen--I guess that means
that handling for dying-in-the-process hasn't been adequately tested...).

     So far, they don't know how to jump onto adjacent fire traps, nor
will fire breathing monsters breath at themselves.  I don't know whether
I'll get around to tackling either of those.

doc/fixes35.0
include/extern.h
src/explode.c
src/mhitm.c
src/muse.c
src/uhitm.c
src/worn.c
src/zap.c

index 45c359d4d058f76abaed076db3220dc0f42dccbf..0e2a1b5ffbbedf738d3be9d9cde0492561b37431 100644 (file)
@@ -325,6 +325,7 @@ scroll of taming/spell of charm monster now gives some feedback
 doppelgangers can take on the shape of alternate roles' quest guardians
 pile_limit option to control when to switch to "there are objects here"
        vs listing objects on floor when hero goes over objects while moving
+some monsters will use fire to prevent selves being turned into green slime
 
 
 Platform- and/or Interface-Specific New Features
index 73ff153a8bf88fffb52e56b042aebd37b75bba29..9c7bb3a3de3656aec25745130aae6b99440b4b8b 100644 (file)
@@ -1471,6 +1471,7 @@ E boolean FDECL(searches_for_item, (struct monst *,struct obj *));
 E boolean FDECL(mon_reflects, (struct monst *,const char *));
 E boolean FDECL(ureflects, (const char *,const char *));
 E boolean FDECL(munstone, (struct monst *,BOOLEAN_P));
+E boolean FDECL(munslime, (struct monst *,BOOLEAN_P));
 
 /* ### music.c ### */
 
@@ -2631,6 +2632,7 @@ E void FDECL(miss, (const char *,struct monst *));
 E struct monst *FDECL(bhit, (int,int,int,int,int (*)(MONST_P,OBJ_P),
                             int (*)(OBJ_P,OBJ_P),struct obj **));
 E struct monst *FDECL(boomhit, (struct obj *,int,int));
+E int FDECL(zhitm, (struct monst *,int,int,struct obj **));
 E int FDECL(burn_floor_paper, (int,int,BOOLEAN_P,BOOLEAN_P));
 E void FDECL(buzz, (int,int,XCHAR_P,XCHAR_P,int,int));
 E void FDECL(melt_ice, (XCHAR_P,XCHAR_P,const char *));
index 771be3944f41d97288334d9bb0d21f6fabd7a0c9..bac28bfbbaae965a289469cabffaddd4e3b36294 100644 (file)
@@ -1,4 +1,4 @@
-/*     SCCS Id: @(#)explode.c  3.5     2005/11/12      */
+/*     SCCS Id: @(#)explode.c  3.5     2007/04/27      */
 /*     Copyright (C) 1990 by Ken Arromdee */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -40,7 +40,7 @@ int expltype;
        int uhurt = 0; /* 0=unhurt, 1=items damaged, 2=you and items damaged */
        const char *str = (const char *) 0;
        int idamres, idamnonres;
-       struct monst *mtmp;
+       struct monst *mtmp, *mdef = 0;
        uchar adtyp;
        int explmask[3][3];
                /* 0=normal explosion, 1=do shieldeff, 2=do nothing */
@@ -66,6 +66,12 @@ int expltype;
                        default:  break;
                }
        }
+       /* muse_unslime: SCR_FIRE */
+       if (expltype < 0) {
+           /* hero gets credit/blame for killing this monster, not others */
+           mdef = m_at(x, y);
+           expltype = -expltype;
+       }
 
        if (olet == MON_EXPLODE) {
            str = killer.name;
@@ -326,16 +332,21 @@ int expltype;
                        mtmp->mhp -= (idamres + idamnonres);
                }
                if (mtmp->mhp <= 0) {
-                       /* KMH -- Don't blame the player for pets killing gas spores */
-                       if (!context.mon_moving) killed(mtmp);
-                       else monkilled(mtmp, "", (int)adtyp);
-               } else if (!context.mon_moving) setmangry(mtmp);
+                   if (mdef ? (mtmp == mdef) : !context.mon_moving)
+                       killed(mtmp);
+                   else
+                       monkilled(mtmp, "", (int)adtyp);
+               } else if (!context.mon_moving) {
+                   /* all affected monsters, even if mdef is set */
+                   setmangry(mtmp);
+               }
        }
 
        /* Do your injury last */
        if (uhurt) {
-               if ((type >= 0 || adtyp == AD_PHYS) &&  /* gas spores */
-                               flags.verbose && olet != SCROLL_CLASS) {
+               /* give message for any monster-induced explosion
+                  or player-induced one other than scroll of fire */
+               if (flags.verbose && (type < 0 || olet != SCROLL_CLASS)) {
                    if (do_hallu) {     /* (see explanation above) */
                        do {
                            Sprintf(hallu_buf, "%s explosion",
index c415f0a866a4631a11a7bbb411ade7b1c6a75bab..e8f88b499ff5e2e7c90faad0f360291880d07fe5 100644 (file)
@@ -1162,10 +1162,16 @@ mdamagem(magr, mdef, mattk)
            case AD_SLIM:
                if (cancelled) break;   /* physical damage only */
                if (!rn2(4) && !slimeproof(pd)) {
-                   if (newcham(mdef, &mons[PM_GREEN_SLIME], FALSE, vis))
-                       pd = mdef->data;
-                   mdef->mstrategy &= ~STRAT_WAITFORU;
-                   res = MM_HIT;
+                   if (!munslime(mdef, FALSE) && mdef->mhp > 0) {
+                       if (newcham(mdef, &mons[PM_GREEN_SLIME], FALSE, vis))
+                           pd = mdef->data;
+                       mdef->mstrategy &= ~STRAT_WAITFORU;
+                       res = MM_HIT;
+                   }
+                   /* munslime attempt could have been fatal,
+                      potentially to multiple monsters (SCR_FIRE) */
+                   if (magr->mhp < 1) res |= MM_AGR_DIED;
+                   if (mdef->mhp < 1) res |= MM_DEF_DIED;
                    tmp = 0;
                }
                break;
index 15462640f57ba2e8e09c04322b92f43ab6758e69..382a3163c68627849529cdfb1c69fb61d18c991a 100644 (file)
@@ -32,6 +32,9 @@ STATIC_DCL void FDECL(mon_consume_unstone, (struct monst *,struct obj *,
        BOOLEAN_P,BOOLEAN_P));
 STATIC_DCL boolean FDECL(cures_stoning, (struct monst *,struct obj *,BOOLEAN_P));
 STATIC_DCL boolean FDECL(mcould_eat_tin, (struct monst *));
+STATIC_DCL boolean FDECL(muse_unslime, (struct monst *,struct obj *,BOOLEAN_P));
+STATIC_DCL int FDECL(cures_sliming, (struct monst *,struct obj *));
+STATIC_DCL boolean FDECL(green_mon, (struct monst *));
 
 static struct musable {
        struct obj *offensive;
@@ -2014,8 +2017,8 @@ struct obj *obj;
                return TRUE;
            break;
        case SCROLL_CLASS:
-           if (typ == SCR_TELEPORTATION || typ == SCR_CREATE_MONSTER
-                   || typ == SCR_EARTH)
+           if (typ == SCR_TELEPORTATION || typ == SCR_CREATE_MONSTER ||
+                   typ == SCR_EARTH || typ == SCR_FIRE)
                return TRUE;
            break;
        case AMULET_CLASS:
@@ -2093,7 +2096,7 @@ const char *str;
 }
 
 boolean
-ureflects (fmt, str)
+ureflects(fmt, str)
 const char *fmt, *str;
 {
        /* Check from outermost to innermost objects */
@@ -2126,7 +2129,6 @@ const char *fmt, *str;
        return FALSE;
 }
 
-
 /* TRUE if the monster ate something */
 boolean
 munstone(mon, by_you)
@@ -2138,6 +2140,7 @@ boolean by_you;
 
        if (resists_ston(mon)) return FALSE;
        if (mon->meating || !mon->mcanmove || mon->msleeping) return FALSE;
+       mon->mstrategy &= ~STRAT_WAITFORU;
 
        tinok = mcould_eat_tin(mon);
        for (obj = mon->minvent; obj; obj = obj->nobj) {
@@ -2214,7 +2217,9 @@ boolean stoning;
        edog->hungrytime += nutrit;
        mon->mconf = 0;
     }
-    mon->mlstmv = monstermoves; /* it takes a turn */
+    /* use up monster's next move */
+    mon->movement -= NORMAL_SPEED;
+    mon->mlstmv = monstermoves;
 }
 
 /* decide whether obj can cure petrification; also used when picking up */
@@ -2229,9 +2234,7 @@ boolean tinok;
     /* corpse, or tin that mon can open */
     return (boolean)(obj->corpsenm == PM_LIZARD ||
                (acidic(&mons[obj->corpsenm]) &&
-                 /* flaming() can use green slime to unstone;
-                    noncorporeal() could too but doesn't need to */
-                 (obj->corpsenm != PM_GREEN_SLIME || flaming(mon->data))));
+                 (obj->corpsenm != PM_GREEN_SLIME || slimeproof(mon->data))));
 }
 
 STATIC_OVL boolean
@@ -2261,4 +2264,135 @@ struct monst *mon;
        return FALSE;
 }
 
+/* TRUE if monster does something to avoid turning into green slime */
+boolean
+munslime(mon, by_you)
+struct monst *mon;
+boolean by_you;
+{
+    struct obj *obj;
+
+    /*
+     * muse_unslime() gives "mon starts turning green", "mon zaps
+     * itself with a wand of fire", and "mon's slime burns away"
+     * messages.  Monsters who don't get any chance at that just have
+     * (via our caller) newcham()'s "mon turns into slime" feedback.
+     */
+
+    if (slimeproof(mon->data)) return FALSE;
+    if (mon->meating || !mon->mcanmove || mon->msleeping) return FALSE;
+    mon->mstrategy &= ~STRAT_WAITFORU;
+
+    for (obj = mon->minvent; obj; obj = obj->nobj)
+       if (cures_sliming(mon, obj))
+           return muse_unslime(mon, obj, by_you);
+
+    /* TODO: check for and move onto an adjacent fire trap */
+    /* TODO: monster with flame attack should use it on self */
+
+    return FALSE;
+}
+
+/* mon uses an item--selected by caller--to burn away incipient slime */
+STATIC_OVL boolean
+muse_unslime(mon, obj, by_you)
+struct monst *mon;
+struct obj *obj;
+boolean by_you;        /* true: if mon kills itself, hero gets credit/blame */
+{
+    struct obj *odummyp;
+    int otyp = obj->otyp, dmg;
+    boolean vis = canseemon(mon), res = TRUE;
+
+    if (vis)
+       pline("%s starts turning %s.", Monnam(mon),
+             green_mon(mon) ? "into ooze" : hcolor(NH_GREEN));
+    /* -4 => sliming, causes quiet loss of enhanced speed */
+    mon_adjust_speed(mon, -4, (struct obj *)0);
+
+    if (otyp == SCR_FIRE) {
+       mreadmsg(mon, obj);
+       if (mon->mconf) {
+           if (cansee(mon->mx, mon->my))
+               pline("Oh, what a pretty fire!");
+           if (vis && !objects[otyp].oc_name_known && !objects[otyp].oc_uname)
+               docall(obj);
+           m_useup(mon, obj);  /* after docall() */
+           vis = FALSE;        /* skip makeknown() below */
+           res = FALSE;        /* failed to cure sliming */
+       } else {
+           m_useup(mon, obj);  /* before explode() */
+           dmg = (2 * (rn1(3, 3) + 2 * bcsign(obj)) + 1) / 3;
+           /* -11 => monster's fireball */
+           explode(mon->mx, mon->my, -11, dmg, SCROLL_CLASS,
+                   /* by_you: override -11 for mon but not others */
+                   by_you ? -EXPL_FIERY : EXPL_FIERY);
+       }
+    } else {   /* wand/horn of fire w/ positive charge count */
+       mzapmsg(mon, obj, TRUE);
+       obj->spe--;
+       /* -1 => monster's wand of fire; 2 => # of damage dice */
+       (void)zhitm(mon, by_you ? 1 : -1, 2, &odummyp);
+    }
+
+    if (vis) {
+       if (res && mon->mhp > 0)
+           pline("%s slime is burned away!", s_suffix(Monnam(mon)));
+       makeknown(otyp);
+    }
+    /* use up monster's next move */
+    mon->movement -= NORMAL_SPEED;
+    mon->mlstmv = monstermoves;
+    return res;
+}
+
+/* decide whether obj can be used to cure green slime */
+STATIC_OVL int
+cures_sliming(mon, obj)
+struct monst *mon;
+struct obj *obj;
+{
+    /* scroll of fire, non-empty wand or horn of fire */
+    if (obj->otyp == SCR_FIRE)
+       return (haseyes(mon->data) && mon->mcansee);
+    /* hero doesn't need hands or even limbs to zap, so mon doesn't either */
+    return ((obj->otyp == WAN_FIRE || obj->otyp == FIRE_HORN) && obj->spe > 0);
+}
+
+/* TRUE if monster appears to be green; for active TEXTCOLOR, we go by
+   the display color, otherwise we just pick things that seem plausibly
+   green (which doesn't necessarily match the TEXTCOLOR categorization) */
+STATIC_OVL boolean
+green_mon(mon)
+struct monst *mon;
+{
+    struct permonst *ptr = mon->data;
+
+    if (Hallucination) return FALSE;
+#ifdef TEXTCOLOR
+    if (iflags.use_color)
+       return (ptr->mcolor == CLR_GREEN || ptr->mcolor == CLR_BRIGHT_GREEN);
+#endif
+    /* approximation */
+    if (strstri(ptr->mname, "green")) return TRUE;
+    switch (monsndx(ptr)) {
+    case PM_FOREST_CENTAUR:
+    case PM_GARTER_SNAKE:
+    case PM_GECKO:
+    case PM_GREMLIN:
+    case PM_HOMUNCULUS:
+    case PM_JUIBLEX:
+    case PM_LEPRECHAUN:
+    case PM_LICHEN:
+    case PM_LIZARD:
+    case PM_WOOD_NYMPH:
+       return TRUE;
+    default:
+       if (is_elf(ptr) && !is_prince(ptr) && !is_lord(ptr) &&
+           ptr != &mons[PM_GREY_ELF]) return TRUE;
+       break;
+    }
+    return FALSE;
+}
+
 /*muse.c*/
index 1e80df4153722315670850270ad6e9de54e8b718..d804cd187f7d3f68bd66af0c2e6c0316bab67475 100644 (file)
@@ -1652,9 +1652,15 @@ register struct attack *mattk;
            case AD_SLIM:
                if (negated) break;     /* physical damage only */
                if (!rn2(4) && !slimeproof(pd)) {
-                   You("turn %s into slime.", mon_nam(mdef));
-                   if (newcham(mdef, &mons[PM_GREEN_SLIME], FALSE, FALSE))
-                       pd = mdef->data;
+                   if (!munslime(mdef, TRUE) && mdef->mhp > 0) {
+                       /* this assumes newcham() won't fail; since hero has
+                          a slime attack, green slimes haven't been geno'd */
+                       You("turn %s into slime.", mon_nam(mdef));
+                       if (newcham(mdef, &mons[PM_GREEN_SLIME], FALSE, FALSE))
+                           pd = mdef->data;
+                   }
+                   /* munslime attempt could have been fatal */
+                   if (mdef->mhp < 1) return 2; /* skip death message */
                    tmp = 0;
                }
                break;
index 67404cd0e6dac1e956366d2d3cc3e2636ce1c151..d1be7c577a524d4f1c0180ace2b3be42d27003ff 100644 (file)
@@ -236,6 +236,10 @@ struct obj *obj;   /* item to make known if effect can be seen */
        if (mon->permspeed == MFAST) mon->permspeed = 0;
        petrify = TRUE;
        break;
+     case -4:                  /* green slime */
+       if (mon->permspeed == MFAST) mon->permspeed = 0;
+       give_msg = FALSE;
+       break;
     }
 
     for (otmp = mon->minvent; otmp; otmp = otmp->nobj)
index eeee7713ee42c72e997629e81186490600d0825a..f4fcbc5a12b49d57a72340a47a208b1d24eaaa68 100644 (file)
--- a/src/zap.c
+++ b/src/zap.c
@@ -23,7 +23,6 @@ STATIC_DCL void FDECL(polyuse, (struct obj *,int,int));
 STATIC_DCL void FDECL(create_polymon, (struct obj *,int));
 STATIC_DCL int FDECL(stone_to_flesh_obj, (struct obj *));
 STATIC_DCL boolean FDECL(zap_updown, (struct obj *));
-STATIC_DCL int FDECL(zhitm, (struct monst *,int,int,struct obj **));
 STATIC_DCL void FDECL(zhitu, (int,int,const char *,XCHAR_P,XCHAR_P));
 STATIC_DCL void FDECL(revive_egg, (struct obj *));
 #ifdef STEED
@@ -3202,7 +3201,8 @@ int dx, dy;
        return (struct monst *)0;
 }
 
-STATIC_OVL int
+/* used by buzz(); also used by munslime(muse.c) */
+int
 zhitm(mon, type, nd, ootmp)                    /* returns damage to mon */
 register struct monst *mon;
 register int type, nd;