Logo Search packages:      
Sourcecode: falconseye version File versions  Download package

muse.c

/*    SCCS Id: @(#)muse.c     3.3   2000/06/02  */
/*    Copyright (C) 1990 by Ken Arromdee                 */
/* NetHack may be freely redistributed.  See license for details.  */

/*
 * Monster item usage routines.
 */

#include "hack.h"
#include "edog.h"

extern const int monstr[];

boolean m_using = FALSE;

/* Let monsters use magic items.  Arbitrary assumptions: Monsters only use
 * scrolls when they can see, monsters know when wands have 0 charges, monsters
 * cannot recognize if items are cursed are not, monsters which are confused
 * don't know not to read scrolls, etc....
 */

STATIC_DCL int FDECL(precheck, (struct monst *,struct obj *));
STATIC_DCL void FDECL(mzapmsg, (struct monst *,struct obj *,BOOLEAN_P));
STATIC_DCL void FDECL(mreadmsg, (struct monst *,struct obj *));
STATIC_DCL void FDECL(mquaffmsg, (struct monst *,struct obj *));
STATIC_PTR int FDECL(mbhitm, (struct monst *,struct obj *));
STATIC_DCL void FDECL(mbhit,
      (struct monst *,int,int FDECL((*),(MONST_P,OBJ_P)),
      int FDECL((*),(OBJ_P,OBJ_P)),struct obj *));
STATIC_DCL void FDECL(you_aggravate, (struct monst *));

static struct musable {
      struct obj *offensive;
      struct obj *defensive;
      struct obj *misc;
      int has_offense, has_defense, has_misc;
      /* =0, no capability; otherwise, different numbers.
       * If it's an object, the object is also set (it's 0 otherwise).
       */
} m;
static int trapx, trapy;
static boolean zap_oseen;
      /* for wands which use mbhitm and are zapped at players.  We usually
       * want an oseen local to the function, but this is impossible since the
       * function mbhitm has to be compatible with the normal zap routines,
       * and those routines don't remember who zapped the wand.
       */

/* Any preliminary checks which may result in the monster being unable to use
 * the item.  Returns 0 if nothing happened, 2 if the monster can't do anything
 * (i.e. it teleported) and 1 if it's dead.
 */
STATIC_OVL int
precheck(mon, obj)
struct monst *mon;
struct obj *obj;
{
      boolean vis;

      if (!obj) return 0;
      vis = cansee(mon->mx, mon->my);

      if (obj->oclass == POTION_CLASS) {
          coord cc;
          static const char *empty = "The potion turns out to be empty.";
          const char *potion_descr;
          struct monst *mtmp;
#define POTION_OCCUPANT_CHANCE(n) (13 + 2*(n))  /* also in potion.c */

          potion_descr = OBJ_DESCR(objects[obj->otyp]);
          if (potion_descr && !strcmp(potion_descr, "milky")) {
              if ( flags.ghost_count < MAXMONNO &&
                !rn2(POTION_OCCUPANT_CHANCE(flags.ghost_count))) {
                if (!enexto(&cc, mon->mx, mon->my, &mons[PM_GHOST])) return 0;
                mquaffmsg(mon, obj);
                m_useup(mon, obj);
                mtmp = makemon(&mons[PM_GHOST], cc.x, cc.y, NO_MM_FLAGS);
                if (!mtmp) {
                  if (vis) pline(empty);
                } else {
                  if (vis) {
                      pline("As %s opens the bottle, an enormous %s emerges!",
                         mon_nam(mon),
                         Hallucination ? rndmonnam() : (const char *)"ghost");
                      pline("%s is frightened to death, and unable to move.",
                            Monnam(mon));
                  }
                  mon->mcanmove = 0;
                  mon->mfrozen = 3;
                }
                return 2;
            }
          }
          if (potion_descr && !strcmp(potion_descr, "smoky") &&
                flags.djinni_count < MAXMONNO &&
                !rn2(POTION_OCCUPANT_CHANCE(flags.djinni_count))) {
            if (!enexto(&cc, mon->mx, mon->my, &mons[PM_DJINNI])) return 0;
            mquaffmsg(mon, obj);
            m_useup(mon, obj);
            mtmp = makemon(&mons[PM_DJINNI], cc.x, cc.y, NO_MM_FLAGS);
            if (!mtmp) {
                if (vis) pline(empty);
            } else {
                if (vis)
                  pline("In a cloud of smoke, %s emerges!",
                                          a_monnam(mtmp));
                pline("%s speaks.", vis ? Monnam(mtmp) : Something);
            /* I suspect few players will be upset that monsters */
            /* can't wish for wands of death here.... */
                if (rn2(2)) {
                  verbalize("You freed me!");
                  mtmp->mpeaceful = 1;
                  set_malign(mtmp);
                } else {
                  verbalize("It is about time.");
                  if (vis) pline("%s vanishes.", Monnam(mtmp));
                  mongone(mtmp);
                }
            }
            return 2;
          }
      }
      if (obj->oclass == WAND_CLASS && obj->cursed && !rn2(100)) {
          int dam = d(obj->spe+2, 6);

          if (flags.soundok) {
            if (vis) pline("%s zaps %s, which suddenly explodes!",
                  Monnam(mon), an(xname(obj)));
            else You_hear("a zap and an explosion in the distance.");
          }
          m_useup(mon, obj);
          if (mon->mhp <= dam) {
            monkilled(mon, "", AD_RBRE);
            return 1;
          }
          else mon->mhp -= dam;
          m.has_defense = m.has_offense = m.has_misc = 0;
          /* Only one needed to be set to 0 but the others are harmless */
      }
      return 0;
}

STATIC_OVL void
mzapmsg(mtmp, otmp, self)
struct monst *mtmp;
struct obj *otmp;
boolean self;
{
      if (!canseemon(mtmp)) {
            if (flags.soundok)
                  You_hear("a %s zap.",
                              (distu(mtmp->mx,mtmp->my) <= (BOLT_LIM+1)*(BOLT_LIM+1)) ?
                              "nearby" : "distant");
      } else if (self)
            pline("%s zaps %sself with %s!",
                  Monnam(mtmp), him[pronoun_gender(mtmp)], doname(otmp));
      else {
            pline("%s zaps %s!", Monnam(mtmp), an(xname(otmp)));
            stop_occupation();
      }
}

STATIC_OVL void
mreadmsg(mtmp, otmp)
struct monst *mtmp;
struct obj *otmp;
{
      boolean vismon = canseemon(mtmp);
      char onambuf[BUFSZ];
      short saverole;
      unsigned savebknown;

      if (!vismon && !flags.soundok)
          return;       /* no feedback */

      otmp->dknown = 1;  /* seeing or hearing it read reveals its label */
      /* shouldn't be able to hear curse/bless status of unseen scrolls;
         for priest characters, bknown will always be set during naming */
      savebknown = otmp->bknown;
      saverole = Role_switch;
      if (!vismon) {
          otmp->bknown = 0;
          if (Role_if(PM_PRIEST)) Role_switch = 0;
      }
      Strcpy(onambuf, singular(otmp, doname));
      Role_switch = saverole;
      otmp->bknown = savebknown;

      if (vismon)
          pline("%s reads %s!", Monnam(mtmp), onambuf);
      else
          You_hear("%s reading %s.",
            x_monnam(mtmp, ARTICLE_A, (char *)0,
                (SUPPRESS_IT|SUPPRESS_INVISIBLE|SUPPRESS_SADDLE), FALSE),
            onambuf);

      if (mtmp->mconf)
          pline("Being confused, %s mispronounces the magic words...",
              vismon ? mon_nam(mtmp) : he[pronoun_gender(mtmp)]);
}

STATIC_OVL void
mquaffmsg(mtmp, otmp)
struct monst *mtmp;
struct obj *otmp;
{
      if (canseemon(mtmp)) {
            otmp->dknown = 1;
            pline("%s drinks %s!", Monnam(mtmp), singular(otmp, doname));
      } else
            if (flags.soundok)
                  You_hear("a chugging sound.");
}

/* Defines for various types of stuff.  The order in which monsters prefer
 * to use them is determined by the order of the code logic, not the
 * numerical order in which they are defined.
 */
#define MUSE_SCR_TELEPORTATION 1
#define MUSE_WAN_TELEPORTATION_SELF 2
#define MUSE_POT_HEALING 3
#define MUSE_POT_EXTRA_HEALING 4
#define MUSE_WAN_DIGGING 5
#define MUSE_TRAPDOOR 6
#define MUSE_TELEPORT_TRAP 7
#define MUSE_UPSTAIRS 8
#define MUSE_DOWNSTAIRS 9
#define MUSE_WAN_CREATE_MONSTER 10
#define MUSE_SCR_CREATE_MONSTER 11
#define MUSE_UP_LADDER 12
#define MUSE_DN_LADDER 13
#define MUSE_SSTAIRS 14
#define MUSE_WAN_TELEPORTATION 15
#define MUSE_BUGLE 16
#define MUSE_UNICORN_HORN 17
#define MUSE_POT_FULL_HEALING 18
/*
#define MUSE_INNATE_TPT 9999
 * We cannot use this.  Since monsters get unlimited teleportation, if they
 * were allowed to teleport at will you could never catch them.  Instead,
 * assume they only teleport at random times, despite the inconsistency that if
 * you polymorph into one you teleport at will.
 */

/* Select a defensive item/action for a monster.  Returns TRUE iff one is
 * found.
 */
boolean
find_defensive(mtmp)
struct monst *mtmp;
{
      register struct obj *obj = 0;
      struct trap *t;
      int x=mtmp->mx, y=mtmp->my;
      boolean stuck = (mtmp == u.ustuck);
      boolean immobile = (mtmp->data->mmove == 0);
      int fraction;

      if (is_animal(mtmp->data) || mindless(mtmp->data))
            return FALSE;
      if(dist2(x, y, mtmp->mux, mtmp->muy) > 25)
            return FALSE;
      if (u.uswallow && stuck) return FALSE;

      m.defensive = (struct obj *)0;
      m.has_defense = 0;

      /* since unicorn horns don't get used up, the monster would look
       * silly trying to use the same cursed horn round after round
       */
      if (mtmp->mconf || mtmp->mstun || !mtmp->mcansee) {
          if (!is_unicorn(mtmp->data) && !nohands(mtmp->data)) {
            for(obj = mtmp->minvent; obj; obj = obj->nobj)
                if (obj->otyp == UNICORN_HORN && !obj->cursed)
                  break;
          }
          if (obj || is_unicorn(mtmp->data)) {
            m.defensive = obj;
            m.has_defense = MUSE_UNICORN_HORN;
            return TRUE;
          }
      }

