/
roa/
roa/lib/boards/
roa/lib/config/
roa/lib/edits/
roa/lib/help/
roa/lib/misc/
roa/lib/plrobjs/
roa/lib/quests/
roa/lib/socials/
roa/lib/www/
roa/lib/www/LEDSign/
roa/lib/www/LEDSign/fonts/
roa/lib/www/LEDSign/scripts/
roa/src/s_inc/
roa/src/sclient/
roa/src/sclient/binary/
roa/src/sclient/text/
roa/src/util/
/************************************************************************
	Realms of Aurealis 		James Rhone aka Vall of RoA

descmenu.c				RoA Descriptor Menuing System.

		******** Heavily modified and expanded ********
		******** 100% Completely Original Code ********
		*** BE AWARE OF ALL RIGHTS AND RESERVATIONS ***
		******** Heavily modified and expanded ********
		******** 100% Completely Original Code ********
		        All rights reserved henceforth. 

    Please note that no guarantees are associated with any code from
Realms of Aurealis.  All code which has been released to the general
public has been done so with an 'as is' pretense.  RoA is based on both
Diku and CircleMUD and ALL licenses from both *MUST* be adhered to as well
as the RoA license.   *** Read, Learn, Understand, Improve ***
*************************************************************************/
#include "conf.h"
#include "sysdep.h"

#include "structures.h"
#include "comm.h"
#include "interpreter.h"
#include "acmd.h"
#include "db.h"
#include "utils.h"
#include "mudlimits.h"
#include "handler.h"
#include "roaolc.h"
#include "magic.h"
#include "house.h"
#include "lists.h"
#include "global.h"
#include "clicomm.h"
#include "screen.h"
#include "htown.h"
#include "mail.h"
#include "objsave.h"
#include "descmenu.h"

// for ease...
#define DCH d->character

// external vars...
extern struct htown_data htowns[];
extern char *title0;
extern char *title1;
extern char *title2;
extern char *title3;
extern char *title4;
extern char *title5;

// func protos
void      echo_off(dsdata *d);
void      echo_on(dsdata *d);
int       isbanned(char *hostname);
int       reserved_word(char *argument);
int       Valid_Name(char *newname);
void      do_start(chdata *ch, BOOL remort);
void      init_char(chdata *ch);
void      update_p_index(int slot, long idnum, char *host);
int       create_entry(char *name);
void      who_to_buf(char *whobuf);
void      update_mortal_stats(char *buf, chdata *ch);
void      string_to_file(char *text, char *fname);

////////////////////
// Support functions
////////////////////

// fill buf with current descriptor state
void fill_descriptor_state(dsdata *d, char *buf)
{
  if (DESCMENU_HANDLER(d) == (void*)descmenu_long_string)
    strcpy(buf, "StringEntry");
  else
  if (DESCMENU_HANDLER(d) == (void*)getname)
    strcpy(buf, "GetName");
  else
  if (DESCMENU_HANDLER(d) == (void*)confirmname)
    strcpy(buf, "ConfirmName");
  else
  if (DESCMENU_HANDLER(d) == (void*)getpasswd)
    strcpy(buf, "GetPwd");
  else
  if (DESCMENU_HANDLER(d) == (void*)motdsequence)
    strcpy(buf, "MotdSeq");
  else
  if (DESCMENU_HANDLER(d) == (void*)getnewpasswd)
    strcpy(buf, "GetNewPwd");
  else
  if (DESCMENU_HANDLER(d) == (void*)confirmnewpasswd)
    strcpy(buf, "ConfirmPwd");
  else
  if (DESCMENU_HANDLER(d) == (void*)getansi)
    strcpy(buf, "GetAnsi");
  else
  if (DESCMENU_HANDLER(d) == (void*)getrace)
    strcpy(buf, "GetRace");
  else
  if (DESCMENU_HANDLER(d) == (void*)getclass)
    strcpy(buf, "GetClass");
  else
  if (DESCMENU_HANDLER(d) == (void*)gethome)
    strcpy(buf, "GetHome");
  else
  if (DESCMENU_HANDLER(d) == (void*)getsex)
    strcpy(buf, "GetSex");
  else
  if (DESCMENU_HANDLER(d) == (void*)getalignment)
    strcpy(buf, "GetAlign");
  else
  if (DESCMENU_HANDLER(d) == (void*)getassassin)
    strcpy(buf, "GetAssassin");
  else
  if (DESCMENU_HANDLER(d) == (void*)rerolltip)
    strcpy(buf, "RerollTip");
  else
  if (DESCMENU_HANDLER(d) == (void*)genericreturn)
    strcpy(buf, "GenReturn");
  else
  if (DESCMENU_HANDLER(d) == (void*)showbackground)
    strcpy(buf, "Background");
  else
  if (DESCMENU_HANDLER(d) == (void*)changepasswd1)
    strcpy(buf, "ChangePwd1");
  else
  if (DESCMENU_HANDLER(d) == (void*)changepasswd2)
    strcpy(buf, "ChangePwd2");
  else
  if (DESCMENU_HANDLER(d) == (void*)changepasswd3)
    strcpy(buf, "ChangePwd3");
  else
  if (DESCMENU_HANDLER(d) == (void*)confirmdelete1)
    strcpy(buf, "ConfirmDelete1");
  else
  if (DESCMENU_HANDLER(d) == (void*)confirmdelete2)
    strcpy(buf, "ConfirmDelete2");
  else
  if (DESCMENU_HANDLER(d) == (void*)mainmenu)
    strcpy(buf, "MainMenu");
  else
    strcpy(buf, "InGame");
}

/* locate entry in p_table with entry->name == name. -1 mrks failed search */
int find_name(char *name)
{
  int	i;

  for (i = 0; i <= top_of_p_table; i++) 
    if (!str_cmp((player_table + i)->name, name))
      return(i);

  return(-1);
}

// checks for a valid name...
int _parse_name(char *arg_orig, char *name)
{
  int	i;
  char *arg = arg_orig;

  skip_spaces(&arg);

  for (i = 0; (*name = *arg); arg++, i++, name++)
    if (!isalpha(*arg) || i > 15)
      return(TRUE);

  return (!i);
}

/* various menus RoA */
void send_creation_menu(dsdata *d)
{
  char buf[MAX_STRING_LENGTH];
  char cstr[MAX_STRING_LENGTH];

  sprintf(buf,"%s Character Creation Sequence\n\r"
              "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\n\r"
              "  %s represents a fictional world in which might and magic reign\n\r"
              "supreme.  All character names must fit into the general medeival theme in which\n\r"
              "%s operates, or you will be asked to delete your character and create a new\n\r"
              "one.  Thank you in advance for your cooperation.\n\n\r"
              "  Before you can begin your journey throughout the Realms, you must enter the\n\r"
              "attributes of your new character.  Sex, race and class are some of the things\n\r"
              "you will need to choose for your new character.  %s vets may notice a couple\n\r"
              "of new questions and traits that one may choose for their character.  During\n\r"
              "this process, you can always return to this menu and start over.\n\n\r",
              shortmudname, longmudname, shortmudname, shortmudname);


  send_to_q("\033[H\033[J",d);
  color_decode(DCH, buf, cstr);
  send_to_q(cstr, d);

  sprintf(buf, "[C] Create character: %%B%s%%0\n\r"
               "[D] Disconnect\n\r"
               "\n\rEnter your choice: ", GET_NAME(DCH));
  color_decode(DCH, buf, cstr);
  send_to_q(cstr, d);
}

