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

dothrow.c

/*    SCCS Id: @(#)dothrow.c  3.3   2000/04/16  */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed.  See license for details. */

/* Contains code for 't' (throw) */

#include "hack.h"

STATIC_DCL int FDECL(throw_obj, (struct obj *,int));
STATIC_DCL void NDECL(autoquiver);
STATIC_DCL int FDECL(gem_accept, (struct monst *, struct obj *));
STATIC_DCL int FDECL(throw_gold, (struct obj *));
STATIC_DCL void FDECL(check_shop_obj, (struct obj *,XCHAR_P,XCHAR_P,BOOLEAN_P));
STATIC_DCL void FDECL(breakobj, (struct obj *,XCHAR_P,XCHAR_P,BOOLEAN_P,BOOLEAN_P));
STATIC_DCL void FDECL(breakmsg, (struct obj *,BOOLEAN_P));
STATIC_DCL boolean FDECL(toss_up,(struct obj *, BOOLEAN_P));
STATIC_DCL boolean FDECL(throwing_weapon, (struct obj *));
STATIC_DCL void FDECL(sho_obj_return_to_u, (struct obj *obj));


static NEARDATA const char toss_objs[] =
      { ALLOW_COUNT, GOLD_CLASS, ALL_CLASSES, WEAPON_CLASS, 0 };
/* different default choices when wielding a sling (gold must be included) */
static NEARDATA const char bullets[] =
      { ALLOW_COUNT, GOLD_CLASS, ALL_CLASSES, GEM_CLASS, 0 };

extern boolean notonhead;     /* for long worms */


/* Throw the selected object, asking for direction */
STATIC_OVL int
throw_obj(obj, shotlimit)
struct obj *obj;
int shotlimit;
{
      struct obj *otmp;
      int multishot = 1;
      schar skill;
      long wep_mask;

      /* ask "in what direction?" */
      if (!getdir((char *)0)) {
            if (obj->oclass == GOLD_CLASS) {
                u.ugold += obj->quan;
                flags.botl = 1;
                dealloc_obj(obj);
            }
            return(0);
      }

      if(obj->oclass == GOLD_CLASS) return(throw_gold(obj));

      if(!canletgo(obj,"throw"))
            return(0);
      if (obj->oartifact == ART_MJOLLNIR && obj != uwep) {
          pline("%s must be wielded before it can be thrown.",
            The(xname(obj)));
            return(0);
      }
      if ((obj->oartifact == ART_MJOLLNIR && ACURR(A_STR) < STR19(25))
         || (obj->otyp == BOULDER && !throws_rocks(youmonst.data))) {
            pline("It's too heavy.");
            return(1);
      }
      if(!u.dx && !u.dy && !u.dz) {
            You("cannot throw an object at yourself.");
            return(0);
      }
      if (!uarmg && !Stone_resistance && (obj->otyp == CORPSE &&
                touch_petrifies(&mons[obj->corpsenm]))) {
            You("throw the %s corpse with your bare %s.",
                mons[obj->corpsenm].mname, body_part(HAND));
            Sprintf(killer_buf, "%s corpse", an(mons[obj->corpsenm].mname));
            instapetrify(killer_buf);
      }
      u_wipe_engr(2);

      /* Multishot calculations
       */
      skill = objects[obj->otyp].oc_skill;
      if ((ammo_and_launcher(obj, uwep) || skill == P_DAGGER ||
                  skill == -P_DART || skill == -P_SHURIKEN) &&
            !(Confusion || Stunned)) {
          /* Bonus if the player is proficient in this weapon... */
          switch (P_SKILL(weapon_type(obj))) {
          default:      break; /* No bonus */
          case P_SKILLED:     multishot++; break;
          case P_EXPERT:      multishot += 2; break;
          }
          /* ...or is using a special weapon for their role... */
          switch (Role_switch) {
          case PM_RANGER:
            multishot++;
            break;
          case PM_ROGUE:
            if (skill == P_DAGGER) multishot++;
            break;
          case PM_SAMURAI:
            if (obj->otyp == YA && uwep && uwep->otyp == YUMI) multishot++;
            break;
          default:
            break;      /* No bonus */
          }
          /* ...or using their race's special bow */
          switch (Race_switch) {
          case PM_ELF:
            if (obj->otyp == ELVEN_ARROW && uwep &&
                        uwep->otyp == ELVEN_BOW) multishot++;
            break;
          case PM_ORC:
            if (obj->otyp == ORCISH_ARROW && uwep &&
                        uwep->otyp == ORCISH_BOW) multishot++;
            break;
          default:
            break;      /* No bonus */
          }
      }

      if (obj->quan < multishot) multishot = (int)obj->quan;
      multishot = rnd(multishot);
      if (shotlimit > 0 && multishot > shotlimit) multishot = shotlimit;

      while (obj && multishot-- > 0) {
            wep_mask = obj->owornmask;
            /* Split this object off from its slot */
            otmp = (struct obj *)0;
            if (obj == uquiver) {
                  if(obj->quan > 1L)
                        setuqwep(otmp = splitobj(obj, 1L));
                  else
                        setuqwep((struct obj *)0);
            } else if (obj == uswapwep) {
                  if(obj->quan > 1L)
                        setuswapwep(otmp = splitobj(obj, 1L));
                  else
                        setuswapwep((struct obj *)0);
            } else if (obj == uwep) {
                if (welded(obj)) {
                  weldmsg(obj);
                  return(1);
                }
                if (obj->quan > 1L)
                  setworn(otmp = splitobj(obj, 1L), W_WEP);
                  /* not setuwep; do not change unweapon */
                else {
                  setuwep((struct obj *)0);
                  if (uwep) return(1); /* unwielded, died, rewielded */
                }
            } else if(obj->quan > 1L)
                  otmp = splitobj(obj, 1L);
            freeinv(obj);
            throwit(obj, wep_mask);
            obj = otmp;
      }     /* while (multishot) */
      return(1);
}


int
dothrow()
{
      register struct obj *obj;
      int shotlimit;

      /*
       * Since some characters shoot multiple missiles at one time,
       * allow user to specify a count prefix for 'f' or 't' to limit
       * number of items thrown (to avoid possibly hitting something
       * behind target after killing it, or perhaps to conserve ammo).
       *
       * Prior to 3.3.0, command ``3t'' meant ``t(shoot) t(shoot) t(shoot)''
       * and took 3 turns.  Now it means ``t(shoot at most 3 missiles)''.
       */
      /* kludge to work around parse()'s pre-decrement of `multi' */
      shotlimit = (multi || save_cm) ? multi + 1 : 0;
      multi = 0;        /* reset; it's been used up */

      if(check_capacity((char *)0)) return(0);
      obj = getobj(uslinging() ? bullets : toss_objs, "throw");
      /* it is also possible to throw food */
      /* (or jewels, or iron balls... ) */

      if (!obj) return(0);
      return throw_obj(obj, shotlimit);
}


