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
------------------------------------------------------------------------------
pc_info.cc
*/

#include "config.h"
#include "string.h"
#include "llist.h"
#include "hash.h"
#include "room.h"
#include "server.h"
#include "area.h"
#include "help.h"
#include "screen.h"
#include "env.h"
#include "spell.h"
#include "pc.h"
#include "social.h"
#include "shop.h"
#include "edit.h"

#include "global.h"


// Need to add lookupPCBit() to make toggle more extensive
// This is just temporary.
void PC::do_toggle( const String & arg )
{
	if( arg == "wiznet" )
		TOGGLE_BIT( pc_bits, PC_WIZNET );
	else return;

	out( "Ok.\n\r" );
}


void PC::do_autoeat( const String & )
{
	if( IS_SET( pc_bits, PC_AUTOEAT ) )
		out( "Autoeat OFF.\n\r" );
	else
		out( "Autoeat ON.\n\r" );
	TOGGLE_BIT( pc_bits, PC_AUTOEAT );
}


void PC::do_autodrink( const String & )
{
	if( IS_SET( pc_bits, PC_AUTODRINK ) )
		out( "Autodrink OFF.\n\r" );
	else
		out( "Autodrink ON.\n\r" );
	TOGGLE_BIT( pc_bits, PC_AUTODRINK );
}


void PC::do_autogold( const String & )
{
	if( IS_SET( pc_bits, PC_AUTOGOLD ) )
		out( "Autogold OFF.\n\r" );
	else
		out( "Autogold ON.\n\r" );
	TOGGLE_BIT( pc_bits, PC_AUTOGOLD );
}


void PC::do_autoloot( const String & )
{
	if( IS_SET( pc_bits, PC_AUTOLOOT ) )
		out( "Autoloot OFF.\n\r" );
	else
		out( "Autoloot ON.\n\r" );
	TOGGLE_BIT( pc_bits, PC_AUTOLOOT );
}


void PC::do_autodir( const String & )
{
	if( IS_SET( pc_bits, PC_AUTODIR ) )
		out( "Autodir OFF.\n\r" );
	else
		out( "Autodir ON.\n\r" );
	TOGGLE_BIT( pc_bits, PC_AUTODIR );
}


void PC::do_autoexits( const String & )
{
	if( IS_SET( pc_bits, PC_AUTOEXITS ) )
		out( "Autoexits OFF.\n\r" );
	else
		out( "Autoexits ON.\n\r" );
	TOGGLE_BIT( pc_bits, PC_AUTOEXITS );
}


void PC::do_uptime( const String & )
{
	extern long max_login;
	extern long cur_login;
	String str;
	time_t now = time(0);
	str	<< ctime( & now );
	str	<< "\r" << server->getUpTime() << ", "
		<< cur_login
		<< (cur_login != 1 ? " users (max was " : " user (max was " )
		<< max_login << ")\n\n\r";
	out( str );
}


void PC::do_afk( const String & arg )
{
	String str;
	if( isAFK() )
	{
		CLR_BIT( pc_bits, PC_AFK );
		afk_messg = "";
		out( "Back in action.\n\r" );
		str << getShort() << " is back at the keyboard.\n\r";
		inRoom()->outAllCharExcept( str, this, 0 );
		return;
	}

	SET_BIT( pc_bits, PC_AFK);
	str << getShort() << " just went into afk mode.\n\r";
	if( (bool)arg )
	{
		afk_messg = arg;
		str << "Message: " <<  afk_messg << "\n\r";
	}

	inRoom()->outAllCharExcept( str, this, 0 );
	out( "AFK mode on.\n\r" ); 
}


void PC::do_title( const String & arg )
{
	if( !(bool)arg )
	{
		out( "Your title is: " );
		out( title );
		out( "\n\rType 'title clear' to clear your title.\n\r" );
		return;	
	}

	if( arg == "clear" )
	{
		title = "";
		out( "Title cleared.\n\r" );
		return;
	}

	if( !arg.hasChar( '~' ) )
	{
		title = arg;
		if( title.len() > 45 )
		{
			title[45] = '\0';
			out( "Title truncated to 45 characters.\n\r" );
		}
		out( "Title set to: " );
		out( title );
		out( "\n\r" );
		return;
	}

	out( "Illegal character '~'\n\r" );
}


void PC::do_list( const String & arg )
{
	Char * ch;
	Object * obj;
	String str;
	int cost;
	ShopKeeper * sk;
	String arg1;
	String arg2;
	int type = 0;
	bool nameFilter = false;

	arg.startArgs();
	arg1 = arg.getArg();

	if ( !arg1 )
	{
	}
	else if ( arg1 == "type" )
	{
		arg1 = arg.getArg();
		type = lookupObjType(arg1);
		if ( !type )
		{
			out("No such object type.\n\r");
			return;
		}
	}
	else if ( arg1 == "all" )
	{
	}
	else
	{
		nameFilter = true;
	}

	arg2 = arg.getArg();


	for_each( in_room->chars, ch )
	{
		if( ch == this )
			continue;

		if( ch->isShopKeeper() )
		{
			if ( !arg2 || ch->isName(arg2) )
				break;
		}
	}

	if( ! ch )
	{
		if ( !arg2 )
			out( "No merchant is here.\n\r" );
		else
			out( "No merchant with that name here" );
		return;
	}

	sk = (ShopKeeper *)ch;

	str << "Goods for sale:\n";

	for_each( sk->inv, obj )
	{
		if( obj->worn() )
			continue;
		if ( type && !obj->isType(type) )
			continue;
		if ( nameFilter && ( !obj->isName(arg1) ) )
			continue;
		cost = sk->sellValue(obj);
		if ( cost != SHOPKEEPER_NOT_TRADING )
			str	<< "[ " << cost << " ] " << obj->getShort() << endl;
	}

	out( str );
}


void PC::do_commands( const String & )
{
	String str( BUF * 2 );
	int count=1;
	int i;

	str << "MUD++ player commands.\n\n\r";
	for( int ihash = 0; ihash < 27; ihash++ )
	{
		i = 0;
		while( cmdlist[ihash][i].commd )
		{
			str.sprintfAdd( "%11s", cmdlist[ihash][i].commd );

			if( !( count++ % 5 ) )
				str += "\n\r";

			i++;
		}
	}

	out( str );  
}

void PC::do_skills( const String & )
{
}

void PC::do_where( const String & )
{
	String str( "Players in this part of the country:\n\r" );
	Area * area = inRoom()->getArea();
	PC *pc;

	LList<PC> tlist = pcs;
	pcs.reset();
	while( ( pc = tlist.peek() ) )
	{
		if( pc->inRoom() && pc->inRoom()->getArea() == area )
			str.sprintfAdd( "%20s - %s\n\r",
							(const char*)pc->getShort(),
							(const char*)pc->inRoom()->getName() );
		tlist.next();
	}

	out( str );
}

void PC::do_socials( const String & )
{
	String str( BUF * 2 );
	Social * social;
	int count=1;

	str << "MUD++ socials.\n\n\r";
	for( int ihash = 0; ihash < 26; ihash++ )
	{
		social_table[ihash].reset();

		while( ( social = social_table[ihash].peek() ) )
		{
			social_table[ihash].next();
			str.sprintfAdd( "%11s", social->getName().chars() );

			if( !( count++ % 5 ) )
				str += "\n\r";
		}
	}

	out( str );  
	out( "\n\r" );
}


