roh/conf/area/
roh/game/talk/
roh/help/
roh/monsters/ocean/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.44b/
/*
 * 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-2009 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 "objects.h"
#include "clans.h"
#include "alchemy.h"

//*********************************************************************
//							Object
//*********************************************************************

Object::Object() {
	int i;

	version = "0.00";
	memset(name, 0, sizeof(name));
	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 = 0;
	memset(flags, 0, sizeof(flags));
	questnum = 0;
	first_obj = 0;
	parent_obj = 0;	parent_room = 0;
	parent_crt = 0;
	numAttacks = bulk = maxbulk = lotteryCycle = max_unique = uniqueId = 0;
	coinCost = shopValue = 0;
	size = NO_SIZE;

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

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

	hooks.setParent(this);
}

Object::~Object() {
	otag	*op = first_obj, *temp=0;
	if(compass)
		delete compass;
	alchemyEffects.clear();
	compass = 0;
	while(op) {
		temp = op->next_tag;
		delete op->obj;
		delete op;
		op = temp;
	}
}

//*********************************************************************
//							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;
	strcpy(newPotion->name, "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;
	otag	*op=0, *fp=0, *prev=0;

	moCopy(o);

	description = o.description;
	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;
	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_obj = o.parent_obj;
	for(i=0; i<4; i++)
		lasttime[i] = o.lasttime[i];

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

	parent_room = o.parent_room;
	parent_crt = o.parent_crt;
	lastMod = o.lastMod;

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

	lotteryCycle = o.lotteryCycle;
	max_unique = o.max_unique;

	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) {
		compass = new MapMarker;
		*compass = *o.compass;
	}

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

	// copy everything contained inside this object
	fp = first_obj;
	op = o.first_obj;
	while(op) {
		fp = new otag;
		fp->obj = new Object;
		*fp->obj = *op->obj;
		fp->obj->parent_obj = this;

		if(prev)
			prev->next_tag = fp;

		prev = fp;
		fp = fp->next_tag;
		op = op->next_tag;
	}
	std::pair<int, AlchemyEffect> p;
	foreach(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);
	}
}


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 ||
		max_unique != o.max_unique ||
		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() ||
		first_obj ||
		o.first_obj ||
		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);

	/*
	std::pair<int, bstring> p;
	foreach(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));
}

bool Object::flagIsSet(int flag) const {
	return(flags[flag/8] & 1<<(flag%8));
}
void Object::setFlag(int flag) {
	flags[flag/8] |= 1<<(flag%8);
}
void Object::clearFlag(int flag) {
	flags[flag/8] &= ~(1<<(flag%8));
}
bool Object::toggleFlag(int flag) {
	if(flagIsSet(flag))
		clearFlag(flag);
	else
		setFlag(flag);
	return(flagIsSet(flag));
}

bstring Object::getVersion() const {
	return(version);
}

void Object::setMade() {
	made = time(0);
}

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

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

	n = weight;
	op = first_obj;
	while(op) {
		if(!op->obj->flagIsSet(O_WEIGHTLESS_CONTAINER))
			n += op->obj->getActualWeight();
		op = op->next_tag;
	}

	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->getConstPlayer();

	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->getRoom(), "%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(const 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->getRoom());
		}
		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());
	}

	MapMarker *mapmarker=0;
	if(creature->area_room)
		mapmarker = &creature->area_room->mapmarker;

	if(	!creature->area_room ||
		!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;
//	int mobNum=0;
//

//
//	str = xstr[xnum];
//	xnum = (xnum + 1)%5;

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

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

	if(flagIsSet(O_IRREGULAR_PLURAL) && num > 1) {
		char    pform[80];
		char    sform[80];
		char    pfile[80];
		FILE    *plural;

		sprintf(pfile, "%s/plurals.txt", GAMEPATH);

		plural = fopen(pfile, "r");
		if(plural != NULL) {
			while (!irrPlural && !(feof (plural))) {
				fflush(plural);
				// get singular form
				fgets(sform, sizeof(sform), plural);
				sform[strlen(sform)-1] = 0;
				if(sform[strlen(sform)-1] == '\r')
					sform[strlen(sform)-1] = 0;
				fflush(plural);
				// get plural form
				fgets(pform, sizeof(pform), plural);
				pform[strlen(pform)-1] = 0;
				if(pform[strlen(pform)-1] == '\r')
					pform[strlen(pform)-1] = 0;

				if(strcmp(name, sform) == 0) {
					objStr << int_to_text(num) << " "  << pform;
					irrPlural = true;
				}
			}
			fclose(plural);
		}
	}
	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 " << name;
			} else
				objStr << name;
		}
		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 {
				ch = low(name[0]);
				if(ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u')
					objStr << "an ";
				else
					objStr << "a ";
			}
			objStr << name;
		}
		else {
			char tempStr[2056];
			strcpy( tempStr, int_to_text(num) );
			strcat(tempStr, " ");

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

			strcat(tempStr, name);
			if(!flagIsSet(O_NO_S_ON_PLURAL) && !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_NULL_MAGIC))
		objStr << " (M)";


	if(flags & ISDM) {
		if(flagIsSet(O_HIDDEN))
			objStr <<  " (h)";
		if(flagIsSet(O_INVISIBLE))
			objStr << " (*)";
		if(flagIsSet(O_SCENERY))
			objStr << " (s)";
	}

	toReturn = objStr.str();

	if(flags & CAP)
		toReturn[0] = up(toReturn[0]);
	return(toReturn);
}


//*********************************************************************
//						isHeavyArmor
//*********************************************************************

bool Object::isHeavyArmor() const {
//	return(type == ARMOR && (subType == "chain" || subType == "plate"));	
	return(type == ARMOR && subType == "plate");
}

bool Object::isMediumArmor() const {
	return(type == ARMOR && subType == "chain");
}

//*********************************************************************
//						isLightArmor
//*********************************************************************

bool Object::isLightArmor() const {
	return(type == ARMOR && (subType == "cloth" || subType == "leather"));
}

//*********************************************************************
//						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;
	otag* op = first_obj;
	while(op) {
		object = op->obj;
		op = op->next_tag;

		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))
		) {
			del_obj_obj(object, this);
			creature->addObj(object);
		}
	}
}

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

bool Object::isKey(const Room* room, const Exit* exit) const {
	// storage room exist must be a storage room key
	if(exit->flagIsSet(X_TO_STORAGE_ROOM))
		return(!strcmp(name, "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);
}