      /* It so happens there are two unrelated cases when we might want to
       * check specifically for healing alone.  The first is when the monster
       * is blind (healing cures blindness).  The second is when the monster
       * is peaceful; then we don't want to flee the player, and by
       * coincidence healing is all there is that doesn't involve fleeing.
       * These would be hard to combine because of the control flow.
       * Pestilence won't use healing even when blind.
       */
      if (!mtmp->mcansee && !nohands(mtmp->data) &&
            mtmp->data != &mons[PM_PESTILENCE]) {
          if ((obj = m_carrying(mtmp, POT_FULL_HEALING)) != 0) {
            m.defensive = obj;
            m.has_defense = MUSE_POT_FULL_HEALING;
            return TRUE;
          }
          if ((obj = m_carrying(mtmp, POT_EXTRA_HEALING)) != 0) {
            m.defensive = obj;
            m.has_defense = MUSE_POT_EXTRA_HEALING;
            return TRUE;
          }
          if ((obj = m_carrying(mtmp, POT_HEALING)) != 0) {
            m.defensive = obj;
            m.has_defense = MUSE_POT_HEALING;
            return TRUE;
          }
      }

      fraction = u.ulevel < 10 ? 5 : u.ulevel < 14 ? 4 : 3;
      if(mtmp->mhp >= mtmp->mhpmax ||
                  (mtmp->mhp >= 10 && mtmp->mhp*fraction >= mtmp->mhpmax))
            return FALSE;

      if (mtmp->mpeaceful) {
          if (!nohands(mtmp->data)) {
            if ((obj = m_carrying(mtmp, POT_FULL_HEALING)) != 0) {
                m.defensive = obj;
                m.has_defense = MUSE_POT_FULL_HEALING;
                return TRUE;
            }
            if ((obj = m_carrying(mtmp, POT_EXTRA_HEALING)) != 0) {
                m.defensive = obj;
                m.has_defense = MUSE_POT_EXTRA_HEALING;
                return TRUE;
            }
            if ((obj = m_carrying(mtmp, POT_HEALING)) != 0) {
                m.defensive = obj;
                m.has_defense = MUSE_POT_HEALING;
                return TRUE;
            }
          }
          return FALSE;
      }

      if (levl[x][y].typ == STAIRS && !stuck && !immobile) {
            if (x == xdnstair && y == ydnstair && !is_floater(mtmp->data))
                  m.has_defense = MUSE_DOWNSTAIRS;
            if (x == xupstair && y == yupstair && ledger_no(&u.uz) != 1)
      /* Unfair to let the monsters leave the dungeon with the Amulet */
      /* (or go to the endlevel since you also need it, to get there) */
                  m.has_defense = MUSE_UPSTAIRS;
      } else if (levl[x][y].typ == LADDER && !stuck && !immobile) {
            if (x == xupladder && y == yupladder)
                  m.has_defense = MUSE_UP_LADDER;
            if (x == xdnladder && y == ydnladder && !is_floater(mtmp->data))
                  m.has_defense = MUSE_DN_LADDER;
      } else if (sstairs.sx && sstairs.sx == x && sstairs.sy == y) {
            m.has_defense = MUSE_SSTAIRS;
      } else if (!stuck && !immobile) {
      /* Note: trap doors take precedence over teleport traps. */
            int xx, yy;

            for(xx = x-1; xx <= x+1; xx++) for(yy = y-1; yy <= y+1; yy++)
            if (isok(xx,yy))
            if (xx != u.ux && yy != u.uy)
            if (mtmp->data != &mons[PM_GRID_BUG] || xx == x || yy == y)
            if ((xx==x && yy==y) || !level.monsters[xx][yy])
            if ((t = t_at(xx,yy)) != 0)
            if ((verysmall(mtmp->data) || throws_rocks(mtmp->data) ||
                 passes_walls(mtmp->data)) || !sobj_at(BOULDER, xx, yy))
            if (!onscary(xx,yy,mtmp)) {
                  if ((t->ttyp == TRAPDOOR || t->ttyp == HOLE)
                        && !is_floater(mtmp->data)
                        && !mtmp->isshk && !mtmp->isgd
                        && !mtmp->ispriest
                        && Can_fall_thru(&u.uz)
                                    ) {
                        trapx = xx;
                        trapy = yy;
                        m.has_defense = MUSE_TRAPDOOR;
                  } else if (t->ttyp == TELEP_TRAP && m.has_defense != MUSE_TRAPDOOR) {
                        trapx = xx;
                        trapy = yy;
                        m.has_defense = MUSE_TELEPORT_TRAP;
                  }
            }
      }

      if (nohands(mtmp->data))      /* can't use objects */
            goto botm;

      if (is_mercenary(mtmp->data) && (obj = m_carrying(mtmp, BUGLE))) {
            int xx, yy;
            struct monst *mon;

            /* Distance is arbitrary.  What we really want to do is
             * have the soldier play the bugle when it sees or
             * remembers soldiers nearby...
             */
            for(xx = x-3; xx <= x+3; xx++) for(yy = y-3; yy <= y+3; yy++)
            if (isok(xx,yy))
            if ((mon = m_at(xx,yy)) && is_mercenary(mon->data) &&
                        mon->data != &mons[PM_GUARD] &&
                        (mon->msleeping || (!mon->mcanmove))) {
                  m.defensive = obj;
                  m.has_defense = MUSE_BUGLE;
            }
      }

      /* use immediate physical escape prior to attempting magic */
      if (m.has_defense)    /* stairs, trap door or tele-trap, bugle alert */
            goto botm;

      /* kludge to cut down on trap destruction (particularly portals) */
      t = t_at(x,y);
      if (t && (t->ttyp == PIT || t->ttyp == SPIKED_PIT ||
              t->ttyp == WEB || t->ttyp == BEAR_TRAP))
            t = 0;            /* ok for monster to dig here */

#define nomore(x) if(m.has_defense==x) continue;
      for (obj = mtmp->minvent; obj; obj = obj->nobj) {
            /* don't always use the same selection pattern */
            if (m.has_defense && !rn2(3)) break;

            /* nomore(MUSE_WAN_DIGGING); */
            if (m.has_defense == MUSE_WAN_DIGGING) break;
            if (obj->otyp == WAN_DIGGING && obj->spe > 0 && !stuck && !t
                && !mtmp->isshk && !mtmp->isgd && !mtmp->ispriest
                && !is_floater(mtmp->data)
                /* digging wouldn't be effective; assume they know that */
                && !(levl[x][y].wall_info & W_NONDIGGABLE)
                && !(Is_botlevel(&u.uz) || In_endgame(&u.uz))
                && !(is_ice(x,y) || is_pool(x,y) || is_lava(x,y))) {
                  m.defensive = obj;
                  m.has_defense = MUSE_WAN_DIGGING;
            }
            nomore(MUSE_WAN_TELEPORTATION_SELF);
            nomore(MUSE_WAN_TELEPORTATION);
            if(obj->otyp == WAN_TELEPORTATION && obj->spe > 0) {
                  m.defensive = obj;
                  m.has_defense = (mon_has_amulet(mtmp))
                        ? MUSE_WAN_TELEPORTATION
                        : MUSE_WAN_TELEPORTATION_SELF;
            }
            nomore(MUSE_SCR_TELEPORTATION);
            if(obj->otyp == SCR_TELEPORTATION && mtmp->mcansee
               && haseyes(mtmp->data)
               && (!obj->cursed ||
                   (!(mtmp->isshk && inhishop(mtmp))
                      && !mtmp->isgd && !mtmp->ispriest))) {
                  m.defensive = obj;
                  m.has_defense = MUSE_SCR_TELEPORTATION;
            }

          if (mtmp->data != &mons[PM_PESTILENCE]) {
            nomore(MUSE_POT_FULL_HEALING);
            if(obj->otyp == POT_FULL_HEALING) {
                  m.defensive = obj;
                  m.has_defense = MUSE_POT_FULL_HEALING;
            }
            nomore(MUSE_POT_EXTRA_HEALING);
            if(obj->otyp == POT_EXTRA_HEALING) {
                  m.defensive = obj;
                  m.has_defense = MUSE_POT_EXTRA_HEALING;
            }
            nomore(MUSE_WAN_CREATE_MONSTER);
            if(obj->otyp == WAN_CREATE_MONSTER && obj->spe > 0) {
                  m.defensive = obj;
                  m.has_defense = MUSE_WAN_CREATE_MONSTER;
            }
            nomore(MUSE_POT_HEALING);
            if(obj->otyp == POT_HEALING) {
                  m.defensive = obj;
                  m.has_defense = MUSE_POT_HEALING;
            }
          } else {      /* Pestilence */
            nomore(MUSE_POT_FULL_HEALING);
            if (obj->otyp == POT_SICKNESS) {
                  m.defensive = obj;
                  m.has_defense = MUSE_POT_FULL_HEALING;
            }
            nomore(MUSE_WAN_CREATE_MONSTER);
            if (obj->otyp == WAN_CREATE_MONSTER && obj->spe > 0) {
                  m.defensive = obj;
                  m.has_defense = MUSE_WAN_CREATE_MONSTER;
            }
          }
            nomore(MUSE_SCR_CREATE_MONSTER);
            if(obj->otyp == SCR_CREATE_MONSTER) {
                  m.defensive = obj;
                  m.has_defense = MUSE_SCR_CREATE_MONSTER;
            }
      }
botm: return((boolean)(!!m.has_defense));
#undef nomore
}

/* Perform a defensive action for a monster.  Must be called immediately
 * after find_defensive().  Return values are 0: did something, 1: died,
 * 2: did something and can't attack again (i.e. teleported).
 */
int
use_defensive(mtmp)
struct monst *mtmp;
{
      int i, fleetim, how = 0;
      struct obj *otmp = m.defensive;
      boolean vis, vismon, oseen;
      const char *mcsa = "%s can see again.";

      if ((i = precheck(mtmp, otmp)) != 0) return i;
      vis = cansee(mtmp->mx, mtmp->my);
      vismon = canseemon(mtmp);
      oseen = otmp && vismon;

      /* when using defensive choice to run away, we want monster to avoid
         rushing right straight back; don't override if already scared */
      fleetim = !mtmp->mflee ? (33 - (30 * mtmp->mhp / mtmp->mhpmax)) : 0;
#define m_flee(m) if (fleetim && !m->iswiz) \
                  { m->mflee = 1; m->mfleetim = fleetim; }

      switch(m.has_defense) {
      case MUSE_UNICORN_HORN:
            if (vismon) {
                if (otmp)
                  pline("%s uses a unicorn horn!", Monnam(mtmp));
                else
                  pline_The("tip of %s's horn glows!", mon_nam(mtmp));
            }
            if (!mtmp->mcansee) {
                mtmp->mcansee = 1;
                mtmp->mblinded = 0;
                if (vismon) pline(mcsa, Monnam(mtmp));
            } else if (mtmp->mconf || mtmp->mstun) {
                mtmp->mconf = mtmp->mstun = 0;
                if (vismon)
                  pline("%s seems steadier now.", Monnam(mtmp));
            } else impossible("No need for unicorn horn?");
            return 2;
      case MUSE_BUGLE:
            if (vismon)
                  pline("%s plays %s!", Monnam(mtmp), doname(otmp));
            else if (flags.soundok)
                  You_hear("a bugle playing reveille!");
            awaken_soldiers();
            return 2;
      case MUSE_WAN_TELEPORTATION_SELF:
            if ((mtmp->isshk && inhishop(mtmp))
                   || mtmp->isgd || mtmp->ispriest) return 2;
            m_flee(mtmp);
            mzapmsg(mtmp, otmp, TRUE);
            otmp->spe--;
            how = WAN_TELEPORTATION;
mon_tele:
            if (tele_restrict(mtmp)) {    /* mysterious force... */
                if (vismon && how)        /* mentions 'teleport' */
                  makeknown(how);
                return 2;
            }
            if ((
#if 0
                  mon_has_amulet(mtmp) ||
#endif
                  On_W_tower_level(&u.uz)) && !rn2(3)) {
                if (vismon)
                  pline("%s seems disoriented for a moment.",
                        Monnam(mtmp));
                return 2;
            }
            if (oseen && how) makeknown(how);
            rloc(mtmp);
            return 2;
      case MUSE_WAN_TELEPORTATION:
            zap_oseen = oseen;
            mzapmsg(mtmp, otmp, FALSE);
            otmp->spe--;
            m_using = TRUE;
            mbhit(mtmp,rn1(8,6),mbhitm,bhito,otmp);
            m_using = FALSE;
            return 2;
      case MUSE_SCR_TELEPORTATION:
          {
            int obj_is_cursed = otmp->cursed;

            if (mtmp->isshk || mtmp->isgd || mtmp->ispriest) return 2;
            m_flee(mtmp);
            mreadmsg(mtmp, otmp);
            m_useup(mtmp, otmp);    /* otmp might be free'ed */
            how = SCR_TELEPORTATION;
            if (obj_is_cursed || mtmp->mconf) {
                  int nlev;
                  d_level flev;

                  if (mon_has_amulet(mtmp) || In_endgame(&u.uz)) {
                      if (vismon)
                        pline("%s seems very disoriented for a moment.",
                              Monnam(mtmp));
                      return 2;
                  }
                  nlev = random_teleport_level();
                  if (nlev == depth(&u.uz)) {
                      if (vismon)
                        pline("%s shudders for a moment.",
                                                Monnam(mtmp));
                      return 2;
                  }
                  get_level(&flev, nlev);
                  migrate_to_level(mtmp, ledger_no(&flev), MIGR_RANDOM,
                        (coord *)0);
                  if (oseen) makeknown(SCR_TELEPORTATION);
            } else goto mon_tele;
            return 2;
          }
      case MUSE_WAN_DIGGING:
          { struct trap *ttmp;

            m_flee(mtmp);
            mzapmsg(mtmp, otmp, FALSE);
            otmp->spe--;
            if (oseen) makeknown(WAN_DIGGING);
            if (IS_FURNITURE(levl[mtmp->mx][mtmp->my].typ) ||
                IS_DRAWBRIDGE(levl[mtmp->mx][mtmp->my].typ) ||
                (is_drawbridge_wall(mtmp->mx, mtmp->my) >= 0) ||
                (sstairs.sx && sstairs.sx == mtmp->mx &&
                           sstairs.sy == mtmp->my)) {
                  pline_The("digging ray is ineffective.");
                  return 2;
            }
            if (!Can_dig_down(&u.uz)) {
                if(canseemon(mtmp))
                  pline_The("%s here is too hard to dig in.",
                              surface(mtmp->mx, mtmp->my));
                return 2;
            }
            ttmp = maketrap(mtmp->mx, mtmp->my, HOLE);
            if (!ttmp) return 2;
            seetrap(ttmp);
            if (vis) {
                pline("%s has made a hole in the %s.", Monnam(mtmp),
                        surface(mtmp->mx, mtmp->my));
                pline("%s %s through...", Monnam(mtmp),
                    is_flyer(mtmp->data) ? "dives" : "falls");
            } else if (flags.soundok)
                  You_hear("%s crash through the %s.", something,
                        surface(mtmp->mx, mtmp->my));
            /* we made sure that there is a level for mtmp to go to */
            migrate_to_level(mtmp, ledger_no(&u.uz) + 1,
                         MIGR_RANDOM, (coord *)0);
            return 2;
          }
      case MUSE_WAN_CREATE_MONSTER:
          { coord cc;
                /* pm: 0 => random, eel => aquatic, croc => amphibious */
            struct permonst *pm = !is_pool(mtmp->mx, mtmp->my) ? 0 :
                       &mons[u.uinwater ? PM_GIANT_EEL : PM_CROCODILE];
            struct monst *mon;

            if (!enexto(&cc, mtmp->mx, mtmp->my, pm)) return 0;
            mzapmsg(mtmp, otmp, FALSE);
            otmp->spe--;
            mon = makemon((struct permonst *)0, cc.x, cc.y, NO_MM_FLAGS);
            if (mon && canspotmon(mon) && oseen)
                makeknown(WAN_CREATE_MONSTER);
            return 2;
          }
      case MUSE_SCR_CREATE_MONSTER:
          { coord cc;
            struct permonst *pm = 0, *fish = 0;
            int cnt = 1;
            struct monst *mon;
            boolean known = FALSE;

            if (!rn2(73)) cnt += rnd(4);
            if (mtmp->mconf || otmp->cursed) cnt += 12;
            if (mtmp->mconf) pm = fish = &mons[PM_ACID_BLOB];
            else if (is_pool(mtmp->mx, mtmp->my))
                fish = &mons[u.uinwater ? PM_GIANT_EEL : PM_CROCODILE];
            mreadmsg(mtmp, otmp);
            while(cnt--) {
                /* `fish' potentially gives bias towards water locations;
                   `pm' is what to actually create (0 => random) */
                if (!enexto(&cc, mtmp->mx, mtmp->my, fish)) break;
                mon = makemon(pm, cc.x, cc.y, NO_MM_FLAGS);
                if (mon && canspotmon(mon)) known = TRUE;
            }
            /* The only case where we don't use oseen.  For wands, you
             * have to be able to see the monster zap the wand to know
             * what type it is.  For teleport scrolls, you have to see
             * the monster to know it teleported.
             */
            if (known)
                makeknown(SCR_CREATE_MONSTER);
            else if (!objects[SCR_CREATE_MONSTER].oc_name_known
                  && !objects[SCR_CREATE_MONSTER].oc_uname)
                docall(otmp);
            m_useup(mtmp, otmp);
            return 2;
          }
      case MUSE_TRAPDOOR:
            /* trap doors on "bottom" levels of dungeons are rock-drop
             * trap doors, not holes in the floor.  We check here for
             * safety.
             */
            if (Is_botlevel(&u.uz)) return 0;
            m_flee(mtmp);
            if (vis) {
                  struct trap *t;
                  t = t_at(trapx,trapy);
                  pline("%s %s into a %s!", Monnam(mtmp),
                  makeplural(locomotion(mtmp->data, "jump")),
                  t->ttyp == TRAPDOOR ? "trap door" : "hole");
                  seetrap(t_at(trapx,trapy));
            }

            /*  don't use rloc_to() because worm tails must "move" */
            remove_monster(mtmp->mx, mtmp->my);
            newsym(mtmp->mx, mtmp->my);   /* update old location */
            place_monster(mtmp, trapx, trapy);
            if (mtmp->wormno) worm_move(mtmp);
            newsym(trapx, trapy);

            migrate_to_level(mtmp, ledger_no(&u.uz) + 1,
                         MIGR_RANDOM, (coord *)0);
            return 2;
      case MUSE_UPSTAIRS:
            /* Monsters without amulets escape the dungeon and are
             * gone for good when they leave up the up stairs.
             * Monsters with amulets would reach the endlevel,
             * which we cannot allow since that would leave the
             * player stranded.
             */
            if (ledger_no(&u.uz) == 1) {
                  if (mon_has_special(mtmp))
                        return 0;
                  if (vismon)
                      pline("%s escapes the dungeon!", Monnam(mtmp));
                  mongone(mtmp);
                  return 2;
            }
            m_flee(mtmp);
            if (Inhell && mon_has_amulet(mtmp) && !rn2(4) &&
                  (dunlev(&u.uz) < dunlevs_in_dungeon(&u.uz) - 3)) {
                if (vismon) pline(
     "As %s climbs the stairs, a mysterious force momentarily surrounds %s...",
                             mon_nam(mtmp), him[pronoun_gender(mtmp)]);
                /* simpler than for the player; this will usually be
                   the Wizard and he'll immediately go right to the
                   upstairs, so there's not much point in having any
                   chance for a random position on the current level */
                migrate_to_level(mtmp, ledger_no(&u.uz) + 1,
                             MIGR_RANDOM, (coord *)0);
            } else {
                if (vismon) pline("%s escapes upstairs!", Monnam(mtmp));
                migrate_to_level(mtmp, ledger_no(&u.uz) - 1,
                             MIGR_STAIRS_DOWN, (coord *)0);
            }
            return 2;
      case MUSE_DOWNSTAIRS:
            m_flee(mtmp);
            if (vismon) pline("%s escapes downstairs!", Monnam(mtmp));
            migrate_to_level(mtmp, ledger_no(&u.uz) + 1,
                         MIGR_STAIRS_UP, (coord *)0);
            return 2;
      case MUSE_UP_LADDER:
            m_flee(mtmp);
            if (vismon) pline("%s escapes up the ladder!", Monnam(mtmp));
            migrate_to_level(mtmp, ledger_no(&u.uz) - 1,
                         MIGR_LADDER_DOWN, (coord *)0);
            return 2;
      case MUSE_DN_LADDER:
            m_flee(mtmp);
            if (vismon) pline("%s escapes down the ladder!", Monnam(mtmp));
            migrate_to_level(mtmp, ledger_no(&u.uz) + 1,
                         MIGR_LADDER_UP, (coord *)0);
            return 2;
      case MUSE_SSTAIRS:
            m_flee(mtmp);
            /* the stairs leading up from the 1st level are */
            /* regular stairs, not sstairs.                 */
            if (sstairs.up) {
                  if (vismon)
                      pline("%s escapes upstairs!", Monnam(mtmp));
                  if(Inhell) {
                      migrate_to_level(mtmp, ledger_no(&sstairs.tolev),
                                   MIGR_RANDOM, (coord *)0);
                      return 2;
                  }
            } else      if (vismon)
                pline("%s escapes downstairs!", Monnam(mtmp));
            migrate_to_level(mtmp, ledger_no(&sstairs.tolev),
                         MIGR_SSTAIRS, (coord *)0);
            return 2;
      case MUSE_TELEPORT_TRAP:
            m_flee(mtmp);
            if (vis) {
                  pline("%s %s onto a teleport trap!", Monnam(mtmp),
                        makeplural(locomotion(mtmp->data, "jump")));
                  seetrap(t_at(trapx,trapy));
            }
            /*  don't use rloc_to() because worm tails must "move" */
            remove_monster(mtmp->mx, mtmp->my);
            newsym(mtmp->mx, mtmp->my);   /* update old location */
            place_monster(mtmp, trapx, trapy);
            if (mtmp->wormno) worm_move(mtmp);
            newsym(trapx, trapy);

            goto mon_tele;
      case MUSE_POT_HEALING:
            mquaffmsg(mtmp, otmp);
            i = d(6 + 2 * bcsign(otmp), 4);
            mtmp->mhp += i;
            if (mtmp->mhp > mtmp->mhpmax) mtmp->mhp = ++mtmp->mhpmax;
            if (!otmp->cursed && !mtmp->mcansee) {
                  mtmp->mcansee = 1;
                  mtmp->mblinded = 0;
                  if (vismon) pline(mcsa, Monnam(mtmp));
            }
            if (vismon) pline("%s looks better.", Monnam(mtmp));
            if (oseen) makeknown(POT_HEALING);
            m_useup(mtmp, otmp);
            return 2;
      case MUSE_POT_EXTRA_HEALING:
            mquaffmsg(mtmp, otmp);
            i = d(6 + 2 * bcsign(otmp), 8);
            mtmp->mhp += i;
            if (mtmp->mhp > mtmp->mhpmax)
                  mtmp->mhp = (mtmp->mhpmax += (otmp->blessed ? 5 : 2));
            if (!mtmp->mcansee) {
                  mtmp->mcansee = 1;
                  mtmp->mblinded = 0;
                  if (vismon) pline(mcsa, Monnam(mtmp));
            }
            if (vismon) pline("%s looks much better.", Monnam(mtmp));
            if (oseen) makeknown(POT_EXTRA_HEALING);
            m_useup(mtmp, otmp);
            return 2;
      case MUSE_POT_FULL_HEALING:
            mquaffmsg(mtmp, otmp);
            if (otmp->otyp == POT_SICKNESS) unbless(otmp); /* Pestilence */
            mtmp->mhp = (mtmp->mhpmax += (otmp->blessed ? 8 : 4));
            if (!mtmp->mcansee && otmp->otyp != POT_SICKNESS) {
                  mtmp->mcansee = 1;
                  mtmp->mblinded = 0;
                  if (vismon) pline(mcsa, Monnam(mtmp));
            }
            if (vismon) pline("%s looks completely healed.", Monnam(mtmp));
            if (oseen) makeknown(otmp->otyp);
            m_useup(mtmp, otmp);
            return 2;
      case 0: return 0; /* i.e. an exploded wand */
      default: impossible("%s wanted to perform action %d?", Monnam(mtmp),
                  m.has_defense);
            break;
      }
      return 0;
#undef m_flee
}

int
rnd_defensive_item(mtmp)
struct monst *mtmp;
{
      struct permonst *pm = mtmp->data;
      int difficulty = monstr[(monsndx(pm))];

      if(is_animal(pm) || attacktype(pm, AT_EXPL) || mindless(mtmp->data)
                  || pm->mlet == S_GHOST
# ifdef KOPS
                  || pm->mlet == S_KOP
# endif
            ) return 0;
      switch (rn2(8 + (difficulty > 3) + (difficulty > 6) +
                        (difficulty > 8))) {
            case 6: case 9:
                  if (!rn2(3)) return WAN_TELEPORTATION;
                  /* else FALLTHRU */
            case 0: case 1:
                  return SCR_TELEPORTATION;
            case 8: case 10:
                  if (!rn2(3)) return WAN_CREATE_MONSTER;
                  /* else FALLTHRU */
            case 2: return SCR_CREATE_MONSTER;
            case 3: return POT_HEALING;
            case 4: return POT_EXTRA_HEALING;
            case 5: return (mtmp->data != &mons[PM_PESTILENCE]) ?
                        POT_FULL_HEALING : POT_SICKNESS;
            case 7: if (is_floater(pm) || mtmp->isshk || mtmp->isgd
                                    || mtmp->ispriest
                                                      )
                        return 0;
                  else
                        return WAN_DIGGING;
      }
      /*NOTREACHED*/
      return 0;
}

#define MUSE_WAN_DEATH 1
#define MUSE_WAN_SLEEP 2
#define MUSE_WAN_FIRE 3
#define MUSE_WAN_COLD 4
#define MUSE_WAN_LIGHTNING 5
#define MUSE_WAN_MAGIC_MISSILE 6
#define MUSE_WAN_STRIKING 7
#define MUSE_SCR_FIRE 8
#define MUSE_POT_PARALYSIS 9
#define MUSE_POT_BLINDNESS 10
#define MUSE_POT_CONFUSION 11
#define MUSE_FROST_HORN 12
#define MUSE_FIRE_HORN 13
#define MUSE_POT_ACID 14
/*#define MUSE_WAN_TELEPORTATION 15*/
#define MUSE_POT_SLEEPING 16
#define MUSE_SCR_EARTH 17

/* Select an offensive item/action for a monster.  Returns TRUE iff one is
 * found.
 */
boolean
find_offensive(mtmp)
struct monst *mtmp;
{
      register struct obj *obj;
      boolean ranged_stuff = lined_up(mtmp);
      boolean reflection_skip = (Reflecting && rn2(2));
      struct obj *helmet = which_armor(mtmp, W_ARMH);

      m.offensive = (struct obj *)0;
      m.has_offense = 0;
      if (mtmp->mpeaceful || is_animal(mtmp->data) ||
                        mindless(mtmp->data) || nohands(mtmp->data))
            return FALSE;
      if (u.uswallow) return FALSE;
      if (in_your_sanctuary(mtmp, 0, 0)) return FALSE;
      if (dmgtype(mtmp->data, AD_HEAL) && !uwep
#ifdef TOURIST
          && !uarmu
#endif
          && !uarm && !uarmh && !uarms && !uarmg && !uarmc && !uarmf)
            return FALSE;

      if (!ranged_stuff) return FALSE;
#define nomore(x) if(m.has_offense==x) continue;
      for(obj=mtmp->minvent; obj; obj=obj->nobj) {
            /* nomore(MUSE_WAN_DEATH); */
            if (!reflection_skip) {
                if(obj->otyp == WAN_DEATH && obj->spe > 0) {
                  m.offensive = obj;
                  m.has_offense = MUSE_WAN_DEATH;
                }
                nomore(MUSE_WAN_SLEEP);
                if(obj->otyp == WAN_SLEEP && obj->spe > 0 && multi >= 0) {
                  m.offensive = obj;
                  m.has_offense = MUSE_WAN_SLEEP;
                }
                nomore(MUSE_WAN_FIRE);
                if(obj->otyp == WAN_FIRE && obj->spe > 0) {
                  m.offensive = obj;
                  m.has_offense = MUSE_WAN_FIRE;
                }
                nomore(MUSE_FIRE_HORN);
                if(obj->otyp == FIRE_HORN && obj->spe > 0) {
                  m.offensive = obj;
                  m.has_offense = MUSE_FIRE_HORN;
                }
                nomore(MUSE_WAN_COLD);
                if(obj->otyp == WAN_COLD && obj->spe > 0) {
                  m.offensive = obj;
                  m.has_offense = MUSE_WAN_COLD;
                }
                nomore(MUSE_FROST_HORN);
                if(obj->otyp == FROST_HORN && obj->spe > 0) {
                  m.offensive = obj;
                  m.has_offense = MUSE_FROST_HORN;
                }
                nomore(MUSE_WAN_LIGHTNING);
                if(obj->otyp == WAN_LIGHTNING && obj->spe > 0) {
                  m.offensive = obj;
                  m.has_offense = MUSE_WAN_LIGHTNING;
                }
                nomore(MUSE_WAN_MAGIC_MISSILE);
                if(obj->otyp == WAN_MAGIC_MISSILE && obj->spe > 0) {
                  m.offensive = obj;
                  m.has_offense = MUSE_WAN_MAGIC_MISSILE;
                }
            }
            nomore(MUSE_WAN_STRIKING);
            if(obj->otyp == WAN_STRIKING && obj->spe > 0) {
                  m.offensive = obj;
                  m.has_offense = MUSE_WAN_STRIKING;
            }
            nomore(MUSE_POT_PARALYSIS);
            if(obj->otyp == POT_PARALYSIS && multi >= 0) {
                  m.offensive = obj;
                  m.has_offense = MUSE_POT_PARALYSIS;
            }
            nomore(MUSE_POT_BLINDNESS);
            if(obj->otyp == POT_BLINDNESS) {
                  m.offensive = obj;
                  m.has_offense = MUSE_POT_BLINDNESS;
            }
            nomore(MUSE_POT_CONFUSION);
            if(obj->otyp == POT_CONFUSION) {
                  m.offensive = obj;
                  m.has_offense = MUSE_POT_CONFUSION;
            }
            nomore(MUSE_POT_SLEEPING);
            if(obj->otyp == POT_SLEEPING) {
                  m.offensive = obj;
                  m.has_offense = MUSE_POT_SLEEPING;
            }
            nomore(MUSE_POT_ACID);
            if(obj->otyp == POT_ACID) {
                  m.offensive = obj;
                  m.has_offense = MUSE_POT_ACID;
            }
            /* we can safely put this scroll here since the locations that
             * are in a 1 square radius are a subset of the locations that
             * are in wand range
             */
            nomore(MUSE_SCR_EARTH);
            if (obj->otyp == SCR_EARTH
                   && ((helmet && is_metallic(helmet)) ||
                        mtmp->mconf || amorphous(mtmp->data) ||
                        passes_walls(mtmp->data) ||
                        noncorporeal(mtmp->data) ||
                        unsolid(mtmp->data) || !rn2(10))
                   && dist2(mtmp->mx,mtmp->my,mtmp->mux,mtmp->muy) <= 2
                   && mtmp->mcansee && haseyes(mtmp->data)
#ifdef REINCARNATION
                   && !Is_rogue_level(&u.uz)
#endif
                   && (!In_endgame(&u.uz) || Is_earthlevel(&u.uz))) {
                m.offensive = obj;
                m.has_offense = MUSE_SCR_EARTH;
            }
#if 0
            nomore(MUSE_SCR_FIRE);
            if (obj->otyp == SCR_FIRE && resists_fire(mtmp)
               && dist2(mtmp->mx,mtmp->my,mtmp->mux,mtmp->muy) <= 2
               && mtmp->mcansee && haseyes(mtmp->data)) {
                  m.offensive = obj;
                  m.has_offense = MUSE_SCR_FIRE;
            }
#endif
      }
      return((boolean)(!!m.has_offense));
#undef nomore
}

