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 code to support and run the text parsing
 * system used by discworld.  This is called 'add_command', please see
 * help on add_command for a more detailed listing.
 * @author Pinkfish
 */

#include <soul.h>
#include <creator.h>
#include <user_parser.h>
#include <command.h>
#include <obj_parser.h>

inherit "/global/player/command";

#define NEW_SOUL

#define MY_MESS_HEADER "#!"
#define OTHER_MESS_HEADER "!#"

class fail_mess_data {
   object* direct;
   object* indirect;
   int weight;
}

private nosave mapping _succ_mess, _fail_mess, _cur_objects, _commands;
private nosave object *_succ_indir, *_succ_mess_dir, *_succ_mess_indir;
private nosave string *_failed_mess, _curpat;

int *pattern_match( string *bits, mixed pattern );
int handle_command( string *bits, int *matches, mixed pattern, mixed command );
string get_fail_messages( string verb, object *fail_obs );
void print_special_messages( string verb );
string *query_word_list( string bing );
varargs int remove_object2( mixed ob, int flag );
private void remove_object_force( object ob );
varargs string create_message( string *bits, int *matches, mixed pattern,
                               object *dir_obs, int flag );
int syntax_messages( string str );
string query_name();

void create() {
    _commands = ([ ]);
    _fail_mess = ([ ]);
    _succ_mess = ([ ]);
    _cur_objects = ([ ]);
    _succ_indir = _succ_mess_dir = _succ_mess_indir = ({ });
    command::create();
} /* create() */

/**
 * This method returns the current internal set of commands.
 * <p>
 * ([ "command_name" :<br>
 * ({ ({ pattern_weight, pattern_str, nn, object, function }) })<br>
 * ])
 * @return the current commands list
 * @see query_p_objects()
 */
mapping query_p_commands() { return _commands; }

/**
 * This method returns the current mapping between objects and commands.<br>
 * ([ object : ({ "cmd1", "cmd2", ... }), ... ])
 * <p>
 * This mapping is used when the object leaves the environment to make
 * the command updating more efficent.
 * @return the current object/command mapping
 * @see query_p_commands()
 */
mapping query_p_objects() { return _cur_objects; }

/**
 * This method returns the information associated with the specific
 * command.
 * @param name the command name to return info on
 * @return the information associated with the command
 */
mixed query_parse_command(string name) { return _commands[name]; }

/**
 * This method returns all the indirect objects used in the success
 * messages.
 * @return the success message
 */
object *query_succ_mess_indir() { return _succ_mess_indir; }

/** @ignore yes */
void parser_commands() {
    add_command("syntax", "<word'verb'>", (: syntax_messages($4[0]) :) );
} /* parser_commands() */

/**
 * This is called by the object the command is being passed on to find
 * whether or not it succeeded on the objects it was passed... and which
 * ones. This can be passed an object.. or an array of objects.
 * Share and enjoy.
 */
int add_succeeded( mixed ob ) {
    object thing;

    if( !ob )
        error("No object specified in add_succeeded.\n");

    if( !pointerp(_succ_indir) )
        _succ_indir = ({ });

    if( objectp(ob) ) {
        if( member_array( ob, _succ_indir ) == -1 )
            _succ_indir += ({ ob });
        return 1;
    }

    if( !pointerp(ob) )
        return 0;

    foreach( thing in ob )
        if( member_array( thing, _succ_indir ) == -1 )
            _succ_indir += ({ thing });

    return 1;

} /* add_succeeded() */

// Mess can also be an array, containing two elements.
int add_succeeded_mess( object dir, mixed incoming_mess, object *in_dir ) {
    string my_mess, other_mess;

    if( !dir )
        error("No object specified in add_succeeded_mess.\n");

    if( !incoming_mess )
        error("No message specified in add_succeeded_mess.\n");

    if( !pointerp(incoming_mess) ) {
        if( stringp(incoming_mess) ) {
            my_mess = MY_MESS_HEADER + incoming_mess;
            other_mess = OTHER_MESS_HEADER + incoming_mess;
        } else {
            error("Message argument to add_succeeded_mess() must be either a "
                "string or an array.\n");
            return 0;
        }
    } else if( sizeof(incoming_mess) == 2 ) {
        my_mess = MY_MESS_HEADER + incoming_mess[0];
        other_mess = OTHER_MESS_HEADER + incoming_mess[1];
    } else {
        write("Message array to add_succeeded_mess() must be two elements "
            "long.\n");
        return 0;
    }

    if( undefinedp( in_dir ) )
        in_dir = ({ });

    if( !_succ_mess[my_mess] ) {
        _succ_mess[my_mess] = ({ ({ dir }), in_dir });
    } else {
        if( member_array( dir, _succ_mess[my_mess][0] ) == -1 )
            _succ_mess[my_mess][0] += ({ dir });
        in_dir = in_dir - _succ_mess[my_mess][1];
        _succ_mess[my_mess][1] += in_dir;
    }

    if( !_succ_mess[other_mess] ) {
        _succ_mess[other_mess] = ({ ({ dir }), in_dir });
    } else {
        if( member_array( dir, _succ_mess[other_mess][0] ) == -1 )
            _succ_mess[other_mess][0] += ({ dir });
        in_dir = in_dir - _succ_mess[other_mess][1];
        _succ_mess[other_mess][1] += in_dir;
    }

    if( member_array( dir, _succ_mess_dir ) == -1 )
        _succ_mess_dir += ({ dir });

    return 1;

} /* add_succeeded_mess() */

int add_failed_mess( object dir, string mess, object *in_dir ) {
    object ob;
    class fail_mess_data fail;

    if( !dir )
        error("No object specified in add_failed_mess.\n");

    if( !mess )
        error("No message specified in add_failed_mess.\n");

    if( !stringp(mess) ) {
        write("Parameter to add_failed_mess() must be a string.\n");
        return 0;
    }

    if( undefinedp( in_dir ) )
        in_dir = ({ });

    if( !_fail_mess[mess] ) {
        fail = new( class fail_mess_data );
        fail->direct = ({ dir });
        fail->indirect = in_dir;
        _fail_mess[mess] = fail;
    } else {
        if( member_array( dir, _fail_mess[mess]->direct ) == -1 )
            _fail_mess[mess]->direct += ({ dir });
        foreach( ob in in_dir )
            if( member_array( ob, _fail_mess[mess]->indirect ) == -1 )
                _fail_mess[mess]->indirect += ({ ob });
    }

    if( member_array( dir, _succ_mess_indir ) == -1 )
        _succ_mess_indir += ({ dir });

    return 1;

} /* add_failed_mess() */

/**
 * This method returns the objects which have success messages already
 * attached for.  This allows you to determine which objects already hace
 * a success message available.  This array is added to by both the
 * add_succeeded_mess and add_failed_mess methods, it disable the
 * autogeneration of these messages.
 * @return the succeeded message objects
 * @see add_succeeded_mess()
 * @see add_failed_mess()
 */
object *query_succ_mess_dir() { return _succ_mess_dir; }

/**
 * This method checks to see if the given object has already added a failed
 * message yet or not.  This is checking for a direct object, not an
 * indirect object.
 * @param dir the object adding the failed message
 * @return 0 if not found, 1 if found
 * @see add_failed_mess()
 */
int query_failed_message_exists( object dir ) {
    string mess;
    class fail_mess_data stuff;

    foreach( mess, stuff in _fail_mess )
        if( member_array( dir, stuff->direct ) != -1 )
            return 1;

    return 0;

} /* query_failed_message_exists() */

/*
 * The id is a useful thingy so that things can remember
 * which pattern was parsed.
 */
