alanthia/area/
alanthia/gods/
alanthia/player/
/***************************************************************************
 *  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
 *  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
 *                                                                         *
 *  Merc Diku Mud improvments copyright (C) 1992, 1993 by Michael          *
 *  Chastain, Michael Quan, and Mitchell Tse.                              *
 *                                                                         *
 *  In order to use any part of this Merc Diku Mud, you must comply with   *
 *  both the original Diku license in 'license.doc' as well the Merc       *
 *  license in 'license.txt'.  In particular, you may not remove either of *
 *  these copyright notices.                                               *
 *                                                                         *
 *  Thanks to abaddon for proof-reading our comm.c and pointing out bugs.  *
 *  Any remaining bugs are, of course, our work, not his.  :)              *
 *                                                                         *
 *  Much time and thought has gone into this software and you are          *
 *  benefitting.  We hope that you share your changes too.  What goes      *
 *  around, comes around.                                                  *
 ***************************************************************************/

/***************************************************************************
*	ROM 2.4 is copyright 1993-1996 Russ Taylor			   *
*	ROM has been brought to you by the ROM consortium		   *
*	    Russ Taylor (rtaylor@efn.org)				   *
*	    Gabrielle Taylor						   *
*	    Brian Moore (zump@rom.org)					   *
*	By using this code, you have agreed to follow the terms of the	   *
*	ROM license, in the file Rom24/doc/rom.license			   *
***************************************************************************/

/*
 * This file contains all of the OS-dependent stuff:
 *   startup, signals, BSD sockets for tcp/ip, i/o, timing.
 *
 * The data flow for input is:
 *    Game_loop ---> Read_from_descriptor ---> Read
 *    Game_loop ---> Read_from_buffer
 *
 * The data flow for output is:
 *    Game_loop ---> Process_Output ---> Write_to_descriptor -> Write
 *
 * The OS-dependent functions are Read_from_descriptor and Write_to_descriptor.
 * -- Furey  26 Jan 1993
 */

#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>

#include <netinet/in.h>

#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>
#include <unistd.h>
#include <malloc.h>
#include <signal.h>
#include <fcntl.h>
#include <netdb.h>
#include <crypt.h>

#include "merc.h"
#include "utils.h"
#include "telnet.h"
#include "recycle.h"
#include "tables.h"
#include "utils.h"

DECLARE_DO_FUN(do_help);
DECLARE_DO_FUN(do_look);
DECLARE_DO_FUN(do_skills);
DECLARE_DO_FUN(do_spells);
DECLARE_DO_FUN(do_outfit);
DECLARE_DO_FUN(do_unread);
DECLARE_DO_FUN(do_asave);
DECLARE_DO_FUN(do_echo);
DECLARE_DO_FUN(do_replay);
DECLARE_DO_FUN(do_quit);

/*
 * Socket and TCP/IP stuff.
 */

const char echo_off_str[] = { IAC, WILL, TELOPT_ECHO, '\0' };
const char echo_on_str[] = { IAC, WONT, TELOPT_ECHO, '\0' };
const char go_ahead_str[] = { IAC, GA, '\0' };
bool rolled;
int stat1[5], stat2[5], stat3[5], stat4[5], stat5[5];

// send a class menu during chargen in a box
void class_menu( CHAR_DATA *ch ) {

	char buf[MIL];
	int count;
	char *sep = " {g+----------------------------------------------------------------------------+{x\r\n";
	char *blank = "{g |                                                                            |{x\r\n";

	send_to_char( "\r\n", ch );
	send_to_char( sep, ch );
   	send_to_char( blank, ch );
	send_to_char( "{g |     {WCurrent Classes            Exp Per Level at 40 CP                      {g|{x\r\n", ch );
   	send_to_char( blank, ch );
	send_to_char( " {g|           {y", ch );

	for( count = 0; count < MAX_CLASS; count++) {
		if ( !class_table[count].valid )
			continue;

			ch->class = count;
			sprintf( buf, "%-30s %d", 
			class_table[count].name,
			exp_per_level( ch, 40 ) );

			buf[0] = UPPER(buf[0]);
			send_to_char( buf, ch );
			send_to_char( "                              {g| \r\n{g |           {y", ch );
			

	}
	send_to_char( "                                                                 {g| \r\n", ch );
   	send_to_char( sep, ch );

	return;
}

