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

monmove.c

/*    SCCS Id: @(#)monmove.c  3.2   96/07/29    */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed.  See license for details. */

/*
**    Japanese version Copyright
**    (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000
**    changing point is marked `JP' (94/6/7)
**    JNetHack may be freely redistributed.  See license for details. 
*/

#include "hack.h"
#include "mfndpos.h"
#include "artifact.h"

#ifdef OVL0

static int FDECL(disturb,(struct monst *));
static void FDECL(distfleeck,(struct monst *,int *,int *,int *));
static int FDECL(m_arrival, (struct monst *));
static void FDECL(watch_on_duty,(struct monst *));

#endif /* OVL0 */
#ifdef OVLB

boolean /* TRUE : mtmp died */
mb_trapped(mtmp)
register struct monst *mtmp;
{
      if (flags.verbose) {
          if (cansee(mtmp->mx, mtmp->my))
/*JP        pline("KABOOM!!  You see a door explode.");*/
            pline("バーン!ドアが爆発するのを見た.");

          else if (flags.soundok)
/*JP        You_hear("a distant explosion.");*/
            pline("遠方で爆発する音を聞いた.");
      }
      wake_nearto(mtmp->mx, mtmp->my, 7*7);
      mtmp->mstun = 1;
      mtmp->mhp -= rnd(15);
      if(mtmp->mhp <= 0) {
            mondied(mtmp);
            if (mtmp->mhp > 0) /* lifesaved */
                  return(FALSE);
            else
                  return(TRUE);
      }
      return(FALSE);
}

#endif /* OVLB */
#ifdef OVL0

static void
watch_on_duty(mtmp)
register struct monst *mtmp;
{
      register s_level *slev = Is_special(&u.uz);
      int   x, y;

      if(slev && slev->flags.town && mtmp->mpeaceful &&
         mtmp->mcansee && m_canseeu(mtmp) && !rn2(3)) {

          if(picking_lock(&x, &y) && IS_DOOR(levl[x][y].typ) &&
             (levl[x][y].doormask & D_LOCKED)) {

            if(couldsee(mtmp->mx, mtmp->my)) {

/*JP          pline("%s yells:", Amonnam(mtmp));*/
              pline("%sは叫んだ:", Amonnam(mtmp));
              if(levl[x][y].looted & D_WARNED) {
/*JP              verbalize("Halt, thief!  You're under arrest!");*/
                  verbalize("待て!ぬすっと!おまえを逮捕する!");
                  (void) angry_guards(!(flags.soundok));
              } else {
/*JP              verbalize("Hey, stop picking that lock!");*/
                  verbalize("おい,鍵を勝手に開けるんじゃない!");
                  levl[x][y].looted |=  D_WARNED;
              }
              stop_occupation();
            }
          }
      }
}

#endif /* OVL0 */
#ifdef OVL1

int
dochugw(mtmp)
      register struct monst *mtmp;
{
      register int x = mtmp->mx, y = mtmp->my;
      boolean already_saw_mon = !occupation ? 0 : canspotmon(mtmp);
      int rd = dochug(mtmp);
      int dd;

      if(Warning && !rd && !mtmp->mpeaceful &&
                  (dd = distu(mtmp->mx,mtmp->my)) < distu(x,y) &&
                  dd < 100 && !canseemon(mtmp)) {
          /* Note: this assumes we only want to warn against the monster to
           * which the weapon does extra damage, as there is no "monster
           * which the weapon warns against" field.
           */
          if (spec_ability(uwep, SPFX_WARN) && spec_dbon(uwep, mtmp, 1))
            warnlevel = 100;
          else if ((int) (mtmp->m_lev / 4) > warnlevel)
            warnlevel = (mtmp->m_lev / 4);
      }
      
      /* a similar check is in monster_nearby() in hack.c */
      /* check whether hero notices monster and stops current activity */
      if (occupation && !rd && !Confusion &&
          (!mtmp->mpeaceful || Hallucination) &&
          /* it's close enough to be a threat */
          distu(mtmp->mx,mtmp->my) <= (BOLT_LIM+1)*(BOLT_LIM+1) &&
          /* and either couldn't see it before, or it was too far away */
          (!already_saw_mon || !couldsee(x,y) ||
            distu(x,y) > (BOLT_LIM+1)*(BOLT_LIM+1)) &&
          /* can see it now, or sense it and would normally see it */
          (canseemon(mtmp) ||
            (sensemon(mtmp) && couldsee(mtmp->mx,mtmp->my))) &&
          !noattacks(mtmp->data) && !onscary(u.ux, u.uy, mtmp))
            stop_occupation();

      return(rd);
}

#endif /* OVL1 */
#ifdef OVL2

boolean
onscary(x, y, mtmp)
int x, y;
struct monst *mtmp;
{
      if (mtmp->isshk || mtmp->isgd || mtmp->iswiz || !mtmp->mcansee ||
                  mtmp->mpeaceful || mtmp->data->mlet == S_HUMAN ||
                  is_lminion(mtmp->data) || is_rider(mtmp->data) ||
                  mtmp->data == &mons[PM_MINOTAUR])
            return(FALSE);

      return (boolean)(sobj_at(SCR_SCARE_MONSTER, x, y)
#ifdef ELBERETH
                   || sengr_at("Elbereth", x, y)
#endif
                   || (mtmp->data->mlet == S_VAMPIRE
                       && IS_ALTAR(levl[x][y].typ)));
}

#endif /* OVL2 */
#ifdef OVL0

/* regenerate lost hit points */
void
mon_regen(mon, digest_meal)
struct monst *mon;
boolean digest_meal;
{
      if (mon->mhp < mon->mhpmax &&
          (moves % 20 == 0 || regenerates(mon->data))) mon->mhp++;
      if (mon->mspec_used) mon->mspec_used--;
      if (digest_meal) {
          if (mon->meating) mon->meating--;
      }
}

/*
 * Possibly awaken the given monster.  Return a 1 if the monster has been
 * jolted awake.
 */
