skylib_fluffos_v3/
skylib_fluffos_v3/bin/
skylib_fluffos_v3/bin/db/
skylib_fluffos_v3/fluffos-2.9-ds2.04/
skylib_fluffos_v3/fluffos-2.9-ds2.04/ChangeLog.old/
skylib_fluffos_v3/fluffos-2.9-ds2.04/Win32/
skylib_fluffos_v3/fluffos-2.9-ds2.04/compat/
skylib_fluffos_v3/fluffos-2.9-ds2.04/compat/simuls/
skylib_fluffos_v3/fluffos-2.9-ds2.04/include/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/clone/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/command/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/data/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/etc/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/include/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/inherit/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/inherit/master/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/log/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/tests/compiler/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/tests/efuns/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/single/tests/operators/
skylib_fluffos_v3/fluffos-2.9-ds2.04/testsuite/u/
skylib_fluffos_v3/fluffos-2.9-ds2.04/tmp/
skylib_fluffos_v3/fluffos-2.9-ds2.04/windows/
skylib_fluffos_v3/mudlib/
skylib_fluffos_v3/mudlib/cmds/
skylib_fluffos_v3/mudlib/cmds/admin/
skylib_fluffos_v3/mudlib/cmds/guild-race/
skylib_fluffos_v3/mudlib/cmds/living/broken/
skylib_fluffos_v3/mudlib/cmds/player/group_cmds/
skylib_fluffos_v3/mudlib/cmds/playtester/
skylib_fluffos_v3/mudlib/d/admin/
skylib_fluffos_v3/mudlib/d/admin/room/
skylib_fluffos_v3/mudlib/d/admin/room/we_care/
skylib_fluffos_v3/mudlib/d/admin/save/
skylib_fluffos_v3/mudlib/d/admin/text/
skylib_fluffos_v3/mudlib/d/learning/TinyTown/buildings/
skylib_fluffos_v3/mudlib/d/learning/TinyTown/map/
skylib_fluffos_v3/mudlib/d/learning/TinyTown/roads/
skylib_fluffos_v3/mudlib/d/learning/chars/
skylib_fluffos_v3/mudlib/d/learning/functions/
skylib_fluffos_v3/mudlib/d/learning/handlers/
skylib_fluffos_v3/mudlib/d/learning/help_topics/
skylib_fluffos_v3/mudlib/d/learning/help_topics/npcs/
skylib_fluffos_v3/mudlib/d/learning/help_topics/objects/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rcs_demo/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rcs_demo/RCS/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rooms/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rooms/crowd/
skylib_fluffos_v3/mudlib/d/learning/help_topics/rooms/situations/
skylib_fluffos_v3/mudlib/d/learning/save/
skylib_fluffos_v3/mudlib/d/learning/school/
skylib_fluffos_v3/mudlib/d/learning/school/add_sc/
skylib_fluffos_v3/mudlib/d/learning/school/characters/
skylib_fluffos_v3/mudlib/d/learning/school/general/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/basic_commands/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/edtutor/
skylib_fluffos_v3/mudlib/d/learning/school/getting-started/unix_tutor/
skylib_fluffos_v3/mudlib/d/learning/school/items/
skylib_fluffos_v3/mudlib/d/learning/school/npc_school/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/room_basic/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/situations/
skylib_fluffos_v3/mudlib/d/learning/school/room_school/terrain_tutor/
skylib_fluffos_v3/mudlib/d/learning/text/
skylib_fluffos_v3/mudlib/d/liaison/
skylib_fluffos_v3/mudlib/d/mudlib/
skylib_fluffos_v3/mudlib/d/mudlib/changes/
skylib_fluffos_v3/mudlib/d/playtesters/
skylib_fluffos_v3/mudlib/d/playtesters/effects/
skylib_fluffos_v3/mudlib/d/playtesters/handlers/
skylib_fluffos_v3/mudlib/d/playtesters/items/
skylib_fluffos_v3/mudlib/d/sage/
skylib_fluffos_v3/mudlib/doc/
skylib_fluffos_v3/mudlib/doc/creator/
skylib_fluffos_v3/mudlib/doc/driver/
skylib_fluffos_v3/mudlib/doc/driver/efuns/arrays/
skylib_fluffos_v3/mudlib/doc/driver/efuns/buffers/
skylib_fluffos_v3/mudlib/doc/driver/efuns/calls/
skylib_fluffos_v3/mudlib/doc/driver/efuns/compile/
skylib_fluffos_v3/mudlib/doc/driver/efuns/filesystem/
skylib_fluffos_v3/mudlib/doc/driver/efuns/floats/
skylib_fluffos_v3/mudlib/doc/driver/efuns/functions/
skylib_fluffos_v3/mudlib/doc/driver/efuns/general/
skylib_fluffos_v3/mudlib/doc/driver/efuns/mappings/
skylib_fluffos_v3/mudlib/doc/driver/efuns/mixed/
skylib_fluffos_v3/mudlib/doc/driver/efuns/mudlib/
skylib_fluffos_v3/mudlib/doc/driver/efuns/numbers/
skylib_fluffos_v3/mudlib/doc/driver/efuns/parsing/
skylib_fluffos_v3/mudlib/doc/login/
skylib_fluffos_v3/mudlib/doc/lpc/basic_manual/
skylib_fluffos_v3/mudlib/doc/lpc/intermediate/
skylib_fluffos_v3/mudlib/doc/new/add_command/
skylib_fluffos_v3/mudlib/doc/new/events/
skylib_fluffos_v3/mudlib/doc/new/handlers/
skylib_fluffos_v3/mudlib/doc/new/living/race/
skylib_fluffos_v3/mudlib/doc/new/living/spells/
skylib_fluffos_v3/mudlib/doc/new/object/
skylib_fluffos_v3/mudlib/doc/new/player/
skylib_fluffos_v3/mudlib/doc/new/room/guild/
skylib_fluffos_v3/mudlib/doc/new/room/outside/
skylib_fluffos_v3/mudlib/doc/new/room/storeroom/
skylib_fluffos_v3/mudlib/doc/object/
skylib_fluffos_v3/mudlib/doc/playtesters/
skylib_fluffos_v3/mudlib/doc/policy/
skylib_fluffos_v3/mudlib/doc/weapons/
skylib_fluffos_v3/mudlib/global/
skylib_fluffos_v3/mudlib/global/creator/
skylib_fluffos_v3/mudlib/handlers/
skylib_fluffos_v3/mudlib/include/casino/
skylib_fluffos_v3/mudlib/include/cmds/
skylib_fluffos_v3/mudlib/include/effects/
skylib_fluffos_v3/mudlib/include/npc/
skylib_fluffos_v3/mudlib/include/room/
skylib_fluffos_v3/mudlib/include/shops/
skylib_fluffos_v3/mudlib/net/daemon/
skylib_fluffos_v3/mudlib/net/daemon/chars/
skylib_fluffos_v3/mudlib/net/inherit/
skylib_fluffos_v3/mudlib/net/obj/
skylib_fluffos_v3/mudlib/net/obj/BACKUPS/
skylib_fluffos_v3/mudlib/obj/amulets/
skylib_fluffos_v3/mudlib/obj/armours/plate/
skylib_fluffos_v3/mudlib/obj/b_day/
skylib_fluffos_v3/mudlib/obj/clothes/transport/horse/
skylib_fluffos_v3/mudlib/obj/faith/symbols/
skylib_fluffos_v3/mudlib/obj/fungi/
skylib_fluffos_v3/mudlib/obj/gatherables/
skylib_fluffos_v3/mudlib/obj/instruments/
skylib_fluffos_v3/mudlib/obj/media/
skylib_fluffos_v3/mudlib/obj/misc/player_shop/
skylib_fluffos_v3/mudlib/obj/monster/godmother/
skylib_fluffos_v3/mudlib/obj/monster/transport/
skylib_fluffos_v3/mudlib/obj/rings/
skylib_fluffos_v3/mudlib/obj/scabbards/
skylib_fluffos_v3/mudlib/obj/spells/
skylib_fluffos_v3/mudlib/obj/stationery/
skylib_fluffos_v3/mudlib/obj/stationery/envelopes/
skylib_fluffos_v3/mudlib/obj/toys/
skylib_fluffos_v3/mudlib/obj/vessels/
skylib_fluffos_v3/mudlib/obj/weapons/axes/
skylib_fluffos_v3/mudlib/obj/weapons/chains/
skylib_fluffos_v3/mudlib/obj/weapons/maces/BACKUPS/
skylib_fluffos_v3/mudlib/save/autodoc/
skylib_fluffos_v3/mudlib/save/book_handler/
skylib_fluffos_v3/mudlib/save/books/history/calarien/
skylib_fluffos_v3/mudlib/save/mail/
skylib_fluffos_v3/mudlib/save/new_soul/data/
skylib_fluffos_v3/mudlib/save/parcels/
skylib_fluffos_v3/mudlib/save/playerinfo/
skylib_fluffos_v3/mudlib/save/players/d/
skylib_fluffos_v3/mudlib/save/players/s/
skylib_fluffos_v3/mudlib/save/random_names/
skylib_fluffos_v3/mudlib/save/random_names/data/
skylib_fluffos_v3/mudlib/save/terrains/
skylib_fluffos_v3/mudlib/save/terrains/tutorial_desert/
skylib_fluffos_v3/mudlib/save/terrains/tutorial_grassy_field/
skylib_fluffos_v3/mudlib/save/terrains/tutorial_mountain/
skylib_fluffos_v3/mudlib/save/todo_lists/
skylib_fluffos_v3/mudlib/secure/
skylib_fluffos_v3/mudlib/secure/cmds/admin/
skylib_fluffos_v3/mudlib/secure/cmds/lord/
skylib_fluffos_v3/mudlib/secure/config/
skylib_fluffos_v3/mudlib/secure/handlers/autodoc/
skylib_fluffos_v3/mudlib/secure/handlers/intermud/
skylib_fluffos_v3/mudlib/secure/include/global/
skylib_fluffos_v3/mudlib/secure/save/
skylib_fluffos_v3/mudlib/secure/save/handlers/
skylib_fluffos_v3/mudlib/secure/std/
skylib_fluffos_v3/mudlib/secure/std/classes/
skylib_fluffos_v3/mudlib/secure/std/modules/
skylib_fluffos_v3/mudlib/std/creator/
skylib_fluffos_v3/mudlib/std/dom/
skylib_fluffos_v3/mudlib/std/effects/
skylib_fluffos_v3/mudlib/std/effects/external/
skylib_fluffos_v3/mudlib/std/effects/fighting/
skylib_fluffos_v3/mudlib/std/effects/magic/
skylib_fluffos_v3/mudlib/std/effects/magic/BACKUPS/
skylib_fluffos_v3/mudlib/std/effects/other/BACKUPS/
skylib_fluffos_v3/mudlib/std/effects/priest/
skylib_fluffos_v3/mudlib/std/effects/room/
skylib_fluffos_v3/mudlib/std/environ/
skylib_fluffos_v3/mudlib/std/guilds/
skylib_fluffos_v3/mudlib/std/guilds/old/
skylib_fluffos_v3/mudlib/std/languages/
skylib_fluffos_v3/mudlib/std/liquids/
skylib_fluffos_v3/mudlib/std/npc/
skylib_fluffos_v3/mudlib/std/npc/goals/
skylib_fluffos_v3/mudlib/std/npc/goals/basic/
skylib_fluffos_v3/mudlib/std/npc/goals/misc/
skylib_fluffos_v3/mudlib/std/npc/plans/
skylib_fluffos_v3/mudlib/std/npc/plans/basic/
skylib_fluffos_v3/mudlib/std/npc/types/
skylib_fluffos_v3/mudlib/std/npc/types/helper/
skylib_fluffos_v3/mudlib/std/npcs/
skylib_fluffos_v3/mudlib/std/outsides/
skylib_fluffos_v3/mudlib/std/races/shadows/
skylib_fluffos_v3/mudlib/std/room/basic/BACKUPS/
skylib_fluffos_v3/mudlib/std/room/basic/topography/
skylib_fluffos_v3/mudlib/std/room/controller/
skylib_fluffos_v3/mudlib/std/room/inherit/topography/
skylib_fluffos_v3/mudlib/std/room/topography/area/
skylib_fluffos_v3/mudlib/std/room/topography/iroom/
skylib_fluffos_v3/mudlib/std/room/topography/milestone/
skylib_fluffos_v3/mudlib/std/shadows/curses/
skylib_fluffos_v3/mudlib/std/shadows/disease/
skylib_fluffos_v3/mudlib/std/shadows/fighting/
skylib_fluffos_v3/mudlib/std/shadows/healing/
skylib_fluffos_v3/mudlib/std/shadows/magic/
skylib_fluffos_v3/mudlib/std/shadows/poison/
skylib_fluffos_v3/mudlib/std/shadows/room/
skylib_fluffos_v3/mudlib/std/shops/controllers/
skylib_fluffos_v3/mudlib/std/shops/objs/
skylib_fluffos_v3/mudlib/std/shops/player_shop/
skylib_fluffos_v3/mudlib/std/socket/
skylib_fluffos_v3/mudlib/std/soul/d/
skylib_fluffos_v3/mudlib/std/soul/e/
skylib_fluffos_v3/mudlib/std/soul/i/
skylib_fluffos_v3/mudlib/std/soul/j/
skylib_fluffos_v3/mudlib/std/soul/k/
skylib_fluffos_v3/mudlib/std/soul/l/
skylib_fluffos_v3/mudlib/std/soul/n/
skylib_fluffos_v3/mudlib/std/soul/o/
skylib_fluffos_v3/mudlib/std/soul/q/
skylib_fluffos_v3/mudlib/std/soul/r/
skylib_fluffos_v3/mudlib/std/soul/u/
skylib_fluffos_v3/mudlib/std/soul/v/
skylib_fluffos_v3/mudlib/std/soul/y/
skylib_fluffos_v3/mudlib/std/soul/z/
skylib_fluffos_v3/mudlib/std/stationery/
skylib_fluffos_v3/mudlib/w/
skylib_fluffos_v3/mudlib/w/default/
skylib_fluffos_v3/mudlib/w/default/armour/
skylib_fluffos_v3/mudlib/w/default/clothes/
skylib_fluffos_v3/mudlib/w/default/item/
skylib_fluffos_v3/mudlib/w/default/npc/
skylib_fluffos_v3/mudlib/w/default/room/
skylib_fluffos_v3/mudlib/w/default/weapon/
skylib_fluffos_v3/mudlib/www/
skylib_fluffos_v3/mudlib/www/java/
skylib_fluffos_v3/mudlib/www/secure/
skylib_fluffos_v3/mudlib/www/secure/lpc/advanced/
skylib_fluffos_v3/mudlib/www/secure/lpc/intermediate/
skylib_fluffos_v3/win32/
/**
 * This file contains all the bits needed to handle rooms, plus the code to
 * handle following when moving.
 * @author Pinkfish
 * @changed Deutha Who knows
 * Severely changed, move the follow code in here from the living object
 * @changed Sandoz - 09/09/02
 * Added recycling for chatters, doors and items.
 * @changed Zrutu - 13/10/04
 * Added support for functions in exit / enter mess
 * @see /std/room/basic_room.c
 * @see recycle_chatter()
 * @see recycle_door()
 * @see recycle_item()
 * @see get_chatter()
 * @see get_door()
 * @see get_item()
 */

#include <climate.h>
#include <player.h>
#include <room.h>

#define CHATSIZE 80
#define DOORSIZE 60

private mapping exit_types, door_types, opposite;
private mixed chatters, doors;
private nosave object *recycled_chatters, *recycled_doors, *recycled_items;
private nosave object *queued_doors, *queued_chatters;
private nosave int chatter_hits, door_hits, item_hits;
private nosave int chatter_misses, door_misses, item_misses;
private nosave int chatter_id, door_id;

private void housekeeping();
private void clean_chatters();
private void clean_doors();

void create() {
    seteuid( master()->creator_file( file_name( TO ) ) );
    // ({ mess, obv, size, func })
    exit_types = ([
        "standard" :({ 0, 1,   400, 0 }),
        "corridor" :({ 0, 1,   250, 0 }),
        "plain"    :({ 0, 1, 10000, 0 }), /* very large */
        "door"     :({ 0, 1,   300, 0 }),
        "stair"    :({ 0, 1,   300, 0 }), /* going up? */
        "hidden"   :({ 0, 0,   300, 0 }), /* hidden non door exit */
        "secret"   :({ 0, 0,   300, 0 }), /* secret door */
        "gate"     :({ 0, 1,   450, 0 }),
        "road"     :({ 0, 1,  1300, 0 }),
        "path"     :({ 0, 1,   800, 0 }),
        "window"   :({ "$N climb$s through a window.\n", 0, 75, 0 }),
        ]);

    door_types = ([
        "door"      : ({ 0, 0, "generic_key", 2, 0, 0 }),
        "secret"    : ({ 0, 0, "generic_key", 3, 1, 0 }),
        "gate"      : ({ 1, 0, "generic_key", 1, 0, 1 }),
        "window"    : ({ 1, 0, "generic_key", 1, 0, 1 }),
        ]);

    opposite = ([
        "north" : ({ 0, "$R$[the ]+south$R$" }),
        "south" : ({ 0, "$R$[the ]+north$R$" }),
        "east" : ({ 0, "$R$[the ]+west$R$" }),
        "west" : ({ 0, "$R$[the ]+east$R$" }),
        "northeast" : ({ 0, "$R$[the ]+southwest$R$" }),
        "southwest" : ({ 0, "$R$[the ]+northeast$R$" }),
        "southeast" : ({ 0, "$R$[the ]+northwest$R$" }),
        "northwest" : ({ 0, "$R$[the ]+southeast$R$" }),
        "up": ({ 0, "below"}), "down" : ({ 0, "above"}),
        "out": ({ 0, "inside"}), "in" : ({ 0, "outside"}),
        "exit": ({ 0, "inside"}), "enter" : ({ 0, "outside"}),
        "hubward": ({ 0, "rimward"}), "rimward" : ({ 0, "hubward"}),
        "turnwise": ({ 0, "widdershins"}),
        "widdershins": ({ 0, "turnwise"}),
        ]);

    chatters = allocate( CHATSIZE );
    doors = allocate( DOORSIZE );

    queued_doors = ({ 0 });
    queued_chatters = ({ 0 });

    recycled_doors = recycled_chatters = recycled_items = ({ });

    call_out( (: housekeeping :), 4 );


} /* create() */

/**
 * This method returns the current list of rooms that are enabled for
 * chatting.
 * @return the current chatters
 */
mixed query_chatters() { return chatters; }

/**
 * This method returns the current list of doors handled by the room
 * handler.
 * @return the current array of doors
 */
mixed query_doors() { return doors; }

/**
 * This method adds an exit type to the current list of available exit types.
 * This is used when the room handler is setup to add all the used exit
 * types.
 * @param type the name of the exit type
 * @param message the message to display when going through the exit
 * @param obvious if the exit is obvious or not
 * @param size the size of the exit (used for heigh restrictions)
 * @param func the function to call when using the exit
 * @return 1 if successfuly added, 0 if not
 * @see remove_exit_type()
 */
int add_exit_type( string type, mixed message, mixed obvious,
                   int size, mixed func ) {
    if( exit_types[type] )
        return 0;

    exit_types[type] = ({ message, obvious, size, func });
    return 1;

} /* add_exit_type() */

/**
 * This method remove the named exit from the type list.
 * @param type the name of the exit type to remove
 * @return always returns 1
 * @see add_exit_type()
 */
int remove_exit_type( string type ) {
    map_delete( exit_types, type );
    return 1;
} /* remove_exit_type() */

/** @ignore yes */
void add_door( object thing ) {
    int number;

    number = random( DOORSIZE );

    if( !pointerp( doors[ number ] ) )
        doors[ number ] = ({ thing });
    else
        doors[ number ] += ({ thing });

} /* add_door() */

