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

#include "config.h"
#include "io.h"
#include "string.h"
#include "llist.h"
#include "description.h"
#include "repop.h"
#include "hash.h"
#include "server.h"
#include "area.h"
#include "object.h"
#include "npc.h"
#include "pc.h"
//#include "shop.h"
#include "room.h"

#include "global.h"

const bitType sector_list[] =
{
	{0,0},
	{"ground",		SECTOR_GROUND		},
	{"air",			SECTOR_AIR			},
	{"water",		SECTOR_WATER		},
	{"underwater",	SECTOR_UNDERWATER	},
	{"uw",			SECTOR_UNDERWATER	},
	{0,0}
};


const bitType room_bit_list[] =
{
	{0,0},
	{"dark",		ROOM_DARK			},
	{"safe",		ROOM_SAFE			},
	{"no-summon",	ROOM_NOSUMMON		},
	{"no-leave",	ROOM_NOLEAVE		},
	{"death-trap",	ROOM_DEATHTRAP		},
	{"drain",		ROOM_DRAIN			},
	{"dt",			ROOM_DEATHTRAP		},
	{"shield",		ROOM_SHIELD			},
	{"fast-mana",	ROOM_FASTMANA		},
	{"fast-hp",		ROOM_FASTHP			},
	{"no-mana",		ROOM_NOMANA			},
	{"no-hp",		ROOM_NOHP			},
	{"indoors",		ROOM_INDOORS		},
	{0,0}
};

int Room::total_count = 0;

Room::~Room()
{
	Repop *pRepop;
	Char *ch;
	Object *obj;
	LList<Object> tList = objects;
	int i;

	total_count--;
	repops.reset();
	while( ( pRepop = repops.remove() ) )
	{
		if( pRepop->getPtr() )
			pRepop->getPtr()->setRepop( 0 );
		pRepop->fordelete();
	}

	chars.reset();
	while( ( ch = chars.remove() ) )
	{
		// Caller should verify whether there are PCs in the room because
		// we killem all.
		ch->fromWorld();
		ch->fordelete();
	}

	inv.reset();
	while( ( obj = inv.remove() ) )
	{
		obj->fromWorld();
		obj->fordelete();
	}

	for ( i =0; i < MAX_DIR; i++)
	{
		if ( exits[i] )
			exits[i]->fordelete();
	}

}

const String & Room::getScope() const
{
	return area->getKey();
}

Room * lookupRoom( const String & x )
{
	return lookupRoom( Index( x ) );
}

Room * lookupRoom( const char * x )
{
	return lookupRoom( Index( x ) );
}

Room * lookupRoom( const Index & x )
{
	Area *area;
	LList<Area> tlist = areas;

	for_each( tlist, area )
		if( area->getKey() == x.getScope() )
			break;

	if( area )
		return area->lookupRoom( x.getKey() );

	return 0;
}

void Room::addRepop( Repop * x )
{
	repops.addAfterCurrent( x );

	// Ugg, this is ugly, just because I am too lazy to write a
	// 'tail' into the LList class. Will have to do that soon.
	// For now this is so repops will add onto the list in order.
	repops.next();
}

void Room::addRepop( int slot, Repop * x )
{
	if( slot > 1 )
	{	
		repops.reset();
		while( slot-- > 2 )
			repops.next();
		repops.addAfterCurrent( x );
	}
	else repops.addTop( x );
}

Repop * Room::rmRepop( int slot )
{
	int i = 0;
	Repop * ptrRepop;
	if( slot < 1 )
		return 0;
	for_each( repops, ptrRepop )
	{
		i++;
		if( i == slot )
		{
			repops.remove( ptrRepop );
			return ptrRepop;
		}
	}

	return 0;
}