static int
disturb(mtmp)
      register struct monst *mtmp;
{
      /*
       * + Ettins are hard to surprise.
       * + Nymphs, jabberwocks, and leprechauns do not easily wake up.
       *
       * Wake up if:
       *    in direct LOS                                   AND
       *    within 10 squares                         AND
       *    not stealthy or (mon is an ettin and 9/10)            AND
       *    (mon is not a nymph, jabberwock, or leprechaun) or 1/50     AND
       *    Aggravate or mon is (dog or human) or
       *        (1/7 and mon is not mimicing furniture or object)
       */
      if(couldsee(mtmp->mx,mtmp->my) &&
            distu(mtmp->mx,mtmp->my) <= 100 &&
            (!Stealth || (mtmp->data == &mons[PM_ETTIN] && rn2(10))) &&
            (!(mtmp->data->mlet == S_NYMPH
                  || mtmp->data == &mons[PM_JABBERWOCK]
                  || mtmp->data->mlet == S_LEPRECHAUN) || !rn2(50)) &&
            (Aggravate_monster
                  || (mtmp->data->mlet == S_DOG ||
                        mtmp->data->mlet == S_HUMAN)
                  || (!rn2(7) && mtmp->m_ap_type != M_AP_FURNITURE &&
                        mtmp->m_ap_type != M_AP_OBJECT) )) {

            mtmp->msleep = 0;
            return(1);
      }
      return(0);
}

static void
distfleeck(mtmp,inrange,nearby,scared)
register struct monst *mtmp;
int *inrange, *nearby, *scared;
{
      int seescaryx, seescaryy;

      *inrange = (dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <=
                                          (BOLT_LIM * BOLT_LIM));
      *nearby = *inrange && monnear(mtmp, mtmp->mux, mtmp->muy);

      /* Note: if your image is displaced, the monster sees the Elbereth
       * at your displaced position, thus never attacking your displaced
       * position, but possibly attacking you by accident.  If you are
       * invisible, it sees the Elbereth at your real position, thus never
       * running into you by accident but possibly attacking the spot
       * where it guesses you are.
       */
      if (!mtmp->mcansee || (Invis && !perceives(mtmp->data))) {
            seescaryx = mtmp->mux;
            seescaryy = mtmp->muy;
      } else {
            seescaryx = u.ux;
            seescaryy = u.uy;
      }
      *scared = (*nearby && (onscary(seescaryx, seescaryy, mtmp) ||
                         (!mtmp->mpeaceful &&
                            in_your_sanctuary(mtmp->mx, mtmp->my))));

      if(*scared && !mtmp->mflee) {
            if (!sticks(uasmon))
                  unstuck(mtmp);    /* monster lets go when fleeing */
            mtmp->mflee = 1;
#ifdef STUPID
            if (rn2(7))
                mtmp->mfleetim = rnd(10);
            else
                mtmp->mfleetim = rnd(100);
#else
            mtmp->mfleetim = rnd(rn2(7) ? 10 : 100);
#endif
      }

}

/* perform a special one-time action for a monster; returns -1 if nothing
   special happened, 0 if monster uses up its turn, 1 if monster is killed */
static int
m_arrival(mon)
struct monst *mon;
{
      mon->mstrategy &= ~STRAT_ARRIVE;    /* always reset */

      return -1;
}

/* returns 1 if monster died moving, 0 otherwise */
/* The whole dochugw/m_move/distfleeck/mfndpos section is serious spaghetti
 * code. --KAA
 */
