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
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 ### */
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 *));
-/* 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. */
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 */
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;
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",
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;
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;
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:
}
boolean
-ureflects (fmt, str)
+ureflects(fmt, str)
const char *fmt, *str;
{
/* Check from outermost to innermost objects */
return FALSE;
}
-
/* TRUE if the monster ate something */
boolean
munstone(mon, 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) {
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 */
/* 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
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*/
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;
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)
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
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;