void send_race_menu(dsdata *d)
{
  char buf[MAX_STRING_LENGTH];
  char cstr[MAX_STRING_LENGTH];

  sprintf(buf,"Race Selection\n\r"
              "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\n\r"
              "  The adventurers of this land stem from a diverse set of races.  Each race\n\r"
              "has different innate abilities and several advantages and disadvantages. Some\r\n"
              "races have immediate abilities upon birth, while others have to grow a bit in\n\r"
              "age and experience before some of their abilities become available.  Choose a\n\r"
              "race below.\n\n\r");

  send_to_q("\033[H\033[J",d);
  color_decode(DCH, buf, cstr);
  send_to_q(cstr, d);

  strcpy(buf, "[0] Human      [1] Elf        [2] Half_elf    [3] Orc        [4] Ogre\n\r"
              "[5] Drow       [6] Dwarf      [7] Pixie       [8] Nixie      [9] Drakyn\n\n\r"
              "[M] Return to main creation menu...\n\n\r"
              "Choose thy race (?# for help): ");

  color_decode(DCH, buf, cstr);
  send_to_q(cstr, d);
}

void send_class_menu(dsdata *d)
{
  char buf[MAX_STRING_LENGTH];
  char cstr[MAX_STRING_LENGTH];
  int i;

  sprintf(buf,"Class Selection\n\r"
              "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\n\r"
              "  Probably the most important decision you will have to make in this creation\n\r"
              "process is that of your class.  Your class determines the main skill or spell\n\r"
              "set that will be available to you throughout your lifetime.  Some classes focus\n\r"
              "on magic as their primary weapon or defense, while members of other classes\n\r"
              "choose to use brute force and/or physical technique for their offensive and\n\r"
              "defensive prowess.  Choose one of the %s classes below.\n\n\r",
              rcarray[GET_RACE(DCH)].racial_string);

  send_to_q("\033[H\033[J",d);

  color_decode(DCH, buf, cstr);
  send_to_q(cstr, d);

  for (i=1; i <= NUM_CLASSES; i++)
    if (rcarray[GET_RACE(DCH)].race_allow[i])
    {
      sprintf(buf,"[%d] %s\n\r", i, clarray[i].class_name);
      color_decode(DCH, buf, cstr);
      send_to_q(cstr, d);
    }

  send_to_q("\n\r[M] Return to main creation menu...\n\r", d);
  send_to_q("\n\rChoose thy class (?# for help): ", d);
}

void send_ansi_menu(dsdata *d)
{
  char buf[MAX_STRING_LENGTH];

  sprintf(buf,"Terminal Selection\n\r"
              "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\n\r"
              "  Before you create your character, %s must be made aware if you are\n\r"
              "using an ANSI capable terminal.  If you answer yes, %s will attempt to send\n\r"
              "you ANSI color sequences.  If you are not using an ANSI compliant terminal,\n\r"
              "or you simply do not want any color, answer no.  Otherwise, answer yes.  You\n\r"
              "can turn color on or off at any time once you are in the game.\n\n\r"
              "ANSI terminal (Yes|No)? ", shortmudname, shortmudname);

  send_to_q("\033[H\033[J",d);
  send_to_q(buf, d);
}

void send_align_menu(dsdata *d)
{
  char buf[MAX_STRING_LENGTH];

  sprintf(buf,"Alignment Selection\n\r"
              "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\n\r"
              "Your alignment indicates whether you are initially good, neutral, or evil\n\r"
              "upon entrance to these Realms.  The lower the number you enter, the more\n\r"
              "evil your character is, while a higher number indicates you are less evil.\n\r"
              "The performance of some classes is directly affected by alignment.  Also, \n\r"
              "alignment is not a constant and will be affected by your actions during your\n\r"
              "adventures.\n\n\r"
              " Demonic |  Neutral  |  Holy  \n\r"
              "------------------------------\n\r"
              "-1000    |     0     |  1000  \n\r"
              "\n\r"
              "Enter your numeric alignment (# between -1000 and 1000): ");

  send_to_q("\033[H\033[J",d);
  send_to_q(buf, d);
}

void send_assassin_menu(dsdata *d)
{
  char buf[MAX_STRING_LENGTH];

  sprintf(buf,"Assassin Selection\n\r"
              "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\n\r"
              "%s offers you the opportunity to participate in FULL playerkilling (PK).  If you\n\r"
              "choose to be an assassin, you can do anything to ANY other assassin.  Basically,\n\r"
              "there are no rules between assassins other than what they themselves establish.\n\r"
              "Be warned, however, if you choose to become an assassin, there is no turning back\n\r"
              "from that path.  You may become an assassin at any time after level %d if you\n\r"
              "choose no now.\n\n\r"
              "Join the Assassin guild (Yes|No)? ", shortmudname,  maxnewbielevel); 

  send_to_q("\033[H\033[J",d);
  send_to_q(buf, d);
}

void send_sex_menu(dsdata *d)
{
  char buf[MAX_STRING_LENGTH];

  sprintf(buf,"Gender Selection\n\r"
              "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\n\r"
              "[M] Male\n\r"
              "[F] Female\n\n\r"
              "Choose thy gender: ");
 
  send_to_q("\033[H\033[J",d);
  send_to_q(buf, d);
}

void send_description_menu(dsdata *d)
{
  char buf[MAX_STRING_LENGTH];
  char cstr[MAX_STRING_LENGTH];

  sprintf(buf,"Description Entry\n\r"
              "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\n\r"
              "  The last thing to do before you can enter these realms is to create a short\n\r"
              "description for you character.  This short description will be what others\n\r"
              "see as your description when they look at you.  Please note that this is a\n\r"
	      "_short_ description, as in 3 lines or less.  Please use hard carriage returns\n\r"
              "between lines (hit return) rather than letting the line wrap by itself.  When\n\r"
              "you have finished, hit return, enter an at (@) symbol, and hit return once\n\r"
              "more.  That is, the at (@) will be on its own line.  Once done, you will be\n\r"
              "at the main menu of %s.\n\n\r", shortmudname);

  send_to_q("\033[H\033[J",d);
  color_decode(DCH, buf, cstr);
  send_to_q(cstr, d);
}