int
dochug(mtmp)
register struct monst *mtmp;
{
      register struct permonst *mdat;
      register int tmp=0;
      int inrange, nearby, scared;

/*    Pre-movement adjustments      */

      if(mtmp->cham && !rn2(6))     /* polymorph chameleons */
          (void) newcham(mtmp, (struct permonst *)0);

      /* regenerate monsters */
      mon_regen(mtmp, FALSE);

      /* polymorph lycanthropes */
      were_change(mtmp);

      mdat = mtmp->data; /* after newcham and were_change */

      if (mtmp->mstrategy & STRAT_ARRIVE) {
          int res = m_arrival(mtmp);
          if (res >= 0) return res;
      }

      /* check for waitmask status change */
      if ((mtmp->mstrategy & STRAT_WAITFORU) &&
            (m_canseeu(mtmp) || mtmp->mhp < mtmp->mhpmax))
          mtmp->mstrategy &= ~STRAT_WAITFORU;

      /* update quest status flags */
      quest_stat_check(mtmp);

      if (!mtmp->mcanmove || (mtmp->mstrategy & STRAT_WAITMASK)) {
          if (Hallucination) newsym(mtmp->mx,mtmp->my);
          if (mtmp->mcanmove && (mtmp->mstrategy & STRAT_CLOSE) &&
             !mtmp->msleep && monnear(mtmp, u.ux, u.uy))
            quest_talk(mtmp); /* give the leaders a chance to speak */
          return(0);    /* other frozen monsters can't do anything */
      }

      /* there is a chance we will wake it */
      if(mtmp->msleep && !disturb(mtmp)) {
            if (Hallucination) newsym(mtmp->mx,mtmp->my);
            return(0);
      }

      /* not frozen or sleeping: wipe out texts written in the dust */
      wipe_engr_at(mtmp->mx, mtmp->my, 1);

      /* confused monsters get unconfused with small probability */
      if (mtmp->mconf && !rn2(50)) mtmp->mconf = 0;

      /* stunned monsters get un-stunned with larger probability */
      if (mtmp->mstun && !rn2(10)) mtmp->mstun = 0;

      /* some monsters teleport */
      if (mtmp->mflee && !rn2(40) && can_teleport(mdat) && !mtmp->iswiz) {
            rloc(mtmp);
            return(0);
      }
      if (mdat->msound == MS_SHRIEK && !um_dist(mtmp->mx, mtmp->my, 1))
          m_respond(mtmp);
      if (mdat == &mons[PM_MEDUSA] && cansee(mtmp->mx, mtmp->my))
          m_respond(mtmp);
      if (mtmp->mhp <= 0) return(1); /* m_respond gaze can kill medusa */
      if (mdat->mmove < rnd(6)) return(0);

      /* fleeing monsters might regain courage */
      if (mtmp->mflee && !mtmp->mfleetim
         && mtmp->mhp == mtmp->mhpmax && !rn2(25)) mtmp->mflee = 0;

      set_apparxy(mtmp);
      /* Must be done after you move and before the monster does.  The
       * set_apparxy() call in m_move() doesn't suffice since the variables
       * inrange, etc. all depend on stuff set by set_apparxy().
       */

      /* Monsters that want to acquire things */
      /* may teleport, so do it before inrange is set */
      if(is_covetous(mdat)) (void) tactics(mtmp);

      /* check distance and scariness of attacks */
      distfleeck(mtmp,&inrange,&nearby,&scared);

      if(find_defensive(mtmp)) {
            if (use_defensive(mtmp) != 0)
                  return 1;
      } else if(find_misc(mtmp)) {
            if (use_misc(mtmp) != 0)
                  return 1;
      }

      /* Demonic Blackmail! */
      if(nearby && mdat->msound == MS_BRIBE &&
         mtmp->mpeaceful && !mtmp->mtame && !u.uswallow) {
            if (mtmp->mux != u.ux || mtmp->muy != u.uy) {
/*JP              pline("%s whispers at thin air.",
                      cansee(mtmp->mux, mtmp->muy) ? Monnam(mtmp) : "It");*/
                  pline("%sがささやいた.",
                      cansee(mtmp->mux, mtmp->muy) ? Monnam(mtmp) : "何者か");

                  if (is_demon(uasmon)) rloc(mtmp);
                    /* "Good hunting, brother" */
                  else {
                      mtmp->minvis = mtmp->perminvis = 0;
                      /* Why?  For the same reason in real demon talk */
/*JP                  pline("%s gets angry!", Amonnam(mtmp));*/
                      pline("%sは怒った!", Amonnam(mtmp));
                      mtmp->mpeaceful = 0;
                      /* since no way is an image going to pay it off */
                  }
            } else if(demon_talk(mtmp)) return(1);    /* you paid it off */
      }

      /* the watch will look around and see if you are up to no good :-) */
      if (mdat == &mons[PM_WATCHMAN] || mdat == &mons[PM_WATCH_CAPTAIN])
            watch_on_duty(mtmp);

      else if (mdat == &mons[PM_MIND_FLAYER] && !rn2(20)) {
            struct monst *m2, *nmon = (struct monst *)0;

            if (canseemon(mtmp))
/*JP              pline("%s concentrates.", Monnam(mtmp));*/
                  pline("%sは精神を集中している.", Monnam(mtmp));
            if (distu(mtmp->mx, mtmp->my) > BOLT_LIM * BOLT_LIM) {
/*JP              You("sense a faint wave of psychic energy.");*/
                  You("サイコエネルギーの波動を感じた.");
                  goto toofar;
            }
/*JP        pline("A wave of psychic energy pours over you!");*/
            pline("あなたはサイコエネルギーの波動を浴びた!");
            if (mtmp->mpeaceful &&
                (!Conflict || resist(mtmp, RING_CLASS, 0, 0)))
/*JP              pline("It feels quite soothing.");*/
                  pline("心がなごんだ.");
            else {
                  register boolean m_sen = sensemon(mtmp);

                  if (m_sen || (Telepat && rn2(2)) || !rn2(10)) {
                        int dmg;
/*JP                    pline("It locks on to your %s!",
                              m_sen ? "telepathy" :
                              Telepat ? "latent telepathy" : "mind");*/
                        pline("それはあなたの%sを直撃した!",
                              m_sen ? "テレパシー能力" :
                              Telepat ? "潜在能力" : "精神");
                        dmg = rnd(15);
                        if (Half_spell_damage) dmg = (dmg+1) / 2;
/*JP                    losehp(dmg, "psychic blast", KILLED_BY_AN);*/
                        losehp(dmg, "サイコ攻撃で", KILLED_BY_AN);
                  }
            }
            for(m2=fmon; m2; m2 = nmon) {
                  nmon = m2->nmon;
                  if (m2->mpeaceful != mtmp->mpeaceful) continue;
                  if (mindless(m2->data)) continue;
                  if (m2 == mtmp) continue;
                  if ((telepathic(m2->data) &&
                      (rn2(2) || m2->mblinded)) || !rn2(10)) {
                        if (cansee(m2->mx, m2->my))
/*JP                        pline("It locks on to %s.", mon_nam(m2));*/
                            pline("%sを直撃した.", mon_nam(m2));
                        m2->mhp -= rnd(15);
                        if (m2->mhp <= 0)
                            monkilled(m2, "", AD_DRIN);
                  }
            }
      }
toofar:
      /* If monster is nearby you, and has to wield a weapon, do so.   This
       * costs the monster a move, of course.
       */
      if((!mtmp->mpeaceful || Conflict) && inrange &&
         dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <= 8
         && attacktype(mdat, AT_WEAP)) {
          if (mtmp->weapon_check == NEED_WEAPON || !MON_WEP(mtmp)) {
            mtmp->weapon_check = NEED_HTH_WEAPON;
            if (mon_wield_item(mtmp) != 0) return(0);
          }
      }

/*    Now the actual movement phase */

      if(!nearby || mtmp->mflee || scared ||
         mtmp->mconf || mtmp->mstun || (mtmp->minvis && !rn2(3)) ||
         (mdat->mlet == S_LEPRECHAUN && !u.ugold && (mtmp->mgold || rn2(2))) ||
         (is_wanderer(mdat) && !rn2(4)) || (Conflict && !mtmp->iswiz) ||
         (!mtmp->mcansee && !rn2(4)) || mtmp->mpeaceful) {

            tmp = m_move(mtmp, 0);
            distfleeck(mtmp,&inrange,&nearby,&scared);      /* recalc */

            switch (tmp) {
                case 0: /* no movement, but it can still attack you */
                case 3: /* absolutely no movement */
                        /* for pets, case 0 and 3 are equivalent */
                  /* During hallucination, monster appearance should
                   * still change - even if it doesn't move.
                   */
                  if(Hallucination) newsym(mtmp->mx,mtmp->my);
                  break;
                case 1: /* monster moved */
                  /* Maybe it stepped on a trap and fell asleep... */
                  if(mtmp->msleep || !mtmp->mcanmove) return(0);
                  if(!nearby &&
                    (ranged_attk(mdat) || find_offensive(mtmp)))
                      break;
                  else if(mdat->mmove <= 12) {
                      /* a monster that's digesting you can move at the
                       * same time -dlc
                       */
                      if(u.uswallow && mtmp == u.ustuck)
                        return(mattacku(mtmp));
                      return(0);
                  }
                  break;
                case 2: /* monster died */
                  return(1);
            }
      }

/*    Now, attack the player if possible - one attack set per monst     */

      if (!mtmp->mpeaceful ||
          (Conflict && !resist(mtmp, RING_CLASS, 0, 0))) {
          if(inrange && !noattacks(mdat) && u.uhp > 0 && !scared && tmp != 3)
            if(mattacku(mtmp)) return(1); /* monster died (e.g. exploded) */

          if(mtmp->wormno) wormhitu(mtmp);
      }
      /* special speeches for quest monsters */
      if(!mtmp->msleep && mtmp->mcanmove && nearby)
            quest_talk(mtmp);
      else
          /* extra emotional attack for vile monsters */
          if(inrange && mtmp->data->msound == MS_CUSS && !mtmp->mpeaceful &&
             couldsee(mtmp->mx, mtmp->my) && !mtmp->minvis && !rn2(5))
            cuss(mtmp);

      /* extra movement for fast monsters */
      if(mdat->mmove-12 > rnd(12)) tmp = m_move(mtmp, 1);
      return(tmp == 2);
}

