clan/
class/
data/
doc/ideas/
doc/mobprogs/
gods/
log/
player/
time/
/*
 *  The unique portions SunderMud code as well as the integration efforts
 *  for code from other sources is based on the efforts of:
 *
 *  Lotherius (elfren@aros.net)
 *
 *  This code can only be used under the terms of the DikuMud, Merc,
 *  and ROM licenses. The same requirements apply to the changes that
 *  have been made.
 *
 * All other copyrights remain in place and in force.
*/


/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  In order to use any part of this Merc Diku Mud, you must comply with   *
 *  both the original Diku license in 'license.doc' as well the Merc       *
 *  license in 'license.txt'.  In particular, you may not remove either of *
 *  these copyright notices.                                               *
 *                                                                         *
 *  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.                                                  *
 ***************************************************************************/

#include "everything.h"

/* command procedures needed */
DECLARE_DO_FUN(do_look		);
DECLARE_DO_FUN(do_recall	);
DECLARE_DO_FUN(do_stand		);


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

const	sh_int	rev_dir		[]		=
{
    2, 3, 0, 1, 5, 4
};

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



/*
 * Local functions.
 */
int	find_door	args( ( CHAR_DATA *ch, char *arg ) );
bool	has_key		args( ( CHAR_DATA *ch, int key ) );