void send_hometown_menu(dsdata *d)
{
  int i, j;
  char buf[MAX_STRING_LENGTH];
  char cstr[MAX_STRING_LENGTH];

  sprintf(buf,"Hometown Selection\n\r"
              "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\n\r"
              "  The world of %s is indeed vast.  From dark and dreary dungeons, to snow-\n\r"
              "topped mountains, the Realms all but cry out for exploration.  Villages, towns\n\r"
              "and cities lie scattered across the world, each one with its own unique history\n\r"
              "and set of legends and lore.  Some are populated by only one race, while others\n\r"
              "are open to all who would call them home.  The hometowns below allow members of\n\r"
              "your %s race (and possibly others) to settle in them.\n\n\r", shortmudname,
              rcarray[GET_RACE(DCH)].racial_string);

  send_to_q("\033[H\033[J",d);
  color_decode(DCH, buf, cstr);
  send_to_q(cstr, d);

  for (i = 0; i < MAX_HTOWNS && htowns[i].name && *htowns[i].name; i++)
   if (htowns[i].on_menu && htowns[i].races[GET_RACE(DCH)])
    {
      sprintf(buf, "[%d] %s\t - ", i, htowns[i].name);
      for (j = 1; j <= NUM_RACES; j++)
	if (htowns[i].races[j])
	{
	  strcat(buf, rcarray[j].race_name);
	  strcat(buf, " ");
	}
	strcat(buf, "\n\r");
        color_decode(DCH, buf, cstr);
        send_to_q(cstr, d);
    }

  send_to_q("\n\r[M] Return to main creation menu...\n\r", d);
  send_to_q("\n\rChoose thy hometown: ", d);
}

void send_attribute_menu(dsdata *d)
{
  char buf[MAX_STRING_LENGTH];
  char cstr[MAX_STRING_LENGTH];

  sprintf(buf,"Attribute Reminder\n\r"
              "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n\n\r"
              "  You have the ability to reroll your attributes (strength, intelligence..)\n\r"
              "as _many_ times as you wish while you are level 1.  Once you advance above\n\r"
              "level 1, you will no longer be able to reroll your attributes.  Once in the\n\r"
              "game, take a moment to look at %%Bhelp reroll%%0.\n\n\r");

  send_to_q("\033[H\033[J",d);
  color_decode(DCH, buf, cstr);
  send_to_q(cstr, d);
}

void replace_deleted(dsdata *d, char *tmp_name)
{
  log("SYSUPD: replace_deleted called");
  free_char(DCH);
  CREATE(DCH, chdata, 1);
  clear_char(DCH);
  CREATE(DCH->pc_specials, struct pc_special_data, 1);
  DCH->desc = d;
  CREATE(DCH->player.name, char, strlen(tmp_name) + 1);
  strcpy(DCH->player.name, CAP(tmp_name));
}

BOOL check_password(dsdata *d, char *arg, BOOL main_server)
{
  char	buf[100];

  if (strncmp(d->pwd, CRYPT(arg, arg), MAX_PWD_LENGTH)) 
  {
    sprintf(buf, "SYSUPD: Bad PW: %s [%s]", GET_NAME(DCH), d->host);
    mudlog(buf, BUG, MAX(LEV_IMM, GET_INVIS_LEV(DCH)), TRUE);
    DCH->pc_specials->saved.bad_pws++;
    save_char(DCH, NOWHERE);

    if (++(d->bad_pws) >= 3) /* 3 strikes and you're out. */
    { 
      send_to_q("Incorrect password... disconnecting.\n\r", d);
      STATE(d) = CON_CLOSE;
    } 
    else 
    if (main_server)
    {
      send_to_q("Incorrect password.\n\rPassword: ", d);
      echo_off(d);
    }
    return FALSE;
  }
  else
    return TRUE;
}

BOOL check_banned(dsdata *d)
{
  char	buf[100];
	 
  if (isbanned(d->host) == BAN_SELECT && !PLR_FLAGGED(DCH, PLR_SITEOK)) 
  {
    sprintf(buf, "Connection attempt for %s denied from %s", GET_NAME(DCH), d->host);
    mudlog(buf, NRM, MAX(LEV_IMM, GET_INVIS_LEV(DCH)), TRUE);

    send_to_q("Sorry, this char has not been cleared for login from your site!\n\r", d);
    STATE(d) = CON_CLOSE;
    return FALSE;
  }
  else
    return TRUE;
}

BOOL check_restrict(dsdata *d)
{
  char	buf[100];

  if (GET_LEVEL(DCH) < restrict) 
  {
    sprintf(buf, "Request for login denied for %s [%s] (wizlock)", GET_NAME(DCH), d->host);
    mudlog(buf, CMP, MAX(LEV_IMM, GET_INVIS_LEV(DCH)), TRUE);

    sprintf(buf, "%s is temporarily restricted to Immortal use only...\n\r", shortmudname);
    S2Q();
    send_to_q("We apologize for any inconveniences this may cause...\n\r", d);
    STATE(d) = CON_CLOSE;
    return FALSE;
  }
  else
    return TRUE;
}

/* check for switched characters on login*/
BOOL check_switched(dsdata *d)
{
  chdata *tmp_ch;
  char buf[100];

  for (tmp_ch = character_list; tmp_ch; tmp_ch = tmp_ch->next)
    if (IS_NPC(tmp_ch) && tmp_ch->desc && tmp_ch->desc->original &&
	!str_cmp(GET_NAME(tmp_ch->desc->original), GET_NAME(DCH)) ) 
    {
      log("SYSUPD: check_switched for loop reached.");
      send_to_q("Disconnecting.", tmp_ch->desc);
      free_char(DCH);

      DCH = tmp_ch->desc->original;

      tmp_ch->desc->character = 0;
      tmp_ch->desc->original = 0;
      STATE(tmp_ch->desc) = CON_CLOSE;

      DCH->desc = d;
      DCH->specials.timer = 0;
      send_to_q("Reconnecting to unswitched char.", d);
      REMOVE_BIT(PLR_FLAGS(DCH), PLR_MAILING | PLR_WRITING);
      sprintf(buf, "%s [%s] has reconnected.", GET_NAME(DCH), d->host);
      mudlog(buf, NRM, MAX(LEV_IMM, GET_INVIS_LEV(DCH)), TRUE);

      // no more menus...
      DESCMENU_PROMPT(d)  = NULL;
      DESCMENU_HANDLER(d) = NULL;
      DESCMENU_DEPTH(d)   = 0;
      STATE(d)            = CON_PLAYING;

      return TRUE;
    }

  return FALSE;
}