STATIC_PTR
int
mbhitm(mtmp, otmp)
register struct monst *mtmp;
register struct obj *otmp;
{
      int tmp;

      boolean reveal_invis = FALSE;
      if (mtmp != &youmonst) {
            mtmp->msleeping = 0;
            if (mtmp->m_ap_type) seemimic(mtmp);
      }
      switch(otmp->otyp) {
      case WAN_STRIKING:
            reveal_invis = TRUE;
            if (mtmp == &youmonst) {
                  if (zap_oseen) makeknown(WAN_STRIKING);
                  if (Antimagic) {
                      shieldeff(u.ux, u.uy);
                      pline("Boing!");
                  } else if (rnd(20) < 10 + u.uac) {
                      pline_The("wand hits you!");
                      tmp = d(2,12);
                      if(Half_spell_damage) tmp = (tmp+1) / 2;
                      losehp(tmp, "wand", KILLED_BY_AN);
                  } else pline_The("wand misses you.");
                  stop_occupation();
                  nomul(0);
            } else if (resists_magm(mtmp)) {
                  shieldeff(mtmp->mx, mtmp->my);
                  pline("Boing!");
            } else if (rnd(20) < 10+find_mac(mtmp)) {
                  tmp = d(2,12);
                  hit("wand", mtmp, exclam(tmp));
                  (void) resist(mtmp, otmp->oclass, tmp, TELL);
                  if (cansee(mtmp->mx, mtmp->my) && zap_oseen)
                        makeknown(WAN_STRIKING);
            } else {
                  miss("wand", mtmp);
                  if (cansee(mtmp->mx, mtmp->my) && zap_oseen)
                        makeknown(WAN_STRIKING);
            }
            break;
      case WAN_TELEPORTATION:
            if (mtmp == &youmonst) {
                  if (zap_oseen) makeknown(WAN_TELEPORTATION);
                  tele();
            } else {
                  /* for consistency with zap.c, don't identify */
                  if (mtmp->ispriest &&
                        *in_rooms(mtmp->mx, mtmp->my, TEMPLE)) {
                      if (cansee(mtmp->mx, mtmp->my))
                        pline("%s resists the magic!", Monnam(mtmp));
                      mtmp->msleeping = 0;
                      if(mtmp->m_ap_type) seemimic(mtmp);
                  } else if (!tele_restrict(mtmp))
                      rloc(mtmp);
            }
            break;
      case WAN_CANCELLATION:
      case SPE_CANCELLATION:
            cancel_monst(mtmp, otmp, FALSE, TRUE, FALSE);
            break;
      }
      if (reveal_invis) {
          if (mtmp->mhp > 0 && cansee(bhitpos.x,bhitpos.y)
                                          && !canspotmon(mtmp))
            map_invisible(bhitpos.x, bhitpos.y);
      }
      return 0;
}

/* A modified bhit() for monsters.  Based on bhit() in zap.c.  Unlike
 * buzz(), bhit() doesn't take into account the possibility of a monster
 * zapping you, so we need a special function for it.  (Unless someone wants
 * to merge the two functions...)
 */
STATIC_OVL void
mbhit(mon,range,fhitm,fhito,obj)
struct monst *mon;                  /* monster shooting the wand */
register int range;                 /* direction and range */
int FDECL((*fhitm),(MONST_P,OBJ_P));
int FDECL((*fhito),(OBJ_P,OBJ_P));  /* fns called when mon/obj hit */
struct obj *obj;              /* 2nd arg to fhitm/fhito */
{
      register struct monst *mtmp;
      register struct obj *otmp;
      register uchar typ;
      int ddx, ddy;

      bhitpos.x = mon->mx;
      bhitpos.y = mon->my;
      ddx = sgn(mon->mux - mon->mx);
      ddy = sgn(mon->muy - mon->my);

      while(range-- > 0) {
            int x,y;

            bhitpos.x += ddx;
            bhitpos.y += ddy;
            x = bhitpos.x; y = bhitpos.y;

            if (!isok(x,y)) {
                bhitpos.x -= ddx;
                bhitpos.y -= ddy;
                break;
            }
            if (find_drawbridge(&x,&y))
                switch (obj->otyp) {
                  case WAN_STRIKING:
                      destroy_drawbridge(x,y);
                }
            if(bhitpos.x==u.ux && bhitpos.y==u.uy) {
                  (*fhitm)(&youmonst, obj);
                  range -= 3;
            } else if(MON_AT(bhitpos.x, bhitpos.y)){
                  mtmp = m_at(bhitpos.x,bhitpos.y);
                  if (cansee(bhitpos.x,bhitpos.y) && !canspotmon(mtmp))
                      map_invisible(bhitpos.x, bhitpos.y);
                  (*fhitm)(mtmp, obj);
                  range -= 3;
            }
            /* modified by GAN to hit all objects */
            if(fhito){
                int hitanything = 0;
                register struct obj *next_obj;

                for(otmp = level.objects[bhitpos.x][bhitpos.y];
                                          otmp; otmp = next_obj) {
                  /* Fix for polymorph bug, Tim Wright */
                  next_obj = otmp->nexthere;
                  hitanything += (*fhito)(otmp, obj);
                }
                if(hitanything)     range--;
            }
            typ = levl[bhitpos.x][bhitpos.y].typ;
            if(IS_DOOR(typ) || typ == SDOOR) {
                switch (obj->otyp) {
                  /* note: monsters don't use opening or locking magic
                     at present, but keep these as placeholders */
                  case WAN_OPENING:
                  case WAN_LOCKING:
                  case WAN_STRIKING:
                      if (doorlock(obj, bhitpos.x, bhitpos.y)) {
                        makeknown(obj->otyp);
                        /* if a shop door gets broken, add it to
                           the shk's fix list (no cost to player) */
                        if (levl[bhitpos.x][bhitpos.y].doormask ==
                              D_BROKEN &&
                            *in_rooms(bhitpos.x, bhitpos.y, SHOPBASE))
                            add_damage(bhitpos.x, bhitpos.y, 0L);
                      }
                      break;
                }
            }
            if(!ZAP_POS(typ) || (IS_DOOR(typ) &&
               (levl[bhitpos.x][bhitpos.y].doormask & (D_LOCKED | D_CLOSED)))
              ) {
                  bhitpos.x -= ddx;
                  bhitpos.y -= ddy;
                  break;
            }
      }
}

/* Perform an offensive action for a monster.  Must be called immediately
 * after find_offensive().  Return values are same as use_defensive().
 */
int
use_offensive(mtmp)
struct monst *mtmp;
{
      int i;
      struct obj *otmp = m.offensive;
      boolean oseen;

      /* offensive potions are not drunk, they're thrown */
      if (otmp->oclass != POTION_CLASS && (i = precheck(mtmp, otmp)) != 0)
            return i;
      oseen = otmp && canseemon(mtmp);

