roh/conf.old/area/
roh/config/code/python/
roh/config/game/area/
roh/config/game/signs/
roh/help/dmhelp/
roh/help/help/
roh/log/
roh/log/staff/
roh/monsters/ocean/
roh/objects/misc/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.47e/
/*
 * objects.cpp
 *	 Object routines.
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * Permission to use, modify and distribute is granted via the
 *  Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License
 *    http://creativecommons.org/licenses/by-nc-sa/3.0/
 *
 * 	Copyright (C) 2007-2012 Jason Mitchell, Randi Mitchell
 * 	   Contributions by Tim Callahan, Jonathan Hseu
 *  Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
 *
 */
#include "mud.h"
#include "commands.h"
#include "objects.h"
#include "clans.h"
#include "alchemy.h"


bool Object::operator< (const Object& t) const {
	return(getCompareStr().compare(t.getCompareStr()) < 0);
}

bstring Object::getCompareStr() const {
	bstring toReturn = getName() + "-" + bstring(adjustment) + "-" + bstring(shopValue) + "-" + getId();
	return(toReturn);
}

bstring DroppedBy::getName() const {
	return(name);
}
bstring DroppedBy::getIndex() const {
	return(index);
}
bstring DroppedBy::getId() const {
	return(id);
}
bstring DroppedBy::getType() const {
	return(type);
}
void DroppedBy::clear() {
	name.clear();
	index.clear();
	id.clear();
	type.clear();
}

//*********************************************************************
//						operator<<
//*********************************************************************

std::ostream& operator<<(std::ostream& out, const DroppedBy& drop) {
	out << drop.name << " (" << drop.index;
	if(!drop.id.empty()) {
		if(!drop.index.empty())
			out << ":";
		out << drop.id;
	}
	out << ") [" << drop.type << "]";
	return(out);
}

bstring DroppedBy::str() {
	std::ostringstream oStr;
	oStr << name << " (" << index;
	if(!id.empty()) {
		if(!index.empty())
			oStr << ":";
		oStr << id;
	}
	oStr << ") [" << type << "]";
	return(oStr.str());
}

void Object::validateId() {
	if(id.empty() || id.equals("-1")) {
		setId(gServer->getNextObjectId());
	}
}

void Object::setDroppedBy(MudObject* dropper, bstring pDropType) {

	droppedBy.name = dropper->getName();
	droppedBy.id = dropper->getId();

	Monster* mDropper = dynamic_cast<Monster*>(dropper);
	Object* oDropper = dynamic_cast<Object*>(dropper);
	UniqueRoom* rDropper = dynamic_cast<UniqueRoom*>(dropper);
	AreaRoom* aDropper = dynamic_cast<AreaRoom*>(dropper);

	if(mDropper) {
		droppedBy.index = mDropper->info.rstr();
	} else if(oDropper) {
		droppedBy.index = oDropper->info.rstr();
	} else if(rDropper) {
		droppedBy.index = rDropper->info.rstr();
	} else if(aDropper) {
		droppedBy.index = aDropper->area->name + aDropper->fullName();
	}
	else {
		droppedBy.index = "";
	}

	droppedBy.type = pDropType;
}
//*********************************************************************
//							Object
//*********************************************************************

Object::Object() {
	int i;

	id = "-1";
	version = "0.00";
	description = effect = "";
	memset(key, 0, sizeof(key));
	memset(use_output, 0, sizeof(use_output));
	memset(use_attack, 0, sizeof(use_attack));
	weight = type = adjustment = shotsMax = shotsCur = armor =
		wearflag = magicpower = level = requiredSkill = clan =
		special = delay = quality = effectStrength = effectDuration = chargesCur = chargesMax = 0;
	memset(flags, 0, sizeof(flags));
	questnum = 0;
	numAttacks = bulk = maxbulk = lotteryCycle = 0;
	coinCost = shopValue = 0;
	size = NO_SIZE;

	for(i=0; i<6; i++)
		lotteryNumbers[i] = 0;

	material = NO_MATERIAL;
	keyVal = minStrength = 0;
	compass = 0;
	increase = 0;
	recipe = 0;
	made = 0;
	extra = 0;

	hooks.setParent(this);
}

Object::~Object() {
	if(compass)
		delete compass;
	if(increase)
		delete increase;
	alchemyEffects.clear();
	compass = 0;
	ObjectSet::iterator it;
	Object* obj;
	for(it = objects.begin() ; it != objects.end() ; ) {
		obj = (*it++);
		delete obj;
	}
	objects.clear();
	gServer->removeDelayedActions(this);
	moDestroy();
}

//*********************************************************************
//							init
//*********************************************************************
// Initiate the object - used when the object is first introduced into the
// world.

