dawn/notes/
dawn/src/
dawn/src/docs/
/**************************************************************************/
// act_obj.cpp - players performing actions relating to objects
/***************************************************************************
 * The Dawn of Time v1.69r (c)1997-2004 Michael Garratt                    *
 * >> A number of people have contributed to the Dawn codebase, with the   *
 *    majority of code written by Michael Garratt - www.dawnoftime.org     *
 * >> To use this source code, you must fully comply with all the licenses *
 *    in licenses.txt... In particular, you may not remove this copyright  *
 *    notice.                                                              *
 ***************************************************************************
 * >> Original Diku Mud copyright (c)1990, 1991 by Sebastian Hammer,       *
 *    Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, & Katja Nyboe.   *
 * >> Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael       *
 *    Chastain, Michael Quan, and Mitchell Tse.                            *
 * >> ROM 2.4 is copyright 1993-1995 Russ Taylor and has been brought to   *
 *    you by the ROM consortium: Russ Taylor(rtaylor@pacinfo.com),         *
 *    Gabrielle Taylor(gtaylor@pacinfo.com) & Brian Moore(rom@rom.efn.org) *
 * >> Oblivion 1.2 is copyright 1996 Wes Wagner                            *
 **************************************************************************/

#include "include.h" // dawn standard includes
#include "msp.h" 
#include "lockers.h"
#include "shop.h"

/* command procedures needed */
DECLARE_DO_FUN(do_split		);
DECLARE_DO_FUN(do_yell		);
DECLARE_DO_FUN(do_say		);
DECLARE_DO_FUN(do_stand		);
char *get_weapontype(OBJ_DATA *obj); /* prototype - handler.c */
bool trapcheck_get(char_data *ch, OBJ_DATA *obj);
SPRESULT spell_fear_magic( int sn, int level, char_data *ch, void *vo,int ); /* magic.cpp */

// Local functions.
#define CD char_data
#define OD OBJ_DATA
bool	remove_obj	args( (char_data *ch, int iWear, bool fReplace ) );
CD *	find_keeper	args( (char_data *ch ) );
int	get_cost	args( (char_data *keeper, OBJ_DATA *obj, bool fBuy ) );
void 	obj_to_keeper	args( (OBJ_DATA *obj, char_data *ch ) );
OD *	get_obj_keeper	args( (char_data *ch,char_data *keeper,char *argument));

#undef OD
#undef CD

bool can_loot(char_data *, OBJ_DATA *)
{
	return true;	
}

/**************************************************************************/
void get_obj( char_data *ch, OBJ_DATA *obj, OBJ_DATA *container)
{
    // variables for AUTOSPLIT 
    char_data *gch;
    int members;
    char buffer[100];

    if ( !CAN_WEAR(obj, OBJWEAR_TAKE) )
	{
		ch->println("You can't take that.");
		return;
    }

	if ( trapcheck_get( ch, obj ))
		return;

	if ( obj->item_type == ITEM_MONEY)
	{
		// check silver first 
		if ((get_carry_weight( ch ) +
			get_silver_weight(obj->value[0])) > can_carry_w( ch ) )
		{
			ch->println("You cannot carry that much weight.");
			return;
		}
		
		// now lets check if the player can carry that much gold 
		if ((get_carry_weight( ch ) +
			get_gold_weight(obj->value[1])) > can_carry_w( ch ) )
		{
			ch->println("You cannot carry that much weight.");
			return;
		}
	}

    if ( !IS_SWITCHED (ch) && (ch->carry_number + get_obj_number( obj ) > can_carry_n( ch )))
    {
        act( "$d: you can't carry that many items.",
            ch, NULL, obj->name, TO_CHAR );
        return;
    }


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

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

    if ( container != NULL ){
		if(container->pIndexData->vnum == OBJ_VNUM_PIT
			&&  !CAN_WEAR(container, OBJWEAR_TAKE)
			&&  !IS_OBJ_STAT(obj,OBJEXTRA_HAD_TIMER))
			obj->timer = 0;
		
        act( "You get $p from $P.", ch, obj, container, TO_CHAR );
        act( "$n gets $p from $P.", ch, obj, container, TO_ROOM );
		REMOVE_BIT(obj->extra_flags,OBJEXTRA_HAD_TIMER);
		obj_from_obj( obj );
    }else{
        act( "You get $p.", ch, obj, container, TO_CHAR );
        act( "$n gets $p.", ch, obj, container, TO_ROOM );
        obj_from_room( obj );
    }

	if ( obj->item_type == ITEM_MONEY)
    {
		ch->silver += obj->value[0];
		ch->gold += obj->value[1];
        if (IS_SET(ch->act,PLR_AUTOSPLIT))
        { // AUTOSPLIT code 
			members = 0;
			for (gch = ch->in_room->people; gch != NULL; gch = gch->next_in_room )
			{
				if (!IS_AFFECTED(gch,AFF_CHARM) && is_same_group( gch, ch ) )
					members++;
			}
			
			if ( members > 1 && (obj->value[0] > 1 || obj->value[1]))
			{
				sprintf(buffer,"%d %d",obj->value[0],obj->value[1]);
				do_split(ch,buffer);	
			}
        }

		if(!IS_NPC(ch)){
			if(obj->lastdrop_id!=ch->id
				&& !strcmp(obj->lastdrop_remote_ip,ch->remote_ip_copy))
			{
				multilog_alertf(ch, "`Y%s<%d>`x getting `Y%d gold `xand `S%d silver `xfrom somewhere "
					"(prob the ground), `M(room %d)`x, this was put there by someone `B(id=%d)`x other than %s but from the same "
					"ip address as `Y%s!`x", 
					ch->name, 
					ch->level,
					obj->value[1],
					obj->value[0],
					ch->in_room_vnum(),
					(int)obj->lastdrop_id,
					ch->name, 
					ch->name);
			}
		}
		extract_obj( obj );
	}else{
		// multilog abuse checks
		if(!IS_NPC(ch)){
			if(obj->lastdrop_id!=ch->id && 
				!strcmp(obj->lastdrop_remote_ip,ch->remote_ip_copy))
			{

				multilog_alertf(ch, 
					"`Y%s<%d>`x getting `Gobject `x'%s'<%d>(%d)`x which was originally put where it is being 'got from' (room %d)"
					"by `Bsomeone (id=%d)`x different than `Y%s`x, but from the same ip address as `Y%s!`x",
					ch->name, 
					ch->level,
					obj->short_descr,
					obj->level,
					obj->pIndexData?obj->pIndexData->vnum:0,
					ch->in_room_vnum(),
					(int)obj->lastdrop_id,
					ch->name, 
					ch->name);
			}
		}		
		obj_to_char( obj, ch );
    }
    return;
}

/************************************************************************/
// get a random object from the characters inventory
OBJ_DATA *get_random_obj( char_data* mob )
{

    OBJ_DATA *obj_next, *obj, *target=NULL;
    int now = 0, highest = 0;
		  
    for ( obj = mob->carrying; obj != NULL; obj = obj_next)
    {
          obj_next = obj->next_content;

          if ( obj->wear_loc != WEAR_NONE 
              || !can_drop_obj( mob, obj ) )
              continue;

          if ( ( now = number_percent() ) > highest )
          {
              target = obj;
              highest = now;
          }
     }

     return target;
}
/************************************************************************/
void donate(char_data *ch, OBJ_DATA *obj)
{
	if(!CAN_WEAR( obj, OBJWEAR_TAKE) || obj->item_type == ITEM_CORPSE_PC){
		act("You can't donate $p.", ch, obj,NULL, TO_CHAR);
		return;
	}

	int room_vnum=0;
	// confirm the room they are going to donate to exists
	if (obj->level < 11){
		if (obj->item_type == ITEM_WEAPON){
			room_vnum=ROOM_VNUM_NEWBIEWEAPON_DONATE;			
		}else if (obj->item_type == ITEM_ARMOR){
			room_vnum=ROOM_VNUM_NEWBIEARMOR_DONATE;			
		}else{
			room_vnum=ROOM_VNUM_NEWBIEMISC_DONATE;			
		}
	}else{
		if (obj->item_type == ITEM_WEAPON){
			room_vnum=ROOM_VNUM_WEAPON_DONATE;			
		}else if (obj->item_type == ITEM_ARMOR){
			room_vnum=ROOM_VNUM_ARMOR_DONATE;
		}else{
			room_vnum=ROOM_VNUM_MISC_DONATE;
		}
	}
	room_index_data *room=get_room_index(room_vnum);

	if(!room){
		act(FORMATF("Bug: couldn't find room %d to donate object %d ($p) "
			"- please inform the admin.", 
			room_vnum, obj->pIndexData?obj->pIndexData->vnum:-1), ch, obj,NULL, TO_CHAR);
		return;
	}
	
	ch->println("Your donation is greatly appreciated");
	ch->printlnf("%s disappears in a puff of smoke.", obj->short_descr);
	act( "$n donates $p.", ch, obj, NULL, TO_ROOM);

	obj_from_room(obj);	
	room_index_data *oldroom=ch->in_room;
	ch->in_room=room;
	
	SET_BIT(obj->extra2_flags,OBJEXTRA2_NOSELL);

	// find the pit in the room
	OBJ_DATA *pit;
	for( pit= room->contents; pit; pit= pit->next_content )
	{
		if(pit->pIndexData && pit->pIndexData->vnum==OBJ_VNUM_PIT){
			break;
		}
	}

	if(pit){
		act( "$p appears in $P.", ch, obj, pit, TO_ROOM);
		obj->timer=200; // objects donated last around 3 hours irl time
		obj_to_obj(obj,pit);
	}else{
		act( "$p appears in the room by your feet.", ch, obj, NULL, TO_ROOM);
		obj_to_room(obj,room);
	}
	ch->in_room=oldroom;
}
/************************************************************************/
// multiroom donation system - by Balo and Kal
void do_donate( char_data *ch, char *argument)
{
    OBJ_DATA *obj;
    char arg[MIL];
    OBJ_DATA *obj_next;
    bool found;
    sh_int max_don;
	
	one_argument( argument, arg);
	
	if(IS_NULLSTR(arg)){
		ch->println("Donate What?!");
		return;
	}
	
	// handle donating a specific item
	if( str_cmp( arg, "all" ) && str_prefix( "all.", arg ) )
	{
		obj = get_obj_list( ch, arg, ch->in_room->contents );
		if (!obj){
			ch->printlnf("I don't see any '%s' on the ground here to donate!", arg);
			return;
		}
		donate(ch, obj);
		return;
	}


	// handle donating all 
	{
		found = false;
		max_don = 0; 
		for ( obj = ch->in_room->contents; obj; obj = obj_next )
		{
			obj_next = obj->next_content;
			if(!CAN_WEAR(obj, OBJWEAR_TAKE)){
				continue;
			}

			if ( ( arg[3] == '\0' || is_name( &arg[4], obj->name ) )
				&&   can_see_obj( ch, obj ) )
			{
				found = true;
				max_don++;
				if (max_don > 100)
				{
					ch->wrapln("That's a lot of stuff!!    Hold up a sec!  "
						"The pipelines in the sky are a little full..  "
						"give it a sec and try again!");
					return;
				}
				donate(ch, obj);
			}
		}
		
		if ( !found ){
			ch->println( "I don't see that here.");
		}
	}
}

/************************************************************************/
void do_get( char_data *ch, char *argument)
{
	char arg1[MIL];
	char arg2[MIL];
	OBJ_DATA *obj;
	OBJ_DATA *obj_next;
	OBJ_DATA *container;
	bool found;
	
	argument = one_argument( argument, arg1 );
	argument = one_argument( argument, arg2 );
	
	if(!str_cmp(arg2,"from")){
		argument = one_argument(argument,arg2);
	}
	
    // Get type. 
    if ( IS_NULLSTR(arg1) )
    {
		ch->println( "Get what?" );
		return;
    }

	// ooc rooms
	if(!IS_IMMORTAL(ch) && IS_SET(ch->in_room->room_flags,ROOM_OOC)){
		ch->println("Not in an OOC room.");
		return;
	}

    if( IS_NULLSTR(arg2) )
	{
		if ( str_cmp( arg1, "all" ) && str_prefix( "all.", arg1 ) )
		{
			// 'get obj' 
			obj = get_obj_list( ch, arg1, ch->in_room->contents );
			if ( obj == NULL )
			{
				act( "I see no $T here.", ch, NULL, arg1, TO_CHAR );
				return;
			}
			
			get_obj( ch, obj, NULL);    
		}
		else
		{
			// 'get all' or 'get all.obj'
			found = false;
			for ( obj = ch->in_room->contents; obj != NULL; obj = obj_next )
			{
				obj_next = obj->next_content;
				if ( ( arg1[3] == '\0' || is_name( &arg1[4], obj->name ) )
					&&   can_see_obj( ch, obj ) )
				{
					// allow creation of objects with hidden name
					if(IS_SET( obj->extra_flags, OBJEXTRA_NO_GET_ALL )){
						continue;
					}
											
					found = true;
					get_obj( ch, obj, NULL);    
				}
			}
			
			if ( !found ) 
			{
				if ( arg1[3] == '\0' ){
					ch->println("I see nothing here.");
				}else{
					act( "I see no $T here.", ch, NULL, &arg1[4], TO_CHAR );
				}
			}
		}
    }
    else
    {
		// 'get ... container'
		if ( !str_cmp( arg2, "all" ) || !str_prefix( "all.", arg2 ) )
		{
			ch->println("You can't do that.");
			ch->println("syntax: get <object> <containing_object>");
			return;
		}
		
		if ( ( container = get_obj_here( ch, arg2 ) ) == NULL )
		{
			act( "I see no $T here.", ch, NULL, arg2, TO_CHAR );
			return;
		}

		// allow creation of objects with hidden name
		if(IS_SET( container->extra_flags, OBJEXTRA_NO_GET_ALL )){
			act( "I see no $T here.", ch, NULL, arg2, TO_CHAR );
			return;
		}
		
		switch ( container->item_type )
		{
		default:
			{
				ch->printlnf( "%s is not a container.", container->short_descr);
				if(IS_NEWBIE(ch)){
					ch->println( "`Wsyntax:`=C get <what> <container>`x");
				}
				return;
			}

		case ITEM_CAULDRON:
		case ITEM_CONTAINER:
		case ITEM_FLASK:
		case ITEM_MORTAR:
		case ITEM_CORPSE_NPC:
			break;
			
		case ITEM_CORPSE_PC:
			{
				
				if (!can_loot(ch,container))
				{
					ch->println("You can't do that.");
					return;
				}
			}
		}
		
		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 ) )
		{
			// 'get obj container' 
			obj = get_obj_list( ch, arg1, container->contains );
			if ( obj == NULL )
			{
				act( "I see nothing like that in the $T.",
					ch, NULL, arg2, TO_CHAR );
				return;
			}
			get_obj( ch, obj, container);    
		}
		else
		{
			// 'get all container' or 'get all.obj container' 
			found = false;
			for ( obj = container->contains; obj != NULL; obj = obj_next )
			{
				obj_next = obj->next_content;
				if ( ( arg1[3] == '\0' || is_name( &arg1[4], obj->name ) )
					&&   can_see_obj( ch, obj ) )
				{
					found = true;
					if (container->pIndexData->vnum == OBJ_VNUM_PIT
						&&  !IS_IMMORTAL(ch))
					{
						ch->println("Don't be so greedy!");
						return;
					}
					get_obj( ch, obj, container);    
				}
			}
			
			if ( !found )
			{
				if ( arg1[3] == '\0' ){
					act( "I see nothing in the $T.",
						ch, NULL, arg2, TO_CHAR );
				}else{
					act( "I see nothing like that in the $T.",
						ch, NULL, arg2, TO_CHAR );
				}
			}
		}
    }
    return;
}

/************************************************************************/
void do_put( char_data *ch, char *argument )
{
    char arg1[MIL];
    char arg2[MIL];
    obj_data *container;
    obj_data *obj;
    obj_data *obj_next;
	
    argument = one_argument( argument, arg1 );
    argument = one_argument( argument, arg2 );
	
    if (!str_cmp(arg2,"in") || !str_cmp(arg2,"on"))
		argument = one_argument(argument,arg2);
	
    if ( arg1[0] == '\0' || arg2[0] == '\0' )
    {
		ch->println("Put what in what?");
		return;
	}

	// ooc rooms
	if(!IS_IMMORTAL(ch) && IS_SET(ch->in_room->room_flags,ROOM_OOC)){
		ch->println("Not in an OOC room.");
		return;
	}
	
    if ( !str_cmp( arg2, "all" ) || !str_prefix( "all.", arg2 ) )
	{
		ch->println("You can't do that.");
		return;
    }
	
    if ( ( container = get_obj_here( ch, arg2 ) ) == NULL )
    {
		act( "I see no $T here.", ch, NULL, arg2, TO_CHAR );
		return;
    }
	
	switch ( container->item_type )
	{
	default:
		ch->printlnf( "%s is not a container.", container->short_descr);
		if(IS_NEWBIE(ch)){
			ch->println( "`Whint?:`=C put <what> <container>`x");
		}
		return;
	case ITEM_CAULDRON:
	case ITEM_CONTAINER:
	case ITEM_FLASK:
	case ITEM_MORTAR:
		break;
	}

    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 ) ) == NULL )
		{
			ch->println("You do not have that item.");
			return;
		}
		
		if ( obj == container )
		{
			ch->println("You can't fold it into itself.");
			return;
		}
		
		if ( !can_drop_obj( ch, obj ) )
		{
			ch->println("You can't let go of it.");
			return;
		}
		
		if (WEIGHT_MULT(obj) != 100)
		{
			ch->println("You have a feeling that would be a bad idea.");
            return;
        }

		if (get_obj_weight(obj) > (container->value[3] * 10))
		{
			ch->printlnf( "%s appears to be too big to ever be put into %s.", 
				obj->short_descr, container->short_descr);
			return;
		}

		if (get_obj_weight( obj ) + get_true_weight( container ) - container->weight 
			> (container->value[0] * 10))
		{
			ch->printlnf( "There doesn't appears to be enough room inside %s to fit %s.`1", 
				container->short_descr, obj->short_descr);
			return;
		}
		
		if (container->pIndexData->vnum == OBJ_VNUM_PIT 
			&&  !CAN_WEAR(container,OBJWEAR_TAKE))
		{
			if (obj->timer){
				SET_BIT(obj->extra_flags,OBJEXTRA_HAD_TIMER);
			}else{
				obj->timer = number_range(100,200);
			}
		}
		obj_from_char( obj );
		obj_to_obj( obj, container );
		
		if (IS_SET(container->value[1],CONT_PUT_ON))
		{
			act("$n puts $p on $P.",ch,obj,container, TO_ROOM);
			act("You put $p on $P.",ch,obj,container, TO_CHAR);
		}
		else
		{
			act( "$n puts $p in $P.", ch, obj, container, TO_ROOM );
			act( "You put $p in $P.", ch, obj, container, TO_CHAR );
		}
    }
    else
    {
		// PC being ordered to do this
		if ( !IS_NPC(ch) && IS_SET( ch->dyn, DYN_IS_BEING_ORDERED ))
		{
			if ( ch->master )
				ch->master->println("Not going to happen.");
			return;
		}

		// 'put all container' or 'put all.obj container'
		for ( obj = ch->carrying; obj != NULL; obj = obj_next )
		{
			obj_next = obj->next_content;
			
			if ( ( arg1[3] == '\0' || is_name( &arg1[4], obj->name ) )
				&&   can_see_obj( ch, obj )
				&&   WEIGHT_MULT(obj) == 100
				&&   obj->wear_loc == WEAR_NONE
				&&   obj != container
				&&   can_drop_obj( ch, obj )
				&&   get_obj_weight( obj ) + get_true_weight( container )
				<= (container->value[0] * 10) 
				&&   get_obj_weight(obj) < (container->value[3] * 10))
			{
				if (container->pIndexData->vnum == OBJ_VNUM_PIT
					&&  !CAN_WEAR(obj, OBJWEAR_TAKE) )
					if (obj->timer)
						SET_BIT(obj->extra_flags,OBJEXTRA_HAD_TIMER);
					else
						obj->timer = number_range(100,200);
					obj_from_char( obj );
					obj_to_obj( obj, container );
					
					if (IS_SET(container->value[1],CONT_PUT_ON))
					{
						act("$n puts $p on $P.",ch,obj,container, TO_ROOM);
						act("You put $p on $P.",ch,obj,container, TO_CHAR);
					}
					else
					{
						act( "$n puts $p in $P.", ch, obj, container, TO_ROOM );
						act( "You put $p in $P.", ch, obj, container, TO_CHAR );
					}
			}
		}
    }
    return;
}


