mud++0.35/etc/
mud++0.35/etc/guilds/
mud++0.35/help/propert/
mud++0.35/mudC/
mud++0.35/player/
mud++0.35/src/interface/
mud++0.35/src/os/cygwin32/
mud++0.35/src/os/win32/
mud++0.35/src/os/win32/bcppbuilder/
mud++0.35/src/osaddon/
mud++0.35/src/util/
/*
....[@@@..[@@@..............[@.................. MUD++ is a written from
....[@..[@..[@..[@..[@..[@@@@@....[@......[@.... scratch multi-user swords and
....[@..[@..[@..[@..[@..[@..[@..[@@@@@..[@@@@@.. sorcery game written in C++.
....[@......[@..[@..[@..[@..[@....[@......[@.... This server is an ongoing
....[@......[@..[@@@@@..[@@@@@.................. development project.  All 
................................................ contributions are welcome. 
....Copyright(C).1995.Melvin.Smith.............. Enjoy. 
------------------------------------------------------------------------------
Melvin Smith (aka Fusion)         msmith@hom.net 
MUD++ development mailing list    mudpp@van.ml.org
------------------------------------------------------------------------------
nanny.cc
*/

#include "config.h"
#include "string.h"
#include "llist.h"
#include "server.h"
#include "screen.h"
#include "room.h"
#include "pc.h"
#include "area.h"
#include "hash.h"

#include "global.h"

extern long cur_login;
extern long max_login;
extern long tot_login;

// These are at the bottom of file
extern const char * MAIN_MENU;
extern const char * RACE_MENU;
extern const char * CLASS_MENU;
const char * newPrompt = \
"Welcome to MUD++ Cyberspace. Type 'help prompt' >";

// Handle newly arrived players
// I used the name 'Nanny' just for old time's sake.


bool checkName( const String & str )
{
	if( !(bool)str )
		return false;

	int i= 0;
	while( str[i] )
	{
		if( !isalpha( str[i] ) )
			return false;
		i++;
	}
	return true;
}


Room * getFirstRoom()
{
	Area * area;
	Room * room;

	areas.reset();
	while( ( area = areas.peek() ) )
	{
		area->roomIndex.reset();
		room = area->roomIndex.peek();
		if( room )
			return room;
		areas.next();
	}
	return 0;
}


bool Nanny( PC *ch, const String & arg )
{
	String arg1 = arg;
	String str;
	String filename;
	Room * room;
	PC * pcOld;

	switch( ch->getState() )
	{
		default:
			return true;

		case STATE_INIT:
			ch->out("What shall we call you ? ");
			ch->setState( STATE_GET_NAME );
			return true;

		case STATE_GET_NAME:
			arg1.toProper();
			pcOld = getPCOther( arg1, ch );
			if( pcOld )
			{
				if( pcOld->getSocket() )
				{
					ch->out( "Already connected.\n\r" );
					ch->setState( STATE_BOOT );
					return true;
				}
				else // Reconnect attempt, don't read player file
				{
					ch->setName( arg1 );
					ch->setState( STATE_GET_OLD_PASSWORD );
					ch->out(INVERSE);
					ch->out("Password:");
					ch->out(NTEXT);
					ch->flush();
					ch->getSocket()->willEcho();
					return true;
				}
			}
				
			if( !checkName( arg1 ) )
			{
				ch->out( "Illegal name.\n\r" );
				ch->setState( STATE_BOOT );
				return true;
			}

			ch->setName( arg1 );
			filename << PLAYER_DIR << '/' << arg1[0] << '/'
				<< arg1;

			if ( 1 )
			{
				InputFile inf( filename );
				int error = -1;

				if( inf ) 
				{
					error = ch->readFrom( inf );
				}
				else
				{
					struct stat st;
					String filegz;
					filegz << filename.chars() << ".gz";
					if ( stat( filegz.chars(), &st ) == 0 )
					{
						str.clr();
						str << "gzip -d " << filegz;
						system( str.chars() );
						InputFile inf1( filename );
						error = ch->readFrom( inf1 );
					}
				}

				if ( error >= 0 )
				{
					ch->setState( STATE_GET_OLD_PASSWORD );
					ch->out(INVERSE);
					ch->out("Password:");
					ch->out(NTEXT);
					ch->flush();
					ch->getSocket()->willEcho();
					return true; 
				}
			}

		/*
			if ( error > 0 )
			{
				Cout << "Error loading pfile" << endl;
				// Do not allow new player ?
			}
		*/
			ch->setState( STATE_CONFIRM_NAME );
			ch->out("Are you sure (y/n)?");
			return true;

		case STATE_CONFIRM_NAME:
			arg1[0] = toupper( arg1[0] );
			switch( arg1[0] )
			{
				default:
					ch->setState( STATE_BOOT );
					return true;
				case 'Y' :
					ch->setShort( ch->getName() );
					str << ch->getName() << " is here.";
					ch->setLong( str );
					ch->setPrompt( newPrompt );
					//ch->setTextEditor( SECURE_EDITOR );
					ch->setState( STATE_GET_NEW_PASSWORD );
					ch->out("Ok.\n\rGive me a password:");
					ch->getSocket()->willEcho();
					return true;
				case 'N' :
					ch->out("What is it then?");
					ch->setState( STATE_GET_NAME );
					return true;
			} 
			return true;

		case STATE_GET_NEW_PASSWORD:
			switch( arg1[0] )
			{
				default:
					ch->setPasswd( arg1.crypt( ch->getName() ) );
					ch->setState( STATE_CONFIRM_PASSWORD );
					ch->out("\n\rAgain:");
					return true;
				case '\n':
					ch->out( "Empty password not allowed.\n\rPassword:" );
					return true;
				case '\0': 
					ch->setState( STATE_BOOT );
					return true;
			}
			return true;

		case STATE_CONFIRM_PASSWORD:
			switch( arg1[0] )
			{
				default:
					if( arg1.crypt( ch->getName() ) != ch->getPasswd() )
					{
						ch->setState( STATE_GET_NEW_PASSWORD );
						ch->out("\n\rPasswords dont match, try again.\n\rPassword:");
						return true;
					}
					ch->getSocket()->wontEcho();
					ch->setState( STATE_GET_SEX );
					ch->out( "\n\rSex (M/F)?" );
					return true;
				case '\0':
					ch->setState( STATE_BOOT );
					return true;
			}
			return true;

		case STATE_GET_OLD_PASSWORD:
			pcOld = getPCOther( ch->getName(), ch );
			// Reconnecting
			if( pcOld )
				ch->setPasswd( pcOld->getPasswd() );	

			switch( arg1[0] )
			{
				default:
					if( (bool) ch->getPasswd()
						&&
						arg1.crypt( ch->getName() ) != ch->getPasswd() )
					{
						ch->out("\n\rImposter!\n\r");
						ch->setState( STATE_BOOT );
						return true;
					}

					if( pcOld )
					{
						// Reconnect
						str << pcOld->getShort() << " has reconnected.";
						wizLog( str, pcOld );
						pcOld->setSocket( ch->getSocket() );
						pcOld->getSocket()->wontEcho();
						pcOld->out( "\n\n\rReconnecting.\n\r" );
						pcs.remove( ch );
						ch->setSocket( 0 ); // Null it before destructor
						ch->fordelete();
						return false;
					}

					ch->getSocket()->wontEcho();
					ch->setState( STATE_MAIN_MENU );
					ch->out( MAIN_MENU );
					return true;
				// This is for empty passwd to allow access
				case '\0':
				case '\n':
					if( !pcOld && !(bool)ch->getPasswd() )
					{
						ch->getSocket()->wontEcho();
						ch->setState( STATE_MAIN_MENU );
						ch->out( MAIN_MENU );
						return true;
					}

					if( pcOld
						&& 
						arg1.crypt( ch->getName() ) == ch->getPasswd() )
					{
						// Reconnect
						pcOld->setSocket( ch->getSocket() );
						pcOld->getSocket()->wontEcho();
						pcOld->out( "Reconnecting.\n\r" );
						pcs.remove( ch );
						ch->setSocket( 0 ); // Null it before destructor
						ch->fordelete();
						return false;
					}

					ch->setState( STATE_BOOT );
					return true;
			}

		case STATE_MAIN_MENU:
			switch( arg1[0] )
			{
				default:
					ch->out( MAIN_MENU );
					return true;
				case '0': 
					ch->setState( STATE_BOOT );
					return true;
				case '1':
					ch->view( "../etc/motd" );
					if ( ch->wantHints() )
						ch->do_hint("");
					ch->out(endl);
					ch->setState( STATE_PLAYING );
					room = lookupRoom( ch->getWasInRoom() );
					if( ! room )
					{
						if( !( room = getFirstRoom() ) )
						{
							ch->out( "MUD++ server panic. Can't lookup room." );
							Cout << "MUD++ server panic. Can't get a room.\n";
							mudpp_exit(0);
						}
					}

					room->addCharInv( ch );
					ch->checkMail();
					str << ch->getName() << " has entered the game.\n\r";
					// add "can-see check here
					room->outAllCharExcept( str, ch, 0 );
					wizLog( str, ch );
					cur_login++;
					tot_login++;	
					if( max_login < cur_login )
						max_login++;
					return true;

				case '5':
					ch->view( "../help/wizlist" );
					ch->page( "" );
					return true;
			}

		case STATE_GET_SEX:
			arg1[0] = toupper( arg1[0] );
			switch( arg1[0] )
			{
				case 'M':	ch->setSex( MALE );
							ch->setState( STATE_GET_RACE );
							ch->out( RACE_MENU );
							return true;
				case 'F':	ch->setSex( FEMALE );
							ch->setState( STATE_GET_RACE );
							ch->out( RACE_MENU );
							return true;
				default:	ch->out( "\n\rType M for male or F for female:" );
							return true;
			}

		case STATE_GET_RACE:
			arg1[0] = toupper( arg1[0] );
			switch( arg1[0] )
			{
				default:
					if( isdigit( arg1[0] ) )
					{
						if( arg1.asInt() <= 0 || arg1.asInt() > MAX_RACE )
						{
							ch->out("Invalid race.\n\r[HIT RETURN]\n\r" );
						}
						else
						{
							ch->setRace( arg.asInt() );
							ch->setState( STATE_GET_CLASS );
							ch->out( CLASS_MENU );
						}
						return true;
					}
					ch->out( RACE_MENU );
					return true;
				case 'H':
					ch->view( "../help/race-general.hlp" );
					return true;
			}

		case STATE_GET_CLASS:
			arg1[0] = toupper( arg1[0] );
			switch( arg1[0] )
			{
				default:
					if( isdigit( arg1[0] ) )
					{
						if( arg1.asInt() <= 0 || arg1.asInt() >= MAX_CLASS )
						{
							ch->out("Invalid class number.\n\r[HIT RETURN]\n\r" );
						}
						else
						{
							ch->setClass( arg.asInt() );
							ch->setState( STATE_MAIN_MENU );
							ch->out( MAIN_MENU );
						}
						return true;
					}
					ch->out( CLASS_MENU );
					return true;
				case 'H':
					ch->out( "Sorry, no helps yet.\n\r[HIT RETURN]\n\r" );
					return true;
			}
	}

	return true;
}