void Object::init(bool selRandom) {
	// this object may turn into another object
	if(selRandom)
		selectRandom();

	if(flagIsSet(O_RANDOM_ENCHANT))
		randomEnchant();

	loadContainerContents();
	setMade();
	setFlag(O_JUST_LOADED);
}

//*********************************************************************
//							getNewPotion
//*********************************************************************

Object* Object::getNewPotion() {
	Object* newPotion = new Object;

	newPotion->type = POTION;
	newPotion->setName( "generic potion");
	strcpy(newPotion->key[0], "generic");
	strcpy(newPotion->key[1], "potion");
	newPotion->weight = 1;
	newPotion->setFlag(O_SAVE_FULL);

	return(newPotion);
}

//*********************************************************************
//							doCopy
//*********************************************************************

void Object::doCopy(const Object& o) {
	int		i=0;

	moCopy(o);

	description = o.description;

	droppedBy = o.droppedBy;

	plural = o.plural;
	for(i=0; i<3; i++)
		strcpy(key[i], o.key[i]);
	strcpy(use_output, o.use_output);
	strcpy(use_attack, o.use_attack);
	version = o.version;
	weight = o.weight;
	type = o.type;
	adjustment = o.adjustment;
	chargesMax = o.chargesMax;
	chargesCur = o.chargesCur;
	shotsMax = o.shotsMax;
	shotsCur = o.shotsCur;
	damage = o.damage;
	armor = o.armor;
	wearflag = o.wearflag;
	magicpower = o.magicpower;
	info = o.info;
	level = o.level;
	quality = o.quality;
	requiredSkill = o.requiredSkill;
	clan = o.clan;
	special = o.special;
	for(i=0; i<OBJ_FLAG_ARRAY_SIZE; i++)
		flags[i] = o.flags[i];
	questnum = o.questnum;
//	parent = o.parent;
	for(i=0; i<4; i++)
		lasttime[i] = o.lasttime[i];

	for(i=0; i<3; i++)
		in_bag[i] = o.in_bag[i];

	lastMod = o.lastMod;

	bulk = o.bulk;
	delay = o.delay;
	maxbulk = o.maxbulk;
	numAttacks = o.numAttacks;

	lotteryCycle = o.lotteryCycle;

	coinCost = o.coinCost;
	deed = o.deed;
	shopValue = o.shopValue;

	value.set(o.value);

	made = o.made;
	keyVal = o.keyVal;
	extra = o.extra;
	questOwner = o.questOwner;

	minStrength = o.minStrength;
	material = o.material;

	for(i=0; i<6; i++)
		lotteryNumbers[i] = o.lotteryNumbers[i];
	refund.set(o.refund);
	size = o.size;

	if(o.compass) {
		if(compass)
			delete compass;
		compass = new MapMarker;
		*compass = *o.compass;
	}
	if(o.increase) {
		if(increase)
			delete increase;
		increase = new ObjIncrease;
		*increase = *o.increase;
	}

	recipe = o.recipe;
	effect = o.effect;
	effectDuration = o.effectDuration;
	effectStrength = o.effectStrength;

	// copy everything contained inside this object
	Object *newObj;
	for(Object* obj : o.objects) {
		newObj = new Object;
		*newObj = *obj;
		addObj(newObj, false);
	}
	for(std::pair<int, AlchemyEffect> p : o.alchemyEffects) {
		alchemyEffects[p.first] = p.second;
	}
	subType = o.subType;

	std::list<CatRef>::const_iterator it;
	randomObjects.clear();
	for(it = o.randomObjects.begin() ; it != o.randomObjects.end() ; it++) {
		randomObjects.push_back(*it);
	}
}

DroppedBy& DroppedBy::operator=(const DroppedBy& o) {
	name = o.name;
	index = o.index;
	id = o.id;
	type = o.type;
	return(*this);
}

Object& Object::operator=(const Object& o) {
	doCopy(o);
	return(*this);
}