/* now check for linkless and usurpable */
BOOL check_linkless(dsdata *d)
{
  chdata *tmp_ch;
  char buf[100];

  for (tmp_ch = character_list; tmp_ch; tmp_ch = tmp_ch->next)
    if (IS_PC(tmp_ch) && !str_cmp(GET_NAME(DCH), GET_NAME(tmp_ch))) 
    {
      if (!tmp_ch->desc) 
      {
	REMOVE_BIT(PLR_FLAGS(tmp_ch), PLR_RITUAL);
	DELAY_TYPE(tmp_ch) = 0;
	send_to_q("Reconnecting.\n\r", d);

        // Added 04/04/98 -callahan
        if (has_mail(GET_IDNUM(DCH)))
          send_to_char("%BYou have mail waiting%0.\n\r", DCH);

	act("$n has reconnected.", TRUE, tmp_ch, 0, 0, TO_ROOM);
	sprintf(buf, "%s [%s] has reconnected.", GET_NAME(DCH), d->host);
        mudlog(buf, NRM, MAX(LEV_IMM, GET_INVIS_LEV(DCH)), TRUE);
      } 
      else 
      {
	REMOVE_BIT(PLR_FLAGS(tmp_ch), PLR_RITUAL);
	DELAY_TYPE(tmp_ch) = 0;
	sprintf(buf, "%s has re-logged in ... disconnecting old socket.", GET_NAME(tmp_ch));
        mudlog(buf, NRM, MAX(LEV_IMM, GET_INVIS_LEV(tmp_ch)), TRUE);

	send_to_q("This body has been usurped!\n\r", tmp_ch->desc);

	tmp_ch->desc->character = 0;
        STATE(tmp_ch->desc) = CON_CLOSE;
	tmp_ch->desc = 0;

	send_to_q("You take over your own body, already in use!\n\r", d);
	act("$n suddenly keels over in pain, surrounded by a white aura...\n\r"
	    "$n's body has been taken over by a new spirit!", TRUE, tmp_ch, 0, 0, TO_ROOM);
      }

      free_char(DCH);
      tmp_ch->desc = d;
      DCH = tmp_ch;
      tmp_ch->specials.timer = 0;
      REMOVE_BIT(PLR_FLAGS(DCH), PLR_MAILING | PLR_WRITING | PLR_BUILDING | PLR_TAGGED | PLR_INTAG);

      // no more menus...
      DESCMENU_PROMPT(d)  = NULL;
      DESCMENU_HANDLER(d) = NULL;
      DESCMENU_DEPTH(d)   = 0;
      STATE(d)            = CON_PLAYING;

      return TRUE;
    }

  return FALSE;
}

void send_main_menu(dsdata *d)
{
  if (VT100(DCH)) 
    send_to_q(VTMENU, d);
  else
    send_to_q(MENU, d);
}

void set_default_language(chdata *ch)
{
  switch (GET_RACE(ch)) {
   case RACE_HUMAN:
	SPEAKING(ch) = LANG_COMMON;
	break;
   case RACE_ELF:
   case RACE_HALF_ELF:
	SPEAKING(ch) = LANG_ELVEN;
	break;
   case RACE_ORC:
	SPEAKING(ch) = LANG_ORCISH;
	break;
   case RACE_OGRE:
	SPEAKING(ch) = LANG_OGRE;
	break;
   case RACE_DROW:
	SPEAKING(ch) = LANG_DROW;
	break;
   case RACE_DWARF:
	SPEAKING(ch) = LANG_DWARVEN;
	break;
   case RACE_PIXIE:
   case RACE_NIXIE:
	SPEAKING(ch) = LANG_IXISH;
	break;
   case RACE_DRAGON:
	SPEAKING(ch) = LANG_DRAGON;
	break;
  }
}

/* prevent people from getting past password confirm mult. times */
// set all of em to CLOSE then set this one to continue 7/13/98 -jtrhone
void prevent_multiples(dsdata *d)
{
  dsdata *k;

  for (k = descriptor_list;k;k = k->next) 
    if (k->character && IS_PC(k->character) && DCH && !str_cmp(GET_NAME(k->character), GET_NAME(DCH))) 
      STATE(k) = CON_CLOSE;

  STATE(d) = CON_DESCMENU;
}

void notify_pword_failures(dsdata *d, int num_fails)
{
  if (num_fails) 
  {
    sprintf(buf, "\n\r\n\r\007\007\007" "%d LOGIN FAILURE%s SINCE LAST SUCCESSFUL LOGIN.\n\r",
            num_fails, (num_fails > 1) ? "S" : "");
    S2Q();
  }
}

BOOL illegal_pwd(dsdata *d, char *arg)
{
  if (!arg || !*arg || strlen(arg) > MAX_PWD_LENGTH || strlen(arg) < 3 || !str_cmp(arg, GET_NAME(DCH))) 
  {
    send_to_q("\n\rIllegal password.\n\r", d);
    send_to_q("Password: ", d);
    return TRUE;
  }
  return FALSE;
}

BOOL matching_pwds(dsdata *d, char *arg)
{
  if (!arg || !*arg || strncmp(d->pwd, CRYPT(arg, arg), MAX_PWD_LENGTH)) 
  {
    send_to_q("\n\rPasswords don't match... start over.\n\r", d);
    descmenu_jump(d, getnewpasswd);
    return FALSE;
  }
  return TRUE;
}

BOOL valid_sex_arg(dsdata *d, char *arg)
{
  *arg = tolower(*arg);
  switch (*arg) {
  case 'm':
	 DCH->player.sex = SEX_MALE;
	 break;
  case 'f':
	 DCH->player.sex = SEX_FEMALE;
	 break;
  default:
	 return FALSE;
  }
  return TRUE;
}

void send_motds(dsdata *d)
{
  char cstr[MAX_STRING_LENGTH];

  if (IS_IMMORTAL(DCH))
  {
    color_decode(DCH, imotd, cstr);
    send_to_q(cstr, d);
  }
  else
  {
    color_decode(DCH, motd, cstr);
    send_to_q(cstr, d);
  }
}

// stuff to load on char immediately after connecting...
void load_immediates(chdata *ch)
{
  void load_aliases(chdata *ch);
  void load_char_quests(chdata *ch);

  load_aliases(ch);
  load_char_quests(ch);
}

int assign_locked_zone(dsdata *d)
{
  int zone;

  if (IS_IMMORTAL(DCH))
    for (zone = 0; zone < NUM_ZONES; zone++)
      if (REAL_ZONE(zone) && ZONE_FLAGGED(zone, Z_LOCKED) && zone_table[zone].locked_id == GET_IDNUM(DCH))
	return (DCH->pc_specials->saved.zone_locked = zone);

  return 0;
}

// things to do as player actually enters game...
// add update for client who lists  6/15/98 -jtrhone
void perform_entry_procs(dsdata *d)
{
  chdata *ch = DCH;
  void check_player_quest(chdata *ch, BOOL day_passed);
  void load_plshops(chdata *ch);
  void update_client_who(void);
  void update_client_eq(dsdata *d);
  void update_client_inv(dsdata *d);
  int save_gskills(chdata *ch);

  if (IS_PC(ch) && ONQUEST(ch) && VALID_QUEST(ONQUEST(ch)))
   check_player_quest(ch, FALSE);

  if (has_mail(GET_IDNUM(ch)))
   send_to_char("%BYou have mail waiting%0.\n\r", ch);

  load_plshops(ch);

  if (assign_locked_zone(d))
   send_to_char("%1%BLocked zone assigned.%0\n\r", ch);

  set_default_language(ch);

  update_client_who();

  // temporarily give everybody who enters knowledge of runic skill 7/8/98 -jtrhone
  if (!ch->gskills[GSKILL_RUNIC].learned)
  {
    set_gskill(ch, GSKILL_RUNIC, 1);
    save_gskills(ch);
    send_to_char("General skill %B%6runic%0 added to study list...\n\r",ch);
  } 
}