/**
 * This method returns information about the door type, the door has
 * extra information associated with it than the standard exit type.
 * This function does a double job of trying to find the corresponding
 * door on the other side of the room.
 * @param type the type of the door
 * @param direc the direction the door points
 * @param dest the destination of the door
 * @return the door type array of information
 */
mixed query_door_type( string type, string direc, mixed dest ) {
    if( !door_types[type] )
        return 0;

    if( functionp(dest) )
        error("Cannot use function pointer destinations with doors.\n");

    // If there isn't a door on the other side.  We don't join.
    call_out("check_door", 1, ({ PO, direc }) );
    return door_types[type] + ({ 0 });

} /* query_door_type() */

/**
 * This method checks to see if the door exists or not.
 * It is passed in the room we are going from and the direction the
 * exit faces in the array.<br>
 * <pre>({ room_from, direction })</pre><br>
 * This is the function which generates those door xx not found messages.
 * @param args the arguements passed into the function
 * @see query_door_type()
 */
void check_door( mixed args ) {
    int ignore;
    string direc, dest;

    if( !args[ 0 ] )
        return;

    args[ 0 ]->set_destination( args[ 1 ] );
    dest = args[ 0 ]->query_destination( args[ 1 ] );

    if( !dest ) {
        tell_room( args[ 0 ], "Error: "+args[ 1 ]+" is no longer an exit.\n");
        return;
    }

    if( !find_object( dest ) ) {
        ignore = 1;
        if( catch( call_other( dest, "??" ) ) ) {
            tell_room( args[ 0 ], "Error: "+dest+" does not exist or does "
                "not load.\n" );
            args[ 0 ]->modify_exit( args[ 1 ], ({ "undoor", 0 }) );
            return;
        }
    }

    direc = dest->query_door( args[ 0 ] );

    if( !direc && !args[ 0 ]->call_door( args[ 1 ], "query_one_way") ) {
        tell_room( args[ 0 ], "Error: "+dest+" does not have a door coming "
            "back here.\n" );
        args[ 0 ]->modify_exit( args[ 1 ], ({ "undoor", 0 }) );
        return;
    }

    args[ 0 ]->modify_exit( args[ 1 ], ({ "other", direc }) );

    // If the other side wasn't loaded anyway, don't bother.
    if( ignore )
        return;

    // This makes sure that whatever the states of the two sides,
    // they'll both end up the same.  Think about it...
    args[ 0 ]->modify_exit( args[ 1 ], ({
        "closed", dest->call_door( direc, "query_closed"),
        "locked", dest->call_door( direc, "query_locked"),
        "closed", dest->call_door( direc, "query_closed") }) );

    if( !args[ 0 ]->call_door( args[ 1 ], "query_closed") &&
        args[ 0 ]->query_property("location") == "outside" )
        add_door( args[ 0 ]->query_door_control( args[ 1 ] ) );

} /* check_door() */

/**
 * This method returns the information associated with the exit type.
 * @param type the exit type to query
 * @param dir the direction the type information is for
 * @return a huge amount of info as specified above
 */
mixed query_exit_type( string type, string dir ) {
    mixed s;

    if( !s = opposite[dir] )
        s = ({ 0, "elsewhere" });

    if( !exit_types[type] )
        return exit_types["standard"] + ({ s, 0, 0, 0, 0, 0, 0, 0, 0, 0 });

    return exit_types[ type ] + ({ s, 0, 0, 0, 0, 0, 0, 0, 0, 0 });

} /* query_exit_type() */

/**
 * This is the code that actually moves the thing around the place.
 * It handles all the weirdness involved with dragging things and other
 * such stuff.
 * @param thing what is being moved
 * @param dir the direction we are going
 * @param dest the destination room
 * @param exit the exit name
 * @param enter the enter name
 * @param move the string to tell the object when it moves
 * @return 1 on success, 0 on failure
 */
int move_thing( object thing, string dir, mixed dest, mixed exit,
                mixed enter, string move ) {
    int ret;
    string arrive, leave;
    object dragging;

    dragging = thing->query_dragging();

    if( dragging && ENV(dragging) != ENV(thing)) {
        thing->reset_dragging();
        dragging = 0;
    }

    if( exit != "none" || objectp( dragging ) ) {
        if( stringp( enter ) )
            enter = ({ 1, enter });
        else if ( !pointerp( enter ) )
            enter = ({ 0, "somewhere"});

        switch ( enter[ 0 ] ) {
          case 0 :
            arrive = replace( thing->query_msgin(), "$F", enter[1] );
          break;
          default :
            arrive = enter[ 1 ];
        }

        if( stringp( exit) )
            leave = exit;
        else if( pointerp( exit ) )
            leave = exit[ 0 ];
        else
            leave = thing->query_msgout();

        leave = replace( leave, "$T", "$R$-"+ dir +"$R$" );
    }

    // Check position...
    if( thing->query_position() != "crouching")
        thing->return_to_default_position(1);

    if( arrive || objectp( dragging ) ) {
        thing->remove_hide_invis("hiding");
        if( stringp( arrive ) && objectp( dragging ) ) {
            arrive += "\n$C$"+thing->query_pronoun()+" drags "+
                dragging->a_short()+" in behind "+thing->HIM+".";
        }

        if( stringp( leave ) && objectp( dragging ) ) {
            leave += "\n$C$"+thing->HE+" drags "+
                dragging->the_short()+" away behind "+thing->HIM+".";
        }

        if( stringp( move ) )
            tell_object( thing, move );

        ret = thing->move( dest, arrive, leave );

        if( !ret && objectp( dragging ) ) {
            if( !dragging->move(ENV(thing)) ) {
                tell_object( thing, "You drag "+
                    dragging->the_short() +" behind you.\n");
                thing->adjust_time_left( -DEFAULT_TIME );
            } else {
                tell_object( thing, "You fail to drag "+
                    dragging->the_short()+" behind you.\n");
            }
        }
    } else {
        if( stringp(move) )
            tell_object( thing, move );
        ret = thing->move( dest );
    }

    return !ret;

} /* move_thing() */

/**
 * @ignore yes
 * This function performs the door checks for exit_move(). It is called
 * for the object moving and each of its followers.  It returns 1 if
 * the player can move or 0 if not, 2 if the player was dead
 */
int exit_move_door_checks( object thing, mixed closed ) {

    closed->force_other();

    if( closed->query_open() )
        return 1;

    if( thing->query_property("demon") || thing->query_property("dead") )
        return 2;

    if( thing->no_use_doors() ) {
        tell_object( thing, "You cannot go through closed doors.\n");
        return notify_fail("");
    }

    if( closed->query_locked() && !closed->moving_unlock(thing) ) {
        // It is locked and invisible...
        if( !closed->query_visible(thing) )
            return 0;

        tell_object( thing, closed->the_short()+" "+
            ({"is", "are"})[closed->query_how_many()] +" locked.\n");
        return notify_fail("");
    }

    if( !closed->moving_open(thing) )
        return 0;

    return 1;

} /* exit_move_door_checks() */

/**
 * @ignore yes
 * This function performs the function checks for exit_move().
 * It returns 1 if the player can move or 0 if not.
 */
int exit_move_func_checks( string verb, string special, object thing,
                           mixed func, object place ) {
    if( stringp(func) )
        return call_other( place, func, verb, thing, special );

    if( functionp(func) )
        return evaluate( func, verb, thing, special );

    if( pointerp(func) )
        return call_other( func[0], func[1], verb, thing, special );

    return 1;

} /* exit_move_func_checks() */

/**
 * This is the main code for moving someone.  The move_thing code above
 * should not be called directly.  This code handlers all the followers
 * and any other things that need to be handled.
 * @param verb the movement verb
 * @param extra extra information
 * @param special special information
 * @param thing the thing to move
 * @return 1 on success, 0 on failure
 */
int exit_move( string verb, string extra, string special, object thing ) {
    int open, flag;
    string leave;
    object place, follower;
    object *okay, *all_followers, *tmp_followers, *more_followers;
    mixed closed, func, dest_other, destination;

    // Find the exit infomation.
    place = ENV( thing );
    verb = place->expand_alias( verb );
    place->set_destination( verb );

    // This checks that the destination is correctly set.
    dest_other = (mixed)place->query_dest_other( verb );
    if( !pointerp(dest_other) )
        return 0;

    if( thing->cannot_walk() )
        return notify_fail("");

    // This checks that the door exists if there should be one.
    if( closed = place->query_door_control( verb ) )
        open = closed->query_open();

    if( objectp(closed) && !open &&
        !( flag = exit_move_door_checks( thing, closed ) ) )
        return 0;

    // Check exit functions.
    func = dest_other[ROOM_FUNC];
    if( func && !thing->query_property("demon") &&
        !exit_move_func_checks( verb, special, thing, func, place ) )
        return 0;

    if( place->query_relative( verb ) )
        leave = thing->find_rel( verb, 0 );
    else
        leave = verb;

    // Check height.
    if( thing->query_height() > dest_other[ ROOM_SIZE ] &&
        !( thing->query_position() == "crouching" &&
        thing->query_height() / 3 <= dest_other[ ROOM_SIZE ] ) ) {
        tell_object( thing, "You are too tall to go that way.\n");
        return notify_fail("");
    }

    // Now actually move.
   
    if(functionp( dest_other[ ROOM_EXIT ]))
        special = evaluate(dest_other[ ROOM_EXIT ]);
    else
    special = dest_other[ ROOM_EXIT ];
    
    destination = dest_other[ ROOM_DEST ];

    if( functionp(destination) )
        destination = evaluate( destination, thing );

    if(functionp(dest_other[ROOM_ENTER])){
       if( !move_thing( thing, verb, destination, special,
                evaluate(dest_other[ROOM_ENTER]), dest_other[ ROOM_MESS ] ) )
           return 0;        
    }else{
           if( !move_thing( thing, verb, destination, special,
                     dest_other[ROOM_ENTER], dest_other[ ROOM_MESS ] ) )
           return 0;         
     }
    
    // exit_move_door_checks() retuned 2 - the player is dead.
    if( flag == 2 )
        tell_object( thing, "You ghost through "+closed->the_short()+".\n");

    thing->return_to_default_position(1);

    okay = ({ });

    if( place && !dest_other[ ROOM_NO_FOLLOW ] ) {
        // Get all the followers of the followers.
        all_followers = thing->query_followers();
        more_followers = all_followers;
        do {
            tmp_followers = ({ });
            foreach( follower in more_followers ) {
                // We only follow people that are actually here.
                // Also make sure we don't end up with duplicates.
                if( follower && ENV( follower ) == place )
                    tmp_followers |= follower->query_followers();
            }
            // Make sure we do not get repeated followers.
            more_followers = tmp_followers - all_followers;
            all_followers |= tmp_followers;
        } while( sizeof(more_followers) );

        // Move all those people following us too!
        foreach ( follower in all_followers ) {
            if( !objectp( follower ) ) {
                thing->remove_follower( follower );
                continue;
            }

            // Make sure they are in the start room and if they are a user
            // they are interactive and the person they are following is
            // visible and they aren't passed out and can walk.
            if( ENV(follower) != place ||
                ( userp(follower) && !interactive(follower) ) ||
                ( !thing->query_visible(follower) || special == "none") ||
                follower->query_property(PASSED_OUT) ||
                follower->cannot_walk() )
                continue;

            // Do the door checks.
            if( objectp(closed) && !open &&
                !exit_move_door_checks( follower, closed ) )
                continue;

            // Do the function checks.
            if( func && !follower->query_property("demon") &&
                !exit_move_func_checks( verb, special, follower, func, place ) )
                continue;

            if( place->query_relative( verb ) ) {
                leave = follower->find_rel( verb, 0 );
                follower->reorient_rel( leave );
            } else {
                leave = verb;
            }

            if( follower->query_height() > dest_other[ ROOM_SIZE ] &&
                !( follower->query_position() == "crouching" &&
                follower->query_height() / 3 <= dest_other[ ROOM_SIZE ] ) ) {
                tell_object( follower, "You are too tall to follow "+
                    thing->the_short()+" "+leave+".\n" );
                continue;
            }

            if( function_exists("check_doing_follow", follower ) &&
                !follower->check_doing_follow( thing, verb, special ) )
                continue;

             if(functionp(dest_other[ROOM_ENTER])){
              if( move_thing( follower, verb, destination, special,
                evaluate(dest_other[ROOM_ENTER]), dest_other[ ROOM_MESS ] ) && 
              living(follower) ){
                 tell_object( follower, "You follow "+
                    thing->the_short()+" "+leave+".\n");

                if( follower->query_visible(thing) )
                    okay += ({ follower });

                follower->return_to_default_position(1);
                follower->adjust_time_left( -DEFAULT_TIME );
              } else {
                tell_object( follower, "You fail to follow "+
                    thing->the_short()+" "+leave+".\n");       
              }
              
             }else{
              if( move_thing( follower, verb, destination, special,
                dest_other[ROOM_ENTER], dest_other[ ROOM_MESS ] ) &&
                living(follower) ) {
                tell_object( follower, "You follow "+
                    thing->the_short()+" "+leave+".\n");

                if( follower->query_visible(thing) )
                    okay += ({ follower });

                follower->return_to_default_position(1);
                follower->adjust_time_left( -DEFAULT_TIME );
            } else {
                tell_object( follower, "You fail to follow "+
                    thing->the_short()+" "+leave+".\n");
            }
          }
        }
    }

    // Move everyone then do the look.  Fix up problems with followers
    // that have light, like the fireflies and blue lights.
    thing->room_look();

    if( sizeof(okay) ) {
        okay->room_look();
        tell_object( thing, query_multiple_short(okay)+" $V$0=follows,"
            "follow$V$ you.\n");
    }

    if( objectp(closed) && !open )
        closed->moving_close(thing);

    return 1;

} /* exit_move() */