      switch(m.has_offense) {
      case MUSE_WAN_DEATH:
      case MUSE_WAN_SLEEP:
      case MUSE_WAN_FIRE:
      case MUSE_WAN_COLD:
      case MUSE_WAN_LIGHTNING:
      case MUSE_WAN_MAGIC_MISSILE:
            mzapmsg(mtmp, otmp, FALSE);
            otmp->spe--;
            if (oseen) makeknown(otmp->otyp);
            m_using = TRUE;
            buzz((int)(-30 - (otmp->otyp - WAN_MAGIC_MISSILE)),
                  (otmp->otyp == WAN_MAGIC_MISSILE) ? 2 : 6,
                  mtmp->mx, mtmp->my,
                  sgn(mtmp->mux-mtmp->mx), sgn(mtmp->muy-mtmp->my));
            m_using = FALSE;
            return (mtmp->mhp <= 0) ? 1 : 2;
      case MUSE_FIRE_HORN:
      case MUSE_FROST_HORN:
            if (oseen) {
                  makeknown(otmp->otyp);
                  pline("%s plays a %s!", Monnam(mtmp), xname(otmp));
            } else
                  You_hear("a horn being played.");
            otmp->spe--;
            m_using = TRUE;
            buzz(-30 - ((otmp->otyp==FROST_HORN) ? AD_COLD-1 : AD_FIRE-1),
                  rn1(6,6), mtmp->mx, mtmp->my,
                  sgn(mtmp->mux-mtmp->mx), sgn(mtmp->muy-mtmp->my));
            m_using = FALSE;
            return (mtmp->mhp <= 0) ? 1 : 2;
      case MUSE_WAN_TELEPORTATION:
      case MUSE_WAN_STRIKING:
            zap_oseen = oseen;
            mzapmsg(mtmp, otmp, FALSE);
            otmp->spe--;
            m_using = TRUE;
            mbhit(mtmp,rn1(8,6),mbhitm,bhito,otmp);
            m_using = FALSE;
            return 2;
      case MUSE_SCR_EARTH:
          {
            /* TODO: handle steeds */
            register int x, y;
            /* don't use monster fields after killing it */
            boolean confused = (mtmp->mconf ? TRUE : FALSE);
            int mmx = mtmp->mx, mmy = mtmp->my;

            mreadmsg(mtmp, otmp);
            /* Identify the scroll */
            if (canspotmon(mtmp)) {
                pline_The("%s rumbles %s %s!", ceiling(mtmp->mx, mtmp->my),
                        otmp->blessed ? "around" : "above",
                        mon_nam(mtmp));
                if (oseen) makeknown(otmp->otyp);
            } else if (cansee(mtmp->mx, mtmp->my)) {
                pline_The("%s rumbles in the middle of nowhere!",
                  ceiling(mtmp->mx, mtmp->my));
                if (mtmp->minvis)
                  map_invisible(mtmp->mx, mtmp->my);
                if (oseen) makeknown(otmp->otyp);
            }

            /* Loop through the surrounding squares */
            for (x = mmx-1; x <= mmx+1; x++) {
                for (y = mmy-1; y <= mmy+1; y++) {
                  /* Is this a suitable spot? */
                  if (isok(x, y) && !closed_door(x, y) &&
                              !IS_ROCK(levl[x][y].typ) &&
                              !IS_AIR(levl[x][y].typ) &&
                              (((x == mmx) && (y == mmy)) ?
                                  !otmp->blessed : !otmp->cursed) &&
                              (x != u.ux || y != u.uy)) {
                      register struct obj *otmp2;
                      register struct monst *mtmp2;

                      /* Make the object(s) */
                      otmp2 = mksobj(confused ? ROCK : BOULDER,
                              FALSE, FALSE);
                      if (!otmp2) continue;  /* Shouldn't happen */
                      otmp2->quan = confused ? rn1(5,2) : 1;
                      otmp2->owt = weight(otmp2);

                      /* Find the monster here (might be same as mtmp) */
                      mtmp2 = m_at(x, y);
                      if (mtmp2 && !amorphous(mtmp2->data) &&
                              !passes_walls(mtmp2->data) &&
                              !noncorporeal(mtmp2->data) &&
                              !unsolid(mtmp2->data)) {
                        struct obj *helmet = which_armor(mtmp2, W_ARMH);
                        int mdmg;

                        if (cansee(mtmp2->mx, mtmp2->my)) {
                            pline("%s is hit by %s!", Monnam(mtmp2),
                                    doname(otmp2));
                            if (mtmp2->minvis && !canspotmon(mtmp2))
                              map_invisible(mtmp2->mx, mtmp2->my);
                        }
                        mdmg = dmgval(otmp2, mtmp2) * otmp2->quan;
                        if (helmet) {
                            if(is_metallic(helmet)) {
                              if (canspotmon(mtmp2))
                                  pline("Fortunately, %s is wearing a hard helmet.", mon_nam(mtmp2));
                              else if (flags.soundok)
                                  You_hear("a clanging sound.");
                              if (mdmg > 2) mdmg = 2;
                            } else {
                              if (canspotmon(mtmp2))
                                  pline("%s's %s does not protect %s.",
                                    Monnam(mtmp2), xname(helmet),
                                    him[pronoun_gender(mtmp2)]);
                            }
                        }
                        mtmp2->mhp -= mdmg;
                        if (mtmp2->mhp <= 0) {
                            pline("%s is killed.", Monnam(mtmp2));
                            mondied(mtmp2);
                        }
                      }
                      /* Drop the rock/boulder to the floor */
                      if (!flooreffects(otmp2, x, y, "fall")) {
                        place_object(otmp2, x, y);
                        stackobj(otmp2);
                        newsym(x, y);  /* map the rock */
                      }
                  }
                }
            }
            m_useup(mtmp, otmp);
            /* Attack the player */
            if (dist2(mmx, mmy, u.ux, u.uy) == 1 && !otmp->cursed) {
                int dmg;
                struct obj *otmp2;

                /* Okay, _you_ write this without repeating the code */
                otmp2 = mksobj(confused ? ROCK : BOULDER,
                        FALSE, FALSE);
                if (!otmp2) goto xxx_noobj;  /* Shouldn't happen */
                otmp2->quan = confused ? rn1(5,2) : 1;
                otmp2->owt = weight(otmp2);
                if (!amorphous(youmonst.data) &&
                      !Passes_walls &&
                      !noncorporeal(youmonst.data) &&
                      !unsolid(youmonst.data)) {
                  You("are hit by %s!", doname(otmp2));
                  dmg = dmgval(otmp2, &youmonst) * otmp2->quan;
                  if (uarmh) {
                      if(is_metallic(uarmh)) {
                        pline("Fortunately, you are wearing a hard helmet.");
                        if (dmg > 2) dmg = 2;
                      } else if (flags.verbose) {
                        Your("%s does not protect you.",
                                    xname(uarmh));
                      }
                  }
                } else
                  dmg = 0;
                if (!flooreffects(otmp2, u.ux, u.uy, "fall")) {
                  place_object(otmp2, u.ux, u.uy);
                  stackobj(otmp2);
                  newsym(u.ux, u.uy);
                }
                if (dmg) losehp(dmg, "scroll of earth", KILLED_BY_AN);
            }
          xxx_noobj:

            return (mtmp->mhp <= 0) ? 1 : 2;
          }
#if 0
      case MUSE_SCR_FIRE:
            {
            boolean vis = cansee(mtmp->mx, mtmp->my);

            mreadmsg(mtmp, otmp);
            if (mtmp->mconf) {
                  if (vis)
                      pline("Oh, what a pretty fire!");
            } else {
                  struct monst *mtmp2;
                  int num;

                  if (vis)
                      pline_The("scroll erupts in a tower of flame!");
                  shieldeff(mtmp->mx, mtmp->my);
                  pline("%s is uninjured.", Monnam(mtmp));
                  (void) destroy_mitem(mtmp, SCROLL_CLASS, AD_FIRE);
                  (void) destroy_mitem(mtmp, SPBOOK_CLASS, AD_FIRE);
                  (void) destroy_mitem(mtmp, POTION_CLASS, AD_FIRE);
                  num = (2*(rn1(3, 3) + 2 * bcsign(otmp)) + 1)/3;
                  if (Fire_resistance)
                      You("are not harmed.");
                  burn_away_slime();
                  if (Half_spell_damage) num = (num+1) / 2;
                  else losehp(num, "scroll of fire", KILLED_BY_AN);
                  for(mtmp2 = fmon; mtmp2; mtmp2 = mtmp2->nmon) {
                     if(DEADMONSTER(mtmp2)) continue;
                     if(mtmp == mtmp2) continue;
                     if(dist2(mtmp2->mx,mtmp2->my,mtmp->mx,mtmp->my) < 3){
                        if (resists_fire(mtmp2)) continue;
                        mtmp2->mhp -= num;
                        if (resists_cold(mtmp2))
                            mtmp2->mhp -= 3*num;
                        if(mtmp2->mhp < 1) {
                            mondied(mtmp2);
                            break;
                        }
                      }
                  }
            }
            return 2;
            }
#endif      /* 0 */
      case MUSE_POT_PARALYSIS:
      case MUSE_POT_BLINDNESS:
      case MUSE_POT_CONFUSION:
      case MUSE_POT_SLEEPING:
      case MUSE_POT_ACID:
            /* Note: this setting of dknown doesn't suffice.  A monster
             * which is out of sight might throw and it hits something _in_
             * sight, a problem not existing with wands because wand rays
             * are not objects.  Also set dknown in mthrowu.c.
             */
            if (cansee(mtmp->mx, mtmp->my)) {
                  otmp->dknown = 1;
                  pline("%s hurls %s!", Monnam(mtmp),
                                    singular(otmp, doname));
            }
            m_throw(mtmp, mtmp->mx, mtmp->my, sgn(mtmp->mux-mtmp->mx),
                  sgn(mtmp->muy-mtmp->my),
                  distmin(mtmp->mx,mtmp->my,mtmp->mux,mtmp->muy), otmp);
            return 2;
      case 0: return 0; /* i.e. an exploded wand */
      default: impossible("%s wanted to perform action %d?", Monnam(mtmp),
                  m.has_offense);
            break;
      }
      return 0;
}

int
rnd_offensive_item(mtmp)
struct monst *mtmp;
{
      struct permonst *pm = mtmp->data;
      int difficulty = monstr[(monsndx(pm))];

      if(is_animal(pm) || attacktype(pm, AT_EXPL) || mindless(mtmp->data)
                  || pm->mlet == S_GHOST
# ifdef KOPS
                  || pm->mlet == S_KOP
# endif
            ) return 0;
      if (difficulty > 7 && !rn2(35)) return WAN_DEATH;
      switch (rn2(9 - (difficulty < 4) + 4 * (difficulty > 6))) {
            case 0: {
                struct obj *helmet = which_armor(mtmp, W_ARMH);

                if ((helmet && is_metallic(helmet)) || amorphous(pm) || passes_walls(pm) || noncorporeal(pm) || unsolid(pm))
                  return SCR_EARTH;
            } /* fall through */
            case 1: return WAN_STRIKING;
            case 2: return POT_ACID;
            case 3: return POT_CONFUSION;
            case 4: return POT_BLINDNESS;
            case 5: return POT_SLEEPING;
            case 6: return POT_PARALYSIS;
            case 7: case 8:
                  return WAN_MAGIC_MISSILE;
            case 9: return WAN_SLEEP;
            case 10: return WAN_FIRE;
            case 11: return WAN_COLD;
            case 12: return WAN_LIGHTNING;
      }
      /*NOTREACHED*/
      return 0;
}

#define MUSE_POT_GAIN_LEVEL 1
#define MUSE_WAN_MAKE_INVISIBLE 2
#define MUSE_POT_INVISIBILITY 3
#define MUSE_POLY_TRAP 4
#define MUSE_WAN_POLYMORPH 5
#define MUSE_POT_SPEED 6
#define MUSE_WAN_SPEED_MONSTER 7
#define MUSE_BULLWHIP 8
#define MUSE_POT_POLYMORPH 9

boolean
find_misc(mtmp)
struct monst *mtmp;
{
      register struct obj *obj;
      struct permonst *mdat = mtmp->data;
      int x = mtmp->mx, y = mtmp->my;
      struct trap *t;
      int xx, yy;
      boolean immobile = (mdat->mmove == 0);
      boolean stuck = (mtmp == u.ustuck);

      m.misc = (struct obj *)0;
      m.has_misc = 0;
      if (is_animal(mdat) || mindless(mdat))
            return 0;
      if (u.uswallow && stuck) return FALSE;

      /* We arbitrarily limit to times when a player is nearby for the
       * same reason as Junior Pac-Man doesn't have energizers eaten until
       * you can see them...
       */
      if(dist2(x, y, mtmp->mux, mtmp->muy) > 36)
            return FALSE;

