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


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

const char *const dir_name[] =
{
    "north", "east", "south", "west", "up", "down"
};

const int rev_dir[] =
{
    DIR_SOUTH, DIR_WEST, DIR_NORTH, DIR_EAST, DIR_DOWN, DIR_UP
};

const int movement_loss[SECT_MAX] =
{
    1, 2, 2, 3, 4, 6, 4, 1, 5, 10, 6, 1, 10
};


/*
 * Local functions.
 */
int find_door		args( ( CHAR_DATA *ch, const char *arg ) );
int find_first_step	args( ( ROOM_INDEX_DATA *start,
				ROOM_INDEX_DATA *target ) );
void add_track_q	args( ( ROOM_INDEX_DATA *room, int dir ) );
void clean_track_q	args( ( void ) );
void found_prey		args( ( CHAR_DATA *ch, CHAR_DATA *victim ) );

/*
 * External functions.
 */
void one_hit		args( ( CHAR_DATA *ch, CHAR_DATA *victim, int dt,
				int wpn ) );


void move_char( CHAR_DATA *ch, int door )
{
    CHAR_DATA *fch;
    CHAR_DATA *fch_next;
    CHAR_DATA *vmob, *vnext;
    OBJ_DATA *obj, *objnext;
    EXIT_DATA *pexit;
    ROOM_INDEX_DATA *in_room;
    ROOM_INDEX_DATA *to_room;
    int moved = PLR_MOVED;	/* Same for both ACT & PLR bits */
    bool stumble = FALSE;

    if( door < 0 || door > 5 )
    {
	bug( "Do_move: bad door %d.", door );
	return;
    }

    /*
     * Prevents infinite move loop in
     * maze zone when group has 2 leaders - Kahn
     */
    if( xIS_SET( ch->act, moved ) )
	return;

    for( vmob = ch->in_room->people; vmob; vmob = vmob->next_in_room )
    {
	if( !vmob->deleted && vmob->fighting == ch )
	{
	    send_to_char( "Not while someone here want's to kill you.\n\r", ch );
	    return;
	}
    }

    if( IS_AFFECTED( ch, AFF_HOLD ) )
    {
	AFFECT_DATA *af;
	char buf[100];

	send_to_char( "You are stuck in a snare!  You can't move!\n\r", ch );
	WAIT_STATE( ch, PULSE_VIOLENCE / 2 );
	web_update( ch );
	for( af = ch->affected; af; af = af->next )
	{
	    if( af->deleted || !xIS_SET( af->bitvector, AFF_HOLD )
		|| af->duration < 0 )
		continue;

	    if( --af->duration == 0 )
	    {
		affect_remove( ch, af );
		sprintf( buf, "You are no longer held back by '%s'.\n\r",
			 skill_table[af->type].name );
		send_to_char( buf, ch );
	    }
	}
	return;
    }

    in_room = ch->in_room;
    if( ( IS_AFFECTED( ch, AFF_CONFUSION ) && number_bits( 1 ) )
	|| ( !IS_NPC( ch ) && number_bits( 7 )
	     < UMIN( 55, ch->pcdata->condition[COND_DRUNK] / 10 - 27 ) ) )
    {
	stumble = TRUE;
	act( "&y$n stumbles and staggers wildly about.",
	     ch, NULL, NULL, TO_ROOM );
	send_to_char( "You trip over your own fool feet.\n\r", ch );
	door = number_range( 0, 5 );
    }

    if( !( pexit = in_room->exit[door] ) || !( to_room = pexit->to_room ) )
    {
	if( stumble )
	{
	    act( "$n whacks into a wall.", ch, NULL, NULL, TO_ROOM );
	    send_to_char( "You run into a wall... Ow that hurt!\n\r", ch );
	}
	else
	    send_to_char( "Alas, you cannot go that way.\n\r", ch );
	return;
    }

    if( IS_SET( pexit->exit_info, EX_CLOSED ) )
    {
	if( !IS_AFFECTED( ch, AFF_PASS_DOOR )
	    && !IS_SET( race_table[ch->race].race_abilities,
			RACE_PASSDOOR ) )
	{
	    if( stumble )
	    {
		act( "$n whacks into a closed door.", ch, NULL, NULL, TO_ROOM );
		send_to_char( "You run into a door... Ow that hurt!\n\r", ch );
	    }

	    act( "The $d is closed.",
		 ch, NULL, pexit->keyword, TO_CHAR );
	    return;
	}

	if( IS_SET( pexit->exit_info, EX_PASSPROOF )
	    && !IS_IMMORTAL( ch ) )
	{
	    if( stumble )
		act( "$n whacks into a closed door.", ch, NULL, NULL, TO_ROOM );
	    act( "You are unable to pass through the $d.  Ouch!",
		 ch, NULL, pexit->keyword, TO_CHAR );
	    return;
	}
    }

    if( IS_AFFECTED( ch, AFF_CHARM ) && ch->master
	&& in_room == ch->master->in_room )
    {
	send_to_char( "What?  And leave your beloved master?\n\r", ch );
	return;
    }

    if( ( to_room->area->plane->min_level > get_trust( ch )
	  || ( !IS_NPC( ch ) && to_room->area != in_room->area
	       && IS_SET( to_room->area->area_flags, AREA_HIDE ) ) )
	&& !IS_BUILDER( ch, to_room->area ) )
    {
	send_to_char( "&rSomething holds you back from going there.&n\n\r", ch );
	return;
    }

    if( room_is_private( to_room ) )
    {
	send_to_char( "That room is private right now.\n\r", ch );
	return;
    }

    if( !IS_NPC( ch ) )
    {
	int move;

	if( door == DIR_UP && ( to_room->sector_type == SECT_AIR
				|| in_room->sector_type == SECT_AIR )
	    && !IS_SET( ch->body_parts, BODY_PART_WINGS )
	    && !xIS_SET( ch->affected_by, AFF_FLYING )
	    && !IS_SET( to_room->room_flags, ROOM_FALL ) )
	{
	    send_to_char( "You can't fly.\n\r", ch );
	    return;
	}
	if( to_room->sector_type == SECT_SPACE
	    && !xIS_SET( ch->affected_by, AFF_FLYING )
	    && !IS_SET( to_room->room_flags, ROOM_FALL ) )
	{
	    send_to_char( "You can't fly there.\n\r", ch );
	    return;
	}

	if( to_room->sector_type != SECT_WATER_NOSWIM
	    && to_room->sector_type != SECT_UNDERWATER
	    && !IS_SET( to_room->room_flags, ROOM_FLOODED )
	    && !xIS_SET( ch->affected_by, AFF_FLYING )
	    && !IS_SET( ch->body_parts, BODY_PART_TAIL )
	    && !IS_SET( ch->body_parts, BODY_PART_WINGS )
	    && !IS_SET( ch->body_parts, BODY_PART_LEGS ) )
	{
	    send_to_char( "You flap around but you cant move!\n\r", ch );
	    return;
	}

	if( in_room->sector_type == SECT_WATER_NOSWIM
	    || to_room->sector_type == SECT_WATER_NOSWIM )
	{
	    for( obj = ch->carrying; obj; obj = obj->next_content )
		if( obj->item_type == ITEM_BOAT )
		    break;

	    /*
	     * Suggestion for flying above water by Sludge
	     */
	    if( !obj && !IS_SET( ch->body_parts, BODY_PART_WINGS )
		&& !xIS_SET( ch->affected_by, AFF_FLYING )
		&& !IS_SET( race_table[ch->race].race_abilities, RACE_SWIM ) )
	    {
		send_to_char( "You need a boat to go there.\n\r", ch );
		return;
	    }
	}

	if( ( in_room->sector_type == SECT_UNDERWATER
	      || to_room->sector_type == SECT_UNDERWATER
	      || IS_SET( in_room->room_flags, ROOM_FLOODED )
	      || IS_SET( to_room->room_flags, ROOM_FLOODED ) )
	    && !IS_SET( race_table[ch->race].race_abilities, RACE_SWIM )
	    && !IS_AFFECTED( ch, AFF_BREATHING )
	    && !IS_IMMORTAL( ch ) )
	{
	    send_to_char( "You need to be able to swim to go there.\n\r", ch );
	    return;
	}

	move = movement_loss[UMIN( SECT_MAX - 1, in_room->sector_type )]
	    + movement_loss[UMIN( SECT_MAX - 1, to_room->sector_type )];
	/*
	 * Flying persons lose constant minimum movement.
	 */
	if( IS_SET( ch->body_parts, BODY_PART_WINGS )
	    || xIS_SET( ch->affected_by, AFF_FLYING ) )
	    move = 2;

	if( ch->move < move )
	{
	    if( !get_success( ch, gsn_stamina, 100 ) )
	    {
		send_to_char( "You are too exhausted.\n\r", ch );
		return;
	    }
	    if( ch->hit < ch->max_hit / 3 )
	    {
		send_to_char(
		    "Your body is too exhausted, "
		    "your tired muscles will move no more.\n\r", ch );
		return;
	    }
	    send_to_char( "You push your tired body past it's limits.\n\r", ch );
	    move -= ch->move;
	    ch->move = 0;
	    ch->hit -= move;
	    WAIT_STATE( ch, skill_table[gsn_stamina].beats );
	}
	else
	{
	    if( IS_AFFECTED( ch, AFF_HASTE ) )
		WAIT_STATE( ch, PULSE_PER_SECOND / 2 );
	    else
		WAIT_STATE( ch, PULSE_PER_SECOND );
	    ch->move -= move;
	}
    }

    if( !IS_AFFECTED( ch, AFF_SNEAK )
	&& ( IS_NPC( ch ) || !xIS_SET( ch->act, PLR_WIZINVIS ) ) )
    {
	if( IS_NPC( ch ) )
	    REMOVE_BIT( SysInfo->flags, SYSINFO_ACT_TRIGGER );
	if( ( ( in_room->sector_type == SECT_WATER_SWIM )
	      || ( in_room->sector_type == SECT_UNDERWATER )
	      || IS_SET( in_room->room_flags, ROOM_FLOODED ) )
	    && ( ( to_room->sector_type == SECT_WATER_SWIM )
		 || ( to_room->sector_type == SECT_UNDERWATER )
		 || IS_SET( to_room->room_flags, ROOM_FLOODED ) ) )
	    act( "&g$n swims $T.", ch, NULL, dir_name[door], TO_ROOM );
	else
	{
	    if( stumble )
		act( "&g$n staggers $T.", ch, NULL, dir_name[door], TO_ROOM );
	    if( !IS_NPC( ch ) && ch->pcdata->setmout
		&& ch->pcdata->setmout[0] != '\0' )
		act( "&g$n $t $T.", ch, ch->pcdata->setmout,
		     dir_name[door], TO_ROOM );
	    else
		act( "&g$n leaves $T.", ch, NULL, dir_name[door], TO_ROOM );
	}
    }

    /*
     * Leave program
     */
    if( ch->in_room == in_room
	&& xIS_SET( in_room->progtypes, LEAVE_PROG ) )
	greet_leave_trigger( ch, NULL, in_room, NULL,
			     door, LEAVE_PROG );

    for( vmob = in_room->people;
	 ch->in_room == in_room && vmob != NULL; vmob = vnext )
    {
	vnext = vmob->next_in_room;

	if( !vmob->deleted && IS_NPC( vmob ) && ch != vmob
	    && vmob->fighting == NULL && IS_AWAKE( vmob )
	    && xIS_SET( vmob->pIndexData->progtypes, LEAVE_PROG ) )
	    greet_leave_trigger( ch, vmob, NULL, NULL, door,
				 LEAVE_PROG );
    }

    for( obj = in_room->contents;
	 ch->in_room == in_room && obj != NULL; obj = objnext )
    {
	objnext = obj->next_content;

	if( !obj->deleted && !ch->deleted
	    && xIS_SET( obj->pIndexData->progtypes, LEAVE_PROG ) )
	    greet_leave_trigger( ch, NULL, NULL, obj, door,
				 LEAVE_PROG );
    }

    /* Safety check, the character may have been deleted or moved by
       the program. */
    if( ch->deleted || ch->in_room != in_room )
	return;

    char_from_room( ch );
    char_to_room( ch, to_room );

    if( !IS_AFFECTED( ch, AFF_SNEAK )
	&& ( IS_NPC( ch ) || !xIS_SET( ch->act, PLR_WIZINVIS ) ) )
    {
	if( IS_NPC( ch ) )
	    REMOVE_BIT( SysInfo->flags, SYSINFO_ACT_TRIGGER );
	if( stumble )
	    act( "&g$n staggers in from the $T.",
		 ch, NULL, dir_name[rev_dir[door]], TO_ROOM );
	else if( !IS_NPC( ch ) && ch->pcdata->setmin &&
	    ch->pcdata->setmin[0] != '\0' )
	    act( "&g$n $t $T.", ch, ch->pcdata->setmin,
		 dir_name[rev_dir[door]], TO_ROOM );
	else
	    act( "&g$n has arrived from the $t.", ch,
		 dir_name[rev_dir[door]], NULL, TO_ROOM );
    }

    /*
     * Because of the addition of the deleted flag, we can do this -Kahn
     */
    if( !IS_NPC( ch ) && !IS_IMMORTAL( ch ) )
    {
	if( to_room->sector_type == SECT_UNDERWATER
	    && !str_cmp( race_table[ch->race].name, "Vampire" ) )
	{
	    send_to_char( "Arrgh!  Large body of water!\n\r", ch );
	    act( "$n thrashes underwater!", ch, NULL, NULL, TO_ROOM );
	    damage( ch, ch, 20, TYPE_UNDEFINED, WEAR_NONE );
	}
	else if( !IS_AFFECTED( ch, AFF_BREATHING )
		 && to_room->sector_type == SECT_UNDERWATER
		 && !IS_SET( ch->body_parts, BODY_PART_GILLS ) )
	{
	    send_to_char( "You can't breathe!\n\r", ch );
	    act( "$n sputters and chokes!", ch, NULL, NULL, TO_ROOM );
	    damage( ch, ch, ch->level + 5, gsn_breathing, WEAR_NONE );
	}
	else if( ch->in_room->sector_type == SECT_SPACE
		 && !IS_AFFECTED( ch, AFF_BREATHING ) )
	{
	    send_to_char( "You can't breathe!\n\r", ch );
	    act( "$n can't breathe, $e is turning red!",
		 ch, NULL, NULL, TO_ROOM );
	    damage( ch, ch, ch->hit / 20 + 5,
		    gsn_breathing, WEAR_NONE );
	}
    }

    /*
     * Suggested by D'Sai from A Moment in Tyme Mud.  Why have mobiles
     * see the room?  -Kahn
     */
    if( ch->desc )
	do_look( ch, AUTOLOOK );

    /*
     * Greet and Entry programs.
     */
    if( IS_NPC( ch )
	&& xIS_SET( ch->pIndexData->progtypes, ENTRY_PROG ) )
	mprog_percent_check( ch, NULL, NULL, NULL, ENTRY_PROG );

    if( ch->in_room == to_room )
    {
	if( ( xIS_SET( to_room->progtypes, ALL_GREET_PROG )
	      && greet_leave_trigger( ch, NULL, to_room, NULL,
				      rev_dir[door], ALL_GREET_PROG ) == 0 )
	    || ( xIS_SET( to_room->progtypes, GREET_PROG )
		 && !IS_AFFECTED( ch, AFF_SNEAK )
		 && greet_leave_trigger( ch, NULL, to_room, NULL,
					 rev_dir[door], GREET_PROG ) == 0 ) )
	{
	    char_from_room( ch );
	    char_to_room( ch, in_room );
	    return;
	}
    }

    for( vmob = to_room->people;
	 ch->in_room == to_room && vmob != NULL; vmob = vnext )
    {
	vnext = vmob->next_in_room;
	if( vmob->deleted || !IS_NPC( vmob ) || ch == vmob )
	    continue;

	if( IS_SET( spec_table[vmob->spec_fun].usage, SPEC_ENTER )
	    && ( *spec_table[vmob->spec_fun].spec_fun )
	    ( vmob, ch, SPEC_ENTER, NULL ) )
	    continue;

	if( vmob->fighting == NULL && IS_AWAKE( vmob ) )
	{
	    if( xIS_SET( vmob->pIndexData->progtypes, ALL_GREET_PROG ) )
		greet_leave_trigger( ch, vmob, NULL, NULL, rev_dir[door],
				     ALL_GREET_PROG );
	    if( xIS_SET( vmob->pIndexData->progtypes, GREET_PROG )
		&& !IS_AFFECTED( ch, AFF_SNEAK ) && can_see( vmob, ch ) )
		greet_leave_trigger( ch, vmob, NULL, NULL, rev_dir[door],
				     GREET_PROG );
	}
    }

    for( obj = to_room->contents;
	 ch->in_room == to_room && obj != NULL; obj = objnext )
    {
	objnext = obj->next_content;

	if( obj->deleted || ch->deleted )
	    continue;

	if( xIS_SET( obj->pIndexData->progtypes, ALL_GREET_PROG ) )
	    greet_leave_trigger( ch, NULL, NULL, obj, rev_dir[door],
				 ALL_GREET_PROG );
	if( xIS_SET( obj->pIndexData->progtypes, GREET_PROG )
	    && !IS_AFFECTED( ch, AFF_SNEAK ) )
	    greet_leave_trigger( ch, NULL, NULL, obj, rev_dir[door],
				 GREET_PROG );
    }

    /*
     * Following.
     */
    xSET_BIT( ch->act, moved );

    for( fch = in_room->people; fch; fch = fch_next )
    {
	fch_next = fch->next_in_room;

	if( fch->deleted )
	    continue;

	if( fch->master == ch && fch->position > POS_RESTING )
	{
	    if( fch->fighting )
		act( "&rYou can't leave while you are still fighting.",
		     fch, NULL, NULL, TO_CHAR );
	    else
	    {
		if( fch->position == POS_SITTING )
		    fch->position = POS_STANDING;

		act( "You follow $N.", fch, NULL, ch, TO_CHAR );
		move_char( fch, door );
	    }
	}
    }

    xREMOVE_BIT( ch->act, moved );
    return;
}


