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/
/*
 * object.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 "craft.h"
#include "unique.h"

//*********************************************************************
//						cmpName
//*********************************************************************
// used to compare names (alphabetically) to each other when the
// object starts with a color character - chops off the front color
// chars

//char* Object::cmpName() {
//	int i=0;
//	while(name[i] == '^' && name[i+1] != '^')
//		i += 2;
//	return(&name[i]);
//}

//*********************************************************************
//						add_obj_obj
//*********************************************************************
// This function adds the object toAdd to the current object

void Object::addObj(Object *toAdd, bool incShots) {
	toAdd->validateId();

	Hooks::run(toAdd, "beforeAddObject", this, "beforeAddToObject");

	add(toAdd);
	if(incShots)
		incShotsCur();
	Hooks::run(toAdd, "afterAddObject", this, "afterAddToObject");
}

//*********************************************************************
//						del_obj_obj
//*********************************************************************
// This function removes the object pointed to by the first parameter
// from the object pointed to by the second.

void Object::delObj(Object	*toDel) {
	Hooks::run(this, "beforeRemoveObject", toDel, "beforeRemoveFromObject");

	decShotsCur();
	toDel->removeFrom();
	Hooks::run(this, "afterRemoveObject", toDel, "afterRemoveFromObject");
}


//*********************************************************************
//						listObjects
//*********************************************************************
// This function produces a string which lists all the objects in a
// player's, room's or object's inventory.

bool listObjectSee(const Player* player, Object* object, bool showAll) {
	return(player->isStaff() || (
		player->canSee(object) &&
		(showAll || !object->flagIsSet(O_HIDDEN)) &&
	    !object->flagIsSet(O_SCENERY)
	) );
}

bstring Container::listObjects(const Player* player, bool showAll, char endColor) const {
	Object	*object=0;
	int		num=1, n=0, flags = player->displayFlags();
	bstring str = "";

	ObjectSet::iterator it;
	for( it = objects.begin() ; it != objects.end() ; ) {
		object = (*it++);
		if(object->inCreature()) {
			if(object->getCreatureParent()->mFlagIsSet(M_TRADES))
				continue;

			if( !showAll && (
					object->flagIsSet(O_NOT_PEEKABLE) ||
					Unique::is(object) ||
					(object->inMonster() && object->flagIsSet(O_BODYPART)) ) )
				continue;
		}

		if(!listObjectSee(player, object, showAll))
			continue;

		num = 1;
		while(it != objects.end()) {
			if(object->showAsSame(player, (*it)) && listObjectSee(player, (*it), showAll)) {
				num++;
				it++;
			} else
				break;
		}
		if(n)
			str += ", ";
		str += object->getObjStr(player, flags, num);
		str += "^";
		str += endColor;
		n++;
	}
	return(str);
}


//*********************************************************************
//						randomEnchant
//*********************************************************************
// This function randomly enchants an object if its random-enchant flag
// is set.

void Object::randomEnchant(int bonus) {
	char	m = mrand(1,100);
	m += bonus;

	if(m > 98)
		setAdjustment(3);
	else if(m > 90)
		setAdjustment(2);
	else if(m > 50)
		setAdjustment(1);

	if(flagIsSet(O_CURSED) && adjustment > 0)
		setAdjustment(adjustment * -1);

	//armor += (int)(adjustment*4.4);
}

// min int 13, cast flag, or mage class
int new_scroll(int level, Object **new_obj) {
	int lvl, realm;


	const char	*delem;
	char	name[128];
	int		num=0;
	char	*p;
	*new_obj = new Object;
	// level 2-6 lvl 1 scrolls, level 7-11 lvl 2 scrolls, level 12-17 lvl 3 scrolls,
	// lvl 18-24 lvl 4 scrolls, lvl 25+ lvl 5 scrolls

	if(level == 1)
		return (-1);

	if(level < 7)
		lvl = 1;
	else if(level < 11)
		lvl = 2;
	else if(level < 17)
		lvl = 3;
	else if(level < 25)
		lvl = 4;
	else
		lvl = 5;

	lvl = MIN(lvl,(mrand(1,100)<=10 ? 3:2));

	if(mrand(1,100) > 10)
		realm = mrand(EARTH,WATER);
	else
		realm = mrand(EARTH,COLD); // cold/elect rarer



	// No index for this object, need to set the save full flag
	(*new_obj)->setFlag(O_SAVE_FULL);

	(*new_obj)->setMagicpower(ospell[(lvl-1)*6 +(realm-1)].splno+1);
	(*new_obj)->description = "It has arcane runes.";
	delem = " ";

	num = mrand(1,10);
	strcpy(name, scrollDesc[realm-1][num-1]);
	strcat(name, " ");
	num = mrand(1,2);
	strcat(name, scrollType[lvl-1][num-1]);
	(*new_obj)->setName( name);
	p = strtok(name, delem);
	if(p)
		strcpy((*new_obj)->key[0], p);
	p = strtok(NULL, delem);
	if(p)
		strcpy((*new_obj)->key[1], p);
	p = strtok(NULL, delem);
	if(p)
		strcpy((*new_obj)->key[2], p);

	(*new_obj)->setWearflag(HELD);
	(*new_obj)->setType(SCROLL);
	(*new_obj)->setWeight(1);
	(*new_obj)->setShotsMax(1);
	(*new_obj)->setShotsCur(1);

	if(lvl >= 4) {
		lvl -= 4;
		lvl *= 6;
		(*new_obj)->setQuestnum(22 + lvl + (realm-1));
	}

	return(1);
}

int Monster::checkScrollDrop() {
	if(	intelligence.getCur() >= 120 ||
		flagIsSet(M_CAN_CAST) ||
		flagIsSet(M_CAST_PRECENT) ||
		cClass == MAGE ||
		cClass == LICH
	) {
		if(mrand(1,100) <= 4 && !isPet())
			return(1);
	}
	return(0);
}

void Object::loadContainerContents() {
	int		count=0, a=0;
	Object	*newObj=0;

	if(type != CONTAINER)
		return;

	for(a=0; a<3; a++)
		if(in_bag[a].id)
			count++;

	if(!count)
		return;

	for(a=0; a<count; a++) {
		if(!in_bag[a].id)
			continue;
		if(!loadObject(in_bag[a], &newObj))
			continue;

		if(mrand(1,100)<25) {
			newObj->setDroppedBy(this, "ContainerContents");
			addObj(newObj);
		} else {
			delete newObj;
		}
	}
}

//*********************************************************************
//						cmdKeep
//*********************************************************************

int cmdKeep(Player* player, cmd* cmnd) {
	Object	*object = player->findObject(player, cmnd, 1);

	if(!object) {
		player->print("You don't have that in your inventory.\n");
		return(0);
	}
	if(object->flagIsSet(O_KEEP)) {
		player->print("That's already being kept.\n");
		return(0);
	}

	player->printColor("You will keep %P.\n", object);
	object->setFlag(O_KEEP);
	return(0);
}

//*********************************************************************
//						cmdUnkeep
//*********************************************************************

int cmdUnkeep(Player* player, cmd* cmnd) {
	Object	*object=0;

	if(!strcmp(cmnd->str[1], "all")) {
		for(Object* obj : player->objects ) {
			obj->clearFlag(O_KEEP);
		}
		player->print("All kept objects cleared.\n");
		return(0);
	}

	object = player->findObject(player, cmnd, 1);
	if(!object) {
		player->print("That is not in your inventory.\n");
		return(0);
	}

	if(!object->flagIsSet(O_KEEP)) {
		player->print("That is not currently protected by keep.\n");
		return(0);
	}

	player->printColor("You no longer are keeping %P.\n", object);
	object->clearFlag(O_KEEP);
	return(0);
}

//*********************************************************************
//						cantDropInBag
//*********************************************************************

bool cantDropInBag(Object* object) {
	for(Object* obj : object->objects) {
		if(obj->flagIsSet(O_NO_DROP))
			return(true);
	}
	return(false);
}

//*********************************************************************
//						findObj
//*********************************************************************

MudObject* Creature::findObjTarget(ObjectSet &set, int findFlags, bstring str, int val, int* match) {
	if(set.empty())
		return(NULL);

	for(Object* obj : set) {
		if(keyTxtEqual(obj, str.c_str()) && canSee(obj)) {
			(*match)++;
			if(*match == val) {
				return(obj);
			}
		}
	}
	return(NULL);
}

//*********************************************************************
//						displayObject
//*********************************************************************

int displayObject(Player* player, Object* target) {
	int percent=0;
	unsigned int i=0;
	char str[2048];
	char filename[256];
	bstring inv = "";

	int flags = player->displayFlags();

	// special 2 is a combo lock, should have normal descriptions
	if(target->getSpecial() == 1) {
		strcpy(str, target->getCName());
		for(i=0; i<strlen(str); i++)
			if(str[i] == ' ')
				str[i] = '_';
		sprintf(filename, "%s/%s.txt", Path::Sign, str);
		if(target->flagIsSet(O_LOGIN_FILE))
			viewLoginFile(player->getSock(), filename);
		else
			viewFile(player->getSock(), filename);
		return(0);
	}
	std::ostringstream oStr;

	if(target->description != "") {
	    oStr << target->description << "\n";
	} else if(!target->getRecipe()) {
		// don't show this message if we have a recipe
	    oStr << "You see nothing special about it.\n";
	}

	if(target->getRecipe()) {
		Recipe *r = gConfig->getRecipe(target->getRecipe());
		if(!r)
			oStr << "Sorry, that recipe is faulty.\n";
		else
			oStr << r;
	}

	if(target->compass)
		oStr << target->getCompass(player, false);

	if(target->getType() == LOTTERYTICKET)
		oStr << "It is good for powerbone cycle #" << target->getLotteryCycle() << ".\n";

	if(player->isEffected("know-aura") || player->getClass() == PALADIN || player->isCt()) {
		if(target->flagIsSet(O_GOOD_ALIGN_ONLY))
			oStr << "It has a blue aura.\n";
		if(target->flagIsSet(O_EVIL_ALIGN_ONLY))
			oStr << "It has a red aura.\n";
	}

	if(target->getType() == CONTAINER) {
		inv = target->listObjects(player, true);
		if(inv != "")
			oStr << "It contains: " << inv << ".\n";
	}

	if(target->getType() == ARMOR)
	    oStr << target->getObjStr(NULL, flags | CAP, 1) << " is considered ^W" << target->getArmorType() << "^x armor.\n";

    if(target->getType() == WEAPON) {
        oStr << target->getObjStr(NULL, flags | CAP, 1) << " is a " << obj_type(target->getType()) << "(" <<  target->getWeaponType() <<").\n";

        if(target->flagIsSet(O_SILVER_OBJECT))
            oStr << "It is alloyed with pure silver.\n";
    }

	if(target->flagIsSet(O_NO_DROP))
		oStr << target->getObjStr(NULL, flags | CAP, 1) << " cannot be dropped.\n";

	if(Lore::isLore(target))
	    oStr << target->getObjStr(NULL, flags | CAP, 1) << " is an object of lore.\n";

	if(target->flagIsSet(O_DARKMETAL))
		oStr << "^yIt is vulnerable to sunlight.\n";

	if(target->flagIsSet(O_DARKNESS))
		oStr << "^DIt is engulfed by an aura of darkness.\n";

	if(Unique::isUnique(target))
	    oStr << target->getObjStr(NULL, flags | CAP, 1) << " is a unique or limited object.\n";


	if(target->getType() == WEAPON && target->flagIsSet(O_ENVENOMED)) {
		oStr << "^gIt drips with poison.\n";
		if((player->getClass() == ASSASSIN && player->getLevel() >= 10) || player->isCt()) {
			oStr << "^gTime remaining before poison deludes: " <<
			   timestr(MAX(0,(target->lasttime[LT_ENVEN].ltime+target->lasttime[LT_ENVEN].interval-time(0)))) << ".\n";
		}
	}

	if(	target->getType() == POISON && ((player->getClass() == ASSASSIN && player->getLevel() >= 10) || player->isCt()))
	    oStr << target->getObjStr(NULL, flags | CAP, 1) << " is a poison.\n";

	if(target->flagIsSet(O_COIN_OPERATED_OBJECT))
	    oStr << target->getObjStr(NULL, flags | CAP, 1) << " costs " << target->getCoinCost() << "gold coins per use.\n";

	if(target->getShotsCur() > 0)
		percent = (100 * (target->getShotsCur())) / (MAX(target->getShotsMax(),1));
	else
		percent = -1;

	if(player->isCt() || !Unique::isUnique(target)) {
		if(	target->getType() == WEAPON ||
			target->getType() == ARMOR ||
			target->getType() == LIGHTSOURCE ||
			target->getType() == WAND ||
			target->getType() == KEY ||
			target->getType() == POISON ||
			target->getType() == BANDAGE
		) {
			if(percent >= 90)
				oStr << "It is in pristine condition.\n";
			else if(percent >= 75)
			    oStr << "It is in excellent condition.\n";
			else if(percent >= 60)
			    oStr << "It is in good condition.\n";
			else if(percent >= 45)
			    oStr << "It has a few scratches.\n";
			else if(percent >= 30)
			    oStr << "It has many scratches and dents.\n";
			else if(percent >= 10)
			    oStr << "It is in bad condition.\n";
			else if(target->getShotsCur())
			    oStr << "It looks like it could fall apart any moment now.\n";
			else
				oStr << "It is broken or used up.\n";
		}

		if(target->flagIsSet(O_TEMP_ENCHANT) && (player->getClass() == MAGE
				|| player->isCt() || player->isEffected("detect-magic")))
		    oStr << "It is weakly enchanted.\n";

	}

	if(target->flagIsSet(O_WEAPON_CASTS) && flags && MAG) {
        if(target->getChargesCur() > 0)
            percent = (100 * (target->getChargesCur())) / (MAX(target->getChargesMax(),1));
        else
            percent = -1;

        if(percent >= 90)
            oStr << "It shimmers brightly.\n";
        else if(percent >= 75)
            oStr << "It has a bright glow about it.\n";
        else if(percent >= 50)
            oStr << "It has a glow about it.\n";
        else if(percent >= 30)
            oStr << "It has a dull glow about it.\n";
        else if(percent >= 10)
            oStr << "It has a faint glow about it.\n";
        else if(target->getChargesCur())
            oStr << "It has a very faint glow about it.\n";
        else
            oStr << "It no longer glows.\n";
	}


	if(target->getSize() != NO_SIZE)
	    oStr << "It is ^W" << getSizeName(target->getSize()).c_str() << "^x.\n";

	if(target->getQuestOwner() != "") {
		if(target->isQuestOwner(player))
			oStr << "You own "<< target->getObjStr(NULL, flags, 1) << ".\n";
		else
		    oStr << "You do not own "<< target->getObjStr(NULL, flags, 1) << ".\n";
	}

	if(target->getType() == POTION || target->getType() == HERB) {
	    oStr << target->showAlchemyEffects(player) << "\n";
	}

	player->printColor("%s", oStr.str().c_str());
	return(0);
}

//*********************************************************************
//						getDamageString
//*********************************************************************

void getDamageString(char atk[50], Creature* player, Object *weapon, bool critical) {
	if(!weapon) {
		strcpy(atk, "^Rpunched^x");

		if(player->getClass() == MONK) {
			switch(critical ? mrand(10,12) : mrand(1,9)) {
			case 1: strcpy(atk, "punched");				break;
			case 2: strcpy(atk, "backhanded");			break;
			case 3: strcpy(atk, "smacked");				break;
			case 4: strcpy(atk, "roundhouse kicked");	break;
			case 5: strcpy(atk, "chopped"); 			break;
			case 6: strcpy(atk, "slapped"); 			break;
			case 7: strcpy(atk, "whacked"); 			break;
			case 8: strcpy(atk, "jabbed"); 				break;
			case 9: strcpy(atk, "throttled"); 			break;
			case 10: strcpy(atk, "decimated"); 			break;
			case 11: strcpy(atk, "annihilated"); 		break;
			case 12: strcpy(atk, "obliterated"); 		break;
			default:
				break;
			}
		} else if(player->isEffected("lycanthropy")) {
			switch(critical ? mrand(10,12) : mrand(1,9)) {
			case 1: strcpy(atk, "clawed");			break;
			case 2: strcpy(atk, "rended");			break;
			case 3: strcpy(atk, "ripped");			break;
			case 4: strcpy(atk, "slashed");			break;
			case 5: strcpy(atk, "cleaved");			break;
			case 6: strcpy(atk, "shredded");		break;
			case 7: strcpy(atk, "thrashed");		break;
			case 8: strcpy(atk, "maimed"); 			break;
			case 9: strcpy(atk, "mangled"); 		break;
			case 10: strcpy(atk, "mutilated"); 		break;
			case 11: strcpy(atk, "disembowled"); 	break;
			case 12: strcpy(atk, "slaughtered"); 	break;
			default: strcpy(atk, "clawed"); 		break;
			}
		}
	} else if(weapon->use_attack[0]) {
		strcpy(atk, weapon->use_attack);
	} else {
		strcpy(atk, weapon->getWeaponVerbPast().c_str());
	}
}

//*********************************************************************
//						getWeaponDelay
//*********************************************************************

bool Object::showAsSame(const Player* player, const Object* object) const {
	return(	getName() == object->getName() &&
			flagIsSet(O_KEEP) == object->flagIsSet(O_KEEP) &&
			isEffected("invisibility") == object->isEffected("invisibility") &&
			isBroken() == object->isBroken() &&
			flagIsSet(O_BEING_PREPARED) == object->flagIsSet(O_BEING_PREPARED) &&
			(	adjustment == object->adjustment ||
				(!player || !player->isEffected("detect-magic")) ||
				(flagIsSet(O_NULL_MAGIC) && object->flagIsSet(O_NULL_MAGIC))
			) &&
			(!player || (player && player->canSee(object)))
	);
}

//*********************************************************************
//						nameCoin
//*********************************************************************

void Object::nameCoin(bstring type, unsigned long value) {
	char temp[80];
	snprintf(temp, 80, "%lu %s coin%s", value, type.c_str(), value != 1 ? "s" : "");
	setName(temp);
}

//*********************************************************************
//						killMortalObjectsOnFloor
//*********************************************************************

void BaseRoom::killMortalObjectsOnFloor() {
	UniqueRoom*	room = getAsUniqueRoom();

	// if area = shop, kill in main room, but not in storage
	if(room && room->info.isArea("shop") && !room->flagIsSet(R_SHOP))
		return;

	Object* object=0;
	bool	sunlight = isSunlight(), isStor = room && room->info.isArea("stor");
	ObjectSet::iterator it;
	for( it = objects.begin() ; it != objects.end() ; ) {
		object = (*it++);

		// if area = stor, don't kill in storage chest
		if(isStor && object->info.id == 1 && object->info.isArea("stor"))
			continue;

		if(sunlight && object->flagIsSet(O_DARKMETAL)) {
			broadcast(NULL, this, "^yThe %s^y was destroyed by the sunlight!", object->getCName());
			object->deleteFromRoom();
			delete object;
		} else if(!Unique::canLoad(object)) {
			broadcast(NULL, this, "^yThe %s^y vanishes!", object->getCName());
			object->deleteFromRoom();
			delete object;
		} else
			object->killUniques();
	}
}

//*********************************************************************
//						killMortalObjects
//*********************************************************************

void BaseRoom::killMortalObjects(bool floor) {
	if(tempNoKillDarkmetal)
		return;
	if(isSunlight()) {
		// kill all players' darkmetal
	    for(Player* ply : players) {
	        ply->killDarkmetal();
	    }
	    for(Monster* mons : monsters) {
	        mons->killDarkmetal();
	    }
	}
	if(floor)
		killMortalObjectsOnFloor();
}

//*********************************************************************
//						killMortalObjects
//*********************************************************************
// if allowed, will kill the darkmetal the target carries

void Creature::killDarkmetal() {
	Player	*pTarget = getAsPlayer();
	Object	*object=0;
	int		i=0;
	bool	found=false;

	if(!getRoomParent()->isSunlight())
		return;

	if(pTarget) {
		if( pTarget->getSock() == NULL ||
			pTarget->isStaff() ||
			!pTarget->flagIsSet(P_DARKMETAL))
			return;
	}

	// kill anything not in a bag
	ObjectSet::iterator it;
	for( it = objects.begin() ; it != objects.end() ; ) {
		object = (*it++);
		if(object && object->flagIsSet(O_DARKMETAL)) {
			if(pTarget)
				printColor("^yYour %s was destroyed by the sunlight!\n", object->getCName());
			else if(isPet())
				printColor("^y%M's %s was destroyed by the sunlight!\n", this, object->getCName());
			delObj(object, true, false, false, false);
			delete object;
			found = true;
		}
	}

	// go no further on mobs
	if(!pTarget) {
		checkDarkness();
		return;
	}

	// that includes EQ!
	for(i=0; i<MAXWEAR; i++) {
		if( pTarget->ready[i] &&
			pTarget->ready[i]->flagIsSet(O_DARKMETAL)
		) {
			printColor("^yYour %s was destroyed by the sunlight!\n", pTarget->ready[i]->getCName());
			// i is wearloc-1, so add 1.   Delete it when done
			pTarget->unequip(i+1, UNEQUIP_DELETE, false);
			found = true;
		}
	}

	if(found) {
		pTarget->checkDarkness();
		pTarget->computeAC();
		pTarget->computeAttackPower();
	}

	pTarget->clearFlag(P_DARKMETAL);
	pTarget->checkDarkness();
}

//*********************************************************************
//						setTempNoKillDarkmetal
//*********************************************************************

void BaseRoom::setTempNoKillDarkmetal(bool noKillDarkmetal) {
	tempNoKillDarkmetal = noKillDarkmetal;
}

//*********************************************************************
//						killUniques
//*********************************************************************

void Monster::killUniques() {
	Object* object=0;
	ObjectSet::iterator it;
	for(it = objects.begin() ; it != objects.end() ; ) {
		object = (*it++);

		if(!Unique::canLoad(object)) {
			delObj(object);
			delete object;
		}
	}
}

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

		if(!Unique::canLoad(object)) {
			delObj(object);
			delete object;
		}
	}
}

//*********************************************************************
//						getMaterialName
//*********************************************************************

bstring getMaterialName(Material material) {
	switch(material) {
	case WOOD:
		return("wood");
	case GLASS:
		return("glass");
	case CLOTH:
		return("cloth");
	case PAPER:
		return("paper");
	case IRON:
		return("iron");
	case STEEL:
		return("steel");
	case MITHRIL:
		return("mithril");
	case ADAMANTIUM:
		return("adamantium");
	case STONE:
		return("stone");
	case ORGANIC:
		return("organic");
	case BONE:
		return("bone");
	case LEATHER:
		return("leather");
	default:
		return("none");
	}
}

short Object::getWeight() const { return(weight); }
short Object::getBulk() const { return(bulk); }
short Object::getMaxbulk() const { return(maxbulk); }
Size Object::getSize() const { return(size); }
short Object::getType() const { return(type); }
short Object::getWearflag() const { return(wearflag); }
short Object::getArmor() const { return(armor); }
short Object::getQuality() const { return(quality); }
short Object::getAdjustment() const { return(adjustment); }
short Object::getNumAttacks() const { return(numAttacks); }
short Object::getShotsMax() const { return(shotsMax); }
short Object::getShotsCur() const { return(shotsCur); }
short Object::getChargesMax() const { return(chargesMax); }
short Object::getChargesCur() const { return(chargesCur); }
short Object::getMagicpower() const { return(magicpower); }
short Object::getLevel() const { return(level); }
int Object::getRequiredSkill() const { return(requiredSkill); }
short Object::getMinStrength() const { return(minStrength); }
short Object::getClan() const { return(clan); }
short Object::getSpecial() const { return(special); }
short Object::getQuestnum() const { return(questnum); }
bstring Object::getEffect() const { return(effect); }
long Object::getEffectDuration() const { return(effectDuration); }
short Object::getEffectStrength() const { return(effectStrength); }
unsigned long Object::getCoinCost() const { return(coinCost); }
unsigned long Object::getShopValue() const { return(shopValue); }
long Object::getMade() const { return(made); }
int Object::getLotteryCycle() const { return(lotteryCycle); }
short Object::getLotteryNumbers(short i) const {return(lotteryNumbers[i]); }
int Object::getRecipe() const { return(recipe); }
Material Object::getMaterial() const { return(material); }
const bstring Object::getSubType() const { return(subType); }
short Object::getDelay() const { return(delay); }
short Object::getExtra() const { return(extra); }
short Object::getWeaponDelay() const {
	if(!this || !delay)
		return(DEFAULT_WEAPON_DELAY);
	return(delay);
}
bstring Object::getQuestOwner() const { return(questOwner); }

//*********************************************************************
//						isQuestOwner
//*********************************************************************
// This is only used for determining certain objects for specific reasons
// (usually quest related). This should not be used for general in-my-inventory
// ownership.

bool Object::isQuestOwner(const Player* player) const {
	if(questOwner == "")
		return(true);
	if(questOwner != player->getName())
		return(false);
	// for sui/remake, a player of the same name will fail this check
	if(player->getCreated() > getMade())
		return(false);
	return(true);
}

//*********************************************************************
//						getWearName
//*********************************************************************

bstring Object::getWearName() {
	switch(wearflag) {
	case 1:
		return("Body");
	case 2:
		return("Arms");
	case 3:
		return("Legs");
	case 4:
		return("Neck");
	case 5:
		return("Belt/Waist");
	case 6:
		return("Hands");
	case 7:
		return("Head");
	case 8:
		return("Feet");
	case 9:
		return("Finger");
	case 10:
	case 11:
	case 12:
	case 13:
	case 14:
	case 15:
	case 16:
		return("Finger (won't work - set to wearloc 9)");
	case 17:
		return("Hold");
	case 18:
		return("Shield");
	case 19:
		return("Face");
	case 20:
		return("Wielded weapon");
	default:
		return("Unknown");
	}
}

//*********************************************************************
//						count_obj
//*********************************************************************
// Return the total number of objects contained within an object.
// If perm_only != 0, then only the permanent objects are counted.

int Object::countObj(bool permOnly) {
	int	total=0;
	for(Object* obj : objects) {
		if(!permOnly || (permOnly && (obj->flagIsSet(O_PERM_ITEM))))
			total++;
	}
	return(total);
}