////////////////////
// Menu utils
////////////////////

// send version and menu title to char in descriptor menus
void descmenu_title_send(char *menu, dsdata *d)
{
  char buf[MAX_STRING_LENGTH];

  if (DCH && PLR_FLAGGED(DCH, PLR_REFRESH))
    clrscr(DCH);
  sprintf(buf, "\n\r    %%B%%5%s %s%%0\n\r", OLC_version, menu);
  S2Q();
}

// dont push onto the stack, just jump descriptor to this menu
int descmenu_jump(dsdata *d, void (*to_menu)(dsdata *d, char *input_str))
{
  DESCMENU_HANDLER(d) = to_menu;
  (*DESCMENU_HANDLER(d))(d, NULL);
  return 1;
}

// dont push onto the stack, just pt d to this menu
int descmenu_next(dsdata *d, void (*to_menu)(dsdata *d, char *input_str))
{
  DESCMENU_HANDLER(d) = to_menu;
  return 1;
}

// throw current onto stack, then jump
int descmenu_push_jump(dsdata *d, void (*to_menu)(dsdata *d, char *input_str))
{
  descmenu_push(d);
  DESCMENU_HANDLER(d) = to_menu;
  (*DESCMENU_HANDLER(d))(d, NULL);
  return 1;
}

// push a menu onto descs stack
int descmenu_push(dsdata *d)
{
  d->menu_args[DESCMENU_DEPTH(d)] = DESCMENU_HANDLER_ARG(d);
  d->menu_stack[DESCMENU_DEPTH(d)++] = DESCMENU_HANDLER(d);
  return 0;
}

// pop a menu off descs stack
int descmenu_pop(dsdata *d)
{
  DESCMENU_HANDLER_ARG(d) = d->menu_args[--DESCMENU_DEPTH(d)];
  DESCMENU_HANDLER(d) = d->menu_stack[DESCMENU_DEPTH(d)];
  return 0;
}

// pop menu off, and call current menu
int descmenu_back(dsdata *d)
{
  descmenu_pop(d);
  (*DESCMENU_HANDLER(d))(d, NULL);
  return 0;
}

ROA_DESCMENU(descmenu_long_string)
{
  descmenu_back(d);
}

// variable multiline string edit in descriptor menus
int do_var_string_arg_desc(dsdata *d, char *prompt, char **return_ptr, int length)
{
    char shellfname[100];
    char fname[100];
    BOOL cliedit = FALSE;
    extern void startshell(dsdata *d, char *x, char *y);

    if (!DCH)
      return -1;

    descmenu_push(d);

    // RoA
    // OK, if using alternate editor for these large files
    // write the current *return_ptr to their edit file so
    // spico can read it
    if (PLR_FLAGGED(DCH, PLR_EXEDIT))
    {
      sprintf(fname, "edits/%s.edit", GET_NAME(DCH));
      unlink(fname);

      if (*return_ptr)
        string_to_file(*return_ptr, fname);
    }

    // add ability to send string to client for editing
    // 2/25/98 -jtrhone
    if (PLR2_FLAGGED(DCH, PLR2_CLIEDIT) && HAS_CLIENT(d))
    {
      cliedit = TRUE;
      client_edit(d, prompt, *return_ptr);
      send_to_char("Switch to RoAClient...", DCH);
    }

    if (*return_ptr)
      free_log(*return_ptr, "do_var_string_arg_desc");
    *return_ptr = NULL;

    DESCMENU_HANDLER(d)       = descmenu_long_string;
    d->max_str                = length;
    d->str                    = return_ptr;

    if (PLR_FLAGGED(DCH, PLR_EXEDIT))
    {
      DESCMENU_PROMPT(d) = NULL;
      sprintf(shellfname, "%s.edit", GET_NAME(DCH));
      startshell(d, "", shellfname);
    }
    else
    if (cliedit)
    {
      DESCMENU_PROMPT(d) = NULL;
    }
    else
    {
      send_to_char(prompt, DCH);
      DESCMENU_PROMPT(d) = NULL;
    }

    return 0;
}

// first, main menu, get chars name...
ROA_DESCMENU(getname)
{
  char *arg = input_str;
  char buf[256], tmp_name[20], *ptr;
  int  player_i, num;
  struct char_file_u tmp_store;
  BOOL mobname = FALSE;

  // if called via code
  if (!input_str)
  {
    // jump to getname menu in descmenu.c
    switch(number(0,5)) {
      case 1:
        send_to_q(title1, d);
        break;
      case 2:
        send_to_q(title2, d);
        break;
      case 3:
        send_to_q(title3, d);
        break;
      case 4:
        send_to_q(title4, d);
        break;
      case 5:
        send_to_q(title5, d);
        break;
      default:
        send_to_q(title0, d);
        break;
    }
    send_to_q(" \n\r", d);
    send_to_q("By what name do you wish to be known? ", d);
    return;
  }

  // else keyed by user input
  if (DCH == NULL)
  {
    CREATE(DCH, chdata, 1);
    clear_char(DCH);

    CREATE(DCH->pc_specials, struct pc_special_data, 1);
    DCH->desc = d;
  }

  skip_spaces(&arg);
  if (!arg || !*arg)
  {
    close_socket(d);
    return;
  }

  if ((_parse_name(arg, tmp_name)) || strlen(tmp_name) < 2 || strlen(tmp_name) > (NAME_LENGTH - 2) ||
       fill_word(strcpy(buf,tmp_name)) || reserved_word(buf))
  {
    send_to_q("Invalid name, please try another.\n\r", d);
    send_to_q("Name: ", d);
    return;
  }

  if ((player_i = load_char(tmp_name, &tmp_store)) > -1)
  {
    d->pos = player_i;
    store_to_char(&tmp_store, DCH);

    if (PLR_FLAGGED(DCH, PLR_DELETED))
    {
      replace_deleted(d, tmp_name);
      descmenu_jump(d, confirmname);
    }
    else
    {
      strcpy(d->pwd, tmp_store.pwd);
      /* undo it just in case they are set */
      REMOVE_BIT(PLR_FLAGS(DCH),PLR_WRITING | PLR_MAILING |
                 PLR_BUILDING | PLR_CRYO | PLR_RITUAL | PLR_ARENA | PLR_TAGGED | PLR_INTAG);
      descmenu_jump(d, getpasswd);
    }
  }
  else /* player unknown gotta make a new */
  {
      // Make sure a mob name isn't used... 03/24/98 -callahan
      for (num = 0; num <= top_of_mobt; num++) {
        if (isname(tmp_name, mob_proto[num].player.name)) {
          mobname = TRUE;
          break;
        }
      }

      if (mobname) {
        send_to_q("You have chosen a name in use by a monster.\n\r", d);
        send_to_q("For your own safety, choose another name: ", d);
        return;
      }

      if (!Valid_Name(tmp_name)) {
        send_to_q("Invalid name, please try another.\n\r", d);
        send_to_q("Name: ", d);
        return;
      }

      // first, lower every char in name, then up the first...
      // enforce name policy... 2/21/98 -jtrhone
      for (ptr = tmp_name; *ptr; ptr++)
        *ptr = tolower(*ptr);
      *tmp_name = UPPER(*tmp_name);

      DCH->player.name = str_dup(tmp_name);
      descmenu_jump(d, confirmname);
  }
}