void PC::do_wizhelp( const String & )
{
	String str( BUF * 2 );
	int count=1;
	int i;

	str << "MUD++ Wizard Commands.\n\n\r";
	for( int ihash = 0; ihash < 27; ihash++ )
	{
		i = 0;
		while( immcmdlist[ihash][i].commd )
		{
			if( authorized( immcmdlist[ihash][i].bit ) )
			{
				str.sprintfAdd( "%11s", immcmdlist[ihash][i].commd );
				if( !( count++ % 5 ) )
					str += "\n\r";
			}

			i++;
		}
	}

	out( str );  
}



void PC::do_help( const String & arg )
{
	if( ! (bool) arg )
	{
		// Simple help
		view( STANDARD_HELP );   
	}
	else
	{
		// Lookup help in helps hashtable
		// View( 'HELP_DIR+filename' )
		// View is very efficient at reading in files and helps can be
		// modified while the game is running.
		// Memory usage is also decreased.

		String fname;
		Help * help;

		help = helps_ht.lookup(arg);

		if ( !help )
		{
			out("\n\rNo help on that.\n\r"); 
			return;
		}

			if ( !help->isAllowed(this) )
		{
			out("\n\rYou are not authorized to read that.\n\r");
			return;
		}

		if ( help->haveTitle() )
			out ( help->getName() );

			fname << HELP_DIR << '/' << help->getFileName();      
			view( fname.chars() );
			return;
	}
}


void PC::do_levels( const String & )
{
	String str( BUF * 2 );
	str << "Exp is per level, not cumalative.\n\r-===================-\n\r"; 
	for( int i = 1; i<=MAX_PC_LEVEL; i++ )
	{
		str.sprintf( " %2d %11d\n\r", i, exp_table[ i ] );
	}

	str += "\n\r";
	out( str );
}


void PC::do_prompt( const String & arg )
{
	String str;

	if( (bool)arg )
	{
		if( arg.len() > 80 )
		{
			out( "Prompt string too long. Must be < 80 characters.\n\r" );
			return;
		}
		else if( arg.hasChar( '~' ) )
		{
			out( "Illegal character '~' in prompt string.\n\r" );
			return;
		}
		else if ( arg == "default" )
		{
			prompt = "[ %h/%Hh %m/%Mm ] ";
			out( "Ok, prompt set to default.\n\r" );
			putPrompt();
			return;
		}

		prompt = arg;
	}
	else
	{
		str << "Your prompt string is currently: \'" <<
			prompt << "\'\n\r";
		out( str );
		return;
	}

	out( "Ok, new prompt has been set.\n\r" );
	putPrompt();
}


void PC::do_clear( const String & )
{
	out( "\x1B[2J" );
}


void PC::do_areas( const String & )
{
	String str( BUF * 2 );
	Area *pArea;
	areas.reset( );
	str.sprintf( "%-13s   %-15s   %-35s\n\r", "Scope", "Builder(s)", "Title" );
	while( ( pArea = areas.peek( ) ) )
	{
		areas.next();
		str.sprintfAdd( "%-13s   %-15s   %-35s\n\r", pArea->getKey().chars(),
				pArea->getBuilder().chars(), pArea->getTitle().chars() );
	}
	out( str );
}


void PC::do_equipment( const String & )
{
	String str( BUF );
	bool anything = false;
	Object *obj;

	str << "\n\rYou are wearing:\n\r";
	for( int wear_pos = EQ_MIN; wear_pos <= EQ_MAX; wear_pos++ )
	{
		if( IS_SET( eq_bits, wear_pos ) )
		{
			obj = getObjWear( wear_pos );
			if( obj )
			{
				str.sprintfAdd( "%25s - %s\n\r",
					getWearPosName( wear_pos ),
					obj->getShort().chars() );
				anything = true;
			}
			else
				str.sprintfAdd( "%25s - %s\n\r",
					getWearPosName( wear_pos ),
					"BUG!! NULL OBJECT EQ POSITION!!" );
				
		}
	}

	if ( ! anything )
		str << "Nothing!  " << BWHITE << "GACK!!!" << NORMAL << "\n\r";

	out( str );
}