/************************************************************************/
void do_drop( char_data *ch, char *argument)
{
    char arg[MIL];
    OBJ_DATA *obj;
    OBJ_DATA *obj_next;
    bool found;

    argument = one_argument( argument, arg );

    if ( arg[0] == '\0' )
    {
		ch->println("Drop what?");
		return;
    }

	// ooc rooms
	if(!IS_IMMORTAL(ch) && IS_SET(ch->in_room->room_flags,ROOM_OOC)){
		ch->println("Not in an OOC room.");
		return;
	}

	if ( is_number( arg ) )
    {
		// 'drop NNNN coins'
		int amount, gold = 0, silver = 0;
		
		amount   = atoi(arg);
		argument = one_argument( argument, arg );
		if ( amount <= 0
			|| ( str_cmp( arg, "coins" ) && str_cmp( arg, "coin" ) &&
			str_cmp( arg, "gold"  ) && str_cmp( arg, "silver") ) )
		{
			ch->println("Sorry, you can't do that.");
			return;
		}
		
		if ( !str_cmp( arg, "coins") || !str_cmp(arg,"coin") 
			||   !str_cmp( arg, "silver"))
		{
			if (ch->silver < amount)
			{
				ch->println("You don't have that much silver.");
				return;
			}
			
			ch->silver -= amount;
			silver = amount;
		}
		
		else
		{
			if (ch->gold < amount)
			{
				ch->println("You don't have that much gold.");
				return;
			}
			
			ch->gold -= amount;
			gold = amount;
		}
		
		for ( obj = ch->in_room->contents; obj != NULL; obj = obj_next )
		{
			obj_next = obj->next_content;
			
			switch ( obj->pIndexData->vnum )
			{
            case OBJ_VNUM_SILVER_ONE:
                silver += 1;
                extract_obj(obj);
				break;
				
            case OBJ_VNUM_GOLD_ONE:
                gold += 1;
                extract_obj( obj );
				break;
				
            case OBJ_VNUM_SILVER_SOME:
                silver += obj->value[0];
                extract_obj(obj);
				break;
				
            case OBJ_VNUM_GOLD_SOME:
                gold += obj->value[1];
                extract_obj( obj );
				break;
				
            case OBJ_VNUM_COINS:
                silver += obj->value[0];
                gold += obj->value[1];
                extract_obj(obj);
				break;
			}
		}
		
		obj=create_money( gold, silver );
		// record details for multilogging detection
		if(!IS_NPC(ch)){
			obj->lastdrop_id=ch->id;
			replace_string(obj->lastdrop_remote_ip,ch->remote_ip_copy);
		}		
		obj_to_room( obj, ch->in_room );
		
		act( "$n drops some coins.", ch, NULL, NULL, TO_ROOM );
		ch->println( "OK." );
		return;
    }

    if ( str_cmp( arg, "all" ) && str_prefix( "all.", arg ) )
    {
		// 'drop obj' 
		if ( ( obj = get_obj_carry( ch, arg ) ) == NULL ){
			ch->println( "You do not have that item." );
			return;
		}
		
		if ( !can_drop_obj( ch, obj ) ){
			ch->println( "You can't let go of it." );
			return;
		}
		
		obj_from_char( obj );

		// record details for multilogging detection
		if(!IS_NPC(ch)){
			obj->lastdrop_id=ch->id;
			replace_string(obj->lastdrop_remote_ip, ch->remote_ip_copy);
		}		

		obj_to_room( obj, ch->in_room );
		act( "$n drops $p.", ch, obj, NULL, TO_ROOM );
		act( "You drop $p.", ch, obj, NULL, TO_CHAR );
		if (IS_OBJ_STAT(obj,OBJEXTRA_MELT_DROP))
		{
			act("$p dissolves into smoke.",ch,obj,NULL,TO_ROOM);
			act("$p dissolves into smoke.",ch,obj,NULL,TO_CHAR);
			extract_obj(obj);
		}
    }
    else
    {
	// PC being ordered to do this
	if ( !IS_NPC(ch) && IS_SET( ch->dyn, DYN_IS_BEING_ORDERED ))
	{
		if ( ch->master )
			ch->master->println("Not going to happen.");
		return;
	}

	// 'drop all' or 'drop all.obj' 
	found = false;
	for ( obj = ch->carrying; obj != NULL; obj = obj_next )
	{
		obj_next = obj->next_content;
		
		if ( ( arg[3] == '\0' || is_name( &arg[4], obj->name ) )
			&&   can_see_obj( ch, obj )
			&&   obj->wear_loc == WEAR_NONE
			&&   can_drop_obj( ch, obj ) )
		{
			found = true;
			obj_from_char( obj );
			// record details for multilogging detection
			if(!IS_NPC(ch)){
				obj->lastdrop_id=ch->id;
				replace_string(obj->lastdrop_remote_ip, ch->remote_ip_copy);
			}		
			obj_to_room( obj, ch->in_room );
			
			act( "$n drops $p.", ch, obj, NULL, TO_ROOM );
			act( "You drop $p.", ch, obj, NULL, TO_CHAR );
			if (IS_OBJ_STAT(obj,OBJEXTRA_MELT_DROP))
			{
				act("$p dissolves into smoke.",ch,obj,NULL,TO_ROOM);
				act("$p dissolves into smoke.",ch,obj,NULL,TO_CHAR);
				extract_obj(obj);
			}
			
        }
	}

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

    return;
}

/************************************************************************/
void do_give_new( char_data *ch, char *argument, int silent)
{
	char arg1 [MIL];
	char arg2 [MIL];
	char buf[MSL];
	char_data *victim;
	OBJ_DATA  *obj;
	
	argument = one_argument( argument, arg1 ); // what
	argument = one_argument( argument, arg2 ); // whom
	
	// display the help if they don't give enough arguments
	if ( IS_NULLSTR(arg1) || IS_NULLSTR(arg2) )
	{
		ch->println("");
		if(!IS_NULLSTR(arg1) && IS_NULLSTR(arg2)){
			ch->printlnf("Give '%s' to who?", arg1);
		}else{
			ch->println( "Give what to whom?");
		}
		ch->println("");
		ch->titlebar( "Giving Objects");
		ch->println( "Syntax: give <object/what> [to] <person/creature>");
		ch->println( "e.g. typing 'give sword guard'");
		ch->println( "     - will give a sword to a guard, (assuming you are carrying one).");
		ch->println( "note: the word 'to' is optional in the give command.");
		ch->titlebar( "Giving Currency");
		// 'give NNNN coins [to] victim'
		ch->println( "Syntax: give <amount> gold [to] <person/creature>");
		ch->println( "e.g. typing 'give 15 silver guard'");
		ch->println( "     - will give 15 of your silver coins to a guard.");
		ch->println( "e.g. typing 'give 2 gold guard'");
		ch->println( "     - will give 2 of your gold coins to a guard.");
		ch->println( "note: 1 gold is worth 10 silver.");
		ch->titlebar( "");
		return;
	}

	// ooc rooms
	if(!IS_IMMORTAL(ch) && IS_SET(ch->in_room->room_flags,ROOM_OOC)){
		ch->println("Not in an OOC room, you can only give objects within the main game realm.");
		return;
	}
	
	if ( is_number( arg1 ) )
	{
		// 'give NNNN coins [to] victim'
		int amount;
		bool silver;
		
		amount   = atoi(arg1);
		if ( amount <= 0
			|| ( str_cmp( arg2, "coins" ) && str_cmp( arg2, "coin" ) &&
			str_cmp( arg2, "gold"  ) && str_cmp( arg2, "silver")) )
		{
			ch->println("Sorry, you can't do that.");
			return;
		}
		
		silver = str_cmp(arg2,"gold");
		
		argument = one_argument( argument, arg2 );
		if ( IS_NULLSTR(arg2))
		{
			ch->printlnf( "Give %d %s to whom?", amount, silver?"silver":"gold" );
			return;
		}

		// using optional 'to' in give command
		if(!str_cmp("to", arg2)){
			argument = one_argument( argument, arg2 ); 
		}

		// find they person they are giving to
		if ( ( victim = get_char_room( ch, arg2 ) ) == NULL )
		{
			ch->printlnf( "You can't seem to find any '%s' here to give %d %s to.", 
				arg2, amount, silver?"silver":"gold");
			return;
		}

		if(victim==ch){
			ch->printlnf("What would be the purpose of giving %d %s to yourself when you already have it?",
					amount, silver?"silver":"gold");
			return;
		}
		
		if (IS_NPC(victim) && IS_SET(victim->act,ACT_IS_CHANGER))
		{
			// check that the money changer can see them 
			if(!can_see(victim,ch)){
				ch->println("They can't see you to give you your change.");
				return;
			}

			// check that the money changer isn't subdued
			if (victim->subdued){
				ch->println("They don't appear to be in a state to exchange your money.");
				return;
			}

			// sleeping
			if (!IS_AWAKE(victim)){
				ch->printlnf( "%s is sleeping and can not change your money right now.", PERS(victim, ch));
				return;
			}	   

		}	

		if ( (!silver && ch->gold < amount) || (silver && ch->silver < amount) )
		{
			ch->printlnf( "You haven't got %d %s to give!", amount, silver?"silver":"gold");
			return;
		}

		// make sure victim can carry that much money 
		if (get_carry_weight(victim) +
			  (silver ? get_silver_weight(amount) : get_gold_weight(amount))
               > can_carry_w( victim ) )
		{
			act( "$N can't carry that much weight.",
				ch, NULL, victim, TO_CHAR );
			return;
		}


		// record details for multilogging detection
		if( !IS_NPC(ch) 
			&& !HAS_CONFIG(ch, CONFIG_IGNORE_MULTILOGINS)
			&& !IS_NPC(victim) 
			&& !HAS_CONFIG(victim, CONFIG_IGNORE_MULTILOGINS)
		  )
		{
			if(!strcmp(ch->remote_ip_copy,victim->remote_ip_copy)){
				multilog_alertf(victim, "`C%s<%d>`B(id=%d) `xgiving %s%d %s`x to `Y%s<%d>`x (room %d), both from same IP!", 
					ch->name,
					ch->level,
					(int)ch->id,
					silver?"`S":"`Y",
					amount, 
					silver?"silver":"gold", 
					victim->name,
					victim->level,
					ch->in_room_vnum());
			}
		}		

		// exchange the money
		if (silver){
			ch->silver		-= amount;
			victim->silver 	+= amount;
		}else{
			ch->gold		-= amount;
			victim->gold	+= amount;
		}
		
		if (silent){
			sprintf(buf, "SILENTGIVE: %s gives %s %d %s.",  ch->name , victim->name, amount, silver ? "silver" : "gold");
			wiznet(buf,ch,NULL,WIZ_SECURE,0,get_trust(ch));
			sprintf(buf,"You SILENTLY give $N %d %s.",amount, silver ? "silver" : "gold");
			act( buf, ch, NULL, victim, TO_CHAR    );
		}else{
			sprintf(buf,"$n gives you %d %s.",amount, silver ? "silver" : "gold");
			act( buf, ch, NULL, victim, TO_VICT    );
			act( "$n gives $N some coins.",  ch, NULL, victim, TO_NOTVICT );
			sprintf(buf,"You give $N %d %s.",amount, silver ? "silver" : "gold");
			act( buf, ch, NULL, victim, TO_CHAR    );
			
			// Bribe trigger
			if ( IS_NPC(victim) && HAS_TRIGGER( victim, TRIG_BRIBE ) ){
				mp_bribe_trigger( victim, ch, silver ? amount : amount * 100 );
				limit_mobile_wealth(victim);
			}		
		}

		// handle money changers giving money back
		if (IS_NPC(victim) && IS_SET(victim->act,ACT_IS_CHANGER))
		{
			int change;
			
			change = (silver ? 95 * amount / 100 / 100 
				: 95 * amount);
			
			
			if (!silver && change > victim->silver){
				victim->silver += change;
			}
			
			if (silver && change > victim->gold){
				victim->gold += change;
			}
			
			if (change < 1 && can_see(victim,ch))
			{
				act(
					"$n tells you 'I'm sorry, you did not give me enough to change.'"
					,victim,NULL,ch,TO_VICT);
				sprintf(buf,"%d %s %s", 
					amount, silver ? "silver" : "gold",ch->name);
				do_give_new(victim,buf, silent);
			}
			else if (can_see(victim,ch))
			{
				sprintf(buf,"%d %s %s", 
					change, silver ? "gold" : "silver",ch->name);
				do_give_new(victim,buf, silent);
				if (silver)
				{
					sprintf(buf,"%d silver %s", 
						(95 * amount / 100 - change * 100),ch->name);
					do_give_new(victim,buf, silent);
				}
				act("$n tells you 'Thank you, come again.'", victim,NULL,ch,TO_VICT);
				limit_mobile_wealth(victim);
			}
		}
		return;
    } // end of code which deals with giving money
	
    
	// === GIVING OBJECTS CODE 
	// give <object> [to] <victim>

	// using optional 'to' in give command
	if(!str_cmp("to", arg2)){
		argument = one_argument( argument, arg2 ); 
	}

	// find the object 
	if ( ( obj = get_obj_carry( ch, arg1 ) ) == NULL )
    {
        ch->printlnf( "You are not carrying any '%s' to give to '%s'.", arg1, arg2 );
        return;
    }
	
    if ( obj->wear_loc != WEAR_NONE )
    {
        ch->printlnf( "You are currently using '%s', you must remove it before giving it to '%s'.", obj->short_descr, arg2 );
        return;
    }
	
	// find the victim to receive the item
    if ( ( victim = get_char_room( ch, arg2 ) ) == NULL ) {
		ch->printlnf( "You can't seem to find any '%s' here to give '%s' to.", arg2, obj->short_descr);
		return;
    }
	
	if(victim==ch){
		ch->printlnf("What would be the purpose of giving %s to yourself?", obj->short_descr);
		return;
	}
		
    if (IS_NPC(victim) && victim->pIndexData->pShop != NULL)
    {
		// if they are a shop keeper without a give trigger which
		// would catch the object, don't accept the item
		if(!mp_would_run_give_trigger(victim, ch, obj )){
			act("$N tells you 'Sorry, you'll have to sell that.'", ch,NULL,victim,TO_CHAR);
			return;
		}
    }
	
    if ( !can_drop_obj( ch, obj ) )
    {
		ch->printlnf( "You can't let go of %s.", obj->short_descr );
		return;
	}
	
    if ( !IS_CONTROLLED(victim) && victim->carry_number + get_obj_number( obj ) > can_carry_n( victim ) )
    {
        act( "$N has $S hands full.", ch, NULL, victim, TO_CHAR );
        return;
    }
	
    if (!IS_CONTROLLED(victim) && get_carry_weight(victim) + 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( FORMATF("$N can't seem to see %s.", obj->short_descr), ch, NULL, victim, TO_CHAR );
        return;
    }
	
    obj_from_char( obj );

	// check lastgive details for multilogging detection
	if(!IS_NPC(victim) 
		&& !HAS_CONFIG(ch, CONFIG_IGNORE_MULTILOGINS)
		&& !HAS_CONFIG(victim, CONFIG_IGNORE_MULTILOGINS)){

		// player giving objects to itself via another player or mob
		if( obj->lastdrop_id!=victim->id 
			&& !strcmp(obj->lastdrop_remote_ip, victim->remote_ip_copy))
		{
			multilog_alertf(victim, "`M%s<%d>`x giving `Gobject '%s' (%d)`x to `Y%s<%d>`x (room %d), which was recently given to "
				"`M%s`x by `Bsomeone (id=%d)`x other than `Y%s`x from the same ip as `Y%s!`x", 
				ch->name, 
				ch->level,
				obj->short_descr,
				obj->pIndexData?obj->pIndexData->vnum:0,
				victim->name,
				victim->level,
				ch->in_room_vnum(),
				ch->name, 
				(int)obj->lastdrop_id,
				victim->name,
				victim->name);	
		// player giving directly to another of their players
		}else if(!strcmp(ch->remote_ip_copy, victim->remote_ip_copy)){
			multilog_alertf(ch, "`C%s<%d>`B(id=%d) `xgiving `Gobject '%s' (%d) `xto `Y%s<%d>`x (room%d) - object exchange between players from the same IP!",
				ch->name, 
				ch->level,
				(int)ch->id,
				obj->short_descr,
				obj->pIndexData?obj->pIndexData->vnum:0,
				victim->name,
				victim->level,
				ch->in_room_vnum());
		}
	}	

	// record host of giver on object if not a mob
	if(!IS_NPC(ch)){
		obj->lastdrop_id=ch->id;
		replace_string(obj->lastdrop_remote_ip, ch->remote_ip_copy);
	}

    obj_to_char( obj, victim );
	
    if (silent)
    {
        sprintf(buf, "SILENTGIVE: %s gives %s to %s.", ch->name, obj->short_descr, victim->name);
        wiznet(buf,ch,NULL,WIZ_SECURE,0,get_trust(ch));
        act( "You SILENTLY give $p to $N.", ch, obj, victim, TO_CHAR    );
    }
    else
    {
        MOBtrigger = false;
        act( "$n gives $p to $N.", ch, obj, victim, TO_NOTVICT );
        act( "$n gives you $p.",   ch, obj, victim, TO_VICT    );
        act( "You give $p to $N.", ch, obj, victim, TO_CHAR    );
        MOBtrigger = true;
    }
	
    // Give mobprog trigger
    if ( IS_NPC(victim) && HAS_TRIGGER( victim, TRIG_GIVE ) ){
        mp_give_trigger( victim, ch, obj );
	}
}

// normal give
void do_give( char_data *ch, char *argument )
{
    do_give_new(ch, argument, false);
}

// silent give 
void do_sgive( char_data *ch, char *argument )
{
    do_give_new(ch, argument, true);
}

/************************************************************************/
/*
 *  Take stuff and money from subdued or sleeping characters
 *  Written by Quenralther and Kalahn - Dec 97
 */
void do_grab( char_data *ch, char *argument )
{
	char arg1 [MIL];
	char arg2 [MIL];
	char buf[MSL];
	char_data *victim;
	OBJ_DATA  *obj;
	int random;
	
	if( IS_OOC(ch) )
	{ ch->println("You may not use grab in an ooc area."); return; }
	
	argument = one_argument( argument, arg1 );
	argument = one_argument( argument, arg2 );
	
	if ( arg1[0] == '\0' || arg2[0] == '\0' )
	{
		ch->println("Take what from whom?");
		return;
	}

	if ( is_number( arg1 ) )
	{
		// 'give NNNN coins victim' 
		int amount;
		bool silver;
		
		amount	 = atoi(arg1);
		if ( amount <= 0
			|| ( str_cmp( arg2, "coins" ) && str_cmp( arg2, "coin" ) &&
			str_cmp( arg2, "gold"  ) && str_cmp( arg2, "silver")) )
		{
			ch->println("Sorry, you can't do that.");
			return;
		}
		
		silver = str_cmp(arg2,"gold");
		
		argument = one_argument( argument, arg2 );
		if ( arg2[0] == '\0' )
		{
			ch->println("Take what from whom?");
			return;
		}
		
		if ( ( victim = get_char_room( ch, arg2 ) ) == NULL )
		{
			ch->println("They aren't here.");
			return;
		}
		
		if IS_AWAKE(victim) 
		{
			ch->println("You can't grab stuff from someone who's awake.");
			return;
		}	   

		if (IS_IMMORTAL(victim))
		{
			ch->println("You failed.");
			return;
		}

		if (is_safe(ch,victim))
		{
			return;
		}

#ifdef unix
		if (!IS_IMMORTAL(ch))
		{
			WAIT_STATE( ch, PULSE_VIOLENCE );
		}
#endif

if ((victim->position == POS_SLEEPING 
    && !IS_IMMORTAL(ch)
    && (!IS_AFFECTED(victim, AFF_SLEEP)
    || (!is_same_group(ch, victim) && number_range(1,5)==1)
    || number_range(1,15)==1)
    )
    || (victim->position<=POS_SLEEPING && IS_KEEPER(victim) && !IS_IMMORTAL(ch))
)
        {
            random = dice(1,100);
            if (IS_KEEPER(victim)
                || ( random < 70 + victim->level-ch->level-ch->modifiers[STAT_QU]/5
                + victim->modifiers[STAT_IN]/5) )            
            {
				// remove sleep spell if necessary
				if ( IS_AFFECTED(ch, AFF_SLEEP) ){
					affect_strip( ch, gsn_sleep );
				}
				victim->position = POS_RESTING;
				act("$n tried to take some coins from $N, who wakes up.",ch,NULL,victim,TO_NOTVICT );
				act("You are awakened by $N trying to take some coins from you.",victim,NULL,ch,TO_CHAR);
				act("Your attempts have woken $N!",ch,NULL,victim,TO_CHAR);

				// wiznet thefts messages
				sprintf(buf,"$N <%d> tried to grab %d coins from %s <%d> and woke them up!", 
					ch->level, amount, victim->name, victim->level);
				wiznet(buf,ch,NULL,WIZ_THEFTS,0,0);
				return;				
			}

			random = dice(1,100);
			if ( random < 75-ch->level+victim->level )
			{
				act("$n couldn't manage to pry any coins away from $N.",ch,NULL,victim,TO_NOTVICT );
				act("You couldn't manage to pry any coins away from $N.",ch,NULL,victim,TO_CHAR);

				// wiznet thefts message
				sprintf(buf,"$N tried to grab %d coins from %s <%d> and failed.", 
					amount, victim->name, victim->level);
				wiznet(buf,ch,NULL,WIZ_THEFTS,0,0);
				return;
			}
		} // endif they are in the sleeping position... if they are below that you can 
		  // always take everything
		
		if (!silver && victim->gold < amount)
		{
			amount = victim->gold;
			// wiznet thefts messages
			sprintf(buf,"$N <%d> took using grab %d gold coins from %s <%d>.", 
				ch->level, amount, victim->name, victim->level);
			wiznet(buf,ch,NULL,WIZ_THEFTS,0,0);
		}
		
		if (silver && victim->silver < amount)
		{
			amount = victim->silver;
			// wiznet thefts messages
			sprintf(buf,"$N <%d> took using grab %d silver coins from %s <%d>.", 
				ch->level, amount, victim->name, victim->level);
			wiznet(buf,ch,NULL,WIZ_THEFTS,0,0);
			return;				
		}
		
		if (silver)
		{
			victim->silver	-= amount;
			ch->silver		+= amount;
		}
		else
		{
			victim->gold	-= amount;
			ch->gold		+= amount;
		}
		
		if (victim->position > POS_SLEEPING)
		{
			act( "$n took some coins from you.", ch, NULL, victim, TO_VICT);
		}
		act( "$n takes some coins from $N.",  ch, NULL, victim, TO_NOTVICT );
		sprintf(buf,"You take %d %s from $N.",amount, silver ? "silver" : "gold");
		act( buf, ch, NULL, victim, TO_CHAR    );
		return;
	} // end of coins
	
	if ( ( victim = get_char_room( ch, arg2 ) ) == NULL )
	{							   
		ch->println("They aren't here.");
		return;
	}
	
	if (IS_AWAKE(victim))
	{
		ch->println("You can't grab stuff from someone who's awake.");
		return;
	}

	if (IS_IMMORTAL(victim))
	{
		ch->println("You failed.");
		return;
	}
	
	obj = get_obj_carry_for_looker( victim, arg1, ch ); // find the object in victims inventory
	
	if ( !obj) // not in their inventory check if they are wearing it
	{
		if ((obj = get_obj_wear(victim, arg1)) != NULL)
		{
			if ( IS_SET(obj->extra_flags, OBJEXTRA_NOREMOVE ))
			{
				ch->println("You can't wrench it away...it's stuck somehow.");
				return;
			}
		}
	}
	
	if (!obj) // check if nothing was found
	{
		ch->println("You can't find that item on their person.");
		return;
	}
	
#ifdef unix
		if (!IS_IMMORTAL(ch))
		{
			WAIT_STATE( ch, PULSE_VIOLENCE );
		}
#endif
	

if ((victim->position == POS_SLEEPING 
    && !IS_IMMORTAL(ch)
    && (!IS_AFFECTED(victim, AFF_SLEEP)
    || (!is_same_group(ch, victim) && number_range(1,5)==1)
    || number_range(1,15)==1)
    )
    || (victim->position<=POS_SLEEPING && IS_KEEPER(victim) && !IS_IMMORTAL(ch))
)
        {
            random = dice(1,100);
            if (IS_KEEPER(victim)
                ||( random < 90 + victim->level - ch->level
                  - ch->modifiers[STAT_AG]/10+victim->modifiers[STAT_IN]/5 )
               )
            {
/*
	if ( victim->position == POS_SLEEPING
		&& (!IS_AFFECTED(victim, AFF_SLEEP)
			|| (!is_same_group(ch, victim) && number_range(1,5)==1)
			|| number_range(1,15)==1
		   )
		)
	{
		random = dice(1,100);
		if ( random < 90 + victim->level - ch->level
              - ch->modifiers[STAT_AG]/10+victim->modifiers[STAT_IN]/5 )
		{
*/
			victim->position = POS_RESTING;
			act("$n tried to take something from $N, who wakes up.",ch,NULL,victim,TO_NOTVICT );
			act("You are awakened by $N trying to take something from you.",victim,NULL,ch,TO_CHAR);
			act("Your attempts have woken $N!",ch,NULL,victim,TO_CHAR);

			// wiznet thefts messages
            sprintf(buf,"$N <%d> tried to grab $p from %s <%d> and woke them up!", 
				ch->level, victim->name, victim->level);
			wiznet(buf,ch, obj,WIZ_THEFTS,0,0);
			return;				
		}
		random = dice(1,100);
		if ( random < 100-ch->level+victim->level-ch->modifiers[STAT_ST]/5+victim->modifiers[STAT_ST]/5 )
		{
			act("$n couldn't manage to pry anything away from $N.",ch,NULL,victim,TO_NOTVICT );
			act("You couldn't manage to pry anything away from $N.",ch,NULL,victim,TO_CHAR);
	
			// wiznet thefts messages
            sprintf(buf,"$N <%d> tried to grab $p from %s <%d> but failed to get it!", 
				ch->level, victim->name, victim->level);
			wiznet(buf,ch, obj,WIZ_THEFTS,0,0);
			return;
		}
	}

	if (is_safe(ch,victim))
	{
		return;
	}
	
	if ( !can_drop_obj( victim, obj ) )
	{
		ch->println("You can't wrench it away...it's stuck somehow.");

		// wiznet thefts messages
        sprintf(buf,"$N <%d> tried to grab $p from %s <%d> but couldn't wrench it away.", 
			ch->level, victim->name, victim->level);
		wiznet(buf,ch, obj,WIZ_THEFTS,0,0);
		return;
	}

    if ( ch->carry_number + get_obj_number( obj ) > can_carry_n( ch ) )
    {
        ch->println("You have your hands full.");
        return;
    }

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


	
	//	object exchanges hands now
	obj_from_char( obj );
	obj_to_char( obj, ch );
	
	act( "$n takes $p from $N.", ch, obj, victim, TO_NOTVICT );
	if ( victim->position > POS_SLEEPING )
	{
		act( "$n takes $p from you.",	ch, obj, victim, TO_VICT	);
	}
	act( "You take $p from $N.", ch, obj, victim, TO_CHAR	 );

	// wiznet thefts messages
    sprintf(buf,"$N <%d> grabbed $p from %s <%d> successfully.", 
		ch->level, victim->name, victim->level);
	wiznet(buf,ch, obj,WIZ_THEFTS,0,0);
	return;
}

/************************************************************************/
/* for poisoning weapons and food/drink */
void do_envenom(char_data *ch, char *argument)
{
	OBJ_DATA *obj;
	AFFECT_DATA af;
	int percent,skill;

	/* find out what */
	if (IS_NULLSTR(argument))
	{
		ch->println("Envenom what item?");
		return;
	}

	obj =  get_obj_list(ch,argument,ch->carrying);

	if (obj== NULL)
	{
		ch->println("You don't have that item.");
		return;
	}

	if ((skill = get_skill(ch,gsn_envenom)) < 1)
	{
		ch->println("Are you crazy? You'd poison yourself!");
		return;
	}

	if (obj->item_type == ITEM_FOOD || obj->item_type == ITEM_DRINK_CON)
	{
		if (number_percent() < skill)  // success!
		{
			act("$n treats $p with deadly poison.",ch,obj,NULL,TO_ROOM);
			act("You treat $p with deadly poison.",ch,obj,NULL,TO_CHAR);
			if (!obj->value[3])
			{
				obj->value[3] = 1;
				check_improve(ch,gsn_envenom,true,4);
			}
			WAIT_STATE(ch,skill_table[gsn_envenom].beats);
			return;
		}
		act("You fail to poison $p.",ch,obj,NULL,TO_CHAR);
		if (!obj->value[3])
		{
			check_improve(ch,gsn_envenom,false,4);
		}
		WAIT_STATE(ch,skill_table[gsn_envenom].beats);
		return;
	}

	if ( obj->item_type == ITEM_WEAPON )
	{
		if (IS_WEAPON_STAT( obj,WEAPON_FLAMING	)
		||  IS_WEAPON_STAT( obj,WEAPON_FROST	)
		||  IS_WEAPON_STAT( obj,WEAPON_VAMPIRIC	)
		||  IS_WEAPON_STAT( obj,WEAPON_SHOCKING	)
		||  IS_WEAPON_STAT( obj,WEAPON_HOLY		))
		{
			act("You can't seem to envenom $p.",ch,obj,NULL,TO_CHAR);
			return;
		}

		if (obj->value[3] < 0 
		||  attack_table[obj->value[3]].damage == DAM_BASH)
		{
			ch->println("You can only envenom edged weapons.");
			return;
		}

		if (IS_WEAPON_STAT(obj,WEAPON_POISON))
		{
			act("$p is already envenomed.",ch,obj,NULL,TO_CHAR);
			return;
		}

		percent = number_percent();
		if (percent < skill)
		{
			af.where     = WHERE_WEAPON;
			af.type      = gsn_poison;
			af.level     = ch->level;
			af.duration  = ch->level / 4;
			af.location  = APPLY_NONE;
			af.modifier  = 0;
			af.bitvector = WEAPON_POISON;
			affect_to_obj(obj,&af);
 
			act("$n coats $p with deadly venom.",ch,obj,NULL,TO_ROOM);
			act("You coat $p with venom.",ch,obj,NULL,TO_CHAR);
			check_improve(ch,gsn_envenom,true,3);
			WAIT_STATE(ch,skill_table[gsn_envenom].beats);
			return;
		}
		else
		{
			act("You fail to envenom $p.",ch,obj,NULL,TO_CHAR);
			check_improve(ch,gsn_envenom,false,3);
		    WAIT_STATE(ch,skill_table[gsn_envenom].beats);
			return;
		}
	}
 	act("You can't poison $p.",ch,obj,NULL,TO_CHAR);
	return;
}

/**************************************************************************/
void do_fill( char_data *ch, char *argument )
{
    char arg[MIL];
    char buf[MSL];
    OBJ_DATA *obj;
    OBJ_DATA *fountain;
    bool found;
	
    one_argument( argument, arg );
	
	if ( arg[0] == '\0' )
	{
		ch->println("Fill what?");
		return;
    }
	
    if ( ( obj = get_obj_carry( ch, arg ) ) == NULL )
    {
		ch->println("You do not have that item.");
		return;
    }
	
    found = false;
    for ( fountain = ch->in_room->contents; fountain != NULL;
	fountain = fountain->next_content )
    {
		if ( fountain->item_type == ITEM_FOUNTAIN )
		{
			found = true;
			break;
		}
	}
	
    if ( !found )
    {
		ch->println("There is no fountain here!");
		return;
    }

	// if the fountain is empty you shouldn't be able to fill a drinkcontainer
	if ( fountain->value[1] == 0 )
	{
		act( "$p is empty.", ch, fountain, NULL, TO_CHAR );
		return;
	}

    if ( obj->item_type != ITEM_DRINK_CON )
    {
		ch->println("You can't fill that.");
		return;
    }
	
    if ( obj->value[1] != 0 && obj->value[2] != fountain->value[2] )
    {
		ch->println("There is already another liquid in it.");
		return;
    }
	
    if ( obj->value[1] >= obj->value[0] )
	{
		ch->println("Your container is full.");
		return;
    }

	// decrement the fountain by the fill amount 
	// if it isn't an infinite fountain
	if(fountain->value[1]>=0){
		fountain->value[1] -= (obj->value[0] - obj->value[1]);

		// in case -1 is unlimited etc
		if ( fountain->value[1] < 0 ){
			fountain->value[1] = 0;
		}
	}


    sprintf(buf,"You fill $p with %s from $P.",
		liq_table[fountain->value[2]].liq_name);
    act( buf, ch, obj,fountain, TO_CHAR );
    sprintf(buf,"$n fills $p with %s from $P.",
		liq_table[fountain->value[2]].liq_name);
    act(buf,ch,obj,fountain,TO_ROOM);
    obj->value[2] = fountain->value[2];
    obj->value[1] = obj->value[0];

    return;
}

/**************************************************************************/
void do_pour (char_data *ch, char *argument)
{
    char arg[MSL],buf[MSL], outbuf[MSL];
    OBJ_DATA *out, *in;
	char_data *vch = NULL;
	int amount;
	
    argument = one_argument(argument,arg);
    
    if (IS_NULLSTR(arg)|| IS_NULLSTR(argument))
    {
		ch->println("Pour what into what?");
		ch->println("Note: to empty a liquid container type `=Cempty <container_name>`x");
		return;
    }
    
	
    if ((out = get_obj_carry(ch,arg)) == NULL)
    {
		ch->println("You don't have that item." );
		return;
    }
	
	if (out->item_type != ITEM_DRINK_CON)
    {
		ch->println("That's not a drink container." );
		return;
	}
	
	sprintf(outbuf,argument);
	one_argument(outbuf,outbuf);
    if (!str_cmp(outbuf,"out"))
    {
		if (out->value[1] == 0)
		{
			ch->println("It's already empty." );
			return;
		}
		
		out->value[1] = 0;
		out->value[3] = 0;
		sprintf(buf,"You invert $p, spilling %s all over the ground.",
			liq_table[out->value[2]].liq_name);
		act(buf,ch,out,NULL,TO_CHAR);
		
		sprintf(buf,"$n inverts $p, spilling %s all over the ground.",
			liq_table[out->value[2]].liq_name);
		act(buf,ch,out,NULL,TO_ROOM);
		return;
    }
	
	if ((in = get_obj_here(ch,argument)) == NULL)
    {
		vch = get_char_room(ch,argument);
		
		if (vch == NULL)
		{
			ch->println("Pour into what?" );
			return;
		}
		
		in = get_eq_char(vch,WEAR_HOLD);
		
		if (in == NULL)
		{
			ch->println("They aren't holding anything." );
			return;
		}
    }
	
    if (in->item_type != ITEM_DRINK_CON)
	{
		ch->println("You can only pour into other drink containers." );
		return;
    }
    
    if (in == out)
    {
		ch->println("You cannot change the laws of physics!");
		return;
    }
	
    if (in->value[1] != 0 && in->value[2] != out->value[2])
    {
		ch->println("They don't hold the same liquid.");
		return;
    }
	
    if (out->value[1] == 0)
    {
		act("There's nothing in $p to pour.",ch,out,NULL,TO_CHAR);
		return;
	}
	
    if (in->value[1] >= in->value[0])
    {
		act("$p is already filled to the top.",ch,in,NULL,TO_CHAR);
		return;
    }
	
    amount = UMIN(out->value[1],in->value[0] - in->value[1]);
	
    in->value[1] += amount;
    out->value[1] -= amount;
    in->value[2] = out->value[2];
    
    if (vch == NULL)
    {
		sprintf(buf,"You pour %s from $p into $P.",
			liq_table[out->value[2]].liq_name);
		act(buf,ch,out,in,TO_CHAR);
		sprintf(buf,"$n pours %s from $p into $P.",
			liq_table[out->value[2]].liq_name);
		act(buf,ch,out,in,TO_ROOM);
	}
	else
    {
        sprintf(buf,"You pour some %s for $N.",
            liq_table[out->value[2]].liq_name);
        act(buf,ch,NULL,vch,TO_CHAR);
		sprintf(buf,"$n pours you some %s.",
			liq_table[out->value[2]].liq_name);
		act(buf,ch,NULL,vch,TO_VICT);
        sprintf(buf,"$n pours some %s for $N.",
            liq_table[out->value[2]].liq_name);
        act(buf,ch,NULL,vch,TO_NOTVICT);
		
    }
}

/**************************************************************************/
void do_drink( char_data *ch, char *argument )
{
    char arg[MIL];
	OBJ_DATA *obj;
	int amount;
	int liquid;
	int max_liq;


	for(max_liq=0; !IS_NULLSTR(liq_table[max_liq].liq_name); max_liq++){		
	}
	max_liq--;
	
    one_argument( argument, arg );
	
    if ( arg[0] == '\0' )
    {
		for ( obj = ch->in_room->contents; obj; obj = obj->next_content )
		{
			if ( obj->item_type == ITEM_FOUNTAIN )
				break;
		}
		
		if ( obj == NULL )
		{
			ch->println( "Drink what?" );
			return;
		}
    }
    else
    {
		if ( ( obj = get_obj_here( ch, arg ) ) == NULL )
		{
			ch->println( "You can't find it." );
			return;
		}
    }
	
    if ( !IS_NPC(ch) && ch->pcdata->condition[COND_DRUNK] > 10 )
    {
		ch->println( "You fail to reach your mouth.  *Hic*" );
		return;
    }
	
    switch ( obj->item_type )
	{
    default:
		ch->println( "You can't drink from that." );
		return;
		
    case ITEM_FOUNTAIN:
	case ITEM_DRINK_CON:
		if ( obj->value[1]== 0 ){
			ch->println( "It is already empty." );
			return;
		}

		liquid = obj->value[2];
        if ( liquid < 0 || liquid>max_liq)
		{
			bugf( "Do_drink: bad liquid number %d.", liquid );
			autonote(NOTE_SNOTE, "do_drink()", "bad liquid number", "imm",
				FORMATF("Do_drink: bad liquid number %d, on object %d (%s), value reset to 0..", 
					liquid, obj->pIndexData?obj->pIndexData->vnum:0, obj->short_descr ), true);
            liquid = obj->value[2] = 0;
        }
		amount = liq_table[liquid].liq_affect[4];
		if(obj->item_type==ITEM_FOUNTAIN){ // naturally drink more from a fountain
			amount*=3;
		}
		amount = UMIN(amount, obj->value[1]);
		break;
		
	}
	
    if (!IS_NPC(ch) && !IS_IMMORTAL(ch) 
		&&  ch->pcdata->condition[COND_FULL] > 45)
    {
		ch->println( "You're too full to drink more." );
		return;
	}
	
    act( "$n drinks $T from $p.",
		ch, obj, liq_table[liquid].liq_name, TO_ROOM );
    act( "You drink $T from $p.",
		ch, obj, liq_table[liquid].liq_name, TO_CHAR );
	
    gain_condition( ch, COND_DRUNK,
		amount * liq_table[liquid].liq_affect[COND_DRUNK] / 36 );
    gain_condition( ch, COND_FULL,
		amount * liq_table[liquid].liq_affect[COND_FULL] / 7 );
    gain_condition( ch, COND_THIRST,
		amount * liq_table[liquid].liq_affect[COND_THIRST] / 10 );
	gain_condition(ch, COND_HUNGER,
		amount * liq_table[liquid].liq_affect[COND_HUNGER] / 2 );
	
	if ( !IS_NPC(ch) && ch->pcdata->condition[COND_DRUNK]  > 10 )
		ch->println("You feel drunk.");
	if ( !IS_NPC(ch) && ch->pcdata->condition[COND_FULL]   > 40 )
		ch->println("You are full.");
    if ( !IS_NPC(ch) && ch->pcdata->condition[COND_THIRST] > 40 )
		ch->println("Your thirst is quenched.");
	
    if ( obj->value[3] != 0 
		&& !HAS_CLASSFLAG(ch, CLASSFLAG_POISON_IMMUNITY)
		)
    {
		// The drink was poisoned!
		AFFECT_DATA af;		
		
		act( "$n chokes and gags.", ch, NULL, NULL, TO_ROOM );
		ch->println("You choke and gag.");
		af.where     = WHERE_AFFECTS;
		af.type      = gsn_poison;
		af.level	 = number_fuzzy(amount); 
		af.duration  = 3 * amount;
		af.location  = APPLY_NONE;
		af.modifier  = 0;
		af.bitvector = AFF_POISON;
		affect_join( ch, &af );
	}
	
	// containers with -1 are infinite, so don't remove the amount.
    if(obj->value[1]>0){
        obj->value[1] -= amount;
		// remove the poison if it is emptied
		if(obj->value[1]<=0){
			obj->value[3]=0;
			obj->value[1]=0;
		}
	}
}

/**************************************************************************/
void do_eat( char_data *ch, char *argument )
{
	char arg[MIL];
    OBJ_DATA *obj;
	
	one_argument( argument, arg );
    if ( arg[0] == '\0' )
	{
		ch->println("Eat what?");
		return;
    }
	
    if ( ( obj = get_obj_carry( ch, arg ) ) == NULL )
    {
		ch->println("You do not have that item.");
		return;
    }
	
    if ( ch->level<LEVEL_IMMORTAL )
    {
        if ( obj->item_type != ITEM_FOOD && obj->item_type != ITEM_PILL )
        {
            ch->println("That's not edible.");
            return;
        }
		
        if ( !IS_NPC(ch) && ch->pcdata->condition[COND_FULL] > 40 
			&& obj->value[0]!=0 && obj->value[1]!=0 )
        {   
			ch->println("You are too full to eat more.");
            return;
        }
    }
	
    act( "$n eats $p.", ch, obj, NULL, TO_ROOM );
    act( "You eat $p.", ch, obj, NULL, TO_CHAR );
	
    switch ( obj->item_type )
    {
		
    case ITEM_FOOD:
		if ( !IS_NPC(ch) )
		{
			int condition;
			
			condition = ch->pcdata->condition[COND_HUNGER];
			gain_condition( ch, COND_HUNGER, obj->value[0] );
			gain_condition( ch, COND_FULL, obj->value[1]);
			if ( condition == 0 && ch->pcdata->condition[COND_HUNGER] > 0 )
				ch->println("You are no longer hungry.");
			else if ( ch->pcdata->condition[COND_FULL] > 40 )
				ch->println("You are full.");
		}
		
		if ( obj->value[3] != 0 
			&& !HAS_CLASSFLAG(ch, CLASSFLAG_POISON_IMMUNITY))
		{
			// The food was poisoned!- do a saving throw
			if ( saves_spell( number_fuzzy(obj->value[0])*2, ch, DAM_POISON))
			{
				act( "$n turns pale for a moment.", ch, 0, 0, TO_ROOM );
				ch->println("You feel a little sick for a moment but it passes.");
			}else{
				AFFECT_DATA af;
				
				act( "$n chokes and gags.", ch, 0, 0, TO_ROOM );
				ch->println("You choke and gag.");
				
				af.where	 = WHERE_AFFECTS;
				af.type      = gsn_poison;
				af.level 	 = number_fuzzy(obj->value[0]);
				af.duration  = 2 * obj->value[0];
				af.location  = APPLY_NONE;
				af.modifier  = 0;
				af.bitvector = AFF_POISON;
				affect_join( ch, &af );
			}
		}
		break;
		
    case ITEM_PILL:

		if ( IS_SET( ch->in_room->room_flags, ROOM_ANTIMAGIC )) {
			ch->println("Nothing seemed to happen.");
			extract_obj( obj );
			return;
		}


        obj_cast_spell( obj->value[1], obj->value[0], ch, ch, NULL );
        obj_cast_spell( obj->value[2], obj->value[0], ch, ch, NULL );
        obj_cast_spell( obj->value[3], obj->value[0], ch, ch, NULL );
        obj_cast_spell( obj->value[4], obj->value[0], ch, ch, NULL );

		 if (ch->fighting){
			WAIT_STATE( ch, 2*PULSE_VIOLENCE );
		 }else{
			WAIT_STATE( ch, 3*PULSE_VIOLENCE/2 );
		 }
        break;
    }
	
    extract_obj( obj );
    return;
}


/**************************************************************************/
// Remove an object, return true if object in wear location has been removed
bool remove_obj( char_data *ch, int iWear, bool fReplace )
{
    OBJ_DATA *obj;

	if ( ( obj = get_eq_char( ch, iWear ) ) == NULL )
	return true;

    if ( !fReplace )
	return false;

    if ( !IS_IMMORTAL(ch) && IS_SET(obj->extra_flags, OBJEXTRA_NOREMOVE) )
    {
		act( "You can't remove $p.", ch, obj, NULL, TO_CHAR );
		return false;
    }

	if (obj->wear_loc==WEAR_SECONDARY){
		act( "You stop using $p as your offhand weapon.", ch, obj, NULL, TO_CHAR );
	}else if (!IS_SET(obj->extra_flags,OBJEXTRA_LODGED)){ 
		act( "You stop using $p.", ch, obj, NULL, TO_CHAR );
	}

	if ( IS_SET( obj->extra_flags, OBJEXTRA_LODGED ))
	{
		REMOVE_BIT( obj->extra_flags, OBJEXTRA_LODGED );
		if ( get_eq_char( ch, WEAR_LODGED_ARM ))
		{
			act( "With a tug you dislodge $p from your arm.", ch, obj, NULL, TO_CHAR );
			act( "With a tug, $n dislodges $p from $s arm.", ch, obj, NULL, TO_ROOM );
			ch->hit -= obj->level / 4;
			obj->wear_loc = -1;
			return true;
		}
		else if ( get_eq_char( ch, WEAR_LODGED_LEG ))
		{
			act( "With a good yank you dislodge $p from your leg.", ch, obj, NULL, TO_CHAR );
			act( "With a good yank, $n dislodges $p from $s leg.", ch, obj, NULL, TO_ROOM );
			ch->hit  -= obj->level / 5;
			ch->move -= obj->level / 2;
			obj->wear_loc = -1;
			return true;
		}
		else if ( get_eq_char( ch, WEAR_LODGED_RIB ))
		{
			act( "With a wrenching pull you dislodge $p from your chest.", ch, obj, NULL, TO_CHAR );
			act( "With a wrenching pull, $n dislodges $p from $s chest.", ch, obj, NULL, TO_ROOM );
			ch->hit  -= obj->level / 2;
			obj->wear_loc = -1;
			return true;
		}
	}else{
		act( "$n stops using $p.", ch, obj, NULL, TO_ROOM );
	}

    unequip_char( ch, obj );
    return true;
}


/**************************************************************************/
/*
 * Wear one object.
 * Optional replacement of existing objects.
 * Big repetitive code, ick.
 */
void wear_obj( char_data *ch, OBJ_DATA *obj, bool fReplace, bool hold )
{
	obj_data *existing_primary_position; // used for object wearing preference system 
	// --- object wearing preference system works as follows:
	// if 'ch' is wearing 2 items in a particular wear location 
	// (e.g. WEAR_FINGER_L, WEAR_FINGER_R), and try to wear an object 
	// into that wear location, the code will favour replacing the item 
	// which doesn't have the same vnum as the object about to be put on.
	// The effect of this, is you can type 'wear golden', 'wear golden'...
	// and you would replace both of your rings with golden rings...
	// instead of the second 'wear golden' replacing what you just
	// wore using the first 'wear golden'.
	// - Kal, Sept 03


	// do check if objects level is LEVEL_IMMORTAL or higher, and it is a
	// mortal trying to wear the object, remove it from them.
	if (( obj->level>LEVEL_HERO)&&(ch->level < LEVEL_IMMORTAL))
	{
		char buf[MSL];
		
		sprintf( buf, "`x%s (%s) lvl %d`1tried to wear `1%s [%d] lvl %d`1It was removed since it was out of level.",
			ch->name,		ch->short_descr,	ch->level, 
			obj->short_descr,	obj->pIndexData->vnum, obj->level);
		autonote(NOTE_SNOTE, "p_anote()","`Yoverlevel object!`x", "imm", buf, true);
		
		ch->printlnf( "You try to use %s and it crumbles to dust.", obj->short_descr );
		
		act( "$n tries to use $p, but it crumbles to dust.",
			ch, obj, NULL, TO_ROOM );
		
		extract_obj(obj);
		return;
	}
	
	if ( IS_SET( obj->attune_flags, ATTUNE_NEED_TO_USE ))
	{
		if ( ch->id != obj->attune_id )
		{
			act( "You cannot use $p until you have attuned yourself to it.", ch, obj, NULL, TO_CHAR );
			return;
		}
	}
	
    if(IS_SET(obj->extra_flags, OBJEXTRA_MAGIC)
		&& HAS_CLASSFLAG(ch, CLASSFLAG_MAGIC_ANTIPATHY))
    {
		act("You have great antipathy towards $p.",ch,obj,NULL,TO_CHAR);
		return;
    }
	
	if ( !IS_ADMIN( ch ))  // Immortal requested not to have this but I wanted it :)
	{
		if(!IS_NPC(ch) && obj->pIndexData->relative_size<
			race_table[ch->race]->low_size &&
			obj->item_type != ITEM_LIGHT &&
			!CAN_WEAR( obj, OBJWEAR_HOLD)  &&
			!CAN_WEAR( obj, OBJWEAR_FLOAT) )
		{
			act("$p is too small for you.",ch,obj,NULL,TO_CHAR);
			return;
		}
		
		
		if(!IS_NPC(ch) && obj->pIndexData->relative_size>
			race_table[ch->race]->high_size &&
			obj->item_type != ITEM_LIGHT &&
			!CAN_WEAR( obj, OBJWEAR_FLOAT) )
		{
			act("$p is too large for you.", ch, obj, NULL, TO_CHAR);
			return;
		}
	}
	
	if ( !IS_IMMORTAL( ch ))
	{
		if(DISALLOWED_OBJECT_FOR_CHAR(obj, ch))
		{
			act("$p cannot be used by your class.",ch,obj,NULL,TO_CHAR); 
			return;
		} 
	}
	
	if ( obj->item_type == ITEM_LIGHT )
	{
		if ( !remove_obj( ch, WEAR_LIGHT, fReplace ) )
			return;
		act( "$n lights $p and holds it.", ch, obj, NULL, TO_ROOM );
		act( "You light $p and hold it.",  ch, obj, NULL, TO_CHAR );
		equip_char( ch, obj, WEAR_LIGHT );
		return;
	}
	
	if ( CAN_WEAR( obj, OBJWEAR_FINGER ) )
	{
		// remove something from the fingers to make way for the item about to go on if necessary
		existing_primary_position=get_eq_char( ch, WEAR_FINGER_L );
		if ( existing_primary_position && get_eq_char( ch, WEAR_FINGER_R ))
		{
			if(existing_primary_position->pIndexData  
				&& obj->pIndexData
				&& existing_primary_position->pIndexData->vnum==obj->pIndexData->vnum)
			{
				// favour replacing the secondary position item over doing an exact swap
				if(   !remove_obj( ch, WEAR_FINGER_R, fReplace )
					&&!remove_obj( ch, WEAR_FINGER_L, fReplace ) ){
					return;
				}
			}else{
				if(   !remove_obj( ch, WEAR_FINGER_L, fReplace )
					&&!remove_obj( ch, WEAR_FINGER_R, fReplace ) ){
					return;
				}
			}
		}

		if ( get_eq_char( ch, WEAR_FINGER_L ) == NULL )
		{
			act( "$n wears $p on $s left finger.",    ch, obj, NULL, TO_ROOM );
			act( "You wear $p on your left finger.",  ch, obj, NULL, TO_CHAR );
			equip_char( ch, obj, WEAR_FINGER_L );
			return;
		}
		
		if ( get_eq_char( ch, WEAR_FINGER_R ) == NULL )
		{
			act( "$n wears $p on $s right finger.",   ch, obj, NULL, TO_ROOM );
			act( "You wear $p on your right finger.", ch, obj, NULL, TO_CHAR );
			equip_char( ch, obj, WEAR_FINGER_R );
			return;
		}
		
		bug( "Wear_obj: no free finger.");
		ch->println("You already wear two rings.");
		return;
	}
	
	if ( CAN_WEAR( obj, OBJWEAR_NECK ) )
	{
		// remove something from the neck to make way for the item about to go on if necessary
		existing_primary_position=get_eq_char( ch, WEAR_NECK_1 );
		if ( existing_primary_position && get_eq_char( ch, WEAR_NECK_2 ))
		{
			if(existing_primary_position->pIndexData  
				&& obj->pIndexData
				&& existing_primary_position->pIndexData->vnum==obj->pIndexData->vnum)
			{
				// favour replacing the secondary position item over doing an exact swap
				if(   !remove_obj( ch, WEAR_NECK_2, fReplace )
					&&!remove_obj( ch, WEAR_NECK_1, fReplace ) ){
					return;
				}
			}else{
				if(   !remove_obj( ch, WEAR_NECK_1, fReplace )
					&&!remove_obj( ch, WEAR_NECK_2, fReplace ) ){
					return;
				}
			}
		}

		// put the item onto the free wear position
		if ( get_eq_char( ch, WEAR_NECK_1 ) == NULL )
		{
			act( "$n wears $p around $s neck.",   ch, obj, NULL, TO_ROOM );
			act( "You wear $p around your neck.", ch, obj, NULL, TO_CHAR );
			equip_char( ch, obj, WEAR_NECK_1 );
			return;
		}
		
		if ( get_eq_char( ch, WEAR_NECK_2 ) == NULL )
		{
			act( "$n wears $p around $s neck.",   ch, obj, NULL, TO_ROOM );
			act( "You wear $p around your neck.", ch, obj, NULL, TO_CHAR );
			equip_char( ch, obj, WEAR_NECK_2 );
			return;
		}
		
		bug("Wear_obj: no free neck.");
		ch->println("You already wear two neck items.");
		return;
	}
	
	if ( CAN_WEAR( obj, OBJWEAR_TORSO ) )
	{
		if ( !remove_obj( ch, WEAR_TORSO, fReplace ) )
			return;
		act( "$n wears $p on $s torso.",   ch, obj, NULL, TO_ROOM );
		act( "You wear $p on your torso.", ch, obj, NULL, TO_CHAR );
		equip_char( ch, obj, WEAR_TORSO );
		return;
	}
	
	if ( CAN_WEAR( obj, OBJWEAR_HEAD ) )
	{
		if ( !remove_obj( ch, WEAR_HEAD, fReplace ) )
			return;
		act( "$n wears $p on $s head.",   ch, obj, NULL, TO_ROOM );
		act( "You wear $p on your head.", ch, obj, NULL, TO_CHAR );
		equip_char( ch, obj, WEAR_HEAD );
		return;
	}
	
	if ( CAN_WEAR( obj, OBJWEAR_LEGS ) )
	{
		if ( !remove_obj( ch, WEAR_LEGS, fReplace ) )
			return;
		act( "$n wears $p on $s legs.",   ch, obj, NULL, TO_ROOM );
		act( "You wear $p on your legs.", ch, obj, NULL, TO_CHAR );
		equip_char( ch, obj, WEAR_LEGS );
		return;
	}
	//////////////
	if (CAN_WEAR(obj, OBJWEAR_EYES))
	{
		if (!remove_obj (ch, WEAR_EYES, fReplace)){
			return;
		}
		act( "$n wears $p on $s eyes.",   ch, obj, NULL, TO_ROOM );
		act( "You wear $p on your eyes.", ch, obj, NULL, TO_CHAR );
		equip_char (ch, obj, WEAR_EYES);
		return;
	}
	
	
	if (CAN_WEAR(obj, OBJWEAR_EAR))
	{
		// remove something from the ears to make way for the item about to go on if necessary
		existing_primary_position=get_eq_char( ch, WEAR_EAR_L );
		if ( existing_primary_position && get_eq_char( ch, WEAR_EAR_R ))
		{
			if(existing_primary_position->pIndexData  
				&& obj->pIndexData
				&& existing_primary_position->pIndexData->vnum==obj->pIndexData->vnum)
			{
				// favour replacing the secondary position item over doing an exact swap
				if(   !remove_obj( ch, WEAR_EAR_R, fReplace )
					&&!remove_obj( ch, WEAR_EAR_L, fReplace ) ){
					return;
				}
			}else{
				if(   !remove_obj( ch, WEAR_EAR_L, fReplace )
					&&!remove_obj( ch, WEAR_EAR_R, fReplace ) ){
					return;
				}
			}
		}

		if (get_eq_char (ch, WEAR_EAR_L) == NULL)
		{
			act( "$n wears $p on $s left ear.",   ch, obj, NULL, TO_ROOM );
			act( "You wear $p on your left ear.", ch, obj, NULL, TO_CHAR );
			equip_char (ch, obj, WEAR_EAR_L);
			return;
		}
		
		if (get_eq_char (ch, WEAR_EAR_R) == NULL)
		{
			act( "$n wears $p on $s right ear.",   ch, obj, NULL, TO_ROOM );
			act( "You wear $p on your right ear.", ch, obj, NULL, TO_CHAR );
			equip_char (ch, obj, WEAR_EAR_R);
			return;
		}
		
		bug("Wear_obj: no free ear.");
		ch->println("You already wear two ear rings.");
		return;
	}
    
	if (CAN_WEAR(obj, OBJWEAR_FACE))
	{
		if (!remove_obj (ch, WEAR_FACE, fReplace))
			return;		
		act( "$n wears $p on $s face.",   ch, obj, NULL, TO_ROOM );
		act( "You wear $p on your face.", ch, obj, NULL, TO_CHAR );
		equip_char (ch, obj, WEAR_FACE);
		return;
	}

	if ( CAN_WEAR( obj, OBJWEAR_BACK ) )
	{
		if ( !remove_obj( ch, WEAR_BACK, fReplace ) )
		return;
		act( "$n wears $p on $s back.",   ch, obj, NULL, TO_ROOM );
		act( "You wear $p on your back.", ch, obj, NULL, TO_CHAR );
		equip_char( ch, obj, WEAR_BACK );
		return;
	}
	
	if (CAN_WEAR(obj, OBJWEAR_ANKLE))
	{
		// remove something from the ankles to make way for the item about to go on if necessary
		existing_primary_position=get_eq_char( ch, WEAR_ANKLE_L );
		if ( existing_primary_position && get_eq_char( ch, WEAR_ANKLE_R ))
		{
			if(existing_primary_position->pIndexData  
				&& obj->pIndexData
				&& existing_primary_position->pIndexData->vnum==obj->pIndexData->vnum)
			{
				// favour replacing the secondary position item over doing an exact swap
				if(   !remove_obj( ch, WEAR_ANKLE_R, fReplace )
					&&!remove_obj( ch, WEAR_ANKLE_L, fReplace ) ){
					return;
				}
			}else{
				if(   !remove_obj( ch, WEAR_ANKLE_L, fReplace )
					&&!remove_obj( ch, WEAR_ANKLE_R, fReplace ) ){
					return;
				}
			}
		}	
		
		if (get_eq_char (ch, WEAR_ANKLE_L) == NULL)
		{
			act( "$n wears $p on $s left ankle.",   ch, obj, NULL, TO_ROOM );
			act( "You wear $p on your left ankle.", ch, obj, NULL, TO_CHAR );
			equip_char (ch, obj, WEAR_ANKLE_L);
			return;
		}
		
		if (get_eq_char (ch, WEAR_ANKLE_R) == NULL)
		{
			act( "$n wears $p on $s right ankle.",   ch, obj, NULL, TO_ROOM );
			act( "You wear $p on your right ankle.", ch, obj, NULL, TO_CHAR );
			equip_char (ch, obj, WEAR_ANKLE_R);
			return;
		}
		
		bug("Wear_obj: no free ankle.");
		ch->println("You already wear items on both your ankles.");
		return;
	}
	//////////////
	
	if ( CAN_WEAR( obj, OBJWEAR_FEET ) )
	{
		if ( !remove_obj( ch, WEAR_FEET, fReplace ) )
			return;
		act( "$n wears $p on $s feet.",   ch, obj, NULL, TO_ROOM );
		act( "You wear $p on your feet.", ch, obj, NULL, TO_CHAR );
		equip_char( ch, obj, WEAR_FEET );
		return;
	}
	
	if ( CAN_WEAR( obj, OBJWEAR_HANDS ) )
	{
		if ( !remove_obj( ch, WEAR_HANDS, fReplace ) )
			return;
		act( "$n wears $p on $s hands.",   ch, obj, NULL, TO_ROOM );
		act( "You wear $p on your hands.", ch, obj, NULL, TO_CHAR );
		equip_char( ch, obj, WEAR_HANDS );
		return;
	}
	
	if ( CAN_WEAR( obj, OBJWEAR_ARMS ) )
	{
		if ( !remove_obj( ch, WEAR_ARMS, fReplace ) )
			return;
		act( "$n wears $p on $s arms.",   ch, obj, NULL, TO_ROOM );
		act( "You wear $p on your arms.", ch, obj, NULL, TO_CHAR );
		equip_char( ch, obj, WEAR_ARMS );
		return;
	}
	
	if ( CAN_WEAR( obj, OBJWEAR_ABOUT ) )
	{
		if ( !remove_obj( ch, WEAR_ABOUT, fReplace ) )
			return;
		act( "$n wears $p about $s torso.",   ch, obj, NULL, TO_ROOM );
		act( "You wear $p about your torso.", ch, obj, NULL, TO_CHAR );
		equip_char( ch, obj, WEAR_ABOUT );
		return;
	}
	
	if ( CAN_WEAR( obj, OBJWEAR_WAIST ) )
	{
		if ( !remove_obj( ch, WEAR_WAIST, fReplace ) )
			return;
		act( "$n wears $p about $s waist.",   ch, obj, NULL, TO_ROOM );
		act( "You wear $p about your waist.", ch, obj, NULL, TO_CHAR );
		equip_char( ch, obj, WEAR_WAIST );
		return;
	}
	
	if ( CAN_WEAR( obj, OBJWEAR_WRIST ) )
	{
		// remove something from the wrists to make way for the item about to go on if necessary
		existing_primary_position=get_eq_char( ch, WEAR_WRIST_L );
		if ( existing_primary_position && get_eq_char( ch, WEAR_WRIST_R ))
		{
			if(existing_primary_position->pIndexData  
				&& obj->pIndexData
				&& existing_primary_position->pIndexData->vnum==obj->pIndexData->vnum)
			{
				// favour replacing the secondary position item over doing an exact swap
				if(   !remove_obj( ch, WEAR_WRIST_R, fReplace )
					&&!remove_obj( ch, WEAR_WRIST_L, fReplace ) ){
					return;
				}
			}else{
				if(   !remove_obj( ch, WEAR_WRIST_L, fReplace )
					&&!remove_obj( ch, WEAR_WRIST_R, fReplace ) ){
					return;
				}
			}
		}	
		
		if ( get_eq_char( ch, WEAR_WRIST_L ) == NULL )
		{
			act( "$n wears $p around $s left wrist.",
				ch, obj, NULL, TO_ROOM );
			act( "You wear $p around your left wrist.",
				ch, obj, NULL, TO_CHAR );
			equip_char( ch, obj, WEAR_WRIST_L );
			return;
		}
		
		if ( get_eq_char( ch, WEAR_WRIST_R ) == NULL )
		{
			act( "$n wears $p around $s right wrist.",
				ch, obj, NULL, TO_ROOM );
			act( "You wear $p around your right wrist.",
				ch, obj, NULL, TO_CHAR );
			equip_char( ch, obj, WEAR_WRIST_R );
			return;
		}
		
		bug("Wear_obj: no free wrist.");
		ch->println("You already wear two wrist items.");
		return;
	}
	
	if ( CAN_WEAR( obj, OBJWEAR_SHIELD ) )
	{
		OBJ_DATA *weapon;
		
		if ( !remove_obj( ch, WEAR_SHIELD, fReplace ) )
			return;
		
		weapon = get_eq_char(ch,WEAR_WIELD);
		if (weapon != NULL && ch->size < SIZE_LARGE
			&&  IS_WEAPON_STAT(weapon,WEAPON_TWO_HANDS))
		{
			ch->println("Your hands are tied up with your two-handed weapon!");
			return;
		}
		
		if (get_eq_char (ch, WEAR_SECONDARY) != NULL)
		{
			if (hold){		
				if ( !remove_obj( ch, WEAR_SECONDARY, true) ){
					return;
				}
			}else{
				ch->println("You cannot use a shield while using 2 weapons.");
				ch->println("(Try using hold instead of wear to automatically remove your offhand weapon.)");
				return;
			}
		}						
		
		act( "$n wears $p as a shield.", ch, obj, NULL, TO_ROOM );
		act( "You wear $p as a shield.", ch, obj, NULL, TO_CHAR );
		equip_char( ch, obj, WEAR_SHIELD );
		return;
	}
	
	if ( CAN_WEAR( obj, OBJWEAR_WIELD ) )
	{
		int sn,skill;
		
		if ( !remove_obj( ch, WEAR_WIELD, fReplace ) )
			return;
		
		if ( !IS_NPC(ch) 
			&& get_obj_weight(obj) > ((ch->modifiers[STAT_ST] + 20) * 10 ))  
			
		{
			ch->println("It is too heavy for you to wield.");
			return;
		}
		
		if (!IS_NPC(ch) && ch->size < SIZE_LARGE
			&&  IS_WEAPON_STAT(obj,WEAPON_TWO_HANDS)
			&&  ((get_eq_char(ch,WEAR_SHIELD) != NULL)
			||  (get_eq_char(ch,WEAR_SECONDARY) != NULL)))
		{
			ch->println("You need two hands free for that weapon.");
			return;
		}
		
		if ( IS_SET( obj->extra2_flags, OBJEXTRA2_NOPRIMARY )) {
			act( "$p is designed to be wielded in your offhand.", ch, obj, NULL, TO_CHAR );
			return;
		}
		
		act( "$n wields $p.", ch, obj, NULL, TO_ROOM );
 		act( "You wield $p.", ch, obj, NULL, TO_CHAR );
		equip_char( ch, obj, WEAR_WIELD );
		
		sn = get_weapon_sn(ch);
		
		if (sn == gsn_hand_to_hand)
			return;
		
		skill = get_weapon_skill(ch,sn);
		
		if (skill >= 100)
			act("$p feels like a part of you!",ch,obj,NULL,TO_CHAR);
		else if (skill > 85)
			act("You feel quite confident with $p.",ch,obj,NULL,TO_CHAR);
		else if (skill > 70)
			act("You are skilled with $p.",ch,obj,NULL,TO_CHAR);
		else if (skill > 50)
			act("Your skill with $p is adequate.",ch,obj,NULL,TO_CHAR);
		else if (skill > 25)
			act("$p feels a little clumsy in your hands.",ch,obj,NULL,TO_CHAR);
		else if (skill > 1)
			act("You fumble and almost drop $p.",ch,obj,NULL,TO_CHAR);
		else
			act("You don't even know which end is up on $p.",
			ch,obj,NULL,TO_CHAR);
		
		return;
	}
	
	if ( CAN_WEAR( obj, OBJWEAR_HOLD ) )
	{
		if (get_eq_char (ch, WEAR_SECONDARY) != NULL)
		{
			if (hold){		
				if ( !remove_obj( ch, WEAR_SECONDARY, true) ){
					return;
				}
			}else{
				ch->println("You cannot hold an item while using 2 weapons.");
				ch->println("(Try using hold instead of wear to automatically remove your offhand weapon.)");
				return;
			}
		}
		
		if ( !remove_obj( ch, WEAR_HOLD, fReplace ) ){
			return;
		}
		act( "$n holds $p in $s hand.",   ch, obj, NULL, TO_ROOM );
		act( "You hold $p in your hand.", ch, obj, NULL, TO_CHAR );
		equip_char( ch, obj, WEAR_HOLD );
		return;
	}
	
	if ( CAN_WEAR(obj,OBJWEAR_FLOAT) )
	{
		if (!remove_obj(ch,WEAR_FLOAT, fReplace) )
			return;
		act("$n releases $p to float next to $m.",ch,obj,NULL,TO_ROOM);
		act("You release $p and it floats next to you.",ch,obj,NULL,TO_CHAR);
		equip_char(ch,obj,WEAR_FLOAT);
		return;
	}
	
	if ( CAN_WEAR( obj, OBJWEAR_LODGED_ARM ) )
	{
		if ( !remove_obj( ch, WEAR_LODGED_ARM, fReplace ) )
			return;
		equip_char( ch, obj, WEAR_LODGED_ARM );
		return;
	}
	
	if ( CAN_WEAR( obj, OBJWEAR_LODGED_LEG ) )
	{
		if ( !remove_obj( ch, WEAR_LODGED_LEG, fReplace ) )
			return;
		equip_char( ch, obj, WEAR_LODGED_LEG );
		return;
	}
	
	if ( CAN_WEAR( obj, OBJWEAR_LODGED_RIB ) )
	{
		if ( !remove_obj( ch, WEAR_LODGED_RIB, fReplace ) )
			return;
		equip_char( ch, obj, WEAR_LODGED_RIB );
		return;
	}
	
	
	
	if ( fReplace )
		ch->println("You can't wear, wield, or hold that.");
	
	return;
}

/**************************************************************************/
void do_wear( char_data *ch, char *argument )
{
	char arg[MIL];
	OBJ_DATA *obj;
	
	one_argument( argument, arg );
	
	if ( IS_NULLSTR(arg) )
	{
		ch->println( "Wear, wield what?" );
		return;
	}
	
	if ( !str_cmp( arg, "all" ) )
	{
		OBJ_DATA *obj_next;
		
		for ( obj = ch->carrying; obj != NULL; obj = obj_next )
		{
			obj_next = obj->next_content;
			if ( obj->wear_loc == WEAR_NONE && can_see_obj( ch, obj ) ){
				wear_obj ( ch, obj, false, false );
			}
		}
		return;
	}

	if ( ( obj = get_obj_carry( ch, arg ) ) == NULL ){
		ch->println( "You do not have that item." );
		return;
	}		
	wear_obj( ch, obj, true, false );
	
    return;
}

/**************************************************************************/
void do_hold( char_data *ch, char *argument )
{
	char arg[MIL];
	OBJ_DATA *obj;
	
	one_argument( argument, arg );
	
	if ( IS_NULLSTR(arg)){
		ch->println( "Hold what?" );
		return;
	}

	if ( ( obj = get_obj_carry( ch, arg ) ) == NULL ){
		ch->println( "You do not have that item." );
		return;
	}		

	wear_obj( ch, obj, true, true );
	
    return;
}


/**************************************************************************/
void do_remove( char_data *ch, char *argument )
{
    char arg[MIL];
    OBJ_DATA *obj;
	
    one_argument( argument, arg );
	
    if ( arg[0] == '\0' )
    {
		ch->println("Remove what?");
		return;
    }
	
    if (!str_cmp(arg, "all"))
    {
		// PC being ordered to do this
		if ( !IS_NPC(ch) && IS_SET( ch->dyn, DYN_IS_BEING_ORDERED ))
		{
			if ( ch->master )
				ch->println("Not going to happen.");
			return;
		}

        bool found =false;
        for ( obj = ch->carrying; obj != NULL; obj = obj->next_content )
        {
            if ( obj->wear_loc != WEAR_NONE
				&&   can_see_obj( ch, obj ) )
            {
				if (!found)
					found = true;
				if ( obj->wear_loc == WEAR_SHEATHED
				||	 obj->wear_loc == WEAR_CONCEALED )
				{
					obj->wear_loc = -1;
				    act( "$n stops using $p.", ch, obj, NULL, TO_ROOM );
					act( "You stop using $p.", ch, obj, NULL, TO_CHAR );
				}
				else
				{
					remove_obj( ch, obj->wear_loc, true );
				}
				WAIT_STATE(ch, 1);
				if (ch->fighting)
					WAIT_STATE(ch, 5);
            }
        }
        if (!found)
        {
            ch->println("You are not wearing anything.");
        }
        return;
    }
	
    if ( ( obj = get_obj_wear( ch, arg ) ) == NULL )
	{
		ch->println("You do not have that item.");
		return;
    }

	if ( obj->wear_loc == WEAR_SHEATHED
	||	 obj->wear_loc == WEAR_CONCEALED )
	{
		obj->wear_loc = -1;
	    act( "$n stops using $p.", ch, obj, NULL, TO_ROOM );
		act( "You stop using $p.", ch, obj, NULL, TO_CHAR );
	}
	else
		remove_obj( ch, obj->wear_loc, true );

	return;
}

/**************************************************************************/
void do_quaff( char_data *ch, char *argument )
{
    char arg[MIL];
	OBJ_DATA *obj;
	
    one_argument( argument, arg );
	
	if ( IS_OOC( ch )) {
		ch->println("Not in an OOC room.");
		return;
	}
	
    if ( arg[0] == '\0' )
    {
		ch->println("Quaff what?");
		return;
    }
	
    if ( ( obj = get_obj_carry( ch, arg ) ) == NULL )
	{
		ch->println("You do not have that potion.");
		return;
    }
	
    if ( obj->item_type != ITEM_POTION )
    {
		ch->println("You can quaff only potions.");
		return;
	}
	
	if ( IS_SET( ch->in_room->room_flags, ROOM_ANTIMAGIC ))
	{
		act( "$n quaffs $p.", ch, obj, NULL, TO_ROOM );
		act( "You quaff $p but nothing seems to happen.", ch, obj, NULL ,TO_CHAR );
		extract_obj( obj );
		return;
	}

	// Added so that barbarians have a good chance of 'fearing magic'. - Tib
	if(IS_SET(obj->extra_flags, OBJEXTRA_MAGIC)
		&& HAS_CLASSFLAG(ch, CLASSFLAG_MAGIC_ANTIPATHY) && number_range(1,3)==1)
	{
		if ( IS_AFFECTED( ch, AFF_FEAR ) || IS_AFFECTED2( ch, AFF2_FEAR_MAGIC) )
		{
			ch->println("The potion shatters on the ground, falling from your shaking hands.");
			extract_obj( obj );
			return;
		}

		act( "$n quaffs $p and screams in terror!", ch, obj, NULL, TO_ROOM );
		act( "You quaff $p and scream as you realize the potion was magic.", 
			ch, obj, NULL ,TO_CHAR );
		msp_to_room(MSPT_ACTION, MSP_SOUND_QUAFF, 0, ch, false, true);
		spell_fear_magic( gsn_fear_magic, UMIN(250, (obj->level*5)), ch, ch, 0);
		extract_obj( obj );
		return;
	}

	if ((ch->level < obj->level)&&(obj->level>10))
	{
		ch->println("This liquid is too powerful for you to drink.");
		return;
	}
	
	act( "$n quaffs $p.", ch, obj, NULL, TO_ROOM );
	act( "You quaff $p.", ch, obj, NULL ,TO_CHAR );


	msp_to_room(MSPT_ACTION, MSP_SOUND_QUAFF, 
					0,
					ch,
					false,
					true);	
	
	obj_cast_spell( obj->value[1], obj->value[0], ch, ch, NULL );
	obj_cast_spell( obj->value[2], obj->value[0], ch, ch, NULL );
	obj_cast_spell( obj->value[3], obj->value[0], ch, ch, NULL );
	obj_cast_spell( obj->value[4], obj->value[0], ch, ch, NULL );
	
	if (ch->fighting){
		WAIT_STATE( ch, 2*PULSE_VIOLENCE );
	}else{
		WAIT_STATE( ch, 3*PULSE_VIOLENCE/2 );
	}
	
	
	extract_obj( obj );
	return;
}

/**************************************************************************/
void do_recite( char_data *ch, char *argument )
{
	char arg1[MIL];
	char arg2[MIL];
	char_data *victim;
	OBJ_DATA *scroll;
	OBJ_DATA *obj;

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

	if ( ( scroll = get_obj_carry( ch, arg1 ) ) == NULL )
	{
		ch->println( "You do not have that scroll." );
		return;
	}

	if(IS_AFFECTED( ch, AFF_BLIND ) ){
		ch->println( "You can't recite scrolls while blind." );
		return;
	}

	if ( scroll->item_type != ITEM_SCROLL )
	{
		ch->println( "You can recite only scrolls." );
		return;
	}

	if (( ch->level < scroll->level)&&(scroll->level>10))
	{
		ch->println( "This scroll is too complex for you to comprehend." );
		return;
	}

	obj = NULL;
	if ( arg2[0] == '\0' )
	{
		victim = ch;
	}
	else
	{
		if ( ( victim = get_char_room ( ch, arg2 ) ) == NULL
			&& ( obj    = get_obj_here  ( ch, arg2 ) ) == NULL )
		{
			ch->println("You can't find it.");
			return;
		}
	}

	act( "$n recites $p.", ch, scroll, NULL, TO_ROOM );
	act( "You recite $p.", ch, scroll, NULL, TO_CHAR );

	if (number_percent() >= 20 + get_skill(ch,gsn_scrolls) * 4/5)
	{
		ch->println("You mispronounce a syllable.");
		check_improve(ch,gsn_scrolls,false,2);
	}
	else
	{
		obj_cast_spell( scroll->value[1], scroll->value[0], ch, victim, obj );
		obj_cast_spell( scroll->value[2], scroll->value[0], ch, victim, obj );
		obj_cast_spell( scroll->value[3], scroll->value[0], ch, victim, obj );
		obj_cast_spell( scroll->value[4], scroll->value[0], ch, victim, obj );
		check_improve(ch,gsn_scrolls,true,2);
	}

	extract_obj( scroll );
	return;
}


/**************************************************************************/
void do_brandish( char_data *ch, char *argument )
{
    OBJ_DATA  *staff;
    int sn;

    // new stuff here 
    char arg2[MIL];
    char_data *victim;
    OBJ_DATA *obj;
    void *vo;
    int target;

    strcpy(arg2, argument);

    if ( ( staff = get_eq_char( ch, WEAR_HOLD ) ) == NULL )
    {
        ch->println("You hold nothing in your hands to brandish.");
        return;
    }

    if ( staff->item_type != ITEM_STAFF )
    {
        ch->println("You can brandish only with a staff.");
        return;
    }

	if ( IS_SET( ch->in_room->room_flags, ROOM_ANTIMAGIC )) {
		act( "Your $p doesn't seem to have any power.", ch, staff, NULL, TO_CHAR );
		return;
	}

    if (( sn = staff->value[3] ) < 0
    ||   sn >= MAX_SKILL
    ||   skill_table[sn].spell_fun == 0 )
    {
        bugf( "do_brandish(): bad sn %d.", sn );
        return;
    }

    WAIT_STATE( ch, 2 * PULSE_VIOLENCE );

    if (staff->value[2] < -1)
    {
        staff->value[2] = -1;
    }

    if (( staff->value[2] > 0 ) || (staff->value[2] = -1))
    {
        act( "$n brandishes $p.", ch, staff, NULL, TO_ROOM );
        act( "You brandish $p.",  ch, staff, NULL, TO_CHAR );

        if ( ch->level < staff->level
        ||  (get_skill(ch,gsn_staves)==0)
        ||   number_percent() >= 20 + get_skill(ch,gsn_staves) * 4/5)
        {
            act ("You fail to invoke $p.",ch,staff,NULL,TO_CHAR);
            act ("...and nothing happens.",ch,NULL,NULL,TO_ROOM);
            check_improve(ch,gsn_staves,false,2);
        }    
        else // cast the spell 
        {   
            // Locate targets.
            victim  = NULL;
            obj     = NULL;
            vo      = NULL;
            target  = TARGET_NONE;
              
            switch ( skill_table[sn].target )
            {
            default:
                bugf( "do_cast(): bad target for sn %d.", sn );
                return;
        
            case TAR_IGNORE:
                break;
        
            case TAR_MOB_OFFENSIVE:
            case TAR_CHAR_OFFENSIVE:
                if ( arg2[0] == '\0' )
                {
                    if ( ( victim = ch->fighting ) == NULL )
                    {
                        ch->println("Brandish the object while aiming it towards whom?");
                        return;
                    }
                }
                else
                {
                    if ( ( victim = get_char_room( ch, arg2 ) ) == NULL )
                    {
                        ch->println("They aren't here.");
                        return;
                    }
                }
           
                if ( !IS_NPC(ch) )
                {
            
                    if (is_safe(ch,victim) && victim != ch)
                    {
                        ch->println("Not on that target.");
                        return;
                    }
                }
            
                if ( IS_AFFECTED(ch, AFF_CHARM) && ch->master == victim )
                {
                    ch->println("You can't do that on your own follower.");
                    return;
                }
            
                vo = (void *) victim;
                target = TARGET_CHAR;
                break;
            
            case TAR_CHAR_DEFENSIVE:
                if ( arg2[0] == '\0' )
                {
                    victim = ch;
                }
                else
                {
                    if ( ( victim = get_char_room( ch, arg2 ) ) == NULL )
                    {
                    ch->println("They aren't here.");
                    return;
                    }
                }
            
                vo = (void *) victim;
                target = TARGET_CHAR;
                break;
            
            case TAR_CHAR_SELF:
                if ( arg2[0] != '\0' && !is_name( arg2, ch->name ) )
                {
                    ch->println("You can't brandish this object while aiming it at another.");
                    return;
                }
            
                vo = (void *) ch;
                target = TARGET_CHAR;
                break;
            
            case TAR_OBJ_INV:
                if ( arg2[0] == '\0' )
                {
                    ch->println("You must brandish the object towards another object?");
                     return;
                }
            
                if ( ( obj = get_obj_carry( ch, arg2 ) ) == NULL )
                {
                    ch->println("You are not carrying that.");
                    return;
                }
            
                vo = (void *) obj;
                target = TARGET_OBJ;
                break;
            
            case TAR_OBJ_MOB_OFF:
            case TAR_OBJ_CHAR_OFF:
                if (arg2[0] == '\0')
				{
					if ((victim = ch->fighting) == NULL)
                    {
						ch->println("Brandish the object on whom or what?");
						return;
                    }
            
                    target = TARGET_CHAR;
                }
                else if ((victim = get_char_room(ch,arg2)) != NULL)
                {
                    target = TARGET_CHAR;
                }
            
                if (target == TARGET_CHAR) // check the sanity of the attack 
                {
                    if(is_safe_spell(ch,victim,false) && victim != ch)
                    {
						ch->println("Not on that target.");
						return;
					}
            
                        if ( IS_AFFECTED(ch, AFF_CHARM) && ch->master == victim )
                        {
                            ch->println("You can't do that on your own follower.");
							return;
						}
						vo = (void *) victim;
				}
				else if ((obj = get_obj_here(ch,arg2)) != NULL)
				{
					vo = (void *) obj;
					target = TARGET_OBJ;
				}
				else
				{
					ch->println("You don't see that here.");
					return;
				}
				break;
			
			case TAR_OBJ_CHAR_DEF:
				if (arg2[0] == '\0')
				{
					vo = (void *) ch;
					target = TARGET_CHAR;
				}
				else if ((victim = get_char_room(ch,arg2)) != NULL)
				{
					vo = (void *) victim;
					target = TARGET_CHAR;
				}
				else if ((obj = get_obj_carry(ch,arg2)) != NULL)
				{
					vo = (void *) obj;
					target = TARGET_OBJ;
				}
				else
				{
					ch->println("You don't see that here.");
					return;
				}
				break;
			} // end of spell type switch
			
			if (IS_NPC(ch) || class_table[ch->clss].fMana)
				// clss has spells
				(*skill_table[sn].spell_fun) ( sn, staff->value[0], ch, vo,target);
			else
				(*skill_table[sn].spell_fun) (sn, staff->value[0], ch, vo,target);
			check_improve(ch,gsn_staves,true,2);
		}
	}

    // get rid of the staff if required 
    if ( staff->value[2] == -1)
	{
		return;
	}
	staff->value[2]--;

	if (staff->value[2] < 1)
	{
		act( "$n's $p blazes bright and is gone.", ch, staff, NULL, TO_ROOM );
		act( "Your $p blazes bright and is gone.", ch, staff, NULL, TO_CHAR );
		extract_obj( staff );
	}

	return;
}

/**************************************************************************/
void do_apply( char_data *ch, char *argument )
{
	char arg[MIL];
	char_data *victim;
	OBJ_DATA *poultice;

	if ( IS_OOC( ch )) {
		ch->println( "Not in an OOC room." );
		return;
	}

	if ( get_skill( ch, gsn_apply ) == 0 )
	{
		ch->println( "You are not educated in the herbal arts." );
		return;
	}

	one_argument( argument, arg );
	
	if ( arg[0] == '\0' )
	{
		ch->println( "Apply what to whom?" );
		return;
	}

	if (( poultice = get_eq_char( ch, WEAR_HOLD )) == NULL )
 	{
		ch->println( "You hold nothing in your hand." );
		return;
	}

	if ( poultice->item_type != ITEM_POULTICE )
	{
		ch->println( "You are not holding a suitable medicinal component." );
		return;
	}

	if (( victim = get_char_room ( ch, arg ) ) == NULL )
	{
		ch->println( "They are not here." );
		return;
	}

	WAIT_STATE( ch, 2 * PULSE_VIOLENCE );

	if ( poultice->value[2] != 0 )
	{
		act( "$n applies $p to $N.", ch, poultice, victim, TO_ROOM );
		act( "You apply $p to $N.", ch, poultice, victim, TO_CHAR );
	}

 	if ( ch->level < poultice->level 
	||  number_percent() >= 20 + get_skill( ch, gsn_apply ) * 4/5 )
	{
		act( "Your efforts to apply $p didn't produce any noticeable effects.",
			ch, poultice, NULL, TO_CHAR );
		act( "$n's efforts to apply $p had no noticeable effects.",
			ch, poultice, NULL, TO_ROOM );
		check_improve( ch, gsn_apply, false, 2 );
	}
	else
	{
		obj_cast_spell( poultice->value[3], poultice->value[0], ch, victim, NULL );
		check_improve( ch, gsn_apply, true, 2 );
	}

    if ( --poultice->value[2] == 0 )
    {
		act( "$n has used up $p.", ch, poultice, NULL, TO_ROOM );
		act( "You have used up $p.", ch, poultice, NULL, TO_CHAR );
		extract_obj( poultice );
	}

	if ( poultice->value[2] < 0 )
		poultice->value[2] = -1;		// set it back to infinite

    return;
}

   
/**************************************************************************/
void duel_unprotect_victim(char_data *victim);
/**************************************************************************/
void do_steal( char_data *ch, char *argument )
{
    char buf  [MSL];
    char arg1 [MIL];
    char arg2 [MIL];
    char_data *victim;
    OBJ_DATA *obj;
    int percent;

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

    if ( arg1[0] == '\0' || arg2[0] == '\0' )
    {
        ch->println( "Steal what from whom?" );
        return;
    }

	// ooc rooms
	if(!IS_IMMORTAL(ch) && IS_SET(ch->in_room->room_flags,ROOM_OOC)){
		ch->println("Not in an OOC room.");
		return;
	}

    if ( ( victim = get_char_room( ch, arg2 ) ) == NULL )
    {
        ch->println( "They aren't here." );
        return;
    }

	if(!IS_NPC(victim) && victim->level<10 && ch->level>(victim->level+10)
		&& !IS_NPC(ch) && !IS_IMMORTAL(ch))
	{
		ch->println( "A faint wall of light appears to surround them preventing you from robbing them." );
		return;
	}

	if (!IS_IMMORTAL(ch) && !IS_NPC(ch) && !IS_NPC(victim) && GAMESETTING4(GAMESET4_PREVENT_STEALING_FROM_PLAYERS)){
		if(GAMESETTING4(GAMESET4_OOC_PREVENTION_MESSAGES)){
			ch->println( "Stealing from other players is not permitted on this mud." );
		}else{
			ch->println( "A faint wall of light appears to surround them preventing you from robbing them." );
		}
		return;
	}

    if ( victim == ch ){
        ch->println( "That's pointless." );
        return;
    }

	if(GAMESETTING5(GAMESET5_NO_STEALING_FROM_FIGHTING_CHARACTERS) && victim->fighting){
		ch->println( "Not while they are fighting!" );
		return;
	}

    if (is_safe(ch,victim)){
        return;
	}

    if ( IS_NPC(victim) && victim->position == POS_FIGHTING)
    {
        ch->println( "You'd better not -- you might get hit." );
        return;
    }

	// optional stealing checks which affect stealing between players
	if (!IS_IMMORTAL(ch) && !IS_NPC(victim)){
		if( GAMESETTING4(GAMESET4_MUST_BE_IN_CLAN_TO_STEAL_FROM_PLAYERS) ){
			ch->println( "You are not in a clan, stealing from players is restricted to clan members." );
			return;
		}
		
		if( GAMESETTING4(GAMESET4_NO_STEALING_FROM_NON_CLANNED) && !victim->clan ){
			ch->println( "They are not in a clan, stealing from players is restricted to clan members." );
			return;
		}

		// optional checks relating to letgain status
		if(GAMESETTING_LETGAINING_IS_REQUIRED){

			// is the potential thief letgained
			if ( GAMESETTING4(GAMESET4_MUST_BE_LETGAINED_TO_STEAL_FROM_PLAYERS)
				&& !IS_LETGAINED(ch))
			{
				ch->println("You can't steal from non letgained players.");
				return;
			}

			// is the intended victim letgained
			if ( GAMESETTING4(GAMESET4_NO_STEALING_FROM_NON_LETGAINED)
				&& !IS_LETGAINED(victim))
			{
				ch->println("You can't steal from non letgained players.");
				return;
			}

			if(GAMESETTING5(GAMESET5_MUST_BE_ACTIVE_TO_BE_INVOLVED_IN_STEALING)){
   				if(!HAS_CONFIG(victim, CONFIG_ACTIVE))
		 		{
 					ch->println( "You can't steal from non active players." );
 					return;
		 		}
  		 		if(!HAS_CONFIG(ch, CONFIG_ACTIVE))
		 		{
 					ch->println( "You must be active in order to steal from players." );
		 			return;
				}
			}
  
 			if(HAS_CONFIG2(ch,CONFIG2_NOPKILL))
		 	{
 				ch->println( "A faint wall of light appears to surround them preventing you from robbing them." );
		 		return;
 			}
 
		 	if(HAS_CONFIG2(victim,CONFIG2_NOPKILL))
 			{
		 		ch->println( "A faint wall of light appears to surround them preventing you from robbing them." );
 				return;
		 	}
 
		}
		// optional: stealing only permitted within ten levels of each other
		if (GAMESETTING4(GAMESET4_NO_STEALING_OUTSIDE_10_LEVELS)){	
			if ( (ch->level - victim->level) > 10  || (ch->level - victim->level) < -10 ) {
				ch->println("You may not steal from a character of more than 10 levels difference to your own." );
				return;
			}
		}
	}

	// silent imms dont get lagged
	if (!IS_SILENT(ch) || !IS_ICIMMORTAL(ch)){
		WAIT_STATE( ch, skill_table[gsn_steal].beats );
	}

	// for 20 minutes you are unsafe if you steal
	if(!IS_NPC(ch) && !IS_NPC(victim)){
		ch->pcdata->unsafe_due_to_stealing_till=current_time+ 20*60; 

		if(ch->duels){
			duel_unprotect_victim(ch);
		}
	}

	if (IS_ICIMMORTAL(victim)){
		ch->println( "You failed." );
		victim->wraplnf( "NOTICE: %s attempted to steal '%s' from you and failed because you are considered an IC immortal.",
			PERS(ch, victim), arg1);
		return;
	}

    percent  = number_percent();
    if (get_skill(ch,gsn_steal) >= 1){
        percent  += ( IS_AWAKE(victim) ? 10 : -50 );
	}
	if(victim->pcdata && victim->pcdata->is_trying_aware){
		percent += (get_skill(victim, gsn_awareness))/6;
	}

    if ( (IS_KEEPER(victim))
        ||(!IS_NPC(ch) && (percent+2*victim->level-2*ch->level > get_skill(ch,gsn_steal)))
		||(!IS_NPC(ch) && get_skill(ch,gsn_steal)==0)  
		|| (ch->level<=LEVEL_HERO && victim->level>LEVEL_HERO))
    {
        /*
         * Failure.
         */
        ch->println( "Oops." );

		// make them go visible
		affect_strip ( ch, gsn_invisibility					);
		affect_strip ( ch, gsn_mass_invis					);
		affect_strip ( ch, gsn_sneak						);
		REMOVE_BIT   ( ch->affected_by, AFF_HIDE			);
		REMOVE_BIT   ( ch->affected_by, AFF_INVISIBLE		);
		REMOVE_BIT   ( ch->affected_by, AFF_SNEAK			);
		
		if (!IS_NPC(victim))
		{
			ch->pksafe=0;
			ch->pknorecall= (UMAX(ch->pknorecall,2));
			ch->pknoquit=(UMAX(ch->pknoquit,10));
		}

        if (IS_AWAKE(victim)){
            act( "$n tried to steal from YOU!", ch, NULL, victim, TO_VICT );
			if( victim->pcdata && victim->pcdata->is_trying_aware )
				check_improve(victim,gsn_awareness,true,1);
		}else{
			if( victim->pcdata && victim->pcdata->is_trying_aware
			 && get_skill(victim, gsn_awareness) > number_percent() )
			{
				victim->println( "You feel the presence of another person lurking near you." );
				victim->println( "You feel someone try to take something from you and instantly pop awake!" );
				victim->position=POS_RESTING;
				do_stand(victim,"");
				check_improve(victim,gsn_awareness,true,2);
			}else{
				victim->println( "You are partially awakened by someone in the room." );
				victim->println( "You roll over and drift back to sleep." );
			}
		}

        act( "$n tried to steal from $N.",  ch, NULL, victim, TO_NOTVICT );

        if ( !IS_NPC(ch) )
        {
            if ( IS_NPC(victim) )
            {
                check_improve(ch,gsn_steal,false,2);
                multi_hit( victim, ch, TYPE_UNDEFINED );
            }
            else
            {
				sprintf(buf,"$N tried to steal from %s <%d>.", 
					victim->name, victim->level);
				wiznet(buf,ch,NULL,WIZ_THEFTS,0,0);
				if(GAMESETTING3(GAMESET3_THIEF_SYSTEM_ENABLED)){
					if(!IS_NPC(ch)){
						if(!IS_THIEF(ch)){
							ch->println( "*** You are now tagged as a THIEF!! ***");
						}
						ch->pcdata->thief_until=current_time+ (60*game_settings->thief_system_tagged_duration);
						save_char_obj( ch );
					}
				}
            }
        }  
        return;
    }
	else 


	if (!IS_NPC(victim))
	{
		// transfer those to the void that are attacked 
		// while linkdead and dont have a pkill timer
		if ( victim->pknorecall ==0 
			&& IS_LINKDEAD(victim)
			&& victim->was_in_room == NULL 
			&& victim->in_room != NULL )
		{
			victim->was_in_room = victim->in_room;
			if ( victim->fighting != NULL )
				stop_fighting( victim, true );
			act( "$n disappears into the void.", victim, NULL, NULL, TO_ROOM );
			victim->println( "You disappear into the void." );
			if (victim->level > 1)
				save_char_obj( victim);
			char_from_room( victim);
			char_to_room( victim, get_room_index( ROOM_VNUM_LIMBO ) );
			return;
		}

		ch->pksafe=0;
		ch->pknoquit=(UMAX(ch->pknoquit,10));
	}

    if (	!str_cmp( arg1, "coin"  )
        ||	!str_cmp( arg1, "coins" )
        ||	!str_cmp( arg1, "gold"  )
        ||	!str_cmp( arg1, "silver"))
    {
        int gold, silver;
        gold = victim->gold * number_range(1, ch->level) / MAX_LEVEL;
        silver = victim->silver * number_range(1,ch->level) / MAX_LEVEL;

        if (gold > victim->gold) gold = victim->gold;
        if (silver > victim->silver) silver = victim->silver;
              
        if ( gold <= 0 && silver <= 0 )
        {
             ch->println( "You couldn't get any coins." );
             return;
        }
        ch->gold        += gold;
        ch->silver      += silver;
        victim->silver  -= silver;
        victim->gold    -= gold;
        if (silver <= 0)
             ch->printlnf( "Bingo!  You got %d gold coins.", gold );
        else if (gold <= 0)
             ch->printlnf( "Bingo!  You got %d silver coins.", silver);
        else
             ch->printlnf( "Bingo!  You got %d silver and %d gold coins.", silver, gold );
    
        check_improve(ch,gsn_steal,true,2);

		if (!IS_NPC(victim))
		{
			sprintf(buf,"$N successfully stole %d silver and %d "
				"gold coins from %s <%d>.", silver, gold, 
				victim->name, victim->level);
			wiznet(buf,ch,NULL,WIZ_THEFTS,0,0);
		}
        return;
    }

    if ( ( obj = get_obj_carry_for_looker( victim, arg1, ch ) ) == NULL )
    {
        ch->println( "You can't find it." );
        return;
    }

    if ( !can_drop_obj( ch, obj )
        ||   IS_SET(obj->extra_flags, OBJEXTRA_INVENTORY) )
    {
        ch->println( "You can't pry it away." );
        return;
    }

    if ( ch->carry_number + get_obj_number( obj ) > can_carry_n( ch ) )
    {
        ch->println( "You have your hands full." );
        return;
    }

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

    obj_from_char( obj );
    obj_to_char( obj, ch );
    check_improve(ch,gsn_steal,true,2);
    ch->println( "Got it!" );
	
	if (!IS_NPC(victim))
	{
		sprintf(buf,"$N successfully stole %s [%d] from %s <%d>.",
			obj->short_descr, obj->pIndexData->vnum, victim->name, 
			victim->level);
		wiznet(buf,ch,NULL,WIZ_THEFTS,0,0);
	}
    return;
}
/**************************************************************************/
/*
 * Shopping commands.
 */
/**************************************************************************/
char_data *find_keeper( char_data *ch )
{
    char_data *keeper;
    SHOP_DATA *pShop;
	
	pShop = NULL;
    for ( keeper = ch->in_room->people; keeper; keeper = keeper->next_in_room )
    {
		if ( IS_NPC(keeper) && (pShop = keeper->pIndexData->pShop) != NULL )
			break;
	}
	
    if ( pShop == NULL )
	{
		ch->println("You can't do that here.");
		return NULL;
    }
	
    // Shop hours.
    if ( time_info.hour < pShop->open_hour )
    {
		do_say( keeper, "Sorry, I am closed. Come back later." );
		return NULL;
    }
    
	if ( time_info.hour > pShop->close_hour )
    {
		do_say( keeper, "Sorry, I am closed. Come back tomorrow." );
		return NULL;
    }
	
    // Invisible or hidden people.
	if ( !can_see( keeper, ch )  && INVIS_LEVEL(ch)<1)
    {
		do_say( keeper, "I don't trade with folks I can't see." );
		return NULL;
	}
	
    return keeper;
}

/**************************************************************************/
// insert an object at the right spot for the keeper 
void obj_to_keeper( OBJ_DATA *obj, char_data *ch )
{
    OBJ_DATA *t_obj, *t_obj_next;
	
	// see if any duplicates are found 
    for (t_obj = ch->carrying; t_obj != NULL; t_obj = t_obj_next)
    {
		t_obj_next = t_obj->next_content;
		
		if (obj->pIndexData == t_obj->pIndexData
			&&  !str_cmp(obj->short_descr,t_obj->short_descr))
		{
			// if this is an unlimited item, destroy the new one 
			if (IS_OBJ_STAT(t_obj,OBJEXTRA_INVENTORY))
			{
				extract_obj(obj);
				return;
			}
			obj->cost = t_obj->cost; // keep it standard 
			break;
		}
    }
	
    if (t_obj == NULL)
	{
		obj->next_content = ch->carrying;
		ch->carrying = obj;
    }
    else
    {
		obj->next_content = t_obj->next_content;
		t_obj->next_content = obj;
	}
	
    obj->carried_by      = ch;
	obj->in_room         = NULL;
    obj->in_obj          = NULL;
    ch->carry_number    += get_obj_number( obj );
    ch->carry_weight    += get_obj_weight( obj );
}

/**************************************************************************/
// get an object from a shopkeeper's list 
OBJ_DATA *get_obj_keeper( char_data *ch, char_data *keeper, char *argument )
{
	char arg[MIL];
	char newarg[MIL];
    OBJ_DATA *obj;
    int number;
    int count;

	// convert 'buy 5' to 'buy 5.'
	if(is_number(argument)){
		sprintf(newarg,"%s.",argument);
		argument=newarg;
	};
	
    number = number_argument( argument, arg );
    count  = 0;
	int uid=get_uid(arg);
	if(uid){
		number=1;
	}

	for ( obj = keeper->carrying; obj; obj = obj->next_content )
    {
		if (obj->wear_loc == WEAR_NONE
			&&  can_see_obj( keeper, obj )
			&&  can_see_obj(ch,obj)			
			&&  (  (uid && uid==obj->uid)
				 || is_name( arg, obj->name ) 
				 )
			)
        {
            if ( ++count == number )
                return obj;
			
			// skip other objects of the same name
			while (obj->next_content != NULL
				&& obj->pIndexData == obj->next_content->pIndexData
				&& !str_cmp(obj->short_descr,obj->next_content->short_descr))
				obj = obj->next_content;
		}
    }
	
    return NULL;
}

/**************************************************************************/
// figure out how much a shop keeper will pay/sell an item for.
int get_cost( char_data *keeper, OBJ_DATA *obj, bool fBuy )
{
	SHOP_DATA *pShop;
    int cost;
	
    if ( obj == NULL || ( pShop = keeper->pIndexData->pShop ) == NULL )
		return 0;

	if(IS_SET(obj->extra2_flags,OBJEXTRA2_NOSELL)){
		// non sellable objects
		return 0; 
	}
	
	if ( fBuy )
    {
		cost = obj->cost * pShop->profit_buy  / 100;
    }
	else
    {
		OBJ_DATA *obj2;
		int itype;
		
		cost = 0;
		for ( itype = 0; itype < MAX_TRADE; itype++ )
		{
			if ( obj->item_type == pShop->buy_type[itype] )
			{
				cost = obj->cost * pShop->profit_sell / 100;
				break;
			}
		}
		
		if (!IS_OBJ_STAT(obj,OBJEXTRA_SELL_EXTRACT))
			for ( obj2 = keeper->carrying; obj2; obj2 = obj2->next_content )
			{
				if ( obj->pIndexData == obj2->pIndexData
					&&   !str_cmp(obj->short_descr,obj2->short_descr) )
					if (IS_OBJ_STAT(obj2,OBJEXTRA_INVENTORY))
						cost /= 2;
					else
						cost = cost * 3 / 4;
			}
    }
	
    if ( obj->item_type == ITEM_STAFF || obj->item_type == ITEM_WAND ) {
		if (obj->value[1] == 0){
			cost /= 4;
		}else{
			cost = cost * obj->value[2] / obj->value[1];
		}
    }
	
    return cost;
}
/**************************************************************************/
void buy_in_petshop( char_data *ch, char *argument )
{
	int cost,roll;
	char arg[MIL];
	char buf[MSL];
	char_data *pet;
	ROOM_INDEX_DATA *pRoomIndexNext;
	ROOM_INDEX_DATA *in_room;
	
	if ( IS_NPC(ch) )
		return;
	
	argument = one_argument(argument,arg);
	
	// petshop storage room is the room which vnum 
	// is one greater than the current room
	pRoomIndexNext = get_room_index( ch->in_room->vnum + 1 );
	
	if ( pRoomIndexNext == NULL )
	{
		bugf( "do_buy(): bad pet shop at vnum %d.", ch->in_room->vnum );
		ch->println("Sorry, you can't buy that here.");
		return;
	}
	
	in_room     = ch->in_room;
	ch->in_room = pRoomIndexNext;
	pet         = get_pet_room( ch, arg );
	ch->in_room = in_room;
	
	if ( pet == NULL || !IS_SET(pet->act, ACT_PET) )
	{
		ch->println("Sorry, you can't buy that here.");
		return;
	}
	
	if ( ch->pet != NULL )
	{
		ch->println("You already own a pet.");
		return;
	}
	
	cost = 10 * pet->level * pet->level;
	
	if IS_CONTROLLED(ch) cost =0;
	
	if ( ch->level < pet->level )
	{
		ch->println("You're not powerful enough to master this pet.");
		return;
	}
	// haggle 
	roll = number_percent();
	if (roll < get_skill(ch,gsn_haggle))
	{
		cost -= cost / 2 * roll / 100;
		if ( (ch->silver + 100 * ch->gold) < cost)
		{				
			ch->printlnf( "You haggle the price down to %d coin%s, but "
				"still can't afford it.", cost, cost==1?"":"s");
			
			if ( (ch->silver + 100 * ch->gold)>0 
				&& (ch->silver + 100 * ch->gold) < (cost* 96/100) )
			{				
				cost=(ch->silver + 100 * ch->gold);
				ch->printlnf( "You explain this to the pet shopkeeper and manage "
					"haggle the price down to the money that you do have!");
			}else{ // didn't get it
				WAIT_STATE(ch, 20); // 5 seconds lag to reduce potential abuse
				return;
			}
		}else{
			ch->printlnf( "You haggle the price down to %d coin%s.",
				cost, cost==1?"":"s");
			check_improve(ch,gsn_haggle,true,4);
		}		
	}else{
		if ( (ch->silver + 100 * ch->gold) < cost )
		{
			ch->println("You can't afford it.");
			return;
		}
	}
	
	deduct_cost(ch,cost);
	pet			= create_mobile( pet->pIndexData, 0 );
	SET_BIT(pet->act, ACT_PET);
	SET_BIT(pet->affected_by, AFF_CHARM);
	pet->comm = COMM_NOTELL|COMM_NOSHOUT|COMM_NOCHANNELS;
	
	argument = one_argument( argument, arg );
	if(!IS_NULLSTR(arg)){
		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'.\r\n",
		pet->description, ch->short_descr );
	free_string( pet->description );
	pet->description = str_dup( buf );
	
	char_to_room( pet, ch->in_room );
	add_follower( pet, ch );
	pet->leader = ch;
	ch->pet = pet;
	ch->println("Enjoy your pet.");
	act( "$n bought $N as a pet.", ch, NULL, pet, TO_ROOM );
}

/**************************************************************************/
void do_lockers(char_data *ch, char *argument);
/**************************************************************************/
void do_buy( char_data *ch, char *argument )
{
    char buf[MSL];
	int cost,roll;
	
    if(IS_NULLSTR(argument)){
		ch->println( "Buy what?" );
		if(IS_NEWBIE(ch)){
			ch->println( "Use the 'list' command to see what is available while near a shopkeeper." );
		}
		return;
    }
	
	smash_tilde(argument);
	
	// pet shops
	if( IS_SET(ch->in_room->room_flags, ROOM_PET_SHOP)){
		buy_in_petshop(ch, argument);
		return;
    }

	// locker code
	if(!str_prefix("locker", argument)){
		do_lockers(ch, FORMATF("%s startrent", argument+6));
		return;
	}

    {
		char_data *keeper;
		OBJ_DATA *obj,*t_obj;
		char arg[MIL];
		int number, count = 1;
		
		if ( ( keeper = find_keeper( ch ) ) == NULL )
			return;
	
		number = mult_argument(argument,arg);
		obj  = get_obj_keeper( ch,keeper, arg );
		cost = get_cost( keeper, obj, true );
				
		if(number<1){
			return;
		}
		
		if ( cost <= 0 || !can_see_obj( ch, obj ) )
		{
			act( "$n tells you 'I don't sell that -- try 'list''.",
				keeper, NULL, ch, TO_VICT );
			return;
		}
		
		if (!IS_OBJ_STAT(obj,OBJEXTRA_INVENTORY))
		{
			for (t_obj = obj->next_content;
			count < number && t_obj != NULL;
			t_obj = t_obj->next_content)
			{
				if (t_obj->pIndexData == obj->pIndexData
					&&  !str_cmp(t_obj->short_descr,obj->short_descr))
					count++;
				else
					break;
			}
			
			if (count < number)
			{
				act("$n tells you 'I don't have that many in stock.",
					keeper,NULL,ch,TO_VICT);
				//ch->reply = keeper;
				return;
			}
		}
		
		if (!IS_CONTROLLED(ch))
		{
				
			if (ch->carry_number +  number * get_obj_number(obj) > can_carry_n(ch))
			{
				ch->println("You can't carry that many items.");
				return;
			}
			
			if ( ch->carry_weight + number * get_obj_weight(obj) > can_carry_w(ch))
			{
				ch->println("You can't carry that much weight.");
				return;
			}
			
			// haggle 
			roll = number_percent();
			if (!IS_OBJ_STAT(obj,OBJEXTRA_SELL_EXTRACT) 
				&& roll < get_skill(ch,gsn_haggle))
			{
				cost -= obj->cost / 2 * roll / 100;
				
				// do can afford checks
				if ( (ch->silver + ch->gold * 100) < cost * number )
				{
					act("You haggle with $N.",ch,NULL,keeper,TO_CHAR);
					if (number > 1){
						act("$n tells you 'I wont sell you that many so cheap.",
						keeper,obj,ch,TO_VICT);
					}else{
						act( "$n tells you 'I wont sell $p that cheap sorry'.",
						keeper, obj, ch, TO_VICT );
					}
					WAIT_STATE(ch, 20); // 5 seconds lag to reduce potential abuse
					return;
				}else{
					act("You haggle with $N.",ch,NULL,keeper,TO_CHAR);
					check_improve(ch,gsn_haggle,true,4);
				}
			}

			// do can afford checks
			if ( (ch->silver + ch->gold * 100) < cost * number )
			{
				if (number > 1)
					act("$n tells you 'You can't afford to buy that many.",
					keeper,obj,ch,TO_VICT);
				else
					act( "$n tells you 'You can't afford to buy $p'.",
					keeper, obj, ch, TO_VICT );
				return;
			}
		}
		
		if (number > 1)
		{
			sprintf(buf,"$n buys $p[%d].",number);
			act(buf,ch,obj,NULL,TO_ROOM);
			sprintf(buf,"You buy $p[%d] for %d silver.",number,cost * number);
			act(buf,ch,obj,NULL,TO_CHAR);
		}
		else
		{
			act( "$n buys $p.", ch, obj, NULL, TO_ROOM );
			sprintf(buf,"You buy $p for %d silver.",cost);
			act( buf, ch, obj, NULL, TO_CHAR );
		}
		
		if (!IS_CONTROLLED (ch))
			deduct_cost(ch,cost * number);
		
		keeper->gold += cost * number/100;
		keeper->silver += cost * number - (cost * number/100) * 100;

		// limit a shopkeepers wealth if more than 10 minutes after a reboot
		if(boot_time+360<current_time){ 
			limit_mobile_wealth(keeper);
		}
		
		for (count = 0; count < number; count++)
		{
			if ( IS_SET( obj->extra_flags, OBJEXTRA_INVENTORY ) ){
				t_obj = create_object( obj->pIndexData);
			}else{
				t_obj = obj;
				obj = obj->next_content;
				obj_from_char( t_obj );
			}
			
			if (t_obj->timer > 0 && !IS_OBJ_STAT(t_obj,OBJEXTRA_HAD_TIMER))
				t_obj->timer = 0;
			
			REMOVE_BIT(t_obj->extra_flags,OBJEXTRA_HAD_TIMER);
			
			REMOVE_BIT(t_obj->extra_flags,OBJEXTRA_INVENTORY);
			
			obj_to_char( t_obj, ch );
			if (cost < t_obj->cost)
				t_obj->cost = cost;
		}
    }
}

/**************************************************************************/
// wear object as a secondary weapon 
void do_second (char_data *ch, char *argument)
{
	OBJ_DATA *obj;

	if(!GAMESETTING2(GAMESET2_NO_SECOND_SKILL_REQUIRED)){
		if ( !IS_NPC(ch) && ch->get_skill(gsn_second)<1){
			ch->println("You dont have a clue how to hold a weapon in your off-hand correctly.");
			return;
		}
	}
	
	if (IS_NULLSTR(argument)){
		ch->println("Wear which weapon in your off-hand?");
		return;
	}
	
	obj = get_obj_carry (ch, argument); // find the obj withing ch's inventory 
	
	if (obj == NULL)
	{
		ch->println("You have no such thing in your backpack.");
		return;
	}
	
	if (( obj->level>LEVEL_HERO)&&(ch->level < LEVEL_IMMORTAL))
	{
		char buf[MSL];
		
		sprintf( buf, "`x%s (%s) lvl %d`1tried to wear `1"
			"%s [%d] lvl %d`1"
			"It was removed since it was out of level.", 
			ch->name,		ch->short_descr,	ch->level, 
			obj->short_descr,	obj->pIndexData->vnum, obj->level);
		
		autonote(NOTE_SNOTE, "p_anote()","`Yoverlevel object!`x", "imm", buf, true);
		
		ch->printlnf( "You try to use %s and it crumbles to dust.", obj->short_descr );
		
		act( "$n tries to use $p, but it crumbles to dust.",
			ch, obj, NULL, TO_ROOM );
		
		extract_obj(obj);
		return;
	}
	
	if(obj->item_type != ITEM_WEAPON)
	{
		ch->println("That is not a weapon.");
		return; }
	
	if(!CAN_WEAR( obj, OBJWEAR_WIELD ))
	{
        ch->println("You can not wield that.");
        return;
	}

	if ( IS_SET( obj->extra2_flags, OBJEXTRA2_NOSECONDARY ))
	{
		act( "You cannot wield $p in your offhand.", ch, obj, NULL, TO_CHAR );
		return;
	}

	if ( !IS_ADMIN( ch )) // Immortal requested not to have this but I wanted it :)
	{
		if(!IS_NPC(ch) && obj->pIndexData->relative_size<
			race_table[ch->race]->low_size)
		{
	        act("$p is too small for you.",ch,obj,NULL,TO_CHAR);
	        return;
		}
	
		if(!IS_NPC(ch) && obj->pIndexData->relative_size>
			race_table[ch->race]->high_size)
		{
	        act("$p is too large for you.",ch,obj,NULL,TO_CHAR);
	        return;
		}
	}

	if ( !IS_IMMORTAL( ch ))
	{
		if(DISALLOWED_OBJECT_FOR_CHAR(obj, ch))
		{
			act("$p cannot be used by your class.",ch,obj,NULL,TO_CHAR);
			return;
		}
	}
	
    if(IS_SET(obj->extra_flags, OBJEXTRA_MAGIC)
		&& HAS_CLASSFLAG(ch, CLASSFLAG_MAGIC_ANTIPATHY))
	{
		act("You have great antipathy towards $p.",ch,obj,NULL,TO_CHAR);
		return;
	}

	
	// check that the character is using a first weapon at all 
	if (get_eq_char (ch, WEAR_WIELD) == NULL) 
	{
		ch->println("You need to wield a primary weapon, before using a secondary one!");
		return;
	}
	
	
	// check for str - secondary weapons have to be lighter 
	if ( get_obj_weight( obj ) > ( (ch->modifiers[STAT_ST] + 15)*5) )
	{
		ch->println("This weapon is too heavy to be used as a secondary weapon by you.");
		return;
	}
	
	if (IS_WEAPON_STAT(get_eq_char(ch,WEAR_WIELD),WEAPON_TWO_HANDS)
		&& ch->size < SIZE_LARGE)
	{
		ch->println("You cannot wear a weapon in your off hand if using a two handed weapon.");
		return;
	}
	
	// check if the char is using a shield or a held weapon 
	if ( get_eq_char (ch,WEAR_SHIELD) != NULL) 
	{
		if ((!remove_obj(ch, WEAR_SHIELD, true))){
	//		ch->println("You cannot use a secondary weapon while using a shield or holding an item.");
			return;
		}
	}

	if ( get_eq_char(ch,WEAR_HOLD) != NULL)
	{		
		if ((!remove_obj(ch, WEAR_HOLD, true))){
	// ch->println("You cannot use a secondary weapon while using a shield or holding an item.");
			return;
		}
	}


	// at last - the char uses the weapon 
	if (!remove_obj(ch, WEAR_SECONDARY, true)) // remove the current weapon if any 
		return;                                // remove obj tells about any no_remove 
	
	if (IS_WEAPON_STAT(obj,WEAPON_TWO_HANDS))
	{
		ch->println("You can't use a two handed weapon in your off hand.");
		return;
	}
	
	act ("$n wields $p in $s off-hand.",ch,obj,NULL,TO_ROOM);
	act ("You wield $p in your off-hand.",ch,obj,NULL,TO_CHAR);
	equip_char ( ch, obj, WEAR_SECONDARY);
	return;
}

/**************************************************************************/
char_data* find_innkeeper(char_data *ch);
/**************************************************************************/
//format_obj_to_char( OBJ_DATA *obj, char_data *ch, bool fShort )
void do_list( char_data *ch, char *argument )
{
    char buf[MSL];
	BUFFER *output;

	if(str_len(argument)>3 && !str_prefix(argument, "lockers")){
		do_lockers(ch, "list");
		return;
	}

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

        pRoomIndexNext = get_room_index( ch->in_room->vnum + 1 );

        if (pRoomIndexNext == NULL )
        {
            bugf( "do_list(): bad pet shop at vnum %d.", ch->in_room->vnum );
            ch->println("You can't do that here.");
            return;
        }

		output = new_buf();
        found = false;
        for ( pet = pRoomIndexNext->people; pet; pet = pet->next_in_room )
        {

            if ( IS_NPC(pet) && IS_SET(pet->act, ACT_PET) )
            {
                if ( !found )
                {
                    found = true;
					add_buf(output,"Pets for sale:\r\n");
                }
                sprintf( buf, "[%2d] %8d - %s\r\n",

                    pet->level,
                    10 * pet->level * pet->level,
                    pet->short_descr );

				// holyvnum
				if (!IS_UNSWITCHED_MOB(ch) && IS_SET(TRUE_CH(ch)->act, PLR_HOLYVNUM))
				{
					char buf2[MSL];
					if (IS_NPC(pet) && pet->pIndexData)
					{
						if (pet->pIndexData->mprogs)
						{
							sprintf(buf2," `#`r*%d,%d*`^\r\n", pet->pIndexData->vnum, pet->level);
						}
						else
						{
							sprintf(buf2," `#`B[%d,%d]`^\r\n", pet->pIndexData->vnum, pet->level);
						}
						buf[str_len(buf)-2]= '\0'; // chop off the \r\n
						strcat(buf,buf2);
					}
				}
				add_buf(output,buf);
            }
        }

        if ( !found ){
			add_buf(output,"Sorry, we're out of pets right now.\r\n");
		}
		ch->sendpage(buf_string(output));
	    free_buf(output);
	    return;
		// end of petshop code
	} else if ( IS_SET(ch->in_room->room_flags, ROOM_INN) ){
	// Innkeeper code.
		ROOM_INDEX_DATA* pRoom	= NULL;
		char_data* pInnKeeper	= NULL;
		cInnData* pInn			= NULL;
		char pListItem[MSL];
		vn_int roomVnum			= 0;
		int roomRate			= 0;
		sh_int roomIdx			= 0;
		bool foundItem			= false;

		output = new_buf();

		// Find the first mob in the room that is an innkeeper.
		if ( !(pInnKeeper=find_innkeeper(ch))  ) {
			return;
		}

		// Just a shortcut pointer to make the rest of the code more readable.
		pInn = pInnKeeper->pIndexData->pInnData;

		// Add valid rooms to the output buffer.
		for(roomIdx=0; roomIdx<MAX_INN; roomIdx++ ) {
			roomVnum = pInn->vnRoom[roomIdx]; 
			roomRate = pInn->shRate[roomIdx];

			if ( roomVnum > 0 ) {
				if ( (pRoom=get_room_index(roomVnum)) != NULL ) {
					if ( !foundItem ) {
						foundItem = true;
						add_buf(output, "INN - ROOMS FOR RENT`1");
						add_buf(output, "[Number  Price/Hour] Description`1");
					}

					sprintf(pListItem, " %6d    %8d  `#%-32s`^\r\n", roomIdx+1, roomRate, pRoom->name);
					add_buf(output, pListItem);
				}
			}
		}

		if ( !foundItem ) {
			add_buf(output, "There are no rooms to rent here.`1");
		}

		ch->sendpage(buf_string(output));
	    free_buf(output);

		return;
    }else{// end of inn start of normal shop
		char_data *keeper;
        OBJ_DATA *obj;
        int count, can_wear;
		int foundcount;
        char arg[MIL];
		char buf2[MSL];
		int cost=0, linecount;

		// listing by level matching system
		bool doingFiltering=false;
		bool levelBasedFiltering=false;
		int max_obj_level=MAX_LEVEL;
		int min_obj_level=0;

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

        // preparse any parameters
		argument=one_argument(argument,arg);
		if (!IS_NULLSTR(arg)){
			doingFiltering=true;
			// figure out if it is level based
			if(is_number(arg)){
				levelBasedFiltering=true;

				max_obj_level=atoi(arg);
				// second level - level range match
				argument=one_argument(argument,arg);

				if(!IS_NULLSTR(arg)){
					if(!is_number(arg)){
						ch->println("You can't search by level and substrings at the same time sorry.");
						return;
					}

					min_obj_level=atoi(arg);
					
					// swap the levels if necessary
					if (min_obj_level>max_obj_level){
						foundcount=max_obj_level;
						max_obj_level=min_obj_level;
						min_obj_level=foundcount;
					}

				}				
			}else{
				// substring match
			}
		}// end of parsing format and show results
      
		output = new_buf();

		foundcount=0;
		linecount=0;
        for ( obj = keeper->carrying; obj && linecount<200;obj = obj->next_content )
        {
            if ( obj->wear_loc == WEAR_NONE
               &&   can_see_obj( ch, obj )
               &&   ( cost = get_cost( keeper, obj, true ) ) > 0 )
            {
				// do the filtering
				if (doingFiltering){
					if(levelBasedFiltering){
						if (obj->level<min_obj_level){
							continue;
						}
						if (obj->level>max_obj_level){
							continue;
						}
					}else{ // substring match filter
						if (!is_name(arg,obj->name)){
							continue;
						}
					}
				}

                if ( foundcount==0 ){
					add_buf(output,"  [Lv  Price Qty] Item                      (type?)\r\n");
                }
				foundcount++;

				// colour code wearable items vs unwearable
				if(	(
						IS_SET(obj->extra_flags, OBJEXTRA_MAGIC)
						&& HAS_CLASSFLAG(ch, CLASSFLAG_MAGIC_ANTIPATHY)				
					)
					||(	   !IS_NPC(ch) 
						&& obj->pIndexData->relative_size 
							< race_table[ch->race]->low_size 
						&& obj->item_type != ITEM_LIGHT 
						&& !CAN_WEAR( obj, OBJWEAR_HOLD)  
						&& !CAN_WEAR( obj, OBJWEAR_FLOAT) 
					  )
					||(	   !IS_NPC(ch) 
						&& obj->pIndexData->relative_size>race_table[ch->race]->high_size 
						&& obj->item_type != ITEM_LIGHT 
						&& !CAN_WEAR( obj, OBJWEAR_FLOAT) 
					  )
				  )
				{
					can_wear=0;
				}else{
					can_wear=1;
				}


				if(!IS_NPC(ch) && can_wear)
				{
					can_wear = DISALLOWED_OBJECT_FOR_CHAR(obj, ch)?0:1;
				}
				
				if(can_wear==1){
					add_buf(output,"`x");
				}else{
					add_buf(output,"`s");
				}
				//// end of colour coding


				// create an mxp short descript
				char short_descr[MSL];
				if(obj->item_type == ITEM_WEAPON){
					strcpy(short_descr,				
						mxp_create_tag(ch, FORMATF("buy-uid %d", obj->uid), FORMATF("%-25s", obj->short_descr)));
				}else{
					strcpy(short_descr,
						mxp_create_tag(ch, FORMATF("buy-uid %d", obj->uid), obj->short_descr));
				}

                if (IS_OBJ_STAT(obj,OBJEXTRA_INVENTORY))
                    if (obj->item_type == ITEM_WEAPON)
                    {
                        sprintf(buf,"[%2d %6d -- ] %s (%s)\r\n",
                        obj->level,cost,short_descr, get_weapontype(obj));
                    }else{
                        sprintf(buf,"[%2d %6d -- ] %s\r\n",
                        obj->level,cost,short_descr);
                    }
                else
                {
                    count = 1;           
                    while (obj->next_content != NULL 
                        && obj->pIndexData == obj->next_content->pIndexData
                        && !str_cmp(obj->short_descr,
                            obj->next_content->short_descr))
                    {
                        obj = obj->next_content;
                        count++;
                    }
                    if (obj->item_type == ITEM_WEAPON)
                    {   
                        sprintf(buf,"[%2d %6d %2d ] %s (%s)\r\n",
                            obj->level,cost,count,short_descr, get_weapontype(obj));
                    }
                    else
                    {
                        sprintf(buf,"[%2d %6d %2d ] %s\r\n",
                            obj->level,cost,count,short_descr);
                    }
                }
				//holyvnum
				if (!IS_UNSWITCHED_MOB(ch) && IS_SET(TRUE_CH(ch)->act, PLR_HOLYVNUM))
				{

					if (obj->pIndexData)
					{
						sprintf(buf2," `#`s[%d,%d]`^\r\n", obj->pIndexData->vnum, obj->level);
						buf[str_len(buf)-2]= '\0'; // chop off the \r\n
						strcat(buf,buf2);
					}
				}

				// number each item
				strcpy(buf2, mxp_create_tag(ch, FORMATF("buy-uid %d", obj->uid), FORMATF("%2d", linecount+1)));
				add_buf(output,buf2);

				// add the the buffer the item details
				add_buf(output,buf);
				linecount++;
            }
        }

        if ( linecount==0 )
		{
			if (IS_NULLSTR(arg))
				ch->println("You can't buy anything here.");
			else
				ch->println("No matching items found.");
		}

		if (linecount>200){
			add_buf(output,"`xCan only list 200 lines on the list at one time.\r\n");
			add_buf(output,"filter the list by level range to access the \r\n"
				"information regarding the complete list.\r\n");
		}else if ( IS_NEWBIE(ch) && linecount>20 )
		{
			add_buf(output,"`xYou can actually filter the list, for example if you wanted to \r\n");
			add_buf(output,"purchase an item that was green, type `=Clist green`x.  If the item was\r\n");
			add_buf(output,"displayed as the 3rd on the list then to purchase it type `=Cbuy 3.green`x\r\n");
		}

        ch->sendpage(buf_string(output));
	    free_buf(output);
        return;
    }
}

