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/
/**
 * The main login object.
 * <p>
 * Originally written by Pinkfish, who knows when.
 * <p>
 * Mangled bunches of times by Pinkfish since then and various other wombles
 * all over the place.
 * <p>
 * Put in the random names and the delete character menu options in
 * January 1996.
 * <p>
 * Got excited again in June 1996 and modified the random name stuff
 * and made the new player ban code a bit keener - Pinkfish.
 * <p>
 * So excited in January 1997 that I wrote a login queue.  Most of it
 * works.  Bug fixing in progress forever.
 * @author Pinkfish
 * @changed A bunch of changes concerning logging in while there is a
 * shut-down and logging in invisible. Also added support for restricting
 * login access - Sandoz, 2003.
 */

#include <random_names.h>
#include <login_handler.h>
#include <clubs.h>
#include <access.h>

inherit "/global/player/family";

#define QUOTE_HANDLER "/handlers/pqf_handler"

#define MAX_RETRIES   3
#define MIN_LEN       3
#define MAX_LEN       12

#undef DEBUG
#undef LOOKUP_IDENT

#ifdef LOOKUP_IDENT
#define IDENT         "/net/daemon/out_auth"
#endif

#define GUEST         1
#define NEW           2
#define CURRENT       3
#define DELETE        4
#define UNDELETE      5

#define LOGON_MENU    1
#define RANDOM_OR_NOT 2
#define MAIN_RANDOM   3
#define RANDOM_NAME   4

#define TIMEOUT_TIME  120
#define THROWOUT_TIME (3*TIMEOUT_TIME)

inherit OBJECT_OBJ;

int cols;
int Str;
int Dex;
int gender;
int creator;
int time_on;
int start_time;

string password;
string title;
string cap_name;
string last_on_from;

mapping new_skills;
mapping aliases;

nosave int auto_authorise;
nosave int go_invis;
nosave int no_times;
nosave int the_rows;
nosave int the_cols;
nosave int login_port;
nosave int login_start_time;
nosave string player_ob;
nosave string terminal_name;
nosave string *random_names;
nosave object other_copy;
nosave object new_copy;

// Used to get the property stuff while not logged on working.
nosave string prop_var_name = "map_prop ";

// For login queue handling.
nosave int can_login;
nosave int login_num;
nosave int net_dead;
nosave string name;

#ifdef LOOKUP_IDENT
nosave string my_ident;
nosave int got_return;
nosave object ident_player_ob;
#endif

mapping restore_stats;

private varargs void logon_menu();
protected void delete_character_menu();
void enter_name( string str, string prompt, int type, mixed *args );
varargs void name_menu(int guest);
protected void delete_character_menu();
void random_name_menu(string lang, int guest, int re_gen);
void main_random_name_menu(int guest);
protected void logon3();
protected void logon4(int bing);
protected void logon5(int new_pl);
protected void begin(int new_pl);
protected void begin2(int new_player);
protected void try_throw_out(int new_pl);
string query_player_file_name(string name);
string query_delete_player_file_name(string name);
protected void check_terms(string str, int new_pl);
void finger_player(string str);
void just_name_menu(int guest);

void create() {
    ::create();
    cols = 79;
    gender = -1;
    Str = -1;
    Dex = -1;
    start_time = time();
    map_prop = ([ ]);
    aliases = ([ ]);
    seteuid( "Root" );
    set_name( "logon" );
    player_ob = "/global/player";
} /* create() */

private void set_name( string nam ) {
    name = nam;
} /* set_name() */

/** @ignore yes */
string query_name() { return name; }

/** @ignore yes */
int query_cols() { return cols; }

/** @ignore yes */
string query_title() { return "the title-less"; }

/** @ignore yes */
int query_creator() { return creator; }

/** @ignore yes */
string query_cap_name() { return cap_name || "Logon"; }

/** @ignore yes */
int query_login_port() { return login_port; }

/** @ignore yes */
void set_login_port( int port ) {
    if( master() == PO )
        login_port = port;
} /* set_login_port() */

private int check_valid_name( string str ) {
    int i;

    for( i = 0; i < strlen(str); i++ ) {
        if( str[i] < 'a' || str[i] > 'z')
            return i;
    }

    return -1;

} /* check_valid_name() */

/** @ignore yes */
void logon() {

    if( SHUTDOWN_H->query_shutdown() < 1 ) {
        write( mud_name()+" is in the process of shutting down.\n"
            "Please try again in a few minutes...\n");
        return dest_me();
    }

    call_out("time_out", TIMEOUT_TIME );
    login_start_time = time();

    if( LOGIN_H->is_discworld_full(0) ) {
        write("***  Warning there are no player slots available  ***\n"
              "***   If you login you will be placed in a queue  ***\n");
    }

#ifdef LOOKUP_IDENT
    // This is a bit of a hassle.  Make it less likely to blow up.
    catch( IDENT->query_auth("finished_auth") );
#endif

    if( TP == TO )
        cat( choice( ({ "/doc/login/LOGON",
                        "/doc/login/LOGON2",
                        "/doc/login/LOGON3" }) ) );
//          cat("/doc/login/XMASLOGON");


    logon_menu( 1 );

} /* logon() */

/** @ignore yes */
private varargs void logon_menu( int flag ) {
    string mess;
    int i;

    if( !flag )
        write("\n");

    if( ( i = SHUTDOWN_H->query_shutdown() ) < 60 ) {
        write( repeat_string("-", cols )+"\n");
        switch( i ) {
          case 1..60 :
            write( sprintf("%|=*s", cols, mud_name()+" is shutting down "
                "in "+i+" minutes.\n") );
          break;
          default :
            write( sprintf("%|=*s", cols, mud_name()+" is shutting down "
                "NOW!\n") );
        }
        write( repeat_string("-", cols )+"\n\n");
    }

    if( i = LOGIN_H->query_login_mode() )
        write( mud_name()+" is currently running in "+
            LOGIN_H->get_login_mode( i )+".\n\n");

    // Check if the IP has been locked down due to excessive login failures.
    if( LOGIN_H->site_lockeddown(query_ip_number(TO)) ) {
        write("Sorry, your site has been locked down for excessive failed "
            "login attempts.  If you have forgotten your password please "
            "email "+ADMIN_EMAIL+".  If you have not failed to login, please "
            "try again later.\n");

        mess = sprintf("Attempted login from locked down site: %s.",
            query_ip_number(TO) );

        log_file("BAD_PASSWORD", "%s %s\n", ctime(time()), mess );
        event( efun::users(), "inform", mess, "bad-password");
        dest_me();
    }

        
    write("Please choose one of the following options:\n"
      "Q - Quit               M - Print this menu again\n"
      "N - New character      U - Short list of who is on-line\n"
      "G - Guest character    D - Delete your character\n"
      "F - Finger someone\n\n"
      "Or, enter your current character's name\n\n");

    write("Your choice [D,F,G,M,N,U,Q,<name>]: " );

    input_to("login_choice");

} /* logon_menu() */

/** @ignore yes */
void login_choice( string bing ) {
    int i;

    bing = lower_case(bing);

    while( bing[0] == ' ' || bing[0] == '\t')
        bing = bing[1..];

    if( !strlen(bing) )
        bing = "z";

    // Long string and not of the format 'f name' (for finger).
    if( strlen(bing) >= MIN_LEN && bing[0..1] != "f ") {
        // It is probably someone entering a name.
        return enter_name( bing, 0, CURRENT, ({ LOGON_MENU }) );
    }

    switch( bing[0] ) {
      case 'n' :
        if( i = LOGIN_H->query_login_mode() ) {
            write("Sorry, you cannot create a new character while "+
                mud_name()+" is running in "+LOGIN_H->get_login_mode(i)+".  "
                "Please try again later.\n");
            break;
        }

        if( BASTARDS_H->no_new_players(TO) ) {
            write("New player logins are disabled from this site.  "
                "If you wish to create a character, please email "+
                ADMIN_EMAIL+" with the player name you would like to "
                "use.\n");
            break;
        }

        write( LOGIN_H->get_message("/doc/login/NEW_USER") );
        name_menu(NEW);
        return;
      case 'g' :
        if( i = LOGIN_H->query_login_mode() ) {
            write("Sorry, you cannot login as a guest while "+
                mud_name()+" is running in "+LOGIN_H->get_login_mode(i)+".  "
                "Please try again later.\n");
            break;
        }

        if( BASTARDS_H->no_new_players(TO) ) {
            write("New player logins are disabled from this site.  "
                "If you wish to create a character, please email "+
                ADMIN_EMAIL+".\n");
            break;
        }

        write( LOGIN_H->get_message("/doc/login/GUEST_LOGIN") );
        write("Enter the name to use: ");
        input_to("enter_name", 0, "Enter the name to use: ", GUEST,
            ({ LOGON_MENU }) );
        return;
      case 'm' :
        logon_menu();
        return;
      case 'q' :
        write("Come back soon!\n");
        dest_me();
        return;
      case 'd' :
        delete_character_menu();
        return ;
      case 'u' :
        write("Here is a list of the people currently playing "+
            mud_name()+":\n"+implode( sort_array( map( filter( users(),
                (: !$1->query_invis() :) ), (: $1->query_cap_name() :) ),
                (: strcmp :) ), ", ")+"\n");
        break;
      case 'f' :
        bing = bing[1..];

        if( bing[0] == ' ') {
            while( bing[0] == ' ')
                bing = bing[1..];
            finger_player(bing);
        } else {
            write("Who do you wish to finger? ");
            input_to("finger_player");
        }
        return;
      default :
        write("Incorrect choice.\n");
        break;
    }

    write("Your choice [M,N,G,U,D,F,Q,<name>]: ");

    input_to("login_choice");
    return;

} /* login_choice() */

/** @ignore yes */
void finger_player( string str ) {
    string ret;

    if( stringp(str) && strlen(str) >=  MIN_LEN ) {
        if( !ret = FINGER_H->finger_info( str, 1 ) )
            write("I am sorry, the player "+str+" does not appear to "
                "exist.\n");
        else
            write( ret );
    } else {
        write("Invalid name, returning to the login menu.\n");
    }

    write("Your choice [M,N,G,U,D,F,Q,<name>]: ");

    input_to("login_choice");
    return;

} /* finger_player() */

/** @ignore yes */
protected void delete_character_menu() {
    write( LOGIN_H->get_message("/doc/login/DELETE") );
    write("\n\nThis is for PERMANENT deletion of your character.\n\n"
        "Which character would you like to delete? " );
    input_to("enter_name", 0, 0, DELETE, ({ LOGON_MENU }) );
} /* delete_character_menu() */

/**
 * @ignore yes
 * Name is set to the name of the character to delete.
 */
protected void delete_character() {
    if( find_player(query_name()) ) {
        write("\n\n"+query_name()+" is currently logged in, unable to "
            "delete.\n\n");
        logon_menu();
        return;
    }

    // To delete ourselves, we move the player file from where it is now
    // into the deleted character dir.
    if( !unguarded( (: rename, query_player_file_name(query_name())+".o",
        query_delete_player_file_name( query_name() )+".o" :) ) ) {
        // Update the time.
        unguarded( (: write_file,
            query_delete_player_file_name(query_name())+".o", "junk 0\n" :) );
        write("\n\nPLEASE READ:\nCharacter deleted.  You have 10 days "
            "cooling off time, during which you may reinstate your "
            "character.  To reinstate it simply log on again and it will "
            "reinstate it for you.\n\n\n");
    } else {
        write("WARNING! Unable to delete your character.\n");
    }

    logon_menu();

} /* delete_character() */

/** @ignore yes */
varargs void name_menu( int guest ) {
    if( SHUTDOWN_H->query_shutdown() < 5 ) {
        write("It is too close to the shut-down to create a new character.\n"
            "Please try again in a few minutes.\n" );
        return dest_me();
    }

    just_name_menu(guest);

} /* name_menu() */

/** @ignore yes */
void just_name_menu( int guest ) {
    write( LOGIN_H->get_message("/doc/login/NEW_USER_NAME") );
    write("\nEnter the name you wish to play on "+mud_name()+": ");
    input_to("just_name", 0, guest );
} /* just_name_menu() */

/** @ignore yes */
void random_or_not( string str, int guest ) {
    if( str[0] == 'y' || str[0] == 'Y')
        main_random_name_menu(guest);
    else if( str[0] == 'n' || str[0] == 'N')
        just_name_menu(guest);
    else
        name_menu(guest);
} /* random_or_not() */

/** @ignore yes */
void just_name( string str, int guest ) {
    if( strlen(str) >= MIN_LEN ) {
        write("\n");
        enter_name( str, 0, guest, ({ RANDOM_OR_NOT, guest }) );
        return;
    }

    switch( str[0] ) {
      case 'm' :
      case 'M' :
      case 'q' :
      case 'Q' :
        logon_menu();
      break;
      case 'r' :
      case 'R' :
        main_random_name_menu(guest);
      break;
      default :
        just_name_menu(guest);
    }

} /* just_name() */

/** @ignore yes */
void main_random_name_menu( int guest ) {
    int i;
    string *langs;

    langs = RANDOM_NAME_GENERATOR->query_languages();

    for( i = 0; i < sizeof(langs); i++ )
        langs[i] = sprintf("%c - %s", ( i + '1'), CAP( langs[i] ) );

    write("You are choosing a name for the first time on "+mud_name()+".\n"
        "You can choose a name which is generated to sound something "
        "like:\n"+implode( langs, "\n")+"\n"
        "M - Main Menu\n"
        "Q - Quit\n\n"
        "Or you can type in a name of your choice now.\n\n"
        "Your choice? ");

    input_to("name_choice", 0, sizeof(langs), guest );

} /* main_random_name_menu() */

/** @ignore yes */
void name_choice( string str, int len, int guest ) {
    string lang;

    if( strlen(str) == 1 ) {
        if( str[0] >= '1' && str[0] <= ('1'+len-1) ) {
            lang = RANDOM_NAME_GENERATOR->query_languages()[str[0]-'1'];
            random_name_menu( lang, guest, 1 );
            return;
        }

        if( str[0] == 'm' || str[0] == 'M') {
            logon_menu();
            return;
        }

        if( str[0] == 'q' || str[0] == 'Q') {
            write("See you again soon!\n");
            dest_me();
            return;
        }

        write("Incorrect choice.\n");

    } else if( str != "") {
        enter_name( str, 0, guest, ({ MAIN_RANDOM, guest }) );
        return;
    }

    main_random_name_menu(guest);

} /* name_choice() */

/** @ignore yes */
void random_name_menu( string lang, int guest, int re_gen ) {
    int i;

    write("Here is a list of 9 random names.  Please choose one:\n");
    if( re_gen ) {
        random_names = allocate(9);
        for( i = 0; i < sizeof(random_names); i++ ) {
            random_names[i] = RANDOM_NAME_GENERATOR->unique_name(lang);
            write( sprintf("%c - %s\n", i+'1', random_names[i] ) );
        }
    } else {
        for( i = 0; i < sizeof(random_names); i++ )
            write( sprintf("%c - %s\n", i+'1', random_names[i] ) );
    }

    write("M - Main Menu\n"
          "N - Name Menu\n"
          "G - Generate a new set of names\n"
          "Q - Quit\n"
          "Or, type in your name of choice\n\n"
          "Your choice? ");

    input_to("random_name_choice", 0, lang, guest );

} /* random_name_menu() */

/** @ignore yes */
void random_name_choice( string str, string lang, int guest ) {
    if( strlen(str) == 1 ) {
        switch( str[0] ) {
          case '1' :
          case '2' :
          case '3' :
          case '4' :
          case '5' :
          case '6' :
          case '7' :
          case '8' :
          case '9' :
            enter_name( random_names[ str[ 0 ] - '1' ], 0, guest,
                ({ RANDOM_NAME, lang, guest }) );
            return;
          case 'n' :
          case 'N' :
            main_random_name_menu(0);
            return;
          case 'm' :
          case 'M' :
            logon_menu();
            return;
          case 'g' :
          case 'G' :
            random_name_menu( lang, guest, 1 );
            return ;
          case 'q' :
          case 'Q' :
            write("See you again soon!\n");
            dest_me();
            return;
          default :
            write("Incorrect choice.\n");
        }
    } else if( str != "") {
        enter_name( str, 0, guest, ({ RANDOM_NAME, lang, guest }) );
        return;
    }

    random_name_menu( lang, guest, 0 );

} /* random_name_choice() */

/** @ignore yes */
void next_menu( mixed *args ) {
    switch( args[ 0 ] ) {
      case LOGON_MENU :
        return logon_menu();
      case RANDOM_OR_NOT :
        return random_or_not("n", args[ 1 ] );
      case MAIN_RANDOM :
        return main_random_name_menu( args[ 1 ] );
      case RANDOM_NAME :
        return random_name_menu( args[ 1 ], args[ 2 ], 0 );
      default :
        write("An error has occurred in next_menu().\n");
        dest_me();
    }
} /* next_menu() */

/** @ignore yes */
void enter_name( string str, string prompt, int type, mixed *args ) {
    int tmp;

    go_invis = 0;
    auto_authorise = 0;

    if( str == "")
        return next_menu( args );

    // Everything blows up on a banishment.
    if( str[0] == ':') {
        str = str[1..];
    } if( str[0] == '#') {
        go_invis = 1;
        str = str[1..];
    } else if( str[0] == '@') {
        go_invis = -1;
        str = str[1..];
    }

    if( !LOGIN_H->query_can_log_in( lower_case(str) ) ) {
        write("You cannot login right now, because "+mud_name()+" is running "
            "in "+LOGIN_H->get_login_mode( LOGIN_H->query_login_mode() )+".  "
            "Please try again later.\n");
        logon_menu();
        return;
    }

    set_name( lower_case(str) );

    if( PLAYER_H->test_banished(query_name()) ) {
        write("Sorry, the player name ["+query_name()+"] has been banished.\n");
        if( prompt ) {
            write(prompt);
            input_to("enter_name", 0, prompt, type, args );
        } else {
            next_menu( args );
        }
        return;
    }

    if( strlen(query_name()) < MIN_LEN && type != CURRENT &&
        type != DELETE ) {
        write("Sorry, the player name ["+query_name()+"] is too short (min "+
            MIN_LEN+" characters).\n");
        if( prompt ) {
            write(prompt);
            input_to("enter_name", 0, prompt, type, args );
        } else {
            next_menu( args );
        }
        return;
    }

    if( strlen(query_name()) > MAX_LEN ) {
        write("Sorry, the player name "+query_name()+" is too long (max "+
            MAX_LEN+" characters).\n");
        if( prompt ) {
            write(prompt);
            input_to("enter_name", 0, prompt, type, args );
        } else {
            next_menu( args );
        }
        return;
    }

    if( ( tmp = check_valid_name( query_name() ) ) != -1 ) {
        write("Invalid character '"+str[tmp..tmp]+"'  ("+str+").\n\n");
        if( prompt ) {
            write(prompt);
            input_to("enter_name", 0, prompt, type, args );
        } else {
            next_menu( args );
        }
        return;
    }

    switch( type ) {
      case GUEST :
        if( LOGIN_H->is_discworld_full(0) ) {
            write("Yesterday is too old, today is much newer.\n"
              "Don't give away your dreams, store them in streams.\n\n"
              "Sorry, guests cannot login currently, too many players.\n");
            logon_menu();
            return;
        }
      case NEW :
        if( sscanf( query_name(), "%*sblack%*s") == 2 ||
            sscanf( query_name(), "%*sblood%*s") == 2 ||
            sscanf( query_name(), "%*scyber%*s") == 2 ||
            sscanf( query_name(), "%*sdark%*s") == 2 ||
            sscanf( query_name(), "%*spenis%*s") == 2 ||
            sscanf( query_name(), "%*scock%*s") == 2 ||
            sscanf( query_name(), "%*spussy%*s") == 2 ||
            sscanf( query_name(), "%*sfuck%*s") == 2 ||
            sscanf( query_name(), "%*sshit%*s") == 2 ||
            sscanf( query_name(), "%*sdeath%*s") == 2 ||
            sscanf( query_name(), "%*sdragon%*s") == 2 ||
            sscanf( query_name(), "%*sfish%*s") == 2 ||
            sscanf( query_name(), "%*spimp%*s") == 2 ||
            sscanf( query_name(), "%*shell%*s") == 2 ||
            sscanf( query_name(), "%*smage%*s") == 2 ||
            sscanf( query_name(), "%*spink%*s") == 2 ||
            sscanf( query_name(), "%*slord%*s") == 2 ||
            sscanf( query_name(), "%*sevil%*s") == 2 ||
            sscanf( query_name(), "%*skiller%*s") == 2 ||
            sscanf( query_name(), "%*sslayer%*s") == 2 ) {
            write("Invalid name.  Please retry.\n");
            if( prompt ) {
                write(prompt);
                input_to("enter_name", 0, prompt, type, args );
            } else {
                next_menu( args );
            }
            return;
        }

        if( PLAYER_H->test_user( query_name() ) ||
            CLUB_HANDLER->is_family( query_name() ) ) {
            write("The name ["+query_name()+"] is already in use.\n"
                "Please choose another name.\n");
            if( prompt ) {
                write(prompt);
                input_to("enter_name", 0, prompt, type, args );
            } else {
                next_menu( args );
            }
            return;
        }

        if( type == GUEST ) {
            title = "the Guest of "+mud_name();
            add_property("guest", 1 );
            return logon3();
        }

        write("You have typed in the name ["+query_name()+"], is this "
            "correct? ");
        input_to("check_name_correct", 0, args );
        break;
      case DELETE :
      case CURRENT :
        if( unguarded( (: restore_object,
            query_delete_player_file_name( query_name() ), 1 :) ) ) {
            if( type == DELETE ) {
                write("\n\n\nYour character is already deleted.\n\n\n");
                if( prompt ) {
                    write(prompt);
                    input_to("enter_name", 0, prompt, type, args );
                } else {
                    next_menu( args );
                }
                return;
            }
        } else if( !unguarded( (: restore_object,
            query_player_file_name( query_name() ), 1 :) ) ) {
            write("The name ["+query_name()+"] is not in our annals, "
                "please try again.\n");
            if( prompt ) {
                write(prompt);
                input_to("enter_name", 0, prompt, type, args );
            } else {
                next_menu( args );
            }
            return;
        } else if( Str == -1 ) {
            type = NEW;
        }

        if( !password && type == DELETE ) {
            write("\nSorry, you cannot delete this character.\n"+
                  "Please see an admin if you have questions.\n\n");

            if( prompt ) {
                write(prompt);
                input_to("enter_name", 0, prompt, type, args );
            } else {
                next_menu( args );
            }
            return;
        }

        if( creator && type == DELETE ) {
            write("\nSorry, you cannot delete a creator character.\n"+
                  "Please ask an admin to demote you first.\n\n");
            if( prompt ) {
                write(prompt);
                input_to("enter_name", 0, prompt, type, args );
            } else {
                next_menu( args );
            }
            return;
        }

        if( BASTARDS_H->no_new_players( TO ) &&
            !query_property("authorised player") ) {
            string place, addr;
            // This is put in so it is possible to reactively allow people to
            // log in from a site, by deleting their last login address.
            if( !last_on_from )
                addr = query_ip_number(TO);
            else
                sscanf( last_on_from, "%s (%s)", place, addr );

            // If it is new player banned and we last logged on from here
            // (or another banned site -- this copes with dynamic ips :).
            // we are automatically authorised.
            // We also allow creators to log in from banned sites, and
            // players over 2 days old.
            if( !creator && time_on > -(2*24*60*60) &&
                BASTARDS_H->query_access( explode( addr, "."), "*") ==
                DEFAULT ) {
                write("This character is not authorised to play from here.\n");
                write("For more information please email "+ADMIN_EMAIL+".  "
                    "Don't forget to include your character's name!\n\n");
                user_event("inform", query_name()+" ("+addr+") tried to log "
                    "in from "+query_ip_number(TO), "cheat");
                log_file("CHEAT", "%s: %s (%s) tried to log in from %s\n",
                    ctime(time()), query_name(), addr, query_ip_number(TO) );
                return next_menu( args );
            } else {
                auto_authorise = 1;
            }
        }
            
        if( !password )
            return logon3();

        if( !creator && SHUTDOWN_H->query_shutdown() < 5 &&
            !find_player(query_name()) ) {
            write("\nSorry, you cannot log in during the shut-down sequence "
                "unless you are already logged in and want to reconnect.  "
                "Please try again in a few minutes.\n\n");
            logon_menu();
            return;
        }

        write("\n");
        if( type != DELETE )
            write( LOGIN_H->get_message("/doc/login/WELCOME") );
        write("Enter your password: ");
        input_to("logon2", 1, type, args );
        break;
      default :
        write("Break down in the system.\n");
        return dest_me();
    }

} /* enter_name() */

/** @ignore yes */
void check_name_correct( string str, mixed *args ) {
    if( strlen(str) < 1 || ( str[0] != 'y' && str[0] != 'Y') ) {
        if( str[0] == 'n' || str[0] == 'N')
            return next_menu( args );
        write("Please enter \"y\" or \"n\".\n"
            "You have typed in the name ["+query_name()+"], is this "
            "correct? ");
        input_to("check_name_correct", 0, args );
        return ;
    }

    write( LOGIN_H->get_message("/doc/login/NEW_USER_PASSWORD") );
    write("Okay, you have chosen the name ["+query_name()+"] for your time "
        "on "+mud_name()+".\n"
        "Have fun, and remember: don't explode.\n"
        "Enter password: ");

    add_property("new player", 1 );
    input_to("logon2", 1, GUEST, args );

} /* check_name_correct() */

/** @ignore yes */
int do_su( string str ) {
    object ob, tp;

    ob = TP;
    tp = TO;

    terminal_name = ob->query_cur_term();
    the_cols = ob->query_cols();
    the_rows = ob->query_rows();

    exec( tp, ob );
    ob->quit();

    login_choice(str);
    return 1;

} /* do_su() */

/** @ignore yes */
void retry_logon( int guest, mixed *args ) {
    write("Try again: ");
    input_to("logon2", 1, guest, args );
} /* retry_logon() */

/** @ignore yes */
void time_out() {
    if( !interactive( TO ) )
        return dest_me();

    // Time them out if they are not in the queue and either idle or
    // not idle but have been sitting around too long.
    if( ( query_idle(TO) > TIMEOUT_TIME ||
        ( time() > login_start_time + THROWOUT_TIME ) ) &&
        member_array( TO, LOGIN_H->query_login_queue() ) == -1 ) {
        write("Time out.\n\n");
        return dest_me();
    }

    call_out("time_out", TIMEOUT_TIME );

} /* time_out() */

/** @ignore yes */
protected void logon2( string str, int type, mixed *args ) {
    string mess;

    write("\n");

    if( !str || str == "") {
        password = 0;
        Dex = -1;
        Str = -1;
        return next_menu( args );
    }

    // Force a 6 character or greater password.
    if( Dex == -1 && !password && strlen(str) < 6 ) {
        write("Password is too short, must be at least 6 characters.\n");
        write("Enter password: ");
        input_to("logon2", 1, type, args );
        return;
    }

    str = crypt( str, password );

    // Password failure.
    if( password && str != password ) {
        // New char entering their password, don't count this as a failed
        // login, hence it's before the failed login stuff.
        if( Dex == -1 ) {
            Str = 13;
            write("The password didn't match up with first password.\n");
            write("Please enter the password again: ");
            input_to("logon2", 1, type, args );
            return;
        }

        // If they've gone over the max_retries record it as a failed login.
        if( ++no_times > MAX_RETRIES ) {
            // Record a failed login from this IP.
            LOGIN_H->failed_login( query_ip_number(TO) );

            write("Too many retries, please wait...\n");

            mess = CAP(query_name())+" failed to login, "+
                ( query_ip_name(TO) != query_ip_number(TO) ?
                  query_ip_name(TO) + " ("+query_ip_number(TO)+")" :
                  query_ip_number(TO) );

            event( users(), "inform", mess, "bad-password");
            log_file("BAD_PASSWORD", "%s: %s\n", ctime(time()), mess );
            no_times = 0;
            // Make this delay a bit.
            call_out( (: logon_menu :), 30 );
            return;
        }

        write("Wrong, please wait...\n");
        call_out( (: retry_logon :), 1, type, args );
        return;
    }

    password = str;

    if( Str == -1 ) {
        Str = 13;
        write("Please enter the password again: " );
        input_to("logon2", 1, type, args );
        return;
    }

    if( Dex == -1 )
        Dex = 13;

    // Check to see if we are reinstating a deleted character.
    if( type != DELETE ) {
        if( unguarded( (: file_size,
            query_delete_player_file_name(query_name())+".o" :) ) != -1 ) {
            if( !unguarded( (: rename,
                query_delete_player_file_name(query_name())+".o",
                query_player_file_name(query_name())+".o" :) ) ) {
                write("Reinstating deleted character!  Good to see you back "
                    "again.\n");
            } else {
                write("\n\n\nUnable to reinstate your character, please send "
                    "an email to "+ADMIN_EMAIL+" if you require further "
                    "assistance.\n\n\n");
                logon_menu();
                return;
            }
        }
        logon3();
    } else {
        delete_character();
    }

} /* logon2() */