void PC::do_inventory( const String & )
{
	String str ( BUF * 2 );
	Object *obj;
	inv.reset();

	str += "\n\rYou are carrying:\n\r";

	while( ( obj = inv.peek() ) )
	{
		inv.next();
		if( obj->wearPos() )
			continue;

		str << obj->getShort() << "\n\r";
	}

	str += "\n\r";
	out( str );
}


// Little ugly code here, have to make sure if the player is
// disoriented, we still print the exits in order of direction
// that he expects, else it would be possible to beat the system.
void PC::do_exits( const String & )
{
	String str = "Obvious Exits:";
  
	for( int door=orientation; door < DIR_UP + orientation; door++ )
	{
		if( !inRoom()->getExit( door % DIR_UP ) )
			continue;
		str.sprintfAdd( "\n\r%-5s - %s%s%s",
				lookupDirName( door - orientation ),
				WHITE, in_room->toRoom( door % DIR_UP )->getName().chars(),
				NORMAL );
	}

	// Always do up and down last, this is seperate so dis-orientation
	// still lists exits as player expects
	if( in_room->getExit( DIR_UP ) )
		str.sprintfAdd( "\n\r%-5s - %s%s%s",
				lookupDirName( DIR_UP ),
				WHITE, in_room->toRoom( DIR_UP )->getName().chars(), NORMAL );

	if( in_room->getExit( DIR_DOWN ) )
		str.sprintfAdd( "\n\r%-5s - %s%s%s",
				lookupDirName( DIR_DOWN ),
				WHITE, in_room->toRoom( DIR_DOWN )->getName().chars(), NORMAL );
 
	str += "\n\r";
	out( str );
}


void PC::do_look( const String & arg )
{
	Object *obj;
	Object *obj2;
	Char   *ch;
	int dir;
	String str( 4096 );
	String strDirs;
	String arg1;
	String arg2;

	arg.startArgs();
	arg1 = arg.getArg();
	arg2 = arg.getArg();

	if( !(bool)arg1 )
	{
		str << "\n\r" << BCYAN << in_room->getName() << NORMAL << "\n\r";
		// No arg so do standard look
		if( IS_SET( pc_bits, PC_AUTODIR ) )
		{
			str << BWHITE << "[Exits:";
			for( dir=orientation; dir < DIR_UP + orientation; dir++ )
			{
				if( !inRoom()->getExit( dir % DIR_UP ) )
					continue;
				str << ' ' << lookupDirName( dir - orientation );
			}

			if( in_room->getExit( DIR_UP ) )
				str << ' ' << lookupDirName( DIR_UP );

			if( in_room->getExit( DIR_DOWN ) )
				str << ' ' << lookupDirName( DIR_DOWN );
 
			str << ']' << NORMAL << "\n\r";
 		}

		// Room::desc has it own \n\r so don't add
		str << CYAN << in_room->getDesc() << NORMAL;

		// show the items
		in_room->inv.reset();
		while( ( obj = in_room->inv.peek() ) )
		{
			in_room->inv.next();
			if( obj->isInvis() )
			{
				if( affectedBy( AFF_DET_INVIS ) )
					str << "   (Invis) " << obj->getLong() << "\n\r";
			}
			else str << "   " << obj->getLong() << "\n\r";
		}

		in_room->chars.reset();
		while( ( ch = in_room->chars.peek() ) )
		{
			in_room->chars.next();
			if( ch == this )
				continue;

			str << ch->getLong() << "\n\r";
		}

		out( str );
		if( IS_SET( pc_bits, PC_AUTOEXITS ) )
			do_exits( "" );
		return;
	}
	else if( arg1 == "in" || arg1 == "inside" )
	{
		// look at contents of a container (arg2)
		if( !(bool)arg2 )
		{
			out( "Look inside what?\n\r" );
			return;
		}

		countedThing cI(arg2);
		obj = getObjInv( cI );
		if ( obj == NULL )
		{
			obj = in_room->getObj( cI );
			if( !obj )
			{
				out( "Nothing by that name here.\n\r" );
				return;
			}	
		}

		if( !obj->isContainer() && !obj->isCorpse() )
		{
			out( "Not much to see.\n\r" );
			return;
		}

		// replace resetInv/getNexObjInv with a const LList &
		// access member function and assign to a temp readonly list
		// for iterating.

		// Reset object inventory list pointer
		obj->inv.reset();
	
		out( obj->getShort() );
		out( " contains:\n\r-------------\n\r" );

		if( !( obj2 = obj->inv.peek() ) )
		{
			out( "Nothing.\n\r" );
			return;
		}

		// Iterate
		while( ( obj2 = obj->inv.peek() ) )
		{
			out( obj2->getShort() );
			out( "\n\r" );
			obj->inv.next();
		}

		return;
	}

	countedThing cI(arg1);
	
	// Find object/mob/player to look at
	ch = in_room->getChar( cI );
	if( ch )
	{
		look( ch );
		return;
	}
	
	// Look at object in inventory and equipment
	obj = getObjInvWear( cI );
	if( obj )
	{
		look( obj );
		return;
	} 

	// Look at an object in room
	obj = in_room->getObj( cI );
	if( obj )
	{
		look( obj );
		return;
	} 

	// Extra description keywords
	String ed = in_room->getExtraDesc( arg1 );	
	if( ed )
	{
		out( ed );
		out( "\n\r" );
		return;
	}

	// Look in a direction
	if(	( dir = getDir( arg1[0] ) ) != DIR_UNDEFINED )
	{
		const Exit *ex = in_room->getExit( dir );
		if( !ex )
		{
			out( "Nothing to see in that direction.\n\r" );
			return;
		}

		if( (bool) ex->getDesc() )
			out( ex->getDesc() );
		else
			out( "You see nothing special in that direction." );
		out( "\n\r" );
		return;
	}

	out( "I see no " );
	out( arg1 );
	out( " here.\n\r" );
}


void PC::do_save( const String & )
{
	if( !save() )
		out( "There was an error saving your character. Report.\n\r" );
	else
		out( "\n\rOk. Saved.\n\r" );
}


void PC::do_password( const String & arg )
{
	String orig;
	String newp;

	arg.startArgs();

	orig	= arg.getArg();
	newp	= arg.getArg();

	if( ! (bool) orig || ! (bool) newp )	
	{
		out( "Usage:   password <old> <new>\n\r" );
		return;
	}

	orig.crypt( getName() );
	
	if( (bool) getPasswd()
		&&
		orig != getPasswd() )
	{
		out( "Old password doesn't match. Password not changed.\n\r" );
		return;
	}

	if( newp.hasChar( '~' ) )
	{
		out( "Illegal character '~'. Password not changed.\n\r" );
		return;
	}

	newp.crypt( getName() );
	password = newp;
	out( "Ok. Password changed.\n\r" );
}


void PC::do_score( const String & )
{
	String str( 8192 );

	str.sprintf( "You are %s.\n\rLevel: %d\n\r", name.chars(), levels[classnow]);
	str.sprintfAdd( "Security: %d\n\r", getSecurity() ); 
  
	if( classnow != CLASS_WIZARD )
	{
		str.sprintfAdd( "You have %ld experience points.\n\r", exp );
		str.sprintfAdd( "You need %ld exp to level %d.\n\r",
					exp_table[levels[classnow]]-exp,
					levels[classnow]+1 );
	}

	str.sprintfAdd( "Str: %d  Int: %d  Wis: %d  Con: %d  Spd: %d\n\r",
							getStr(), getInt(), getWis(), getCon(), getSpd() );
	str.sprintfAdd( "%d(%d) hp   %d(%d) mana\n\r",
							getHP(), getMaxHP(), getMana(), getMaxMana() );
	str.sprintfAdd( "You are carrying %d items weighing %d stones.\n\r",
							getCarriedCount(), getCarriedWeight() );

	str.sprintfAdd( "You have %ld gold coins.\n\r", getGold() );
	str.sprintfAdd( "Damroll: %d  Hitroll: %d  Armor: %d\n\r",
							getDamRoll(), getHitRoll(), getArmor() ); 

	str.sprintfAdd( "Flags: " );
	str += "\n\r";

	if( fighting )
	{
		str << "You are fighting " << fighting->getShort() << ".\n\r";
	}


	if( getHunger() < -10 )
		str << "You are starving.\n\r";
	else if( getHunger() < 0 )
		str << "You could use something to eat.\n\r";

	if( getThirst() < -10 )
		str << "You are dying of thirst.\n\r";
	else if( getThirst() < 0 )
		str << "You throat is slightly parched.\n\r";
 
	str << "Affected by:\n\r";

	Affect *paf;

	//aff_list.reset();
	for_each( aff_list, paf )
	{
		if( paf->getSpellType() )
			str.sprintfAdd( "Spell '%s' for %d hours.\n\r",
				paf->getSpellType()->getName(), paf->getTimer() );
	}

	out( str );
}