const char *MAIN_MENU  =  "\x1B[2J"
"\n\r"
"                            |\\|\\  | | |\\   _|_ _|_\n\r"
"                            | | | |_| |_|   |   |  \n\r"
"\n\r"
"                                 (M)ulti\n\r"
"                                 (U)ser\n\r"
"                          /\\     (D)imension\n\r"
"                     /\\ /    \\    +                      /\\     \n\r"
"                   /    \\      \\  +                 /\\ /    \\   \n\r" 
"                 //       \\      \\                //   \\      \\ \n\r"
"               -==============================================-\n\r"
"              |        Welcome to MUD++...                     |\n\r"
"              |-==============================================-|\n\r"
"              |        [1] Enter the game.                     |\n\r"
"              |        [2] Change password.                    |\n\r"
"              |        [3] Delete this character.              |\n\r"
"              |        [4] A little help to get started.       |\n\r"
"              |        [5] List the resident Wizards.          |\n\r"
"              |        [0] Quit.                               |\n\r"
"              |________________________________________________|\n\r"
"                       Select -> ";

const char *CLASS_MENU =  "\x1B[2J"
"-=====================================-\n\r"
" Type help <num> to see stats for class\n\r"
"-=====================================-\n\r"
" [1 ] Magic User\n\r"
" [2 ] Cleric\n\r"
" [3 ] Thief\n\r"
" [4 ] Warrior\n\r"
" [5 ] Monk\n\r"
" [6 ] Pscionicist\n\r"
"-=====================================-\n\r"
" Select a class -> ";

const char *RACE_MENU =   "\x1B[2J"
"This may be your most important choice in the creation of\n\r"
"your character. The game is based around natural abilities,\n\r"
"tendencies, navigation, size, alignment, magic resistance,\n\r"
"fire resistance and many stats are bound by the race you choose.\n\r"
"-========================================-\n\r"
" Type help <number> to describe that race\n\r"
" or help 'races' for a general description\n\r"
"-========================================-\n\r"
" [1 ] Human\n\r"
" [2 ] Elf\n\r"
" [3 ] Troll\n\r"
" [4 ] Faerie       (winged)\n\r"
" [5 ] Dwarf\n\r"
" [6 ] Gnome\n\r"
" [7 ] Halfling\n\r"
" [8 ] Minotaur\n\r"
" [9 ] Hobgoblin\n\r"
" [10] Ogre\n\r"
" [11] Gyosha       (winged)\n\r"
" [12] Triton       (aquatic)\n\r"
"-========================================-\n\r"
" Select a race -> ";