bool Object::operator==(const Object& o) const {
	int		i=0;

	if(	description != o.description ||
		version != o.version ||
		weight != o.weight ||
		type != o.type ||
		adjustment != o.adjustment ||
		shotsMax != o.shotsMax ||
		shotsCur != o.shotsCur ||
		damage != o.damage ||
		armor != o.armor ||
		wearflag != o.wearflag ||
		magicpower != o.magicpower ||
		info != o.info ||
		level != o.level ||
		quality != o.quality ||
		requiredSkill != o.requiredSkill ||
		clan != o.clan ||
		special != o.special ||
		questnum != o.questnum ||
		lastMod != o.lastMod ||
		bulk != o.bulk ||
		delay != o.delay ||
		maxbulk != o.maxbulk ||
		numAttacks != o.numAttacks ||
		lotteryCycle != o.lotteryCycle ||
		coinCost != o.coinCost ||
		deed != o.deed ||
		shopValue != o.shopValue ||
		made != o.made ||
		keyVal != o.keyVal ||
		extra != o.extra ||
		questOwner != o.questOwner ||
		minStrength != o.minStrength ||
		material != o.material ||
		size != o.size ||
		recipe != o.recipe ||
		effect != o.effect ||
		effectDuration != o.effectDuration ||
		effectStrength != o.effectStrength ||
		subType != o.subType ||
		randomObjects.size() != o.randomObjects.size() ||
		alchemyEffects.size() != o.alchemyEffects.size() ||
		!objects.empty() ||
		!o.objects.empty() ||
		strcmp(use_output, o.use_output) ||
		strcmp(use_attack, o.use_attack) ||
		value != o.value ||
		refund != o.refund
	)
		return(false);

	for(i=0; i<3; i++)
		if(strcmp(key[i], o.key[i]))
			return(false);
	for(i=0; i<OBJ_FLAG_ARRAY_SIZE; i++)
		if(flags[i] != o.flags[i])
			return(false);
	for(i=0; i<4; i++) {
		if(	lasttime[i].interval != o.lasttime[i].interval ||
			lasttime[i].ltime != o.lasttime[i].ltime ||
			lasttime[i].misc != o.lasttime[i].misc
		)
			return(false);
	}
	for(i=0; i<3; i++)
		if(in_bag[i] != o.in_bag[i])
			return(false);
	for(i=0; i<6; i++)
		if(lotteryNumbers[i] != o.lotteryNumbers[i])
			return(false);

	if(compass && o.compass) {
		if(*compass != *o.compass)
			return(false);
	} else if(compass || o.compass)
		return(false);

	/*
	for(std::pair<int, bstring> p : o.alchemyEffects) {
		alchemyEffects.insert(p);
	}
	*/

	std::list<CatRef>::const_iterator rIt, orIt;
	rIt = randomObjects.begin();
	orIt = o.randomObjects.begin();
	for(; rIt != randomObjects.end() ;) {
		if(*rIt != *orIt)
			return(false);
		rIt++;
		orIt++;
	}
	return(true);
}
bool Object::operator!=(const Object& o) const {
	return(!(*this==o));
}

//*********************************************************************
//							getActualWeight
//*********************************************************************
// This function computes the total amount of weight of an object and
// all its contents.

int Object::getActualWeight() const {
	int		n=0;

	n = weight;

	for(Object* obj : objects) {
		if(!obj->flagIsSet(O_WEIGHTLESS_CONTAINER))
			n += obj->getActualWeight();
	}

	return(n);
}

//*********************************************************************
//							getActualBulk
//*********************************************************************

int Object::getActualBulk() const {
	int		n=0;

	if(flagIsSet(O_BULKLESS_OBJECT))
		return(0);

	if(bulk <= 0) {
		switch(type) {
		case	WEAPON:
			{
				bstring category = getWeaponCategory();
				if(category == "crushing")
					n = 5;
				else if(category == "piercing")
					n = 4;
				else if(category == "slashing")
					n = 4;
				else if(category == "ranged")
					n = 6;
				else if(category == "chopping")
					n = 8;
				else
					n = 4;
			}
			break;
		case	MONEY:
		case	POISON:
		case	POTION:
			n = 4;
			break;
		case ARMOR:
			switch(wearflag) {
			case BODY:
				n = 20;
				break;
			case ARMS:
				n = 12;
				break;
			case LEGS:
				n = 14;
				break;
			case NECK:
				n = 6;
				break;
			case BELT:
			case HANDS:
			case HEAD:
			case FEET:
			case HELD:
			case FACE:
				n = 4;
				break;
			case FINGER:

			case FINGER2:
			case FINGER3:
			case FINGER4:
			case FINGER5:
			case FINGER6:
			case FINGER7:
			case FINGER8:
				n = 1;
				break;
			case SHIELD:
				n = 10;
				break;
			default:
				n =1;
				break;
			}
			break;
		case SCROLL:
		case WAND:
		case SONGSCROLL:
		case MISC:
			n = 3;
			break;
		case KEY:
			n = 1;
			break;
		case CONTAINER:
			n = 5;
			break;
		case LIGHTSOURCE:
		case BANDAGE:
			n = 2;
			break;
		}
	} else
		n = bulk;

	return(n);
}

//*********************************************************************
//							raceRestrict
//*********************************************************************
// become a random object