void PC::do_time( const String & )
{
	out( envir.getTime() );
}

void PC::do_weather( const String & )
{
	if( in_room->isIndoors() )
	{
		out( "You can't see the weather indoors.\n\r" );
		return;
	}

	out( envir.getWeather() );
}


// Who improvements by Klepto
void PC::do_who( const String & )
{
	extern long max_login;
	PC *ch;
	int count = 0;
	String str( BUF * 4 ); 
	LList<PC> tlist;

	str += "                               Lost Realms II\n\r";
	str += "[]=============*=========*===================*==========*====================[]\n\r";
	str += " |    Name     |  Level  | Race/ Class/ Sex  |  Kills   |  Flags             |\n\r";
	str += "[]=============*=========*===================*==========*====================[]\n\r";
	if( authorized( WIZARD ) )
	{
		tlist = shellpcs;
		tlist.reset();      // reset the list
		while( ( ch = tlist.peek() ) )
		{
			str.sprintfAdd( " | %-11s | %2d(%3d) | %-12s%3s-%c | 0000/000 | %s%s\n\r",
					ch->getName().chars(),
					ch->getLevel(),
					ch->getLevel(),
					lookupRaceName( ch->getRace() ),
					ch->className(),
					ch->getSex() == 1 ? 'M' : 'F',
					"[*OS SHELL*]",
					ch->getEditor() ? "[*EDITOR*]" : "" );
			tlist.next(); 
		}
	}

	tlist = pcs;
	for( int i = MAX_PC_LEVEL; i >= 0; i-- )
	{
		tlist.reset();      // reset the list
		while( ( ch = tlist.peek() ) )
		{
			if( ch->getLevel() != i || !ch->inRoom() )
			{
				tlist.next();
				continue;
			}
			count++;
			str.sprintfAdd( " | %-11s | %2d(%3d) | %-12s%3s-%c | 0000/000 | %s%s%s\n\r",
					ch->getName().chars(),
					ch->getLevel(),
					ch->getLevel(),
					lookupRaceName( ch->getRace() ),
					ch->className(),
					ch->getSex() == 1 ? 'M' : 'F',
					ch->getSocket() ? "" : "[*LOST LINK*]",
					ch->isAFK() ? "[*AFK*]" : "",
					ch->getEditor() ? "[*EDITOR*]" : "" );
			tlist.next(); 
		}
	}
	str += "[]-------------*---------*-------------------*----------*--------------------[]\n\r";
	str.sprintfAdd( "Total - %d players (max was %d)\n\r", count, max_login );
	out( str );
}