/************************************************************************/
void sell_object(char_data *ch, char_data *keeper, OBJ_DATA *obj)
{
    char buf[MSL];
    int cost,roll;
    
	if ( !can_drop_obj( ch, obj ) )
    {
		ch->printlnf( "You can't let go of %s.", obj->short_descr);
		return;
    }

	if  (( obj->item_type == ITEM_CAULDRON 
		|| obj->item_type == ITEM_CONTAINER
		|| obj->item_type == ITEM_FLASK
		|| obj->item_type == ITEM_MORTAR )
		&& obj->contains)
    {
		ch->printlnf( "Try emptying everything out of %s before selling it.", obj->short_descr);
		return;
    }

	if (!can_see_obj(keeper,obj))
	{
		act("$n doesn't appear to be able to see $p.",keeper,obj,ch,TO_VICT);
		return;
	}

    if ( ( cost = get_cost( keeper, obj, false ) ) <= 0 )
    {
		act( "`s$n looks uninterested in $p.`x", keeper, obj, ch, TO_VICT );
		return;
    }
    if ( cost > (keeper-> silver + 100 * keeper->gold) )
	{
		act("$n tells you 'I'm afraid I don't have enough wealth to buy $p.",
			keeper,obj,ch,TO_VICT);
		return;
    }

    act( "$n sells $p.", ch, obj, NULL, TO_ROOM );

    // haggle 
    roll = number_percent();
    if (!IS_OBJ_STAT(obj,OBJEXTRA_SELL_EXTRACT) && roll < get_skill(ch,gsn_haggle))
    {
        ch->println("You haggle with the shopkeeper.");
        cost += obj->cost / 2 * roll / 100;
		cost = UMIN(cost,95 * get_cost(keeper,obj,true) / 100);
		cost = UMIN(cost,(keeper->silver + 100 * keeper->gold));
        check_improve(ch,gsn_haggle,true,4);
    }
    
	if (((int)cost/100)>0)
	{
		sprintf( buf, "You sell $p for %d silver and %d gold piece%s.",
			cost - (cost/100) * 100, cost/100, cost == 1 ? "" : "s" );
	}
	else
	{
		sprintf( buf, "You sell $p for %d silver piece%s.",
			cost - (cost/100) * 100, cost == 1 ? "" : "s" );
	}

	act( buf, ch, obj, NULL, TO_CHAR );
	ch->gold     += cost/100;
	ch->silver 	 += cost - (cost/100) * 100;

    deduct_cost(keeper,cost);
    if ( keeper->gold < 0 )
		keeper->gold = 0;
    if ( keeper->silver< 0)
		keeper->silver = 0;

    if ( obj->item_type == ITEM_TRASH || IS_OBJ_STAT(obj,OBJEXTRA_SELL_EXTRACT))
    {
		extract_obj( obj );
    }
    else
    {
		obj_from_char( obj );
		if (obj->timer)
			SET_BIT(obj->extra_flags,OBJEXTRA_HAD_TIMER);
		else
			obj->timer = number_range(50,100);
		obj_to_keeper( obj, keeper );
    }

}

