/
LIB3/
LIB3/D/ADMIN/
LIB3/D/ADMIN/OBJ/
LIB3/D/ADMIN/ROOM/W/
LIB3/D/HOME/
LIB3/D/HOME/CITY/ARENA/
LIB3/D/HOME/CITY/ITEMS/
LIB3/D/HOME/CITY/POSTOFFI/
LIB3/DOC/
LIB3/GLOBAL/SPECIAL/
LIB3/GLOBAL/VIRTUAL/
LIB3/NET/
LIB3/NET/CONFIG/
LIB3/NET/DAEMON/CHARS/
LIB3/NET/GOPHER/
LIB3/NET/INHERIT/
LIB3/NET/OBJ/
LIB3/NET/SAVE/
LIB3/NET/VIRTUAL/
LIB3/OBJ/B_DAY/
LIB3/OBJ/HANDLERS/TERM_TYP/
LIB3/PLAYERS/B/
LIB3/PLAYERS/N/
LIB3/ROOM/
LIB3/SAVE/
LIB3/SAVE/BOARDS/
LIB3/SAVE/ENVIRON/
LIB3/SAVE/POST/
LIB3/STD/COMMANDS/SHADOWS/
LIB3/STD/CREATOR/
LIB3/STD/DOM/
LIB3/STD/EFFECTS/
LIB3/STD/EFFECTS/HEALING/
LIB3/STD/EFFECTS/OTHER/
LIB3/STD/EFFECTS/POISONS/
LIB3/STD/ENVIRON/
LIB3/STD/GUILDS/
LIB3/STD/LIQUIDS/
LIB3/STD/ROOM/
LIB3/STD/TRIGGER/SHADOW/
LIB3/W/
LIB3/W/BANNOR/
LIB3/W/NEWSTYLE/
/*
 *  My new and wonderous new room.c
 *  It will never work as the frog says to the chicken
 *  Modified by Godot on 3/6/93 to fix punctuation in do_search message.
 *
 *  93-10-18  Bannor - Added add_monster() and support functions.
 *  93-OCT-31 Bannor - Added #pragma
 *  light_mess implemented, Newstyle, 08/02/94
 *
 */
#pragma save_binary
#define TB(x) if(find_player("bannor")) tell_object(find_player("bannor"),x)

inherit "/std/basic/light";
inherit "/std/basic/property";
inherit "/std/basic/cute_look";
inherit "/std/basic/extra_look";
inherit "/std/basic/desc";
#include "room.h"
#include "door.h"
#include "monster.h"


#define ROOM_HAND "/obj/handlers/room_handler"
#define STD_ORDERS ({\
  "north", ({ -1, 0, 0 }), "south", ({ 1, 0, 0 }), "east", ({ 0, -1, 0 }),\
  "west", ({ 0, 1, 0 }), "up", ({ 0, 0, -1 }), "down", ({ 0, 0, 1 }),\
  "northeast", ({ -1, -1, 0 }), "northwest", ({ -1, 1, 0 }),\
  "southwest", ({ 1, 1, 0 }), "southeast", ({ 1, -1, 0 }), })
#define SHORTEN ([\
  "north":"n", "south":"s", "west":"w", "east":"e", "northeast":"ne",\
  "northwest":"nw", "southeast":"se", "southwest":"sw", "up":"u",\
  "down":"d" ])
/* By NS, 29/4/94, at Niveks request. */
#define LENGTHEN ([ \
   "n" : "north",  "ne" : "northeast", "e" : "east",\
   "se" : "southeast", "s" : "south", "sw" : "southwest",\
   "w" : "west", "nw" : "northwest", "u" : "up", "d" : "down" ])

static mapping items, door_control;
static string exit_string, short_exit_string, room_zone, dark_mess, light_mess, *dest_direc, *aliases;
static mixed *dest_other;
static int *co_ord, room_size;
static object *destables, *hidden_objects;

object *monster_list;
mapping monsters_to_load;


/* prototypes */
string  query_short_exit_string();
int     add_monster( string monster_file, int num );
int     remove_monster( string monster_file );
object *query_monsters();
mapping query_monsters_to_load();
int     count_monsters( string monster_file );

/* By NS, 29/4/94, at Niveks request */
string expand_direction(string abbr)
{
 if(LENGTHEN[abbr]) return LENGTHEN[abbr];
  else return abbr;
}


