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_TILE_H_96C7EE7CF8CD48E59D5D554A181F0C56
21#define FS_TILE_H_96C7EE7CF8CD48E59D5D554A181F0C56
22
23#include "cylinder.h"
24#include "item.h"
25#include "tools.h"
26#include "spectators.h"
27
28class Creature;
29class Teleport;
30class TrashHolder;
31class Mailbox;
32class MagicField;
33class QTreeLeafNode;
34class BedItem;
35
36using CreatureVector = std::vector<Creature*>;
37using ItemVector = std::vector<Item*>;
38
39enum tileflags_t : uint32_t {
40 TILESTATE_NONE = 0,
41
42 TILESTATE_FLOORCHANGE_DOWN = 1 << 0,
43 TILESTATE_FLOORCHANGE_NORTH = 1 << 1,
44 TILESTATE_FLOORCHANGE_SOUTH = 1 << 2,
45 TILESTATE_FLOORCHANGE_EAST = 1 << 3,
46 TILESTATE_FLOORCHANGE_WEST = 1 << 4,
47 TILESTATE_FLOORCHANGE_SOUTH_ALT = 1 << 5,
48 TILESTATE_FLOORCHANGE_EAST_ALT = 1 << 6,
49 TILESTATE_PROTECTIONZONE = 1 << 7,
50 TILESTATE_NOPVPZONE = 1 << 8,
51 TILESTATE_NOLOGOUT = 1 << 9,
52 TILESTATE_PVPZONE = 1 << 10,
53 TILESTATE_TELEPORT = 1 << 11,
54 TILESTATE_MAGICFIELD = 1 << 12,
55 TILESTATE_MAILBOX = 1 << 13,
56 TILESTATE_TRASHHOLDER = 1 << 14,
57 TILESTATE_BED = 1 << 15,
58 TILESTATE_DEPOT = 1 << 16,
59 TILESTATE_BLOCKSOLID = 1 << 17,
60 TILESTATE_BLOCKPATH = 1 << 18,
61 TILESTATE_IMMOVABLEBLOCKSOLID = 1 << 19,
62 TILESTATE_IMMOVABLEBLOCKPATH = 1 << 20,
63 TILESTATE_IMMOVABLENOFIELDBLOCKPATH = 1 << 21,
64 TILESTATE_NOFIELDBLOCKPATH = 1 << 22,
65 TILESTATE_SUPPORTS_HANGABLE = 1 << 23,
66
67 TILESTATE_FLOORCHANGE = TILESTATE_FLOORCHANGE_DOWN | TILESTATE_FLOORCHANGE_NORTH | TILESTATE_FLOORCHANGE_SOUTH | TILESTATE_FLOORCHANGE_EAST | TILESTATE_FLOORCHANGE_WEST | TILESTATE_FLOORCHANGE_SOUTH_ALT | TILESTATE_FLOORCHANGE_EAST_ALT,
68};
69
70enum ZoneType_t {
71 ZONE_PROTECTION,
72 ZONE_NOPVP,
73 ZONE_PVP,
74 ZONE_NOLOGOUT,
75 ZONE_NORMAL,
76};
77
78class TileItemVector : private ItemVector
79{
80 public:
81 using ItemVector::begin;
82 using ItemVector::end;
83 using ItemVector::rbegin;
84 using ItemVector::rend;
85 using ItemVector::size;
86 using ItemVector::clear;
87 using ItemVector::at;
88 using ItemVector::insert;
89 using ItemVector::erase;
90 using ItemVector::push_back;
91 using ItemVector::value_type;
92 using ItemVector::iterator;
93 using ItemVector::const_iterator;
94 using ItemVector::reverse_iterator;
95 using ItemVector::const_reverse_iterator;
96 using ItemVector::empty;
97
98 iterator getBeginDownItem() {
99 return begin();
100 }
101 const_iterator getBeginDownItem() const {
102 return begin();
103 }
104 iterator getEndDownItem() {
105 return begin() + downItemCount;
106 }
107 const_iterator getEndDownItem() const {
108 return begin() + downItemCount;
109 }
110 iterator getBeginTopItem() {
111 return getEndDownItem();
112 }
113 const_iterator getBeginTopItem() const {
114 return getEndDownItem();
115 }
116 iterator getEndTopItem() {
117 return end();
118 }
119 const_iterator getEndTopItem() const {
120 return end();
121 }
122
123 uint32_t getTopItemCount() const {
124 return size() - downItemCount;
125 }
126 uint32_t getDownItemCount() const {
127 return downItemCount;
128 }
129 inline Item* getTopTopItem() const {
130 if (getTopItemCount() == 0) {
131 return nullptr;
132 }
133 return *(getEndTopItem() - 1);
134 }
135 inline Item* getTopDownItem() const {
136 if (downItemCount == 0) {
137 return nullptr;
138 }
139 return *getBeginDownItem();
140 }
141 void addDownItemCount(int32_t increment) {
142 downItemCount += increment;
143 }
144
145 private:
146 uint16_t downItemCount = 0;
147};
148
149class Tile : public Cylinder
150{
151 public:
152 static Tile& nullptr_tile;
153 Tile(uint16_t x, uint16_t y, uint8_t z) : tilePos(x, y, z) {}
154 virtual ~Tile() {
155 delete ground;
156 };
157
158 // non-copyable
159 Tile(const Tile&) = delete;
160 Tile& operator=(const Tile&) = delete;
161
162 virtual TileItemVector* getItemList() = 0;
163 virtual const TileItemVector* getItemList() const = 0;
164 virtual TileItemVector* makeItemList() = 0;
165
166 virtual CreatureVector* getCreatures() = 0;
167 virtual const CreatureVector* getCreatures() const = 0;
168 virtual CreatureVector* makeCreatures() = 0;
169
170 int32_t getThrowRange() const override final {
171 return 0;
172 }
173 bool isPushable() const override final {
174 return false;
175 }
176
177 MagicField* getFieldItem() const;
178 Teleport* getTeleportItem() const;
179 TrashHolder* getTrashHolder() const;
180 Mailbox* getMailbox() const;
181 BedItem* getBedItem() const;
182
183 Creature* getTopCreature() const;
184 const Creature* getBottomCreature() const;
185 Creature* getTopVisibleCreature(const Creature* creature) const;
186 const Creature* getBottomVisibleCreature(const Creature* creature) const;
187 Item* getTopTopItem() const;
188 Item* getTopDownItem() const;
189 bool isMoveableBlocking() const;
190 Thing* getTopVisibleThing(const Creature* creature);
191 Item* getItemByTopOrder(int32_t topOrder);
192
193 size_t getThingCount() const {
194 size_t thingCount = getCreatureCount() + getItemCount();
195 if (ground) {
196 thingCount++;
197 }
198 return thingCount;
199 }
200 // If these return != 0 the associated vectors are guaranteed to exists
201 size_t getCreatureCount() const;
202 size_t getItemCount() const;
203 uint32_t getTopItemCount() const;
204 uint32_t getDownItemCount() const;
205
206 bool hasProperty(ITEMPROPERTY prop) const;
207 bool hasProperty(const Item* exclude, ITEMPROPERTY prop) const;
208
209 bool hasFlag(uint32_t flag) const {
210 return hasBitSet(flag, this->flags);
211 }
212 void setFlag(uint32_t flag) {
213 this->flags |= flag;
214 }
215 void resetFlag(uint32_t flag) {
216 this->flags &= ~flag;
217 }
218
219 ZoneType_t getZone() const {
220 if (hasFlag(TILESTATE_PROTECTIONZONE)) {
221 return ZONE_PROTECTION;
222 } else if (hasFlag(TILESTATE_NOPVPZONE)) {
223 return ZONE_NOPVP;
224 } else if (hasFlag(TILESTATE_PVPZONE)) {
225 return ZONE_PVP;
226 } else {
227 return ZONE_NORMAL;
228 }
229 }
230
231 bool hasHeight(uint32_t n) const;
232
233 std::string getDescription(int32_t lookDistance) const override final;
234
235 int32_t getClientIndexOfCreature(const Player* player, const Creature* creature) const;
236 int32_t getStackposOfCreature(const Player* player, const Creature* creature) const;
237 int32_t getStackposOfItem(const Player* player, const Item* item) const;
238
239 //cylinder implementations
240 ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count,
241 uint32_t flags, Creature* actor = nullptr) const override;
242 ReturnValue queryMaxCount(int32_t index, const Thing& thing, uint32_t count,
243 uint32_t& maxQueryCount, uint32_t flags) const override final;
244 ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const override final;
245 Tile* queryDestination(int32_t& index, const Thing& thing, Item** destItem, uint32_t& flags) override;
246
247 void addThing(Thing* thing) override final;
248 void addThing(int32_t index, Thing* thing) override;
249
250 void updateThing(Thing* thing, uint16_t itemId, uint32_t count) override final;
251 void replaceThing(uint32_t index, Thing* thing) override final;
252
253 void removeThing(Thing* thing, uint32_t count) override final;
254
255 void removeCreature(Creature* creature);
256
257 int32_t getThingIndex(const Thing* thing) const override final;
258 size_t getFirstIndex() const override final;
259 size_t getLastIndex() const override final;
260 uint32_t getItemTypeCount(uint16_t itemId, int32_t subType = -1) const override final;
261 Thing* getThing(size_t index) const override final;
262
263 void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) override final;
264 void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) override final;
265
266 void internalAddThing(Thing* thing) override final;
267 void internalAddThing(uint32_t index, Thing* thing) override;
268
269 const Position& getPosition() const override final {
270 return tilePos;
271 }
272
273 bool isRemoved() const override final {
274 return false;
275 }
276
277 Item* getUseItem(int32_t index) const;
278
279 Item* getGround() const {
280 return ground;
281 }
282 void setGround(Item* item) {
283 ground = item;
284 }
285
286 private:
287 void onAddTileItem(Item* item);
288 void onUpdateTileItem(Item* oldItem, const ItemType& oldType, Item* newItem, const ItemType& newType);
289 void onRemoveTileItem(const SpectatorVec& spectators, const std::vector<int32_t>& oldStackPosVector, Item* item);
290 void onUpdateTile(const SpectatorVec& spectators);
291
292 void setTileFlags(const Item* item);
293 void resetTileFlags(const Item* item);
294
295 Item* ground = nullptr;
296 Position tilePos;
297 uint32_t flags = 0;
298};
299
300// Used for walkable tiles, where there is high likeliness of
301// items being added/removed
302class DynamicTile : public Tile
303{
304 // By allocating the vectors in-house, we avoid some memory fragmentation
305 TileItemVector items;
306 CreatureVector creatures;
307
308 public:
309 DynamicTile(uint16_t x, uint16_t y, uint8_t z) : Tile(x, y, z) {}
310 ~DynamicTile() {
311 for (Item* item : items) {
312 item->decrementReferenceCounter();
313 }
314 }
315
316 // non-copyable
317 DynamicTile(const DynamicTile&) = delete;
318 DynamicTile& operator=(const DynamicTile&) = delete;
319
320 TileItemVector* getItemList() override {
321 return &items;
322 }
323 const TileItemVector* getItemList() const override {
324 return &items;
325 }
326 TileItemVector* makeItemList() override {
327 return &items;
328 }
329
330 CreatureVector* getCreatures() override {
331 return &creatures;
332 }
333 const CreatureVector* getCreatures() const override {
334 return &creatures;
335 }
336 CreatureVector* makeCreatures() override {
337 return &creatures;
338 }
339};
340
341// For blocking tiles, where we very rarely actually have items
342class StaticTile final : public Tile
343{
344 // We very rarely even need the vectors, so don't keep them in memory
345 std::unique_ptr<TileItemVector> items;
346 std::unique_ptr<CreatureVector> creatures;
347
348 public:
349 StaticTile(uint16_t x, uint16_t y, uint8_t z) : Tile(x, y, z) {}
350 ~StaticTile() {
351 if (items) {
352 for (Item* item : *items) {
353 item->decrementReferenceCounter();
354 }
355 }
356 }
357
358 // non-copyable
359 StaticTile(const StaticTile&) = delete;
360 StaticTile& operator=(const StaticTile&) = delete;
361
362 TileItemVector* getItemList() override {
363 return items.get();
364 }
365 const TileItemVector* getItemList() const override {
366 return items.get();
367 }
368 TileItemVector* makeItemList() override {
369 if (!items) {
370 items.reset(new TileItemVector);
371 }
372 return items.get();
373 }
374
375 CreatureVector* getCreatures() override {
376 return creatures.get();
377 }
378 const CreatureVector* getCreatures() const override {
379 return creatures.get();
380 }
381 CreatureVector* makeCreatures() override {
382 if (!creatures) {
383 creatures.reset(new CreatureVector);
384 }
385 return creatures.get();
386 }
387};
388
389#endif
390