/**************************************************************************/
void do_sell( char_data *ch, char *argument )
{
    char arg[MIL], allarg[MIL];
    char_data *keeper;
    OBJ_DATA *obj, *obj_next;
	int count=0;

    one_argument( argument, arg );

	if (!str_cmp("all", arg )){
		strcpy(arg,"all.");
	}

    if ( !str_prefix( "all.", arg ) )
    {
		strcpy(allarg, arg+4);
	}

    if ( arg[0] == '\0' )
    {
		ch->println("Sell what?");
		return;
    }

	if ( ( keeper = find_keeper( ch ) ) == NULL )
		return;

    if ( str_prefix( "all.", arg) )
    {
		if ( ( obj = get_obj_carry( ch, arg ) ) == NULL )
		{
			act( "$n tells you 'You don't have that item'",keeper, NULL, ch, TO_VICT );
			return;
		}
	}
	else
	{
		if ( ( obj = get_obj_carry( ch, allarg ) ) == NULL )
		{
			ch->println( "No matching items found to sell." );
			return;
		}
	}

	if (!can_see(keeper,ch))
	{
		act("$n doesn't appear to be able to see you.",keeper,NULL,ch,TO_VICT);
		return;
	}

/*
	Slortar 24/03/02
	This check is handled in sell_object and does not exit the function when argument is 'all'
	if (!can_see_obj(keeper,obj))
	{
		act("$n doesn't appear to be able to see $p.",keeper,obj,ch,TO_VICT);
		return;
	}
*/

/************/
    if ( str_prefix( "all.", arg ) )
    {
		// 'sell obj' 
		if ( ( obj = get_obj_carry( ch, arg ) ) == NULL )
		{
			ch->println( "You do not have that item." );
			return;
		}
		sell_object(ch, keeper, obj);
	}
	else // 'sell all.obj' 
	{
		// PC being ordered to do this
		if ( !IS_NPC(ch) && IS_SET( ch->dyn, DYN_IS_BEING_ORDERED ))
		{
			if ( ch->master ){
				ch->master->println( "Not going to happen." );
			}
			return;
		}

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

			if (is_name( allarg, obj->name)
			&&   can_see_obj( ch, obj )
			&&   obj->wear_loc == WEAR_NONE)
			{
				if(IS_NULLSTR(allarg) && get_cost( keeper, obj, false )<=0 ){ 
					// true if was a 'sell all' and keeper not interested in type of object
					continue;
				}
				if (count++>25)
				{
					ch->println( "You can only sell up to 25 items at a time." );
					return;
				}
				sell_object(ch, keeper, obj);
				
				// lag for selling multiple items
				WAIT_STATE(ch, 1);
				if (ch->fighting)
					WAIT_STATE(ch, 5);

			}
		}

		if(IS_NULLSTR(allarg) && count==0){ 
			act( "`s$n doesn't look interested in anything you have.`x", keeper, obj, ch, TO_VICT );
		}else if(count==25){
			act( "`sYou have nothing else that $n is interested in buying.`x", keeper, obj, ch, TO_VICT );
		}
	}
    return;
}

/**************************************************************************/
void do_value( char_data *ch, char *argument )
{
    char buf[MSL];
    char arg[MIL];
    char_data *keeper;
    OBJ_DATA *obj;
    int cost;

	one_argument( argument, arg );

	if ( IS_NULLSTR(arg) )
	{
		ch->println( "Value what?" );
		return;
	}

	if ( ( keeper = find_keeper( ch ) ) == NULL )
		return;

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

	if (!can_see_obj(keeper,obj))
	{
		act("$n doesn't see what you are offering.",keeper,NULL,ch,TO_VICT);
		return;
	}

	if ( !can_drop_obj( ch, obj ) )
	{
		ch->println( "You can't let go of it." );
		return;
	}

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

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

	return;
}

/**************************************************************************/
// by Rathern 
void do_slice( char_data *ch, char *argument )
{
	OBJ_DATA *corpse;
	OBJ_DATA *obj;
	OBJ_DATA *slice;
	int lag;
	bool found;
	char buf[MSL];
	found = false;
	
	
	if ( !IS_NPC(ch) && 
		(ch->level < skill_table[gsn_slice].skill_level[ch->clss]
		|| ch->pcdata->learned[gsn_slice] < 1 ))   // skill is not known
	{
		ch->println("You are not learned in slicing meat from objects.");
		return;
	}
	
	if ( IS_NULLSTR(argument) )
	{ 
		ch->println("From what do you wish to slice meat?");
		return;
	}
	
	
	if ( ( obj = get_eq_char( ch, WEAR_WIELD ) ) == NULL
		||   ( obj->value[3] != 1 && obj->value[3] != 2 && obj->value[3] != 3
		&& obj->value[3] != 11) )
	{
		ch->println("You need to wield a sharp weapon.");
		return;
	}
	
	if ( (corpse = get_obj_here( ch, argument )) == NULL)
	{  
		ch->println("You can't find that here.");
		return;
	}
	
	if (corpse->item_type != ITEM_CORPSE_NPC 
		&& corpse->item_type != ITEM_CORPSE_PC)
	{
		ch->println("That is not a suitable source of meat.");
		return;
	}
		
	// fiddle with the corpse after its been sliced to much 
	if (corpse->timer < 2)
	{
		sprintf( buf,"A sliced up and rotting %s is lying here.",
			corpse->short_descr);
		free_string(corpse->description);
		corpse->description=str_dup(buf);
		ch->println("That meat is to old and used now.");
		return;
	}
	
	
	if ( get_obj_index(OBJ_VNUM_SLICE) == NULL )
	{
		bugf("Vnum %d not found for do_slice!", OBJ_VNUM_SLICE);
		ch->printlnf( "Vnum %d not found for do_slice!, please report to the admin.", OBJ_VNUM_SLICE);
		return;
	}
	
	if ( !IS_NPC(ch) && !IS_IMMORTAL(ch) && number_percent() >
		ch->pcdata->learned[gsn_slice] )
		
	{
		ch->println("You fail to slice the meat properly.");
		check_improve(ch,gsn_slice,false,1);
		
		// Just in case they die :> 
				
		if ( number_percent() + ch->modifiers[STAT_QU] - 13 < 10)
		{
			act("You cut yourself, ouch that hurt!.",ch,NULL,NULL,TO_CHAR);
			damage( ch, ch, obj->level / 2, gsn_slice, DAM_SLASH, false );
		}		
		return;
	}
	
    // make and restring the slice of meat 
    buf[0]='\0';
    strcat(buf,"a slice of raw meat from ");
    strcat(buf,corpse->short_descr);
    strcat(buf," is lying here.");
    slice = create_object( get_obj_index(OBJ_VNUM_SLICE));
	free_string(slice->description);
    slice->description=str_dup(buf);
    
    buf[0]='\0';
    strcat(buf,"a slice of raw meat from ");
    strcat(buf,corpse->short_descr);
	free_string(slice->short_descr);
    slice->short_descr=str_dup(buf);
	slice->timer = 24;				// make the meat rot in 1 day	
	
    act("$n cuts a slice of meat from $p.", ch, corpse, NULL, TO_ROOM);
    act("You cut a slice of meat from $p.", ch, corpse, NULL, TO_CHAR);
    
	
    // give the sliced meat to the character 
    obj_to_char(slice, ch);
	
    // make it so only 5 slices can be cut from any one corpse 	
    corpse->value[2]+=10;
    if (corpse->value[2] > 40)
    {
        corpse->timer = 1;
    }
	
    // delay depends on skill 
    lag = skill_table[gsn_slice].beats;
    if (!IS_NPC(ch))
    {
        lag -= (ch->pcdata->learned[gsn_slice] * 30 /100 );
    }
	// reduce the lag for first slicing
	if (corpse->value[2]==10){
		lag/=3;
	}else if (corpse->value[2]==20){
		lag/=2;
	}
	
    check_improve(ch,gsn_slice,true,1);
	
    WAIT_STATE(ch,lag);
    return;
}