/* simple functions */
int     test_add( object ob, int flag )
{
    return 1;
}
int     test_remove( object ob, int flag )
{
    return 1;
}
int     add_weight( int n )
{
    return 1;
}
int    *query_co_ord()
{
    return co_ord;
}
void    set_co_ord( int *new )
{
    co_ord = new;
}
string  query_dark_mess()
{
    return dark_mess;
}
void    set_dark_mess( string str )
{
    dark_mess = str;
}
void    set_room_size( int siz )
{
    room_size = siz;
}
int     query_room_size()
{
    return room_size;
}
void    set_light_mess( string s )
{
    light_mess = s;
}
string  query_light_mess()
{
    return light_mess;
}

void    create()
{
    dest_other = ({ });
    dest_direc = ({ });
    items = ([ ]);
    aliases = ({ });
    destables = ({ });
    hidden_objects = ({ });
    door_control = ([ ]);
    monster_list = ({ });
    room_zone = "nowhere";
    seteuid( (string)"/secure/master"->creator_file( file_name( this_object() ) ) );
    set_dark_mess( "It is too dark to see." );
    set_light_mess( "The light is too bright for you to see." );
    if( !room_size )
	room_size = 5;
    property::create();
    extra_look::create();
    add_property( "determinate", "" );
    add_property( "location", "inside" );
    this_object()->setup();
    this_object()->reset();
}				/* create() */


reset()
/* reset(), if you make a reset() in something that inherits this
   make sure you call ::reset().
 */
{
    int     count, num;
    mixed * monster_map;
    object  monst;

    if( monsters_to_load )
    {
	while( (monster_map = each( monsters_to_load )) != ({ }) )
	{
	    num = count_monsters( monster_map[ 0 ] );
	    if( num < monster_map[ 1 ] )
	    {
		for( count = num; count < monster_map[ 1 ]; count++ )
		{
		    monst = clone_object( monster_map[ 0 ] );
		    /* now add it to the list of monsters */
		    if( !monster_list )
			monster_list = ({ monst });
		    else
			monster_list += ({ monst });

		    monst->move( this_object() );
		}

	    }
	}
    }
}				/* reset() */




string  expand_alias( string str );


string short( int dark )
{
    if( dark )
	return ::short( 1 );

    if( query_verb() == "glance" )
	return ::short( 0 ) + query_short_exit_string();

    else
	return ::short( 0 );
}				/* short() */


int     id( string str )
{
    int     i;

    return 0;
    str = expand_alias( str );
    return items[ str ];
}				/* id() */


string  expand_alias( string str )
{
    int     i;

    if( !aliases )
	return str;

    if( (i = member_array( str, aliases )) == -1 )
	return str;

    if( i % 2 )
	return aliases[ i - 1 ];
    return str;
}				/* expand_alias() */


/*
   **  Yes, folks.  This creates that really stupid obvious exits message you
   **  keep getting.  Hope you really hate it.
   **  Specially changed to handle stupid things.  If you can figure it out I
   **  will shake your hand.  Look for docs coming soon to a universe near you.
 */
string  query_dirs_string()
{
    string  ret, *dirs;
    int     no, i, nostore;
    mixed   tmp;

    if( !dest_direc || sizeof( dest_direc ) == 0 )
	dest_direc = ({ });
    dirs = ({ });
    for( i = 0; i < sizeof( dest_other ); i += 2 )
	if( intp( tmp = dest_other[ i + 1 ][ ROOM_OBV ] ) && tmp )
	{
	    no += 1;
	    dirs += ({ dest_other[ i ] });
	}
	else
	    if( stringp( tmp ) )
	    {
		nostore = 1;
		if( call_other( this_object(), tmp, dest_other[ i ] ) )
		    dirs += ({ dest_other[ i ] });
	    }
	    else
		if( pointerp( tmp ) )
		{
		    nostore = 1;
		    if( call_other( tmp[ 0 ], tmp[ 1 ], dest_other[ i ] ) )
			dirs += ({ dest_other[ i ] });
		}
    if( sizeof( dirs ) == 0 )
    {
	if( nostore )
	    return "%^GREEN%^There are no obvious exits.%^RESET%^";
	exit_string = "%^GREEN%^There are no obvious exits.%^RESET%^";
	return exit_string;
    }
    if( sizeof( dirs ) == 1 )
    {
	if( nostore )
	    return "%^GREEN%^There is one obvious exit: " + dirs[ 0 ] + ".%^RESET%^";
	exit_string = "%^GREEN%^There is one obvious exit: " + dirs[ 0 ] + ".%^RESET%^";
	return exit_string;
    }
    ret = " and " + dirs[ sizeof( dirs ) - 1 ] + ".";
    dirs = delete( dirs, sizeof( dirs ) - 1, 1 );
    if( nostore )
	return "%^GREEN%^There are " + query_num( sizeof( dirs ) + 1, 0 ) +
	    " obvious exits: " + implode( dirs, ", " ) + ret + "%^RESET%^";
    exit_string = "%^GREEN%^There are " + query_num( sizeof( dirs ) + 1, 0 ) +
	" obvious exits: " + implode( dirs, ", " ) + ret + "%^RESET%^";
    return exit_string;
}				/* query_dirs_string() */


