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.					 ||
    || ----------------------------------------------------------------- ||
    ||                          mob_commands.c                           ||
    || Special commands for use in MUDPrograms.  Based on N'Atas-ha's    ||
    || MOBPrograms and SMAUG's MUDPrograms.                              ||
 *_/<>\_________________________________________________________________/<>\_*/


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

/*
 * Local functions.
 */
void mp_force_sub args( ( CHAR_DATA *ch, const char *argument, bool quiet ) );


/*
 * Display the MudPrograms and variables present.
 */
void append_progs( BUFFER *buf, MPROG_DATA *mprg, const char *argument,
		   MPROG_VAR *var )
{
    int num = 0;
    int argnum = -1;
    bool sh = FALSE;

    if( argument[0] != '\0' )
    {
	if( is_number( argument ) )
	    argnum = atoi( argument );
	else if( !str_cmp( argument, "short" ) )
	    sh = TRUE;
	else
	    sh = TRUE;
    }
    for( ; mprg; mprg = mprg->next, num++ )
    {
	if( argnum >= 0 && num != argnum )
	    continue;

	bprintf( buf, "[%3d] %-20s", num,
		 flag_string( mud_prog_flags, &mprg->type ) );
	bprintf( buf, "arg-[%s]\n\r", mprg->arglist );

	if( sh )
	    continue;

	bprintf( buf, "%s\n\r", mprg->comlist );
	if( argnum >= 0 )
	    break;
    }
    num = 0;
    for( ; argnum < 0 && var; var = var->next )
    {
	if( num++ == 0 )
	    buffer_strcat( buf, "Variables:\n\r" );
	bprintf( buf, "%s = %s\n\r", var->name, var->value );
    }
    if( argnum >= 0 && !mprg )
    {
	buffer_clear( buf );
	buffer_strcat( buf, "No such Program found.\n\r" );
    }
}


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

    argument = one_argument( argument, arg );

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

    if( ( victim = get_char_world( ch, arg ) ) == NULL )
    {
	send_to_char( "They aren't here.\n\r", ch );
	return;
    }

    if( !IS_NPC( victim ) )
    {
	send_to_char( "Only Mobiles can have Programs!\n\r", ch );
	return;
    }

    if( !( victim->pIndexData->mudprogs ) )
    {
	send_to_char( "That Mobile has no Programs set.\n\r", ch );
	return;
    }
    buf = buffer_new( MAX_STRING_LENGTH );

    bprintf( buf, "Name: %s.  Vnum: %d.\n\r",
	     victim->name, victim->pIndexData->vnum );

    bprintf( buf, "Short description: %s.\n\rLong  description: %s",
	     victim->short_descr,
	     victim->long_descr[0] != '\0' ?
	     victim->long_descr : "(none).\n\r" );

    bprintf( buf, "Hp: %d/%d.  Mana: %d/%d.  Move: %d/%d. \n\r",
	     victim->hit, victim->max_hit,
	     total_mana( victim->mana ), total_mana( victim->max_mana ),
	     victim->move, victim->max_move );

    bprintf( buf, "Lv: %d.  Class: %d.  Align: %d.  Gold: %d.\n\r",
	     victim->level, victim->class, victim->alignment,
	     victim->gold );

    append_progs( buf, victim->pIndexData->mudprogs, argument,
		  victim->variables );
    send_to_char( buf->data, ch );
    buffer_free( buf );

    return;
}


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

    argument = one_argument( argument, arg );

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

    if( ( obj = get_obj_here( ch, arg ) ) == NULL )
    {
	send_to_char( "That isn't here.\n\r", ch );
	return;
    }

    if( !( obj->pIndexData->mudprogs ) )
    {
	send_to_char( "That Object has no Programs set.\n\r", ch );
	return;
    }

    buf = buffer_new( MAX_STRING_LENGTH );

    bprintf( buf, "Name: %s.  Vnum: %d.	 Level: %d.\n\r",
		       obj->name, obj->pIndexData->vnum, obj->level );
    bprintf( buf, "Short description: %s.\n\r",
	     obj->short_descr );

    append_progs( buf, obj->pIndexData->mudprogs, argument,
		  obj->variables );
    send_to_char( buf->data, ch );
    buffer_free( buf );

    return;
}


void do_rpstat( CHAR_DATA *ch, const char *argument )
{
    BUFFER *buf;
    ROOM_INDEX_DATA *room;

    room = ch->in_room;

    if( !( room->mudprogs ) )
    {
	send_to_char( "This room has no Programs set.\n\r", ch );
	return;
    }

    buf = buffer_new( MAX_STRING_LENGTH );

    bprintf( buf, "Name: %s.  Vnum: %d.\n\r", room->name, room->vnum );

    append_progs( buf, room->mudprogs, argument, room->variables );
    send_to_char( buf->data, ch );
    buffer_free( buf );

    return;
}


/*
 * This command allows access to all the funky commands in this file
 * through the mudprog interpreter.  It is important to note that the
 * check against players using this is on descriptor.  This has the
 * benefit of allowing mobprograms, through mp_qforce, make players run
 * these commands, huge flexibility advantages, no loopholes forseen, yet!
 *
 * However,
 *	Utmost care at all times please.	--Symposium
 */
void do_mpcomm( CHAR_DATA *ch, const char *argument )
{
    if( ch->desc )
    {
	send_to_char( "You commune with the mobiles, how boring.\n\r", ch );
	return;
    }

    if( !prog_only_cmnd( ch, argument ) )
	progbug( ch, "bad mpcommand" );
    return;
}


/* prints the message to the room at large */
void mp_aecho( CHAR_DATA *ch, const char *argument )
{
    DESCRIPTOR_DATA *d;

    if( argument[0] == '\0' )
    {
	progbug( ch, "aecho - called w/o argument." );
	return;
    }

    for( d = descriptor_list; d; d = d->next )
    {
	if( !IS_SET( d->interpreter->flags, INTERPRETER_NOMESSAGE )
	    && ch->in_room->area == d->character->in_room->area
	    && IS_AWAKE( d->character ) )
	    act( argument, ch, NULL, d->character, TO_VICT );
    }
    return;
}