/**************************************************************************/
// Kalahn, May 98 - based off do_pour()
void do_empty(char_data *ch, char *argument)
{
    char arg[MSL], buf[MSL];
    OBJ_DATA *out;

    argument = one_argument(argument,arg);
    
    if (IS_NULLSTR(arg))
    {
		ch->println("Empty what drink container?");
		return;
    }
	
	if ((out = get_obj_carry(ch,arg)) == NULL)
    {
		ch->println("You don't have that item.");
		return;
    }
	
	if (out->item_type != ITEM_DRINK_CON)
    {
		ch->println("That's not a drink container.");
		return;
	}
	
	if (out->value[1] == 0)
	{
		ch->println("It's already empty.");
		return;
	}
	
	out->value[1] = 0;
	out->value[3] = 0;
	sprintf(buf,"You invert $p, spilling %s all over the ground.",
		liq_table[out->value[2]].liq_name);
	act(buf,ch,out,NULL,TO_CHAR);
	
	sprintf(buf,"$n inverts $p, spilling %s all over the ground.",
		liq_table[out->value[2]].liq_name);
	act(buf,ch,out,NULL,TO_ROOM);
	return;
}

/**************************************************************************/
// By Kerenos & Kal - July 98
void do_throw( char_data *ch, char *argument )
{
    char arg1[MIL], arg2[MIL];
    OBJ_DATA		*obj;
    ROOM_INDEX_DATA	*in_room, *to_room;
    EXIT_DATA		*pexit;
    int			door;
	
    argument = one_argument( argument, arg1 );
    argument = one_argument( argument, arg2 );
	
    if ( IS_NULLSTR(arg1) || IS_NULLSTR(arg2))
    {
		ch->println("Throw what in which direction?");
		ch->println("syntax: throw <object> <direction>");
		return;
    }

	if (IS_OOC(ch)){
		ch->println("You can't throw objects in OOC rooms.");
		return;
	}
	
    if (( obj = get_obj_carry( ch, arg1)) == NULL )
    {
		ch->println("You are not carrying that item.");
		return;
    }
	
    if ( !can_drop_obj( ch, obj ))
    {
		ch->println("You can't let go of that.");
		return;
    }

	door = dir_lookup( arg2 );
	
    if ( door == -1 )
	{
		ch->printlnf( "'%s' is an invalid direction.", arg2 );
		ch->println("Syntax: throw <object> <direction>");
		return;
    }

	in_room = ch->in_room;
	to_room = NULL;
	
	if ( (( pexit   = in_room->exit[door] ) == NULL
	||  ( to_room = pexit->u1.to_room   ) == NULL
	||  !can_see_room( ch, pexit->u1.to_room )) 
	&&  strcmp( arg2, "down" ))
	{
		ch->println("You cannot throw in that direction.");
		return;
    }

	if ( to_room == NULL )  {
	    act( "You throw $p to the floor.", ch, obj, NULL, TO_CHAR );
	    act( "$n throws $p to the floor.", ch, obj, NULL, TO_ROOM );

	    obj_from_char( obj );
		obj_to_room( obj, in_room );
	}
	else
	{
		if (IS_SET(to_room->room_flags, ROOM_OOC)){
			ch->println("You can't throw objects into OOC rooms.");
			return;
		}
	
	// Allow those with holywalk to throw thru closed doors 
	    if ( IS_SET( pexit->exit_info, EX_CLOSED )
			&& !IS_SET(TRUE_CH(ch)->act, PLR_HOLYWALK) )
	    {
			ch->println("Try opening the door first.");
			return;
	    }

	    act( "You throw $p $T.", ch, obj, dir_name[door], TO_CHAR );
	    act( "$n throws $p $T.", ch, obj, dir_name[door], TO_ROOM );
	    obj_from_char( obj );
	    obj_to_room( obj, to_room );
	
	    ch->in_room = to_room;
	    act( "With a resounding thud, $p lands in the room.", ch, obj, NULL, TO_ROOM );
	    ch->in_room = in_room;
	}

    if ( IS_OBJ_STAT( obj, OBJEXTRA_MELT_DROP ))
    {
		act( "$p dissolves into smoke.", ch, obj, NULL, TO_ROOM );
		extract_obj( obj );
    }

#ifdef unix
	if (!IS_IMMORTAL(ch))
	{
		WAIT_STATE( ch, PULSE_VIOLENCE );
	}
#endif
}

