/*
....[@@@..[@@@..............[@.................. 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@falcon.mercer.peachnet.edu
MUD++ development mailing list mudpp-list@spice.com
------------------------------------------------------------------------------
room.cc
*/
#include "file.h"
#include "string.h"
#include "llist.h"
#include "description.h"
#include "repop.h"
#include "indexable.h"
#include "server.h"
#include "area.h"
#include "object.h"
#include "npc.h"
#include "shop.h"
#include "room.h"
#include "global.h"
Room::~Room()
{
Repop *rep;
Char *ch;
Object *obj;
LList<Object> tList = objects;
repops.reset();
while( ( rep = repops.remove() ) )
{
if( rep->getPtr() )
rep->getPtr()->setRepop( 0 );
delete rep;
}
chars.reset();
while( ( ch = chars.remove() ) )
{
// Caller should verify whether there are PCs in the room because
// we killem all.
ch->fromWorld();
delete ch;
}
inv.reset();
while( ( obj = inv.remove() ) )
{
obj->fromWorld();
delete obj;
}
}
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;
tlist.reset();
while( ( area = tlist.peek() ) )
{
tlist.next();
if( area->getKey() == x.getScope() )
break;
}
if( area )
return area->lookupRoom( x );
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::repop()
{
Repop *repop;
NPC *npc;
NPC *npcLast = 0;
Object *obj = 0;
Object *objLast = 0;
const Object *objProto;
const NPC *npcProto;
repops.reset();
while( ( repop = repops.peek() ) )
{
repops.next();
// 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( repop->getPtr() )
{
if( repop->type == 'M' )
npcLast = 0;
else if( repop->type == 'O' )
objLast = (Object *)repop->getPtr();
continue;
}
switch( repop->type )
{
default: continue;
// M - load NPC
// O - load Object
// G - give Object to last NPC
// E - equip last NPC with Object
// 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( repop->index );
if( npcProto )
{
if( npcProto->isShopKeeper() )
npc = new ShopKeeper( *npcProto );
else
npc = new NPC( *npcProto );
npc->toWorld();
npcLast = npc;
addCharInv( npc );
npc->setRepop( repop );
repop->setPtr( npc );
}
break;
case 'O': // Load object, set objLast to this one.
objProto = lookupObj( repop->index );
if( objProto )
{
obj = new Object( *objProto );
obj->toWorld();
objLast = obj;
addObjInv( obj );
obj->setRepop( repop );
repop->setPtr( obj );
}
break;
case 'E': // Check for last NPC loaded. Equip object
if( !npcLast )
continue;
objProto = lookupObj( repop->index );
if( objProto )
{
obj = new Object( *objProto );
obj->toWorld();
npcLast->addObjInv( obj );
// val = wear position
npcLast->equip( obj, repop->val );
obj->setRepop( repop );
repop->setPtr( obj );
}
break;
case 'G': // Check for last NPC loaded. Give object to him.
if( !npcLast )
continue;
objProto = lookupObj( repop->index );
if( objProto )
{
obj = new Object( *objProto );
obj->toWorld();
npcLast->addObjInv( obj );
Cout << "Giving " << obj->getShort()
<< " to " << npcLast->getShort()
<< endl;
obj->setRepop( repop );
repop->setPtr( obj );
}
else
{
Cout << "Repop lookup failed for "
<< repop->index.asString() << endl;
}
break;
case 'P': // Check for last object loaded
if( !objLast )
continue;
objProto = lookupObj( repop->index );
if( objProto )
{
obj = new Object( *objProto );
obj->toWorld();
objLast->addObjInv( obj );
obj->setRepop( repop );
repop->setPtr( obj );
}
break;
case 'N': // Nest object in immediate last object loaded.
// Set last obj then to last immediate.
if( !( objLast = obj ) )
continue;
objProto = lookupObj( repop->index );
if( objProto )
{
obj = new Object( *objProto );
obj->toWorld();
objLast->addObjInv( obj );
obj->setRepop( repop );
repop->setPtr( obj );
}
break;
}
}
}
const String & Room::getExtraDesc( const char * x )
{
static String retNULL;
Description * d;
ed.reset();
while( ( d = ed.peek() ) )
{
ed.next();
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;
chars.reset();
while( ( ch = chars.peek() ) )
{
ch->out( str );
chars.next();
}
}
void Room::outAllCharExcept( const char *str, Char *ch1, Char *ch2 )
{
Char *ch;
chars.reset();
while( ( ch = chars.peek() ) )
{
if( !(ch == ch1) && !(ch == ch2) )
ch->out( str );
chars.next();
}
}
int Room::readFrom( InFile &in )
{
int err;
char buf[ BUF*2 ];
if( !in )
in.error("InFile stream not open.");
if( *in.getword( buf ) != '{' )
in.error( "Room::readFrom() - no '{' found");
// index = in.getword( buf );
name = in.getstring( buf );
desc = in.getstring( buf );
/*original_flags =*/ in.getnum();
/* expansion */ in.getnum();
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.getword( 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 *repop = new Repop;
if( ( err = repop->readFrom( in ) ) == 0 )
addRepop( repop );
else
{
Cout << "Invalid repop code, skipping.\n";
delete repop;
}
}
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;
default:
return 1;
}
}
return 1;
}
int Room::writeTo( OutFile &out ) const
{
String str;
if( !out )
return 0;
out << '{' << endl;
// out << index << endl;
out << name << '~' << endl;
out << desc << '~' << endl;
out << 0 << ' ' << 0 << endl;
/* write the doors */
for( int i = 0; i < MAX_DIR; i++ )
{
if( exits[i] )
{
out << "D{" << i << " "
<< exits[i]->getName() << " "
<< exits[i]->original_flags[1] << " "
<< exits[i]->key.asString() << " "
<< exits[i]->room_index.asString() << " "
<< exits[i]->desc << "~}\n";
}
}
// end doors
// write the repops
Repop *repop;
repops.reset();
while( (repop = repops.peek()) )
{
repops.next();
out<<"R";
repop->writeTo( out );
out<<endl;
}
out << '}' << 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 )
error( "Room::toRoom - exit points to NULL room." );
return exits[ dir ]->room_ptr;
}
void Room::addExit( Exit *exit, int dir )
{
if( dir < MAX_DIR )
exits[ dir ] = exit;
}
void Room::deleteExit( int dir )
{
if( dir >= 0 && dir < MAX_DIR )
{
delete exits[ dir ];
exits[ dir ] = 0;
}
}
Char *Room::getChar( const String & str )
{
Char *ch;
chars.reset();
while( ( ch = chars.peek() ) )
{
chars.next();
if( ch->isName( str ) )
break;
}
return ch;
}
PC *Room::getPC( const String & str )
{
Char *ch;
chars.reset();
while( ( ch = chars.peek() ) )
{
chars.next();
if( ch->isNPC )
continue;
if( ch->isName( str ) )
break;
}
return (PC*)ch;
}
NPC *Room::getNPC( const String & str )
{
Char *ch;
chars.reset();
while( ( ch = chars.peek() ) )
{
chars.next();
if( ch->isPC )
continue;
if( ch->isName( str ) )
return (NPC*)ch;
}
return 0;
}
Object * Room::getObj( const String & str )
{
Object *obj;
inv.reset();
while( ( obj = inv.peek() ) )
{
inv.next();
if( obj->isName( str ) )
return obj;
}
return 0;
}
void Exit::reset( )
{
flags = original_flags;
}
void Exit::hardLink( )
{
room_ptr = lookupRoom( room_index );
}