/*      SCCS Id: @(#)dothrow.c  3.1     93/06/15        */
/* 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 int FDECL(gem_accept, (struct monst *, struct obj *));
static int FDECL(throw_gold, (struct obj *));
static void FDECL(check_shop_obj, (struct obj *,XCHAR_P,XCHAR_P,BOOLEAN_P));

static NEARDATA const char toss_objs[] =
	{ ALLOW_COUNT, GOLD_CLASS, ALL_CLASSES, WEAPON_CLASS, 0 };
extern boolean notonhead;       /* for long worms */

/* a flag for mjollnir -- have it return to "quiver" instead of
   weapon flag if fired */

static char fire_weapon;

int
dothrow()
{
	register struct obj *obj;

	fire_weapon = 0;
	if(check_capacity(NULL)) return(0);
	obj = getobj(toss_objs, "throw");
	/* it is also possible to throw food */
	/* (or jewels, or iron balls... ) */

	if(!obj || !getdir(NULL)) {       /* ask "in what direction?" */
		if (obj && 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) {
		You("must be wielding %s in order to throw it.", xname(obj));
		return(0);
	}
	if ((obj->oartifact == ART_MJOLLNIR && ACURR(A_STR) <= 123)
	   || (obj->otyp == BOULDER
#ifdef POLYSELF
					&& !throws_rocks(uasmon)
#endif
								)) {
		pline("It's too heavy.");
		return(1);
	}
	if(!u.dx && !u.dy && !u.dz) {
		You("cannot throw an object at yourself.");
		return(0);
	}
	u_wipe_engr(2);

	if (obj == uquiver) {
	    if(obj->quan > 1L)
		setuqwep(splitobj(obj, 1L));
	    else {
		setuqwep((struct obj *)0);
		if (uquiver) return(1); /* unwielded, died, rewielded */
	    }
	}
	else if (obj == uswapwep) {
	    if(obj->quan > 1L)
		setuswapwep(splitobj(obj, 1L));
	    else {
		setuswapwep((struct obj *)0);
		if (uswapwep) return(1); /* unwielded, died, rewielded */
	    }
	}
	else if(obj == uwep) {
	    if(welded(obj)) {
		weldmsg(obj, FALSE);
		return(1);
	    }
	    if(obj->quan > 1L)
		setuwep(splitobj(obj, 1L));
	    else {
		setuwep((struct obj *)0);
		if (uwep) return(1); /* unwielded, died, rewielded */
	    }
	}
	else if(obj->quan > 1L)
		(void) splitobj(obj, 1L);
	freeinv(obj);
	return(throwit(obj));
}

/* [Tom] shooting stuff from quiver -- copied directly from above */


int
dofire()
{
	register struct obj *obj;

	fire_weapon = 1;
	if(check_capacity(NULL)) return(0);
	obj = uquiver;
	if (!obj) {
	  pline("You have no ammunition readied!");
	  return(0);
	}

	if(!getdir(NULL)) {       /* ask "in what direction?" */
		return(0);
	}

	if(!u.dx && !u.dy && !u.dz) {
		You("cannot fire an object at yourself.");
		return(0);
	}
	u_wipe_engr(2);

	if (obj == uquiver) {
	    if(obj->quan > 1L)
		setuqwep(splitobj(obj, 1L));
	    else {
		setuqwep((struct obj *)0);
		if (uquiver) return(1); /* unwielded, died, rewielded */
	    }
	}
	else if(obj->quan > 1L)
		(void) splitobj(obj, 1L);
	freeinv(obj);
	return(throwit(obj));
}

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 (breaks(obj, TRUE)) return;
	else if(obj->oclass == POTION_CLASS) {
		pline("The flask%s break%s, and you smell a peculiar odor...",
		      plur(obj->quan), (obj->quan == 1L) ? "s" : "");
		potionbreathe(obj);
		if(*u.ushops)
		    check_shop_obj(obj, u.ux, u.uy, TRUE);
		obfree(obj, (struct obj *)0);
	} else {
		if(ship_object(obj, u.ux, u.uy, FALSE)) 
		    return;
		dropy(obj);
	}
}

/*
 * The player moves through the air for a few squares as a result of
 * throwing or kicking something.  To simplify matters, 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.
 * dx and dy should be the direction of the hurtle, not of the original
 * kick or throw.
 */
void
hurtle(dx, dy, range)
    int dx, dy, range;
{
    register struct monst *mon;
    struct obj *obj;
    int nx, ny;

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

    nomul(-range);
    You("%s in the opposite direction.", range > 1 ? "hurtle" : "float");
    while(range--) {
	nx = u.ux + dx;
	ny = u.uy + dy;

	if(!isok(nx,ny)) break;
	if(IS_ROCK(levl[nx][ny].typ) || closed_door(nx,ny) ||
	   (IS_DOOR(levl[nx][ny].typ) && (levl[nx][ny].doormask & D_ISOPEN))) {
	    pline("Ouch!");
	    losehp(rnd(2+range), IS_ROCK(levl[nx][ny].typ) ?
		   "bumping into a wall" : "bumping into a door", KILLED_BY);
	    break;
	}

	if ((obj = sobj_at(BOULDER,nx,ny)) != 0) {
	    You("bump into a %s.  Ouch!", xname(obj));
	    losehp(rnd(2+range), "bumping into a boulder", KILLED_BY);
	    break;
	}

	u.ux = nx;
	u.uy = ny;
	newsym(u.ux - dx, u.uy - dy);
	if ((mon = m_at(u.ux, u.uy)) != 0) {
	    You("bump into %s.", a_monnam(mon));
	    wakeup(mon);
	    if(Is_airlevel(&u.uz))
		mnexto(mon);
	    else {
		/* sorry, not ricochets */
		u.ux -= dx;
		u.uy -= dy;
	    }
	    range = 0;
	}

	newsym(u.ux, u.uy);

	if(range) {
	    flush_screen(1);
	    delay_output();
	}
    }
}

static void
check_shop_obj(obj, x, y, broken)
register struct obj *obj;
register xchar x, y;
register boolean broken;
{
	register struct monst *shkp = 
			shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE));

	if(!shkp) return;
	if(!inside_shop(u.ux, u.uy)) return;

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

	if(!costly_spot(x, y) ||
		   *in_rooms(u.ux, u.uy, 0) != *in_rooms(x, y, 0)) { 
		if(!inside_shop(x, y) && obj->unpaid) {
		    (void)stolen_value(obj, u.ux, u.uy, 
				 (shkp && 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);
		}
}


int
throwit(obj)
register struct obj *obj;
{
	register struct monst *mon;
	register int range, urange;
	boolean impaired = (Confusion || Stunned || Blind ||
			   Hallucination || Fumbling);
	int do_death = 0;

	if ((obj->cursed || obj->greased) && (u.dx || u.dy) && !rn2(7)) {
		boolean slipok = TRUE;
	    if ((obj->oclass == WEAPON_CLASS || obj->oclass == GEM_CLASS)
		&& uwep && (objects[obj->otyp].w_propellor > 0) &&
		(objects[obj->otyp].w_propellor ==
					     -objects[uwep->otyp].w_propellor))
		pline("%s misfires!", The(xname(obj)));
	    else {
		/* only slip if it's greased or meant to be thrown */
		if(obj->greased ||
		   (obj->otyp >= DART && obj->otyp <= JAVELIN) ||
		   (obj->otyp >= DAGGER && obj->otyp <= CRYSKNIFE &&
		    obj->otyp != ATHAME) || obj->otyp == WAR_HAMMER)
		    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 && pl_character[0] == 'V' &&
	      obj->oartifact == ART_MJOLLNIR && !impaired) {
	      pline("%s hits the ceiling and returns to your hand!",
		    The(xname(obj)));
	      obj = addinv(obj);
	      (void) encumber_msg();
	      if (!fire_weapon) setuwep(obj);
	      else setuqwep(obj);
	      return(1);
	  }
	  if (u.dz < 0 && !Is_airlevel(&u.uz) && !Underwater && !Is_waterlevel(&u.uz)) {
	    pline("%s hits the ceiling, then falls back on top of your %s.",
		Doname2(obj),           /* note: obj->quan == 1 */
		body_part(HEAD));
	    if(obj->oclass == POTION_CLASS)
		potionhit(&youmonst, obj);
	    else {
		int dmg = rnd((int)(obj->owt));
	      
		if (uarmh) {
		    if(is_metallic(uarmh)) {
			pline("Fortunately, you are wearing a hard helmet.");
			dmg = 1;
		    } else if (flags.verbose)
			Your("%s does not protect you.", xname(uarmh));
		} else if (obj->otyp == CORPSE &&
				obj->corpsenm == PM_COCKATRICE) {
#ifdef POLYSELF
		    if(!resists_ston(uasmon))
			if(!(poly_when_stoned(uasmon) &&
					polymon(PM_STONE_GOLEM))) {
#endif
			killer = doname(obj);
			You("turn to stone.");
			do_death = STONING;
#ifdef POLYSELF
		    }
#endif
		}

		if (!breaks(obj, TRUE)) {
		    if(!ship_object(obj, u.ux, u.uy, FALSE)) {
			dropy(obj);
		    }
		}
		if (do_death == STONING)
		    done(STONING);
		else
		    losehp(dmg, "falling object", KILLED_BY_AN);
	    }
	  } else hitfloor(obj);
	  return(1);

	} else if(obj->otyp == BOOMERANG && !Underwater) {
		if(Is_airlevel(&u.uz) || Levitation) hurtle(-u.dx, -u.dy, 1);
		mon = boomhit(u.dx, u.dy);
		if(mon == &youmonst) {          /* the thing was caught */
			exercise(A_DEX, TRUE);
			(void) addinv(obj);
			(void) encumber_msg();
			return(1);
		}
	} else {
		urange = (int)(ACURRSTR)/2;
		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 ((obj->oclass == WEAPON_CLASS || obj->oclass == GEM_CLASS)
		    && uwep && objects[obj->otyp].w_propellor) {
		    if (objects[obj->otyp].w_propellor ==
					     -objects[uwep->otyp].w_propellor)
			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;
		}

#ifdef POLYSELF
		if (obj->otyp == BOULDER) range = 20;
#endif
		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 (*)()) 0,(int (*)()) 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);
	}
	if(mon) {
		if(mon->isshk && (!inside_shop(u.ux, u.uy) ||
		   !index(in_rooms(mon->mx, mon->my, SHOPBASE), *u.ushops))) { 
		    if(obj->otyp == PICK_AXE) {
			register struct obj *otmp;

			/* check if the pick axe was caught through  */
			/* a successful call to shkcatch() in bhit() */
			for (otmp = mon->minvent; otmp; otmp = otmp->nobj)
			     if (otmp == obj) return(1);
		    }
		    wakeup(mon);
		    hot_pursuit(mon);
		}
		(void) snuff_candle(obj);
		/* awake monster if sleeping */
		wakeup(mon);
		notonhead = (bhitpos.x != mon->mx || bhitpos.y != mon->my);
		if(thitmonst(mon, obj)) return(1);
	}
	if(!u.uswallow) {
		/* the code following might become part of dropy() */
		int obj_glyph = obj_to_glyph(obj);
		boolean gone = FALSE;

		if (obj->oartifact == ART_MJOLLNIR &&
			pl_character[0] == 'V' && rn2(100)) {
		    /* we must be wearing Gauntlets of Power to get here */

		    /* 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_glyph);
			while(x != u.ux || y != u.uy) {
			    tmp_at(x, y);
			    delay_output();
			    x -= u.dx; y -= u.dy;
			}
			tmp_at(DISP_END, 0);
		    }

		    if (!impaired && rn2(100)) {
			pline("%s returns to your hand!", The(xname(obj)));
			obj = addinv(obj);
			(void) encumber_msg();
			if (!fire_weapon) setuwep(obj);
			else setuqwep(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 (1);
			dropy(obj);
		    }
		    return (1);
		}
		if (!IS_SOFT(levl[bhitpos.x][bhitpos.y].typ) && !u.uinwater &&
		    obj->oclass == POTION_CLASS && rn2(2)) {
		    if(distu(bhitpos.x, bhitpos.y) < 3 && rn2(5)) {
			pline("The flask breaks, and you smell a peculiar odor...");
			potionbreathe(obj);
		    } else if(!Blind)
			pline("The flask breaks.");
		    else pline("Crash!");
		    if(*u.ushops)
			check_shop_obj(obj, bhitpos.x, bhitpos.y, TRUE);
		    obfree(obj, (struct obj *)0);
		    gone = TRUE;
		}
		if (gone || (!IS_SOFT(levl[bhitpos.x][bhitpos.y].typ) &&
			     breaks(obj, TRUE))) {
		    tmp_at(DISP_FLASH, obj_glyph);
		    tmp_at(bhitpos.x, bhitpos.y);
		    delay_output();
		    tmp_at(DISP_END, 0);
		    return(1);
		}
		if(flooreffects(obj,bhitpos.x,bhitpos.y,"fall")) return(1);
		if(obj->otyp == CRYSKNIFE)
			obj->otyp = WORM_TOOTH;
		if(mon && mon->isshk && obj->otyp == PICK_AXE) {
			mpickobj(mon, obj);
			if(*u.ushops)
			    check_shop_obj(obj, bhitpos.x, bhitpos.y, FALSE);
			return(1);
		}
		(void) snuff_candle(obj);
		if(!mon && obj != uball) {
		    if(ship_object(obj, bhitpos.x, bhitpos.y, FALSE))
			return(1);
		}
		obj->nobj = fobj;
		fobj = obj;
		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);
	} else {
		/* ball is not picked up by monster */
		if (obj != uball) mpickobj(u.ustuck,obj);
	}
	return(1);
}