varargs int add_command( string command, object ob, mixed format, function funct ) {
    int *found, i, j;
    string *old_format;
    mixed pattern;

    if( !command )
        error("No command specified in add_command.\n");

    if( !ob )
        error("No object specified in add_command.\n");

    if( funct && !functionp(funct) )
        error("Invalid function specified in add_command.\n");

    if( !format )
        format = ({"<direct:object>"});

    if( stringp(format) )
        format = ({ format });

    if( !i = sizeof(format) )
        return 0;

    found = allocate( i );
    old_format = allocate( i );

    // Change the old format strings to the new sort.
    for( i = 0; i < sizeof(format); i++ ) {
        if( member_array('%', format[i] ) != -1 ) {
            old_format[i] = format[i];
            // Should fix all of the thingies.
            format[i] = replace( format[i], ({
                "%D", "<direct:object>", "%I", "<indirect:object>",
                "%s", "<string>", "%p", "<preposition>",
                "%d/%d", "<fraction>", "%d", "<number>",
                "%w", "<word>", "'", "", " / ", "|"}) );
        }
    }

    if( _commands[command] ) {
        for( i = 0; i < sizeof(_commands[command]); i++ ) {
            j = member_array( _commands[command][i][PATTERN_STRING], format );
            if( j != -1 ) {
                int idx;
                // Its already in there...  Easy.
                // Let's check whether the object is already in there.
                // It shouldn't be, but it doesn't hurt :-)
                idx = member_array( ob, _commands[command][i] );
                if( idx != -1 ) {
                    _commands[command][i][idx] = ob;
                    _commands[command][i][idx + 1] = funct;
                } else
                    _commands[command][i] += ({ ob, funct });
                found[j] = 1;
            }
        }
    }

    for( i = 0 ;i < sizeof(format); i++ ) {
        if( !found[i] ) {
            // Its not there.  Damn.  Ok, now for the tricky bits.
            // We put in here a promise to create a pattern...
            pattern = PATTERN_OB->query_pattern( format[i] );
            if( !pattern )
                return 0;
            // Now. We need to use the weight information to place the
            // pattern into the array.
            if( !_commands[command] )
                _commands[command] = ({ });
            for( j = 0; j < sizeof(_commands[command]); j++ ) {
                if( _commands[command][j][PATTERN_WEIGHT] < pattern[0] ) {
                    if( !j )
                       _commands[command] = ({ ({ pattern[0], format[i],
                           old_format[i], ob, funct }) }) +
                           _commands[command][j..];
                    else
                        _commands[command] = _commands[command][0..j-1] +
                            ({ ({ pattern[0], format[i], old_format[i],
                            ob, funct, }) }) + _commands[command][j..];
                    break;
                }
            }
            if( j == sizeof(_commands[command]) )
                _commands[command] += ({ ({ pattern[0], format[i],
                    old_format[i], ob, funct, }) });
        }
    }

    if( _cur_objects[ob] ) {
        if( member_array( command, _cur_objects[ob] ) == -1 )
            _cur_objects[ob] += ({ command });
    } else
        _cur_objects[ob] = ({ command });

    return 1;

} /* add_command() */


/*
 * This is the bit that handles object leaving the environment of the
 * player.
 */
int remove_object( mixed ob, int was_env ) {
    if( objectp(ob) && !_cur_objects[ob] && !was_env )
        return 0;
    remove_object2( ({ ob, was_env }) );
    return 1;
} /* remove_object() */

/**
 * This method does all the horrible work of removing objects
 * and being evil.
 * The bit that does all the horrible work...
 * We check in here to make sure we havent come back to the same spot.
 * This is so we don't go round deleteing things we shouldn't.
 */
varargs int remove_object2( mixed ob, int flag ) {
    int was_env;

    if( pointerp(ob) ) {
        was_env = ob[1];
        ob = ob[0];
    }

    // Either we are in the same room, or the object exists in our inventory
    // or in the inventory of the room we are in...
    if( !ob || ( !flag && ( environment() == ob || environment(ob) == TO ||
        ENV(ob) == environment() ) ) )
        return 0;

    if( was_env )
        return sizeof( filter( ob->find_inv_match() + ({ ob }),
            (: remove_object2($1) :) ) );

    remove_object_force(ob);
    return 1;

} /* remove_object2() */

/**
 * This method will remove a lost object and all zeroed objects for a
 * specific command.
 */
private void remove_from_command( string cmd, object ob ) {
    int k, j;

    if( _commands[cmd] ) {
        // Go through all the formats one bye one.
        // Deleting both this object and any dested ones it runs accross.
        for( j = 0; j < sizeof(_commands[cmd]); j++ ) {
            for( k = OBJS; k < sizeof(_commands[cmd][j]); k += 2 ) {
                if( !_commands[cmd][j][k] || _commands[cmd][j][k] == ob ) {
                    _commands[cmd][j] = _commands[cmd][j][0..k-1] +
                                        _commands[cmd][j][k+2..];
                    k -= 2;
                }
            }
            if( sizeof( _commands[cmd][j]) == OBJS ) {
                _commands[cmd] = ( !j ? _commands[cmd][1..] :
                    _commands[cmd][0..j-1] + _commands[cmd][j+1..] );
                j--;
            }
        }
        // Check to see if the entire command goes after deletion of formats.
        if( !sizeof(_commands[cmd]) )
            map_delete( _commands, cmd );
    }

} /* remove_from_command() */

/**
 * This will force the removal of the specified object from the array.
 */
private void remove_object_force( object ob ) {
    string ind;

    // Check to see if our illustious object exists or not.
    if( !_cur_objects[ob] )
        return 0;

    foreach( ind in _cur_objects[ob] )
        remove_from_command(ind, ob);

    // Delete the object from the object table.
    map_delete( _cur_objects, ob );

} /* remove_object_force() */

/** @ignore yes */
void event_dest_me( object ob ) {
    // Don't call this out as otherwise we end up with a 0 which may
    // mess up the mapping a bit.
    if( !_cur_objects[ob] )
        return ;

    remove_object2( ob, 1 );

} /* event_dest_me() */

/*
 * Handle leaving.  Check to see if it is me leaving.  If it is, then
 * remove all the objects from the inventory.
 */
/** @ignore yes */
void event_exit( object ob, string mess, object dest ) {
    if( dest == TO || dest == environment() )
        return;
    remove_object2( ob, 1 );
} /* event_exit() */

/** @ignore yes */
void me_moveing( object from ) {
    // Use this so as to get the hidden objects as well.
    if( from )
        remove_object( from, 1 );
} /* me_moveing() */

/**
 * This method does all the real work for add_command parsing.
 */