/* prints the argument to all the rooms aroud the mobile */
void mp_asound( CHAR_DATA *ch, const char *argument )
{
    ROOM_INDEX_DATA *was_in_room;
    int door;

    if( argument[0] == '\0' )
    {
	progbug( ch, "Mpasound - No argument." );
	return;
    }

    was_in_room = ch->in_room;
    for( door = 0; door <= 5; door++ )
    {
	EXIT_DATA *pexit;

	if( ( pexit = was_in_room->exit[door] ) != NULL
	    && pexit->to_room != NULL
	    && pexit->to_room != was_in_room )
	{
	    ch->in_room = pexit->to_room;
	    REMOVE_BIT( SysInfo->flags, SYSINFO_ACT_TRIGGER );
	    act( argument, ch, NULL, NULL, TO_ROOM );
	}
    }

    ch->in_room = was_in_room;
    return;
}


/* Lets the mobile do a command at another location. Very useful */
void mp_at( CHAR_DATA *ch, const char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    ROOM_INDEX_DATA *location;
    ROOM_INDEX_DATA *original;
    OBJ_DATA *orig_on;
    CHAR_DATA *wch;

    argument = one_argument( argument, arg );

    if( arg[0] == '\0' || argument[0] == '\0' )
    {
	progbug( ch, "Mpat - Bad argument: %s %s.", arg, argument );
	return;
    }

    if( ( location = find_location( ch, arg ) ) == NULL )
    {
	progbug( ch, "Mpat - No such location: %s.", arg );
	return;
    }

    original = ch->in_room;
    orig_on = ch->on;
    char_from_room( ch );
    char_to_room( ch, location );
    if( !prog_only_cmnd( ch, argument ) )
	interpret( ch, argument );

    /*
     * See if 'ch' still exists before continuing!
     * Handles 'at XXXX quit' case.
     */
    for( wch = char_list; wch != NULL; wch = wch->next )
    {
	if( wch == ch && !wch->deleted )
	{
	    char_from_room( ch );
	    char_to_room( ch, original );
	    ch->on = orig_on;
	    break;
	}
    }

    return;
}


/* Hides the mobile quite effectively, mobile only */
void mp_burrow( CHAR_DATA *ch, const char *argument )
{
    if( IS_NPC( ch ) )
	xSET_BIT( ch->act, ACT_BURIED );
}


void mp_damage( CHAR_DATA *ch, const char *argument )
{
    char buf[MAX_INPUT_LENGTH];
    char arg1[MAX_INPUT_LENGTH];
    CHAR_DATA *victim;
    CHAR_DATA *nextinroom;
    int dam;
    int already_fighting = FALSE;

    argument = one_argument( argument, arg1 );
    if ( !str_cmp( arg1, "all" ) )
    {
	for( victim = ch->in_room->people; victim; victim = nextinroom )
	{
	    nextinroom = victim->next_in_room;
	    if( victim != ch && can_see( ch, victim ) )
	    {
		sprintf( buf, "'%s' %s", name_expand( arg1, victim ),
			 argument );
		mp_damage( ch, buf );
	    }
	}
	return;
    }
    if( argument[0] == '\0' )
    {
	progbug( ch, "Mpdamage: no damage amount given." );
	return;
    }
    if( ( victim = get_char_room( ch, arg1 ) ) == NULL )
    {
	progbug( ch, "Mpdamage: victim not in room." );
	return;
    }
    if( victim == ch )
    {
	progbug( ch, "Mpdamage: trying to damage self." );
	return;
    }
    dam = atoi( argument );
    if( ( dam < 0) || ( dam > 32000 ) )
    {
	progbug( ch, "Mpdamage: invalid (nonexistent?) argument: %s" );
	return;
    }
    if( ch->fighting )
	already_fighting = TRUE;
    damage( ch, victim, dam, TYPE_MPDAMAGE, WEAR_NONE );
    if( already_fighting )
	stop_fighting( ch, TRUE );
    return;
}


/*
 * Either lag PC characters (not all that useful).
 * or delay an NPC that will allow it to trigger a delay_prog.
 */
void mp_delay( CHAR_DATA *ch, const char *argument )
{
    CHAR_DATA *victim;
    char arg[MAX_INPUT_LENGTH];
    int val;
    EVENT *e;

    argument = one_argument( argument, arg );
    if( !*arg )
	victim = ch;
    else if( !( victim = get_char_world( ch, arg ) ) )
	return;

    val = atoi( argument );
    if( val < 0 )
    {
	progbug( ch, "Bad mp_delay argument: %s %s.", arg, argument );
	return;
    }

    if( IS_NPC( victim ) )
    {
	if( victim->pIndexData->vnum == MOB_VNUM_SUPERMOB )
	{
	    progbug( ch, "Trying to delay supermob.", arg, argument );
	    return;
	}

	if( xIS_SET( victim->pIndexData->progtypes, DELAY_PROG ) )
	{
	    e = create_char_event( victim, evn_prog_trigger,
				   val );
	    e->data[0] = DELAY_PROG;
	}
    }

    victim->wait = val;
    return;
}


/*
 * Deposit some gold into the current area's economy            -Thoric
 */
void mp_deposit( CHAR_DATA *ch, const char *argument )
{
    int gold;

    if( argument[0] == '\0' )
    {
	progbug( ch, "deposit - bad syntax" );
	return;
    }
    gold = atoi( argument );
    if( gold <= ch->gold && ch->in_room )
    {
	ch->gold -= gold;
	ch->in_room->area->economy += gold;
    }
}


/*
 * Withdraw some gold from the current area's economy           -Thoric
 */
void mp_withdraw( CHAR_DATA *ch, const char *argument )
{
    int gold;

    if( argument[0] == '\0' )
    {
	progbug( ch, "withdraw - bad syntax" );
	return;
    }
    gold = atoi( argument );
    if( ch->gold < 1000000000 && gold < 1000000000 && ch->in_room
	 && ch->in_room->area->economy >= gold )
    {
	ch->gold += gold;
	ch->in_room->area -= gold;
    }
}


void mp_dream( CHAR_DATA *ch, const char *argument )
{
    char arg1[MAX_STRING_LENGTH];
    CHAR_DATA *vict;

    argument = one_argument( argument, arg1 );

    if( ( vict = get_char_world( ch, arg1 ) ) == NULL )
    {
	progbug( ch, "Mpdream: No such character" );
	return;
    }

    if( vict->position <= POS_SLEEPING )
    {
	send_to_char( argument, vict );
	send_to_char( "\n\r", vict );
    }
    return;
}


/* prints the message to the room at large */
void mp_echo( CHAR_DATA *ch, const char *argument )
{
    if( argument[0] == '\0' )
    {
	progbug( ch, "echo - called w/o argument." );
	return;
    }

    act( argument, ch, NULL, NULL, TO_ROOM );
    return;
}


/* prints the message to everyone in the room other than the mob and victim */
void mp_echoaround( CHAR_DATA *ch, const char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    CHAR_DATA *victim;

    argument = one_argument( argument, arg );

    if( arg[0] == '\0' )
    {
	progbug( ch, "echoaround - No argument." );
	return;
    }

    if( !( victim = get_char_room( ch, arg ) ) )
    {
	progbug( ch, "echoaround - victim does not exist: %s.", arg );
	return;
    }

    act( argument, ch, NULL, victim, TO_NOTVICT );
    return;
}


/* prints the message to only the victim */
void mp_echoat( CHAR_DATA *ch, const char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    CHAR_DATA *victim;

    argument = one_argument( argument, arg );

    if( arg[0] == '\0' || argument[0] == '\0' )
    {
	progbug( ch, "echoat - No argument." );
	return;
    }

    if( !( victim = get_char_room( ch, arg ) ) )
    {
	progbug( ch, "echoat - victim does not exist: %s.", arg );
	return;
    }

    act( argument, ch, NULL, victim, TO_VICT );
    return;
}


void mp_force_sub( CHAR_DATA *ch, const char *argument, bool quiet )
{
    char arg[MAX_INPUT_LENGTH];
    DESCRIPTOR_DATA *d;

    argument = one_argument( argument, arg );

    if( arg[0] == '\0' || argument[0] == '\0' )
    {
	progbug( ch, "Mp[q]force - Bad syntax: %s %s.",arg, argument );
	return;
    }

    if( !str_cmp( arg, "all" ) )
    {
	CHAR_DATA *vch;
	CHAR_DATA *vch_next;

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

	    if( vch->in_room == ch->in_room
		&& get_trust( vch ) < get_trust( ch )
		&& can_see( ch, vch ) )
	    {
		d = vch->desc;
		if( quiet )
		    vch->desc = NULL;
		if( !quiet || !prog_only_cmnd( vch, argument ) )
		    interpret( vch, argument );
		vch->desc = d;
	    }
	}
    }
    else
    {
	CHAR_DATA *victim;

	if( ( victim = get_char_room( ch, arg ) ) == NULL )
	{
	    progbug( ch, "Mp[q]force - No such victim." );
	    return;
	}

	if( victim == ch )
	{
	    progbug( ch, "Mp[q]force - Forcing oneself." );
	    return;
	}

	d = victim->desc;
	if( quiet )
	    victim->desc = NULL;
	if( !quiet || !prog_only_cmnd( victim, argument ) )
	    interpret( victim, argument );
	victim->desc = d;
    }

    return;
}


/*
 * Lets the mobile force someone to do something.  Must be mortal level
 * and the all argument only affects those in the room with the mobile.
 *
 * Note: additionally this force command allows the character access to
 * the extra commands commands in this file, either through mp_comm or
 * directly.
 * --Symposium
 */
void mp_qforce( CHAR_DATA *ch, const char *argument )
{
    mp_force_sub( ch, argument, TRUE );
}


/* Standard force, no tricks allowed */
void mp_force( CHAR_DATA *ch, const char *argument )
{
    mp_force_sub( ch, argument, FALSE );
}


/* Lets the mobile goto any location it wishes that is not private */
void mp_goto( CHAR_DATA *ch, const char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    ROOM_INDEX_DATA *location;

    one_argument( argument, arg );
    if( arg[0] == '\0' )
    {
	progbug( ch, "Mpgoto - No argument." );
	return;
    }

    if( ( location = find_location( ch, arg ) ) == NULL )
    {
	progbug( ch, "Mpgoto - No such location: %s.", arg );
	return;
    }

    if( ch->fighting != NULL )
	stop_fighting( ch, TRUE );

    char_from_room( ch );
    char_to_room( ch, location );

    return;
}


void mp_grant( CHAR_DATA *ch, const char *argument )
{
    char arg1[MAX_INPUT_LENGTH];
    CHAR_DATA *victim;
    int num;

    argument = one_argument( argument, arg1 );
    if( argument[0] == '\0' || !is_number( argument ) )
    {
	progbug( ch, "grant: called with no arguments" );
	return;
    }
    if( !( victim = get_char_world( ch, arg1 ) ) )
    {
	progbug( ch, "grant: can't find player to grant." );
	return;
    }
    num = atoi_special( argument ) * 100;
    sprintf( arg1, "%s has granted you %d experience!\n\r",
	    ch->name, num / 100 );
    wiznetf( ch, WIZ_LEVELS, get_trust( ch ),
	     "%s has just granted %s %d experience (mp_grant).",
	     ch->name, victim->name, num / 100 );

    send_to_char( arg1, victim );
    gain_exp( victim, num );
    return;
}


/* lets the mobile destroy an object in its inventory
   it can also destroy a worn object and it can destroy
   items using all.xxxxx or just plain all of them */
void mp_junk( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    OBJ_DATA *obj_next;

    if( argument[0] == '\0' )
    {
	progbug( ch, "Mpjunk - No argument." );
	return;
    }

    if( str_cmp( argument, "all" ) && str_prefix( "all.", argument ) )
    {
	if( ( obj = get_obj_wear( ch, argument ) ) != NULL )
	{
	    unequip_char( ch, obj );
	    extract_obj( obj );
	    return;
	}
	if( ( obj = get_obj_carry( ch, argument ) ) == NULL )
	    return;
	extract_obj( obj );
    }
    else
	for( obj = ch->carrying; obj != NULL; obj = obj_next )
	{
	    obj_next = obj->next_content;
	    if( argument[3] == '\0' || is_obj_name( obj, &argument[4] ) )
	    {
		if( obj->wear_loc != WEAR_NONE )
		    unequip_char( ch, obj );
		extract_obj( obj );
	    }
	}

    return;
}


/* lets the mobile kill any player or mobile without murder */
void mp_kill( CHAR_DATA *ch, const char *argument )
{
    CHAR_DATA *victim;

    if( argument[0] == '\0' )
    {
	progbug( ch, "MpKill - no argument." );
	return;
    }

    if( ( victim = get_char_room( ch, argument ) ) == NULL )
    {
	progbug( ch, "MpKill - Victim not in room: %s.", argument );
	return;
    }

    if( victim == ch )
    {
	progbug( ch, "MpKill - Bad victim to attack, self." );
	return;
    }

    if( IS_AFFECTED( ch, AFF_CHARM ) && ch->master == victim )
    {
	progbug( ch, "MpKill - Charmed mob attacking master: %s.",
		       ch->master->name );
	return;
    }

    if( ch->position == POS_FIGHTING )
    {
	progbug( ch, "MpKill - Already fighting." );
	return;
    }

    multi_hit( ch, victim, TYPE_UNDEFINED );
    return;
}