int
thitmonst(mon, obj)
register struct monst *mon;
register struct obj   *obj;
{
	register int    tmp; /* Base chance to hit */
	register int    disttmp; /* distance modifier */

	/* 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);
#ifdef POLYSELF
	if (u.umonnum >= 0) tmp += uasmon->mlevel;
	else
#endif
		tmp += 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 */
	disttmp = 3 - distmin(u.ux, u.uy, mon->mx, mon->my);
	if(disttmp < -4) disttmp = -4;
	tmp += disttmp;

	/* it's easier to hit a larger target */
	if(bigmonst(mon->data)) tmp++;

	/* gloves are a hinderance to proper use of bows */
	if (uarmg && uwep && -objects[uwep->otyp].w_propellor == WP_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_SWIMMING:
			case GAUNTLETS_OF_DEXTERITY:
			    break;
			default: impossible("Unknown type of gloves (%d)",
			    uarmg->otyp);
		}
	}

	if(mon->msleep) {
		mon->msleep = 0;
		tmp += 2;
	}
	if(!mon->mcanmove || !mon->data->mmove) {
		tmp += 4;
		if(!rn2(10)) {
			mon->mcanmove = 1;
			mon->mfrozen = 0;
		}
	}
	if (is_orc(mon->data) && pl_character[0]=='E') tmp++;
	if (u.uswallow && mon == u.ustuck) tmp += 1000; /* Guaranteed hit */

	if(obj->oclass == GEM_CLASS && mon->data->mlet == S_UNICORN) {
		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));
		}
	}
#ifdef MULDGN
	/* don't make game unwinnable if naive player throws artifact
	   at leader.... */
	if (mon->data->msound == MS_LEADER && is_quest_artifact(obj)) {
		if (mon->mcanmove)
			pline("%s ignores %s.", Monnam(mon), the(xname(obj)));
		return(0);
	}