void do_north( CHAR_DATA *ch, const char *argument )
{
    move_char( ch, DIR_NORTH );
    return;
}


void do_east( CHAR_DATA *ch, const char *argument )
{
    move_char( ch, DIR_EAST );
    return;
}


void do_south( CHAR_DATA *ch, const char *argument )
{
    move_char( ch, DIR_SOUTH );
    return;
}


void do_west( CHAR_DATA *ch, const char *argument )
{
    move_char( ch, DIR_WEST );
    return;
}


void do_up( CHAR_DATA *ch, const char *argument )
{
    move_char( ch, DIR_UP );
    return;
}


void do_down( CHAR_DATA *ch, const char *argument )
{
    move_char( ch, DIR_DOWN );
    return;
}


int find_door( CHAR_DATA *ch, const char *arg )
{
    EXIT_DATA *pexit;
    int door;

    if( !str_prefix( arg, "north" ) )
	door = 0;
    else if( !str_prefix( arg, "east" ) )
	door = 1;
    else if( !str_prefix( arg, "south" ) )
	door = 2;
    else if( !str_prefix( arg, "west" ) )
	door = 3;
    else if( !str_prefix( arg, "up" ) )
	door = 4;
    else if( !str_prefix( arg, "down" ) )
	door = 5;
    else
    {
	for( door = 0; door <= 5; door++ )
	{
	    if( ( pexit = ch->in_room->exit[door] )
		&& IS_SET( pexit->exit_info, EX_ISDOOR )
		&& pexit->keyword
		&& is_name( arg, pexit->keyword ) )
		return door;
	}

	return -1;
    }

    if( !( pexit = ch->in_room->exit[door] )
	|| !IS_SET( pexit->exit_info, EX_ISDOOR ) )
	return -1;

    return door;
}


void do_open( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    int door, dam;

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

    if( ( door = find_door( ch, argument ) ) >= 0 )
    {
	/* 'open door' */
	EXIT_DATA *pexit;
	EXIT_DATA *pexit_rev;
	ROOM_INDEX_DATA *to_room;

	pexit = ch->in_room->exit[door];
	if( !IS_SET( pexit->exit_info, EX_CLOSED ) )
	{
	    send_to_char( "It's already open.\n\r", ch );
	    return;
	}

	if( IS_SET( pexit->exit_info, EX_LOCKED ) )
	{
	    send_to_char( "It's locked.\n\r", ch );
	    return;
	}

	REMOVE_BIT( pexit->exit_info, EX_CLOSED );
	act( "&y$n opens the $d.&n", ch, NULL, pexit->keyword, TO_ROOM );
	send_to_char( "Ok.\n\r", ch );

	/*
	 * open the other side
	 */
	if( ( to_room = pexit->to_room )
	    && ( pexit_rev = to_room->exit[rev_dir[door]] )
	    && pexit_rev->to_room == ch->in_room )
	{
	    CHAR_DATA *rch;

	    REMOVE_BIT( pexit_rev->exit_info, EX_CLOSED );
	    for( rch = to_room->people; rch; rch = rch->next_in_room )
	    {
		if( rch->deleted )
		    continue;
		act( "&yThe $d opens.&n", rch, NULL, pexit_rev->keyword, TO_ALL );
		break;
	    }
	}

	/* trapped door routine */
	if( IS_SET( pexit->exit_info, EX_TRAPPED ) )
	{
	    int trap;

	    trap = number_range( gsn_first_trap, gsn_last_trap );
	    dam = 4 + ch->level / 2 + ch->hit / 10;
	    if( number_percent( ) < power( 8, 7, get_curr_dex( ch ) - 16 ) )
	    {
		act( "&rA $t shoots out and barely misses $n!&n", ch,
		    skill_table[trap].noun_damage, NULL, TO_ROOM );
		act( "&rA $t shoots out and barely misses you!&n", ch,
		    skill_table[trap].noun_damage, NULL, TO_CHAR );
	    }

	    else
	    {
		act( "&rA $t shoots out and strikes $n!&n", ch,
		    skill_table[trap].noun_damage, NULL, TO_ROOM );
		act( "&rA $t shoots out and strikes you!&n", ch,
		    skill_table[trap].noun_damage, NULL, TO_CHAR );
		damage( ch, ch, dam, trap, WEAR_NONE );
	    }
	}

	if( door == DIR_DOWN )
	    char_fall_check( ch, 0 );
	return;
    }

    if( ( obj = get_obj_here( ch, argument ) ) )
    {
	/* 'open object' */
	if( obj->item_type != ITEM_CONTAINER )
	{
	    send_to_char( "That's not a container.\n\r", ch );
	    return;
	}

	if( !IS_SET( obj->value[1], CONT_CLOSED ) )
	{
	    send_to_char( "It's already open.\n\r", ch );
	    return;
	}

	if( !IS_SET( obj->value[1], CONT_CLOSEABLE ) )
	{
	    send_to_char( "You can't do that.\n\r", ch );
	    return;
	}

	if( IS_SET( obj->value[1], CONT_LOCKED ) )
	{
	    send_to_char( "It's locked.\n\r", ch );
	    return;
	}

	if( IS_SET( obj->value[1], CONT_TRAPPED ) )
	{
	    send_to_char( "That was trapped!\n\r", ch );
	    dam = 4 + ch->level / 2 + ch->hit / 12;
	    if( number_percent( ) < power( 10, 7, get_curr_dex( ch ) - 15 ) )
	    {
		act( "&rA small dart shoots out and barely misses $n!&n", ch,
		    NULL, NULL, TO_ROOM );
		act( "&rA small dart shoots out and barely misses you!&n", ch,
		    NULL, NULL, TO_CHAR );
	    }
	    else
	    {
		act( "&rA small dart shoots out and strikes $n!&n", ch,
		    NULL, NULL, TO_ROOM );
		act( "&rA small dart shoots out and strikes you!&n", ch,
		    NULL, NULL, TO_CHAR );
		damage( ch, ch, dam, gsn_first_trap, WEAR_NONE );
	    }
	}

	REMOVE_BIT( obj->value[1], CONT_CLOSED );
	send_to_char( "Ok.\n\r", ch );
	act( "&y$n opens $p&y.&n", ch, obj, NULL, TO_ROOM );
	if( xIS_SET( obj->pIndexData->progtypes, OPEN_PROG ) )
	    oprog_percent_check( ch, obj, NULL, OPEN_PROG );
	return;
    }

    act( "I see no $T here.", ch, NULL, argument, TO_CHAR );
    return;
}


void do_close( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
	    int door;

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

    if( ( door = find_door( ch, argument ) ) >= 0 )
    {
	/*
	   'close door'
	 */
	EXIT_DATA *pexit;
	EXIT_DATA *pexit_rev;
	ROOM_INDEX_DATA *to_room;

	pexit = ch->in_room->exit[door];
	if( IS_SET( pexit->exit_info, EX_CLOSED ) )
	{
	    send_to_char( "It's already closed.\n\r", ch );
	    return;
	}

	if( IS_SET( pexit->exit_info, EX_BASHED ) )
	{
	    act( "The $d has been bashed open and cannot be closed.",
		ch, NULL, pexit->keyword, TO_CHAR );
	    return;
	}

	SET_BIT( pexit->exit_info, EX_CLOSED );
	act( "&y$n closes the $d.&n", ch, NULL, pexit->keyword, TO_ROOM );
	send_to_char( "Ok.\n\r", ch );

	/*
	   close the other side
	 */
	if( ( to_room = pexit->to_room )
	    && ( pexit_rev = to_room->exit[rev_dir[door]] )
	    && pexit_rev->to_room == ch->in_room )
	{
	    CHAR_DATA *rch;

	    SET_BIT( pexit_rev->exit_info, EX_CLOSED );
	    for( rch = to_room->people; rch; rch = rch->next_in_room )
	    {
		if( rch->deleted )
		    continue;
		act( "The $d closes.", rch, NULL, pexit_rev->keyword, TO_CHAR );
	    }
	}

	return;
    }

    if( ( obj = get_obj_here( ch, argument ) ) )
    {
	/*
	   'close object'
	 */
	if( obj->item_type != ITEM_CONTAINER )
	{
	    send_to_char( "That's not a container.\n\r", ch );
	    return;
	}

	if( IS_SET( obj->value[1], CONT_CLOSED ) )
	{
	    send_to_char( "It's already closed.\n\r", ch );
	    return;
	}

	if( !IS_SET( obj->value[1], CONT_CLOSEABLE ) )
	{
	    send_to_char( "You can't do that.\n\r", ch );
	    return;
	}

	SET_BIT( obj->value[1], CONT_CLOSED );
	send_to_char( "Ok.\n\r", ch );
	act( "&y$n closes $p&y.&n", ch, obj, NULL, TO_ROOM );
	if( xIS_SET( obj->pIndexData->progtypes, CLOSE_PROG ) )
	    oprog_percent_check( ch, obj, NULL, CLOSE_PROG );
	return;
    }

    act( "I see no $T here.", ch, NULL, argument, TO_CHAR );
    return;
}


OBJ_DATA *has_key( CHAR_DATA *ch, int key )
{
    OBJ_DATA *obj;

    for( obj = ch->carrying; obj; obj = obj->next_content )
    {
	if( !obj->deleted && obj->pIndexData->vnum == key
	    && can_see_obj( ch, obj ) )
	    return obj;
    }

    return NULL;
}


void do_lock( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    int door;

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

    if( ( door = find_door( ch, argument ) ) >= 0 )
    {
	/*
	   'lock door'
	 */
	EXIT_DATA *pexit;
	EXIT_DATA *pexit_rev;
	ROOM_INDEX_DATA *to_room;

	pexit = ch->in_room->exit[door];
	if( !IS_SET( pexit->exit_info, EX_CLOSED ) )
	{
	    send_to_char( "It's not closed.\n\r", ch );
	    return;
	}
	if( pexit->key < 0 )
	{
	    send_to_char( "It can't be locked.\n\r", ch );
	    return;
	}
	if( !has_key( ch, pexit->key ) )
	{
	    send_to_char( "You lack the key.\n\r", ch );
	    return;
	}
	if( IS_SET( pexit->exit_info, EX_LOCKED ) )
	{
	    send_to_char( "It's already locked.\n\r", ch );
	    return;
	}

	SET_BIT( pexit->exit_info, EX_LOCKED );
	send_to_char( "&y*Click*&n\n\r", ch );
	act( "&y$n locks the $d.&n", ch, NULL, pexit->keyword, TO_ROOM );

	/*
	   lock the other side
	 */
	if( ( to_room = pexit->to_room )
	    && ( pexit_rev = to_room->exit[rev_dir[door]] )
	    && pexit_rev->to_room == ch->in_room )
	{
	    SET_BIT( pexit_rev->exit_info, EX_LOCKED );
	}
	return;
    }

    if( ( obj = get_obj_here( ch, argument ) ) )
    {
	/*
	   'lock object'
	 */
	if( obj->item_type != ITEM_CONTAINER )
	{
	    send_to_char( "That's not a container.\n\r", ch );
	    return;
	}
	if( !IS_SET( obj->value[1], CONT_CLOSED ) )
	{
	    send_to_char( "It's not closed.\n\r", ch );
	    return;
	}
	if( obj->value[2] < 0 )
	{
	    send_to_char( "It can't be locked.\n\r", ch );
	    return;
	}

	if( !has_key( ch, obj->value[2] ) )
	{
	    send_to_char( "You lack the key.\n\r", ch );
	    return;
	}

	if( IS_SET( obj->value[1], CONT_LOCKED ) )
	{
	    send_to_char( "It's already locked.\n\r", ch );
	    return;
	}

	SET_BIT( obj->value[1], CONT_LOCKED );
	send_to_char( "*Click*\n\r", ch );
	act( "$n locks $p.", ch, obj, NULL, TO_ROOM );
	return;
    }

    act( "I see no $T here.", ch, NULL, argument, TO_CHAR );
    return;
}