/** @ignore yes */
protected void logon3() {
    cap_name = CAP( query_name() );
    logon4( !query_property("guest") );
} /* logon3() */

/** @ignore yes */
protected void get_sex( string str, int bing ) {
    switch( lower_case(str) ) {
      case "m" :
      case "male" :
        gender = 1;
      break;
      case "f" :
      case "female" :
        gender = 2;
      break;
      default :
        write("That's too weird even for this game!\n"
              "Try male or female: ");
        input_to("get_sex");
        return;
    }

    logon5(bing);

} /* get_sex() */

/** @ignore yes */
protected void logon4( int bing ) {
    if( gender == -1 ) {
        write("Would you like this character to be male or female? ");
        input_to("get_sex", bing );
        return;
    }

    logon5(bing);

} /* logon4() */

/** @ignore yes */
protected void check_terms( string str, int new_pl ) {
    write( LOGIN_H->get_message("/doc/login/WELCOME") );
    logon5(new_pl);
} /* check_terms() */

/** @ignore yes */
protected void logon5( int new_pl ) {
    object ob, tmp, *obs;

    if( query_name() != "root" && query_name() != "guest")
        other_copy = find_player(query_name());

    // This is the check for upgrade calls.
    if( other_copy == TP )
        return begin(new_pl);

    // Check to see if they are in the login queue already.
    if( member_array( query_name(), map( LOGIN_H->query_login_queue(),
        (: $1->query_name() :) ) ) != -1 ) {
        write("You are already in the login queue.\n");

        obs = filter( LOGIN_H->query_login_queue(),
            (: $1->query_name() == $2 :), query_name() );
        other_copy = obs[0];

        if( other_copy && other_copy->query_login_ob() ) {
            // They are already in the login queue.
            write("Reconnecting to you in the login queue.\n");
            if( interactive(other_copy) ) {
                tell_object( other_copy, "Disconnected by someone from "+
                    query_ip_name(TO)+".\n");
                ob = clone_object(OBJECT_OBJ);
                tmp = other_copy;
                exec( ob, other_copy );
                ob->dest_me();
            }
            tmp = other_copy;
            ob = TO;
            exec( other_copy, ob );
            return;
        } else {
            dest_me();
        }
        return;
    }

    // Check to see if mud is full and then shove them into the queue.
    if( LOGIN_H->is_discworld_full(WITHOUT_LOGINS_NOT_IN_QUEUE) ||
        sizeof( LOGIN_HANDLER->query_login_queue() ) ) {
        // Always allow creators and test characters.
        if( !creator && !query_property("test character") ) {
            write("Sorry, there are no player slots available.\n\n");
            LOGIN_H->add_to_login_queue(TO);
            login_num = sizeof( LOGIN_H->query_login_queue() );
            if( other_copy ) {
                // Hack!  Hack!  Warning!  No idea why reconnection to net
                // deads is not working.  So force them to quit instead.
                other_copy->quit();
                write("You were net dead when you left, shuffling you to the "
                    "start of the queue...\n");
                net_dead = 1;
            } else {
                write("Placing you in the login queue: you have position "+
                    login_num+".\nPlease wait.  Type \"quit\" to escape.\n");
            }
            remove_call_out("time_out");
            call_out("check_status", 30, new_pl );
            input_to("idle_loop", 0, new_pl );
            return;
        }
    }

    if( other_copy ) {
        try_throw_out(new_pl);
        return;
    }

    begin(new_pl);

} /* logon5() */

/*
 * This is the idle loop stuff.  It checks to see if the people want to
 * leave the queue nicely.  Not absolutely nessessary I guess.
 */
/** @ignore yes */
void idle_loop( string str, int new_pl ) {
    if( can_login == 2 )
        try_throw_out(new_pl);
    else if( can_login == 3 )
        begin(new_pl);
    else if( strlen(str) && lower_case(str)[0] == 'q')
        dest_me();
    else
        input_to("idle_loop", 0, new_pl );
} /* idle_loop() */

/*
 * This is called from the login_handler when we are ready to leave the
 * login queue.
 */
/** @ignore yes */
void remove_from_login_queue() {
    object other_copy;

    can_login = 1;
    other_copy = find_player(query_name());
    if( other_copy ) {
        can_login = 2;
    } else {
        can_login = 3;
        tell_object( TO, "\n\n\n\nYou have exited the login queue!  "
            "Please press return to continue: ");
        call_out("time_out", TIMEOUT_TIME );
    }

    // Just in case.
    LOGIN_H->remove_from_login_queue(TO);

} /* remove_from_login_queue() */

/*
 * This is the call out done now and then.  It prints the cute messages.
 */