nomask int new_parser( string str ) {
    string *bits, new_str;
    mixed wombat, *soul_stuff, *pattern, *command_stuff, *stuff;
    int i, j, t, ret, flag;

    // A hook for shorthand commands.
    if( strlen(str) ) {
        switch( str[0] ) {
          case ''' :
            str = "say "+str[1..];
          break;
          case '\"' :
            str = "lsay "+str[1..];
          break;
          case ':' :
            str = "emote "+str[1..];
          break;
          default :
        }
    }

    /*
     * First explode the input string into words.  The first word is the
     * command.  Test to see if it exists...
     */
    bits = explode( str, " ") - ({ "", 0 });

    if( !sizeof(bits) )
        return 0;

    if( sizeof(bits) == 1 && bits[0] == "," )
        return 0;

    command_stuff = cmdPatterns(bits[0]);
    // Check to see if the player has the afk flag and
    // if so, remove it along with the afk message.
    if( bits[0] != "afk" && TO->query_property("afk") &&
        !sizeof( filter( previous_object(-1),
              (: file_name($1) == SOUL_OBJECT  :) ) ) ) {
        TO->remove_property("afk");
        TO->remove_property("afk_string");
        write("You are no longer listed as being afk.\n");
    }

    if( TO->command_shadowed(bits[0], implode(bits[1..], " ") ) )
        return 1;

    if( CHANNEL_H->do_chat( bits[0], implode(bits[1..], " ") ) )
        return 1;

    new_str = implode( bits, " ");
    if( strsrch( new_str, ",," ) != -1 ) {
        for( t = 0; t < 1; t++ ) {
                  new_str = replace_string( new_str, ",,", ",");
            if( strsrch( new_str, ",," ) != -1 )
                t--;
              }
        bits = explode( new_str, " ") - ({ "", 0 });
    }

    if( sizeof( explode( new_str, "," ) ) < 2 ) {
        new_str = replace_string( str, ",", "");
        bits = explode( new_str, " ") - ({ "", 0 });
    }


#ifdef NEW_SOUL
    if( !_commands[bits[0]] && !sizeof(command_stuff) ) {
        soul_stuff = SOUL_OBJECT->query_soul_command(bits[0]);
        if( !soul_stuff )
            return 0;
    }
#else
    if( !_commands[bits[0]] && !sizeof(command_stuff) )
        return 0;
#endif
    // Only the word...
    _failed_mess = ({ "", "" });
#ifdef NOPE
    if( sizeof(bits) == 1 && !soul_stuff ) {
        // No args, check to see if there are any no arg strings.
        for( i = 0; i < sizeof(_commands[bits[0]]); i++ )
             if( _commands[bits[0]][i][PATTERN_STRING] == "" )
                 break;

        // Check the command stuff...
        if( i >= sizeof(_commands[bits[0]]) ) {
            for( j = 0; j < sizeof(command_stuff); j++ )
                if( command_stuff[j][PATTERN_STRING] == "" )
                    break;
        }

        if( i >= sizeof(_commands[bits[0]] ) && j >= sizeof(command_stuff) ) {
            if( _failed_mess[1] == "" )
                notify_fail( ( _failed_mess[0] == "" ? "See \"syntax "+
                    bits[ 0 ]+"\" for the input patterns.\n" :
                    _failed_mess[0] ) );
            else
                notify_fail(_failed_mess[1]);
            return 0;
        }
    }
#endif

    // This allows other code to find out what verb has been used,
    // since query_verb() will always return "" (verb is *).
    current_verb = bits[ 0 ];

    // Ok, we have found a command.  There may be one or more commands
    // stored in this array.  Starting from the first one we check to
    // see if our pattern matches.
    if( !soul_stuff ) {
        for( i = 0, j = 0; i < sizeof(_commands[bits[0]]) ||
            j < sizeof(command_stuff); ) {
            // Check both patterns groups to see which should go first.
            // It at the same level, the ones setup elsewhere should have
            // precedence.
            if( j >= sizeof(command_stuff) ||
                ( i < sizeof(_commands[bits[0]] ) &&
                _commands[bits[0]][i][PATTERN_WEIGHT] >=
                command_stuff[j][PATTERN_WEIGHT]) ) {
                stuff = _commands[bits[0]][i++];
                flag = 0;
            } else {
                stuff = command_stuff[j++];
                flag = 1;
            }

            pattern = PATTERN_OB->query_pattern(stuff[PATTERN_STRING]);
            if( !pattern )
                continue;

            wombat = pattern_match( bits, pattern );
            if( wombat && !sizeof(wombat) ) {
                int cont;
                string pat;
                mixed junk;

                if( _curpat[<2] == '}' || _curpat[<2] == ']' ) {
                    _curpat = ( _curpat[<2] == '}' ?
                        _curpat[0..strsrch(_curpat, '{', -1)-2] :
                        _curpat[0..strsrch(_curpat, '[', -1)-2] );
                } else {
                    _curpat = implode( ( explode( _curpat, " ") -
                        ({ 0, "" }) )[0..<2], " ");
                }

                if( flag ) {
                    foreach (junk in command_stuff[j..]) {
                        pat = junk[PATTERN_STRING];
                        pat = replace( pat, ({
                            ":object", "", ":living", "", ":any-living", "",
                            ":distant-living", "", ":here", "", ":me>", ">",
                            ":here-me", "", ":me-here", ""
                            }) );
                        if( _curpat == pat ) {
                            cont = 1;
                            break;
                        }
                    }
                    if( !cont )
                        j = 100;
                } else {
                    foreach( junk in _commands[bits[0]][i..] ) {
                        pat = junk[PATTERN_STRING];
                        pat = replace( pat, ({
                            ":object", "", ":living", "", ":any-living", "",
                            ":distant-living", "", ":here", "", ":me>", ">",
                            ":here-me", "", ":me-here", ""
                            }) );
                        if( _curpat == pat ) {
                            cont = 1;
                            break;
                        }
                    }
                    if( !cont )
                        i = 100;
                }
                continue;
            }

            if( wombat &&
                ( ret = handle_command( bits, wombat, pattern, stuff ) ) ) {
                if( ret == -1 ) {
                    if( flag )
                        j = 100;
                    else
                        i = 100;
                    continue;
                }
                return 1;
            }
        }
    }

#ifdef NEW_SOUL
    if( !soul_stuff )
        soul_stuff = SOUL_OBJECT->query_soul_command(bits[0]);

    for( i = 0; i < sizeof(soul_stuff); i++ ) {
        pattern = PATTERN_OB->query_pattern(soul_stuff[i][PATTERN_STRING]);
        wombat = pattern_match( bits, pattern );
        if( wombat &&
            handle_command( bits, wombat, pattern, soul_stuff[i] ) )
            return 1;
    }
#endif
    // This will construct useful error messages.
    if( _failed_mess[1] == "" ) {
        if( !query_notify_fail() )
            notify_fail( ( _failed_mess[0] == "" ? "See \"syntax "+bits[ 0 ]+
                "\" for the input patterns.\n" : _failed_mess[0] ) );
    } else {
        notify_fail( _failed_mess[1] );
    }

    _fail_mess = ([ ]);
    return 0;

} /* new_parser() */

int syntax_messages( string str ) {
    string the_mess;
    mixed soul_stuff, command_stuff, tmp;

    if( !str ) {
        notify_fail("Syntax: syntax <verb>\n");
        return 0;
    }

    command_stuff = cmdPatterns(str);
    soul_stuff = SOUL_OBJECT->query_soul_command(str);

    if( member_array( str, TO->query_channels() ) != -1 ) {
        CHANNEL_H->channel_syntax(str);
        return 1;
    }

    if( !_commands[str] && !soul_stuff && !sizeof(command_stuff) ) {
        notify_fail("Could not find the verb '"+str+"'.\n");
        return 0;
    }

    the_mess = "Forms of syntax available for the command \""+ str +"\":\n";

    if( sizeof(_commands[str]) )
        foreach( tmp in _commands[str] )
            the_mess += str+" "+PATTERN_OB->query_short_pattern(
                        tmp[PATTERN_STRING])+"\n";

    if( sizeof(command_stuff) )
        foreach( tmp in command_stuff )
            the_mess += str+" "+PATTERN_OB->query_short_pattern(
                        tmp[PATTERN_STRING])+"\n";

    if( sizeof(soul_stuff) )
        foreach( tmp in soul_stuff )
            the_mess += str+" "+PATTERN_OB->query_short_pattern(
                        tmp[PATTERN_STRING])+"\n";

    write(the_mess);
    return 1;

} /* syntax_messages() */

int *pattern_match( string *bits, mixed pattern ) {
    string *elms;
    int *delayed, *matches;
    int pos, last, failed, j, opt, i, word_offset, spaces, wcount;
    mixed tmp;

    // Ok, we need to check to see if the nice pattern we have in
    // comm exists somewhere in bits.
    pos = wcount = 1;
    _curpat = "";
    matches = ({ 0 });
    delayed = ({ });

    for( i = 1; i < sizeof(pattern) && !failed; i++ ) {
        // Ok, check matching rules.
        if( pos >= sizeof(bits) )
            failed = 1;

        _curpat += PATTERN[pattern[i]];

        switch( pattern[i] ) {
          case DIRECT_OBJECT :
          case INDIRECT_OBJECT :
            i++; // Move to the type marker.
            if( pattern[i] == TARGET_PLAYER ) {
                if( last ) {
                    delayed += ({ SINGLE_WORD });
                    word_offset++;
                    // Push the search position forward one.
                    // It needs to be there at least.
                    pos++;
                } else {
                    pos++;
                    matches += ({ pos - 1 });
                }
                failed |= pos > sizeof(bits);
            } else {
                // We don't know where this one ends, so we don't
                // put a match onto the thingy.
                if( last ) {
                    delayed += ({ SINGLE_WORD });
                } else {
                    delayed = ({ STRING });
                    last = FIND_FIRST;
                }
                word_offset++;
                // All strings must be at least 1 word long.
                pos++;
            }
            i++; // Skip the environment marker.
          break;
          case STRING :
            // This here delayed should not exist!
            if( last ) {
                pos -= word_offset-1;
                for( j = 0; j < sizeof(delayed); j++ ) {
                    switch( delayed[j] ) {
                      case STRING :
                        matches += ({ pos - 1 });
                        pos++;
                      break;
                      case OPTIONAL :
                        matches += ({ matches[sizeof(matches) - 1] });
                      break;
                      case SINGLE_WORD :
                        matches += ({ pos - 1 });
                        pos++;
                      break;
                    }
                }
            }
            delayed = ({ STRING });
            word_offset = 1;
            // Strings should be at least one word long.
            pos++;
            last = FIND_LAST;
          break;
          case QUOTED_STRING :
            if( last ) {
                while( pos < sizeof(bits) && bits[pos][0] != '"' &&
                    bits[pos][0] != '\'' && bits[pos][0] != '`') {
                    pos++;
                }

                if( pos < sizeof(bits) ) {
                    pos -= word_offset;
                    for( j = 0; j < sizeof(delayed); j++ ) {
                        switch( delayed[j] ) {
                          case STRING :
                            matches += ({ pos });
                            pos++;
                          break;
                          case OPTIONAL :
                            matches += ({ matches[sizeof(matches) - 1] });
                          break;
                          case SINGLE_WORD :
                            matches += ({ pos });
                            pos++;
                          break;
                        }
                    }
                    last = FIND_NONE;
                } else {
                    failed = 1;
                }
            }

            if( pos > sizeof(bits) )
                failed = 1;

            if( !failed ) {
                switch( bits[pos][0] ) {
                  case '"' :
                  case '\'' :
                  case '`' :
                    // Ok.  See if we can find the end...
                    j = pos;
                    while( j < sizeof(bits) &&
                        bits[j][strlen(bits[j]) - 1] != bits[pos][0] ) {
                        j++;
                    }
                    if( j < sizeof(bits) ) {
                        matches += ({ j });
                        pos = j + 1;
                    } else {
                        failed = 1;
                    }
                  break;
                  default :
                    failed = 1;
                    break;
                }
            }
          break;
          case SHORT_STRING :
            if( last ) {
                delayed += ({ SINGLE_WORD });
            } else {
                delayed = ({ STRING });
            }
            word_offset++;
            // Strings have to be at least one word.
            pos++;
            last = FIND_FIRST;
          break;
          case SINGLE_WORD :
            // The last bit carrys through here too.
            if( last ) {
                // Some sort of delay.
                // Hmm, need to keep track of the delayed and single_word
                // status of each bit we push.
                delayed += ({ SINGLE_WORD });
                word_offset++;
                // Push the search position forward one.
                // It needs to be there at least.
                pos++;
            } else {
                // Means the word we are pointing at must be the one we want.
                // Womble!
                matches += ({ pos });
                pos++;
            }
            failed |= pos > sizeof(bits);
          break;
          case NUMBER :
          case FRACTION :
            failed = 1;
            if( last == FIND_LAST ) {
                for( j = sizeof(bits) - 1; j >= pos; j-- )
                    if( ( bits[j][0] >= '0' && bits[j][0] <= '9' ) ||
                        ( pattern[i] != FRACTION && bits[j][0] == '-' &&
                        bits[j][1] >= '0' && bits[j][1] <= '9' ) ) {
                        // Found number!
                        if( pattern[i] != FRACTION ||
                            sizeof( explode( bits[j], "/") ) > 1 ) {
                            failed = 0;
                            // We always point to the next match.
                            pos = j + 1;
                            break;
                        }
                    }
            } else if( last == FIND_FIRST ) {
                for( j = pos; j < sizeof(bits); j++ )
                    if( ( bits[j][0] >= '0' && bits[j][0] <= '9' ) ||
                        ( pattern[i] != FRACTION && bits[j][0] == '-' &&
                        bits[j][1] >= '0' && bits[j][1] <= '9') ) {
                        // Found number!
                        if( pattern[i] != FRACTION ||
                            sizeof( explode( bits[j], "/") ) > 1 ) {
                            failed = 0;
                            // We always point to the next match.
                            pos = j + 1;
                            break;
                        }
                    }
            } else {
                if( pos < sizeof(bits) && ( ( bits[pos][0] >= '0' &&
                    bits[pos][0] <= '9' ) || ( pattern[i] != FRACTION &&
                    bits[pos][0] == '-' && bits[pos][1] >= '0' &&
                    bits[pos][1] <= '9' ) ) ) {
                    failed = 0;
                    pos++;
                } else {
                    failed = 1;
                }
            }

            if( !failed ) {
                if( sizeof(delayed) ) {
                    // Amount which is needed at least to handle the pattern.
                    pos -= word_offset;
                    for( j = 0; j < sizeof(delayed); j++ ) {
                        switch( delayed[j] ) {
                          case STRING :
                            matches += ({ pos - 1 });
                            pos++;
                          break;
                          case OPTIONAL :
                            matches += ({ matches[sizeof(matches) - 1] });
                          break;
                          case SINGLE_WORD :
                            matches += ({ pos - 1 });
                            pos++;
                          break;
                        }
                    }
                    delayed = ({ });
                    word_offset = 0;
                }
                last = FIND_NONE;
                matches += ({ pos - 1 });
            }
          break;
          case OPTIONAL_SPACES :
          case OPTIONAL :
          case WORD_LIST_SPACES :
            switch( pattern[i] ) {
              case OPTIONAL_SPACES :
                spaces = opt = 1;
              break;
              case OPTIONAL:
                opt = 1;
              break;
              case WORD_LIST_SPACES :
                spaces = 1;
              break;
            }
          case WORD_LIST :
            // Find word list. The next element is the list name.
            // ---
            // How do we find a list?  Big question?
            // Currently thought of method.  Use the array subtraction
            // operation to find if there is any intersection.
            // ---
            // Method 2: Use member_array a lot.
            if( pointerp(pattern[++i]) ) {
                string *words;

                if( spaces ) {
                    tmp = "";

                    foreach( words in pattern[i] )
                        tmp += implode( words, " ") + "|";
                    tmp = tmp[0..<2];
                }
                _curpat += ( sizeof(pattern[i]) > 1 ? "{" : "");
                _curpat += ( spaces ? tmp : implode( pattern[i], "|") )+
                    ( opt ? "] " : ( sizeof(pattern[i]) > 1 ? "} " : " ") );
                elms = pattern[i];
            } else {
                _curpat += pattern[i] + ( opt ? "] " : "} ");
                elms = (string *)master()->query_word_list( pattern[i] );
                if( !elms ) {
                    // Could be a local word list then?
                    elms = query_word_list(pattern[i]);
                }
            }

            if( !pointerp(elms) || !sizeof(elms) )
                failed = 1;
            else {
                if( !( last || failed || spaces ) ) {
                    // Means that the word we are pointing at must
                    // be one of em!
                    tmp = member_array( bits[pos], elms );
                    if( tmp == -1 )
                        failed = 1;
                } else if( sizeof(elms) == 1 && last == FIND_FIRST &&
                    !spaces ) {
                    // Only one word.
                    // Definitely quicker to do a member_array.
                    tmp = member_array( elms[0], bits[pos..] );
                    if( tmp != -1 )
                        pos += tmp;
                    else
                        failed = 1;
                } else if( !spaces ) {
                    tmp = bits[pos..] - elms;
                    if( sizeof(tmp) < sizeof(bits)-pos ) {
                        // Ok, one exists...
                        if( last == FIND_FIRST ) {
                            for( j = 0; ( j + pos ) < sizeof(bits) &&
                                 j < sizeof(tmp) && bits[j+pos] == tmp[j];
                                 j++ );
                            pos += j;
                        } else {
                            int k;
                            for( j = sizeof(tmp)-1, k = sizeof(bits)-1;
                                 j >= 0 && bits[k] == tmp[j]; j--, k-- );
                            pos = k;
                        }
                    } else {
                        failed = 1;
                    }
                } else {
                    string *elem;
                    int success;

                    foreach( elem in elms ) {
                        if( !last ) {
                            // Means that the words we are pointing at
                            // must be one of em!
                            if( implode(bits[pos..pos+sizeof(elem)-1], " ") ==
                                implode(elem, " ") ) {
                                success = 1;
                                wcount = sizeof(elem);
                                break;
                            }
                            failed = 1;
                        } else {
                            tmp = bits[pos..] - elem;
                            if( sizeof(tmp) <=
                                ( sizeof(bits) - pos - sizeof(elem) ) ) {
                                // Okay, one exists...
                                success = 1;
                                wcount = sizeof(elem);
                                if( last == FIND_FIRST ) {
                                    for( j = 0; ( j + pos ) < sizeof(bits) &&
                                         j < sizeof(tmp) &&
                                         bits[j+pos] == tmp[j]; j++ );
                                    pos += j;
                                    break;
                                } else {
                                    int k;
                                    for( j = sizeof(tmp)-1,
                                         k = sizeof(bits)-1;
                                         j >= 0 && bits[k] == tmp[j]; j--,
                                         k-- );
                                    pos = k - wcount + 1;
                                    break;
                                }
                            } else {
                                failed = 1;
                            }
                        }
                    }
                    if( success )
                        failed = 0;
                }
            }

            if( opt && failed ) {
                failed = 0;
                if( !last )
                    // Keep the -1 for the optional stuff..?
                    matches += ({ pos - 1 });
                else {
                    delayed += ({ OPTIONAL });
                    opt = 0;
                    spaces = 0;
                    break;
                }
            } else {
                if( !failed )
                    pos += wcount;
                if( sizeof(delayed) && !failed ) {
                    // Amount which is needed at least to handle the pattern.
                    pos -= word_offset;
                    for( j = 0; j < sizeof(delayed); j++ ) {
                        switch( delayed[j] ) {
                          case STRING :
                            matches += ({ pos - 1 });
                            pos++;
                          break;
                          case OPTIONAL :
                            matches += ({ matches[sizeof(matches) - 1] });
                          break;
                          case SINGLE_WORD :
                            matches += ({ pos - 1 });
                            pos++;
                          break;
                        }
                    }
                    delayed = ({ });
                    word_offset = 0;
                }
                // Only matching one word... So the next item must be ours.
                if( !failed ) {
                    last = FIND_NONE;
                    matches += ({ pos - 1 });
                }
                wcount = 1;
                delayed = ({ });
            }
            opt = 0;
            spaces = 0;
            last = FIND_NONE;
            break;
        }
    }

    if( sizeof(delayed) ) {
        pos = sizeof(bits) + 1;
        // Amount which is needed at least to handle the pattern.
        // Last pos should be the end thingy.
        pos -= word_offset;
        for( j = 0; j < sizeof(delayed); j++ ) {
            switch( delayed[j] ) {
              case OPTIONAL :
                matches += ({ matches[sizeof(matches) - 1] });
              break;
              case SINGLE_WORD :
              case STRING :
                matches += ({ pos - 1 });
                pos++;
              break;
            }
        }
        delayed = ({ });
        word_offset = 0;
        pos = sizeof(bits);
    }

    matches += ({ sizeof(bits) });

    if( failed || pos != sizeof(bits) )
        return 0;

    return matches + ({ sizeof(bits)+1 });

} /* pattern_match() */

int check_living( object ob ) {
    return living(ob);
} /* check_living() */

int check_if_creator( object ob ) {
    return ob != TP && creatorp(ob) && ob->query_visible(TP);
} /* check_if_creator() */

int check_if_allowed( object ob ) {
    return ob != TP && reference_allowed( ob, TP );
} /* check_if_allowed() */

/** @ignore yes */
class obj_match my_find_match( string pattern, object *where ) {
    object ob;
    class obj_match omatch;

    where = filter( where, (: $1 && ( !$1->query_closed() ||
        ( $1->query_closed() && !$1->query_property("opaque") ) ) :) );

    if( !sizeof(where) ) {
        omatch = new( class obj_match );
        omatch->text = lower_case(pattern);
        omatch->objects = ({ });
        omatch->result = OBJ_PARSER_BAD_ENVIRONMENT;
        return omatch;
    }

    omatch = (class obj_match)match_objects_in_environments(
        pattern, where, 0, TO );

    if( omatch->result != OBJ_PARSER_SUCCESS && creatorp(TO) ) {
        ob = find_object(pattern);
        if( ob && member_array( ENV(ob), where ) != -1 ) {
            omatch->text = lower_case(pattern);
            omatch->objects = ({ ob });
            omatch->result = OBJ_PARSER_SUCCESS;
        }
    }

    return omatch;

} /* my_find_match() */

private class obj_match match_objects( int type, string pattern, object *env ) {
    class obj_match omatch;
    object *liv;
    int i;

    switch( type ) {
      case WIZ_PRESENT_TARGET :
        omatch = new( class obj_match );
        omatch->text = pattern;
        omatch->objects = WIZ_PRESENT->wiz_present( pattern, env[0] );
        omatch->result = ( sizeof(omatch->objects) ? OBJ_PARSER_SUCCESS :
                                                     OBJ_PARSER_NO_MATCH );
      break;
      case ANY_OBJECT :
        omatch = my_find_match( pattern, env );
      break;
      case DISTANT_LIVING :
        omatch = new( class obj_match );
        omatch->text = pattern;

        if( environment() && !creatorp(TO) &&
            environment()->query_property("no remote") ) {
            omatch->objects = ({ });
            omatch->result = OBJ_PARSER_BAD_ENVIRONMENT;
            break;
        }

        omatch->objects = filter( map( explode( lower_case(pattern), ","),
            (: find_living( (string)TO->expand_nickname($1) ) :) ),
            (: $1 && check_if_allowed($1) :) );
        omatch->result = ( sizeof(omatch->objects) ? OBJ_PARSER_SUCCESS :
                                                     OBJ_PARSER_NO_MATCH );
      break;
      case LIVING :
        omatch = my_find_match( pattern, env );

        if( !sizeof( omatch->objects ) ) {
            omatch->result = OBJ_PARSER_NO_MATCH;
            break;
        }

        liv = filter( omatch->objects, "check_living", TO );

        if( !sizeof(liv) ) {
            omatch->result = OBJ_PARSER_NOT_LIVING;
            break;
        }

        omatch->objects = liv;
        omatch->result = OBJ_PARSER_SUCCESS;

      break;
      case TARGET_PLAYER :
        omatch = new( class obj_match );
        omatch->text = pattern;

        if( environment() && !creatorp(TO) &&
            environment()->query_property("no remote") ) {
            omatch->objects = ({ });
            omatch->result = OBJ_PARSER_BAD_ENVIRONMENT;
            break;
        }

        omatch->objects = filter( map( explode( lower_case(pattern), ","),
            (: find_player( (string)TO->expand_nickname($1) ) :) ),
            (: $1 && check_if_allowed($1) :) );

        if( !sizeof(omatch->objects) ) {
            if( pattern == "creators" && creatorp(TO) )
                omatch->objects = filter( users(), (: check_if_creator($1) :) );
            if( pattern == "someone" ) {
                omatch->objects = filter( users(), (: check_if_allowed($1) :) );
                if( ( i = sizeof(omatch->objects) ) > 1 )
                    omatch->objects = ({ omatch->objects[random(i)] });
            }
        }

        omatch->result = ( sizeof(omatch->objects) ? OBJ_PARSER_SUCCESS :
                                                     OBJ_PARSER_NO_MATCH );

      break;
      case ANY_LIVING :
        omatch = my_find_match( pattern, env );

        if( omatch->result == OBJ_PARSER_SUCCESS ) {
            liv = filter( omatch->objects, "check_living", TO );
            if( !sizeof(liv) ) {
                omatch->result = OBJ_PARSER_NOT_LIVING;
            } else {
                omatch->objects = liv;
                omatch->result = OBJ_PARSER_SUCCESS;
            }
        }

        if( omatch->result != OBJ_PARSER_SUCCESS ) {
            omatch = new( class obj_match );
            omatch->text = pattern;

            if( environment() && !creatorp(TO) &&
                environment()->query_property("no remote") ) {
                omatch->objects = ({ });
                omatch->result = OBJ_PARSER_BAD_ENVIRONMENT;
            } else {
                omatch->objects = filter( map( explode(
                    lower_case(pattern), ","),
                    (: find_living( (string)TO->expand_nickname($1) ) :) ),
                    (: $1 && check_if_allowed($1) :) );
                omatch->result = ( sizeof(omatch->objects) ?
                    OBJ_PARSER_SUCCESS : OBJ_PARSER_NO_MATCH );
            }
        }

        if( omatch->result != OBJ_PARSER_SUCCESS ) {
            omatch = new( class obj_match );
            omatch->text = pattern;

            if( pattern == "creators" && creatorp(TO) )
                omatch->objects = filter( users(),
                    (: check_if_creator($1) :) );
            if( pattern == "someone") {
                omatch->objects = filter( users(),
                    (: check_if_allowed($1) :) );
                if( ( i = sizeof(omatch->objects) ) > 1 )
                    omatch->objects = ({ omatch->objects[random(i)] });
            }
            omatch->result = ( sizeof(omatch->objects) ?
                OBJ_PARSER_SUCCESS : OBJ_PARSER_NO_MATCH );
        }
    }

    if( omatch && sizeof(omatch->objects) )
        omatch->objects = uniq_array(omatch->objects);

    if( !omatch ) {
        omatch = new( class obj_match );
        omatch->text = pattern;
        omatch->objects = ({ });
        omatch->result = OBJ_PARSER_NO_MATCH;
    }

    return omatch;

} /* match_objects() */

void setup_failed_mess( class obj_match failed_match ) {
    _failed_mess[0] += match_objects_failed_mess(failed_match);
} /* setup_failed_mess() */

int handle_command( string *bits, int *matches, mixed pattern, mixed command ) {
    string *add_comm_bit;
    object *env, *fail_ob;
    int p, failed, i, j, k;
    class obj_match omatch;
    class obj_match direct_obs;
    class obj_match failed_match;
    mixed start, indirect_obs, ret, stuff, indir_match, bity;
    string dir_match, pattern_str, fail_mess_check, fail_mesg, succ_mesg;

    if( !sizeof(matches) )
        return 0;

    /*
     * Ok, we have a match of some sort.  First, do we have any objects
     * to match?  If we do.  Look for them to make sure they
     * really do exist.
     */
    start = 1;
    add_comm_bit = ({ });
    indirect_obs = ({ });
    direct_obs = new( class obj_match );
    direct_obs->objects = ({ });

    for( i = 1, p = 1; i < sizeof(pattern) && !failed_match; i++, p++ ) {
        int env_direct_obs = 0;

        switch( pattern[i] ) {
          case DIRECT_OBJECT :
            dir_match = implode( bits[start..matches[p]], " ");
            if( intp(pattern[i+2]) ) {
                switch (pattern[i+2]) {
                  case ENV_ME :
                    env = ({ TO });
                  break;
                  case ENV_HERE :
                    env = ({ environment() });
                  break;
                  case ENV_HERE_ME :
                    env = ({ environment(), TO });
                  break;
                  case ENV_ME_HERE :
                    env = ({ TO, environment() });
                  break;
                }
            } else if( stringp(pattern[i+2]) ) {
                ret = load_object( pattern[i+2] );
                if( ret )
                    env = ({ ret });
            }

            direct_obs = match_objects( pattern[++i], dir_match, env );

            i++; // Skip environment.
            add_comm_bit += ({ dir_match });
            start = matches[p];
            if( direct_obs->result != OBJ_PARSER_SUCCESS )
                failed_match = direct_obs;
            break;
          case INDIRECT_OBJECT :
            pattern_str = implode( bits[start..matches[p]], " ");
            if( intp(pattern[i+2]) ) {
                switch (pattern[i+2]) {
                  case ENV_ME :
                    env = ({ TO });
                  break;
                  case ENV_HERE :
                    env = ({ environment() });
                  break;
                  case ENV_HERE_ME :
                    env = ({ environment(), TO });
                  break;
                  case ENV_ME_HERE :
                    env = ({ TO, environment() });
                  break;
                }
            } else if( stringp(pattern[i+2]) ) {
                ret = load_object(pattern[i+2]);
                if( ret )
                    env = ({ ret });
            }

            if( pattern[i+2] == ENV_DIRECT_OBS ) {
                bity = pattern[i+2];
                i++;
                if( !sizeof(bity) && !intp(bity) ) {
                    omatch = new( class obj_match );
                    omatch->text = pattern_str;
                    omatch->objects = ({ });
                    omatch->result = OBJ_PARSER_NO_MATCH;
                } else {
                    omatch = new( class obj_match );
                    omatch->text = pattern_str;
                    env_direct_obs = bity;
                    omatch->result = OBJ_PARSER_SUCCESS;
                }
            } else {
                omatch = match_objects( pattern[++i], pattern_str, env );
            }

            i++; // Skip environment specifier.
            add_comm_bit += ({ pattern_str });
            if( omatch->result != OBJ_PARSER_SUCCESS )
                failed_match = omatch;
            else {
                if( stringp(indir_match) ) {
                    if( pointerp(indir_match) ) {
                        // Already an array.
                        indirect_obs += ({ omatch->objects || env_direct_obs });
                        indir_match += ({ pattern_str });
                    } else {
                        // Make an array.
                        indirect_obs = ({ indirect_obs, omatch->objects || env_direct_obs });
                        indir_match = ({ indir_match, pattern_str });
                    }
                } else {
                    indirect_obs = omatch->objects || env_direct_obs;
                    indir_match = pattern_str;
                }
            }
            start = matches[p];
          break;
          case SINGLE_WORD :
          case STRING :
          case SHORT_STRING :
            add_comm_bit += ({ implode( bits[start..matches[p]], " ") });
          break;
          case QUOTED_STRING :
            pattern_str = implode( bits[start..matches[p]], " ");
            add_comm_bit += ({ pattern_str[1..<2] });
          break;
          case NUMBER :
            sscanf( implode( bits[start..matches[p]], " "), "%d", j );
            add_comm_bit += ({ j });
          break;
          case FRACTION :
            sscanf( implode( bits[start..matches[p]], " "), "%d/%s", j, k );
            add_comm_bit += ({ j, k });
          break;
          case OPTIONAL_SPACES :
          case OPTIONAL :
            i++; // Skip optional bits.
          break;
          case WORD_LIST_SPACES :
          case WORD_LIST :
            // These are variable and therefore there must be
            // something associated with them.
            if( pointerp(pattern[++i]) && sizeof(pattern[i]) > 1 )
                add_comm_bit += ({ implode( bits[start..matches[p]], " ") });
          break;
        }
        start = matches[p]+1;
    }

    if( failed_match ) {
        if( query_notify_fail() ) {
            _failed_mess[1] += query_notify_fail();
            notify_fail(0);
        } else {
            setup_failed_mess(failed_match);
        }
        return !matches[0] - 1;
    }

    pattern_str = ( command[OLD_PATTERN_STRING] ?
                    command[OLD_PATTERN_STRING] :
                    command[PATTERN_STRING] );

    _fail_mess = ([ ]);
    _succ_mess = ([ ]);
    _succ_mess_dir = ({ });
    _succ_mess_indir = ({ });
    ret = 0;

    // If this only has a direct-obs method in it, then assume the
    // direct object is the one that defined the method.
    if( !sizeof(direct_obs->objects) && ( indirect_obs == ENV_DIRECT_OBS ||
        sizeof( indirect_obs & ({ ENV_DIRECT_OBS }) ) ) ) {
        // Grab all the direct ones.
        direct_obs->objects = ({ });
        for( i = OBJS; i < sizeof(command); i += 2 )
            direct_obs->objects += ({ command[i] });
    }

    if( !sizeof(direct_obs->objects) ) {
        // Cycle through each object.  Finish when one handles the command.
        for( i = OBJS; i < sizeof(command) && !ret; i += 2 ) {
            // Straight command thingy.  No direct objects to handle at all.
            // Ok, what we do in this case is call the function on
            // the set object.
            if( !add_comm_bit ) {
                add_comm_bit = ({ });
                start = 1;
                for( j = 0; j < sizeof(matches); j++ ) {
                    add_comm_bit += implode( bits[start..matches[j]], " ");
                    start = matches[j]+1;
                }
            }

            if( functionp(command[i+1]) ) {
                ret = evaluate( command[i+1], indirect_obs, dir_match,
                      indir_match, add_comm_bit, pattern_str, bits[0] );
            } else {
                start = command[i];
                if( !start ) {
                    remove_object_force(start);
                    if( start == command[i] )
                        remove_from_command( bits[0], start );
                    ret = 0;
                    continue;
                }
                do {
                    if( function_exists("do_"+bits[0], start ) ) {
                        ret = call_other( start, "do_"+bits[0], indirect_obs,
                            dir_match, indir_match, add_comm_bit,
                            pattern_str );
                        break;
                    } else {
                        start = shadow( start, 0 );
                    }
                } while( start );

                if( !start ) {
                    ret = call_other( command[i], "command_control", bits[0],
                        indirect_obs, dir_match, indir_match, add_comm_bit,
                        pattern_str );
                }
            }
        }
        // Since there are no direct objects.  We don't actually auto generate
        // a message.  That is left up to the object in question.
        if( !ret || ret == -1 ) {
            if( query_notify_fail() ) {
                _failed_mess[1] += query_notify_fail();
                notify_fail(0);
            } else {
                fail_mess_check = get_fail_messages( bits[ 0 ], ({ }) );
                if( _failed_mess[1] == "")
                    _failed_mess[1] += fail_mess_check;
            }
        }

        if( _succ_mess_dir )
            print_special_messages( bits[ 0 ] );

        _fail_mess = ([ ]);
        return ret;
    }

    // Ok, now we have some direct objects.
    // This means we are now working very close to the way that
    // the old add_command did.  Exactly the same actually.
    // ---
    // We go over each direct object and call a function on it.
    // This return value we use to auto generate a success message.
    // However, you can also set your own success message.
    fail_ob = ({ });
    bity = ({ });
    failed = 0;

    for( i = 0; i < sizeof(direct_obs->objects); i++ ) {
        // Check to make sure this object has the command added to it.
        j = member_array( direct_obs->objects[i], command, OBJS );
        if( j == -1 ) {
            fail_ob += ({ direct_obs->objects[i] });
            continue;
        }
        // Only allow objects with a short.
        if( !direct_obs->objects[i]->short() )
            continue;

        if( intp(indirect_obs) ) {
            failed_match = my_find_match( indir_match,
                ({ direct_obs->objects[i] }) );
            if( failed_match->result == OBJ_PARSER_SUCCESS )
                stuff = failed_match->objects;
        } else if( pointerp(indir_match) ) {
            failed_match = 0;
            stuff = copy(indirect_obs);
            for( k = 0; k < sizeof(indirect_obs); k++ ) {
                if( intp(stuff[k]) ) {
                    failed_match = my_find_match( indir_match[k],
                        ({ direct_obs->objects[i] }) );
                    if( failed_match->result != OBJ_PARSER_SUCCESS )
                        break;
                    stuff[k] = failed_match->objects;
                }
            }
        } else {
            stuff = indirect_obs;
            failed_match = 0;
        }

        if( failed_match && failed_match->result != OBJ_PARSER_SUCCESS ) {
            setup_failed_mess(failed_match);
            failed = 1;
            continue;
        }

        if( functionp(command[j+1]) ) {
            ret = evaluate( command[j+1], stuff, dir_match, indir_match,
                add_comm_bit, pattern_str, bits[0] );
        } else {
            // See if our command object was dested.
            start = direct_obs->objects[i];
            if( !start ) {
                remove_object_force(start);
                if( start == command[i] )
                    remove_from_command( bits[0], start );
                ret = 0;
                continue;
            }

            // Check to see if the object has the "do_"+verb function.
            do {
                if( function_exists("do_"+bits[0], start ) ) {
                    ret = call_other( start, "do_"+bits[0], stuff,
                          dir_match, indir_match, add_comm_bit, pattern_str );
                    break;
                } else {
                    start = shadow( start, 0 );
                }
            } while( start );

            if( !start ) {
                ret = call_other( direct_obs->objects[i], "command_control",
                    bits[0], stuff, dir_match, indir_match, add_comm_bit,
                    pattern_str );
            }
        }

        // Figure out what to do with the return value.
        if( stringp(ret) || pointerp(ret) ) {
            bity += ({ ret });
        } else if( ret && ret != -1 ) {
            bity += ({ direct_obs->objects[i] });
        } else {
            fail_ob += ({ direct_obs->objects[i] });
            if( ret == -1 )
                failed = 1;
        }
    }

    // Ok, with this type, we auto generate success and fail messages.
    if( !sizeof(bity) ) {
        // Failure!  We never got any non zero return values.
        fail_mess_check = get_fail_messages( bits[ 0 ], fail_ob );
        if( query_notify_fail() ) {
            _failed_mess[1] += query_notify_fail();
            notify_fail(0);
        } else if( _failed_mess[1] == "" ) {
            // Only generate a fail message if we do not already have one.
            _failed_mess[1] += fail_mess_check;
            if( _failed_mess[1] == "" && sizeof(fail_ob) ) {
                notify_fail(0);
                fail_mesg = create_message( bits, matches, pattern,
                    fail_ob, 1 );
                if( !pointerp(indirect_obs) ) {
                    _failed_mess[1] += "You cannot "+bits[ 0 ]+replace_string(
                        fail_mesg, "$succ_indir$", ( pointerp( indir_match ) ?
                        query_multiple_short( indir_match ) : indir_match ) );
                } else {
                    if( pointerp(indir_match) ) {
                        bity = explode("F"+fail_mesg, "$succ_indir$");
                        _failed_mess[1] += "You cannot "+bits[ 0 ];
                        bity[0] = bity[0][1..];
                        for( i = 0; i < sizeof(bity) - 1; i++ ) {
                            if( i >= sizeof(indir_match) ) {
                                _failed_mess[1] += bity[i]+
                                    query_multiple_short( ( stuff &&
                                    pointerp(stuff[<1]) ? stuff[<1] :
                                    indir_match[<1] ) );
                            } else {
                                _failed_mess[1] += bity[i]+( stuff &&
                                    pointerp(stuff[i]) ? query_multiple_short(
                                    stuff[i] ) : indir_match[i] );
                            }
                        }
                        _failed_mess[1] += bity[<1];
                    } else {
                        if( member_array( TP, indirect_obs ) == -1 ) {
                            _failed_mess[1] += "You cannot "+bits[ 0 ]+
                            replace_string( fail_mesg, "$succ_indir$",
                            query_multiple_short( indirect_obs, "the") );
                        } else {
                            _failed_mess[1] += "You cannot "+bits[ 0 ]+
                            replace_string( fail_mesg, "$succ_indir$",
                            query_multiple_short( indirect_obs - ({ TP })+
                            ({ "yourself" }), "the") );
                        }
                    }
                }
            }
        }
        _fail_mess = ([ ]);
        return !failed - 1;
    }

    // We succeeded somewhere.  So print our success stuff.
    // Use > here, otherwise we print the default stuff even if
    // _succ_mess_dir has more messages in it than bity.
    if( sizeof(bity) > sizeof(_succ_mess_dir) ) {
        // Auto generated success messages.
        succ_mesg = create_message( bits, matches, pattern, bity -
            _succ_mess_dir );
        if( member_array( TP, _succ_indir ) == -1 ) {
            write("You "+bits[ 0 ]+replace( succ_mesg, "$succ_indir$",
                query_multiple_short( _succ_indir, "one") ) );
            stuff = _succ_indir;
        } else {
            write("You "+bits[0]+replace( succ_mesg, "$succ_indir$",
                query_multiple_short( _succ_indir - ({ TP }) + ({"yourself"}),
                "one") ) );
            stuff = _succ_indir - ({ TP });
            stuff = ({ (string)TP->HIM+"self" }) + stuff;
        }
        say( (string)TP->the_short()+" "+pluralize(bits[0])+
            replace( succ_mesg, "$succ_indir$", query_multiple_short(
            stuff, "one") ), _succ_indir );
        for( i = 0; i < sizeof( _succ_indir ); i++ ) {
            if( _succ_indir[ i ] != TP ) {
                tell_object( _succ_indir[ i ], (string)TP->the_short()+" "+
                    pluralize(bits[0]) + replace( succ_mesg, "$succ_indir$",
                    query_multiple_short( stuff - ({ _succ_indir[i] })+
                    ({"you"}), "one") ) );
            }
        }
        // And print any special messages too.
        if( sizeof(_succ_mess_dir) )
            print_special_messages( bits[ 0 ] );
    } else {
        // Creator generated success messages.
        print_special_messages( bits[ 0 ] );
    }

    _succ_indir = ({ });
    _succ_mess = ([ ]);
    _succ_mess_dir = ({ });

    return 1;

} /* handle_command() */

void print_special_messages( string verb ) {
    int i, j;
    string words, *messes;
    string type;
    mixed stuff, *tmp;

    messes = keys( _succ_mess );
    for( i = 0; i < sizeof( messes ); i++ ) {
        if( functionp( messes[i] ) || ( messes[i][0..1] == MY_MESS_HEADER &&
            strlen(messes[i]) > 2 ) ) {
            // 0 as the first arg means, this_player().  Write message.
            if( functionp(messes[i]) ) {
                words = evaluate( messes[i], 0 );
            } else {
                words = messes[i][2..];
            }

            type = "one";

            if( strsrch("$Iposs$", words ) != -1 ) {
                type = "poss";
                words = replace_string( words, "$Iposs$", "");
            }

            if( strsrch("$Ithe$", words ) != -1 ) {
                type = "the";
                words = replace_string( words, "$Ithe$", "");
            }

            if( strsrch("$Ia$", words ) != -1 ) {
                type = "a";
                words = replace_string( words, "$Ia$", "");
            }

            if( member_array( TP, _succ_mess[ messes[ i ] ][ 1 ] ) == -1 ) {
                words = CAP( replace( words, ({
                    "$C$", "$CATFROG", "$N", "you", "$p ", "your ",
                    "$r", "you", "$o", "your", "$V", verb, "$es", "",
                    "$s", "", "$I", query_multiple_short(
                    _succ_mess[ messes[ i ] ][ 1 ], type ) }) ) );
            } else {
                words = CAP( replace( words, ({
                    "$C$", "$CATFROG", "$N", "you", "$p ", "your ",
                    "$r", "you", "$o", "your", "$V", verb, "$es", "",
                    "$s", "", "$I", query_multiple_short(
                    (mixed)_succ_mess[ messes[ i ] ][ 1 ] - ({ TP }) +
                    ({ "yourself" }), type ) }) ) );
            }

            write( replace( words, ({
                "$D", query_multiple_short( _succ_mess[ messes[ i ] ][ 0 ],
                "one"), "$CATFROG", "$C$"}) ) );

        } else if( messes[i][0..1] == OTHER_MESS_HEADER &&
            strlen(messes[i]) > 2 ) {
            if( functionp(messes[i]) ) {
                words = replace( evaluate(messes[i], 1 ), ({
                    "$C$", "$CATFROG", "$N", (string)TP->the_short(),
                    "$p ", (string)TP->query_possessive()+" ",
                    "$r", (string)TP->query_pronoun(),
                    "$o", (string)TP->query_objective(),
                    "$V", pluralize( verb ), "$es", "es", "$s", "s"
                    }) );
            } else {
                words = replace( messes[i][2..], ({
                    "$C$", "$CATFROG", "$N", (string)TP->the_short(),
                    "$p ", (string)TP->query_possessive() +" ",
                    "$r", (string)TP->query_pronoun(),
                    "$o", (string)TP->query_objective(),
                    "$V", pluralize( verb ), "$es", "es", "$s", "s"
                    }) );
            }

            words = replace( words, "$D",
                query_multiple_short( _succ_mess[ messes[ i ] ][ 0 ],
                "one") );

            if( member_array( TP, _succ_mess[ messes[i] ][ 1 ]) == -1 ) {
                stuff = _succ_mess[messes[i]][1];
            } else {
                stuff = ({ (string)TP->query_objective()+"self" });
                stuff += (mixed)_succ_mess[ messes[ i ] ][ 1 ] - ({ TP });
            }

            type = "one";

            if( strsrch("$Iposs$", words ) != -1 ) {
                type = "poss";
                words = replace_string( words, "$Iposs$", "");
            }

            if( strsrch("$Ithe$", words ) != -1 ) {
                type = "the";
                words = replace_string( words, "$Ithe$", "");
            }

            if( strsrch("$Ia$", words ) != -1 ) {
                type = "a";
                words = replace_string( words, "$Ia$", "");
            }

            say( CAP( replace( words, ({
                "$I", query_multiple_short( stuff, type ),
                "$CATFROG", "$C$" }) ) ), _succ_mess[ messes[ i ] ][ 1 ] );
            for( j = 0; j < sizeof( _succ_mess[ messes[ i ] ][ 1 ] ); j++ ) {
                if( _succ_mess[ messes[ i ] ][ 1 ][ j ] != TP ) {
                    if( strsrch( words, "$I's") != -1 ) {
                        tmp = stuff - _succ_mess[ messes[ i ] ][ 1 ][ j..j ];
                        tell_object( _succ_mess[ messes[ i ] ][ 1 ][ j ],
                            CAP( replace( words, ({ "$I's",
                                query_multiple_short( tmp + ({"your"}) ),
                                "$I", query_multiple_short( tmp + ({"you"}),
                                "one"), "$CATFROG", "$C$"
                                }) ) ) );
                    } else {
                        tell_object( _succ_mess[ messes[ i ] ][ 1 ][ j ],
                            CAP( replace( words, ({ "$I",
                            query_multiple_short( stuff - ({
                            _succ_mess[ messes[ i ] ][ 1 ][ j ] })+
                            ({"you"}), "one"), "$CATFROG", "$C$"
                            }) ) ) );
                    }
                }
            }
        }
    }
} /* print_special_messages() */

/*
 * Don't know if I will need this.  I guess I do though.  Oh well,
 * so it explodes.
 */
string get_fail_messages( string verb, object *fail_obs ) {
    string whole, words, mess, *str;
    object ob;
    class fail_mess_data data;

    whole = "";
    foreach( mess, data in _fail_mess ) {
        if( !stringp( mess ) )
            continue;

        str = ({ });

        foreach( ob in data->direct )
            str += ({"one_short:"+file_name(ob) });

        words = TP->evaluate_message( ({ mess, ({ str }) }) );
        words = replace( words, ({
            "$D", query_multiple_short( data->direct, "one"),
            "$V", verb
            }) );

        if( member_array( TP, data->indirect ) == -1 )
            words = replace( words, "$I", query_multiple_short(
                             data->indirect, "one") );
        else
            words = replace( words, "$I", query_multiple_short(
                             data->indirect - ({ TP }) + ({"yourself"}),
                             "one") );
        whole += "$C$"+words;
    }

    return whole;

} /* get_fail_messages() */

/** @ignore yes */
string *query_word_list( string list ) { return 0; }

varargs string create_message( string *bits, int *matches, mixed pattern,
                               object *dir, int flag ) {
    string ret;
    int i, pos;

    ret = " ";

    if( matches[0] )
        matches[0] = 0;

    for( i = 1; i <sizeof(pattern); i++, pos++ ) {
        switch( pattern[i] ) {
          case DIRECT_OBJECT :
            if( member_array( TP, dir ) != -1 )
                ret += query_multiple_short( dir - ({ TP }) + ({"yourself"}),
                    ( flag ? "a" : "one") );
            else
                ret += query_multiple_short( dir, ( flag ? "a" : "one") );
            i += 2;
          break;
          case INDIRECT_OBJECT :
            ret += "$succ_indir$";
            i += 2;
          break;
          case SHORT_STRING :
          case STRING :
            ret += implode( bits[matches[pos]+1..matches[pos+1]], " ");
          break;
          case WORD_LIST_SPACES :
            pos += matches[pos+1]-1;
          case WORD_LIST :
            ret += implode( bits[matches[pos]+1..matches[pos+1]], " ");
            i++;
          break;
          case NUMBER :
          case FRACTION :
          case SINGLE_WORD :
            // Can only be one word...  Must be this one.
            ret += bits[matches[pos]+1];
          break;
          case OPTIONAL_SPACES :
            if( !matches[pos] == matches[pos+1] )
                pos += matches[pos+1]-1;
          case OPTIONAL :
            if( matches[pos] == matches[pos+1] ) {
                if( pointerp(pattern[i+1][0]) )
                    ret += implode( pattern[i+1][0], " ");
                else
                    ret += pattern[i+1][0];
            } else {
                ret += implode( bits[matches[pos]+1..matches[pos+1]], " ");
            }
            i++;
          break;
        }
        if( i + 1 < sizeof(pattern) )
            ret += " ";
    }

    return ret+".\n";

} /* create_mesage() */