void do_unlock( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    int door;

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

    if( ( door = find_door( ch, argument ) ) >= 0 )
    {
	/* 'unlock door' */
	EXIT_DATA *pexit;
	EXIT_DATA *pexit_rev;
	ROOM_INDEX_DATA *to_room;

	pexit = ch->in_room->exit[door];
	if( !IS_SET( pexit->exit_info, EX_CLOSED ) )
	{
	    send_to_char( "It's not closed.\n\r", ch );
	    return;
	}
	if( pexit->key < 0 )
	{
	    send_to_char( "It can't be unlocked.\n\r", ch );
	    return;
	}
	if( !( obj = has_key( ch, pexit->key ) ) )
	{
	    send_to_char( "You lack the key.\n\r", ch );
	    return;
	}
	if( !IS_SET( pexit->exit_info, EX_LOCKED ) )
	{
	    send_to_char( "It's already unlocked.\n\r", ch );
	    return;
	}

	REMOVE_BIT( pexit->exit_info, EX_LOCKED );
	send_to_char( "&y*Click*&n\n\r", ch );
	act( "&y$n unlocks the $d.&n", ch, NULL, pexit->keyword, TO_ROOM );

	/* unlock the other side */
	if( ( to_room = pexit->to_room )
	    && ( pexit_rev = to_room->exit[rev_dir[door]] )
	    && pexit_rev->to_room == ch->in_room )
	    REMOVE_BIT( pexit_rev->exit_info, EX_LOCKED );

	if( xIS_SET( obj->pIndexData->progtypes, USE_PROG ) )
	    oprog_percent_check( ch, obj, NULL, USE_PROG );

	if( IS_SET( pexit->exit_info, EX_EAT_KEY ) )
	{
	    act( "The $d makes a grinding noise and $p turns to powder!",
		 ch, obj, pexit->keyword, TO_ALL );
	    extract_obj( obj );
	}
	return;
    }

    if( ( obj = get_obj_here( ch, argument ) ) )
    {
	/* 'unlock object' */
	if( obj->item_type != ITEM_CONTAINER )
	{
	    send_to_char( "That's not a container.\n\r", ch );
	    return;
	}
	if( !IS_SET( obj->value[1], CONT_CLOSED ) )
	{
	    send_to_char( "It's not closed.\n\r", ch );
	    return;
	}
	if( obj->value[2] < 0 )
	{
	    send_to_char( "It can't be unlocked.\n\r", ch );
	    return;
	}
	if( !has_key( ch, obj->value[2] ) )
	{
	    send_to_char( "You lack the key.\n\r", ch );
	    return;
	}
	if( !IS_SET( obj->value[1], CONT_LOCKED ) )
	{
	    send_to_char( "It's already unlocked.\n\r", ch );
	    return;
	}
	if( IS_SET( obj->value[1], CONT_EAT_KEY ) )
	{
	    extract_obj( has_key( ch, obj->value[2] ) );
	    act( "$p eats the key!", ch, obj, NULL, TO_ALL );
	}

	REMOVE_BIT( obj->value[1], CONT_LOCKED );
	send_to_char( "*Click*\n\r", ch );
	act( "$n unlocks $p.", ch, obj, NULL, TO_ROOM );
	return;
    }

    act( "I see no $T here.", ch, NULL, argument, TO_CHAR );
    return;
}


void do_pick( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    CHAR_DATA *gch;
    char arg[MAX_INPUT_LENGTH];
    int door;
    EVENT *e;

    one_argument( argument, arg );

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

    /* look for guards */
    for( gch = ch->in_room->people; gch; gch = gch->next_in_room )
    {
	if( gch->deleted )
	    continue;
	if( IS_NPC( gch ) && IS_AWAKE( gch ) && ch->level + 5 < gch->level )
	{
	    act( "$N is standing too close to the lock.",
		ch, NULL, gch, TO_CHAR );
	    return;
	}

    }

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

    if( ( door = find_door( ch, arg ) ) >= 0 )
    {
	/* 'pick door' */
	EXIT_DATA *pexit;

	pexit = ch->in_room->exit[door];
	if( !IS_SET( pexit->exit_info, EX_CLOSED ) )
	{
	    send_to_char( "It's not closed.\n\r", ch );
	    return;
	}
	if( !IS_SET( pexit->exit_info, EX_LOCKED ) )
	{
	    send_to_char( "It's already unlocked.\n\r", ch );
	    return;
	}

	act( "$n begin$% to pick the $d.", ch, NULL, pexit->keyword, TO_ALL );
	if( IS_SET( pexit->exit_info, EX_TRAPPED ) )
	    create_char_event( ch, evn_picktrap,
				   number_range( 1, skill_table[gsn_pick_lock].beats ) );

	e = create_char_event( ch, evn_picklock,
			       skill_table[gsn_pick_lock].beats );
	e->data[0] = 0;		/* picking a door */
	e->data[1] = door;	/* which door to pick */
	return;
    }

    if( ( obj = get_obj_here( ch, arg ) ) )
    {
	/* 'pick object' */
	if( obj->item_type != ITEM_CONTAINER )
	{
	    send_to_char( "That's not a container.\n\r", ch );
	    return;
	}
	if( !IS_SET( obj->value[1], CONT_CLOSED ) )
	{
	    send_to_char( "It's not closed.\n\r", ch );
	    return;
	}
	if( !IS_SET( obj->value[1], CONT_LOCKED ) )
	{
	    send_to_char( "It's already unlocked.\n\r", ch );
	    return;
	}
	act( "$n begin$% to pick $p.", ch, obj, NULL, TO_ALL );
	if( IS_SET( obj->value[1], CONT_TRAPPED ) )
	    create_char_event( ch, evn_picktrap,
				   number_range( 1, skill_table[gsn_pick_lock].beats ) );

	e = create_char_event( ch, evn_picklock,
			       skill_table[gsn_pick_lock].beats );
	e->data[0] = 1;		/* picking a container */
	e->extra.type = TARGET_OBJ;
	e->extra.target.obj = obj;
	return;
    }

    act( "I see no $T here.", ch, NULL, arg, TO_CHAR );
    return;
}


void do_stand( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj = NULL;
    char buf[MAX_INPUT_LENGTH];

    if( argument[0] != '\0' )
    {
	if( ch->position == POS_FIGHTING )
	{
	    send_to_char( "Maybe you should finish fighting first?\n\r", ch );
	    return;
	}

	one_argument( argument, buf );
	if( !str_cmp( buf, "on" )
	    || !str_cmp( buf, "in" )
	    || !str_cmp( buf, "at" ) )
	    argument = one_argument( argument, buf );

	obj = get_obj_list( ch, argument, ch->in_room->contents );
	if( obj == NULL )
	{
	    send_to_char( "You don't see that here.\n\r", ch );
	    return;
	}
	if( obj->item_type != ITEM_FURNITURE
	    || ( !IS_SET( obj->value[0], FURN_STAND_AT )
		 && !IS_SET( obj->value[0], FURN_STAND_ON )
		 && !IS_SET( obj->value[0], FURN_STAND_IN ) ) )
	{
	    send_to_char( "You can't seem to find a place to stand.\n\r", ch );
	    return;
	}
	if( ch->on != obj && count_users( obj ) >= obj->value[1] )
	{
	    act( "There's no room to stand on $p.",
		 ch, obj, NULL, TO_CHAR );
	    return;
	}

	/* hack to ensure a message when a player is already standing */
	ch->position = POS_RESTING;
	ch->on = obj;
    }

    switch( ch->position )
    {
    case POS_GETTING_UP:
    case POS_SMASHED:
	ch->position = POS_STANDING;
	act( "&y$n manage$% to stand.", ch, NULL, NULL, TO_ALL );
	break;

    case POS_SLEEPING:
	if( IS_AFFECTED( ch, AFF_SLEEP ) )
	{
	    send_to_char( "You can't wake up!\n\r", ch );
	    return;
	}
	ch->position = POS_STANDING;
	if( obj == NULL)
	{
	    act( "&g$n wake$% and stand$% up.", ch, NULL, NULL, TO_ALL );
	    ch->on = NULL;
	}
	else if( IS_SET( obj->value[0], FURN_STAND_ON ) )
	    act( "$n wake$% and stand$% on $p.", ch, obj, NULL, TO_ALL );
	else if( IS_SET( obj->value[0], FURN_STAND_AT ) )
	   act("$n wake$% and stand$% at $p.", ch, obj, NULL, TO_ALL );
	else
	    act( "&g$n wake$% and stand$% in $p.", ch, obj, NULL, TO_ALL );
	break;

    case POS_RESTING:
    case POS_SITTING:
	ch->position = POS_STANDING;
	if( !obj )
	{
	    act( "&g$n stand$% up.&n", ch, NULL, NULL, TO_ALL );
	    ch->on = NULL;
	}
	else if( IS_SET( obj->value[0], FURN_STAND_ON ) )
	    act( "$n stand$% on $p.", ch, obj, NULL, TO_ALL );
	else if( IS_SET( obj->value[0], FURN_STAND_AT ) )
	   act("$n stand$% at $p.", ch, obj, NULL, TO_ALL );
	else
	    act( "&g$n stand$% in $p.", ch, obj, NULL, TO_ALL );
	break;

    case POS_FIGHTING:
	send_to_char( "You are already fighting!\n\r", ch );
	break;

    case POS_STANDING:
	send_to_char( "You are already standing.\n\r", ch );
	break;
    }
    return;
}


void do_sit( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    char buf[MAX_INPUT_LENGTH];
    const char *waksit;

    if( IS_AFFECTED( ch, AFF_DEAD ) )
    {
	send_to_char( "You can't rest until you die.\n\r", ch );
	return;
    }
    if( ch->position == POS_FIGHTING )
    {
	send_to_char( "Not while you're fighting!\n\r", ch );
	return;
    }

    /* okay, now that we know we can rest, find an object to rest on */
    if( argument[0] != '\0' )
    {
	one_argument( argument, buf );
	if( !str_cmp( buf, "on" )
	    || !str_cmp( buf, "in" )
	    || !str_cmp( buf, "at" ) )
	    argument = one_argument( argument, buf );

	obj = get_obj_list( ch, argument, ch->in_room->contents );
	if( obj == NULL )
	{
	    send_to_char( "You don't see that here.\n\r", ch );
	    return;
	}
    }
    else obj = ch->on;

    if( obj != NULL )
    {
	if( obj->item_type != ITEM_FURNITURE
	    || ( !IS_SET( obj->value[0], FURN_REST_ON )
		 && !IS_SET( obj->value[0], FURN_REST_IN )
		 && !IS_SET( obj->value[0], FURN_REST_AT ) ) )
	{
	    send_to_char( "You can't rest on that.\n\r", ch );
	    return;
	}

	if( obj != NULL && ch->on != obj && count_users( obj ) >= obj->value[1] )
	{
	    act( "There's no more room on $p.", ch, obj, NULL, TO_CHAR );
	    return;
	}

	ch->on = obj;
    }


    stop_juggling( ch );
    switch( ch->position )
    {
    case POS_SLEEPING:
    case POS_RESTING:
	if( IS_AFFECTED( ch, AFF_SLEEP ) )
	{
	    send_to_char( "You can't wake up!\n\r", ch );
	    return;
	}

	if( xIS_SET( ch->in_room->progtypes, REST_PROG )
	    && rprog_percent_check( ch->in_room, ch, NULL, NULL, REST_PROG ) == 0 )
	    return;
	if( ch->position == POS_SLEEPING )
	    waksit = "wake";
	else
	    waksit = "sit";

	ch->position = POS_SITTING;
	if( !obj )
	    act( "&g$n $T$% up and sit$%.&n", ch, NULL, waksit, TO_ALL );
	else if( IS_SET( obj->value[0], FURN_REST_ON ) )
	    act( "$n $T$% up and sit$% on $p.", ch, obj, waksit, TO_ALL );
	else if( IS_SET( obj->value[0], FURN_REST_AT ) )
	    act( "$n $T$% up and sit$% at $p.", ch, obj, waksit, TO_ALL );
	else
	    act( "$n $T$% up and sit$% in $p.", ch, obj, waksit, TO_ALL );
	break;

    case POS_SITTING:
	send_to_char( "You are already sitting.\n\r", ch );
	break;

    case POS_FIGHTING:
	send_to_char( "Not while you're fighting!\n\r", ch );
	break;

    case POS_STANDING:
	if( xIS_SET( ch->in_room->progtypes, REST_PROG )
	    && rprog_percent_check( ch->in_room, ch, NULL, NULL, REST_PROG ) == 0 )
	    return;

	if( !obj )
	    act( "&g$n sit$% down.", ch, NULL, NULL, TO_ALL );
	else if( IS_SET( obj->value[0], FURN_REST_ON ) )
	    act("$n sit$% on $p.", ch, obj, NULL, TO_ALL );
	else if( IS_SET( obj->value[0], FURN_REST_AT ) )
	    act("$n sit$% at $p.", ch, obj, NULL, TO_ALL );
	else if( IS_SET( obj->value[0], FURN_REST_IN ) )
	    act("$n sit$% in $p.", ch, obj, NULL, TO_ALL );
	ch->position = POS_SITTING;
	break;
    }
    return;
}


void do_rest( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    char buf[MAX_INPUT_LENGTH];

    if( IS_AFFECTED( ch, AFF_DEAD ) )
    {
	send_to_char( "You can't rest until you die.\n\r", ch );
	return;
    }
    if( ch->position == POS_FIGHTING )
    {
	send_to_char( "Not while you're fighting!\n\r", ch );
	return;
    }

    /* okay, now that we know we can rest, find an object to rest on */
    if( argument[0] != '\0' )
    {
	one_argument( argument, buf );
	if( !str_cmp( buf, "on" )
	    || !str_cmp( buf, "in" )
	    || !str_cmp( buf, "at" ) )
	    argument = one_argument( argument, buf );

	obj = get_obj_list( ch, argument, ch->in_room->contents );
	if( obj == NULL )
	{
	    send_to_char( "You don't see that here.\n\r", ch );
	    return;
	}
    }
    else obj = ch->on;

    if( obj != NULL )
    {
	if( obj->item_type != ITEM_FURNITURE
	    || ( !IS_SET( obj->value[0], FURN_REST_ON )
		 && !IS_SET( obj->value[0], FURN_REST_IN )
		 && !IS_SET( obj->value[0], FURN_REST_AT ) ) )
	{
	    send_to_char( "You can't rest on that.\n\r", ch );
	    return;
	}

	if( obj != NULL && ch->on != obj && count_users( obj ) >= obj->value[1] )
	{
	    act( "There's no more room on $p.", ch, obj, NULL, TO_CHAR );
	    return;
	}

	ch->on = obj;
    }


    stop_juggling( ch );
    switch( ch->position )
    {
    case POS_SLEEPING:
	if( IS_AFFECTED( ch, AFF_SLEEP ) )
	{
	    send_to_char( "You can't wake up!\n\r", ch );
	    return;
	}

	if( xIS_SET( ch->in_room->progtypes, REST_PROG )
	    && rprog_percent_check( ch->in_room, ch, NULL, NULL, REST_PROG ) == 0 )
	    return;

	ch->position = POS_RESTING;
	if( !obj )
	    act( "&g$n wake$% up and rest$%.&n", ch, NULL, NULL, TO_ALL );
	else if( IS_SET( obj->value[0], FURN_REST_ON ) )
	    act("$n wake$% up and rest$% on $p.", ch, obj, NULL, TO_ALL );
	else if( IS_SET( obj->value[0], FURN_REST_AT ) )
	    act("$n wake$% up and rest$% at $p.", ch, obj, NULL, TO_ALL );
	else
	    act("$n wake$% up and rest$% in $p.", ch, obj, NULL, TO_ALL );
	break;

    case POS_RESTING:
	send_to_char( "You are already resting.\n\r", ch );
	break;

    case POS_FIGHTING:
	send_to_char( "Not while you're fighting!\n\r", ch );
	break;

    case POS_SITTING:
	if( xIS_SET( ch->in_room->progtypes, REST_PROG )
	    && rprog_percent_check( ch->in_room, ch, NULL, NULL, REST_PROG ) == 0 )
	    return;

	if( !obj )
	    act( "&g$n rest$%.", ch, NULL, NULL, TO_ALL );
	else if( IS_SET( obj->value[0], FURN_REST_ON ) )
	    act("$n recline$% back on $p.", ch, obj, NULL, TO_ALL );
	else if( IS_SET( obj->value[0], FURN_REST_AT ) )
	    act("$n recline$% back at $p.", ch, obj, NULL, TO_ALL );
	else if( IS_SET( obj->value[0], FURN_REST_IN ) )
	    act("$n reclines back in $p.", ch, obj, NULL, TO_ALL );
	ch->position = POS_RESTING;
	break;

    case POS_STANDING:
	if( xIS_SET( ch->in_room->progtypes, REST_PROG )
	    && rprog_percent_check( ch->in_room, ch, NULL, NULL, REST_PROG ) == 0 )
	    return;

	if( !obj )
	    act( "&g$n sit$% down and rest$%.&n", ch, NULL, NULL, TO_ALL );
	else if( IS_SET( obj->value[0], FURN_REST_ON ) )
	    act("$n sit$% on $p and rest$%.", ch, obj, NULL, TO_ALL );
	else if( IS_SET( obj->value[0], FURN_REST_AT ) )
	    act("$n sit$% at $p and rest$%.", ch, obj, NULL, TO_ALL );
	else if( IS_SET( obj->value[0], FURN_REST_IN ) )
	    act("$n sit$% in $p and rest$%.", ch, obj, NULL, TO_ALL );
	ch->position = POS_RESTING;
	break;
    }
    return;
}


