mud++0.35/etc/
mud++0.35/etc/guilds/
mud++0.35/help/propert/
mud++0.35/mudC/
mud++0.35/player/
mud++0.35/src/interface/
mud++0.35/src/os/cygwin32/
mud++0.35/src/os/win32/
mud++0.35/src/os/win32/bcppbuilder/
mud++0.35/src/osaddon/
mud++0.35/src/util/
/*
....[@@@..[@@@..............[@.................. 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
------------------------------------------------------------------------------
object.cc
*/

#include "config.h"
#include "io.h"
#include "string.h"
#include "index.h"
#include "hash.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[] = 
{
	{0,				0			},
	{"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[] =
{
	{0,					0		},
	{"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		}
};

// I have disabled unused objects for now - Artur
const bitType obj_type_list[] =
{
	{0,					0			},
//	{"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		},
//	{"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		},
//	{"compass",			ITEM_COMPASS	},
	{0,				0			}
};


const bitType dam_types[] =
{
	{0,		0	},
	{"punch",	PUNCH	},
	{"kick",	KICK	},
	{"crush",	CRUSH	},
	{"pierce",	PIERCE	},
	{"slash",	SLASH	},
	{"whip",	WHIP	},
	{"claw",	CLAW	},
	{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>",
	0
};

int Object::total_count = 0;

Object::~Object()
{
	total_count--;
	// Extract and destroy all objects inside this one
	// Add a bug check here later if non-containers have inv

	// deleting obj destroys any objects inside obj if it is container
	Object *obj;
	while( ( obj = inv.remove() ) )
	{
		obj->extract();
		obj->fordelete();
	}
}


// Remove all links between object and world, repop, etc.
// 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 );
	if( repop )
		repop->setPtr( 0 );
	objects.remove( this );
}


void Object::setObjBit( const String & x )
{
	int bit = getObjBit( x );
	if( bit )
		SET_BIT( obj_bits, bit );
}


void Object::clrObjBit( const String & x )
{
	int bit = getObjBit( x );
	if( bit )
		CLR_BIT( obj_bits, bit );
}


void Object::toggleObjBit( const String & x )
{
	int bit = getObjBit( x );
	if( bit )
		TOGGLE_BIT( obj_bits, bit );
}


void Object::setWearBit( const String & x )
{
	int bit = getWearBit( x );
	if( bit )
		SET_BIT( wear_bits, bit );
}


void Object::clrWearBit( const String & x )
{
	int bit = getWearBit( x );
	if( bit )
		CLR_BIT( wear_bits, bit );
}


void Object::toggleWearBit( const String & x )
{
	int bit = getWearBit( x );
	if( bit )
		TOGGLE_BIT( wear_bits, bit );
}


// There is no longer Proto and persistent write & read
// They are in same fun, prototype objects just do not have most
// of transient data

int Object::readFrom( StaticInput &in )
{
	String str;
	char buf[ BUF ];
	Object * obj;

	if( *in.getword( buf ) != '{' )
		in.error( "Object::ReadFromFile() - expected '{' not found." );

	setName( in.getstring( buf ) );
	setShort( in.getstring( buf ) );
	setLong( in.getstring( buf ) );

//	str = in.getword( buf );
//	obj_type = lookupObjType( str );

	in.getword( buf );
	size = lookupSize( buf );
	in.getnum(); // spare
	in.getnum(); // spare

	readFromTypeSpecific(in);

	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

	const SpellType *stp;
	Spell *sp;
	Modifier *mod;
	Affect *aff;
	struct TriggerData * td;
	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();
						if( ( stp = lookupSpell( buf ) ) )
						{
							sp = new Spell( stp, spellLevel );
							spell_list.add( sp );
						}
						in.getword( buf );
						continue;

			// Modifier
			case 'M':	in.getword( buf );
						in.getword( buf );
						modAmt = in.getnum();
						if( ( modLoc = lookupModType( buf ) ) != -1 )
						{
							mod = new Modifier( modLoc, modAmt );
							mod_list.addTop( mod );
						}
						in.getword( buf );
						continue;
			
			case 'O':	obj = Object::createObject( lookupObjType(in.getword(buf)) );
						if ( obj == NULL )
							in.error("Wrong object type.\n\r");
						obj->readFrom( in );
						addObjInv( obj );
						continue;	

			// add check for 'Trig' not 'T'
			case 'T':	in.getword(buf);
						td = new (struct TriggerData);
						td->type = lookupBit( otrig_types, in.getword( buf ) );
						td->fun = lookupOtrig( td->type, in.getword(buf) );
						if ( (td->type == 0) || (td->fun == NULL) )
						{
							delete td;
							in.error("Object::ReadFromFile - unrecognized trigger data");
							continue;
						}
						addTrigger(td);
						in.getword(buf); // '}'
						continue;
				
		
			default:
				Cout << "[" << buf << "]\n";
				in.error( "Object::ReadFromFile() - expected '}' at end of Object." );
		}
	}

	return 0; 
}


int Object::writeTo( Output &outf ) const
{
	outf << "{" << endl;
	outf << name << TERM_CHAR << endl;
	outf << shortdesc << TERM_CHAR << endl;
	outf << longdesc << TERM_CHAR << endl;
//	outf << lookupObjTypeName( obj_type )  << endl;
	outf << lookupSizeName( size ) <<' '<< 0 <<' '<< 0 << endl;
	

	writeToTypeSpecific(outf);
	outf.putbitfield( obj_bits, MAX_OBJ_BIT_FIELDS );
	outf << endl;
	outf.putbitfield( wear_bits, MAX_WEAR_BIT_FIELDS );
	outf << endl;

	outf << 1 /* security */ << " " << cost      << " " << weight << " "
		<< 1 /*level*/    << " " << endl;

	// Write persistent data
	outf << timer << ' ' << wear_pos << endl;
	// end

	Spell *sp;
	for_each( spell_list, sp )
		outf << "S{" << sp->getName() << "~ " << sp->getLevel() << "}\n"; 

	Modifier *mod;
	for_each( mod_list, mod )
		outf << "M{" << mod->getName() << ' ' << mod->getAmt() << "}\n";

	Affect *aff;
	for_each( aff_list, aff )
		outf << "A{~ -1}\n";

	struct TriggerData * tg;
	for_each( triggers, tg )
		outf << "Trig{ " << lookupBitName(otrig_types, tg->type) <<
		" " << lookupOtrigName(tg->type, tg->fun) << "}" << endl;


	Object * obj;
	for_each( inv, obj )
	{
		outf << "O " << obj->typeName() << endl;
		obj->writeTo( outf );
	}

	outf << "}" << endl;
	return 1;
}


bool Object::isWearable() const
{
	for( int i = 0; i < MAX_WEAR_BIT_FIELDS; i++ )
		if( wear_bits[ i ] )
			return true;

	return false;
}


void Object::setWearPos( int pos )
{
	if( pos <= EQ_MAX ) 
		wear_pos = pos;
}


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 * x )
{
#ifdef DEBUG
	if( inRoom() || inChar() || inObj() )
	{
		Cout << "BUG: Object::toRoom - already owned.\n";
		return;    
	}
#endif
	in_room = x;
}