/** @ignore yes */
void add_chatter( object thing, int number ) {
    number /= 4;

    if( number > CHATSIZE - 1 )
        number = CHATSIZE - 1;

    if( !pointerp( chatters[ number ] ) )
        chatters[ number ] = ({ thing });
    else
        chatters[ number ] += ({ thing });

} /* add_chatter() */

/** @ignore yes */
void check_chatters() {
    object *things;

    things = chatters[ 0 ];
    chatters[0..<2] = chatters[1..<1];
    chatters[<1] = 0;

    if( pointerp( things ) ) {
        things -= queued_chatters;
        map( things, (: $1->make_chat() :) );
    }

} /* check_chatters() */

/** @ignore yes */
void check_doors() {
    int wind;
    string dest, other, mess;
    object mine, thing, *things;

    things = doors[ 0 ];
    doors[0..<2] = doors[1..<1];
    doors[<1] = 0;

    if( pointerp( things ) ) {
        things -= queued_doors;
        foreach( thing in things ) {
            if( thing->query_closed() || thing->query_stuck() )
                continue;

            dest = thing->query_dest();
            other = thing->query_other_id();
            mine = thing->query_my_room();
            wind = WEATHER_H->calc_actual( mine, WINDSP );

            if( random( 25 ) > wind ) {
                add_door( thing );
                return;
            }

            switch( wind ) {
              case -1000 .. 20 :
                mess = "blow$s shut in the breeze.\n";
              break;
              case 21 .. 40 :
                mess = "blow$s shut in the wind.\n";
              break;
              default :
                mess = "slam$s shut in the wind.\n";
            }

            if( find_object( dest ) ) {
                dest->modify_exit( other, ({"closed", 1 }) );
                dest->tell_door( other, "The $D "+mess, 0 );
            }

            thing->set_closed( 1 );
            thing->tell_door("The $D "+mess, 0 );

        }
    }

} /* check_doors() */

/** @ignore yes */
private void housekeeping() {
    call_out( (: check_doors :), 2 );
    call_out( (: housekeeping :), 4 );
    check_chatters();
} /* housekeeping() */

/** @ignore yes */
private object *clean_array( object *obs, object *minus ) {
    if( sizeof(obs) )
        obs -= minus;
    return obs;
} /* clean_array() */

/** @ignore yes */
private void clean_doors() {
    door_id = 0;
    doors = map( doors, (: clean_array( $1, $2 ) :), queued_doors );
    queued_doors = ({ 0 });
} /* clean_doors() */

/** @ignore yes */
private void clean_chatters() {
    chatter_id = 0;
    chatters = map( chatters, (: clean_array( $1, $2 ) :), queued_chatters );
    queued_chatters = ({ 0 });
} /* clean_chatters() */

/**
 * This method recycles a chatter object and is used by rooms.
 * It calls reload_object() on the chatter, removes it from the
 * chatters array and adds it to the recycled_chatters array.
 * It can then be re-used by using get_chatter().
 * Only objects whose base_name is CHATTER_OBJECT can be recycled.
 * @param ob the chatter object to recycle
 * @return 1 if successfully recycled, 0 if not
 * @see get_chatter()
 */
int recycle_chatter( object ob ) {
    if( base_name(ob) == CHATTER_OBJECT ) {
        if( !chatter_id )
            chatter_id = call_out( (: clean_chatters :), 15 );
        reload_object(ob);
        queued_chatters += ({ ob });
        recycled_chatters += ({ ob });
        return 1;
    }

    return 0;

} /* recycle_chatter() */

/**
 * This method gets a recycled chatter from the handler,
 * or clones a new one, if needed.  This is used by rooms.
 * @return a chatter object
 * @see recycle_chatter()
 */
object get_chatter() {
    object ret;

    if( sizeof( recycled_chatters -= ({ 0 }) ) ) {
        ret = recycled_chatters[0];
        recycled_chatters = recycled_chatters[1..];
        chatter_hits++;
        if( chatter_id ) {
            remove_call_out(chatter_id);
            clean_chatters();
        }
    } else {
        ret = clone_object(CHATTER_OBJECT);
        chatter_misses++;
    }

    return ret;

} /* get_chatter() */