/* KMH -- Automatically fill quiver */
/* Suggested by Jeffrey Bay <jbay@convex.hp.com> */
static void
autoquiver()
{
      register struct obj *otmp, *oammo = 0, *omissile = 0, *omisc = 0;

      if (uquiver)
            return;

      /* Scan through the inventory */
      for (otmp = invent; otmp; otmp = otmp->nobj) {
            if (otmp->owornmask || otmp->oartifact || !otmp->dknown) {
                  ;     /* Skip it */
            } else if (otmp->otyp == ROCK ||
                  /* seen rocks or known flint or known glass */
                  (objects[otmp->otyp].oc_name_known &&
                   otmp->otyp == FLINT) ||
                  (objects[otmp->otyp].oc_name_known &&
                   otmp->oclass == GEM_CLASS &&
                   objects[otmp->otyp].oc_material == GLASS)) {
                  if (uslinging())
                      oammo = otmp;
                  else if (!omisc)
                      omisc = otmp;
            } else if (otmp->oclass == GEM_CLASS) {
                  ;     /* skip non-rock gems--they're ammo but
                           player has to select them explicitly */
            } else if (is_ammo(otmp)) {
                  if (ammo_and_launcher(otmp, uwep))
                        /* Ammo matched with launcher (bow and arrow, crossbow and bolt) */
                        oammo = otmp;
                  else
                        /* Mismatched ammo (no better than an ordinary weapon) */
                        omisc = otmp;
            } else if (is_missile(otmp)) {
                  /* Missile (dart, shuriken, etc.) */
                  omissile = otmp;
            } else if (otmp->oclass == WEAPON_CLASS && !is_launcher(otmp)) {
                  /* Ordinary weapon */
                  omisc = otmp;
            }
      }

      /* Pick the best choice */
      if (oammo)
            setuqwep(oammo);
      else if (omissile)
            setuqwep(omissile);
      else if (omisc)
            setuqwep(omisc);

      return;
}


/* Throw from the quiver */
int
dofire()
{
      int shotlimit;

      if(check_capacity((char *)0)) return(0);
      if (!uquiver) {
            if (!flags.autoquiver) {
                  /* Don't automatically fill the quiver */
                  You("have no ammunition readied!");
                  return(dothrow());
            }
            autoquiver();
            if (!uquiver) {
                  You("have nothing appropriate for your quiver!");
                  return(dothrow());
            } else {
                  You("fill your quiver:");
                  prinv((char *)0, uquiver, 0L);
            }
      }

      /*
       * Since some characters shoot multiple missiles at one time,
       * allow user to specify a count prefix for 'f' or 't' to limit
       * number of items thrown (to avoid possibly hitting something
       * behind target after killing it, or perhaps to conserve ammo).
       *
       * The number specified can never increase the number of missiles.
       * Using ``5f'' when the shooting skill (plus RNG) dictates launch
       * of 3 projectiles will result in 3 being shot, not 5.
       */
      /* kludge to work around parse()'s pre-decrement of `multi' */
      shotlimit = (multi || save_cm) ? multi + 1 : 0;
      multi = 0;        /* reset; it's been used up */

      return throw_obj(uquiver, shotlimit);
}


/*
 * Object hits floor at hero's feet.  Called from drop() and throwit().
 */
void
hitfloor(obj)
register struct obj *obj;
{
      if (IS_SOFT(levl[u.ux][u.uy].typ) || u.uinwater) {
            dropy(obj);
            return;
      }
      if (IS_ALTAR(levl[u.ux][u.uy].typ))
            doaltarobj(obj);
      else
            pline("%s hit%s the %s.", Doname2(obj),
                  (obj->quan == 1L) ? "s" : "", surface(u.ux,u.uy));

      if (hero_breaks(obj, u.ux, u.uy, TRUE)) return;
      if (ship_object(obj, u.ux, u.uy, FALSE)) return;
      dropy(obj);
}

/*
 * Walk a path from src_cc to dest_cc, calling a proc for each location
 * except the starting one.  If the proc returns FALSE, stop walking
 * and return FALSE.  If stopped early, dest_cc will be the location
 * before the failed callback.
 */
boolean
walk_path(src_cc, dest_cc, check_proc, arg)
    coord *src_cc;
    coord *dest_cc;
    boolean FDECL((*check_proc), (genericptr_t, int, int));
    genericptr_t arg;
{
    int x, y, dx, dy, x_change, y_change, err, i, prev_x, prev_y;
    boolean keep_going = TRUE;

    /* Use Bresenham's Line Algorithm to walk from src to dest */
    dx = dest_cc->x - src_cc->x;
    dy = dest_cc->y - src_cc->y;
    prev_x = x = src_cc->x;
    prev_y = y = src_cc->y;

    if (dx < 0) {
      x_change = -1;
      dx = -dx;
    } else
      x_change = 1;
    if (dy < 0) {
      y_change = -1;
      dy = -dy;
    } else
      y_change = 1;

    i = err = 0;
    if (dx < dy) {
      while (i++ < dy) {
          prev_x = x;
          prev_y = y;
          y += y_change;
          err += dx;
          if (err >= dy) {
            x += x_change;
            err -= dy;
          }
      /* check for early exit condition */
      if (!(keep_going = (*check_proc)(arg, x, y)))
          break;
      }
    } else {
      while (i++ < dx) {
          prev_x = x;
          prev_y = y;
          x += x_change;
          err += dy;
          if (err >= dx) {
            y += y_change;
            err -= dx;
          }
      /* check for early exit condition */
      if (!(keep_going = (*check_proc)(arg, x, y)))
          break;
      }
    }

    if (keep_going)
      return TRUE;      /* successful */

    dest_cc->x = prev_x;
    dest_cc->y = prev_y;
    return FALSE;
}