void Object::selectRandom() {
	std::list<CatRef>::const_iterator it;
	CatRef cr;
	Object* object=0;
	int which = randomObjects.size();

	// Load all means we will become every object in this list (trade only)
	// rather than a random one. Thus, don't use this code.
	if(!which || flagIsSet(O_LOAD_ALL))
		return;
	which = mrand(1, which);

	for(it = randomObjects.begin(); it != randomObjects.end(); it++) {
		which--;
		if(!which) {
			cr = *it;
			break;
		}
	}

	if(!loadObject(cr, &object))
		return;

	*this = *object;
	delete object;
}

//*********************************************************************
//							raceRestrict
//*********************************************************************

bool Object::raceRestrict(const Creature* creature, bool p) const {
	if(raceRestrict(creature)) {
		if(p) creature->checkStaff("Your race prevents you from using %P.\n", this);
		if(!creature->isStaff()) return(true);
	}
	return(false);
}

bool Object::raceRestrict(const Creature* creature) const {
	bool pass = false;

	// if no flags are set
	if(	!flagIsSet(O_SEL_DWARF) &&
		!flagIsSet(O_SEL_ELF) &&
		!flagIsSet(O_SEL_HALFELF) &&
		!flagIsSet(O_SEL_HALFLING) &&
		!flagIsSet(O_SEL_HUMAN) &&
		!flagIsSet(O_SEL_ORC) &&
		!flagIsSet(O_SEL_HALFGIANT) &&
		!flagIsSet(O_SEL_GNOME) &&
		!flagIsSet(O_SEL_TROLL) &&
		!flagIsSet(O_SEL_HALFORC) &&
		!flagIsSet(O_SEL_OGRE) &&
		!flagIsSet(O_SEL_DARKELF) &&
		!flagIsSet(O_SEL_GOBLIN) &&
		!flagIsSet(O_SEL_MINOTAUR) &&
		!flagIsSet(O_SEL_SERAPH) &&
		!flagIsSet(O_SEL_KOBOLD) &&
		!flagIsSet(O_SEL_CAMBION) &&
		!flagIsSet(O_SEL_BARBARIAN) &&
		!flagIsSet(O_SEL_KATARAN) &&
		!flagIsSet(O_SEL_TIEFLING) &&
		!flagIsSet(O_RSEL_INVERT)
	)
		return(false);

	// if the race flag is set and they match, they pass
	pass = (
		(flagIsSet(O_SEL_DWARF) && creature->isRace(DWARF)) ||
		(flagIsSet(O_SEL_ELF) && creature->isRace(ELF)) ||
		(flagIsSet(O_SEL_HALFELF) && creature->isRace(HALFELF)) ||
		(flagIsSet(O_SEL_HALFLING) && creature->isRace(HALFLING)) ||
		(flagIsSet(O_SEL_HUMAN) && creature->isRace(HUMAN)) ||
		(flagIsSet(O_SEL_ORC) && creature->isRace(ORC)) ||
		(flagIsSet(O_SEL_HALFGIANT) && creature->isRace(HALFGIANT)) ||
		(flagIsSet(O_SEL_GNOME) && creature->isRace(GNOME)) ||
		(flagIsSet(O_SEL_TROLL) && creature->isRace(TROLL)) ||
		(flagIsSet(O_SEL_HALFORC) && creature->isRace(HALFORC)) ||
		(flagIsSet(O_SEL_OGRE) && creature->isRace(OGRE)) ||
		(flagIsSet(O_SEL_DARKELF) && creature->isRace(DARKELF)) ||
		(flagIsSet(O_SEL_GOBLIN) && creature->isRace(GOBLIN)) ||
		(flagIsSet(O_SEL_MINOTAUR) && creature->isRace(MINOTAUR)) ||
		(flagIsSet(O_SEL_SERAPH) && creature->isRace(SERAPH)) ||
		(flagIsSet(O_SEL_KOBOLD) && creature->isRace(KOBOLD)) ||
		(flagIsSet(O_SEL_CAMBION) && creature->isRace(CAMBION)) ||
		(flagIsSet(O_SEL_BARBARIAN) && creature->isRace(BARBARIAN)) ||
		(flagIsSet(O_SEL_KATARAN) && creature->isRace(KATARAN)) ||
		(flagIsSet(O_SEL_TIEFLING) && creature->isRace(TIEFLING))
	);

	if(flagIsSet(O_RSEL_INVERT)) pass = !pass;

	return(!pass);
}

//*********************************************************************
//							classRestrict
//*********************************************************************
// We need more information here - we can't just pass/fail them like
// we usually do.