#endif
	if(obj->oclass == WEAPON_CLASS || obj->otyp == PICK_AXE ||
	   obj->otyp == UNICORN_HORN || obj->oclass == GEM_CLASS) {
		if(obj->otyp < DART || obj->oclass == GEM_CLASS) {
		    if (!uwep ||
			objects[obj->otyp].w_propellor !=
			-objects[uwep->otyp].w_propellor) {
			tmp -= 4;
		    } else {
			tmp += uwep->spe - uwep->oeroded;
			/*
			 * Elves and Samurais are highly trained w/bows,
			 * especially their own special types of bow.
			 * Polymorphing won't make you a bow expert.
			 */
			if ((pl_character[0] == 'E' || pl_character[0] == 'S')
			     && -objects[uwep->otyp].w_propellor == WP_BOW)
			    tmp++;
			if (pl_character[0] == 'E' && uwep->otyp == ELVEN_BOW)
			    tmp++;
			if (pl_character[0] == 'S' && uwep->otyp == YUMI)
			    tmp++;
		    }
		} else if(obj->otyp == BOOMERANG) tmp += 4;
		tmp += obj->spe;
		tmp += hitval(obj, mon->data);
		if(tmp >= rnd(20)) {
			if(hmon(mon,obj,1)){
			  /* mon still alive */
			  cutworm(mon, bhitpos.x, bhitpos.y, obj);
			}
			exercise(A_DEX, TRUE);
			/* projectiles thrown disappear sometimes */
			if((obj->otyp < BOOMERANG || obj->oclass == GEM_CLASS)
								&& rn2(3)) {
				if(*u.ushops)
				     check_shop_obj(obj, bhitpos.x, 
							   bhitpos.y, TRUE);
				/* check bill; free */
				obfree(obj, (struct obj *)0);
				return(1);
			}
		} else miss(xname(obj), mon);
	} else if(obj->otyp == HEAVY_IRON_BALL) {
		if(obj != uball) tmp += 2;
		exercise(A_STR, TRUE);
		if(tmp >= rnd(20)) {
			(void) hmon(mon,obj,1);
			exercise(A_DEX, TRUE);
		} else miss(xname(obj), mon);
	} else if (obj->otyp == BOULDER) {
		tmp += 6;  /* Likely to hit! */
		exercise(A_STR, TRUE);
		if(tmp >= rnd(20)) {
			(void) hmon(mon,obj,1);
			exercise(A_DEX, TRUE);
		} else miss(xname(obj), mon);
	} else if((obj->otyp == CREAM_PIE
#ifdef POLYSELF
			|| obj->otyp == BLINDING_VENOM
#endif
					) && ACURR(A_DEX) >= rnd(10)) {
		(void) hmon(mon,obj,1); /* can't die from it */
#ifdef POLYSELF
	} else if(obj->otyp == ACID_VENOM && ACURR(A_DEX) >= rnd(10)) {
		(void) hmon(mon,obj,1);
#endif
	} else if(obj->oclass == POTION_CLASS && ACURR(A_DEX) >= rnd(15)) {
		potionhit(mon, obj);
		return(1);
	} else {
		pline("%s misses %s.", The(xname(obj)), mon_nam(mon));
		if(obj->oclass == FOOD_CLASS && is_domestic(mon->data))
			if(tamedog(mon,obj)) return(1);
/* [Tom] Dorothy wants more pets... */
		if(obj->otyp == BANANA && mon->data->mlet == S_YETI)
			if(tamedog(mon,obj)) return(1);
		if(obj->otyp == CHEESE && mon->data == &mons[PM_GIANT_RAT])
			if(tamedog(mon,obj)) return(1);
		if(obj->otyp == CHEESE && mon->data == &mons[PM_SEWER_RAT])
			if(tamedog(mon,obj)) return(1);
		if(obj->otyp == CHEESE && mon->data == &mons[PM_BLACK_RAT])
			if(tamedog(mon,obj)) return(1);
		if(obj->otyp == CHEESE && mon->data == &mons[PM_PACK_RAT])
			if(tamedog(mon,obj)) return(1);
		if(obj->otyp == CARROT && mon->data == &mons[PM_RABBIT])
			if(tamedog(mon,obj)) return(1);
		if(obj->otyp == CARROT && mon->data == &mons[PM_RABID_RABBIT])
			if(tamedog(mon,obj)) return(1);
	}
	return(0);
}