/*
 * Single step for the hero flying through the air from jumping, flying,
 * etc.  Called from hurtle() and jump() via walk_path().  We expect the
 * argument to be a pointer to an integer -- the range -- which is
 * used in the calculation of points off it we hit something.
 *
 * Bumping into monsters won't cause damage but will wake them and make
 * them angry.  Auto-pickup isn't done, since you don't have control over
 * your movements at the time.
 *
 * Possible additions/changes:
 *    o really attack monster if we hit one
 *    o set stunned if we hit a wall or door
 *    o reset nomul when we stop
 *    o creepy feeling if pass through monster (if ever implemented...)
 *    o bounce off walls
 *    o let jumps go over boulders
 */
boolean
hurtle_step(arg, x, y)
    genericptr_t arg;
    int x, y;
{
    int ox, oy, *range = (int *)arg;
    struct obj *obj;
    struct monst *mon;
    boolean may_pass = TRUE;

    if (!isok(x,y)) {
      You_feel("the spirits holding you back.");
      return FALSE;
    }

    if (!Passes_walls || !(may_pass = may_passwall(x, y))) {
      if (IS_ROCK(levl[x][y].typ) || closed_door(x,y)) {
          pline("Ouch!");
          losehp(rnd(2+*range), IS_ROCK(levl[x][y].typ) ?
               "bumping into a wall" : "bumping into a door", KILLED_BY);
          return FALSE;
      }
      if (levl[x][y].typ == IRONBARS) {
          You("crash into some iron bars.  Ouch!");
          losehp(rnd(2+*range), "crashing into iron bars", KILLED_BY);
          return FALSE;
      }
      if ((obj = sobj_at(BOULDER,x,y)) != 0) {
          You("bump into a %s.  Ouch!", xname(obj));
          losehp(rnd(2+*range), "bumping into a boulder", KILLED_BY);
          return FALSE;
      }
      if (!may_pass) {
          /* did we hit a no-dig non-wall position? */
          You("smack into something!");
          losehp(rnd(2+*range), "touching the edge of the universe", KILLED_BY);
          return FALSE;
      }
    }

    if ((mon = m_at(x, y)) != 0) {
      You("bump into %s.", a_monnam(mon));
      wakeup(mon);
      return FALSE;
    }

    ox = u.ux;
    oy = u.uy;
    u.ux = x;
    u.uy = y;
    newsym(ox, oy);           /* update old position */
    vision_recalc(1);         /* update for new position */
    flush_screen(1);
    if (--*range < 0)         /* make sure our range never goes negative */
      *range = 0;
    if (*range != 0)
      delay_output();
    return TRUE;
}

/*
 * The player moves through the air for a few squares as a result of
 * throwing or kicking something.
 *
 * dx and dy should be the direction of the hurtle, not of the original
 * kick or throw and be only.
 */
void
hurtle(dx, dy, range, verbose)
    int dx, dy, range;
    boolean verbose;
{
    coord uc, cc;

    /* The chain is stretched vertically, so you shouldn't be able to move
     * very far diagonally.  The premise that you should be able to move one
     * spot leads to calculations that allow you to only move one spot away
     * from the ball, if you are levitating over the ball, or one spot
     * towards the ball, if you are at the end of the chain.  Rather than
     * bother with all of that, assume that there is no slack in the chain
     * for diagonal movement, give the player a message and return.
     */
    if(Punished && !carried(uball)) {
      You_feel("a tug from the iron ball.");
      nomul(0);
      return;
    } else if (u.utrap) {
      You("are anchored by the %s.",
          u.utraptype == TT_WEB ? "web" : u.utraptype == TT_LAVA ? "lava" :
            u.utraptype == TT_INFLOOR ? surface(u.ux,u.uy) : "trap");
      nomul(0);
      return;
    }

    /* make sure dx and dy are [-1,0,1] */
    dx = sgn(dx);
    dy = sgn(dy);

    if(!range || (!dx && !dy) || u.ustuck) return; /* paranoia */

    nomul(-range);
    if (verbose)
      You("%s in the opposite direction.", range > 1 ? "hurtle" : "float");
    if (In_sokoban(&u.uz))
      change_luck(-1);  /* Sokoban guilt */
    uc.x = u.ux;
    uc.y = u.uy;
    /* this setting of cc is only correct if dx and dy are [-1,0,1] only */
    cc.x = u.ux + (dx * range);
    cc.y = u.uy + (dy * range);
    (void) walk_path(&uc, &cc, hurtle_step, (genericptr_t)&range);
}

STATIC_OVL void
check_shop_obj(obj, x, y, broken)
register struct obj *obj;
register xchar x, y;
register boolean broken;
{
      struct monst *shkp = shop_keeper(*u.ushops);

      if(!shkp) return;

      if(broken) {
            if (obj->unpaid) {
                (void)stolen_value(obj, u.ux, u.uy,
                               (boolean)shkp->mpeaceful, FALSE);
                subfrombill(obj, shkp);
            }
            obj->no_charge = 1;
            return;
      }

      if (!costly_spot(x, y) || *in_rooms(x, y, SHOPBASE) != *u.ushops) {
            /* thrown out of a shop or into a different shop */
            if (obj->unpaid) {
                (void)stolen_value(obj, u.ux, u.uy,
                               (boolean)shkp->mpeaceful, FALSE);
                subfrombill(obj, shkp);
            }
      } else {
            if (costly_spot(u.ux, u.uy) && costly_spot(x, y)) {
                if(obj->unpaid) subfrombill(obj, shkp);
                else if(!(x == shkp->mx && y == shkp->my))
                      sellobj(obj, x, y);
            }
      }
}

/*
 * Hero tosses an object upwards with appropriate consequences.
 *
 * Returns FALSE if the object is gone.
 */
STATIC_OVL boolean
toss_up(obj, hitsroof)
struct obj *obj;
boolean hitsroof;
{
    const char *almost;
    /* note: obj->quan == 1 */

    if (hitsroof) {
      if (breaktest(obj)) {
            pline("%s hits the %s.", Doname2(obj), ceiling(u.ux, u.uy));
            breakmsg(obj, !Blind);
            breakobj(obj, u.ux, u.uy, TRUE, TRUE);
            return FALSE;
      }
      almost = "";
    } else {
      almost = " almost";
    }
    pline("%s%s hits the %s, then falls back on top of your %s.",
        Doname2(obj), almost, ceiling(u.ux,u.uy), body_part(HEAD));

    /* object now hits you */

    if (obj->oclass == POTION_CLASS) {
      potionhit(&youmonst, obj, TRUE);
    } else if (breaktest(obj)) {
      int otyp = obj->otyp, ocorpsenm = obj->corpsenm;
      int blindinc;

      breakmsg(obj, !Blind);
      breakobj(obj, u.ux, u.uy, TRUE, TRUE);
      obj = 0;    /* it's now gone */
      switch (otyp) {
      case EGG:
            if (touch_petrifies(&mons[ocorpsenm]) &&
                !uarmh && !Stone_resistance &&
                !(poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM)))
            goto petrify;
      case CREAM_PIE:
      case BLINDING_VENOM:
            pline("You've got it all over your %s!", body_part(FACE));
            blindinc = rnd(25);
            if (blindinc && !Blindfolded) {
                if (otyp != BLINDING_VENOM)
                  u.ucreamed += blindinc;
                else if (!Blind)
                  pline("It blinds you!");
                make_blinded(Blinded + blindinc, FALSE);
            }
            break;
      default:
            break;
      }
      return FALSE;
    } else {            /* neither potion nor other breaking object */
      boolean less_damage = uarmh && is_metallic(uarmh);
      int dmg = dmgval(obj, &youmonst);

      if (!dmg) { /* probably wasn't a weapon; base damage on weight */
          dmg = (int) obj->owt / 100;
          if (dmg < 1) dmg = 1;
          else if (dmg > 6) dmg = 6;
          if (youmonst.data == &mons[PM_SHADE] &&
                objects[obj->otyp].oc_material != SILVER)
            dmg = 0;
      }
      if (dmg > 1 && less_damage) dmg = 1;
      if (dmg > 0) dmg += u.udaminc;
      if (dmg < 0) dmg = 0;   /* beware negative rings of increase damage */

      if (uarmh) {
          if (less_damage && dmg < (Upolyd ? u.mh : u.uhp))
            pline("Fortunately, you are wearing a hard helmet.");
          else if (flags.verbose &&
                !(obj->otyp == CORPSE && touch_petrifies(&mons[obj->corpsenm])))
            Your("%s does not protect you.", xname(uarmh));
      } else if (obj->otyp == CORPSE && touch_petrifies(&mons[obj->corpsenm])) {
          if (!Stone_resistance &&
                !(poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))) {
 petrify:
            killer_format = KILLED_BY;
            killer = "elementary physics";      /* "what goes up..." */
            You("turn to stone.");
            if (obj) dropy(obj);    /* bypass most of hitfloor() */
            done(STONING);
            return obj ? TRUE : FALSE;
          }
      }
      hitfloor(obj);
      losehp(dmg, "falling object", KILLED_BY_AN);
    }
    return TRUE;
}

/* return true for weapon meant to be thrown; excludes ammo */
STATIC_OVL boolean
throwing_weapon(obj)
struct obj *obj;
{
      return (is_missile(obj) || is_spear(obj) ||
                  /* daggers and knife (excludes scalpel) */
                  (is_blade(obj) && (objects[obj->otyp].oc_dir & PIERCE)) ||
                  /* special cases [might want to add AXE] */
                  obj->otyp == WAR_HAMMER || obj->otyp == AKLYS);
}

/* the currently thrown object is returning to you (not for boomerangs) */
STATIC_OVL void
sho_obj_return_to_u(obj)
struct obj *obj;
{
    /* might already be our location (bounced off a wall) */
    if (bhitpos.x != u.ux || bhitpos.y != u.uy) {
      int x = bhitpos.x - u.dx, y = bhitpos.y - u.dy;

      tmp_at(DISP_FLASH, obj_to_glyph(obj));
      while(x != u.ux || y != u.uy) {
          tmp_at(x, y);
          delay_output();
          x -= u.dx; y -= u.dy;
      }
      tmp_at(DISP_END, 0);
    }
}

