/* ....[@@@..[@@@..............[@.................. 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 ); }