ROA_DESCMENU(confirmname)
{
  char *arg = input_str;

  if (!input_str)
  {
    sprintf(buf, "Is this correct, %s (Y/N)? ", DCH->player.name);
    S2Q();
    return;
  }

  skip_spaces(&arg);
  if (*arg == 'y' || *arg == 'Y')
  {
    if (isbanned(d->host) >= BAN_NEW)
    {
      sprintf(buf, "Request for new char %s denied from [%s] (siteban)", GET_NAME(DCH), d->host);
      mudlog(buf, NRM, LEV_GOD, TRUE);
      send_to_q("Sorry, new characters not allowed from your site!\n\r", d);
      STATE(d) = CON_CLOSE;
      return;
    }

    if (restrict)
    {
      send_to_q("Sorry, new players can't be created at the moment.\n\r", d);
      sprintf(buf, "Request for new char %s denied from %s (wizlock)", GET_NAME(DCH), d->host);
      mudlog(buf, NRM, LEV_GOD, TRUE);
      STATE(d) = CON_CLOSE;
      return;
    }

    descmenu_jump(d, getnewpasswd);
  }
  else
  if (*arg == 'n' || *arg == 'N')
  {
    send_to_q("Ok, what IS it, then? ", d);
    FREENULL(DCH->player.name);
    descmenu_next(d, getname);
  }
  else
    send_to_q("Please type Yes or No: ", d);
}

ROA_DESCMENU(getpasswd)
{
  char *arg = input_str;

  if (!input_str)
  {
    send_to_q("Password: ", d);
    echo_off(d);
    return;
  }

  echo_on(d);

  skip_spaces(&arg);
  if (!*arg)
    close_socket(d);
  else
  {
    if (PLR_FLAGGED(DCH, PLR_INVSTART)) 
      GET_INVIS_LEV(DCH) = GET_LEVEL(DCH);

    if (!check_password(d, arg, TRUE))
      return;

    save_char(DCH, NOWHERE);

    if (!check_banned(d))
      return;
    if (!check_restrict(d))
      return;
    if (check_switched(d))
      return;
    if (check_linkless(d))
      return;

    prevent_multiples(d);

    // stuff to load on char immediately after connecting...
    load_immediates(DCH);

    sprintf(buf, "%s [%s] has connected.", GET_NAME(DCH), d->host);
    mudlog(buf, BRF, MAX(LEV_IMM, GET_INVIS_LEV(DCH)), TRUE);

    descmenu_jump(d, motdsequence);
  }
}

ROA_DESCMENU(motdsequence)
{
  if (!input_str)
  {
    send_to_q(credit_sequence, d);
    send_motds(d);

    notify_pword_failures(d, DCH->pc_specials->saved.bad_pws); 
    DCH->pc_specials->saved.bad_pws = 0;
    return;
  }

  descmenu_jump(d, mainmenu);
}

ROA_DESCMENU(getnewpasswd)
{
  char *arg = input_str;

  if (!input_str)
  {
    send_to_q("New character.\n\r", d);
    sprintf(buf, "Enter password for %s: ", GET_NAME(DCH));
    send_to_q(buf, d);
    echo_off(d);
    return;
  }

  skip_spaces(&arg);
  if (illegal_pwd(d, arg))
    return;

  strncpy(d->pwd, CRYPT(arg, arg), MAX_PWD_LENGTH);
  *(d->pwd + MAX_PWD_LENGTH) = '\0';

  descmenu_jump(d, confirmnewpasswd);
}

ROA_DESCMENU(confirmnewpasswd)
{
  char *arg = input_str;

  if (!input_str)
  {
    send_to_q("\n\rPlease retype password: ", d);
    return;
  }

  skip_spaces(&arg);

  if (!matching_pwds(d, arg))
    return;

  echo_on(d);

  descmenu_jump(d, getansi);
}

ROA_DESCMENU(getansi)
{
  char *arg = input_str;

  if (!input_str)
  {
    send_ansi_menu(d);
    return;
  }

  skip_spaces(&arg);
  *arg = tolower(*arg);
  if (*arg != 'y' && *arg != 'n')
  {
    send_ansi_menu(d);
    return;
  }

  if (*arg == 'y' || *arg == 'Y')
    SET_BIT(PRF_FLAGS(DCH), PRF_COLOR);

  descmenu_jump(d, begincreation);
}

ROA_DESCMENU(getsex)
{
  char *arg = input_str;

  if (!input_str)
  {
    send_sex_menu(d);
    return;
  }

  skip_spaces(&arg);
  if (!valid_sex_arg(d, arg))
  {
    send_sex_menu(d);
    return;
  }

  descmenu_jump(d, getrace);
}

ROA_DESCMENU(begincreation)
{
  char *arg = input_str;

  if (!input_str)
  {
    send_creation_menu(d);
    return;
  }

  skip_spaces(&arg);
  if (!*arg)
  {
    send_creation_menu(d);
    return;
  }

  *arg = tolower(*arg);
  if (*arg == 'D' || *arg == 'd')
  {
    STATE(d) = CON_CLOSE;
    return;
  }
  else
  if (*arg == 'C' || *arg == 'c')
  {
    descmenu_jump(d, getsex);
    return;
  }

  send_creation_menu(d);
}

ROA_DESCMENU(getrace)
{
  char *arg = input_str;
  int i;

  if (!input_str)
  {
    send_race_menu(d);
    return;
  }

  skip_spaces(&arg);
  if (!*arg)
  {
    send_race_menu(d);
    return;
  }

  if (*arg == 'M' || *arg == 'm')
  {
    descmenu_jump(d, begincreation);
    return;
  }

  if (*arg == '?')
  {
    arg++;

    skip_spaces(&arg);

    if (is_number(arg))
      i = atoi(arg) + 1;  // have to offset by 1
    else {
      send_race_menu(d);
      return;
    }

    // range check against num_races...
    if (i < 1 || i > NUM_RACES)
    {
      send_race_menu(d);
      return;
    }

    strcpy(buf2, rcarray[i].race_name);
    do_help(DCH, buf2, 0, 0);

    descmenu_push_jump(d, genericreturn);
    return;
  }

  *buf = *arg;
  *(buf+1) = '\0';

  i = atoi(buf);
  if ((i < 0) || (i > 9))
  {
    send_race_menu(d);
    return;
  }

  /* 0 is UNDEFINED!!! so bump choice up 1 */
  DCH->pc_specials->saved.race = i + 1;

  descmenu_jump(d, getclass);
}

