diff --git a/doc/fixes35.0 b/doc/fixes35.0 index 45c359d4d..0e2a1b5ff 100644 --- a/doc/fixes35.0 +++ b/doc/fixes35.0 @@ -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 diff --git a/include/extern.h b/include/extern.h index 73ff153a8..9c7bb3a3d 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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 *)); diff --git a/src/explode.c b/src/explode.c index 771be3944..bac28bfbb 100644 --- a/src/explode.c +++ b/src/explode.c @@ -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", diff --git a/src/mhitm.c b/src/mhitm.c index c415f0a86..e8f88b499 100644 --- a/src/mhitm.c +++ b/src/mhitm.c @@ -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; diff --git a/src/muse.c b/src/muse.c index 15462640f..382a3163c 100644 --- a/src/muse.c +++ b/src/muse.c @@ -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*/ diff --git a/src/uhitm.c b/src/uhitm.c index 1e80df415..d804cd187 100644 --- a/src/uhitm.c +++ b/src/uhitm.c @@ -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; diff --git a/src/worn.c b/src/worn.c index 67404cd0e..d1be7c577 100644 --- a/src/worn.c +++ b/src/worn.c @@ -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) diff --git a/src/zap.c b/src/zap.c index eeee7713e..f4fcbc5a1 100644 --- 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;