void do_cm ( CHAR_DATA *ch, char *argument ) {

	class_menu( ch );
	return;
}



void race_menu( CHAR_DATA *ch ) {

	char buf[MIL];
	RACE_DATA *race;
	int i = 0;
	char *sep = " {g+----------------------------------------------------------------------------+{x\r\n";
	char *blank = "{g |                                                                            |{x\r\n";

	send_to_char( "\r\n", ch );
	send_to_char( sep, ch );
   	send_to_char( blank, ch );

			send_to_char( "{g |{W    Race        Points   Max Stats      Race       Points    Max Stats      {g| ", ch);
			send_to_char( "{g |{W                      St In Wi Co De                      St In Wi Co De    {g| ", ch);
	send_to_char( "{g |    ", ch );
   for (race = race_first; race != NULL; race = race->next) {

		if ( !race->pc_race )
			continue;

			sprintf( buf, "%15s{x %-3d %-2d %-2d %-2d %-2d %-2d    ", 
			race->gen_name,
			race->points, 
			race->stats[STAT_STR],
			race->stats[STAT_INT],
			race->stats[STAT_WIS],
			race->stats[STAT_CON],
			race->stats[STAT_DEX] );

			buf[0] = UPPER(buf[0]);
			send_to_char( buf, ch );
			i++;

			if (i % 2 == 0)
				send_to_char("{g|\r\n |    ", ch);
	}

	if (i % 2 != 0)
		send_to_char("                                    {g|\r\n", ch);

   	send_to_char( blank, ch );
   	send_to_char( sep, ch );

	return;
}

void do_rm( CHAR_DATA *ch, char * argument ) {

	race_menu( ch );

	return;

}

/*
 * Parse a name for acceptability.
 */
bool check_parse_name(char *name)
{
    /*
     * Reserved words.
     */
    if (is_name (name,
	 "all auto immortal self someone something the you demise balance circle loner honor"))
	    return FALSE;


    /*
     * Length restrictions.
     */

    if (strlen(name) < 2)
	return FALSE;

    if (strlen(name) > 12)
	return FALSE;

    /*
     * Alphanumerics only.
     * Lock out IllIll twits.
     */
    {
	char *pc;
	bool fIll, adjcaps = FALSE, cleancaps = FALSE;
	int total_caps = 0;

	fIll = TRUE;
	for (pc = name; *pc != '\0'; pc++) {
	    if (!isalpha(*pc))
		return FALSE;

	    if (isupper(*pc)) {	/* ugly anti-caps hack */
		if (adjcaps)
		    cleancaps = TRUE;
		total_caps++;
		adjcaps = TRUE;
	    } else
		adjcaps = FALSE;

	    if (LOWER(*pc) != 'i' && LOWER(*pc) != 'l')
		fIll = FALSE;
	}

	if (fIll)
	    return FALSE;

	if (cleancaps
	    || (total_caps > (strlen(name)) / 2
		&& strlen(name) < 3)) return FALSE;
    }

    /*
     * Prevent players from naming themselves after mobs.
     */
    {
	extern MOB_INDEX_DATA *mob_index_hash[MAX_KEY_HASH];
	MOB_INDEX_DATA *pMobIndex;
	int iHash;

	for (iHash = 0; iHash < MAX_KEY_HASH; iHash++) {
	    for (pMobIndex = mob_index_hash[iHash];
		 pMobIndex != NULL; pMobIndex = pMobIndex->next) {
		if (is_name(name, pMobIndex->player_name))
		    return FALSE;
	    }
	}
    }

    return TRUE;
}

/*
 * Deal with sockets that haven't logged in yet.
 */