void move_char( CHAR_DATA *ch, int door, bool follow )
{
    CHAR_DATA *fch;
    CHAR_DATA *fch_next;
    ROOM_INDEX_DATA *in_room;
    ROOM_INDEX_DATA *to_room;
    EXIT_DATA *pexit;

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

    in_room = ch->in_room;
    if ( ( pexit   = in_room->exit[door] ) == NULL
    ||   ( to_room = pexit->u1.to_room   ) == NULL 
    ||	 !can_see_room(ch,pexit->u1.to_room))
    {
	send_to_char( "Alas, you cannot go that way.\n\r", ch );
	return;
    }

    if ( IS_SET(pexit->exit_info, EX_CLOSED)
		&& !IS_SET(pexit->exit_info, EX_HIDDEN) 
    	&&   !IS_AFFECTED(ch, AFF_PASS_DOOR) )
    {
	act( "The $d is closed.", ch, NULL, pexit->keyword, TO_CHAR );
	return;
    }
    
	if ( IS_SET(pexit->exit_info, EX_CLOSED)
		&& IS_SET(pexit->exit_info, EX_HIDDEN) 
    	&&   !IS_AFFECTED(ch, AFF_PASS_DOOR) )
    {
	send_to_char ( "Alas, you cannot go that way.\n\r", ch);
	return;
	}
	
	if ( IS_SET(pexit->exit_info, EX_CLOSED)
		&& IS_SET(pexit->exit_info, EX_HIDDEN) 
		&& IS_SET(pexit->exit_info, EX_NO_PASS)
    	&& IS_AFFECTED(ch, AFF_PASS_DOOR) )
    {
	send_to_char ( "Alas, you cannot go that way.\n\r", ch);
	return;
	}

	if ( IS_SET(pexit->exit_info, EX_CLOSED)
		&& IS_SET(pexit->exit_info, EX_NO_PASS)
    	&& IS_AFFECTED(ch, AFF_PASS_DOOR) )
	{
	send_to_char ( "A strange energy blocks your pass_door spell.\n\r",ch);
	return;
	}
		

    if ( IS_AFFECTED(ch, AFF_CHARM)
    &&   ch->master != NULL
    &&   in_room == ch->master->in_room )
    {
	send_to_char( "What?  And leave your beloved master?\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;
	char buf[100];
	char weathermsg[20];
	char walkmsg[10];

	if ( in_room->sector_type == SECT_AIR
	||   to_room->sector_type == SECT_AIR )
	{
	    if ( !IS_AFFECTED(ch, AFF_FLYING) && !IS_IMMORTAL(ch))
	    {
		send_to_char( "You can't fly.\n\r", ch );
		return;
	    }
	}

	if (( in_room->sector_type == SECT_WATER_NOSWIM
	||    to_room->sector_type == SECT_WATER_NOSWIM )
  	&&    !IS_AFFECTED(ch,AFF_FLYING))
	{
	    OBJ_DATA *obj;
	    bool found;

	    /*
	     * Look for a boat.
	     */
	    found = FALSE;

	    if (IS_IMMORTAL(ch))
		found = TRUE;

	    for ( obj = ch->carrying; obj != NULL; obj = obj->next_content )
	    {
		if ( obj->item_type == ITEM_BOAT )
		{
		    found = TRUE;
		    break;
		}
	    }
	    if ( !found )
	    {
		send_to_char( "You need a boat 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)]
	     ;

        move /= 2;  /* i.e. the average */

	if ( ch->move < move )
	{
	    send_to_char( "You are too exhausted.\n\r", ch );
	    return;
	}

	/* move messages */
       
	/* set move words */

	weathermsg[0] = '\0';
	walkmsg[0] = '\0';

	if (weather_info.change >=0) /* summer */
        {
	    switch ( weather_info.sky )
	    {
		case SKY_CLOUDLESS:
		    strcat(weathermsg,"hot");
		    strcat(walkmsg,"walk");
		    break;
		case SKY_CLOUDY:
		    strcat(weathermsg,"warm");
		    strcat(walkmsg,"walk");
		    break;
		case SKY_RAINING:
		    strcat(weathermsg,"wet");
		    strcat(walkmsg,"splash");
		    break;
		case SKY_LIGHTNING:
		    strcat(weathermsg,"stormy");
		    strcat(walkmsg,"splash");
		    break;
	    } /* end of switch */
	} /* end of if */
	else
	{
	    switch ( weather_info.sky)
	    {
		case SKY_CLOUDLESS:
		    strcat(weathermsg,"frigid");
		    strcat(walkmsg,"walk");
		    break;
		case SKY_CLOUDY:
		    strcat(weathermsg,"cold");
		    strcat(walkmsg,"walk");
		    break;
		case SKY_RAINING:
		    strcat(weathermsg,"snowy");
		    strcat(walkmsg,"trudge");
		    break;
		case SKY_LIGHTNING:
		    strcat(weathermsg,"blizzard-stricken");
		    strcat(walkmsg,"trudge");
		    break;
	    } /* end of switch */
	}  /* end of else */

	if ( IS_AFFECTED(ch,AFF_FLYING) )
	{
	    walkmsg[0] = '\0';
	    strcat(walkmsg,"fly");
	}

	/* leaving an indoors room */

        if ( ( in_room->sector_type == SECT_INSIDE )
	   ||( in_room->sector_type == SECT_UNDERGROUND ) )
	{
	switch(to_room->sector_type)
	{
	    case SECT_INSIDE:
	    case SECT_WATER_SWIM:
	    case SECT_WATER_NOSWIM:
	    case SECT_MAX:
	    case SECT_UNDERGROUND:
		break; /* no message for this movement */
	    default:
	        buf[0] = '\0';
		sprintf(buf,"You %s into the %s %s.\n\r",
			walkmsg,
			weathermsg,
			flag_string(sector_name, to_room->sector_type) ); 
		send_to_char(buf,ch);
		break;
	} /* end of switch */
	} /* end of indoors room leave routine */

	/* leaving a city */

        if ( in_room->sector_type == SECT_CITY )
        {
        switch(to_room->sector_type)
	{
	    case SECT_CITY:
	    case SECT_WATER_SWIM:
	    case SECT_WATER_NOSWIM:
	    case SECT_MAX:
                break; /* no message for this movement */
	    case SECT_INSIDE:
                buf[0] = '\0';
		sprintf(buf,"You leave the %s city streets and take shelter indoors.\n\r",
			weathermsg );
		send_to_char(buf,ch);
		break;
	    case SECT_UNDERGROUND:
                send_to_char("You go underground.\n\r",ch);
		break;
	    default:
                buf[0] = '\0';
                sprintf(buf,"You %s out of the city for the %s %s.\n\r",
                        walkmsg,
                        weathermsg,
                        flag_string(sector_name, to_room->sector_type) );
                send_to_char(buf,ch);
		break;
	} /* end of switch */
	} /* end of leaving city */

	/* leaving a field, forest, hills, or mountain. */

        if ( ( in_room->sector_type == SECT_FIELD )
	    || (in_room->sector_type == SECT_FOREST )
	    || (in_room->sector_type == SECT_HILLS )
	    || (in_room->sector_type == SECT_DESERT )
	    || (in_room->sector_type == SECT_MOUNTAIN ) )
        {
	switch (to_room->sector_type)
	{
	    case SECT_WATER_SWIM:
	    case SECT_WATER_NOSWIM:
	    case SECT_MAX: 
                break; /* no message for this movement */
	    case SECT_INSIDE:
                buf[0] = '\0';
		sprintf(buf,"You take shelter indoors from the %s %s.\n\r",
			weathermsg,
			flag_string( sector_name, in_room->sector_type) );
		send_to_char(buf,ch);
		break;
	    case SECT_UNDERGROUND:
		send_to_char("You go underground.\n\r",ch);
		break;
	    case SECT_CITY:
		sprintf(buf,"You leave the %s and enter a %s city.\n\r",
			flag_string( sector_name, in_room->sector_type),
			weathermsg );
		send_to_char(buf,ch);
		break;
	    default:
		if (in_room->sector_type == to_room->sector_type)
			break; /* no msg if no change */
		sprintf(buf,"You head into some %s.\n\r",
			flag_string( sector_name, to_room->sector_type) );
		send_to_char(buf,ch);
		break;
	} /* end switch */
        } /* end of leaving forest, field, hills, mountain, desert */

	/* leaving the air */

	if (in_room->sector_type == SECT_AIR)
	{
	    switch (to_room->sector_type)
	    {
		case SECT_AIR:
		case SECT_MAX:
			break; /* no msg */
		default:
			buf[0]='\0';
			sprintf(buf,"You land in the %s %s.\n\r",
			    weathermsg,
			    flag_string(sector_name, to_room->sector_type) );
			send_to_char(buf,ch);
			break;
	    } /* end switch */
	} /* end leaving the air */

	WAIT_STATE( ch, 1 );
	ch->move -= move;
    }

    if ( !IS_AFFECTED(ch, AFF_SNEAK)
    && ( IS_NPC(ch) || !IS_SET(ch->act, PLR_WIZINVIS) ) )
	act( "$n leaves $T.", ch, NULL, dir_name[door], TO_ROOM );

    char_from_room( ch );
    char_to_room( ch, to_room );
    if ( !IS_AFFECTED(ch, AFF_SNEAK)
    && ( IS_NPC(ch) || !IS_SET(ch->act, PLR_WIZINVIS) ) )
	act( "$n has arrived.", ch, NULL, NULL, TO_ROOM );

    do_look( ch, "auto" );

    if (in_room == to_room) /* no circular follows */
	return;

    /* do the recruit proc */

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

#if defined(DEBUGINFO)
	send_to_char("inside the recruit proc\n\r",ch);
#endif

        if ( IS_NPC(fch) && (fch->level < ch->level)
	    && (!IS_NPC(ch) )
            && (!IS_SET(fch->act, ACT_SENTINEL) )
            && (!IS_SET(fch->act, ACT_AGGRESSIVE) )
	    && (!IS_SET(fch->act, ACT_PET) )
	    && (!IS_SET(fch->act, ACT_FOLLOWER) )
	    && (!IS_SET(fch->act, ACT_PRACTICE) )
	    && (!IS_SET(fch->act, ACT_NOPURGE) )
	    && (!IS_SET(fch->act, ACT_IS_HEALER) )
	    && (!IS_AFFECTED(fch, AFF_CHARM) ) )
        {
            int pass;
            int chance;
	    int skillchance;
	    int diff;

#if defined(DEBUGINFO)
	    char buff[MAX_STRING_LENGTH];

	    send_to_char("inside the actual recruit proc means there is a valid mob\n\r",ch);
#endif

            pass = 1; /* base chance of passing is 1% */


	    /* bonus if alignment is within 150 of char */
	    diff = fch->alignment - ch->alignment;
	    if ( (diff <= 150) && (diff >= -150) )
		pass +=1;

	    /* add charisma check here */

	    if ((fch->alignment>250) && (ch->alignment<250))
		pass =0;


	    skillchance = number_percent();

	    chance = number_percent();

#if defined(DEBUGINFO)
	    sprintf(buff,"diff %d\n\rpass(mob) %d\n\rchance(rnd) %d\n\rskillchance %d\n\rch %s\n\rwch %s\n\r",
		    diff,
		    pass,
		    chance,
		    skillchance,
		    ch->name,
		    fch->short_descr );

	    send_to_char(buff,ch);
	    log_string(buff);
#endif

	    if ( (pass >= chance) && (skillchance < ch->pcdata->learned[gsn_recruit]) )
	    { /* make new follower! */
#if defined(DEBUGINFO)
		send_to_char("inside the make a new follower proc\n\r",ch);
#endif
		SET_BIT(fch->act, ACT_FOLLOWER);
        	SET_BIT(fch->affected_by, AFF_CHARM);
		fch->comm = COMM_NOTELL|COMM_NOSHOUT|COMM_NOCHANNELS;
		add_follower( fch, ch );
        	fch->leader = ch;
		act( "$N starts has heard of your reputation and starts following you!", fch, NULL, NULL,TO_CHAR );
        	act( "$N now follows $n!", fch, NULL, ch, TO_ROOM );
		check_improve(ch, gsn_recruit, TRUE, 1);
	    } /* end of new follow */
	} /* end of recruit routine */
    } /* end of for loop */

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

        if ( fch->master == ch && IS_AFFECTED(fch,AFF_CHARM)
        &&   fch->position < POS_STANDING)
            do_stand(fch,"");

	if ( fch->master == ch && fch->position == POS_STANDING )
	{

	    if (IS_SET(ch->in_room->room_flags,ROOM_LAW)
	    &&  (IS_NPC(fch) && IS_SET(fch->act,ACT_AGGRESSIVE)))
	    {
		act("You can't bring $N into the city.",
		    ch,NULL,fch,TO_CHAR);
		act("You aren't allowed in the city.",
		    fch,NULL,NULL,TO_CHAR);
		return;
	    }

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


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



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



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



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



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



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



int find_door( CHAR_DATA *ch, char *arg )
{
    EXIT_DATA *pexit;
    int door;
	
    if ( !str_cmp( arg, "n" ) || !str_cmp( arg, "north" ) ) door = 0;
    else if ( !str_cmp( arg, "e" ) || !str_cmp( arg, "east"  ) ) door = 1;
    else if ( !str_cmp( arg, "s" ) || !str_cmp( arg, "south" ) ) door = 2;
    else if ( !str_cmp( arg, "w" ) || !str_cmp( arg, "west"  ) ) door = 3;
    else if ( !str_cmp( arg, "u" ) || !str_cmp( arg, "up"    ) ) door = 4;
    else if ( !str_cmp( arg, "d" ) || !str_cmp( arg, "down"  ) ) door = 5;
    else
    {
	for ( door = 0; door <= 5; door++ )
	{
	    if ( ( pexit = ch->in_room->exit[door] ) != NULL
	    &&   IS_SET(pexit->exit_info, EX_ISDOOR)
	    &&   pexit->keyword != NULL
	    &&   is_name( arg, pexit->keyword ) )
		return door;
	}
	return -1;
    }

    if ( ( pexit = ch->in_room->exit[door] ) == NULL )
    {
	act( "I see no door $T here.", ch, NULL, arg, TO_CHAR );
	return -1;
    }

    if ( !IS_SET(pexit->exit_info, EX_ISDOOR) )
    {
	send_to_char( "You can't do that.\n\r", ch );
	return -1;
    }

    return door;
}



void do_open( CHAR_DATA *ch, char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    OBJ_DATA *obj;
    int door=-1;

    one_argument( argument, arg );

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

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

	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( "$n opens the $d.", ch, NULL, pexit->keyword, TO_ROOM );
	send_to_char( "Ok.\n\r", ch );

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

	    REMOVE_BIT( pexit_rev->exit_info, EX_CLOSED );
	    for ( rch = to_room->people; rch != NULL; rch = rch->next_in_room )
		act( "The $d opens.", rch, NULL, pexit_rev->keyword, TO_CHAR );
	}
	return;
    }
    
	if ( ( obj = get_obj_here( ch, arg ) ) != NULL )
    {
	/* '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; }

	REMOVE_BIT(obj->value[1], CONT_CLOSED);
	send_to_char( "Ok.\n\r", ch );
	act( "$n opens $p.", ch, obj, NULL, TO_ROOM );
	return;
    }
act( "I see no $T here.", ch, NULL, arg, TO_CHAR );
return;
}



void do_close( CHAR_DATA *ch, char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    OBJ_DATA *obj;
    int door;

    one_argument( argument, arg );

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

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

	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; }

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

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

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

    if ( ( obj = get_obj_here( ch, arg ) ) != NULL )
    {
	/* '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( "$n closes $p.", ch, obj, NULL, TO_ROOM );
	return;
   	}
    act( "I see no $T here.", ch, NULL, arg, TO_CHAR );
    return;
}



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

    for ( obj = ch->carrying; obj != NULL; obj = obj->next_content )
    {
	if ( obj->pIndexData->vnum == key )
	    return TRUE;
    }

    return FALSE;
}



void do_lock( CHAR_DATA *ch, char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    OBJ_DATA *obj;
    int door;

    one_argument( argument, arg );

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

    if ( ( obj = get_obj_here( ch, arg ) ) != NULL )
    {
	/* '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;
    }

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

	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( "*Click*\n\r", ch );
	act( "$n locks the $d.", ch, NULL, pexit->keyword, TO_ROOM );

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

    return;
}



void do_unlock( CHAR_DATA *ch, char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    OBJ_DATA *obj;
    int door;

    one_argument( argument, arg );

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

    if ( ( obj = get_obj_here( ch, arg ) ) != NULL )
    {
	/* '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; }

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

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

	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 ( !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( "*Click*\n\r", ch );
	act( "$n unlocks the $d.", ch, NULL, pexit->keyword, TO_ROOM );

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

    return;
}



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

    one_argument( argument, arg );

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

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

    /* look for guards */
    for ( gch = ch->in_room->people; gch; gch = gch->next_in_room )
    {
	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;
	}
    }

    if ( !IS_NPC(ch) && number_percent( ) > ch->pcdata->learned[gsn_pick_lock] )
    {
	send_to_char( "You failed.\n\r", ch);
	check_improve(ch,gsn_pick_lock,FALSE,2);
	return;
    }

    if ( ( obj = get_obj_here( ch, arg ) ) != NULL )
    {
	/* '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 ( obj->value[2] < 0 )
	    { send_to_char( "It can't be unlocked.\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_PICKPROOF) )
	    { send_to_char( "You failed.\n\r",             ch ); return; }

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

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

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

	REMOVE_BIT(pexit->exit_info, EX_LOCKED);
	send_to_char( "*Click*\n\r", ch );
	act( "$n picks the $d.", ch, NULL, pexit->keyword, TO_ROOM );
	check_improve(ch,gsn_pick_lock,TRUE,2);

	/* pick the other side */
	if ( ( to_room   = pexit->u1.to_room            ) != NULL
	&&   ( pexit_rev = to_room->exit[rev_dir[door]] ) != NULL
	&&   pexit_rev->u1.to_room == ch->in_room )
	{
	    REMOVE_BIT( pexit_rev->exit_info, EX_LOCKED );
	}
    }

    return;
}

void do_stand( CHAR_DATA *ch, char *argument )
{
	if ( IS_AFFECTED(ch, AFF_SLEEP) )
	    {
		send_to_char( "You are asleep and cannot wake up!\n\r", ch );
	 	return; 
		}
    switch ( ch->position )
    {
    case POS_SLEEPING:
	send_to_char( "You wake and stand up.\n\r", ch );
	act( "$n wakes and stands up.", ch, NULL, NULL, TO_ROOM );
	ch->position = POS_STANDING;
	break;

    case POS_RESTING: case POS_SITTING:
	send_to_char( "You stand up.\n\r", ch );
	act( "$n stands up.", ch, NULL, NULL, TO_ROOM );
	ch->position = POS_STANDING;
	break;

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

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

    return;
}



void do_rest( CHAR_DATA *ch, char *argument )
{
	if (IS_AFFECTED(ch, AFF_SLEEP))
		{
		send_to_char ("You are asleep and cannot wake up!\n\r",ch);
		return;
		}
    switch ( ch->position )
    {
    case POS_SLEEPING:
	send_to_char( "You wake up and start resting.\n\r", ch );
	act ("$n wakes up and starts resting.",ch,NULL,NULL,TO_ROOM);
	ch->position = POS_RESTING;
	break;

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

    case POS_STANDING:
	send_to_char( "You rest.\n\r", ch );
	act( "$n sits down and rests.", ch, NULL, NULL, TO_ROOM );
	ch->position = POS_RESTING;
	break;

    case POS_SITTING:
	send_to_char("You rest.\n\r",ch);
	act("$n rests.",ch,NULL,NULL,TO_ROOM);
	ch->position = POS_RESTING;
	break;

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


    return;
}


void do_sit (CHAR_DATA *ch, char *argument )
{
	if (IS_AFFECTED(ch, AFF_SLEEP) )
		{
		send_to_char ("You are asleep and cannot wake up!\n\r",ch);
		return;
		}
    switch (ch->position)
    {
	case POS_SLEEPING:
	    send_to_char("You wake up.\n\r",ch);
	    act("$n wakes and sits up.\n\r",ch,NULL,NULL,TO_ROOM);
	    ch->position = POS_SITTING;
	    break;
 	case POS_RESTING:
	    send_to_char("You stop resting.\n\r",ch);
	    ch->position = POS_SITTING;
	    break;
	case POS_SITTING:
	    send_to_char("You are already sitting down.\n\r",ch);
	    break;
	case POS_FIGHTING:
	    send_to_char("Maybe you should finish this fight first?\n\r",ch);
	    break;
	case POS_STANDING:
	    send_to_char("You sit down.\n\r",ch);
	    act("$n sits down on the ground.\n\r",ch,NULL,NULL,TO_ROOM);
	    ch->position = POS_SITTING;
	    break;
    }
    return;
}


void do_sleep( CHAR_DATA *ch, char *argument )
{
    switch ( ch->position )
    {
    case POS_SLEEPING:
	send_to_char( "You are already sleeping.\n\r", ch );
	break;

    case POS_RESTING:
    case POS_SITTING:
    case POS_STANDING: 
	send_to_char( "You go to sleep.\n\r", ch );
	act( "$n goes to sleep.", ch, NULL, NULL, TO_ROOM );
	ch->position = POS_SLEEPING;
	break;

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

    return;
}

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

    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 ) ) == NULL )
	{ 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( "You can't wake $M!",   ch, NULL, victim, TO_CHAR );  return; }

    victim->position = POS_STANDING;
    act( "You wake $M.", ch, NULL, victim, TO_CHAR );
    act( "$n wakes you.", ch, NULL, victim, TO_VICT );
    return;
}



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

    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] )
    {
	check_improve(ch,gsn_sneak,TRUE,3);
	af.type      = gsn_sneak;
	af.level     = ch->level; 
	af.duration  = ch->level;
	af.location  = APPLY_NONE;
	af.modifier  = 0;
	af.bitvector = AFF_SNEAK;
	affect_to_char( ch, &af );
    }
    else
	check_improve(ch,gsn_sneak,FALSE,3);

    return;
}