bool Object::classRestrict(const Creature* creature, bool p) const {
	if(classRestrict(creature)) {
		if(p) creature->checkStaff("Your class prevents you from using %P.\n", this);
		if(!creature->isStaff()) return(true);
	}
	return(false);
}

bool Object::classRestrict(const Creature* creature) const {
	bool pass = false;
	const Player* player = creature->getAsConstPlayer();

	int cClass = creature->getClass();
	if(player && player->getClass() == MAGE && (player->getSecondClass() == ASSASSIN || player->getSecondClass() == THIEF))
		cClass = player->getSecondClass();

	if(flagIsSet(O_NO_MAGE) && (cClass == MAGE || cClass == LICH))
		return(true);

	// if no flags are set
	if(	!flagIsSet(O_SEL_ASSASSIN) &&
		!flagIsSet(O_SEL_BERSERKER) &&
		!flagIsSet(O_SEL_CLERIC) &&
		!flagIsSet(O_SEL_FIGHTER) &&
		!flagIsSet(O_SEL_MAGE) &&
		!flagIsSet(O_SEL_PALADIN) &&
		!flagIsSet(O_SEL_RANGER) &&
		!flagIsSet(O_SEL_THIEF) &&
		!flagIsSet(O_SEL_VAMPIRE) &&
		!flagIsSet(O_SEL_MONK) &&
		!flagIsSet(O_SEL_DEATHKNIGHT) &&
		!flagIsSet(O_SEL_DRUID) &&
		!flagIsSet(O_SEL_LICH) &&
		!flagIsSet(O_SEL_WEREWOLF) &&
		!flagIsSet(O_SEL_BARD) &&
		!flagIsSet(O_SEL_ROGUE) &&
		!flagIsSet(O_CSEL_INVERT)
	) {
		// we need to do some special rules before we say they can use the item

		// only blunts for monks
		if(wearflag == WIELD && cClass == MONK && getWeaponCategory() != "crushing")
			return(true);

		// only sharps for wolves
		if(wearflag == WIELD && creature->isEffected("lycanthropy") && getWeaponCategory() != "slashing")
			return(true);

		if(type == ARMOR && (cClass == MONK || creature->isEffected("lycanthropy")))
			return(true);

		// no rings or shields for monk/wolf/lich
		if(	(	wearflag == FINGER ||
				wearflag == SHIELD
			) &&
			(	cClass == MONK ||
				creature->isEffected("lycanthropy") ||
				cClass == LICH
			)
		)
			return(true);

		return(false);
	}

	// if the class flag is set and they match, they pass
	pass = (
		(flagIsSet(O_SEL_ASSASSIN) && cClass == ASSASSIN) ||
		(flagIsSet(O_SEL_BERSERKER) && cClass == BERSERKER) ||
		(flagIsSet(O_SEL_CLERIC) && cClass == CLERIC) ||
		(flagIsSet(O_SEL_FIGHTER) && cClass == FIGHTER) ||
		(flagIsSet(O_SEL_MAGE) && cClass == MAGE) ||
		(flagIsSet(O_SEL_PALADIN) && cClass == PALADIN) ||
		(flagIsSet(O_SEL_RANGER) && cClass == RANGER) ||
		(flagIsSet(O_SEL_THIEF) && cClass == THIEF) ||
		(flagIsSet(O_SEL_VAMPIRE) && creature->isEffected("vampirism")) ||
		(flagIsSet(O_SEL_MONK) && cClass == MONK) ||
		(flagIsSet(O_SEL_DEATHKNIGHT) && cClass == DEATHKNIGHT) ||
		(flagIsSet(O_SEL_DRUID) && cClass == DRUID) ||
		(flagIsSet(O_SEL_LICH) && cClass == LICH) ||
		(flagIsSet(O_SEL_WEREWOLF) && creature->isEffected("lycanthropy")) ||
		(flagIsSet(O_SEL_BARD) && cClass == BARD) ||
		(flagIsSet(O_SEL_ROGUE) && cClass == ROGUE)
	);

	if(flagIsSet(O_CSEL_INVERT)) pass = !pass;

	return(!pass);
}


//*********************************************************************
//							levelRestrict
//*********************************************************************

bool Object::levelRestrict(const Creature* creature, bool p) const {
	if(level > creature->getLevel()) {
		if(p) creature->checkStaff("You are not experienced enough to use that.\n");
		if(!creature->isStaff()) return(true);
	}
	return(false);
}

