/* ....[@@@..[@@@..............[@.................. 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 ------------------------------------------------------------------------------ object.cc */ #include "io.h" #include "string.h" #include "index.h" #include "indexable.h" #include "room.h" #include "llist.h" #include "repop.h" #include "bit.h" #include "spell.h" #include "affect.h" #include "combat.h" #include "char.h" #include "area.h" #include "object.h" #include "global.h" // All wear locations are not possible for example // if you are double wielding, then no light or shield const bitType wear_bit_list[] = { {"undefined", WEAR_NONE }, {"head", WEAR_HEAD }, {"neck", WEAR_NECK }, {"back", WEAR_BACK }, {"body", WEAR_BODY }, {"arms", WEAR_ARMS }, {"wrist", WEAR_WRIST }, {"hands", WEAR_HANDS }, {"held", WEAR_HOLD }, {"finger", WEAR_FINGER }, {"waist", WEAR_WAIST }, {"legs", WEAR_LEGS }, {"feet", WEAR_FEET }, {"shield", WEAR_SHIELD }, {"wield", WEAR_WIELD }, {0, 0} }; const bitType obj_bit_list[] = { {"unused", OBJ_UNUSED }, {"invisible", OBJ_INVISIBLE }, {"hidden", OBJ_HIDDEN }, {"notake", OBJ_NOTAKE }, {"nodrop", OBJ_NODROP }, {"edible", OBJ_EDIBLE }, {"nosave", OBJ_NOSAVE }, {"good", OBJ_GOOD }, {"evil", OBJ_EVIL }, {"antigood", OBJ_ANTIGOOD }, {"antievil", OBJ_ANTIEVIL }, {"antineutral", OBJ_ANTINEUTRAL }, {"magic", OBJ_MAGIC }, {"glow", OBJ_GLOW }, {"sanctuary", OBJ_SANCTUARY }, {"poisoned", OBJ_POISONED }, {0, 0} }; const bitType obj_type_list[] = { {"unused", ITEM_UNUSED }, {"trash", ITEM_TRASH }, {"jewel", ITEM_JEWEL }, {"gold", ITEM_GOLD }, {"container", ITEM_CONTAINER }, {"liquid-container", ITEM_LIQUID_CONTAINER }, {"armor", ITEM_ARMOR }, {"cloth", ITEM_CLOTH }, {"weapon", ITEM_WEAPON }, {"staff", ITEM_STAFF }, {"wand", ITEM_WAND }, {"orb", ITEM_ORB }, {"corpse", ITEM_CORPSE }, {"energy", ITEM_ENERGY }, {"scroll", ITEM_SCROLL }, {"potion", ITEM_POTION }, {"food", ITEM_FOOD }, {"key", ITEM_KEY }, {"staff", ITEM_STAFF }, {0, 0} }; const bitType dam_types[] = { {"none", NONE }, {"punch", PUNCH }, {"kick", KICK }, {"crush", CRUSH }, {"pierce", PIERCE }, {"slash", SLASH }, {"whip", WHIP }, {0, 0 } }; const char *wear_pos_list[] = { "undefined", "<worn on head>", "<worn on face>", "<worn on neck>", "<worn on neck>", "<worn on back>", "<worn on body>", "<worn on arms>", "<worn on left wrist>", "<worn on right wrist>", "<worn on hands>", "<held in left>", "<held in right>", "<worn on left finger>", "<worn on right finger>", "<worn on waist>", "<worn on legs>", "<worn on feet>", "<shield left>", "<shield right>", "<wielded left>", "<wielded right>", "" }; Object::~Object() { // Extract and destroy all objects inside this one // Add a bug check here later if non-containers have inv Object *obj; while( ( obj = inv.remove() ) ) { // remove from global list obj->fromWorld(); // deleting obj destroys any objects inside obj if it is container delete obj; } if( repop ) repop->setPtr( 0 ); } // Remove all links between object and world // After extract we can safely do anything with object that // we want including deleting it. This could all be in the // destructor but my opinion on self-removing destructor methods // is they cause more bugs than they are worth. void Object::extract() { if( in_room ) in_room->inv.remove( this ); else if( in_obj ) in_obj->rmObjInv( this ); else if( in_char ) in_char->inv.remove( this ); objects.remove( this ); } int Object::readProtoFrom( InFile &in ) { char buf[ BUF ]; if( *in.getword( buf ) != '{' ) { in.error( "Object::ReadFromFile() - expected '{' not found." ); } setName( in.getstring( buf ) ); setShort( in.getstring( buf ) ); setLong( in.getstring( buf ) ); in.getword( buf ); obj_type = getObjType( buf ); // The items are grouped by type for future expansion // because I know they will begin to differ soon. switch( obj_type ) { case ITEM_KEY: default: value[ 0 ] = in.getnum(); value[ 1 ] = in.getnum(); value[ 2 ] = in.getnum(); value[ 3 ] = in.getnum(); value[ 4 ] = in.getnum(); value[ 5 ] = in.getnum(); value[ 6 ] = in.getnum(); break; case ITEM_WEAPON: in.getword( buf ); value[0] = getDamType( buf ); if( value[0] == NONE ) { Cout << "BUG: Invalid dam type " << buf << " for " << getShort() << endl; } value[ 1 ] = in.getnum(); value[ 2 ] = in.getnum(); value[ 3 ] = in.getnum(); value[ 4 ] = in.getnum(); value[ 5 ] = in.getnum(); value[ 6 ] = in.getnum(); break; case ITEM_FOOD: case ITEM_ARMOR: value[ 0 ] = in.getnum(); value[ 1 ] = in.getnum(); value[ 2 ] = in.getnum(); value[ 3 ] = in.getnum(); value[ 4 ] = in.getnum(); value[ 5 ] = in.getnum(); value[ 6 ] = in.getnum(); break; case ITEM_ORB: case ITEM_WAND: case ITEM_STAFF: value[ 0 ] = in.getnum(); value[ 1 ] = in.getnum(); value[ 2 ] = in.getnum(); value[ 3 ] = in.getnum(); value[ 4 ] = in.getnum(); value[ 5 ] = in.getnum(); value[ 6 ] = in.getnum(); break; case ITEM_SCROLL: value[ 0 ] = in.getnum(); value[ 1 ] = in.getnum(); value[ 2 ] = in.getnum(); value[ 3 ] = in.getnum(); value[ 4 ] = in.getnum(); value[ 5 ] = in.getnum(); in.getstring( buf ); value[ 6 ] = 0; // (long) languageLookup( in.getstring( buf ) ); break; case ITEM_POTION: value[ 0 ] = in.getnum(); value[ 1 ] = in.getnum(); value[ 2 ] = in.getnum(); value[ 3 ] = in.getnum(); value[ 4 ] = in.getnum(); value[ 5 ] = in.getnum(); value[ 6 ] = in.getnum(); break; } in.getbitfield( obj_bits ); in.getbitfield( wear_bits ); in.getnum(); // security for now cost = in.getnum(); weight = in.getnum(); /*level = */ in.getnum(); Spell *sp; Modifier *mod; Affect *aff; int modAmt; int modLoc; // int spellLevel; while( 1 ) { switch( *in.getword( buf ) ) { case '}': return 0; // Affect case 'A': in.getword( buf ); in.getstring( buf ); in.getnum(); in.getword( buf ); continue; // Spell case 'S': in.getword( buf ); in.getstring( buf ); /* spellLevel = */ in.getnum(); sp = (Spell *)lookupSpell( buf ); spells.add( sp ); in.getword( buf ); continue; // Modifier case 'M': in.getword( buf ); in.getword( buf ); modAmt = in.getnum(); if( ( modLoc = getModType( buf ) ) != -1 ) { mod = new Modifier( modLoc, modAmt ); mods.addTop( mod ); } in.getword( buf ); continue; default: in.error( "Object::ReadFromFile() - expected '}' at end of Object." ); } } return 0; } // Differs from readProtoFrom() // readFrom reads a persistent object with such values // as timer, charges, etc. which aren't stored in the prototype int Object::readFrom( InFile &in ) { char buf[ BUF ]; if( *in.getword( buf ) != '{' ) { in.error( "Object::ReadFromFile() - expected '{' not found." ); } setName( in.getstring( buf ) ); setShort( in.getstring( buf ) ); setLong( in.getstring( buf ) ); in.getword( buf ); obj_type = getObjType( buf ); // The items are grouped by type for future expansion // because I know they will begin to differ soon. switch( obj_type ) { case ITEM_KEY: default: value[ 0 ] = in.getnum(); value[ 1 ] = in.getnum(); value[ 2 ] = in.getnum(); value[ 3 ] = in.getnum(); value[ 4 ] = in.getnum(); value[ 5 ] = in.getnum(); value[ 6 ] = in.getnum(); break; case ITEM_WEAPON: in.getword( buf ); value[0] = getDamType( buf ); if( value[0] == NONE ) { Cout << "BUG: Invalid dam type " << buf << " for " << getShort() << endl; } value[ 1 ] = in.getnum(); value[ 2 ] = in.getnum(); value[ 3 ] = in.getnum(); value[ 4 ] = in.getnum(); value[ 5 ] = in.getnum(); value[ 6 ] = in.getnum(); break; case ITEM_FOOD: case ITEM_ARMOR: value[ 0 ] = in.getnum(); value[ 1 ] = in.getnum(); value[ 2 ] = in.getnum(); value[ 3 ] = in.getnum(); value[ 4 ] = in.getnum(); value[ 5 ] = in.getnum(); value[ 6 ] = in.getnum(); break; case ITEM_ORB: case ITEM_WAND: case ITEM_STAFF: value[ 0 ] = in.getnum(); value[ 1 ] = in.getnum(); value[ 2 ] = in.getnum(); value[ 3 ] = in.getnum(); value[ 4 ] = in.getnum(); value[ 5 ] = in.getnum(); value[ 6 ] = in.getnum(); break; case ITEM_SCROLL: value[ 0 ] = in.getnum(); value[ 1 ] = in.getnum(); value[ 2 ] = in.getnum(); value[ 3 ] = in.getnum(); value[ 4 ] = in.getnum(); value[ 5 ] = in.getnum(); in.getstring( buf ); value[ 6 ] = 0; // (long) languageLookup( in.getstring( buf ) ); break; case ITEM_POTION: value[ 0 ] = in.getnum(); value[ 1 ] = in.getnum(); value[ 2 ] = in.getnum(); value[ 3 ] = in.getnum(); value[ 4 ] = in.getnum(); value[ 5 ] = in.getnum(); value[ 6 ] = in.getnum(); break; } in.getbitfield( obj_bits ); in.getbitfield( wear_bits ); in.getnum(); // security for now cost = in.getnum(); weight = in.getnum(); /*level = */ in.getnum(); // Persistent values timer = in.getnum(); wear_pos = in.getnum(); // end Spell *sp; Modifier *mod; Affect *aff; int modAmt; int modLoc; // int spellLevel; while( 1 ) { switch( *in.getword( buf ) ) { case '}': return 0; // Affect case 'A': in.getword( buf ); in.getstring( buf ); in.getnum(); in.getword( buf ); continue; // Spell case 'S': in.getword( buf ); in.getstring( buf ); /* spellLevel = */ in.getnum(); sp = (Spell *)lookupSpell( buf ); spells.add( sp ); in.getword( buf ); continue; // Modifier case 'M': in.getword( buf ); in.getword( buf ); modAmt = in.getnum(); if( ( modLoc = getModType( buf ) ) != -1 ) { mod = new Modifier( modLoc, modAmt ); mods.addTop( mod ); } in.getword( buf ); continue; default: in.error( "Object::ReadFromFile() - expected '}' at end of Object." ); } } return 0; } int Object::writeProtoTo( OutFile &out ) const { out << "{" << endl; out << name << TERM_CHAR << endl; out << shortdesc << TERM_CHAR << endl; out << longdesc << TERM_CHAR << endl; out << getObjTypeName( obj_type ) << endl; switch( obj_type ) { case ITEM_KEY: default: out << value[ 0 ] << ' ' << value[ 1 ] << ' ' << value[ 2 ] << ' ' << value[ 3 ] << ' ' << value[ 4 ] << ' ' << value[ 5 ] << ' ' << value[ 6 ] << ' ' << endl; break; case ITEM_WEAPON: out << getDamTypeName( value[ 0 ] ) << ' ' << value[ 1 ] << ' ' << value[ 2 ] << ' ' << value[ 3 ] << ' ' << value[ 4 ] << ' ' << value[ 5 ] << ' ' << value[ 6 ] << ' ' << endl; break; case ITEM_FOOD: case ITEM_ARMOR: out << value[ 0 ] << ' ' << value[ 1 ] << ' ' << value[ 2 ] << ' ' << value[ 3 ] << ' ' << value[ 4 ] << ' ' << value[ 5 ] << ' ' << value[ 6 ] << ' ' << endl; break; case ITEM_ORB: case ITEM_WAND: case ITEM_STAFF: out << value[ 0 ] << ' ' << value[ 1 ] << ' ' << value[ 2 ] << ' ' << value[ 3 ] << ' ' << value[ 4 ] << ' ' << value[ 5 ] << ' ' << value[ 6 ] << ' ' << endl; break; case ITEM_SCROLL: out << value[ 0 ] << ' ' << value[ 1 ] << ' ' << value[ 2 ] << ' ' << value[ 3 ] << ' ' << value[ 4 ] << ' ' << value[ 5 ] << ' ' << "none~" // value[ 6 ] << endl; break; case ITEM_POTION: out << value[ 0 ] << ' ' << value[ 1 ] << ' ' << value[ 2 ] << ' ' << value[ 3 ] << ' ' << value[ 4 ] << ' ' << value[ 5 ] << ' ' << value[ 6 ] << ' ' << endl; break; } out.putbitfield( obj_bits, MAX_OBJ_BIT_FIELDS ); out << endl; out.putbitfield( wear_bits, MAX_WEAR_BIT_FIELDS ); out << endl; out << 1 /* security */ << " " << cost << " " << weight << " " << 1 /*level*/ << " " << endl; Spell *sp; spells.reset(); while( ( sp = spells.peek() ) ) { spells.next(); out << "S{" << sp->getName() << "~}\n"; } Modifier *mod; mods.reset(); while( ( mod = mods.peek() ) ) { mods.next(); out << "M{" << mod->getName() << ' ' << mod->getAmt() << "}\n"; } Affect *aff; affects.reset(); while( ( aff = affects.peek() ) ) { affects.next(); out << "A{~ -1}\n"; } out << "}" << endl; return 1; } // Write a persistent object to file, use readFrom() // to reload object, not readProtoFrom() int Object::writeTo( OutFile &out ) const { out << "{" << endl; out << name << TERM_CHAR << endl; out << shortdesc << TERM_CHAR << endl; out << longdesc << TERM_CHAR << endl; out << getObjTypeName( obj_type ) << endl; switch( obj_type ) { case ITEM_KEY: default: out << value[ 0 ] << ' ' << value[ 1 ] << ' ' << value[ 2 ] << ' ' << value[ 3 ] << ' ' << value[ 4 ] << ' ' << value[ 5 ] << ' ' << value[ 6 ] << ' ' << endl; break; case ITEM_WEAPON: out << getDamTypeName( value[ 0 ] ) << ' ' << value[ 1 ] << ' ' << value[ 2 ] << ' ' << value[ 3 ] << ' ' << value[ 4 ] << ' ' << value[ 5 ] << ' ' << value[ 6 ] << ' ' << endl; break; case ITEM_FOOD: case ITEM_ARMOR: out << value[ 0 ] << ' ' << value[ 1 ] << ' ' << value[ 2 ] << ' ' << value[ 3 ] << ' ' << value[ 4 ] << ' ' << value[ 5 ] << ' ' << value[ 6 ] << ' ' << endl; break; case ITEM_ORB: case ITEM_WAND: case ITEM_STAFF: out << value[ 0 ] << ' ' << value[ 1 ] << ' ' << value[ 2 ] << ' ' << value[ 3 ] << ' ' << value[ 4 ] << ' ' << value[ 5 ] << ' ' << value[ 6 ] << ' ' << endl; break; case ITEM_SCROLL: out << value[ 0 ] << ' ' << value[ 1 ] << ' ' << value[ 2 ] << ' ' << value[ 3 ] << ' ' << value[ 4 ] << ' ' << value[ 5 ] << ' ' << "none~" // value[ 6 ] << endl; break; case ITEM_POTION: out << value[ 0 ] << ' ' << value[ 1 ] << ' ' << value[ 2 ] << ' ' << value[ 3 ] << ' ' << value[ 4 ] << ' ' << value[ 5 ] << ' ' << value[ 6 ] << ' ' << endl; break; } out.putbitfield( obj_bits, MAX_OBJ_BIT_FIELDS ); out << endl; out.putbitfield( wear_bits, MAX_WEAR_BIT_FIELDS ); out << endl; out << 1 /* security */ << " " << cost << " " << weight << " " << 1 /*level*/ << " " << endl; // Write persistent data out << timer << ' ' << wear_pos << endl; // end Spell *sp; spells.reset(); while( ( sp = spells.peek() ) ) { spells.next(); out << "S{" << sp->getName() << "~}\n"; } Modifier *mod; mods.reset(); while( ( mod = mods.peek() ) ) { mods.next(); out << "M{" << mod->getName() << ' ' << mod->getAmt() << "}\n"; } Affect *aff; affects.reset(); while( ( aff = affects.peek() ) ) { affects.next(); out << "A{~ -1}\n"; } out << "}" << endl; return 1; } const char *Object::showWearBits() { static char buf[ BUF ]; *buf = '\0'; for( int i = 0; wear_bit_list[ i ].name; i++ ) { if( IS_SET( wear_bits, wear_bit_list[ i ].val ) ) sprintf( buf+strlen(buf), "(%s)", wear_bit_list[ i ].name ); } return buf; } const char *Object::typeName() { return getObjTypeName( obj_type ); } int Object::getType() { return obj_type; } void Object::setType( int type ) { obj_type = type; } bool Object::isWearable() { for( int i = 0; i < MAX_WEAR_BIT_FIELDS; i++ ) if( wear_bits[ i ] ) return true; return false; } int Object::wearBit( int bit ) { return IS_SET( wear_bits, bit ); } void Object::toggleWearBit( int bit ) { TOGGLE_BIT( wear_bits, bit ); } void Object::setWearPos( int pos ) { if( pos <= EQ_MAX ) wear_pos = pos; } int Object::wearPos() { return wear_pos; } int Object::worn() { return wearPos(); } void Object::fromChar() { #ifdef DEBUG if( !inChar() ) { Cout << "BUG: Object::fromChar - not carried.\n"; return; } #endif in_char = 0; } void Object::toChar( Char *ch ) { #ifdef DEBUG if( inRoom() || inChar() || inObj() ) { Cout << "BUG: Object::toChar - already owned.\n"; return; } #endif in_char = ch; } void Object::fromRoom() { #ifdef DEBUG if( !inRoom() ) { Cout << "BUG: Object::fromRoom - not in room.\n"; return; } #endif in_room = 0; } void Object::toRoom( Room *toRoom ) { #ifdef DEBUG if( inRoom() || inChar() || inObj() ) { Cout << "BUG: Object::toRoom - already owned.\n"; return; } #endif in_room = toRoom; } // Leave to caller to assert obj isObject void Object::toObj( Object * obj ) { #ifdef DEBUG if( inRoom() || inChar() || inObj() ) { Cout << "BUG: Object::toObj - already owned.\n"; return; } #endif in_obj = obj; } void Object::fromObj() { #ifdef DEBUG if( !inObj() ) { Cout << "BUG: Object::fromObj - not in object.\n"; return; } #endif in_obj = 0; } void Object::toWorld() { objects.add( this ); } void Object::fromWorld() { objects.remove( this ); } void Object::addObjInv( Object * obj ) { obj->toObj( this ); weight += obj->getWeight(); inv.add( obj ); } Object * Object::getObjInv( const char * str ) { Object * obj; inv.reset(); while( ( obj = inv.peek() ) ) { if( obj->isName( str ) ) return obj; } return 0; } Object * Object::getObjInv( const String & str ) { Object * obj; inv.reset(); while( ( obj = inv.peek() ) ) { if( obj->isName( str ) ) return obj; } return 0; } void Object::rmObjInv( Object * obj ) { obj->fromObj(); weight -= obj->getWeight(); inv.remove( obj ); } void Object::resetInv() { inv.reset(); } Object * Object::getCurObjInv() { return inv.peek(); } Object * Object::getNextObjInv() { Object * obj = inv.peek(); inv.next(); return obj; } int Object::getWeightCap() { if( obj_type != ITEM_CONTAINER && obj_type != ITEM_LIQUID_CONTAINER ) return 0; return value[0]; } int Object::getVolCap() { if( obj_type != ITEM_CONTAINER && obj_type != ITEM_LIQUID_CONTAINER ) return 0; return value[1]; } int Object::getWeightContained() { if( obj_type != ITEM_CONTAINER && obj_type != ITEM_LIQUID_CONTAINER ) return 0; return value[2]; } int Object::getVolContained() { if( obj_type != ITEM_CONTAINER && obj_type != ITEM_LIQUID_CONTAINER ) return 0; return value[3]; } Spell * Object::getSpell1() { spells.reset(); if( spells.peek() ) return spells.peek(); return (Spell *)spellNone; } Spell * Object::getSpell2() { spells.reset(); if( spells.peek() ) spells.next(); if( spells.peek() ) return spells.peek(); return (Spell*)spellNone; } Spell * Object::getSpell3() { return (Spell*)spellNone; } Spell * Object::getSpell4() { return (Spell*)spellNone; } void Object::cast( Thing * target ) { Spell * sp; spells.reset(); while( ( sp = spells.peek() ) ) { spells.next(); sp->cast( this, target ); } } const char *getObjBitName( int flag ) { for( int i = 1; obj_bit_list[i].name; i++ ) { if( flag == obj_bit_list[i].val ) return obj_bit_list[i].name; } return ""; } int getObjBit( const char *str ) { int len = strlen( str ); for( int i = 1; obj_bit_list[i].name; i++ ) { if( *str != *obj_bit_list[i].name ) continue; if( !strncmp( str, obj_bit_list[i].name, len )) return obj_bit_list[i].val; } return 0; } const char *getObjTypeName( int type ) { for( int i = 1; obj_type_list[i].name; i++ ) { if( type == obj_type_list[i].val ) return obj_type_list[i].name; } return "undefined"; } int getObjType( const char *str ) { int len = strlen( str ); for( int i = 1; obj_type_list[i].name; i++ ) { if( *str != *obj_type_list[i].name ) continue; if( !strncmp( str, obj_type_list[i].name, len )) return obj_type_list[i].val; } return -1; } const char *getWearPosName( int pos ) { return wear_pos_list[pos]; } const char *getWearBitName( int bit ) { for( int i = 1; wear_bit_list[i].name; i++ ) { if( bit == wear_bit_list[i].val ) return wear_bit_list[i].name; } return "undefined"; } int getWearBit( const char *name ) { int len = strlen( name ); for( int i=1; wear_bit_list[i].name; i++ ) { if( *wear_bit_list[i].name != *name ) continue; if( !strncmp( name, wear_bit_list[i].name, len ) ) return wear_bit_list[i].val; } return 0; } Object * lookupObj( const Index & x ) { Object *obj; Area *area; LList<Area> tlist = areas; tlist.reset(); while( ( area = tlist.peek() ) ) { tlist.next(); // No scope means global search if( !x.getScope() ) if( ( obj = area->lookupObj( x ) ) ) return obj; if( area->getKey() == x.getScope() ) break; } if( area ) return area->lookupObj( x ); return 0; } Object * lookupObj( const String & x ) { return lookupObj( Index( x ) ); } Object * lookupObj( const char * x ) { return lookupObj( Index( x ) ); } const char * damTypeName( int val ) { return getBitName( dam_types, val ); }