void
throwit(obj, wep_mask)
register struct obj *obj;
long wep_mask;    /* used to re-equip returning boomerang */
{
      register struct monst *mon;
      register int range, urange;
      boolean impaired = (Confusion || Stunned || Blind ||
                     Hallucination || Fumbling);

      if ((obj->cursed || obj->greased) && (u.dx || u.dy) && !rn2(7)) {
          boolean slipok = TRUE;
          if (ammo_and_launcher(obj, uwep))
            pline("%s misfires!", The(xname(obj)));
          else {
            /* only slip if it's greased or meant to be thrown */
            if (obj->greased || throwing_weapon(obj))
                /* BUG: this message is grammatically incorrect if obj has
                   a plural name; greased gloves or boots for instance. */
                pline("%s slips as you throw it!", The(xname(obj)));
            else slipok = FALSE;
          }
          if (slipok) {
            u.dx = rn2(3)-1;
            u.dy = rn2(3)-1;
            if (!u.dx && !u.dy) u.dz = 1;
            impaired = TRUE;
          }
      }

      if(u.uswallow) {
            mon = u.ustuck;
            bhitpos.x = mon->mx;
            bhitpos.y = mon->my;
      } else if(u.dz) {
          if (u.dz < 0 && Role_if(PM_VALKYRIE) &&
                obj->oartifact == ART_MJOLLNIR && !impaired) {
            pline("%s hits the %s and returns to your hand!",
                  The(xname(obj)), ceiling(u.ux,u.uy));
            obj = addinv(obj);
            (void) encumber_msg();
            setuwep(obj);
          } else if (u.dz < 0 && !Is_airlevel(&u.uz) &&
                !Underwater && !Is_waterlevel(&u.uz)) {
            (void) toss_up(obj, rn2(5));
          } else {
            hitfloor(obj);
          }
          return;

      } else if(obj->otyp == BOOMERANG && !Underwater) {
            if(Is_airlevel(&u.uz) || Levitation)
                hurtle(-u.dx, -u.dy, 1, TRUE);
            mon = boomhit(u.dx, u.dy);
            if(mon == &youmonst) {        /* the thing was caught */
                  exercise(A_DEX, TRUE);
                  obj = addinv(obj);
                  (void) encumber_msg();
                  if (wep_mask && !(obj->owornmask & wep_mask))
                      setworn(obj, wep_mask);
                  return;
            }
      } else {
            urange = (int)(ACURRSTR)/2;
            /* balls are easy to throw or at least roll */
            /* also, this insures the maximum range of a ball is greater
             * than 1, so the effects from throwing attached balls are
             * actually possible
             */
            if (obj->otyp == HEAVY_IRON_BALL)
                  range = urange - (int)(obj->owt/100);
            else
                  range = urange - (int)(obj->owt/40);
            if (obj == uball) {
                  if (u.ustuck) range = 1;
                  else if (range >= 5) range = 5;
            }
            if (range < 1) range = 1;

            if (is_ammo(obj)) {
                if (ammo_and_launcher(obj, uwep))
                  range++;
                else
                  range /= 2;
            }

            if (Is_airlevel(&u.uz) || Levitation) {
                /* action, reaction... */
                urange -= range;
                if(urange < 1) urange = 1;
                range -= urange;
                if(range < 1) range = 1;
            }

            if (obj->otyp == BOULDER)
                range = 20;         /* you must be giant */
            else if (obj->oartifact == ART_MJOLLNIR)
                range = (range + 1) / 2;  /* it's heavy */
            else if (obj == uball && u.utrap && u.utraptype == TT_INFLOOR)
                range = 1;

            if (Underwater) range = 1;

            mon = bhit(u.dx, u.dy, range, THROWN_WEAPON,
                     (int FDECL((*),(MONST_P,OBJ_P)))0,
                     (int FDECL((*),(OBJ_P,OBJ_P)))0,
                     obj);

            /* have to do this after bhit() so u.ux & u.uy are correct */
            if(Is_airlevel(&u.uz) || Levitation)
                hurtle(-u.dx, -u.dy, urange, TRUE);
      }

      if (mon) {
            boolean obj_gone;

            if (mon->isshk &&
                  obj->where == OBJ_MINVENT && obj->ocarry == mon)
                return;       /* alert shk caught it */
            (void) snuff_candle(obj);
            notonhead = (bhitpos.x != mon->mx || bhitpos.y != mon->my);
            obj_gone = thitmonst(mon, obj);
            /* Monster may have been tamed; this frees old mon */
            mon = m_at(bhitpos.x, bhitpos.y);

            /* [perhaps this should be moved into thitmonst or hmon] */
            if (mon && mon->isshk &&
                  (!inside_shop(u.ux, u.uy) ||
                   !index(in_rooms(mon->mx, mon->my, SHOPBASE), *u.ushops)))
                hot_pursuit(mon);

            if (obj_gone) return;
      }

      if (u.uswallow) {
            /* ball is not picked up by monster */
            if (obj != uball) (void) mpickobj(u.ustuck,obj);
      } else {
            /* the code following might become part of dropy() */
            if (obj->oartifact == ART_MJOLLNIR &&
                  Role_if(PM_VALKYRIE) && rn2(100)) {
                /* we must be wearing Gauntlets of Power to get here */
                sho_obj_return_to_u(obj);     /* display its flight */

                if (!impaired && rn2(100)) {
                  pline("%s returns to your hand!", The(xname(obj)));
                  obj = addinv(obj);
                  (void) encumber_msg();
                  setuwep(obj);
                  if(cansee(bhitpos.x, bhitpos.y))
                      newsym(bhitpos.x,bhitpos.y);
                } else {
                  int dmg = rnd(4);
                  if (Blind)
                      pline("%s hits your %s!",
                          The(xname(obj)), body_part(ARM));
                  else
                      pline("%s flies back toward you, hitting your %s!",
                          The(xname(obj)), body_part(ARM));
                  (void) artifact_hit((struct monst *) 0, &youmonst,
                                  obj, &dmg, 0);
                  losehp(dmg, xname(obj), KILLED_BY);
                  if(ship_object(obj, u.ux, u.uy, FALSE))
                        return;
                  dropy(obj);
                }
                return;
            }

            if (!IS_SOFT(levl[bhitpos.x][bhitpos.y].typ) &&
                  breaktest(obj)) {
                tmp_at(DISP_FLASH, obj_to_glyph(obj));
                tmp_at(bhitpos.x, bhitpos.y);
                delay_output();
                tmp_at(DISP_END, 0);
                breakmsg(obj, cansee(bhitpos.x, bhitpos.y));
                breakobj(obj, bhitpos.x, bhitpos.y, TRUE, TRUE);
                return;
            }
            if(flooreffects(obj,bhitpos.x,bhitpos.y,"fall")) return;
            if (obj->otyp == CRYSKNIFE && (!obj->oerodeproof || !rn2(10))) {
                obj->otyp = WORM_TOOTH;
                obj->oerodeproof = 0;
            }
            if (mon && mon->isshk && is_pick(obj)) {
                if (cansee(bhitpos.x, bhitpos.y))
                  pline("%s snatches up %s.",
                        Monnam(mon), the(xname(obj)));
                if(*u.ushops)
                  check_shop_obj(obj, bhitpos.x, bhitpos.y, FALSE);
                (void) mpickobj(mon, obj);      /* may merge and free obj */
                return;
            }
            (void) snuff_candle(obj);
            if (!mon && ship_object(obj, bhitpos.x, bhitpos.y, FALSE))
                return;
            place_object(obj, bhitpos.x, bhitpos.y);
            if(*u.ushops && obj != uball)
                check_shop_obj(obj, bhitpos.x, bhitpos.y, FALSE);

            stackobj(obj);
            if (obj == uball)
                drop_ball(bhitpos.x, bhitpos.y);
            if (cansee(bhitpos.x, bhitpos.y))
                newsym(bhitpos.x,bhitpos.y);
            if (obj_sheds_light(obj))
                vision_full_recalc = 1;
      }
}

/* an object may hit a monster; various factors adjust the chance of hitting */
int
omon_adj(mon, obj, mon_notices)
struct monst *mon;
struct obj *obj;
boolean mon_notices;
{
      int tmp = 0;

      /* size of target affects the chance of hitting */
      tmp += (mon->data->msize - MZ_MEDIUM);          /* -2..+5 */
      /* sleeping target is more likely to be hit */
      if (mon->msleeping) {
          tmp += 2;
          if (mon_notices) mon->msleeping = 0;
      }
      /* ditto for immobilized target */
      if (!mon->mcanmove || !mon->data->mmove) {
          tmp += 4;
          if (mon_notices && mon->data->mmove && !rn2(10)) {
            mon->mcanmove = 1;
            mon->mfrozen = 0;
          }
      }
      /* some objects are more likely to hit than others */
      switch (obj->otyp) {
      case HEAVY_IRON_BALL:
          if (obj != uball) tmp += 2;
          break;
      case BOULDER:
          tmp += 6;
          break;
      default:
          if (obj->oclass == WEAPON_CLASS || is_weptool(obj) ||
                obj->oclass == GEM_CLASS)
            tmp += hitval(obj, mon);
          break;
      }
      return tmp;
}

