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