/* lets the mobile load an item or mobile.  All items
   are loaded into inventory.  you can specify a level with
   the load object portion as well. */
void mp_mload( CHAR_DATA *ch, const char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    MOB_INDEX_DATA *pMobIndex;
    CHAR_DATA *victim;

    one_argument( argument, arg );

    if( arg[0] == '\0' || !is_number( arg ) )
    {
	progbug( ch, "Mpmload - Bad vnum as arg: %s.", arg );
	return;
    }

    if( ( pMobIndex = get_mob_index( atoi( arg ) ) ) == NULL )
    {
	progbug( ch, "Mpmload - Bad mob vnum: %s.", arg );
	return;
    }

    victim = create_mobile( pMobIndex );
    char_to_room( victim, ch->in_room );
    return;
}


/*
 * Trimmed down version of mset.
 */
void mp_mset( CHAR_DATA *ch, const char *argument )
{
    CHAR_DATA *vict;
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    char arg3[MAX_INPUT_LENGTH];
    int value;
    int *ival = NULL;
    char **sval = NULL;

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

    if( !( vict = get_char_world( ch, arg1 ) ) )
    {
	progbug( ch, "mset: non-existant character %s.", arg1 );
	return;
    }

    value = atoi_functions( arg3 );

    if( !str_cmp( arg2, "short" ) )
	sval = &vict->short_descr;
    else if( !str_cmp( arg2, "long" ) )
    {
	strcat( arg3, "\n\r" );
	sval = &vict->long_descr;
    }
    else if( !str_cmp( arg2, "title" ) )
    {
	set_title( vict, arg3 );
	return;
    }
    else if( !str_cmp( arg2, "class" ) )
    {
	if( value < 0 || value >= MAX_CLASS )
	{
	    for( value = 0; value < MAX_CLASS; value++ )
	    {
		if( !str_cmp( arg3, class_table[value].who_name )
		    || !str_cmp( arg3, class_table[value].name ) )
		    break;
	    }

	    if( value >= MAX_CLASS )
	    {
		progbug( ch, "mset: invalid class." );
		return;
	    }
	    vict->class = value;
	    return;
	}
	if( value == -1 && !IS_NPC( ch ) )
	{
	    progbug( ch, "mset: invalid class 'none' on a PC." );
	    return;
	}
	vict->class = value;
	return;
    }
    else if( !str_cmp( arg2, "sex" ) )
    {
	if( IS_AFFECTED( vict, AFF_POLYMORPH ) )
	    return;

	if( value < 0 || value > 2 )
	{
	    if( !str_cmp( arg3, "male" ) )
		vict->sex = SEX_MALE;
	    else if( !str_cmp( arg3, "female" ) )
		vict->sex = SEX_FEMALE;
	    else if( !str_cmp( arg3, "neuter" ) )
		vict->sex = SEX_NEUTRAL;
	    else
		progbug( ch, "mset: invalid sex." );
	    return;
	}

	vict->sex = value;
	return;
    }
    else if( !str_cmp( arg2, "race" ) )
    {
	if( IS_AFFECTED( vict, AFF_POLYMORPH ) )
	    return;
	value = race_lookup( arg3 );

	if( value < 0 )
	{
	    progbug( ch, "mset: invalid race." );
	    return;
	}
	vict->race = value;
	return;
    }
    else if( !str_cmp( arg2, "sublevel" ) )
    {
	if( value < 0 || value > vict->level )
	{
	    progbug( ch, "mset: sublevel out of range." );
	    return;
	}
	vict->sublevel = value;
	return;
    }
    else if( !str_cmp( arg2, "gold" ) )
	ival = &vict->gold;
    else if( !str_cmp( arg2, "hp" ) )
	ival = &vict->hit;
    else if( !str_cmp( arg2, "maxhp" ) )
	ival = &vict->max_hit;
    else if( !str_prefix( "mana", arg2 ) || !str_prefix( "maxmana", arg2 ) )
    {
	int which;
	argument = &arg2[4];
	if( !str_prefix( "max", arg2 ) )
	    argument += 3;
	while( isspace( *argument ) )
	    argument++;
	if( !str_cmp( argument, "air" ) )
	    which = MAGIC_AIR;
	else if( !str_cmp( argument, "earth" ) )
	    which = MAGIC_EARTH;
	else if( !str_cmp( argument, "fire" ) )
	    which = MAGIC_FIRE;
	else if( !str_cmp( argument, "spirit" ) )
	    which = MAGIC_SPIRIT;
	else if( !str_cmp( argument, "water" ) )
	    which = MAGIC_WATER;
	else
	{
	    progbug( ch, "mset: bad magic sphere." );
	    return;
	}
	if( !str_prefix( "max", arg2 ) )
	    ival = &vict->max_mana[which];
	else
	    ival = &vict->mana[which];
    }
    else if( !str_cmp( arg2, "move" ) )
	ival = &vict->move;
    else if( !str_cmp( arg2, "maxmove" ) )
	ival = &vict->max_move;
    else if( !str_cmp( arg2, "practice" ) )
	ival = &vict->practice;
    else if( !str_cmp( arg2, "align" ) )
    {
	if( value < -1000 || value > 1000 )
	    return;
	ival = &vict->alignment;
    }
    if( !IS_NPC( vict ) )
    {
	if( !str_cmp( arg2, "name" ) )
	    sval = &vict->name;
	else if( !str_cmp( arg2, "str" ) )
	    ival = &vict->pcdata->perm_str;
	else if( !str_cmp( arg2, "int" ) )
	    ival = &vict->pcdata->perm_int;
	else if( !str_cmp( arg2, "wis" ) )
	    ival = &vict->pcdata->perm_wis;
	else if( !str_cmp( arg2, "dex" ) )
	    ival = &vict->pcdata->perm_dex;
	else if( !str_cmp( arg2, "con" ) )
	    ival = &vict->pcdata->perm_con;
	else if( !str_cmp( arg2, "multi" ) )
	{
	    char classbuf[ MAX_INPUT_LENGTH ];

	    if( !str_cmp( argument, "all clear" ) )
	    {
		vict->class = get_first_class( vict );
		for( value = 0; value < AVAIL_CLASS; value++ )
		    vict->pcdata->multi_class[value] = CLASS_UNKNOWN;
		return;
	    }

	    argument = one_argument( argument, classbuf );
	    for( value = 0; value < AVAIL_CLASS; value++ )
		if( !str_cmp( classbuf, class_table[value].who_name )
		    || !str_cmp( classbuf, class_table[value].name ) )
		    break;
	    if( value >= AVAIL_CLASS )
	    {
		progbug( ch, "mset multi: bad class name %s", classbuf );
		return;
	    }

	    if( !str_cmp( argument, "clear" ) )
	    {
		if( vict->pcdata->multi_class[value] > CLASS_ADEPT )
		{
		    progbug( ch, "mset multi clear: bad args" );
		    return;
		}
		vict->pcdata->multi_class[value] = CLASS_UNKNOWN;
		if( vict->sublevel )
		    vict->sublevel = 0;
		return;
	    }
	    if( !str_cmp( argument, "aspire" ) )
	    {
		if( get_aspire_class( vict ) >= 0 )
		{
		    progbug( ch, "mset multi aspire: bad args" );
		    return;
		}
		vict->pcdata->multi_class[value] = CLASS_ASPIRING;
		return;
	    }
	    if( !str_cmp( argument, "adept" ) )
	    {
		if( vict->pcdata->multi_class[value] > CLASS_ADEPT )
		{
		    progbug( ch, "mset multi adept: bad args" );
		    return;
		}
		vict->pcdata->multi_class[value] = CLASS_ADEPT;
		return;
	    }

	    progbug( ch, "mset multi: bad args" );
	    return;
	}
	else if( !str_cmp( arg2, "qscore" ) )
	    ival = &vict->pcdata->quest->score;
	else if( !str_cmp( arg2, "qtime" ) )
	    ival = &vict->pcdata->quest->time;
	else if( !str_prefix( "magic", arg2 ) )
	{
	    int which;
	    argument = &arg2[4];
	    while( isspace( *argument ) )
		argument++;
	    if( !str_cmp( argument, "air" ) )
		which = MAGIC_AIR;
	    else if( !str_cmp( argument, "earth" ) )
		which = MAGIC_EARTH;
	    else if( !str_cmp( argument, "fire" ) )
		which = MAGIC_FIRE;
	    else if( !str_cmp( argument, "spirit" ) )
		which = MAGIC_SPIRIT;
	    else if( !str_cmp( argument, "water" ) )
		which = MAGIC_WATER;
	    else
	    {
		progbug( ch, "mset magic: bad magic sphere." );
		return;
	    }
	    ival = &vict->pcdata->perm_magic[which];
	}
	else if( !str_cmp( arg2, "thirst" ) )
	{
	    strcat( arg3, "0" );
	    ival = &vict->pcdata->condition[COND_THIRST];
	}
	else if( !str_cmp( arg2, "full" ) )
	{
	    strcat( arg3, "0" );
	    ival = &vict->pcdata->condition[COND_FULL];
	}
	else if( !str_cmp( arg2, "drunk" ) )
	{
	    strcat( arg3, "0" );
	    ival = &vict->pcdata->condition[COND_DRUNK];
	}
	else  if( !str_cmp( arg2, "clan" ) )
	{
	    CLAN_DATA *clan;

	    if( !str_cmp( arg3, "none" ) )
	    {
		if( is_clan( vict ) )
		    remove_from_clan( vict );
		return;
	    }
	    if( !( clan = clan_lookup( arg3 ) ) )
	    {
		progbug( ch, "mset clan: non-existant clan." );
		return;
	    }

	    if( vict->pcdata->clan == clan )
		return;
	    if( is_clan( vict ) )
		remove_from_clan( vict );
	    add_to_clan( vict, clan, RANK_CLANSMAN );
	    return;
	}
	else if( !str_cmp( arg2, "religion" ) )
	{
	    RELIGION_DATA *religion;

	    if ( !( religion = religion_lookup( arg3 ) ) )
	    {
		progbug( ch, "mset religion: invalid religion." );
		return;
	    }

	    vict->pcdata->religion = religion;
	    return;
	}
	else if( !str_cmp( arg2, "clanrank" ) )
	{
	    CLAN_DATA *clan;

	    if( !( clan = vict->pcdata->clan ) )
	    {
		progbug( ch, "mset clanrank: vict not in clan." );
		return;
	    }

	    if( value < RANK_EXILED )
	    {
		remove_from_clan( vict );
		return;
	    }
	    if( value > RANK_OVERLORD )
	    {
		progbug( ch, "mset clanrank: bad clan rank %d.", value );
		return;
	    }
	    if( vict->pcdata->clan_rank == value )
		return;
	    switch( value )
	    {
	    default:
		break;
	    case RANK_CLANHERO:
		if( clan->clanheros >= clan->members / 3 )
		{
		    progbug( ch, "mset clanrank: too many heroes." );
		    return;
		}
		break;
	    case RANK_CHIEFTAIN:
		if( clan->members < 10 )
		{
		    progbug( ch, "mset clanrank: too many cheiftans." );
		    return;
		}
		break;
	    case RANK_OVERLORD:
		if( clan->overlord != NULL && clan->overlord[0] != '\0' )
		{
		    progbug( ch, "mest clanrank: already an overlord." );
		    return;
		}
		break;
	    }
	    remove_from_clan( vict );
	    add_to_clan( vict, clan, value );
	    return;
	}
    }
    else	/* IS_NPC(vict) */
    {
	if( !str_cmp( arg2, "name" ) )
	    sval = &vict->name;
	else if( !str_cmp( arg2, "level" ) )
	    ival = &vict->level;
	else if( !str_cmp( arg2, "spec" ) )
	{
	    if( ( value = spec_lookup( arg3 ) ) <= 0 )
	    {
		progbug( ch, "mset spec: invalid spec_fun name" );
		return;
	    }
	    ival = &vict->spec_fun;
	}
    }

    if( ival )
	*ival = atoi_functions( arg3 );
    else if( sval )
    {
	free_string( *sval );
	*sval = str_dup( arg3 );
    }
    else
    {
	progbug( ch, "mset: unknown field %s.", arg2 );
	return;
    }
    return;
}