void do_sleep( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    char buf[MAX_INPUT_LENGTH];

    if( IS_AFFECTED( ch, AFF_DEAD ) )
    {
	send_to_char( "You can't rest until you die.\n\r", ch );
	return;
    }

    stop_juggling( ch );
    switch( ch->position )
    {
    case POS_SLEEPING:
	send_to_char( "You are already sleeping.\n\r", ch );
	break;

    case POS_GETTING_UP:
    case POS_SMASHED:
	if( ch->fighting )
	{
	    send_to_char( "That could be a bad idea right now.\n\r", ch );
	    return;
	}
    case POS_SITTING:
    case POS_RESTING:
    case POS_STANDING:
	if( argument[0] == '\0' && ch->on == NULL )
	{
	    act( "&g$n fall$% fast sleep$%.&n", ch, NULL, NULL, TO_ALL );
	    ch->position = POS_SLEEPING;
	    break;
	}
	if( argument[0] == '\0' )
	    obj = ch->on;
	else
	{
	    one_argument( argument, buf );
	    if( !str_cmp( buf, "on" )
		|| !str_cmp( buf, "in" )
		|| !str_cmp( buf, "at" ) )
		argument = one_argument( argument, buf );

	    obj = get_obj_list( ch, argument,  ch->in_room->contents );
	}

	if( !obj || obj->item_type != ITEM_FURNITURE
	    || ( !IS_SET( obj->value[0], FURN_SLEEP_ON )
		 && !IS_SET( obj->value[0], FURN_SLEEP_IN )
		 && !IS_SET( obj->value[0], FURN_SLEEP_AT ) ) )
	{
	    send_to_char( "You can't sleep on that!\n\r", ch );
	    return;
	}

	if( obj != NULL && ch->on != obj && count_users( obj ) >= obj->value[1] )
	{
	    act( "There's no more room on $p.", ch, obj, NULL, TO_CHAR );
	    return;
	}

	if( xIS_SET( ch->in_room->progtypes, SLEEP_PROG )
	    && rprog_percent_check( ch->in_room, ch, NULL, NULL, SLEEP_PROG ) == 0 )
	    return;

	ch->on = obj;
	if( IS_SET( obj->value[0], FURN_SLEEP_IN ) )
	{
	    act( "You go to sleep in $p.", ch, obj, NULL, TO_CHAR );
	    act("$n goes to sleep in $p.", ch, obj, NULL, TO_ROOM );
	}
	else if( IS_SET( obj->value[0], FURN_SLEEP_ON ) )
	{
	    act( "You go to sleep on $p.", ch, obj, NULL, TO_CHAR );
	    act("$n goes to sleep on $p.", ch, obj, NULL, TO_ROOM );
	}
	else
	{
	    act( "You go to sleep at $p.", ch, obj, NULL, TO_CHAR );
	    act("$n goes to sleep at $p.", ch, obj, NULL, TO_ROOM );
	}
	ch->position = POS_SLEEPING;
	break;

    case POS_FIGHTING:
	send_to_char( "&rNot while you're fighting!&n\n\r", ch );
	break;
    }

    return;
}


void do_wake( CHAR_DATA *ch, const char *argument )
{
    CHAR_DATA *victim;
    char arg[MAX_INPUT_LENGTH];

    one_argument( argument, arg );
    if( arg[0] == '\0' )
    {
	do_stand( ch, argument );
	return;
    }
    if( !IS_AWAKE( ch ) )
    {
	send_to_char( "You are asleep yourself!\n\r", ch );
	return;
    }
    if( !( victim = get_char_room( ch, arg ) ) )
    {
	send_to_char( "They aren't here.\n\r", ch );
	return;
    }
    if( IS_AWAKE( victim ) )
    {
	act( "$N is already awake.", ch, NULL, victim, TO_CHAR );
	return;
    }
    if( IS_AFFECTED( victim, AFF_SLEEP ) )
    {
	act( "&rYou can't wake $M!&n", ch, NULL, victim, TO_CHAR );
	return;
    }
    if( victim->wait > 0 && ( victim->position == POS_SMASHED
			      || victim->position == POS_GETTING_UP ) )
    {
	send_to_char( "They are awake but too heavy to help up.\n\r", ch );
	return;
    }

    act( "&gYou wake $M.&n", ch, NULL, victim, TO_CHAR );
    act( "&g$n wakes you.&n", ch, NULL, victim, TO_VICT );
    do_stand( victim, "" );
    return;
}


void do_sneak( CHAR_DATA *ch, const char *argument )
{
    AFFECT_DATA af;

    /* Don't allow charmed mobs to do this, check player's skill */
    if( ( IS_NPC( ch ) && IS_AFFECTED( ch, AFF_CHARM ) )
	|| ( !IS_NPC( ch ) && !can_use( ch, gsn_sneak ) ) )
    {
	bad_command( ch );
	return;
    }

    send_to_char( "You attempt to move silently.\n\r", ch );
    affect_strip( ch, gsn_sneak );

    if( IS_NPC( ch ) || number_percent( ) < ch->pcdata->learned[gsn_sneak] )
    {
	af.type = gsn_sneak;
	af.level = ch->level;
	af.duration = ch->level;
	af.location = APPLY_NONE;
	af.modifier = 0;
	vset( af.bitvector, AFF_SNEAK );
	affect_to_char( ch, &af, NULL );
    }

    return;
}


void do_hide( CHAR_DATA *ch, const char *argument )
{
    /* Dont allow charmed mobiles to do this, check player's skill */
    if( ( IS_NPC( ch ) && IS_AFFECTED( ch, AFF_CHARM ) )
	|| ( !IS_NPC( ch ) && !can_use( ch, gsn_hide ) ) )
    {
	bad_command( ch );
	return;
    }

    send_to_char( "You attempt to hide.\n\r", ch );

    if( IS_AFFECTED( ch, AFF_HIDE ) )
	xREMOVE_BIT( ch->affected_by, AFF_HIDE );

    if( IS_NPC( ch ) || number_percent( ) < ch->pcdata->learned[gsn_hide] )
	xSET_BIT( ch->affected_by, AFF_HIDE );

    return;
}


void do_move_hidden( CHAR_DATA *ch, const char *argument )
{
    AFFECT_DATA af;

    /* Dont allow charmed mobiles to do this, check player's skill */
    if( ( IS_NPC( ch ) && IS_AFFECTED( ch, AFF_CHARM ) )
	|| ( !IS_NPC( ch ) && !can_use( ch, gsn_move_hidden ) ) )
    {
	bad_command( ch );
	return;
    }

    if( can_use( ch, gsn_sneak ) )
	do_sneak( ch, "" );

    if( IS_AFFECTED( ch, AFF_HIDE ) )
	xREMOVE_BIT( ch->affected_by, AFF_HIDE );
    affect_strip( ch, gsn_move_hidden );

    send_to_char( "You attempt to move in the shadows.\n\r", ch );

    af.type = gsn_move_hidden;
    af.level = 0;
    af.duration = 2 * ch->level / 3;
    af.location = APPLY_NONE;
    af.modifier = 0;
    vset( af.bitvector, AFF_HIDE );

    if( IS_NPC( ch )
	|| number_percent() < ch->pcdata->learned[gsn_move_hidden] )
	affect_to_char( ch, &af, ch );

    return;
}


/*
 * Contributed by Alander.
 */
void do_visible( CHAR_DATA *ch, const char *argument )
{
    AFFECT_DATA *af;

    for( af = ch->affected; af; af = af->next )
    {
	if( af->deleted || af->duration < 0 )
	    continue;

	if( af->type == gsn_invis || af->type == gsn_mass_invis
	    || af->type == gsn_vanish || af->type == gsn_sneak
	    || af->type == gsn_move_hidden )
	    affect_remove( ch, af );
    }
    if( !IS_NPC( ch ) )
    {
	ROOM_INDEX_DATA *room = ch->in_room;
	char_from_room( ch );
	xREMOVE_BIT( ch->act, PLR_WIZINVIS );
	char_to_room( ch, room );
    }
    else
	xREMOVE_BIT( ch->act, ACT_BURIED );
    send_to_char( "Ok.\n\r", ch );
    act( "$n becomes visible.", ch, NULL, NULL, TO_ROOM );
    return;
}


void do_recall( CHAR_DATA *ch, const char *argument )
{
    ROOM_INDEX_DATA *location;
    char buf[MAX_STRING_LENGTH];
    int place;

    if( !str_cmp( argument, "reset" ) )
    {
	ch->recall_room = -1;
	send_to_char( "&gRecall is set to default location.&n\n\r", ch );
	return;
    }

    if( IS_SET( ch->in_room->room_flags, ROOM_NO_RECALL )
	|| is_affected( ch, gsn_hex ) )
    {
	send_to_char( "&rYou stand on cursed ground, you can't recall.&n\n\r",
		      ch );
	return;
    }

    if( !str_cmp( argument, "set" ) )
    {
	send_to_char( "&gRecall set to this room.&n\n\r", ch );
	ch->recall_room = ch->in_room->vnum;
	return;
    }

    act( "&g$n recalls.&n", ch, NULL, NULL, TO_ROOM );
    if( !str_cmp( argument, "|clan|" ) )
    {
	if( !is_clan( ch ) )	/* sanity check */
	{
	    send_to_char( "You don't belong to any clan, guild or order!\n\r",
			  ch );
	    return;
	}
	place = ch->pcdata->clan->recall;
    }
    else if( ch->recall_room > 0 )
	place = ch->recall_room;
    else
	place = ch->in_room->area->recall;
    if( !( location = get_room_index( place ) )
	|| location->area->plane != ch->in_room->area->plane )
    {
	send_to_char( "You are completely lost.\n\r", ch );
	return;
    }

    if( ch->in_room == location )
	return;

    if( ch->fighting )
    {
	int lose;

	if( number_bits( 1 ) == 0 )
	{
	    WAIT_STATE( ch, PULSE_PER_SECOND );
	    lose = ( ch->desc ) ? 2500 : 5000;
	    gain_exp( ch, 0 - lose );
	    sprintf( buf, "&yYou failed!  &rYou lose &y%d&r exps.&n\n\r", lose / 100 );
	    send_to_char( buf, ch );
	    return;
	}

	lose = ( ch->desc ) ? 5000 : 10000;
	gain_exp( ch, 0 - lose );
	sprintf( buf, "&yYou recall from combat!  &rYou lose &y%d&r exps.&n\n\r", lose / 100 );
	send_to_char( buf, ch );
	stop_fighting( ch, TRUE );
    }

    ch->move /= 2;
    act( "&g$n disappears.&n", ch, NULL, NULL, TO_ROOM );
    char_from_room( ch );
    char_to_room( ch, location );
    act( "&g$n appears in the room.&n", ch, NULL, NULL, TO_ROOM );
    do_look( ch, AUTOLOOK );

    return;
}


void do_arena( CHAR_DATA *ch, const char *argument )
{
    ROOM_INDEX_DATA *arena;

    if( IS_SET( ch->in_room->room_flags, ROOM_NO_RECALL )
	|| is_affected( ch, gsn_hex ) )
    {
	send_to_char( "You are cursed, you cannot seek the arena.\n\r", ch );
	return;
    }

    arena = get_room_index( ROOM_VNUM_ARENA );
    act( "$n leaves for battle!", ch, NULL, NULL, TO_ROOM );
    char_from_room( ch );
    char_to_room( ch, arena );
    act( "$n arrives ready for a fight!", ch, NULL, NULL, TO_ROOM );
    do_look( ch, AUTOLOOK );
    return;
}