void nanny(DESCRIPTOR_DATA * d, char *argument)
{
    DESCRIPTOR_DATA *d_old, *d_next;
	RACE_DATA *race;
    extern const char *ansi_greeting;
    extern const char *no_ansi_greeting;
    char buf[MAX_STRING_LENGTH];
    char arg[MAX_INPUT_LENGTH];

    CHAR_DATA *ch;
    char *pwdnew;
    char *p;
    int iClass, i, x, weapon, iStat;
    bool fOld;
    bool BadStat = FALSE;


    /* Delete leading spaces UNLESS character is writing a note */


    while (isspace(*argument))
	argument++;

    ch = d->character;

    switch (d->connected) {

    default:
	bug("Nanny: bad d->connected %d.", d->connected);
	close_socket(d);
	return;

    case CON_ASK_IF_COLOUR:

	if ((argument[0] == 'n') || (argument[0] == 'N'))
	    d->ansi = FALSE;
	else
	    d->ansi = TRUE;

	/*
	 * Send the greeting.
	 */
	{

	    if (d->ansi) {

		write_color_to_buffer(ansi_greeting, d);

	    } else {

		write_to_buffer(d, no_ansi_greeting, 0);
	    }
	}

	d->connected = CON_GET_NAME;
	return;

    case CON_GET_NAME:
	if (argument[0] == '\0') {
	    close_socket(d);
	    return;
	}
	argument[0] = UPPER(argument[0]);
	if (!check_parse_name(argument)) {
	    write_to_buffer(d, "Illegal name, try another.\n\rName: ", 0);
	    return;
	}
	fOld = load_char_obj(d, argument);
	ch = d->character;

	if (IS_SET(ch->act, PLR_DENY)) {
	    sprintf(log_buf, "Denying access to %s@%s.", argument,
		    d->host);
	    log_string(log_buf);
	    write_to_buffer(d, "You are denied access.\n\r", 0);
	    close_socket(d);
	    return;
	}
	if (check_reconnect(d, argument, FALSE)) {
	    fOld = TRUE;
	} else {
	    if (wizlock && !IS_HERO(ch)) {
		write_to_buffer(d, "The game is wizlocked.\n\r", 0);
		close_socket(d);
		return;
	    }
	}

	if (fOld) {
	    if (d->ansi)
		write_to_buffer(d, C_GREEN, 0);
	    write_to_buffer(d, "Password: ", 0);
	    write_to_buffer(d, CLEAR, 0);
	    write_to_buffer(d, echo_off_str, 0);
	    d->connected = CON_GET_OLD_PASSWORD;
	    return;
	} else {
	    /* New player */
	    if (newlock) {
		write_to_buffer(d, "The game is newlocked.\n\r", 0);
		close_socket(d);
		return;
	    }
	    sprintf(buf,
		    "Please make sure that your name is appropriate for a fantasy realm.\nDid I get that right, %s (Y/N)? ",
		    argument);
	    write_to_buffer(d, buf, 0);
	    d->connected = CON_CONFIRM_NEW_NAME;
	    return;
	}

	if (check_playing(d, ch->name))
	    return;

	break;

    case CON_GET_OLD_PASSWORD:
#if defined(unix)
	write_to_buffer(d, "\n\r", 2);
#endif

	if (strcmp(crypt(argument, ch->pcdata->pwd), ch->pcdata->pwd)) {
	    write_to_buffer(d, "Wrong password.\n\r", 0);
	    close_socket(d);
	    return;
	}
	if (ch->pcdata->pwd[0] == 0) {
	    write_to_buffer(d, "Warning! Null password!\n\r", 0);
	    write_to_buffer(d, "Please report old password with bug.\n\r",
			    0);
	    write_to_buffer(d,
			    "Type 'password null <new password>' to fix.\n\r",
			    0);
	}
	write_to_buffer(d, echo_on_str, 0);

	if (check_reconnect(d, ch->name, TRUE))
	    return;

	if (check_playing(d, ch->name))
	    return;

	sprintf(log_buf, "%s@%s has connected.", ch->name, d->host);

	log_string(log_buf);
	wiznet(log_buf, NULL, NULL, WIZ_SITES, 0, get_trust(ch));
	if (IS_HERO(ch)) {
	    do_help(ch, "imotd");
	    d->connected = CON_READ_IMOTD;
	} else {
	    do_help(ch, "motd");
	    d->connected = CON_READ_MOTD;
	}
	break;

/* RT code for breaking link */

    case CON_BREAK_CONNECT:
	switch (*argument) {
	case 'y':
	case 'Y':
	    for (d_old = descriptor_list; d_old != NULL; d_old = d_next) {
		d_next = d_old->next;
		if (d_old == d || d_old->character == NULL)
		    continue;

		if (str_cmp(ch->name, d_old->character->name))
		    continue;

		close_socket(d_old);
	    }
	    if (check_reconnect(d, ch->name, TRUE))
		return;
	    write_to_buffer(d, "Reconnect attempt failed.\n\rName: ", 0);
	    if (d->character != NULL) {
		free_char(d->character);
		d->character = NULL;
	    }
	    d->connected = CON_GET_NAME;
	    break;

	case 'n':
	case 'N':
	    write_to_buffer(d, "Name: ", 0);
	    if (d->character != NULL) {
		free_char(d->character);
		d->character = NULL;
	    }
	    d->connected = CON_GET_NAME;
	    break;

	default:
	    write_to_buffer(d, "Please type Y or N? ", 0);
	    break;
	}
	close_socket(ch->desc);
	break;

    case CON_CONFIRM_NEW_NAME:
	switch (*argument) {
	case 'y':
	case 'Y':
	    sprintf(buf, "New character.\n\rGive me a password for %s: %s",
		    ch->name, echo_off_str);
	    write_to_buffer(d, buf, 0);
	    d->connected = CON_GET_NEW_PASSWORD;
	    if (d->ansi)
		SET_BIT(ch->act, PLR_COLOUR);
	    break;

	case 'n':
	case 'N':
	    write_to_buffer(d, "Ok, what IS it, then? ", 0);
	    free_char(d->character);
	    d->character = NULL;
	    d->connected = CON_GET_NAME;
	    break;

	default:
	    write_to_buffer(d, "Please type Yes or No? ", 0);
	    break;
	}
	break;

    case CON_GET_NEW_PASSWORD:
#if defined(unix)
	write_to_buffer(d, "\n\r", 2);
#endif

	if (strlen(argument) < 5) {
	    write_to_buffer(d,
			    "Password must be at least five characters long.\n\rPassword: ",
			    0);
	    return;
	}
	pwdnew = crypt(argument, ch->name);
	for (p = pwdnew; *p != '\0'; p++) {
	    if (*p == '~') {
		write_to_buffer(d,
				"New password not acceptable, try again.\n\rPassword: ",
				0);
		return;
	    }
	}

	free_string(ch->pcdata->pwd);
	ch->pcdata->pwd = str_dup(pwdnew);
	write_to_buffer(d, "Please retype password: ", 0);
	d->connected = CON_CONFIRM_NEW_PASSWORD;
	break;

    case CON_CONFIRM_NEW_PASSWORD:
#if defined(unix)
	write_to_buffer(d, "\n\r", 2);
#endif

	if (strcmp(crypt(argument, ch->pcdata->pwd), ch->pcdata->pwd)) {
	    write_to_buffer(d,
			    "Passwords don't match.\n\rRetype password: ",
			    0);
	    d->connected = CON_GET_NEW_PASSWORD;
	    return;
	}
	write_to_buffer(d, echo_on_str, 0);
	write_to_buffer(d, "The following races are available:\n\r ", 0);

	race_menu( ch );

	write_to_buffer(d, "\r\n ", 1);
	write_to_buffer(d, "\n\r", 0);
	write_to_buffer(d,
			"What is your race (help for more information)? ",
			0);
	d->connected = CON_GET_NEW_RACE;
	break;

    case CON_GET_NEW_RACE:
	one_argument(argument, arg);

	if (!strcmp(arg, "help")) {
	    argument = one_argument(argument, arg);
	    if (argument[0] == '\0')
		do_help(ch, "race help");
	    else
		do_help(ch, argument);

		race_menu( ch );

	    write_to_buffer(d, "\r\n ", 1);
	    write_to_buffer(d, "\n\r", 0);
	    write_to_buffer(d,
			    "What is your race (help for more information)? ",
			    0);
	    break;
	}
	race = gen_race_lookup(argument);

	if ((!race) || !race->pc_race ) {
	    write_to_buffer(d, "That's not a valid race!\n\r", 0);
	    write_to_buffer(d, "The following races are available:\n\r ",
			    0);

		race_menu( ch );

	    write_to_buffer(d,
			    "What is your race? (help for more information) ",
			    0);
	    break;
	}
	ch->race = race;
	/* initialize stats */
	for (i = 0; i < MAX_STATS; i++)
	    ch->perm_stat[i] = race->stats[i];

	ch->affected_by = ch->affected_by | race->aff;
	ch->imm_flags = ch->imm_flags | race->imm;
	ch->res_flags = ch->res_flags | race->res;
	ch->vuln_flags = ch->vuln_flags | race->vuln;
	ch->form = race->form;
	ch->parts = race->parts;

	/* add racial skills */
	for (i = 0; i < 5; i++) {
	    if (race->skills[i] == NULL)
		break;
	    group_add(ch, race->skills[i], FALSE);
	}
	/* add cost */
	ch->pcdata->points = race->points;
	ch->size = race->size;
	write_to_buffer(d, "The Following classes are available:\r\n", 0);
	class_menu( ch );
	write_to_buffer(d, "\r\nSelect a class: ", 0);
	d->connected = CON_GET_NEW_CLASS;
	break;


    case CON_GET_NEW_CLASS:

	if (!strcmp(arg, "help")) {
	    argument = one_argument(argument, arg);
	    if (argument[0] == '\0')
		do_help(ch, "class help");
	    else
		do_help(ch, argument);
	}

	iClass = class_lookup(argument);

	if ((iClass == -1) || !class_table[iClass].valid ) {
	    write_to_buffer(d, "That's not a class!\r\n", 0);
		class_menu( ch );
	    write_to_buffer(d, "\r\nSelect a class: ", 0);
	    return;
	}
	ch->class = iClass;

	sprintf(log_buf, "%s@%s new player.", ch->name, d->host);
	log_string(log_buf);
	wiznet("Newbie alert!  $N sighted.", ch, NULL, WIZ_NEWBIE, 0, 0);
	wiznet(log_buf, NULL, NULL, WIZ_SITES, 0, get_trust(ch));
	write_to_buffer(d, "\n\r", 2);
	write_to_buffer(d, "What is your sex (M/F)? ", 0);
	d->connected = CON_GET_NEW_SEX;
	break;

    case CON_GET_NEW_SEX:
	switch (argument[0]) {
	case 'm':
	case 'M':
	    ch->sex = SEX_MALE;
	    ch->pcdata->true_sex = SEX_MALE;
	    break;
	case 'f':
	case 'F':
	    ch->sex = SEX_FEMALE;
	    ch->pcdata->true_sex = SEX_FEMALE;
	    break;
	default:
	    write_to_buffer(d, "That's not a sex.\n\rWhat IS your sex? ",
			    0);
	    return;
	}
	write_to_buffer(d, "\r\nPress enter to start rolling your stats.",
			0);
	d->connected = CON_GET_STATS;
	break;

    case CON_GET_STATS:
	if (rolled == TRUE && (argument[0] > 4) && (argument[1] < 1))
	    switch (argument[0]) {
	    case '0':
	    case '1':
	    case '2':
	    case '3':
	    case '4':
		ch->perm_stat[0] = stat1[atoi(argument)];
		ch->perm_stat[1] = stat2[atoi(argument)];
		ch->perm_stat[2] = stat3[atoi(argument)];
		ch->perm_stat[3] = stat4[atoi(argument)];
		ch->perm_stat[4] = stat5[atoi(argument)];
		write_to_buffer(d,
				"\r\nYou may be good, neutral, or evil.\n\r",
				0);
		write_to_buffer(d, "Which alignment (G/N/E)? ", 0);
		d->connected = CON_GET_ALIGNMENT;

		break;
	    default:
		write_to_buffer(d, "\n\r", 0);
		write_to_buffer(d,
				"                       0    1    2    3    4\n\r",
				0);
		write_to_buffer(d, "     Strength     :", 0);
		for (x = 0; x < 5; x++) {
		    stat1[x] = number_range(9, 16);
		    sprintf(buf, "   %2d", stat1[x]);
		    write_to_buffer(d, buf, 0);
		}
		write_to_buffer(d, "\n\r     Intelligence :", 0);
		for (x = 0; x < 5; x++) {
		    stat2[x] = number_range(9, 16);
		    sprintf(buf, "   %2d", stat2[x]);
		    write_to_buffer(d, buf, 0);
		}
		write_to_buffer(d, "\n\r     Wisdom       :", 0);
		for (x = 0; x < 5; x++) {
		    stat3[x] = number_range(9, 16);
		    sprintf(buf, "   %2d", stat3[x]);
		    write_to_buffer(d, buf, 0);
		}
		write_to_buffer(d, "\n\r     Dexterity    :", 0);
		for (x = 0; x < 5; x++) {
		    stat4[x] = number_range(9, 16);
		    sprintf(buf, "   %2d", stat4[x]);
		    write_to_buffer(d, buf, 0);
		}
		write_to_buffer(d, "\n\r     Constitution :", 0);
		for (x = 0; x < 5; x++) {
		    stat5[x] = number_range(9, 16);
		    sprintf(buf, "   %2d", stat5[x]);
		    write_to_buffer(d, buf, 0);
		}
		write_to_buffer(d,
				"\n\r\n\r     Press enter to roll again, else enter number of column: ",
				0);
		rolled = TRUE;
		break;
	} else {
	    write_to_buffer(d, "\r\n", 0);
	    write_to_buffer(d,
			    "                       0    1    2    3    4\n\r",
			    0);
	    write_to_buffer(d, "     Strength     :", 0);
	    for (x = 0; x < 5; x++) {
		stat1[x] = number_range(9, 16);
		sprintf(buf, "   %2d", stat1[x]);
		write_to_buffer(d, buf, 0);
	    }
	    write_to_buffer(d, "\n\r     Intelligence :", 0);
	    for (x = 0; x < 5; x++) {
		stat2[x] = number_range(9, 16);
		sprintf(buf, "   %2d", stat2[x]);
		write_to_buffer(d, buf, 0);
	    }
	    write_to_buffer(d, "\n\r     Wisdom       :", 0);
	    for (x = 0; x < 5; x++) {
		stat3[x] = number_range(9, 16);
		sprintf(buf, "   %2d", stat3[x]);
		write_to_buffer(d, buf, 0);
	    }
	    write_to_buffer(d, "\n\r     Dexterity    :", 0);
	    for (x = 0; x < 5; x++) {
		stat4[x] = number_range(9, 16);
		sprintf(buf, "   %2d", stat4[x]);
		write_to_buffer(d, buf, 0);
	    }
	    write_to_buffer(d, "\n\r     Constitution :", 0);
	    for (x = 0; x < 5; x++) {
		stat5[x] = number_range(9, 16);
		sprintf(buf, "   %2d", stat5[x]);
		write_to_buffer(d, buf, 0);
	    }
	    write_to_buffer(d,
			    "\n\r\n\r     Press enter to roll again, else enter number of column: ",
			    0);
	    rolled = TRUE;
	}
	break;

    case CON_GET_ALIGNMENT:
	switch (argument[0]) {
	case 'g':
	case 'G':
	    ch->alignment = 750;
	    break;
	case 'n':
	case 'N':
	    ch->alignment = 0;
	    break;
	case 'e':
	case 'E':
	    ch->alignment = -750;
	    break;
	default:
	    write_to_buffer(d, "That's not a valid alignment.\n\r", 0);
	    write_to_buffer(d, "Which alignment (G/N/E)? ", 0);
	    return;
	}

	write_to_buffer(d, "\n\r", 0);

	group_add(ch, "rom basics", FALSE);
	group_add(ch, class_table[ch->class].base_group, FALSE);
	ch->pcdata->learned[gsn_recall] = 50;
	write_to_buffer(d, "Do you wish to customize this character?\n\r",
			0);
	write_to_buffer(d,
			"Customization takes time, but allows a wider range of skills and abilities.\n\r",
			0);
	write_to_buffer(d, "Customize (Y/N)? ", 0);
	d->connected = CON_DEFAULT_CHOICE;
	break;

    case CON_DEFAULT_CHOICE:
	write_to_buffer(d, "\n\r", 2);
	switch (argument[0]) {
	case 'y':
	case 'Y':
	    ch->gen_data = alloc_perm(sizeof(*ch->gen_data));
	    ch->gen_data->points_chosen = ch->pcdata->points;
	    do_help(ch, "group header");
	    list_group_costs(ch);
	    write_to_buffer(d,
			    "You already have the following skills:\n\r",
			    0);
	    do_skills(ch, "");
	    do_help(ch, "menu choice");
	    d->connected = CON_GEN_GROUPS;
	    break;
	case 'n':
	case 'N':
	    group_add(ch, class_table[ch->class].default_group, TRUE);
	    write_to_buffer(d, "\n\r", 2);
	    write_to_buffer(d,
			    "Please pick a weapon from the following choices:\n\r\r\n",
			    0);
	    buf[0] = '\0';
	    for (i = 0; weapon_table[i].name != NULL; i++)
		if (ch->pcdata->learned[*weapon_table[i].gsn] > 0) {
		    strcat(buf, weapon_table[i].name);
		    strcat(buf, " ");
		}
	    strcat(buf, "\n\r\n\rYour choice? ");
	    write_to_buffer(d, buf, 0);
	    d->connected = CON_PICK_WEAPON;
	    break;
	default:
	    write_to_buffer(d, "Please answer (Y/N)? ", 0);
	    return;
	}
	break;

    case CON_PICK_WEAPON:
	write_to_buffer(d, "\n\r", 2);
	weapon = weapon_lookup(argument);
	if (weapon == -1
	    || ch->pcdata->learned[*weapon_table[weapon].gsn] <= 0) {
	    write_to_buffer(d,
			    "That's not a valid selection. Choices are:\n\r",
			    0);
	    buf[0] = '\0';
	    for (i = 0; weapon_table[i].name != NULL; i++)
		if (ch->pcdata->learned[*weapon_table[i].gsn] > 0) {
		    strcat(buf, weapon_table[i].name);
		    strcat(buf, " ");
		}
	    strcat(buf, "\n\rYour choice? ");
	    write_to_buffer(d, buf, 0);
	    return;
	}
	ch->pcdata->learned[*weapon_table[weapon].gsn] = 40;

	if (IS_IMMORTAL(ch)) {
	    do_help(ch, "imotd");
	    d->connected = CON_READ_IMOTD;
	} else {
	    do_help(ch, "motd");
	    d->connected = CON_READ_MOTD;
	}
	break;

    case CON_GEN_GROUPS:
	send_to_char("\n\r", ch);
	if (!str_cmp(argument, "done")) {
	    sprintf(buf, "Creation points: %d\n\r", ch->pcdata->points);
	    send_to_char(buf, ch);
	    if (ch->pcdata->points < 40)
		ch->train = (40 - ch->pcdata->points + 1) / 2;
	    send_to_char(buf, ch);
	    send_to_char
		("\r\nPlease pick a weapon from the following choices:\n\r",
		 ch);
	    buf[0] = '\0';
	    for (i = 0; weapon_table[i].name != NULL; i++)
		if (ch->pcdata->learned[*weapon_table[i].gsn] > 0) {
		    strcat(buf, weapon_table[i].name);
		    strcat(buf, "\r\n ");
		}
	    strcat(buf, "\n\rYour choice? ");
	    write_to_buffer(d, buf, 0);
	    d->connected = CON_PICK_WEAPON;
	    break;
	}
	if (!parse_gen_groups(ch, argument))
	    send_to_char
		("Choices are: list, learned, premise, add, drop, info, help, and done.\n\r",
		 ch);

	send_to_char( "Choice (add,drop,list,help)? ", ch );
	break;

    case CON_READ_IMOTD:
	write_to_buffer(d, "\n\r", 2);
	do_help(ch, "motd");
	d->connected = CON_READ_MOTD;
	break;

    case CON_READ_MOTD:

	if (ch->pcdata == NULL || ch->pcdata->pwd[0] == '\0') {
	    write_to_buffer(d, "Warning! Null password!\n\r", 0);
	    write_to_buffer(d, "Please report old password with bug.\n\r",
			    0);
	    write_to_buffer(d,
			    "Type 'password null <new password>' to fix.\n\r",
			    0);
	}
	send_to_char
	    ("Welcome to Alanthia! Please do not feed the mobiles.\n\r",
	     ch);

	ch->next = char_list;
	char_list = ch;
	d->connected = CON_PLAYING;
	reset_char(ch);

	if (ch->max_blood < ch->level) {
	    ch->max_blood = ch->level;
	    ch->pcdata->perm_blood = ch->level;
	}
	if (ch->level == 0) {
	    char color_title[] = { 'G', 'M', 'B', 'C', 'Y', 'R' };

	    char color = color_title[number_range(0, 5)];
	    ch->perm_stat[class_table[ch->class].attr_prime] += 1;
	    ch->level = 1;
	    ch->exp = exp_per_level(ch, ch->pcdata->points);
	    ch->hit = ch->max_hit;
	    ch->mana = ch->max_mana;
	    ch->move = ch->max_move;
	    ch->blood = ch->max_blood;
	    ch->train = 3;
	    ch->practice = 5;
	    ch->outfit = FALSE;

	    if (IS_VAMPIRE(ch) || IS_WRAITH(ch)) {
		ch->pcdata->condition[COND_THIRST] = -1;
		ch->pcdata->condition[COND_HUNGER] = -1;
		ch->pcdata->condition[COND_DRUNK] = -1;
	    }
	    SET_BIT(ch->comm, COMM_SHOW_AFFECTS);
	    SET_BIT(ch->comm, COMM_ANNOUNCE);
	    SET_BIT(ch->act, PLR_AUTOEXIT);
	    SET_BIT(ch->act, PLR_AUTODAMAGE);
	    SET_BIT(ch->act, PLR_AUTOLOOT);
	    SET_BIT(ch->act, PLR_AUTOSAC);
	    SET_BIT(ch->act, PLR_AUTOASSIST);
	    SET_BIT(ch->act, PLR_AUTOSPLIT);

	    ch->prompt = str_dup("{c<%hhp %mm %vmv %bbp>{x ");

	    sprintf(buf, "the {%cN{%ceWBiE{x", color, LOWER(color));
	    set_title(ch, buf);

	    write_to_buffer(d, "\n\r", 2);

	    char_to_room(ch, get_room_index(ROOM_VNUM_SCHOOL));

	    do_outfit(ch, "");

	    send_to_char("\n\r", ch);
	    do_help(ch, "NEWBIE INFO");
	    send_to_char("\n\r", ch);
	} else if (ch->in_room != NULL) {
	    char_to_room(ch, ch->in_room);
	} else if (IS_IMMORTAL(ch)) {
	    char_to_room(ch, get_room_index(ROOM_VNUM_CHAT));
	} else {
	    char_to_room(ch, get_room_index(ROOM_VNUM_TEMPLE));
	}


/* 
 *  Sanity checking for bogus levels, bad stats, etc. WDL, 4.10.98
 */

	/* Check for bad stats */

	for (iStat = 0; iStat < 5; iStat++) {
	    if ((ch->perm_stat[iStat] >= 26)
		|| (ch->perm_stat[iStat] <= 0)) {
		ch->perm_stat[iStat] = 12;
		BadStat = TRUE;
	    }
	}

	/* Holler loudly */
	if (BadStat == TRUE) {
	    wiznet("$N had bogus stat, fixing...", ch, NULL,
		   WIZ_SITES, 0, 0);

	    sprintf(log_buf, "**BUG** %s@%s had bogus stats.", ch->name,
		    d->host);
	    log_string(log_buf);
	}
	/* Check for bad levels */
	if ((ch->level < 0) || (ch->level > MAX_LEVEL)) {
	    ch->level = 1;

	    wiznet("$N has bogus level! Possible hack attempt!", ch, NULL,
		   WIZ_SITES, 0, 0);

	    sprintf(log_buf, "**BUG** %s@%s used bogus level! Kill them.",
		    ch->name, d->host);
	    log_string(log_buf);
	}
	act("$n appears in the room.", ch, NULL, NULL, TO_ROOM);

	/* For the Announce Channel */
	announce("{c[{RANNOUNCE{c] $n has stumbled into Alanthia.{x", ch);

	do_look(ch, "auto");
	save_char_obj(ch);
	wiznet("$N has left real life behind.", ch, NULL, WIZ_LOGINS,
	       WIZ_SITES, get_trust(ch));

	if (ch->pet != NULL) {
	    char_to_room(ch->pet, ch->in_room);
	    act("$n appears in the room.", ch->pet, NULL, NULL, TO_ROOM);
	}
	do_unread(ch, "");
	break;
    }

    return;
}