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#ifndef FS_ITEM_H_009A319FB13D477D9EEFFBBD9BB83562
21#define FS_ITEM_H_009A319FB13D477D9EEFFBBD9BB83562
22
23#include "cylinder.h"
24#include "items.h"
25#include "luascript.h"
26#include "thing.h"
27#include "tools.h"
28#include <typeinfo>
29
30#include <boost/lexical_cast.hpp>
31#include <boost/variant.hpp>
32#include <deque>
33
34class Creature;
35class Player;
36class Container;
37class Depot;
38class Teleport;
39class TrashHolder;
40class Mailbox;
41class Door;
42class MagicField;
43class BedItem;
44
45enum ITEMPROPERTY {
46 CONST_PROP_BLOCKSOLID = 0,
47 CONST_PROP_HASHEIGHT,
48 CONST_PROP_BLOCKPROJECTILE,
49 CONST_PROP_BLOCKPATH,
50 CONST_PROP_ISVERTICAL,
51 CONST_PROP_ISHORIZONTAL,
52 CONST_PROP_MOVEABLE,
53 CONST_PROP_IMMOVABLEBLOCKSOLID,
54 CONST_PROP_IMMOVABLEBLOCKPATH,
55 CONST_PROP_IMMOVABLENOFIELDBLOCKPATH,
56 CONST_PROP_NOFIELDBLOCKPATH,
57 CONST_PROP_SUPPORTHANGABLE,
58};
59
60enum TradeEvents_t {
61 ON_TRADE_TRANSFER,
62 ON_TRADE_CANCEL,
63};
64
65enum ItemDecayState_t : uint8_t {
66 DECAYING_FALSE = 0,
67 DECAYING_TRUE,
68 DECAYING_PENDING,
69};
70
71enum AttrTypes_t {
72 // ATTR_DESCRIPTION = 1,
73 // ATTR_EXT_FILE = 2,
74 ATTR_TILE_FLAGS = 3,
75 ATTR_ACTION_ID = 4,
76 ATTR_UNIQUE_ID = 5,
77 ATTR_TEXT = 6,
78 ATTR_DESC = 7,
79 ATTR_TELE_DEST = 8,
80 ATTR_ITEM = 9,
81 ATTR_DEPOT_ID = 10,
82 // ATTR_EXT_SPAWN_FILE = 11,
83 ATTR_RUNE_CHARGES = 12,
84 // ATTR_EXT_HOUSE_FILE = 13,
85 ATTR_HOUSEDOORID = 14,
86 ATTR_COUNT = 15,
87 ATTR_DURATION = 16,
88 ATTR_DECAYING_STATE = 17,
89 ATTR_WRITTENDATE = 18,
90 ATTR_WRITTENBY = 19,
91 ATTR_SLEEPERGUID = 20,
92 ATTR_SLEEPSTART = 21,
93 ATTR_CHARGES = 22,
94 ATTR_CONTAINER_ITEMS = 23,
95 ATTR_NAME = 24,
96 ATTR_ARTICLE = 25,
97 ATTR_PLURALNAME = 26,
98 ATTR_WEIGHT = 27,
99 ATTR_ATTACK = 28,
100 ATTR_DEFENSE = 29,
101 ATTR_EXTRADEFENSE = 30,
102 ATTR_ARMOR = 31,
103 ATTR_HITCHANCE = 32,
104 ATTR_SHOOTRANGE = 33,
105 ATTR_CUSTOM_ATTRIBUTES = 34,
106 ATTR_DECAYTO = 35,
107 ATTR_WRAPID = 36
108};
109
110enum Attr_ReadValue {
111 ATTR_READ_CONTINUE,
112 ATTR_READ_ERROR,
113 ATTR_READ_END,
114};
115
116class ItemAttributes {
117public:
118 ItemAttributes() = default;
119
120 void setSpecialDescription(const std::string &desc) {
121 setStrAttr(ITEM_ATTRIBUTE_DESCRIPTION, desc);
122 }
123 const std::string &getSpecialDescription() const {
124 return getStrAttr(ITEM_ATTRIBUTE_DESCRIPTION);
125 }
126
127 void setText(const std::string &text) {
128 setStrAttr(ITEM_ATTRIBUTE_TEXT, text);
129 }
130 void resetText() { removeAttribute(ITEM_ATTRIBUTE_TEXT); }
131 const std::string &getText() const { return getStrAttr(ITEM_ATTRIBUTE_TEXT); }
132
133 void setDate(int32_t n) { setIntAttr(ITEM_ATTRIBUTE_DATE, n); }
134 void resetDate() { removeAttribute(ITEM_ATTRIBUTE_DATE); }
135 time_t getDate() const {
136 return static_cast<time_t>(getIntAttr(ITEM_ATTRIBUTE_DATE));
137 }
138
139 void setWriter(const std::string &writer) {
140 setStrAttr(ITEM_ATTRIBUTE_WRITER, writer);
141 }
142 void resetWriter() { removeAttribute(ITEM_ATTRIBUTE_WRITER); }
143 const std::string &getWriter() const {
144 return getStrAttr(ITEM_ATTRIBUTE_WRITER);
145 }
146
147 void setActionId(uint16_t n) { setIntAttr(ITEM_ATTRIBUTE_ACTIONID, n); }
148 uint16_t getActionId() const {
149 return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_ACTIONID));
150 }
151
152 void setUniqueId(uint16_t n) { setIntAttr(ITEM_ATTRIBUTE_UNIQUEID, n); }
153 uint16_t getUniqueId() const {
154 return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_UNIQUEID));
155 }
156
157 void setCharges(uint16_t n) { setIntAttr(ITEM_ATTRIBUTE_CHARGES, n); }
158 uint16_t getCharges() const {
159 return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_CHARGES));
160 }
161
162 void setFluidType(uint16_t n) { setIntAttr(ITEM_ATTRIBUTE_FLUIDTYPE, n); }
163 uint16_t getFluidType() const {
164 return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_FLUIDTYPE));
165 }
166
167 void setOwner(uint32_t owner) { setIntAttr(ITEM_ATTRIBUTE_OWNER, owner); }
168 uint32_t getOwner() const { return getIntAttr(ITEM_ATTRIBUTE_OWNER); }
169
170 void setCorpseOwner(uint32_t corpseOwner) {
171 setIntAttr(ITEM_ATTRIBUTE_CORPSEOWNER, corpseOwner);
172 }
173 uint32_t getCorpseOwner() const {
174 return getIntAttr(ITEM_ATTRIBUTE_CORPSEOWNER);
175 }
176
177 void setDuration(int32_t time) { setIntAttr(ITEM_ATTRIBUTE_DURATION, time); }
178 void decreaseDuration(int32_t time) {
179 increaseIntAttr(ITEM_ATTRIBUTE_DURATION, -time);
180 }
181 uint32_t getDuration() const { return getIntAttr(ITEM_ATTRIBUTE_DURATION); }
182
183 void setDecaying(ItemDecayState_t decayState) {
184 setIntAttr(ITEM_ATTRIBUTE_DECAYSTATE, decayState);
185 }
186 ItemDecayState_t getDecaying() const {
187 return static_cast<ItemDecayState_t>(getIntAttr(ITEM_ATTRIBUTE_DECAYSTATE));
188 }
189
190 struct CustomAttribute {
191 typedef boost::variant<boost::blank, std::string, int64_t, double, bool>
192 VariantAttribute;
193 VariantAttribute value;
194
195 CustomAttribute() : value(boost::blank()) {}
196
197 template <typename T> explicit CustomAttribute(const T &v) : value(v) {}
198
199 template <typename T> void set(const T &v) { value = v; }
200
201 template <typename T> const T &get();
202
203 struct PushLuaVisitor : public boost::static_visitor<> {
204 lua_State *L;
205
206 explicit PushLuaVisitor(lua_State *L) : boost::static_visitor<>(), L(L) {}
207
208 void operator()(const boost::blank &) const { lua_pushnil(L); }
209
210 void operator()(const std::string &v) const {
211 LuaScriptInterface::pushString(L, v);
212 }
213
214 void operator()(bool v) const { LuaScriptInterface::pushBoolean(L, v); }
215
216 void operator()(const int64_t &v) const { lua_pushnumber(L, v); }
217
218 void operator()(const double &v) const { lua_pushnumber(L, v); }
219 };
220
221 void pushToLua(lua_State *L) const {
222 boost::apply_visitor(PushLuaVisitor(L), value);
223 }
224
225 struct SerializeVisitor : public boost::static_visitor<> {
226 PropWriteStream &propWriteStream;
227
228 explicit SerializeVisitor(PropWriteStream &propWriteStream)
229 : boost::static_visitor<>(), propWriteStream(propWriteStream) {}
230
231 void operator()(const boost::blank &) const {}
232
233 void operator()(const std::string &v) const {
234 propWriteStream.writeString(v);
235 }
236
237 template <typename T> void operator()(const T &v) const {
238 propWriteStream.write<T>(v);
239 }
240 };
241
242 void serialize(PropWriteStream &propWriteStream) const {
243 propWriteStream.write<uint8_t>(static_cast<uint8_t>(value.which()));
244 boost::apply_visitor(SerializeVisitor(propWriteStream), value);
245 }
246
247 bool unserialize(PropStream &propStream) {
248 // This is hard coded so it's not general, depends on the position of the
249 // variants.
250 uint8_t pos;
251 if (!propStream.read<uint8_t>(pos)) {
252 return false;
253 }
254
255 switch (pos) {
256 case 1: { // std::string
257 std::string tmp;
258 if (!propStream.readString(tmp)) {
259 return false;
260 }
261 value = tmp;
262 break;
263 }
264
265 case 2: { // int64_t
266 int64_t tmp;
267 if (!propStream.read<int64_t>(tmp)) {
268 return false;
269 }
270 value = tmp;
271 break;
272 }
273
274 case 3: { // double
275 double tmp;
276 if (!propStream.read<double>(tmp)) {
277 return false;
278 }
279 value = tmp;
280 break;
281 }
282
283 case 4: { // bool
284 bool tmp;
285 if (!propStream.read<bool>(tmp)) {
286 return false;
287 }
288 value = tmp;
289 break;
290 }
291
292 default: {
293 value = boost::blank();
294 return false;
295 }
296 }
297 return true;
298 }
299 };
300
301private:
302 bool hasAttribute(itemAttrTypes type) const {
303 return (type & attributeBits) != 0;
304 }
305 void removeAttribute(itemAttrTypes type);
306
307 static std::string emptyString;
308 static int64_t emptyInt;
309 static double emptyDouble;
310 static bool emptyBool;
311
312 typedef std::unordered_map<std::string, CustomAttribute> CustomAttributeMap;
313
314 struct Attribute {
315 union {
316 int64_t integer;
317 std::string *string;
318 CustomAttributeMap *custom;
319 } value;
320 itemAttrTypes type;
321
322 explicit Attribute(itemAttrTypes type) : type(type) {
323 memset(&value, 0, sizeof(value));
324 }
325 Attribute(const Attribute &i) {
326 type = i.type;
327 if (ItemAttributes::isIntAttrType(type)) {
328 value.integer = i.value.integer;
329 } else if (ItemAttributes::isStrAttrType(type)) {
330 value.string = new std::string(*i.value.string);
331 } else if (ItemAttributes::isCustomAttrType(type)) {
332 value.custom = new CustomAttributeMap(*i.value.custom);
333 } else {
334 memset(&value, 0, sizeof(value));
335 }
336 }
337 Attribute(Attribute &&attribute)
338 : value(attribute.value), type(attribute.type) {
339 memset(&attribute.value, 0, sizeof(value));
340 attribute.type = ITEM_ATTRIBUTE_NONE;
341 }
342 ~Attribute() {
343 if (ItemAttributes::isStrAttrType(type)) {
344 delete value.string;
345 } else if (ItemAttributes::isCustomAttrType(type)) {
346 delete value.custom;
347 }
348 }
349 Attribute &operator=(Attribute other) {
350 Attribute::swap(*this, other);
351 return *this;
352 }
353 Attribute &operator=(Attribute &&other) {
354 if (this != &other) {
355 if (ItemAttributes::isStrAttrType(type)) {
356 delete value.string;
357 } else if (ItemAttributes::isCustomAttrType(type)) {
358 delete value.custom;
359 }
360
361 value = other.value;
362 type = other.type;
363
364 memset(&other.value, 0, sizeof(value));
365 other.type = ITEM_ATTRIBUTE_NONE;
366 }
367 return *this;
368 }
369
370 static void swap(Attribute &first, Attribute &second) {
371 std::swap(first.value, second.value);
372 std::swap(first.type, second.type);
373 }
374 };
375
376 std::vector<Attribute> attributes;
377 uint32_t attributeBits = 0;
378
379 const std::string &getStrAttr(itemAttrTypes type) const;
380 void setStrAttr(itemAttrTypes type, const std::string &value);
381
382 int64_t getIntAttr(itemAttrTypes type) const;
383 void setIntAttr(itemAttrTypes type, int64_t value);
384 void increaseIntAttr(itemAttrTypes type, int64_t value);
385
386 const Attribute *getExistingAttr(itemAttrTypes type) const;
387 Attribute &getAttr(itemAttrTypes type);
388
389 CustomAttributeMap *getCustomAttributeMap() {
390 if (!hasAttribute(ITEM_ATTRIBUTE_CUSTOM)) {
391 return nullptr;
392 }
393
394 return getAttr(ITEM_ATTRIBUTE_CUSTOM).value.custom;
395 }
396
397 template <typename R> void setCustomAttribute(int64_t key, R value) {
398 std::string tmp = boost::lexical_cast<std::string>(key);
399 setCustomAttribute(tmp, value);
400 }
401
402 void setCustomAttribute(int64_t key, CustomAttribute &value) {
403 std::string tmp = boost::lexical_cast<std::string>(key);
404 setCustomAttribute(tmp, value);
405 }
406
407 template <typename R> void setCustomAttribute(std::string &key, R value) {
408 toLowerCaseString(key);
409 if (hasAttribute(ITEM_ATTRIBUTE_CUSTOM)) {
410 removeCustomAttribute(key);
411 } else {
412 getAttr(ITEM_ATTRIBUTE_CUSTOM).value.custom = new CustomAttributeMap();
413 }
414 getAttr(ITEM_ATTRIBUTE_CUSTOM).value.custom->emplace(key, value);
415 }
416
417 void setCustomAttribute(std::string &key, CustomAttribute &value) {
418 toLowerCaseString(key);
419 if (hasAttribute(ITEM_ATTRIBUTE_CUSTOM)) {
420 removeCustomAttribute(key);
421 } else {
422 getAttr(ITEM_ATTRIBUTE_CUSTOM).value.custom = new CustomAttributeMap();
423 }
424 getAttr(ITEM_ATTRIBUTE_CUSTOM)
425 .value.custom->insert(std::make_pair(std::move(key), std::move(value)));
426 }
427
428 const CustomAttribute *getCustomAttribute(int64_t key) {
429 std::string tmp = boost::lexical_cast<std::string>(key);
430 return getCustomAttribute(tmp);
431 }
432
433 const CustomAttribute *getCustomAttribute(const std::string &key) {
434 if (const CustomAttributeMap *customAttrMap = getCustomAttributeMap()) {
435 auto it = customAttrMap->find(asLowerCaseString(key));
436 if (it != customAttrMap->end()) {
437 return &(it->second);
438 }
439 }
440 return nullptr;
441 }
442
443 bool removeCustomAttribute(int64_t key) {
444 std::string tmp = boost::lexical_cast<std::string>(key);
445 return removeCustomAttribute(tmp);
446 }
447
448 bool removeCustomAttribute(const std::string &key) {
449 if (CustomAttributeMap *customAttrMap = getCustomAttributeMap()) {
450 auto it = customAttrMap->find(asLowerCaseString(key));
451 if (it != customAttrMap->end()) {
452 customAttrMap->erase(it);
453 return true;
454 }
455 }
456 return false;
457 }
458
459 const static uint32_t intAttributeTypes =
460 ITEM_ATTRIBUTE_ACTIONID | ITEM_ATTRIBUTE_UNIQUEID | ITEM_ATTRIBUTE_DATE |
461 ITEM_ATTRIBUTE_WEIGHT | ITEM_ATTRIBUTE_ATTACK | ITEM_ATTRIBUTE_DEFENSE |
462 ITEM_ATTRIBUTE_EXTRADEFENSE | ITEM_ATTRIBUTE_ARMOR |
463 ITEM_ATTRIBUTE_HITCHANCE | ITEM_ATTRIBUTE_SHOOTRANGE |
464 ITEM_ATTRIBUTE_OWNER | ITEM_ATTRIBUTE_DURATION |
465 ITEM_ATTRIBUTE_DECAYSTATE | ITEM_ATTRIBUTE_CORPSEOWNER |
466 ITEM_ATTRIBUTE_CHARGES | ITEM_ATTRIBUTE_FLUIDTYPE |
467 ITEM_ATTRIBUTE_DOORID | ITEM_ATTRIBUTE_DECAYTO | ITEM_ATTRIBUTE_WRAPID;
468 const static uint32_t stringAttributeTypes =
469 ITEM_ATTRIBUTE_DESCRIPTION | ITEM_ATTRIBUTE_TEXT | ITEM_ATTRIBUTE_WRITER |
470 ITEM_ATTRIBUTE_NAME | ITEM_ATTRIBUTE_ARTICLE | ITEM_ATTRIBUTE_PLURALNAME;
471
472public:
473 static bool isIntAttrType(itemAttrTypes type) {
474 return (type & intAttributeTypes) == type;
475 }
476 static bool isStrAttrType(itemAttrTypes type) {
477 return (type & stringAttributeTypes) == type;
478 }
479 inline static bool isCustomAttrType(itemAttrTypes type) {
480 return (type & ITEM_ATTRIBUTE_CUSTOM) == type;
481 }
482
483 const std::vector<Attribute> &getList() const { return attributes; }
484
485 friend class Item;
486};
487
488class Item : virtual public Thing {
489public:
490 // Factory member to create item of right type based on type
491 static Item *CreateItem(const uint16_t type, uint16_t count = 0);
492 static Container *CreateItemAsContainer(const uint16_t type, uint16_t size);
493 static Item *CreateItem(PropStream &propStream);
494 static Items items;
495
496 // Constructor for items
497 Item(const uint16_t type, uint16_t count = 0);
498 Item(const Item &i);
499 virtual Item *clone() const;
500
501 virtual ~Item() = default;
502
503 // non-assignable
504 Item &operator=(const Item &) = delete;
505
506 bool equals(const Item *otherItem) const;
507
508 Item *getItem() override final { return this; }
509 const Item *getItem() const override final { return this; }
510 virtual Teleport *getTeleport() { return nullptr; }
511 virtual const Teleport *getTeleport() const { return nullptr; }
512 virtual TrashHolder *getTrashHolder() { return nullptr; }
513 virtual const TrashHolder *getTrashHolder() const { return nullptr; }
514 virtual Mailbox *getMailbox() { return nullptr; }
515 virtual const Mailbox *getMailbox() const { return nullptr; }
516 virtual Door *getDoor() { return nullptr; }
517 virtual const Door *getDoor() const { return nullptr; }
518 virtual MagicField *getMagicField() { return nullptr; }
519 virtual const MagicField *getMagicField() const { return nullptr; }
520 virtual BedItem *getBed() { return nullptr; }
521 virtual const BedItem *getBed() const { return nullptr; }
522
523 const std::string &getStrAttr(itemAttrTypes type) const {
524 if (!attributes) {
525 return ItemAttributes::emptyString;
526 }
527 return attributes->getStrAttr(type);
528 }
529 void setStrAttr(itemAttrTypes type, const std::string &value) {
530 getAttributes()->setStrAttr(type, value);
531 }
532
533 int64_t getIntAttr(itemAttrTypes type) const {
534 if (!attributes) {
535 return 0;
536 }
537 return attributes->getIntAttr(type);
538 }
539 void setIntAttr(itemAttrTypes type, int64_t value) {
540 getAttributes()->setIntAttr(type, value);
541 }
542 void increaseIntAttr(itemAttrTypes type, int64_t value) {
543 getAttributes()->increaseIntAttr(type, value);
544 }
545
546 void removeAttribute(itemAttrTypes type) {
547 if (attributes) {
548 attributes->removeAttribute(type);
549 }
550 }
551 bool hasAttribute(itemAttrTypes type) const {
552 if (!attributes) {
553 return false;
554 }
555 return attributes->hasAttribute(type);
556 }
557
558 template <typename R> void setCustomAttribute(std::string &key, R value) {
559 getAttributes()->setCustomAttribute(key, value);
560 }
561
562 void setCustomAttribute(std::string &key,
563 ItemAttributes::CustomAttribute &value) {
564 getAttributes()->setCustomAttribute(key, value);
565 }
566
567 const ItemAttributes::CustomAttribute *getCustomAttribute(int64_t key) {
568 if (!attributes) {
569 return nullptr;
570 }
571 return getAttributes()->getCustomAttribute(key);
572 }
573
574 const ItemAttributes::CustomAttribute *
575 getCustomAttribute(const std::string &key) {
576 if (!attributes) {
577 return nullptr;
578 }
579 return getAttributes()->getCustomAttribute(key);
580 }
581
582 bool removeCustomAttribute(int64_t key) {
583 if (!attributes) {
584 return false;
585 }
586 return getAttributes()->removeCustomAttribute(key);
587 }
588
589 bool removeCustomAttribute(const std::string &key) {
590 if (!attributes) {
591 return false;
592 }
593 return getAttributes()->removeCustomAttribute(key);
594 }
595
596 void setSpecialDescription(const std::string &desc) {
597 setStrAttr(ITEM_ATTRIBUTE_DESCRIPTION, desc);
598 }
599 const std::string &getSpecialDescription() const {
600 return getStrAttr(ITEM_ATTRIBUTE_DESCRIPTION);
601 }
602
603 void setText(const std::string &text) {
604 setStrAttr(ITEM_ATTRIBUTE_TEXT, text);
605 }
606 void resetText() { removeAttribute(ITEM_ATTRIBUTE_TEXT); }
607 const std::string &getText() const { return getStrAttr(ITEM_ATTRIBUTE_TEXT); }
608
609 void setDate(int32_t n) { setIntAttr(ITEM_ATTRIBUTE_DATE, n); }
610 void resetDate() { removeAttribute(ITEM_ATTRIBUTE_DATE); }
611 time_t getDate() const {
612 return static_cast<time_t>(getIntAttr(ITEM_ATTRIBUTE_DATE));
613 }
614
615 void setWriter(const std::string &writer) {
616 setStrAttr(ITEM_ATTRIBUTE_WRITER, writer);
617 }
618 void resetWriter() { removeAttribute(ITEM_ATTRIBUTE_WRITER); }
619 const std::string &getWriter() const {
620 return getStrAttr(ITEM_ATTRIBUTE_WRITER);
621 }
622
623 void setActionId(uint16_t n) {
624 if (n < 100) {
625 n = 100;
626 }
627
628 setIntAttr(ITEM_ATTRIBUTE_ACTIONID, n);
629 }
630 uint16_t getActionId() const {
631 if (!attributes) {
632 return 0;
633 }
634 return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_ACTIONID));
635 }
636
637 uint16_t getUniqueId() const {
638 if (!attributes) {
639 return 0;
640 }
641 return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_UNIQUEID));
642 }
643
644 void setCharges(uint16_t n) { setIntAttr(ITEM_ATTRIBUTE_CHARGES, n); }
645 uint16_t getCharges() const {
646 if (!attributes) {
647 return 0;
648 }
649 return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_CHARGES));
650 }
651
652 void setFluidType(uint16_t n) { setIntAttr(ITEM_ATTRIBUTE_FLUIDTYPE, n); }
653 uint16_t getFluidType() const {
654 if (!attributes) {
655 return 0;
656 }
657 return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_FLUIDTYPE));
658 }
659
660 void setOwner(uint32_t owner) { setIntAttr(ITEM_ATTRIBUTE_OWNER, owner); }
661 uint32_t getOwner() const {
662 if (!attributes) {
663 return 0;
664 }
665 return getIntAttr(ITEM_ATTRIBUTE_OWNER);
666 }
667
668 void setCorpseOwner(uint32_t corpseOwner) {
669 setIntAttr(ITEM_ATTRIBUTE_CORPSEOWNER, corpseOwner);
670 }
671 uint32_t getCorpseOwner() const {
672 if (!attributes) {
673 return 0;
674 }
675 return getIntAttr(ITEM_ATTRIBUTE_CORPSEOWNER);
676 }
677
678 void setDuration(int32_t time) { setIntAttr(ITEM_ATTRIBUTE_DURATION, time); }
679 void decreaseDuration(int32_t time) {
680 increaseIntAttr(ITEM_ATTRIBUTE_DURATION, -time);
681 }
682 uint32_t getDuration() const {
683 if (!attributes) {
684 return 0;
685 }
686 return getIntAttr(ITEM_ATTRIBUTE_DURATION);
687 }
688
689 void setDecaying(ItemDecayState_t decayState) {
690 setIntAttr(ITEM_ATTRIBUTE_DECAYSTATE, decayState);
691 }
692 ItemDecayState_t getDecaying() const {
693 if (!attributes) {
694 return DECAYING_FALSE;
695 }
696 return static_cast<ItemDecayState_t>(getIntAttr(ITEM_ATTRIBUTE_DECAYSTATE));
697 }
698
699 void setDecayTo(int32_t decayTo) {
700 setIntAttr(ITEM_ATTRIBUTE_DECAYTO, decayTo);
701 }
702 int32_t getDecayTo() const {
703 if (hasAttribute(ITEM_ATTRIBUTE_DECAYTO)) {
704 return getIntAttr(ITEM_ATTRIBUTE_DECAYTO);
705 }
706 return items[id].decayTo;
707 }
708
709 static std::string getDescription(const ItemType &it, int32_t lookDistance,
710 const Item *item = nullptr,
711 int32_t subType = -1,
712 bool addArticle = true);
713 static std::string getNameDescription(const ItemType &it,
714 const Item *item = nullptr,
715 int32_t subType = -1,
716 bool addArticle = true);
717 static std::string getWeightDescription(const ItemType &it, uint32_t weight,
718 uint32_t count = 1);
719
720 std::string getDescription(int32_t lookDistance) const override final;
721 std::string getNameDescription() const;
722 std::string getWeightDescription() const;
723
724 // serialization
725 virtual Attr_ReadValue readAttr(AttrTypes_t attr, PropStream &propStream);
726 bool unserializeAttr(PropStream &propStream);
727 virtual bool unserializeItemNode(OTB::Loader &, const OTB::Node &,
728 PropStream &propStream);
729
730 virtual void serializeAttr(PropWriteStream &propWriteStream) const;
731
732 bool isPushable() const override final { return isMoveable(); }
733 int32_t getThrowRange() const override final {
734 return (isPickupable() ? 15 : 2);
735 }
736
737 uint16_t getID() const { return id; }
738 uint16_t getClientID() const { return items[id].clientId; }
739 void setID(uint16_t newid);
740
741 // Returns the player that is holding this item in his inventory
742 Player *getHoldingPlayer() const;
743
744 WeaponType_t getWeaponType() const { return items[id].weaponType; }
745 Ammo_t getAmmoType() const { return items[id].ammoType; }
746 uint8_t getShootRange() const {
747 if (hasAttribute(ITEM_ATTRIBUTE_SHOOTRANGE)) {
748 return getIntAttr(ITEM_ATTRIBUTE_SHOOTRANGE);
749 }
750 return items[id].shootRange;
751 }
752
753 virtual uint32_t getWeight() const;
754 uint32_t getBaseWeight() const {
755 if (hasAttribute(ITEM_ATTRIBUTE_WEIGHT)) {
756 return getIntAttr(ITEM_ATTRIBUTE_WEIGHT);
757 }
758 return items[id].weight;
759 }
760 int32_t getAttack() const {
761 if (hasAttribute(ITEM_ATTRIBUTE_ATTACK)) {
762 return getIntAttr(ITEM_ATTRIBUTE_ATTACK);
763 }
764 return items[id].attack;
765 }
766 int32_t getArmor() const {
767 if (hasAttribute(ITEM_ATTRIBUTE_ARMOR)) {
768 return getIntAttr(ITEM_ATTRIBUTE_ARMOR);
769 }
770 return items[id].armor;
771 }
772 int32_t getDefense() const {
773 if (hasAttribute(ITEM_ATTRIBUTE_DEFENSE)) {
774 return getIntAttr(ITEM_ATTRIBUTE_DEFENSE);
775 }
776 return items[id].defense;
777 }
778 int32_t getExtraDefense() const {
779 if (hasAttribute(ITEM_ATTRIBUTE_EXTRADEFENSE)) {
780 return getIntAttr(ITEM_ATTRIBUTE_EXTRADEFENSE);
781 }
782 return items[id].extraDefense;
783 }
784 int32_t getSlotPosition() const { return items[id].slotPosition; }
785 int8_t getHitChance() const {
786 if (hasAttribute(ITEM_ATTRIBUTE_HITCHANCE)) {
787 return getIntAttr(ITEM_ATTRIBUTE_HITCHANCE);
788 }
789 return items[id].hitChance;
790 }
791
792 uint32_t getWorth() const;
793 LightInfo getLightInfo() const;
794
795 bool hasProperty(ITEMPROPERTY prop) const;
796 bool isBlocking() const { return items[id].blockSolid; }
797 bool isStackable() const { return items[id].stackable; }
798 bool isAlwaysOnTop() const { return items[id].alwaysOnTop; }
799 bool isGroundTile() const { return items[id].isGroundTile(); }
800 bool isMagicField() const { return items[id].isMagicField(); }
801 bool isMoveable() const { return items[id].moveable; }
802 bool isPickupable() const { return items[id].pickupable; }
803 bool isUseable() const { return items[id].useable; }
804 bool isHangable() const { return items[id].isHangable; }
805 bool isRotatable() const {
806 const ItemType &it = items[id];
807 return it.rotatable && it.rotateTo;
808 }
809 bool hasWalkStack() const { return items[id].walkStack; }
810
811 const std::string &getName() const {
812 if (hasAttribute(ITEM_ATTRIBUTE_NAME)) {
813 return getStrAttr(ITEM_ATTRIBUTE_NAME);
814 }
815 return items[id].name;
816 }
817 const std::string getPluralName() const {
818 if (hasAttribute(ITEM_ATTRIBUTE_PLURALNAME)) {
819 return getStrAttr(ITEM_ATTRIBUTE_PLURALNAME);
820 }
821 return items[id].getPluralName();
822 }
823 const std::string &getArticle() const {
824 if (hasAttribute(ITEM_ATTRIBUTE_ARTICLE)) {
825 return getStrAttr(ITEM_ATTRIBUTE_ARTICLE);
826 }
827 return items[id].article;
828 }
829
830 // get the number of items
831 uint16_t getItemCount() const { return count; }
832 void setItemCount(uint8_t n) { count = n; }
833
834 static uint32_t countByType(const Item *i, int32_t subType) {
835 if (subType == -1 || subType == i->getSubType()) {
836 return i->getItemCount();
837 }
838
839 return 0;
840 }
841
842 void setDefaultSubtype();
843 uint16_t getSubType() const;
844 void setSubType(uint16_t n);
845
846 void setUniqueId(uint16_t n);
847
848 void setDefaultDuration() {
849 uint32_t duration = getDefaultDuration();
850 if (duration != 0) {
851 setDuration(duration);
852 }
853 }
854 uint32_t getDefaultDuration() const { return items[id].decayTime * 1000; }
855 bool canDecay() const;
856
857 virtual bool canRemove() const { return true; }
858 virtual bool canTransform() const { return true; }
859 virtual void onRemoved();
860 virtual void onTradeEvent(TradeEvents_t, Player *) {}
861
862 virtual void startDecaying();
863
864 bool isLoadedFromMap() const { return loadedFromMap; }
865 void setLoadedFromMap(bool value) { loadedFromMap = value; }
866 bool isCleanable() const {
867 return !loadedFromMap && canRemove() && isPickupable() &&
868 !hasAttribute(ITEM_ATTRIBUTE_UNIQUEID) &&
869 !hasAttribute(ITEM_ATTRIBUTE_ACTIONID);
870 }
871
872 bool hasMarketAttributes() const;
873
874 std::unique_ptr<ItemAttributes> &getAttributes() {
875 if (!attributes) {
876 attributes.reset(new ItemAttributes());
877 }
878 return attributes;
879 }
880
881 void incrementReferenceCounter() { ++referenceCounter; }
882 void decrementReferenceCounter() {
883 if (--referenceCounter == 0) {
884 delete this;
885 }
886 }
887
888 Cylinder *getParent() const override { return parent; }
889 void setParent(Cylinder *cylinder) override { parent = cylinder; }
890 Cylinder *getTopParent();
891 const Cylinder *getTopParent() const;
892 Tile *getTile() override;
893 const Tile *getTile() const override;
894 bool isRemoved() const override { return !parent || parent->isRemoved(); }
895
896protected:
897 Cylinder *parent = nullptr;
898
899 uint16_t id; // the same id as in ItemType
900
901private:
902 std::string getWeightDescription(uint32_t weight) const;
903
904 std::unique_ptr<ItemAttributes> attributes;
905
906 uint32_t referenceCounter = 0;
907
908 uint8_t count = 1; // number of stacked items
909
910 bool loadedFromMap = false;
911
912 // Don't add variables here, use the ItemAttribute class.
913};
914
915using ItemList = std::list<Item *>;
916using ItemDeque = std::deque<Item *>;
917
918#endif
919