// 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::addObjInv( Object * obj )
{
	obj->toObj( this );
	weight += obj->getWeight();
	inv.add( obj );
}


Object * Object::getObjInv( const String & str )
{
	Object * obj;
	countedThing cI(str);
	parseCompositeName( obj, inv, cI );
	return obj;
}


// Get first object of type otype (OBJ_FOOD, etc.)
Object * Object::getObjInv( int otype )
{
	Object * obj;
	for_each( inv, obj )
		if (obj->isType(otype))
			break;
	return obj;
}


Object * Object::getObjInv( countedThing & cI )
{
	Object * obj;
	parseCompositeName( obj, inv, cI );
	return obj;
}


void Object::rmObjInv( Object * obj )
{
	obj->fromObj();
	weight -= obj->getWeight();
	inv.remove( obj );
}


Spell * Object::getSpell1()
{
	spell_list.reset();
	if( spell_list.peek() )
		return spell_list.peek();
	return 0;
}


Spell * Object::getSpell2()
{
	spell_list.reset();
	if( spell_list.peek() )
		spell_list.next();
	if( spell_list.peek() )
		return spell_list.peek();
	return 0;
}


Spell * Object::getSpell3()
{
	return 0;
}


Spell * Object::getSpell4()
{
	return 0;
}


void Object::cast( MudObject * target )
{
	Spell * sp;
	for_each( spell_list, sp )
		if( sp->objCanCast() )
			sp->cast( this, target );
}