void do_train( CHAR_DATA *ch, const char *argument )
{
    CHAR_DATA *mob;
    const char *pOutput;
    int *pAbility;
    int cost;
    int bone_flag = 0;		/* Added for training of hp ma mv */

    if( IS_NPC( ch ) )
	return;

    /*
     * Check for trainer.
     */
    for( mob = ch->in_room->people; mob; mob = mob->next_in_room )
    {
	if( IS_NPC( mob ) && xIS_SET( mob->act, ACT_TRAIN ) )
	    break;
    }
    if( !mob || argument[0] == '\0' )
    {
	argument = "foo";
    }

    if( !str_prefix( "str", argument ) )
    {
	cost = ch->pcdata->perm_str - ( race_table[ch->race].str_mod + 15 );
	if( class_table[ch->class].attr_prime == APPLY_STR )
	    cost--;
	pAbility = &ch->pcdata->perm_str;
	pOutput = "strength";
    }
    else if( !str_prefix( "int", argument ) )
    {
	cost = ch->pcdata->perm_int - ( race_table[ch->race].int_mod + 15 );
	if( class_table[ch->class].attr_prime == APPLY_INT )
	    cost--;
	pAbility = &ch->pcdata->perm_int;
	pOutput = "intelligence";
    }
    else if( !str_prefix( "wis", argument ) )
    {
	cost = ch->pcdata->perm_wis - ( race_table[ch->race].wis_mod + 15 );
	if( class_table[ch->class].attr_prime == APPLY_WIS )
	    cost--;
	pAbility = &ch->pcdata->perm_wis;
	pOutput = "wisdom";
    }
    else if( !str_prefix( "dex", argument ) )
    {
	cost = ch->pcdata->perm_dex - ( race_table[ch->race].dex_mod + 15 );
	if( class_table[ch->class].attr_prime == APPLY_DEX )
	    cost--;
	pAbility = &ch->pcdata->perm_dex;
	pOutput = "dexterity";
    }
    else if( !str_prefix( "con", argument ) )
    {
	cost = ch->pcdata->perm_con - ( race_table[ch->race].con_mod + 15 );
	if( class_table[ch->class].attr_prime == APPLY_CON )
	    cost--;
	pAbility = &ch->pcdata->perm_con;
	pOutput = "constitution";
    }
    else if( !str_prefix( "magic ", argument ) )
    {
	cost = 250;
	argument += 6;
	if( !str_prefix( "air", argument ) )
	{
	    pAbility = &ch->pcdata->perm_magic[MAGIC_AIR];
	    pOutput = "air magic";
	    bone_flag = MAGIC_AIR;
	}
	else if( !str_prefix( "earth", argument ) )
	{
	    pAbility = &ch->pcdata->perm_magic[MAGIC_EARTH];
	    pOutput = "earth magic";
	    bone_flag = MAGIC_EARTH;
	}
	else if( !str_prefix( "fire", argument ) )
	{
	    pAbility = &ch->pcdata->perm_magic[MAGIC_FIRE];
	    pOutput = "fire magic";
	    bone_flag = MAGIC_FIRE;
	}
	else if( !str_prefix( "spirit", argument ) )
	{
	    pAbility = &ch->pcdata->perm_magic[MAGIC_SPIRIT];
	    pOutput = "spirit magic";
	    bone_flag = MAGIC_SPIRIT;
	}
	else if( !str_prefix( "water", argument ) )
	{
	    pAbility = &ch->pcdata->perm_magic[MAGIC_WATER];
	    pOutput = "water magic";
	    bone_flag = MAGIC_WATER;
	}
	else
	{
	    send_to_char( "Syntax: train magic <element>\n\r"
			  "Air, Earth, Fire, Spirit and Water.\n\r", ch );
	    return;
	}
	bone_flag++;
    }
    /*       ---------------- By Bonecrusher -------------------      */

    else if( !str_cmp( argument, "hp" ) )
    {
	cost = 15 + class_table[ch->class].fMana;
	bone_flag = -1;
	pAbility = &ch->max_hit;
	pOutput = "hit points";
    }
    else if( !str_prefix( "mana ", argument ) )
    {
	cost = 25 - class_table[ch->class].fMana;
	bone_flag = -2;
	argument += 5;
	if( !str_prefix( "air", argument ) )
	{
	    pAbility = &ch->max_mana[MAGIC_AIR];
	    pOutput = "air mana";
	}
	else if( !str_prefix( "earth", argument ) )
	{
	    pAbility = &ch->max_mana[MAGIC_EARTH];
	    pOutput = "earth mana";
	}
	else if( !str_prefix( "fire", argument ) )
	{
	    pAbility = &ch->max_mana[MAGIC_FIRE];
	    pOutput = "fire mana";
	}
	else if( !str_prefix( "spirit", argument ) )
	{
	    pAbility = &ch->max_mana[MAGIC_SPIRIT];
	    pOutput = "spirit mana";
	}
	else if( !str_prefix( "water", argument ) )
	{
	    pAbility = &ch->max_mana[MAGIC_WATER];
	    pOutput = "water mana";
	}
	else
	{
	    send_to_char( "Syntax: train mana <element>\n\r"
			  "Air, Earth, Fire, Spirit and Water.\n\r", ch );
	    return;
	}
    }
    else if( !str_cmp( argument, "move" ) )
    {
	cost = 10;
	bone_flag = -1;
	pAbility = &ch->max_move;
	pOutput = "move points";
    }

    /*       --------------------------------------------     */

    else
    {
	if( mob )
	    act( "&c$N looks you over thoughtfully, judging your worth.",
		 ch, NULL, mob, TO_CHAR );
	charprintf( ch, "&gYou have &c%d&g practice sessions.\n\r", ch->practice );
	send_to_char( "&bStat ( current )   Cost to train\n\r", ch );
	cost = ch->pcdata->perm_str - 15 - race_table[ch->race].str_mod;
	charprintf( ch, "&bStrength    ( &c%3d&b )  &r%d\n\r", ch->pcdata->perm_str,
		( class_table[ch->class].attr_prime == APPLY_STR )
		? power( 300, 50, cost - 1 )
		: power( 300, 50, cost ) );
	cost = ch->pcdata->perm_int - 15 - race_table[ch->race].int_mod;
	charprintf( ch, "&bIntelligence( &c%3d&b )  &r%d\n\r", ch->pcdata->perm_int,
		( class_table[ch->class].attr_prime == APPLY_INT )
		? power( 300, 50, cost - 1 )
		: power( 300, 50, cost ) );
	cost = ch->pcdata->perm_wis - 15 - race_table[ch->race].wis_mod;
	charprintf( ch, "&bWisdom      ( &c%3d&b )  &r%d\n\r", ch->pcdata->perm_wis,
		( class_table[ch->class].attr_prime == APPLY_WIS )
		? power( 300, 50, cost - 1 )
		: power( 300, 50, cost ) );
	cost = ch->pcdata->perm_dex - 15 - race_table[ch->race].dex_mod;
	charprintf( ch, "&bDexterity   ( &c%3d&b )  &r%d\n\r", ch->pcdata->perm_dex,
		( class_table[ch->class].attr_prime == APPLY_DEX )
		? power( 300, 50, cost - 1 )
		: power( 300, 50, cost ) );
	cost = ch->pcdata->perm_con - 15 - race_table[ch->race].con_mod;
	charprintf( ch, "&bConstitution( &c%3d&b )  &r%d\n\r", ch->pcdata->perm_con,
		( class_table[ch->class].attr_prime == APPLY_CON )
		? power( 300, 50, cost - 1 )
		: power( 300, 50, cost ) );
	send_to_char( "&gMagic:\n\r", ch );
	charprintf( ch, "  &cAir       &b( &c%3d&b )  &r250\n\r",
		    ch->pcdata->perm_magic[MAGIC_AIR] );
	charprintf( ch, "  &yEarth     &b( &c%3d&b )  &r250\n\r",
		    ch->pcdata->perm_magic[MAGIC_EARTH] );
	charprintf( ch, "  &rFire      &b( &c%3d&b )  &r250\n\r",
		    ch->pcdata->perm_magic[MAGIC_FIRE] );
	charprintf( ch, "  &wSpirit    &b( &c%3d&b )  &r250\n\r",
		    ch->pcdata->perm_magic[MAGIC_SPIRIT] );
	charprintf( ch, "  &bWater     &b( &c%3d&b )  &r250\n\r",
		    ch->pcdata->perm_magic[MAGIC_WATER] );
	charprintf( ch, "&yhp: &r%2d&y	   mana: &r%2d&y   move: &r10&n\n\r",
		15 + class_table[ch->class].fMana,
		25 - class_table[ch->class].fMana );
	return;
    }

    if( bone_flag == 0 )
	cost = power( 300, 50, cost );
    if( cost > ch->practice )
    {
	send_to_char( "&rYou don't have enough practices.&n\n\r", ch );
	return;
    }

    ch->practice -= cost;

    if( bone_flag == 0 )		/* standard stat train */
	*pAbility += 1;
    else if( bone_flag == -1 )		/* train hps or moves */
	*pAbility += number_fuzzy( 2 );
    else if( bone_flag == -2 )		/* train mana */
	*pAbility += dice( 2, 6 ) + 8;
    else				/* train magic */
    {
	bone_flag--;
	*pAbility += 1;
	ch->max_mana[bone_flag] += dice( 2, 6 ) + 18;
    }

    if( bone_flag >= 0 )
    {
	act( "&gYour &y$T&g increases!&n", ch, NULL, pOutput, TO_CHAR );
	act( "&g$n's &y$T&g increases!&n", ch, NULL, pOutput, TO_ROOM );
    }
    else
    {
	act( "&gYour &y$T&g increase!&n", ch, NULL, pOutput, TO_CHAR );
	act( "&g$n's &y$T&g increase!&n", ch, NULL, pOutput, TO_ROOM );
    }

    return;
}


void do_chameleon( CHAR_DATA *ch, const char *argument )
{
    if( !IS_NPC( ch ) && !can_use( ch, gsn_chameleon ) )
    {
	bad_command( ch );
	return;
    }

    send_to_char( "You attempt to blend in with your surroundings.\n\r", ch );

    if( IS_AFFECTED( ch, AFF_HIDE ) )
	xREMOVE_BIT( ch->affected_by, AFF_HIDE );

    if( IS_NPC( ch ) || get_success( ch, gsn_chameleon, 100 ) )
	xSET_BIT( ch->affected_by, AFF_HIDE );

    return;
}


void do_heighten( CHAR_DATA *ch, const char *argument )
{
    AFFECT_DATA af;

    if( !IS_NPC( ch ) && !can_use( ch, gsn_heighten ) )
    {
	bad_command( ch );
	return;
    }

    if( is_affected( ch, gsn_heighten ) )
    {
	send_to_char( "Your senses are allready heightened.\n\r", ch );
	return;
    }

    if( IS_NPC( ch ) || get_success( ch, gsn_heighten, 100 ) )
    {
	af.type = gsn_heighten;
	af.level = 0;
	af.duration = 24;
	af.modifier = 0;
	af.location = APPLY_NONE;
	vset( af.bitvector, AFF_DETECT_INVIS );
	xSET_BIT( af.bitvector, AFF_DETECT_HIDDEN );
	xSET_BIT( af.bitvector, AFF_INFRARED );
	affect_to_char( ch, &af, NULL );

	send_to_char( "Your senses are heightened.\n\r", ch );
    }
    else
	send_to_char( "You fail to heighten your senses.\n\r", ch );

    return;
}


/*
 * Bash code by Thelonius for EnvyMud ( originally bash_door )
 * Damage modified using Morpheus's code
 * Message for bashproof doors by that wacky guy Kahn
 */
void do_bash( CHAR_DATA *ch, const char *argument )
{
    CHAR_DATA *gch;
    char arg[MAX_INPUT_LENGTH];
    int door;

    if( IS_NPC( ch ) || ( !IS_NPC( ch ) && !can_use( ch, gsn_bash ) ) )
    {
	send_to_char( "You're not enough of a warrior to bash doors!\n\r",
		     ch );
	return;
    }

    one_argument( argument, arg );

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

    if( ch->fighting )
    {
	send_to_char( "You can't break off your fight.\n\r", ch );
	return;
    }

    if( ( door = find_door( ch, arg ) ) >= 0 )
    {
	ROOM_INDEX_DATA *to_room;
	EXIT_DATA *pexit;
	EXIT_DATA *pexit_rev;
	int chance;

	pexit = ch->in_room->exit[door];
	if( !IS_SET( pexit->exit_info, EX_CLOSED ) )
	{
	    send_to_char( "Calm down.  It is already open.\n\r", ch );
	    return;
	}

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

	if( !IS_NPC( ch ) )
	    chance = get_success( ch, gsn_bash, 200 ) / 2;
	else
	    chance = 0;

	if( IS_SET( pexit->exit_info, EX_LOCKED ) )
	    chance /= 2;

	if( IS_SET( pexit->exit_info, EX_BASHPROOF ) )
	{
	    act( "WHAAAAM!!!  You bash against the $d, but it doesn't budge.",
		ch, NULL, pexit->keyword, TO_CHAR );
	    act( "WHAAAAM!!!  $n bashes against the $d, but it holds strong.",
		ch, NULL, pexit->keyword, TO_ROOM );
	    damage( ch, ch, ( ch->max_hit / 5 ), gsn_bash, WEAR_NONE );
	    return;
	}

	if( ( get_curr_str( ch ) >= 20 )
	    && number_percent( ) <
	    ( chance + 4 * ( get_curr_str( ch ) - 20 ) ) )
	{
	    /* Success */
	    REMOVE_BIT( pexit->exit_info, EX_CLOSED );
	    if( IS_SET( pexit->exit_info, EX_LOCKED ) )
		REMOVE_BIT( pexit->exit_info, EX_LOCKED );

	    SET_BIT( pexit->exit_info, EX_BASHED );

	    act( "Crash!  You bashed open the $d!",
		ch, NULL, pexit->keyword, TO_CHAR );
	    act( "$n bashes open the $d!",
		ch, NULL, pexit->keyword, TO_ROOM );

	    damage( ch, ch, ( ch->max_hit / 20 ), gsn_bash, WEAR_NONE );

	    /* Bash through the other side */
	    if( ( to_room = pexit->to_room )
		&& ( pexit_rev = to_room->exit[rev_dir[door]] )
		&& pexit_rev->to_room == ch->in_room )
	    {
		CHAR_DATA *rch;

		REMOVE_BIT( pexit_rev->exit_info, EX_CLOSED );
		if( IS_SET( pexit_rev->exit_info, EX_LOCKED ) )
		    REMOVE_BIT( pexit_rev->exit_info, EX_LOCKED );

		SET_BIT( pexit_rev->exit_info, EX_BASHED );

		for( rch = to_room->people; rch; rch = rch->next_in_room )
		{
		    if( rch->deleted )
			continue;
		    act( "The $d crashes open!",
			rch, NULL, pexit_rev->keyword, TO_CHAR );
		}
	    }
	}
	else
	{
	    /* Failure */
	    act( "OW!  You bash against the $d, but it doesn't budge.",
		ch, NULL, pexit->keyword, TO_CHAR );
	    act( "$n bashes against the $d, but it holds strong.",
		ch, NULL, pexit->keyword, TO_ROOM );
	    damage( ch, ch, ( ch->max_hit / 10 ), gsn_bash, WEAR_NONE );
	}
    }

    /*
     * Check for "guards"... anyone bashing a door is considered as
     * a potential aggressor, and there's a 25% chance that mobs
     * will do unto before being done unto.
     * But first...let's make sure ch ain't dead?  That'd be a pain.
     */

    if( ch->deleted || ch->hit <= 1 )
	return;

    for( gch = ch->in_room->people; gch; gch = gch->next_in_room )
    {
	if( !gch->deleted && gch != ch
	    && IS_AWAKE( gch ) && !gch->fighting
	    && can_see( gch, ch )
	    && IS_NPC( gch ) && !IS_AFFECTED( gch, AFF_CHARM )
	    && ( ch->level - gch->level <= 4 )
	    && number_bits( 2 ) == 0 )
	    multi_hit( gch, ch, TYPE_UNDEFINED );
    }

    return;
}


/*
 * Snare skill by Binky for EnvyMud
 */