void Room::repop()
{
			Repop	*pRepop;
			NPC		*npc;
			NPC		*npcLast = 0;
			Object	*obj = 0;
			Object	*objLast = 0;
			Object	*objLastImmediate = 0;
	const	Object	*objProto;
	const	NPC		*npcProto;

	if ( TgUpdate() )
		return;

	for_each( repops, pRepop )
	{
		// This allows things like resetting a sword inside a stone
		// or other cool things. I don't do it with NPC's because
		// of possibility of a thief stealing something everytime
		// a NPC got re-equipped. No re-equips.

		if( pRepop->getPtr() )
		{
			if( pRepop->type == 'M' )
				npcLast = 0;
			else if( pRepop->type == 'O' )
				objLast = (Object *)pRepop->getPtr();

			continue;
		}

		switch( pRepop->type )
		{
			default: continue;

			// M - load NPC
			// G - give Object to last NPC
			// E - equip last NPC with Object
			// O - load Object to room
			// P - put Object in last Object loaded with 'O' code
			// N - nest object in 'immediate' last object loaded

			case 'M':		// Load NPC, set npcLast to this one.

							npcProto = lookupNPC( pRepop->index );
							if( npcProto )
							{

/*								if( npcProto->isShopKeeper() )
									npc = new ShopKeeper(  *(ShopKeeper *)npcProto );
								else
								*/

									npc = new NPC( *npcProto ); 
								if ( npc->TgCreated(this,pRepop ) )
								{
									npcLast = 0;
									break;
								}
								npc->toWorld();
								npcLast = npc;
								addCharInv( npc );
								npc->setRepop( pRepop );
								pRepop->setPtr( npc );
							}

							break;


			case 'O':		// Load object, set objLast to this one.

							objProto = lookupObj( pRepop->index );
							if( objProto )
							{
								obj = objProto->clone(); 
								if ( obj->TgCreated(this, pRepop) )
								{
									objLast = 0;
									break;
								}
								obj->toWorld();
								objLast = objLastImmediate = obj;
								addObjInv( obj );
								obj->setRepop( pRepop );
								pRepop->setPtr( obj );
							}

							break;


			case 'E':		// Check for last NPC loaded. Equip object 

							if( !npcLast )
								continue;

							objProto = lookupObj( pRepop->index );
							if( objProto )
							{
								obj = objProto->clone();
								if ( obj->TgCreated(this, pRepop) )
								{
									objLast = 0;
									break;
								}
								obj->toWorld();
								npcLast->addObjInv( obj );
								// val = wear position
								npcLast->equip( obj, pRepop->val );

								obj->setRepop( pRepop );
								pRepop->setPtr( obj );
								objLast = objLastImmediate = obj;
							}

							break;


			case 'G':		// Check for last NPC loaded. Give object to him.

							if( !npcLast )
								continue;

							objProto = lookupObj( pRepop->index );
							if( objProto )
							{
								obj = objProto->clone();
								if ( obj->TgCreated(this, pRepop) )
								{
									objLast = 0;
									break;
								}
								obj->toWorld();
								npcLast->addObjInv( obj );

								Cout << "Giving " << obj->getShort()
									<< " to " << npcLast->getShort()
									<< endl;

								obj->setRepop( pRepop );
								pRepop->setPtr( obj );
								objLast = objLastImmediate = obj;
							}
							else
							{
								Cout << "Repop lookup failed for "
										<< pRepop->index.asString() << endl;
							}
							break;


			case 'P':		// Put object in last object.
							// Doesn't change objLast

							if( !objLast )
								continue;

							objProto = lookupObj( pRepop->index );
							if( objProto )
							{
								obj = objProto->clone();
								if ( obj->TgCreated(this, pRepop) )
								{
									objLast = 0;
									break;
								}
								obj->toWorld();
								objLast->addObjInv( obj );

								obj->setRepop( pRepop );
								pRepop->setPtr( obj );
								objLastImmediate = obj;
							}

							break;

			case 'N':		// Nest object in immediate last object loaded.
							// Doesn't change objLast
							if( !objLastImmediate )
								continue;

							objProto = lookupObj( pRepop->index );
							if( objProto )
							{
								obj = objProto->clone();
								if ( obj->TgCreated(this, pRepop) )
								{
									objLast = 0;
									break;
								}
								obj->toWorld();
								objLastImmediate->addObjInv( obj );
								obj->setRepop( pRepop );
								pRepop->setPtr( obj );
								objLastImmediate = obj;
							}

							break;
		}
	}
}