void Object::reportVals( String & ) const
{
	return;
}

char * Object::setVals( const String &  )
{
	return "No such value.\n\r";
}

void Object::describeItself( String & str )
{
	int i;
	String str2;
	String str3;
	struct TriggerData * td;

	for( i = 1; obj_bit_list[i].name; i++ )
		if( objBitSet( obj_bit_list[i].val ) )
			str2 << obj_bit_list[i].name << ' ';

	for( i = 1; wear_bit_list[i].name; i++ )
		if( wearBitSet( wear_bit_list[i].val ) )
			str3 << wear_bit_list[i].name << ' ';

	str << "OBJECT\n\r"
		<< "Keywords: " << getName() << "\n\r"
		<< "Shortdesc: " << getShort() << "\n\r"
		<< "Longdesc: " << getLong() << "\n\r"
		<< "Type: " << typeName() << "\n\r"
		<< "Size: " << lookupSizeName( getSize() ) << "\n\r"
		<< "Cost: " << getCost() << "\n\r"
		<< "Obj-bits: " << str2 << "\n\r"
		<< "Wear-bits: " << str3 << "\n\r";
	if( getSpell1() )
		str << "Spell1: " << getSpell1()->getName() << "\n\r";
	if( getSpell2() )
		str << "Spell2: " << getSpell2()->getName() << "\n\r";
	if( getSpell3() )
		str << "Spell3: " << getSpell3()->getName() << "\n\r";
	if( getSpell4() )
		str << "Spell4: " << getSpell4()->getName() << "\n\r";

	reportVals(str);

	for_each( triggers, td)
	{
		str << "Trigger " << lookupBitName( otrig_types, td->type) <<
			" " << lookupOtrigName( td->type, td->fun ) << "\n\r";
	}
}


const char *getWearPosName( int pos )
{
	return wear_pos_list[pos];
}


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( ! (bool) x.getScope() )
			if( ( obj = area->lookupObj( x.getKey() ) ) )
				return obj;

		if( area->getKey() == x.getScope() )
			break;
	}

	if( area )
		return area->lookupObj( x.getKey() );
	return 0;
}


Object * lookupObj( const String & x )
{
	return lookupObj( Index( x ) );
}


Object * lookupObj( const char * x )
{
	return lookupObj( Index( x ) );
}



bool Object::TgCreated( Room * where, Repop * by )
{
	if ( !IS_SET(trigger_bits, OTRIG_CREATED) )
		return false;

	return ((otrigT_created)findTrigger(OTRIG_CREATED )) 
  						(this, where, by);
	
}

bool Object::TgUpdate()
{
	if ( !IS_SET(trigger_bits, OTRIG_UPDATE) )
		return false;

	return ((otrigT_update)findTrigger(OTRIG_UPDATE )) 
  						(this);
	
}

bool Object::TgTimerOut()
{
	if ( !IS_SET(trigger_bits, OTRIG_TIMER_OUT) )
		return false;

	return ((otrigT_timer_out)findTrigger(OTRIG_TIMER_OUT )) 
  						(this);
	
}

