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

#include "config.h"
#include "string.h"
#include "llist.h"
#include "index.h"
#include "room.h"
#include "repop.h"
#include "hash.h"
#include "server.h"
#include "bit.h"
#include "edit.h"
#include "area.h"
#include "pc.h"
#include "object.h"
#include "global.h"
#include "trigs.h"

void RoomEditor::command( const String & arg )
{
	String str;
	String str2;
	String arg1;
	String arg2;
	String arg3;
	Area * pArea;
	Exit *pExit;
	struct TriggerData * td;

	int val1;
	int i;

	// If in text editor, pass args on to text editor

	if( text )
	{
		// If text editor is finished, do something with its content
		// or cancel.
		text->command( arg );
		if( text->done() )
		{
			if( text->getState() == ED_CANCEL )
			{
				delete text;
				text = 0;
				state = ED_NONE;
				return;
			}

			switch( state )
			{
				case ED_DESC:	room->setDesc( text->asStr() );
								room->getArea()->setDirtyBit();
								delete text;
								text = 0;
								state = ED_NONE;
								return;
				default:		delete text;
								text = 0;
								state = ED_NONE;
								return;
			}
		}
		return;
	}
	else if( (bool)filename )
	{
		char buf[ 8092 ];
		InputFile inf( filename );
		if( !inf )
		{
			pc->out( "System error. Can't read temp file.\n\r" );
			filename.clr();
			return;
		}

		inf.getstring( buf );
		switch ( state )
		{
			case ED_DESC:
		room->setDesc( buf );
				room->getArea()->setDirtyBit();
				state = ED_NONE;
				break;
			default:
				state = ED_NONE;
				break;
		}
		inf.close();
		remove( filename.chars() );
		filename.clr();
		return;
	}

	if( !(bool)arg || arg == "\n" )
	{
		room->describeItself(str);
		pc->out( str );
	} 
	else
	{
		Index index1;
		Index index2;
		int dir;
		int revdir;
		Room *pRoom;

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

		area = room->getArea();

		if( arg1 == "name" )
		{
			room->setName( arg2 );
			pc->out( "Name set to '" );
			pc->out( arg2 );
			pc->out( "'\n\r" );
			area->setDirtyBit();
			return;
		}
		else if( arg1 == "desc" )
		{
			if( arg2[0] == '+' )
			{
				arg2.shiftLeft();
				str << room->getDesc() << "\n\r" << arg2;
				room->setDesc( str );
			}
			else if ( arg2 )
			{
				str << arg2 << "\n\r";
				room->setDesc(str);
			}
			else
			{
				editText( room->getDesc() );
				//state = ED_DESC;
				return;
			}
			area->setDirtyBit();
			return;
		}
		else if( arg1 == "index" )
		{
			area->removeRoom( room );
			room->setKey( arg2 );
			area->addRoom( room );
			area->setDirtyBit();
			return;
		}
		else if( arg1 == "bitmask" )
		{
			if( !(bool)arg2 )
			{
				pc->out( "Set which bit in bitmask?\n\r" );
				return;
			}

			if( ( val1 = lookupRoomBit( arg2 ) ) )
			{
				SET_BIT( room_bits, val1 );
				pc->out( "Bit toggled in default bitmask.\n\r" );
				return;
			}
		}
		else if( arg1 == "copy" )
		{
			if( !(bool)arg2 )
			{
				pc->out( "Copy which room?\n\r" );
				return;
			}

			if( !( pRoom = lookupRoom( arg2 ) ) )
			{
				pc->out( "No such room to copy.\n\r" );
				return;
			}

			room->setName( pRoom->getName() );
			room->setDesc( pRoom->getDesc() );
			area->setDirtyBit();
			return;
		}
		else if( arg1 == "new" || arg1 == "create" )
		{
			if( !(bool)arg2 )
			{
				pc->out( "Usage:    new <index>\n\r" );
				return;
			}

			index = arg2.getArgRest();
			if( (bool)index.getScope() )
			{
				if( !( pArea = lookupArea( index.getScope() ) ) )
				{
					pc->out( "No such area.\n\r" );
					return;
				}
			}
			else pArea = area;

			if( !( pRoom = pArea->lookupRoom( index.getKey() ) ) )
			{
				pRoom = new Room( pArea );
				pRoom->setName( "No name" );
				pRoom->setDesc( "No description" );
				pRoom->setKey( index.getKey() );
				// Editor is a friend class of Room
				memcpy( pRoom->room_bits, room_bits,
						sizeof( room_bits[0] ) * MAX_ROOM_BIT_FIELDS );
				pArea->addRoom( pRoom );
				pc->out( "New room created.\n\r" );
				pArea->setDirtyBit();
				room->rmCharInv( pc );
				pRoom->addCharInv( pc );
				room = pRoom;
				area = pArea;
			}
			else pc->out( "That room already exists.\n\r" );
			return;
		}
		else if( arg1 == "save" )
		{
			area->save();
			area->rmDirtyBit();
			pc->out( "Zone saved.\n\r" );
			return;
		}
		else if( arg1 == "goto" )
		{
			pc->do_goto( arg2 );
			setRoom( pc->inRoom() );
			return;
		}
		else if( arg1 == "repop" )
		{
			if( !(bool)arg2 || arg2 == "help" )
			{
				pc->out( "repop mob <npc-index> {chance}\n\r" );
				pc->out( "repop <#> give <obj-index> {chance}\n\r" );
				pc->out( "repop <#> eq <obj-index> {chance}\n\r" );
				pc->out( "repop obj <obj-index> {chance}\n\r" );
				pc->out( "repop <#> put <obj-index> {chance}\n\r" );
				pc->out( "repop <#> nest <obj-index> {chance}\n\r" );
				return;
			}
			else
			if( arg2 == "list" || arg2 == "show" )
			{
				pc->do_repops( "list" );
				return;
			}
			else
			{
				parseRepop( arg2 );
				return;
			}
		}
		else if( arg1 == "list" )
		{
			/*
			Room * pRoom;
			for_each( area->roomIndex, pRoom )
				str.sprintfAdd( "%15s - %s\n\r", pRoom->getKey().chars(),
										pRoom->getName().chars() );
			pc->out( str );
			*/
			pc->do_rfind(arg2);
			return;
		}
		else if( arg1 == "help" || arg1 == "?" )
		{
			pc->view( "../help/redit.hlp" );
			pc->page( "" );
			return;
		}
		else if( arg1 == "room-bits" )
		{
			pc->view( "../help/room-bits.hlp" );
			pc->page( "" );
			return;
		}
		else if ( arg1 == "trigadd" )
		{
			void * fun;

			arg2.startArgs();
			arg1 = arg2.getArg();
			str2 = arg2.getArg();
			
			val1 = lookupBit( rtrig_types, arg1 );
			if ( val1 == 0 )
			{
				pc->out("No such bit.\n\r");
				return;
			}

			fun = lookupRtrig( val1, str2.chars() );
			if ( fun == NULL )
			{
				pc->out("No such function for that type of trigger.\n\r");
				return;
			}

			for_each(room->triggers, td )
			{
				if ( td->type == val1 )
					break;
			}
	
			if ( td != NULL )
			{
				pc->out("Replacing old trigger.\n\r");
				room->removeTrigger(td);
			}

			td = new (struct TriggerData);
			td->type = val1;
			td->fun = fun;
			room->addTrigger(td);
			pc->out("Trigger added.\n\r");
			return;

		}
		else if ( arg1 == "trigdel" )
		{
			val1 = lookupBit( rtrig_types, arg2 );
			if ( val1 == 0 )
			{
				pc->out("No such bit.\n\r");
				return;
			}
	
			for_each(room->triggers, td )
			{
				if ( td->type == val1 )
					break;
			}

			if ( td == NULL )
			{
				pc->out("This room has no such trigger.\n\r");
				return;
			}
	
			room->removeTrigger(td);
			pc->out("Trigger removed.\n\r");
			return;
		}
		else if ( arg1 == "triglist" )
		{
			if ( !(bool)arg2 )
			{
				// list types of triggers
				str << "Trigger types for Rooms " <<endl;
				for ( i=1; rtrig_types[i].name; i++ )
				{
					str << rtrig_types[i].name << endl;
				}
				pc->out(str);
				return;
			}
			else
			{
				// list triggers for given type
				const char * name;
				val1 = lookupBit( rtrig_types, arg2 );
				if ( val1 == 0 )
				{
					pc->out("No such bit.\n\r");
					return;
				}
				str << "Triggers for type " << arg2 << ":" << endl;	
				for ( i = 0; ; i++ )
				{
					name = getRtrigName(val1, i);
					if ( !name )
						break;
					str << name << endl;
				}
				pc->out(str);
				return;
			}
		}

		if( ( dir = getDir( (const char *)arg1 ) ) != DIR_UNDEFINED )
		{
			revdir = getRevDir( dir );
			if( !(bool)arg2 )
			{
				if( !( pRoom = room->toRoom( dir ) ) )
				{
					pc->out( "No exit in that direction.\n\r" );
					return;
				}
				pc->out( "Moving to room " );
				pc->out( pRoom->getKey() );
				pc->out( ".\n\r" );
				room->rmCharInv( pc );
				pRoom->addCharInv( pc );
				room = pRoom;
				area = pRoom->getArea();
				return;
			}
			else	
			{
				// Break into 3rd arg
				arg2.startArgs();
				arg3 = arg2.getArg();
				if( arg3 == "dig" || arg3 == "room" )
				{
					if( ( pRoom = room->toRoom( dir ) ) )
					{
						pc->out( "Already an exit in that direction.\n\r" );
						return;
					} 

					if( !(bool)arg2.getArgRest() )
					{
						pc->out( "No room specified.\n\r" );
						return;
					}

					index = arg2.getArgRest();
					if( (bool)index.getScope()
						&& !( pArea = lookupArea( index.getScope() ) ) )
					{
						pc->out( "No such area.\n\r" );
						return;
					}
					else pArea = area;

					index.setScope( pArea->getKey() );

					if( !( pRoom = pArea->lookupRoom( index.getKey() ) ) )
					{
						pRoom = new Room( area );
						pRoom->setName( "No name" );
						pRoom->setDesc( "No description" );
						pRoom->setKey( index.getKey() );
						// RoomEditor is a friend class of Room
						memcpy( pRoom->room_bits, room_bits,
								sizeof( room_bits[0] ) * MAX_ROOM_BIT_FIELDS );
						pArea->addRoom( pRoom );
						pc->out( "New room created.\n\r" );
					}
					else if( arg3 == "room" )
					{
						pc->out( "Can't create, that room already exists.\n\r" );
						return;
					}
					else if( pRoom->toRoom( revdir ) )
					{
						pc->out( "Exit exists on other side.\n\r" );
						return;
					}

					// Convert what was 'e' to 'east' for constructor
					//arg1 = lookupDirName( dir );
					pExit = new Exit( "", index, "", 0, "" );
					room->addExit( pExit, dir );
					room->hardLink();

					// Link reverse side
					index.setScope( room->getArea()->getKey() );
					index.setKey( room->getKey() );

					pExit = new Exit( "", index, "", 0, "" );
					pRoom->addExit( pExit, revdir );
					pRoom->hardLink();

					pc->out( "Exit linked.\n\r" );
					area->setDirtyBit();
					if( arg3 == "dig" )
					{
						room->rmCharInv( pc );
						pRoom->addCharInv( pc );
						room = pRoom;
						area = pArea;
					}
					return;
				}
				if( arg3 == "link" || arg3 == "to" )
				{
					if( ( pRoom = room->toRoom( dir ) ) )
					{
						pc->out( "Already an exit in that direction.\n\r" );
						return;
					} 

					if( !(bool)arg2.getArgRest() )
					{
						pc->out( "No room specified.\n\r" );
						return;
					}

					index = arg2.getArgRest();
					if( (bool)index.getScope() )
					{
						if( !( pArea = lookupArea( index.getScope() ) ) )
						{
							pc->out( "No such area.\n\r" );
							return;
						}
					}
					else pArea = area;

					index.setScope( pArea->getKey() );
					if( !( pRoom = pArea->lookupRoom( index.getKey() ) ) )
					{
						pc->out( "No such room.\n\r" );
						return;
					}

					if( pRoom->toRoom( revdir ) )
					{
						pc->out( "Exit exists on other side.\n\r" );
						return;
					}

					pExit = new Exit( "", index, "", 0, "" );
					room->addExit( pExit, dir );
					room->hardLink();

					// Link reverse side
					index.setScope( room->getArea()->getKey() );
					index.setKey( room->getKey() );

					pExit = new Exit( "", index, "", 0, "" );
					pRoom->addExit( pExit, revdir );
					pRoom->hardLink();
					pc->out( "Exit linked.\n\r" );
					area->setDirtyBit();
					return;
				}
				else if( arg3 == "delete" )
				{
					if( !room->getExit( dir ) )
					{
						pc->out( "No exit in that direction.\n\r" );
						return;
					} 

					room->toRoom( dir )->deleteExit( revdir );
					room->deleteExit( dir );
					pc->out( "Exit deleted.\n\r" );
					area->setDirtyBit();
					return;	
				}
				else if( arg3 == "name" )
				{
					if( !room->getExit( dir ) )
					{
						pc->out( "No exit in that direction.\n\r" );
						return;
					} 

					room->getExit( dir )->setName( arg2.getArgRest() );
					area->setDirtyBit();
					pc->out( "Exit name set.\n\r" );
					return;
				}
				else if( arg3 == "desc" || arg3 == "description" )
				{
					if( !room->getExit( dir ) )
					{
						pc->out( "No exit in that direction.\n\r" );
						return;
					} 

					room->getExit( dir )->setDesc( arg2.getArgRest() );
					area->setDirtyBit();
					pc->out( "Exit description set.\n\r" );
					return;
				}

				if( ( val1 = lookupExitBit( arg3 ) ) )
				{
					if( !room->getExit( dir ) )
					{
						pc->out( "No exit in that direction.\n\r" );
						return;
					} 

					room->getExit( dir )->toggleExitBit( val1 );
					area->setDirtyBit();
					pc->out( "Exit bit toggled.\n\r" );
					return;
				}
			}
		}

		if( ( val1 = lookupSector( arg1 ) ) ) 
		{
			room->setSector( val1 );
			pc->out( "Sector type set.\n\r" );
			area->setDirtyBit();
			return;
		}

		if( ( val1 = lookupRoomBit( arg1 ) ) ) 
		{
			room->toggleRoomBit( val1 );
			pc->out( "Room bit toggled.\n\r" );
			area->setDirtyBit();
			return;
		}

		pc->out( "Invalid command.\n\r" );
	}
}