void do_hide( CHAR_DATA *ch, char *argument )
{
    send_to_char( "You attempt to hide.\n\r", ch );

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

    if ( IS_NPC(ch) || number_percent( ) < ch->pcdata->learned[gsn_hide] )
    {
	SET_BIT(ch->affected_by, AFF_HIDE);
	check_improve(ch,gsn_hide,TRUE,3);
    }
    else
	check_improve(ch,gsn_hide,FALSE,3);

    return;
}



/*
 * Contributed by Alander.
 */
void do_visible( CHAR_DATA *ch, char *argument )
{
    affect_strip ( ch, gsn_invis			);
    affect_strip ( ch, gsn_mass_invis			);
    affect_strip ( ch, gsn_sneak			);
    REMOVE_BIT   ( ch->affected_by, AFF_HIDE		);
    REMOVE_BIT   ( ch->affected_by, AFF_INVISIBLE	);
    REMOVE_BIT   ( ch->affected_by, AFF_SNEAK		);
	if (IS_AFFECTED (ch, AFF_POLY))
		{
		affect_strip ( ch, skill_lookup ("mask self"));
		undo_mask	 (ch);
		REMOVE_BIT	 ( ch->affected_by, AFF_POLY );
		}
    send_to_char( "Ok.\n\r", ch );
    return;
}

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

    if (IS_NPC(ch) && (!IS_SET(ch->act,ACT_PET) 
	|| !IS_SET(ch->act,ACT_FOLLOWER) ) )
    {
	send_to_char("Only players can recall.\n\r",ch);
	return;
    }
  
    act( "$n prays for transportation!", ch, 0, 0, TO_ROOM );

    if ( ( location = get_room_index( ch->recall_perm ) ) == NULL )
    {
	send_to_char( "You are completely lost.\n\r", ch );
	return;
    }

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

    if ( IS_SET(ch->in_room->room_flags, ROOM_NO_RECALL)
    ||   IS_AFFECTED(ch, AFF_CURSE) )
    {
	send_to_char( "Zeran has forsaken you.\n\r", ch );
	return;
    }

    if IS_AFFECTED(ch, AFF_MELD)
    {
	send_to_char( "You cannot concentrate enough to pray.\n\r", ch );
	return;
    }

    if ( ( victim = ch->fighting ) != NULL )
    {
	int lose,skill;

	if (IS_NPC(ch))
	    skill = 40 + ch->level;
	else
	    skill = ch->pcdata->learned[gsn_recall];

	if ( number_percent() < 80 * skill / 100 )
	{
	    check_improve(ch,gsn_recall,FALSE,6);
	    WAIT_STATE( ch, 4 );
	    sprintf( buf, "You failed!.\n\r");
	    send_to_char( buf, ch );
	    return;
	}

	lose = (ch->desc != NULL) ? 25 : 50;
	gain_exp( ch, 0 - lose );
	check_improve(ch,gsn_recall,TRUE,4);
	sprintf( buf, "You recall from combat!  You lose %d exps.\n\r", lose );
	send_to_char( buf, ch );
	stop_fighting( ch, TRUE );
	
    }

    ch->move /= 2;
    act( "$n disappears.", ch, NULL, NULL, TO_ROOM );
    char_from_room( ch );
    char_to_room( ch, location );
    act( "$n appears in the room.", ch, NULL, NULL, TO_ROOM );
    do_look( ch, "auto" );
    
    if (ch->pet != NULL)
	do_recall(ch->pet,"");

    return;
}