/**
 * This method recycles a door object and is used by rooms.
 * It calls reload_object() on the door, removes it from the
 * doors array and adds it to the recycled_doors array.
 * It can then be re-used by using get_door().
 * Only objects whose base_name is DOOR_OBJECT can be recycled.
 * @param ob the door object to recycle
 * @return 1 if successfully recycled, 0 if not
 * @see get_door()
 */
int recycle_door( object ob ) {
    if( base_name(ob) == DOOR_OBJECT ) {
        if( !door_id )
            door_id = call_out( (: clean_doors :), 15 );
        reload_object(ob);
        queued_doors += ({ ob });
        recycled_doors += ({ ob });
        return 1;
    }

    return 0;

} /* recycle_door() */

/**
 * This method gets a recycled door from the handler,
 * or clones a new one, if needed.  This is used by rooms.
 * @return a door object
 * @see recycle_door()
 */
object get_door() {
    object ret;

    if( sizeof( recycled_doors -= ({ 0 }) ) ) {
        ret = recycled_doors[0];
        recycled_doors = recycled_doors[1..];
        door_hits++;
        if( door_id ) {
            remove_call_out(door_id);
            clean_doors();
        }
    } else {
        ret = clone_object(DOOR_OBJECT);
        door_misses++;
    }

    return ret;

} /* get_door() */

/**
 * This method recycles an item object and is used by rooms.
 * It calls reload_object() on the item and puts it in the
 * recycled_items array.
 * It can then be re-used by using get_item().
 * Only objects whose base_name is ITEM_OBJECT can be recycled.
 * @param ob the item object to recycle
 * @return 1 if successfully recycled, 0 if not
 * @see get_item()
 */
int recycle_item( object ob ) {
    if( base_name(ob) == ITEM_OBJECT ) {
        reload_object(ob);
        recycled_items += ({ ob });
        return 1;
    }

    return 0;

} /* recycle_item() */

/**
 * This method gets a recycled item object from the handler,
 * or clones a new one, if needed.  This is used by rooms.
 * @return an item object
 * @see recycle_item()
 */
object get_item() {
    object ret;

    if( sizeof( recycled_items -= ({ 0 }) ) ) {
        ret = recycled_items[0];
        recycled_items = recycled_items[1..];
        item_hits++;
    } else {
        ret = clone_object(ITEM_OBJECT);
        item_misses++;
    }

    ret->set_my_room(PO);

    return ret;

} /* get_item() */

/** @ignore yes */
object *query_recycled_chatters() { return recycled_chatters; }

/** @ignore yes */
object *query_recycled_doors() { return recycled_doors; }

/** @ignore yes */
object *query_recycled_items() { return recycled_items; }

/** @ignore yes */
void dest_me() {
    if( sizeof( recycled_chatters -= ({ 0 }) ) )
        recycled_chatters->dest_me();
    if( sizeof( recycled_doors -= ({ 0 }) ) )
        recycled_doors->dest_me();
    if( sizeof( recycled_items -= ({ 0 }) ) )
        recycled_items->dest_me();
    destruct(TO);
} /* dest_me() */

/**
 * @ignore yes
 * Survive the updates.
 */
mapping query_dynamic_auto_load() {
    return ([
        "exit_types" : exit_types,
        "door_types" : door_types,
        "opposite"   : opposite,
        "chatters"   : chatters,
        "doors"      : doors,
    ]);
} /* query_dynamic_auto_load() */

/**
 * @ignore yes
 * Survive the updates.
 */
void init_dynamic_arg( mapping maps ) {
    if( maps["exit_types"] )
        exit_types = maps["exit_types"];
    if( maps["door_types"] )
        door_types = maps["door_types"];
    if( maps["opposite"] )
        opposite = maps["opposite"];
    if( maps["chatters"] )
        chatters = maps["chatters"];
    if( maps["doors"] )
        doors = maps["doors"];
} /* init_dynamic_arg() */

/** @ignore yes */
mixed stats() {
    int door_count, chatter_count;
    int doors_missing, chatters_missing, items_missing;
    mixed temp;

    foreach( temp in chatters )
        chatter_count += sizeof( temp );

    foreach( temp in doors )
        door_count += sizeof( temp );

    doors_missing -= sizeof( children(DOOR_OBJECT) -
        ({ find_object(DOOR_OBJECT) }) ) - door_misses;
    chatters_missing -= sizeof( children(CHATTER_OBJECT) -
        ({ find_object(CHATTER_OBJECT) }) ) - chatter_misses;
    items_missing -= sizeof( children(ITEM_OBJECT) -
        ({ find_object(ITEM_OBJECT) }) ) - item_misses;

    return ({
        ({"exit types", sizeof( exit_types ) }),
        ({"door types", sizeof( door_types ) }),
        ({"opposites",  sizeof( opposite ) }),
        ({"chatters",  chatter_count }),
        ({"doors", door_count }),
        ({"recycled chatters", sizeof(recycled_chatters) }),
        ({"recycled doors", sizeof(recycled_doors) }),
        ({"recycled items", sizeof(recycled_items) }),
        ({"chatter hits", chatter_hits }),
        ({"door hits", door_hits }),
        ({"item hits", item_hits }),
        ({"chatter misses", chatter_misses }),
        ({"door misses", door_misses }),
        ({"item misses", item_misses }),
        ({"missing chatters", chatters_missing > 0 ? chatters_missing : 0 }),
        ({"missing doors", doors_missing > 0 ? doors_missing : 0 }),
        ({"missing items", items_missing > 0 ? items_missing : 0 }),
        ({"total hits", chatter_hits + door_hits + item_hits }),
        ({"total misses", chatter_misses + door_misses + item_misses }),
    });

} /* stats() */