/**************************************************************************/
// mods by Kerenos 
void do_zap( char_data *ch, char *argument )
{
    OBJ_DATA  *wand;
    int sn;
	
    // new stuff here 
    char arg2[MIL];
    char_data *victim;
    OBJ_DATA *obj;
    void *vo;
    int target;
	
    strcpy(arg2, argument);
	
    if ( ( wand = get_eq_char( ch, WEAR_HOLD ) ) == NULL )
    {
        ch->println("You are holding nothing in your hands.");
        return;
    }
	
    if ( wand->item_type != ITEM_WAND )
    {
        ch->println("You can only zap with a wand.");
        return;
    }
	
	if ( IS_SET( ch->in_room->room_flags, ROOM_ANTIMAGIC )) {
		act( "$p doesn't seem to have any power.", ch, wand, NULL, TO_CHAR );
		return;
	}
	
    if (( sn = wand->value[3] ) < 0
		||   sn >= MAX_SKILL
		||   skill_table[sn].spell_fun == 0 )
    {
        bugf( "do_zap(): bad sn %d.", sn );
        return;
    }
	
    WAIT_STATE( ch, 2 * PULSE_VIOLENCE );
	
    if (wand->value[2] < -1){
		wand->value[2] = -1;
    }
	
    if (( wand->value[2] > 0 ) || (wand->value[2] = -1))
    {
        act( "$n holds out $p.", ch, wand, NULL, TO_ROOM );
        act( "You hold out $p.",  ch, wand, NULL, TO_CHAR );
		
        if ( ch->level < wand->level
			||  (get_skill(ch,gsn_wands)==0)
			||   number_percent() >= 20 + get_skill(ch,gsn_wands) * 4/5)
        {
            act ("Your efforts with $p produce only smoke and sparks.",ch,wand,NULL,TO_CHAR);
            act ("...and nothing happens.",ch,NULL,NULL,TO_ROOM);
            check_improve(ch,gsn_wands,false,2);
        }    
        else // cast the spell 
        {   
            // Locate targets.
            victim  = NULL;
            obj     = NULL;
            vo      = NULL;
            target  = TARGET_NONE;
			
            switch ( skill_table[sn].target )
            {
            default:
                bugf( "do_zap(): bad target for sn %d.", sn );
                return;
				
            case TAR_IGNORE:
                break;
				
            case TAR_MOB_OFFENSIVE:
            case TAR_CHAR_OFFENSIVE:
                if ( arg2[0] == '\0' )
                {
                    if ( ( victim = ch->fighting ) == NULL )
                    {
                        ch->println("Zap whom?");
                        return;
                    }
                }
                else
                {
                    if ( ( victim = get_char_room( ch, arg2 ) ) == NULL )
                    {
                        ch->println("They aren't here.");
                        return;
                    }
                }
				
                if ( !IS_NPC(ch) )
                {
					
                    if (is_safe(ch,victim) && victim != ch)
                    {
                        ch->println("Not on that target.");
                        return;
                    }
                }
				
                if ( IS_AFFECTED(ch, AFF_CHARM) && ch->master == victim )
                {
                    ch->println("You can't do that on your own follower.");
                    return;
                }
				
                vo = (void *) victim;
                target = TARGET_CHAR;
                break;
				
            case TAR_CHAR_DEFENSIVE:
                if ( arg2[0] == '\0' )
                {
                    victim = ch;
                }
                else
                {
                    if ( ( victim = get_char_room( ch, arg2 ) ) == NULL )
                    {
						ch->println("They aren't here.");
						return;
                    }
                }
				
                vo = (void *) victim;
                target = TARGET_CHAR;
                break;
				
            case TAR_CHAR_SELF:
                if ( arg2[0] != '\0' && !is_name( arg2, ch->name ) )
                {
                    ch->println("You can't zap yourself while aiming it at another.");
                    return;
                }
				
                vo = (void *) ch;
                target = TARGET_CHAR;
                break;
				
            case TAR_OBJ_INV:
                if ( arg2[0] == '\0' )
                {
                    ch->println("You can't find that.");
					return;
                }
				
                if ( ( obj = get_obj_carry( ch, arg2 ) ) == NULL )
                {
                    ch->println("You are not carrying that.");
                    return;
                }
				
                vo = (void *) obj;
                target = TARGET_OBJ;
                break;
				
            case TAR_OBJ_MOB_OFF:
            case TAR_OBJ_CHAR_OFF:
                if (arg2[0] == '\0')
                {
                    if ((victim = ch->fighting) == NULL)
                    {
                        ch->println("Zap whom or what?");
                        return;
                    }
					
                    target = TARGET_CHAR;
                }
                else if ((victim = get_char_room(ch,arg2)) != NULL)
                {
                    target = TARGET_CHAR;
                }
				
                if (target == TARGET_CHAR) // check the sanity of the attack 
                {
                    if(is_safe_spell(ch,victim,false) && victim != ch)
                    {
						ch->println("Not on that target.");
						return;
                    }
					
					if ( IS_AFFECTED(ch, AFF_CHARM) && ch->master == victim )
					{
						ch->println("You can't do that on your own follower.");
						return;
					}
					
					
                    vo = (void *) victim;
                }
                else if ((obj = get_obj_here(ch,arg2)) != NULL)
                {
                    vo = (void *) obj;
                    target = TARGET_OBJ;
                }
                else
                {
                    ch->println("You don't see that here.");
                    return;
                }
                break; 
				
            case TAR_OBJ_CHAR_DEF:
				if (arg2[0] == '\0')
				{
					vo = (void *) ch;
					target = TARGET_CHAR;                                                 
				}
				else if ((victim = get_char_room(ch,arg2)) != NULL)
				{
					vo = (void *) victim;
					target = TARGET_CHAR;
                }
                else if ((obj = get_obj_carry(ch,arg2)) != NULL)
                {
                    vo = (void *) obj;
                    target = TARGET_OBJ;
                }
                else
                {
                    ch->println("You don't see that here.");
                    return;
                }
                break;
            }// end of spell type switch
			
            if (IS_NPC(ch) || class_table[ch->clss].fMana)
				// clss has spells
                (*skill_table[sn].spell_fun) ( sn, wand->value[0], ch, vo,target);
            else
                (*skill_table[sn].spell_fun) (sn, wand->value[0], ch, vo,target);
            check_improve(ch,gsn_wands,true,2);
			
        }
    }
	
    // get rid of the staff if required 
    if ( wand->value[2] == -1)
    {
        return;
    }
    wand->value[2]--;
	
    if (wand->value[2] < 1)
    {
        act( "$p blazes bright and is gone.", ch, wand, NULL, TO_ROOM );
        act( "$p blazes bright and is gone.", ch, wand, NULL, TO_CHAR );
        extract_obj( wand );
    }
	
    return;
}