void do_snare( CHAR_DATA *ch, const char *argument )
{
    CHAR_DATA *victim;
    AFFECT_DATA af;
    char arg[MAX_INPUT_LENGTH];

    one_argument( argument, arg );

    /*
     *	First, this checks for case of no second argument (valid only
     *	while fighting already).  Later, if an argument is given, it
     *	checks validity of argument.  Unsuccessful snares flow through
     *	and receive messages at the end of the function.
     */
    if( arg[0] == '\0' )
    {
	if( !( victim = ch->fighting ) )
	{
	    send_to_char( "Ensnare whom?\n\r", ch );
	    return;
	}

	/* No argument, but already fighting: valid use of snare */
	WAIT_STATE( ch, skill_table[gsn_snare].beats );

	/* Only appropriately skilled PCs and uncharmed mobs */
	if( ( IS_NPC( ch ) && !IS_AFFECTED( ch, AFF_CHARM ) )
	    || ( !IS_NPC( ch )
		&& number_percent( ) < ch->pcdata->learned[gsn_snare] ) )
	{
	    affect_strip( victim, gsn_snare );

	    af.type = gsn_snare;
	    af.level = 0;
	    af.duration = 1 + ( ( ch->level ) / 8 );
	    af.location = APPLY_NONE;
	    af.modifier = 0;
	    vset( af.bitvector, AFF_HOLD );

	    affect_to_char( victim, &af, NULL );

	    act( "You have ensnared $M!", ch, NULL, victim, TO_CHAR );
	    act( "$n has ensnared you!", ch, NULL, victim, TO_VICT );
	    act( "$n has ensnared $N.", ch, NULL, victim, TO_NOTVICT );
	}
	else
	{
	    act( "You failed to ensnare $M.  Uh oh!",
		ch, NULL, victim, TO_CHAR );
	    act( "$n tried to ensnare you!  Get $m!",
		ch, NULL, victim, TO_VICT );
	    act( "$n attempted to ensnare $N, but failed!",
		ch, NULL, victim, TO_NOTVICT );
	}
    }
    else	/* argument supplied */
    {
	if( !( victim = get_char_room( ch, arg ) ) )
	{
	    send_to_char( "They aren't here.\n\r", ch );
	    return;
	}

	if( !IS_NPC( ch ) && !IS_NPC( victim ) )
	{
	    send_to_char( "You can't ensnare another player.\n\r", ch );
	    return;
	}

	if( victim != ch->fighting )	/* TRUE if not fighting, or fighting  */
	{			/* if person other than victim	      */
	    if( ch->fighting )	/* TRUE if fighting other than vict.  */
	    {
		send_to_char(
		    "Take care of the person you are fighting first!\n\r",
		    ch );
		return;
	    }

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

	    /* here, arg supplied, ch not fighting		 */
	    /* only appropriately skilled PCs and uncharmed mobs */
	    if( ( IS_NPC( ch ) && !IS_AFFECTED( ch, AFF_CHARM ) )
		|| ( !IS_NPC( ch ) && get_success( ch, gsn_snare, 100 ) ) )
	    {
		affect_strip( victim, gsn_snare );

		af.type = gsn_snare;
		af.level = 0;
		af.duration = 3 + ( ( ch->level ) / 8 );
		af.location = APPLY_NONE;
		af.modifier = 0;
		vset( af.bitvector, AFF_HOLD );

		affect_to_char( victim, &af, NULL );

		act( "You have ensnared $M!", ch, NULL, victim, TO_CHAR );
		act( "$n has ensnared you!", ch, NULL, victim, TO_VICT );
		act( "$n has ensnared $N.", ch, NULL, victim, TO_NOTVICT );
	    }
	    else
	    {
		act( "You failed to ensnare $M.	 Uh oh!",
		    ch, NULL, victim, TO_CHAR );
		act( "$n tried to ensnare you!	Get $m!",
		    ch, NULL, victim, TO_VICT );
		act( "$n attempted to ensnare $N, but failed!",
		    ch, NULL, victim, TO_NOTVICT );
	    }

	    if( IS_NPC( ch ) && IS_AFFECTED( ch, AFF_CHARM ) )
	    {
		/* go for the one who wanted to fight : ) */
		multi_hit( victim, ch->master, TYPE_UNDEFINED );
	    }
	    else
	    {
		multi_hit( victim, ch, TYPE_UNDEFINED );
	    }
	}
	else
	{
	    /* we are already fighting the intended victim */
	    WAIT_STATE( ch, skill_table[gsn_snare].beats );

	    /* charmed mobs not allowed to do this */
	    if( ( IS_NPC( ch ) && !IS_AFFECTED( ch, AFF_CHARM ) )
		|| ( !IS_NPC( ch ) && get_success( ch, gsn_snare, 100 ) ) )
	    {
		affect_strip( victim, gsn_snare );

		af.type = gsn_snare;
		af.level = 0;
		af.duration = 1 + ( ( ch->level ) / 8 );
		af.location = APPLY_NONE;
		af.modifier = 0;
		vset( af.bitvector, AFF_HOLD );

		affect_to_char( victim, &af, NULL );

		act( "You have ensnared $M!", ch, NULL, victim, TO_CHAR );
		act( "$n has ensnared you!", ch, NULL, victim, TO_VICT );
		act( "$n has ensnared $N.", ch, NULL, victim, TO_NOTVICT );
	    }
	    else
	    {
		act( "You failed to ensnare $M.	 Uh oh!",
		    ch, NULL, victim, TO_CHAR );
		act( "$n tried to ensnare you!	Get $m!",
		    ch, NULL, victim, TO_VICT );
		act( "$n attempted to ensnare $N, but failed!",
		    ch, NULL, victim, TO_NOTVICT );
	    }
	}
    }

    return;
}


/*
 * Untangle by Thelonius for EnvyMud
 */
void do_untangle( CHAR_DATA *ch, const char *argument )
{
    CHAR_DATA *victim;
    char arg[MAX_INPUT_LENGTH];

    if( !IS_NPC( ch ) && !can_use( ch, gsn_untangle ) )
    {
	send_to_char( "You aren't nimble enough.\n\r", ch );
	return;
    }

    one_argument( argument, arg );

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

    if( !IS_AFFECTED( victim, AFF_HOLD ) )
    {
	send_to_char( "They don't seem to be stuck at all.\n\r", ch );
	return;
    }

    if( ( IS_NPC( ch ) && !IS_AFFECTED( ch, AFF_CHARM ) )
	|| ( !IS_NPC( ch ) && get_success( ch, gsn_untangle, 100 ) ) )
    {
	affect_strip( victim, gsn_snare );
	affect_strip( victim, gsn_strangle );
	affect_strip( victim, gsn_web );
	xREMOVE_BIT( victim->affected_by, AFF_HOLD );

	if( victim != ch )
	    act( "$n untangle$% $n.", ch, NULL, victim, TO_ALL );
	else
	    act( "$n untangle$% $mself.", ch, NULL, NULL, TO_ALL );
    }
    else
    {
	send_to_char( "You fail to free them from the tangle.\n\r", ch );
    }
}


/*
 *  Menu for all game functions.
 *  Thelonius (Monk)	5/94
 *
 * Removed for Daleken, there are improved versions available,
 * we should use those instead.
 */


void do_setmin( CHAR_DATA *ch, const char *argument )
{
    char buf[MAX_INPUT_LENGTH];

    if( IS_NPC( ch ) )
	return;

    if( !str_cmp( argument, "clear" )
	|| !str_cmp( argument, "reset" )
	|| !str_cmp( argument, "none" ) )
    {
	free_string( ch->pcdata->setmin );
	ch->pcdata->setmin = &str_empty[0];
    }
    else if( argument[0] != '\0' )
    {
	strcpy( buf, argument );
	free_string( ch->pcdata->setmin );
	ch->pcdata->setmin = str_dup( buf );
    }

    send_to_char( "Your setmin is now set as:\n\r", ch );
    sprintf( buf, " %s %s <direction>.\n\r",
	    ch->name, ch->pcdata->setmin );
    send_to_char( buf, ch );
    return;
}


void do_setmout( CHAR_DATA *ch, const char *argument )
{
    char buf[MAX_INPUT_LENGTH];

    if( IS_NPC( ch ) )
	return;

    if( !str_cmp( argument, "clear" )
	|| !str_cmp( argument, "reset" )
	|| !str_cmp( argument, "none" ) )
    {
	free_string( ch->pcdata->setmout );
	ch->pcdata->setmout = &str_empty[0];
    }
    else if( argument[0] != '\0' )
    {
	strcpy( buf, argument );
	free_string( ch->pcdata->setmout );
	ch->pcdata->setmout = str_dup( buf );
    }

    send_to_char( "Your setmout is now set as:\n\r", ch );
    sprintf( buf, "   %s %s <direction>.\n\r",
	    ch->name, ch->pcdata->setmout );
    send_to_char( buf, ch );
    return;
}


void do_enter( CHAR_DATA *ch, const char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    OBJ_DATA *obj;
    ROOM_INDEX_DATA *room_to;
    int num_found = 0;
    int which;

    which = number_argument( argument, arg );
    for( obj = ch->in_room->contents; obj && num_found < which; obj = obj->next_content )
    {
	if( obj->item_type == ITEM_PORTAL && is_obj_name( obj, arg ) )
	{
	    if( ++num_found == which )
		break;
	}
    }

    if( !obj )
    {
	for( obj = ch->carrying; obj && num_found < which; obj = obj->next_content )
	{
	    if( obj->item_type == ITEM_PORTAL && is_obj_name( obj, arg ) )
	    {
		if( ++num_found == which )
		    break;
	    }
	}
    }

    if( num_found != which )
    {
	send_to_char( "Sorry that isn't here.\n\r", ch );
	return;
    }

    /* enter the portal */
    room_to = get_room_index( obj->value[0] );
    if( !room_to )
    {
	send_to_char( "Sorry that isn't a real portal.\n\r", ch );
	return;
    }

    if( room_is_private( room_to ) )
    {
	send_to_char( "That room is private at the moment.\n\r", ch );
	return;
    }

    if( ( room_to->area->plane->min_level > get_trust( ch )
	  || ( !IS_NPC( ch )
	       && IS_SET( room_to->area->area_flags, AREA_HIDE ) ) )
	  && !IS_BUILDER( ch, room_to->area ) )
    {
	send_to_char(
	    "&rYou would be ripped to shreds if you entered that!&n\n\r",
		      ch );
	return;
    }

    if( obj->action && obj->action[0] != '\0' )
	send_to_char( obj->action, ch );
    act( "$n disappears into $p.", ch, obj, NULL, TO_ROOM );
    char_from_room( ch );
    char_to_room( ch, room_to );
    act( "The world turns inside out as $n steps from $p.",
	 ch, obj, NULL, TO_ROOM );
    if( ch->desc )
	do_look( ch, AUTOLOOK );

    return;
}


#define TRACK_ERROR -1
#define TRACK_ALLREADY_THERE -2
#define TOROOM( x, y ) ( ( x )->exit[( y )]->to_room )

TRACK_DATA *track_top = NULL, *track_bottom = NULL;

/* Tracking coded by Symposium based on CircleMUD BFS. */
void do_track( CHAR_DATA *ch, const char *argument )
{
    CHAR_DATA *target, *wch;
    char arg[MAX_INPUT_LENGTH];
    int direction;
    int number;
    int count;

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

    /* since we can only track characters that are in the same area
       we have to use a slightly modified get_char_world so that
       we don't end up counting all those mobiles that aren't trackable
       --Symposium */
    if( !( target = get_char_room( ch, argument ) ) )
    {
	target = NULL;
	number = number_argument( argument, arg );
	count = 0;
	for( wch = char_list; wch; wch = wch->next )
	{
	    if( wch->deleted || !can_see( ch, wch )
		|| !is_char_name( wch, arg ) )
		continue;
	    if( wch->in_room && wch->in_room->area &&
		wch->in_room->area != ch->in_room->area )
		continue;
	    if( ++count == number )
	    {
		target = wch;
		break;
	    }
	}
    }

    if( !target || ( target->in_room->area != ch->in_room->area )
	|| !get_success( ch, gsn_track, 100 ) )
    {
	send_to_char( "You can't seem to find a trail from here.\n\r", ch );
	ch->tracking = NULL;
	return;
    }

    if( target->in_room == ch->in_room )
    {
	send_to_char( "They are right in this room, don't strain yourself.\n\r", ch );
	ch->tracking = NULL;
	return;
    }

    ch->tracking = target;
    direction = find_first_step( ch->in_room, ch->tracking->in_room );
    clear_track_marks( ch->in_room );
    switch( direction )
    {
    case TRACK_ERROR:
	send_to_char( "The trail goes cold and you lose track of your quarry.\n\r", ch );
	ch->tracking = NULL;
	break;
    case TRACK_ALLREADY_THERE:
	send_to_char( "&YYou have found your quarry!&n\n\r", ch );
	ch->tracking = NULL;
	break;
    default:
	act( "&yYou sense your quarry's trail to the $t.&n", ch, dir_name[direction], NULL, TO_CHAR );
    }

    return;
}


/* automatic tracking called when you autolook */
void auto_track( CHAR_DATA *ch )
{
    int direction;

    if( !ch->tracking || ch->tracking->deleted )
	return;
    if( ch->tracking->in_room == ch->in_room )
    {
	send_to_char( "&YYou have found your quarry!&n\n\r", ch );
	ch->tracking = NULL;
	return;
    }

    if( ( ch->tracking->in_room->area != ch->in_room->area )
	|| !get_success( ch, gsn_track, 110 ) )
    {
	send_to_char( "The trail goes cold and you lose track of your quarry.\n\r", ch );
	ch->tracking = NULL;
	return;
    }

    direction = find_first_step( ch->in_room, ch->tracking->in_room );
    clear_track_marks( ch->in_room );
    switch( direction )
    {
    case TRACK_ERROR:
	send_to_char( "The trail goes cold and you lose track of your quarry.\n\r", ch );
	ch->tracking = NULL;
	break;
    case TRACK_ALLREADY_THERE:
	send_to_char( "&YYou have found your quarry!&n\n\r", ch );
	ch->tracking = NULL;
	break;
    default:
	act( "&yYou sense your quarry's trail to the $t.&n", ch, dir_name[direction], NULL, TO_CHAR );
    }

    return;
}


void add_track_q( ROOM_INDEX_DATA *room, int dir )
{
    TRACK_DATA *curr;

    curr = (TRACK_DATA *)alloc_mem( sizeof( *curr ) );

    curr->room = room;
    curr->dir = dir;
    curr->next = NULL;

    if( track_bottom )
    {
	track_bottom->next = curr;
	track_bottom = curr;
    }
    else
	track_top = track_bottom = curr;
    return;
}


void remove_track_q( void )
{
    TRACK_DATA *curr;

    curr = track_top;

    if( !( track_top = track_top->next ) )
	track_bottom = NULL;

    free_mem( (void *)curr, sizeof( curr ) );
    return;
}


void clean_track_q( void )
{
    while( track_top )
	remove_track_q( );
    return;
}


int find_first_step( ROOM_INDEX_DATA *start, ROOM_INDEX_DATA *target )
{
    int curr_dir;

    if( start == NULL || target == NULL )
    {
	bug( "Illegal value passed to find_first_step (act_move.c)" );
	return TRACK_ERROR;
    }

    if( start == target )
	return TRACK_ALLREADY_THERE;

    SET_BIT( start->room_flags, ROOM_TRACK_MARK );

    /* first, queue the first steps, saving which direction we're going. */
    for( curr_dir = 0; curr_dir < 6; curr_dir++ )
	if( start->exit[curr_dir] && start->exit[curr_dir]->to_room
	    && TOROOM( start, curr_dir ) && start->exit[curr_dir]
	    && TOROOM( start, curr_dir )->area == start->area
	    && !IS_SET( TOROOM( start, curr_dir )->room_flags,
			ROOM_TRACK_MARK ) )
	{
	    SET_BIT( TOROOM( start, curr_dir )->room_flags, ROOM_TRACK_MARK );
	    add_track_q( TOROOM( start, curr_dir ), curr_dir );
	}

    /* now, do the classic BFS. */
    while( track_top )
    {
	if( track_top->room == target )
	{
	    curr_dir = track_top->dir;
	    clean_track_q( );
	    return curr_dir;
	}
	else    /* add all rooms around the current queue head */
	{
	    for( curr_dir = 0; curr_dir < 6; ++curr_dir )
		if( track_top->room->exit[curr_dir]
		    && track_top->room->exit[curr_dir]->to_room
		    && TOROOM( track_top->room, curr_dir )
		    && track_top->room->exit[curr_dir]
		    && TOROOM( track_top->room, curr_dir )->area == track_top->room->area
		    && !IS_SET( TOROOM( track_top->room, curr_dir )->room_flags, ROOM_TRACK_MARK ) )
		{
		    SET_BIT( TOROOM( track_top->room, curr_dir )->room_flags, ROOM_TRACK_MARK );
		    add_track_q( TOROOM( track_top->room, curr_dir ), track_top->dir );
		}

	    remove_track_q( );
	}
    }

    return TRACK_ERROR;
}