ROA_DESCMENU(getclass)
{
  char *arg = input_str;
  int i;

  if (!input_str)
  {
    send_class_menu(d);
    return;
  }

  skip_spaces(&arg);
  if (!*arg) {
    send_class_menu(d);
    return;
  }

  if (*arg == 'M' || *arg == 'm')
  {
    descmenu_jump(d, begincreation);
    return;
  }

  if (*arg == '?')
  {
    arg++;

    skip_spaces(&arg);

    if (is_number(arg))
      i = atoi(arg);
    else {
      send_class_menu(d);
      return;
    }

    // range check against num_classes...
    if (i < 1 || i > NUM_CLASSES)
    {
      send_class_menu(d);
      return;
    }

    strcpy(buf2, clarray[i].class_name);
    do_help(DCH, buf2, 0, 0);

    descmenu_push_jump(d, genericreturn);
    return;
  }

  i = atoi(arg);

  if ((i < 1) || (i > NUM_CLASSES) || (!rcarray[GET_RACE(DCH)].race_allow[i]))
  {
    send_class_menu(d);
    return;
  }

  GET_CLASS(DCH) = i;

  descmenu_jump(d, gethome);
}

ROA_DESCMENU(gethome)
{
  char *arg = input_str;
  int i;

  if (!input_str)
  {
    send_hometown_menu(d);
    return;
  }

  skip_spaces(&arg);
  if (*arg == 'M' || *arg == 'm')
  {
    descmenu_jump(d, begincreation);
    return;
  }

  // fixed 1 digit limitation  6/1/98 -jtrhone
  i = atoi(arg);
  if ((i < 0) || (i >= MAX_HTOWNS) || !htowns[i].on_menu || !htowns[i].name || !*htowns[i].name ||
      (!htowns[i].races[GET_RACE(DCH)]))
  {
    send_hometown_menu(d);
    return;
  }

  GET_HTOWN(DCH) = i;
  GET_LOADROOM(DCH) = htowns[i].rvnum;

  descmenu_jump(d, getalignment);
}

ROA_DESCMENU(getalignment)
{
  char *arg = input_str;
  int num;

  if (!input_str)
  {
    send_align_menu(d);
    return;
  }

  skip_spaces(&arg);
  if (!*arg)
  {
    send_align_menu(d);
    return;
  }

  num = atoi(arg);
  if (num < -1000 || num > 1000)
  {
    send_align_menu(d);
    return;
  }

  GET_ALIGNMENT(DCH) = num;
  descmenu_jump(d, getassassin);
}

ROA_DESCMENU(getassassin)
{
  char *arg = input_str;

  if (!input_str)
  {
    send_assassin_menu(d);
    return;
  }

  skip_spaces(&arg);
  if (!*arg)
  {
    send_assassin_menu(d);
    return;
  }

  *arg = tolower(*arg);
  if (*arg == 'y')
  {
    send_to_q("\n\rYou become an Assassin!\n\r", d);
    SET_BIT(PRF_FLAGS(DCH), PRF_ASSASSIN);
  }
  else
  if (*arg == 'n')
    REMOVE_BIT(PRF_FLAGS(DCH), PRF_ASSASSIN);
  else
  {
    send_assassin_menu(d);
    return;
  }

  // ok, continue onwards!
  if (d->pos < 0)
    d->pos = create_entry(GET_NAME(DCH));

  init_char(DCH);
  save_char(DCH, NOWHERE);

  /* make sure we update the index in memory... -roa */
  update_p_index(top_of_p_table, GET_IDNUM(DCH), d->host);

  // special case because of description...
  send_description_menu(d);
  descmenu_next(d, rerolltip);
  do_var_string_arg_desc(d, "Terminate description with a '@':\n\r", 
                         &DCH->player.description, EXDESC_LENGTH);
}

ROA_DESCMENU(rerolltip)
{
  if (!input_str)
  {
    sprintf(buf, "%s [%s] new adventurer.", GET_NAME(DCH), d->host);
    mudlog(buf, NRM, LEV_IMM, TRUE);
    send_attribute_menu(d);
    send_to_q("\n\r\n*** PRESS RETURN: ", d);
    return;
  }

  descmenu_jump(d, motdsequence);
}

ROA_DESCMENU(genericreturn)
{
  send_to_q("\n\r\n*** PRESS RETURN: ", d);
  descmenu_pop(d);
}

ROA_DESCMENU(showbackground)
{
  if (!input_str)
  {
    page_string(d, background, 0);
    return;
  }

  descmenu_jump(d, mainmenu);
}

