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