void mp_oload( CHAR_DATA *ch, const char *argument )
{
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    OBJ_INDEX_DATA *pObjIndex;
    OBJ_DATA *obj;
    int level;

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

    if( arg1[0] == '\0' || !is_number( arg1 ) )
    {
	progbug( ch, "Mpoload - Bad syntax: %s %s %s.",
		       arg1, arg2, argument );
	return;
    }

    if( arg2[0] == '\0' )
    {
	level = 0;
    }
    else
    {
	/*
	 * New feature from Alander.
	 */
	if( !is_number( arg2 ) )
	{
	    progbug( ch, "Mpoload - Bad syntax: arg2: %s.", arg2 );
	    return;
	}
	level = atoi( arg2 );
	if( level < 0 || level > get_trust( ch ) )
	{
	    progbug( ch, "Mpoload - Bad level: %s.", arg2 );
	    return;
	}
    }

    if( ( pObjIndex = get_obj_index( atoi( arg1 ) ) ) == NULL )
    {
	progbug( ch, "Mpoload - Bad vnum arg: %s.", arg1 );
	return;
    }

    obj = create_object( pObjIndex, level );
    if( CAN_WEAR( obj, ITEM_TAKE ) )
    {
	obj_to_char( obj, ch );
    }
    else
    {
	obj_to_room( obj, ch->in_room );
    }

    return;
}