      if (!stuck && !immobile && !mtmp->cham && monstr[monsndx(mdat)] < 6) {
        boolean ignore_boulders = (verysmall(mdat) ||
                             throws_rocks(mdat) ||
                             passes_walls(mdat));
        for(xx = x-1; xx <= x+1; xx++)
          for(yy = y-1; yy <= y+1; yy++)
            if (isok(xx,yy) && (xx != u.ux || yy != u.uy))
                if (mdat != &mons[PM_GRID_BUG] || xx == x || yy == y)
                  if (/* (xx==x && yy==y) || */ !level.monsters[xx][yy])
                      if ((t = t_at(xx, yy)) != 0 &&
                        (ignore_boulders || !sobj_at(BOULDER, xx, yy))
                        && !onscary(xx, yy, mtmp)) {
                        if (t->ttyp == POLY_TRAP) {
                            trapx = xx;
                            trapy = yy;
                            m.has_misc = MUSE_POLY_TRAP;
                            return TRUE;
                        }
                      }
      }
      if (nohands(mdat))
            return 0;

#define nomore(x) if(m.has_misc==x) continue;
      for(obj=mtmp->minvent; obj; obj=obj->nobj) {
            /* Monsters shouldn't recognize cursed items; this kludge is */
            /* necessary to prevent serious problems though... */
            if(obj->otyp == POT_GAIN_LEVEL && (!obj->cursed ||
                      (!mtmp->isgd && !mtmp->isshk && !mtmp->ispriest))) {
                  m.misc = obj;
                  m.has_misc = MUSE_POT_GAIN_LEVEL;
            }
            nomore(MUSE_BULLWHIP);
            if(obj->otyp == BULLWHIP && (MON_WEP(mtmp) == obj) &&
               distu(mtmp->mx,mtmp->my)==1 && uwep && !mtmp->mpeaceful) {
                  m.misc = obj;
                  m.has_misc = MUSE_BULLWHIP;
            }
            /* Note: peaceful/tame monsters won't make themselves
             * invisible unless you can see them.  Not really right, but...
             */
            nomore(MUSE_WAN_MAKE_INVISIBLE);
            if(obj->otyp == WAN_MAKE_INVISIBLE && obj->spe > 0 &&
                !mtmp->minvis && !mtmp->invis_blkd &&
                (!mtmp->mpeaceful || See_invisible) &&
                (mdat != &mons[PM_MEDUSA] || mtmp->mcan)) {
                  m.misc = obj;
                  m.has_misc = MUSE_WAN_MAKE_INVISIBLE;
            }
            nomore(MUSE_POT_INVISIBILITY);
            if(obj->otyp == POT_INVISIBILITY &&
                !mtmp->minvis && !mtmp->invis_blkd &&
                (!mtmp->mpeaceful || See_invisible) &&
                (mdat != &mons[PM_MEDUSA] || mtmp->mcan)) {
                  m.misc = obj;
                  m.has_misc = MUSE_POT_INVISIBILITY;
            }
            nomore(MUSE_WAN_SPEED_MONSTER);
            if(obj->otyp == WAN_SPEED_MONSTER && obj->spe > 0
                        && mtmp->mspeed != MFAST && !mtmp->isgd) {
                  m.misc = obj;
                  m.has_misc = MUSE_WAN_SPEED_MONSTER;
            }
            nomore(MUSE_POT_SPEED);
            if(obj->otyp == POT_SPEED && mtmp->mspeed != MFAST
                                          && !mtmp->isgd) {
                  m.misc = obj;
                  m.has_misc = MUSE_POT_SPEED;
            }
            nomore(MUSE_WAN_POLYMORPH);
            if(obj->otyp == WAN_POLYMORPH && obj->spe > 0 && !mtmp->cham
                        && monstr[monsndx(mdat)] < 6) {
                  m.misc = obj;
                  m.has_misc = MUSE_WAN_POLYMORPH;
            }
            nomore(MUSE_POT_POLYMORPH);
            if(obj->otyp == POT_POLYMORPH && !mtmp->cham
                        && monstr[monsndx(mdat)] < 6) {
                  m.misc = obj;
                  m.has_misc = MUSE_POT_POLYMORPH;
            }
      }
      return((boolean)(!!m.has_misc));
#undef nomore
}

int
use_misc(mtmp)
struct monst *mtmp;
{
      int i;
      struct obj *otmp = m.misc;
      boolean vis, vismon, oseen;
      char nambuf[BUFSZ];

      if ((i = precheck(mtmp, otmp)) != 0) return i;
      vis = cansee(mtmp->mx, mtmp->my);
      vismon = canseemon(mtmp);
      oseen = otmp && vismon;

      switch(m.has_misc) {
      case MUSE_POT_GAIN_LEVEL:
            mquaffmsg(mtmp, otmp);
            if (otmp->cursed) {
                if (Can_rise_up(mtmp->mx, mtmp->my, &u.uz)) {
                  register int tolev = depth(&u.uz)-1;
                  d_level tolevel;

                  get_level(&tolevel, tolev);
                  /* insurance against future changes... */
                  if(on_level(&tolevel, &u.uz)) goto skipmsg;
                  if (vismon) {
                      pline("%s rises up, through the %s!",
                          Monnam(mtmp), ceiling(mtmp->mx, mtmp->my));
                      if(!objects[POT_GAIN_LEVEL].oc_name_known
                        && !objects[POT_GAIN_LEVEL].oc_uname)
                        docall(otmp);
                  }
                  m_useup(mtmp, otmp);
                  migrate_to_level(mtmp, ledger_no(&tolevel),
                               MIGR_RANDOM, (coord *)0);
                  return 2;
                } else {
skipmsg:
                  if (vismon) {
                      pline("%s looks uneasy.", Monnam(mtmp));
                      if(!objects[POT_GAIN_LEVEL].oc_name_known
                        && !objects[POT_GAIN_LEVEL].oc_uname)
                        docall(otmp);
                  }
                  m_useup(mtmp, otmp);
                  return 2;
                }
            }
            if (vismon) pline("%s seems more experienced.", Monnam(mtmp));
            if (oseen) makeknown(POT_GAIN_LEVEL);
            m_useup(mtmp, otmp);
            if (!grow_up(mtmp,(struct monst *)0)) return 1;
                  /* grew into genocided monster */
            return 2;
      case MUSE_WAN_MAKE_INVISIBLE:
      case MUSE_POT_INVISIBILITY:
            if (otmp->otyp == WAN_MAKE_INVISIBLE) {
                mzapmsg(mtmp, otmp, TRUE);
                otmp->spe--;
            } else
                mquaffmsg(mtmp, otmp);
            /* format monster's name before altering its visibility */
            Strcpy(nambuf, See_invisible ? Monnam(mtmp) : mon_nam(mtmp));
            mon_set_minvis(mtmp);
            if (vismon && mtmp->minvis) { /* was seen, now invisible */
                if (See_invisible)
                  pline("%s body takes on a %s transparency.",
                        s_suffix(nambuf),
                        Hallucination ? "normal" : "strange");
                else
                  pline("Suddenly you cannot see %s.", nambuf);
                if (oseen) makeknown(otmp->otyp);
            }
            if (otmp->otyp == POT_INVISIBILITY) {
                if (otmp->cursed) you_aggravate(mtmp);
                m_useup(mtmp, otmp);
            }
            return 2;
      case MUSE_WAN_SPEED_MONSTER:
            mzapmsg(mtmp, otmp, TRUE);
            otmp->spe--;
            mon_adjust_speed(mtmp, 1);
            return 2;
      case MUSE_POT_SPEED:
            mquaffmsg(mtmp, otmp);
            /* note difference in potion effect due to substantially
               different methods of maintaining speed ratings:
               player's character becomes "very fast" temporarily;
               monster becomes "one stage faster" permanently */
            if (vismon)
                pline("%s is suddenly moving faster.", Monnam(mtmp));
            if (oseen) makeknown(POT_SPEED);
            mon_adjust_speed(mtmp, 1);
            m_useup(mtmp, otmp);
            return 2;
      case MUSE_WAN_POLYMORPH:
            mzapmsg(mtmp, otmp, TRUE);
            otmp->spe--;
            (void) newcham(mtmp, rndmonst());
            if (oseen) makeknown(WAN_POLYMORPH);
            return 2;
      case MUSE_POT_POLYMORPH:
            mquaffmsg(mtmp, otmp);
            if (vismon) pline("%s suddenly mutates!", Monnam(mtmp));
            (void) newcham(mtmp, rndmonst());
            if (oseen) makeknown(POT_POLYMORPH);
            m_useup(mtmp, otmp);
            return 2;
      case MUSE_POLY_TRAP:
            if (vismon)
                pline("%s deliberately %s onto a polymorph trap!",
                  Monnam(mtmp),
                  makeplural(locomotion(mtmp->data, "jump")));
            if (vis) seetrap(t_at(trapx,trapy));

            /*  don't use rloc() due to worms */
            remove_monster(mtmp->mx, mtmp->my);
            newsym(mtmp->mx, mtmp->my);
            place_monster(mtmp, trapx, trapy);
            if (mtmp->wormno) worm_move(mtmp);
            newsym(trapx, trapy);

            (void) newcham(mtmp, (struct permonst *)0);
            return 2;
      case MUSE_BULLWHIP:
            /* attempt to disarm hero */
            if (uwep && !rn2(5)) {
                const char *The_whip = vismon ? "The bullwhip" : "A whip";
                int where_to = rn2(4);
                struct obj *obj = uwep;
                const char *hand;
                char the_weapon[BUFSZ];

                Strcpy(the_weapon, the(xname(obj)));
                hand = body_part(HAND);
                if (bimanual(obj)) hand = makeplural(hand);

                if (vismon)
                  pline("%s flicks a bullwhip towards your %s!",
                        Monnam(mtmp), hand);
                if (obj->otyp == HEAVY_IRON_BALL) {
                  pline("%s fails to wrap around %s.",
                        The_whip, the_weapon);
                  return 1;
                }
                pline("%s wraps around %s you're wielding!",
                    The_whip, the_weapon);
                if (obj->cursed) {
                  pline("%s is welded to your %s%c",
                        (obj->quan == 1L) ? "It" : "They",
                        hand, !obj->bknown ? '!' : '.');
                  obj->bknown = 1;
                  where_to = 0;
                }
                if (!where_to) {
                  pline_The("whip slips free.");  /* not `The_whip' */
                  return 1;
                }
                freeinv(obj);
                uwepgone();
                switch (where_to) {
                  case 1:           /* onto floor beneath mon */
                      pline("%s yanks %s from your %s!", Monnam(mtmp),
                          the_weapon, hand);
                      if (obj->otyp == CRYSKNIFE && (!obj->oerodeproof || !rn2(10))) {
                        obj->otyp = WORM_TOOTH;
                        obj->oerodeproof = 0;
                      }
                      place_object(obj, mtmp->mx, mtmp->my);
                      break;
                  case 2:           /* onto floor beneath you */
                      pline("%s yanks %s to the %s!", Monnam(mtmp),
                          the_weapon, surface(u.ux, u.uy));
                      dropy(obj);
                      break;
                  case 3:           /* into mon's inventory */
                      pline("%s snatches %s!", Monnam(mtmp),
                          the_weapon);
                      (void) mpickobj(mtmp,obj);
                      break;
                }
                return 1;
            }
            return 0;
      case 0: return 0; /* i.e. an exploded wand */
      default: impossible("%s wanted to perform action %d?", Monnam(mtmp),
                  m.has_misc);
            break;
      }
      return 0;
}

