daleken/
daleken/data/notes/
daleken/data/player/
daleken/data/system/poses/
daleken/doc/Homepage/images/
daleken/log/
/*___________________________________________________________________________*
   )()(			  DalekenMUD 1.12 (C) 2000			)()(
   `]['		       by Martin Thomson, Lee Brooks,			`]['
    ||		       Ken Herbert and David Jacques			 ||
    || ----------------------------------------------------------------- ||
    || Envy Diku Mud improvements copyright (C) 1994 by Michael Quan,	 ||
    || David Love, Guilherme 'Willie' Arnold, and Mitchell Tse.		 ||
    || Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael	 ||
    || Chastain, Michael Quan, and Mitchell Tse.			 ||
    || Original Diku Mud copyright (C) 1990, 1991			 ||
    || by Sebastian Hammer, Michael Seifert, Hans Henrik St{rfeldt,	 ||
    || Tom Madsen, and Katja Nyboe.					 ||
    || ----------------------------------------------------------------- ||
    || Any use of this software must follow the licenses of the		 ||
    || creators.  Much time and thought has gone into this software and	 ||
    || you are benefitting. We hope that you share your changes too.	 ||
    || What goes around, comes around.					 ||
    || ----------------------------------------------------------------- ||
    ||                          act_obj.c                                ||
    || Object manipulation code.                                         ||
 *_/<>\_________________________________________________________________/<>\_*/


#include "mud.h"
#include "event.h"

/*
 * Local Functions.
 */
void sac_obj		args( ( CHAR_DATA *ch, OBJ_DATA *obj ) );


/*
 * Wear location / body part reliance table, new slots can be added easily here.
 * Format:
 * ITEM_WEAR_* - where the item is worn.
 * BODY_PART_* - what body part(s) is/are required to wear.
 * - message when they don't have the part(s).
 * - display of slot for 'equipment' and 'peek'.
 * - message that the character gets when they wear it, see act().
 * - message that everyone else gets when they wear it, see act().
 */
const struct wear_stuff wear_table[MAX_WEAR] =
{

    { 0, 0, NULL, NULL },

    {
	ITEM_WEAR_HEAD,
	BODY_PART_HEAD, "You don't have a head.",
	"&b<worn on head>                &n",
	"&bYou wear $p on your head.",
	"&b$n wears $p on $s head."
    },

    {
	ITEM_WEAR_HORNS,
	BODY_PART_HORNS, "You don't have any horns.",
	"&b<worn on horns>               &n",
	"&bYou wear $p on your horns.",
	"&b$n wears $p on $s horns."
    },

    {
	ITEM_WEAR_EAR,
	BODY_PART_EAR_L, "You don't have a left ear.",
	"&b<worn through left ear>       &n",
	"&bYou stick $p through your left ear.",
	"&b$n sticks $p through $s left ear."
    },

    {
	ITEM_WEAR_EAR,
	BODY_PART_EAR_R, "You don't have a right ear.",
	"&b<worn through right ear>      &n",
	"&bYou stick $p through your right ear.",
	"&b$n sticks $p through $s right ear."
    },

    {
	ITEM_WEAR_FACE,
	BODY_PART_HEAD, "You don't have a head.",
	"&b<worn on face>                &n",
	"&bYou put $p on your face.",
	"&b$n puts $p on $s face."
    },

    {
	ITEM_WEAR_NOSE,
	BODY_PART_NOSE, "You don't have a nose.",
	"&b<worn on nose>                &n",
	"&bYou wear $p on your nose.",
	"&b$n wears $p on $s nose."
    },

    {
	ITEM_WEAR_NECK,
	BODY_PART_HEAD | BODY_PART_TORSO, "You don't have a head and body.",
	"&b<worn on neck (1)>            &n",
	"&bYou slip $p over your head.",
	"&b$n slips $p over $s head."
    },

    {
	ITEM_WEAR_NECK,
	BODY_PART_HEAD | BODY_PART_TORSO, "You don't have a head and body.",
	"&b<worn on neck (2)>            &n",
	"&bYou slip $p over your head.",
	"&b$n slips $p over $s head."
    },

    {
	ITEM_WEAR_SHOULDERS,
	BODY_PART_TORSO, "You don't have a body.",
	"&b<worn about shoulders>        &n",
	"&bYou arrange $p around your shoulders.",
	"&b$n arranges $p around $s shoulders."
    },

    {
	ITEM_WEAR_WINGS,
	BODY_PART_WINGS, "You don't have any wings.",
	"&b<worn on wings>               &n",
	"&bYou wear $p on your wings.",
	"&b$n wears $p on $s wings."
    },

    {
	ITEM_FLOAT,
	0, "You stupid, stupid monkey!",
	"&b<floating over left shoulder> &n",
	"&bYou release $p to float over your left shoulder.",
	"&b$n releases $p to float over $s left shoulder."
    },

    {
	ITEM_FLOAT,
	0, "You stupid, stupid monkey!",
	"&b<floating over right shoulder>&n",
	"&bYou release $p to float over your right shoulder.",
	"&b$n releases $p to float over $s right shoulder."
    },

    {
	ITEM_WEAR_ARMS,
	BODY_PART_ARMS, "You don't have any arms.",
	"&b<worn on arms>                &n",
	"&bYou wear $p on your arms.",
	"&b$n wears $p on $s arms."
    },

    {
	ITEM_WEAR_WRIST,
	BODY_PART_ARM_L, "You don't have a left arm.",
	"&b<worn on left wrist>          &n",
	"&bYou clasp $p around your left wrist.",
	"&b$n clasps $p around $s left wrist."
    },

    {
	ITEM_WEAR_WRIST,
	BODY_PART_ARM_R, "You don't have a right arm.",
	"&b<worn on right wrist>         &n",
	"&bYou clasp $p around your right wrist.",
	"&b$n clasps $p around $s right wrist."
    },

    {
	ITEM_WEAR_HANDS,
	BODY_PART_HANDS, "You don't have a left and right hand.",
	"&b<worn on hands>               &n",
	"&bYou slip your hands into $p.",
	"&b$n slip your hands into $p."
    },

    {
	ITEM_WEAR_FINGER,
	BODY_PART_FINGERS_L, "You don't have any left hand fingers.",
	"&b<worn on left finger>         &n",
	"&bYou slip $p into a finger on your left hand.",
	"&b$n slips $p onto a finger on $s left hand."
    },

    {
	ITEM_WEAR_FINGER,
	BODY_PART_FINGERS_R, "You don't have any right hand fingers.",
	"&b<worn on right finger>        &n",
	"&bYou slip $p onto a finger on your right hand.",
	"&b$n slips $p onto a finger on $s right hand."
    },

    {
	ITEM_WIELD,
	BODY_PART_HAND_R | BODY_PART_FINGERS_R, "You don't have a right hand and fingers.",
	"&b<wielded in right hand>       &n",
	"&bYou wield $p in your right hand.",
	"&b$n wields $p in $s right hand."
    },

    {
	ITEM_HOLD,
	BODY_PART_HAND_L | BODY_PART_FINGERS_L, "You don't have a left hand and fingers.",
	"&b<held in left hand>           &n",
	"&bYou hold $p in your left hand.",
	"&b$n holds $p in $s left hand."
    },

    {
	ITEM_HOLD,
	BODY_PART_HAND_R | BODY_PART_FINGERS_R, "You don't have a right hand and fingers.",
	"&b<held in right hand>          &n",
	"&bYou hold $p in your right hand.",
	"&b$n holds $p in $s right hand."
    },

    {
	ITEM_WEAR_SHIELD,
	BODY_PART_HAND_L | BODY_PART_FINGERS_L, "You don't have a left hand and fingers.",
	"&b<worn as a shield>            &n",
	"&bYou wear $p in your shield hand.",
	"&b$n wears $p in $s shield hand."
    },

    {
	ITEM_WIELD,
	BODY_PART_HAND_L | BODY_PART_FINGERS_L, "You don't have a left hand and fingers.",
	"&b<wielded in left hand>        &n",
	"&bYou wield $p in your left hand.",
	"&b$n wields $p in $s left hand."
    },

    {
	ITEM_WIELD_DOUBLE,
	BODY_PART_HANDS | BODY_PART_FINGERS, "You don't have enough hands and fingers.",
	"&b<wielded two-handed>          &n",
	"&bYou wield $p in both hands, ururur.",
	"&b$n wields $p two-handed, pretty impressive eh?"
    },

    {
	ITEM_WEAR_BODY,
	BODY_PART_TORSO, "You don't have a body.",
	"&b<worn on body>                &n",
	"&bYou wear $p on your body.",
	"&b$n wears $p on $s body."
    },

    {
	ITEM_WEAR_WAIST,
	BODY_PART_TORSO, "You don't have a body.",
	"&b<worn about waist>            &n",
	"&bYou wrap $p around your waist.",
	"&b$n wraps $p around $s waist."
    },

    {
	ITEM_WEAR_LEGS,
	BODY_PART_LEGS, "You don't have any legs.",
	"&b<worn on legs>                &n",
	"&bYou slip your legs into $p.",
	"&b$n slips $s legs into $p."
    },

    {
	ITEM_WEAR_ANKLE,
	BODY_PART_LEG_L | BODY_PART_FOOT_L,
	"You don't have a left leg or foot.",
	"&b<worn on left ankle>          &n",
	"&bYou clasp $p around your left ankle.",
	"&b$n clasps $p around $s left ankle."
    },

    {
	ITEM_WEAR_ANKLE,
	BODY_PART_LEG_R | BODY_PART_FOOT_R,
	"You don't have a right leg or foot.",
	"&b<worn on right ankle>         &n",
	"&bYou clasp $p around your right ankle.",
	"&b$n clasps $p around $s right ankle."
    },

    {
	ITEM_WEAR_FEET,
	BODY_PART_FEET, "You don't have any feet.",
	"&b<worn on feet>                &n",
	"&bYou wear $p on your feet.",
	"&b$n wears $p on $s feet."
    },

    {
	0,	/* not here for wearing -- Juggling */
	BODY_PART_FEET, "You are a hopeless juggler.",
	"&b<whirling above the head>     &n",
	"&bYou toss $p up and begin to juggle it nervously.",
	"&b$n throws $p in the air juggling it easily."
    }
};


/*
 * Local functions.
 */
void get_obj	args( ( CHAR_DATA *ch, OBJ_DATA *obj,
			OBJ_DATA *container ) );
void wear_obj	args( ( CHAR_DATA *ch, OBJ_DATA *obj,
			bool fReplace ) );
CHAR_DATA *find_keeper args( ( CHAR_DATA *ch, char *argument ) );
int get_cost	args( ( CHAR_DATA *keeper, CHAR_DATA *customer, OBJ_DATA *obj,
			bool fBuy ) );

void get_obj( CHAR_DATA *ch, OBJ_DATA *obj, OBJ_DATA *container )
{
    if( !CAN_WEAR( obj, ITEM_TAKE ) )
    {
	send_to_char( "You can't take that.\n\r", ch );
	return;
    }

    if( IS_SET( obj->extra_flags, ITEM_BURIED ) )
    {
	send_to_char( "That item is buried, you cant get it.\n\r", ch );
	return;
    }
    if( IS_SET( obj->extra_flags, ITEM_QUEST )
	&& ( IS_NPC( ch ) || ch->pcdata->quest->target != obj ) )
    {
	send_to_char( "You can't touch someone elses quest item.\n\r", ch );
	return;
    }

    if( obj->item_type != ITEM_MONEY )
    {
	if( ch->carry_number >= can_carry_n( ch ) )
	{
	    act( "&c$d&g: you can't carry that many items.&n",
		ch, NULL, obj->name, TO_CHAR );
	    return;
	}

	if( ch->carry_weight + get_obj_weight( obj ) > can_carry_w( ch ) )
	{
	    act( "&c$d&g: you can't carry that much weight.&n",
		ch, NULL, obj->name, TO_CHAR );
	    return;
	}
    }

    if( obj->in_room != NULL )
    {
	CHAR_DATA *gch;

	for( gch = obj->in_room->people; gch != NULL; gch = gch->next_in_room )
	    if( gch->on == obj )
	    {
		act( "$N appear$% to be using $p.", ch, obj, gch, TO_CHAR );
		return;
	    }
    }

    if( xIS_SET( obj->pIndexData->progtypes, GET_PROG ) )
    {
	if( oprog_percent_check( ch, obj, NULL, GET_PROG ) == 0 )
	    return;
    }

    if( container )
    {
	act( "&gYou get $p from $P.&n", ch, obj, container, TO_CHAR );
	act( "&g$n gets $p from $P.&n", ch, obj, container, TO_ROOM );

	if( container->item_type == ITEM_PORTAL )
	{
	    ROOM_INDEX_DATA *room = obj->in_room;

	    obj_from_room( obj );
	    if( room && room->people )
	    {
		act( "&gYou see a large arm come and take $p.&n",
		    room->people, obj, NULL, TO_CHAR );
		act( "&gYou see a large arm come and take $p.&n",
		    room->people, obj, NULL, TO_ROOM );
	    }
	}
	else
	    obj_from_obj( obj );
    }
    else
    {
	act( "&gYou get $p.&n", ch, obj, container, TO_CHAR );
	act( "&g$n gets $p.&n", ch, obj, container, TO_ROOM );
	obj_from_room( obj );
    }

    if( IS_OBJ_STAT( obj, ITEM_HOLY ) )
    {
	act( "&gYou are burned by &rHOLY FIRE&g from $p.  &rOuch!&n",
	    ch, obj, NULL, TO_CHAR );
	act( "&g$n is burned by &rHOLY FIRE&g from $p!&n",
	    ch, obj, NULL, TO_ROOM );
	damage( ch, ch, 50, gsn_burn, WEAR_NONE );
	if( ch->deleted )
	    return;
    }

    if( obj->item_type == ITEM_MONEY )
    {
	char buf[MAX_INPUT_LENGTH];
	int amount;

	amount = obj->value[0];
	ch->gold += amount;
	if( IS_NPC( ch ) )
	    ch->in_room->area->economy += amount;

	if( amount > 1 )
	{
	    charprintf( ch, "&gYou counted &y%d&g coins.&n\n\r", amount );

	    /* Autosplit by Symposium */
	    if( xIS_SET( ch->act, PLR_AUTOSPLIT ) && amount > 1 )
	    {
		sprintf( buf, "%d " AUTOLOOK, amount );
		do_split( ch, buf );
	    }
	}

	extract_obj( obj );
	return;
    }
    obj_to_char( obj, ch );
    return;
}


void do_get( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    OBJ_DATA *container;
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    ROOM_INDEX_DATA *room = NULL;
    bool found;

    argument = one_argument( argument, arg1 );
    one_argument( argument, arg2 );

    /*
     * Get type.
     */
    if( arg1[0] == '\0' )
    {
	send_to_char( "Get what?\n\r", ch );
	return;
    }

    if( arg2[0] == '\0' )
    {
	if( IS_AFFECTED( ch, AFF_GHOUL ) )
	{
	    send_to_char( "You can't get anything in ghoul form.\n\r", ch );
	    return;
	}
	if( str_cmp( arg1, "all" ) && str_prefix( "all.", arg1 ) )
	{
	    /*
	     * 'get obj'
	     */
	    obj = get_obj_list( ch, arg1, ch->in_room->contents );
	    if( !obj )
	    {
		act( "&gI see no $T here.&n", ch, NULL, arg1, TO_CHAR );
		return;
	    }

	    get_obj( ch, obj, NULL );
	}
	else
	{
	    /*
	     * 'get all' or 'get all.obj'
	     */
	    OBJ_DATA *obj_next;

	    found = FALSE;
	    for( obj = ch->in_room->contents; obj; obj = obj_next )
	    {
		obj_next = obj->next_content;

		if( ( arg1[3] == '\0' || is_obj_name( obj, &arg1[4] ) )
		    && can_see_obj( ch, obj ) )
		{
		    found = TRUE;
		    get_obj( ch, obj, NULL );
		}
	    }

	    if( !found )
	    {
		if( arg1[3] == '\0' )
		    send_to_char( "I see nothing here.\n\r", ch );
		else
		    act( "&gI see no $T here.&n", ch, NULL, &arg1[4], TO_CHAR );
	    }
	}
    }
    else
    {
	/*
	  'get ... container'
	*/
	if( !str_cmp( arg2, "all" ) || !str_prefix( "all.", arg2 ) )
	{
	    send_to_char( "You can't do that.\n\r", ch );
	    return;
	}

	if( !( container = get_obj_here( ch, arg2 ) ) )
	{
	    act( "&gI see no $T here.&n", ch, NULL, arg2, TO_CHAR );
	    return;
	}

	if( IS_AFFECTED( ch, AFF_GHOUL )
	    && container->item_type != ITEM_CORPSE_PC
	    && container->carried_by != ch )
	{
	    send_to_char( "&gYou can't get anything in ghoul form.&n\n\r", ch );
	    return;
	}

	switch( container->item_type )
	{
	default:
	    send_to_char( "&gThat's not a container.&n\n\r", ch );
	    return;

	case ITEM_PORTAL:
	    room = get_room_index( container->value[0] );
	    if( !room )
	    {
		send_to_char( "You can't use that.\n\r", ch );
		return;
	    }
	    break;

	case ITEM_CONTAINER:
	case ITEM_CORPSE_NPC:
	    break;

	case ITEM_CORPSE_PC:
	    if( IS_NPC( ch ) || ( !IS_IMMORTAL( ch )
				  && str_infix( ch->name, container->name ) ) )
	    {
		send_to_char( "&gYou can't do that.&n\n\r", ch );
		return;
	    }
	}

	if( !room && IS_SET( container->value[1], CONT_CLOSED ) )
	{
	    act( "&gThe $d is closed.&n", ch, NULL, container->name, TO_CHAR );
	    return;
	}

	if( str_cmp( arg1, "all" ) && str_prefix( "all.", arg1 ) )
	{
	    /*
	      'get obj container'
	    */
	    if( !room )
		obj = get_obj_list( ch, arg1, container->contains );
	    else
		obj = get_obj_list( ch, arg1, room->contents );
	    if( !obj )
	    {
		act( "&gI see nothing like that in the $T.&n",
		     ch, NULL, arg2, TO_CHAR );
		return;
	    }

	    if( !xIS_SET( container->pIndexData->progtypes, LOOT_PROG )
		|| oprog_percent_check( ch, container, NULL, LOOT_PROG ) )
		get_obj( ch, obj, container );
	}
	else
	{
	    /*
	      'get all container' or 'get all.obj container'
	    */
	    OBJ_DATA *obj_next;

	    found = FALSE;
	    for( obj = ( room ) ? room->contents : container->contains;
		 obj; obj = obj_next )
	    {
		obj_next = obj->next_content;

		if( ( arg1[3] == '\0' || is_obj_name( obj, &arg1[4] ) )
		    && can_see_obj( ch, obj ) )
		{
		    found = TRUE;
		    get_obj( ch, obj, container );
		}
	    }

	    if( !xIS_SET( container->pIndexData->progtypes, LOOT_PROG )
		|| oprog_percent_check( ch, container, NULL, LOOT_PROG ) )
	    {

		if( !found )
		{
		    if( arg1[3] == '\0' )
			act( "&gI see nothing in the $T.&n",
			     ch, NULL, arg2, TO_CHAR );
		    else
			act( "&gI see nothing like that in the $T.&n",
			     ch, NULL, arg2, TO_CHAR );
		}
	    }
	}
    }

    return;
}