/*
 * Trimmed down version of oset.
 */
void mp_oset( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    char arg3[MAX_INPUT_LENGTH];
    int value;
    int *ival = NULL;
    char **sval = NULL;

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

    if( !( obj = get_obj_here( ch, arg1 ) ) )
    {
	progbug( ch, "oset: non-existant object %s.", arg1 );
	return;
    }

    value = atoi_functions( arg3 );

    if( !str_cmp( arg2, "name" ) )
	sval = &obj->name;
    else if( !str_cmp( arg2, "short" ) )
	sval = &obj->short_descr;
    else if( !str_cmp( arg2, "long" ) )
    {
	strcat( arg3, "\n\r" );
	sval = &obj->description;
    }
    else if( !str_cmp( arg2, "action" ) )
    {
	strcat( arg3, "\n\r" );
	sval = &obj->action;
    }
    else if( !str_cmp( arg2, "value0" ) || !str_cmp( arg2, "v0" ) )
	ival = &obj->value[0];
    else if( !str_cmp( arg2, "value1" ) || !str_cmp( arg2, "v1" ) )
	ival = &obj->value[2];
    else if( !str_cmp( arg2, "value2" ) || !str_cmp( arg2, "v2" ) )
	ival = &obj->value[2];
    else if( !str_cmp( arg2, "value3" ) || !str_cmp( arg2, "v3" ) )
	ival = &obj->value[3];
    else if( !str_cmp( arg2, "extra" ) )
	ival = &obj->extra_flags;
    else if( !str_cmp( arg2, "wear" ) )
	ival = &obj->wear_flags;
    else if( !str_cmp( arg2, "level" ) )
	ival = &obj->level;
    else if( !str_cmp( arg2, "weight" ) )
	ival = &obj->weight;		/* be careful! no checks! */
    else if( !str_cmp( arg2, "cost" ) )
	ival = &obj->cost;
    else if( !str_cmp( arg2, "timer" ) )
    {
	set_timer_tick( obj, value );
	return;
    }
    else if( !str_cmp( arg2, "imptimer" ) )
    {
	create_obj_event( obj, evn_imp_grab, value * PULSE_TICK );
	return;
    }
    else if( !str_cmp( arg2, "required" ) )
    {
	obj->required_skill = skill_lookup( arg3 );
	return;
    }
    else if( !str_cmp( arg2, "condition" ) )
    {
	value *= 10;
	ival = &obj->condition;
    }

    if( ival )
	*ival = value;
    else if( sval )
    {
	free_string( *sval );
	*sval = str_dup( arg3 );
    }
    else
    {
	progbug( ch, "oset: unknown field %s.", arg2 );
	return;
    }
    return;
}


/*
 * openpassage <#fromvnum> <#tovnum> <dir>
 * only a one directional passage, <dir> can be number/n/s/e...
 *
 * Potential Problem: these might not be able to be removed by progs
 * in rooms which have exit randomisations.  --Symposium
 */
void mp_openpassage( CHAR_DATA *ch, const char *argument )
{
    char arg1[ MAX_INPUT_LENGTH ];
    char arg2[ MAX_INPUT_LENGTH ];
    ROOM_INDEX_DATA *targetRoom, *fromRoom;
    int targetRoomVnum, fromRoomVnum, exit_num;

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

    if( arg1[0] == '\0' || arg2[0] == '\0' || argument[0] == '\0' )
    {
	progbug( ch, "Mpopenpassage - Bad syntax '%s' '%s' %s",
		       arg1, arg2, argument );
	return;
    }
    if( !is_number( arg1 ) || !is_number( arg2 ) )
    {
	progbug( ch, "MpOpenPassage - Bad syntax: %s %s ...", arg1, arg2 );
	return;
    }

    fromRoomVnum = atoi( arg1 );
    targetRoomVnum = atoi( arg2 );
    if( ( fromRoom = get_room_index( fromRoomVnum ) ) == NULL
	|| ( targetRoom = get_room_index( targetRoomVnum ) ) == NULL )
    {
	progbug( ch, "MpOpenPassage - Bad room vnums: %d - %d",
		       fromRoomVnum, targetRoomVnum );
	return;
    }

    if( isdigit( argument[0] ) )
    {
	exit_num = argument[0] - '0';
    }
    else switch( argument[0] )
    {
    case 'n': case 'N':
	exit_num = 0;		break;
    case 'e': case 'E':
	exit_num = 1;		break;
    case 's': case 'S':
	exit_num = 2;		break;
    case 'w': case 'W':
	exit_num = 3;		break;
    case 'u': case 'U':
	exit_num = 4;		break;
    case 'd': case 'D':
	exit_num = 5;		break;
    default:
	progbug( ch, "mpopenpassage: Bad direction." );
	return;
    }

    if( exit_num < 0 || exit_num >= MAX_DIR )
    {
	progbug( ch, "mpopenpassage: invalid exit number %d.", exit_num );
	return;
    }
    if( fromRoom->exit[exit_num] != NULL )
    {
	if( !IS_SET( fromRoom->exit[exit_num]->exit_info, EX_TEMPORARY ) )
	    progbug( ch, "MpOpenPassage - Exit exists" );
	return;
    }
    fromRoom->exit[exit_num] = new_exit( );
    fromRoom->exit[exit_num]->in_room = fromRoom;
    fromRoom->exit[exit_num]->to_room = targetRoom;
    fromRoom->exit[exit_num]->key = -1;
    SET_BIT( fromRoom->exit[exit_num]->exit_info, EX_TEMPORARY );
    SET_BIT( fromRoom->exit[exit_num]->rs_flags, EX_TEMPORARY );
}