/* thrown object misses target monster */
STATIC_OVL void
tmiss(obj, mon)
struct obj *obj;
struct monst *mon;
{
    miss(xname(obj), mon);
    if (!rn2(3)) wakeup(mon);
    return;
}

#define quest_arti_hits_leader(obj,mon)   \
  (obj->oartifact && is_quest_artifact(obj) && (mon->data->msound == MS_LEADER))

/*
 * Object thrown by player arrives at monster's location.
 * Return 1 if obj has disappeared or otherwise been taken care of,
 * 0 if caller must take care of it.
 */
int
thitmonst(mon, obj)
register struct monst *mon;
register struct obj   *obj;
{
      register int      tmp; /* Base chance to hit */
      register int      disttmp; /* distance modifier */
      int otyp = obj->otyp;
      boolean guaranteed_hit = (u.uswallow && mon == u.ustuck);

      /* Differences from melee weapons:
       *
       * Dex still gives a bonus, but strength does not.
       * Polymorphed players lacking attacks may still throw.
       * There's a base -1 to hit.
       * No bonuses for fleeing or stunned targets (they don't dodge
       *    melee blows as readily, but dodging arrows is hard anyway).
       * Not affected by traps, etc.
       * Certain items which don't in themselves do damage ignore tmp.
       * Distance and monster size affect chance to hit.
       */
      tmp = -1 + Luck + find_mac(mon) + u.uhitinc +
                  maybe_polyd(youmonst.data->mlevel, u.ulevel);
      if (ACURR(A_DEX) < 4) tmp -= 3;
      else if (ACURR(A_DEX) < 6) tmp -= 2;
      else if (ACURR(A_DEX) < 8) tmp -= 1;
      else if (ACURR(A_DEX) >= 14) tmp += (ACURR(A_DEX) - 14);

      /* Modify to-hit depending on distance; but keep it sane.
       * Polearms get a distance penalty even when wielded; it's
       * hard to hit at a distance.
       */
      disttmp = 3 - distmin(u.ux, u.uy, mon->mx, mon->my);
      if(disttmp < -4) disttmp = -4;
      tmp += disttmp;

      /* gloves are a hinderance to proper use of bows */
      if (uarmg && uwep && objects[uwep->otyp].oc_skill == P_BOW) {
          switch (uarmg->otyp) {
          case GAUNTLETS_OF_POWER:    /* metal */
            tmp -= 2;
            break;
          case GAUNTLETS_OF_FUMBLING:
            tmp -= 3;
            break;
          case LEATHER_GLOVES:
          case GAUNTLETS_OF_DEXTERITY:
            break;
          default:
            impossible("Unknown type of gloves (%d)", uarmg->otyp);
            break;
          }
      }

      tmp += omon_adj(mon, obj, TRUE);
      if (is_orc(mon->data) && maybe_polyd(is_elf(youmonst.data),
                  Race_if(PM_ELF)))
          tmp++;
      if (guaranteed_hit) {
          tmp += 1000; /* Guaranteed hit */
      }

      if (obj->oclass == GEM_CLASS && is_unicorn(mon->data)) {
          if (mon->mtame) {
            pline("%s catches and drops %s.", Monnam(mon), the(xname(obj)));
            return 0;
          } else {
            pline("%s catches %s.", Monnam(mon), the(xname(obj)));
            return gem_accept(mon, obj);
          }
      }

      /* don't make game unwinnable if naive player throws artifact
         at leader.... */
      if (quest_arti_hits_leader(obj, mon)) {
          /* not wakeup(), which angers non-tame monsters */
          mon->msleeping = 0;
          mon->mstrategy &= ~STRAT_WAITMASK;

          if (mon->mcanmove) {
            pline("%s catches %s.", Monnam(mon), the(xname(obj)));
            if (mon->mpeaceful) {
                boolean next2u = monnear(mon, u.ux, u.uy);

                finish_quest(obj);  /* acknowledge quest completion */
                pline("%s %s %s back to you.", Monnam(mon),
                    (next2u ? "hands" : "tosses"), the(xname(obj)));
                if (!next2u) sho_obj_return_to_u(obj);
                obj = addinv(obj);  /* back into your inventory */
                (void) encumber_msg();
            } else {
                /* angry leader caught it and isn't returning it */
                (void) mpickobj(mon, obj);
            }
            return 1;         /* caller doesn't need to place it */
          }
          return(0);
      }

      if (obj->oclass == WEAPON_CLASS || is_weptool(obj) ||
            obj->oclass == GEM_CLASS) {
          if (is_ammo(obj)) {
            if (!ammo_and_launcher(obj, uwep)) {
                tmp -= 4;
            } else {
                tmp += uwep->spe - greatest_erosion(uwep);
                tmp += weapon_hit_bonus(uwep);
                /*
                 * Elves and Samurais are highly trained w/bows,
                 * especially their own special types of bow.
                 * Polymorphing won't make you a bow expert.
                 */
                if ((Race_if(PM_ELF) || Role_if(PM_SAMURAI)) &&
                        objects[uwep->otyp].oc_skill == P_BOW) {
                  tmp++;
                  if (is_elf(youmonst.data) && uwep->otyp == ELVEN_BOW) tmp++;
                  else if (Role_if(PM_SAMURAI) && uwep->otyp == YUMI) tmp++;
                }
            }
          } else {
            if (otyp == BOOMERANG)        /* arbitrary */
                tmp += 4;
            else if (throwing_weapon(obj))      /* meant to be thrown */
                tmp += 2;
            else                    /* not meant to be thrown */
                tmp -= 2;
            /* we know we're dealing with a weapon or weptool handled
               by WEAPON_SKILLS once ammo objects have been excluded */
            tmp += weapon_hit_bonus(obj);
          }

          if (tmp >= rnd(20)) {
            if (hmon(mon,obj,1)) {  /* mon still alive */
                cutworm(mon, bhitpos.x, bhitpos.y, obj);
            }
            exercise(A_DEX, TRUE);
            /* projectiles other than magic stones
               sometimes disappear when thrown */
            if (objects[otyp].oc_skill < P_NONE &&
                        objects[otyp].oc_skill > -P_BOOMERANG &&
                        !objects[otyp].oc_magic && rn2(3)) {
                if (*u.ushops)
                  check_shop_obj(obj, bhitpos.x,bhitpos.y, TRUE);
                obfree(obj, (struct obj *)0);
                return 1;
            }
          } else {
            tmiss(obj, mon);
          }

      } else if (otyp == HEAVY_IRON_BALL) {
          exercise(A_STR, TRUE);
          if (tmp >= rnd(20)) {
            int was_swallowed = guaranteed_hit;

            exercise(A_DEX, TRUE);
            if (!hmon(mon,obj,1)) {       /* mon killed */
                if (was_swallowed && !u.uswallow && obj == uball)
                  return 1;   /* already did placebc() */
            }
          } else {
            tmiss(obj, mon);
          }

      } else if (otyp == BOULDER) {
          exercise(A_STR, TRUE);
          if (tmp >= rnd(20)) {
            exercise(A_DEX, TRUE);
            (void) hmon(mon,obj,1);
          } else {
            tmiss(obj, mon);
          }

      } else if ((otyp == EGG || otyp == CREAM_PIE ||
                otyp == BLINDING_VENOM || otyp == ACID_VENOM) &&
            (guaranteed_hit || ACURR(A_DEX) > rnd(25))) {
          (void) hmon(mon, obj, 1);
          return 1;     /* hmon used it up */

      } else if (obj->oclass == POTION_CLASS &&
            (guaranteed_hit || ACURR(A_DEX) > rnd(25))) {
          potionhit(mon, obj, TRUE);
          return 1;

      } else if (obj->oclass == FOOD_CLASS &&
               is_domestic(mon->data) && tamedog(mon,obj)) {
          return 1;           /* food is gone */
      } else if (guaranteed_hit) {
          /* this assumes that guaranteed_hit is due to swallowing */
          pline("%s vanishes into %s %s.",
            The(xname(obj)), s_suffix(mon_nam(mon)),
            is_animal(u.ustuck->data) ? "entrails" : "currents");
          wakeup(mon);
      } else {
          tmiss(obj, mon);
      }

      return 0;
}