static NEARDATA const char practical[] = { WEAPON_CLASS, ARMOR_CLASS, GEM_CLASS, FOOD_CLASS, 0 };
static NEARDATA const char magical[] = {
      AMULET_CLASS, POTION_CLASS, SCROLL_CLASS, WAND_CLASS, RING_CLASS,
      SPBOOK_CLASS, 0 };
static NEARDATA const char indigestion[] = { BALL_CLASS, ROCK_CLASS, 0 };
static NEARDATA const char boulder_class[] = { ROCK_CLASS, 0 };
static NEARDATA const char gem_class[] = { GEM_CLASS, 0 };

boolean
itsstuck(mtmp)
register struct monst *mtmp;
{
      if (sticks(uasmon) && mtmp==u.ustuck && !u.uswallow) {
/*JP        pline("%s cannot escape from you!", Monnam(mtmp));*/
            pline("%sはあなたから逃げられない!", Monnam(mtmp));
            return(TRUE);
      }
      return(FALSE);
}

/* Return values:
 * 0: did not move, but can still attack and do other stuff.
 * 1: moved, possibly can attack.
 * 2: monster died.
 * 3: did not move, and can't do anything else either.
 */
int
m_move(mtmp, after)
register struct monst *mtmp;
register int after;
{
      register int appr;
      xchar gx,gy,nix,niy,chcnt;
      int chi;    /* could be schar except for stupid Sun-2 compiler */
      boolean likegold=0, likegems=0, likeobjs=0, likemagic=0, conceals=0;
      boolean likerock=0, can_tunnel=0;
      boolean can_open=0, can_unlock=0, doorbuster=0;
      boolean uses_items=0;
      struct permonst *ptr;
      struct monst *mtoo;
      schar mmoved = 0; /* not strictly nec.: chi >= 0 will do */
      long info[9];
      long flag;
      int  omx = mtmp->mx, omy = mtmp->my;
      struct obj *mw_tmp;

      if(mtmp->mtrapped) {
          int i = mintrap(mtmp);
          if(i >= 2) { newsym(mtmp->mx,mtmp->my); return(2); }/* it died */
          if(i == 1) return(0);     /* still in trap, so didn't move */
      }
      ptr = mtmp->data; /* mintrap() can change mtmp->data -dlc */

      if (mtmp->meating) {
          mtmp->meating--;
          return 3;                 /* still eating */
      }
      if (hides_under(ptr) && OBJ_AT(mtmp->mx, mtmp->my) && rn2(10))
          return 0;           /* do not leave hiding place */

      set_apparxy(mtmp);
      /* where does mtmp think you are? */
      /* Not necessary if m_move called from this file, but necessary in
       * other calls of m_move (ex. leprechauns dodging)
       */
      can_tunnel = tunnels(ptr) &&
#ifdef REINCARNATION
            !Is_rogue_level(&u.uz) &&
#endif
            (!needspick(ptr) || m_carrying(mtmp, PICK_AXE));
      can_open = !(nohands(ptr) || verysmall(ptr));
      can_unlock = ((can_open && m_carrying(mtmp, SKELETON_KEY)) || mtmp->iswiz);
      doorbuster = is_giant(ptr);
      if(mtmp->wormno) goto not_special;
      /* my dog gets special treatment */
      if(mtmp->mtame) {
          mmoved = dog_move(mtmp, after);
          goto postmov;
      }

      /* likewise for shopkeeper */
      if(mtmp->isshk) {
          mmoved = shk_move(mtmp);
          if(mmoved == -2) return(2);
          if(mmoved >= 0) goto postmov;
          mmoved = 0;         /* follow player outside shop */
      }

      /* and for the guard */
      if(mtmp->isgd) {
          mmoved = gd_move(mtmp);
          if(mmoved == -2) return(2);
          if(mmoved >= 0) goto postmov;
          mmoved = 0;
      }

      /* and the acquisitive monsters get special treatment */
      if(is_covetous(ptr)) {
          xchar tx = STRAT_GOALX(mtmp->mstrategy),
              ty = STRAT_GOALY(mtmp->mstrategy);
          struct monst *intruder = m_at(tx, ty);
          /*
           * if there's a monster on the object or in possesion of it,
           * attack it.
           */
          if((dist2(mtmp->mx, mtmp->my, tx, ty) < 2) &&
             intruder && (intruder != mtmp)) {

            if(mattackm(mtmp, intruder) == 2) return(2);
            mmoved = 1;
          } else mmoved = 0;
          goto postmov;
      }

      /* and for the priest */
      if(mtmp->ispriest) {
          mmoved = pri_move(mtmp);
          if(mmoved == -2) return(2);
          if(mmoved >= 0) goto postmov;
          mmoved = 0;
      }

#ifdef MAIL
      if(ptr == &mons[PM_MAIL_DAEMON]) {
          if(flags.soundok && canseemon(mtmp))
/*JP        verbalize("I'm late!");*/
            verbalize("遅くなってすまない!");
          mongone(mtmp);
          return(2);    
      }
#endif

      /* teleport if that lies in our nature */
      if(ptr == &mons[PM_TENGU] && !rn2(5) && !mtmp->mcan) {
          if(mtmp->mhp < 7 || mtmp->mpeaceful || rn2(2))
            rloc(mtmp);
          else
            mnexto(mtmp);
          mmoved = 1;
          goto postmov;
      }
not_special:
      if(u.uswallow && !mtmp->mflee && u.ustuck != mtmp) return(1);
      omx = mtmp->mx;
      omy = mtmp->my;
      gx = mtmp->mux;
      gy = mtmp->muy;
      appr = mtmp->mflee ? -1 : 1;
      if (mtmp->mconf || (u.uswallow && mtmp == u.ustuck))
            appr = 0;
      else {
            boolean should_see = (couldsee(omx, omy) &&
                              (levl[gx][gy].lit ||
                               !levl[omx][omy].lit) &&
                              (dist2(omx, omy, gx, gy) <= 36));

            if (!mtmp->mcansee ||
                (should_see && Invis && !perceives(ptr) && rn2(11)) ||
                (u.usym == S_MIMIC_DEF) || u.uundetected ||
                (u.usym == 0 && !likes_gold(ptr)) ||  /* 0 => gold */
                (mtmp->mpeaceful && !mtmp->isshk) ||  /* allow shks to follow */
                ((ptr->mlet == S_STALKER || ptr->mlet == S_BAT ||
                  ptr->mlet == S_LIGHT) && !rn2(3)))
                  appr = 0;
      
            if(monsndx(ptr) == PM_LEPRECHAUN && (appr == 1) &&
               (mtmp->mgold > u.ugold))
                  appr = -1;

            if (!should_see && can_track(ptr)) {
                  register coord *cp;

                  cp = gettrack(omx,omy);
                  if (cp) {
                        gx = cp->x;
                        gy = cp->y;
                  }
            }
      }

      if ((!mtmp->mpeaceful || !rn2(10))
#ifdef REINCARNATION
                            && (!Is_rogue_level(&u.uz))
#endif
                                              ) {
          boolean in_line = lined_up(mtmp) &&
            (distmin(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <=
                (throws_rocks(uasmon) ? 20 : ACURRSTR/2+1)
            );

          if (appr != 1 || !in_line) {
            /* Monsters in combat won't pick stuff up, avoiding the
             * situation where you toss arrows at it and it has nothing
             * better to do than pick the arrows up.
             */
            register int pctload = (curr_mon_load(mtmp) * 100) /
                  max_mon_load(mtmp);

            /* look for gold or jewels nearby */
            likegold = (likes_gold(ptr) && pctload < 95);
            likegems = (likes_gems(ptr) && pctload < 85);
            uses_items = (!mindless(ptr) && !is_animal(ptr)
                  && pctload < 75);
            likeobjs = (likes_objs(ptr) && pctload < 75);
            likemagic = (likes_magic(ptr) && pctload < 85);
            likerock = (throws_rocks(ptr) && pctload < 50);
            conceals = hides_under(ptr);
          }
      }

#define SQSRCHRADIUS    5

      { register int minr = SQSRCHRADIUS; /* not too far away */
      register struct obj *otmp;
      register int xx, yy;
      int oomx, oomy, lmx, lmy;

      /* cut down the search radius if it thinks character is closer. */
      if(distmin(mtmp->mux, mtmp->muy, omx, omy) < SQSRCHRADIUS &&
          !mtmp->mpeaceful) minr--;
      /* guards shouldn't get too distracted */
      if(!mtmp->mpeaceful && is_mercenary(ptr)) minr = 1;

      if((likegold || likegems || likeobjs || likemagic || likerock || conceals)
            && (!*in_rooms(omx, omy, SHOPBASE) || (!rn2(25) && !mtmp->isshk))) {
      look_for_obj:
          oomx = min(COLNO-1, omx+minr);
          oomy = min(ROWNO-1, omy+minr);
          lmx = max(1, omx-minr);
          lmy = max(0, omy-minr);
          for(otmp = fobj; otmp; otmp = otmp->nobj) {
            /* monsters may pick rocks up, but won't go out of their way
               to grab them; this might hamper sling wielders, but it cuts
               down on move overhead by filtering out most common item */
            if (otmp->otyp == ROCK) continue;
            xx = otmp->ox;
            yy = otmp->oy;
            /* Nymphs take everything.  Most other creatures should not
             * pick up corpses except as a special case like in
             * searches_for_item().  We need to do this check in
             * mpickstuff() as well.
             */
            if(xx >= lmx && xx <= oomx && yy >= lmy && yy <= oomy) {
                /* don't get stuck circling around an object that's underneath
                   an immobile or hidden monster; paralysis victims excluded */
                if ((mtoo = m_at(xx,yy)) != 0 &&
                  (mtoo->msleep || mtoo->mundetected ||
                   (mtoo->mappearance && !mtoo->iswiz) ||
                   !mtoo->data->mmove)) continue;

                if(((likegold && otmp->oclass == GOLD_CLASS) ||
                   (likeobjs && index(practical, otmp->oclass) &&
                  (otmp->otyp != CORPSE || (ptr->mlet == S_NYMPH
                     && !is_rider(&mons[otmp->corpsenm])))) ||
                   (likemagic && index(magical, otmp->oclass)) ||
                   (uses_items && searches_for_item(mtmp, otmp)) ||
                   (likerock && otmp->otyp == BOULDER) ||
                   (likegems && otmp->oclass == GEM_CLASS &&
                  objects[otmp->otyp].oc_material != MINERAL) ||
                   (conceals && !cansee(otmp->ox,otmp->oy)) ||
                   (ptr == &mons[PM_GELATINOUS_CUBE] &&
                  !index(indigestion, otmp->oclass) &&
                  !(otmp->otyp == CORPSE &&
                    otmp->corpsenm == PM_COCKATRICE))
                  ) && touch_artifact(otmp,mtmp)) {
                  if(can_carry(mtmp,otmp) &&
                     (throws_rocks(ptr) ||
                        !sobj_at(BOULDER,xx,yy)) &&
                     (ptr->mlet != S_UNICORN ||
                      objects[otmp->otyp].oc_material == GEMSTONE) &&
                     /* Don't get stuck circling an Elbereth */
                     !(onscary(xx, yy, mtmp))) {
                      minr = distmin(omx,omy,xx,yy);
                      oomx = min(COLNO-1, omx+minr);
                      oomy = min(ROWNO-1, omy+minr);
                      lmx = max(1, omx-minr);
                      lmy = max(0, omy-minr);
                      gx = otmp->ox;
                      gy = otmp->oy;
                      if (gx == omx && gy == omy) {
                        mmoved = 3; /* actually unnecessary */
                        goto postmov;
                      }
                  }
                }
            }
          }
      } else if(likegold) {
          /* don't try to pick up anything else, but use the same loop */
          uses_items = 0;
          likegems = likeobjs = likemagic = likerock = conceals = 0;
          goto look_for_obj;
      }

      if(minr < SQSRCHRADIUS && appr == -1) {
          if(distmin(omx,omy,mtmp->mux,mtmp->muy) <= 3) {
            gx = mtmp->mux;
            gy = mtmp->muy;
          } else
            appr = 1;
      }
      }

      if (can_tunnel && needspick(ptr) &&
            (mw_tmp = MON_WEP(mtmp)) != 0 && mw_tmp->otyp != PICK_AXE) {
          /* not wielding its pick-axe yet; if its current weapon
             is cursed, then it can't switch; when approaching,
             it won't switch if the desired destination can be reached
             without digging [to inhibit repeated pick/weapon flip-flop] */
          if (mw_tmp->cursed || (appr > 0 && m_cansee(mtmp, gx, gy)))
            can_tunnel = FALSE;
      }

      nix = omx;
      niy = omy;
      flag = 0L;
      if (mtmp->mpeaceful && (!Conflict || resist(mtmp, RING_CLASS, 0, 0)))
          flag |= (ALLOW_SANCT | ALLOW_SSM);
      else flag |= ALLOW_U;
      if (ptr->mlet == S_UNICORN) flag |= NOTONL;
      if (passes_walls(ptr)) flag |= (ALLOW_WALL | ALLOW_ROCK);
      if (can_tunnel) flag |= ALLOW_DIG;
      if (is_human(ptr) || ptr == &mons[PM_MINOTAUR]) flag |= ALLOW_SSM;
      if (is_undead(ptr) && ptr->mlet != S_GHOST) flag |= NOGARLIC;
      if (throws_rocks(ptr)) flag |= ALLOW_ROCK;
      if (can_open) flag |= OPENDOOR;
      if (can_unlock) flag |= UNLOCKDOOR;
      if (doorbuster) flag |= BUSTDOOR;
      {
          register int i, j, nx, ny, nearer;
          int jcnt, cnt;
          int ndist, nidist;
          register coord *mtrk;
          coord poss[9];

          cnt = mfndpos(mtmp, poss, info, flag);
          chcnt = 0;
          jcnt = min(MTSZ, cnt-1);
          chi = -1;
          nidist = dist2(nix,niy,gx,gy);
          /* allow monsters be shortsighted on some levels for balance */
          if(!mtmp->mpeaceful && level.flags.shortsighted &&
             nidist > (couldsee(nix,niy) ? 144 : 36) && appr == 1) appr = 0;

          for(i=0; i < cnt; i++) {
            nx = poss[i].x;
            ny = poss[i].y;

            if (appr != 0) {
                mtrk = &mtmp->mtrack[0];
                for(j=0; j < jcnt; mtrk++, j++)
                  if(nx == mtrk->x && ny == mtrk->y)
                      if(rn2(4*(cnt-j)))
                        goto nxti;
            }

            nearer = ((ndist = dist2(nx,ny,gx,gy)) < nidist);

            if((appr == 1 && nearer) || (appr == -1 && !nearer) ||
               (!appr && !rn2(++chcnt)) || !mmoved) {
                nix = nx;
                niy = ny;
                nidist = ndist;
                chi = i;
                mmoved = 1;
            }
          nxti:   ;
          }
      }

      if(mmoved) {
          register int j;

          if (mmoved==1 && (u.ux != nix || u.uy != niy) && itsstuck(mtmp))
            return(3);

          if (mmoved==1 && can_tunnel && needspick(ptr) &&
            (!(mw_tmp = MON_WEP(mtmp)) || mw_tmp->otyp != PICK_AXE)) {
            mtmp->weapon_check = NEED_PICK_AXE;
            (void)mon_wield_item(mtmp);
          }
          /* If ALLOW_U is set, either it's trying to attack you, or it
           * thinks it is.  In either case, attack this spot in preference to
           * all others.
           */
      /* Actually, this whole section of code doesn't work as you'd expect.
       * Most attacks are handled in dochug().  It calls distfleeck(), which
       * among other things sets nearby if the monster is near you--and if
       * nearby is set, we never call m_move unless it is a special case
       * (confused, stun, etc.)  The effect is that this ALLOW_U (and
       * mfndpos) has no effect for normal attacks, though it lets a confused
       * monster attack you by accident.
       */
          if(info[chi] & ALLOW_U) {
            nix = mtmp->mux;
            niy = mtmp->muy;
          }
          if (nix == u.ux && niy == u.uy) {
            mtmp->mux = u.ux;
            mtmp->muy = u.uy;
            return(0);
          }
          /* The monster may attack another based on 1 of 2 conditions:
           * 1 - It may be confused.
           * 2 - It may mistake the monster for your (displaced) image.
           * Pets get taken care of above and shouldn't reach this code.
           * Conflict gets handled even farther away (movemon()).
           */
          if((info[chi] & ALLOW_M) ||
               (nix == mtmp->mux && niy == mtmp->muy)) {
            struct monst *mtmp2;
            int mstatus;
            mtmp2 = m_at(nix,niy);

            mstatus = mattackm(mtmp, mtmp2);

            if (mstatus & MM_AGR_DIED)          /* aggressor died */
                return 2;

            if ((mstatus & MM_HIT) && !(mstatus & MM_DEF_DIED)  &&
                rn2(4) && mtmp2->mlstmv != monstermoves) {
                mstatus = mattackm(mtmp2, mtmp);      /* return attack */
                if (mstatus & MM_DEF_DIED)
                  return 2;
            }
            return 3;
          }

          remove_monster(omx, omy);
          place_monster(mtmp, nix, niy);
          for(j = MTSZ-1; j > 0; j--)
            mtmp->mtrack[j] = mtmp->mtrack[j-1];
          mtmp->mtrack[0].x = omx;
          mtmp->mtrack[0].y = omy;
          /* Place a segment at the old position. */
          if (mtmp->wormno) worm_move(mtmp);
      } else {
          if(ptr->mlet == S_UNICORN && rn2(2)) {
            rloc(mtmp);
            return(1);
          }
          if(mtmp->wormno) worm_nomove(mtmp);
      }
postmov:
      if(mmoved == 1 || mmoved == 3) {
          boolean canseeit = cansee(mtmp->mx, mtmp->my);

          if(mmoved == 1) {
            newsym(omx,omy);        /* update the old position */
            if (mintrap(mtmp) >= 2) {
                if(mtmp->mx) newsym(mtmp->mx,mtmp->my);
                return(2);    /* it died */
            }
            ptr = mtmp->data;

            /* open a door, or crash through it, if you can */
            if(IS_DOOR(levl[mtmp->mx][mtmp->my].typ)
                  && !passes_walls(ptr) /* doesn't need to open doors */
                  && !can_tunnel /* taken care of below */
                  ) {
                struct rm *here = &levl[mtmp->mx][mtmp->my];
                boolean btrapped = (here->doormask & D_TRAPPED);

                if(here->doormask & (D_LOCKED|D_CLOSED) && amorphous(ptr)) {
                  if (flags.verbose && canseeit)
/*JP                  pline("%s %ss under the door.", Monnam(mtmp),
                          (ptr == &mons[PM_FOG_CLOUD] ||
                           ptr == &mons[PM_YELLOW_LIGHT])
                          ? "flow" : "ooze");*/
                      pline("%sは扉の下から%s.", Monnam(mtmp),
                          (ptr == &mons[PM_FOG_CLOUD] ||
                           ptr == &mons[PM_YELLOW_LIGHT])
                          ? "流れでた" : "にじみでた");
                } else if(here->doormask & D_LOCKED && can_unlock) {
                  if(btrapped) {
                      here->doormask = D_NODOOR;
                      newsym(mtmp->mx, mtmp->my);
                      unblock_point(mtmp->mx,mtmp->my); /* vision */
                      if(mb_trapped(mtmp)) return(2);
                  } else {
                      if (flags.verbose) {
                        if (canseeit)
/*JP                       You("see a door unlock and open.");*/
                           You("扉の鍵がはずれ,開くのを見た.");
                        else if (flags.soundok)
/*JP                       You_hear("a door unlock and open.");*/
                           You_hear("扉の鍵がはずれ,開く音を聞いた.");

                      }
                      here->doormask = D_ISOPEN;
                      /* newsym(mtmp->mx, mtmp->my); */
                      unblock_point(mtmp->mx,mtmp->my); /* vision */
                  }
                } else if (here->doormask == D_CLOSED && can_open) {
                  if(btrapped) {
                      here->doormask = D_NODOOR;
                      newsym(mtmp->mx, mtmp->my);
                      unblock_point(mtmp->mx,mtmp->my); /* vision */
                      if(mb_trapped(mtmp)) return(2);
                  } else {
                      if (flags.verbose) {
                        if (canseeit)
/*JP                         You("see a door open.");*/
                             You("扉が開くのを見た.");
                        else if (flags.soundok)
/*JP                         You_hear("a door open.");*/
                             You_hear("扉が開く音を聞いた.");
                      }
                      here->doormask = D_ISOPEN;
                      /* newsym(mtmp->mx, mtmp->my); */  /* done below */
                      unblock_point(mtmp->mx,mtmp->my); /* vision */
                  }
                } else if (here->doormask & (D_LOCKED|D_CLOSED)) {
                  /* mfndpos guarantees this must be a doorbuster */
                  if(btrapped) {
                      here->doormask = D_NODOOR;
                      newsym(mtmp->mx, mtmp->my);
                      unblock_point(mtmp->mx,mtmp->my); /* vision */
                      if(mb_trapped(mtmp)) return(2);
                  } else {
                      if (flags.verbose) {
                        if (canseeit)
/*JP                        You("see a door crash open.");*/
                            You("扉が破壊されるのを見た.");
                        else if (flags.soundok)
/*JP                        You_hear("a door crash open.");*/
                            You_hear("扉が破壊される音を聞いた.");
                      }
                      if (here->doormask & D_LOCKED && !rn2(2))
                            here->doormask = D_NODOOR;
                      else here->doormask = D_BROKEN;
                      /* newsym(mtmp->mx, mtmp->my); */ /* done below */
                      unblock_point(mtmp->mx,mtmp->my); /* vision */
                  }
                  /* if it's a shop door, schedule repair */
                  if (*in_rooms(mtmp->mx, mtmp->my, SHOPBASE))
                      add_damage(mtmp->mx, mtmp->my, 0L);
                }
            }

            /* possibly dig */
            if (can_tunnel && mdig_tunnel(mtmp))
                  return(2);  /* mon died (position already updated) */

            /* set also in domove(), hack.c */
            if (u.uswallow && mtmp == u.ustuck &&
                              (mtmp->mx != omx || mtmp->my != omy)) {
                /* If the monster moved, then update */
                u.ux0 = u.ux;
                u.uy0 = u.uy;
                u.ux = mtmp->mx;
                u.uy = mtmp->my;
                swallowed(0);
            } else
            newsym(mtmp->mx,mtmp->my);
          }
          if(OBJ_AT(mtmp->mx, mtmp->my) && mtmp->mcanmove) {
            /* Maybe a rock mole just ate some metal object */
            if (metallivorous(ptr)) {
                if (meatgold(mtmp) == 2) return 2;    /* it died */
            }

            if(g_at(mtmp->mx,mtmp->my) && likegold) mpickgold(mtmp);

            /* Maybe a cube ate just about anything */
            if (ptr == &mons[PM_GELATINOUS_CUBE]) {
                if (meatobj(mtmp) == 2) return 2;     /* it died */
            }

            if(!*in_rooms(mtmp->mx, mtmp->my, SHOPBASE) || !rn2(25)) {
                boolean picked = FALSE;

                if(likeobjs) picked |= mpickstuff(mtmp, practical);
                if(likemagic) picked |= mpickstuff(mtmp, magical);
                if(likerock) picked |= mpickstuff(mtmp, boulder_class);
                if(likegems) picked |= mpickstuff(mtmp, gem_class);
                if(uses_items) picked |= mpickstuff(mtmp, (char *)0);
                if(picked) mmoved = 3;
            }

            if(mtmp->minvis) {
                newsym(mtmp->mx, mtmp->my);
                if (mtmp->wormno) see_wsegs(mtmp);
            }
          }

          if(hides_under(ptr) || ptr->mlet == S_EEL) {
            /* Always set--or reset--mundetected if it's already hidden
               (just in case the object it was hiding under went away);
               usually set mundetected unless monster can't move.  */
            if (mtmp->mundetected ||
                  (mtmp->mcanmove && !mtmp->msleep && rn2(5)))
                mtmp->mundetected = (ptr->mlet != S_EEL) ?
                  OBJ_AT(mtmp->mx, mtmp->my) :
                  (is_pool(mtmp->mx, mtmp->my) && !Is_waterlevel(&u.uz));
            newsym(mtmp->mx, mtmp->my);
          }
      }
      return(mmoved);
}

#endif /* OVL0 */
#ifdef OVL2

boolean
closed_door(x, y)
register int x, y;
{
      return((boolean)(IS_DOOR(levl[x][y].typ) &&
                  (levl[x][y].doormask & (D_LOCKED | D_CLOSED))));
}

boolean
accessible(x, y)
register int x, y;
{
      return((boolean)(ACCESSIBLE(levl[x][y].typ) && !closed_door(x, y)));
}

#endif /* OVL2 */
#ifdef OVL0

/* decide where the monster thinks you are standing */
void
set_apparxy(mtmp)
register struct monst *mtmp;
{
      boolean notseen, gotu;
      register int disp, mx = mtmp->mux, my = mtmp->muy;

      /*
       * do cheapest and/or most likely tests first
       */

      /* pet knows your smell; grabber still has hold of you */
      if (mtmp->mtame || mtmp == u.ustuck) goto found_you;

      /* monsters which know where you are don't suddenly forget,
         if you haven't moved away */
      if (mx == u.ux && my == u.uy) goto found_you;

      notseen = (!mtmp->mcansee || (Invis && !perceives(mtmp->data)));
      /* add cases as required.  eg. Displacement ... */
      disp = ((notseen || Underwater) ? 1 :
            Displaced ? (couldsee(mx, my) ? 2 : 1) : 0);
      if (!disp) goto found_you;

      /* without something like the following, invis. and displ.
         are too powerful */
      gotu = notseen ? !rn2(3) : Displaced ? !rn2(4) : FALSE;

#if 0       /* this never worked as intended & isn't needed anyway */
      /* If invis but not displaced, staying around gets you 'discovered' */
      gotu |= (!Displaced && u.dx == 0 && u.dy == 0);
#endif

      if (!gotu) {
          register int try_cnt = 0;
          do {
            if (++try_cnt > 200) goto found_you;            /* punt */
            mx = u.ux - disp + rn2(2*disp+1);
            my = u.uy - disp + rn2(2*disp+1);
          } while (!isok(mx,my)
              || (disp != 2 && mx == mtmp->mx && my == mtmp->my)
              || ((mx != u.ux || my != u.uy) &&
                  !passes_walls(mtmp->data) &&
                  (!ACCESSIBLE(levl[mx][my].typ) ||
                  (closed_door(mx, my) && !amorphous(mtmp->data)))));
      } else {
found_you:
          mx = u.ux;
          my = u.uy;
      }

      mtmp->mux = mx;
      mtmp->muy = my;
}

#endif /* OVL0 */

/*monmove.c*/

Generated by  Doxygen 1.6.0   Back to index