/* recursor to remove all marks left by the track routine */
void clear_track_marks( ROOM_INDEX_DATA *start )
{
    int i;

    if( start && IS_SET( start->room_flags, ROOM_TRACK_MARK ) )
    {
	REMOVE_BIT( start->room_flags, ROOM_TRACK_MARK );
	for( i = 0; i < 6; ++i )
	    if( start->exit[i] )
		clear_track_marks( TOROOM( start, i ) );
    }

    return;
}


void do_charge( CHAR_DATA *ch, const char *argument )
{
    CHAR_DATA *target, *next;
    ROOM_INDEX_DATA *old_room = ch->in_room;
    int dir, move = 100;

    if( IS_NPC( ch ) )
	return;

    if( ch->level < LEVEL_HERO && !can_use( ch, gsn_charge ) )
    {
	send_to_char( "Maybe you should learn how first.\n\r", ch );
	return;
    }

    if( argument[0] == '\0' )
    {
	send_to_char( "Which way to go?\n\r", ch );
	return;
    }

    if( ch->move < move )
    {
	if( !get_success( ch, gsn_stamina, 100 ) )
	{
	    send_to_char( "You are too exhausted.\n\r", ch );
	    return;
	}
	if( ch->hit < ch->max_hit / 3 )
	{
	    send_to_char(
		"Your body is too exhausted, "
		"your tired muscles will move no more.\n\r", ch );
	    return;
	}
	send_to_char( "You push your tired body past it's limits.\n\r", ch );
	move -= ch->move;
	ch->move = 0;
	ch->hit -= move;
	WAIT_STATE( ch, skill_table[gsn_stamina].beats );
    }
    else
	ch->move -= move;

    switch( LOWER( argument[0] ) )
    {
    case 'n':	dir = DIR_NORTH;	break;
    case 'e':	dir = DIR_EAST;		break;
    case 's':	dir = DIR_SOUTH;	break;
    case 'w':	dir = DIR_WEST;		break;
    case 'u':	dir = DIR_UP;		break;
    case 'd':	dir = DIR_DOWN;		break;
    default:
	send_to_char( "Please specify a direction.\n\r", ch );
	return;
    }

    act( "You charge $t", ch, dir_name[dir], NULL, TO_CHAR );
    act( "$n screams a battle cry and charges $t.", ch, dir_name[dir], NULL, TO_ROOM );
    interpret( ch, dir_name[dir] );
    if( old_room == ch->in_room )
	return;
    act( "$n charges in from the $t.", ch, dir_name[rev_dir[dir]], NULL, TO_ROOM );
    if( !get_success( ch, gsn_charge, 100 ) )
    {
	send_to_char( "You lose your nerve.\n\r", ch );
	act( "$n starts to charge but loses $s nerve.", ch, NULL, NULL, TO_ROOM );
	return;
    }

    for( target = ch->in_room->people; target; target = next )
    {
	next = target->next_in_room;
	if( target->deleted )
	    continue;
	if( !IS_NPC( ch ) && !IS_NPC( target ) )
	    continue;
	if( is_affected( target, AFF_CHARM ) )
	    continue;
	target->position = POS_SMASHED;
	target->wait = 2 * PULSE_VIOLENCE;
	multi_hit( ch, target, gsn_charge );
    }

    return;
}


void do_disable( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    char arg[MAX_INPUT_LENGTH];
    int door;
    int dam, trap;

    one_argument( argument, arg );

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

    if( ( door = find_door( ch, arg ) ) >= 0 )
    {
	/* 'open door' */
	EXIT_DATA *pexit;
	EXIT_DATA *pexit_rev;
	ROOM_INDEX_DATA *to_room;

	pexit = ch->in_room->exit[door];
	if( !IS_SET( pexit->exit_info, EX_CLOSED ) )
	{
	    send_to_char( "It's already open.\n\r", ch );
	    return;
	}

	if( !IS_SET( pexit->exit_info, EX_TRAPPED ) )
	{
	    send_to_char( "It's not even trapped.\n\r", ch );
	    return;
	}

	if( get_success( ch, gsn_disable_traps, 60 ) )
	{
	    act( "$n disables the trap on the $d.",
		 ch, NULL, pexit->keyword, TO_ROOM );
	    act( "You disable the trap on the $d.",
		 ch, NULL, pexit->keyword, TO_CHAR );
	    REMOVE_BIT( pexit->exit_info, EX_TRAPPED );
	    if( ( to_room = pexit->to_room )
		&& ( pexit_rev = to_room->exit[rev_dir[door]] )
		&& pexit_rev->to_room == ch->in_room )
	    {
		REMOVE_BIT( pexit_rev->exit_info, EX_TRAPPED );
	    }
	}
	else
	    send_to_char(
		"Despite your efforts, the trap remains active.\n\r",
			  ch );

	trap = number_range( gsn_first_trap, gsn_last_trap );
	dam = 4 + ch->level / 2 + ch->hit / 12;
	if( number_percent( ) < power( ch->pcdata->learned[gsn_disable_traps],
				     7, get_curr_dex( ch ) - 15 ) )
	{
	    act( "A $t shoots out and barely misses $n!", ch,
		skill_table[trap].noun_damage, NULL, TO_ROOM );
	    act( "A $t shoots out and barely misses you!", ch,
		skill_table[trap].noun_damage, NULL, TO_CHAR );
	}
	else
	{
	    act( "A $t shoots out and strikes $n!", ch,
		skill_table[trap].noun_damage, NULL, TO_ROOM );
	    act( "A $t shoots out and strikes you!", ch,
		skill_table[trap].noun_damage, NULL, TO_CHAR );
	    damage( ch, ch, dam, trap, WEAR_NONE );
	}

	return;
    }

    if( ( obj = get_obj_here( ch, arg ) ) )
    {
	/* 'open object' */
	if( obj->item_type != ITEM_CONTAINER )
	{
	    send_to_char( "That's not a container.\n\r", ch );
	    return;
	}
	if( !IS_SET( obj->value[1], CONT_CLOSED ) )
	{
	    send_to_char( "It's already open.\n\r", ch );
	    return;
	}
	if( !IS_SET( obj->value[1], CONT_TRAPPED ) )
	{
	    send_to_char( "It's not even trapped.\n\r", ch );
	    return;
	}

	if( get_success( ch, gsn_disable_traps, 60 ) )
	{
	    send_to_char( "You remove the trap.\n\r", ch );
	    REMOVE_BIT( obj->value[1], CONT_TRAPPED );
	}
	else
	    send_to_char( "You fail to remove the trap.\n\r", ch );
	dam = 4 + ch->level / 2 + ch->hit / 12;
	if( number_percent( ) < power( ch->pcdata->learned[gsn_disable_traps],
				     7, get_curr_dex( ch ) - 15 ) )
	{
	    act( "A small dart shoots out and barely misses $n!", ch,
		NULL, NULL, TO_ROOM );
	    act( "A small dart shoots out and barely misses you!", ch,
		NULL, NULL, TO_CHAR );
	}
	else
	{
	    act( "A small dart shoots out and strikes $n!", ch,
		NULL, NULL, TO_ROOM );
	    act( "A small dart shoots out and strikes you!", ch,
		NULL, NULL, TO_CHAR );
	    damage( ch, ch, dam, gsn_first_trap, WEAR_NONE );
	}

	return;
    }

    act( "You see no $T here.", ch, NULL, arg, TO_CHAR );
    return;
}


void do_meditate( CHAR_DATA *ch, const char *argument )
{
    if( IS_NPC( ch ) || !can_use( ch, gsn_meditation ) )
    {
	send_to_char( "You lack the ability to meditate.\n\r", ch );
	return;
    }
    if( ch->position != POS_RESTING && ch->position != POS_SITTING )
    {
	send_to_char( "You must be resting first.\n\r", ch );
	return;
    }

    act( "$n hums softly in an attempt to find inner peace.", ch, NULL, NULL, TO_ROOM );
    send_to_char( "You try to concentrate on nothing...\n\r", ch );
    if( get_success( ch, gsn_meditation, 100 ) )
	ch->position = POS_MEDITATING;

    WAIT_STATE( ch, skill_table[gsn_meditation].beats );
    return;
}


void throw_hit( CHAR_DATA *ch, CHAR_DATA *victim, OBJ_DATA *thing, int dir )
{
    char buf[MAX_STRING_LENGTH];
    int dam, basic_dam, combat_no;

    if( victim->position == POS_DEAD )
	return;
    act( "$n is hit by the flying object.", victim, NULL, NULL, TO_ROOM );
    send_to_char( "You are hit by the flying object.\n\r", victim );

    dam = GET_AC( victim ) - 100;
    dam += ( get_size( ch ) - get_size( victim ) ) / 2;
    if( !can_see_obj( victim, thing ) )
	dam += 50;

    if( ch->class == CLASS_NONE )
    {
	combat_no = 400;
    }
    else
    {
	combat_no = class_table[ch->class].combat_no;
    }

    dam = dam * 100 + ch->level * combat_no;
    dam = UMIN( -1, dam / 100 );
    dam += number_range( 0, get_hitroll( ch ) * 6 + 5 );
    dam += number_range( 0, ch->level * 4 + 10 );
    if( thing->item_type == ITEM_WEAPON )
    {
	basic_dam = number_range( thing->value[1], thing->value[2] );
	if( basic_dam > 100000 )
	    bug( "Throw_hit basic_dam range > 100000 from %d to %d",
		       thing->value[1], thing->value[2] );

	if( thing->required_skill > 0 )
	    basic_dam = basic_dam * 2 / 3;
    }
    else
    {
	basic_dam = number_range( thing->weight / 2, thing->weight * 3 / 2 );
	basic_dam = UMIN( basic_dam, ch->level * 2 );
    }

    if( IS_SET( thing->extra_flags, ITEM_HORNED ) && number_bits( 5 ) == 0 )
	basic_dam *= 2;
    dam = ( dam / 10 ) + basic_dam;
    dam += get_damroll( ch ) / 10;
    /*
     * Now the other bonuses.
     */
    if( thing && IS_SET( thing->extra_flags, ITEM_CHARGED ) )
    {
	if( ch->level < L_SEN )
	    REMOVE_BIT( thing->extra_flags, ITEM_CHARGED );
	dam += dam * get_curr_int( ch ) / 10;
    }

    if( dam < 0 )
	dam = 0;

    /*
     * Hit them now then quickly stop the fight.
     */
    damage( ch, victim, dam, gsn_throw_weapon, WEAR_NONE );
    if( victim->deleted || victim->position == POS_DEAD )
	return;
    stop_fighting( ch, FALSE );
    stop_fighting( victim, FALSE );

    if( IS_NPC( ch ) || !can_see( victim, ch ) )
    {
	do_yell( victim, "Who threw that!" );
	return;
    }

    sprintf( buf, "I saw you throw that, %s!", ch->name );
    do_yell( victim, buf );
    if( IS_NPC( victim ) && !victim->fighting && victim->position != POS_DEAD )
    {
	if( xIS_SET( victim->act, ACT_SENTINEL ) )
	{
	    obj_from_room( thing );
	    obj_to_char( thing, victim );
	    sprintf( buf, "'%s' %s", thing->name, dir_name[dir] );
	    do_throw( victim, buf );
	}
	else
	{
	    if( number_bits( 2 ) == 0 )
	    {
		obj_from_room( thing );
		obj_to_char( thing, victim );
		act( "$n picks up $p and runs $T.",
		     victim, thing, dir_name[dir], TO_ROOM );
	    }
	    else
		act( "$n screams and runs $T.",
		     victim, thing, dir_name[dir], TO_ROOM );

	    strcpy( buf, dir_name[dir] );
	    interpret( victim, buf );
	    if( victim->in_room == ch->in_room )
	    {
		act( "$N has just arrived and $E is not happy.",
		     ch, NULL, victim, TO_CHAR );
		act( "$N has arrived and $E is after $n with a vengeance.",
		     ch, NULL, victim, TO_ROOM );
		multi_hit( victim, ch, TYPE_UNDEFINED );
	    }
	    else
	    {
		sprintf( buf, "Hey %s, where did you go?", ch->name );
		do_yell( victim, buf );
	    }
	}
    }

    return;
}


void do_throw( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    CHAR_DATA *target, *victim = NULL;
    EXIT_DATA *pexit;
    char arg[MAX_INPUT_LENGTH];
    int number;
    int dir;
    char buf[MAX_INPUT_LENGTH];

    /* Throwing people around is handled in fight.c */
    if( throw( ch, argument ) )
	return;

    argument = one_argument( argument, arg );

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

    if( ch->fighting )
    {
	send_to_char( "No way!\n\r", ch );
	return;
    }

    if( !str_cmp( arg, "all" ) )
    {
	send_to_char( "You can't throw everything at once.\n\r", ch );
	return;
    }
    if( !( obj = get_obj_carry( ch, arg ) ) )
    {
	send_to_char( "You do not have that item.\n\r", ch );
	return;
    }

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

    switch( LOWER( argument[0] ) )
    {
    case 'n':
	dir = DIR_NORTH;
	pexit = ch->in_room->exit[DIR_NORTH];
	break;
    case 'e':
	dir = DIR_EAST;
	pexit = ch->in_room->exit[DIR_EAST];
	break;
    case 's':
	dir = DIR_SOUTH;
	pexit = ch->in_room->exit[DIR_SOUTH];
	break;
    case 'w':
	dir = DIR_WEST;
	pexit = ch->in_room->exit[DIR_WEST];
	break;
    case 'u':
	dir = DIR_UP;
	pexit = ch->in_room->exit[DIR_UP];
	break;
    case 'd':
	dir = DIR_DOWN;
	pexit = ch->in_room->exit[DIR_DOWN];
	break;
    default:
	send_to_char( "Which way was that?\n\r", ch );
	return;
    }

    /* change the constant here to whatever you think best */
    number = str_app_wield( get_curr_str( ch ) ) - get_obj_weight( obj );
    number *= get_success( ch, gsn_throw_weapon, 100 );
    number /= get_obj_weight( obj );
    if( obj->item_type == ITEM_WEAPON )
	number += str_app_wield( get_curr_str( ch ) );
    number += number_percent();
    obj_from_char( obj );
    if( number < 0 )
    {
	send_to_char( "You try to throw it, but it's too heavy to throw.\n\r",
		      ch );
	act( "$n tries to throw $p but $e fumbles and drops it.\n\r",
	     ch, obj, NULL, TO_ROOM );
	obj_to_room( obj, ch->in_room );
	return;
    }

    act( "$n throw$% $p $T.", ch, obj, dir_name[dir], TO_ALL );
    if( !pexit )
    {
	act( "$p thuds into the wall.", ch, obj, NULL, TO_ROOM );
	act( "$p thuds into the wall.", ch, obj, NULL, TO_CHAR );
	obj_to_room( obj, ch->in_room );
	if( obj->item_type == ITEM_EXPLOSIVE )
	    explode( obj );
	return;
    }

    if( IS_SET( pexit->exit_info, EX_CLOSED ) )
    {
	act( "$p thuds into a door.", ch, obj, NULL, TO_ROOM );
	act( "$p thuds into a door.", ch, obj, NULL, TO_CHAR );
	obj_to_room( obj, ch->in_room );
	if( obj->item_type == ITEM_EXPLOSIVE )
	    explode( obj );
	return;
    }

    obj_to_room( obj, pexit->to_room );
    sprintf( buf, "%s flys in from the %s and lands at your feet.\n\r",
	     capitalize( obj->short_descr ), dir_name[rev_dir[dir]] );
    send_to_room( buf, pexit->to_room );
    if( obj->item_type == ITEM_EXPLOSIVE )
    {
	explode( obj );
	return;
    }
    number = 0;
    for( target = pexit->to_room->people; target; target = target->next_in_room )
    {
	if( !is_safe( target, ch )
	    && number_range( 0, number++ ) == 0 )
	    victim = target;
    }
    if( victim )
	throw_hit( ch, victim, obj, rev_dir[dir] );

    return;
}