/*
   **  Yes, folks.  This creates that really stupid obvious exits message you
   **  keep getting.  Hope you really hate it.
   **  Specially changed to handle stupid things.  If you can figure it out I
   **  will shake your hand.  Look for docs coming soon to a universe near you.
 */
string  query_short_exit_string()
{
    string  ret, *dirs;
    int     no, i, nostore, add;
    mixed   tmp;

    if( short_exit_string )
	return short_exit_string;
    if( !dest_direc || sizeof( dest_direc ) == 0 )
	dest_direc = ({ });
    dirs = ({ });
    for( i = 0; i < sizeof( dest_other ); i += 2 )
    {
	add = 0;
	if( intp( tmp = dest_other[ i + 1 ][ ROOM_OBV ] ) && tmp )
	{
	    no += 1;
	    add = 1;
	}
	else
	    if( stringp( tmp ) )
	    {
		nostore = 1;
		add = (int)call_other( this_object(), tmp, dest_other[ i ] );
	    }
	    else
		if( pointerp( dest_other[ i + 1 ][ ROOM_OBV ] ) )
		{
		    nostore = 1;
		    add = (int)call_other( tmp[ 0 ], tmp[ 1 ], dest_other[ i ] );
		}
	if( !add )
	    continue;
	if( (ret = SHORTEN[ dest_other[ i ] ]) )
	    dirs += ({ ret });
	else
	    dirs += ({ dest_other[ i ] });
    }
    if( sizeof( dirs ) == 0 )
    {
	if( nostore )
	    return " %^GREEN%^[none]%^RESET%^";
	return short_exit_string = " %^GREEN%^[none]%^RESET%^";
    }
    if( nostore )
	return " %^GREEN%^[" + implode( dirs, "," ) + "]%^RESET%^";
    return short_exit_string = " %^GREEN%^[" + implode( dirs, "," ) + "]%^RESET%^";
}				/* query_short_exit_string() */


string long( string str, int dark )
{
    int     i;
    string  ret, s;

    if( dark == 1 )
	return query_dark_mess() + "\n";
    if( dark == -1 )
	return query_light_mess() + "\n";
    if( !str )
    {
	ret = "";
	ret = query_short() + "\n";
	ret += query_long() + "\n";
	s = calc_extra_look();
	if( s && s != "" )
	    ret += s + "\n";
	if( !exit_string )
	    s = query_dirs_string();
	else
	    s = exit_string;
	ret += s + "\n";
	return ret + query_contents( "" );
    }
    str = expand_alias( str );
    return items[ str ];
}				/* long() */


void    calc_co_ord()
{
    int *   old_co_ord, i, j, k;

    for( i = 0; i < sizeof( dest_other ) && !co_ord; i += 2 )
	if( find_object( dest_other[ i + 1 ][ ROOM_DEST ] ) )
	    if( !catch( (old_co_ord = (int *)dest_other[ i + 1 ][ ROOM_DEST ]->query_co_ord()) ) )
		if( old_co_ord )
		{
		    co_ord = old_co_ord + ({ });
		    if( (j = member_array( dest_other[ i ], STD_ORDERS )) != -1 )
			for( k = 0; k < 3; k++ )
			    co_ord[ k ] += STD_ORDERS[ j + 1 ][ k ] * room_size;
		}
}				/* calc_co_ord() */