bool Object::TgPicked( Char *caller, Room * from )
{
	if ( !IS_SET(trigger_bits, OTRIG_PICKED) )
		return false;

	return ((otrigT_picked)findTrigger(OTRIG_PICKED )) 
  						(this,caller,from);
	
}

bool Object::TgDropped( Char *caller, Room * to )
{
	if ( !IS_SET(trigger_bits, OTRIG_DROPPED) )
		return false;

	return ((otrigT_dropped)findTrigger(OTRIG_DROPPED )) 
  						(this,caller,to);
}



bool Object::TgGiven(Char * caller, Char * to)
{
	if ( !IS_SET(trigger_bits, OTRIG_GIVEN) )
		return false;

	return ((otrigT_given)findTrigger(OTRIG_GIVEN )) 
  						(this,caller,to);
	
}

bool Object::TgWorn(Char * caller)
{
	if ( !IS_SET(trigger_bits, OTRIG_WORN) )
		return false;

	return ((otrigT_worn)findTrigger(OTRIG_WORN )) 
  						(this,caller);
	
}

bool Object::TgRemoved(Char * caller)
{
	if ( !IS_SET(trigger_bits, OTRIG_REMOVED) )
		return false;

	return ((otrigT_removed)findTrigger(OTRIG_REMOVED)) 
  						(this,caller);
	
}

bool Object::TgUsed(Char * caller, MudObject * target)
{
	if ( !IS_SET(trigger_bits, OTRIG_USED) )
		return false;

	return ((otrigT_used)findTrigger(OTRIG_USED)) 
  						(this,caller,target);
}


bool Object::TgLookedAt( Char * caller )
{
	if ( !IS_SET(trigger_bits, OTRIG_LOOKED_AT) )
		return false;

	return ((otrigT_looked_at)findTrigger(OTRIG_LOOKED_AT )) 
  						(this, caller);
	
}

bool Object::TgItemPutInto( Char * caller, Object * what )
{
	if ( !IS_SET(trigger_bits, OTRIG_ITEM_PUT_INTO) )
		return false;

	return ((otrigT_item_put_into)findTrigger(OTRIG_ITEM_PUT_INTO )) 
  						(this,caller,what);
}

bool Object::TgItemGetFrom( Char * caller, Object * what )
{
	if ( !IS_SET(trigger_bits, OTRIG_ITEM_GET_FROM) )
		return false;

	return ((otrigT_item_get_from)findTrigger(OTRIG_ITEM_GET_FROM )) 
  						(this,caller,what);

}

// STATIC
Object * Object::createObject( int type )
{
	switch (type)
	{
		case ITEM_WEAPON:           return new ObjWeapon(); 
		case ITEM_ARMOR:            return new ObjArmor(); 
		case ITEM_CONTAINER:        return new ObjContainer(); 
		case ITEM_FOOD:             return new ObjFood(); 
		case ITEM_CORPSE:           return new ObjCorpse(); 
		case ITEM_GOLD:             return new ObjGold(); 
		case ITEM_LIQUID_CONTAINER: return new ObjLiquidContainer(); 
		default: return NULL;
	}
}


// Classes derived from Object class

/*
// superclass call at end
int ObjTemplate::readFromTypeSpecific( StaticInput & in )
{
	return Object::readFromTypeSpecific(in);
}
int ObjTemplate::writeToTypeSpecific( Output & outf ) const
{
	return Object::writeToTypeSpecific(outf);
}

// superclass call at beginning
void ObjTemplate::reportVals( String & str ) const
{
	Object::reportVals(str);
}

// superclass call at end
char * ObjTemplate::setVals( const String & arg )
{							 
	Object::setVals(str);
}
*/