/*
 * closepassage <#room> <dir>
 * as above (SEE NOTE)
 */
void mp_closepassage( CHAR_DATA *ch, const char *argument )
{
    char arg[MAX_INPUT_LENGTH];
    ROOM_INDEX_DATA *room;
    int exit_num;

    argument = one_argument( argument, arg );

    if( arg[0] == '\0' || argument[0] == '\0' )
    {
	progbug( ch, "mp_close_passage: illegal arguments: %s %s",
		       arg, argument );
	return;
    }
    if( ( room = get_room_index( atoi( arg ) ) ) == NULL )
    {
	progbug( ch, "mp_close_passage: couldn't find room '%s'!", arg );
	return;
    }

    if( isdigit( argument[0] ) )
    {
	exit_num = argument[0] - '0';
    }
    else switch( argument[0] )
    {
    case 'n': case 'N':
	exit_num = 0;		break;
    case 'e': case 'E':
	exit_num = 1;		break;
    case 's': case 'S':
	exit_num = 2;		break;
    case 'w': case 'W':
	exit_num = 3;		break;
    case 'u': case 'U':
	exit_num = 4;		break;
    case 'd': case 'D':
	exit_num = 5;		break;
    default:
	progbug( ch, "mpclosepassage: Bad direction." );
	return;
    }
    if( exit_num < 0 || exit_num > MAX_DIR || !room->exit[exit_num]
	|| !IS_SET( room->exit[exit_num]->rs_flags
		    | room->exit[exit_num]->exit_info, EX_TEMPORARY ) )
    {
	progbug( ch, "mpclosepassage: exit %d from %d isn't temporary.",
		       exit_num, room->vnum );
	return;
    }
    free_exit( room->exit[exit_num] );
    room->exit[exit_num] = NULL;
    return;
}


/*
 * Stop fights in the room.
 */
void mp_peace( CHAR_DATA *ch, const char *argument )
{
    CHAR_DATA *rch;

    for( rch = ch->in_room->people; rch; rch = rch->next_in_room )
    {
	if( rch->fighting )
	    stop_fighting( rch, TRUE );
    }

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


/* Lets the mobile purge all objects and other npcs in the room,
   or purge a specified object or mob in the room.  It can purge
   itself, but this had best be the last command in the MOBprogram
   otherwise ugly stuff will happen */
void mp_purge( CHAR_DATA *ch, const char *argument )
{
    purge_room( ch, argument );
    return;
}


/*
 * Set a 'prog variable.
 */
void mp_set( CHAR_DATA *ch, const char *argument )
{
    char var[MAX_INPUT_LENGTH];

    argument = first_arg( argument, var, FALSE );
    set_mob_var( ch, var, argument );
}


/*
 * Delete/remove a 'prog variable.
 */
void mp_delete( CHAR_DATA *ch, const char *argument )
{
    delete_mob_var( ch, argument );
}


/* Lets the mobile transfer people.  The all argument transfers
   everyone in the current room to the specified location. */
void mp_transfer( CHAR_DATA *ch, const char *argument )
{
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    ROOM_INDEX_DATA *location;
    DESCRIPTOR_DATA *d;
    CHAR_DATA *victim;

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

    if( arg1[0] == '\0' )
    {
	progbug( ch, "Mptransfer - Bad syntax: %s %s %s.",
		       arg1, arg2, argument );
	return;
    }

    if( !str_cmp( arg1, "all" ) )
    {
	for( d = descriptor_list; d != NULL; d = d->next )
	{
	    if( !IS_SET( d->interpreter->flags, INTERPRETER_SAFE )
		&& d->character != ch
		&& d->character->in_room != NULL
		&& can_see( ch, d->character ) )
	    {
		char buf[MAX_STRING_LENGTH];

		sprintf( buf, "%s %s", d->character->name, arg2 );
		do_transfer( ch, buf );
	    }
	}
	return;
    }

    /*
     * Thanks to Grodyn for the optional location parameter.
     */
    if( arg2[0] == '\0' )
    {
	location = ch->in_room;
    }
    else
    {
	if( ( location = find_location( ch, arg2 ) ) == NULL )
	{
	    progbug( ch, "Mptransfer - No such location: %s.", arg2 );
	    return;
	}

	if( room_is_private( location ) )
	{
	    progbug( ch, "Mptransfer - Private room: vnum %d.",
			   location->vnum );
	    return;
	}
    }

    if( ( victim = get_char_world( ch, arg1 ) ) == NULL )
    {
	progbug( ch, "Mptransfer - No such person %s.", arg1 );
	return;
    }

    if( victim->in_room == NULL )
    {
	progbug( ch, "Mptransfer - Victim in Limbo." );
	return;
    }

    if( victim->fighting != NULL )
	stop_fighting( victim, TRUE );

    char_from_room( victim );
    char_to_room( victim, location );

    return;
}


/* Lets the mobile transfer objects.
 * Use otransfer <object> character <character>
 *     otransfer <object> object <object>
 *     otransfer <object> [room] <room>
 */
void mp_otransfer( CHAR_DATA *ch, const char *argument )
{
    char arg1[MAX_INPUT_LENGTH];
    char arg2[MAX_INPUT_LENGTH];
    ROOM_INDEX_DATA *location = NULL;
    CHAR_DATA *vict = NULL;
    OBJ_DATA *obj;
    OBJ_DATA *objto = NULL;

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

    if( arg1[0] == '\0' || arg2[0] == '\0' )
    {
	progbug( ch, "Otransfer - Bad syntax: %s %s %s.",
		       arg1, arg2, argument );
	return;
    }

    if( ( obj = get_obj_here( ch, arg1 ) ) == NULL )
    {
	progbug( ch, "Otransfer - No such object %s.", arg1 );
	return;
    }

    if( !str_prefix( arg2, "character" ) )
    {
	if( ( vict = get_char_world( ch, argument ) ) == NULL )
	{
	    progbug( ch, "Otransfer - No such character: %s.", argument );
	    return;
	}
    }
    else if( !str_prefix( arg2, "object" ) )
    {
	if( ( objto = get_obj_here( ch, argument ) ) == NULL )
	{
	    progbug( ch, "Otransfer - No such object: %s.", argument );
	    return;
	}
    }
    else if( !str_prefix( arg2, "room" ) )
	strcpy( arg2, argument );

    if( !vict && !objto
	&& ( location = find_location( ch, arg2 ) ) == NULL )
    {
	progbug( ch, "Otransfer - No such location: %s.", arg2 );
	return;
    }

    if( obj->in_room )
	obj_from_room( obj );
    else if( obj->in_obj )
	obj_from_obj( obj );
    else if( obj->carried_by )
	obj_from_char( obj );
    else
	progbug( ch, "Otransfer - Obj [%s] not anywhere.", obj->name );

    if( vict )
	obj_to_char( obj, vict );
    else if( objto )
	obj_to_obj( obj, objto );
    else if( location )
    {
	obj_to_room( obj, location );
	strip_events( &obj->events, evn_imp_grab );
    }

    return;
}


/*
 * Trigger a sub_prog on another mobile.
 * Usage: trigger <mob> <trigger>
 * The mobile need not be visible to the triggerer if
 * it is in the same room.
 */
void mp_trigger( CHAR_DATA *ch, const char *argument )
{
    CHAR_DATA *mob;
    char arg[MAX_INPUT_LENGTH];
    char name[MAX_INPUT_LENGTH];
    int count;

    argument = one_argument( argument, arg );
    count = number_argument( arg, name );
    for( mob = ch->in_room->people; mob; mob = mob->next_in_room )
    {
	if( !mob->deleted && is_char_name( mob, name ) && --count <= 0 )
	    break;
    }
    if( !mob )
	mob = get_char_world( ch, arg );

    if( arg[0] == '\0' || argument[0] == '\0' || !mob || mob == ch
	|| !IS_NPC( mob ) )
    {
	progbug( ch, "Bad arguments to mptrigger mob." );
	return;
    }
    mprog_sub_trigger( mob, ch, argument );
    return;
}


/* Trimmed down and silent version of do_open with no triggers or traps */
void mp_open( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    int door;

    if( argument[0] == '\0' )
    {
	progbug( ch, "Trying to open nothingness." );
	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];

	REMOVE_BIT( pexit->exit_info, EX_CLOSED );

	/*
	 * 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 )
	    REMOVE_BIT( pexit_rev->exit_info, EX_CLOSED );

	return;
    }

    if( ( obj = get_obj_here( ch, argument ) ) )
    {
	/* 'open object' */
	if( obj->item_type != ITEM_CONTAINER )
	{
	    progbug( ch, "Trying to open a non-container '%s'.",
			   argument );
	    return;
	}

	if( !IS_SET( obj->value[1], CONT_CLOSEABLE ) )
	{
	    progbug( ch, "Trying to open a non-closable '%s'.",
			   argument );
	    return;
	}

	REMOVE_BIT( obj->value[1], CONT_CLOSED );
	return;
    }

    progbug( ch, "Couldn't open a '%s'.", argument );
    return;
}