void do_enter (CHAR_DATA *ch, char *argument)
	{
	OBJ_DATA *portal;
	OBJ_DATA *tmp=NULL;
	ROOM_INDEX_DATA *dest;
	char buf[MAX_STRING_LENGTH];
    char arg1[MAX_INPUT_LENGTH];
	int room;
	bool pet=FALSE;
	bool link_obj=FALSE;

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

	/* check fighting */	
	if (ch->fighting != NULL)
		{
		send_to_char ("Not while you're fighting!\n\r", ch);
		return;
		}

	/* get portal name */	
	one_argument (argument, arg1);
	
	if ((portal = get_obj_here (ch, arg1)) == NULL)	
		{
		sprintf (buf, "You see no %s here.\n\r", arg1);
		send_to_char (buf, ch);
		return;
		}

	if (portal->item_type != ITEM_PORTAL)
		{
		send_to_char ("You can't enter that.\n\r", ch);
		return;
		}
	
	if (portal->carried_by != NULL)
		{
		sprintf (buf, "You have to put %s on the ground first.\n\r", portal->short_descr);
		send_to_char (buf, ch);
		return;
		}	

	/* check level of portal vs level of character */	
	if (get_trust(ch) < portal->level)
		{
		sprintf (buf, "You step into %s, but it throws you back out!\n\r", portal->short_descr) ;
		send_to_char (buf, ch);
		return;
		}
	
	/* validate portal destination */
	room = portal->value[0];

	if ((dest = get_room_index(room)) == NULL)
		{
		sprintf (buf, "Bad portal room vnum %d", room);
		bug (buf, 0);
		send_to_char ("Bad portal, please inform the Imp.\n\r", ch);
		return;
		}
	
	/* Running with assumption that either the builder made the portal			
		sensibly, or else created via the spell, in which case, its
		rules should have handled things like private rooms and such.
		Shouldn't need to check those cases here.
	*/
	
	/* determination if link object exists in destination room */
	if (portal->value[1] == 0)
		link_obj = FALSE;	
	else if (dest->contents == NULL)
		link_obj = FALSE;
	else
		{
		for ( tmp = dest->contents; tmp != NULL; tmp = tmp->next_content)
			{
			if (tmp->pIndexData->vnum == portal->value[1])
				{
				link_obj = TRUE;
				break;
				}
			}
		}
	
	if (ch->pet != NULL && ch->in_room == ch->pet->in_room)
		pet=TRUE;
	
	send_to_char ("Time and space unravel before your eyes!\n\r", ch);
    act("$n steps into $P and {cvanishes{x!",ch,NULL,portal,TO_ROOM);
	char_from_room (ch);
	char_to_room(ch, dest);
	if (link_obj)
		{
		act("An ear piercing whine fills the room....",ch,NULL,NULL,TO_ROOM);
   		act("$n steps out of $P!",ch,NULL,tmp,TO_ROOM);
   		act("You step out of $P into $t!",ch,dest->name,tmp,TO_CHAR);
		}
	else
		{
		act("An ear piercing whine fills the room....",ch,NULL,NULL,TO_ROOM);
   		act("A rip in time and space appears and $n steps forth from it!",ch,NULL,NULL,TO_ROOM);
   		act("You step out of a rip in time and space into $t!",ch,dest->name,tmp,TO_CHAR);
		}
	do_look (ch, "auto"); 

    if (pet)
    	{
    	act("$n steps into $P and {cvanishes{x!",ch->pet,NULL,portal,TO_ROOM);
    	send_to_char("Time and space unravel before your eyes!\n\r",ch->pet);
    	char_from_room(ch->pet);
    	char_to_room(ch->pet,dest);
		if (link_obj)
			{
   			act("$n steps out of $P!",ch->pet,NULL,tmp,TO_ROOM);
   			act("You step out of $P into $t!",ch->pet,dest->name,tmp,TO_CHAR);
			}
		else
			{
   			act("$n steps forth from the rip.",ch->pet,NULL,NULL,TO_ROOM);
   			act("You step out of a rip in time and space into another room!",ch->pet,NULL,tmp,TO_CHAR);
			}
    	do_look(ch->pet,"auto");
    	}
	
	return;
	}