Source:NetHack 3.6.1/src/dogmove.c

From NetHackWiki
(Redirected from Source:Dogmove.c)
Jump to: navigation, search

Below is the full text to dogmove.c from the source code of NetHack 3.6.1. To link to a particular line, write [[Source:NetHack 3.6.1/src/dogmove.c#line123]], for example.

Top of file

  1.  /* NetHack 3.6	dogmove.c	$NHDT-Date: 1502753407 2017/08/14 23:30:07 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.63 $ */
  2.  /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
  3.  /*-Copyright (c) Robert Patrick Rankin, 2012. */
  4.  /* NetHack may be freely redistributed.  See license for details. */

The NetHack General Public License applies to screenshots, source code and other content from NetHack.

This content was modified from the original NetHack source code distribution (by splitting up NetHack content between wiki pages, and possibly further editing). See the page history for a list of who changed it, and on what dates.

  1.  
  2.  #include "hack.h"
  3.  
  4.  #include "mfndpos.h"
  5.  
  6.  extern boolean notonhead;
  7.  
  8.  STATIC_DCL boolean FDECL(dog_hunger, (struct monst *, struct edog *));
  9.  STATIC_DCL int FDECL(dog_invent, (struct monst *, struct edog *, int));
  10.  STATIC_DCL int FDECL(dog_goal, (struct monst *, struct edog *, int, int, int));
  11.  STATIC_DCL struct monst *FDECL(find_targ, (struct monst *, int, int, int));
  12.  STATIC_OVL int FDECL(find_friends, (struct monst *, struct monst *, int));
  13.  STATIC_DCL struct monst *FDECL(best_target, (struct monst *));
  14.  STATIC_DCL long FDECL(score_targ, (struct monst *, struct monst *));
  15.  STATIC_DCL boolean FDECL(can_reach_location, (struct monst *, XCHAR_P,
  16.                                                XCHAR_P, XCHAR_P, XCHAR_P));
  17.  STATIC_DCL boolean FDECL(could_reach_item, (struct monst *, XCHAR_P, XCHAR_P));
  18.  STATIC_DCL void FDECL(quickmimic, (struct monst *));
  19.  

droppables

  1.  /* pick a carried item for pet to drop */
  2.  struct obj *
  3.  droppables(mon)
  4.  struct monst *mon;
  5.  {
  6.      struct obj *obj, *wep, dummy, *pickaxe, *unihorn, *key;
  7.  
  8.      dummy = zeroobj;
  9.      dummy.otyp = GOLD_PIECE; /* not STRANGE_OBJECT or tools of interest */
  10.      dummy.oartifact = 1; /* so real artifact won't override "don't keep it" */
  11.      pickaxe = unihorn = key = (struct obj *) 0;
  12.      wep = MON_WEP(mon);
  13.  
  14.      if (is_animal(mon->data) || mindless(mon->data)) {
  15.          /* won't hang on to any objects of these types */
  16.          pickaxe = unihorn = key = &dummy; /* act as if already have them */
  17.      } else {
  18.          /* don't hang on to pick-axe if can't use one or don't need one */
  19.          if (!tunnels(mon->data) || !needspick(mon->data))
  20.              pickaxe = &dummy;
  21.          /* don't hang on to key if can't open doors */
  22.          if (nohands(mon->data) || verysmall(mon->data))
  23.              key = &dummy;
  24.      }
  25.      if (wep) {
  26.          if (is_pick(wep))
  27.              pickaxe = wep;
  28.          if (wep->otyp == UNICORN_HORN)
  29.              unihorn = wep;
  30.          /* don't need any wielded check for keys... */
  31.      }
  32.  
  33.      for (obj = mon->minvent; obj; obj = obj->nobj) {
  34.          switch (obj->otyp) {
  35.          case DWARVISH_MATTOCK:
  36.              /* reject mattock if couldn't wield it */
  37.              if (which_armor(mon, W_ARMS))
  38.                  break;
  39.              /* keep mattock in preference to pick unless pick is already
  40.                 wielded or is an artifact and mattock isn't */
  41.              if (pickaxe && pickaxe->otyp == PICK_AXE && pickaxe != wep
  42.                  && (!pickaxe->oartifact || obj->oartifact))
  43.                  return pickaxe; /* drop the one we earlier decided to keep */
  44.          /*FALLTHRU*/
  45.          case PICK_AXE:
  46.              if (!pickaxe || (obj->oartifact && !pickaxe->oartifact)) {
  47.                  if (pickaxe)
  48.                      return pickaxe;
  49.                  pickaxe = obj; /* keep this digging tool */
  50.                  continue;
  51.              }
  52.              break;
  53.  
  54.          case UNICORN_HORN:
  55.              /* reject cursed unicorn horns */
  56.              if (obj->cursed)
  57.                  break;
  58.              /* keep artifact unihorn in preference to ordinary one */
  59.              if (!unihorn || (obj->oartifact && !unihorn->oartifact)) {
  60.                  if (unihorn)
  61.                      return unihorn;
  62.                  unihorn = obj; /* keep this unicorn horn */
  63.                  continue;
  64.              }
  65.              break;
  66.  
  67.          case SKELETON_KEY:
  68.              /* keep key in preference to lock-pick */
  69.              if (key && key->otyp == LOCK_PICK
  70.                  && (!key->oartifact || obj->oartifact))
  71.                  return key; /* drop the one we earlier decided to keep */
  72.          /*FALLTHRU*/
  73.          case LOCK_PICK:
  74.              /* keep lock-pick in preference to credit card */
  75.              if (key && key->otyp == CREDIT_CARD
  76.                  && (!key->oartifact || obj->oartifact))
  77.                  return key;
  78.          /*FALLTHRU*/
  79.          case CREDIT_CARD:
  80.              if (!key || (obj->oartifact && !key->oartifact)) {
  81.                  if (key)
  82.                      return key;
  83.                  key = obj; /* keep this unlocking tool */
  84.                  continue;
  85.              }
  86.              break;
  87.  
  88.          default:
  89.              break;
  90.          }
  91.  
  92.          if (!obj->owornmask && obj != wep)
  93.              return obj;
  94.      }
  95.  
  96.      return (struct obj *) 0; /* don't drop anything */
  97.  }
  98.  
  99.  static NEARDATA const char nofetch[] = { BALL_CLASS, CHAIN_CLASS, ROCK_CLASS,
  100.                                           0 };
  101.  
  102.  STATIC_VAR xchar gtyp, gx, gy; /* type and position of dog's current goal */
  103.  
  104.  STATIC_PTR void FDECL(wantdoor, (int, int, genericptr_t));
  105.  

cursed_object_at

  1.  boolean
  2.  cursed_object_at(x, y)
  3.  int x, y;
  4.  {
  5.      struct obj *otmp;
  6.  
  7.      for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
  8.          if (otmp->cursed)
  9.              return TRUE;
  10.      return FALSE;
  11.  }
  12.  

dog_nutrition

  1.  int
  2.  dog_nutrition(mtmp, obj)
  3.  struct monst *mtmp;
  4.  struct obj *obj;
  5.  {
  6.      int nutrit;
  7.  
  8.      /*
  9.       * It is arbitrary that the pet takes the same length of time to eat
  10.       * as a human, but gets more nutritional value.
  11.       */
  12.      if (obj->oclass == FOOD_CLASS) {
  13.          if (obj->otyp == CORPSE) {
  14.              mtmp->meating = 3 + (mons[obj->corpsenm].cwt >> 6);
  15.              nutrit = mons[obj->corpsenm].cnutrit;
  16.          } else {
  17.              mtmp->meating = objects[obj->otyp].oc_delay;
  18.              nutrit = objects[obj->otyp].oc_nutrition;
  19.          }
  20.          switch (mtmp->data->msize) {
  21.          case MZ_TINY:
  22.              nutrit *= 8;
  23.              break;
  24.          case MZ_SMALL:
  25.              nutrit *= 6;
  26.              break;
  27.          default:
  28.          case MZ_MEDIUM:
  29.              nutrit *= 5;
  30.              break;
  31.          case MZ_LARGE:
  32.              nutrit *= 4;
  33.              break;
  34.          case MZ_HUGE:
  35.              nutrit *= 3;
  36.              break;
  37.          case MZ_GIGANTIC:
  38.              nutrit *= 2;
  39.              break;
  40.          }
  41.          if (obj->oeaten) {
  42.              mtmp->meating = eaten_stat(mtmp->meating, obj);
  43.              nutrit = eaten_stat(nutrit, obj);
  44.          }
  45.      } else if (obj->oclass == COIN_CLASS) {
  46.          mtmp->meating = (int) (obj->quan / 2000) + 1;
  47.          if (mtmp->meating < 0)
  48.              mtmp->meating = 1;
  49.          nutrit = (int) (obj->quan / 20);
  50.          if (nutrit < 0)
  51.              nutrit = 0;
  52.      } else {
  53.          /* Unusual pet such as gelatinous cube eating odd stuff.
  54.           * meating made consistent with wild monsters in mon.c.
  55.           * nutrit made consistent with polymorphed player nutrit in
  56.           * eat.c.  (This also applies to pets eating gold.)
  57.           */
  58.          mtmp->meating = obj->owt / 20 + 1;
  59.          nutrit = 5 * objects[obj->otyp].oc_nutrition;
  60.      }
  61.      return nutrit;
  62.  }
  63.  

dog_eat

  1.  /* returns 2 if pet dies, otherwise 1 */
  2.  int
  3.  dog_eat(mtmp, obj, x, y, devour)
  4.  register struct monst *mtmp;
  5.  register struct obj *obj; /* if unpaid, then thrown or kicked by hero */
  6.  int x, y; /* dog's starting location, might be different from current */
  7.  boolean devour;
  8.  {
  9.      register struct edog *edog = EDOG(mtmp);
  10.      boolean poly, grow, heal, eyes, slimer, deadmimic;
  11.      int nutrit;
  12.      long oprice;
  13.      char objnambuf[BUFSZ];
  14.  
  15.      objnambuf[0] = '\0';
  16.      if (edog->hungrytime < monstermoves)
  17.          edog->hungrytime = monstermoves;
  18.      nutrit = dog_nutrition(mtmp, obj);
  19.  
  20.      deadmimic = (obj->otyp == CORPSE && (obj->corpsenm == PM_SMALL_MIMIC
  21.                                           || obj->corpsenm == PM_LARGE_MIMIC
  22.                                           || obj->corpsenm == PM_GIANT_MIMIC));
  23.      slimer = (obj->otyp == CORPSE && obj->corpsenm == PM_GREEN_SLIME);
  24.      poly = polyfodder(obj);
  25.      grow = mlevelgain(obj);
  26.      heal = mhealup(obj);
  27.      eyes = (obj->otyp == CARROT);
  28.  
  29.      if (devour) {
  30.          if (mtmp->meating > 1)
  31.              mtmp->meating /= 2;
  32.          if (nutrit > 1)
  33.              nutrit = (nutrit * 3) / 4;
  34.      }
  35.      edog->hungrytime += nutrit;
  36.      mtmp->mconf = 0;
  37.      if (edog->mhpmax_penalty) {
  38.          /* no longer starving */
  39.          mtmp->mhpmax += edog->mhpmax_penalty;
  40.          edog->mhpmax_penalty = 0;
  41.      }
  42.      if (mtmp->mflee && mtmp->mfleetim > 1)
  43.          mtmp->mfleetim /= 2;
  44.      if (mtmp->mtame < 20)
  45.          mtmp->mtame++;
  46.      if (x != mtmp->mx || y != mtmp->my) { /* moved & ate on same turn */
  47.          newsym(x, y);
  48.          newsym(mtmp->mx, mtmp->my);
  49.      }
  50.  
  51.      /* food items are eaten one at a time; entire stack for other stuff */
  52.      if (obj->quan > 1L && obj->oclass == FOOD_CLASS)
  53.          obj = splitobj(obj, 1L);
  54.      if (obj->unpaid)
  55.          iflags.suppress_price++;
  56.      if (is_pool(x, y) && !Underwater) {
  57.          /* Don't print obj */
  58.          /* TODO: Reveal presence of sea monster (especially sharks) */
  59.      } else {
  60.          /* food is at monster's current location, <mx,my>;
  61.             <x,y> was monster's location at start of this turn;
  62.             they might be the same but will be different when
  63.             the monster is moving+eating on same turn */
  64.          boolean seeobj = cansee(mtmp->mx, mtmp->my),
  65.                  sawpet = cansee(x, y) && mon_visible(mtmp);
  66.  
  67.          /* Observe the action if either the food location or the pet
  68.             itself is in view.  When pet which was in view moves to an
  69.             unseen spot to eat the food there, avoid referring to that
  70.             pet as "it".  However, we want "it" if invisible/unsensed
  71.             pet eats visible food. */
  72.          if (sawpet || (seeobj && canspotmon(mtmp))) {
  73.              if (tunnels(mtmp->data))
  74.                  pline("%s digs in.", noit_Monnam(mtmp));
  75.              else
  76.                  pline("%s %s %s.", noit_Monnam(mtmp),
  77.                        devour ? "devours" : "eats", distant_name(obj, doname));
  78.          } else if (seeobj)
  79.              pline("It %s %s.", devour ? "devours" : "eats",
  80.                    distant_name(obj, doname));
  81.      }
  82.      if (obj->unpaid) {
  83.          Strcpy(objnambuf, xname(obj));
  84.          iflags.suppress_price--;
  85.      }
  86.      /* It's a reward if it's DOGFOOD and the player dropped/threw it.
  87.         We know the player had it if invlet is set. -dlc */
  88.      if (dogfood(mtmp, obj) == DOGFOOD && obj->invlet)
  89.  #ifdef LINT
  90.          edog->apport = 0;
  91.  #else
  92.          edog->apport += (int) (200L / ((long) edog->dropdist + monstermoves
  93.                                         - edog->droptime));
  94.  #endif
  95.      if (mtmp->data == &mons[PM_RUST_MONSTER] && obj->oerodeproof) {
  96.          /* The object's rustproofing is gone now */
  97.          if (obj->unpaid)
  98.              costly_alteration(obj, COST_DEGRD);
  99.          obj->oerodeproof = 0;
  100.          mtmp->mstun = 1;
  101.          if (canseemon(mtmp) && flags.verbose) {
  102.              pline("%s spits %s out in disgust!", Monnam(mtmp),
  103.                    distant_name(obj, doname));
  104.          }
  105.      } else if (obj == uball) {
  106.          unpunish();
  107.          delobj(obj); /* we assume this can't be unpaid */
  108.      } else if (obj == uchain) {
  109.          unpunish();
  110.      } else {
  111.          if (obj->unpaid) {
  112.              /* edible item owned by shop has been thrown or kicked
  113.                 by hero and caught by tame or food-tameable monst */
  114.              oprice = unpaid_cost(obj, TRUE);
  115.              pline("That %s will cost you %ld %s.", objnambuf, oprice,
  116.                    currency(oprice));
  117.              /* delobj->obfree will handle actual shop billing update */
  118.          }
  119.          delobj(obj);
  120.      }
  121.  
  122.  #if 0 /* pet is eating, so slime recovery is not feasible... */
  123.      /* turning into slime might be cureable */
  124.      if (slimer && munslime(mtmp, FALSE)) {
  125.          /* but the cure (fire directed at self) might be fatal */
  126.          if (mtmp->mhp < 1)
  127.              return 2;
  128.          slimer = FALSE; /* sliming is avoided, skip polymorph */
  129.      }
  130.  #endif
  131.  
  132.      if (poly || slimer) {
  133.          struct permonst *ptr = slimer ? &mons[PM_GREEN_SLIME] : 0;
  134.  
  135.          (void) newcham(mtmp, ptr, FALSE, cansee(mtmp->mx, mtmp->my));
  136.      }
  137.  
  138.      /* limit "instant" growth to prevent potential abuse */
  139.      if (grow && (int) mtmp->m_lev < (int) mtmp->data->mlevel + 15) {
  140.          if (!grow_up(mtmp, (struct monst *) 0))
  141.              return 2;
  142.      }
  143.      if (heal)
  144.          mtmp->mhp = mtmp->mhpmax;
  145.      if ((eyes || heal) && !mtmp->mcansee)
  146.          mcureblindness(mtmp, canseemon(mtmp));
  147.      if (deadmimic)
  148.          quickmimic(mtmp);
  149.      return 1;
  150.  }
  151.  

dog_hunger

  1.  /* hunger effects -- returns TRUE on starvation */
  2.  STATIC_OVL boolean
  3.  dog_hunger(mtmp, edog)
  4.  struct monst *mtmp;
  5.  struct edog *edog;
  6.  {
  7.      if (monstermoves > edog->hungrytime + 500) {
  8.          if (!carnivorous(mtmp->data) && !herbivorous(mtmp->data)) {
  9.              edog->hungrytime = monstermoves + 500;
  10.              /* but not too high; it might polymorph */
  11.          } else if (!edog->mhpmax_penalty) {
  12.              /* starving pets are limited in healing */
  13.              int newmhpmax = mtmp->mhpmax / 3;
  14.              mtmp->mconf = 1;
  15.              edog->mhpmax_penalty = mtmp->mhpmax - newmhpmax;
  16.              mtmp->mhpmax = newmhpmax;
  17.              if (mtmp->mhp > mtmp->mhpmax)
  18.                  mtmp->mhp = mtmp->mhpmax;
  19.              if (mtmp->mhp < 1)
  20.                  goto dog_died;
  21.              if (cansee(mtmp->mx, mtmp->my))
  22.                  pline("%s is confused from hunger.", Monnam(mtmp));
  23.              else if (couldsee(mtmp->mx, mtmp->my))
  24.                  beg(mtmp);
  25.              else
  26.                  You_feel("worried about %s.", y_monnam(mtmp));
  27.              stop_occupation();
  28.          } else if (monstermoves > edog->hungrytime + 750 || mtmp->mhp < 1) {
  29.          dog_died:
  30.              if (mtmp->mleashed && mtmp != u.usteed)
  31.                  Your("leash goes slack.");
  32.              else if (cansee(mtmp->mx, mtmp->my))
  33.                  pline("%s starves.", Monnam(mtmp));
  34.              else
  35.                  You_feel("%s for a moment.",
  36.                           Hallucination ? "bummed" : "sad");
  37.              mondied(mtmp);
  38.              return  TRUE;
  39.          }
  40.      }
  41.      return FALSE;
  42.  }
  43.  

dog_invent

  1.  /* do something with object (drop, pick up, eat) at current position
  2.   * returns 1 if object eaten (since that counts as dog's move), 2 if died
  3.   */
  4.  STATIC_OVL int
  5.  dog_invent(mtmp, edog, udist)
  6.  register struct monst *mtmp;
  7.  register struct edog *edog;
  8.  int udist;
  9.  {
  10.      register int omx, omy, carryamt = 0;
  11.      struct obj *obj, *otmp;
  12.  
  13.      if (mtmp->msleeping || !mtmp->mcanmove)
  14.          return 0;
  15.  
  16.      omx = mtmp->mx;
  17.      omy = mtmp->my;
  18.  
  19.      /* If we are carrying something then we drop it (perhaps near @).
  20.       * Note: if apport == 1 then our behaviour is independent of udist.
  21.       * Use udist+1 so steed won't cause divide by zero.
  22.       */
  23.      if (droppables(mtmp)) {
  24.          if (!rn2(udist + 1) || !rn2(edog->apport))
  25.              if (rn2(10) < edog->apport) {
  26.                  relobj(mtmp, (int) mtmp->minvis, TRUE);
  27.                  if (edog->apport > 1)
  28.                      edog->apport--;
  29.                  edog->dropdist = udist; /* hpscdi!jon */
  30.                  edog->droptime = monstermoves;
  31.              }
  32.      } else {
  33.          if ((obj = level.objects[omx][omy]) != 0
  34.              && !index(nofetch, obj->oclass)
  35.  #ifdef MAIL
  36.              && obj->otyp != SCR_MAIL
  37.  #endif
  38.              ) {
  39.              int edible = dogfood(mtmp, obj);
  40.  
  41.              if ((edible <= CADAVER
  42.                   /* starving pet is more aggressive about eating */
  43.                   || (edog->mhpmax_penalty && edible == ACCFOOD))
  44.                  && could_reach_item(mtmp, obj->ox, obj->oy))
  45.                  return dog_eat(mtmp, obj, omx, omy, FALSE);
  46.  
  47.              carryamt = can_carry(mtmp, obj);
  48.              if (carryamt > 0 && !obj->cursed
  49.                  && could_reach_item(mtmp, obj->ox, obj->oy)) {
  50.                  if (rn2(20) < edog->apport + 3) {
  51.                      if (rn2(udist) || !rn2(edog->apport)) {
  52.                          otmp = obj;
  53.                          if (carryamt != obj->quan)
  54.                              otmp = splitobj(obj, carryamt);
  55.                          if (cansee(omx, omy) && flags.verbose)
  56.                              pline("%s picks up %s.", Monnam(mtmp),
  57.                                    distant_name(otmp, doname));
  58.                          obj_extract_self(otmp);
  59.                          newsym(omx, omy);
  60.                          (void) mpickobj(mtmp, otmp);
  61.                          if (attacktype(mtmp->data, AT_WEAP)
  62.                              && mtmp->weapon_check == NEED_WEAPON) {
  63.                              mtmp->weapon_check = NEED_HTH_WEAPON;
  64.                              (void) mon_wield_item(mtmp);
  65.                          }
  66.                          m_dowear(mtmp, FALSE);
  67.                      }
  68.                  }
  69.              }
  70.          }
  71.      }
  72.      return 0;
  73.  }
  74.  

dog_goal

  1.  /* set dog's goal -- gtyp, gx, gy;
  2.     returns -1/0/1 (dog's desire to approach player) or -2 (abort move) */
  3.  STATIC_OVL int
  4.  dog_goal(mtmp, edog, after, udist, whappr)
  5.  register struct monst *mtmp;
  6.  struct edog *edog;
  7.  int after, udist, whappr;
  8.  {
  9.      register int omx, omy;
  10.      boolean in_masters_sight, dog_has_minvent;
  11.      register struct obj *obj;
  12.      xchar otyp;
  13.      int appr;
  14.  
  15.      /* Steeds don't move on their own will */
  16.      if (mtmp == u.usteed)
  17.          return -2;
  18.  
  19.      omx = mtmp->mx;
  20.      omy = mtmp->my;
  21.  
  22.      in_masters_sight = couldsee(omx, omy);
  23.      dog_has_minvent = (droppables(mtmp) != 0);
  24.  
  25.      if (!edog || mtmp->mleashed) { /* he's not going anywhere... */
  26.          gtyp = APPORT;
  27.          gx = u.ux;
  28.          gy = u.uy;
  29.      } else {
  30.  #define DDIST(x, y) (dist2(x, y, omx, omy))
  31.  #define SQSRCHRADIUS 5
  32.          int min_x, max_x, min_y, max_y;
  33.          register int nx, ny;
  34.  
  35.          gtyp = UNDEF; /* no goal as yet */
  36.          gx = gy = 0;  /* suppress 'used before set' message */
  37.  
  38.          if ((min_x = omx - SQSRCHRADIUS) < 1)
  39.              min_x = 1;
  40.          if ((max_x = omx + SQSRCHRADIUS) >= COLNO)
  41.              max_x = COLNO - 1;
  42.          if ((min_y = omy - SQSRCHRADIUS) < 0)
  43.              min_y = 0;
  44.          if ((max_y = omy + SQSRCHRADIUS) >= ROWNO)
  45.              max_y = ROWNO - 1;
  46.  
  47.          /* nearby food is the first choice, then other objects */
  48.          for (obj = fobj; obj; obj = obj->nobj) {
  49.              nx = obj->ox;
  50.              ny = obj->oy;
  51.              if (nx >= min_x && nx <= max_x && ny >= min_y && ny <= max_y) {
  52.                  otyp = dogfood(mtmp, obj);
  53.                  /* skip inferior goals */
  54.                  if (otyp > gtyp || otyp == UNDEF)
  55.                      continue;
  56.                  /* avoid cursed items unless starving */
  57.                  if (cursed_object_at(nx, ny)
  58.                      && !(edog->mhpmax_penalty && otyp < MANFOOD))
  59.                      continue;
  60.                  /* skip completely unreachable goals */
  61.                  if (!could_reach_item(mtmp, nx, ny)
  62.                      || !can_reach_location(mtmp, mtmp->mx, mtmp->my, nx, ny))
  63.                      continue;
  64.                  if (otyp < MANFOOD) {
  65.                      if (otyp < gtyp || DDIST(nx, ny) < DDIST(gx, gy)) {
  66.                          gx = nx;
  67.                          gy = ny;
  68.                          gtyp = otyp;
  69.                      }
  70.                  } else if (gtyp == UNDEF && in_masters_sight
  71.                             && !dog_has_minvent
  72.                             && (!levl[omx][omy].lit || levl[u.ux][u.uy].lit)
  73.                             && (otyp == MANFOOD || m_cansee(mtmp, nx, ny))
  74.                             && edog->apport > rn2(8)
  75.                             && can_carry(mtmp, obj) > 0) {
  76.                      gx = nx;
  77.                      gy = ny;
  78.                      gtyp = APPORT;
  79.                  }
  80.              }
  81.          }
  82.      }
  83.  
  84.      /* follow player if appropriate */
  85.      if (gtyp == UNDEF || (gtyp != DOGFOOD && gtyp != APPORT
  86.                            && monstermoves < edog->hungrytime)) {
  87.          gx = u.ux;
  88.          gy = u.uy;
  89.          if (after && udist <= 4 && gx == u.ux && gy == u.uy)
  90.              return -2;
  91.          appr = (udist >= 9) ? 1 : (mtmp->mflee) ? -1 : 0;
  92.          if (udist > 1) {
  93.              if (!IS_ROOM(levl[u.ux][u.uy].typ) || !rn2(4) || whappr
  94.                  || (dog_has_minvent && rn2(edog->apport)))
  95.                  appr = 1;
  96.          }
  97.          /* if you have dog food it'll follow you more closely */
  98.          if (appr == 0)
  99.              for (obj = invent; obj; obj = obj->nobj)
  100.                  if (dogfood(mtmp, obj) == DOGFOOD) {
  101.                      appr = 1;
  102.                      break;
  103.                  }
  104.      } else
  105.          appr = 1; /* gtyp != UNDEF */
  106.      if (mtmp->mconf)
  107.          appr = 0;
  108.  
  109.  #define FARAWAY (COLNO + 2) /* position outside screen */
  110.      if (gx == u.ux && gy == u.uy && !in_masters_sight) {
  111.          register coord *cp;
  112.  
  113.          cp = gettrack(omx, omy);
  114.          if (cp) {
  115.              gx = cp->x;
  116.              gy = cp->y;
  117.              if (edog)
  118.                  edog->ogoal.x = 0;
  119.          } else {
  120.              /* assume master hasn't moved far, and reuse previous goal */
  121.              if (edog && edog->ogoal.x
  122.                  && (edog->ogoal.x != omx || edog->ogoal.y != omy)) {
  123.                  gx = edog->ogoal.x;
  124.                  gy = edog->ogoal.y;
  125.                  edog->ogoal.x = 0;
  126.              } else {
  127.                  int fardist = FARAWAY * FARAWAY;
  128.                  gx = gy = FARAWAY; /* random */
  129.                  do_clear_area(omx, omy, 9, wantdoor, (genericptr_t) &fardist);
  130.  
  131.                  /* here gx == FARAWAY e.g. when dog is in a vault */
  132.                  if (gx == FARAWAY || (gx == omx && gy == omy)) {
  133.                      gx = u.ux;
  134.                      gy = u.uy;
  135.                  } else if (edog) {
  136.                      edog->ogoal.x = gx;
  137.                      edog->ogoal.y = gy;
  138.                  }
  139.              }
  140.          }
  141.      } else if (edog) {
  142.          edog->ogoal.x = 0;
  143.      }
  144.      return appr;
  145.  }
  146.  
  147.  

find_targ

  1.  STATIC_OVL struct monst *
  2.  find_targ(mtmp, dx, dy, maxdist)
  3.  register struct monst *mtmp;
  4.  int dx, dy;
  5.  int maxdist;
  6.  {
  7.      struct monst *targ = 0;
  8.      int curx = mtmp->mx, cury = mtmp->my;
  9.      int dist = 0;
  10.  
  11.      /* Walk outwards */
  12.      for ( ; dist < maxdist; ++dist) {
  13.          curx += dx;
  14.          cury += dy;
  15.          if (!isok(curx, cury))
  16.              break;
  17.  
  18.          /* FIXME: Check if we hit a wall/door/boulder to
  19.           *        short-circuit unnecessary subsequent checks
  20.           */
  21.  
  22.          /* If we can't see up to here, forget it - will this
  23.           * mean pets in corridors don't breathe at monsters
  24.           * in rooms? If so, is that necessarily bad?
  25.           */
  26.          if (!m_cansee(mtmp, curx, cury))
  27.              break;
  28.  
  29.          targ = m_at(curx, cury);
  30.  
  31.          if (curx == mtmp->mux && cury == mtmp->muy)
  32.              return &youmonst;
  33.  
  34.          if (targ) {
  35.              /* Is the monster visible to the pet? */
  36.              if ((!targ->minvis || perceives(mtmp->data)) &&
  37.                  !targ->mundetected)
  38.                  break;
  39.  
  40.              /* If the pet can't see it, it assumes it aint there */
  41.              targ = 0;
  42.          }
  43.      }
  44.      return targ;
  45.  }
  46.  

find_friends

  1.  STATIC_OVL int
  2.  find_friends(mtmp, mtarg, maxdist)
  3.  struct monst *mtmp, *mtarg;
  4.  int    maxdist;
  5.  {
  6.      struct monst *pal;
  7.      int dx = sgn(mtarg->mx - mtmp->mx),
  8.          dy = sgn(mtarg->my - mtmp->my);
  9.      int curx = mtarg->mx, cury = mtarg->my;
  10.      int dist = distmin(mtarg->mx, mtarg->my, mtmp->mx, mtmp->my);
  11.  
  12.      for ( ; dist <= maxdist; ++dist) {
  13.          curx += dx;
  14.          cury += dy;
  15.  
  16.          if (!isok(curx, cury))
  17.              return 0;
  18.  
  19.          /* If the pet can't see beyond this point, don't
  20.           * check any farther
  21.           */
  22.          if (!m_cansee(mtmp, curx, cury))
  23.              return 0;
  24.  
  25.          /* Does pet think you're here? */
  26.          if (mtmp->mux == curx && mtmp->muy == cury)
  27.              return 1;
  28.  
  29.          pal = m_at(curx, cury);
  30.  
  31.          if (pal) {
  32.              if (pal->mtame) {
  33.                  /* Pet won't notice invisible pets */
  34.                  if (!pal->minvis || perceives(mtmp->data))
  35.                      return 1;
  36.              } else {
  37.                  /* Quest leaders and guardians are always seen */
  38.                  if (pal->data->msound == MS_LEADER
  39.                      || pal->data->msound == MS_GUARDIAN)
  40.                      return 1;
  41.              }
  42.          }
  43.      }
  44.      return 0;
  45.  }
  46.  

score_targ

  1.  STATIC_OVL long
  2.  score_targ(mtmp, mtarg)
  3.  struct monst *mtmp, *mtarg;
  4.  {
  5.      long score = 0L;
  6.  
  7.      /* If the monster is confused, normal scoring is disrupted -
  8.       * anything may happen
  9.       */
  10.  
  11.      /* Give 1 in 3 chance of safe breathing even if pet is confused or
  12.       * if you're on the quest start level */
  13.      if (!mtmp->mconf || !rn2(3) || Is_qstart(&u.uz)) {
  14.          aligntyp align1 = A_NONE, align2 = A_NONE; /* For priests, minions */
  15.          boolean faith1 = TRUE,  faith2 = TRUE;
  16.  
  17.          if (mtmp->isminion)
  18.              align1 = EMIN(mtmp)->min_align;
  19.          else if (mtmp->ispriest)
  20.              align1 = EPRI(mtmp)->shralign;
  21.          else
  22.              faith1 = FALSE;
  23.          if (mtarg->isminion)
  24.              align2 = EMIN(mtarg)->min_align; /* MAR */
  25.          else if (mtarg->ispriest)
  26.              align2 = EPRI(mtarg)->shralign; /* MAR */
  27.          else
  28.              faith2 = FALSE;
  29.  
  30.          /* Never target quest friendlies */
  31.          if (mtarg->data->msound == MS_LEADER
  32.              || mtarg->data->msound == MS_GUARDIAN)
  33.              return -5000L;
  34.          /* D: Fixed angelic beings using gaze attacks on coaligned priests */
  35.          if (faith1 && faith2 && align1 == align2 && mtarg->mpeaceful) {
  36.              score -= 5000L;
  37.              return score;
  38.          }
  39.          /* Is monster adjacent? */
  40.          if (distmin(mtmp->mx, mtmp->my, mtarg->mx, mtarg->my) <= 1) {
  41.              score -= 3000L;
  42.              return score;
  43.          }
  44.          /* Is the monster peaceful or tame? */
  45.          if (/*mtarg->mpeaceful ||*/ mtarg->mtame || mtarg == &youmonst) {
  46.              /* Pets will never be targeted */
  47.              score -= 3000L;
  48.              return score;
  49.          }
  50.          /* Is master/pet behind monster? Check up to 15 squares beyond pet. */
  51.          if (find_friends(mtmp, mtarg, 15)) {
  52.              score -= 3000L;
  53.              return score;
  54.          }
  55.          /* Target hostile monsters in preference to peaceful ones */
  56.          if (!mtarg->mpeaceful)
  57.              score += 10;
  58.          /* Is the monster passive? Don't waste energy on it, if so */
  59.          if (mtarg->data->mattk[0].aatyp == AT_NONE)
  60.              score -= 1000;
  61.          /* Even weak pets with breath attacks shouldn't take on very
  62.             low-level monsters. Wasting breath on lichens is ridiculous. */
  63.          if ((mtarg->m_lev < 2 && mtmp->m_lev > 5)
  64.              || (mtmp->m_lev > 12 && mtarg->m_lev < mtmp->m_lev - 9
  65.                  && u.ulevel > 8 && mtarg->m_lev < u.ulevel - 7))
  66.              score -= 25;
  67.          /* And pets will hesitate to attack vastly stronger foes.
  68.             This penalty will be discarded if master's in trouble. */
  69.          if (mtarg->m_lev > mtmp->m_lev + 4L)
  70.              score -= (mtarg->m_lev - mtmp->m_lev) * 20L;
  71.          /* All things being the same, go for the beefiest monster. This
  72.             bonus should not be large enough to override the pet's aversion
  73.             to attacking much stronger monsters. */
  74.          score += mtarg->m_lev * 2 + mtarg->mhp / 3;
  75.      }
  76.      /* Fuzz factor to make things less predictable when very
  77.         similar targets are abundant. */
  78.      score += rnd(5);
  79.      /* Pet may decide not to use ranged attack when confused */
  80.      if (mtmp->mconf && !rn2(3))
  81.          score -= 1000;
  82.      return score;
  83.  }
  84.  
  85.  

best_target

  1.  STATIC_OVL struct monst *
  2.  best_target(mtmp)
  3.  struct monst *mtmp;   /* Pet */
  4.  {
  5.      int dx, dy;
  6.      long bestscore = -40000L, currscore;
  7.      struct monst *best_targ = 0, *temp_targ = 0;
  8.  
  9.      /* Help! */
  10.      if (!mtmp)
  11.          return 0;
  12.  
  13.      /* If the pet is blind, it's not going to see any target */
  14.      if (!mtmp->mcansee)
  15.          return 0;
  16.  
  17.      /* Search for any monsters lined up with the pet, within an arbitrary
  18.       * distance from the pet (7 squares, even along diagonals). Monsters
  19.       * are assigned scores and the best score is chosen.
  20.       */
  21.      for (dy = -1; dy < 2; ++dy) {
  22.          for (dx = -1; dx < 2; ++dx) {
  23.              if (!dx && !dy)
  24.                  continue;
  25.              /* Traverse the line to find the first monster within 7
  26.               * squares. Invisible monsters are skipped (if the
  27.               * pet doesn't have see invisible).
  28.               */
  29.              temp_targ = find_targ(mtmp, dx, dy, 7);
  30.  
  31.              /* Nothing in this line? */
  32.              if (!temp_targ)
  33.                  continue;
  34.  
  35.              /* Decide how attractive the target is */
  36.              currscore = score_targ(mtmp, temp_targ);
  37.  
  38.              if (currscore > bestscore) {
  39.                  bestscore = currscore;
  40.                  best_targ = temp_targ;
  41.              }
  42.          }
  43.      }
  44.  
  45.      /* Filter out targets the pet doesn't like */
  46.      if (bestscore < 0L)
  47.          best_targ = 0;
  48.  
  49.      return best_targ;
  50.  }
  51.  
  52.  

dog_move

  1.  /* return 0 (no move), 1 (move) or 2 (dead) */
  2.  int
  3.  dog_move(mtmp, after)
  4.  register struct monst *mtmp;
  5.  int after; /* this is extra fast monster movement */
  6.  {
  7.      int omx, omy; /* original mtmp position */
  8.      int appr, whappr, udist;
  9.      int i, j, k;
  10.      register struct edog *edog = EDOG(mtmp);
  11.      struct obj *obj = (struct obj *) 0;
  12.      xchar otyp;
  13.      boolean has_edog, cursemsg[9], do_eat = FALSE;
  14.      boolean better_with_displacing = FALSE;
  15.      xchar nix, niy;      /* position mtmp is (considering) moving to */
  16.      register int nx, ny; /* temporary coordinates */
  17.      xchar cnt, uncursedcnt, chcnt;
  18.      int chi = -1, nidist, ndist;
  19.      coord poss[9];
  20.      long info[9], allowflags;
  21.  #define GDIST(x, y) (dist2(x, y, gx, gy))
  22.  
  23.      /*
  24.       * Tame Angels have isminion set and an ispriest structure instead of
  25.       * an edog structure.  Fortunately, guardian Angels need not worry
  26.       * about mundane things like eating and fetching objects, and can
  27.       * spend all their energy defending the player.  (They are the only
  28.       * monsters with other structures that can be tame.)
  29.       */
  30.      has_edog = !mtmp->isminion;
  31.  
  32.      omx = mtmp->mx;
  33.      omy = mtmp->my;
  34.      if (has_edog && dog_hunger(mtmp, edog))
  35.          return 2; /* starved */
  36.  
  37.      udist = distu(omx, omy);
  38.      /* Let steeds eat and maybe throw rider during Conflict */
  39.      if (mtmp == u.usteed) {
  40.          if (Conflict && !resist(mtmp, RING_CLASS, 0, 0)) {
  41.              dismount_steed(DISMOUNT_THROWN);
  42.              return 1;
  43.          }
  44.          udist = 1;
  45.      } else if (!udist)
  46.          /* maybe we tamed him while being swallowed --jgm */
  47.          return 0;
  48.  
  49.      nix = omx; /* set before newdogpos */
  50.      niy = omy;
  51.      cursemsg[0] = FALSE; /* lint suppression */
  52.      info[0] = 0;         /* ditto */
  53.  
  54.      if (has_edog) {
  55.          j = dog_invent(mtmp, edog, udist);
  56.          if (j == 2)
  57.              return 2; /* died */
  58.          else if (j == 1)
  59.              goto newdogpos; /* eating something */
  60.  
  61.          whappr = (monstermoves - edog->whistletime < 5);
  62.      } else
  63.          whappr = 0;
  64.  
  65.      appr = dog_goal(mtmp, has_edog ? edog : (struct edog *) 0, after, udist,
  66.                      whappr);
  67.      if (appr == -2)
  68.          return 0;
  69.  
  70.      allowflags = ALLOW_M | ALLOW_TRAPS | ALLOW_SSM | ALLOW_SANCT;
  71.      if (passes_walls(mtmp->data))
  72.          allowflags |= (ALLOW_ROCK | ALLOW_WALL);
  73.      if (passes_bars(mtmp->data))
  74.          allowflags |= ALLOW_BARS;
  75.      if (throws_rocks(mtmp->data))
  76.          allowflags |= ALLOW_ROCK;
  77.      if (is_displacer(mtmp->data))
  78.          allowflags |= ALLOW_MDISP;
  79.      if (Conflict && !resist(mtmp, RING_CLASS, 0, 0)) {
  80.          allowflags |= ALLOW_U;
  81.          if (!has_edog) {
  82.              /* Guardian angel refuses to be conflicted; rather,
  83.               * it disappears, angrily, and sends in some nasties
  84.               */
  85.              lose_guardian_angel(mtmp);
  86.              return 2; /* current monster is gone */
  87.          }
  88.      }
  89.  #if 0 /* [this is now handled in dochug()] */
  90.      if (!Conflict && !mtmp->mconf
  91.          && mtmp == u.ustuck && !sticks(youmonst.data)) {
  92.          unstuck(mtmp); /* swallowed case handled above */
  93.          You("get released!");
  94.      }
  95.  #endif
  96.      if (!nohands(mtmp->data) && !verysmall(mtmp->data)) {
  97.          allowflags |= OPENDOOR;
  98.          if (monhaskey(mtmp, TRUE))
  99.              allowflags |= UNLOCKDOOR;
  100.          /* note:  the Wizard and Riders can unlock doors without a key;
  101.             they won't use that ability if someone manages to tame them */
  102.      }
  103.      if (is_giant(mtmp->data))
  104.          allowflags |= BUSTDOOR;
  105.      if (tunnels(mtmp->data)
  106.          && !Is_rogue_level(&u.uz)) /* same restriction as m_move() */
  107.          allowflags |= ALLOW_DIG;
  108.      cnt = mfndpos(mtmp, poss, info, allowflags);
  109.  
  110.      /* Normally dogs don't step on cursed items, but if they have no
  111.       * other choice they will.  This requires checking ahead of time
  112.       * to see how many uncursed item squares are around.
  113.       */
  114.      uncursedcnt = 0;
  115.      for (i = 0; i < cnt; i++) {
  116.          nx = poss[i].x;
  117.          ny = poss[i].y;
  118.          if (MON_AT(nx, ny) && !((info[i] & ALLOW_M) || info[i] & ALLOW_MDISP))
  119.              continue;
  120.          if (cursed_object_at(nx, ny))
  121.              continue;
  122.          uncursedcnt++;
  123.      }
  124.  
  125.      better_with_displacing = should_displace(mtmp, poss, info, cnt, gx, gy);
  126.  
  127.      chcnt = 0;
  128.      chi = -1;
  129.      nidist = GDIST(nix, niy);
  130.  
  131.      for (i = 0; i < cnt; i++) {
  132.          nx = poss[i].x;
  133.          ny = poss[i].y;
  134.          cursemsg[i] = FALSE;
  135.  
  136.          /* if leashed, we drag him along. */
  137.          if (mtmp->mleashed && distu(nx, ny) > 4)
  138.              continue;
  139.  
  140.          /* if a guardian, try to stay close by choice */
  141.          if (!has_edog && (j = distu(nx, ny)) > 16 && j >= udist)
  142.              continue;
  143.  
  144.          if ((info[i] & ALLOW_M) && MON_AT(nx, ny)) {
  145.              int mstatus;
  146.              register struct monst *mtmp2 = m_at(nx, ny);
  147.  
  148.              if ((int) mtmp2->m_lev >= (int) mtmp->m_lev + 2
  149.                  || (mtmp2->data == &mons[PM_FLOATING_EYE] && rn2(10)
  150.                      && mtmp->mcansee && haseyes(mtmp->data) && mtmp2->mcansee
  151.                      && (perceives(mtmp->data) || !mtmp2->minvis))
  152.                  || (mtmp2->data == &mons[PM_GELATINOUS_CUBE] && rn2(10))
  153.                  || (max_passive_dmg(mtmp2, mtmp) >= mtmp->mhp)
  154.                  || ((mtmp->mhp * 4 < mtmp->mhpmax
  155.                       || mtmp2->data->msound == MS_GUARDIAN
  156.                       || mtmp2->data->msound == MS_LEADER) && mtmp2->mpeaceful
  157.                      && !Conflict)
  158.                  || (touch_petrifies(mtmp2->data) && !resists_ston(mtmp)))
  159.                  continue;
  160.  
  161.              if (after)
  162.                  return 0; /* hit only once each move */
  163.  
  164.              notonhead = 0;
  165.              mstatus = mattackm(mtmp, mtmp2);
  166.  
  167.              /* aggressor (pet) died */
  168.              if (mstatus & MM_AGR_DIED)
  169.                  return 2;
  170.  
  171.              if ((mstatus & MM_HIT) && !(mstatus & MM_DEF_DIED) && rn2(4)
  172.                  && mtmp2->mlstmv != monstermoves
  173.                  && !onscary(mtmp->mx, mtmp->my, mtmp2)
  174.                  /* monnear check needed: long worms hit on tail */
  175.                  && monnear(mtmp2, mtmp->mx, mtmp->my)) {
  176.                  mstatus = mattackm(mtmp2, mtmp); /* return attack */
  177.                  if (mstatus & MM_DEF_DIED)
  178.                      return 2;
  179.              }
  180.              return 0;
  181.          }
  182.          if ((info[i] & ALLOW_MDISP) && MON_AT(nx, ny)
  183.              && better_with_displacing && !undesirable_disp(mtmp, nx, ny)) {
  184.              int mstatus;
  185.              register struct monst *mtmp2 = m_at(nx, ny);
  186.  
  187.              mstatus = mdisplacem(mtmp, mtmp2, FALSE); /* displace monster */
  188.              if (mstatus & MM_DEF_DIED)
  189.                  return 2;
  190.              return 0;
  191.          }
  192.  
  193.          {
  194.              /* Dog avoids harmful traps, but perhaps it has to pass one
  195.               * in order to follow player.  (Non-harmful traps do not
  196.               * have ALLOW_TRAPS in info[].)  The dog only avoids the
  197.               * trap if you've seen it, unlike enemies who avoid traps
  198.               * if they've seen some trap of that type sometime in the
  199.               * past.  (Neither behavior is really realistic.)
  200.               */
  201.              struct trap *trap;
  202.  
  203.              if ((info[i] & ALLOW_TRAPS) && (trap = t_at(nx, ny))) {
  204.                  if (mtmp->mleashed) {
  205.                      if (!Deaf)
  206.                          whimper(mtmp);
  207.                  } else {
  208.                      /* 1/40 chance of stepping on it anyway, in case
  209.                       * it has to pass one to follow the player...
  210.                       */
  211.                      if (trap->tseen && rn2(40))
  212.                          continue;
  213.                  }
  214.              }
  215.          }
  216.  
  217.          /* dog eschews cursed objects, but likes dog food */
  218.          /* (minion isn't interested; `cursemsg' stays FALSE) */
  219.          if (has_edog)
  220.              for (obj = level.objects[nx][ny]; obj; obj = obj->nexthere) {
  221.                  if (obj->cursed) {
  222.                      cursemsg[i] = TRUE;
  223.                  } else if ((otyp = dogfood(mtmp, obj)) < MANFOOD
  224.                           && (otyp < ACCFOOD
  225.                               || edog->hungrytime <= monstermoves)) {
  226.                      /* Note: our dog likes the food so much that he
  227.                       * might eat it even when it conceals a cursed object */
  228.                      nix = nx;
  229.                      niy = ny;
  230.                      chi = i;
  231.                      do_eat = TRUE;
  232.                      cursemsg[i] = FALSE; /* not reluctant */
  233.                      goto newdogpos;
  234.                  }
  235.              }
  236.          /* didn't find something to eat; if we saw a cursed item and
  237.             aren't being forced to walk on it, usually keep looking */
  238.          if (cursemsg[i] && !mtmp->mleashed && uncursedcnt > 0
  239.              && rn2(13 * uncursedcnt))
  240.              continue;
  241.  
  242.          /* lessen the chance of backtracking to previous position(s) */
  243.          /* This causes unintended issues for pets trying to follow
  244.             the hero. Thus, only run it if not leashed and >5 tiles
  245.             away. */
  246.          if (!mtmp->mleashed &&
  247.              distmin(mtmp->mx, mtmp->my, u.ux, u.uy) > 5) {
  248.              k = has_edog ? uncursedcnt : cnt;
  249.              for (j = 0; j < MTSZ && j < k - 1; j++)
  250.                  if (nx == mtmp->mtrack[j].x &&
  251.                      ny == mtmp->mtrack[j].y)
  252.                      if (rn2(MTSZ * (k - j)))
  253.                          goto nxti;
  254.          }
  255.  
  256.          j = ((ndist = GDIST(nx, ny)) - nidist) * appr;
  257.          if ((j == 0 && !rn2(++chcnt)) || j < 0
  258.              || (j > 0 && !whappr
  259.                  && ((omx == nix && omy == niy && !rn2(3)) || !rn2(12)))) {
  260.              nix = nx;
  261.              niy = ny;
  262.              nidist = ndist;
  263.              if (j < 0)
  264.                  chcnt = 0;
  265.              chi = i;
  266.          }
  267.      nxti:
  268.          ;
  269.      }
  270.  
  271.      /* Pet hasn't attacked anything but is considering moving -
  272.       * now's the time for ranged attacks. Note that the pet can move
  273.       * after it performs its ranged attack. Should this be changed?
  274.       */
  275.      {
  276.          struct monst *mtarg;
  277.          int hungry = 0;
  278.  
  279.          /* How hungry is the pet? */
  280.          if (!mtmp->isminion) {
  281.              struct edog *dog = EDOG(mtmp);
  282.              hungry = (monstermoves > (dog->hungrytime + 300));
  283.          }
  284.  
  285.          /* Identify the best target in a straight line from the pet;
  286.           * if there is such a target, we'll let the pet attempt an
  287.           * attack.
  288.           */
  289.          mtarg = best_target(mtmp);
  290.  
  291.          /* Hungry pets are unlikely to use breath/spit attacks */
  292.          if (mtarg && (!hungry || !rn2(5))) {
  293.              int mstatus;
  294.  
  295.              if (mtarg == &youmonst) {
  296.                  if (mattacku(mtmp))
  297.                      return 2;
  298.              } else {
  299.                  mstatus = mattackm(mtmp, mtarg);
  300.  
  301.                  /* Shouldn't happen, really */
  302.                  if (mstatus & MM_AGR_DIED)
  303.                      return 2;
  304.  
  305.                  /* Allow the targeted nasty to strike back - if
  306.                   * the targeted beast doesn't have a ranged attack,
  307.                   * nothing will happen.
  308.                   */
  309.                  if ((mstatus & MM_HIT) && !(mstatus & MM_DEF_DIED)
  310.                      && rn2(4) && mtarg != &youmonst) {
  311.  
  312.                      /* Can monster see? If it can, it can retaliate
  313.                       * even if the pet is invisible, since it'll see
  314.                       * the direction from which the ranged attack came;
  315.                       * if it's blind or unseeing, it can't retaliate
  316.                       */
  317.                      if (mtarg->mcansee && haseyes(mtarg->data)) {
  318.                          mstatus = mattackm(mtarg, mtmp);
  319.                          if (mstatus & MM_DEF_DIED)
  320.                              return 2;
  321.                      }
  322.                  }
  323.              }
  324.          }
  325.      }
  326.  
  327.  newdogpos:
  328.      if (nix != omx || niy != omy) {
  329.          boolean wasseen;
  330.  
  331.          if (info[chi] & ALLOW_U) {
  332.              if (mtmp->mleashed) { /* play it safe */
  333.                  pline("%s breaks loose of %s leash!", Monnam(mtmp),
  334.                        mhis(mtmp));
  335.                  m_unleash(mtmp, FALSE);
  336.              }
  337.              (void) mattacku(mtmp);
  338.              return 0;
  339.          }
  340.          if (!m_in_out_region(mtmp, nix, niy))
  341.              return 1;
  342.          if (m_digweapon_check(mtmp, nix,niy))
  343.              return 0;
  344.  
  345.          /* insert a worm_move() if worms ever begin to eat things */
  346.          wasseen = canseemon(mtmp);
  347.          remove_monster(omx, omy);
  348.          place_monster(mtmp, nix, niy);
  349.          if (cursemsg[chi] && (wasseen || canseemon(mtmp))) {
  350.              /* describe top item of pile, not necessarily cursed item itself;
  351.                 don't use glyph_at() here--it would return the pet but we want
  352.                 to know whether an object is remembered at this map location */
  353.              struct obj *o = (!Hallucination && level.flags.hero_memory
  354.                               && glyph_is_object(levl[nix][niy].glyph))
  355.                                 ? vobj_at(nix, niy) : 0;
  356.              const char *what = o ? distant_name(o, doname) : something;
  357.  
  358.              pline("%s %s reluctantly over %s.", noit_Monnam(mtmp),
  359.                    vtense((char *) 0, locomotion(mtmp->data, "step")), what);
  360.          }
  361.          for (j = MTSZ - 1; j > 0; j--)
  362.              mtmp->mtrack[j] = mtmp->mtrack[j - 1];
  363.          mtmp->mtrack[0].x = omx;
  364.          mtmp->mtrack[0].y = omy;
  365.          /* We have to know if the pet's going to do a combined eat and
  366.           * move before moving it, but it can't eat until after being
  367.           * moved.  Thus the do_eat flag.
  368.           */
  369.          if (do_eat) {
  370.              if (dog_eat(mtmp, obj, omx, omy, FALSE) == 2)
  371.                  return 2;
  372.          }
  373.      } else if (mtmp->mleashed && distu(omx, omy) > 4) {
  374.          /* an incredible kludge, but the only way to keep pooch near
  375.           * after it spends time eating or in a trap, etc.
  376.           */
  377.          coord cc;
  378.  
  379.          nx = sgn(omx - u.ux);
  380.          ny = sgn(omy - u.uy);
  381.          cc.x = u.ux + nx;
  382.          cc.y = u.uy + ny;
  383.          if (goodpos(cc.x, cc.y, mtmp, 0))
  384.              goto dognext;
  385.  
  386.          i = xytod(nx, ny);
  387.          for (j = (i + 7) % 8; j < (i + 1) % 8; j++) {
  388.              dtoxy(&cc, j);
  389.              if (goodpos(cc.x, cc.y, mtmp, 0))
  390.                  goto dognext;
  391.          }
  392.          for (j = (i + 6) % 8; j < (i + 2) % 8; j++) {
  393.              dtoxy(&cc, j);
  394.              if (goodpos(cc.x, cc.y, mtmp, 0))
  395.                  goto dognext;
  396.          }
  397.          cc.x = mtmp->mx;
  398.          cc.y = mtmp->my;
  399.      dognext:
  400.          if (!m_in_out_region(mtmp, nix, niy))
  401.              return 1;
  402.          remove_monster(mtmp->mx, mtmp->my);
  403.          place_monster(mtmp, cc.x, cc.y);
  404.          newsym(cc.x, cc.y);
  405.          set_apparxy(mtmp);
  406.      }
  407.      return 1;
  408.  }
  409.  

could_reach_item

  1.  /* check if a monster could pick up objects from a location */
  2.  STATIC_OVL boolean
  3.  could_reach_item(mon, nx, ny)
  4.  struct monst *mon;
  5.  xchar nx, ny;
  6.  {
  7.      if ((!is_pool(nx, ny) || is_swimmer(mon->data))
  8.          && (!is_lava(nx, ny) || likes_lava(mon->data))
  9.          && (!sobj_at(BOULDER, nx, ny) || throws_rocks(mon->data)))
  10.          return TRUE;
  11.      return FALSE;
  12.  }
  13.  

can_reach_location

  1.  /* Hack to prevent a dog from being endlessly stuck near an object that
  2.   * it can't reach, such as caught in a teleport scroll niche.  It recursively
  3.   * checks to see if the squares in between are good.  The checking could be
  4.   * a little smarter; a full check would probably be useful in m_move() too.
  5.   * Since the maximum food distance is 5, this should never be more than 5
  6.   * calls deep.
  7.   */
  8.  STATIC_OVL boolean
  9.  can_reach_location(mon, mx, my, fx, fy)
  10.  struct monst *mon;
  11.  xchar mx, my, fx, fy;
  12.  {
  13.      int i, j;
  14.      int dist;
  15.  
  16.      if (mx == fx && my == fy)
  17.          return TRUE;
  18.      if (!isok(mx, my))
  19.          return FALSE; /* should not happen */
  20.  
  21.      dist = dist2(mx, my, fx, fy);
  22.      for (i = mx - 1; i <= mx + 1; i++) {
  23.          for (j = my - 1; j <= my + 1; j++) {
  24.              if (!isok(i, j))
  25.                  continue;
  26.              if (dist2(i, j, fx, fy) >= dist)
  27.                  continue;
  28.              if (IS_ROCK(levl[i][j].typ) && !passes_walls(mon->data)
  29.                  && (!may_dig(i, j) || !tunnels(mon->data)))
  30.                  continue;
  31.              if (IS_DOOR(levl[i][j].typ)
  32.                  && (levl[i][j].doormask & (D_CLOSED | D_LOCKED)))
  33.                  continue;
  34.              if (!could_reach_item(mon, i, j))
  35.                  continue;
  36.              if (can_reach_location(mon, i, j, fx, fy))
  37.                  return TRUE;
  38.          }
  39.      }
  40.      return FALSE;
  41.  }
  42.  

wantdoor

  1.  /* do_clear_area client */
  2.  STATIC_PTR void
  3.  wantdoor(x, y, distance)
  4.  int x, y;
  5.  genericptr_t distance;
  6.  {
  7.      int ndist, *dist_ptr = (int *) distance;
  8.  
  9.      if (*dist_ptr > (ndist = distu(x, y))) {
  10.          gx = x;
  11.          gy = y;
  12.          *dist_ptr = ndist;
  13.      }
  14.  }
  15.  
  16.  static struct qmchoices {
  17.      int mndx;             /* type of pet, 0 means any  */
  18.      char mlet;            /* symbol of pet, 0 means any */
  19.      unsigned mappearance; /* mimic this */
  20.      uchar m_ap_type;      /* what is the thing it is mimicing? */
  21.  } qm[] = {
  22.      /* Things that some pets might be thinking about at the time */
  23.      { PM_LITTLE_DOG, 0, PM_KITTEN, M_AP_MONSTER },
  24.      { PM_DOG, 0, PM_HOUSECAT, M_AP_MONSTER },
  25.      { PM_LARGE_DOG, 0, PM_LARGE_CAT, M_AP_MONSTER },
  26.      { PM_KITTEN, 0, PM_LITTLE_DOG, M_AP_MONSTER },
  27.      { PM_HOUSECAT, 0, PM_DOG, M_AP_MONSTER },
  28.      { PM_LARGE_CAT, 0, PM_LARGE_DOG, M_AP_MONSTER },
  29.      { PM_HOUSECAT, 0, PM_GIANT_RAT, M_AP_MONSTER },
  30.      { 0, S_DOG, SINK,
  31.        M_AP_FURNITURE }, /* sorry, no fire hydrants in NetHack */
  32.      { 0, 0, TRIPE_RATION, M_AP_OBJECT }, /* leave this at end */
  33.  };
  34.  

finish_meating

  1.  void
  2.  finish_meating(mtmp)
  3.  struct monst *mtmp;
  4.  {
  5.      mtmp->meating = 0;
  6.      if (mtmp->m_ap_type && mtmp->mappearance && mtmp->cham == NON_PM) {
  7.          /* was eating a mimic and now appearance needs resetting */
  8.          mtmp->m_ap_type = 0;
  9.          mtmp->mappearance = 0;
  10.          newsym(mtmp->mx, mtmp->my);
  11.      }
  12.  }
  13.  

quickmimic

  1.  STATIC_OVL void
  2.  quickmimic(mtmp)
  3.  struct monst *mtmp;
  4.  {
  5.      int idx = 0, trycnt = 5, spotted;
  6.      char buf[BUFSZ];
  7.  
  8.      if (Protection_from_shape_changers || !mtmp->meating)
  9.          return;
  10.  
  11.      do {
  12.          idx = rn2(SIZE(qm));
  13.          if (qm[idx].mndx != 0 && monsndx(mtmp->data) == qm[idx].mndx)
  14.              break;
  15.          if (qm[idx].mlet != 0 && mtmp->data->mlet == qm[idx].mlet)
  16.              break;
  17.          if (qm[idx].mndx == 0 && qm[idx].mlet == 0)
  18.              break;
  19.      } while (--trycnt > 0);
  20.      if (trycnt == 0)
  21.          idx = SIZE(qm) - 1;
  22.  
  23.      Strcpy(buf, mon_nam(mtmp));
  24.      spotted = canspotmon(mtmp);
  25.  
  26.      mtmp->m_ap_type = qm[idx].m_ap_type;
  27.      mtmp->mappearance = qm[idx].mappearance;
  28.  
  29.      if (spotted || cansee(mtmp->mx, mtmp->my) || canspotmon(mtmp)) {
  30.          /* this isn't quite right; if sensing a monster without being
  31.             able to see its location, you really shouldn't be told you
  32.             sense it becoming furniture or an object that you can't see
  33.             (on the other hand, perhaps you're sensing a brief glimpse
  34.             of its mind as it changes form) */
  35.          newsym(mtmp->mx, mtmp->my);
  36.          You("%s %s %sappear%s where %s was!",
  37.              cansee(mtmp->mx, mtmp->my) ? "see" : "sense that",
  38.              (mtmp->m_ap_type == M_AP_FURNITURE)
  39.                  ? an(defsyms[mtmp->mappearance].explanation)
  40.                  : (mtmp->m_ap_type == M_AP_OBJECT
  41.                     && OBJ_DESCR(objects[mtmp->mappearance]))
  42.                        ? an(OBJ_DESCR(objects[mtmp->mappearance]))
  43.                        : (mtmp->m_ap_type == M_AP_OBJECT
  44.                           && OBJ_NAME(objects[mtmp->mappearance]))
  45.                              ? an(OBJ_NAME(objects[mtmp->mappearance]))
  46.                              : (mtmp->m_ap_type == M_AP_MONSTER)
  47.                                    ? an(mons[mtmp->mappearance].mname)
  48.                                    : something,
  49.              cansee(mtmp->mx, mtmp->my) ? "" : "has ",
  50.              cansee(mtmp->mx, mtmp->my) ? "" : "ed",
  51.              buf);
  52.          display_nhwindow(WIN_MAP, TRUE);
  53.      }
  54.  }
  55.  
  56.  /*dogmove.c*/