void    init()
{
    int     i, j;
    mapping done;

    if( !dest_direc )
	return 0;

    done = ([ ]);
    for( i = 0; i < sizeof( dest_direc ); i++ )
    {
	if( !done[ dest_direc[ i ] ] )
	    add_action( "do_exit_command", dest_direc[ i ] );
	done[ dest_direc[ i ] ] = 1;
	if( (j = member_array( dest_direc[ i ], aliases )) != -1 )
	{
	    string *al;

	    al = aliases;

	    do
	    {
		if( !(j % 2) )
		{
		    if( !done[ al[ j + 1 ] ] )
		    {
			add_action( "do_exit_command", al[ j + 1 ] );
			done[ al[ j + 1 ] ] = 1;
		    }
		}
		else
		    j++;
		al = al[ j + 2..sizeof( al ) ];
	    }
	    while( (j = member_array( dest_direc[ i ], al )) != -1 );
	}
    }
    add_action( "do_search", "search" );
    if( !pointerp( co_ord ) )
	calc_co_ord();
    for( i = 0; i < sizeof( hidden_objects ); i++ )
	if( objectp( hidden_objects[ i ] ) && hidden_objects[ i ] )
	    hidden_objects[ i ]->init();
	else
	{
	    hidden_objects = delete( hidden_objects, i, 1 );
	    i--;
	}
    if( items[ "The object" ] )
	items[ "The object" ]->init();
}				/* init() */


/*
   **  Not particularly useful.
   **  But here just the same; great for debugging purposes.
 */
string *query_direc()
{
    return dest_direc;
}


/*
   **  It IS useful to get directions + where they go to.
   **  BTW this is not a frog.
 */
mixed * query_dest_dir()
{
    int     i;
    string *retval;

    retval = ({ });
    for( i = 0; i < sizeof( dest_other ); i += 2 )
	retval += ({ dest_other[ i ], dest_other[ i + 1 ][ ROOM_DEST ] });
    return retval;
}				/* query_dest_dir() */


mixed * query_dest_other()
{
    return dest_other;
}


void    set_zone( string str )
{
    room_zone = str;
}				/* set_zone() */


string  query_zone()
{
    return room_zone;
}				/* query_zone() */


/*
   **  This function puts the directions into the thingy list.
   **  I am sure you know what I mean.
 */
string  expand_direc( string str )
{
    string  s1, s2;

    if( !str )
	str = "";
    if( sscanf( str, "%s %s", s1, s2 ) == 2 )
	return s1;
    return str;
}				/* expand_direc() */


/*
   **  give_full_path(string) -- added by Phretys@NewMoon  on  4/93.
   **  This will allow you to use room names without the full path if the dest
   **  room is in the same directory as the current room.
 */
string  give_full_path( string str )
{
    string  path;
    int     i;

    if( str[ 0 ] == '/' )
	return str;
    path = file_name( this_object() );
    for( i = strlen( path ) - 1;( i >= 0 ) && (path[ i ] != '/'); i-- )
	;
    path = (i ? extract( path, 0, i ) : "");
    return path + str;
}


/*
   **  Ok.... I am going to rewrite the add_exit, modify_exit and
   **  remove_exit code.... I hope you dont mind.
 */


int     add_exit( string direc, mixed dest, string type )
{
    mixed * stuff;

    if( !dest_other )
	dest_other = ({ });

    if( member_array( direc, dest_other ) != -1 )
	return 0;

    /* if the type fails... it will return a standard exit style */
    dest = give_full_path( dest );
    stuff = ({ dest }) +ROOM_HAND->query_exit_type( type, direc );
    dest_other += ({ direc, stuff });
    dest_direc += ({ expand_direc( direc ) });
    exit_string = 0;
    short_exit_string = 0;
    if( (stuff = (mixed)ROOM_HAND->query_door_type( type, direc, dest )) )
    {
	/* need that to cache the new exit message */
	door_control[ direc ] = ({ clone_object( DOOR_OBJECT ) });
	door_control[ direc ][ 0 ]->setup_door( direc, this_object(), dest, stuff );
	hidden_objects += ({ door_control[ direc ][ 0 ] });
	/* We cheat and add the door exit to the door_control array */
/* Added for Ted to ensure doors get dested with rooms */
	destables += ({ door_control[ direc ] });
	door_control[ dest ] = direc;
    }
    return 1;
}				/* add_exit() */