ROA_DESCMENU(mainmenu)
{
  char *arg = input_str;
  BOOL extract = FALSE;
  dsdata *k, *next;
  chdata *nch;
  int load_room;

  skip_spaces(&arg);

  if (!arg || !*arg)
  {
    send_main_menu(d);
    return;
  }

  switch (*arg)
  {
    case '0':
      close_socket(d);
      break;

    case '1':
      /* lets require a description of some sort... RoA */
      if (requirelongdesc && (!DCH->player.description || !*DCH->player.description))
      {
        send_to_q("You must enter a description of this character to enter the game.\n\r\n\r",d);
        send_main_menu(d);
        break;
      }

      extract = FALSE;

      /* this code is to prevent people from multiply logging in */
      for (k = descriptor_list; k; k=next)
      {
        next = k->next;
        if (D_CHECK(k) && !str_cmp(GET_NAME(k->character), GET_NAME(DCH)) )
        {
          send_to_q("Your character has been deleted.\n\r",d);
          close_socket(d);
          extract = TRUE;
          break;
        }
      }

      if (extract)
        break;

      extract = FALSE;
      for (nch = character_list;nch;nch = nch->next)
      {
        if(IS_PC(nch) && !str_cmp(GET_NAME(nch), GET_NAME(DCH)))
        {
          send_to_q("Your character has been extracted.\n\r", d);
          close_socket(d);
          extract = TRUE;
          break;
        }
      }

      if (extract)
        break;

      reset_char(DCH);

      load_char_objects(DCH);

      if (GET_HTOWN(DCH) < 0 || GET_HTOWN(DCH) >= MAX_HTOWNS || !htowns[GET_HTOWN(DCH)].name || !*htowns[GET_HTOWN(DCH)].name)
      {
        GET_HTOWN(DCH) = 0;
        GET_LOADROOM(DCH) = DEF_GATEWAY;
        send_to_char("%B%1Invalid Hometown. Using default.%0\n\r", DCH);
      }

      if (!autowear(DCH))
        send_to_char("Autowear positioning error.\n\r", DCH);

      save_char(DCH, NOWHERE);

      send_to_char(welcome, DCH);
      DCH->next = character_list;
      character_list = DCH;

      if (IS_IMMORTAL(DCH))
      {
        if (real_room(GET_LOADROOM(DCH)) >= 0)
          load_room = real_room(GET_LOADROOM(DCH));
        else
          load_room = r_immort_start_room;
       }
       else
       {
         // if LOADROOM flag set on plr, houses for example, and the room is real
         if (PLR_FLAGGED(DCH, PLR_LOADROOM) && real_room(GET_LOADROOM(DCH)) > 0)
           load_room = real_room(GET_LOADROOM(DCH));
         else // else try hometown loadroom
         {
           GET_LOADROOM(DCH) = htowns[GET_HTOWN(DCH)].rvnum;

           if (PLR_FLAGGED(DCH, PLR_FROZEN))
             load_room = r_frozen_start_room;
           else
           if (!GET_LEVEL(DCH))
             load_room = r_newbie_start_room;
           else
           if ((real_room(GET_LOADROOM(DCH)) >= 0) && !ZONE_FLAGGED(GET_LOADROOM(DCH)/100, Z_LOCKED) &&
               !ZONE_FLAGGED(GET_LOADROOM(DCH)/100, Z_CLOSED))
             load_room = real_room(GET_LOADROOM(DCH));
           else
             load_room = r_mortal_start_room;
         }
       }

       char_to_room(DCH, load_room);
       act("$n has entered the realms.", TRUE, DCH, 0, 0, TO_ROOM);

       /* init if vt100 */
       if (VT100(DCH))
         init_vtbar(DCH);

       if (!GET_LEVEL(DCH)) {
         do_start(DCH, FALSE);
         send_to_char(start, DCH);
       }

       do_look_at_room(DCH, 0, 0);

       perform_entry_procs(d);

       // no more menus...
       DESCMENU_PROMPT(d)  = NULL;
       DESCMENU_HANDLER(d) = NULL;
       DESCMENU_DEPTH(d)   = 0;
       STATE(d)            = CON_PLAYING;
       d->prompt_mode = 1;
       break;

     case '2':
       send_to_q("Enter the text you'd like others to see when they look at you.\n\r", d);
       do_var_string_arg_desc(d, "Terminate description with a '@':\n\r", 
                              &DCH->player.description, EXDESC_LENGTH);
       break;

     case '3':
       descmenu_jump(d, showbackground);
       break;

     case '4':
       descmenu_jump(d, changepasswd1);
       break;

     case '5':
       who_to_buf(buf2);
       killp(buf2);
       send_to_q(buf2, d);
       descmenu_push_jump(d, genericreturn);
       break;

     case '6':
       update_mortal_stats(buf2, DCH);
       killp(buf2);
       send_to_q(buf2, d);
       descmenu_push_jump(d, genericreturn);
       break;

     case '9':
       descmenu_jump(d, confirmdelete1);
       break;

     default:
       send_to_q("\n\rThat's not a menu choice!\n\r", d);
       send_main_menu(d);
       break;
  }
}

ROA_DESCMENU(changepasswd1)
{
  char *arg = input_str;

  if (!input_str) 
  {
    send_to_q("Enter your old password: ", d);
    echo_off(d);
    return;
  }

  skip_spaces(&arg);
  if (strncmp(d->pwd, CRYPT(arg, arg), MAX_PWD_LENGTH)) 
  {
    send_to_q("\n\rIncorrect password.\n\r", d);
    echo_on(d);
    descmenu_jump(d, mainmenu);
    return;
  } 
  else 
    descmenu_jump(d, changepasswd2);
}

ROA_DESCMENU(changepasswd2)
{
  char *arg = input_str;

  if (!input_str)
  {
    send_to_q("\n\rEnter a new password: ", d);
    return;
  }

  skip_spaces(&arg);
  if (!*arg || strlen(arg) > MAX_PWD_LENGTH || strlen(arg) < 3 || !str_cmp(arg, GET_NAME(DCH))) 
  {
    send_to_q("\n\rIllegal password.\n\r", d);
    send_to_q("Password: ", d);
    return;
  }

  strncpy(d->pwd, CRYPT(arg, arg), MAX_PWD_LENGTH);
  *(d->pwd + MAX_PWD_LENGTH) = '\0';

  descmenu_jump(d, changepasswd3);
}

ROA_DESCMENU(changepasswd3)
{
  char *arg = input_str;

  if (!input_str)
  {
    send_to_q("\n\rPlease retype password: ", d);
    return;
  }

  skip_spaces(&arg);
  if (strncmp(d->pwd, CRYPT(arg, arg), MAX_PWD_LENGTH)) 
  {
    send_to_q("\n\rPasswords don't match... start over.\n\r", d);
    descmenu_jump(d, changepasswd1);
    return;
  }

  send_to_q("\n\rDone.  You must enter the Realms to make the change final.\n\r", d);
  echo_on(d);
  descmenu_jump(d, mainmenu);
}

ROA_DESCMENU(confirmdelete1)
{
  char *arg = input_str;

  if (!input_str)
  {
    send_to_q("\n\r\007Enter your password for verification: ", d);
    echo_off(d);
    return;
  }

  skip_spaces(&arg);
  echo_on(d);
  if (strncmp(d->pwd, CRYPT(arg, arg), MAX_PWD_LENGTH)) 
  {
    send_to_q("\n\rIncorrect password.\n\r", d);
    descmenu_jump(d, mainmenu);
  } 
  else 
    descmenu_jump(d, confirmdelete2);
}

ROA_DESCMENU(confirmdelete2)
{
  char *arg = input_str;

  if (!input_str)
  {
    send_to_q("\n\rYOU ARE ABOUT TO DELETE THIS CHARACTER PERMANENTLY.\n\rARE YOU ABSOLUTELY SURE?\n\r\n\r"
              "Please type \"yes\" to confirm: ", d);
    return;
  }

  skip_spaces(&arg);
  if (!str_cmp(arg, "yes") || !str_cmp(arg, "YES")) 
  {
    if (PLR_FLAGGED(DCH, PLR_FROZEN)) 
    {
      send_to_q("You try to kill yourself, but the ice stops you.\n\r",d);
      send_to_q("Character not deleted.\n\r\n\r",d);
      STATE(d) = CON_CLOSE;
      return;
    }

    if (GET_LEVEL(DCH) < LEV_CIMP)
      SET_BIT(PLR_FLAGS(DCH), PLR_DELETED);

    save_char(DCH, NOWHERE);
    delete_alias_file(DCH);
    delete_object_file(DCH);
    delete_reimb_file(DCH);
    sprintf(buf, "Character '%s' deleted!\n\rGoodbye.\n\r", GET_NAME(DCH));
    S2Q();
    STATE(d) = CON_CLOSE;

    sprintf(buf, "%s (lev %d) has self-deleted.", GET_NAME(DCH), GET_LEVEL(DCH));
    mudlog(buf, NRM, LEV_GOD, TRUE);
    return;
  } 
  else 
  {
    send_to_q("Character not deleted.\n\r\n\r", d);
    descmenu_jump(d, mainmenu);
  }
}