STATIC_OVL void
you_aggravate(mtmp)
struct monst *mtmp;
{
      pline("For some reason, %s presence is known to you.",
            s_suffix(noit_mon_nam(mtmp)));
      cls();
#ifdef CLIPPING
      cliparound(mtmp->mx, mtmp->my);
#endif
      show_glyph(mtmp->mx, mtmp->my, mon_to_glyph(mtmp));
      display_self();
      You_feel("aggravated at %s.", noit_mon_nam(mtmp));
      display_nhwindow(WIN_MAP, TRUE);
      docrt();
      if (unconscious()) {
            multi = -1;
            nomovemsg =
                  "Aggravated, you are jolted into full consciousness.";
      }
      newsym(mtmp->mx,mtmp->my);
      if (!canspotmon(mtmp))
          map_invisible(mtmp->mx, mtmp->my);
}

int
rnd_misc_item(mtmp)
struct monst *mtmp;
{
      struct permonst *pm = mtmp->data;
      int difficulty = monstr[(monsndx(pm))];

      if(is_animal(pm) || attacktype(pm, AT_EXPL) || mindless(mtmp->data)
                  || pm->mlet == S_GHOST
# ifdef KOPS
                  || pm->mlet == S_KOP
# endif
            ) return 0;
      /* Unlike other rnd_item functions, we only allow _weak_ monsters
       * to have this item; after all, the item will be used to strengthen
       * the monster and strong monsters won't use it at all...
       */
      if (difficulty < 6 && !rn2(30))
          return rn2(6) ? POT_POLYMORPH : WAN_POLYMORPH;

      if (!rn2(40) && !nonliving(pm)) return AMULET_OF_LIFE_SAVING;

      switch (rn2(3)) {
            case 0:
                  if (mtmp->isgd) return 0;
                  return rn2(6) ? POT_SPEED : WAN_SPEED_MONSTER;
            case 1:
                  if (mtmp->mpeaceful && !See_invisible) return 0;
                  return rn2(6) ? POT_INVISIBILITY : WAN_MAKE_INVISIBLE;
            case 2:
                  return POT_GAIN_LEVEL;
      }
      /*NOTREACHED*/
      return 0;
}

boolean
searches_for_item(mon, obj)
struct monst *mon;
struct obj *obj;
{
      int typ = obj->otyp;

      if (is_animal(mon->data) ||
            mindless(mon->data) ||
            mon->data == &mons[PM_GHOST]) /* don't loot bones piles */
          return FALSE;

      if (typ == WAN_MAKE_INVISIBLE || typ == POT_INVISIBILITY)
          return (boolean)(!mon->minvis && !mon->invis_blkd);
      if (typ == WAN_SPEED_MONSTER || typ == POT_SPEED)
          return (boolean)(mon->mspeed != MFAST);

      switch (obj->oclass) {
      case WAND_CLASS:
          if (typ == WAN_DIGGING)
            return (boolean)(!is_floater(mon->data));
          if (typ == WAN_POLYMORPH)
            return (boolean)(monstr[monsndx(mon->data)] < 6);
          if (objects[typ].oc_dir == RAY ||
                typ == WAN_STRIKING ||
                typ == WAN_TELEPORTATION ||
                typ == WAN_CREATE_MONSTER)
            return TRUE;
          break;
      case POTION_CLASS:
          if (typ == POT_HEALING ||
                typ == POT_EXTRA_HEALING ||
                typ == POT_FULL_HEALING ||
                typ == POT_POLYMORPH ||
                typ == POT_GAIN_LEVEL ||
                typ == POT_PARALYSIS ||
                typ == POT_SLEEPING ||
                typ == POT_ACID ||
                typ == POT_BLINDNESS ||
                typ == POT_CONFUSION)
            return TRUE;
          break;
      case SCROLL_CLASS:
          if (typ == SCR_TELEPORTATION || typ == SCR_CREATE_MONSTER
                || typ == SCR_EARTH)
            return TRUE;
          break;
      case AMULET_CLASS:
          if (typ == AMULET_OF_LIFE_SAVING)
            return (boolean)(!nonliving(mon->data));
          if (typ == AMULET_OF_REFLECTION)
            return TRUE;
          break;
      case TOOL_CLASS:
          if (typ == PICK_AXE)
            return (boolean)needspick(mon->data);
          if (typ == UNICORN_HORN)
            return (boolean)(!obj->cursed && !is_unicorn(mon->data));
          if (typ == FROST_HORN || typ == FIRE_HORN)
            return TRUE;
          break;
      case FOOD_CLASS:
          if (typ == CORPSE)
            return (boolean)(((mon->misc_worn_check & W_ARMG) &&
                            touch_petrifies(&mons[obj->corpsenm])) ||
                        (!resists_ston(mon) &&
                            (obj->corpsenm == PM_LIZARD ||
                              acidic(&mons[obj->corpsenm]))));
          if (typ == EGG)
            return (boolean)(touch_petrifies(&mons[obj->corpsenm]));
          break;
      default:
          break;
      }

      return FALSE;
}

boolean
mon_reflects(mon,str)
struct monst *mon;
const char *str;
{
      struct obj *orefl = which_armor(mon, W_ARMS);

      if (orefl && orefl->otyp == SHIELD_OF_REFLECTION) {
          if (str) {
            pline(str, s_suffix(mon_nam(mon)), "shield");
            makeknown(SHIELD_OF_REFLECTION);
          }
          return TRUE;
      } else if ((orefl = which_armor(mon, W_AMUL)) &&
                        orefl->otyp == AMULET_OF_REFLECTION) {
          if (str) {
            pline(str, s_suffix(mon_nam(mon)), "amulet");
            makeknown(AMULET_OF_REFLECTION);
          }
          return TRUE;
      } else if ((orefl = which_armor(mon, W_ARM)) &&
            (orefl->otyp == SILVER_DRAGON_SCALES || orefl->otyp == SILVER_DRAGON_SCALE_MAIL)) {
          if (str)
            pline(str, s_suffix(mon_nam(mon)), "armor");
          return TRUE;
      } else if (mon->data == &mons[PM_SILVER_DRAGON]) {
          /* Silver dragons only reflect when mature; babies do not */
          if (str)
            pline(str, s_suffix(mon_nam(mon)), "scales");
          return TRUE;
      }
      return FALSE;
}

boolean
ureflects (fmt, str)
const char *fmt, *str;
{
      /* Check from outermost to innermost objects */
      if (EReflecting & W_ARMS) {
          if (fmt && str) {
            pline(fmt, str, "shield");
            makeknown(SHIELD_OF_REFLECTION);
          }
          return TRUE;
      } else if (EReflecting & W_WEP) {
          /* Due to wielded artifact weapon */
          if (fmt && str)
            pline(fmt, str, "weapon");
          return TRUE;
      } else if (EReflecting & W_AMUL) {
          if (fmt && str) {
            pline(fmt, str, "medallion");
            makeknown(AMULET_OF_REFLECTION);
          }
          return TRUE;
      } else if (EReflecting & W_ARM) {
          if (fmt && str)
            pline(fmt, str, "armor");
          return TRUE;
      } else if (youmonst.data == &mons[PM_SILVER_DRAGON]) {
          if (fmt && str)
            pline(fmt, str, "scales");
          return TRUE;
      }
      return FALSE;
}


/* TRUE if the monster ate something */
boolean
munstone(mon, by_you)
struct monst *mon;
boolean by_you;
{
      struct obj *obj;

      if (resists_ston(mon)) return FALSE;
      if (mon->meating || !mon->mcanmove || mon->msleeping) return FALSE;

      for(obj = mon->minvent; obj; obj = obj->nobj) {
          /* Monsters can also use potions of acid */
          if ((obj->otyp == POT_ACID) || (obj->otyp == CORPSE &&
                  (obj->corpsenm == PM_LIZARD || acidic(&mons[obj->corpsenm])))) {
            int nutrit = dog_nutrition(mon, obj); /* also sets meating */

            if (canseemon(mon)) {
                long save_quan = obj->quan;

                obj->quan = 1L;
                pline("%s %ss %s.", Monnam(mon),
                        (obj->otyp == POT_ACID) ? "quaff" : "eat",
                        distant_name(obj,doname));
                obj->quan = save_quan;
            } else if (flags.soundok)
                You_hear("%s.", (obj->otyp == POT_ACID) ? "drinking" : "chewing");
            m_useup(mon, obj);
            if (((obj->otyp == POT_ACID) || acidic(&mons[obj->corpsenm])) &&
                        !resists_acid(mon)) {
                mon->mhp -= rnd(15);
                pline("%s has a very bad case of stomach acid.",
                  Monnam(mon));
            }
            if (mon->mhp <= 0) {
                pline("%s dies!", Monnam(mon));
                if (by_you) xkilled(mon, 0);
                else mondead(mon);
                return TRUE;
            }
            if (canseemon(mon)) {
                if (Hallucination)
            pline("What a pity - %s just ruined a future piece of art!",
                  mon_nam(mon));
                else
                  pline("%s seems limber!", Monnam(mon));
            }
            if (mon->mtame && !mon->isminion) {
                struct edog *edog = EDOG(mon);

                if (edog->hungrytime < moves) edog->hungrytime = moves;
                edog->hungrytime += nutrit;
                mon->mconf = 0;
            }
            mon->mlstmv = monstermoves; /* it takes a turn */
            return TRUE;
          }
      }
      return FALSE;
}

/*muse.c*/

Generated by  Doxygen 1.6.0   Back to index