int     modify_exit( string direc, mixed *data )
{
    int     i, j;

    if( (i = member_array( direc, dest_other )) == -1 )
	return 0;

    for( j = 0; j < sizeof( data ); j += 2 )
	switch( lower_case( data[ j ] ) )
	{
	    case "message":
		dest_other[ i + 1 ][ ROOM_MESS ] = data[ j + 1 ];
		break;
	    case "obvious":
		if( dest_other[ i + 1 ][ ROOM_OBV ] != data[ j + 1 ] )
		{
		    exit_string = 0;	/* State changed... */
		    short_exit_string = 0;
		}
		dest_other[ i + 1 ][ ROOM_OBV ] = data[ j + 1 ];
		break;
	    case "function":
		dest_other[ i + 1 ][ ROOM_FUNC ] = data[ j + 1 ];
		break;
	    case "size":
		dest_other[ i + 1 ][ ROOM_SIZE ] = data[ j + 1 ];
		break;
	    case "enter":
		dest_other[ i + 1 ][ ROOM_ENTER ] = data[ j + 1 ];
		break;
	    case "dest":
		dest_other[ i + 1 ][ ROOM_DEST ] = data[ j + 1 ];
		break;
	    case "open":
		if( door_control[ direc ] )
		    door_control[ direc ][ 0 ]->set_open( data[ j + 1 ] );
		break;
	    case "locked":
		if( door_control[ direc ] )
		    door_control[ direc ][ 0 ]->set_locked( data[ j + 1 ] );
		break;
	    case "key":
		if( door_control[ direc ] )
		    door_control[ direc ][ 0 ]->set_key_prop( data[ j + 1 ] );
		break;
	    case "other":
		if( door_control[ direc ] )
		    door_control[ direc ][ 0 ]->set_other_id( data[ j + 1 ] );
		break;
	    case "difficulty":
		if( door_control[ direc ] )
		    door_control[ direc ][ 0 ]->set_difficulty( data[ j + 1 ] );
		break;
	    case "open desc":
		if( door_control[ direc ] )
		    door_control[ direc ][ 0 ]->set_open_desc( data[ j + 1 ] );
		break;
	    case "open/close func":
		if( door_control[ direc ] )
		    door_control[ direc ][ 0 ]->set_open_func( data[ j + 1 ] );
		break;
	    case "lock/unlock func":
		if( door_control[ direc ] )
		    door_control[ direc ][ 0 ]->set_unlock_func( data[ j + 1 ] );
		break;
	    case "close desc":
		if( door_control[ direc ] )
		    door_control[ direc ][ 0 ]->set_close_desc( data[ j + 1 ] );
		break;
	    case "door short":
		if( door_control[ direc ] )
		    door_control[ direc ][ 0 ]->set_short( data[ j + 1 ] );
		break;
	    case "undoor":
		if( door_control[ direc ] )
		{
		    door_control[ direc ][ 0 ]->go_away();
		    hidden_objects = hidden_objects - ({ door_control[ direc ][ 0 ] });
		}
		door_control = m_delete( door_control, direc );
		break;
	}
    return 1;
}				/* modify_exit() */


int     remove_exit( string direc )
{
    int     i;

    if( !dest_other )
	dest_other = ({ });
    if( (i = member_array( direc, dest_other )) == -1 )
	return 0;
    dest_other = delete( dest_other, i, 2 );
    dest_direc = delete( dest_direc, i / 2, 1 );
    if( door_control[ direc ] )
    {
	door_control[ direc ][ 0 ]->go_away();
	hidden_objects = hidden_objects - ({ door_control[ direc ][ 0 ] });
    }
    door_control = m_delete( door_control, direc );
    short_exit_string = 0;
    exit_string = 0;
    return 1;
}				/* remove_exit() */


/*
   **  Added by Ember cause it seemed useful, and I couldn't see any other way
   **  of getting this info ... should it be possible for creators to get
   **  hold of the door_control objects?
   **  Oh yeah, it's the 15th-feb-93
 */
int     query_door_open( string direc )
{
    if( !pointerp( door_control[ direc ] ) )
	return 0;
    if( !door_control[ direc ][ 0 ] )
	return 0;
    return( int ) door_control[ direc ][ 0 ]->query_open();
}