void RoomEditor::parseRepop( const String & arg )
{
	String str;
	String arg1;
	String arg2;
	String arg3;
	int chance = 100;
	int slot = 0;
	Repop * ptrRepop;
	
	arg.startArgs();
	arg1 = arg.getArg();
	arg2 = arg.getArg();
	arg3 = arg.getArg();

	if( arg1.isNumber() )
	{
		if( ( slot = arg1.asInt() ) <= 0 )
		{
			pc->out( "Repop slot must be a positive non-zero number.\n\r" );
			return;
		}

		arg1 = arg2;
		arg2 = arg3;
		arg3 = arg.getArg();
		if( !(bool)arg1 )
		{
			str << "Current repop slot set to " << slot << ".\n\r";
			pc->out( str );
			last_slot = slot;
			return;
		}
	}
	else slot = last_slot + 1;

	if( arg1 == "del" || arg1 == "delete" || arg1 == "remove" )
	{
		if( !( ptrRepop = room->rmRepop( slot ) ) )
		{
			pc->out( "Invalid repop slot number.\n\r" );
			return;
		}

		pc->out( "Deleting repop.\n\r" );
		if( ptrRepop->getPtr() )
			ptrRepop->getPtr()->setRepop( 0 );
		ptrRepop->fordelete();
		last_slot = slot - 1;
		return;
	}

	if( arg1 == "mob" || arg1 == "mobile" || arg1 == "npc" )	
	{
		if( !(bool)arg2 )
		{
			pc->out( "Repop which npc?\n\r" );
			return;
		}

		if( (bool)arg3 )
			chance = arg3.asInt();

		ptrRepop = new Repop( 'M', arg2, chance );
		room->addRepop( slot, ptrRepop );
		area->setDirtyBit();
		str << "Added NPC repop at slot " << slot << ".\n\r";
		if( !lookupNPC( arg2 ) )
			str << "WARNING: NPC prototype '" << arg2 << "' not in database.\n\r";
		pc->out( str );
		last_slot = slot;
		return;
	}
	else if( arg1 == "give" )
	{
		if( !(bool)arg2 )
		{
			pc->out( "Give which object?\n\r" );
			return;
		}

		if( (bool)arg3 )
			chance = arg3.asInt();

		ptrRepop = new Repop( 'G', arg2, chance );
		room->addRepop( slot, ptrRepop );
		area->setDirtyBit();
		str << "Added 'give item to NPC' repop at slot " << slot << ".\n\r";
		if( !lookupObj( arg2 ) )
			str << "WARNING: Obj prototype '" << arg2 << "' not in database.\n\r";
		pc->out( str );
		last_slot = slot;
		return;
	}
	else if( arg1 == "eq" || arg1 == "equip" )
	{
		if( !(bool)arg2 )
		{
			pc->out( "Equip which object?\n\r" );
			return;
		}

		if( (bool)arg3 )
			chance = arg3.asInt();

		ptrRepop = new Repop( 'E', arg2, chance );
		room->addRepop( slot, ptrRepop );
		area->setDirtyBit();
		str << "Added 'equip NPC with item' repop at slot " << slot << ".\n\r";
		if( !lookupObj( arg2 ) )
			str << "WARNING: Obj prototype '" << arg2 << "' not in database.\n\r";
		pc->out( str );
		last_slot = slot;
		return;
	}
	else if( arg1 == "obj" || arg1 == "object" )
	{
		if( arg2.isNumber() )
		{
			slot = arg2.asInt();
			arg2 = arg3;
			arg3 = arg.getArg();
		}

		if( !(bool)arg2 )
		{
			pc->out( "Repop which object?\n\r" );
			return;
		}

		if( (bool)arg3 )
			chance = arg3.asInt();

		ptrRepop = new Repop( 'O', arg2, chance );
		room->addRepop( slot, ptrRepop );
		area->setDirtyBit();
		str << "Added Object repop at slot " << slot << ".\n\r";
		if( !lookupObj( arg2 ) )
			str << "WARNING: Obj prototype '" << arg2 << "' not in database.\n\r";
		pc->out( str );
		last_slot = slot;
		return;
	}
	else if( arg1 == "put" )
	{
		if( !(bool)arg2 )
		{
			pc->out( "Put which object?\n\r" );
			return;
		}

		if( (bool) arg3 )
			chance = arg3.asInt();

		ptrRepop = new Repop( 'P', arg2, chance );
		room->addRepop( slot, ptrRepop );
		area->setDirtyBit();
		str << "Added 'put object in object' repop at slot " << slot << ".\n\r";
		if( !lookupObj( arg2 ) )
			str << "WARNING: Obj prototype '" << arg2 << "' not in database.\n\r";
		pc->out( "str" );
		last_slot = slot;
		return;
	}
	else if( arg1 == "nest" )
	{
		if( !(bool)arg2 )
		{
			pc->out( "Nest which object?\n\r" );
			return;
		}

		if( (bool) arg3 )
			chance = arg3.asInt();

		ptrRepop = new Repop( 'N', arg2, chance );
		room->addRepop( slot, ptrRepop );
		area->setDirtyBit();
		str << "Added 'nest object in object' repop at slot " << slot << ".\n\r";
		if( !lookupObj( arg2 ) )
			str << "WARNING: Obj prototype '" << arg2 << "' not in database.\n\r";
		pc->out( "str" );
		last_slot = slot;
		return;
	}

	pc->out( "Error parsing repop.\n\r" );
	pc->out( "Type 'repop help' for syntax info.\n\r" );	
}