void sink( CHAR_DATA *ch )
{
    ROOM_INDEX_DATA *toroom;
    OBJ_DATA *boat;

    if( ch->in_room->sector_type != SECT_WATER_SWIM
	&& ch->in_room->sector_type != SECT_WATER_NOSWIM
	&& ch->in_room->sector_type != SECT_UNDERWATER
	&& !IS_SET( ch->in_room->room_flags, ROOM_FLOODED ) )
	return;
    for( boat = ch->carrying; boat; boat = boat->next_content )
    {
	if( !boat->deleted && boat->item_type == ITEM_BOAT )
	    break;
    }
    if( !ch->in_room->exit[boat ? DIR_UP : DIR_DOWN]
	|| !( toroom = ch->in_room->exit[boat ? DIR_UP : DIR_DOWN]->to_room ) )
	return;
    if( toroom->sector_type != SECT_WATER_SWIM
	&& toroom->sector_type != SECT_WATER_NOSWIM
	&& toroom->sector_type != SECT_UNDERWATER
	&& !IS_SET( toroom->room_flags, ROOM_FLOODED ) )
	return;
    if( boat )
    {
	act( "&bYour boat forces you to float higher in the water.",
	     ch, NULL, NULL, TO_CHAR );
	act( "&b$n floats up, bouyed by $s boat.", ch, NULL, NULL, TO_ROOM );
    }
    else
	act( "&b$n slowly sink$% in the water.", ch, NULL, NULL, TO_ALL );
    char_from_room( ch );
    char_to_room( ch, toroom );
    if( boat )
	act( "&b$n has bobbed up from below.", ch, NULL, NULL, TO_ROOM );
    else
	act( "&b$n has drifted in from above.", ch, NULL, NULL, TO_ROOM );
    do_look( ch, AUTOLOOK );
    return;
}


void found_prey( CHAR_DATA *ch, CHAR_DATA *victim )
{
    char buf [ MAX_STRING_LENGTH ];
    char victname [ MAX_INPUT_LENGTH ];

    if( !victim || victim->deleted )
    {
	bug( "Found_prey: null victim" );
	return;
    }

    if( !victim->in_room )
    {
	bug( "Found_prey: null victim->in_room" );
	return;
    }

    sprintf( victname, ( victim->short_descr && victim->short_descr[0] )
	     ? victim->short_descr : victim->name );
    if( !can_see( ch, victim ) )
    {
	if( number_percent( ) < 90 )
	    return;
	switch( number_bits( 2 ) )
	{
	case 0:
	    sprintf( buf, "Don't make me find you, %s!", victname );
	    do_say( ch, buf );
	    break;
	case 1:
	    act( "$n sniffs around the room for $N.", ch, NULL, victim, TO_CANSEE );
	    do_say( ch, "I can smell your blood!" );
	    break;
	case 2:
	    sprintf( buf, "I'm going to tear %s apart!", victname );
	    do_yell( ch, buf );
	    break;
	case 3:
	    do_say( ch, "Just wait until I find you..." );
	    break;
	}
	return;
    }

    if( IS_SET( ch->in_room->room_flags, ROOM_SAFE ) )
    {
	if( number_bits( 3 ) )
	    return;
	switch( number_bits( 2 ) )
	{
	case 0:
	    do_say( ch, "C'mon out, you coward!" );
	    sprintf( buf, "%s is a bloody coward!", victname );
	    do_yell( ch, buf );
	    break;
	case 1:
	    sprintf( buf, "Let's take this outside, %s", victname );
	    do_say( ch, buf );
	    break;
	case 2:
	    sprintf( buf, "%s is a yellow-bellied wimp!", victname );
	    do_yell( ch, buf );
	    break;
	case 3:
	    act( "$n take$% a few swipes at $N.", ch, NULL, victim, TO_ALL );
	    break;
	}
	return;
    }

    switch( number_bits( 2 ) )
    {
    case 0:
	sprintf( buf, "Your blood is mine, %s!", victname );
	do_yell( ch, buf);
	break;
    case 1:
	sprintf( buf, "Alas, we meet again, %s!", victname );
	do_say( ch, buf );
	break;
    case 2:
	sprintf( buf, "What do you want on your tombstone, %s?", victname );
	do_say( ch, buf );
	break;
    case 3:
	act( "$n lunges at $N from out of nowhere!", ch, NULL, victim, TO_ROOM );
	act( "You lunge at $N catching $M off guard!", ch, NULL, victim, TO_CHAR );
	break;
    }
    ch->tracking = NULL;
    multi_hit( ch, victim, TYPE_UNDEFINED );
    return;
}


/* Mobs can now track a character that has fled.
 * This means they follow them and there is a greater chance that
 * they will attack them	-Symposium
 * Returns true when the hunt needs to continue.
 */
bool mob_track_update( CHAR_DATA *ch )
{
    int direction;

    if( xIS_SET( ch->act, ACT_SENTINEL ) || !xIS_SET( ch->act, ACT_HUNTER )
	|| !can_see( ch, ch->tracking ) )
    {
	ch->tracking = NULL;
	return FALSE;
    }

/*    if( IS_AFFECTED( ch, AFF_BLEEDING ) || ch->hit < ch->tracking->hit )
      {
      if( ch->tracking->in_room == ch->in_room )
      do_flee( ch, "" );
      return TRUE;
      }
*/

    if( ch->in_room == ch->tracking->in_room )
    {
	if( ch->fighting )
	    return FALSE;
	found_prey( ch, ch->tracking );
	return FALSE;
    }

    act( "$n carefully sniffs the air.", ch, NULL, NULL, TO_CANSEE );
    if( number_percent( ) < 30 - URANGE( -25, ch->level + ch->tracking->level, 25 ) )
    {
	act( "$n loses track of $s quarry.", ch, NULL, NULL, TO_CANSEE );
	ch->tracking = NULL;
	return FALSE;
    }

    direction = find_first_step( ch->in_room, ch->tracking->in_room );
    clear_track_marks( ch->in_room );

    if( IS_SET( ch->in_room->exit[direction]->exit_info, EX_CLOSED ) )
    {
	do_open( ch, dir_name[direction] );
	return TRUE;
    }

    switch( direction )
    {
    case TRACK_ERROR:
	act( "$n loses track of $s quarry.", ch, NULL, NULL, TO_CANSEE );
	ch->tracking = NULL;
	return FALSE;
    case TRACK_ALLREADY_THERE:
	if( ch->tracking && ch->in_room == ch->tracking->in_room )
	    found_prey( ch, ch->tracking );
	ch->tracking = NULL;
	return FALSE;
    default:
	move_char( ch, direction );
	break;
    }

    return TRUE;
}


void do_push_drag( CHAR_DATA *ch, const char *argument, const char *verb )
{
    char arg1[MAX_INPUT_LENGTH];
    char buf[MAX_STRING_LENGTH];
    ROOM_INDEX_DATA *in_room;
    ROOM_INDEX_DATA *to_room;
    CHAR_DATA *victim;
    EXIT_DATA *pexit;
    OBJ_DATA *obj;
    int door;

    argument = one_argument( argument, arg1 );
    victim = get_char_room( ch, arg1 );
    obj = get_obj_list( ch, arg1, ch->in_room->contents );

    if( arg1[0] == '\0' || argument[0] == '\0' )
    {
	sprintf( buf, "%s whom or what where?\n\r", capitalize( verb ) );
	send_to_char( buf, ch );
	return;
    }

    if( ( !victim || !can_see( ch,victim ) )
	&& ( !obj || !can_see_obj( ch,obj ) ) )
    {
	sprintf( buf,"%s whom or what where?\n\r", capitalize( verb ) );
	send_to_char( buf, ch );
	return;
    }

    if( !str_prefix( argument, "north" ) ) door = 0;
    else if( !str_prefix( argument, "east"	 ) ) door = 1;
    else if( !str_prefix( argument, "south" ) ) door = 2;
    else if( !str_prefix( argument, "west"	 ) ) door = 3;
    else if( !str_prefix( argument, "up"	 ) ) door = 4;
    else if( !str_prefix( argument, "down"	 ) ) door = 5;
    else
    {
	sprintf( buf, "Alas, you cannot %s in that direction.\n\r", verb );
	send_to_char( buf, ch );
	return;
    }

    if( obj )
    {
	in_room = obj->in_room;
	if( ( pexit = in_room->exit[door] ) == NULL
	    || ( to_room = pexit->to_room ) == NULL )
	{
	    sprintf( buf, "Alas, you cannot %s in that direction.\n\r", verb );
	    send_to_char( buf, ch );
	    return;
	}

	if( !IS_IMMORTAL( ch )
	    && ( IS_SET( ch->in_room->room_flags, ROOM_SAFE )
		 || IS_SET( ch->in_room->room_flags, ROOM_PRIVATE )
		 || IS_SET( ch->in_room->room_flags, ROOM_SOLITARY )
		 || IS_SET( ch->in_room->room_flags, ROOM_NO_RECALL )
		 || number_percent() > 75
		 || !IS_SET( obj->wear_flags, ITEM_TAKE ) ) )
	{
	    send_to_char( "It won't budge.\n\r", ch );
	    return;
	}

	if( IS_SET( pexit->exit_info, EX_CLOSED )
	    || IS_SET( pexit->exit_info, EX_PASSPROOF ) )
	{
	    act( "You cannot $t it through the $d.",
		 ch, verb, pexit->keyword, TO_CHAR );
	    act( "$n decides to $t $P around!", ch, verb, obj, TO_ROOM );
	    return;
	}

	act( "You attempt to $T $p out of the room.", ch, obj, verb, TO_CHAR );
	act( "$n is attempting to $T $p out of the room.",
	     ch, obj, verb, TO_ROOM );
	if( obj->weight > ( 2 * can_carry_w( ch ) ) )
	{
	    act( "$p is too heavy to $T.\n\r", ch, obj, verb, TO_CHAR);
	    act( "$n attempts to $T $p, but it is too heavy.\n\r",
		 ch, obj, verb, TO_ROOM);
	    return;
	}

	if( ch->move > 25 )
	{
	    if( !str_cmp( verb, "drag" ) )
		move_char( ch, door );
	    if( ch->in_room == in_room )
		return;
	    ch->move -= 25;
	    send_to_char( "You succeed!\n\r", ch );
	    act( "$n succeeds!", ch, NULL, NULL, TO_ROOM );
	    obj_from_room( obj );
	    obj_to_room( obj, to_room );
	}
	else
	{
	    sprintf( buf, "You are too tired to %s anything around!\n\r", verb );
	    send_to_char( buf, ch );
	}
    }
    else
    {
	if( ch == victim )
	{
	    act( "You $t yourself about the room and look very silly.",
		 ch, verb, NULL, TO_CHAR );
	    act( "$n decides to be silly and $t $mself about the room.",
		 ch, verb, NULL, TO_ROOM );
	    return;
	}

	in_room = victim->in_room;
	if( ( pexit = in_room->exit[door] ) == NULL
	    || ( to_room = pexit->to_room ) == NULL )
	{
	    sprintf( buf, "Alas, you cannot %s them that way.\n\r", verb );
	    send_to_char( buf, ch );
	    return;
	}

	if( IS_SET( pexit->exit_info, EX_CLOSED )
	    && ( !IS_AFFECTED( victim, AFF_PASS_DOOR )
		 || IS_SET( pexit->exit_info, EX_PASSPROOF ) ) )
	{
	    act( "You try to $t them through the $d.",
		 ch, verb, pexit->keyword, TO_CHAR );
	    act( "$n decides to $t you around!", ch, verb, victim, TO_VICT );
	    act( "$n decides to $t $N around!", ch, verb, victim, TO_NOTVICT );
	    return;
	}

	act( "You attempt to $t $N out of the room.",
	     ch, verb, victim, TO_CHAR );
	act( "$n is attempting to $t you out of the room!",
	     ch, verb, victim, TO_VICT );
	act( "$n is attempting to $t $N out of the room.",
	     ch, verb, victim, TO_NOTVICT );

	if( !IS_IMMORTAL( ch )
	    || ( IS_NPC( victim )
		 && ( xIS_SET( victim->act,ACT_TRAIN )
		      || xIS_SET( victim->act, ACT_PRACTICE )
		      || xIS_SET( victim->act, ACT_BANKER )
		      || victim->pIndexData->pShop ) )
	    || victim->in_room == NULL
	    || IS_SET( victim->in_room->room_flags, ROOM_SAFE )
	    || IS_SET( victim->in_room->room_flags, ROOM_PRIVATE )
	    || IS_SET( victim->in_room->room_flags, ROOM_SOLITARY )
	    || IS_SET( victim->in_room->room_flags, ROOM_NO_RECALL )
	    || IS_SET( ch->in_room->room_flags, ROOM_NO_RECALL )
	    || ( !str_cmp( verb, "drag" ) && victim->position >= POS_STANDING )
	    || ( !str_cmp( verb, "push" ) && victim->position != POS_STANDING )
	    || is_safe( ch, victim )
	    || ( number_bits( 2 ) == 0 )
	    || victim->level >= ch->level + 5
	    || ( !IS_NPC( victim ) && victim->level >= LEVEL_HERO ) )
	{
	    send_to_char( "They won't budge.\n\r", ch );
	    return;
	}

	if( IS_NPC( victim ) && number_bits( 2 ) == 0 )
	{
	    do_say( victim, "Hey don't push me around!" );
	    multi_hit( victim, ch, TYPE_UNDEFINED );
	}
	else if( ch->move > 25 )
	{
	    ch->move -= 25;
	    send_to_char( "You succeed!\n\r", ch );
	    act( "$n succeeds!", ch, NULL, NULL, TO_ROOM );
	    if( !str_cmp( verb, "drag" ) )
		move_char( ch, door );
	    move_char( victim, door );
	}
	else
	{
	    sprintf( buf, "You are too tired to %s anybody around!\n\r", verb );
	    send_to_char( buf, ch );
	}
    }

    return;
}


void do_push( CHAR_DATA *ch, const char *argument )
{
    do_push_drag( ch, argument, "push" );
    return;
}


void do_drag( CHAR_DATA *ch, const char *argument )
{
    do_push_drag( ch, argument, "drag" );
    return;
}