/** * Tibia GIMUD Server - a free and open-source MMORPG server emulator * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef FS_ITEM_H_009A319FB13D477D9EEFFBBD9BB83562 #define FS_ITEM_H_009A319FB13D477D9EEFFBBD9BB83562 #include "cylinder.h" #include "thing.h" #include "items.h" #include class Creature; class Player; class Container; class Depot; class Teleport; class Mailbox; class DepotLocker; class Door; class MagicField; class BedItem; enum ITEMPROPERTY { CONST_PROP_BLOCKSOLID = 0, CONST_PROP_HASHEIGHT, CONST_PROP_BLOCKPROJECTILE, CONST_PROP_BLOCKPATH, CONST_PROP_ISVERTICAL, CONST_PROP_ISHORIZONTAL, CONST_PROP_MOVEABLE, CONST_PROP_IMMOVABLEBLOCKSOLID, CONST_PROP_IMMOVABLEBLOCKPATH, CONST_PROP_IMMOVABLENOFIELDBLOCKPATH, CONST_PROP_NOFIELDBLOCKPATH, CONST_PROP_SUPPORTHANGABLE, CONST_PROP_UNLAY, }; enum TradeEvents_t { ON_TRADE_TRANSFER, ON_TRADE_CANCEL, }; enum ItemDecayState_t : uint8_t { DECAYING_FALSE = 0, DECAYING_TRUE, DECAYING_PENDING, }; enum AttrTypes_t { //ATTR_DESCRIPTION = 1, //ATTR_EXT_FILE = 2, ATTR_TILE_FLAGS = 3, ATTR_ACTION_ID = 4, ATTR_MOVEMENT_ID = 5, ATTR_TEXT = 6, ATTR_DESC = 7, ATTR_TELE_DEST = 8, ATTR_ITEM = 9, ATTR_DEPOT_ID = 10, //ATTR_EXT_SPAWN_FILE = 11, ATTR_RUNE_CHARGES = 12, //ATTR_EXT_HOUSE_FILE = 13, ATTR_HOUSEDOORID = 14, ATTR_COUNT = 15, ATTR_DURATION = 16, ATTR_DECAYING_STATE = 17, ATTR_WRITTENDATE = 18, ATTR_WRITTENBY = 19, ATTR_SLEEPERGUID = 20, ATTR_SLEEPSTART = 21, ATTR_CHARGES = 22, ATTR_KEYNUMBER = 23, ATTR_KEYHOLENUMBER = 24, ATTR_DOORQUESTNUMBER = 25, ATTR_DOORQUESTVALUE = 26, ATTR_DOORLEVEL = 27, ATTR_CHESTQUESTNUMBER = 28, // add non-OTBM attributes after here ATTR_CONTAINER_ITEMS = 29, ATTR_NAME = 30, ATTR_ARTICLE = 31, ATTR_PLURALNAME = 32, ATTR_WEIGHT = 33, ATTR_ATTACK = 34, ATTR_DEFENSE = 35, ATTR_ARMOR = 36, ATTR_SHOOTRANGE = 37, }; enum Attr_ReadValue { ATTR_READ_CONTINUE, ATTR_READ_ERROR, ATTR_READ_END, }; class ItemAttributes { public: ItemAttributes() = default; void setSpecialDescription(const std::string& desc) { setStrAttr(ITEM_ATTRIBUTE_DESCRIPTION, desc); } const std::string& getSpecialDescription() const { return getStrAttr(ITEM_ATTRIBUTE_DESCRIPTION); } void setText(const std::string& text) { setStrAttr(ITEM_ATTRIBUTE_TEXT, text); } void resetText() { removeAttribute(ITEM_ATTRIBUTE_TEXT); } const std::string& getText() const { return getStrAttr(ITEM_ATTRIBUTE_TEXT); } void setDate(int32_t n) { setIntAttr(ITEM_ATTRIBUTE_DATE, n); } void resetDate() { removeAttribute(ITEM_ATTRIBUTE_DATE); } time_t getDate() const { return static_cast(getIntAttr(ITEM_ATTRIBUTE_DATE)); } void setWriter(const std::string& writer) { setStrAttr(ITEM_ATTRIBUTE_WRITER, writer); } void resetWriter() { removeAttribute(ITEM_ATTRIBUTE_WRITER); } const std::string& getWriter() const { return getStrAttr(ITEM_ATTRIBUTE_WRITER); } void setActionId(uint16_t n) { setIntAttr(ITEM_ATTRIBUTE_ACTIONID, n); } uint16_t getActionId() const { return static_cast(getIntAttr(ITEM_ATTRIBUTE_ACTIONID)); } void setMovementID(uint16_t n) { setIntAttr(ITEM_ATTRIBUTE_MOVEMENTID, n); } uint16_t getMovementId() const { return static_cast(getIntAttr(ITEM_ATTRIBUTE_MOVEMENTID)); } void setKeyNumber(uint16_t n) { setIntAttr(ITEM_ATTRIBUTE_KEYNUMBER, n); } uint16_t getKeyNumber() const { return static_cast(getIntAttr(ITEM_ATTRIBUTE_KEYNUMBER)); } void setKeyHoleNumber(uint16_t n) { setIntAttr(ITEM_ATTRIBUTE_KEYHOLENUMBER, n); } uint16_t getKeyHoleNumber() const { return static_cast(getIntAttr(ITEM_ATTRIBUTE_KEYHOLENUMBER)); } void setDoorQuestNumber(uint16_t n) { setIntAttr(ITEM_ATTRIBUTE_DOORQUESTNUMBER, n); } uint16_t getDoorQuestNumber() const { return static_cast(getIntAttr(ITEM_ATTRIBUTE_DOORQUESTNUMBER)); } void setDoorQuestValue(uint16_t n) { setIntAttr(ITEM_ATTRIBUTE_DOORQUESTVALUE, n); } uint16_t getDoorQuestValue() const { return static_cast(getIntAttr(ITEM_ATTRIBUTE_DOORQUESTVALUE)); } void setDoorLevel(uint16_t n) { setIntAttr(ITEM_ATTRIBUTE_DOORLEVEL, n); } uint16_t getDoorLevel() const { return static_cast(getIntAttr(ITEM_ATTRIBUTE_DOORLEVEL)); } void setChestQuestNumber(uint16_t n) { setIntAttr(ITEM_ATTRIBUTE_CHESTQUESTNUMBER, n); } uint16_t getChestQuestNumber() const { return static_cast(getIntAttr(ITEM_ATTRIBUTE_CHESTQUESTNUMBER)); } void setCharges(uint16_t n) { setIntAttr(ITEM_ATTRIBUTE_CHARGES, n); } uint16_t getCharges() const { return static_cast(getIntAttr(ITEM_ATTRIBUTE_CHARGES)); } void setFluidType(uint16_t n) { setIntAttr(ITEM_ATTRIBUTE_FLUIDTYPE, n); } uint16_t getFluidType() const { return static_cast(getIntAttr(ITEM_ATTRIBUTE_FLUIDTYPE)); } void setOwner(uint32_t owner) { setIntAttr(ITEM_ATTRIBUTE_OWNER, owner); } uint32_t getOwner() const { return getIntAttr(ITEM_ATTRIBUTE_OWNER); } void setDuration(int32_t time) { setIntAttr(ITEM_ATTRIBUTE_DURATION, time); } void decreaseDuration(int32_t time) { increaseIntAttr(ITEM_ATTRIBUTE_DURATION, -time); } uint32_t getDuration() const { return getIntAttr(ITEM_ATTRIBUTE_DURATION); } void setDecaying(ItemDecayState_t decayState) { setIntAttr(ITEM_ATTRIBUTE_DECAYSTATE, decayState); } ItemDecayState_t getDecaying() const { return static_cast(getIntAttr(ITEM_ATTRIBUTE_DECAYSTATE)); } protected: inline bool hasAttribute(itemAttrTypes type) const { return (type & attributeBits) != 0; } void removeAttribute(itemAttrTypes type); static std::string emptyString; struct Attribute { union { int64_t integer; std::string* string; } value; itemAttrTypes type; explicit Attribute(itemAttrTypes type) : type(type) { memset(&value, 0, sizeof(value)); } Attribute(const Attribute& i) { type = i.type; if (ItemAttributes::isIntAttrType(type)) { value.integer = i.value.integer; } else if (ItemAttributes::isStrAttrType(type)) { value.string = new std::string(*i.value.string); } else { memset(&value, 0, sizeof(value)); } } Attribute(Attribute&& attribute) : value(attribute.value), type(attribute.type) { memset(&attribute.value, 0, sizeof(value)); attribute.type = ITEM_ATTRIBUTE_NONE; } ~Attribute() { if (ItemAttributes::isStrAttrType(type)) { delete value.string; } } Attribute& operator=(Attribute other) { Attribute::swap(*this, other); return *this; } Attribute& operator=(Attribute&& other) { if (this != &other) { if (ItemAttributes::isStrAttrType(type)) { delete value.string; } value = other.value; type = other.type; memset(&other.value, 0, sizeof(value)); other.type = ITEM_ATTRIBUTE_NONE; } return *this; } static void swap(Attribute& first, Attribute& second) { std::swap(first.value, second.value); std::swap(first.type, second.type); } }; std::forward_list attributes; uint32_t attributeBits = 0; const std::string& getStrAttr(itemAttrTypes type) const; void setStrAttr(itemAttrTypes type, const std::string& value); int64_t getIntAttr(itemAttrTypes type) const; void setIntAttr(itemAttrTypes type, int64_t value); void increaseIntAttr(itemAttrTypes type, int64_t value); const Attribute* getExistingAttr(itemAttrTypes type) const; Attribute& getAttr(itemAttrTypes type); public: inline static bool isIntAttrType(itemAttrTypes type) { return (type & 0xFFFFE13) != 0; } inline static bool isStrAttrType(itemAttrTypes type) { return (type & 0x1EC) != 0; } const std::forward_list& getList() const { return attributes; } friend class Item; }; class Item : virtual public Thing { public: //Factory member to create item of right type based on type static Item* CreateItem(const uint16_t type, uint16_t count = 0); static Container* CreateItemAsContainer(const uint16_t type, uint16_t size); static Item* CreateItem(PropStream& propStream); static Items items; // Constructor for items Item(const uint16_t type, uint16_t count = 0); Item(const Item& i); virtual Item* clone() const; virtual ~Item() = default; // non-assignable Item& operator=(const Item&) = delete; bool equals(const Item* otherItem) const; Item* getItem() final { return this; } const Item* getItem() const final { return this; } virtual Teleport* getTeleport() { return nullptr; } virtual const Teleport* getTeleport() const { return nullptr; } virtual Mailbox* getMailbox() { return nullptr; } virtual const Mailbox* getMailbox() const { return nullptr; } virtual DepotLocker* getDepotLocker() { return nullptr; } virtual const DepotLocker* getDepotLocker() const { return nullptr; } virtual Door* getDoor() { return nullptr; } virtual const Door* getDoor() const { return nullptr; } virtual MagicField* getMagicField() { return nullptr; } virtual const MagicField* getMagicField() const { return nullptr; } virtual BedItem* getBed() { return nullptr; } virtual const BedItem* getBed() const { return nullptr; } const std::string& getStrAttr(itemAttrTypes type) const { if (!attributes) { return ItemAttributes::emptyString; } return attributes->getStrAttr(type); } void setStrAttr(itemAttrTypes type, const std::string& value) { getAttributes()->setStrAttr(type, value); } int32_t getIntAttr(itemAttrTypes type) const { if (!attributes) { return 0; } return attributes->getIntAttr(type); } void setIntAttr(itemAttrTypes type, int32_t value) { getAttributes()->setIntAttr(type, value); } void increaseIntAttr(itemAttrTypes type, int32_t value) { getAttributes()->increaseIntAttr(type, value); } void removeAttribute(itemAttrTypes type) { if (attributes) { attributes->removeAttribute(type); } } bool hasAttribute(itemAttrTypes type) const { if (!attributes) { return false; } return attributes->hasAttribute(type); } void setSpecialDescription(const std::string& desc) { setStrAttr(ITEM_ATTRIBUTE_DESCRIPTION, desc); } const std::string& getSpecialDescription() const { return getStrAttr(ITEM_ATTRIBUTE_DESCRIPTION); } void setText(const std::string& text) { setStrAttr(ITEM_ATTRIBUTE_TEXT, text); } void resetText() { removeAttribute(ITEM_ATTRIBUTE_TEXT); } const std::string& getText() const { return getStrAttr(ITEM_ATTRIBUTE_TEXT); } void setDate(int32_t n) { setIntAttr(ITEM_ATTRIBUTE_DATE, n); } void resetDate() { removeAttribute(ITEM_ATTRIBUTE_DATE); } time_t getDate() const { return static_cast(getIntAttr(ITEM_ATTRIBUTE_DATE)); } void setWriter(const std::string& writer) { setStrAttr(ITEM_ATTRIBUTE_WRITER, writer); } void resetWriter() { removeAttribute(ITEM_ATTRIBUTE_WRITER); } const std::string& getWriter() const { return getStrAttr(ITEM_ATTRIBUTE_WRITER); } void setActionId(uint16_t n) { setIntAttr(ITEM_ATTRIBUTE_ACTIONID, n); } uint16_t getActionId() const { if (!attributes) { return 0; } return static_cast(getIntAttr(ITEM_ATTRIBUTE_ACTIONID)); } void setMovementID(uint16_t n) { setIntAttr(ITEM_ATTRIBUTE_MOVEMENTID, n); } uint16_t getMovementId() const { if (!attributes) { return 0; } return static_cast(getIntAttr(ITEM_ATTRIBUTE_MOVEMENTID)); } void setCharges(uint16_t n) { setIntAttr(ITEM_ATTRIBUTE_CHARGES, n); } uint16_t getCharges() const { if (!attributes) { return 0; } return static_cast(getIntAttr(ITEM_ATTRIBUTE_CHARGES)); } void setFluidType(uint16_t n) { setIntAttr(ITEM_ATTRIBUTE_FLUIDTYPE, n); } uint16_t getFluidType() const { if (!attributes) { return 0; } return static_cast(getIntAttr(ITEM_ATTRIBUTE_FLUIDTYPE)); } void setOwner(uint32_t owner) { setIntAttr(ITEM_ATTRIBUTE_OWNER, owner); } uint32_t getOwner() const { if (!attributes) { return 0; } return getIntAttr(ITEM_ATTRIBUTE_OWNER); } void setCorpseOwner(uint32_t corpseOwner) { setIntAttr(ITEM_ATTRIBUTE_CORPSEOWNER, corpseOwner); } uint32_t getCorpseOwner() const { if (!attributes) { return 0; } return getIntAttr(ITEM_ATTRIBUTE_CORPSEOWNER); } void setDuration(int32_t time) { setIntAttr(ITEM_ATTRIBUTE_DURATION, time); } void decreaseDuration(int32_t time) { increaseIntAttr(ITEM_ATTRIBUTE_DURATION, -time); } uint32_t getDuration() const { if (!attributes) { return 0; } return getIntAttr(ITEM_ATTRIBUTE_DURATION); } void setDecaying(ItemDecayState_t decayState) { setIntAttr(ITEM_ATTRIBUTE_DECAYSTATE, decayState); } ItemDecayState_t getDecaying() const { if (!attributes) { return DECAYING_FALSE; } return static_cast(getIntAttr(ITEM_ATTRIBUTE_DECAYSTATE)); } static std::string getDescription(const ItemType& it, int32_t lookDistance, const Item* item = nullptr, int32_t subType = -1, bool addArticle = true); static std::string getNameDescription(const ItemType& it, const Item* item = nullptr, int32_t subType = -1, bool addArticle = true); static std::string getWeightDescription(const ItemType& it, uint32_t weight, uint32_t count = 1); std::string getDescription(int32_t lookDistance) const final; std::string getNameDescription() const; std::string getWeightDescription() const; //serialization virtual Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream); bool unserializeAttr(PropStream& propStream); virtual bool unserializeItemNode(FileLoader& f, NODE node, PropStream& propStream); virtual void serializeAttr(PropWriteStream& propWriteStream) const; bool isPushable() const final { return isMoveable(); } int32_t getThrowRange() const final { return (isPickupable() ? 15 : 2); } uint16_t getID() const { return id; } void setID(uint16_t newid); // Returns the player that is holding this item in his inventory Player* getHoldingPlayer() const; CombatType_t getDamageType() const { return items[id].damageType; } CombatType_t getCombatType() const { return items[id].combatType; } WeaponType_t getWeaponType() const { return items[id].weaponType; } Ammo_t getAmmoType() const { return items[id].ammoType; } uint8_t getShootRange() const { if (hasAttribute(ITEM_ATTRIBUTE_SHOOTRANGE)) { return getIntAttr(ITEM_ATTRIBUTE_SHOOTRANGE); } return items[id].shootRange; } uint8_t getMissileType() const { return items[id].shootType; } uint8_t getFragility() const { return items[id].fragility; } int32_t getAttackStrength() const { return items[id].attackStrength; } int32_t getAttackVariation() const { return items[id].attackVariation; } int32_t getManaConsumption() const { return items[id].manaConsumption; } uint32_t getMinimumLevel() const { return items[id].minReqLevel; } int32_t getWeaponSpecialEffect() const { return items[id].weaponSpecialEffect; } virtual uint32_t getWeight() const; uint32_t getBaseWeight() const { if (hasAttribute(ITEM_ATTRIBUTE_WEIGHT)) { return getIntAttr(ITEM_ATTRIBUTE_WEIGHT); } return items[id].weight; } int32_t getAttack() const { if (hasAttribute(ITEM_ATTRIBUTE_ATTACK)) { return getIntAttr(ITEM_ATTRIBUTE_ATTACK); } return items[id].attack; } int32_t getArmor() const { if (hasAttribute(ITEM_ATTRIBUTE_ARMOR)) { return getIntAttr(ITEM_ATTRIBUTE_ARMOR); } return items[id].armor; } int32_t getDefense() const { if (hasAttribute(ITEM_ATTRIBUTE_DEFENSE)) { return getIntAttr(ITEM_ATTRIBUTE_DEFENSE); } return items[id].defense; } int32_t getSlotPosition() const { return items[id].slotPosition; } uint16_t getDisguiseId() const { return items[id].disguiseId; } uint32_t getWorth() const; void getLight(LightInfo& lightInfo) const; bool hasProperty(ITEMPROPERTY prop) const; bool isBlocking() const { return items[id].blockSolid; } bool isStackable() const { return items[id].stackable; } bool isAlwaysOnTop() const { return items[id].alwaysOnTop; } bool isGroundTile() const { return items[id].isGroundTile(); } bool isMagicField() const { return items[id].isMagicField(); } bool isSplash() const { return items[id].isSplash(); } bool isMoveable() const { return items[id].moveable; } bool isPickupable() const { return items[id].pickupable; } bool isHangable() const { return items[id].isHangable; } bool isRotatable() const { const ItemType& it = items[id]; return it.rotatable && it.rotateTo; } bool isDisguised() const { return items[id].disguise; } bool isChangeUse() const { return items[id].changeUse; } bool isChestQuest() const { return items[id].isChest(); } bool hasCollisionEvent() const { return items[id].collisionEvent; } bool hasSeparationEvent() const { return items[id].separationEvent; } bool hasUseEvent() const { return items[id].useEvent; } bool hasMultiUseEvent() const { return items[id].multiUseEvent; } bool canDistUse() const { return items[id].distUse; } bool isRune() const { return items[id].isRune(); } const std::string& getName() const { if (hasAttribute(ITEM_ATTRIBUTE_NAME)) { return getStrAttr(ITEM_ATTRIBUTE_NAME); } return items[id].name; } const std::string getPluralName() const { if (hasAttribute(ITEM_ATTRIBUTE_PLURALNAME)) { return getStrAttr(ITEM_ATTRIBUTE_PLURALNAME); } return items[id].getPluralName(); } const std::string& getArticle() const { if (hasAttribute(ITEM_ATTRIBUTE_ARTICLE)) { return getStrAttr(ITEM_ATTRIBUTE_ARTICLE); } return items[id].article; } // get the number of items uint16_t getItemCount() const { return count; } void setItemCount(uint8_t n) { count = n; } static uint32_t countByType(const Item* i, int32_t subType); void setDefaultSubtype(); uint16_t getSubType() const; void setSubType(uint16_t n); void setDefaultDuration() { uint32_t duration = getDefaultDuration(); if (duration != 0) { setDuration(duration); } } uint32_t getDefaultDuration() const { return items[id].decayTime * 1000; } bool canDecay() const; virtual bool canRemove() const { return true; } virtual bool canTransform() const { return true; } virtual void onRemoved(); virtual void onTradeEvent(TradeEvents_t, Player*) {} virtual void startDecaying(); void setLoadedFromMap(bool value) { loadedFromMap = value; } bool isCleanable() const { return !loadedFromMap && canRemove() && isPickupable() && !hasAttribute(ITEM_ATTRIBUTE_ACTIONID); } std::unique_ptr& getAttributes() { if (!attributes) { attributes.reset(new ItemAttributes()); } return attributes; } void incrementReferenceCounter() { ++referenceCounter; } void decrementReferenceCounter() { if (--referenceCounter == 0) { delete this; } } Cylinder* getParent() const { return parent; } void setParent(Cylinder* cylinder) { parent = cylinder; } Cylinder* getTopParent(); const Cylinder* getTopParent() const; Tile* getTile(); const Tile* getTile() const; bool isRemoved() const { return !parent || parent->isRemoved(); } protected: std::string getWeightDescription(uint32_t weight) const; Cylinder* parent = nullptr; std::unique_ptr attributes; uint32_t referenceCounter = 0; uint16_t id; // the same id as in ItemType uint8_t count = 1; // number of stacked items bool loadedFromMap = false; //Don't add variables here, use the ItemAttribute class. }; typedef std::list ItemList; typedef std::deque ItemDeque; inline uint32_t Item::countByType(const Item* i, int32_t subType) { if (subType == -1 || subType == i->getSubType()) { return i->getItemCount(); } return 0; } #endif