bool Object::skillRestrict(const Creature* creature, bool p) const {
	int skillLevel = 0;

	bstring skill = "";

	if(type == ARMOR) {
		skill = getArmorType();
		if(skill == "shield" || skill == "ring")
			skill = "";
	} else if(type == WEAPON) {
		skill = getWeaponType();
	} else if(flagIsSet(O_FISHING)) {
		skill = "fishing";
	}
	if(skill != "") {
		skillLevel = (int)creature->getSkillGained(skill);
		if(requiredSkill > skillLevel) {
			if(p) creature->checkStaff("You do not have enough training in ^W%s%s^x to use that!\n", skill.c_str(), type == ARMOR ? " armor" : "");
			if(!creature->isStaff()) return(true);
		}
	}

	return(false);
}

//*********************************************************************
//							levelRestrict
//*********************************************************************

bool Object::alignRestrict(const Creature* creature, bool p) const {
	if(	(flagIsSet(O_GOOD_ALIGN_ONLY) && creature->getAdjustedAlignment() < NEUTRAL) ||
		(flagIsSet(O_EVIL_ALIGN_ONLY) && creature->getAdjustedAlignment() > NEUTRAL) )
	{
		if(p) {
			creature->checkStaff("%O shocks you and you drop it.\n", this);
			if(!creature->isStaff())
				broadcast(creature->getSock(), creature->getConstRoomParent(), "%M is shocked by %P.", creature, this);
		}
		if(!creature->isStaff()) return(true);
	}
	return(false);
}



//*********************************************************************
//							sexRestrict
//*********************************************************************

bool Object::sexRestrict(const Creature* creature, bool p) const {
	Sex sex = creature->getSex();
	if(flagIsSet(O_MALE_ONLY) && sex != SEX_MALE) {
		if(p) creature->checkStaff("Only males can use that.\n");
		if(!creature->isStaff()) return(true);
	}
	if(flagIsSet(O_FEMALE_ONLY) && sex != SEX_FEMALE) {
		if(p) creature->checkStaff("Only females can use that.\n");
		if(!creature->isStaff()) return(true);
	}
	if(flagIsSet(O_SEXLESS_ONLY) && sex != SEX_NONE) {
		if(p) creature->checkStaff("Only creatures without a gender can use that.\n");
		if(!creature->isStaff()) return(true);
	}
	return(false);
}


//*********************************************************************
//							strRestrict
//*********************************************************************

bool Object::strRestrict(Creature* creature, bool p) const {
	if(minStrength > creature->strength.getCur()) {
		if(!p) creature->checkStaff("You are currently not strong enough to use that.\n");
		if(!creature->isStaff()) return(true);
	}
	return(false);
}


//*********************************************************************
//							clanRestrict
//*********************************************************************

bool Object::clanRestrict(const Creature* creature, bool p) const {
	if(clanRestrict(creature)) {
		if(p) creature->checkStaff("Your allegience prevents you from using %P.\n", this);
		if(!creature->isStaff()) return(true);
	}
	return(false);
}

bool Object::clanRestrict(const Creature* creature) const {
	int c = creature->getClan();
	if(creature->getDeity()) {
		const Clan* clan = gConfig->getClanByDeity(creature->getDeity());
		if(clan && clan->getId() != 0)
			c = clan->getId();
	}

	// if the object requires pledging, let any clan pass
	if(flagIsSet(O_PLEDGED_ONLY))
		return(!c);

	// if no flags are set
	if(	!flagIsSet(O_CLAN_1) &&
		!flagIsSet(O_CLAN_2) &&
		!flagIsSet(O_CLAN_3) &&
		!flagIsSet(O_CLAN_4) &&
		!flagIsSet(O_CLAN_5) &&
		!flagIsSet(O_CLAN_6) &&
		!flagIsSet(O_CLAN_7) &&
		!flagIsSet(O_CLAN_8) &&
		!flagIsSet(O_CLAN_9) &&
		!flagIsSet(O_CLAN_10) &&
		!flagIsSet(O_CLAN_11) &&
		!flagIsSet(O_CLAN_12) )
		return(false);

	// if the clan flag is set and they match, they pass
	if(	(flagIsSet(O_CLAN_1) && c == 1) ||
		(flagIsSet(O_CLAN_2) && c == 2) ||
		(flagIsSet(O_CLAN_3) && c == 3) ||
		(flagIsSet(O_CLAN_4) && c == 4) ||
		(flagIsSet(O_CLAN_5) && c == 5) ||
		(flagIsSet(O_CLAN_6) && c == 6) ||
		(flagIsSet(O_CLAN_7) && c == 7) ||
		(flagIsSet(O_CLAN_8) && c == 8) ||
		(flagIsSet(O_CLAN_9) && c == 9) ||
		(flagIsSet(O_CLAN_10) && c == 10) ||
		(flagIsSet(O_CLAN_11) && c == 11) ||
		(flagIsSet(O_CLAN_12) && c == 12) )
		return(false);

	return(true);
}


//*********************************************************************
//							lawchaoRestrict
//*********************************************************************

bool Object::lawchaoRestrict(const Creature* creature, bool p) const {
	if(	(flagIsSet(O_CHAOTIC_ONLY) && !creature->flagIsSet(P_CHAOTIC)) ||
		(flagIsSet(O_LAWFUL_ONLY) && creature->flagIsSet(P_CHAOTIC)) )
	{
		if(p) creature->checkStaff("You are unable to use %P.\n", this);
		if(!creature->isStaff()) return(true);
	}
	return(false);
}


//*********************************************************************
//						doRestrict
//*********************************************************************

bool Object::doRestrict(Creature* creature, bool p) {
	if(clanRestrict(creature, p))
		return(true);
	if(levelRestrict(creature, p))
		return(true);
	if(skillRestrict(creature, p))
		return(true);
	if(strRestrict(creature, p))
		return(true);
	if(classRestrict(creature, p))
		return(true);
	if(raceRestrict(creature, p))
		return(true);
	if(sexRestrict(creature, p))
		return(true);
	if(clanRestrict(creature, p))
		return(true);
	if(lawchaoRestrict(creature, p))
		return(true);
	if(alignRestrict(creature, p)) {
		if(p && !creature->isStaff()) {
			creature->delObj(this, false, true);
			addToRoom(creature->getRoomParent());
		}
		return(true);
	}
	return(false);
}

//*********************************************************************
//							getCompass
//*********************************************************************

bstring Object::getCompass(const Creature* creature, bool useName) {
	std::ostringstream oStr;

	if(useName)
		oStr << getObjStr(creature, CAP, 1);
	else
		oStr <<  "It";
	oStr <<  " ";

	if(!compass) {
		oStr << "appears to be broken.\n";
		return(oStr.str());
	}

	const MapMarker *mapmarker=0;
	if(creature->inAreaRoom())
		mapmarker = &creature->getConstAreaRoomParent()->mapmarker;

	if(	!creature->inAreaRoom() ||
		!mapmarker->getArea() ||
		!compass->getArea() ||
		mapmarker->getArea() != compass->getArea() ||
		*mapmarker == *compass
	) {
		oStr << "is currently spinning in circles.\n";
		return(oStr.str());
	}

	oStr << "points " << mapmarker->direction(compass) << ". The target is "
		 << mapmarker->distance(compass) << ".\n";
	return(oStr.str());
}


//*********************************************************************
//						getObjStr
//*********************************************************************

bstring Object::getObjStr(const Creature* viewer, int flags, int num) const {
	std::ostringstream objStr;
	bstring toReturn = "";
	char ch;

	if(!this)
		return("(NULL OBJ)");

	if(flagIsSet(O_DARKNESS))
		objStr << "^D";

	if(viewer != NULL)
		flags |= viewer->displayFlags();
	bool irrPlural = false;


	// use new plural code - on object
	if(num > 1 && plural != "") {
		objStr << int_to_text(num) << " "  << plural;
		irrPlural = true;
	}

	if(!irrPlural) {
		// Either not an irregular plural, or we couldn't find a match in the irregular plural file
		if(num == 0) {
			if(!flagIsSet(O_NO_PREFIX)) {
				objStr << "the " << getName();
			} else
				objStr << getName();
		}
		else if(num == 1) {
			if(flagIsSet(O_NO_PREFIX) || (info.id == 0 && !strcmp(key[0], "gold") && type == MONEY))
				objStr <<  "";
			else if(flagIsSet(O_SOME_PREFIX))
				objStr << "some ";
			else {
				// handle articles even when the item starts with a color
				int pos=0;
				while(getName()[pos] == '^') pos += 2;
				ch = low(getName()[pos]);

				if(ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u')
					objStr << "an ";
				else
					objStr << "a ";
			}
			objStr << getName();
		}
		else {
			char tempStr[2056];
			strcpy( tempStr, int_to_text(num) );
			strcat(tempStr, " ");

			if(flagIsSet(O_SOME_PREFIX))
				strcat(tempStr, "sets of ");

			strcat(tempStr, getCName());
			if(!flagIsSet(O_SOME_PREFIX)) {
				tempStr[strlen(tempStr)+1] = 0;
				tempStr[strlen(tempStr)+2] = 0;
				if(tempStr[strlen(tempStr)-1] == 's' ||
						tempStr[strlen(tempStr)-1] == 'x') {
					tempStr[strlen(tempStr)] = 'e';
					tempStr[strlen(tempStr)] = 's';
				} else
					tempStr[strlen(tempStr)] = 's';
			}
			objStr << tempStr;
		}
	}


	if(flagIsSet(O_NULL_MAGIC) && ((flags & ISDM) || (flags & ISCT))) {
		objStr << " (+" << adjustment << ")(n)";
	} else if((flags & MAG) && adjustment && !flagIsSet(O_NULL_MAGIC)) {
		objStr << " (";
		if(adjustment >= 0)
			objStr << "+";
		objStr << adjustment << ")";
	} else if((flags & MAG) && (magicpower || flagIsSet(O_MAGIC)) && !flagIsSet(O_NULL_MAGIC))
		objStr << " (M)";


	if(flags & ISDM) {
		if(flagIsSet(O_HIDDEN))
			objStr <<  " (h)";
		if(isEffected("invisibility"))
			objStr << " (*)";
		if(flagIsSet(O_SCENERY))
			objStr << " (s)";
		if(flagIsSet(O_NOT_PEEKABLE))
			objStr << "(NoPeek)";
		if(flagIsSet(O_NO_STEAL))
			objStr << "(NoSteal)";
		if(flagIsSet(O_BODYPART))
			objStr << "(BodyPart)";

	}

	if(isBroken())
		objStr << " (broken)";
	objStr << "^x";

	toReturn = objStr.str();

	if(flags & CAP) {
		int pos = 0;
		// don't capitalize colors
		while(toReturn[pos] == '^') pos += 2;
		toReturn[pos] = up(toReturn[pos]);
	}
	
	return(toReturn);
}


//*********************************************************************
//						popBag
//*********************************************************************
// removes an object from the bag, usually when the bag is stolen or destroyed

void Object::popBag(Creature* creature, bool quest, bool drop, bool steal, bool bodypart, bool dissolve) {
	Object* object=0;

	ObjectSet::iterator it;
	for(it = objects.begin() ; it != objects.end() ; ) {
		object = (*it++);

		if(	(quest && object->questnum) ||
			(dissolve && object->flagIsSet(O_RESIST_DISOLVE)) ||
			(drop && object->flagIsSet(O_NO_DROP)) ||
			(steal && object->flagIsSet(O_NO_STEAL)) ||
			(bodypart && object->flagIsSet(O_BODYPART))
		) {
			this->delObj(object);
			creature->addObj(object);
		}
	}
}

//*********************************************************************
//						isKey
//*********************************************************************

bool Object::isKey(const UniqueRoom* room, const Exit* exit) const {
	// storage room exist must be a storage room key
	if(exit->flagIsSet(X_TO_STORAGE_ROOM))
		return(getName() == "storage room key");

	// key numbers must match
	if(getKey() != exit->getKey())
		return(false);

	if(!room)
		return(true);

	// For the key to work, it must be from the same area
	// ie: no using oceancrest keys in highport!
	bstring area = exit->getKeyArea();
	if(area == "")
		area = room->info.area;

	if(!info.isArea("") && !info.isArea(area))
		return(false);
	return(true);
}


//*********************************************************************
//						spawnObjects
//*********************************************************************
// room = the catref for the destination room

void spawnObjects(const bstring& room, const bstring& objects) {
	UniqueRoom *dest = 0;
	Object* object=0;
	CatRef	cr;

	getCatRef(room, &cr, 0);

	if(!loadRoom(cr, &dest))
		return;

	bstring obj = "";
	int i=0;
	dest->expelPlayers(true, false, false);

	// make sure any existing objects of this type are removed
	if(!dest->objects.empty()) {
		do {
			obj = getFullstrTextTrun(objects, i++);
			if(obj != "")
			{
				getCatRef(obj, &cr, 0);
				ObjectSet::iterator it;
				for( it = dest->objects.begin() ; it != dest->objects.end() ; ) {
					object = (*it++);

					if(object->info == cr) {
						object->deleteFromRoom();
						delete object;
					}
				}
			}
		} while(obj != "");
	}

	// add the objects
	i=0;

	do {
		obj = getFullstrTextTrun(objects, i++);
		if(obj != "")
		{
			getCatRef(obj, &cr, 0);

			if(loadObject(cr, &object)) {
				// no need to spawn darkmetal items in a sunlit room
				if(object->flagIsSet(O_DARKMETAL) && dest->isSunlight())
					delete object;
				else {
					object->addToRoom(dest);
					object->setDroppedBy(dest, "SpawnObjects");
				}
			}
		}
	} while(obj != "");
}


double Object::getDps() {
    short num = numAttacks;
    if(!numAttacks)
        num = 1;

    return(( (damage.average() + adjustment) * (1+num) / 2) / (getWeaponDelay()/10.0));
}