Guest User

item.cpp

a guest
Apr 3rd, 2014
67
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. ////////////////////////////////////////////////////////////////////////
  2. // OpenTibia - an opensource roleplaying game
  3. ////////////////////////////////////////////////////////////////////////
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with this program.  If not, see <http://www.gnu.org/licenses/>.
  16. ////////////////////////////////////////////////////////////////////////
  17. #include "otpch.h"
  18. #include <iostream>
  19. #include <iomanip>
  20.  
  21. #include "item.h"
  22. #include "container.h"
  23. #include "depotchest.h"
  24. #include "depotlocker.h"
  25.  
  26. #include "teleport.h"
  27. #include "trashholder.h"
  28. #include "mailbox.h"
  29.  
  30. #include "luascript.h"
  31. #include "combat.h"
  32.  
  33. #include "house.h"
  34. #include "beds.h"
  35.  
  36. #include "actions.h"
  37. #include "configmanager.h"
  38. #include "game.h"
  39. #include "movement.h"
  40. #include "attributesmod.h"
  41.  
  42. extern Game g_game;
  43. extern ConfigManager g_config;
  44. extern MoveEvents* g_moveEvents;
  45.  
  46. Items Item::items;
  47. Item* Item::CreateItem(const uint16_t type, uint16_t amount/* = 0*/)
  48. {
  49.     const ItemType& it = Item::items[type];
  50.     if(it.group == ITEM_GROUP_DEPRECATED)
  51.     {
  52.         #ifdef __DEBUG__
  53.         std::clog << "[Error - Item::CreateItem] Item " << it.id << " has been declared as deprecated" << std::endl;
  54.         #endif
  55.         return NULL;
  56.     }
  57.  
  58.     if(!it.id)
  59.         return NULL;
  60.  
  61.     Item* newItem = NULL;
  62.     if(it.isDepot())
  63.         newItem = new DepotLocker(type);
  64.     else if(it.isContainer())
  65.         newItem = new Container(type);
  66.     else if(it.isTeleport())
  67.         newItem = new Teleport(type);
  68.     else if(it.isMagicField())
  69.         newItem = new MagicField(type);
  70.     else if(it.isDoor())
  71.         newItem = new Door(type);
  72.     else if(it.isTrashHolder())
  73.         newItem = new TrashHolder(type, it.magicEffect);
  74.     else if(it.isMailbox())
  75.         newItem = new Mailbox(type);
  76.     else if(it.isBed())
  77.         newItem = new BedItem(type);
  78.     else if(it.id >= 2210 && it.id <= 2212)
  79.         newItem = new Item(type - 3, amount);
  80.     else if(it.id == 2215 || it.id == 2216)
  81.         newItem = new Item(type - 2, amount);
  82.     else if(it.id >= 2202 && it.id <= 2206)
  83.         newItem = new Item(type - 37, amount);
  84.     else if(it.id == 2640)
  85.         newItem = new Item(6132, amount);
  86.     else if(it.id == 6301)
  87.         newItem = new Item(6300, amount);
  88.     else if(it.id == 18528)
  89.         newItem = new Item(18408, amount);
  90.     else
  91.         newItem = new Item(type, amount);
  92.  
  93.     newItem->addRef();
  94.     return newItem;
  95. }
  96.  
  97. Item* Item::CreateItem(PropStream& propStream)
  98. {
  99.     uint16_t type;
  100.     if(!propStream.getShort(type))
  101.         return NULL;
  102.  
  103.     return Item::CreateItem(items.getRandomizedItem(type), 0);
  104. }
  105.  
  106. bool Item::loadItem(xmlNodePtr node, Container* parent)
  107. {
  108.     if(!xmlStrcmp(node->name, (const xmlChar*)"item"))
  109.         return false;
  110.  
  111.     int32_t intValue;
  112.     std::string strValue;
  113.  
  114.     Item* item = NULL;
  115.     if(readXMLInteger(node, "id", intValue))
  116.         item = Item::CreateItem(intValue);
  117.  
  118.     if(!item)
  119.         return false;
  120.  
  121.     if(readXMLString(node, "attributes", strValue))
  122.     {
  123.         StringVec v, attr = explodeString(strValue, ";");
  124.         for(StringVec::iterator it = attr.begin(); it != attr.end(); ++it)
  125.         {
  126.             v = explodeString((*it), ",");
  127.             if(v.size() < 2)
  128.                 continue;
  129.  
  130.             if(atoi(v[1].c_str()) || v[1] == "0")
  131.                 item->setAttribute(v[0].c_str(), atoi(v[1].c_str()));
  132.             else
  133.                 item->setAttribute(v[0].c_str(), v[1]);
  134.         }
  135.     }
  136.  
  137.     //compatibility
  138.     if(readXMLInteger(node, "subtype", intValue) || readXMLInteger(node, "subType", intValue))
  139.         item->setSubType(intValue);
  140.  
  141.     if(readXMLInteger(node, "actionId", intValue) || readXMLInteger(node, "actionid", intValue)
  142.         || readXMLInteger(node, "aid", intValue))
  143.         item->setActionId(intValue);
  144.  
  145.     if(readXMLInteger(node, "uniqueId", intValue) || readXMLInteger(node, "uniqueid", intValue)
  146.         || readXMLInteger(node, "uid", intValue))
  147.         item->setUniqueId(intValue);
  148.  
  149.     if(readXMLString(node, "text", strValue))
  150.         item->setText(strValue);
  151.  
  152.     if(item->getContainer())
  153.         loadContainer(node, item->getContainer());
  154.  
  155.     if(parent)
  156.         parent->addItem(item);
  157.  
  158.     return true;
  159. }
  160.  
  161. bool Item::loadContainer(xmlNodePtr parentNode, Container* parent)
  162. {
  163.     for(xmlNodePtr node = parentNode->children; node; node = node->next)
  164.     {
  165.         if(node->type != XML_ELEMENT_NODE)
  166.             continue;
  167.  
  168.         if(!xmlStrcmp(node->name, (const xmlChar*)"item") && !loadItem(node, parent))
  169.             return false;
  170.     }
  171.  
  172.     return true;
  173. }
  174.  
  175. Item::Item(const uint16_t type, uint16_t amount/* = 0*/):
  176.     ItemAttributes(), id(type)
  177. {
  178.     raid = NULL;
  179.     loadedFromMap = false;
  180.  
  181.     setItemCount(1);
  182.     setDefaultDuration();
  183.  
  184.     const ItemType& it = items[type];
  185.     if(it.isFluidContainer() || it.isSplash())
  186.         setFluidType(amount);
  187.     else if(it.stackable)
  188.     {
  189.         if(amount)
  190.             setItemCount(amount);
  191.         else if(it.charges)
  192.             setItemCount(it.charges);
  193.     }
  194.     else if(it.charges)
  195.         setCharges(amount ? amount : it.charges);
  196. }
  197.  
  198. Item* Item::clone() const
  199. {
  200.     Item* tmp = Item::CreateItem(id, count);
  201.     if(!tmp)
  202.         return NULL;
  203.  
  204.     if(!attributes || attributes->empty())
  205.         return tmp;
  206.  
  207.     tmp->createAttributes();
  208.     *tmp->attributes = *attributes;
  209.     tmp->eraseAttribute("uid");
  210.     return tmp;
  211. }
  212.  
  213. void Item::copyAttributes(Item* item)
  214. {
  215.     if(item && item->attributes && !item->attributes->empty())
  216.     {
  217.         createAttributes();
  218.         *attributes = *item->attributes;
  219.         eraseAttribute("uid");
  220.     }
  221.  
  222.     eraseAttribute("decaying");
  223.     eraseAttribute("duration");
  224. }
  225.  
  226. void Item::makeUnique(Item* parent)
  227. {
  228.     if(!parent || !parent->getUniqueId())
  229.         return;
  230.  
  231.     ScriptEnviroment::removeUniqueThing(parent);
  232.     setUniqueId(parent->getUniqueId());
  233.     parent->eraseAttribute("uid");
  234. }
  235.  
  236. void Item::onRemoved()
  237. {
  238.     if(raid)
  239.     {
  240.         raid->unRef();
  241.         raid = NULL;
  242.     }
  243.  
  244.     ScriptEnviroment::removeTempItem(this);
  245.     if(getUniqueId())
  246.         ScriptEnviroment::removeUniqueThing(this);
  247. }
  248.  
  249. void Item::setDefaultSubtype()
  250. {
  251.     setItemCount(1);
  252.     const ItemType& it = items[id];
  253.     if(it.charges)
  254.         setCharges(it.charges);
  255. }
  256.  
  257. void Item::setID(uint16_t newId)
  258. {
  259.     const ItemType& it = Item::items[newId];
  260.     const ItemType& pit = Item::items[id];
  261.     id = newId;
  262.  
  263.     uint32_t newDuration = it.decayTime * 1000;
  264.     if(!newDuration && !it.stopTime && it.decayTo == -1)
  265.     {
  266.         eraseAttribute("decaying");
  267.         eraseAttribute("duration");
  268.     }
  269.  
  270.     eraseAttribute("corpseowner");
  271.     if(newDuration > 0 && (!pit.stopTime || !hasIntegerAttribute("duration")))
  272.     {
  273.         setDecaying(DECAYING_FALSE);
  274.         setDuration(newDuration);
  275.     }
  276. }
  277.  
  278. bool Item::floorChange(FloorChange_t change/* = CHANGE_NONE*/) const
  279. {
  280.     if(change < CHANGE_NONE)
  281.         return Item::items[id].floorChange[change];
  282.  
  283.     for(int32_t i = CHANGE_PRE_FIRST; i < CHANGE_LAST; ++i)
  284.     {
  285.         if(Item::items[id].floorChange[i])
  286.             return true;
  287.     }
  288.  
  289.     return false;
  290. }
  291.  
  292. Player* Item::getHoldingPlayer()
  293. {
  294.     for(Cylinder* p = getParent(); p; p = p->getParent())
  295.     {
  296.         if(p->getCreature())
  297.             return p->getCreature()->getPlayer();
  298.     }
  299.  
  300.     return NULL;
  301. }
  302.  
  303. const Player* Item::getHoldingPlayer() const
  304. {
  305.     return const_cast<Item*>(this)->getHoldingPlayer();
  306. }
  307.  
  308. uint16_t Item::getSubType() const
  309. {
  310.     const ItemType& it = items[id];
  311.     if(it.isFluidContainer() || it.isSplash())
  312.         return getFluidType();
  313.  
  314.     if(it.charges)
  315.         return getCharges();
  316.  
  317.     return count;
  318. }
  319.  
  320. void Item::setSubType(uint16_t n)
  321. {
  322.     const ItemType& it = items[id];
  323.     if(it.isFluidContainer() || it.isSplash())
  324.         setFluidType(n);
  325.     else if(it.charges)
  326.         setCharges(n);
  327.     else
  328.         count = n;
  329. }
  330.  
  331. Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream)
  332. {
  333.     switch(attr)
  334.     {
  335.         case ATTR_COUNT:
  336.         {
  337.             uint8_t _count;
  338.             if(!propStream.getByte(_count))
  339.                 return ATTR_READ_ERROR;
  340.  
  341.             setSubType((uint16_t)_count);
  342.             break;
  343.         }
  344.  
  345.         case ATTR_ACTION_ID:
  346.         {
  347.             uint16_t aid;
  348.             if(!propStream.getShort(aid))
  349.                 return ATTR_READ_ERROR;
  350.  
  351.             setAttribute("aid", aid);
  352.             break;
  353.         }
  354.  
  355.         case ATTR_UNIQUE_ID:
  356.         {
  357.             uint16_t uid;
  358.             if(!propStream.getShort(uid))
  359.                 return ATTR_READ_ERROR;
  360.  
  361.             setUniqueId(uid);
  362.             break;
  363.         }
  364.  
  365.         case ATTR_NAME:
  366.         {
  367.             std::string name;
  368.             if(!propStream.getString(name))
  369.                 return ATTR_READ_ERROR;
  370.  
  371.             setAttribute("name", name);
  372.             break;
  373.         }
  374.  
  375.         case ATTR_PLURALNAME:
  376.         {
  377.             std::string name;
  378.             if(!propStream.getString(name))
  379.                 return ATTR_READ_ERROR;
  380.  
  381.             setAttribute("pluralname", name);
  382.             break;
  383.         }
  384.  
  385.         case ATTR_ARTICLE:
  386.         {
  387.             std::string article;
  388.             if(!propStream.getString(article))
  389.                 return ATTR_READ_ERROR;
  390.  
  391.             setAttribute("article", article);
  392.             break;
  393.         }
  394.  
  395.         case ATTR_ATTACK:
  396.         {
  397.             int32_t attack;
  398.             if(!propStream.getLong((uint32_t&)attack))
  399.                 return ATTR_READ_ERROR;
  400.  
  401.             setAttribute("attack", attack);
  402.             break;
  403.         }
  404.  
  405.         case ATTR_EXTRAATTACK:
  406.         {
  407.             int32_t attack;
  408.             if(!propStream.getLong((uint32_t&)attack))
  409.                 return ATTR_READ_ERROR;
  410.  
  411.             setAttribute("extraattack", attack);
  412.             break;
  413.         }
  414.  
  415.         case ATTR_DEFENSE:
  416.         {
  417.             int32_t defense;
  418.             if(!propStream.getLong((uint32_t&)defense))
  419.                 return ATTR_READ_ERROR;
  420.  
  421.             setAttribute("defense", defense);
  422.             break;
  423.         }
  424.  
  425.         case ATTR_EXTRADEFENSE:
  426.         {
  427.             int32_t defense;
  428.             if(!propStream.getLong((uint32_t&)defense))
  429.                 return ATTR_READ_ERROR;
  430.  
  431.             setAttribute("extradefense", defense);
  432.             break;
  433.         }
  434.  
  435.         case ATTR_ARMOR:
  436.         {
  437.             int32_t armor;
  438.             if(!propStream.getLong((uint32_t&)armor))
  439.                 return ATTR_READ_ERROR;
  440.  
  441.             setAttribute("armor", armor);
  442.             break;
  443.         }
  444.  
  445.         case ATTR_ATTACKSPEED:
  446.         {
  447.             int32_t attackSpeed;
  448.             if(!propStream.getLong((uint32_t&)attackSpeed))
  449.                 return ATTR_READ_ERROR;
  450.  
  451.             setAttribute("attackspeed", attackSpeed);
  452.             break;
  453.         }
  454.  
  455.         case ATTR_HITCHANCE:
  456.         {
  457.             int32_t hitChance;
  458.             if(!propStream.getLong((uint32_t&)hitChance))
  459.                 return ATTR_READ_ERROR;
  460.  
  461.             setAttribute("hitchance", hitChance);
  462.             break;
  463.         }
  464.  
  465.         case ATTR_SCRIPTPROTECTED:
  466.         {
  467.             uint8_t protection;
  468.             if(!propStream.getByte(protection))
  469.                 return ATTR_READ_ERROR;
  470.  
  471.             setAttribute("scriptprotected", protection != 0);
  472.             break;
  473.         }
  474.  
  475.         case ATTR_DUALWIELD:
  476.         {
  477.             uint8_t wield;
  478.             if(!propStream.getByte(wield))
  479.                 return ATTR_READ_ERROR;
  480.  
  481.             setAttribute("dualwield", wield != 0);
  482.             break;
  483.         }
  484.  
  485.         case ATTR_TEXT:
  486.         {
  487.             std::string text;
  488.             if(!propStream.getString(text))
  489.                 return ATTR_READ_ERROR;
  490.  
  491.             setAttribute("text", text);
  492.             break;
  493.         }
  494.  
  495.         case ATTR_WRITTENDATE:
  496.         {
  497.             int32_t date;
  498.             if(!propStream.getLong((uint32_t&)date))
  499.                 return ATTR_READ_ERROR;
  500.  
  501.             setAttribute("date", date);
  502.             break;
  503.         }
  504.  
  505.         case ATTR_WRITTENBY:
  506.         {
  507.             std::string writer;
  508.             if(!propStream.getString(writer))
  509.                 return ATTR_READ_ERROR;
  510.  
  511.             setAttribute("writer", writer);
  512.             break;
  513.         }
  514.  
  515.         case ATTR_DESC:
  516.         {
  517.             std::string text;
  518.             if(!propStream.getString(text))
  519.                 return ATTR_READ_ERROR;
  520.  
  521.             setAttribute("description", text);
  522.             break;
  523.         }
  524.  
  525.         case ATTR_RUNE_CHARGES:
  526.         {
  527.             uint8_t charges;
  528.             if(!propStream.getByte(charges))
  529.                 return ATTR_READ_ERROR;
  530.  
  531.             setSubType((uint16_t)charges);
  532.             break;
  533.         }
  534.  
  535.         case ATTR_CHARGES:
  536.         {
  537.             uint16_t charges;
  538.             if(!propStream.getShort(charges))
  539.                 return ATTR_READ_ERROR;
  540.  
  541.             setSubType(charges);
  542.             break;
  543.         }
  544.  
  545.         case ATTR_DURATION:
  546.         {
  547.             int32_t duration;
  548.             if(!propStream.getLong((uint32_t&)duration))
  549.                 return ATTR_READ_ERROR;
  550.  
  551.             setAttribute("duration", duration);
  552.             break;
  553.         }
  554.  
  555.         case ATTR_DECAYING_STATE:
  556.         {
  557.             uint8_t state;
  558.             if(!propStream.getByte(state))
  559.                 return ATTR_READ_ERROR;
  560.  
  561.             if((ItemDecayState_t)state != DECAYING_FALSE)
  562.                 setAttribute("decaying", (int32_t)DECAYING_PENDING);
  563.  
  564.             break;
  565.         }
  566.  
  567.         //these should be handled through derived classes
  568.         //if these are called then something has changed in the items.otb since the map was saved
  569.         //just read the values
  570.  
  571.         //Depot class
  572.         case ATTR_DEPOT_ID:
  573.         {
  574.             uint16_t depot;
  575.             if(!propStream.getShort(depot))
  576.                 return ATTR_READ_ERROR;
  577.  
  578.             break;
  579.         }
  580.  
  581.         //Door class
  582.         case ATTR_HOUSEDOORID:
  583.         {
  584.             uint8_t door;
  585.             if(!propStream.getByte(door))
  586.                 return ATTR_READ_ERROR;
  587.  
  588.             break;
  589.         }
  590.  
  591.         //Teleport class
  592.         case ATTR_TELE_DEST:
  593.         {
  594.             TeleportDest* dest;
  595.             if(!propStream.getStruct(dest))
  596.                 return ATTR_READ_ERROR;
  597.  
  598.             break;
  599.         }
  600.  
  601.         //Bed class
  602.         case ATTR_SLEEPERGUID:
  603.         {
  604.             uint32_t sleeper;
  605.             if(!propStream.getLong(sleeper))
  606.                 return ATTR_READ_ERROR;
  607.  
  608.             break;
  609.         }
  610.  
  611.         case ATTR_SLEEPSTART:
  612.         {
  613.             uint32_t sleepStart;
  614.             if(!propStream.getLong(sleepStart))
  615.                 return ATTR_READ_ERROR;
  616.  
  617.             break;
  618.         }
  619.  
  620.         //Container class
  621.         case ATTR_CONTAINER_ITEMS:
  622.         {
  623.             uint32_t _count;
  624.             propStream.getLong(_count);
  625.             return ATTR_READ_ERROR;
  626.         }
  627.  
  628.         //ItemAttributes class
  629.         case ATTR_ATTRIBUTE_MAP:
  630.         {
  631.             bool unique = hasIntegerAttribute("uid"), ret = unserializeMap(propStream);
  632.             if(!unique && hasIntegerAttribute("uid")) // unfortunately we have to do this
  633.                 ScriptEnviroment::addUniqueThing(this);
  634.  
  635.             // this attribute has a custom behavior as well
  636.             if(getDecaying() != DECAYING_FALSE)
  637.                 setDecaying(DECAYING_PENDING);
  638.  
  639.             if(ret)
  640.                 break;
  641.         }
  642.  
  643.         default:
  644.             return ATTR_READ_ERROR;
  645.     }
  646.  
  647.     return ATTR_READ_CONTINUE;
  648. }
  649.  
  650. bool Item::unserializeAttr(PropStream& propStream)
  651. {
  652.     uint8_t attrType = ATTR_END;
  653.     while(propStream.getByte(attrType) && attrType != ATTR_END)
  654.     {
  655.         switch(readAttr((AttrTypes_t)attrType, propStream))
  656.         {
  657.             case ATTR_READ_ERROR:
  658.                 return false;
  659.  
  660.             case ATTR_READ_END:
  661.                 return true;
  662.  
  663.             default:
  664.                 break;
  665.         }
  666.     }
  667.  
  668.     return true;
  669. }
  670.  
  671. bool Item::serializeAttr(PropWriteStream& propWriteStream) const
  672. {
  673.     if(isStackable() || isFluidContainer() || isSplash())
  674.     {
  675.         propWriteStream.addByte(ATTR_COUNT);
  676.         propWriteStream.addByte((uint8_t)getSubType());
  677.     }
  678.  
  679.     if(attributes && !attributes->empty())
  680.     {
  681.         propWriteStream.addByte(ATTR_ATTRIBUTE_MAP);
  682.         serializeMap(propWriteStream);
  683.     }
  684.  
  685.     return true;
  686. }
  687.  
  688. bool Item::hasProperty(enum ITEMPROPERTY prop) const
  689. {
  690.     const ItemType& it = items[id];
  691.     switch(prop)
  692.     {
  693.         case BLOCKSOLID:
  694.             if(it.blockSolid)
  695.                 return true;
  696.  
  697.             break;
  698.  
  699.         case MOVABLE:
  700.             if(it.movable && (!loadedFromMap || (!getUniqueId()
  701.                 && (!getActionId() || !getContainer()))))
  702.                 return true;
  703.  
  704.             break;
  705.  
  706.         case HASHEIGHT:
  707.             if(it.hasHeight)
  708.                 return true;
  709.  
  710.             break;
  711.  
  712.         case BLOCKPROJECTILE:
  713.             if(it.blockProjectile)
  714.                 return true;
  715.  
  716.             break;
  717.  
  718.         case BLOCKPATH:
  719.             if(it.blockPathFind)
  720.                 return true;
  721.  
  722.             break;
  723.  
  724.         case ISVERTICAL:
  725.             if(it.isVertical)
  726.                 return true;
  727.  
  728.             break;
  729.  
  730.         case ISHORIZONTAL:
  731.             if(it.isHorizontal)
  732.                 return true;
  733.  
  734.             break;
  735.  
  736.         case IMMOVABLEBLOCKSOLID:
  737.             if(it.blockSolid && (!it.movable || (loadedFromMap &&
  738.                 (getUniqueId() || (getActionId() && getContainer())))))
  739.                 return true;
  740.  
  741.             break;
  742.  
  743.         case IMMOVABLEBLOCKPATH:
  744.             if(it.blockPathFind && (!it.movable || (loadedFromMap &&
  745.                 (getUniqueId() || (getActionId() && getContainer())))))
  746.                 return true;
  747.  
  748.             break;
  749.  
  750.         case SUPPORTHANGABLE:
  751.             if(it.isHorizontal || it.isVertical)
  752.                 return true;
  753.  
  754.             break;
  755.  
  756.         case IMMOVABLENOFIELDBLOCKPATH:
  757.             if(!it.isMagicField() && it.blockPathFind && (!it.movable || (loadedFromMap &&
  758.                 (getUniqueId() || (getActionId() && getContainer())))))
  759.                 return true;
  760.  
  761.             break;
  762.  
  763.         case NOFIELDBLOCKPATH:
  764.             if(!it.isMagicField() && it.blockPathFind)
  765.                 return true;
  766.  
  767.             break;
  768.  
  769.         case FLOORCHANGEDOWN:
  770.             if(it.floorChange[CHANGE_DOWN])
  771.                 return true;
  772.  
  773.             break;
  774.  
  775.         case FLOORCHANGEUP:
  776.             for(uint16_t i = CHANGE_FIRST; i <= CHANGE_PRE_LAST; ++i)
  777.             {
  778.                 if(it.floorChange[i])
  779.                     return true;
  780.             }
  781.  
  782.             break;
  783.  
  784.         default:
  785.             break;
  786.     }
  787.  
  788.     return false;
  789. }
  790.  
  791. double Item::getWeight() const
  792. {
  793.     if(isStackable())
  794.         return items[id].weight * std::max((int32_t)1, (int32_t)count);
  795.  
  796.     return items[id].weight;
  797. }
  798.  
  799. std::string Item::getDescription(const ItemType& it, int32_t lookDistance, const Item* item/* = NULL*/,
  800.     int32_t subType/* = -1*/, bool addArticle/* = true*/)
  801. {
  802.     std::stringstream s;
  803.     s << getNameDescription(it, item, subType, addArticle);
  804.     if(item)
  805.         subType = item->getSubType();
  806.  
  807.     bool dot = true;
  808.     if(it.isRune())
  809.     {
  810.         if(!it.runeSpellName.empty())
  811.             s << "(\"" << it.runeSpellName << "\")";
  812.  
  813.         if(it.runeLevel > 0 || it.runeMagLevel > 0 || (it.vocationString != "" && it.wieldInfo == 0))
  814.         {
  815.             s << "." << std::endl << "It can only be used";
  816.             if(it.vocationString != "" && it.wieldInfo == 0)
  817.                 s << " by " << it.vocationString;
  818.  
  819.             bool begin = true;
  820.             if(it.runeLevel > 0)
  821.             {
  822.                 begin = false;
  823.                 s << " with level " << it.runeLevel;
  824.             }
  825.  
  826.             if(it.runeMagLevel > 0)
  827.             {
  828.                 begin = false;
  829.                 s << " " << (begin ? "with" : "and") << " magic level " << it.runeMagLevel;
  830.             }
  831.  
  832.             if(!begin)
  833.                 s << " or higher";
  834.         }
  835.     }
  836.     else if(it.weaponType != WEAPON_NONE)
  837.     {
  838.         bool begin = true;
  839.         if(it.weaponType == WEAPON_DIST && it.ammoType != AMMO_NONE)
  840.         {
  841.             begin = false;
  842.             s << " (Range:" << int32_t(item ? item->getShootRange() : it.shootRange);
  843.             if(it.attack || it.extraAttack || (item && (item->getAttack() || item->getExtraAttack())))
  844.             {
  845.                 s << ", Atk " << std::showpos << int32_t(item ? item->getAttack() : it.attack);
  846.                 if(it.extraAttack || (item && item->getExtraAttack()))
  847.                     s << " " << std::showpos << int32_t(item ? item->getExtraAttack() : it.extraAttack) << std::noshowpos;
  848.             }
  849.  
  850.             if(it.hitChance != -1 || (item && item->getHitChance() != -1))
  851.                 s << ", Hit% " << std::showpos << (item ? item->getHitChance() : it.hitChance) << std::noshowpos;
  852.  
  853.             if(it.attackSpeed || (item && item->getAttackSpeed()))
  854.                 s << ", AS: " << (item ? item->getAttackSpeed() : it.attackSpeed);
  855.         }
  856.         else if(it.weaponType != WEAPON_AMMO && it.weaponType != WEAPON_WAND)
  857.         {
  858.             if(it.attack || it.extraAttack || (item && (item->getAttack() || item->getExtraAttack())))
  859.             {
  860.                 begin = false;
  861.                 s << " (Atk:";
  862.                 if(it.abilities.elementType != COMBAT_NONE)
  863.                 {
  864.                     s << std::max((int32_t)0, int32_t((item ? item->getAttack() : it.attack) - it.abilities.elementDamage));
  865.                     if(it.extraAttack || (item && item->getExtraAttack()))
  866.                         s << " " << std::showpos << int32_t(item ? item->getExtraAttack() : it.extraAttack) << std::noshowpos;
  867.  
  868.                     s << " physical + " << it.abilities.elementDamage << " " << getCombatName(it.abilities.elementType);
  869.                 }
  870.                 else
  871.                 {
  872.                     s << int32_t(item ? item->getAttack() : it.attack);
  873.                     if(it.extraAttack || (item && item->getExtraAttack()))
  874.                         s << " " << std::showpos << int32_t(item ? item->getExtraAttack() : it.extraAttack) << std::noshowpos;
  875.                 }
  876.             }
  877.  
  878.             if(it.defense || it.extraDefense || (item && (item->getDefense() || item->getExtraDefense())))
  879.             {
  880.                 if(begin)
  881.                 {
  882.                     begin = false;
  883.                     s << " (";
  884.                 }
  885.                 else
  886.                     s << ", ";
  887.  
  888.                 s << "Def:" << int32_t(item ? item->getDefense() : it.defense);
  889.                 if(it.extraDefense || (item && item->getExtraDefense()))
  890.                     s << " " << std::showpos << int32_t(item ? item->getExtraDefense() : it.extraDefense) << std::noshowpos;
  891.             }
  892.         }
  893.  
  894.         for(uint16_t i = SKILL_FIRST; i <= SKILL_LAST; ++i)
  895.         {
  896.             // BEGIN ATTRIBUTES MOD
  897.             // Get the value if any from items attribute list
  898.             boost::any attr = item->getAttribute(CONVERSION::SKILL[i]);
  899.  
  900.             // Make sure we either have abilities or an attribute
  901.             if(!it.abilities.skills[i] && attr.empty())
  902.                 continue;
  903.  
  904.             if(begin)
  905.             {
  906.                 begin = false;
  907.                 s << " (";
  908.             }
  909.             else
  910.                 s << ", ";
  911.  
  912.             // Holds the total increased value
  913.             int32_t val = 0;
  914.  
  915.             // If we have an ability then include that into the answer
  916.             if (it.abilities.skills[i])
  917.                 val = (int32_t)it.abilities.skills[i];
  918.            
  919.             // Make sure the value exist and that it is either float or integer.
  920.             if (!attr.empty() && ((attr.type() == typeid(float)) || (attr.type() == typeid(int32_t))))
  921.             {
  922.                 // Add attribute value to val
  923.                 val += boost::any_cast<int32_t>(attr);
  924.             }
  925.  
  926.             s << getSkillName(i) << " " << std::showpos << val << std::noshowpos;
  927.             // END ATTRIBUTES MOD
  928.         }
  929.  
  930.         // BEGIN ATTRIBUTES MOD
  931.         boost::any attr = item->getAttribute(CONVERSION::STATS[STAT_MAGICLEVEL]);
  932.  
  933.         if(it.abilities.stats[STAT_MAGICLEVEL] || !attr.empty())
  934.         {
  935.             if(begin)
  936.             {
  937.                 begin = false;
  938.                 s << " (";
  939.             }
  940.             else
  941.                 s << ", ";
  942.  
  943.             // Holds the total increased value
  944.             int32_t val = 0;
  945.  
  946.             // If we have an ability then include that into the value
  947.             if (it.abilities.stats[STAT_MAGICLEVEL])
  948.                 val = (int32_t)it.abilities.stats[STAT_MAGICLEVEL];
  949.  
  950.             // Make sure the value exist and that it is either float or integer.
  951.             if (!attr.empty() && ((attr.type() == typeid(float)) || (attr.type() == typeid(int32_t))))
  952.             {
  953.                 // Add attribute value to val
  954.                 val += boost::any_cast<int32_t>(attr);
  955.             }
  956.  
  957.             s << "magic level " << std::showpos << val << std::noshowpos;
  958.         }
  959.         // END ATTRIBUTES MOD
  960.  
  961.             int32_t show = it.abilities->absorb[COMBAT_ALL];
  962.             if(!show)
  963.             {
  964.                 bool tmp = true;
  965.                 for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i <<= 1)
  966.                 {
  967.                     if(!it.abilities->absorb[i])
  968.                         continue;
  969.  
  970.                     if(tmp)
  971.                     {
  972.                         tmp = false;
  973.                         if(begin)
  974.                         {
  975.                             begin = false;
  976.                             s << " (";
  977.                         }
  978.                         else
  979.                             s << ", ";
  980.  
  981.                         s << "protection ";
  982.                     }
  983.                     else
  984.                         s << ", ";
  985.  
  986.                     s << getCombatName((CombatType_t)i) << " " << std::showpos << it.abilities->absorb[i] << std::noshowpos << "%";
  987.                 }
  988.             }
  989.             else
  990.             {
  991.                 if(begin)
  992.                 {
  993.                     begin = false;
  994.                     s << " (";
  995.                 }
  996.                 else
  997.                     s << ", ";
  998.  
  999.                 s << "protection all " << std::showpos << show << std::noshowpos << "%";
  1000.             }
  1001.  
  1002.             show = it.abilities->fieldAbsorb[COMBAT_ALL];
  1003.             if(!show)
  1004.             {
  1005.                 bool tmp = true;
  1006.                 for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i <<= 1)
  1007.                 {
  1008.                     if(!it.abilities->fieldAbsorb[i])
  1009.                         continue;
  1010.  
  1011.                     if(tmp)
  1012.                     {
  1013.                         tmp = false;
  1014.                         if(begin)
  1015.                         {
  1016.                             begin = false;
  1017.                             s << " (";
  1018.                         }
  1019.                         else
  1020.                             s << ", ";
  1021.  
  1022.                         s << "protection ";
  1023.                     }
  1024.                     else
  1025.                         s << ", ";
  1026.  
  1027.                     s << getCombatName((CombatType_t)i) << " field " << std::showpos << it.abilities->absorb[i] << std::noshowpos << "%";
  1028.                 }
  1029.             }
  1030.             else
  1031.             {
  1032.                 if(begin)
  1033.                 {
  1034.                     begin = false;
  1035.                     s << " (";
  1036.                 }
  1037.                 else
  1038.                     s << ", ";
  1039.  
  1040.                 s << "protection all fields " << std::showpos << show << std::noshowpos << "%";
  1041.             }
  1042.  
  1043.             show = it.abilities->reflect[REFLECT_CHANCE][COMBAT_ALL];
  1044.             if(!show)
  1045.             {
  1046.                 bool tmp = true;
  1047.                 for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i <<= 1)
  1048.                 {
  1049.                     if(!it.abilities->reflect[REFLECT_CHANCE][i] || !it.abilities->reflect[REFLECT_PERCENT][i])
  1050.                         continue;
  1051.  
  1052.                     if(tmp)
  1053.                     {
  1054.                         tmp = false;
  1055.                         if(begin)
  1056.                         {
  1057.                             begin = false;
  1058.                             s << " (";
  1059.                         }
  1060.                         else
  1061.                             s << ", ";
  1062.  
  1063.                         s << "reflect: ";
  1064.                     }
  1065.                     else
  1066.                         s << ", ";
  1067.  
  1068.                     s << it.abilities->reflect[REFLECT_CHANCE][i] << "% for ";
  1069.                     if(it.abilities->reflect[REFLECT_PERCENT][i] > 99)
  1070.                         s << "whole";
  1071.                     else if(it.abilities->reflect[REFLECT_PERCENT][i] >= 75)
  1072.                         s << "huge";
  1073.                     else if(it.abilities->reflect[REFLECT_PERCENT][i] >= 50)
  1074.                         s << "medium";
  1075.                     else if(it.abilities->reflect[REFLECT_PERCENT][i] >= 25)
  1076.                         s << "small";
  1077.                     else
  1078.                         s << "tiny";
  1079.  
  1080.                     s << getCombatName((CombatType_t)i);
  1081.                 }
  1082.  
  1083.                 if(!tmp)
  1084.                     s << " damage";
  1085.             }
  1086.             else
  1087.             {
  1088.                 if(begin)
  1089.                 {
  1090.                     begin = false;
  1091.                     s << " (";
  1092.                 }
  1093.                 else
  1094.                     s << ", ";
  1095.  
  1096.                 int32_t tmp = it.abilities->reflect[REFLECT_PERCENT][COMBAT_ALL];
  1097.                 s << "reflect: " << show << "% for ";
  1098.                 if(tmp)
  1099.                 {
  1100.                     if(tmp > 99)
  1101.                         s << "whole";
  1102.                     else if(tmp >= 75)
  1103.                         s << "huge";
  1104.                     else if(tmp >= 50)
  1105.                         s << "medium";
  1106.                     else if(tmp >= 25)
  1107.                         s << "small";
  1108.                     else
  1109.                         s << "tiny";
  1110.                 }
  1111.                 else
  1112.                     s << "mixed";
  1113.  
  1114.                 s << " damage";
  1115.             }
  1116.  
  1117.             if(it.abilities->speed)
  1118.             {
  1119.                 if(begin)
  1120.                 {
  1121.                     begin = false;
  1122.                     s << " (";
  1123.                 }
  1124.                 else
  1125.                     s << ", ";
  1126.  
  1127.                 s << "speed " << std::showpos << (int32_t)(it.abilities->speed / 2) << std::noshowpos;
  1128.             }
  1129.  
  1130.             if(it.abilities->invisible)
  1131.             {
  1132.                 if(begin)
  1133.                 {
  1134.                     begin = false;
  1135.                     s << " (";
  1136.                 }
  1137.                 else
  1138.                     s << ", ";
  1139.  
  1140.                 s << "invisibility";
  1141.             }
  1142.  
  1143.             if(it.abilities->regeneration)
  1144.             {
  1145.                 if(begin)
  1146.                 {
  1147.                     begin = false;
  1148.                     s << " (";
  1149.                 }
  1150.                 else
  1151.                     s << ", ";
  1152.  
  1153.                 s << "faster regeneration";
  1154.             }
  1155.  
  1156.             if(it.abilities->manaShield)
  1157.             {
  1158.                 if(begin)
  1159.                 {
  1160.                     begin = false;
  1161.                     s << " (";
  1162.                 }
  1163.                 else
  1164.                     s << ", ";
  1165.  
  1166.                 s << "mana shield";
  1167.             }
  1168.  
  1169.             if(hasBitSet(CONDITION_DRUNK, it.abilities->conditionSuppressions))
  1170.             {
  1171.                 if(begin)
  1172.                 {
  1173.                     begin = false;
  1174.                     s << " (";
  1175.                 }
  1176.                 else
  1177.                     s << ", ";
  1178.  
  1179.                 s << "hard drinking";
  1180.             }
  1181.         }
  1182.  
  1183.         if(it.dualWield || (item && item->isDualWield()))
  1184.         {
  1185.             if(begin)
  1186.             {
  1187.                 begin = false;
  1188.                 s << " (";
  1189.             }
  1190.             else
  1191.                 s << ", ";
  1192.  
  1193.             s << "dual wielding";
  1194.         }
  1195.  
  1196.         if(!begin)
  1197.             s << ")";
  1198.     }
  1199.     else if(it.armor || (item && item->getArmor()) || it.showAttributes)
  1200.     {
  1201.         int32_t tmp = it.armor;
  1202.         if(item)
  1203.             tmp = item->getArmor();
  1204.  
  1205.         bool begin = true;
  1206.         if(tmp)
  1207.         {
  1208.             s << " (Arm:" << tmp;
  1209.             begin = false;
  1210.         }
  1211.  
  1212.         for(uint16_t i = SKILL_FIRST; i <= SKILL_LAST; ++i)
  1213.         {
  1214.             // BEGIN ATTRIBUTES MOD
  1215.             // Get the value if any from items attribute list
  1216.             boost::any attr = item->getAttribute(CONVERSION::SKILL[i]);
  1217.  
  1218.             // Make sure we either have abilities or an attribute
  1219.             if(!it.abilities.skills[i] && attr.empty())
  1220.                 continue;
  1221.  
  1222.             if(begin)
  1223.             {
  1224.                 begin = false;
  1225.                 s << " (";
  1226.             }
  1227.             else
  1228.                 s << ", ";
  1229.  
  1230.             // Holds the total increased value
  1231.             int32_t val = 0;
  1232.  
  1233.             // If we have an ability then include that into the value
  1234.             if (it.abilities.skills[i])
  1235.                 val = (int32_t)it.abilities.skills[i];
  1236.  
  1237.             // Make sure the value exist and that it is either float or integer.
  1238.             if (!attr.empty() && ((attr.type() == typeid(float)) || (attr.type() == typeid(int32_t))))
  1239.             {
  1240.                 // Add attribute value to val
  1241.                 val += boost::any_cast<int32_t>(attr);
  1242.             }
  1243.  
  1244.             s << getSkillName(i) << " " << std::showpos << val << std::noshowpos;
  1245.             // END ATTRIBUTES MOD
  1246.         }
  1247.  
  1248.         // BEGIN ATTRIBUTES MOD
  1249.         boost::any attr = item->getAttribute(CONVERSION::STATS[STAT_MAGICLEVEL]);
  1250.  
  1251.         if(it.abilities.stats[STAT_MAGICLEVEL] || !attr.empty())
  1252.         {
  1253.             if(begin)
  1254.             {
  1255.                 begin = false;
  1256.                 s << " (";
  1257.             }
  1258.             else
  1259.                 s << ", ";
  1260.  
  1261.             // Holds the total increased value
  1262.             int32_t val = 0;
  1263.  
  1264.             // If we have an ability then include that into the value
  1265.             if (it.abilities.stats[STAT_MAGICLEVEL])
  1266.                 val = (int32_t)it.abilities.stats[STAT_MAGICLEVEL];
  1267.  
  1268.             // Make sure the value exist and that it is either float or integer.
  1269.             if (!attr.empty() && ((attr.type() == typeid(float)) || (attr.type() == typeid(int32_t))))
  1270.             {
  1271.                 // Add attribute value to val
  1272.                 val += boost::any_cast<int32_t>(attr);
  1273.             }
  1274.  
  1275.             s << "magic level " << std::showpos << val << std::noshowpos;
  1276.         }
  1277.         // END ATTRIBUTES MOD
  1278.  
  1279.             int32_t show = it.abilities->absorb[COMBAT_ALL];
  1280.             if(!show)
  1281.             {
  1282.                 bool tmp = true;
  1283.                 for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i <<= 1)
  1284.                 {
  1285.                     if(!it.abilities->absorb[i])
  1286.                         continue;
  1287.  
  1288.                     if(tmp)
  1289.                     {
  1290.                         tmp = false;
  1291.                         if(begin)
  1292.                         {
  1293.                             begin = false;
  1294.                             s << " (";
  1295.                         }
  1296.                         else
  1297.                             s << ", ";
  1298.  
  1299.                         s << "protection ";
  1300.                     }
  1301.                     else
  1302.                         s << ", ";
  1303.  
  1304.                     s << getCombatName((CombatType_t)i) << " " << std::showpos << it.abilities->absorb[i] << std::noshowpos << "%";
  1305.                 }
  1306.             }
  1307.             else
  1308.             {
  1309.                 if(begin)
  1310.                 {
  1311.                     begin = false;
  1312.                     s << " (";
  1313.                 }
  1314.                 else
  1315.                     s << ", ";
  1316.  
  1317.                 s << "protection all " << std::showpos << show << std::noshowpos << "%";
  1318.             }
  1319.  
  1320.             show = it.abilities->reflect[REFLECT_CHANCE][COMBAT_ALL];
  1321.             if(!show)
  1322.             {
  1323.                 bool tmp = true;
  1324.                 for(uint32_t i = (COMBAT_FIRST + 1); i <= COMBAT_LAST; i <<= 1)
  1325.                 {
  1326.                     if(!it.abilities->reflect[REFLECT_CHANCE][i] || !it.abilities->reflect[REFLECT_PERCENT][i])
  1327.                         continue;
  1328.  
  1329.                     if(tmp)
  1330.                     {
  1331.                         tmp = false;
  1332.                         if(begin)
  1333.                         {
  1334.                             begin = false;
  1335.                             s << " (";
  1336.                         }
  1337.                         else
  1338.                             s << ", ";
  1339.  
  1340.                         s << "reflect: ";
  1341.                     }
  1342.                     else
  1343.                         s << ", ";
  1344.  
  1345.                     s << it.abilities->reflect[REFLECT_CHANCE][i] << "% for ";
  1346.                     if(it.abilities->reflect[REFLECT_PERCENT][i] > 99)
  1347.                         s << "whole";
  1348.                     else if(it.abilities->reflect[REFLECT_PERCENT][i] >= 75)
  1349.                         s << "huge";
  1350.                     else if(it.abilities->reflect[REFLECT_PERCENT][i] >= 50)
  1351.                         s << "medium";
  1352.                     else if(it.abilities->reflect[REFLECT_PERCENT][i] >= 25)
  1353.                         s << "small";
  1354.                     else
  1355.                         s << "tiny";
  1356.  
  1357.                     s << getCombatName((CombatType_t)i);
  1358.                 }
  1359.  
  1360.                 if(!tmp)
  1361.                     s << " damage";
  1362.             }
  1363.             else
  1364.             {
  1365.                 if(begin)
  1366.                 {
  1367.                     begin = false;
  1368.                     s << " (";
  1369.                 }
  1370.                 else
  1371.                     s << ", ";
  1372.  
  1373.                 int32_t tmp = it.abilities->reflect[REFLECT_PERCENT][COMBAT_ALL];
  1374.                 s << "reflect: " << show << "% for ";
  1375.                 if(tmp)
  1376.                 {
  1377.                     if(tmp > 99)
  1378.                         s << "whole";
  1379.                     else if(tmp >= 75)
  1380.                         s << "huge";
  1381.                     else if(tmp >= 50)
  1382.                         s << "medium";
  1383.                     else if(tmp >= 25)
  1384.                         s << "small";
  1385.                     else
  1386.                         s << "tiny";
  1387.                 }
  1388.                 else
  1389.                     s << "mixed";
  1390.  
  1391.                 s << " damage";
  1392.             }
  1393.  
  1394.             if(it.abilities->speed)
  1395.             {
  1396.                 if(begin)
  1397.                 {
  1398.                     begin = false;
  1399.                     s << " (";
  1400.                 }
  1401.                 else
  1402.                     s << ", ";
  1403.  
  1404.                 s << "speed " << std::showpos << (int32_t)(it.abilities->speed / 2) << std::noshowpos;
  1405.             }
  1406.  
  1407.             if(it.abilities->invisible)
  1408.             {
  1409.                 if(begin)
  1410.                 {
  1411.                     begin = false;
  1412.                     s << " (";
  1413.                 }
  1414.                 else
  1415.                     s << ", ";
  1416.  
  1417.                 s << "invisibility";
  1418.             }
  1419.  
  1420.             if(it.abilities.regeneration ||
  1421.             !item->getAttribute(CONVERSION::HEALTH_REGEN_GAIN).empty() ||
  1422.             !item->getAttribute(CONVERSION::HEALTH_REGEN_TICK).empty() ||
  1423.             !item->getAttribute(CONVERSION::MANA_REGEN_GAIN).empty() ||
  1424.             !item->getAttribute(CONVERSION::MANA_REGEN_TICK).empty() )
  1425.             // END ATTRIBUTES MOD
  1426.             {  
  1427.                 if(begin)
  1428.                 {
  1429.                     begin = false;
  1430.                     s << " (";
  1431.                 }
  1432.                 else
  1433.                     s << ", ";
  1434.  
  1435.                 s << "faster regeneration";
  1436.             }
  1437.  
  1438.             if(it.abilities->manaShield)
  1439.             {
  1440.                 if(begin)
  1441.                 {
  1442.                     begin = false;
  1443.                     s << " (";
  1444.                 }
  1445.                 else
  1446.                     s << ", ";
  1447.  
  1448.                 s << "mana shield";
  1449.             }
  1450.  
  1451.             if(hasBitSet(CONDITION_DRUNK, it.abilities->conditionSuppressions))
  1452.             {
  1453.                 if(begin)
  1454.                 {
  1455.                     begin = false;
  1456.                     s << " (";
  1457.                 }
  1458.                 else
  1459.                     s << ", ";
  1460.  
  1461.                 s << "hard drinking";
  1462.             }
  1463.  
  1464.             if(!begin)
  1465.                 s << ")";
  1466.         }
  1467.     }
  1468.     else if(it.isContainer())
  1469.         s << " (Vol:" << (int32_t)it.maxItems << ")";
  1470.     else if(it.isKey())
  1471.         s << " (Key:" << (item ? (int32_t)item->getActionId() : 0) << ")";
  1472.     else if(it.isFluidContainer())
  1473.     {
  1474.         if(subType > 0)
  1475.             s << " of " << (items[subType].name.length() ? items[subType].name : "unknown");
  1476.         else
  1477.             s << ". It is empty";
  1478.     }
  1479.     else if(it.isSplash())
  1480.     {
  1481.         s << " of ";
  1482.         if(subType > 0 && items[subType].name.length())
  1483.             s << items[subType].name;
  1484.         else
  1485.             s << "unknown";
  1486.     }
  1487.     else if(it.allowDistRead)
  1488.     {
  1489.         s << "." << std::endl;
  1490.         if(item && !item->getText().empty())
  1491.         {
  1492.             if(lookDistance <= 4)
  1493.             {
  1494.                 if(!item->getWriter().empty())
  1495.                 {
  1496.                     s << item->getWriter() << " wrote";
  1497.                     time_t date = item->getDate();
  1498.                     if(date > 0)
  1499.                         s << " on " << formatDate(date);
  1500.  
  1501.                     s << ": ";
  1502.                 }
  1503.                 else
  1504.                     s << "You read: ";
  1505.  
  1506.                 std::string text = item->getText();
  1507.                 s << text;
  1508.  
  1509.                 char end = *text.rbegin();
  1510.                 if(end == '?' || end == '!' || end == '.')
  1511.                     dot = false;
  1512.             }
  1513.             else
  1514.                 s << "You are too far away to read it";
  1515.         }
  1516.         else
  1517.             s << "Nothing is written on it";
  1518.     }
  1519.     else if(it.levelDoor && item && item->getActionId() >= (int32_t)it.levelDoor && item->getActionId()
  1520.         <= ((int32_t)it.levelDoor + g_config.getNumber(ConfigManager::MAXIMUM_DOOR_LEVEL)))
  1521.         s << " for level " << item->getActionId() - it.levelDoor;
  1522.  
  1523.     if(it.showCharges)
  1524.         s << " that has " << subType << " charge" << (subType != 1 ? "s" : "") << " left";
  1525.  
  1526.     if(it.showDuration)
  1527.     {
  1528.         if(item && item->hasIntegerAttribute("duration"))
  1529.         {
  1530.             int32_t duration = item->getDuration() / 1000;
  1531.             s << " that will expire in ";
  1532.             if(duration >= 86400)
  1533.             {
  1534.                 uint16_t days = duration / 86400;
  1535.                 uint16_t hours = (duration % 86400) / 3600;
  1536.                 s << days << " day" << (days > 1 ? "s" : "");
  1537.                 if(hours > 0)
  1538.                     s << " and " << hours << " hour" << (hours > 1 ? "s" : "");
  1539.             }
  1540.             else if(duration >= 3600)
  1541.             {
  1542.                 uint16_t hours = duration / 3600;
  1543.                 uint16_t minutes = (duration % 3600) / 60;
  1544.                 s << hours << " hour" << (hours > 1 ? "s" : "");
  1545.                 if(hours > 0)
  1546.                     s << " and " << minutes << " minute" << (minutes > 1 ? "s" : "");
  1547.             }
  1548.             else if(duration >= 60)
  1549.             {
  1550.                 uint16_t minutes = duration / 60;
  1551.                 uint16_t seconds = duration % 60;
  1552.                 s << minutes << " minute" << (minutes > 1 ? "s" : "");
  1553.                 if(seconds > 0)
  1554.                     s << " and " << seconds << " second" << (seconds > 1 ? "s" : "");
  1555.             }
  1556.             else
  1557.                 s << duration << " second" << (duration > 1 ? "s" : "");
  1558.         }
  1559.         else
  1560.             s << " that is brand-new";
  1561.     }
  1562.  
  1563.     if(dot)
  1564.         s << ".";
  1565.  
  1566.     if(it.wieldInfo)
  1567.     {
  1568.         s << std::endl << "It can only be wielded properly by ";
  1569.         if(it.wieldInfo & WIELDINFO_PREMIUM)
  1570.             s << "premium ";
  1571.  
  1572.         if(it.wieldInfo & WIELDINFO_VOCREQ)
  1573.             s << it.vocationString;
  1574.         else
  1575.             s << "players";
  1576.  
  1577.         if(it.wieldInfo & WIELDINFO_LEVEL)
  1578.             s << " of level " << (int32_t)it.minReqLevel << " or higher";
  1579.  
  1580.         if(it.wieldInfo & WIELDINFO_MAGLV)
  1581.         {
  1582.             if(it.wieldInfo & WIELDINFO_LEVEL)
  1583.                 s << " and";
  1584.             else
  1585.                 s << " of";
  1586.  
  1587.             s << " magic level " << (int32_t)it.minReqMagicLevel << " or higher";
  1588.         }
  1589.  
  1590.         s << ".";
  1591.     }
  1592.  
  1593.     if(lookDistance <= 1 && it.pickupable)
  1594.     {
  1595.         std::string tmp;
  1596.         if(!item)
  1597.             tmp = getWeightDescription(it.weight, it.stackable && it.showCount, subType);
  1598.         else
  1599.             tmp = item->getWeightDescription();
  1600.  
  1601.         if(!tmp.empty())
  1602.             s << std::endl << tmp;
  1603.     }
  1604.  
  1605.     if(item && !item->getSpecialDescription().empty())
  1606.         s << std::endl << item->getSpecialDescription();
  1607.     else if(!it.description.empty() && lookDistance <= 1)
  1608.         s << std::endl << it.description;
  1609.  
  1610.     std::string str = s.str();
  1611.     if(str.find("|PLAYERNAME|") != std::string::npos)
  1612.     {
  1613.         std::string tmp = "You";
  1614.         if(item)
  1615.         {
  1616.             if(const Player* player = item->getHoldingPlayer())
  1617.                 tmp = player->getName();
  1618.         }
  1619.  
  1620.         replaceString(str, "|PLAYERNAME|", tmp);
  1621.     }
  1622.  
  1623.     if(str.find("|TIME|") != std::string::npos || str.find("|DATE|") != std::string::npos || str.find(
  1624.         "|DAY|") != std::string::npos || str.find("|MONTH|") != std::string::npos || str.find(
  1625.         "|YEAR|") != std::string::npos || str.find("|HOUR|") != std::string::npos || str.find(
  1626.         "|MINUTES|") != std::string::npos || str.find("|SECONDS|") != std::string::npos ||
  1627.         str.find("|WEEKDAY|") != std::string::npos || str.find("|YEARDAY|") != std::string::npos)
  1628.     {
  1629.         time_t now = time(NULL);
  1630.         tm* ts = localtime(&now);
  1631.  
  1632.         std::stringstream ss;
  1633.         ss << ts->tm_sec;
  1634.         replaceString(str, "|SECONDS|", ss.str());
  1635.  
  1636.         ss.str("");
  1637.         ss << ts->tm_min;
  1638.         replaceString(str, "|MINUTES|", ss.str());
  1639.  
  1640.         ss.str("");
  1641.         ss << ts->tm_hour;
  1642.         replaceString(str, "|HOUR|", ss.str());
  1643.  
  1644.         ss.str("");
  1645.         ss << ts->tm_mday;
  1646.         replaceString(str, "|DAY|", ss.str());
  1647.  
  1648.         ss.str("");
  1649.         ss << (ts->tm_mon + 1);
  1650.         replaceString(str, "|MONTH|", ss.str());
  1651.  
  1652.         ss.str("");
  1653.         ss << (ts->tm_year + 1900);
  1654.         replaceString(str, "|YEAR|", ss.str());
  1655.  
  1656.         ss.str("");
  1657.         ss << ts->tm_wday;
  1658.         replaceString(str, "|WEEKDAY|", ss.str());
  1659.  
  1660.         ss.str("");
  1661.         ss << ts->tm_yday;
  1662.         replaceString(str, "|YEARDAY|", ss.str());
  1663.  
  1664.         ss.str("");
  1665.         ss << ts->tm_hour << ":" << ts->tm_min << ":" << ts->tm_sec;
  1666.         replaceString(str, "|TIME|", ss.str());
  1667.  
  1668.         ss.str("");
  1669.         replaceString(str, "|DATE|", formatDateEx(now));
  1670.     }
  1671.  
  1672.     return str;
  1673. }
  1674.  
  1675. std::string Item::getNameDescription(const ItemType& it, const Item* item/* = NULL*/, int32_t subType/* = -1*/, bool addArticle/* = true*/)
  1676. {
  1677.     if(item)
  1678.         subType = item->getSubType();
  1679.  
  1680.     std::stringstream s;
  1681.     if(it.loaded || (item && !item->getName().empty()))
  1682.     {
  1683.         if(subType > 1 && it.stackable && it.showCount)
  1684.             s << subType << " " << (item ? item->getPluralName() : it.pluralName);
  1685.         else
  1686.         {
  1687.             if(addArticle)
  1688.             {
  1689.                 if(item && !item->getArticle().empty())
  1690.                     s << item->getArticle() << " ";
  1691.                 else if(!it.article.empty())
  1692.                     s << it.article << " ";
  1693.             }
  1694.  
  1695.             s << (item ? item->getName() : it.name);
  1696.         }
  1697.     }
  1698.     else if(it.name.empty())
  1699.         s << "an item of type " << it.id << ", please report it to gamemaster";
  1700.     else
  1701.         s << "an item '" << it.name << "', please report it to gamemaster";
  1702.  
  1703.     return s.str();
  1704. }
  1705.  
  1706. std::string Item::getWeightDescription(double weight, bool stackable, uint32_t count/* = 1*/)
  1707. {
  1708.     if(weight <= 0)
  1709.         return "";
  1710.  
  1711.     std::stringstream s;
  1712.     if(stackable && count > 1)
  1713.         s << "They weigh " << std::fixed << std::setprecision(2) << weight << " oz.";
  1714.     else
  1715.         s << "It weighs " << std::fixed << std::setprecision(2) << weight << " oz.";
  1716.  
  1717.     return s.str();
  1718. }
  1719.  
  1720. void Item::setActionId(int32_t aid, bool callEvent/* = true*/)
  1721. {
  1722.     Tile* tile = NULL;
  1723.     if(callEvent)
  1724.         tile = getTile();
  1725.  
  1726.     if(tile && getActionId())
  1727.         g_moveEvents->onRemoveTileItem(tile, this);
  1728.  
  1729.     setAttribute("aid", aid);
  1730.     if(tile)
  1731.         g_moveEvents->onAddTileItem(tile, this);
  1732. }
  1733.  
  1734. void Item::resetActionId(bool callEvent/* = true*/)
  1735. {
  1736.     if(!getActionId())
  1737.         return;
  1738.  
  1739.     Tile* tile = NULL;
  1740.     if(callEvent)
  1741.         tile = getTile();
  1742.  
  1743.     eraseAttribute("aid");
  1744.     if(tile)
  1745.         g_moveEvents->onAddTileItem(tile, this);
  1746. }
  1747.  
  1748. void Item::setUniqueId(int32_t uid)
  1749. {
  1750.     if(getUniqueId())
  1751.         return;
  1752.  
  1753.     setAttribute("uid", uid);
  1754.     ScriptEnviroment::addUniqueThing(this);
  1755. }
  1756.  
  1757. bool Item::canDecay()
  1758. {
  1759.     if(isRemoved())
  1760.         return false;
  1761.  
  1762.     if(loadedFromMap && (getUniqueId() || (getActionId() && getContainer())))
  1763.         return false;
  1764.  
  1765.     const ItemType& it = Item::items[id];
  1766.     return it.decayTo >= 0 && it.decayTime;
  1767. }
  1768.  
  1769. void Item::getLight(LightInfo& lightInfo)
  1770. {
  1771.     const ItemType& it = items[id];
  1772.     lightInfo.color = it.lightColor;
  1773.     lightInfo.level = it.lightLevel;
  1774. }
  1775.  
  1776. void Item::__startDecaying()
  1777. {
  1778.     g_game.startDecay(this);
  1779. }
RAW Paste Data