void do_put( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *container;
    OBJ_DATA *obj;
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    ROOM_INDEX_DATA *room = NULL;

    argument = one_argument( argument, arg1 );
    one_argument( argument, arg2 );

    if( arg1[0] == '\0' || arg2[0] == '\0' )
    {
	send_to_char( "Put what in what?\n\r", ch );
	return;
    }

    if( !str_cmp( arg2, "all" ) || !str_prefix( "all.", arg2 ) )
    {
	send_to_char( "You can't do that.\n\r", ch );
	return;
    }

    if( !( container = get_obj_here( ch, arg2 ) ) )
    {
	act( "I see no $T here.", ch, NULL, arg2, TO_CHAR );
	return;
    }

    if( container->item_type != ITEM_CONTAINER
	&& container->item_type != ITEM_PORTAL )
    {
	send_to_char( "That's not a container.\n\r", ch );
	return;
    }
    if( container->item_type == ITEM_PORTAL )
	room = get_room_index( container->value[0] );

    if( IS_SET( container->value[1], CONT_CLOSED ) )
    {
	act( "The $d is closed.", ch, NULL, container->name, TO_CHAR );
	return;
    }

    if( str_cmp( arg1, "all" ) && str_prefix( "all.", arg1 ) )
    {
	/*
	  'put obj container'
	*/
	if( !( obj = get_obj_carry( ch, arg1 ) ) )
	{
	    send_to_char( "You do not have that item.\n\r", ch );
	    return;
	}

	if( obj == container )
	{
	    send_to_char( "You can't fold it into itself.\n\r", ch );
	    return;
	}

	if( !can_drop_obj( ch, obj ) )
	{
	    send_to_char( "You can't let go of it.\n\r", ch );
	    return;
	}

	if( container->item_type == ITEM_CONTAINER
	    && !IS_SET( container->value[1], CONT_WEIGHTLESS )
	    && get_obj_weight( obj ) + get_obj_weight( container )
	    - container->weight > container->value[0] )
	{
	    send_to_char( "It won't fit.\n\r", ch );
	    return;
	}

	obj_from_char( obj );
	act( "&gYou put $p &gin $P&g.&n", ch, obj, container, TO_CHAR );
	act( "&g$n puts $p &gin $P&g.&n", ch, obj, container, TO_ROOM );
	if( container->item_type == ITEM_PORTAL )
	{
	    if( room )
	    {
		obj_to_room( obj, room );
		if( room->people )
		{
		    act( "$p drops from the sky.", room->people,
			 obj, NULL, TO_CHAR );
		    act( "$p drops from the sky.", room->people,
			 obj, NULL, TO_ROOM );
		}
	    }
	}
	else
	{
	    if( IS_SET( container->value[1], CONT_HOLEY ) )
	    {
		act( "&g$p &gdrops through to the floor.&n",
		     ch, obj, container, TO_ROOM );
		obj_to_room( obj, ch->in_room );
	    }
	    else if( IS_SET( container->value[1], CONT_HUNGRY ) )
	    {
		extract_obj( obj );
		act( "&r$P &rburps loudly.&n", ch, NULL, container, TO_CHAR );
		act( "&r$P &rburps loudly.&n", ch, NULL, container, TO_ROOM );
	    }
	    else
		obj_to_obj( obj, container );
	    oprog_container_putin( ch, container, obj );
	}
    }
    else
    {
	/*
	 * 'put all container' or 'put all.obj container'
	 */
	OBJ_DATA *obj_next;

	for( obj = ch->carrying; obj; obj = obj_next )
	{
	    obj_next = obj->next_content;

	    if( ( arg1[3] == '\0' || is_obj_name( obj, &arg1[4] ) )
		&& can_see_obj( ch, obj )
		&& obj->wear_loc == WEAR_NONE
		&& obj != container
		&& can_drop_obj( ch, obj )
		&& ( ( container->item_type == ITEM_CONTAINER
		       && ( IS_SET( container->value[1], CONT_WEIGHTLESS )
			    || get_obj_weight( obj ) + get_obj_weight( container )
			    <= container->value[0] ) )
		     || ( container->item_type == ITEM_PORTAL && room ) )
		&& !( obj->item_type == ITEM_WEAPON && ch->level < L_APP
		      && IS_SET( obj->extra_flags, ITEM_CHARGED ) ) )
	    {
		obj_from_char( obj );
		act( "&gYou put $p&g in $P&g.&n",
		     ch, obj, container, TO_CHAR );
		act( "&g$n puts $p&g in $P&g.&n",
		     ch, obj, container, TO_ROOM );
		if( room )
		{
		    obj_to_room( obj, room );
		    if( room->people )
		    {
			act( "$p drops from the sky.",
			     room->people, obj, NULL, TO_CHAR );
			act( "$p drops from the sky.",
			     room->people, obj, NULL, TO_ROOM );
		    }
		}
		else
		{
		    if( IS_SET( container->value[1], CONT_HOLEY ) )
		    {
			act( "$p drops through to the floor.",
			     ch, obj, container, TO_ROOM );
			obj_to_room( obj, ch->in_room );
		    }
		    else if( IS_SET( container->value[1], CONT_HUNGRY ) )
		    {
			extract_obj( obj );
			act( "$P burps loudly.",
			     ch, NULL, container, TO_CHAR );
			act( "$P burps loudly.",
			     ch, NULL, container, TO_ROOM );
		    }
		    else
			obj_to_obj( obj, container );
		    oprog_container_putin( ch, container, obj );
		}
	    }
	}
    }

    return;
}


void do_drop( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    char arg[MAX_INPUT_LENGTH];
    bool found;

    argument = one_argument( argument, arg );

    if( arg[0] == '\0' )
    {
	send_to_char( "Drop what?\n\r", ch );
	return;
    }

    if( is_number_special( arg ) )
    {
	/*
	 * 'drop NNNN coins'
	 */
	OBJ_DATA *obj_next;
	int amount;

	amount = atoi_special( arg );
	one_argument( argument, arg );

	if( amount <= 0 || str_prefix( arg, "coins" ) )
	{
	    send_to_char( "Sorry, you can't do that.\n\r", ch );
	    return;
	}

	if( ch->gold < amount )
	{
	    send_to_char( "You haven't got that many coins.\n\r", ch );
	    return;
	}

	ch->gold -= amount;
	if( IS_NPC( ch ) )
	    ch->in_room->area->economy -= amount;

	for( obj = ch->in_room->contents; obj; obj = obj_next )
	{
	    obj_next = obj->next_content;

	    if( obj->deleted )
		continue;

	    switch( obj->pIndexData->vnum )
	    {
	    case OBJ_VNUM_MONEY_ONE:
		amount += 1;
		extract_obj( obj );
		break;

	    case OBJ_VNUM_MONEY_SOME:
		amount += obj->value[0];
		extract_obj( obj );
		break;
	    }
	}
	send_to_char( "OK.\n\r", ch );
	act( "$n drops some gold.", ch, NULL, NULL, TO_ROOM );
	obj = create_money( amount );
	obj_to_room( obj, ch->in_room );
	return;
    }

    if( str_cmp( arg, "all" ) && str_prefix( "all.", arg ) )
    {
	/*
	 * 'drop obj'
	 */
	if( !( obj = get_obj_carry( ch, arg ) ) )
	{
	    send_to_char( "You do not have that item.\n\r", ch );
	    return;
	}

	if( !can_drop_obj( ch, obj ) )
	{
	    send_to_char( "You can't let go of it.\n\r", ch );
	    return;
	}

	if( xIS_SET( obj->pIndexData->progtypes, DROP_PROG ) )
	{
	    if( oprog_percent_check( ch, obj, NULL, DROP_PROG ) == 0 )
		return;
	}

	if( xIS_SET( ch->in_room->progtypes, DROP_PROG ) )
	{
	    if( rprog_percent_check( ch->in_room, ch, NULL, NULL, DROP_PROG ) == 0 )
		return;
	}

	obj_from_char( obj );
	act( "&gYou drop $p&g.&n", ch, obj, NULL, TO_CHAR );
	act( "&g$n drops $p&g.&n", ch, obj, NULL, TO_ROOM );
	if( IS_SET( obj->extra_flags, ITEM_FRAGILE ) && number_bits( 1 ) == 0 )
	{
	    act( "&g$p crumbles into dust as it hits the ground.",
		 ch, obj, NULL, TO_CHAR );
	    act( "&g$p crumbles into dust as it hits the ground.",
		 ch, obj, NULL, TO_ROOM );
	    extract_obj( obj );
	}
	else
	{
	    obj_to_room( obj, ch->in_room );
	}
    }
    else
    {
	/*
	 * 'drop all' or 'drop all.obj'
	 */
	OBJ_DATA *obj_next;

	found = FALSE;
	for( obj = ch->carrying; obj; obj = obj_next )
	{
	    obj_next = obj->next_content;

	    if( ( arg[3] == '\0' || is_obj_name( obj, &arg[4] ) )
		&& can_see_obj( ch, obj )
		&& obj->wear_loc == WEAR_NONE
		&& can_drop_obj( ch, obj )
		&& ( !xIS_SET( obj->pIndexData->progtypes, DROP_PROG )
		     || oprog_percent_check( ch, obj, NULL, DROP_PROG ) ) )
	    {
		found = TRUE;

		obj_from_char( obj );
		act( "&gYou drop $p&g.&n", ch, obj, NULL, TO_CHAR );
		act( "&g$n drops $p&g.&n", ch, obj, NULL, TO_ROOM );
		if( IS_SET( obj->extra_flags, ITEM_FRAGILE )
		    && number_bits( 1 ) == 0 )
		{
		    act( "&g$p crumbles into dust as it hits the ground.",
			 ch, obj, NULL, TO_CHAR );
		    act( "&g$p crumbles into dust as it hits the ground.",
			 ch, obj, NULL, TO_ROOM );
		    extract_obj( obj );
		}
		else
		    obj_to_room( obj, ch->in_room );
	    }
	}

	if( !found )
	{
	    if( arg[3] == '\0' )
		send_to_char( "You are not carrying anything.\n\r", ch );
	    else
		act( "You are not carrying any $T.",
		     ch, NULL, &arg[4], TO_CHAR );
	}
    }

    return;
}


void do_give( CHAR_DATA *ch, const char *argument )
{
    CHAR_DATA *victim;
    OBJ_DATA *obj;
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];

    argument = one_argument( argument, arg1 );
    argument = one_argument( argument, arg2 );

    if( arg1[0] == '\0' || arg2[0] == '\0' )
    {
	send_to_char( "Give what to whom?\n\r", ch );
	return;
    }

    if( is_number_special( arg1 ) )
    {
	/*
	 * 'give NNNN coins victim'
	 */
	char buf[MAX_STRING_LENGTH];
	int amount;

	amount = atoi_special( arg1 );
	if( amount <= 0 || str_prefix( arg2, "coins" ) )
	{
	    send_to_char( "Sorry, you can't do that.\n\r", ch );
	    return;
	}

	one_argument( argument, arg2 );

	if( arg2[0] == '\0' )
	{
	    send_to_char( "Give what to whom?\n\r", ch );
	    return;
	}

	if( !( victim = get_char_room( ch, arg2 ) ) )
	{
	    send_to_char( "They aren't here.\n\r", ch );
	    return;
	}

	if( ch->gold < amount )
	{
	    send_to_char( "You haven't got that much gold.\n\r", ch );
	    return;
	}

	ch->gold -= amount;
	victim->gold += amount;
	if( IS_NPC( victim ) )
	    victim->in_room->area->economy += amount;
	act( "You give $N some gold.", ch, NULL, victim, TO_CHAR );

	sprintf( buf, "$n gives you %d gold.", amount );
	act( buf, ch, NULL, victim, TO_VICT );

	act( "$n gives $N some gold.", ch, NULL, victim, TO_NOTVICT );
	if( IS_NPC( victim ) &&
	    IS_SET( spec_table[victim->spec_fun].usage, SPEC_BRIBE ) )
	    ( *spec_table[victim->spec_fun].spec_fun )
		( victim, ch, SPEC_GIVE, &amount );
	mprog_bribe_trigger( victim, ch, amount );
	return;
    }

    if( !( obj = get_obj_carry( ch, arg1 ) ) )
    {
	send_to_char( "You do not have that item.\n\r", ch );
	return;
    }

    if( obj->wear_loc != WEAR_NONE )
    {
	send_to_char( "You must remove it first.\n\r", ch );
	return;
    }

    if( !( victim = get_char_room( ch, arg2 ) ) )
    {
	send_to_char( "They aren't here.\n\r", ch );
	return;
    }

    if( !can_drop_obj( ch, obj ) )
    {
	send_to_char( "You can't let go of it.\n\r", ch );
	return;
    }

    if( ( IS_OBJ_STAT( obj, ITEM_HOLY )
	  && !str_cmp( race_table[victim->race].name, "Vampire" ) )
	|| ( IS_NPC( victim ) && ( victim->pIndexData->pShop ) ) )
    {
	act( "$N refuses the $p.", ch, obj, victim, TO_CHAR );
	act( "$n tries to give $N a $p but $E refuses.",
	     ch, obj, victim, TO_ROOM );
	act( "You refuse to accept the $p from $n.",
	     ch, obj, victim, TO_VICT );
	return;
    }

    if( victim->carry_number >= can_carry_n( victim ) )
    {
	act( "$N has $S hands full.", ch, NULL, victim, TO_CHAR );
	return;
    }

    if( victim->carry_weight + get_obj_weight( obj ) > can_carry_w( victim ) )
    {
	act( "$N can't carry that much weight.", ch, NULL, victim, TO_CHAR );
	return;
    }

    if( !can_see_obj( victim, obj ) )
    {
	act( "$N can't see it.", ch, NULL, victim, TO_CHAR );
	return;
    }

    if( xIS_SET( obj->pIndexData->progtypes, GIVE_PROG ) )
    {
	if( oprog_percent_check( ch, obj, NULL, GIVE_PROG ) == 0 )
	    return;
    }

    obj_from_char( obj );
    obj_to_char( obj, victim );
    REMOVE_BIT( SysInfo->flags, SYSINFO_ACT_TRIGGER );
    act( "You give $p to $N.", ch, obj, victim, TO_CHAR );
    REMOVE_BIT( SysInfo->flags, SYSINFO_ACT_TRIGGER );
    act( "$n gives you $p.", ch, obj, victim, TO_VICT );
    act( "$n gives $p to $N.", ch, obj, victim, TO_NOTVICT );
    if( IS_NPC( victim ) &&
	IS_SET( spec_table[victim->spec_fun].usage, SPEC_GIVE ) )
	( *spec_table[victim->spec_fun].spec_fun )
	    ( victim, ch, SPEC_GIVE, obj );
    mprog_give_trigger( victim, ch, obj );
    return;
}


void do_fill( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    OBJ_DATA *fountain;
    bool found;

    if( !check_blind( ch ) )
	return;

    if( argument[0] == '\0' )
    {
	send_to_char( "Fill what?\n\r", ch );
	return;
    }

    if( !( obj = get_obj_carry( ch, argument ) ) )
    {
	send_to_char( "You do not have that item.\n\r", ch );
	return;
    }

    found = FALSE;
    for( fountain = ch->in_room->contents; fountain;
	 fountain = fountain->next_content )
    {
	if( fountain->item_type == ITEM_FOUNTAIN )
	{
	    found = TRUE;
	    break;
	}
    }

    if( !found )
    {
	send_to_char( "There is no fountain here!\n\r", ch );
	return;
    }

    if( obj->item_type != ITEM_DRINK_CON )
    {
	send_to_char( "You can't fill that.\n\r", ch );
	return;
    }

    if( obj->value[1] != 0 && obj->value[2] != fountain->value[2] )
    {
	send_to_char( "There is a different liquid in it.\n\r", ch );
	return;
    }

    if( obj->value[1] >= obj->value[0] )
    {
	send_to_char( "Your container is full.\n\r", ch );
	return;
    }

    act( "You fill $p from $P.", ch, obj, fountain, TO_CHAR );
    weight_change_object( obj, UMAX( 0, obj->value[0] - obj->value[1] ) / 10 );
    obj->value[2] = fountain->value[2];
    obj->value[1] = obj->value[0];
    return;
}


void do_drink( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    int amount;
    int liquid;

    if( !check_blind( ch ) )
	return;

    if( argument[0] == '\0' )
    {
	for( obj = ch->in_room->contents; obj; obj = obj->next_content )
	{
	    if( !obj->deleted && obj->item_type == ITEM_FOUNTAIN )
		break;
	}

	if( !obj )
	{
	    send_to_char( "Drink what?\n\r", ch );
	    return;
	}
    }
    else
    {
	if( !( obj = get_obj_here( ch, argument ) ) )
	{
	    send_to_char( "You can't find it.\n\r", ch );
	    return;
	}
    }

    if( !IS_NPC( ch ) && ch->pcdata->condition[COND_DRUNK] > 500 )
    {
	send_to_char( "You fail to reach your mouth.  *Hic*\n\r", ch );
	return;
    }

    switch( obj->item_type )
    {
    default:
	send_to_char( "You can't drink from that.\n\r", ch );
	break;

    case ITEM_FOUNTAIN:
	liquid = obj->value[2];

	if( !str_cmp( race_table[ch->race].name, "vampire" )
	    && liquid != LIQ_BLOOD )
	{
	    send_to_char( "You can't drink from that.\n\r", ch );
	    break;
	}

	if( !IS_NPC( ch ) )
	{
	    if( liq_table[liquid].liq_affect[COND_THIRST] > 0 )
	    {
		amount = race_table[ch->race].thirst_mod * 10
		    - ch->pcdata->condition[COND_THIRST];
		amount /= liq_table[liquid].liq_affect[COND_THIRST];
		ch->pcdata->condition[COND_THIRST]
		    = race_table[ch->race].thirst_mod * 10;
	    }
	    else
		/* drink 10 times until you find it doesn't quench thirst */
	    {
		amount = 100;
		gain_condition( ch, COND_THIRST,
			       100 * liq_table[liquid].liq_affect[COND_THIRST] );
	    }

	    gain_condition( ch, COND_FULL,
			    amount * liq_table[liquid].liq_affect[COND_FULL] );
	    gain_condition( ch, COND_DRUNK,
			    amount * liq_table[liquid].liq_affect[COND_DRUNK] );
	}

	act( "You drink $T from $p.", ch, obj,
	    liq_table[liquid].liq_name, TO_CHAR );
	act( "$n drinks $T from $p.", ch, obj,
	    liq_table[liquid].liq_name, TO_ROOM );
	if( obj->action && obj->action[0] != '\0' )
	    send_to_char( obj->action, ch );

	if( IS_NPC( ch ) || ch->pcdata->condition[COND_THIRST]
	    > race_table[ch->race].thirst_mod * 8 )
	    send_to_char( "You have quenched your thirst completely.\n\r", ch );
	else
	    send_to_char( "The drink fails to refresh your thirst.\n\r", ch );
	break;

    case ITEM_DRINK_CON:
	liquid = obj->value[2];

	if( obj->value[1] <= 0 )
	{
	    send_to_char( "It is already empty.\n\r", ch );
	    return;
	}

	act( "You drink $T from $p.",
	    ch, obj, liq_table[liquid].liq_name, TO_CHAR );
	act( "$n drinks $T from $p.",
	    ch, obj, liq_table[liquid].liq_name, TO_ROOM );
	if( obj->action && obj->action[0] != '\0' )
	    send_to_char( obj->action, ch );

	amount = number_range( 3, 10 );
	amount = UMIN( amount, obj->value[1] ) * 10;

	weight_change_object( obj, 0 - UMIN( amount, obj->weight ) / 10 );
	gain_condition( ch, COND_DRUNK,
		       amount * liq_table[liquid].liq_affect[COND_DRUNK] );
	if( str_cmp( race_table[ch->race].name, "vampire" ) )
	{
	    gain_condition( ch, COND_FULL,
			   amount * liq_table[liquid].liq_affect[COND_FULL] );
	    gain_condition( ch, COND_THIRST,
			   amount * liq_table[liquid].liq_affect[COND_THIRST] );
	}
	else if( liquid == LIQ_BLOOD )
	{
	    gain_condition( ch, COND_FULL, amount * 20 );
	    gain_condition( ch, COND_THIRST, amount * 12 );
	}

	if( !IS_NPC( ch ) )
	{
	    if( ch->pcdata->condition[COND_DRUNK] > 300 )
		send_to_char( "You feel drunk.\n\r", ch );
	    if( ch->pcdata->condition[COND_FULL]
		> race_table[ch->race].hunger_mod * 8 )
		send_to_char( "You are full.\n\r", ch );
	    if( ch->pcdata->condition[COND_THIRST]
		> race_table[ch->race].thirst_mod * 8 )
		send_to_char( "You do not feel thirsty.\n\r", ch );
	}

	if( obj->value[3] != 0
	    && !IS_SET( race_table[ch->race].race_abilities, RACE_NO_POISON ) )
	{
	    /* The shit was poisoned ! */
	    AFFECT_DATA af;

	    send_to_char( "You choke and gag.\n\r", ch );
	    act( "$n chokes and gags.", ch, NULL, NULL, TO_ROOM );
	    af.type = gsn_poison;
	    af.duration = 3 * amount;
	    af.location = APPLY_STR;
	    af.modifier = -2;
	    vset( af.bitvector, AFF_POISON );
	    affect_join( ch, &af );
	}

	obj->value[1] -= amount / 10;
	if( obj->value[1] <= 0 )	/* empty - no poison */
	    obj->value[3] = 0;
	break;
    }

    return;
}


void do_eat( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;

    if( !check_blind( ch ) )
	return;

    if( argument[0] == '\0' )
    {
	send_to_char( "Eat what?\n\r", ch );
	return;
    }

    if( !( obj = get_obj_carry( ch, argument ) ) )
    {
	send_to_char( "You do not have that item.\n\r", ch );
	return;
    }

    if( !IS_IMMORTAL( ch ) )
    {
	if( obj->item_type != ITEM_FOOD && obj->item_type != ITEM_PILL
	    && obj->item_type != ITEM_LIMB
	    && ( obj->item_type != ITEM_CORPSE_NPC
		 || get_size( ch ) - 40 < race_table[obj->value[0]].size ) )
	{
	    send_to_char( "That's not edible.\n\r", ch );
	    return;
	}

	if( !IS_NPC( ch ) && ch->pcdata->condition[COND_FULL]
	    > race_table[ch->race].hunger_mod * 8 )
	{
	    send_to_char( "You are too full to eat more.\n\r", ch );
	    return;
	}
    }

    act( "You eat $p.", ch, obj, NULL, TO_CHAR );
    act( "$n eats $p.", ch, obj, NULL, TO_ROOM );

    switch( obj->item_type )
    {
    case ITEM_LIMB:
    case ITEM_FOOD:
    case ITEM_CORPSE_NPC:
	if( obj->action && obj->action[0] )
	    send_to_char( obj->action, ch );
	if( !IS_NPC( ch ) )
	{
	    int condition;

	    condition = ch->pcdata->condition[COND_FULL];
	    if( str_cmp( race_table[ch->race].name, "vampire" ) )
		gain_condition( ch, COND_FULL,
				obj->item_type == ITEM_FOOD
				? obj->value[0] * 10 :
				obj->item_type == ITEM_LIMB
				? 150 : 750 );

	    if( ch->pcdata->condition[COND_FULL]
		> race_table[ch->race].hunger_mod * 8 )
		send_to_char( "You are full.\n\r", ch );
	    else if( condition == 0 && ch->pcdata->condition[COND_FULL] > 0 )
		send_to_char( "You are no longer hungry.\n\r", ch );
	}

	if( obj->item_type != ITEM_CORPSE_NPC && obj->value[3] != 0
	    && !IS_SET( race_table[ch->race].race_abilities, RACE_NO_POISON ) )
	{
	    /* The shit was poisoned! */
	    AFFECT_DATA af;

	    act( "$n chokes and gags.", ch, 0, 0, TO_ROOM );
	    send_to_char( "You choke and gag.\n\r", ch );

	    af.type = gsn_poison;
	    af.duration = 2 * obj->value[0];
	    af.location = APPLY_STR;
	    af.modifier = -2;
	    vset( af.bitvector, AFF_POISON );
	    affect_join( ch, &af );
	}
	if( xIS_SET( obj->pIndexData->progtypes, USE_PROG ) )
	    oprog_percent_check( ch, obj, NULL, USE_PROG );
	break;

    case ITEM_PILL:
	if( obj->action && obj->action[0] )
	    send_to_char( obj->action, ch );

	if( obj->level > ch->level + 3 )
	    act( "$p is too high level for you.", ch, obj, NULL, TO_CHAR );
	else
	{
	    obj_cast_spell( obj->value[1], obj->value[0], ch, ch, NULL, NULL );
	    obj_cast_spell( obj->value[2], obj->value[0], ch, ch, NULL, NULL );
	    obj_cast_spell( obj->value[3], obj->value[0], ch, ch, NULL, NULL );
	}
	if( xIS_SET( obj->pIndexData->progtypes, USE_PROG ) )
	    oprog_percent_check( ch, obj, NULL, USE_PROG );
	break;
    }

    if( !IS_NPC( ch )
	|| ( IS_NPC( ch ) && is_affected( ch, gsn_charm_person ) ) )
	extract_obj( obj );
    return;
}


/*
 * Remove an object.
 */
bool remove_obj( CHAR_DATA *ch, int iWear, bool fReplace )
{
    OBJ_DATA *obj;

    if( !( obj = get_eq_char( ch, iWear ) ) )
	return TRUE;

    if( !fReplace )
	return FALSE;

    if( IS_SET( obj->extra_flags, ITEM_NOREMOVE ) )
    {
	act( "You can't remove $p.", ch, obj, NULL, TO_CHAR );
	return FALSE;
    }

    /* Questionable code */
    if( ch->class == CLASS_ANGEL && obj->pIndexData->vnum == 46 )
    {
	send_to_char( "Angels cannot remove their wings.\n\r", ch );
	return FALSE;
    }

    if( !xIS_SET( obj->pIndexData->progtypes, REMOVE_PROG )
	|| oprog_percent_check( ch, obj, NULL, REMOVE_PROG ) )
    {
	act( "$n stops using $p.", ch, obj, NULL, TO_ROOM );
	act( "You stop using $p.", ch, obj, NULL, TO_CHAR );
	unequip_char( ch, obj );
	return TRUE;
    }
    else
	return FALSE;
}


void remove_juggled( CHAR_DATA *ch, OBJ_DATA *obj )
{
    if( obj->wear_loc != WEAR_JUGGLED )
	return;
    act( "$n smoothly catches $p.", ch, obj, NULL, TO_ROOM );
    act( "You grab $p carefully.", ch, obj, NULL, TO_CHAR );
    unequip_char( ch, obj );
    if( xIS_SET( obj->pIndexData->progtypes, REMOVE_PROG ) )
	oprog_percent_check( ch, obj, NULL, REMOVE_PROG );
}


/*
 * New, neater wear_obj, still the need for some yucky stuff though,
 * watch for the hands, this requires some sticky code.
 * Compatible with the previous version in all respects.
 * --Symposium.
 */
void wear_obj( CHAR_DATA *ch, OBJ_DATA *obj, bool fReplace )
{
    int i, j;
    int wear_right = WEAR_NONE;
    int wear_left = WEAR_NONE;
    int weight;


    if( IS_SET( obj->extra_flags, ITEM_BURIED ) )
    {
	send_to_char( "You cant very well wear a buried item.\n\r", ch );
	return;
    }

    if( ch->level < obj->level - 3 && obj->item_type != ITEM_LIMB )
    {
	charprintf( ch, "You must be level %d to use this object.\n\r",
		 obj->level - 3 );
	act( "$n tries to use $p, but is too inexperienced.",
	     ch, obj, NULL, TO_ROOM );
	return;
    }

    if( obj->required_skill > 0 && !can_use( ch, obj->required_skill ) )
    {
	send_to_char( "You need a skill to use this item.\n\r", ch );
	act( "$n tries to wear $p but lacks the skill.",
	     ch, obj, NULL, TO_ROOM );
	return;
    }

    if( obj->item_type == ITEM_ARMOUR && obj->value[1] >= 0
	&& ( get_size( ch ) + 10 < obj->value[1]
	     || get_size( ch ) - 10 > obj->value[1] ) )
    {
	if( get_size( ch ) > obj->value[1] )
	    send_to_char(
		"You find that it is a little too small for you.\n\r", ch );
	else
	    send_to_char(
		"You find that it is a little too large for you.\n\r", ch );
	act( "$n tries to wear $p, but it doesn't fit.",
	     ch, obj, NULL, TO_ROOM );
	return;
    }

    for( i = 0; i < MAX_WEAR; i++ )
    {
	if( !CAN_WEAR( obj, wear_table[i].wear_flag ) )
	    continue;

	/* find if there is another slot that the item can occupy */
	for( j = i+1; j < MAX_WEAR; j++ )
	    if( CAN_WEAR( obj, wear_table[j].wear_flag ) )
		break;

	/* WHAT A MESS!!!!! -- Symposium */
	if( i == WEAR_WIELD_R || i == WEAR_HOLD_L || i == WEAR_WIELD_DOUBLE
	    || i == WEAR_SHIELD )
	{
	    if( get_eq_char( ch, WEAR_WIELD_R ) )
		wear_right = WEAR_WIELD_R;
	    else if( get_eq_char( ch, WEAR_HOLD_R ) )
		wear_right = WEAR_HOLD_R;
	    if( get_eq_char( ch, WEAR_WIELD_L ) )
		wear_left = WEAR_WIELD_L;
	    else if( get_eq_char( ch, WEAR_HOLD_L ) )
		wear_left = WEAR_HOLD_L;
	    else if( get_eq_char( ch, WEAR_SHIELD ) )
		wear_left = WEAR_SHIELD;
	    if( get_eq_char( ch, WEAR_WIELD_DOUBLE ) )
	    {
		wear_right = WEAR_WIELD_DOUBLE;
		wear_left = WEAR_WIELD_DOUBLE;
	    }
	    switch( i )
	    {
	    case WEAR_WIELD_R:
		if( ( IS_NPC( ch ) || can_use( ch, gsn_dual )
		      || IS_SET( race_table[ch->race].race_abilities, RACE_DUAL_WIELD ) )
		    && wear_right == WEAR_WIELD_R )
		{
		    if( wear_left == WEAR_NONE
			|| remove_obj( ch, wear_left, fReplace )
			|| remove_obj( ch, wear_right, fReplace ) )
			break;
		    else
			continue;
		}
		else if( wear_right != WEAR_NONE
			 && !remove_obj( ch, wear_right, fReplace ) )
		    continue;
		if( get_obj_weight( obj ) > str_app_wield( get_curr_str( ch ) ) )
		{
		    send_to_char( "It is too heavy for you to wield.\n\r", ch );
		    continue;
		}
		break;

	    case WEAR_HOLD_L:
		if( wear_left == WEAR_NONE || wear_right == WEAR_NONE )
		    break;
		if( !remove_obj( ch, wear_left, fReplace )
		    && !remove_obj( ch, wear_right, fReplace ) )
		    continue;
		if( get_eq_char( ch, wear_left ) )
		    i = WEAR_HOLD_R;
		break;

	    case WEAR_SHIELD:
		if( wear_left != WEAR_NONE
		    && !remove_obj( ch, wear_left, fReplace ) )
		    continue;
		break;

	    case WEAR_WIELD_DOUBLE:
		if( !IS_NPC( ch ) && !can_use( ch, gsn_two_handed ) )
		{
		    act( "You can't use $P yet.", ch, NULL, obj, TO_CHAR );
		    return;
		}
		if( wear_left == WEAR_WIELD_DOUBLE
		    && !remove_obj( ch, wear_left, fReplace ) )
		    continue;
		if( ( wear_left != WEAR_NONE
		      && !remove_obj( ch, wear_left, fReplace ) )
		    || ( wear_right != WEAR_NONE
			 && !remove_obj( ch, wear_right, fReplace ) ) )
		    continue;
		break;
	    }

	    /*
	     * weapon weight stuff, this is also a true mess!
	     * check if wielding in another hand and then check if
	     * combined weight is enough.
	     * --Symposium
	     */
	    switch( i )
	    {
	    case WEAR_WIELD_R:
		weight = get_obj_weight( obj );
		if( get_eq_char( ch, WEAR_WIELD_L ) )
		    weight += get_obj_weight( get_eq_char( ch, WEAR_WIELD_L ) );
		else if( get_eq_char( ch, WEAR_WIELD_R ) )
		    weight += get_obj_weight( get_eq_char( ch, WEAR_WIELD_R ) );
		if( weight > str_app_wield( get_curr_str( ch ) ) )
		{
		    send_to_char( "It is too heavy for you to wield.\n\r", ch );
		    continue;
		}
	    case WEAR_WIELD_DOUBLE:
		if( get_obj_weight( obj ) > str_app_wield( get_curr_str( ch ) ) )
		{
		    send_to_char( "It is too heavy for you to wield.\n\r", ch );
		    return;
		}
	    default:
		break;
	    }
	}
	/* these should have been taken care of before */
	else if( i == WEAR_WIELD_L || i == WEAR_HOLD_R )
	    continue;
	/* second equip slot, check this too */
	else if( j < MAX_WEAR )
	{
	    if( get_eq_char( ch, i ) && get_eq_char( ch, j )
		&& !remove_obj( ch, i, fReplace )
		&& !remove_obj( ch, j, fReplace ) )
		continue;
	}
	else
	{
	    if( !remove_obj( ch, i, fReplace ) )
		continue;
	}

	/*
	 * this problem could be avoided by making the first hand
	 * for held items the right hand, however this makes it a
	 * little less player friendly and the confusion with gear
	 * changes is allready sufficient.
	 * --Symposium
	 */
	if( get_eq_char( ch, i )
	    || ( i == WEAR_HOLD_L && wear_left != WEAR_NONE
		 && get_eq_char( ch, wear_left ) ) )
	    i = j;

	if( ( ( ch->body_parts & wear_table[i].body_parts )
	      != wear_table[i].body_parts ) )
	{
	    if( j >= MAX_WEAR || get_eq_char( ch, j ) )
	    {
		act( wear_table[i].no_part, ch, NULL, NULL, TO_CHAR );
		continue;
	    }
	    i = j;
	    if( ( ch->body_parts & wear_table[i].body_parts )
		!= wear_table[i].body_parts )
	    {
		act( wear_table[i].no_part, ch, NULL, NULL, TO_CHAR );
		continue;
	    }
	}
	if( !xIS_SET( obj->pIndexData->progtypes, WEAR_PROG )
	    || oprog_percent_check( ch, obj, NULL, WEAR_PROG ) )
	{
	    act( wear_table[i].wear_on_ch, ch, obj, NULL, TO_CHAR );
	    act( wear_table[i].wear_on_room, ch, obj, NULL, TO_ROOM );
	    equip_char( ch, obj, i );
	}
	return;
    }
    if( fReplace )
	send_to_char( "You can't wear, wield, or hold that.\n\r", ch );
    return;
}


void do_wear( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;

    if( !IS_NPC( ch ) && IS_AFFECTED( ch, AFF_GHOUL ) )
    {
	send_to_char(
	    "You may not wear, wield, or hold anything in ghoul form.\n\r",
	    ch );
	return;
    }

    if( argument[0] == '\0' )
    {
	send_to_char( "Wear, wield, or hold what?\n\r", ch );
	return;
    }

    if( !str_cmp( argument, "all" ) )
    {
	OBJ_DATA *obj_next;

	for( obj = ch->carrying; obj; obj = obj_next )
	{
	    obj_next = obj->next_content;

	    if( obj->wear_loc != WEAR_NONE || !can_see_obj( ch, obj ) )
		continue;

	    if( CAN_WEAR( obj, ITEM_WIELD )
		&& IS_SET( race_table[ch->race].race_abilities,
			   RACE_NO_WEAPON_WIELD ) )
		continue;

	    wear_obj( ch, obj, FALSE );
	}
	return;
    }
    else
    {
	if( !( obj = get_obj_carry( ch, argument ) ) )
	{
	    send_to_char( "You do not have that item.\n\r", ch );
	    return;
	}

	if( CAN_WEAR( obj, ITEM_WIELD )
	    && IS_SET( race_table[ch->race].race_abilities,
		       RACE_NO_WEAPON_WIELD ) )
	{
	    send_to_char( "You are not able to wield a weapon.\n\r", ch );
	    return;
	}

	wear_obj( ch, obj, TRUE );
    }

    return;
}


void do_remove( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;

    if( argument[0] == '\0' )
    {
	send_to_char( "Remove what?\n\r", ch );
	return;
    }

    if( !str_cmp( argument, "all" ) )
    {
	for( obj = ch->carrying; obj; obj = obj->next_content )
	{
	    if( obj->deleted || !can_see_obj( ch, obj ) )
		continue;
	    if( obj->wear_loc == WEAR_JUGGLED )
		remove_juggled( ch, obj );
	    else if( obj->wear_loc != WEAR_NONE )
		remove_obj( ch, obj->wear_loc, TRUE );
	}
	return;
    }

    if( !( obj = get_obj_wear( ch, argument ) ) )
    {
	send_to_char( "You do not have that item.\n\r", ch );
	return;
    }

    if( obj->wear_loc == WEAR_JUGGLED )
	remove_juggled( ch, obj );
    else
	remove_obj( ch, obj->wear_loc, TRUE );
    return;
}


void do_juggle( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    OBJ_DATA *other;
    OBJ_DATA *right = NULL;
    OBJ_DATA *left = NULL;
    int num = 0;
    int weight = 0;

    if( !IS_NPC( ch ) && !can_use( ch, gsn_juggle ) )
    {
	send_to_char(
	    "You'd better not try to juggle dangerous implements just yet.\n\r", ch );
	return;
    }

    if( argument[0] == '\0' )
    {
	send_to_char( "You want to juggle what?\n\r", ch );
	return;
    }

    if( !IS_NPC( ch ) && IS_AFFECTED( ch, AFF_GHOUL ) )
    {
	send_to_char(
	    "You may not wear, wield, or hold anything in ghoul form.\n\r",
	    ch );
	return;
    }

    if( !( obj = get_obj_here( ch, argument ) ) )
    {
	send_to_char( "You can't find it.\n\r", ch );
	return;
    }

    if( IS_OBJ_STAT( obj, ITEM_NOREMOVE ) || IS_OBJ_STAT( obj, ITEM_NODROP )
	|| obj->item_type != ITEM_WEAPON )
    {
	act( "$p is too clumsy to juggle.\n\r", ch, obj, NULL, TO_CHAR );
	return;
    }

    for( other = ch->carrying; other; other = other->next_content )
    {
	if( other->deleted )
	    continue;
	if( other->wear_loc == WEAR_WIELD_L )
	    left = other;
	else if( other->wear_loc == WEAR_WIELD_R )
	    right = other;
	else if( other->wear_loc == WEAR_JUGGLED )
	{
	    if( ++num >= MAX_JUGGLED - 2 )
	    {
		send_to_char( "You are testing your skills to the limit, no more.\n\r", ch );
		return;
	    }
	    weight += get_obj_weight( other );
	}
    }
    if( !left || !right )
    {
	send_to_char( "You have to have a weapon in each hand to start.\n\r",
		      ch );
	return;
    }
    weight += get_obj_weight( left );
    weight += get_obj_weight( right );
    weight += get_obj_weight( obj );
    if( weight > str_app_wield( get_curr_str( ch ) ) * 2 / 3 )
    {
	send_to_char( "You can't hold that much weight aloft.\n\r", ch );
	return;
    }
    if( !xIS_SET( obj->pIndexData->progtypes, WEAR_PROG )
	|| oprog_percent_check( ch, obj, NULL, WEAR_PROG ) )
    {
	act( wear_table[WEAR_JUGGLED].wear_on_ch, ch, obj, NULL, TO_CHAR );
	act( wear_table[WEAR_JUGGLED].wear_on_room, ch, obj, NULL, TO_ROOM );
	equip_char( ch, obj, WEAR_JUGGLED );
	WAIT_STATE( ch, skill_table[gsn_juggle].beats );
    }
    return;
}


void stop_juggling( CHAR_DATA *ch )
{
    OBJ_DATA *obj;
    if( ch->position < POS_FIGHTING )
	return;
    juggle_shuffle( ch );	/* could be lethal */
    if( ch->deleted || ch->position < POS_FIGHTING
	|| !( obj = get_eq_char( ch, WEAR_JUGGLED ) ) )
	return;
    send_to_char( "You stop juggling.\n\r", ch );
    do
    {
	unequip_char( ch, obj );
    }
    while( ( obj = get_eq_char( ch, WEAR_JUGGLED ) ) );
    return;
}


void do_sacrifice( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    bool found = FALSE;

    if( argument[0] == '\0' || !str_cmp( argument, ch->name ) )
    {
	send_to_char( "God appreciates your offer and may accept it later.\n\r", ch );
	act( "$n offers $mself to $s God, who graciously declines.",
	     ch, NULL, NULL, TO_ROOM );
	return;
    }

    if ( str_cmp( argument, "all" ) && str_prefix( "all.", argument ) )
    {
	obj = get_obj_list( ch, argument, ch->in_room->contents );
	if( !obj )
	{
	    send_to_char( "You can't find it.\n\r", ch );
	    return;
	}
	if( !CAN_WEAR( obj, ITEM_TAKE ) )
	{
	    act( "$p is not an acceptable sacrifice.", ch, obj, NULL, TO_CHAR );
	    return;
	}

	sac_obj( ch, obj );
    }
    else
    {
	/* 'sac all' or 'sac all.obj' */
	OBJ_DATA *obj_next;

	for ( obj = ch->in_room->contents; obj; obj = obj_next )
	{
	    obj_next = obj->next_content;

	    if( ( argument[3] == '\0' || is_obj_name( obj, &argument[4] ) )
		 && CAN_WEAR( obj, ITEM_TAKE ) )
	    {
		found = TRUE;
		sac_obj( ch, obj );
	    }
	}

	if( !found )
	{
	    if( argument[3] == '\0' )
		send_to_char( "You can't find anything to sacrifice.\n\r", ch );
	    else
		act( "You can't find any $T to sacrifice.",
		     ch, NULL, &argument[4], TO_CHAR );
	}
    }
    return;
}


void sac_obj( CHAR_DATA *ch, OBJ_DATA *obj )
{
    AFFECT_DATA *af;
    CHAR_DATA *mob;
    MOB_INDEX_DATA *pMob;
    char buf[MAX_STRING_LENGTH];
    int i, event, level;
    int race;

    race = obj->value[0];
    level = obj->level;

    act( "$n sacrifices $p to $g.", ch, obj, NULL, TO_ROOM );
    if( obj->item_type == ITEM_CORPSE_NPC && !IS_NPC( ch )
	&& ch->pcdata->clan && ch->pcdata->clan->clan_type == CLAN_ORDER )
	add_karma( ch->pcdata->clan, 1 );

    if( xIS_SET( obj->pIndexData->progtypes, SAC_PROG )
	&& oprog_percent_check( ch, obj, NULL, SAC_PROG ) == 0 )
	return;

    if( !obj->deleted )
	extract_obj( obj );

    event = number_bits( 4 );

    if( IS_NPC( ch ) || !ch->pcdata->religion
	|| !IS_SET( ch->pcdata->religion->sac_events, 1<<event ) )
	event = SAC_EVENT_GOLD;
    switch( event )
    {
    case SAC_EVENT_GOLD:
	if( number_bits( 3 ) == 0 )
	{
	    send_to_char( "Your god smiles on you.\n\r", ch );
	    level *= 2;
	}
    default:
	level = number_fuzzy( level );
	charprintf( ch, "&yGod gives you %d gold coin%s for your sacrifice.&n\n\r",
		    level, level != 1 ? "s" : "" );
	ch->gold += level;
	break;

    case SAC_EVENT_BLESS:
	act( "A light shines down on $n.", ch, NULL, NULL, TO_ROOM );
	i = skill_lookup( "bless" );
	spell_bless( i, UMIN( 30, level / 2 ), ch, ch );
	break;

    case SAC_EVENT_CURSE:
	act( "A dark cloud surrounds $n.", ch, NULL, NULL, TO_ROOM );
	send_to_char( "You feel unclean.\n\r", ch );
	af = new_affect( );
	af->type = gsn_hex;
	af->duration = UMIN( 10, level / 2 );
	vzero( af->bitvector );
	af->location = APPLY_HITROLL;
	af->modifier = UMAX( -10, -2 - level / 20 );
	affect_to_char( ch, af, NULL );
	break;

    case SAC_EVENT_ARMOUR:
	act( "A light shines down on $n.", ch, NULL, NULL, TO_ROOM );
	i = skill_lookup( "armour" );
	spell_armour( i, UMIN( 40, level / 2 ), ch, ch );
	break;

    case SAC_EVENT_HEAL:
	act( "A light shines down on $n.", ch, NULL, NULL, TO_ROOM );
	i = skill_lookup( "cure" );
	spell_cure( i, UMIN( 30, level / 2 ), ch, ch );
	break;

    case SAC_EVENT_DISPEL_EVIL:
	act( "A ball of fire falls from the heavens upon $n.",
	     ch, NULL, NULL, TO_ROOM );
	i = skill_lookup( "condemn" );
	spell_condemn( i, UMIN( 30, level / 2 ), NULL, ch );
	break;

    case SAC_EVENT_BLIND:
	act( "A very bright light shines down on $n",
	     ch, NULL, NULL, TO_ROOM );
	send_to_char( "You are struck blind!\n\r", ch );
	i = skill_lookup( "blindness" );
	af = new_affect( );
	af->type = i;
	af->duration = UMIN( 10, level / 2 );
	vset( af->bitvector, AFF_BLIND );
	af->modifier = 0;
	af->location = 0;
	affect_to_char( ch, af, NULL );
	break;

    case SAC_EVENT_BAMF:
	act( "$n disappears suddenly.", ch, NULL, NULL, TO_ROOM );
	send_to_char( "You have been transferred.\n\r", ch );
	bamf( ch );
	break;

    case SAC_EVENT_MANA:
	send_to_char( "You feel a strong burst of power.\n\r", ch );
	mana_gain( ch );
	break;

    case SAC_EVENT_FOOD:
	act( "$n's God provides for $m.", ch, NULL, NULL, TO_ROOM );
	send_to_char( "God provides for you.\n\r", ch );
	i = skill_lookup( "create food" );
	spell_create_food( i, UMIN( 20, level / 2 ), ch, ch );
	break;

    case SAC_EVENT_WATER:
	act( "$n's God provides for $m.", ch, NULL, NULL, TO_ROOM );
	send_to_char( "God provides for you.\n\r", ch );
	ch->pcdata->condition[COND_THIRST] = 96;
	break;

    case SAC_EVENT_DRUNK:
	act( "$n's God makes fun for $m.", ch, NULL, NULL, TO_ROOM );
	send_to_char( "You feel a little light headed.\n\r", ch );
	ch->pcdata->condition[COND_DRUNK] += UMIN( obj->level * 5, 300 );
	break;

    case SAC_EVENT_RAISE:
	act( "$n tries to sacrifice the corpse but it's alive.",
	     ch, NULL, NULL, TO_ROOM );
	send_to_char( "Oh No! It lives!\n\r", ch );

	pMob = get_mob_index( MOB_VNUM_ZOMBIE );
	pMob->level += level - 5;
	mob = create_mobile( pMob );
	pMob->level += 5 - level;

	sprintf( buf, mob->name, race_table[race].name );
	free_string( mob->name );
	mob->name = str_dup( buf );
	sprintf( buf, mob->short_descr, race_table[race].name );
	free_string( mob->short_descr );
	mob->short_descr = str_dup( buf );
	sprintf( buf, mob->long_descr, race_table[race].name );
	free_string( mob->long_descr );
	mob->long_descr = str_dup( buf );

	mob->gold = 0;

	char_to_room( mob, ch->in_room );
	act( "$n rises from the ground and attacks!",
	     mob, NULL, NULL, TO_ROOM );
	multi_hit( mob, ch, TYPE_UNDEFINED );
	create_char_event( mob, evn_gate_demon,
			   percent_fuzzy( level * 15 + 300, 12 )
			   * PULSE_PER_SECOND );
	break;

    case SAC_EVENT_PLAGUE:
	act( "A dark cloud surrounds $n.", ch, NULL, NULL, TO_ROOM );
	send_to_char( "You feel cold and you shiver.\n\r", ch );
	af = new_affect( );
	af->type = gsn_plague;
	af->duration = UMIN( 20, level / 2 );
	af->location = APPLY_STR;
	af->modifier = 0 - UMIN( 5, level / 10 );
	vset( af->bitvector, AFF_PLAGUE );
	affect_to_char( ch, af, NULL );
	break;
    }

    return;
}


void do_study( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *book;
    int amount, chance;

    if( IS_NPC( ch ) )
    {
	send_to_char( "You can't learn anything more than you already know.\n\r",
		      ch );
	return;
    }

    REMOVE_BIT( ch->pcdata->pc_bits, PC_BIT_RACIAL );

    if( !( book = get_obj_carry( ch, argument ) ) )
    {
	send_to_char( "You do not have that scroll.\n\r", ch );
	return;
    }

    if( book->item_type != ITEM_BOOK )
    {
	send_to_char( "You can study only books.\n\r", ch );
	return;
    }

    if( book->level > ch->level + 3 )
    {
	send_to_char( "You are too inexperienced to use this.\n\r", ch );
	return;
    }

    WAIT_STATE( ch, skill_table[gsn_study].beats );

    act( "$n concentrate$% hard on learning from $p.",
	 ch, book, NULL, TO_ALL );
    if( book->action && book->action[0] )
	send_to_char( book->action, ch );

    if( book->value[0] <= 0 )
    {
	send_to_char( "The pages all seem to be blank!", ch );
	return;
    }

    /*
     * Scrolls skill by Binky for EnvyMud, modified by Thelonius
     */
    if( !IS_NPC( ch )
	&& !get_success( ch, gsn_study, 80 ) )
    {
	switch( number_bits( 3 ) )
	{
	case 0:
	case 1:
	case 2:
	case 3:
	    act( "$n can't understand $p at all.",
		 ch, book, NULL, TO_ALL );
	    return;
	case 4:
	case 5:
	case 6:
	    act( "$n must have said something incorrectly.",
		 ch, NULL, NULL, TO_ALL );
	    act( "$p disolves to dust in your hands.",
		 ch, book, NULL, TO_CHAR );
	    act( "$p suddenly disolves into fine dust.",
		 ch, book, NULL, TO_ROOM );
	    extract_obj( book );
	    return;
	case 7:
	    act( "$n must have triggered something, $p suddenly explodes!",
		 ch, book, NULL, TO_ALL );
	    /*
	     * damage( ) call after extract_obj in case the damage would
	     * have extracted ch.  This is okay because we merely mark
	     * obj->deleted; it still retains all values until list_update.
	     * Sloppy?	Okay, create another integer variable. ---Thelonius
	     */
	    extract_obj( book );
	    damage( ch, ch, 2 * book->level, gsn_study, WEAR_NONE );
	    return;
	}
    }

    if( can_use( ch, book->value[0] ) )
	amount = adept_level( ch, book->value[0] ) + book->value[2];
    else
	amount = book->value[2];

    /* absolute maximum */
    amount = UMIN( amount, book->value[3] );
    amount -= ch->pcdata->learned[book->value[0]];
    /* relative maximum */
    amount = UMIN( amount, book->value[1] );

    if( amount <= 0 )
    {
	act( "You can't learn any more from $p.", ch, book, NULL, TO_CHAR );
	return;
    }
    chance = number_range( 1, get_curr_int( ch ) + get_curr_wis( ch ) / 2 );
    if( chance < amount )
    {
	act( "&BWith some difficulty, you manage to learn about $T from $p.",
	     ch, book, skill_table[book->value[0]].name, TO_CHAR );
	ch->pcdata->learned[book->value[0]] += chance;
    }
    else
    {
	act( "&BYou study $p and learn a little about $T!",
	     ch, book, skill_table[book->value[0]].name, TO_CHAR );
	ch->pcdata->learned[book->value[0]] += amount;
    }
    act( "$n appears to have gleaned some knowledge from $p.",
	 ch, book, NULL, TO_ROOM );
    extract_obj( book );
    return;
}


void do_steal( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    CHAR_DATA *victim;
    char buf[MAX_STRING_LENGTH];
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    char arg[MAX_INPUT_LENGTH];
    int number;
    int count;
    int percent;

    if( IS_NPC( ch ) )
    {
	send_to_char( "That, you cannot do.\n\r", ch );
	return;
    }

    argument = one_argument( argument, arg1 );
    one_argument( argument, arg2 );

    if( arg1[0] == '\0' || arg2[0] == '\0' )
    {
	send_to_char( "Steal what from whom?\n\r", ch );
	return;
    }

    if( !( victim = get_char_room( ch, arg2 ) ) )
    {
	send_to_char( "They aren't here.\n\r", ch );
	return;
    }

    if( victim == ch )
    {
	send_to_char( "Instant success, you steal everything!\n\r", ch );
	return;
    }

    if( is_safe( ch, victim ) )
    {
	send_to_char( "You cannot steal from them.\n\r", ch );
	return;
    }

    obj = NULL;

    WAIT_STATE( ch, skill_table[gsn_steal].beats );

    /*
     * Modified chances for stealing by Morpheus
     */
    percent = ch->level - victim->level;	/* Base value */

    percent += number_range( -10, 10 );	/* Luck */

    if( !IS_AWAKE( victim ) )
	percent += 25;		/* Sleeping characters are easier */

    percent += ch->pcdata->learned[gsn_steal];	/* Character ability */

    if( IS_AFFECTED( ch, AFF_SNEAK ) )
	percent += 5;		/* Quiet characters steal better */

    if( !can_see( ch, victim ) )
	percent += 10;		/* Unseen characters steal better */

    if( !str_prefix( arg1, "coins" ) || !str_cmp( arg1, "gold" ) )
	percent = (int)( percent * 1.2 );	/* Gold is fairly easy to steal */
    else
    {
	number = number_argument( arg1, arg );
	count = 0;
	for( obj = victim->carrying; obj; obj = obj->next_content )
	{
	    if( can_see_obj( ch, obj )
		&& is_obj_name( obj, arg ) )
	    {
		if( ++count == number )
		    break;
	    }
	}

	if( !obj )
	{
	    send_to_char( "You can't find it.\n\r", ch );
	    return;
	}

	if( obj->wear_loc == WEAR_NONE )
	    /* Items in inventory are harder */
	    percent = ( int )( percent * .8 );
	else
	    percent = ( int )( percent * .3 );
    }

    if( ( !IS_NPC( victim )
	  && ( !xIS_SET( victim->act, PLR_OUTLAW )
	       || ch->level - victim->level < 5 ) )
	|| percent < number_percent( ) )
    {
	/*
	 * Failure.
	 */
	send_to_char( "Oops.\n\r", ch );
	act( "$n tried to steal from you.\n\r", ch, NULL, victim, TO_VICT );
	act( "$n tried to steal from $N.\n\r", ch, NULL, victim, TO_NOTVICT );
	sprintf( buf, "%s is a bloody thief!", ch->name );
	do_shout( victim, buf );
	if( IS_NPC( victim ) || is_clan_enemy( ch, victim )
	    || is_clan_enemy( victim, ch ) )	/* may not be commutative */
	{
	    multi_hit( victim, ch, TYPE_UNDEFINED );
	}
	else
	{
	    send_to_char(
		"You can't PK!  You lose 1000 exps.\n\r", ch );
	    gain_exp( ch, -100000 );
	    log_string( buf );
	    if( !xIS_SET( ch->act, PLR_OUTLAW ) )
	    {
		xSET_BIT( ch->act, PLR_OUTLAW );
		send_to_char( "*** You are now an OUTLAW!! ***\n\r", ch );
		save_char_obj( ch );
	    }
	}
	return;
    }

    if( !str_prefix( arg1, "coins" )
	|| !str_cmp( arg1, "gold" ) )
    {
	int amount;

	amount = victim->gold * number_range( 1, 10 ) / 100;
	if( amount <= 0 )
	{
	    send_to_char( "You couldn't get any gold.\n\r", ch );
	    return;
	}

	ch->gold += amount;
	victim->gold -= amount;
	if( IS_NPC( victim ) )
	    victim->in_room->area->economy -= amount;

	charprintf( ch, "Bingo!	 You got %d gold coins.\n\r", amount );
	return;
    }

    if( !can_drop_obj( ch, obj )
	|| IS_SET( obj->extra_flags, ITEM_INVENTORY )
	|| obj->level > ch->level + 3 )
    {
	send_to_char( "You can't pry it away.\n\r", ch );
	return;
    }

    if( ch->carry_number >= can_carry_n( ch ) )
    {
	send_to_char( "You have your hands full.\n\r", ch );
	return;
    }

    if( ch->carry_weight + get_obj_weight( obj ) > can_carry_w( ch ) )
    {
	send_to_char( "You can't carry that much weight.\n\r", ch );
	return;
    }

    if( obj->wear_loc != WEAR_NONE )
    {
	send_to_char( "Very daring, and you got it!\n\r", ch );
	unequip_char( victim, obj );
    }

    obj_from_char( obj );
    obj_to_char( obj, ch );
    send_to_char( "Ok.\n\r", ch );
    return;
}


