/** * 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_TILE_H_96C7EE7CF8CD48E59D5D554A181F0C56 #define FS_TILE_H_96C7EE7CF8CD48E59D5D554A181F0C56 #include #include "cylinder.h" #include "item.h" #include "tools.h" class Creature; class Teleport; class Mailbox; class DepotLocker; class MagicField; class QTreeLeafNode; class BedItem; typedef std::vector CreatureVector; typedef std::vector ItemVector; typedef std::unordered_set SpectatorVec; enum tileflags_t : uint32_t { TILESTATE_NONE = 0, TILESTATE_PROTECTIONZONE = 1 << 0, TILESTATE_NOPVPZONE = 1 << 1, TILESTATE_NOLOGOUT = 1 << 2, TILESTATE_PVPZONE = 1 << 3, TILESTATE_REFRESH = 1 << 4, TILESTATE_TELEPORT = 1 << 5, TILESTATE_MAGICFIELD = 1 << 6, TILESTATE_MAILBOX = 1 << 7, TILESTATE_BED = 1 << 8, TILESTATE_DEPOT = 1 << 9, TILESTATE_BLOCKSOLID = 1 << 10, TILESTATE_BLOCKPATH = 1 << 11, TILESTATE_IMMOVABLEBLOCKSOLID = 1 << 12, TILESTATE_IMMOVABLEBLOCKPATH = 1 << 13, TILESTATE_IMMOVABLENOFIELDBLOCKPATH = 1 << 14, TILESTATE_NOFIELDBLOCKPATH = 1 << 15, TILESTATE_SUPPORTS_HANGABLE = 1 << 16, TILESTATE_FIREDAMAGE = 1 << 17, TILESTATE_POISONDAMAGE = 1 << 18, TILESTATE_ENERGYDAMAGE = 1 << 19, }; enum ZoneType_t { ZONE_PROTECTION, ZONE_NOPVP, ZONE_PVP, ZONE_NOLOGOUT, ZONE_NORMAL, }; class TileItemVector : private ItemVector { public: using ItemVector::begin; using ItemVector::end; using ItemVector::rbegin; using ItemVector::rend; using ItemVector::size; using ItemVector::clear; using ItemVector::at; using ItemVector::insert; using ItemVector::erase; using ItemVector::push_back; using ItemVector::value_type; using ItemVector::iterator; using ItemVector::const_iterator; using ItemVector::reverse_iterator; using ItemVector::const_reverse_iterator; iterator getBeginDownItem() { return begin(); } const_iterator getBeginDownItem() const { return begin(); } iterator getEndDownItem() { return begin() + downItemCount; } const_iterator getEndDownItem() const { return begin() + downItemCount; } iterator getBeginTopItem() { return getEndDownItem(); } const_iterator getBeginTopItem() const { return getEndDownItem(); } iterator getEndTopItem() { return end(); } const_iterator getEndTopItem() const { return end(); } uint32_t getTopItemCount() const { return size() - downItemCount; } uint32_t getDownItemCount() const { return downItemCount; } inline Item* getTopTopItem() const { if (getTopItemCount() == 0) { return nullptr; } return *(getEndTopItem() - 1); } inline Item* getTopDownItem() const { if (downItemCount == 0) { return nullptr; } return *getBeginDownItem(); } void addDownItemCount(int32_t increment) { downItemCount += increment; } private: uint16_t downItemCount = 0; }; class Tile : public Cylinder { public: static Tile& nullptr_tile; Tile(uint16_t x, uint16_t y, uint8_t z) : tilePos(x, y, z) {} virtual ~Tile(); // non-copyable Tile(const Tile&) = delete; Tile& operator=(const Tile&) = delete; virtual TileItemVector* getItemList() = 0; virtual const TileItemVector* getItemList() const = 0; virtual TileItemVector* makeItemList() = 0; virtual CreatureVector* getCreatures() = 0; virtual const CreatureVector* getCreatures() const = 0; virtual CreatureVector* makeCreatures() = 0; int32_t getThrowRange() const final { return 0; } bool isPushable() const final { return false; } MagicField* getFieldItem() const; Teleport* getTeleportItem() const; Mailbox* getMailbox() const; DepotLocker* getDepotLocker() const; BedItem* getBedItem() const; Creature* getTopCreature() const; const Creature* getBottomCreature() const; Creature* getTopVisibleCreature(const Creature* creature) const; const Creature* getBottomVisibleCreature(const Creature* creature) const; Item* getTopTopItem() const; Item* getTopDownItem() const; bool isMoveableBlocking() const; Thing* getTopVisibleThing(const Creature* creature); Item* getItemByTopOrder(int32_t topOrder); size_t getThingCount() const { size_t thingCount = getCreatureCount() + getItemCount(); if (ground) { thingCount++; } return thingCount; } // If these return != 0 the associated vectors are guaranteed to exists size_t getCreatureCount() const; size_t getItemCount() const; uint32_t getTopItemCount() const; uint32_t getDownItemCount() const; bool hasProperty(ITEMPROPERTY prop) const; bool hasProperty(const Item* exclude, ITEMPROPERTY prop) const; inline bool hasFlag(uint32_t flag) const { return hasBitSet(flag, this->flags); } inline void setFlag(uint32_t flag) { this->flags |= flag; } inline void resetFlag(uint32_t flag) { this->flags &= ~flag; } ZoneType_t getZone() const { if (hasFlag(TILESTATE_PROTECTIONZONE)) { return ZONE_PROTECTION; } else if (hasFlag(TILESTATE_NOPVPZONE)) { return ZONE_NOPVP; } else if (hasFlag(TILESTATE_PVPZONE)) { return ZONE_PVP; } else { return ZONE_NORMAL; } } bool hasHeight(uint32_t n) const; int32_t getHeight(); std::string getDescription(int32_t lookDistance) const final; int32_t getClientIndexOfCreature(const Player* player, const Creature* creature) const; int32_t getStackposOfCreature(const Player* player, const Creature* creature) const; int32_t getStackposOfItem(const Player* player, const Item* item) const; //cylinder implementations ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count, uint32_t flags, Creature* actor = nullptr) const override; ReturnValue queryMaxCount(int32_t index, const Thing& thing, uint32_t count, uint32_t& maxQueryCount, uint32_t flags) const final; ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const final; Tile* queryDestination(int32_t& index, const Thing& thing, Item** destItem, uint32_t& flags) override; void addThing(Thing* thing) final; void addThing(int32_t index, Thing* thing) override; void updateThing(Thing* thing, uint16_t itemId, uint32_t count) final; void replaceThing(uint32_t index, Thing* thing) final; void removeThing(Thing* thing, uint32_t count) final; void removeCreature(Creature* creature); int32_t getThingIndex(const Thing* thing) const final; size_t getFirstIndex() const final; size_t getLastIndex() const final; uint32_t getItemTypeCount(uint16_t itemId, int32_t subType = -1) const final; Thing* getThing(size_t index) const final; void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) final; void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) final; void internalAddThing(Thing* thing) final; void internalAddThing(uint32_t index, Thing* thing) override; const Position& getPosition() const final { return tilePos; } bool isRemoved() const final { return false; } Item* getUseItem(int32_t index) const; Item* getGround() const { return ground; } void setGround(Item* item) { ground = item; } private: void onAddTileItem(Item* item); void onUpdateTileItem(Item* oldItem, const ItemType& oldType, Item* newItem, const ItemType& newType); void onRemoveTileItem(const SpectatorVec& list, const std::vector& oldStackPosVector, Item* item); void onUpdateTile(const SpectatorVec& list); void setTileFlags(const Item* item); void resetTileFlags(const Item* item); protected: Item* ground = nullptr; Position tilePos; uint32_t flags = 0; }; // Used for walkable tiles, where there is high likeliness of // items being added/removed class DynamicTile : public Tile { // By allocating the vectors in-house, we avoid some memory fragmentation TileItemVector items; CreatureVector creatures; public: DynamicTile(uint16_t x, uint16_t y, uint8_t z) : Tile(x, y, z) {} ~DynamicTile(); // non-copyable DynamicTile(const DynamicTile&) = delete; DynamicTile& operator=(const DynamicTile&) = delete; TileItemVector* getItemList() final { return &items; } const TileItemVector* getItemList() const final { return &items; } TileItemVector* makeItemList() final { return &items; } CreatureVector* getCreatures() final { return &creatures; } const CreatureVector* getCreatures() const final { return &creatures; } CreatureVector* makeCreatures() final { return &creatures; } }; // For blocking tiles, where we very rarely actually have items class StaticTile final : public Tile { // We very rarely even need the vectors, so don't keep them in memory std::unique_ptr items; std::unique_ptr creatures; public: StaticTile(uint16_t x, uint16_t y, uint8_t z) : Tile(x, y, z) {} ~StaticTile(); // non-copyable StaticTile(const StaticTile&) = delete; StaticTile& operator=(const StaticTile&) = delete; TileItemVector* getItemList() final { return items.get(); } const TileItemVector* getItemList() const final { return items.get(); } TileItemVector* makeItemList() final { if (!items) { items.reset(new TileItemVector); } return items.get(); } CreatureVector* getCreatures() final { return creatures.get(); } const CreatureVector* getCreatures() const final { return creatures.get(); } CreatureVector* makeCreatures() final { if (!creatures) { creatures.reset(new CreatureVector); } return creatures.get(); } }; inline Tile::~Tile() { delete ground; } inline StaticTile::~StaticTile() { if (items) { for (Item* item : *items) { item->decrementReferenceCounter(); } } } inline DynamicTile::~DynamicTile() { for (Item* item : items) { item->decrementReferenceCounter(); } } #endif