int ObjWeapon::readFromTypeSpecific( StaticInput & in )
{
	char buf[BUF];

	in.getword( buf );
	dam_type = ::getDamType( buf );
	if( dam_type == NONE )
	{
		Cout << "BUG: Invalid dam type " << buf << " for " << getShort() << endl;
	}

	min_dam = in.getnum();
	max_dam = in.getnum();

	return Object::readFromTypeSpecific(in);
	
}

int ObjWeapon::writeToTypeSpecific( Output & outf ) const
{
	outf << getDamTypeName( dam_type ) << ' ' << min_dam << ' ' << max_dam << endl;
	return Object::writeToTypeSpecific( outf );
}

void ObjWeapon::reportVals( String & str ) const
{
	Object::reportVals(str);
	str << "DamType: " << getDamTypeName( dam_type ) << 
		"Min: " << min_dam << "Max: " << max_dam << endl;
}

char * ObjWeapon::setVals( const String & str )
{
	String arg1;
	String arg2;
	int i;
	str.startArgs();
	arg1 = str.getArg();
	arg2 = str.getArgRest();

	if ( arg1 == "DamType" )
	{
		i = ::getDamType( arg2.chars() );
		if ( i == NONE )
		{
			return "Wrong damage type.\n\r";
		}
		dam_type = i;
		return "DamType set.\n\r";
	}
	else if ( arg1 == "Min" )
	{
		if ( !arg2.isNumber() )
			return "Must be number.\n\r";
		
		i = arg2.asInt();
		if ( i < 0 )
			return "Must be positive.\n\r";
		min_dam = i;
		return "Min damage set.\n\r";
	}
	else if ( arg1 == "Max" )
	{
		if ( !arg2.isNumber() )
			return "Must be number.\n\r";
		
		i = arg2.asInt();
		if ( i < 0 )
			return "Must be positive.\n\r";
		max_dam = i;
		return "Max damage set.\n\r";
	}

	return Object::setVals(str);
}



int ObjFood::readFromTypeSpecific( StaticInput & in )
{
	food_worth = in.getnum();
	return Object::readFromTypeSpecific(in);
}

int ObjFood::writeToTypeSpecific( Output & outf ) const 
{
	outf << food_worth << endl;
	return Object::writeToTypeSpecific(outf);
}

void ObjFood::reportVals( String & str ) const
{
	Object::reportVals(str);
	str << "Worth (in food units): " << food_worth << endl;
}

char * ObjFood::setVals( const String & str )
{
	String arg1;
	String arg2;
	int i;
	str.startArgs();
	arg1 = str.getArg();
	arg2 = str.getArgRest();
	
	if ( arg1 == "Worth" )
	{
		if ( !arg2.isNumber() )
			return "This value must be a number.\n\r";
		i = arg2.asInt();
		if ( i < 0 )
			return "Must be postive.\n\r";
		food_worth = i;
		return "Food worth set.\n\r";
	}
	
	return Object::setVals(str);
}

int ObjLiquidContainer::readFromTypeSpecific( StaticInput & in )
{
	amount = in.getnum();
	return Object::readFromTypeSpecific(in);
}

int ObjLiquidContainer::writeToTypeSpecific( Output & outf ) const
{
	outf << amount << endl;
	return Object::writeToTypeSpecific(outf);
}

void ObjLiquidContainer::reportVals( String & str ) const
{
	Object::reportVals(str);
	str << "Amount of liquid: " << amount << endl;
}

char * ObjLiquidContainer::setVals( const String & str )
{
	String arg1;
	String arg2;
	int i;
	str.startArgs();
	arg1 = str.getArg();
	arg2 = str.getArgRest();
	
	if ( arg1 == "Amount" )
	{
		if ( !arg2.isNumber() )
			return "This value must be a number.\n\r";
		i = arg2.asInt();
		if ( i < 0 )
			return "Must be postive.\n\r";
		amount = i;
		return "Amount of liquid set.\n\r";
	}
	
	return Object::setVals(str);
}