void PC::pc_report( const char * fname, const String & comment )
{
	OutputFile outf(fname, Append);
	String frm;
	if ( !outf )
	{
		Cout << "Problem with opening " << fname <<endl;
		return;
	}
/*
	frm.sprintf("[%10.10s@%-15.15s] %s\n\r", getName().chars(), 
		(inRoom()->getScope() + ':' + inRoom()->getKey()).chars() 
			, comment.chars());
	outf << frm;
*/

	outf << '[' << getName() << '@' << inRoom()->getScope() << ':' <<
		inRoom()->getKey() << "] " << comment << "\n";

	outf.close();

}

#define BUG_FILE "../etc/bugs.txt"
#define IDEA_FILE "../etc/ideas.txt"
#define TYPO_FILE "../etc/typos.txt"

void PC::do_bug( const String & arg )
{
	pc_report(BUG_FILE, arg);
	out("Your bug report has been noted.\n\r");
}

void PC::do_idea( const String & arg )
{
	pc_report(IDEA_FILE, arg);
	out("Your idea report has been noted.\n\r");
}

void PC::do_typo( const String & arg )
{
	pc_report(TYPO_FILE, arg);
	out("Your typo report has been noted.\n\r");
}

void PC::do_checkreport( const String & args )
{
	String arg1;
	String arg2;
	const char * filename;

	args.startArgs();
	arg1 = args.getArg();
	arg2 = args.getArgRest();

	if ( arg1 == "bug" )
		filename = BUG_FILE;
	else if ( arg1 == "idea" )
		filename = IDEA_FILE;
	else if ( arg1 == "typo" )
		filename = TYPO_FILE;
	else
	{
		out( "Usage: checkreport <bug/idea/typo> [clean/view]\n\r" );
		return;
	}

	if ( !arg2 || (arg2 == "view") )
	{
		view(filename);
		return;
	}
	
	if (arg2 == "clean" )
	{
		// I'm guessing as about this privilages. Somebody (Fusion?) has
		// to outline logic behind each priv bit - Artur
		if (authorized(SUPERUSER) || authorized(ADMIN) )
		{
			unlink(filename);
			out("Report file deleted.\n\r");
			return;
		}
		else
		{
			out( "You are not allowed to delete entries.\n\r");
			return;
		}
	}

	out( "Usage: checkreport <bug/idea/typo> [clean/view]\n\r" );
}

void PC::do_hint( const String & arg )
{
	int i;
	String str;

	if ( (!arg) )
	{
		
		last_hint++;
		if ( last_hint >= hints.length() )
			last_hint = 0;
		out(hints[last_hint]);
	}
	else if ( (arg == "help") || (arg == "?") )
	{
		view( HINTS_HELP );
	}
	else if ( arg.isNumber() )
	{
		i = arg.asInt();
		if ( (i >= hints.length()) || (i<0) )
		{
			out ( "No hint with such number.\n\r");
			return;
		}
		out( hints[i] );
		last_hint = i;
	}
	else if ( arg == "info" )
	{
		str << "You can access " << hints.length() << " hints." << endl;
		if ( last_hint >= 0 )
			str << "Last hint you read has number " << last_hint << endl;
		else
			str << "You have hints turned off.\n\r";
		out(str);
	}
	else if ( arg == "reload" )
	{
		// AUTHORIZATION CHECK
		// ...

		str << "Hints file reloaded with " << loadHints() << " hints." <<endl;
		out(str);
	}
	else if ( arg == "off" )
	{
		out( "You turned automatic hints off.");
		last_hint = -1;
	}
	else if ( arg == "on" )
	{
		out ( "You have turned automatic hints on." );
		if ( last_hint < 0 )
			last_hint = 0;
	}
	else if ( arg == "edit" )
	{
		// AUTHORIZATION CHECK
		// ...
 		editor = new TextfileEditor(this);
		editor->command( HINTS_FILE );
		return;
	}
	else
	{
		out ( "Unknown command.\n\r" );
		view(HINTS_HELP);
	}

}