static 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);
	mpickobj(mon, obj);
	if(*u.ushops) check_shop_obj(obj, mon->mx, mon->my, TRUE);
	ret = 1;

nopick:
	if(!Blind) pline(buf);
	rloc(mon);
	return(ret);
}

/* returns 0 if object doesn't break    */
/* returns 1 if object broke            */
int
breaks(obj, loose)
register struct obj   *obj;
register boolean loose;         /* if not loose, obj is in fobj chain */
{
	if (obj_resists(obj, 1, 100)) return 0;

	switch(obj->otyp) {
		case MIRROR:
			change_luck(-2);        /* and fall through */
		case CRYSTAL_BALL:
		case ORB_OF_DESTRUCTION:                        
		case ORB_OF_ENCHANTMENT:
		case ORB_OF_CHARGING:
#ifdef TOURIST
		case EXPENSIVE_CAMERA:
#endif
			if(!Blind)
			    pline("%s shatters into a thousand pieces!",
				Doname2(obj));
			else You("hear something shatter!");
			break;
		case EGG:
			pline("Splat!");
			break;
		case CREAM_PIE:
			pline("What a mess!");
			break;
		case ACID_VENOM:
		case BLINDING_VENOM:
			pline("Splash!");
			break;
		default:
			return 0;
	}

	/* it is currently assumed that 'loose' is co-extensive
	 * with 'thrown'.  if this changes, an explicit 'thrown'
	 * arg must be added to breaks() to ensure proper 
	 * treatment of shop objs.
	 */
	if(loose) {
		newsym(obj->ox,obj->oy);
		if(*u.ushops) 
		    check_shop_obj(obj, obj->ox, obj->oy, TRUE);
		obfree(obj, (struct obj *)0);
	} else {
		/* it is assumed that the obj is a floor-object */ 
		register struct monst *shkp = 0;
		boolean costly, insider;
		long loss = 0L;

		costly = (costly_spot(obj->ox, obj->oy) && 
				   (shkp = shop_keeper(*in_rooms(obj->ox,
				  obj->oy, SHOPBASE))) != (struct monst *)0);
		insider = (*u.ushops && inside_shop(u.ux, u.uy) &&
			 *in_rooms(obj->ox, obj->oy, SHOPBASE) == *u.ushops);

		if(costly)
		    loss = stolen_value(obj, u.ux, u.uy, 
				 (shkp && shkp->mpeaceful), FALSE);
		if(loss && !insider)
		    make_angry_shk(shkp, obj->ox, obj->oy);

		delobj(obj);
	}
	return(1);
}

/*
 *  Note that the gold object is *not* attached to the fobj chain.
 */
static 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 ceiling, then falls back on top of your %s.",
		    body_part(HEAD));
		    /* some self damage? */
		    if(uarmh) pline("Fortunately, you are wearing a helmet!");
		}
		if(flooreffects(obj,u.ux,u.uy,"fall")) return(1);
		if(u.dz > 0) pline("The gold hits the %s.", surface(u.ux,u.uy));
		obj->nobj = fobj;       /* add the gold to the object list */
		fobj = obj;
		place_object(obj,u.ux,u.uy);
		if(*u.ushops) sellobj(obj, u.ux, u.uy);
		stackobj(obj);
		newsym(u.ux,u.uy);
		return 1;
	}

	/* 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 (*)()) 0, (int (*)()) 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);
	obj->nobj = fobj;       /* add the gold to the object list */
	fobj = obj;
	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*/
