////////////////////////////////////////////////////////////////////////
// OpenTibia - an opensource roleplaying game
////////////////////////////////////////////////////////////////////////
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
////////////////////////////////////////////////////////////////////////
#include "otpch.h"
#include
#include
#include "item.h"
#include "container.h"
#include "depot.h"
#include "teleport.h"
#include "trashholder.h"
#include "mailbox.h"
#include "luascript.h"
#include "combat.h"
#include "house.h"
#include "beds.h"
#include "actions.h"
#include "configmanager.h"
#include "game.h"
extern Game g_game;
extern ConfigManager g_config;
Items Item::items;
Item* Item::CreateItem(const uint16_t _type, uint16_t _count /*= 1*/)
{
Item* newItem = NULL;
const ItemType& it = Item::items[_type];
if(it.group == ITEM_GROUP_DEPRECATED)
{
#ifdef __DEBUG__
std::cout << "Error: [Item::CreateItem] Item id " << it.id << " has been declared deprecated." << std::endl;
#endif
return NULL;
}
if(it.id)
{
if(it.isDepot())
newItem = new Depot(_type);
else if(it.isContainer())
newItem = new Container(_type);
else if(it.isTeleport())
newItem = new Teleport(_type);
else if(it.isMagicField())
newItem = new MagicField(_type);
else if(it.isDoor())
newItem = new Door(_type);
else if(it.isTrashHolder())
newItem = new TrashHolder(_type, it.magicEffect);
else if(it.isMailbox())
newItem = new Mailbox(_type);
else if(it.isBed())
newItem = new BedItem(_type);
else if(it.id >= 2210 && it.id <= 2212)
newItem = new Item(_type - 3, _count);
else if(it.id == 2215 || it.id == 2216)
newItem = new Item(_type - 2, _count);
else if(it.id >= 2202 && it.id <= 2206)
newItem = new Item(_type - 37, _count);
else if(it.id == 2640)
newItem = new Item(6132, _count);
else if(it.id == 6301)
newItem = new Item(6300, _count);
else
newItem = new Item(_type, _count);
newItem->useThing2();
}
return newItem;
}
Item* Item::CreateItem(PropStream& propStream)
{
uint16_t _id;
if(!propStream.GET_USHORT(_id))
return NULL;
return Item::CreateItem(items.getRandomizedItem(_id), 0);
}
Item::Item(const uint16_t _type, uint16_t _count /*= 0*/):
ItemAttributes()
{
id = _type;
count = 1;
const ItemType& it = items[id];
if(it.charges)
setCharges(it.charges);
if(it.isFluidContainer() || it.isSplash())
setFluidType(_count);
else if(it.stackable && _count)
count = _count;
else if(it.charges && _count)
setCharges(_count);
loadedFromMap = false;
setDefaultDuration();
}
Item::Item(const Item& i):
Thing(), ItemAttributes()
{
//std::cout << "Item copy constructor " << this << std::endl;
id = i.id;
count = i.count;
m_attributes = i.m_attributes;
if(i.m_firstAttr)
m_firstAttr = new Attribute(*i.m_firstAttr);
}
Item* Item::clone() const
{
Item* tmp = Item::CreateItem(id, count);
tmp->m_attributes = m_attributes;
if(m_firstAttr)
tmp->m_firstAttr = new Attribute(*m_firstAttr);
return tmp;
}
void Item::copyAttributes(Item* item)
{
m_attributes = item->m_attributes;
if(item->m_firstAttr)
m_firstAttr = new Attribute(*item->m_firstAttr);
removeAttribute(ATTR_ITEM_DECAYING);
removeAttribute(ATTR_ITEM_DURATION);
}
Item::~Item()
{
if(getUniqueId())
ScriptEnviroment::removeUniqueThing(this);
}
void Item::setDefaultSubtype()
{
count = 1;
const ItemType& it = items[id];
if(it.charges)
setCharges(it.charges);
}
void Item::setID(uint16_t newid)
{
const ItemType& prevIt = Item::items[id];
id = newid;
const ItemType& it = Item::items[newid];
uint32_t newDuration = it.decayTime * 1000;
if(newDuration == 0 && !it.stopTime && it.decayTo == -1)
{
removeAttribute(ATTR_ITEM_DECAYING);
removeAttribute(ATTR_ITEM_DURATION);
}
if(hasAttribute(ATTR_ITEM_CORPSEOWNER))
removeAttribute(ATTR_ITEM_CORPSEOWNER);
if(newDuration > 0 && (!prevIt.stopTime || !hasAttribute(ATTR_ITEM_DURATION)))
{
setDecaying(DECAYING_FALSE);
setDuration(newDuration);
}
}
uint16_t Item::getSubType() const
{
const ItemType& it = items[getID()];
if(it.isFluidContainer() || it.isSplash())
return getFluidType();
if(it.charges)
return getCharges();
return count;
}
void Item::setSubType(uint16_t n)
{
const ItemType& it = items[id];
if(it.isFluidContainer() || it.isSplash())
setFluidType(n);
else if(it.charges)
setCharges(n);
else
count = n;
}
bool Item::readAttr(AttrTypes_t attr, PropStream& propStream)
{
switch(attr)
{
case ATTR_COUNT:
{
uint8_t _count = 0;
if(!propStream.GET_UCHAR(_count))
return false;
setSubType(_count);
break;
}
case ATTR_ACTION_ID:
{
uint16_t _actionid = 0;
if(!propStream.GET_USHORT(_actionid))
return false;
setActionId(_actionid);
break;
}
case ATTR_UNIQUE_ID:
{
uint16_t _uniqueid;
if(!propStream.GET_USHORT(_uniqueid))
return false;
setUniqueId(_uniqueid);
break;
}
case ATTR_NAME:
{
std::string _name;
if(!propStream.GET_STRING(_name))
return false;
setName(_name);
break;
}
case ATTR_PLURALNAME:
{
std::string _pluralname;
if(!propStream.GET_STRING(_pluralname))
return false;
setPluralName(_pluralname);
break;
}
case ATTR_ARTICLE:
{
std::string _article;
if(!propStream.GET_STRING(_article))
return false;
setArticle(_article);
break;
}
case ATTR_ATTACK:
{
int32_t _attack;
if(!propStream.GET_ULONG((uint32_t&)_attack))
return false;
setAttack(_attack);
break;
}
case ATTR_EXTRAATTACK:
{
int32_t _extraattack;
if(!propStream.GET_ULONG((uint32_t&)_extraattack))
return false;
setExtraAttack(_extraattack);
break;
}
case ATTR_DEFENSE:
{
int32_t _defense;
if(!propStream.GET_ULONG((uint32_t&)_defense))
return false;
setDefense(_defense);
break;
}
case ATTR_EXTRADEFENSE:
{
int32_t _extradefense;
if(!propStream.GET_ULONG((uint32_t&)_extradefense))
return false;
setExtraDefense(_extradefense);
break;
}
case ATTR_ARMOR:
{
int32_t _armor;
if(!propStream.GET_ULONG((uint32_t&)_armor))
return false;
setArmor(_armor);
break;
}
case ATTR_ATTACKSPEED:
{
int32_t _attackspeed;
if(!propStream.GET_ULONG((uint32_t&)_attackspeed))
return false;
setAttackSpeed(_attackspeed);
break;
}
case ATTR_HITCHANCE:
{
int32_t _hitchance;
if(!propStream.GET_ULONG((uint32_t&)_hitchance))
return false;
setHitChance(_hitchance);
break;
}
case ATTR_SCRIPTPROTECTED:
{
uint8_t _scriptProtected = 0;
if(!propStream.GET_UCHAR(_scriptProtected))
return false;
setScriptProtected(_scriptProtected);
break;
}
case ATTR_TEXT:
{
std::string _text;
if(!propStream.GET_STRING(_text))
return false;
setText(_text);
break;
}
case ATTR_WRITTENDATE:
{
uint32_t _writtenDate;
if(!propStream.GET_ULONG(_writtenDate))
return false;
setDate(_writtenDate);
break;
}
case ATTR_WRITTENBY:
{
std::string _writer;
if(!propStream.GET_STRING(_writer))
return false;
setWriter(_writer);
break;
}
case ATTR_DESC:
{
std::string _text;
if(!propStream.GET_STRING(_text))
return false;
setSpecialDescription(_text);
break;
}
case ATTR_RUNE_CHARGES:
{
uint8_t _charges = 1;
if(!propStream.GET_UCHAR(_charges))
return false;
setSubType((uint16_t)_charges);
break;
}
case ATTR_CHARGES:
{
uint16_t _charges = 1;
if(!propStream.GET_USHORT(_charges))
return false;
setSubType(_charges);
break;
}
case ATTR_DURATION:
{
uint32_t duration = 0;
if(!propStream.GET_ULONG(duration))
return false;
setDuration(std::max((uint32_t)0, duration));
break;
}
case ATTR_DECAYING_STATE:
{
uint8_t state = 0;
if(!propStream.GET_UCHAR(state))
return false;
if(state != DECAYING_FALSE)
setDecaying(DECAYING_PENDING);
break;
}
//These should be handled through derived classes
//If these are called then something has changed in the items.otb since the map was saved
//Just read the values
//Depot class
case ATTR_DEPOT_ID:
{
uint16_t _depotId;
if(!propStream.GET_USHORT(_depotId))
return false;
return true;
}
//Door class
case ATTR_HOUSEDOORID:
{
uint8_t _doorId;
if(!propStream.GET_UCHAR(_doorId))
return false;
return true;
}
//Teleport class
case ATTR_TELE_DEST:
{
TeleportDest* _teleDest;
if(!propStream.GET_STRUCT(_teleDest))
return false;
return true;
}
default:
return false;
}
return true;
}
bool Item::unserializeAttr(PropStream& propStream)
{
uint8_t attrType;
while(propStream.GET_UCHAR(attrType) && attrType != ATTR_END)
{
if(!readAttr((AttrTypes_t)attrType, propStream))
{
if(attrType != ATTR_CONTAINER_ITEMS)
std::cout << "Failed to unserialize attr_type: " << (AttrTypes_t)attrType << " for item: " << id << std::endl;
return false;
}
}
return true;
}
bool Item::serializeAttr(PropWriteStream& propWriteStream) const
{
if(isStackable() || isFluidContainer() || isSplash())
{
propWriteStream.ADD_UCHAR(ATTR_COUNT);
propWriteStream.ADD_UCHAR(getSubType());
}
if(hasCharges())
{
propWriteStream.ADD_UCHAR(ATTR_CHARGES);
propWriteStream.ADD_USHORT(getCharges());
}
if(!isNotMoveable())
{
if(getActionId())
{
propWriteStream.ADD_UCHAR(ATTR_ACTION_ID);
propWriteStream.ADD_USHORT(getActionId());
}
if(getUniqueId())
{
propWriteStream.ADD_UCHAR(ATTR_UNIQUE_ID);
propWriteStream.ADD_USHORT(getUniqueId());
}
}
const std::string& _text = getText();
if(_text.length() > 0)
{
propWriteStream.ADD_UCHAR(ATTR_TEXT);
propWriteStream.ADD_STRING(_text);
}
if(getDate())
{
propWriteStream.ADD_UCHAR(ATTR_WRITTENDATE);
propWriteStream.ADD_ULONG(getDate());
}
const std::string& _writer = getWriter();
if(_writer.length() > 0)
{
propWriteStream.ADD_UCHAR(ATTR_WRITTENBY);
propWriteStream.ADD_STRING(_writer);
}
const std::string& _specialDesc = getSpecialDescription();
if(_specialDesc.length() > 0)
{
propWriteStream.ADD_UCHAR(ATTR_DESC);
propWriteStream.ADD_STRING(_specialDesc);
}
if(hasAttribute(ATTR_ITEM_DURATION))
{
propWriteStream.ADD_UCHAR(ATTR_DURATION);
propWriteStream.ADD_ULONG(getDuration());
}
uint32_t decayState = getDecaying();
if(decayState == DECAYING_TRUE || decayState == DECAYING_PENDING)
{
propWriteStream.ADD_UCHAR(ATTR_DECAYING_STATE);
propWriteStream.ADD_UCHAR(decayState);
}
if(getStrAttr(ATTR_ITEM_NAME) != "")
{
const std::string& _name = getName();
if(_name.length() > 0)
{
propWriteStream.ADD_UCHAR(ATTR_NAME);
propWriteStream.ADD_STRING(_name);
}
}
if(getStrAttr(ATTR_ITEM_PLURALNAME) != "")
{
const std::string& _pluralname = getPluralName();
if(_pluralname.length() > 0)
{
propWriteStream.ADD_UCHAR(ATTR_PLURALNAME);
propWriteStream.ADD_STRING(_pluralname);
}
}
if(getStrAttr(ATTR_ITEM_ARTICLE) != "")
{
const std::string& _article = getArticle();
if(_article.length() > 0)
{
propWriteStream.ADD_UCHAR(ATTR_ARTICLE);
propWriteStream.ADD_STRING(_article);
}
}
if(hasAttribute(ATTR_ITEM_ATTACK))
{
propWriteStream.ADD_UCHAR(ATTR_ATTACK);
propWriteStream.ADD_ULONG(getAttack());
}
if(hasAttribute(ATTR_ITEM_EXTRAATTACK))
{
propWriteStream.ADD_UCHAR(ATTR_EXTRAATTACK);
propWriteStream.ADD_ULONG(getExtraAttack());
}
if(hasAttribute(ATTR_ITEM_DEFENSE))
{
propWriteStream.ADD_UCHAR(ATTR_DEFENSE);
propWriteStream.ADD_ULONG(getDefense());
}
if(hasAttribute(ATTR_ITEM_EXTRADEFENSE))
{
propWriteStream.ADD_UCHAR(ATTR_EXTRADEFENSE);
propWriteStream.ADD_ULONG(getExtraDefense());
}
if(hasAttribute(ATTR_ITEM_ARMOR))
{
propWriteStream.ADD_UCHAR(ATTR_ARMOR);
propWriteStream.ADD_ULONG(getArmor());
}
if(hasAttribute(ATTR_ITEM_ATTACKSPEED))
{
propWriteStream.ADD_UCHAR(ATTR_ATTACKSPEED);
propWriteStream.ADD_ULONG(getAttackSpeed());
}
if(hasAttribute(ATTR_ITEM_HITCHANCE))
{
propWriteStream.ADD_UCHAR(ATTR_HITCHANCE);
propWriteStream.ADD_ULONG(getHitChance());
}
if(hasAttribute(ATTR_ITEM_SCRIPTPROTECTED))
{
propWriteStream.ADD_UCHAR(ATTR_SCRIPTPROTECTED);
propWriteStream.ADD_UCHAR(isScriptProtected() ? 1 : 0);
}
return true;
}
bool Item::hasProperty(enum ITEMPROPERTY prop) const
{
const ItemType& it = items[id];
switch(prop)
{
case BLOCKSOLID:
if(it.blockSolid)
return true;
break;
case MOVEABLE:
if(it.moveable && (!isLoadedFromMap() ||
(getUniqueId() == 0 && (getActionId() == 0 || getContainer()))))
return true;
break;
case HASHEIGHT:
if(it.hasHeight)
return true;
break;
case BLOCKPROJECTILE:
if(it.blockProjectile)
return true;
break;
case BLOCKPATH:
if(it.blockPathFind)
return true;
break;
case ISVERTICAL:
if(it.isVertical)
return true;
break;
case ISHORIZONTAL:
if(it.isHorizontal)
return true;
break;
case IMMOVABLEBLOCKSOLID:
if(it.blockSolid && (!it.moveable || (isLoadedFromMap() &&
(getUniqueId() || (getActionId() && getContainer())))))
return true;
break;
case IMMOVABLEBLOCKPATH:
if(it.blockPathFind && (!it.moveable || (isLoadedFromMap() &&
(getUniqueId() || (getActionId() && getContainer())))))
return true;
break;
case SUPPORTHANGABLE:
if(it.isHorizontal || it.isVertical)
return true;
break;
case IMMOVABLENOFIELDBLOCKPATH:
if(!it.isMagicField() && it.blockPathFind && (!it.moveable || (isLoadedFromMap() &&
(getUniqueId() || (getActionId() && getContainer())))))
return true;
break;
case NOFIELDBLOCKPATH:
if(!it.isMagicField() && it.blockPathFind)
return true;
break;
default:
break;
}
return false;
}
double Item::getWeight() const
{
if(isStackable())
return items[id].weight * std::max((int32_t)1, (int32_t)count);
return items[id].weight;
}
std::string Item::getDescription(const ItemType& it, int32_t lookDistance, const Item* item /*= NULL*/,
int32_t subType /*= -1*/, bool addArticle /*= true*/)
{
std::stringstream s;
s << getNameDescription(it, item, subType, addArticle);
if(item)
subType = item->getSubType();
bool dot = true;
if(it.isRune())
{
s << "(";
if(!it.runeSpellName.empty())
s << "\"" << it.runeSpellName << "\", ";
s << "Charges:" << subType << ")";
if(it.runeLevel > 0 || it.runeMagLevel > 0 || (it.vocationString != "" && it.wieldInfo == 0))
{
s << "." << std::endl << "It can only be used";
if(it.vocationString != "" && it.wieldInfo == 0)
s << " by " << it.vocationString;
bool begin = true;
if(it.runeLevel > 0)
{
begin = false;
s << " with level " << it.runeLevel;
}
if(it.runeMagLevel > 0)
{
begin = false;
s << " " << (begin ? "with" : "and") << " magic level " << it.runeMagLevel;
}
if(!begin)
s << " or higher";
}
}
else if(it.weaponType != WEAPON_NONE)
{
s << " (";
bool begin = true;
if(it.weaponType == WEAPON_DIST && it.ammoType != AMMO_NONE)
{
begin = false;
s << "Range:" << int32_t(item ? item->getShootRange() : it.shootRange);
if(it.attack || it.extraAttack || (item && (item->getAttack() || item->getExtraAttack())))
{
s << ", Atk " << std::showpos << int32_t(item ? item->getAttack() : it.attack);
if(it.extraAttack || (item && item->getExtraAttack()))
s << " " << std::showpos << int32_t(item ? item->getExtraAttack() : it.extraAttack) << std::noshowpos;
}
if(it.hitChance != -1 || (item && item->getHitChance() != -1))
s << ", Hit% " << std::showpos << (item ? item->getHitChance() : it.hitChance) << std::noshowpos;
}
else if(it.weaponType != WEAPON_AMMO && it.weaponType != WEAPON_WAND)
{
if(it.attack || it.extraAttack || (item && (item->getAttack() || item->getExtraAttack())))
{
begin = false;
s << "Atk:";
if(it.abilities.elementType != COMBAT_NONE && it.decayTo < 1)
{
s << std::max((int32_t)0, int32_t((item ? item->getAttack() : it.attack) - it.abilities.elementDamage));
if(it.extraAttack || (item && item->getExtraAttack()))
s << " " << std::showpos << int32_t(item ? item->getExtraAttack() : it.extraAttack) << std::noshowpos;
s << " physical + " << it.abilities.elementDamage << " " << getCombatName(it.abilities.elementType);
}
else
{
s << int32_t(item ? item->getAttack() : it.attack);
if(it.extraAttack || (item && item->getExtraAttack()))
s << " " << std::showpos << int32_t(item ? item->getExtraAttack() : it.extraAttack) << std::noshowpos;
}
}
if(it.defense || it.extraDefense || (item && (item->getDefense() || item->getExtraDefense())))
{
if(!begin)
s << ", ";
begin = false;
s << "Def:" << int32_t(item ? item->getDefense() : it.defense);
if(it.extraDefense || (item && item->getExtraDefense()))
s << " " << std::showpos << int32_t(item ? item->getExtraDefense() : it.extraDefense) << std::noshowpos;
}
}
for(uint16_t i = SKILL_FIRST; i <= SKILL_LAST; i++)
{
if(!it.abilities.skills[i])
continue;
if(!begin)
s << ", ";
begin = false;
s << getSkillName(i) << " " << std::showpos << (int32_t)it.abilities.skills[i] << std::noshowpos;
}
if(it.abilities.stats[STAT_MAGICLEVEL])
{
if(!begin)
s << ", ";
begin = false;
s << "magic level " << std::showpos << (int32_t)it.abilities.stats[STAT_MAGICLEVEL] << std::noshowpos;
}
int32_t show = it.abilities.absorbPercent[COMBAT_FIRST];
for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i++)
{
if(it.abilities.absorbPercent[i] == show)
continue;
show = 0;
break;
}
if(!show)
{
bool tmp = true;
for(uint32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++)
{
if(!it.abilities.absorbPercent[i])
continue;
if(tmp)
{
if(!begin)
s << ", ";
begin = tmp = false;
s << "protection ";
}
else
s << ", ";
s << getCombatName((CombatType_t)i) << " " << std::showpos << it.abilities.absorbPercent[i] << std::noshowpos << "%";
}
}
else
{
if(!begin)
s << ", ";
begin = false;
s << "protection all " << std::showpos << show << std::noshowpos << "%";
}
if(it.abilities.speed)
{
if(!begin)
s << ", ";
begin = false;
s << "speed " << std::showpos << (int32_t)(it.abilities.speed / 2) << std::noshowpos;
}
s << ")";
}
else if(it.armor || (item && item->getArmor()) || it.showAttributes)
{
int32_t tmp = it.armor;
if(item)
tmp = item->getArmor();
if(tmp)
s << " (Arm:" << tmp;
for(uint16_t i = SKILL_FIRST; i <= SKILL_LAST; i++)
{
if(it.abilities.skills[i])
s << ", " << getSkillName(i) << " " << std::showpos << (int32_t)it.abilities.skills[i] << std::noshowpos;
}
if(it.abilities.stats[STAT_MAGICLEVEL])
s << ", magic level " << std::showpos << (int32_t)it.abilities.stats[STAT_MAGICLEVEL] << std::noshowpos;
int32_t show = it.abilities.absorbPercent[COMBAT_FIRST];
for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i++)
{
if(it.abilities.absorbPercent[i] == show)
continue;
show = 0;
break;
}
if(!show)
{
bool tmp = true;
for(uint32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++)
{
if(!it.abilities.absorbPercent[i])
continue;
if(tmp)
{
tmp = false;
s << ", protection ";
}
else
s << ", ";
s << getCombatName((CombatType_t)i) << " " << std::showpos << it.abilities.absorbPercent[i] << std::noshowpos << "%";
}
}
else
s << ", protection all " << std::showpos << show << std::noshowpos << "%";
if(it.abilities.speed)
s << ", speed " << std::showpos << (int32_t)(it.abilities.speed / 2) << std::noshowpos;
s << ")";
}
else if(it.isContainer())
s << " (Vol:" << (int32_t)it.maxItems << ")";
else if(it.isKey())
s << " (Key:" << (item ? (int32_t)item->getActionId() : 0) << ")";
else if(it.isFluidContainer())
{
if(subType > 0)
s << " of " << (items[subType].name.length() ? items[subType].name : "unknown");
else
s << ". It is empty";
}
else if(it.isSplash())
{
s << " of ";
if(subType > 0 && items[subType].name.length())
s << items[subType].name;
else
s << "unknown";
}
else if(it.allowDistRead)
{
s << std::endl;
if(item && item->getText() != "")
{
if(lookDistance <= 4)
{
if(item->getWriter().length())
{
s << item->getWriter() << " wrote";
time_t date = item->getDate();
if(date > 0)
{
char buf[16];
formatDate2(date, buf);
s << " on " << buf;
}
s << ": ";
}
else
s << "You read: ";
s << item->getText();
dot = false;
}
else
s << "You are too far away to read it";
}
else
s << "Nothing is written on it";
}
else if(it.levelDoor && item && item->getActionId() >= it.levelDoor &&
item->getActionId() <= (it.levelDoor + g_config.getNumber(ConfigManager::MAXIMUM_DOOR_LEVEL)))
s << " for level " << item->getActionId() - it.levelDoor;
if(it.showCharges)
s << " that has " << subType << " charge" << (subType != 1 ? "s" : "") << " left";
if(it.showDuration)
{
if(item && item->hasAttribute(ATTR_ITEM_DURATION))
{
int32_t duration = item->getDuration() / 1000;
s << " that has energy for ";
if(duration >= 120)
s << duration / 60 << " minutes left";
else if(duration > 60)
s << "1 minute left";
else
s << " less than a minute left";
}
else
s << " that is brand-new";
}
if(dot)
s << ".";
if(it.wieldInfo)
{
s << std::endl << "It can only be wielded properly by ";
if(it.wieldInfo & WIELDINFO_PREMIUM)
s << "premium ";
if(it.wieldInfo & WIELDINFO_VOCREQ)
s << it.vocationString;
else
s << "players";
if(it.wieldInfo & WIELDINFO_LEVEL)
s << " of level " << (int32_t)it.minReqLevel << " or higher";
if(it.wieldInfo & WIELDINFO_MAGLV)
{
if(it.wieldInfo & WIELDINFO_LEVEL)
s << " and";
else
s << " of";
s << " magic level " << (int32_t)it.minReqMagicLevel << " or higher";
}
s << ".";
}
if(lookDistance <= 1)
{
double weight = (!item ? it.weight : item->getWeight());
if(weight > 0)
s << std::endl << getWeightDescription(it, weight);
}
if(it.abilities.elementType != COMBAT_NONE && it.decayTo > 0)
{
s << std::endl << "It is temporarily enchanted with " << getCombatName(it.abilities.elementType) << " (";
s << std::max((int32_t)0, int32_t((item ? item->getAttack() : it.attack) - it.abilities.elementDamage));
if(it.extraAttack || (item && item->getExtraAttack()))
s << " " << std::showpos << int32_t(item ? item->getExtraAttack() : it.extraAttack) << std::noshowpos;
s << " physical + " << it.abilities.elementDamage << " " << getCombatName(it.abilities.elementType) << " damage).";
}
if(item && !item->getSpecialDescription().empty())
s << std::endl << item->getSpecialDescription().c_str();
else if(!it.description.empty() && lookDistance <= 1)
s << std::endl << it.description;
return s.str();
}
std::string Item::getNameDescription(const ItemType& it, const Item* item /*= NULL*/, int32_t subType /*= -1*/, bool addArticle /*= true*/)
{
if(item)
subType = item->getSubType();
std::stringstream s;
if(it.name.length() || (item && item->getName().length()))
{
if(subType > 1 && it.stackable && it.showCount)
s << subType << " " << (item ? item->getPluralName() : it.pluralName);
else
{
if(addArticle)
{
if(item && !item->getArticle().empty())
s << item->getArticle() << " ";
else if(!it.article.empty())
s << it.article << " ";
}
s << (item ? item->getName() : it.name);
}
}
else
s << "an item of type " << it.id << ", please report it to gamemaster";
return s.str();
}
std::string Item::getWeightDescription(const ItemType& it, double weight, uint32_t count /*= 1*/)
{
std::stringstream s;
if(it.stackable && count > 1)
s << "They weigh " << std::fixed << std::setprecision(2) << weight << " oz.";
else
s << "It weighs " << std::fixed << std::setprecision(2) << weight << " oz.";
return s.str();
}
std::string Item::getWeightDescription() const
{
double weight = getWeight();
return (weight > 0 ? getWeightDescription(weight) : "");
}
void Item::setUniqueId(uint16_t n)
{
if(getUniqueId() && getActionId() != 2000)
return;
ItemAttributes::setUniqueId(n);
ScriptEnviroment::addUniqueThing(this);
}
bool Item::canDecay()
{
if(isRemoved())
return false;
if(isLoadedFromMap() && (getUniqueId() || (getActionId() && getContainer())))
return false;
const ItemType& it = Item::items[id];
if(it.decayTo == -1 || it.decayTime == 0)
return false;
return true;
}
void Item::getLight(LightInfo& lightInfo)
{
const ItemType& it = items[id];
lightInfo.color = it.lightColor;
lightInfo.level = it.lightLevel;
}
std::string ItemAttributes::emptyString("");
const std::string& ItemAttributes::getStrAttr(itemAttrTypes type) const
{
if(!validateStrAttrType(type))
return emptyString;
if(Attribute* attr = getAttrConst(type))
return *(std::string*)attr->value;
return emptyString;
}
void ItemAttributes::setStrAttr(itemAttrTypes type, const std::string& value)
{
if(!validateStrAttrType(type))
return;
if(!value.length())
return;
if(Attribute* attr = getAttr(type))
{
if(attr->value)
delete (std::string*)attr->value;
attr->value = (void*)new std::string(value);
}
}
bool ItemAttributes::hasAttribute(itemAttrTypes type) const
{
if(!validateIntAttrType(type))
return false;
Attribute* attr = getAttrConst(type);
return attr != NULL;
}
void ItemAttributes::removeAttribute(itemAttrTypes type)
{
//check if we have it
if((type & m_attributes))
{
//go trough the linked list until find it
Attribute* prevAttr = NULL;
Attribute* curAttr = m_firstAttr;
while(curAttr != NULL)
{
if(curAttr->type == type)
{
//found so remove it from the linked list
if(prevAttr)
prevAttr->next = curAttr->next;
else
m_firstAttr = curAttr->next;
//remove it from flags
m_attributes = m_attributes & ~type;
//delete string if it is string type
if(validateStrAttrType(type))
delete (std::string*)curAttr->value;
//finally delete the attribute and return
delete curAttr;
return;
}
//advance in the linked list
prevAttr = curAttr;
curAttr = curAttr->next;
}
}
}
uint32_t ItemAttributes::getIntAttr(itemAttrTypes type) const
{
if(!validateIntAttrType(type))
return 0;
if(Attribute* attr = getAttrConst(type))
return static_cast(0xFFFFFFFF & reinterpret_cast(attr->value));
return 0;
}
void ItemAttributes::setIntAttr(itemAttrTypes type, int32_t value)
{
if(!validateIntAttrType(type))
return;
if(Attribute* attr = getAttr(type))
attr->value = reinterpret_cast(static_cast(value));
}
void ItemAttributes::increaseIntAttr(itemAttrTypes type, int32_t value)
{
if(!validateIntAttrType(type))
return;
if(Attribute* attr = getAttr(type))
attr->value = reinterpret_cast(static_cast(static_cast(0xFFFFFFFF & reinterpret_cast(attr->value)) + value));
}
bool ItemAttributes::validateIntAttrType(itemAttrTypes type)
{
switch(type)
{
case ATTR_ITEM_ACTIONID:
case ATTR_ITEM_UNIQUEID:
case ATTR_ITEM_OWNER:
case ATTR_ITEM_DURATION:
case ATTR_ITEM_DECAYING:
case ATTR_ITEM_WRITTENDATE:
case ATTR_ITEM_CORPSEOWNER:
case ATTR_ITEM_CHARGES:
case ATTR_ITEM_FLUIDTYPE:
case ATTR_ITEM_DOORID:
case ATTR_ITEM_ATTACK:
case ATTR_ITEM_EXTRAATTACK:
case ATTR_ITEM_DEFENSE:
case ATTR_ITEM_EXTRADEFENSE:
case ATTR_ITEM_ARMOR:
case ATTR_ITEM_SCRIPTPROTECTED:
case ATTR_ITEM_HITCHANCE:
case ATTR_ITEM_SHOOTRANGE:
case ATTR_ITEM_ATTACKSPEED:
return true;
default:
break;
}
return false;
}
bool ItemAttributes::validateStrAttrType(itemAttrTypes type)
{
switch(type)
{
case ATTR_ITEM_DESC:
case ATTR_ITEM_TEXT:
case ATTR_ITEM_WRITTENBY:
case ATTR_ITEM_NAME:
case ATTR_ITEM_PLURALNAME:
case ATTR_ITEM_ARTICLE:
return true;
default:
break;
}
return false;
}
void ItemAttributes::addAttr(Attribute* attr)
{
if(m_firstAttr)
{
Attribute* curAttr = m_firstAttr;
while(curAttr->next)
curAttr = curAttr->next;
curAttr->next = attr;
}
else
m_firstAttr = attr;
m_attributes = m_attributes | attr->type;
}
ItemAttributes::Attribute* ItemAttributes::getAttrConst(itemAttrTypes type) const
{
if((type & m_attributes) == 0)
return NULL;
Attribute* curAttr = m_firstAttr;
while(curAttr)
{
if(curAttr->type == type)
return curAttr;
curAttr = curAttr->next;
}
std::cout << "[Warning - ItemAttributes::getAttrConst] (type & m_attributes) but attribute not found" << std::endl;
return NULL;
}
ItemAttributes::Attribute* ItemAttributes::getAttr(itemAttrTypes type)
{
Attribute* curAttr;
if((type & m_attributes) == 0)
{
curAttr = new Attribute(type);
addAttr(curAttr);
return curAttr;
}
else
{
curAttr = m_firstAttr;
while(curAttr)
{
if(curAttr->type == type)
return curAttr;
curAttr = curAttr->next;
}
}
std::cout << "[Warning - ItemAttributes::getAttr] (type & m_attributes) but attribute not found" << std::endl;
curAttr = new Attribute(type);
addAttr(curAttr);
return curAttr;
}
void ItemAttributes::deleteAttrs(Attribute* attr)
{
if(!attr)
return;
if(validateStrAttrType(attr->type))
delete (std::string*)attr->value;
Attribute* next_attr = attr->next;
attr->next = NULL;
if(next_attr)
deleteAttrs(next_attr);
delete attr;
}
void Item::__startDecaying()
{
g_game.startDecay(this);
}