STATIC_OVL int
gem_accept(mon, obj)
register struct monst *mon;
register struct obj *obj;
{
      char buf[BUFSZ];
      boolean is_buddy = sgn(mon->data->maligntyp) == sgn(u.ualign.type);
      boolean is_gem = objects[obj->otyp].oc_material == GEMSTONE;
      int ret = 0;
      static NEARDATA const char nogood[] = " is not interested in your junk.";
      static NEARDATA const char acceptgift[] = " accepts your gift.";
      static NEARDATA const char maybeluck[] = " hesitatingly";
      static NEARDATA const char noluck[] = " graciously";
      static NEARDATA const char addluck[] = " gratefully";

      Strcpy(buf,Monnam(mon));
      mon->mpeaceful = 1;

      /* object properly identified */
      if(obj->dknown && objects[obj->otyp].oc_name_known) {
            if(is_gem) {
                  if(is_buddy) {
                        Strcat(buf,addluck);
                        change_luck(5);
                  } else {
                        Strcat(buf,maybeluck);
                        change_luck(rn2(7)-3);
                  }
            } else {
                  Strcat(buf,nogood);
                  goto nopick;
            }
      /* making guesses */
      } else if(obj->onamelth || objects[obj->otyp].oc_uname) {
            if(is_gem) {
                  if(is_buddy) {
                        Strcat(buf,addluck);
                        change_luck(2);
                  } else {
                        Strcat(buf,maybeluck);
                        change_luck(rn2(3)-1);
                  }
            } else {
                  Strcat(buf,nogood);
                  goto nopick;
            }
      /* value completely unknown to @ */
      } else {
            if(is_gem) {
                  if(is_buddy) {
                        Strcat(buf,addluck);
                        change_luck(1);
                  } else {
                        Strcat(buf,maybeluck);
                        change_luck(rn2(3)-1);
                  }
            } else {
                  Strcat(buf,noluck);
            }
      }
      Strcat(buf,acceptgift);
      if(*u.ushops) check_shop_obj(obj, mon->mx, mon->my, TRUE);
      (void) mpickobj(mon, obj);    /* may merge and free obj */
      ret = 1;

nopick:
      if(!Blind) pline("%s", buf);
      if (!tele_restrict(mon)) rloc(mon);
      return(ret);
}

/*
 * Comments about the restructuring of the old breaks() routine.
 *
 * There are now three distinct phases to object breaking:
 *     breaktest() - which makes the check/decision about whether the
 *                   object is going to break.
 *     breakmsg()  - which outputs a message about the breakage,
 *                   appropriate for that particular object. Should
 *                   only be called after a positve breaktest().
 *                   on the object and, if it going to be called,
 *                   it must be called before calling breakobj().
 *                   Calling breakmsg() is optional.
 *     breakobj()  - which actually does the breakage and the side-effects
 *                   of breaking that particular object. This should
 *                   only be called after a positive breaktest() on the
 *                   object.
 *
 * Each of the above routines is currently static to this source module.
 * There are two routines callable from outside this source module which
 * perform the routines above in the correct sequence.
 *
 *   hero_breaks() - called when an object is to be broken as a result
 *                   of something that the hero has done. (throwing it,
 *                   kicking it, etc.)
 *   breaks()      - called when an object is to be broken for some
 *                   reason other than the hero doing something to it.
 */

/*
 * The hero causes breakage of an object (throwing, dropping it, etc.)
 * Return 0 if the object didn't break, 1 if the object broke.
 */
int
hero_breaks(obj, x, y, from_invent)
struct obj *obj;
xchar x, y;       /* object location (ox, oy may not be right) */
boolean from_invent;    /* thrown or dropped by player; maybe on shop bill */
{
      boolean in_view = !Blind;
      if (!breaktest(obj)) return 0;
      breakmsg(obj, in_view);
      breakobj(obj, x, y, TRUE, from_invent);
      return 1;
}

/*
 * The object is going to break for a reason other than the hero doing
 * something to it.
 * Return 0 if the object doesn't break, 1 if the object broke.
 */
int
breaks(obj, x, y)
struct obj *obj;
xchar x, y;       /* object location (ox, oy may not be right) */
{
      boolean in_view = Blind ? FALSE : cansee(x, y);

      if (!breaktest(obj)) return 0;
      breakmsg(obj, in_view);
      breakobj(obj, x, y, FALSE, FALSE);
      return 1;
}

/*
 * Unconditionally break an object. Assumes all resistance checks
 * and break messages have been delivered prior to getting here.
 * This routine assumes the cause is the hero if heros_fault is TRUE.
 *
 */