int     query_exit( string direc )
{
    return( member_array( direc, dest_other ) != -1 );
}				/* query_exit() */


int     query_size( string direc )
{
    int     i;

    if( (i = member_array( direc, dest_other )) == -1 )
	return 0;
    if( stringp( dest_other[ i + 1 ][ ROOM_SIZE ] ) )
	return( int ) call_other( this_object(), dest_other[ i + 1 ][ ROOM_SIZE ] );

    if( pointerp( dest_other[ i + 1 ][ ROOM_SIZE ] ) )
	return( int ) call_other( dest_other[ i + 1 ][ ROOM_SIZE ][ 0 ],
				  dest_other[ i + 1 ][ ROOM_SIZE ][ 1 ] );

    return dest_other[ i + 1 ][ ROOM_SIZE ];
}				/* query_size() */


int     do_exit_command( string str, mixed verb, object ob, object foll )
{
    string  special_mess, closed;
    int     i;
    mixed   zip;

    if( !verb )
	verb = query_verb();
    else
    {
	if( pointerp( verb ) )
	{
	    special_mess = verb[ 1 ];
	    verb = verb[ 0 ];
	}
	if( sscanf( verb, "%s %s", verb, str ) != 2 )
	    str = "";
    }
    if( !ob )
	ob = this_player();
    if( (i = member_array( verb, dest_direc )) == -1 )
	if( (i = member_array( verb, aliases )) == -1 )
	    return 0;
	else
	    if( (i = member_array( aliases[ i - 1 ], dest_direc )) == -1 )
		return 0;

    /* ok must be two command exit */
    if( dest_direc[ i ] != dest_other[ i * 2 ] )
    {
	string  s1, s2;
	int     j;

	sscanf( dest_other[ i * 2 ], "%s %s", s1, s2 );
	str = expand_alias( str );
	if( s2 != str )
	{
	    zip = dest_direc[ i + 1..sizeof( dest_direc ) ];
	    while( 1 )
		if( (j = member_array( verb, zip )) != -1 )
		{
		    i += j + 1;
		    sscanf( dest_other[ i * 2 ], "%s %s", s1, s2 );
		    if( str == s2 )
			break;
		    zip = zip[ j + 1..sizeof( zip ) ];
		}
		else
		    return 0;
	}
    }
    /* First check for lockedness of doors etc */
    if( zip = door_control[ dest_other[ i * 2 ] ] )
    {
	if( zip[ 0 ]->query_locked() )
	    /* Locked...  We auto-unlock, if they have the key */
	    if( !zip[ 0 ]->moveing_unlock( ob ) )
		return 0;
	if( !zip[ 0 ]->query_open() )
	{			/* Closed open it and close it after us. */
	    if( !zip[ 0 ]->moveing_open( ob ) )
		return 0;
	    closed = zip[ 0 ];
	}
    }
    if( dest_other[ i * 2 + 1 ][ ROOM_FUNC ] )
	if( stringp( dest_other[ i * 2 + 1 ][ ROOM_FUNC ] ) )
	{
	    if( !call_other( this_object(), dest_other[ i * 2 + 1 ][ ROOM_FUNC ],
			     str, ob, special_mess ) )
		return 0;
	}
	else
	{
	    if( pointerp( dest_other[ i * 2 + 1 ][ ROOM_FUNC ] ) )
		if( !call_other( dest_other[ i * 2 + 1 ][ ROOM_FUNC ][ 0 ],
				 dest_other[ i * 2 + 1 ][ ROOM_FUNC ][ 1 ], ob, special_mess ) )
		    return 0;
	}
    if( !special_mess )
	zip = (int)ob->move_player( dest_other[ i * 2 ], dest_other[ i * 2 + 1 ][ ROOM_DEST ],
				    dest_other[ i * 2 + 1 ][ ROOM_MESS ], foll,
				    dest_other[ i * 2 + 1 ][ ROOM_ENTER ] );
    else
	zip = (int)ob->move_player( dest_other[ i * 2 ], dest_other[ i * 2 + 1 ][ ROOM_DEST ],
				    special_mess, foll,
				    dest_other[ i * 2 + 1 ][ ROOM_ENTER ] );
    if( closed )
	closed->moveing_close( ob );
    return zip;
}				/* do_exit_command() */