/*
 * Shopping commands.
 */
CHAR_DATA *find_keeper( CHAR_DATA *ch, char *argument )
{
    CHAR_DATA *keeper;
    SHOP_DATA *pShop;
    char buf[MAX_STRING_LENGTH];

    pShop = NULL;
    for( keeper = ch->in_room->people; keeper; keeper = keeper->next_in_room )
    {
	if( IS_NPC( keeper ) && ( pShop = keeper->pIndexData->pShop )
	    && ( argument[0] == '\0' || is_char_name( keeper, argument ) ) )
	    break;
    }

    if( !keeper || !pShop || IS_AFFECTED( keeper, AFF_CHARM ) )
    {
	send_to_char( "You can't do that here.\n\r", ch );
	return NULL;
    }

    /*
     * Undesirables.
     */
    if( !IS_NPC( ch ) && xIS_SET( ch->act, PLR_OUTLAW ) )
    {
	do_say( keeper, "OUTLAWs are not welcome!" );
	sprintf( buf, "%s the OUTLAW is over here!", ch->name );
	do_shout( keeper, buf );
	return NULL;
    }

    /*
     * Shop hours.
     */
    if( pShop->open_hour > pShop->close_hour )
    {
	if( ch->in_room->area->plane->time.hour < pShop->open_hour
	    && ch->in_room->area->plane->time.hour > pShop->close_hour )
	{
	    do_say( keeper, "Sorry, come back later." );
	    return NULL;
	}
    }
    else
    {
	if( ch->in_room->area->plane->time.hour < pShop->open_hour )
	{
	    do_say( keeper, "Sorry, come back later." );
	    return NULL;
	}

	if( ch->in_room->area->plane->time.hour > pShop->close_hour )
	{
	    do_say( keeper, "Sorry, come back tomorrow." );
	    return NULL;
	}
    }

    /*
     * Invisible or hidden people.
     */
    if( !can_see( keeper, ch ) )
    {
	do_say( keeper, "I don't trade with folks I can't see." );
	return NULL;
    }

    return keeper;
}


CHAR_DATA *find_smith( CHAR_DATA *ch, const char *argument )
{
    CHAR_DATA *smith;
    SHOP_DATA *pShop;
    char buf[MAX_STRING_LENGTH];

    pShop = NULL;
    for( smith = ch->in_room->people; smith; smith = smith->next_in_room )
    {
	if( IS_NPC( smith ) && ( pShop = smith->pIndexData->pShop )
	    && xIS_SET( smith->act, ACT_SMITH )
	    && ( argument[0] == '\0' || is_char_name( smith, argument ) ) )
	    break;
    }

    if( !smith || !pShop || IS_AFFECTED( smith, AFF_CHARM ) )
    {
	send_to_char( "You can't do that here.\n\r", ch );
	return NULL;
    }

    /*
     * Undesirables.
     */
    if( !IS_NPC( ch ) && xIS_SET( ch->act, PLR_OUTLAW ) )
    {
	do_say( smith, "OUTLAWs are not welcome!" );
	sprintf( buf, "%s the OUTLAW is over here!", ch->name );
	do_shout( smith, buf );
	return NULL;
    }

    /*
     * Shop hours.
     */
    if( pShop->open_hour > pShop->close_hour )
    {
	if( ch->in_room->area->plane->time.hour < pShop->open_hour
	    && ch->in_room->area->plane->time.hour > pShop->close_hour )
	{
	    do_say( smith, "I start work later, see you then." );
	    return NULL;
	}
    }
    else
    {
	if( ch->in_room->area->plane->time.hour < pShop->open_hour )
	{
	    do_say( smith, "I start work later, see you then." );
	    return NULL;
	}

	if( ch->in_room->area->plane->time.hour > pShop->close_hour )
	{
	    do_say( smith, "It's too late to work, come back tomorrow." );
	    return NULL;
	}
    }

    /*
     * Invisible or hidden people.
     */
    if( !can_see( smith, ch ) )
    {
	do_say( smith, "Who said that?" );
	return NULL;
    }

    return smith;
}


int get_cost( CHAR_DATA *keeper, CHAR_DATA *customer, OBJ_DATA *obj, bool fBuy )
{
    SHOP_DATA *pShop;
    int cost;
    int margin;

    if( !obj || ( IS_SET( obj->extra_flags, ITEM_INVENTORY ) && !fBuy )
	|| !( pShop = keeper->pIndexData->pShop ) )
	return 0;

    if( fBuy )
    {
	margin = pShop->profit_buy - 100;
	if( customer && !IS_NPC( customer ) )
	{
	    margin *= UMIN( 100, 100 - get_success( customer, gsn_haggle, 300 ) * 2 / 3 );
	    margin /= 100;
	}
	margin += 100;
	if( obj->item_type == ITEM_TREASURE )
	    cost = obj->value[0] * margin / 100;
	else
	    cost = obj->cost * margin / 100;
    }
    else
    {
	OBJ_DATA *obj2;
	int itype;

	/* Questmasters don't buy stuff */
	if( xIS_SET( keeper->act, ACT_QUESTMASTER ) )
	    return -1;

	margin = pShop->profit_sell - 100;
	if( customer && !IS_NPC( customer ) )
	{
	    margin *= UMIN( 100, 100 - get_success( customer, gsn_haggle, 300 ) * 2 / 3 );
	    margin /= 100;
	}
	margin += 100;
	cost = 0;
	for( itype = 0; itype < MAX_TRADE; itype++ )
	{
	    if( obj->item_type == pShop->buy_type[itype] )
	    {
		if( obj->item_type == ITEM_TREASURE )
		    cost = obj->value[0] * margin / 100;
		else
		    cost = obj->cost * margin / 100;
		break;
	    }
	}

	for( obj2 = keeper->carrying; obj2; obj2 = obj2->next_content )
	{
	    if( obj->pIndexData == obj2->pIndexData )
		cost /= 2;
	}
    }

    if( obj->item_type == ITEM_STAFF || obj->item_type == ITEM_WAND )
	cost = cost * obj->value[2] / obj->value[1];

    return cost;
}


/*
 * Multiple object buy modifications by Erwin Andreasen
 * Obtained from Canth's snippets page at:
 * http://www.xs4all.nl/~phule/snippets/snippets.html
 */
void do_buy( CHAR_DATA *ch, const char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    char arg3[MAX_INPUT_LENGTH];

    argument = one_argument( argument, arg );
    argument = one_argument( argument, arg2 );
    one_argument( argument, arg3 );

    if( arg[0] == '\0' )
    {
	send_to_char( "Buy what?\n\r", ch );
	return;
    }

    if( IS_SET( ch->in_room->room_flags, ROOM_PET_SHOP ) )
    {
	CHAR_DATA *pet;
	ROOM_INDEX_DATA *pRoomIndexNext;
	ROOM_INDEX_DATA *in_room;
	char buf[MAX_STRING_LENGTH];

	if( IS_NPC( ch ) )
	    return;

	pRoomIndexNext = get_room_index( ch->in_room->vnum + 1 );
	if( !pRoomIndexNext )
	{
	    bug( "Do_buy: bad pet shop at vnum %d.", ch->in_room->vnum );
	    send_to_char( "Sorry, you can't buy that here.\n\r", ch );
	    return;
	}

	in_room = ch->in_room;
	ch->in_room = pRoomIndexNext;
	pet = get_char_room( ch, arg );
	ch->in_room = in_room;

	if( !pet || !xIS_SET( pet->act, ACT_PET ) )
	{
	    send_to_char( "Sorry, you can't buy that here.\n\r", ch );
	    return;
	}

	if( xIS_SET( ch->act, PLR_BOUGHT_PET ) )
	{
	    send_to_char( "You already bought one pet this level.\n\r", ch );
	    return;
	}

	if( ch->gold < 10 * pet->level * pet->level )
	{
	    send_to_char( "You can't afford it.\n\r", ch );
	    return;
	}

	if( ch->level < pet->level )
	{
	    send_to_char( "You're not ready for this pet.\n\r", ch );
	    return;
	}

	ch->gold -= 10 * pet->level * pet->level;
	ch->in_room->area->economy += 10 * pet->level * pet->level;
	pet = create_mobile( pet->pIndexData );
	xSET_BIT( ch->act, PLR_BOUGHT_PET );
	xSET_BIT( pet->act, ACT_PET );
	xSET_BIT( pet->affected_by, AFF_CHARM );

	one_argument( argument, arg );

	if( arg[0] != '\0' )
	{
	    sprintf( buf, "%s %s", pet->name, arg );
	    free_string( pet->name );
	    pet->name = str_dup( buf );
	}

	sprintf( buf, "%sA neck tag says 'I belong to %s'.\n\r",
		 pet->description, ch->name );
	free_string( pet->description );
	pet->description = str_dup( buf );

	char_to_room( pet, ch->in_room );
	add_follower( pet, ch );
	send_to_char( "Enjoy your pet.\n\r", ch );
	act( "$n bought $N as a pet.", ch, NULL, pet, TO_ROOM );
	return;
    }
    else
    {
	OBJ_DATA *obj;
	CHAR_DATA *keeper;
	int cost;
	int item_count = 1;	/* buy only one by default */
	int *gold;

	if( is_number_special( arg ) )
	{
	    item_count = atoi_special( arg );
	    strcpy( arg, arg2 );
	    strcpy( arg2, arg3 );
	}

	if( !( keeper = find_keeper( ch, arg2 ) ) )
	    return;

	if( xIS_SET( keeper->act, ACT_QUESTMASTER ) )
	{
	    if( IS_NPC( ch ) )
		return;
	    gold = &ch->pcdata->quest->score;
	}
	else
	    gold = &ch->gold;

	obj = get_obj_carry( keeper, arg );
	cost = get_cost( keeper, ch, obj, TRUE );

	if( cost <= 0 || !can_see_obj( ch, obj ) )
	{
	    act( "&B$n tells you 'I don't sell that -- try 'list''.",
		 keeper, NULL, ch, TO_VICT );
	    ch->reply = keeper;
	    return;
	}

	if( item_count < 1 )
	{
	    send_to_char( "Buy how many?  Number must be more than 0.\n\r",
			  ch );
	    return;
	}

	if( *gold < ( cost * item_count ) )
	{
	    if( item_count == 1 )
	    {
		act( "&B$n tells you 'You can't afford to buy $p'.",
		     keeper, obj, ch, TO_VICT );
		ch->reply = keeper;
		return;
	    }
	    else
	    {
		char buf[MAX_STRING_LENGTH];

		if( ( *gold / cost ) > 0 )
		    sprintf( buf,
			     "$n tells you, 'You can only afford %d of those!'",
			     ( *gold / cost ) );
		else
		    sprintf( buf, "$n tells you, 'You can't even afford 1'." );

		act( buf, keeper, obj, ch, TO_VICT );
		ch->reply = keeper;
		return;
	    }
	}

	if( obj->level > ch->level + 3 )
	{
	    act( "$n tells you 'You can't use $p yet'.",
		 keeper, obj, ch, TO_VICT );
	    ch->reply = keeper;
	    return;
	}

	if( ch->carry_number + ( item_count * get_obj_number( obj ) ) >
	    can_carry_n( ch ) )
	{
	    send_to_char( "You can't carry that many items.\n\r", ch );
	    return;
	}

	if( ch->carry_weight + ( item_count * get_obj_weight( obj ) ) >
	    can_carry_w( ch ) )
	{
	    send_to_char( "You can't carry that much weight.\n\r", ch );
	    return;
	}

	if( ( item_count > 1 )
	    && !IS_SET( obj->extra_flags, ITEM_INVENTORY ) )
	{
	    act( "$n tells you, 'Sorry - $p is something I have only one of'",
		 keeper, obj, ch, TO_CHAR );
	    ch->reply = keeper;
	    return;
	}
	{
	    char buf[MAX_STRING_LENGTH];

	    if( item_count == 1 )
	    {
		sprintf( buf, "&gYou buy $p for &y%s&g %s.",
			 int_to_str_special( cost ),
			 xIS_SET( keeper->act, ACT_QUESTMASTER )
			 ? "quest points" : "gold" );
		act( buf, ch, obj, NULL, TO_CHAR );
		act( "&g$n buys $p.", ch, obj, NULL, TO_ROOM );
	    }
	    else
	    {
		sprintf( buf, "$n buys %d * $p.", item_count );
		act( buf, ch, obj, NULL, TO_ROOM );
		sprintf( buf, "You buy %d * $p @ %s %s each.", item_count,
			 int_to_str_special( cost ),
			 xIS_SET( keeper->act, ACT_QUESTMASTER )
			 ? "quest points" : "gold");
		act( buf, ch, obj, NULL, TO_CHAR );
	    }
	}
	*gold -= cost * item_count;
	if( !xIS_SET( keeper->act, ACT_QUESTMASTER ) )
	    ch->in_room->area->economy += cost * item_count;

	if( IS_SET( obj->extra_flags, ITEM_INVENTORY ) )
	    for( ; item_count > 0; item_count-- )
	    {
		OBJ_DATA *sell, *content;
		sell = create_object( obj->pIndexData, obj->level );
		obj_to_char( sell, ch );
		for( content = obj->contains; content;
		     content = content->next_content )
		    if( !content->deleted )
			obj_to_obj( create_object( content->pIndexData, content->level ), sell );
	    }
	else
	{
	    obj_from_char( obj );
	    obj_to_char( obj, ch );
	}

	return;
    }
}


void do_list( CHAR_DATA *ch, const char *argument )
{
    BUFFER *outbuf = buffer_new( MAX_STRING_LENGTH );

    if( IS_SET( ch->in_room->room_flags, ROOM_PET_SHOP ) )
    {
	CHAR_DATA *pet;
	ROOM_INDEX_DATA *pRoomIndexNext;
	bool found;

	pRoomIndexNext = get_room_index( ch->in_room->vnum + 1 );
	if( !pRoomIndexNext )
	{
	    bug( "Do_list: bad pet shop at vnum %d.", ch->in_room->vnum );
	    send_to_char( "You can't do that here.\n\r", ch );
	    return;
	}

	found = FALSE;
	for( pet = pRoomIndexNext->people; pet; pet = pet->next_in_room )
	{
	    if( IS_NPC( pet ) && xIS_SET( pet->act, ACT_PET ) )
	    {
		if( !found )
		{
		    found = TRUE;
		    buffer_strcat( outbuf, "&bPets for sale:\n\r" );
		}
		bprintf( outbuf, "&b[&m%2d&b] &y%8d&b - &c%s&n\n\r",
			 pet->level,
			 10 * pet->level * pet->level,
			 pet->short_descr );
	    }
	}
	if( !found )
	    send_to_char( "Sorry, we're out of pets right now.\n\r", ch );

	{
	    char * p;
	    p = outbuf->data;
	    while( ( p = strstr( p, "&x" ) ) )
		*(p + 1) = 'c';
	}

	send_to_char( outbuf->data, ch );
	buffer_free( outbuf );
	return;
    }
    else
    {
	OBJ_DATA *obj;
	CHAR_DATA *keeper;
	char arg[MAX_INPUT_LENGTH];
	char arg2[MAX_INPUT_LENGTH];
	int cost;
	bool found;

	argument = one_argument( argument, arg );
	one_argument( argument, arg2 );

	if( !( keeper = find_keeper( ch, arg2 ) ) )
	    return;

	found = FALSE;
	for( obj = keeper->carrying; obj; obj = obj->next_content )
	{
	    if( obj->wear_loc != WEAR_NONE
		|| ( cost = get_cost( keeper, NULL, obj, TRUE ) ) < 0 )
		continue;

	    if( can_see_obj( ch, obj )
		&& ( arg[0] == '\0'
		     || !str_cmp( arg, "all" )
		     || is_obj_name( obj, arg ) ) )
	    {
		if( !found )
		{
		    found = TRUE;
		    buffer_strcat( outbuf, "&b[&mLvl &rPrice&b]&y Item\n\r" );
		}

		bprintf( outbuf, "&b[&m%3d &r%5s&b]&y %s.&n\n\r",
			 obj->level, int_to_str_special( cost ),
			 capitalize( obj->short_descr ) );
	    }
	}

	if( !found )
	{
	    if( arg[0] == '\0' )
		send_to_char( "You can't buy anything here.\n\r", ch );
	    else
		send_to_char( "You can't buy that here.\n\r", ch );
	    return;
	}

	{
	    char * p;
	    p = outbuf->data;
	    while( ( p = strstr( p, "&x" ) ) )
		*(p + 1) = 'y';
	}

	send_to_char( outbuf->data, ch );
	buffer_free( outbuf );
	return;
    }
}


void do_sell( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj, *invobj;
    CHAR_DATA *keeper;
    char buf[MAX_STRING_LENGTH];
    char arg[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    int cost;

    argument = one_argument( argument, arg );
    one_argument( argument, arg2 );

    if( arg[0] == '\0' )
    {
	send_to_char( "Sell what?\n\r", ch );
	return;
    }

    if( !( keeper = find_keeper( ch, arg2 ) ) )
	return;

    if( !( obj = get_obj_carry( ch, arg ) ) )
    {
	act( "&B$n tells you 'You don't have that item'.",
	    keeper, NULL, ch, TO_VICT );
	ch->reply = keeper;
	return;
    }

    if( !can_drop_obj( ch, obj ) )
    {
	send_to_char( "You can't let go of it.\n\r", ch );
	return;
    }

    if( !can_see_obj( keeper, obj ) )
    {
	act( "&B$n tells you 'I can't see that item'.",
	    keeper, NULL, ch, TO_VICT );
	ch->reply = keeper;
	return;
    }

    if( ( cost = get_cost( keeper, ch, obj, FALSE ) ) <= 0
	|| obj->level > LEVEL_HERO )
    {
	act( "$n looks uninterested in $p.", keeper, obj, ch, TO_VICT );
	return;
    }

    if( IS_SET( obj->extra_flags, ITEM_POISONED ) )
    {
	act( "&B$n tells you 'I won't buy that!	 It's poisoned!'",
	    keeper, NULL, ch, TO_VICT );
	ch->reply = keeper;
	return;
    }

    if( IS_SET( obj->extra_flags, ITEM_OWNER ) )
    {
	act( "&B$n tells you 'That item would be of no use to me'",
	    keeper, NULL, ch, TO_VICT );
	ch->reply = keeper;
	return;
    }

    sprintf( buf, "&gYou sell $p for &y%s&g gold piece%s.",
	    int_to_str_special( cost ), cost == 1 ? "" : "s" );
    act( buf, ch, obj, NULL, TO_CHAR );
    act( "&g$n sells $p.", ch, obj, NULL, TO_ROOM );
    ch->gold += cost;
    ch->in_room->area->economy -= cost;

    if( obj->item_type == ITEM_TRASH )
    {
	extract_obj( obj );
    }
    else
    {
	int count = 0;
	obj_from_char( obj );

	for( invobj = keeper->carrying; invobj; invobj = invobj->next_content )
	{
	    if( invobj->pIndexData == obj->pIndexData )
		++count;
	}
	if( count >= 3 )
	    extract_obj( obj );
	else
	    obj_to_char( obj, keeper );
    }

    return;
}


void do_value( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    CHAR_DATA *keeper;
    char buf[MAX_STRING_LENGTH];
    char arg[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    int cost;

    argument = one_argument( argument, arg );
    one_argument( argument, arg2 );

    if( arg[0] == '\0' )
    {
	send_to_char( "Value what?\n\r", ch );
	return;
    }

    if( !( keeper = find_keeper( ch, arg2 ) ) )
	return;

    if( !( obj = get_obj_carry( ch, arg ) ) )
    {
	act( "&B$n tells you 'You don't have that item'.",
	    keeper, NULL, ch, TO_VICT );
	ch->reply = keeper;
	return;
    }

    if( !can_drop_obj( ch, obj ) )
    {
	send_to_char( "You can't let go of it.\n\r", ch );
	return;
    }

    if( !can_see_obj( keeper, obj ) )
    {
	act( "&B$n tells you 'You are offering me an imaginary object!?!?'.",
	    keeper, NULL, ch, TO_VICT );
	ch->reply = keeper;
	return;
    }

    if( ( cost = get_cost( keeper, ch, obj, FALSE ) ) <= 0 )
    {
	act( "$n looks uninterested in $p.", keeper, obj, ch, TO_VICT );
	return;
    }

    if( IS_SET( obj->extra_flags, ITEM_POISONED ) )
    {
	act( "&B$n tells you 'I won't buy that!	 It's poisoned!'",
	    keeper, NULL, ch, TO_VICT );
	ch->reply = keeper;
	return;
    }

    if( IS_SET( obj->extra_flags, ITEM_OWNER ) )
    {
	act( "&B$n tells you 'That would be of no use to me'",
	    keeper, NULL, ch, TO_VICT );
	ch->reply = keeper;
	return;
    }

    sprintf( buf, "&B$n tells you 'I'll give you %d gold coins for $p'.", cost );
    act( buf, keeper, obj, ch, TO_VICT );
    ch->reply = keeper;

    return;
}


/*
 * Poison weapon by Thelonius for EnvyMud
 */
void do_poison_weapon( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    OBJ_DATA *pobj;
    OBJ_DATA *wobj;
    char arg[MAX_INPUT_LENGTH];
    int timer;

    /*
     * Don't allow mobs or unskilled pcs to do this
     */
    if( IS_NPC( ch ) || ( !IS_NPC( ch )
			  && !can_use( ch, gsn_poison_weapon ) ) )
    {
	send_to_char( "What do you think you are, a thief?\n\r", ch );
	return;
    }

    one_argument( argument, arg );

    if( arg[0] == '\0' )
    {
	send_to_char( "What are you trying to poison?\n\r", ch );
	return;
    }
    if( ch->fighting )
    {
	send_to_char( "While you're fighting?  Nice try.\n\r", ch );
	return;
    }
    if( !( obj = get_obj_carry( ch, arg ) ) )
    {
	send_to_char( "You do not have that weapon.\n\r", ch );
	return;
    }
    if( obj->item_type != ITEM_WEAPON )
    {
	send_to_char( "That item is not a weapon.\n\r", ch );
	return;
    }
    if( IS_OBJ_STAT( obj, ITEM_POISONED ) )
    {
	send_to_char( "That weapon is already poisoned.\n\r", ch );
	return;
    }

    /*
     * Now we have a valid weapon...check to see if we have the powder.
     */
    for( pobj = ch->carrying; pobj; pobj = pobj->next_content )
    {
	if( pobj->pIndexData->vnum == OBJ_VNUM_BLACK_POWDER )
	    break;
    }
    if( !pobj )
    {
	send_to_char( "You do not have the black poison powder.\n\r", ch );
	return;
    }

    /*
     * Okay, we have the powder...do we have water?
     */
    for( wobj = ch->carrying; wobj; wobj = wobj->next_content )
    {
	if( wobj->item_type == ITEM_DRINK_CON
	    && wobj->value[1] > 0
	    && wobj->value[2] == 0 )
	    break;
    }
    if( !wobj )
    {
	send_to_char( "You have no water to mix with the powder.\n\r", ch );
	return;
    }

    /*
     * Great, we have the ingredients...but is the thief smart enough?
     */
    if( !IS_NPC( ch ) && get_curr_wis( ch ) < 19 )
    {
	send_to_char( "You can't quite remember what to do...\n\r", ch );
	return;
    }
    /*
     * And does the thief have steady enough hands?
     */
    if( !IS_NPC( ch )
	&& ( get_curr_dex( ch ) < 20
	    || ch->pcdata->condition[COND_DRUNK] > 0 ) )
    {
	send_to_char(
	    "Your hands aren't steady enough to properly mix the poison.\n\r",
			ch );
	return;
    }

    WAIT_STATE( ch, skill_table[gsn_poison_weapon].beats );

    /*
     * Check the skill percentage
     */
    if( !IS_NPC( ch )
	&& number_percent( ) > ch->pcdata->learned[gsn_poison_weapon] )
    {
	send_to_char( "You failed and spill some on yourself.  Ouch!\n\r",
		     ch );
	damage( ch, ch, ch->level, gsn_poison_weapon, WEAR_NONE );
	act( "$n spills the poison all over!", ch, NULL, NULL, TO_ROOM );
	extract_obj( pobj );
	extract_obj( wobj );
	return;
    }

    /*
     * Well, I'm tired of waiting.  Are you?
     */
    act( "You mix $p in $P, creating a deadly poison!",
	ch, pobj, wobj, TO_CHAR );
    act( "$n mixes $p in $P, creating a deadly poison!",
	ch, pobj, wobj, TO_ROOM );
    act( "You pour the poison over $p, which glistens wickedly!",
	ch, obj, NULL, TO_CHAR );
    act( "$n pours the poison over $p, which glistens wickedly!",
	ch, obj, NULL, TO_ROOM );
    SET_BIT( obj->extra_flags, ITEM_POISONED );
    obj->cost *= ch->level;

    /*
     * Set an object timer.  Don't want proliferation of poisoned weapons
     */
    timer = 10 + ch->level;

    if( IS_OBJ_STAT( obj, ITEM_BLESS ) )
	timer *= 2;
    if( IS_OBJ_STAT( obj, ITEM_MAGIC ) )
	timer *= 2;

    set_timer_tick( obj, timer );

    /*
     * WHAT?  All of that, just for that one bit?  How lame. ; )
     */
    act( "The remainder of the poison eats through $p.",
	ch, wobj, NULL, TO_CHAR );
    act( "The remainder of the poison eats through $p.",
	ch, wobj, NULL, TO_ROOM );
    extract_obj( pobj );
    extract_obj( wobj );

    return;
}


/*
 * Contributed by BoneCrusher of EnvyMud.
 */
void do_donate( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *container;
    OBJ_DATA *obj;
    OBJ_DATA *obj_next;

    if( argument [0] == '\0' )
    {
	send_to_char( "Donate what?\n\r", ch );
	return;
    }

    if( ( container = get_obj_world( ch, "donation" ) ) == NULL )
    {
	send_to_char( "The donation pit is missing from the world.\n\r", ch );
	return;
    }

    if( str_cmp( argument, "all" ) && str_prefix( "all.", argument  ) )
    {
	if( ( obj = get_obj_carry( ch, argument  ) ) == NULL )
	{
	    send_to_char( "You do not have that item.\n\r", ch );
	    return;
	}

	if( !can_drop_obj( ch, obj ) )
	{
	    send_to_char( "You can't let go of it.\n\r", ch );
	    return;
	}

	if( get_obj_weight( obj ) + get_obj_weight( container )
	    > container->value[0] )
	{
	    send_to_char( "It won't fit.\n\r", ch );
	    return;
	}

	if( obj->item_type == ITEM_TRASH
	    || obj->item_type == ITEM_FOOD
	    || obj->item_type == ITEM_KEY
	    || obj->item_type == ITEM_PILL )
	{
	    act( "You send $p flying to the $P.", ch, obj, container,
		 TO_CHAR );
	    extract_obj( obj );
	    return;
	}

	obj_from_char( obj );
	obj_to_obj( obj, container );
	act( "$n sends $p flying to the $P.", ch, obj, container, TO_ROOM );
	act( "You send $p flying to the $P.", ch, obj, container, TO_CHAR );
	send_to_room( "A loud clank is heard from the pit!",
		      container->in_room );
    }
    else
    {
	for( obj = ch->carrying; obj; obj = obj_next )
	{
	    obj_next = obj->next_content;

	    if( ( argument[3] == '\0' || is_obj_name( obj, &argument[4] ) )
		&& can_see_obj( ch, obj )
		&& obj->wear_loc == WEAR_NONE
		&& obj != container
		&& can_drop_obj( ch, obj )
		&& get_obj_weight( obj ) + get_obj_weight( container )
		<= container->value[0] )
	    {

		if( obj->item_type == ITEM_TRASH
		    || obj->item_type == ITEM_FOOD
		    || obj->item_type == ITEM_KEY
		    || obj->item_type == ITEM_PILL )
		{
		    act( "You send $p flying to the $P.", ch, obj, container,
			 TO_CHAR );
		    extract_obj( obj );
		    continue;
		}

		obj_from_char( obj );
		obj_to_obj( obj, container );
		act( "$n sends $p flying to the $P.", ch, obj, container,
		     TO_ROOM );
		act( "You send $p flying to the $P.", ch, obj, container,
		     TO_CHAR );
		send_to_room( "A loud clank is heard from the pit!",
			      container->in_room );
	    }
	}
    }

    return;
}


void do_carve( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *cor = get_obj_here( ch, argument );
    OBJ_DATA *content, *next;
    char buf[100];

    if( IS_NPC( ch ) || !can_use( ch, gsn_carve ) )
    {
	send_to_char( "Maybe you should learn how first.", ch );
	return;
    }
    if( !cor || cor->item_type != ITEM_CORPSE_NPC )
    {
	send_to_char( "You can't carve that.\n\r", ch );
	return;
    }
    if( ( !( content = get_eq_char( ch, WEAR_WIELD_R ) )
	  || content->value[3] != 1 )
	&& ( !( content = get_eq_char( ch, WEAR_WIELD_DOUBLE ) )
	     || content->value[3] != 1 ) )
    {
	send_to_char( "You need to wield a slicing weapon.\n\r", ch );
	return;
    }
    WAIT_STATE( ch, skill_table[gsn_carve].beats );
    for( content = cor->contains; content && content != cor; content = next )
    {
	next = content->next_content;
	obj_from_obj( content );
	obj_to_room( content, ch->in_room );
    }
    if( get_success( ch, gsn_carve, 85 ) )
    {
	if( number_bits( 2 ) == 0 )
	{
	    act( "$n wildy savages $p, sending chunks flying!",
		 ch, cor, NULL, TO_ROOM );
	    send_to_char( "You attack the corpse and "
			  "large meaty chunks fly about the room!\n\r", ch );
	}
	else
	{
	    act( "$n carves $p into nice steaks.", ch, cor, NULL, TO_ROOM );
	    send_to_char( "You carve it up neatly into good meat.\n\r", ch );
	}
	content = create_object( get_obj_index( OBJ_VNUM_MUSHROOM ), cor->level );
	free_string( content->name );
	sprintf( buf, "steaks %s", race_table[cor->value[0]].name );
	content->name = str_dup( buf );
	free_string( content->short_descr );
	sprintf( buf, "some %s steaks", race_table[cor->value[0]].name );
	content->short_descr = str_dup( capitalize( buf ) );
	free_string( content->description );
	sprintf( buf, "A pile of %s steaks look very tasty.",
		 race_table[cor->value[0]].name );
	content->description = str_dup( capitalize( buf ) );
	obj_to_room( content, ch->in_room );
	set_timer_tick( content, dice( 5, 6 ) );
	content->value[0] = number_range( cor->level, cor->level * 2 );
    }
    else
    {
	act( "$n carves $P into a horrible mess.", ch, cor, NULL, TO_ROOM );
	send_to_char( "You try to carve it but mess up.\n\r", ch );
    }
    free_string( cor->name );
    cor->name = str_dup( "mess rotten" );
    free_string( cor->short_descr );
    cor->short_descr = str_dup( "a rotten mess" );
    free_string( cor->description );
    cor->description = str_dup( "A mangled heap sits here smelling slightly." );
    cor->item_type = ITEM_TRASH;
    REMOVE_BIT( cor->wear_flags, ITEM_TAKE );
    cor->extra_flags = 0;
    set_timer_tick( cor, dice( 5, 6 ) );

    return;
}


#define NOT_ALLOWED "destroy cursed remove hex"
void do_engrave( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    AFFECT_DATA *paf, *oaf;
    char arg1[MAX_INPUT_LENGTH];
    int sn, level, chance, i, cost[MAGIC_MAX + 1];

    if( IS_NPC( ch ) || !can_use( ch, gsn_engrave ) )
    {
	send_to_char( "You lack the skills and initiative.\n\r", ch );
	return;
    }
    argument = one_argument( argument, arg1 );
    if( argument[0] == '\0' || arg1[0] == '\0' )
    {
	send_to_char( "Engrave what item with which spell?\n\r", ch );
	return;
    }
    obj = get_obj_carry( ch, arg1 );
    if( !obj || obj->wear_loc != WEAR_NONE )
    {
	send_to_char( "You aren't carrying that item.\n\r", ch );
	return;
    }
    if( IS_SET( obj->extra_flags, ITEM_RUNED ) ||
	( obj->item_type != ITEM_ARMOUR && obj->item_type != ITEM_WEAPON ) )
    {
	send_to_char( "You can't engrave on that item.\n\r", ch );
	return;
    }
    sn = skill_lookup( argument );
    if( sn <= 0 || !can_use( ch, sn )
	|| ( skill_table[sn].target != TAR_CHAR_OFFENSIVE
	     && skill_table[sn].target != TAR_CHAR_DEFENSIVE
	     && skill_table[sn].target != TAR_CHAR_SELF )
	|| !str_infix( skill_table[sn].name, NOT_ALLOWED ) )
    {
	send_to_char( "You can't engrave that spell.\n\r", ch );
	return;
    }
    if( ( skill_table[sn].target == TAR_CHAR_OFFENSIVE
	  && obj->item_type != ITEM_WEAPON ) ||
	skill_table[sn].spell_fun == spell_null )
    {
	send_to_char( "Such a thing is not possible.\n\r", ch );
	return;
    }

    /* how many levels has this person had the skill */
    level = skill_table[sn].skill_level[ch->class];
    for( i = 0; i < NUM_MULTI_CLASS && level > ch->level; ++i )
    {
	if( ch->pcdata->multi_class[i] >= CLASS_ADEPT
	    && skill_table[sn].skill_level[i] < level )
	    level = skill_table[sn].skill_level[i];
    }
    level = ch->level - level;
    for( i = 0; i < NUM_MULTI_CLASS && level > ch->level; ++i )
    {
	if( ch->pcdata->multi_class[i] == CLASS_ASPIRING
	    && level > ch->sublevel - skill_table[sn].skill_level[i] )
	    level = ch->sublevel - skill_table[sn].skill_level[i];
    }
    if( ++level <= 0 )
    {
	send_to_char( "You have no chance of doing that.\n\r", ch );
	return;
    }
    /* mana cost */
    for( i = 0; i < MAGIC_MAX; ++i )
    {
	cost[i] = 3 * mana_cost( ch, sn, i );
       /* chance += cost[i]; */
	if( !IS_NPC( ch ) && ch->mana[i] < cost[i] )
	{
	    send_to_char( "You don't have enough mana.\n\r", ch );
	    return;
	}
    }
    cost[MAGIC_MAX] = 3 * mana_cost( ch, sn, MAGIC_MAX );
    if( total_mana( ch->mana ) - total_mana( cost ) < cost[MAGIC_MAX] )
    {
	send_to_char( "You don't have enough mana.\n\r", ch );
	return;
    }
    for( i = 0; i < MAGIC_MAX; ++i )
	ch->mana[i] -= cost[i];
    take_generic_mana( ch, cost[MAGIC_MAX] );

    /* chance of success */
    chance = get_success( ch, gsn_engrave, 250 );
    chance += get_success( ch, sn, 250 );
    chance += ch->level - obj->level;
    chance = UMAX( 1, 195 - number_range( 0, chance ) );
    SET_BIT( obj->extra_flags, ITEM_RUNED );
    obj->level += UMIN( ch->level - obj->level, 1 );
    if( chance < number_fuzzy( level ) || IS_IMMORTAL( ch ) )
    {
	ch->pcdata->learned[sn] = UMAX( 0, ch->pcdata->learned[sn]
					- class_table[ch->class].skill_adept );
	if( skill_table[sn].target == TAR_CHAR_OFFENSIVE
	    || !str_cmp( skill_table[sn].name, "dispel magic" ) )
	{
	    obj->value[0] = sn;
	}
	else
	{
	    affect_strip( ch, sn );
	    ( *skill_table[sn].spell_fun )
		( sn, ch->level, ch, (void *)ch );
	    for( paf = ch->affected; paf; paf = paf->next )
	    {
		if( paf->type != sn )
		    continue;
		oaf = alloc_perm( sizeof( *oaf ) );
		oaf->type = sn;
		oaf->duration = -1;
		oaf->location = paf->location;
		oaf->modifier = paf->modifier;
		vcopy( oaf->bitvector, paf->bitvector );
		oaf->next = obj->affected;
		obj->affected = oaf;
	    }
	    affect_strip( ch, sn );
	}
    }
    act( "$n begins to engrave a spell on $p.", ch, obj, NULL, TO_ROOM );
    act( "You begin to engrave a spell on $p.", ch, obj, NULL, TO_CHAR );
    WAIT_STATE( ch, skill_table[gsn_engrave].beats );
    return;
}
#undef NOT_ALLOWED


void do_modify_armour( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;

    if( IS_NPC( ch ) || !can_use( ch, gsn_modify_armour ) )
    {
	send_to_char( "Learn how to do this first.\n\r", ch );
	return;
    }
    if( argument[0] == '\0' || !( obj = get_obj_carry( ch, argument ) ) )
    {
	send_to_char( "Modify which?\n\r", ch );
	return;
    }
    if( obj->item_type != ITEM_ARMOUR || obj->value[1] == -1 )
    {
	send_to_char( "You can't modify that.\n\r", ch );
	return;
    }
    if( obj->wear_loc != WEAR_NONE )
    {
	send_to_char( "Take it off first please.\n\r", ch );
	return;
    }
    if( IS_SET( obj->extra_flags, ITEM_FRAGILE ) )
    {
	send_to_char( "That item is far to brittle to modify.\n\r", ch );
	return;
    }
    WAIT_STATE( ch, skill_table[gsn_modify_armour].beats );
    if( number_range( 0, get_success( ch, gsn_modify_armour, 250 ) * 2 ) <= 1 )
	/* change constants to your discretion */
    {
	send_to_char( "You try as hard as you can but you stuff up.\n\r", ch );
	extract_obj( obj );
	return;
    }

    /* modify, at the moment there is a maximum size
       shift of 2 places allthough that can be changed if you like */
    if( obj->value[1] < get_size( ch ) - 10 )
    {
	act( "You work $p larger so it fits you better.",
	     ch, obj, NULL, TO_CHAR );
	act( "$n works $p larger so it fits $m better.",
	     ch, obj, NULL, TO_ROOM );
	obj->value[1] = UMIN( race_table[ch->race].size, obj->value[1] + 20 );
    }
    else if( obj->value[1] > get_size( ch ) + 10 )
    {
	act( "You work $p smaller so it fits you better.",
	     ch, obj, NULL, TO_CHAR );
	act( "$n works $p smaller so it fits $m better.",
	     ch, obj, NULL, TO_ROOM );
	obj->value[1] = UMAX( race_table[ch->race].size, obj->value[1] - 20 );
    }
    else
    {
	send_to_char( "No modifications were really necessary.\n\r", ch );
	obj->value[1] = race_table[ch->race].size;
    }

    if( number_bits( 4 ) == 0 && !get_success( ch, gsn_modify_armour, 30 )
	&& !IS_SET( obj->extra_flags, ITEM_FRAGILE ) )
    {
	act( "&r$p has become more fragile!", ch, obj, NULL, TO_CHAR );
	SET_BIT( obj->extra_flags, ITEM_FRAGILE );
    }
    if( number_bits( 2 ) == 0 )
	mod_item_condition( ch, obj, 1 );
    /* armour class is lowered slightly by the modification */
    if( ch->pcdata->learned[gsn_modify_armour] < 110 )
    {
	obj->value[0] *= ( ch->pcdata->learned[gsn_modify_armour] - 10 );
	obj->value[0] /= ( obj->level >= ch->level )
	    ? ( 100 + obj->level - ch->level )
	    : 100;
    }
    return;
}


void do_lighten( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    int chance;

    if( IS_NPC( ch ) || !can_use( ch, gsn_lighten ) )
    {
	send_to_char( "Learn how to do this first.\n\r", ch );
	return;
    }
    if( argument[0] == '\0' || !( obj = get_obj_carry( ch, argument ) ) )
    {
	send_to_char( "Modify which?\n\r", ch );
	return;
    }
    if( obj->wear_loc != WEAR_NONE )
    {
	send_to_char( "Take it off first please.\n\r", ch );
	return;
    }
    if( IS_SET( obj->extra_flags, ITEM_FRAGILE ) )
    {
	send_to_char( "That item is far to brittle to modify.\n\r", ch );
	return;
    }

    act( "$n begin$% work on $p, making it lighter.", ch, obj, NULL, TO_ALL );
    WAIT_STATE( ch, skill_table[gsn_lighten].beats );
    if( number_range( 0, get_success( ch, gsn_lighten, 250 ) * 4 ) <= 1 )
	/* change constants to your discretion */
    {
	act( "$n remove$% too much material and $p crumbles.",
		      ch, obj, NULL, TO_ALL );
	extract_obj( obj );
	return;
    }
    chance = obj->weight * 2 + ch->pcdata->learned[gsn_lighten];
    chance += ch->level - obj->level + obj->condition / 10;
    chance = dice( 2, chance ) / 2;
    if( number_bits( 3 ) == 0 && chance < 250
	&& !IS_SET( obj->extra_flags, ITEM_FRAGILE ) )
    {
	act( "&r$p has become more fragile!", ch, obj, NULL, TO_CHAR );
	SET_BIT( obj->extra_flags, ITEM_FRAGILE );
    }
    if( chance <= 180 )
    {
	mod_item_condition( ch, obj, 1 );
	if( obj->deleted )
	    return;
    }
    if( chance <= 100 || obj->weight <= 1 )
    {
	send_to_char( "You find it impossible to lighten the item without ruining it further.\n\r", ch );
	return;
    }
    chance = UMIN( chance - 100, 250 );
    chance /= 50;
    obj->weight *= 10;
    for( ; chance > 0; --chance )
	obj->weight = obj->weight * 4 / 5;
    obj->weight += 4;
    obj->weight /= 10;
    act( "You work $p so it becomes lighter.", ch, obj, NULL, TO_CHAR );
    act( "$n works $p so it becomes lighter.", ch, obj, NULL, TO_ROOM );

    return;
}


void do_sharpen( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    OBJ_DATA *stone;
    int chance;

    if( IS_NPC( ch ) || !can_use( ch, gsn_sharpen_weapon ) )
    {
	send_to_char( "Learn how to do this first.\n\r", ch );
	return;
    }
    if( argument[0] == '\0' || !( obj = get_obj_carry( ch, argument ) ) )
    {
	send_to_char( "Sharpen which?\n\r", ch );
	return;
    }
    if( ( !( stone = get_eq_char( ch, WEAR_HOLD_L ) )
	 || stone->pIndexData->vnum != OBJ_VNUM_WHETSTONE )
	&& ( !( stone = get_eq_char( ch, WEAR_HOLD_R ) )
	    || stone->pIndexData->vnum != OBJ_VNUM_WHETSTONE ) )
    {
	send_to_char( "You must be using a whetstone first.\n\r", ch );
	return;
    }
    if( obj->item_type != ITEM_WEAPON )
    {
	send_to_char( "You can't sharpen that.\n\r", ch );
	return;
    }
    if( obj->wear_loc != WEAR_NONE )
    {
	send_to_char( "Take it off first please.\n\r", ch );
	return;
    }
    if( obj->value[1] + 6 + obj->level / 40 >= obj->value[2] )
    {
	send_to_char( "That no longer needs any more sharpening.\n\r", ch );
	return;
    }
    if( IS_SET( obj->extra_flags, ITEM_FRAGILE ) )
    {
	send_to_char( "That weapon is far too brittle to sharpen.\n\r", ch );
	return;
    }

    act( "$n begins to sharpen $p.", ch, obj, NULL, TO_ROOM );
    stone->value[0] += dice( 1, 6 );
    if( stone->value[0] > 50 )
    {
	act( "$P has worn down to a useless sliver.",
	    ch, NULL, stone, TO_CHAR );
	act( "$P has worn down to a useless sliver.",
	    ch, NULL, stone, TO_ROOM );
	extract_obj( stone );
    }

    WAIT_STATE( ch, skill_table[gsn_sharpen_weapon].beats );
    chance = number_range( 0, get_success( ch, gsn_sharpen_weapon, 250 ) );
    chance += UMIN( 25, ch->level - obj->level );
    if( IS_SET( obj->extra_flags, ITEM_FRAGILE ) )
	chance = number_range( 0, chance );
    if( chance < 1 )
    {
	send_to_char( "You fumble and the weapon shatters.\n\r", ch );
	act( "$p is destroyed!", ch, obj, NULL, TO_ROOM );
	extract_obj( obj );
	return;
    }
    if( number_bits( 4 ) == 0 )
    {
	mod_item_condition( ch, obj, 1 );
	if( obj->deleted )
	    return;
    }
    if( number_bits( 5 ) == 0 && chance < 60
	&& !IS_SET( obj->extra_flags, ITEM_FRAGILE ) )
    {
	act( "&r$p has become more fragile!", ch, obj, NULL, TO_CHAR );
	SET_BIT( obj->extra_flags, ITEM_FRAGILE );
    }
    if( chance < 20 )
    {
	send_to_char( "Nothing much happens.\n\r", ch );
	return;
    }
    SET_BIT( obj->extra_flags, ITEM_SHARP );
    if( number_percent( ) == 99 )
    {
	act( "You give $p a deadly sharp edge!", ch, obj, NULL, TO_CHAR );
	act( "$n gives $p a deadly sharp edge!", ch, obj, NULL, TO_ROOM );
	obj->value[2] += number_fuzzy( 1 ) + ch->level / 50;
    }
    else
    {
	act( "You hone the edge on $p.", ch, obj, NULL, TO_CHAR );
	act( "$n hones the edge on $p.", ch, obj, NULL, TO_ROOM );
    }
    obj->value[1] = UMIN( obj->value[2] - 6 - obj->level / 40,
			  obj->value[1] + 2 + ch->level / 50
			  + ( obj->value[2] - obj->value[1] ) / 10 );
    return;
}


void do_call( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj, *in_obj;

    if( is_name( "mortician", ch->name ) )
    {
	obj = get_obj_world( ch, argument );
	if( !obj )
	{
	    send_to_char( "You can't find it.\n\r", ch );
	    return;
	}
	if( obj->carried_by == ch )
	{
	    send_to_char( "You have it.\n\r", ch );
	    return;
	}

	if( obj->in_room )
	    obj_from_room( obj );
	else if( obj->carried_by )
	{
	    if( obj->wear_loc != WEAR_NONE )
		remove_obj( obj->carried_by, obj->wear_loc, TRUE );
	    obj_from_char( obj );
	}
	else if( obj->in_obj )
	{
	    for( in_obj = obj->in_obj; in_obj; in_obj = in_obj->in_obj )
		;
	    if( in_obj->carried_by != ch )
		obj_from_obj( obj );
	}
	obj_to_room( obj, ch->in_room );
	strip_events( &obj->events, evn_imp_grab );

	act( "$n utters the words 'Ereh Yesproc'.", ch, obj, NULL, TO_ROOM );
	act( "The $p appears on the ground!", ch, obj, NULL, TO_ROOM );
	act( "You call for the $p, and it is dumped on the ground at your feet.", ch, obj, NULL, TO_CHAR );
	return;
    }

    if( IS_NPC( ch ) )
	return;
    if( !( *argument ) )
    {
	send_to_char( "You have to specify an item.\n\r", ch );
	return;
    }
    if( str_cmp( argument, "all" ) )
    {
	obj = get_obj_world( ch, argument );
	if( !obj )
	{
	    send_to_char( "You can't find it.\n\r", ch );
	    return;
	}
	if( !is_owner( ch, obj ) )
	{
	    send_to_char( "You can't call something you don't own.\n\r", ch );
	    return;
	}
	if( obj->carried_by == ch )
	{
	    send_to_char( "You have it.\n\r", ch );
	    return;
	}

	if( obj->in_room )
	    obj_from_room( obj );
	else if( obj->carried_by )
	{
	    if( obj->wear_loc != WEAR_NONE )
		remove_obj( obj->carried_by, obj->wear_loc, TRUE );
	    obj_from_char( obj );
	}
	else if( obj->in_obj )
	{
	    for( in_obj = obj->in_obj; in_obj && in_obj->in_obj;
		 in_obj = in_obj->in_obj )
		;
	    if( in_obj->carried_by != ch )
		obj_from_obj( obj );
	    else
	    {
		send_to_char( "It's in the bag.\n\r", ch );
		return;
	    }
	}
	obj_to_char( obj, ch );
	act( "$n calls and $p appears in $s hands.", ch, obj, NULL, TO_ROOM );
	act( "$p materialises in your hands.", ch, obj, NULL, TO_CHAR );
	return;
    }
    /* call all */
    for( obj = object_list; obj; obj = obj->next )
    {
	if( obj->deleted || obj->carried_by == ch )
	    continue;
	if( IS_SET( obj->extra_flags, ITEM_OWNER )
	    && is_owner( ch, obj ) )
	{
	    if( obj->in_room )
		obj_from_room( obj );
	    else if( obj->carried_by )
	    {
		if( obj->wear_loc != WEAR_NONE )
		    remove_obj( obj->carried_by, obj->wear_loc, TRUE );
		obj_from_char( obj );
	    }
	    else if( obj->in_obj )
	    {
		in_obj = obj->in_obj;
		while( in_obj && in_obj->in_obj )
		    in_obj = in_obj->in_obj;
		if( in_obj->carried_by != ch )
		    obj_from_obj( obj );
		else
		    continue;
	    }
	    obj_to_char( obj, ch );
	    act( "$n calls and $p appears in $s hands.", ch, obj, NULL, TO_ROOM );
	    act( "$p materialises in your hands.", ch, obj, NULL, TO_CHAR );
	}
    }
    return;
}


void do_bury( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;

    if( IS_NPC( ch ) || !can_use( ch, gsn_bury_item ) )
    {
	send_to_char( "You must learn how first.\n\r", ch );
	return;
    }

    if( ch->in_room->sector_type == SECT_AIR
	|| ch->in_room->sector_type == SECT_SPACE )
    {
	send_to_char( "Bury in WHAT?  You should smoke less of the weed man!\n\r",
		      ch );
	return;
    }
    if( ch->in_room->sector_type == SECT_WATER_SWIM
	|| ch->in_room->sector_type == SECT_WATER_NOSWIM
	|| ch->in_room->sector_type == SECT_UNDERWATER )
    {
	send_to_char( "You are in water, do you think you can dig at that?\n\r",
		      ch );
	return;
    }

    obj = get_obj_carry( ch, argument );
    if( !obj )
    {
	send_to_char( "Bury what exactly?\n\r", ch );
	return;
    }
    if( IS_SET( obj->extra_flags, ITEM_BURIED ) )
    {
	send_to_char( "It seems to be pretty well covered as it is.\n\r", ch );
	return;
    }

    WAIT_STATE( ch, 2 * PULSE_VIOLENCE );
    if( get_success( ch, gsn_bury_item, 100 ) == 0 )
    {
	send_to_char( "You failed to cover the item properly.\n\r", ch );
	return;
    }

    SET_BIT( obj->extra_flags, ITEM_BURIED );
    if( obj->carried_by )
	obj_from_char( obj );
    if( obj->in_obj )		/* just in case */
	obj_from_obj( obj );
    if( !obj->in_room )
    {
	obj_to_room( obj, ch->in_room );
	strip_events( &obj->events, evn_imp_grab );
    }
    act( "$n buries $p in the dirt.", ch, obj, NULL, TO_ROOM );
    send_to_char( "Ok.\n\r", ch );
    return;
}


void do_dig( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    CHAR_DATA *mob;
    static const char* const dig_messsage[SECT_MAX][2] = {
	{ "You start to break some floor boards when you dig.",
	  "$n starts to break some floor boards as $e starts digging."
	},
	{ "You lift a few cobble stones and digging into the packed earth beneath.",
	  "$n is making a mess of the street with $s digging."
	},
	{ "You make a nice hole while digging up a lot of dirt.",
	  "$n digs a hole and goes about $s business."
	},
	{ "You seem to be hitting alot of roots when you dig.",
	  "$n look like $e is trying to dig up a tree!"
	},
	{ "You dig up more clay than dirt here.",
	  "$n seems to be digging up alot of clay."
	},
	{ "You start to chip away at the rock here.",
	  "$n bangs away at the side of the mountain."
	},
	{ "You can't dig in the water!",
	  NULL
	},
	{ "You can't dig in the water!",
	  NULL
	},
	{ "You can't dig in the water!",
	  NULL
	},
	{ "You can't dig up air!",
	  NULL
	},
	{ "You start shovelling piles of sand.",
	  "$n begins to shovel piles of sand."
	},
	{ "You can't dig through a vacuum!",
	  NULL
	},
	{ "You dig a hole slowly as it continually fills with muddy water.",
	  "$n begins the futile task of digging a hole in the soggy ground.",
	}
    };

    if( IS_NPC( ch ) )
    {
	send_to_char( "You are unable to do this.\n\r", ch );
	return;
    }

    if( dig_messsage[ch->in_room->sector_type][0] != NULL )
	act( dig_messsage[ch->in_room->sector_type][0],
	     ch, NULL, NULL, TO_CHAR );
    if( dig_messsage[ch->in_room->sector_type][1] != NULL )
	act( dig_messsage[ch->in_room->sector_type][1],
	     ch, NULL, NULL, TO_ROOM );
    else
	return;

    WAIT_STATE( ch, 2 * PULSE_VIOLENCE );

    /*
     * Dig for an object.
     */
    for( obj = ch->in_room->contents; obj; obj = obj->next_content )
    {
	if( !obj->deleted && IS_SET( obj->extra_flags, ITEM_BURIED )
	    && ch->pcdata->learned[gsn_bury_item] + 50 > number_bits( 8 ) )
	    break;
    }
    if( obj )
    {
	REMOVE_BIT( obj->extra_flags, ITEM_BURIED );
	act( "$n dig$% $p up from the ground.", ch, obj, NULL, TO_ALL );
	if( IS_SET( obj->wear_flags, ITEM_TAKE ) )
	{
	    obj_from_room( obj );
	    obj_to_char( obj, ch );
	}
	return;
    }

    /*
     * Dig for a mob.
     */
    for( mob = ch->in_room->people; mob; mob = mob->next_in_room )
    {
	if( !mob->deleted && IS_NPC( mob )
	    && xIS_SET( mob->act, ACT_BURIED ) && number_bits( 3 ) < 6 )
	    break;
    }

    if( mob )
    {
	act( "$n reveal$% $N!", ch, NULL, mob, TO_ALL );
	xREMOVE_BIT( mob->act, ACT_BURIED );
	if( !xIS_SET( mob->act, ACT_WIMPY ) && !is_safe( mob, ch ) )
	{
	    act( "&r$N is not happy, $E lunges at $n!", ch, NULL, mob, TO_ALL );
	    multi_hit( mob, ch, TYPE_UNDEFINED );
	}
	return;
    }

    if( number_bits( 3 ) == 0 )
    {
	int race;
	MOB_INDEX_DATA *pMob;
	char buf[MAX_STRING_LENGTH];

	switch( number_bits( 4 ) )
	{
	case 0:	    case 1:	    case 2:
	case 3:	    case 4:
	    pMob = get_mob_index( MOB_VNUM_SKELETON );
	    break;
	default:
	    pMob = get_mob_index( MOB_VNUM_ZOMBIE );
	    break;
	case 12:	    case 13:	    case 14:
	    pMob = get_mob_index( MOB_VNUM_MUMMY );
	    break;
	case 15:
	    pMob = get_mob_index( MOB_VNUM_LICH );
	    break;
	}

	pMob->level += (ch->level + ch->in_room->area->min) / 2 - 5;
	mob = create_mobile( pMob );
	pMob->level += 5 - (ch->level + ch->in_room->area->min) / 2;
	mob->gold = 0;
	race = number_range( 0, MAX_RACE - 1 );
	mob->resil_mod = ( race_table[race].resil - 1000 ) / 2;

	sprintf( buf, mob->name, race_table[race].name );
	free_string( mob->name );
	mob->name = str_dup( buf );
	sprintf( buf, mob->short_descr, race_table[race].name );
	free_string( mob->short_descr );
	mob->short_descr = str_dup( buf );
	sprintf( buf, mob->long_descr, race_table[race].name );
	free_string( mob->long_descr );
	mob->long_descr = str_dup( buf );

	char_to_room( mob, ch->in_room );
	act( "Oh no, you have disturbed $N's rest!",
	     ch, NULL, mob, TO_CHAR );
	act( "$n rises from the ground and attacks!",
	     mob, NULL, NULL, TO_ROOM );
	multi_hit( mob, ch, TYPE_UNDEFINED );
	return;
    }
    send_to_char( "You dig but come up with nothing.\n\r", ch );
    return;
}


void do_prepare( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *seeds;
    char buf[MAX_INPUT_LENGTH];
    char arg1[MAX_INPUT_LENGTH];	/* spell1 name */
    int timer;

    argument = one_argument( argument, arg1 );

    if( !can_use( ch, gsn_herbalism ) )
    {
	send_to_char( "You can't do that.\n\r", ch );
	return;
    }
    if( !( *argument ) || !arg1[0] )
    {
	send_to_char( "Usage: prepare <spell-name> [spell-name]\n\r", ch );
	return;
    }
    seeds = get_eq_char( ch, WEAR_HOLD_L );
    if( !seeds || seeds->pIndexData->vnum != OBJ_VNUM_HERBS )
    {
	if( !( seeds = get_eq_char( ch, WEAR_HOLD_R ) )
	    || seeds->pIndexData->vnum != OBJ_VNUM_HERBS )
	{
	    send_to_char( "Sorry you aren't holding any seeds to plant.\n\r",
			  ch );
	    return;
	}
    }

    /* get the spells */
    seeds->value[1] = skill_lookup( arg1 );
    if( argument && *argument )
	seeds->value[2] = skill_lookup( argument );
    else
	seeds->value[2] = -1;
    if( seeds->value[1] <= 0 || ( *argument && seeds->value[2] < 0 ) )
    {
	send_to_char( "You have chosen a spell that doesn't exist.\n\r", ch );
	seeds->value[1] = -1;
	seeds->value[2] = -1;
	return;
    }
    if( !can_use( ch, seeds->value[1] )
	|| !can_use( ch, seeds->value[2] ) )
    {
	send_to_char( "You cannot prepare a spell that you dont know.\n\r",
		      ch );
	seeds->value[1] = -1;
	seeds->value[2] = -1;
	return;
    }

    /* chance of losing a spell from the pill */
    if( !get_success( ch, gsn_herbalism, 100 ) )
    {
	if( argument && *argument )
	{
	    seeds->value[1] = seeds->value[2];
	    seeds->value[2] = -1;
	}
	else
	    seeds->value[1] = -1;
    }
    if( !get_success( ch, gsn_herbalism, 100 ) )
	seeds->value[2] = -1;

    /* chance of adding poison to the pill */
    if( !get_success( ch, gsn_herbalism, 100 ) )
    {
	if( seeds->value[1] <= 0 )
	    seeds->value[1] = gsn_poison;
	else if( seeds->value[2] <= 0 )
	    seeds->value[2] = gsn_poison;
	else if( seeds->value[3] <= 0 )
	    seeds->value[3] = gsn_poison;
    }

    /* set all other values for the new/changed item */
    REMOVE_BIT( seeds->wear_flags, ITEM_TAKE );
    seeds->value[0] = number_fuzzy( ( seeds->level + ch->level ) / 2 );
/*  seeds->item_type = ITEM_PLANT; */
    timer = 200 - get_success( ch, gsn_herbalism, 250 );
    if( number_bits( 8 ) > timer && number_bits( 3 ) == 0 )
    {
	timer = number_range( 10, timer * 2 );
	create_obj_event( seeds, evn_plant_die, timer );
    }
    else
    {
	timer = number_range( timer * 2, timer * 3 );
	create_obj_event( seeds, evn_plant_grow, timer );
    }

    sprintf( buf, "plant %s", seeds->name );
    free_string( seeds->name );
    seeds->name = str_dup( buf );
    sprintf( buf, "a plant grown from %s", seeds->short_descr );
    free_string( seeds->short_descr );
    seeds->short_descr = str_dup( buf );
    strcpy( buf, "A plant is here, growing rapidly." );
    free_string( seeds->description );
    seeds->description = str_dup( buf );

    if( seeds->carried_by )
	obj_from_char( seeds );
    else if( seeds->in_room )
	obj_from_room( seeds );
    obj_to_room( seeds, ch->in_room );
    strip_events( &seeds->events, evn_imp_grab );

    act( "$n plants some seeds.", ch, NULL, NULL, TO_ROOM );
    send_to_char( "You prepare the seeds and plant them.\n\r", ch );

    return;
}


void do_harvest( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *tree;
    int num, i, skill;
    OBJ_DATA *pill;

    if( !can_use( ch, gsn_herbalism ) )
    {
	send_to_char( "You have little knowledge of herbalism.\n\r", ch );
	return;
    }
    if( !( *argument ) )
    {
	send_to_char( "Usage: harvest <plant>\n\r", ch );
	return;
    }
    tree = get_obj_here( ch, argument );
    if( !tree )
    {
	send_to_char( "You can't find it.\n\r", ch );
	return;
    }
    if( tree->item_type != ITEM_PLANT )
    {
	send_to_char( "It isn't yet ready for harvest.\n\r", ch );
	return;
    }
    num = UMIN( 25 + number_percent( ), 100 )
	+ UMIN( 10, ch->level - tree->level );
    skill = get_success( ch, gsn_herbalism, 150 );
    num = skill / num;
    if( num == 0 )
    {
	send_to_char( "You totally ruin the harvest and gain nothing.\n\r", ch );
	extract_obj( tree );
	return;
    }
    for( i = 0; i < num; ++i )
    {
	pill = create_object( get_obj_index( OBJ_VNUM_MAGIC_PILL ), ch->level - 5 );
	pill->value[0] = tree->value[0];
	pill->value[1] = tree->value[1];
	pill->value[2] = tree->value[2];
	pill->value[3] = tree->value[3];
	if( pill->value[0] > 0 )
	    SET_BIT( pill->extra_flags, ITEM_GLOW );
	if( number_percent( ) > skill )
	    SET_BIT( pill->extra_flags, ITEM_NODROP );
	obj_to_char( pill, ch );
    }
    act( "$n harvests $p for magical pills.", ch, tree, NULL, TO_ROOM );
    act( "You harvest $p and gain some pills.", ch, tree, NULL, TO_CHAR );
    extract_obj( tree );

    return;
}


void do_appraise( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    CHAR_DATA *smith;
    char arg[MAX_INPUT_LENGTH];
    int cost;

    if( argument[0] =='\0' )
    {
	send_to_char( "Appraise what?\n\r", ch );
	return;
    }

    argument = one_argument( argument, arg );
    if( !( smith = find_smith( ch, argument ) ) )
	return;
    if( !( obj = get_obj_here( ch, arg ) ) )
    {
	send_to_char( "You can't find it.\n\r", ch );
	return;
    }
    if( obj->condition >= 1000 )
    {
	send_to_char( "That item is in perfect condition.\n\r", ch );
	return;
    }
    if( IS_OBJ_STAT( obj, ITEM_FRAGILE ) )
    {
	send_to_char( "That item is far too fragile to repair.\n\r", ch );
	return;
    }
    if( !can_see_obj( smith, obj ) )
    {
	act( "&B$n tells you 'I can't see that item'.",
	     smith, NULL, ch, TO_VICT );
	ch->reply = smith;
	return;
    }
    if( ( cost = get_cost( smith, ch, obj, TRUE ) ) <= 0 )
    {
	act( "&B$n tells you 'I don't know how to repair $p'.",
	     smith, obj, ch, TO_VICT );
	return;
    }
    cost = cost * ( 1000 - obj->condition ) / 1000 + obj->level;

    sprintf( arg, "&B$n tells you '$p would cost &y%s&B gold piece%s to smith'.",
	     int_to_str_special( cost ), cost == 1 ? "" : "s" );
    act( arg, smith, obj, ch, TO_VICT );
    return;
}


void do_smith( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj, *obj_next;
    CHAR_DATA *smith;
    char arg[MAX_INPUT_LENGTH];
    int cost;

    if( argument[0] =='\0' )
    {
	send_to_char( "Smith what?\n\r", ch );
	return;
    }

    argument = one_argument( argument, arg );
    if( !( smith = find_smith( ch, argument ) ) )
	return;

    if( !str_cmp( arg, "all" ) || !str_prefix( arg, "all." ) )
    {
	for( obj = ch->carrying; obj; obj = obj_next )
	{
	    obj_next = obj->next_content;
	    if( obj->deleted || obj->condition >= 1000
		|| IS_OBJ_STAT( obj, ITEM_FRAGILE )
		|| ( arg[3] == '.' && !is_obj_name( obj, &arg[4] ) )
		|| !can_drop_obj( ch, obj )
		|| !can_see_obj( smith, obj ) )
		continue;
	    if( ( cost = get_cost( smith, ch, obj, TRUE ) ) <= 0 )
		continue;
	    cost = cost * ( 1000 - obj->condition ) / 1000 + obj->level;
	    if( cost > ch->gold )
	    {
		act( "&B$n tells you 'You can't afford to repair any more'.",
		     smith, obj, ch, TO_VICT );
		return;
	    }

	    sprintf( arg, "&gYou repair $p for &y%s&g gold piece%s.",
		     int_to_str_special( cost ), cost == 1 ? "" : "s" );
	    act( arg, ch, obj, NULL, TO_CHAR );
	    ch->gold -= cost;
	    ch->in_room->area->economy += cost;
	    obj->condition = 1000;
	    if( xIS_SET( obj->pIndexData->progtypes, REPAIR_PROG ) )
		oprog_percent_check( ch, obj, smith, REPAIR_PROG );
	}
	send_to_char( "All your gear is now in top condition.\n\r", ch );
    }
    else
    {
	if( !( obj = get_obj_here( ch, arg ) ) )
	{
	    send_to_char( "You can't find it.\n\r", ch );
	    return;
	}
	if( obj->condition >= 1000 )
	{
	    send_to_char( "That item is in perfect condition.\n\r", ch );
	    return;
	}
	if( IS_OBJ_STAT( obj, ITEM_FRAGILE ) )
	{
	    send_to_char( "That item is far too fragile to repair.\n\r", ch );
	    return;
	}
	if( !can_drop_obj( ch, obj ) )
	{
	    send_to_char( "You can't let go of it.\n\r", ch );
	    return;
	}
	if( !can_see_obj( smith, obj ) )
	{
	    act( "&B$n tells you 'I can't see that item'.",
		 smith, NULL, ch, TO_VICT );
	    ch->reply = smith;
	    return;
	}
	if( ( cost = get_cost( smith, ch, obj, TRUE ) ) <= 0 )
	{
	    act( "&B$n tells you 'I don't know how to repair $p'.",
		 smith, obj, ch, TO_VICT );
	    return;
	}
	cost = cost * ( 1000 - obj->condition ) / 1000;
	cost += obj->level * UMAX( 100, 1000 - obj->pIndexData->material )
	    / 100;
	if( cost > ch->gold )
	{
	    act( "&B$n tells you 'You can't afford my fee, sorry'.",
		 smith, obj, ch, TO_VICT );
	    return;
	}

	sprintf( arg, "&gYou repair $p for &y%s&g gold piece%s.",
		 int_to_str_special( cost ), cost == 1 ? "" : "s" );
	act( arg, ch, obj, NULL, TO_CHAR );
	ch->gold -= cost;
	ch->in_room->area->economy += cost;
	obj->condition = 1000;
	if( xIS_SET( obj->pIndexData->progtypes, REPAIR_PROG ) )
	    oprog_percent_check( ch, obj, smith, REPAIR_PROG );
    }
    return;
}


void do_repair( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj, *mat;
    char arg[MAX_INPUT_LENGTH];

    if( !can_use( ch, gsn_repair ) )
    {
	send_to_char( "Learn how first.\n\r", ch );
	return;
    }

    argument = one_argument( argument, arg );
    obj = get_obj_here( ch, arg );
    mat = get_obj_here( ch, argument );
    if( !obj || !mat )
    {
	send_to_char( "You can't find it.\n\r", ch );
	return;
    }
    if( obj->pIndexData->material != mat->pIndexData->material )
    {
	send_to_char( "You can't use that to repair with.\n\r", ch );
	return;
    }
    if( IS_OBJ_STAT( obj, ITEM_FRAGILE ) )
    {
	send_to_char( "That item is far too fragile to repair.\n\r", ch );
	return;
    }

    extract_obj( mat );
    if( !get_success( ch, gsn_repair, 100 ) )
    {
	act( "$n attempts to mend $p.", ch, obj, NULL, TO_ROOM );
	send_to_char( "You stuff up.\n\r", ch );
	if( get_success( ch, gsn_repair, 80 ) )
	    return;
	act( "$p is destroyed.", ch, obj, NULL, TO_ROOM );
	act( "$p is destroyed.", ch, obj, NULL, TO_CHAR );
	extract_obj( obj );
	return;
    }

    if( number_bits( 7 ) == 0 )
    {
	act( "Your attempts at repair also make $p more fragile.",
	     ch, obj, NULL, TO_CHAR );
	SET_BIT( obj->extra_flags, ITEM_FRAGILE );
    }

    obj->condition = 1000;
    act( "$n repairs $p.", ch, obj, NULL, TO_ROOM );
    act( "You repair $p.", ch, obj, NULL, TO_CHAR );
    WAIT_STATE( ch, skill_table[gsn_repair].beats );
    if( xIS_SET( obj->pIndexData->progtypes, REPAIR_PROG ) )
	oprog_percent_check( ch, obj, NULL, REPAIR_PROG );

    return;
}


void do_forage( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    int vnum;

    WAIT_STATE( ch, 2 * PULSE_VIOLENCE );
    if( !IS_SET( ch->in_room->room_flags, ROOM_FORAGE )
	|| !get_success( ch, gsn_forage, 75 ) )
    {
	send_to_char( "You look around, but you see nothing edible.\n\r", ch );
	return;
    }
    vnum = OBJ_VNUM_FORAGE_FIRST + ch->in_room->sector_type;
    vnum = URANGE( OBJ_VNUM_FORAGE_FIRST, vnum, OBJ_VNUM_FORAGE_LAST );
    obj = create_object( get_obj_index( vnum ), ch->level );
    obj->cost = 0;
    obj_to_char( obj, ch );
    act( "You forage for a while and come up with $P.",
	 ch, NULL, obj, TO_CHAR );
    WAIT_STATE( ch, PULSE_VIOLENCE * 3 );
    return;
}


void do_embalm( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *cor;
    OBJ_DATA *bandages;
    int sn;

    if( IS_NPC( ch ) || !can_use( ch, ( sn = skill_lookup( "embalm" ) ) ) )
    {
	send_to_char( "Learn how to do this first.\n\r", ch );
	return;
    }
    if( argument[0] == '\0' || !( cor = get_obj_here( ch, argument ) ) )
    {
	send_to_char( "Embalm which?\n\r", ch );
	return;
    }
    if( cor->item_type != ITEM_CORPSE_NPC
	&& cor->item_type != ITEM_CORPSE_PC )
    {
	send_to_char( "You can only embalm corpses.\n\r", ch );
	return;
    }
    if( cor->weight != 100 )
    {
	send_to_char( "This corpse cannot be embalmed.\n\r", ch );
	return;
    }
    if( !( bandages = has_key( ch, OBJ_VNUM_BANDAGES ) ) )
    {
	send_to_char( "You must have some bandages first.\n\r", ch );
	return;
    }
    WAIT_STATE( ch, skill_table[sn].beats );
    obj_from_char( bandages );
    extract_obj( bandages );
    act( "$n embalm$% $p.", ch, cor, NULL, TO_ALL );
    if( cor->carried_by )	/* should be carried by "ch" */
    {
	obj_from_char( cor );
	cor->weight -= UMIN( 100, get_success( ch, sn, 100 ) );
	obj_to_char( cor, ch );
    }
    else
	cor->weight -= get_success( ch, sn, 100 );
    if( get_time_left( cor->events, evn_obj_decay ) >= 0 )
    {
	if( cor->item_type == ITEM_CORPSE_PC )
	    strip_events( &cor->events, evn_obj_decay );
	else
	    set_timer_tick( cor, UMIN( 20, ch->level ) + dice( 4, 20 ) );
    }
    return;
}