/* Trimmed down and silent version of do_close with no triggers or traps */
void mp_close( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    int door;

    if( argument[0] == '\0' )
    {
	progbug( ch, "Trying to close nothingness." );
	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];

	SET_BIT( pexit->exit_info, EX_CLOSED );

	/*
	  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 )
	    SET_BIT( pexit_rev->exit_info, EX_CLOSED );

	return;
    }

    if( ( obj = get_obj_here( ch, argument ) ) )
    {
	if( obj->item_type != ITEM_CONTAINER )
	{
	    progbug( ch, "Trying to open a non-container '%s'.",
			   argument );
	    return;
	}

	if( !IS_SET( obj->value[1], CONT_CLOSEABLE ) )
	{
	    progbug( ch, "Trying to open a non-closable '%s'.",
			   argument );
	    return;
	}

	SET_BIT( obj->value[1], CONT_CLOSED );
	return;
    }

    progbug( ch, "Couldn't close a '%s'.", argument );
    return;
}


/* Trimmed down and silent version of do_lock. */
void mp_lock( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    int door;

    if( argument[0] == '\0' )
    {
	progbug( ch, "Trying to lock nothingness." );
	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 ) )
	{
	    progbug( ch, "Trying to lock an open door." );
	    return;
	}
/* Let's just see if this causes problems, it should be ok as the
   resets still take care of these.
	if( pexit->key < 0 )
	{
	    progbug( ch, "Trying to lock an unlockable door." );
	    return;
	}
*/
	if( IS_SET( pexit->exit_info, EX_LOCKED ) )
	    return;

	SET_BIT( pexit->exit_info, EX_LOCKED );

	/*
	   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 )
	{
	    progbug( ch, "Trying to lock a non-container '%s'.",
			   argument );
	    return;
	}
	if( !IS_SET( obj->value[1], CONT_CLOSED ) )
	{
	    progbug( ch, "Trying to lock an open container '%s'.",
			   argument );
	    return;
	}
	if( obj->value[2] < 0 )
	{
	    progbug( ch, "Trying to lock an unlockable container '%s'.",
			   argument );
	    return;
	}

	if( IS_SET( obj->value[1], CONT_LOCKED ) )
	    return;

	SET_BIT( obj->value[1], CONT_LOCKED );
	return;
    }

    progbug( ch, "Trying to lock non-existant '%s'", argument );
    return;
}


/* Trimmed down, safe and silent version of do_unlock */
void mp_unlock( CHAR_DATA *ch, const char *argument )
{
    OBJ_DATA *obj;
    int door;

    if( argument[0] == '\0' )
    {
	progbug( ch, "Trying to unlock nothingness." );
	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 ) )
	    return;
	if( !IS_SET( pexit->exit_info, EX_LOCKED ) )
	    return;

	REMOVE_BIT( pexit->exit_info, EX_LOCKED );

	/* 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 );
	return;
    }

    if( ( obj = get_obj_here( ch, argument ) ) )
    {
	/* 'unlock object' */
	if( obj->item_type != ITEM_CONTAINER )
	{
	    progbug( ch, "Trying to unlock a non-container '%s'.",
			   argument );
	    return;
	}
	if( !IS_SET( obj->value[1], CONT_CLOSED ) )
	    return;
	if( !IS_SET( obj->value[1], CONT_LOCKED ) )
	    return;

	REMOVE_BIT( obj->value[1], CONT_LOCKED );
	return;
    }

    progbug( ch, "Trying to unlock non-existant '%s'.", argument );
    return;
}