/*
   **  Ok, we have done all the exit junk, now for the item bits and pieces.
   **  Share and enjoy your plastic pal who is fun to be with.
 */
varargs int add_item( string str, string desc, int no_plural )
{
    object  ob;
    int     i;

    if( !items )
	items = ([ ]);

    ob = items[ "The object" ];
    if( !ob )
    {
	ob = clone_object( ITEM_OBJECT );
	items[ "The object" ] = ob;
    }
    ob->setup_item( str, desc, no_plural );
    return 1;
}				/* add_item() */


/*
   **  Ok.  Remove item is nasty :(  But....  We will add it.
   **  It will remove all things that reference the given desc.
 */
int     remove_item( string str )
{
    object  ob;

    if( !items )
	items = ([ ]);

    ob = items[ "The object" ];
    if( !ob )
	return 0;
    return( int ) ob->remove_item( str );
}				/* remove_item() */


/*
   **  Change all descs...  It does a match and goes wimble.
 */
int     modify_item( string str, string new_desc )
{
    object  ob;

    if( !items )
	items = ([ ]);

    ob = items[ "The object" ];
    if( !ob )
	return 0;
    return( int ) ob->modify_item( str, new_desc );
}				/* modify_item() */


mapping query_items()
{
    return items;
}


/*
   **  Alias stuff
 */

int     add_alias( mixed name, string str )
{
    int     i;

    if( !aliases )
	aliases = ({ });

    if( pointerp( name ) )
    {
	for( i = 0; i < sizeof( name ); i++ )
	    add_alias( name[ i ], str );
	return 1;
    }

    aliases += ({ str, name });
    return 1;
}				/* add_alias() */


int     modify_alias( string str, string name )
{
    int     i;

    if( !aliases )
	return 0;

    if( (i = member_array( str, aliases )) == -1 || !(i % 2) )
	return 0;

    aliases[ i + 1 ] = name;
    return 1;
}				/* modify_alias() */


int     remove_alias( string str )
{
    int     i;

    if( !aliases )
	return 0;

    if( (i = member_array( str, aliases )) == -1 || !(i % 2) )
	return 0;

    aliases = delete( aliases, i, 2 );
    return 1;
}				/* remove_alias() */


int     query_no_writing()
{
    return 1;
}


void    dest_me()
{
    int     i;

    if( !destables )
	destables = ({ });
    if( mappingp( items ) )
	destables += m_values( items );
    for( i = 0; i < sizeof( destables ); i++ )
	if( destables[ i ] )
	    destables[ i ]->dest_me();
    for( i = 0; i < sizeof( monster_list ); i++ )
	if( monster_list[ i ] )
	{
	    MONSTER_HAND->zap_monster( monster_list[ i ] );
	}
    destruct( this_object() );
}				/* dest_me() */


void    clean_up()
{
    mixed * arr;
    int     i;

    arr = all_inventory( this_object() );
    for( i = 0; i < sizeof( arr ); i++ )
	if( interactive( arr[ i ] ) )
	    return;
    arr->dest_me();
    dest_me();
}				/* clean_up() */


object *find_inv_match( string str )
{
    return all_inventory( this_object() ) + hidden_objects + m_values( items );
}				/* find_inv_match() */


int     do_search( string str )
{
    write( ({ 
		 "You search around for a while, but don't find anything.\n",
		 "You scrounge around, but don't find anything.\n",
		 "You look carefully at everything, but you find nothing.\n",
		 "After a few minutes of intensive searching, you find nothing.\n"
		  })[ random( 4 ) ] );
    say( this_player()->query_cap_name() + " searches around the room a bit.\n" );
    event( this_object(), "player_search" );
    return 1;
}				/* do_search() */


/*
   **  Cute, useful add_sign.
   **
   **  add_sign(string long, string read_mess, string short, string name)
   **  [short and name are optional]
   **  This file can be inherited and will return a sign that can be used
   **  by a room or object in any way it sees fit.  This function was the
   **  brainchild of Wyrm - 7 Feb '92
   **  If the short is left out.. The sign isnt moved into the room,
   **  but it can still be looked at and read.
 */
varargs object add_sign( string long, string mess, string short, string sname )
{
    object  sign;

    sign = clone_object( "/std/object" );

    if( !sname )
	sname = "sign";
    sign->set_name( sname );
    sign->set_main_plural( pluralize( sname ) );
    if( short )
	        sign->set_short( short );
    sign->set_long( long );

    sign->set_read_mess( mess );
    sign->reset_get();
    if( short &&short !="" )
	        sign->move( this_object() );

    else
	hidden_objects += ({ sign });
    destables += ({ sign });
    return sign;
}				/* add_sign() */


mapping query_door_control()
{
    return door_control;
}


void    tell_door( string dir, string mess )
{
    if( !door_control[ dir ] )
	return;
    door_control[ dir ][ 0 ]->tell_door( mess );
}				/* tell_door() */


mixed   call_door( string dir, string func, mixed arg1, mixed arg2, mixed arg3 )
{
    if( !door_control[ dir ] )
	return;
    return( mixed ) call_other( door_control[ dir ][ 0 ], func, arg1, arg2, arg3 );
}				/* call_door() */


mixed   query_door( mixed dest )
{
    mixed   bing;

    if( (bing = door_control[ dest ]) )
	if( !pointerp( bing ) )
	    return bing;
    if( objectp( dest ) )
	if( (bing = door_control[ file_name( dest ) ]) )
	    if( !pointerp( bing ) )
		return bing;
    return 0;
}				/* query_door() */


mixed   stats()
{
    mixed * exits, *tmp;
    int     i;

    exits = ({ });
    for( i = 0; i < sizeof( dest_other ); i += 2 )
	exits += ({ ({ "Direction", dest_other[ i ], }),
		    ({ "Destination", dest_other[ i + 1 ][ ROOM_DEST ] }) });
    if( co_ord )
	tmp = ({ ({ "co-ord x", co_ord[ 0 ] }),
		 ({ "co-ord y", co_ord[ 1 ] }),
		 ({ "co-ord z", co_ord[ 2 ] }), });
    else
	tmp = ({ });
    return({ ({ "short", short( 0 ), }),
	             ({ "location", query_property( "location" ), }),
	             ({ "move zone", room_zone }),
	      })+    tmp + exits +::stats();
}				/* stats() */


int     query_decay()
{
    return 10;
}


/* Number of move points used by an attack... */
int     attack_speed()
{
    return 15;
}


void    add_hidden_object( object ob )
{
    hidden_objects += ({ ob });
}				/* add_hidden_object() */


int     remove_hidden_object( object ob )
{
    int     i;

    if( (i = member_array( ob, hidden_objects )) == -1 )
	return 0;
    hidden_objects = delete( hidden_objects, i, 1 );
    return 1;
}				/* remove_hidden_object() */


object *query_hidden_objects()
{
    return hidden_objects;
}				/* query_hidden_objects() */



int     add_monster( string monster_file, int num )
{
    object  monster;

    if( !monsters_to_load )
      monsters_to_load = ([ monster_file:num ]);
    else
	monsters_to_load[ monster_file ] = num;
    return 1;
}				/* add_monster() */


int     remove_monster( string monster_file )
{
    if( !mappingp( monsters_to_load ) )
	return 0;
    if( monsters_to_load[ monster_file ] )
    {
	map_delete( monsters_to_load, monster_file );
	return 1;
    }
    return 0;
}				/* remove_monster() */


object *query_monsters()
/* return the list of monsters in the room */
{
    return monster_list;
}				/* query_monsters() */


mapping query_monsters_to_load()
{
    return monsters_to_load;
}				/* query_monster_list() */


int     count_monsters( string monster_file )
/* return how many of the named monsters are loaded by this room */
/* Monster_file should be the full path/file name with out a ".c" */
{
    int     count, num;
    string  file, junk;

    monster_list -= ({ 0 });
    if( !monster_file )
	return 0;
    num = 0;
    /* strip .c from the file name if it's there */
    /* note Vobjects need thier extention */
    if( sscanf( monster_file, "%s.c", file ) == 1 )
	monster_file = file;
    for( count = 0; count < sizeof( monster_list ); count++ )
    {
	if( monster_list[ count ] )
	{
	    file = file_name( monster_list[ count ] );
	    sscanf( file, "%s#%s", file, junk );
	    if( file == monster_file )
		num++;
	}
    }
    return num;
}				/* count_monsters() */