/** @ignore yes */
void check_status(int new_pl) {
    int tmp_num;
    object other_copy;

    if( !can_login ) {
        tmp_num = member_array( TO, LOGIN_H->query_login_queue() ) + 1;
        if( tmp_num == 0 ) {
            // Oh dear, we got deleted. Put ourselves back in.
            LOGIN_H->add_to_login_queue(TO);
            tmp_num = member_array( TO, LOGIN_H->query_login_queue() ) + 1;
        }

        if( tmp_num > 0 && tmp_num != login_num && !other_copy ) {
            if( !net_dead )
                write("You now have position "+tmp_num+" in the queue.\n");
        }

        login_num = tmp_num;
        write( choice( ({
            "Yawn...",
            "This queue is boring.",
            "I wonder how much longer?",
            "I wish this would end soon.",
            }) )+"\n");

        call_out("check_status", 30, new_pl );
    }

} /* check_status() */

/** @ignore yes */
protected void try_throw_out( int new_pl ) {
    object tmp, ob;

    if( !other_copy ) {
        write("Oh dear.  Your other copy has been eaten.  "
            "Logging in normally.\n");
        begin(new_pl);
        return ;
    }

    if( interactive(other_copy) ) {
        ob = clone_object(OBJECT_OBJ);
        tmp = other_copy;
        exec( ob, other_copy );
        ob->dest_me();
    }

    tmp = other_copy;
    ob = TO;
    exec( other_copy, ob );

#ifdef LOOKUP_IDENT
    other_copy->set_my_ident(my_ident);
#endif

    // Force a look.
    other_copy->look_me();

    LOGIN_H->player_reconnected( other_copy->query_name() );

    event( ENV(other_copy), "see", other_copy->query_cap_name()+" has "
        "reconnected.\n", other_copy, ({ other_copy }) );

    if( function_exists("inform_reconnect_game", other_copy ) )
        other_copy->inform_reconnect_game();

#ifdef LOOKUP_IDENT
    if( got_return )
        return dest_me();
    ident_player_ob = other_copy;
#else
    dest_me();
#endif

} /* try_throw_out() */

/** @ignore yes */
protected void begin( int new_pl ) {
    player_ob = BASTARDS_H->query_player_ob( query_name(), 1 );

    if( !player_ob ) {
        logon_menu();
        return ;
    }

    begin2( new_pl );

} /* begin() */

/** @ignore yes */
protected void begin2( int new_player ) {
    object tp;

    // Clone the player object.
    if( catch( new_copy = clone_object(player_ob) ) ) {
        write("Oh no, someone has broken the player object!  "+
            "Come back later.\n");
        return dest_me();
    }

    if( !new_copy ) {
        write("Argh!  Something bad happened.\n");
        return dest_me();
    }

    new_copy->set_name( query_name() );
    new_copy->set_password( password );

    tp = TO;
    exec( new_copy, tp );

    if( tp != TO )
        tp->quit();

    if( query_property("guest") ) {
        new_copy->add_property("guest", 1 );
        new_copy->set_title(title);
    }

#ifdef LOOKUP_IDENT
    new_copy->move_player_to_start( query_name(),
        query_property("new player"), cap_name, my_ident, go_invis );
#else
    new_copy->move_player_to_start( query_name(),
        query_property("new player"), cap_name, 0, go_invis );
#endif

    new_copy->set_gender(gender);

    if( new_player )
        new_copy->add_skill_level("general.language.common", 100 );

    // Wheee.
    if( terminal_name )
        new_copy->terminal_type( terminal_name );

    if( the_cols && the_rows )
        new_copy->window_size( the_cols, the_rows );

    if( auto_authorise )
        new_copy->add_property("authorised player",
            query_ip_number( new_copy ) );

    if( query_ip_number(new_copy) == query_ip_name(new_copy) )
        resolve( query_ip_number(new_copy), "query_level");

#ifdef LOOKUP_IDENT
    if( got_return )
        return dest_me();
    ident_player_ob = new_copy;
#endif

    dest_me();

} /* begin2() */

/** @ignore yes */
int query_level() { return 1; }

/** @ignore yes */
int query_gender() { return gender; }

private void do_special_add_property( string fname, string prop, mixed val ) {
    string frog, *bits;
    mapping glue;
    int i, len;

    frog = read_file(fname);

    if( !frog ) {
#ifdef DEBUG
        log_file("LOGIN_ERROR", ctime(time())+ ": Could not read file "+
            fname+" .\n");
#endif
        return;
    }

    bits = explode( frog, "\n");
    len = strlen( prop_var_name ) - 1;

    for( i = 0; i < sizeof(bits); i++ ) {
        if( bits[i][0..len] == prop_var_name ) {
            glue = restore_variable( bits[i][len + 1..] );
            glue[prop] = val;
            bits[i] = prop_var_name + save_variable(glue);
            break;
        }
    }

    reset_eval_cost();

    // We really do not want to break in here...
    if( !write_file( fname, implode( bits, "\n"), 1 ) )
        error( sprintf("Failed to write to %s (prop: %s)\n", fname, prop ) );

    write_file( fname, "\n");

} /* do_special_add_property() */

/**
 * This method allows you to add properties onto players who are not
 * currently logged on.
 * @param pname name of player to change
 * @param prop the property to change
 * @param val the new value for it
 * @return 1 upon success, 0 upon failure
 */
int special_add_property( string pname, string prop, mixed val ) {
    object ob;
    string tmp_name;

    if( !stringp(prop) || !stringp(pname) )
        return 0;

    if( !PLAYER_H->test_user(pname) )
        return 0;

    if( ob = find_player(pname) ) {
        ob->add_property( prop, val );
    } else {
        tmp_name = query_player_file_name(pname)+".o";
        unguarded( (: do_special_add_property( $(tmp_name), $(prop),
            $(val) ) :) );
        PLAYER_H->special_add_property( pname, prop, val );
    }

    return 1;

} /* special_add_property() */

/**
 * @ignore yes
 * This function is called by the upgrade object.
 */
