Advertisement
Guest User

Untitled

a guest
Oct 19th, 2018
89
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 38.25 KB | None | 0 0
  1. /**
  2.  * The Forgotten Server - a free and open-source MMORPG server emulator
  3.  * Copyright (C) 2017  Mark Samman <mark.samman@gmail.com>
  4.  *
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License along
  16.  * with this program; if not, write to the Free Software Foundation, Inc.,
  17.  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  18.  */
  19.  
  20. #include "otpch.h"
  21.  
  22. #include "item.h"
  23. #include "container.h"
  24. #include "teleport.h"
  25. #include "trashholder.h"
  26. #include "mailbox.h"
  27. #include "house.h"
  28. #include "game.h"
  29. #include "bed.h"
  30.  
  31. #include "actions.h"
  32. #include "spells.h"
  33.  
  34. extern Game g_game;
  35. extern Spells* g_spells;
  36. extern Vocations g_vocations;
  37.  
  38. Items Item::items;
  39.  
  40. Item* Item::CreateItem(const uint16_t type, uint16_t count /*= 0*/)
  41. {
  42.     Item* newItem = nullptr;
  43.  
  44.     const ItemType& it = Item::items[type];
  45.     if (it.group == ITEM_GROUP_DEPRECATED) {
  46.         return nullptr;
  47.     }
  48.  
  49.     if (it.stackable && count == 0) {
  50.         count = 1;
  51.     }
  52.  
  53.     if (it.id != 0) {
  54.         if (it.isDepot()) {
  55.             newItem = new DepotLocker(type);
  56.         } else if (it.isContainer()) {
  57.             newItem = new Container(type);
  58.         } else if (it.isTeleport()) {
  59.             newItem = new Teleport(type);
  60.         } else if (it.isMagicField()) {
  61.             newItem = new MagicField(type);
  62.         } else if (it.isDoor()) {
  63.             newItem = new Door(type);
  64.         } else if (it.isTrashHolder()) {
  65.             newItem = new TrashHolder(type);
  66.         } else if (it.isMailbox()) {
  67.             newItem = new Mailbox(type);
  68.         } else if (it.isBed()) {
  69.             newItem = new BedItem(type);
  70.         } else if (it.id >= 2210 && it.id <= 2212) {
  71.             newItem = new Item(type - 3, count);
  72.         } else if (it.id == 2215 || it.id == 2216) {
  73.             newItem = new Item(type - 2, count);
  74.         } else if (it.id >= 2202 && it.id <= 2206) {
  75.             newItem = new Item(type - 37, count);
  76.         } else if (it.id == 2640) {
  77.             newItem = new Item(6132, count);
  78.         } else if (it.id == 6301) {
  79.             newItem = new Item(6300, count);
  80.         } else if (it.id == 18528) {
  81.             newItem = new Item(18408, count);
  82.         } else {
  83.             newItem = new Item(type, count);
  84.         }
  85.  
  86.         newItem->incrementReferenceCounter();
  87.     }
  88.  
  89.     return newItem;
  90. }
  91.  
  92. Container* Item::CreateItemAsContainer(const uint16_t type, uint16_t size)
  93. {
  94.     const ItemType& it = Item::items[type];
  95.     if (it.id == 0 || it.group == ITEM_GROUP_DEPRECATED || it.stackable || it.useable || it.moveable || it.pickupable || it.isDepot() || it.isSplash() || it.isDoor()) {
  96.         return nullptr;
  97.     }
  98.  
  99.     Container* newItem = new Container(type, size);
  100.     newItem->incrementReferenceCounter();
  101.     return newItem;
  102. }
  103.  
  104. Item* Item::CreateItem(PropStream& propStream)
  105. {
  106.     uint16_t id;
  107.     if (!propStream.read<uint16_t>(id)) {
  108.         return nullptr;
  109.     }
  110.  
  111.     switch (id) {
  112.         case ITEM_FIREFIELD_PVP_FULL:
  113.             id = ITEM_FIREFIELD_PERSISTENT_FULL;
  114.             break;
  115.  
  116.         case ITEM_FIREFIELD_PVP_MEDIUM:
  117.             id = ITEM_FIREFIELD_PERSISTENT_MEDIUM;
  118.             break;
  119.  
  120.         case ITEM_FIREFIELD_PVP_SMALL:
  121.             id = ITEM_FIREFIELD_PERSISTENT_SMALL;
  122.             break;
  123.  
  124.         case ITEM_ENERGYFIELD_PVP:
  125.             id = ITEM_ENERGYFIELD_PERSISTENT;
  126.             break;
  127.  
  128.         case ITEM_POISONFIELD_PVP:
  129.             id = ITEM_POISONFIELD_PERSISTENT;
  130.             break;
  131.  
  132.         case ITEM_MAGICWALL:
  133.             id = ITEM_MAGICWALL_PERSISTENT;
  134.             break;
  135.  
  136.         case ITEM_WILDGROWTH:
  137.             id = ITEM_WILDGROWTH_PERSISTENT;
  138.             break;
  139.  
  140.         default:
  141.             break;
  142.     }
  143.  
  144.     return Item::CreateItem(id, 0);
  145. }
  146.  
  147. Item::Item(const uint16_t type, uint16_t count /*= 0*/) :
  148.     id(type)
  149. {
  150.     const ItemType& it = items[id];
  151.  
  152.     if (it.isFluidContainer() || it.isSplash()) {
  153.         setFluidType(count);
  154.     } else if (it.stackable) {
  155.         if (count != 0) {
  156.             setItemCount(count);
  157.         } else if (it.charges != 0) {
  158.             setItemCount(it.charges);
  159.         }
  160.     } else if (it.charges != 0) {
  161.         if (count != 0) {
  162.             setCharges(count);
  163.         } else {
  164.             setCharges(it.charges);
  165.         }
  166.     }
  167.  
  168.     setDefaultDuration();
  169. }
  170.  
  171. Item::Item(const Item& i) :
  172.     Thing(), id(i.id), count(i.count), loadedFromMap(i.loadedFromMap)
  173. {
  174.     if (i.attributes) {
  175.         attributes.reset(new ItemAttributes(*i.attributes));
  176.     }
  177. }
  178.  
  179. Item* Item::clone() const
  180. {
  181.     Item* item = Item::CreateItem(id, count);
  182.     if (attributes) {
  183.         item->attributes.reset(new ItemAttributes(*attributes));
  184.     }
  185.     return item;
  186. }
  187.  
  188. bool Item::equals(const Item* otherItem) const
  189. {
  190.     if (!otherItem || id != otherItem->id) {
  191.         return false;
  192.     }
  193.  
  194.     if (!attributes) {
  195.         return !otherItem->attributes;
  196.     }
  197.  
  198.     const auto& otherAttributes = otherItem->attributes;
  199.     if (!otherAttributes || attributes->attributeBits != otherAttributes->attributeBits) {
  200.         return false;
  201.     }
  202.  
  203.     const auto& attributeList = attributes->attributes;
  204.     const auto& otherAttributeList = otherAttributes->attributes;
  205.     for (const auto& attribute : attributeList) {
  206.         if (ItemAttributes::isStrAttrType(attribute.type)) {
  207.             for (const auto& otherAttribute : otherAttributeList) {
  208.                 if (attribute.type == otherAttribute.type && *attribute.value.string != *otherAttribute.value.string) {
  209.                     return false;
  210.                 }
  211.             }
  212.         } else {
  213.             for (const auto& otherAttribute : otherAttributeList) {
  214.                 if (attribute.type == otherAttribute.type && attribute.value.integer != otherAttribute.value.integer) {
  215.                     return false;
  216.                 }
  217.             }
  218.         }
  219.     }
  220.     return true;
  221. }
  222.  
  223. void Item::setDefaultSubtype()
  224. {
  225.     const ItemType& it = items[id];
  226.  
  227.     setItemCount(1);
  228.  
  229.     if (it.charges != 0) {
  230.         if (it.stackable) {
  231.             setItemCount(it.charges);
  232.         } else {
  233.             setCharges(it.charges);
  234.         }
  235.     }
  236. }
  237.  
  238. void Item::onRemoved()
  239. {
  240.     ScriptEnvironment::removeTempItem(this);
  241.  
  242.     if (hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
  243.         g_game.removeUniqueItem(getUniqueId());
  244.     }
  245. }
  246.  
  247. void Item::setID(uint16_t newid)
  248. {
  249.     const ItemType& prevIt = Item::items[id];
  250.     id = newid;
  251.  
  252.     const ItemType& it = Item::items[newid];
  253.     uint32_t newDuration = it.decayTime * 1000;
  254.  
  255.     if (newDuration == 0 && !it.stopTime && it.decayTo < 0) {
  256.         removeAttribute(ITEM_ATTRIBUTE_DECAYSTATE);
  257.         removeAttribute(ITEM_ATTRIBUTE_DURATION);
  258.     }
  259.  
  260.     removeAttribute(ITEM_ATTRIBUTE_CORPSEOWNER);
  261.  
  262.     if (newDuration > 0 && (!prevIt.stopTime || !hasAttribute(ITEM_ATTRIBUTE_DURATION))) {
  263.         setDecaying(DECAYING_FALSE);
  264.         setDuration(newDuration);
  265.     }
  266. }
  267.  
  268. Cylinder* Item::getTopParent()
  269. {
  270.     Cylinder* aux = getParent();
  271.     Cylinder* prevaux = dynamic_cast<Cylinder*>(this);
  272.     if (!aux) {
  273.         return prevaux;
  274.     }
  275.  
  276.     while (aux->getParent() != nullptr) {
  277.         prevaux = aux;
  278.         aux = aux->getParent();
  279.     }
  280.  
  281.     if (prevaux) {
  282.         return prevaux;
  283.     }
  284.     return aux;
  285. }
  286.  
  287. const Cylinder* Item::getTopParent() const
  288. {
  289.     const Cylinder* aux = getParent();
  290.     const Cylinder* prevaux = dynamic_cast<const Cylinder*>(this);
  291.     if (!aux) {
  292.         return prevaux;
  293.     }
  294.  
  295.     while (aux->getParent() != nullptr) {
  296.         prevaux = aux;
  297.         aux = aux->getParent();
  298.     }
  299.  
  300.     if (prevaux) {
  301.         return prevaux;
  302.     }
  303.     return aux;
  304. }
  305.  
  306. Tile* Item::getTile()
  307. {
  308.     Cylinder* cylinder = getTopParent();
  309.     //get root cylinder
  310.     if (cylinder && cylinder->getParent()) {
  311.         cylinder = cylinder->getParent();
  312.     }
  313.     return dynamic_cast<Tile*>(cylinder);
  314. }
  315.  
  316. const Tile* Item::getTile() const
  317. {
  318.     const Cylinder* cylinder = getTopParent();
  319.     //get root cylinder
  320.     if (cylinder && cylinder->getParent()) {
  321.         cylinder = cylinder->getParent();
  322.     }
  323.     return dynamic_cast<const Tile*>(cylinder);
  324. }
  325.  
  326. uint16_t Item::getSubType() const
  327. {
  328.     const ItemType& it = items[id];
  329.     if (it.isFluidContainer() || it.isSplash()) {
  330.         return getFluidType();
  331.     } else if (it.stackable) {
  332.         return count;
  333.     } else if (it.charges != 0) {
  334.         return getCharges();
  335.     }
  336.     return count;
  337. }
  338.  
  339. Player* Item::getHoldingPlayer() const
  340. {
  341.     Cylinder* p = getParent();
  342.     while (p) {
  343.         if (p->getCreature()) {
  344.             return p->getCreature()->getPlayer();
  345.         }
  346.  
  347.         p = p->getParent();
  348.     }
  349.     return nullptr;
  350. }
  351.  
  352. void Item::setSubType(uint16_t n)
  353. {
  354.     const ItemType& it = items[id];
  355.     if (it.isFluidContainer() || it.isSplash()) {
  356.         setFluidType(n);
  357.     } else if (it.stackable) {
  358.         setItemCount(n);
  359.     } else if (it.charges != 0) {
  360.         setCharges(n);
  361.     } else {
  362.         setItemCount(n);
  363.     }
  364. }
  365.  
  366. Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream)
  367. {
  368.     switch (attr) {
  369.         case ATTR_COUNT:
  370.         case ATTR_RUNE_CHARGES: {
  371.             uint8_t count;
  372.             if (!propStream.read<uint8_t>(count)) {
  373.                 return ATTR_READ_ERROR;
  374.             }
  375.  
  376.             setSubType(count);
  377.             break;
  378.         }
  379.  
  380.         case ATTR_ACTION_ID: {
  381.             uint16_t actionId;
  382.             if (!propStream.read<uint16_t>(actionId)) {
  383.                 return ATTR_READ_ERROR;
  384.             }
  385.  
  386.             setActionId(actionId);
  387.             break;
  388.         }
  389.  
  390.         case ATTR_UNIQUE_ID: {
  391.             uint16_t uniqueId;
  392.             if (!propStream.read<uint16_t>(uniqueId)) {
  393.                 return ATTR_READ_ERROR;
  394.             }
  395.  
  396.             setUniqueId(uniqueId);
  397.             break;
  398.         }
  399.  
  400.         case ATTR_TEXT: {
  401.             std::string text;
  402.             if (!propStream.readString(text)) {
  403.                 return ATTR_READ_ERROR;
  404.             }
  405.  
  406.             setText(text);
  407.             break;
  408.         }
  409.  
  410.         case ATTR_WRITTENDATE: {
  411.             uint32_t writtenDate;
  412.             if (!propStream.read<uint32_t>(writtenDate)) {
  413.                 return ATTR_READ_ERROR;
  414.             }
  415.  
  416.             setDate(writtenDate);
  417.             break;
  418.         }
  419.  
  420.         case ATTR_WRITTENBY: {
  421.             std::string writer;
  422.             if (!propStream.readString(writer)) {
  423.                 return ATTR_READ_ERROR;
  424.             }
  425.  
  426.             setWriter(writer);
  427.             break;
  428.         }
  429.  
  430.         case ATTR_DESC: {
  431.             std::string text;
  432.             if (!propStream.readString(text)) {
  433.                 return ATTR_READ_ERROR;
  434.             }
  435.  
  436.             setSpecialDescription(text);
  437.             break;
  438.         }
  439.  
  440.         case ATTR_CHARGES: {
  441.             uint16_t charges;
  442.             if (!propStream.read<uint16_t>(charges)) {
  443.                 return ATTR_READ_ERROR;
  444.             }
  445.  
  446.             setSubType(charges);
  447.             break;
  448.         }
  449.  
  450.         case ATTR_DURATION: {
  451.             int32_t duration;
  452.             if (!propStream.read<int32_t>(duration)) {
  453.                 return ATTR_READ_ERROR;
  454.             }
  455.  
  456.             setDuration(std::max<int32_t>(0, duration));
  457.             break;
  458.         }
  459.  
  460.         case ATTR_DECAYING_STATE: {
  461.             uint8_t state;
  462.             if (!propStream.read<uint8_t>(state)) {
  463.                 return ATTR_READ_ERROR;
  464.             }
  465.  
  466.             if (state != DECAYING_FALSE) {
  467.                 setDecaying(DECAYING_PENDING);
  468.             }
  469.             break;
  470.         }
  471.  
  472.         case ATTR_NAME: {
  473.             std::string name;
  474.             if (!propStream.readString(name)) {
  475.                 return ATTR_READ_ERROR;
  476.             }
  477.  
  478.             setStrAttr(ITEM_ATTRIBUTE_NAME, name);
  479.             break;
  480.         }
  481.  
  482.         case ATTR_ARTICLE: {
  483.             std::string article;
  484.             if (!propStream.readString(article)) {
  485.                 return ATTR_READ_ERROR;
  486.             }
  487.  
  488.             setStrAttr(ITEM_ATTRIBUTE_ARTICLE, article);
  489.             break;
  490.         }
  491.  
  492.         case ATTR_PLURALNAME: {
  493.             std::string pluralName;
  494.             if (!propStream.readString(pluralName)) {
  495.                 return ATTR_READ_ERROR;
  496.             }
  497.  
  498.             setStrAttr(ITEM_ATTRIBUTE_PLURALNAME, pluralName);
  499.             break;
  500.         }
  501.  
  502.         case ATTR_WEIGHT: {
  503.             uint32_t weight;
  504.             if (!propStream.read<uint32_t>(weight)) {
  505.                 return ATTR_READ_ERROR;
  506.             }
  507.  
  508.             setIntAttr(ITEM_ATTRIBUTE_WEIGHT, weight);
  509.             break;
  510.         }
  511.  
  512.         case ATTR_ATTACK: {
  513.             int32_t attack;
  514.             if (!propStream.read<int32_t>(attack)) {
  515.                 return ATTR_READ_ERROR;
  516.             }
  517.  
  518.             setIntAttr(ITEM_ATTRIBUTE_ATTACK, attack);
  519.             break;
  520.         }
  521.  
  522.         case ATTR_DEFENSE: {
  523.             int32_t defense;
  524.             if (!propStream.read<int32_t>(defense)) {
  525.                 return ATTR_READ_ERROR;
  526.             }
  527.  
  528.             setIntAttr(ITEM_ATTRIBUTE_DEFENSE, defense);
  529.             break;
  530.         }
  531.  
  532.         case ATTR_EXTRADEFENSE: {
  533.             int32_t extraDefense;
  534.             if (!propStream.read<int32_t>(extraDefense)) {
  535.                 return ATTR_READ_ERROR;
  536.             }
  537.  
  538.             setIntAttr(ITEM_ATTRIBUTE_EXTRADEFENSE, extraDefense);
  539.             break;
  540.         }
  541.  
  542.         case ATTR_ARMOR: {
  543.             int32_t armor;
  544.             if (!propStream.read<int32_t>(armor)) {
  545.                 return ATTR_READ_ERROR;
  546.             }
  547.  
  548.             setIntAttr(ITEM_ATTRIBUTE_ARMOR, armor);
  549.             break;
  550.         }
  551.  
  552.         case ATTR_HITCHANCE: {
  553.             int8_t hitChance;
  554.             if (!propStream.read<int8_t>(hitChance)) {
  555.                 return ATTR_READ_ERROR;
  556.             }
  557.  
  558.             setIntAttr(ITEM_ATTRIBUTE_HITCHANCE, hitChance);
  559.             break;
  560.         }
  561.  
  562.         case ATTR_SHOOTRANGE: {
  563.             uint8_t shootRange;
  564.             if (!propStream.read<uint8_t>(shootRange)) {
  565.                 return ATTR_READ_ERROR;
  566.             }
  567.  
  568.             setIntAttr(ITEM_ATTRIBUTE_SHOOTRANGE, shootRange);
  569.             break;
  570.         }
  571.  
  572.         //these should be handled through derived classes
  573.         //If these are called then something has changed in the items.xml since the map was saved
  574.         //just read the values
  575.  
  576.         //Depot class
  577.         case ATTR_DEPOT_ID: {
  578.             if (!propStream.skip(2)) {
  579.                 return ATTR_READ_ERROR;
  580.             }
  581.             break;
  582.         }
  583.  
  584.         //Door class
  585.         case ATTR_HOUSEDOORID: {
  586.             if (!propStream.skip(1)) {
  587.                 return ATTR_READ_ERROR;
  588.             }
  589.             break;
  590.         }
  591.  
  592.         //Bed class
  593.         case ATTR_SLEEPERGUID: {
  594.             if (!propStream.skip(4)) {
  595.                 return ATTR_READ_ERROR;
  596.             }
  597.             break;
  598.         }
  599.  
  600.         case ATTR_SLEEPSTART: {
  601.             if (!propStream.skip(4)) {
  602.                 return ATTR_READ_ERROR;
  603.             }
  604.             break;
  605.         }
  606.  
  607.         //Teleport class
  608.         case ATTR_TELE_DEST: {
  609.             if (!propStream.skip(5)) {
  610.                 return ATTR_READ_ERROR;
  611.             }
  612.             break;
  613.         }
  614.  
  615.         //Container class
  616.         case ATTR_CONTAINER_ITEMS: {
  617.             return ATTR_READ_ERROR;
  618.         }
  619.  
  620.         default:
  621.             return ATTR_READ_ERROR;
  622.     }
  623.  
  624.     return ATTR_READ_CONTINUE;
  625. }
  626.  
  627. bool Item::unserializeAttr(PropStream& propStream)
  628. {
  629.     uint8_t attr_type;
  630.     while (propStream.read<uint8_t>(attr_type) && attr_type != 0) {
  631.         Attr_ReadValue ret = readAttr(static_cast<AttrTypes_t>(attr_type), propStream);
  632.         if (ret == ATTR_READ_ERROR) {
  633.             return false;
  634.         } else if (ret == ATTR_READ_END) {
  635.             return true;
  636.         }
  637.     }
  638.     return true;
  639. }
  640.  
  641. bool Item::unserializeItemNode(OTB::Loader&, const OTB::Node&, PropStream& propStream)
  642. {
  643.     return unserializeAttr(propStream);
  644. }
  645.  
  646. void Item::serializeAttr(PropWriteStream& propWriteStream) const
  647. {
  648.     const ItemType& it = items[id];
  649.     if (it.stackable || it.isFluidContainer() || it.isSplash()) {
  650.         propWriteStream.write<uint8_t>(ATTR_COUNT);
  651.         propWriteStream.write<uint8_t>(getSubType());
  652.     }
  653.  
  654.     uint16_t charges = getCharges();
  655.     if (charges != 0) {
  656.         propWriteStream.write<uint8_t>(ATTR_CHARGES);
  657.         propWriteStream.write<uint16_t>(charges);
  658.     }
  659.  
  660.     if (it.moveable) {
  661.         uint16_t actionId = getActionId();
  662.         if (actionId != 0) {
  663.             propWriteStream.write<uint8_t>(ATTR_ACTION_ID);
  664.             propWriteStream.write<uint16_t>(actionId);
  665.         }
  666.     }
  667.  
  668.     const std::string& text = getText();
  669.     if (!text.empty()) {
  670.         propWriteStream.write<uint8_t>(ATTR_TEXT);
  671.         propWriteStream.writeString(text);
  672.     }
  673.  
  674.     const time_t writtenDate = getDate();
  675.     if (writtenDate != 0) {
  676.         propWriteStream.write<uint8_t>(ATTR_WRITTENDATE);
  677.         propWriteStream.write<uint32_t>(writtenDate);
  678.     }
  679.  
  680.     const std::string& writer = getWriter();
  681.     if (!writer.empty()) {
  682.         propWriteStream.write<uint8_t>(ATTR_WRITTENBY);
  683.         propWriteStream.writeString(writer);
  684.     }
  685.  
  686.     const std::string& specialDesc = getSpecialDescription();
  687.     if (!specialDesc.empty()) {
  688.         propWriteStream.write<uint8_t>(ATTR_DESC);
  689.         propWriteStream.writeString(specialDesc);
  690.     }
  691.  
  692.     if (hasAttribute(ITEM_ATTRIBUTE_DURATION)) {
  693.         propWriteStream.write<uint8_t>(ATTR_DURATION);
  694.         propWriteStream.write<uint32_t>(getIntAttr(ITEM_ATTRIBUTE_DURATION));
  695.     }
  696.  
  697.     ItemDecayState_t decayState = getDecaying();
  698.     if (decayState == DECAYING_TRUE || decayState == DECAYING_PENDING) {
  699.         propWriteStream.write<uint8_t>(ATTR_DECAYING_STATE);
  700.         propWriteStream.write<uint8_t>(decayState);
  701.     }
  702.  
  703.     if (hasAttribute(ITEM_ATTRIBUTE_NAME)) {
  704.         propWriteStream.write<uint8_t>(ATTR_NAME);
  705.         propWriteStream.writeString(getStrAttr(ITEM_ATTRIBUTE_NAME));
  706.     }
  707.  
  708.     if (hasAttribute(ITEM_ATTRIBUTE_ARTICLE)) {
  709.         propWriteStream.write<uint8_t>(ATTR_ARTICLE);
  710.         propWriteStream.writeString(getStrAttr(ITEM_ATTRIBUTE_ARTICLE));
  711.     }
  712.  
  713.     if (hasAttribute(ITEM_ATTRIBUTE_PLURALNAME)) {
  714.         propWriteStream.write<uint8_t>(ATTR_PLURALNAME);
  715.         propWriteStream.writeString(getStrAttr(ITEM_ATTRIBUTE_PLURALNAME));
  716.     }
  717.  
  718.     if (hasAttribute(ITEM_ATTRIBUTE_WEIGHT)) {
  719.         propWriteStream.write<uint8_t>(ATTR_WEIGHT);
  720.         propWriteStream.write<uint32_t>(getIntAttr(ITEM_ATTRIBUTE_WEIGHT));
  721.     }
  722.  
  723.     if (hasAttribute(ITEM_ATTRIBUTE_ATTACK)) {
  724.         propWriteStream.write<uint8_t>(ATTR_ATTACK);
  725.         propWriteStream.write<int32_t>(getIntAttr(ITEM_ATTRIBUTE_ATTACK));
  726.     }
  727.  
  728.     if (hasAttribute(ITEM_ATTRIBUTE_DEFENSE)) {
  729.         propWriteStream.write<uint8_t>(ATTR_DEFENSE);
  730.         propWriteStream.write<int32_t>(getIntAttr(ITEM_ATTRIBUTE_DEFENSE));
  731.     }
  732.  
  733.     if (hasAttribute(ITEM_ATTRIBUTE_EXTRADEFENSE)) {
  734.         propWriteStream.write<uint8_t>(ATTR_EXTRADEFENSE);
  735.         propWriteStream.write<int32_t>(getIntAttr(ITEM_ATTRIBUTE_EXTRADEFENSE));
  736.     }
  737.  
  738.     if (hasAttribute(ITEM_ATTRIBUTE_ARMOR)) {
  739.         propWriteStream.write<uint8_t>(ATTR_ARMOR);
  740.         propWriteStream.write<int32_t>(getIntAttr(ITEM_ATTRIBUTE_ARMOR));
  741.     }
  742.  
  743.     if (hasAttribute(ITEM_ATTRIBUTE_HITCHANCE)) {
  744.         propWriteStream.write<uint8_t>(ATTR_HITCHANCE);
  745.         propWriteStream.write<int8_t>(getIntAttr(ITEM_ATTRIBUTE_HITCHANCE));
  746.     }
  747.  
  748.     if (hasAttribute(ITEM_ATTRIBUTE_SHOOTRANGE)) {
  749.         propWriteStream.write<uint8_t>(ATTR_SHOOTRANGE);
  750.         propWriteStream.write<uint8_t>(getIntAttr(ITEM_ATTRIBUTE_SHOOTRANGE));
  751.     }
  752. }
  753.  
  754. bool Item::hasProperty(ITEMPROPERTY prop) const
  755. {
  756.     const ItemType& it = items[id];
  757.     switch (prop) {
  758.         case CONST_PROP_BLOCKSOLID: return it.blockSolid;
  759.         case CONST_PROP_MOVEABLE: return it.moveable && !hasAttribute(ITEM_ATTRIBUTE_UNIQUEID);
  760.         case CONST_PROP_HASHEIGHT: return it.hasHeight;
  761.         case CONST_PROP_BLOCKPROJECTILE: return it.blockProjectile;
  762.         case CONST_PROP_BLOCKPATH: return it.blockPathFind;
  763.         case CONST_PROP_ISVERTICAL: return it.isVertical;
  764.         case CONST_PROP_ISHORIZONTAL: return it.isHorizontal;
  765.         case CONST_PROP_IMMOVABLEBLOCKSOLID: return it.blockSolid && (!it.moveable || hasAttribute(ITEM_ATTRIBUTE_UNIQUEID));
  766.         case CONST_PROP_IMMOVABLEBLOCKPATH: return it.blockPathFind && (!it.moveable || hasAttribute(ITEM_ATTRIBUTE_UNIQUEID));
  767.         case CONST_PROP_IMMOVABLENOFIELDBLOCKPATH: return !it.isMagicField() && it.blockPathFind && (!it.moveable || hasAttribute(ITEM_ATTRIBUTE_UNIQUEID));
  768.         case CONST_PROP_NOFIELDBLOCKPATH: return !it.isMagicField() && it.blockPathFind;
  769.         case CONST_PROP_SUPPORTHANGABLE: return it.isHorizontal || it.isVertical;
  770.         default: return false;
  771.     }
  772. }
  773.  
  774. uint32_t Item::getWeight() const
  775. {
  776.     uint32_t weight = getBaseWeight();
  777.     if (isStackable()) {
  778.         return weight * std::max<uint32_t>(1, getItemCount());
  779.     }
  780.     return weight;
  781. }
  782.  
  783. std::string Item::getDescription(const ItemType& it, int32_t lookDistance,
  784.                                  const Item* item /*= nullptr*/, int32_t subType /*= -1*/, bool addArticle /*= true*/)
  785. {
  786.     const std::string* text = nullptr;
  787.  
  788.     std::ostringstream s;
  789.     s << getNameDescription(it, item, subType, addArticle);
  790.  
  791.     if (item) {
  792.         subType = item->getSubType();
  793.     }
  794.  
  795.     if (it.isRune()) {
  796.         if (it.runeLevel > 0 || it.runeMagLevel > 0) {
  797.             if (RuneSpell* rune = g_spells->getRuneSpell(it.id)) {
  798.                 int32_t tmpSubType = subType;
  799.                 if (item) {
  800.                     tmpSubType = item->getSubType();
  801.                 }
  802.                 s << ". " << (it.stackable && tmpSubType > 1 ? "Isto" : "Isto") << " pode ser usado somente por ";
  803.  
  804.                 const VocSpellMap& vocMap = rune->getVocMap();
  805.                 std::vector<Vocation*> showVocMap;
  806.  
  807.                 // vocations are usually listed with the unpromoted and promoted version, the latter being
  808.                 // hidden from description, so `total / 2` is most likely the amount of vocations to be shown.
  809.                 showVocMap.reserve(vocMap.size() / 2);
  810.                 for (const auto& voc : vocMap) {
  811.                     if (voc.second) {
  812.                         showVocMap.push_back(g_vocations.getVocation(voc.first));
  813.                     }
  814.                 }
  815.  
  816.                 if (!showVocMap.empty()) {
  817.                     auto vocIt = showVocMap.begin(), vocLast = (showVocMap.end() - 1);
  818.                     while (vocIt != vocLast) {
  819.                         s << asLowerCaseString((*vocIt)->getVocName()) << "s";
  820.                         if (++vocIt == vocLast) {
  821.                             s << " e ";
  822.                         } else {
  823.                             s << ", ";
  824.                         }
  825.                     }
  826.                     s << asLowerCaseString((*vocLast)->getVocName()) << "s";
  827.                 } else {
  828.                     s << "jogadores";
  829.                 }
  830.  
  831.                 s << " com";
  832.  
  833.                 if (it.runeLevel > 0) {
  834.                     s << " level " << it.runeLevel;
  835.                 }
  836.  
  837.                 if (it.runeMagLevel > 0) {
  838.                     if (it.runeLevel > 0) {
  839.                         s << " e";
  840.                     }
  841.  
  842.                     s << " level magico " << it.runeMagLevel;
  843.                 }
  844.  
  845.                 s << " ou maior";
  846.             }
  847.         }
  848.     } else if (it.weaponType != WEAPON_NONE) {
  849.         bool begin = true;
  850.         if (it.weaponType == WEAPON_DISTANCE && it.ammoType != AMMO_NONE) {
  851.             s << " (Alcance:" << static_cast<uint16_t>(item ? item->getShootRange() : it.shootRange);
  852.  
  853.             int32_t attack;
  854.             int8_t hitChance;
  855.             if (item) {
  856.                 attack = item->getAttack();
  857.                 hitChance = item->getHitChance();
  858.             } else {
  859.                 attack = it.attack;
  860.                 hitChance = it.hitChance;
  861.             }
  862.  
  863.             if (attack != 0) {
  864.                 s << ", Ataque" << std::showpos << attack << std::noshowpos;
  865.             }
  866.  
  867.             if (hitChance != 0) {
  868.                 s << ", Hit%" << std::showpos << static_cast<int16_t>(hitChance) << std::noshowpos;
  869.             }
  870.  
  871.             begin = false;
  872.         } else if (it.weaponType != WEAPON_AMMO) {
  873.  
  874.             int32_t attack, defense, extraDefense;
  875.             if (item) {
  876.                 attack = item->getAttack();
  877.                 defense = item->getDefense();
  878.                 extraDefense = item->getExtraDefense();
  879.                
  880.             } else {
  881.                 attack = it.attack;
  882.                 defense = it.defense;
  883.                 extraDefense = it.extraDefense;
  884.  
  885.             }
  886.  
  887.             if (attack != 0) {
  888.                 begin = false;
  889.                 s << " \nAtaque: " << attack;
  890.  
  891.                 if (it.abilities && it.abilities->elementType != COMBAT_NONE && it.abilities->elementDamage != 0) {
  892.                     s << " Fisico + " << it.abilities->elementDamage << ' ' << getCombatName(it.abilities->elementType);
  893.                    
  894.                 }
  895.             }
  896.            
  897.             if (it.abilities && it.abilities->elementType != COMBAT_NONE && it.abilities->elementDamage != 0) {
  898.                 s << " \nAtaque: ";
  899.                 s << it.abilities->elementDamage << ' de ' << getCombatName(it.abilities->elementType);
  900.             }
  901.  
  902.             if (defense != 0 || extraDefense != 0) {
  903.                 if (begin) {
  904.                     begin = false;
  905.                     s << " ";
  906.                 } else {
  907.                     s << ". ";
  908.                 }
  909.  
  910.                 s << "\nDefesa: " << defense;
  911.                 if (extraDefense != 0) {
  912.                     s << ' ' << std::showpos << extraDefense << std::noshowpos;
  913.                 }
  914.             }
  915.         }
  916.  
  917.                
  918.         if (it.abilities) {
  919.             for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; i++) {
  920.                 if (!it.abilities->skills[i]) {
  921.                     continue;
  922.                 }
  923.  
  924.                 if (begin) {
  925.                     begin = false;
  926.                     s << " ";
  927.                 } else {
  928.                     s << ". ";
  929.                 }
  930.  
  931.                 s << "\n" << getSkillName(i) << ' ' << std::showpos << it.abilities->skills[i] << std::noshowpos;
  932.             }
  933.  
  934.             if (it.abilities->stats[STAT_MAGICPOINTS]) {
  935.                 if (begin) {
  936.                     begin = false;
  937.                     s << " ";
  938.                 } else {
  939.                     s << ". ";
  940.                 }
  941.                
  942.                 s << "\n" << "nivel magico " << std::showpos << it.abilities->stats[STAT_MAGICPOINTS] << std::noshowpos;
  943.             }
  944.  
  945.            
  946.            
  947.             int16_t show = it.abilities->absorbPercent[0];
  948.             if (show != 0) {
  949.                 for (size_t i = 1; i < COMBAT_COUNT; ++i) {
  950.                     if (it.abilities->absorbPercent[i] != show) {
  951.                         show = 0;
  952.                         break;
  953.                     }
  954.                 }
  955.             }
  956.  
  957.             if (show == 0) {
  958.                 bool tmp = true;
  959.                
  960.                 for (size_t i = 0; i < COMBAT_COUNT; ++i) {
  961.                     if (it.abilities->absorbPercent[i] == 0) {
  962.                         continue;
  963.                     }
  964.  
  965.                     if (tmp) {
  966.                         tmp = false;
  967.  
  968.                         if (begin) {
  969.                             begin = false;
  970.                             s << " ";
  971.                         } else {
  972.                             s << ". ";
  973.                         }
  974.  
  975.                         s << "protecao ";
  976.                     } else {
  977.                         s << ". ";
  978.                     }
  979.  
  980.                     s << "\n" << getCombatName(indexToCombatType(i)) << ' ' << std::showpos << it.abilities->absorbPercent[i] << std::noshowpos << '%';
  981.                 }
  982.             } else {
  983.                 if (begin) {
  984.                     begin = false;
  985.                     s << " ";
  986.                 } else {
  987.                     s << ". ";
  988.                 }
  989.  
  990.                 s << "\n" << "protecao a tudo " << std::showpos << show << std::noshowpos << '%';
  991.             }
  992.  
  993.             show = it.abilities->fieldAbsorbPercent[0];
  994.             if (show != 0) {
  995.                 for (size_t i = 1; i < COMBAT_COUNT; ++i) {
  996.                     if (it.abilities->absorbPercent[i] != show) {
  997.                         show = 0;
  998.                         break;
  999.                     }
  1000.                 }
  1001.             }
  1002.  
  1003.             if (show == 0) {
  1004.                 bool tmp = true;
  1005.  
  1006.                 for (size_t i = 0; i < COMBAT_COUNT; ++i) {
  1007.                     if (it.abilities->fieldAbsorbPercent[i] == 0) {
  1008.                         continue;
  1009.                     }
  1010.  
  1011.                     if (tmp) {
  1012.                         tmp = false;
  1013.  
  1014.                         if (begin) {
  1015.                             begin = false;
  1016.                             s << " ";
  1017.                         } else {
  1018.                             s << ". ";
  1019.                         }
  1020.  
  1021.                         s << "protecao ";
  1022.                     } else {
  1023.                         s << ". ";
  1024.                     }
  1025.  
  1026.                     s << "\n" << getCombatName(indexToCombatType(i)) << " campo " << std::showpos << it.abilities->fieldAbsorbPercent[i] << std::noshowpos << '%';
  1027.                 }
  1028.             } else {
  1029.                 if (begin) {
  1030.                     begin = false;
  1031.                     s << " ";
  1032.                 } else {
  1033.                     s << ". ";
  1034.                 }
  1035.  
  1036.                 s << "\n" << "protecao a todos os campos " << std::showpos << show << std::noshowpos << '%';
  1037.             }
  1038.  
  1039.             if (it.abilities->speed) {
  1040.                 if (begin) {
  1041.                     begin = false;
  1042.                     s << " ";
  1043.                 } else {
  1044.                     s << ". ";
  1045.                 }
  1046.  
  1047.                 s << "\n" << "velocidade " << std::showpos << (it.abilities->speed >> 1) << std::noshowpos;
  1048.             }
  1049.  
  1050.         }
  1051.  
  1052.  
  1053.         // if (!begin) {
  1054.         //   s << '.';
  1055.         // }
  1056.     } else if (it.armor != 0 || (item && item->getArmor() != 0) || it.showAttributes) {
  1057.         bool begin = true;
  1058.  
  1059.         int32_t armor = (item ? item->getArmor() : it.armor);
  1060.         if (armor != 0) {
  1061.             s << " Armadura: " << armor;
  1062.             begin = false;
  1063.         }
  1064.  
  1065.         if (it.abilities) {
  1066.             for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; i++) {
  1067.                 if (!it.abilities->skills[i]) {
  1068.                     continue;
  1069.                 }
  1070.  
  1071.                 if (begin) {
  1072.                     begin = false;
  1073.                     s << " ";
  1074.                 } else {
  1075.                     s << " ";
  1076.                 }
  1077.  
  1078.                 s << "\n" << getSkillName(i) << ' ' << std::showpos << it.abilities->skills[i] << std::noshowpos;
  1079.             }
  1080.  
  1081.             if (it.abilities->stats[STAT_MAGICPOINTS]) {
  1082.                 if (begin) {
  1083.                     begin = false;
  1084.                     s << " ";
  1085.                 } else {
  1086.                     s << " ";
  1087.                 }
  1088.  
  1089.                 s << "\n" << "level magico " << std::showpos << it.abilities->stats[STAT_MAGICPOINTS] << std::noshowpos;
  1090.             }
  1091.  
  1092.             int16_t show = it.abilities->absorbPercent[0];
  1093.             if (show != 0) {
  1094.                 for (size_t i = 1; i < COMBAT_COUNT; ++i) {
  1095.                     if (it.abilities->absorbPercent[i] != show) {
  1096.                         show = 0;
  1097.                         break;
  1098.                     }
  1099.                 }
  1100.             }
  1101.  
  1102.             if (!show) {
  1103.                 bool protectionBegin = true;
  1104.                 for (size_t i = 0; i < COMBAT_COUNT; ++i) {
  1105.                     if (it.abilities->absorbPercent[i] == 0) {
  1106.                         continue;
  1107.                     }
  1108.  
  1109.                     if (protectionBegin) {
  1110.                         protectionBegin = false;
  1111.  
  1112.                         if (begin) {
  1113.                             begin = false;
  1114.                             s << " ";
  1115.                         } else {
  1116.                             s << " ";
  1117.                         }
  1118.  
  1119.                         s << "protecao ";
  1120.                     } else {
  1121.                         s << " ";
  1122.                     }
  1123.  
  1124.                     s << "\n" << getCombatName(indexToCombatType(i)) << ' ' << std::showpos << it.abilities->absorbPercent[i] << std::noshowpos << '%';
  1125.                 }
  1126.             } else {
  1127.                 if (begin) {
  1128.                     begin = false;
  1129.                     s << " ";
  1130.                 } else {
  1131.                     s << " ";
  1132.                 }
  1133.  
  1134.                 s << "\n" << "protecao a tudo " << std::showpos << show << std::noshowpos << '%';
  1135.             }
  1136.  
  1137.             show = it.abilities->fieldAbsorbPercent[0];
  1138.             if (show != 0) {
  1139.                 for (size_t i = 1; i < COMBAT_COUNT; ++i) {
  1140.                     if (it.abilities->absorbPercent[i] != show) {
  1141.                         show = 0;
  1142.                         break;
  1143.                     }
  1144.                 }
  1145.             }
  1146.  
  1147.             if (!show) {
  1148.                 bool tmp = true;
  1149.  
  1150.                 for (size_t i = 0; i < COMBAT_COUNT; ++i) {
  1151.                     if (it.abilities->fieldAbsorbPercent[i] == 0) {
  1152.                         continue;
  1153.                     }
  1154.  
  1155.                     if (tmp) {
  1156.                         tmp = false;
  1157.  
  1158.                         if (begin) {
  1159.                             begin = false;
  1160.                             s << " ";
  1161.                         } else {
  1162.                             s << " ";
  1163.                         }
  1164.  
  1165.                         s << "protecao ";
  1166.                     } else {
  1167.                         s << " ";
  1168.                     }
  1169.  
  1170.                     s << "\n" << getCombatName(indexToCombatType(i)) << " campo " << std::showpos << it.abilities->fieldAbsorbPercent[i] << std::noshowpos << '%';
  1171.                 }
  1172.             } else {
  1173.                 if (begin) {
  1174.                     begin = false;
  1175.                     s << " ";
  1176.                 } else {
  1177.                     s << " ";
  1178.                 }
  1179.  
  1180.                 s << "\n" << "protecao de todos os campos " << std::showpos << show << std::noshowpos << '%';
  1181.             }
  1182.  
  1183.             if (it.abilities->speed) {
  1184.                 if (begin) {
  1185.                     begin = false;
  1186.                     s << " ";
  1187.                 } else {
  1188.                     s << " ";
  1189.                 }
  1190.  
  1191.                 s << "\n" << "velocidade " << std::showpos << (it.abilities->speed >> 1) << std::noshowpos;
  1192.             }
  1193.         }
  1194.  
  1195.         //if (!begin) {
  1196.         //  s << '';
  1197.     //  }
  1198.     } else if (it.isContainer() || (item && item->getContainer())) {
  1199.         uint32_t volume = 0;
  1200.         if (!item || !item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
  1201.             if (it.isContainer()) {
  1202.                 volume = it.maxItems;
  1203.             } else {
  1204.                 volume = item->getContainer()->capacity();
  1205.             }
  1206.         }
  1207.  
  1208.         if (volume != 0) {
  1209.             s << " (Volume: " << volume << ')';
  1210.         }
  1211.     } else {
  1212.         bool found = true;
  1213.  
  1214.         if (it.abilities) {
  1215.             if (it.abilities->speed > 0) {
  1216.                 s << " (velocidade " << std::showpos << (it.abilities->speed / 2) << std::noshowpos << ')';
  1217.             } else if (hasBitSet(CONDITION_DRUNK, it.abilities->conditionSuppressions)) {
  1218.                 s << " (beber muito)";
  1219.             } else if (it.abilities->invisible) {
  1220.                 s << " (invisibilidade)";
  1221.             } else if (it.abilities->regeneration) {
  1222.                 s << " (regeneracao rapida)";
  1223.             } else if (it.abilities->manaShield) {
  1224.                 s << " (escudo de mana)";
  1225.             } else {
  1226.                 found = false;
  1227.             }
  1228.         } else {
  1229.             found = false;
  1230.         }
  1231.  
  1232.         if (!found) {
  1233.             if (it.isKey()) {
  1234.                 s << " (Chave: " << (item ? item->getActionId() : 0) << ')';
  1235.             } else if (it.isFluidContainer()) {
  1236.                 if (subType > 0) {
  1237.                     const std::string& itemName = items[subType].name;
  1238.                     s << " de " << (!itemName.empty() ? itemName : "desconhecido");
  1239.                 } else {
  1240.                     s << ". Esta vazio";
  1241.                 }
  1242.             } else if (it.isSplash()) {
  1243.                 s << " de ";
  1244.  
  1245.                 if (subType > 0 && !items[subType].name.empty()) {
  1246.                     s << items[subType].name;
  1247.                 } else {
  1248.                     s << "desconhecido";
  1249.                 }
  1250.             } else if (it.allowDistRead && (it.id < 7369 || it.id > 7371)) {
  1251.                 s << ".\n";
  1252.  
  1253.                 if (lookDistance <= 4) {
  1254.                     if (item) {
  1255.                         text = &item->getText();
  1256.                         if (!text->empty()) {
  1257.                             const std::string& writer = item->getWriter();
  1258.                             if (!writer.empty()) {
  1259.                                 s << writer << " wrote";
  1260.                                 time_t date = item->getDate();
  1261.                                 if (date != 0) {
  1262.                                     s << " on " << formatDateShort(date);
  1263.                                 }
  1264.                                 s << ": ";
  1265.                             } else {
  1266.                                 s << "Voce le: ";
  1267.                             }
  1268.                             s << *text;
  1269.                         } else {
  1270.                             s << "Nada esta escrito";
  1271.                         }
  1272.                     } else {
  1273.                         s << "Nada esta escrito";
  1274.                     }
  1275.                 } else {
  1276.                     s << "Voce esta muito longe para ler";
  1277.                 }
  1278.             } else if (it.levelDoor != 0 && item) {
  1279.                 uint16_t actionId = item->getActionId();
  1280.                 if (actionId >= it.levelDoor) {
  1281.                     s << " para level " << (actionId - it.levelDoor);
  1282.                 }
  1283.             }
  1284.         }
  1285.     }
  1286.  
  1287.     if (it.showCharges) {
  1288.         s << " \nTem " << subType << " carga" << (subType != 1 ? "s" : "") << " restante";
  1289.     }
  1290.  
  1291.  
  1292.     if (it.showDuration) {
  1293.         if (item && item->hasAttribute(ITEM_ATTRIBUTE_DURATION)) {
  1294.             uint32_t duration = item->getDuration() / 1000;
  1295.             s << " ira expirar em ";
  1296.  
  1297.             if (duration >= 86400) {
  1298.                 uint16_t days = duration / 86400;
  1299.                 uint16_t hours = (duration % 86400) / 3600;
  1300.                 s << days << " dia" << (days != 1 ? "s" : "");
  1301.  
  1302.                 if (hours > 0) {
  1303.                     s << " e " << hours << " hora" << (hours != 1 ? "s" : "");
  1304.                 }
  1305.             } else if (duration >= 3600) {
  1306.                 uint16_t hours = duration / 3600;
  1307.                 uint16_t minutes = (duration % 3600) / 60;
  1308.                 s << hours << " hora" << (hours != 1 ? "s" : "");
  1309.  
  1310.                 if (minutes > 0) {
  1311.                     s << " e " << minutes << " minuto" << (minutes != 1 ? "s" : "");
  1312.                 }
  1313.             } else if (duration >= 60) {
  1314.                 uint16_t minutes = duration / 60;
  1315.                 s << minutes << " minuto" << (minutes != 1 ? "s" : "");
  1316.                 uint16_t seconds = duration % 60;
  1317.  
  1318.                 if (seconds > 0) {
  1319.                     s << " e " << seconds << " segundo" << (seconds != 1 ? "s" : "");
  1320.                 }
  1321.             } else {
  1322.                 s << duration << " segundo" << (duration != 1 ? "s" : "");
  1323.             }
  1324.         } else {
  1325.             s << " isso e novinho em folha";
  1326.         }
  1327.     }
  1328.  
  1329.     if (!it.allowDistRead || (it.id >= 7369 && it.id <= 7371)) {
  1330.         s << '.';
  1331.     } else {
  1332.         if (!text && item) {
  1333.             text = &item->getText();
  1334.         }
  1335.  
  1336.         if (!text || text->empty()) {
  1337.             s << '.';
  1338.         }
  1339.     }
  1340.  
  1341.  
  1342.     if (it.wieldInfo != 0) {
  1343.         s << "\nSo pode ser usado por ";
  1344.  
  1345.         if (it.wieldInfo & WIELDINFO_PREMIUM) {
  1346.             s << "premium ";
  1347.         }
  1348.  
  1349.         if (!it.vocationString.empty()) {
  1350.             s << it.vocationString;
  1351.         } else {
  1352.             s << "jogadores";
  1353.         }
  1354.  
  1355.         if (it.wieldInfo & WIELDINFO_LEVEL) {
  1356.             s << " de nivel " << it.minReqLevel << " ou maior";
  1357.         }
  1358.  
  1359.         if (it.wieldInfo & WIELDINFO_MAGLV) {
  1360.             if (it.wieldInfo & WIELDINFO_LEVEL) {
  1361.                 s << " e";
  1362.             } else {
  1363.                 s << " de";
  1364.             }
  1365.  
  1366.             s << " nivel magico " << it.minReqMagicLevel << " ou maior";
  1367.         }
  1368.  
  1369.         s << '.';
  1370.     }
  1371.  
  1372.     if (lookDistance <= 1) {
  1373.         if (item) {
  1374.             const uint32_t weight = item->getWeight();
  1375.             if (weight != 0 && it.pickupable) {
  1376.                 s << '\n' << getWeightDescription(it, weight, item->getItemCount());
  1377.             }
  1378.         } else if (it.weight != 0 && it.pickupable) {
  1379.             s << '\n' << getWeightDescription(it, it.weight);
  1380.         }
  1381.     }
  1382.  
  1383.     if (item) {
  1384.         const std::string& specialDescription = item->getSpecialDescription();
  1385.         if (!specialDescription.empty()) {
  1386.             s << '\n' << specialDescription;
  1387.         } else if (lookDistance <= 1 && !it.description.empty()) {
  1388.             s << '\n' << it.description;
  1389.         }
  1390.     } else if (lookDistance <= 1 && !it.description.empty()) {
  1391.         s << '\n' << it.description;
  1392.     }
  1393.  
  1394.     if (it.allowDistRead && it.id >= 7369 && it.id <= 7371) {
  1395.         if (!text && item) {
  1396.             text = &item->getText();
  1397.         }
  1398.  
  1399.         if (text && !text->empty()) {
  1400.             s << '\n' << *text;
  1401.         }
  1402.     }
  1403.     return s.str();
  1404. }
  1405.  
  1406. std::string Item::getDescription(int32_t lookDistance) const
  1407. {
  1408.     const ItemType& it = items[id];
  1409.     return getDescription(it, lookDistance, this);
  1410. }
  1411.  
  1412. std::string Item::getNameDescription(const ItemType& it, const Item* item /*= nullptr*/, int32_t subType /*= -1*/, bool addArticle /*= true*/)
  1413. {
  1414.     if (item) {
  1415.         subType = item->getSubType();
  1416.     }
  1417.  
  1418.     std::ostringstream s;
  1419.  
  1420.     const std::string& name = (item ? item->getName() : it.name);
  1421.     if (!name.empty()) {
  1422.         if (it.stackable && subType > 1) {
  1423.             if (it.showCount) {
  1424.                 s << subType << ' ';
  1425.             }
  1426.  
  1427.             s << (item ? item->getPluralName() : it.getPluralName());
  1428.         } else {
  1429.             if (addArticle) {
  1430.                 const std::string& article = (item ? item->getArticle() : it.article);
  1431.                 if (!article.empty()) {
  1432.                     s << article << ' ';
  1433.                 }
  1434.             }
  1435.  
  1436.             s << name;
  1437.         }
  1438.     } else {
  1439.         s << "um item do tipo " << it.id;
  1440.     }
  1441.     return s.str();
  1442. }
  1443.  
  1444. std::string Item::getNameDescription() const
  1445. {
  1446.     const ItemType& it = items[id];
  1447.     return getNameDescription(it, this);
  1448. }
  1449.  
  1450. std::string Item::getWeightDescription(const ItemType& it, uint32_t weight, uint32_t count /*= 1*/)
  1451. {
  1452.     std::ostringstream ss;
  1453.     if (it.stackable && count > 1 && it.showCount != 0) {
  1454.         ss << "Peso ";
  1455.     } else {
  1456.         ss << "Peso ";
  1457.     }
  1458.  
  1459.     if (weight < 10) {
  1460.         ss << "0.0" << weight;
  1461.     } else if (weight < 100) {
  1462.         ss << "0." << weight;
  1463.     } else {
  1464.         std::string weightString = std::to_string(weight);
  1465.         weightString.insert(weightString.end() - 2, '.');
  1466.         ss << weightString;
  1467.     }
  1468.  
  1469.     ss << " Kg.";
  1470.     return ss.str();
  1471. }
  1472.  
  1473. std::string Item::getWeightDescription(uint32_t weight) const
  1474. {
  1475.     const ItemType& it = Item::items[id];
  1476.     return getWeightDescription(it, weight, getItemCount());
  1477. }
  1478.  
  1479. std::string Item::getWeightDescription() const
  1480. {
  1481.     uint32_t weight = getWeight();
  1482.     if (weight == 0) {
  1483.         return std::string();
  1484.     }
  1485.     return getWeightDescription(weight);
  1486. }
  1487.  
  1488. void Item::setUniqueId(uint16_t n)
  1489. {
  1490.     if (hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
  1491.         return;
  1492.     }
  1493.  
  1494.     if (g_game.addUniqueItem(n, this)) {
  1495.         getAttributes()->setUniqueId(n);
  1496.     }
  1497. }
  1498.  
  1499. bool Item::canDecay() const
  1500. {
  1501.     if (isRemoved()) {
  1502.         return false;
  1503.     }
  1504.  
  1505.     const ItemType& it = Item::items[id];
  1506.     if (it.decayTo < 0 || it.decayTime == 0) {
  1507.         return false;
  1508.     }
  1509.  
  1510.     if (hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
  1511.         return false;
  1512.     }
  1513.  
  1514.     return true;
  1515. }
  1516.  
  1517. uint32_t Item::getWorth() const
  1518. {
  1519.     switch (id) {
  1520.         case ITEM_GOLD_COIN:
  1521.             return count;
  1522.  
  1523.         case ITEM_PLATINUM_COIN:
  1524.             return count * 100;
  1525.  
  1526.         case ITEM_CRYSTAL_COIN:
  1527.             return count * 10000;
  1528.  
  1529.         default:
  1530.             return 0;
  1531.     }
  1532. }
  1533.  
  1534. LightInfo Item::getLightInfo() const
  1535. {
  1536.     const ItemType& it = items[id];
  1537.     return {it.lightColor, it.lightLevel};
  1538. }
  1539.  
  1540. std::string ItemAttributes::emptyString;
  1541.  
  1542. const std::string& ItemAttributes::getStrAttr(itemAttrTypes type) const
  1543. {
  1544.     if (!isStrAttrType(type)) {
  1545.         return emptyString;
  1546.     }
  1547.  
  1548.     const Attribute* attr = getExistingAttr(type);
  1549.     if (!attr) {
  1550.         return emptyString;
  1551.     }
  1552.     return *attr->value.string;
  1553. }
  1554.  
  1555. void ItemAttributes::setStrAttr(itemAttrTypes type, const std::string& value)
  1556. {
  1557.     if (!isStrAttrType(type)) {
  1558.         return;
  1559.     }
  1560.  
  1561.     if (value.empty()) {
  1562.         return;
  1563.     }
  1564.  
  1565.     Attribute& attr = getAttr(type);
  1566.     delete attr.value.string;
  1567.     attr.value.string = new std::string(value);
  1568. }
  1569.  
  1570. void ItemAttributes::removeAttribute(itemAttrTypes type)
  1571. {
  1572.     if (!hasAttribute(type)) {
  1573.         return;
  1574.     }
  1575.  
  1576.     auto prev_it = attributes.cbegin();
  1577.     if ((*prev_it).type == type) {
  1578.         attributes.pop_front();
  1579.     } else {
  1580.         auto it = prev_it, end = attributes.cend();
  1581.         while (++it != end) {
  1582.             if ((*it).type == type) {
  1583.                 attributes.erase_after(prev_it);
  1584.                 break;
  1585.             }
  1586.             prev_it = it;
  1587.         }
  1588.     }
  1589.     attributeBits &= ~type;
  1590. }
  1591.  
  1592. int64_t ItemAttributes::getIntAttr(itemAttrTypes type) const
  1593. {
  1594.     if (!isIntAttrType(type)) {
  1595.         return 0;
  1596.     }
  1597.  
  1598.     const Attribute* attr = getExistingAttr(type);
  1599.     if (!attr) {
  1600.         return 0;
  1601.     }
  1602.     return attr->value.integer;
  1603. }
  1604.  
  1605. void ItemAttributes::setIntAttr(itemAttrTypes type, int64_t value)
  1606. {
  1607.     if (!isIntAttrType(type)) {
  1608.         return;
  1609.     }
  1610.  
  1611.     getAttr(type).value.integer = value;
  1612. }
  1613.  
  1614. void ItemAttributes::increaseIntAttr(itemAttrTypes type, int64_t value)
  1615. {
  1616.     if (!isIntAttrType(type)) {
  1617.         return;
  1618.     }
  1619.  
  1620.     getAttr(type).value.integer += value;
  1621. }
  1622.  
  1623. const ItemAttributes::Attribute* ItemAttributes::getExistingAttr(itemAttrTypes type) const
  1624. {
  1625.     if (hasAttribute(type)) {
  1626.         for (const Attribute& attribute : attributes) {
  1627.             if (attribute.type == type) {
  1628.                 return &attribute;
  1629.             }
  1630.         }
  1631.     }
  1632.     return nullptr;
  1633. }
  1634.  
  1635. ItemAttributes::Attribute& ItemAttributes::getAttr(itemAttrTypes type)
  1636. {
  1637.     if (hasAttribute(type)) {
  1638.         for (Attribute& attribute : attributes) {
  1639.             if (attribute.type == type) {
  1640.                 return attribute;
  1641.             }
  1642.         }
  1643.     }
  1644.  
  1645.     attributeBits |= type;
  1646.     attributes.emplace_front(type);
  1647.     return attributes.front();
  1648. }
  1649.  
  1650. void Item::startDecaying()
  1651. {
  1652.     g_game.startDecay(this);
  1653. }
  1654.  
  1655. bool Item::hasMarketAttributes() const
  1656. {
  1657.     if (attributes == nullptr) {
  1658.         return true;
  1659.     }
  1660.  
  1661.     for (const auto& attr : attributes->getList()) {
  1662.         if (attr.type == ITEM_ATTRIBUTE_CHARGES) {
  1663.             uint16_t charges = static_cast<uint16_t>(attr.value.integer);
  1664.             if (charges != items[id].charges) {
  1665.                 return false;
  1666.             }
  1667.         } else if (attr.type == ITEM_ATTRIBUTE_DURATION) {
  1668.             uint32_t duration = static_cast<uint32_t>(attr.value.integer);
  1669.             if (duration != getDefaultDuration()) {
  1670.                 return false;
  1671.             }
  1672.         } else {
  1673.             return false;
  1674.         }
  1675.     }
  1676.     return true;
  1677. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement