Advertisement
Guest User

Untitled

a guest
Dec 13th, 2018
130
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 40.59 KB | None | 0 0
  1. /**
  2. * The Forgotten Server - a free and open-source MMORPG server emulator
  3. * Copyright (C) 2018 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. case ATTR_CUSTOM_ATTRIBUTES: {
  621. uint64_t size;
  622. if (!propStream.read<uint64_t>(size)) {
  623. return ATTR_READ_ERROR;
  624. }
  625.  
  626. for (uint64_t i = 0; i < size; i++) {
  627. // Unserialize key type and value
  628. std::string key;
  629. if (!propStream.readString(key)) {
  630. return ATTR_READ_ERROR;
  631. };
  632.  
  633. // Unserialize value type and value
  634. ItemAttributes::CustomAttribute val;
  635. if (!val.unserialize(propStream)) {
  636. return ATTR_READ_ERROR;
  637. }
  638.  
  639. setCustomAttribute(key, val);
  640. }
  641. break;
  642. }
  643.  
  644. default:
  645. return ATTR_READ_ERROR;
  646. }
  647.  
  648. return ATTR_READ_CONTINUE;
  649. }
  650.  
  651. bool Item::unserializeAttr(PropStream& propStream)
  652. {
  653. uint8_t attr_type;
  654. while (propStream.read<uint8_t>(attr_type) && attr_type != 0) {
  655. Attr_ReadValue ret = readAttr(static_cast<AttrTypes_t>(attr_type), propStream);
  656. if (ret == ATTR_READ_ERROR) {
  657. return false;
  658. } else if (ret == ATTR_READ_END) {
  659. return true;
  660. }
  661. }
  662. return true;
  663. }
  664.  
  665. bool Item::unserializeItemNode(OTB::Loader&, const OTB::Node&, PropStream& propStream)
  666. {
  667. return unserializeAttr(propStream);
  668. }
  669.  
  670. void Item::serializeAttr(PropWriteStream& propWriteStream) const
  671. {
  672. const ItemType& it = items[id];
  673. if (it.stackable || it.isFluidContainer() || it.isSplash()) {
  674. propWriteStream.write<uint8_t>(ATTR_COUNT);
  675. propWriteStream.write<uint8_t>(getSubType());
  676. }
  677.  
  678. uint16_t charges = getCharges();
  679. if (charges != 0) {
  680. propWriteStream.write<uint8_t>(ATTR_CHARGES);
  681. propWriteStream.write<uint16_t>(charges);
  682. }
  683.  
  684. if (it.moveable) {
  685. uint16_t actionId = getActionId();
  686. if (actionId != 0) {
  687. propWriteStream.write<uint8_t>(ATTR_ACTION_ID);
  688. propWriteStream.write<uint16_t>(actionId);
  689. }
  690. }
  691.  
  692. const std::string& text = getText();
  693. if (!text.empty()) {
  694. propWriteStream.write<uint8_t>(ATTR_TEXT);
  695. propWriteStream.writeString(text);
  696. }
  697.  
  698. const time_t writtenDate = getDate();
  699. if (writtenDate != 0) {
  700. propWriteStream.write<uint8_t>(ATTR_WRITTENDATE);
  701. propWriteStream.write<uint32_t>(writtenDate);
  702. }
  703.  
  704. const std::string& writer = getWriter();
  705. if (!writer.empty()) {
  706. propWriteStream.write<uint8_t>(ATTR_WRITTENBY);
  707. propWriteStream.writeString(writer);
  708. }
  709.  
  710. const std::string& specialDesc = getSpecialDescription();
  711. if (!specialDesc.empty()) {
  712. propWriteStream.write<uint8_t>(ATTR_DESC);
  713. propWriteStream.writeString(specialDesc);
  714. }
  715.  
  716. if (hasAttribute(ITEM_ATTRIBUTE_DURATION)) {
  717. propWriteStream.write<uint8_t>(ATTR_DURATION);
  718. propWriteStream.write<uint32_t>(getIntAttr(ITEM_ATTRIBUTE_DURATION));
  719. }
  720.  
  721. ItemDecayState_t decayState = getDecaying();
  722. if (decayState == DECAYING_TRUE || decayState == DECAYING_PENDING) {
  723. propWriteStream.write<uint8_t>(ATTR_DECAYING_STATE);
  724. propWriteStream.write<uint8_t>(decayState);
  725. }
  726.  
  727. if (hasAttribute(ITEM_ATTRIBUTE_NAME)) {
  728. propWriteStream.write<uint8_t>(ATTR_NAME);
  729. propWriteStream.writeString(getStrAttr(ITEM_ATTRIBUTE_NAME));
  730. }
  731.  
  732. if (hasAttribute(ITEM_ATTRIBUTE_ARTICLE)) {
  733. propWriteStream.write<uint8_t>(ATTR_ARTICLE);
  734. propWriteStream.writeString(getStrAttr(ITEM_ATTRIBUTE_ARTICLE));
  735. }
  736.  
  737. if (hasAttribute(ITEM_ATTRIBUTE_PLURALNAME)) {
  738. propWriteStream.write<uint8_t>(ATTR_PLURALNAME);
  739. propWriteStream.writeString(getStrAttr(ITEM_ATTRIBUTE_PLURALNAME));
  740. }
  741.  
  742. if (hasAttribute(ITEM_ATTRIBUTE_WEIGHT)) {
  743. propWriteStream.write<uint8_t>(ATTR_WEIGHT);
  744. propWriteStream.write<uint32_t>(getIntAttr(ITEM_ATTRIBUTE_WEIGHT));
  745. }
  746.  
  747. if (hasAttribute(ITEM_ATTRIBUTE_ATTACK)) {
  748. propWriteStream.write<uint8_t>(ATTR_ATTACK);
  749. propWriteStream.write<int32_t>(getIntAttr(ITEM_ATTRIBUTE_ATTACK));
  750. }
  751.  
  752. if (hasAttribute(ITEM_ATTRIBUTE_DEFENSE)) {
  753. propWriteStream.write<uint8_t>(ATTR_DEFENSE);
  754. propWriteStream.write<int32_t>(getIntAttr(ITEM_ATTRIBUTE_DEFENSE));
  755. }
  756.  
  757. if (hasAttribute(ITEM_ATTRIBUTE_EXTRADEFENSE)) {
  758. propWriteStream.write<uint8_t>(ATTR_EXTRADEFENSE);
  759. propWriteStream.write<int32_t>(getIntAttr(ITEM_ATTRIBUTE_EXTRADEFENSE));
  760. }
  761.  
  762. if (hasAttribute(ITEM_ATTRIBUTE_ARMOR)) {
  763. propWriteStream.write<uint8_t>(ATTR_ARMOR);
  764. propWriteStream.write<int32_t>(getIntAttr(ITEM_ATTRIBUTE_ARMOR));
  765. }
  766.  
  767. if (hasAttribute(ITEM_ATTRIBUTE_HITCHANCE)) {
  768. propWriteStream.write<uint8_t>(ATTR_HITCHANCE);
  769. propWriteStream.write<int8_t>(getIntAttr(ITEM_ATTRIBUTE_HITCHANCE));
  770. }
  771.  
  772. if (hasAttribute(ITEM_ATTRIBUTE_SHOOTRANGE)) {
  773. propWriteStream.write<uint8_t>(ATTR_SHOOTRANGE);
  774. propWriteStream.write<uint8_t>(getIntAttr(ITEM_ATTRIBUTE_SHOOTRANGE));
  775. }
  776.  
  777. if (hasAttribute(ITEM_ATTRIBUTE_CUSTOM)) {
  778. const ItemAttributes::CustomAttributeMap* customAttrMap = attributes->getCustomAttributeMap();
  779. propWriteStream.write<uint8_t>(ATTR_CUSTOM_ATTRIBUTES);
  780. propWriteStream.write<uint64_t>(static_cast<uint64_t>(customAttrMap->size()));
  781. for (const auto &entry : *customAttrMap) {
  782. // Serializing key type and value
  783. propWriteStream.writeString(entry.first);
  784.  
  785. // Serializing value type and value
  786. entry.second.serialize(propWriteStream);
  787. }
  788. }
  789. }
  790.  
  791. bool Item::hasProperty(ITEMPROPERTY prop) const
  792. {
  793. const ItemType& it = items[id];
  794. switch (prop) {
  795. case CONST_PROP_BLOCKSOLID: return it.blockSolid;
  796. case CONST_PROP_MOVEABLE: return it.moveable && !hasAttribute(ITEM_ATTRIBUTE_UNIQUEID);
  797. case CONST_PROP_HASHEIGHT: return it.hasHeight;
  798. case CONST_PROP_BLOCKPROJECTILE: return it.blockProjectile;
  799. case CONST_PROP_BLOCKPATH: return it.blockPathFind;
  800. case CONST_PROP_ISVERTICAL: return it.isVertical;
  801. case CONST_PROP_ISHORIZONTAL: return it.isHorizontal;
  802. case CONST_PROP_IMMOVABLEBLOCKSOLID: return it.blockSolid && (!it.moveable || hasAttribute(ITEM_ATTRIBUTE_UNIQUEID));
  803. case CONST_PROP_IMMOVABLEBLOCKPATH: return it.blockPathFind && (!it.moveable || hasAttribute(ITEM_ATTRIBUTE_UNIQUEID));
  804. case CONST_PROP_IMMOVABLENOFIELDBLOCKPATH: return !it.isMagicField() && it.blockPathFind && (!it.moveable || hasAttribute(ITEM_ATTRIBUTE_UNIQUEID));
  805. case CONST_PROP_NOFIELDBLOCKPATH: return !it.isMagicField() && it.blockPathFind;
  806. case CONST_PROP_SUPPORTHANGABLE: return it.isHorizontal || it.isVertical;
  807. default: return false;
  808. }
  809. }
  810.  
  811. uint32_t Item::getWeight() const
  812. {
  813. uint32_t weight = getBaseWeight();
  814. if (isStackable()) {
  815. return weight * std::max<uint32_t>(1, getItemCount());
  816. }
  817. return weight;
  818. }
  819.  
  820. std::string Item::getDescription(const ItemType& it, int32_t lookDistance,
  821. const Item* item /*= nullptr*/, int32_t subType /*= -1*/, bool addArticle /*= true*/)
  822. {
  823. const std::string* text = nullptr;
  824.  
  825. std::ostringstream s;
  826. s << getNameDescription(it, item, subType, addArticle);
  827.  
  828. if (item) {
  829. subType = item->getSubType();
  830. }
  831.  
  832. if (it.isRune()) {
  833. if (it.runeLevel > 0 || it.runeMagLevel > 0) {
  834. if (RuneSpell* rune = g_spells->getRuneSpell(it.id)) {
  835. int32_t tmpSubType = subType;
  836. if (item) {
  837. tmpSubType = item->getSubType();
  838. }
  839. s << ". " << (it.stackable && tmpSubType > 1 ? "They" : "It") << " can only be used by ";
  840.  
  841. const VocSpellMap& vocMap = rune->getVocMap();
  842. std::vector<Vocation*> showVocMap;
  843.  
  844. // vocations are usually listed with the unpromoted and promoted version, the latter being
  845. // hidden from description, so `total / 2` is most likely the amount of vocations to be shown.
  846. showVocMap.reserve(vocMap.size() / 2);
  847. for (const auto& voc : vocMap) {
  848. if (voc.second) {
  849. showVocMap.push_back(g_vocations.getVocation(voc.first));
  850. }
  851. }
  852.  
  853. if (!showVocMap.empty()) {
  854. auto vocIt = showVocMap.begin(), vocLast = (showVocMap.end() - 1);
  855. while (vocIt != vocLast) {
  856. s << asLowerCaseString((*vocIt)->getVocName()) << "s";
  857. if (++vocIt == vocLast) {
  858. s << " and ";
  859. } else {
  860. s << ", ";
  861. }
  862. }
  863. s << asLowerCaseString((*vocLast)->getVocName()) << "s";
  864. } else {
  865. s << "players";
  866. }
  867.  
  868. s << " with";
  869.  
  870. if (it.runeLevel > 0) {
  871. s << " level " << it.runeLevel;
  872. }
  873.  
  874. if (it.runeMagLevel > 0) {
  875. if (it.runeLevel > 0) {
  876. s << " and";
  877. }
  878.  
  879. s << " magic level " << it.runeMagLevel;
  880. }
  881.  
  882. s << " or higher";
  883. }
  884. }
  885. } else if (it.weaponType != WEAPON_NONE) {
  886. bool begin = true;
  887. if (it.weaponType == WEAPON_DISTANCE && it.ammoType != AMMO_NONE) {
  888. s << " (Range:" << static_cast<uint16_t>(item ? item->getShootRange() : it.shootRange);
  889.  
  890. int32_t attack;
  891. int8_t hitChance;
  892. if (item) {
  893. attack = item->getAttack();
  894. hitChance = item->getHitChance();
  895. } else {
  896. attack = it.attack;
  897. hitChance = it.hitChance;
  898. }
  899.  
  900. if (attack != 0) {
  901. s << ", Atk" << std::showpos << attack << std::noshowpos;
  902. }
  903.  
  904. if (hitChance != 0) {
  905. s << ", Hit%" << std::showpos << static_cast<int16_t>(hitChance) << std::noshowpos;
  906. }
  907.  
  908. begin = false;
  909. } else if (it.weaponType != WEAPON_AMMO) {
  910.  
  911. int32_t attack, defense, extraDefense, reflection;
  912. if (item) {
  913. attack = item->getAttack();
  914. defense = item->getDefense();
  915. extraDefense = item->getExtraDefense();
  916. reflection = item->getReflection();
  917. } else {
  918. attack = it.attack;
  919. defense = it.defense;
  920. extraDefense = it.extraDefense;
  921. reflection = it.reflection;
  922. }
  923. if (attack != 0) {
  924. begin = false;
  925. s << " (Atk:" << attack;
  926.  
  927. if (it.abilities && it.abilities->elementType != COMBAT_NONE && it.abilities->elementDamage != 0) {
  928. s << " physical + " << it.abilities->elementDamage << ' ' << getCombatName(it.abilities->elementType);
  929. }
  930. }
  931.  
  932. if (defense != 0 || extraDefense != 0) {
  933. if (begin) {
  934. begin = false;
  935. s << " (";
  936. } else {
  937. s << ", ";
  938. }
  939.  
  940. s << "Def:" << defense;
  941. if (extraDefense != 0) {
  942. s << ' ' << std::showpos << extraDefense << std::noshowpos;
  943. }
  944. }
  945. }
  946.  
  947. if (reflection != 0) {
  948. if (begin) {
  949. begin = false;
  950. s << " (";
  951. } else {
  952. s << ", ";
  953. }
  954.  
  955. s << "Reflection: " << reflection << std::noshowpos << "%";
  956.  
  957. }
  958.  
  959. if (it.abilities) {
  960. for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; i++) {
  961. if (!it.abilities->skills[i]) {
  962. continue;
  963. }
  964.  
  965. if (begin) {
  966. begin = false;
  967. s << " (";
  968. } else {
  969. s << ", ";
  970. }
  971.  
  972. s << getSkillName(i) << ' ' << std::showpos << it.abilities->skills[i] << std::noshowpos;
  973. }
  974.  
  975. for (uint8_t i = SPECIALSKILL_FIRST; i <= SPECIALSKILL_LAST; i++) {
  976. if (!it.abilities->specialSkills[i]) {
  977. continue;
  978. }
  979.  
  980. if (begin) {
  981. begin = false;
  982. s << " (";
  983. } else {
  984. s << ", ";
  985. }
  986.  
  987. s << getSpecialSkillName(i) << ' ' << std::showpos << it.abilities->specialSkills[i] << '%' << std::noshowpos;
  988. }
  989.  
  990. if (it.abilities->stats[STAT_MAGICPOINTS]) {
  991. if (begin) {
  992. begin = false;
  993. s << " (";
  994. } else {
  995. s << ", ";
  996. }
  997.  
  998. s << "magic level " << std::showpos << it.abilities->stats[STAT_MAGICPOINTS] << std::noshowpos;
  999. }
  1000.  
  1001. int16_t show = it.abilities->absorbPercent[0];
  1002. if (show != 0) {
  1003. for (size_t i = 1; i < COMBAT_COUNT; ++i) {
  1004. if (it.abilities->absorbPercent[i] != show) {
  1005. show = 0;
  1006. break;
  1007. }
  1008. }
  1009. }
  1010.  
  1011. if (show == 0) {
  1012. bool tmp = true;
  1013.  
  1014. for (size_t i = 0; i < COMBAT_COUNT; ++i) {
  1015. if (it.abilities->absorbPercent[i] == 0) {
  1016. continue;
  1017. }
  1018.  
  1019. if (tmp) {
  1020. tmp = false;
  1021.  
  1022. if (begin) {
  1023. begin = false;
  1024. s << " (";
  1025. } else {
  1026. s << ", ";
  1027. }
  1028.  
  1029. s << "protection ";
  1030. } else {
  1031. s << ", ";
  1032. }
  1033.  
  1034. s << getCombatName(indexToCombatType(i)) << ' ' << std::showpos << it.abilities->absorbPercent[i] << std::noshowpos << '%';
  1035. }
  1036. } else {
  1037. if (begin) {
  1038. begin = false;
  1039. s << " (";
  1040. } else {
  1041. s << ", ";
  1042. }
  1043.  
  1044. s << "protection all " << std::showpos << show << std::noshowpos << '%';
  1045. }
  1046.  
  1047. show = it.abilities->fieldAbsorbPercent[0];
  1048. if (show != 0) {
  1049. for (size_t i = 1; i < COMBAT_COUNT; ++i) {
  1050. if (it.abilities->absorbPercent[i] != show) {
  1051. show = 0;
  1052. break;
  1053. }
  1054. }
  1055. }
  1056.  
  1057. if (show == 0) {
  1058. bool tmp = true;
  1059.  
  1060. for (size_t i = 0; i < COMBAT_COUNT; ++i) {
  1061. if (it.abilities->fieldAbsorbPercent[i] == 0) {
  1062. continue;
  1063. }
  1064.  
  1065. if (tmp) {
  1066. tmp = false;
  1067.  
  1068. if (begin) {
  1069. begin = false;
  1070. s << " (";
  1071. } else {
  1072. s << ", ";
  1073. }
  1074.  
  1075. s << "protection ";
  1076. } else {
  1077. s << ", ";
  1078. }
  1079.  
  1080. s << getCombatName(indexToCombatType(i)) << " field " << std::showpos << it.abilities->fieldAbsorbPercent[i] << std::noshowpos << '%';
  1081. }
  1082. } else {
  1083. if (begin) {
  1084. begin = false;
  1085. s << " (";
  1086. } else {
  1087. s << ", ";
  1088. }
  1089.  
  1090. s << "protection all fields " << std::showpos << show << std::noshowpos << '%';
  1091. }
  1092.  
  1093. if (it.abilities->speed) {
  1094. if (begin) {
  1095. begin = false;
  1096. s << " (";
  1097. } else {
  1098. s << ", ";
  1099. }
  1100.  
  1101. s << "speed " << std::showpos << (it.abilities->speed >> 1) << std::noshowpos;
  1102. }
  1103. }
  1104.  
  1105. if (!begin) {
  1106. s << ')';
  1107. }
  1108. } else if (it.armor != 0 || (item && item->getArmor() != 0) || it.showAttributes) {
  1109. bool begin = true;
  1110.  
  1111. int32_t armor = (item ? item->getArmor() : it.armor);
  1112. if (armor != 0) {
  1113. s << " (Arm:" << armor;
  1114. begin = false;
  1115. }
  1116.  
  1117. if (it.abilities) {
  1118. for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; i++) {
  1119. if (!it.abilities->skills[i]) {
  1120. continue;
  1121. }
  1122.  
  1123. if (begin) {
  1124. begin = false;
  1125. s << " (";
  1126. } else {
  1127. s << ", ";
  1128. }
  1129.  
  1130. s << getSkillName(i) << ' ' << std::showpos << it.abilities->skills[i] << std::noshowpos;
  1131. }
  1132.  
  1133. if (it.abilities->stats[STAT_MAGICPOINTS]) {
  1134. if (begin) {
  1135. begin = false;
  1136. s << " (";
  1137. } else {
  1138. s << ", ";
  1139. }
  1140.  
  1141. s << "magic level " << std::showpos << it.abilities->stats[STAT_MAGICPOINTS] << std::noshowpos;
  1142. }
  1143.  
  1144. int16_t show = it.abilities->absorbPercent[0];
  1145. if (show != 0) {
  1146. for (size_t i = 1; i < COMBAT_COUNT; ++i) {
  1147. if (it.abilities->absorbPercent[i] != show) {
  1148. show = 0;
  1149. break;
  1150. }
  1151. }
  1152. }
  1153.  
  1154. if (!show) {
  1155. bool protectionBegin = true;
  1156. for (size_t i = 0; i < COMBAT_COUNT; ++i) {
  1157. if (it.abilities->absorbPercent[i] == 0) {
  1158. continue;
  1159. }
  1160.  
  1161. if (protectionBegin) {
  1162. protectionBegin = false;
  1163.  
  1164. if (begin) {
  1165. begin = false;
  1166. s << " (";
  1167. } else {
  1168. s << ", ";
  1169. }
  1170.  
  1171. s << "protection ";
  1172. } else {
  1173. s << ", ";
  1174. }
  1175.  
  1176. s << getCombatName(indexToCombatType(i)) << ' ' << std::showpos << it.abilities->absorbPercent[i] << std::noshowpos << '%';
  1177. }
  1178. } else {
  1179. if (begin) {
  1180. begin = false;
  1181. s << " (";
  1182. } else {
  1183. s << ", ";
  1184. }
  1185.  
  1186. s << "protection all " << std::showpos << show << std::noshowpos << '%';
  1187. }
  1188.  
  1189. show = it.abilities->fieldAbsorbPercent[0];
  1190. if (show != 0) {
  1191. for (size_t i = 1; i < COMBAT_COUNT; ++i) {
  1192. if (it.abilities->absorbPercent[i] != show) {
  1193. show = 0;
  1194. break;
  1195. }
  1196. }
  1197. }
  1198.  
  1199. if (!show) {
  1200. bool tmp = true;
  1201.  
  1202. for (size_t i = 0; i < COMBAT_COUNT; ++i) {
  1203. if (it.abilities->fieldAbsorbPercent[i] == 0) {
  1204. continue;
  1205. }
  1206.  
  1207. if (tmp) {
  1208. tmp = false;
  1209.  
  1210. if (begin) {
  1211. begin = false;
  1212. s << " (";
  1213. } else {
  1214. s << ", ";
  1215. }
  1216.  
  1217. s << "protection ";
  1218. } else {
  1219. s << ", ";
  1220. }
  1221.  
  1222. s << getCombatName(indexToCombatType(i)) << " field " << std::showpos << it.abilities->fieldAbsorbPercent[i] << std::noshowpos << '%';
  1223. }
  1224. } else {
  1225. if (begin) {
  1226. begin = false;
  1227. s << " (";
  1228. } else {
  1229. s << ", ";
  1230. }
  1231.  
  1232. s << "protection all fields " << std::showpos << show << std::noshowpos << '%';
  1233. }
  1234.  
  1235. if (it.abilities->speed) {
  1236. if (begin) {
  1237. begin = false;
  1238. s << " (";
  1239. } else {
  1240. s << ", ";
  1241. }
  1242.  
  1243. s << "speed " << std::showpos << (it.abilities->speed >> 1) << std::noshowpos;
  1244. }
  1245. }
  1246.  
  1247. if (!begin) {
  1248. s << ')';
  1249. }
  1250. } else if (it.isContainer() || (item && item->getContainer())) {
  1251. uint32_t volume = 0;
  1252. if (!item || !item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
  1253. if (it.isContainer()) {
  1254. volume = it.maxItems;
  1255. } else {
  1256. volume = item->getContainer()->capacity();
  1257. }
  1258. }
  1259.  
  1260. if (volume != 0) {
  1261. s << " (Vol:" << volume << ')';
  1262. }
  1263. } else {
  1264. bool found = true;
  1265.  
  1266. if (it.abilities) {
  1267. if (it.abilities->speed > 0) {
  1268. s << " (speed " << std::showpos << (it.abilities->speed / 2) << std::noshowpos << ')';
  1269. } else if (hasBitSet(CONDITION_DRUNK, it.abilities->conditionSuppressions)) {
  1270. s << " (hard drinking)";
  1271. } else if (it.abilities->invisible) {
  1272. s << " (invisibility)";
  1273. } else if (it.abilities->regeneration) {
  1274. s << " (faster regeneration)";
  1275. } else if (it.abilities->manaShield) {
  1276. s << " (mana shield)";
  1277. } else {
  1278. found = false;
  1279. }
  1280. } else {
  1281. found = false;
  1282. }
  1283.  
  1284. if (!found) {
  1285. if (it.isKey()) {
  1286. s << " (Key:" << (item ? item->getActionId() : 0) << ')';
  1287. } else if (it.isFluidContainer()) {
  1288. if (subType > 0) {
  1289. const std::string& itemName = items[subType].name;
  1290. s << " of " << (!itemName.empty() ? itemName : "unknown");
  1291. } else {
  1292. s << ". It is empty";
  1293. }
  1294. } else if (it.isSplash()) {
  1295. s << " of ";
  1296.  
  1297. if (subType > 0 && !items[subType].name.empty()) {
  1298. s << items[subType].name;
  1299. } else {
  1300. s << "unknown";
  1301. }
  1302. } else if (it.allowDistRead && (it.id < 7369 || it.id > 7371)) {
  1303. s << ".\n";
  1304.  
  1305. if (lookDistance <= 4) {
  1306. if (item) {
  1307. text = &item->getText();
  1308. if (!text->empty()) {
  1309. const std::string& writer = item->getWriter();
  1310. if (!writer.empty()) {
  1311. s << writer << " wrote";
  1312. time_t date = item->getDate();
  1313. if (date != 0) {
  1314. s << " on " << formatDateShort(date);
  1315. }
  1316. s << ": ";
  1317. } else {
  1318. s << "You read: ";
  1319. }
  1320. s << *text;
  1321. } else {
  1322. s << "Nothing is written on it";
  1323. }
  1324. } else {
  1325. s << "Nothing is written on it";
  1326. }
  1327. } else {
  1328. s << "You are too far away to read it";
  1329. }
  1330. } else if (it.levelDoor != 0 && item) {
  1331. uint16_t actionId = item->getActionId();
  1332. if (actionId >= it.levelDoor) {
  1333. s << " for level " << (actionId - it.levelDoor);
  1334. }
  1335. }
  1336. }
  1337. }
  1338.  
  1339. if (it.showCharges) {
  1340. s << " that has " << subType << " charge" << (subType != 1 ? "s" : "") << " left";
  1341. }
  1342.  
  1343. if (it.showDuration) {
  1344. if (item && item->hasAttribute(ITEM_ATTRIBUTE_DURATION)) {
  1345. uint32_t duration = item->getDuration() / 1000;
  1346. s << " that will expire in ";
  1347.  
  1348. if (duration >= 86400) {
  1349. uint16_t days = duration / 86400;
  1350. uint16_t hours = (duration % 86400) / 3600;
  1351. s << days << " day" << (days != 1 ? "s" : "");
  1352.  
  1353. if (hours > 0) {
  1354. s << " and " << hours << " hour" << (hours != 1 ? "s" : "");
  1355. }
  1356. } else if (duration >= 3600) {
  1357. uint16_t hours = duration / 3600;
  1358. uint16_t minutes = (duration % 3600) / 60;
  1359. s << hours << " hour" << (hours != 1 ? "s" : "");
  1360.  
  1361. if (minutes > 0) {
  1362. s << " and " << minutes << " minute" << (minutes != 1 ? "s" : "");
  1363. }
  1364. } else if (duration >= 60) {
  1365. uint16_t minutes = duration / 60;
  1366. s << minutes << " minute" << (minutes != 1 ? "s" : "");
  1367. uint16_t seconds = duration % 60;
  1368.  
  1369. if (seconds > 0) {
  1370. s << " and " << seconds << " second" << (seconds != 1 ? "s" : "");
  1371. }
  1372. } else {
  1373. s << duration << " second" << (duration != 1 ? "s" : "");
  1374. }
  1375. } else {
  1376. s << " that is brand-new";
  1377. }
  1378. }
  1379.  
  1380. if (!it.allowDistRead || (it.id >= 7369 && it.id <= 7371)) {
  1381. s << '.';
  1382. } else {
  1383. if (!text && item) {
  1384. text = &item->getText();
  1385. }
  1386.  
  1387. if (!text || text->empty()) {
  1388. s << '.';
  1389. }
  1390. }
  1391.  
  1392. if (it.wieldInfo != 0) {
  1393. s << "\nIt can only be wielded properly by ";
  1394.  
  1395. if (it.wieldInfo & WIELDINFO_PREMIUM) {
  1396. s << "premium ";
  1397. }
  1398.  
  1399. if (!it.vocationString.empty()) {
  1400. s << it.vocationString;
  1401. } else {
  1402. s << "players";
  1403. }
  1404.  
  1405. if (it.wieldInfo & WIELDINFO_LEVEL) {
  1406. s << " of level " << it.minReqLevel << " or higher";
  1407. }
  1408.  
  1409. if (it.wieldInfo & WIELDINFO_MAGLV) {
  1410. if (it.wieldInfo & WIELDINFO_LEVEL) {
  1411. s << " and";
  1412. } else {
  1413. s << " of";
  1414. }
  1415.  
  1416. s << " magic level " << it.minReqMagicLevel << " or higher";
  1417. }
  1418.  
  1419. s << '.';
  1420. }
  1421.  
  1422. if (lookDistance <= 1) {
  1423. if (item) {
  1424. const uint32_t weight = item->getWeight();
  1425. if (weight != 0 && it.pickupable) {
  1426. s << '\n' << getWeightDescription(it, weight, item->getItemCount());
  1427. }
  1428. } else if (it.weight != 0 && it.pickupable) {
  1429. s << '\n' << getWeightDescription(it, it.weight);
  1430. }
  1431. }
  1432.  
  1433. if (item) {
  1434. const std::string& specialDescription = item->getSpecialDescription();
  1435. if (!specialDescription.empty()) {
  1436. s << '\n' << specialDescription;
  1437. } else if (lookDistance <= 1 && !it.description.empty()) {
  1438. s << '\n' << it.description;
  1439. }
  1440. } else if (lookDistance <= 1 && !it.description.empty()) {
  1441. s << '\n' << it.description;
  1442. }
  1443.  
  1444. if (it.allowDistRead && it.id >= 7369 && it.id <= 7371) {
  1445. if (!text && item) {
  1446. text = &item->getText();
  1447. }
  1448.  
  1449. if (text && !text->empty()) {
  1450. s << '\n' << *text;
  1451. }
  1452. }
  1453. return s.str();
  1454. }
  1455.  
  1456. std::string Item::getDescription(int32_t lookDistance) const
  1457. {
  1458. const ItemType& it = items[id];
  1459. return getDescription(it, lookDistance, this);
  1460. }
  1461.  
  1462. std::string Item::getNameDescription(const ItemType& it, const Item* item /*= nullptr*/, int32_t subType /*= -1*/, bool addArticle /*= true*/)
  1463. {
  1464. if (item) {
  1465. subType = item->getSubType();
  1466. }
  1467.  
  1468. std::ostringstream s;
  1469.  
  1470. const std::string& name = (item ? item->getName() : it.name);
  1471. if (!name.empty()) {
  1472. if (it.stackable && subType > 1) {
  1473. if (it.showCount) {
  1474. s << subType << ' ';
  1475. }
  1476.  
  1477. s << (item ? item->getPluralName() : it.getPluralName());
  1478. } else {
  1479. if (addArticle) {
  1480. const std::string& article = (item ? item->getArticle() : it.article);
  1481. if (!article.empty()) {
  1482. s << article << ' ';
  1483. }
  1484. }
  1485.  
  1486. s << name;
  1487. }
  1488. } else {
  1489. s << "an item of type " << it.id;
  1490. }
  1491. return s.str();
  1492. }
  1493.  
  1494. std::string Item::getNameDescription() const
  1495. {
  1496. const ItemType& it = items[id];
  1497. return getNameDescription(it, this);
  1498. }
  1499.  
  1500. std::string Item::getWeightDescription(const ItemType& it, uint32_t weight, uint32_t count /*= 1*/)
  1501. {
  1502. std::ostringstream ss;
  1503. if (it.stackable && count > 1 && it.showCount != 0) {
  1504. ss << "They weigh ";
  1505. } else {
  1506. ss << "It weighs ";
  1507. }
  1508.  
  1509. if (weight < 10) {
  1510. ss << "0.0" << weight;
  1511. } else if (weight < 100) {
  1512. ss << "0." << weight;
  1513. } else {
  1514. std::string weightString = std::to_string(weight);
  1515. weightString.insert(weightString.end() - 2, '.');
  1516. ss << weightString;
  1517. }
  1518.  
  1519. ss << " oz.";
  1520. return ss.str();
  1521. }
  1522.  
  1523. std::string Item::getWeightDescription(uint32_t weight) const
  1524. {
  1525. const ItemType& it = Item::items[id];
  1526. return getWeightDescription(it, weight, getItemCount());
  1527. }
  1528.  
  1529. std::string Item::getWeightDescription() const
  1530. {
  1531. uint32_t weight = getWeight();
  1532. if (weight == 0) {
  1533. return std::string();
  1534. }
  1535. return getWeightDescription(weight);
  1536. }
  1537.  
  1538. void Item::setUniqueId(uint16_t n)
  1539. {
  1540. if (hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
  1541. return;
  1542. }
  1543.  
  1544. if (g_game.addUniqueItem(n, this)) {
  1545. getAttributes()->setUniqueId(n);
  1546. }
  1547. }
  1548.  
  1549. bool Item::canDecay() const
  1550. {
  1551. if (isRemoved()) {
  1552. return false;
  1553. }
  1554.  
  1555. const ItemType& it = Item::items[id];
  1556. if (it.decayTo < 0 || it.decayTime == 0) {
  1557. return false;
  1558. }
  1559.  
  1560. if (hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
  1561. return false;
  1562. }
  1563.  
  1564. return true;
  1565. }
  1566.  
  1567. uint32_t Item::getWorth() const
  1568. {
  1569. switch (id) {
  1570. case ITEM_GOLD_COIN:
  1571. return count;
  1572.  
  1573. case ITEM_PLATINUM_COIN:
  1574. return count * 100;
  1575.  
  1576. case ITEM_CRYSTAL_COIN:
  1577. return count * 10000;
  1578.  
  1579. default:
  1580. return 0;
  1581. }
  1582. }
  1583.  
  1584. LightInfo Item::getLightInfo() const
  1585. {
  1586. const ItemType& it = items[id];
  1587. return {it.lightLevel, it.lightColor};
  1588. }
  1589.  
  1590. std::string ItemAttributes::emptyString;
  1591. int64_t ItemAttributes::emptyInt;
  1592. double ItemAttributes::emptyDouble;
  1593. bool ItemAttributes::emptyBool;
  1594.  
  1595. const std::string& ItemAttributes::getStrAttr(itemAttrTypes type) const
  1596. {
  1597. if (!isStrAttrType(type)) {
  1598. return emptyString;
  1599. }
  1600.  
  1601. const Attribute* attr = getExistingAttr(type);
  1602. if (!attr) {
  1603. return emptyString;
  1604. }
  1605. return *attr->value.string;
  1606. }
  1607.  
  1608. void ItemAttributes::setStrAttr(itemAttrTypes type, const std::string& value)
  1609. {
  1610. if (!isStrAttrType(type)) {
  1611. return;
  1612. }
  1613.  
  1614. if (value.empty()) {
  1615. return;
  1616. }
  1617.  
  1618. Attribute& attr = getAttr(type);
  1619. delete attr.value.string;
  1620. attr.value.string = new std::string(value);
  1621. }
  1622.  
  1623. void ItemAttributes::removeAttribute(itemAttrTypes type)
  1624. {
  1625. if (!hasAttribute(type)) {
  1626. return;
  1627. }
  1628.  
  1629. auto prev_it = attributes.cbegin();
  1630. if ((*prev_it).type == type) {
  1631. attributes.pop_front();
  1632. } else {
  1633. auto it = prev_it, end = attributes.cend();
  1634. while (++it != end) {
  1635. if ((*it).type == type) {
  1636. attributes.erase_after(prev_it);
  1637. break;
  1638. }
  1639. prev_it = it;
  1640. }
  1641. }
  1642. attributeBits &= ~type;
  1643. }
  1644.  
  1645. int64_t ItemAttributes::getIntAttr(itemAttrTypes type) const
  1646. {
  1647. if (!isIntAttrType(type)) {
  1648. return 0;
  1649. }
  1650.  
  1651. const Attribute* attr = getExistingAttr(type);
  1652. if (!attr) {
  1653. return 0;
  1654. }
  1655. return attr->value.integer;
  1656. }
  1657.  
  1658. void ItemAttributes::setIntAttr(itemAttrTypes type, int64_t value)
  1659. {
  1660. if (!isIntAttrType(type)) {
  1661. return;
  1662. }
  1663.  
  1664. getAttr(type).value.integer = value;
  1665. }
  1666.  
  1667. void ItemAttributes::increaseIntAttr(itemAttrTypes type, int64_t value)
  1668. {
  1669. if (!isIntAttrType(type)) {
  1670. return;
  1671. }
  1672.  
  1673. getAttr(type).value.integer += value;
  1674. }
  1675.  
  1676. const ItemAttributes::Attribute* ItemAttributes::getExistingAttr(itemAttrTypes type) const
  1677. {
  1678. if (hasAttribute(type)) {
  1679. for (const Attribute& attribute : attributes) {
  1680. if (attribute.type == type) {
  1681. return &attribute;
  1682. }
  1683. }
  1684. }
  1685. return nullptr;
  1686. }
  1687.  
  1688. ItemAttributes::Attribute& ItemAttributes::getAttr(itemAttrTypes type)
  1689. {
  1690. if (hasAttribute(type)) {
  1691. for (Attribute& attribute : attributes) {
  1692. if (attribute.type == type) {
  1693. return attribute;
  1694. }
  1695. }
  1696. }
  1697.  
  1698. attributeBits |= type;
  1699. attributes.emplace_front(type);
  1700. return attributes.front();
  1701. }
  1702.  
  1703. void Item::startDecaying()
  1704. {
  1705. g_game.startDecay(this);
  1706. }
  1707.  
  1708. bool Item::hasMarketAttributes() const
  1709. {
  1710. if (attributes == nullptr) {
  1711. return true;
  1712. }
  1713.  
  1714. for (const auto& attr : attributes->getList()) {
  1715. if (attr.type == ITEM_ATTRIBUTE_CHARGES) {
  1716. uint16_t charges = static_cast<uint16_t>(attr.value.integer);
  1717. if (charges != items[id].charges) {
  1718. return false;
  1719. }
  1720. } else if (attr.type == ITEM_ATTRIBUTE_DURATION) {
  1721. uint32_t duration = static_cast<uint32_t>(attr.value.integer);
  1722. if (duration != getDefaultDuration()) {
  1723. return false;
  1724. }
  1725. } else {
  1726. return false;
  1727. }
  1728. }
  1729. return true;
  1730. }
  1731.  
  1732. template<>
  1733. const std::string& ItemAttributes::CustomAttribute::get<std::string>() {
  1734. if (value.type() == typeid(std::string)) {
  1735. return boost::get<std::string>(value);
  1736. }
  1737.  
  1738. return emptyString;
  1739. }
  1740.  
  1741. template<>
  1742. const int64_t& ItemAttributes::CustomAttribute::get<int64_t>() {
  1743. if (value.type() == typeid(int64_t)) {
  1744. return boost::get<int64_t>(value);
  1745. }
  1746.  
  1747. return emptyInt;
  1748. }
  1749.  
  1750. template<>
  1751. const double& ItemAttributes::CustomAttribute::get<double>() {
  1752. if (value.type() == typeid(double)) {
  1753. return boost::get<double>(value);
  1754. }
  1755.  
  1756. return emptyDouble;
  1757. }
  1758.  
  1759. template<>
  1760. const bool& ItemAttributes::CustomAttribute::get<bool>() {
  1761. if (value.type() == typeid(bool)) {
  1762. return boost::get<bool>(value);
  1763. }
  1764.  
  1765. return emptyBool;
  1766. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement