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