/************************************************************************/
void do_place( char_data *ch, char *argument)
{
	char arg1 [MIL];
	char arg2 [MIL];
	char_data *victim;
	OBJ_DATA  *obj;

	argument = one_argument( argument, arg1 );
	argument = one_argument( argument, arg2 );
	
	if ( arg1[0] == '\0' || arg2[0] == '\0' )
	{
		ch->println("Place what on whom?");
		return;
	}
	
    if ( ( obj = get_obj_carry( ch, arg1 ) ) == NULL )
    {
        ch->println("You do not have that item.");
        return;
    }
	
    if ( obj->wear_loc != WEAR_NONE )
    {
        ch->println("You must remove it first.");
        return;
    }

    if ( !can_drop_obj( ch, obj ) )
    {
		ch->println("You can't let go of it.");
		return;
	}

	int	chance = get_skill(ch, gsn_place);
	if (!str_cmp( arg2, "ground"))
	{
		char_data *vch, *vch_next;
		int lowerchance;

		for ( vch = char_list; vch != NULL; vch = vch_next )
		{
			vch_next = vch->next;
			lowerchance=0;

			if ( vch->in_room == NULL )
				continue;
			if ( vch->in_room == ch->in_room )
			{
				if(vch->pcdata && vch->pcdata->is_trying_aware)
					lowerchance = (get_skill(vch, gsn_awareness))/6;
				
				lowerchance += (vch->level - ch->level)/3;

				if(IS_IMMORTAL(vch) || (number_percent()+lowerchance > chance && str_cmp(ch->name, vch->name))){
					vch->printlnf( "You notice %s drop %s", ch->short_descr, obj->short_descr);
					check_improve(vch,gsn_awareness,true,1);
				}
			}
		}

		// silently drop it
		{
			int old_invis_level;
			SET_BIT(ch->dyn,DYN_SILENTLY);
			// back up the wizi level
			old_invis_level= ch->invis_level;
			// hide it from mortals
			ch->invis_level= UMAX(ch->invis_level, LEVEL_IMMORTAL);
			// drop the object
			do_drop(ch, arg1);
			// restore the wizi level
			ch->invis_level=old_invis_level;
		}

		WAIT_STATE( ch, skill_table[gsn_place].beats );
//shouldn't check improve if all they are doing is dropping it
		return;
	}

    if ( ( victim = get_char_room( ch, arg2 ) ) == NULL )
    {
        ch->println("They aren't here.");
        return;
    }

	if (!IS_NPC(victim) && victim->level<10 && ch->level>(victim->level+10)
		&& !IS_NPC(ch) && !IS_IMMORTAL(ch))
	{
		ch->println("A faint wall of light appears to surround them preventing you from placing that on them.");
		return;
	}

    if ( victim == ch )
    {
        ch->println("That's pointless.");
        return;
    }
	
    if (IS_NPC(victim) && victim->pIndexData->pShop != NULL)
    {
        act("$N tells you 'Sorry, you'll have to sell that.'",
			ch,NULL,victim,TO_CHAR);
        return;
    }
	
    if ( !IS_CONTROLLED(victim) && victim->carry_number + get_obj_number( obj ) > can_carry_n( victim ) )
    {
        act( "$N has $S hands full.", ch, NULL, victim, TO_CHAR );
        return;
    }
	
    if (!IS_CONTROLLED(victim) && get_carry_weight(victim) + get_obj_weight(obj) > can_carry_w( victim ) )
    {
        act( "$N can't carry that much weight.", ch, NULL, victim, TO_CHAR );
        return;
    }
	
	if(victim->pcdata && victim->pcdata->is_trying_aware)
		chance -= (get_skill(victim, gsn_awareness))/6;
	if(!IS_AWAKE(victim))
		chance += 50;

	if(number_percent() < chance+((ch->level - victim->level)/3))
	{
		obj_from_char( obj );
		obj_to_char( obj, victim );
		ch->printlnf( "You successfully place %s on %s.", obj->short_descr, victim->short_descr );
		check_improve(ch, gsn_place, true, 1);
		WAIT_STATE( ch, skill_table[gsn_place].beats );
		return;
	}
	
	act( "$n tries to sneak $p to $N.", ch, obj, victim, TO_NOTVICT );
	act( "You try to sneak $p to $N but get caught!", ch, obj, victim, TO_CHAR    );

	if (IS_AWAKE(victim)){
		act( "$n tries to sneak $p into your inventory!",   ch, obj, victim, TO_VICT    );
		if(victim->pcdata && victim->pcdata->is_trying_aware )
			check_improve(victim,gsn_awareness,true,1);
	}
    else{
		if( victim->pcdata && victim->pcdata->is_trying_aware
		 && get_skill(victim, gsn_awareness) > number_percent() )
		{
			victim->printlnf( "You feel the presence of another person lurking near you.\r\n"
				"You feel someone messing with your things and instantly pop awake!" );
			victim->position=POS_RESTING;
			do_stand(victim,"");
			check_improve(victim,gsn_awareness,true,2);
		}else{
			victim->printlnf( "You are partially awakened by someone in the room,\r\n"
				          "you roll over and drift back to sleep." );
		}
	}
	
	affect_strip ( ch, gsn_invisibility					);
	affect_strip ( ch, gsn_mass_invis					);
	affect_strip ( ch, gsn_sneak						);
	REMOVE_BIT   ( ch->affected_by, AFF_HIDE			);
	REMOVE_BIT   ( ch->affected_by, AFF_INVISIBLE		);
	REMOVE_BIT   ( ch->affected_by, AFF_SNEAK			);
	
	if (!IS_NPC(victim))
	{
		ch->pksafe=0;
		ch->pknorecall= (UMAX(ch->pknorecall,2));
		ch->pknoquit=(UMAX(ch->pknoquit,10));
	}

	check_improve(ch, gsn_place, false, 1);
	WAIT_STATE( ch, skill_table[gsn_place].beats*3/2 );

    return;
}
/************************************************************************/
void do_attune( char_data *ch, char *argument)
{
	OBJ_DATA *obj;
	int modifier = 0;
	int percent  = number_percent();
	int failure  = 0;
	int seconds;

    if ( IS_NULLSTR( argument ))
    {
		ch->println( "Attune to which item?" );
		return;
    }
	
	if ( IS_NPC( ch ))
	{
		ch->println( "Players only!" );
		return;
	}

    obj =  get_obj_list(ch,argument,ch->carrying);
	
    if (obj== NULL)
    {
        ch->println( "You don't have that item." );
        return;
    }
	
	if ( !IS_SET( obj->attune_flags, ATTUNE_NEED_TO_USE ))
	{
			ch->println( "You don't need to attune yourself to that." );
			return;
	}

	if ( obj->attune_id == ch->id )
	{
		act( "You have already attuned yourself to $p.", ch, obj, NULL, TO_CHAR );
		return;
	}

	if ( IS_SET( obj->attune_flags, ATTUNE_PREVIOUS ))
	{
		if ( IS_SET( obj->attune_flags, ATTUNE_ONCE_ONLY ))
		{
			act( "$p cannot be reattuned, it only obeys one master.", ch, obj, NULL, TO_CHAR );
			return;
		}
	}

	if ( IS_SET( obj->attune_flags, ATTUNE_EQUAL_LEVEL ))
	{
		if (obj->level > ch->level)
		{
			act( "You lack the power to attune yourself to $p.", ch, obj, NULL, TO_CHAR );
			return;
		}
	}

	if ( current_time < obj->attune_next )
	{
		act( "You cannot attune to $p yet.", ch, obj, NULL, TO_CHAR );
		return;
	}

	// ATTUNE mods

	modifier = ( ch->level - obj->level );

	if ( modifier > 0 ) modifier *= 3;		// +3% per positive level diff
	else modifier *=5;						// -5% per negative level diff

	modifier += 50;							// 50% mean chance
											// EM+EM+PR/3 bonus
	modifier += (( ch->modifiers[STAT_EM] +  ch->modifiers[STAT_EM] +ch->modifiers[STAT_PR] )/3);
											// LORE bonus
	modifier += ((( get_skill( ch, gsn_lore) - 50 ) / 5) +
		(get_skill(ch, gsn_lore) > 0) ? 5 : 0);  // +5% if lore > 50... range is -10 to +15%

	if ( IS_SET( obj->attune_flags, ATTUNE_TRIVIAL ))
		modifier += 25;
	if ( IS_SET( obj->attune_flags, ATTUNE_EASY ))
		modifier += 10;
	if ( IS_SET( obj->attune_flags, ATTUNE_HARD ))
		modifier -= 10;
	if ( IS_SET( obj->attune_flags, ATTUNE_INFURIATING ))
		modifier -= 25;
	if ( IS_SET( obj->attune_flags, ATTUNE_PREVIOUS ))
		obj->attune_modifier -= 10;
	
	modifier += obj->attune_modifier;

	failure = percent - modifier;

	// flag it as previously attempt at attunement for next time :)
	SET_BIT( obj->attune_flags, ATTUNE_PREVIOUS );

	// they failed, let's set the next attune time (in ticks)
	if ( failure > 0 )
	{
		if ( IS_SET( obj->attune_flags, ATTUNE_VANISH ))
		{
			act( "$p glows brightly for an instant, then crumbles to dust.", ch, obj, NULL, TO_CHAR );
			act( "A flash of light emanates from $n and is gone in an instant.", ch, NULL, NULL, TO_ROOM );
			extract_obj(obj);
			return;
		}

		seconds = obj->level * 3600;		// 1 hour per object level base
		
		if ( failure > 30 )
		{
			obj->attune_modifier -= 25;
			seconds *= 2;
		}
		else if ( failure > 10 )
		{
			obj->attune_modifier -= 10;
			seconds *= 3/2;
		}
		else if ( failure > 0 )
		{
			obj->attune_modifier += 10;
			seconds /= 2;
		}
		if ( IS_SET( obj->attune_flags, ATTUNE_TRIVIAL ))
			seconds /= 4;
		if ( IS_SET( obj->attune_flags, ATTUNE_EASY ))
			seconds /= 2;
		if ( IS_SET( obj->attune_flags, ATTUNE_HARD ))
			seconds *= 2;
		if ( IS_SET( obj->attune_flags, ATTUNE_INFURIATING ))
			seconds *= 3;

		obj->attune_next = current_time + seconds;

		act( "You try to exert your will upon $p but were unable to attune yourself to it.", ch, obj, NULL, TO_CHAR );
	
		if ( !is_affected( ch, gsn_cause_headache ))
		{
			AFFECT_DATA af;

			ch->println( "Your head seems to explode with a sudden wave of indescribable pain!" );
			af.where		= WHERE_MODIFIER;
			af.type			= gsn_cause_headache;
			af.level		= obj->level;
			af.duration		= obj->level;
			af.location		= APPLY_SD;
			af.modifier		= - obj->level/5;
			af.bitvector	= 0;
			affect_to_char( ch, &af );
		}
		act( "$p pulsates softly and falls to the ground.", ch, obj, NULL, TO_CHAR );
		act( "$n holds $s head in pain and drops $p.", ch, obj, NULL, TO_ROOM );
		obj_from_char( obj );
		obj_to_room( obj, ch->in_room );
		return;
	}
	else
	{
		obj->attune_id = ch->id;
		obj->attune_next = 0;
		act( "A bright flash envelops $p as you concentrate on it.", ch, obj, NULL, TO_CHAR );
		act( "You have become one with $p.", ch, obj, NULL, TO_CHAR );
		act( "$n is bathed in a flash of light as $e holds $p.", ch, obj, NULL, TO_ROOM );
	}

	return;
}
/**************************************************************************/
void letter_read( char_data *ch, OBJ_DATA *letter );
/**************************************************************************/
// Show an object to another player - Kal Mar 01
void do_show( char_data *ch, char *argument)
{
	char arg[MIL];
	char *pdesc;
	char objname[MIL];
	int number,count;
	OBJ_DATA *obj;

	argument=one_argument(argument, arg);
	number = number_argument(arg,objname);
	count = 0;

	if(IS_NULLSTR(argument)){
		ch->println("Show what object to whom?");
		ch->println("hint: if you want to display your affects at the bottom of your score use the `=Cshowaffects`x command");
		return;
	}

	char_data *victim=get_char_room(ch, argument);
	if(!victim){
		ch->printlnf("Could not find any '%s' in the room to show '%s' to.",
			argument, objname);
		return;
	}

	if(!IS_AWAKE(victim)){
		ch->printlnf("%s doesnt appear to be awake.", capitalize(PERS(victim, ch)));
		return;
	}

	if(!can_see(victim, ch)){
		ch->printlnf("%s doesn't appear to be able to see you.", capitalize(PERS(victim, ch)));
		return;
	}

    for ( obj = ch->carrying; obj != NULL; obj = obj->next_content )
    {
        if ( can_see_obj( ch, obj ) )
        {  // player can see object
			
			// parchments take highest priority
            if ( obj->item_type == ITEM_PARCHMENT && is_name( objname, obj->name ) )
            {
                if (++count == number)
                {
					if(can_see_obj( victim, obj )){
						ch->printlnf("You show %s to %s.", obj->short_descr, PERS(victim, ch));
						victim->printlnf("`W%s shows you %s, it reads:`x",	capitalize(PERS(ch, victim)), obj->short_descr);
						letter_read( victim, obj );
					}else{
						ch->printlnf("It doesn't appear as though %s can see %s.",
							PERS(victim, ch), obj->short_descr);
					}
                    return;
                }
            }
            // use only one of the sets of extended descriptions 
            if (obj->extra_descr)
            { // unique objects extended descriptions 
                pdesc = get_extra_descr( objname, obj->extra_descr );
                if ( pdesc != NULL )
                {
                    if (++count == number)
                    {
						if(can_see_obj( victim, obj )){
							ch->printlnf("You show %s to %s.", obj->short_descr, PERS(victim, ch));
							victim->printlnf("`W%s shows you %s:`x", capitalize(PERS(ch, victim)), obj->short_descr);
	                        victim->sendpage(pdesc);
						}else{
							ch->printlnf("It doesn't appear as though %s can see %s.",
								PERS(victim, ch), obj->short_descr);
						}
                        return;
                    }
                    else
                    {
                        continue;
                    }
                }
            }
            else // vnums extended descriptions
            {        
                pdesc = get_extra_descr( objname, obj->pIndexData->extra_descr );
                if ( pdesc != NULL )
                {
                    if (++count == number)
                    { 
						if(can_see_obj( victim, obj )){
							ch->printlnf("You show %s to %s.", obj->short_descr, PERS(victim, ch));
							victim->printlnf("`W%s shows you %s:`x", capitalize(PERS(ch, victim)), obj->short_descr);
	                        victim->sendpage(pdesc);
						}else{
							ch->printlnf("It doesn't appear as though %s can see %s.",
								PERS(victim, ch), obj->short_descr);
						}
                        return;
                    }
                    else
                    {
                        continue;
                    }
                }
            }
			
            if ( is_name( objname, obj->name ) )
            {
                if (++count == number)
                {
					if(can_see_obj( victim, obj )){
						ch->printlnf("You show %s to %s.", obj->short_descr, PERS(victim, ch));
						victim->printlnf("`W%s shows you %s:`x", capitalize(PERS(ch, victim)), obj->short_descr);
	                    victim->printlnf( "%s", obj->description );
					}else{
						ch->printlnf("It doesn't appear as though %s can see %s.",
							PERS(victim, ch), obj->short_descr);
					}
                    return;
                }
            }
        }
		
    }
	ch->printlnf("You possess no '%s' object to show %s.", arg, PERS(victim, ch));
}
/************************************************************************/
/************************************************************************/
/************************************************************************/
/************************************************************************/