398 lines
11 KiB
C++

/**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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 <unordered_set>
#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<Creature*> CreatureVector;
typedef std::vector<Item*> ItemVector;
typedef std::unordered_set<Creature*> 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;
Creature* getBottomCreatureUH() const;
const Creature* getBottomCreature() const;
Creature* getTopVisibleCreature(const Creature* creature) const;
Creature* getBottomVisibleCreatureUH(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<int32_t>& 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<TileItemVector> items;
std::unique_ptr<CreatureVector> 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