void new_player( object old ) {
    object new_pl;
    string name, ob_name;
#ifdef LOOKUP_IDENT
    string ident;

    ident = query_ident(old);
#endif

    name = old->query_name();
    cap_name = old->query_cap_name();
    creator = creatorp(old);
    terminal_name = old->query_cur_term();
    the_cols = old->query_cols();
    the_rows = old->query_rows();
    exec( TO, old );
    creator = creatorp(old);
    old->quit();
    creator = creatorp(TP);
    ob_name = BASTARDS_H->query_player_ob( name, 1 );

    if( !ob_name ) {
        logon_menu();
        return ;
    } else {
        new_pl = clone_object(ob_name);
    }

    exec( new_pl, TO );
    new_pl->terminal_type( terminal_name );
    new_pl->window_size( the_cols, the_rows );
#ifdef LOOKUP_IDENT
    new_pl->move_player_to_start( name, 0, cap_name, ident );
#else
    new_pl->move_player_to_start( name, 0, cap_name );
#endif

    dest_me();

} /* new_player() */

/** @ignore yes */
int query_prevent_shadow() { return 1; }

/**
 * @ignore yes
 * Needed so the object actually writes things to the screen.  Mildly
 * embarrassing otherwise.
 */
void do_efun_write( string str ) {
    efun::tell_object( TO, sprintf("%-=*s", cols, strip_colours( str ) ) );
} /* do_efun_write() */

/** @ignore yes */
string query_object_type() { return "X"; }

/** @ignore yes */
string query_gender_string() { return "blue"; }

#ifdef LOOKUP_IDENT
/** @ignore yes */
void finished_auth( string m_i ) {
    got_return = 1;
    my_ident = m_i;
    if( ident_player_ob ) {
#ifdef DEBUG
        TCRE("shaydz", "Found ident of "+m_i+" (player_ob) "+name+"\n");
#endif
        ident_player_ob->set_my_ident(m_i);
        dest_me();
    } else
#ifdef DEBUG
        TCRE("shaydz", "Found ident "+m_i+" "+name+"\n");
#endif
} /* found_ident() */
#endif

/** @ignore yes */
void terminal_type( string type ) { terminal_name = type; }

/** @ignore yes */
void window_size( int width, int height ) {
    the_cols = width;
    the_rows = height;
} /* window_size() */

/** @ignore yes */
int query_invis() { return 1; }

/** @ignore yes */
void net_dead() {
    LOGIN_H->remove_from_login_queue(TO);
    dest_me();
} /* net_dead() */

/** @ignore yes */
void dest_me() {
    LOGIN_H->remove_from_login_queue(TO);
    ::dest_me();
} /* dest_me() */

/**
 * This method determines if the object is a login object.
 * @return always returns 1
 */
int query_login_ob() { return 1; }

/**
 * This method tells us if the object is net dead.
 * @return 1 if they are net dead, 0 if they are not
 */
int query_net_dead() { return net_dead; }

/**
 * This method returns the path to the player file.
 * This is the player files actual normal location, it was written to
 * allow moving the player files aruond easier.
 * @param name the name of hte player whose file to find
 * @see test_user()
 * @see query_delete_player_file_name()
 */
string query_player_file_name( string name ) {
    return PLAYER_SAVE_DIR+name[0..0]+"/"+name;
} /* query_player_file_name() */

/**
 * This method returns the path to the deleted player file.
 * This is the deleted player files actual normal location, it was written to
 * allow moving the player files aruond easier.
 * @param name the name of the player whose file to find
 * @see test_user()
 * @see query_delete_player_file_name()
 */
string query_delete_player_file_name( string name ) {
    return PLAYER_SAVE_DIR+DELETE_DIR+"/"+name;
} /* query_player_file_name() */

/** @ignore yes */
string *parse_command_adjectiv_id_list() {
    return object::parse_command_adjectiv_id_list();
} /* parse_command_adjectiv_id_list() */

/** @ignore yes */
string *parse_command_plural_id_list() {
    return object::parse_command_plural_id_list();
} /* parse_command_plural_id_list() */

/** @ignore yes */
string *parse_command_id_list() {
    return object::parse_command_id_list();
} /* parse_command_id_list() */

/*
 * GRRR, THESE FUNCTIONS SHOULD BE GOTTEN RID OF.
 * BUT ONLY AFTER MAKING SURE THE LIB ISN'T USING ANY. :/
 * - SANDOZ
 */
/** @ignore yes */
int test_user(string str) { return PLAYER_H->test_user(str); }

/** @ignore yes */
int test_gender(string str) { return PLAYER_H->test_gender(str); }

/** @ignore yes */
int test_active(string str) { return PLAYER_H->test_active(str); }

/** @ignore yes */
int test_level(string str) { return PLAYER_H->test_level(str); }

/** @ignore yes */
int test_banished(string str) { return PLAYER_H->test_banished(str); }

/** @ignore yes */
string test_real_name( string str ) {
    return PLAYER_H->test_real_name(str);
} /* test_real_name() */

/** @ignore yes */
string test_email( string str ) { return PLAYER_H->test_email(str); }

/** @ignore yes */
mixed test_property(string word, string str) {
    return PLAYER_H->test_property(word, str);
} /* test_property() */

/** @ignore yes */
string test_deity(string str) { return PLAYER_H->test_deity(str); }

/** @ignore yes */
int test_age(string str) { return PLAYER_H->test_age(str); }

/** @ignore yes */
int test_last(string str) { return PLAYER_H->test_last(str); }

/** @ignore yes */
int test_start_time(string str) {
    return PLAYER_H->test_start_time(str);
} /* test_start_time() */

/** @ignore yes */
string test_family(string str) { return PLAYER_H->test_family(str); }

/** @ignore yes */
int test_password( string name, string pass ) {
    return PLAYER_H->test_password( name, pass );
} /* test_password() */

/** @ignore yes */
string query_signature(string str) {
    return PLAYER_H->query_signature(str);
} /* query_signature() */