STATIC_OVL void
breakobj(obj, x, y, heros_fault, from_invent)
struct obj *obj;
xchar x, y;       /* object location (ox, oy may not be right) */
boolean heros_fault;
boolean from_invent;
{
      switch (obj->oclass == POTION_CLASS ? POT_WATER : obj->otyp) {
            case MIRROR:
                  if (heros_fault)
                      change_luck(-2);
                  break;
            case POT_WATER:         /* really, all potions */
                  if (obj->otyp == POT_OIL && obj->lamplit) {
                      splatter_burning_oil(x,y);
                  } else if (distu(x,y) <= 2) {
                      /* [what about "familiar odor" when known?] */
                      if (obj->otyp != POT_WATER)
                        You("smell a peculiar odor...");
                      potionbreathe(obj);
                  }
                  /* monster breathing isn't handled... [yet?] */
                  break;
            case EGG:
                  /* breaking your own eggs is bad luck */
                  if (heros_fault && obj->spe && obj->corpsenm >= LOW_PM)
                      change_luck((schar) -min(obj->quan, 5L));
                  break;
      }
      if (heros_fault) {
          if (from_invent) {
            if (*u.ushops)
                  check_shop_obj(obj, x, y, TRUE);
          } else if (!obj->no_charge && costly_spot(x, y)) {
            /* it is assumed that the obj is a floor-object */
            char *o_shop = in_rooms(x, y, SHOPBASE);
            struct monst *shkp = shop_keeper(*o_shop);

            if (shkp) {       /* (implies *o_shop != '\0') */
                static NEARDATA long lastmovetime = 0L;
                static NEARDATA boolean peaceful_shk = FALSE;
                /*  We want to base shk actions on her peacefulness
                  at start of this turn, so that "simultaneous"
                  multiple breakage isn't drastically worse than
                  single breakage.  (ought to be done via ESHK)  */
                if (moves != lastmovetime)
                  peaceful_shk = shkp->mpeaceful;
                if (stolen_value(obj, x, y, peaceful_shk, FALSE) > 0L &&
                  (*o_shop != u.ushops[0] || !inside_shop(u.ux, u.uy)) &&
                  moves != lastmovetime) make_angry_shk(shkp, x, y);
                lastmovetime = moves;
            }
          }
      }
      delobj(obj);
}

/*
 * Check to see if obj is going to break, but don't actually break it.
 * Return 0 if the object isn't going to break, 1 if it is.
 */
boolean
breaktest(obj)
struct obj *obj;
{
      if (obj_resists(obj, 1, 99)) return 0;
      switch (obj->oclass == POTION_CLASS ? POT_WATER : obj->otyp) {
            case MIRROR:
            case CRYSTAL_BALL:
#ifdef TOURIST
            case EXPENSIVE_CAMERA:
#endif
            case POT_WATER:         /* really, all potions */
            case EGG:
            case CREAM_PIE:
            case ACID_VENOM:
            case BLINDING_VENOM:
                  return 1;
            default:
                  return 0;
      }
}

STATIC_OVL void
breakmsg(obj, in_view)
struct obj *obj;
boolean in_view;
{
      const char *to_pieces;

      to_pieces = "";
      switch (obj->oclass == POTION_CLASS ? POT_WATER : obj->otyp) {
            case MIRROR:
            case CRYSTAL_BALL:
#ifdef TOURIST
            case EXPENSIVE_CAMERA:
#endif
                  to_pieces = " into a thousand pieces";
                  /*FALLTHRU*/
            case POT_WATER:         /* really, all potions */
                  if (!in_view)
                      You_hear("%s shatter!", something);
                  else
                      pline("%s shatter%s%s!", Doname2(obj),
                        (obj->quan==1) ? "s" : "", to_pieces);
                  break;
            case EGG:
                  pline("Splat!");
                  break;
            case CREAM_PIE:
                  if (in_view) pline("What a mess!");
                  break;
            case ACID_VENOM:
            case BLINDING_VENOM:
                  pline("Splash!");
                  break;
      }
}

/*
 *  Note that the gold object is *not* attached to the fobj chain.
 */
STATIC_OVL int
throw_gold(obj)
struct obj *obj;
{
      int range, odx, ody;
      long zorks = obj->quan;
      register struct monst *mon;

      if(u.uswallow) {
            pline(is_animal(u.ustuck->data) ?
                  "%s in the %s's entrails." : "%s into %s.",
                  "The gold disappears", mon_nam(u.ustuck));
            u.ustuck->mgold += zorks;
            dealloc_obj(obj);
            return(1);
      }

      if(u.dz) {
            if (u.dz < 0 && !Is_airlevel(&u.uz) &&
                              !Underwater && !Is_waterlevel(&u.uz)) {
      pline_The("gold hits the %s, then falls back on top of your %s.",
                ceiling(u.ux,u.uy), body_part(HEAD));
                /* some self damage? */
                if(uarmh) pline("Fortunately, you are wearing a helmet!");
            }
            bhitpos.x = u.ux;
            bhitpos.y = u.uy;
      } else {
            /* consistent with range for normal objects */
            range = (int)((ACURRSTR)/2 - obj->owt/40);

            /* see if the gold has a place to move into */
            odx = u.ux + u.dx;
            ody = u.uy + u.dy;
            if(!ZAP_POS(levl[odx][ody].typ) || closed_door(odx, ody)) {
                  bhitpos.x = u.ux;
                  bhitpos.y = u.uy;
            } else {
                  mon = bhit(u.dx, u.dy, range, THROWN_WEAPON,
                           (int FDECL((*),(MONST_P,OBJ_P)))0,
                           (int FDECL((*),(OBJ_P,OBJ_P)))0,
                           obj);
                  if(mon) {
                      if (ghitm(mon, obj))      /* was it caught? */
                        return 1;
                  } else {
                      if(ship_object(obj, bhitpos.x, bhitpos.y, FALSE))
                        return 1;
                  }
            }
      }

      if(flooreffects(obj,bhitpos.x,bhitpos.y,"fall")) return(1);
      if(u.dz > 0)
            pline_The("gold hits the %s.", surface(bhitpos.x,bhitpos.y));
      place_object(obj,bhitpos.x,bhitpos.y);
      if(*u.ushops) sellobj(obj, bhitpos.x, bhitpos.y);
      stackobj(obj);
      newsym(bhitpos.x,bhitpos.y);
      return(1);
}

/*dothrow.c*/

Generated by  Doxygen 1.6.0   Back to index