Advertisement
Guest User

Untitled

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