const String & Room::getExtraDesc( const char * x )
{
	static String retNULL;
	Description * d;

	for_each( ed, d )
		if( d->isName( x ) )
			return d->getDesc();

	return retNULL;	
}


void Room::addObjInv( Object * obj )
{
	obj->toRoom( this );
	inv.add( obj );
}


void Room::rmObjInv( Object * obj )
{
	obj->fromRoom();
	inv.remove( obj );
}


void Room::addCharInv( Char *ch )
{
	ch->toRoom( this );
	chars.add( ch );
}


void Room::rmCharInv( Char *ch )
{
	ch->fromRoom();
	chars.remove( ch );
}


void Room::out( const char *str )
{
	Char *ch;
	for_each( chars, ch )
		ch->out( str );
}


void Room::outAllExcept( const String & str, Thing * t1, Thing * t2 )
{
	Char * ch, * c1 = 0, * c2 = 0;

	c1 = ( Char * ) t1;
	c2 = ( Char * ) t2;

	for_each( chars, ch )
		if( ch != t1 && ch != t2 )
			ch->out( str );
}


void Room::outAllCharExcept( const char *str, Char *ch1, Char *ch2 )
{
	Char *ch;
	for_each( chars, ch )
		if( ch != ch1 && ch != ch2 )
			ch->out( str );
}


void Room::interp( const String & in, Char * n, Char * N, Object * o, Object * O )
{
	Char *ch;
	for_each( chars, ch )
		if( ch != n && ch != N )
			ch->interp( in, n, N, o, O );
}


int Room::readFrom( StaticInput &in )
{
	int err;
	char buf[ BUF*2 ];

	if( !in )
		in.error("StaticInput stream not open.");

	if( *in.getword( buf ) != '{' )
		in.error( "Room::readFrom() - no '{' found");
 
	name = in.getstring( buf );
	desc = in.getstring( buf );
	/*unused*/ in.getnum();
	/*unused*/ in.getnum();
	sector = ::lookupSector( in.getword( buf ) );
	terrain = in.getnum();
	in.getbitfield( room_bits );

	while(1)
	{
		in.getword( buf );
		switch( *buf )
		{ 
			//  Door block
			//  Syntax example: <dir> <name> <flags> <key-index> <to_room> <desc>
			//    D {
			//      4 coffin 1|2|32 thalos:key-3  keroon:room-1
			//    }

			case 'D':
			{
				in.getword( buf );
				if( !*buf == '{' )
					in.error( "Door block not found for symbol 'D'" );

				int    dir     =  in.getnum();
				String doorName =  in.getstring( buf );
				int    flags   =  in.getnum();
				Index kIndex   =  in.getword( buf );
				Index rIndex   =  in.getword( buf );
				String str = in.getstring( buf );

				if( dir > 5 )
					in.error( "Room::readFrom - direction out of range.\n" );

				exits[ dir ] = new Exit( doorName, rIndex, kIndex, flags, str ); 

				if( *in.getword( buf ) != '}' )
					in.error( "Room::readFrom - door block invalid." );
				break;
			}
			break;

			case 'R':
			{
				Repop *pRepop = new Repop;
				if( ( err = pRepop->readFrom( in ) ) == 0 )
					addRepop( pRepop );
				else
				{
					Cout << "Invalid repop code, skipping.\n";
					pRepop->fordelete();
				}
			}      
			break;

			case 'E':
			{
				// Extra description block
				Description *d = new Description;
				if( ( err = d->readFrom( in ) ) == 0 )
					ed.addTop( d );
				else
				{
					Cout << "Invalid extra description block, skipping.\n";
					delete d;
				}
			}
			break;
			
			// add full 'Trig' keyword
			case 'T':
			{
				in.getword(buf);
				struct TriggerData * td = new (struct TriggerData);
				td->type = lookupBit( rtrig_types, in.getword( buf ) );
				td->fun = lookupRtrig( td->type, in.getword(buf) );
				if ( (td->type == 0) || (td->fun == NULL) )
				{
					delete td;
					in.error("Room:readFrom - unrecognized trigger data");
					continue;
				}
				addTrigger(td);
				in.getword(buf);
			}
			break;
			
			default:
				return 1;
		}
	}
	return 1;
}


int Room::writeTo( Output & outf ) const
{
	String strExitName;

	if( !outf )
		return 0;

	outf << '{'   << endl;
	outf << name  << '~' << endl;
	outf << desc  << '~' << endl;
	outf << 0 << ' ' << 0 << ' '
		<< lookupSectorName( sector ) << ' ' << terrain << endl;
	outf.putbitfield( room_bits, MAX_ROOM_BIT_FIELDS );
	outf << endl;

	/* write the doors */
	for( int i = 0; i < MAX_DIR; i++ )
	{
		if( exits[i] )
		{
			if( !(bool)exits[i]->getName() )
				strExitName = "none";
			else
				strExitName = exits[i]->getName();

			outf	<< "D{" << i << " "
					<< strExitName << "~"
					<< exits[i]->original_flags[1] << " "
					<< exits[i]->key.asString() << " "
					<< exits[i]->room_index.asString() << " "
					<< exits[i]->desc << "~}\n";
		}
	}
	// end doors

	// write the repops
	Repop *pRepop;
	for_each( repops, pRepop )
	{
		outf << "R";
		pRepop->writeTo( outf );
		outf << endl;  
	} 

	struct TriggerData * tg;
	for_each( triggers, tg )
		outf << "Trig{ " << lookupBitName(rtrig_types, tg->type) <<
		" " << lookupRtrigName(tg->type, tg->fun) << "}" << endl;

	

	outf << '}' << endl;
	return 1;
}


// Destroy ExitIndex, create Exit and link to the real room.
void Room::hardLink()
{
	for( int i = 0; i < MAX_DIR; i++ )
		if( exits[ i ] )
			exits[ i ]->hardLink();
}


Exit * Room::getExit( int dir )
{
	if( dir >= MAX_DIR )
		return 0;
	return exits[ dir ];
}


Room *Room::toRoom( int dir )
{
	if( dir >= MAX_DIR )
		return 0;

	//Need to finish this
	if( !exits[ dir ] )
		return 0;

	if( !exits[ dir ]->room_ptr )
	{
		Cout << "Room::toRoom " << getName() << ":exit points to NULL room.\n"; 
		return 0;
//		exit(0);
	}

	return exits[ dir ]->room_ptr;
}


void Room::addExit( Exit *pExit, int dir )
{
	if( dir < MAX_DIR )
		exits[ dir ] = pExit; 
}

 
void Room::deleteExit( int dir )
{
	if( dir >= 0 && dir < MAX_DIR )
	{
		exits[ dir ]->fordelete();
		exits[ dir ] = 0;
	}
}


Char *Room::getChar( const String & str )
{
	Char  * ch;
	countedThing cI(str);

	parseCompositeName( ch, chars, cI );
	return ch;
}

Char * Room::getChar( countedThing & cI )
{
	Char  * ch;
	parseCompositeName( ch, chars, cI );
	return ch;
}



PC *Room::getPC( const String & str )
{
	Char *ch;
	for_each( chars, ch )
	{
		if( ch->isNPC() )
			continue;
		if( ch->isName( str ) )
			break;
	}
	return (PC*)ch;
}


NPC * Room::getNPC( const String & str )
{
	Char *ch;
	for_each( chars, ch )
	{
		if( ch->isPC() )
			continue;
		if( ch->isName( str ) )
			return (NPC*)ch;
	} 
	return 0;
}


Object * Room::getObj( const String & str )
{
	countedThing cI(str);
	Object  * obj;
	parseCompositeName( obj, inv, cI );
	return obj;
}

Object * Room::getObj( countedThing & cI )
{
	Object * obj;
	parseCompositeName( obj, inv, cI );
	return obj;
}

void Room::describeItself( String & str )
{
	String str2;
	int i;
	Exit * pExit;
	struct TriggerData * td;

	for( i = 1; room_bit_list[i].name; i++ )
		if( roomBitIsSet( room_bit_list[i].val ) )
			str2 << room_bit_list[i].name << ' ';

	// Display room and return
	str << "ROOM\n\r"
		<< "Area: " << getArea()->getName() << "\n\r"
		<< "Index: " << getKey() << "\n\r"
		<< "Room bits: " << str2 << "\n\r"
		<< "Room affects: " << "\n\r"
		<< "Sector: " << lookupSectorName( getSector() ) << "\n\r"
		<< "Name: " << getName() << "\n\r"
		<< "Room text:\n\r" << getDesc()
		;
	for( i = 0; i < MAX_DIR; i++ )
	{
		if( !( pExit = getExit( i ) ) )
			continue;
			str2.clr();
		for( int j = 1; exit_bits[j].name; j++ )
			if( pExit->exitBitSet( exit_bits[j].val ) )
				str2 << exit_bits[j].name << ' ';
			// Modify later to use -->, <-- and <--> for doors
		str.sprintfAdd( "%5s: %-20s %-15s [%s]\n\r",
			lookupDirName( i ),
			pExit->getIndex().asString().chars(),
			pExit->getName().chars(),
			str2.chars()
		);
	}

	for_each( triggers, td)
	{
		str << "Trigger " << lookupBitName( rtrig_types, td->type) <<
		" " << lookupRtrigName( td->type, td->fun ) << "\n\r";
	}

}


bool Room::TgUpdate()
{
	if ( !IS_SET(trigger_bits, RTRIG_UPDATE) )
		return false;

	return ((rtrigT_update)findTrigger(RTRIG_UPDATE )) 
  						(this);

}

bool Room::TgDroppedItem( Char * caller, Object * what )
{
	if ( !IS_SET(trigger_bits, RTRIG_DROPPED_ITEM) )
		return false;

	return ((rtrigT_dropped_item)findTrigger(RTRIG_DROPPED_ITEM )) 
  						(this,caller,what);

}

bool Room::TgPickedItem( Char * caller, Object * what )
{
	if ( !IS_SET(trigger_bits, RTRIG_PICKED_ITEM) )
		return false;

	return ((rtrigT_picked_item)findTrigger(RTRIG_PICKED_ITEM )) 
  						(this,caller,what);
}

bool Room::TgStrangeCmd(Char * caller, const String & cmd, const String & args)
{
	if ( !IS_SET(trigger_bits, RTRIG_STRANGE_CMD) )
		return false;

	return ((rtrigT_strange_cmd)findTrigger(RTRIG_STRANGE_CMD )) 
  						(this,caller,cmd,args);

}

bool Room::TgSaidIn( Char * who, const char * what )
{
	if ( !IS_SET(trigger_bits, RTRIG_SAID_IN) )
		return false;

	return ((rtrigT_said_in)findTrigger(RTRIG_SAID_IN )) 
  						(this, who, what);

}

bool Room::TgSaidIn( Char * who, const String & what )
{
	return TgSaidIn(who, what.chars() );
}

bool Room::TgLeft( Char * who, Room * to )
{
	if ( !IS_SET(trigger_bits, RTRIG_LEFT) )
		return false;

	return ((rtrigT_left)findTrigger(RTRIG_LEFT )) 
  						(this,who,to);

}

bool Room::TgEntered( Char * who, Room * to )
{
	if ( !IS_SET(trigger_bits, RTRIG_ENTERED) )
		return false;

	return ((rtrigT_entered)findTrigger(RTRIG_ENTERED )) 
  						(this,who,to);

}

bool Room::TgSocialedIn( Char * who,Char * target,const Social * social)
{
	if ( !IS_SET(trigger_bits, RTRIG_SOCIALED_IN) )
		return false;

	return ((rtrigT_socialed_in)findTrigger(RTRIG_SOCIALED_IN )) 
  						(this,who,target,social);

}