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_ITEMS_H_4E2221634ABA45FE85BA50F710669B3C
21#define FS_ITEMS_H_4E2221634ABA45FE85BA50F710669B3C
22
23#include "const.h"
24#include "enums.h"
25#include "itemloader.h"
26#include "position.h"
27
28enum SlotPositionBits : uint32_t {
29 SLOTP_WHEREEVER = 0xFFFFFFFF,
30 SLOTP_HEAD = 1 << 0,
31 SLOTP_NECKLACE = 1 << 1,
32 SLOTP_BACKPACK = 1 << 2,
33 SLOTP_ARMOR = 1 << 3,
34 SLOTP_RIGHT = 1 << 4,
35 SLOTP_LEFT = 1 << 5,
36 SLOTP_LEGS = 1 << 6,
37 SLOTP_FEET = 1 << 7,
38 SLOTP_RING = 1 << 8,
39 SLOTP_AMMO = 1 << 9,
40 SLOTP_DEPOT = 1 << 10,
41 SLOTP_TWO_HAND = 1 << 11,
42 SLOTP_HAND = (SLOTP_LEFT | SLOTP_RIGHT)
43};
44
45enum ItemTypes_t {
46 ITEM_TYPE_NONE,
47 ITEM_TYPE_DEPOT,
48 ITEM_TYPE_MAILBOX,
49 ITEM_TYPE_TRASHHOLDER,
50 ITEM_TYPE_CONTAINER,
51 ITEM_TYPE_DOOR,
52 ITEM_TYPE_MAGICFIELD,
53 ITEM_TYPE_TELEPORT,
54 ITEM_TYPE_BED,
55 ITEM_TYPE_KEY,
56 ITEM_TYPE_RUNE,
57 ITEM_TYPE_LAST
58};
59
60enum ItemParseAttributes_t {
61 ITEM_PARSE_TYPE,
62 ITEM_PARSE_DESCRIPTION,
63 ITEM_PARSE_RUNESPELLNAME,
64 ITEM_PARSE_WEIGHT,
65 ITEM_PARSE_SHOWCOUNT,
66 ITEM_PARSE_ARMOR,
67 ITEM_PARSE_DEFENSE,
68 ITEM_PARSE_EXTRADEF,
69 ITEM_PARSE_ATTACK,
70 ITEM_PARSE_ROTATETO,
71 ITEM_PARSE_MOVEABLE,
72 ITEM_PARSE_BLOCKPROJECTILE,
73 ITEM_PARSE_PICKUPABLE,
74 ITEM_PARSE_FORCESERIALIZE,
75 ITEM_PARSE_FLOORCHANGE,
76 ITEM_PARSE_CORPSETYPE,
77 ITEM_PARSE_CONTAINERSIZE,
78 ITEM_PARSE_FLUIDSOURCE,
79 ITEM_PARSE_READABLE,
80 ITEM_PARSE_WRITEABLE,
81 ITEM_PARSE_MAXTEXTLEN,
82 ITEM_PARSE_WRITEONCEITEMID,
83 ITEM_PARSE_WEAPONTYPE,
84 ITEM_PARSE_SLOTTYPE,
85 ITEM_PARSE_AMMOTYPE,
86 ITEM_PARSE_SHOOTTYPE,
87 ITEM_PARSE_EFFECT,
88 ITEM_PARSE_RANGE,
89 ITEM_PARSE_STOPDURATION,
90 ITEM_PARSE_DECAYTO,
91 ITEM_PARSE_TRANSFORMEQUIPTO,
92 ITEM_PARSE_TRANSFORMDEEQUIPTO,
93 ITEM_PARSE_DURATION,
94 ITEM_PARSE_SHOWDURATION,
95 ITEM_PARSE_CHARGES,
96 ITEM_PARSE_SHOWCHARGES,
97 ITEM_PARSE_SHOWATTRIBUTES,
98 ITEM_PARSE_HITCHANCE,
99 ITEM_PARSE_MAXHITCHANCE,
100 ITEM_PARSE_INVISIBLE,
101 ITEM_PARSE_SPEED,
102 ITEM_PARSE_HEALTHGAIN,
103 ITEM_PARSE_HEALTHTICKS,
104 ITEM_PARSE_MANAGAIN,
105 ITEM_PARSE_MANATICKS,
106 ITEM_PARSE_MANASHIELD,
107 ITEM_PARSE_SKILLSWORD,
108 ITEM_PARSE_SKILLAXE,
109 ITEM_PARSE_SKILLCLUB,
110 ITEM_PARSE_SKILLDIST,
111 ITEM_PARSE_SKILLFISH,
112 ITEM_PARSE_SKILLSHIELD,
113 ITEM_PARSE_SKILLFIST,
114 ITEM_PARSE_MAXHITPOINTS,
115 ITEM_PARSE_MAXHITPOINTSPERCENT,
116 ITEM_PARSE_MAXMANAPOINTS,
117 ITEM_PARSE_MAXMANAPOINTSPERCENT,
118 ITEM_PARSE_MAGICPOINTS,
119 ITEM_PARSE_MAGICPOINTSPERCENT,
120 ITEM_PARSE_CRITICALHITCHANCE,
121 ITEM_PARSE_CRITICALHITAMOUNT,
122 ITEM_PARSE_LIFELEECHCHANCE,
123 ITEM_PARSE_LIFELEECHAMOUNT,
124 ITEM_PARSE_MANALEECHCHANCE,
125 ITEM_PARSE_MANALEECHAMOUNT,
126 ITEM_PARSE_FIELDABSORBPERCENTENERGY,
127 ITEM_PARSE_FIELDABSORBPERCENTFIRE,
128 ITEM_PARSE_FIELDABSORBPERCENTPOISON,
129 ITEM_PARSE_ABSORBPERCENTALL,
130 ITEM_PARSE_ABSORBPERCENTELEMENTS,
131 ITEM_PARSE_ABSORBPERCENTMAGIC,
132 ITEM_PARSE_ABSORBPERCENTENERGY,
133 ITEM_PARSE_ABSORBPERCENTFIRE,
134 ITEM_PARSE_ABSORBPERCENTPOISON,
135 ITEM_PARSE_ABSORBPERCENTICE,
136 ITEM_PARSE_ABSORBPERCENTHOLY,
137 ITEM_PARSE_ABSORBPERCENTDEATH,
138 ITEM_PARSE_ABSORBPERCENTLIFEDRAIN,
139 ITEM_PARSE_ABSORBPERCENTMANADRAIN,
140 ITEM_PARSE_ABSORBPERCENTDROWN,
141 ITEM_PARSE_ABSORBPERCENTPHYSICAL,
142 ITEM_PARSE_ABSORBPERCENTHEALING,
143 ITEM_PARSE_ABSORBPERCENTUNDEFINED,
144 ITEM_PARSE_SUPPRESSDRUNK,
145 ITEM_PARSE_SUPPRESSENERGY,
146 ITEM_PARSE_SUPPRESSFIRE,
147 ITEM_PARSE_SUPPRESSPOISON,
148 ITEM_PARSE_SUPPRESSDROWN,
149 ITEM_PARSE_SUPPRESSPHYSICAL,
150 ITEM_PARSE_SUPPRESSFREEZE,
151 ITEM_PARSE_SUPPRESSDAZZLE,
152 ITEM_PARSE_SUPPRESSCURSE,
153 ITEM_PARSE_FIELD,
154 ITEM_PARSE_REPLACEABLE,
155 ITEM_PARSE_PARTNERDIRECTION,
156 ITEM_PARSE_LEVELDOOR,
157 ITEM_PARSE_MALETRANSFORMTO,
158 ITEM_PARSE_FEMALETRANSFORMTO,
159 ITEM_PARSE_TRANSFORMTO,
160 ITEM_PARSE_DESTROYTO,
161 ITEM_PARSE_ELEMENTICE,
162 ITEM_PARSE_ELEMENTEARTH,
163 ITEM_PARSE_ELEMENTFIRE,
164 ITEM_PARSE_ELEMENTENERGY,
165 ITEM_PARSE_WALKSTACK,
166 ITEM_PARSE_BLOCKING,
167 ITEM_PARSE_ALLOWDISTREAD,
168};
169
170struct Abilities {
171 uint32_t healthGain = 0;
172 uint32_t healthTicks = 0;
173 uint32_t manaGain = 0;
174 uint32_t manaTicks = 0;
175
176 uint32_t conditionImmunities = 0;
177 uint32_t conditionSuppressions = 0;
178
179 //stats modifiers
180 int32_t stats[STAT_LAST + 1] = { 0 };
181 int32_t statsPercent[STAT_LAST + 1] = { 0 };
182
183 //extra skill modifiers
184 int32_t skills[SKILL_LAST + 1] = { 0 };
185 int32_t specialSkills[SPECIALSKILL_LAST + 1] = { 0 };
186
187 int32_t speed = 0;
188
189 // field damage abilities modifiers
190 int16_t fieldAbsorbPercent[COMBAT_COUNT] = { 0 };
191
192 //damage abilities modifiers
193 int16_t absorbPercent[COMBAT_COUNT] = { 0 };
194
195 //elemental damage
196 uint16_t elementDamage = 0;
197 CombatType_t elementType = COMBAT_NONE;
198
199 bool manaShield = false;
200 bool invisible = false;
201 bool regeneration = false;
202};
203
204class ConditionDamage;
205
206class ItemType
207{
208 public:
209 ItemType() = default;
210
211 //non-copyable
212 ItemType(const ItemType& other) = delete;
213 ItemType& operator=(const ItemType& other) = delete;
214
215 ItemType(ItemType&& other) = default;
216 ItemType& operator=(ItemType&& other) = default;
217
218 bool isGroundTile() const {
219 return group == ITEM_GROUP_GROUND;
220 }
221 bool isContainer() const {
222 return group == ITEM_GROUP_CONTAINER;
223 }
224 bool isSplash() const {
225 return group == ITEM_GROUP_SPLASH;
226 }
227 bool isFluidContainer() const {
228 return group == ITEM_GROUP_FLUID;
229 }
230
231 bool isDoor() const {
232 return (type == ITEM_TYPE_DOOR);
233 }
234 bool isMagicField() const {
235 return (type == ITEM_TYPE_MAGICFIELD);
236 }
237 bool isTeleport() const {
238 return (type == ITEM_TYPE_TELEPORT);
239 }
240 bool isKey() const {
241 return (type == ITEM_TYPE_KEY);
242 }
243 bool isDepot() const {
244 return (type == ITEM_TYPE_DEPOT);
245 }
246 bool isMailbox() const {
247 return (type == ITEM_TYPE_MAILBOX);
248 }
249 bool isTrashHolder() const {
250 return (type == ITEM_TYPE_TRASHHOLDER);
251 }
252 bool isBed() const {
253 return (type == ITEM_TYPE_BED);
254 }
255 bool isRune() const {
256 return (type == ITEM_TYPE_RUNE);
257 }
258 bool isPickupable() const {
259 return (allowPickupable || pickupable);
260 }
261 bool isUseable() const {
262 return (useable);
263 }
264 bool hasSubType() const {
265 return (isFluidContainer() || isSplash() || stackable || charges != 0);
266 }
267
268 Abilities& getAbilities() {
269 if (!abilities) {
270 abilities.reset(new Abilities());
271 }
272 return *abilities;
273 }
274
275 std::string getPluralName() const {
276 if (!pluralName.empty()) {
277 return pluralName;
278 }
279
280 if (showCount == 0) {
281 return name;
282 }
283
284 std::string str;
285 str.reserve(name.length() + 1);
286 str.assign(name);
287 str += 's';
288 return str;
289 }
290
291 itemgroup_t group = ITEM_GROUP_NONE;
292 ItemTypes_t type = ITEM_TYPE_NONE;
293 uint16_t id = 0;
294 uint16_t clientId = 0;
295 bool stackable = false;
296 bool isAnimation = false;
297
298 std::string name;
299 std::string article;
300 std::string pluralName;
301 std::string description;
302 std::string runeSpellName;
303 std::string vocationString;
304
305 std::unique_ptr<Abilities> abilities;
306 std::unique_ptr<ConditionDamage> conditionDamage;
307
308 uint32_t weight = 0;
309 uint32_t levelDoor = 0;
310 uint32_t decayTime = 0;
311 uint32_t wieldInfo = 0;
312 uint32_t minReqLevel = 0;
313 uint32_t minReqMagicLevel = 0;
314 uint32_t charges = 0;
315 int32_t maxHitChance = -1;
316 int32_t decayTo = -1;
317 int32_t attack = 0;
318 int32_t defense = 0;
319 int32_t extraDefense = 0;
320 int32_t armor = 0;
321 uint16_t rotateTo = 0;
322 int32_t runeMagLevel = 0;
323 int32_t runeLevel = 0;
324
325 CombatType_t combatType = COMBAT_NONE;
326
327 uint16_t transformToOnUse[2] = {0, 0};
328 uint16_t transformToFree = 0;
329 uint16_t destroyTo = 0;
330 uint16_t maxTextLen = 0;
331 uint16_t writeOnceItemId = 0;
332 uint16_t transformEquipTo = 0;
333 uint16_t transformDeEquipTo = 0;
334 uint16_t maxItems = 8;
335 uint16_t slotPosition = SLOTP_HAND;
336 uint16_t speed = 0;
337 uint16_t wareId = 0;
338
339 MagicEffectClasses magicEffect = CONST_ME_NONE;
340 Direction bedPartnerDir = DIRECTION_NONE;
341 WeaponType_t weaponType = WEAPON_NONE;
342 Ammo_t ammoType = AMMO_NONE;
343 ShootType_t shootType = CONST_ANI_NONE;
344 RaceType_t corpseType = RACE_NONE;
345 FluidTypes_t fluidSource = FLUID_NONE;
346
347 uint8_t floorChange = 0;
348 uint8_t alwaysOnTopOrder = 0;
349 uint8_t lightLevel = 0;
350 uint8_t lightColor = 0;
351 uint8_t shootRange = 1;
352 int8_t hitChance = 0;
353
354 bool forceUse = false;
355 bool forceSerialize = false;
356 bool hasHeight = false;
357 bool walkStack = true;
358 bool blockSolid = false;
359 bool blockPickupable = false;
360 bool blockProjectile = false;
361 bool blockPathFind = false;
362 bool allowPickupable = false;
363 bool showDuration = false;
364 bool showCharges = false;
365 bool showAttributes = false;
366 bool replaceable = true;
367 bool pickupable = false;
368 bool rotatable = false;
369 bool useable = false;
370 bool moveable = false;
371 bool alwaysOnTop = false;
372 bool canReadText = false;
373 bool canWriteText = false;
374 bool isVertical = false;
375 bool isHorizontal = false;
376 bool isHangable = false;
377 bool allowDistRead = false;
378 bool lookThrough = false;
379 bool stopTime = false;
380 bool showCount = true;
381};
382
383class Items
384{
385 public:
386 using NameMap = std::unordered_multimap<std::string, uint16_t>;
387 using InventoryVector = std::vector<uint16_t>;
388
389 Items();
390
391 // non-copyable
392 Items(const Items&) = delete;
393 Items& operator=(const Items&) = delete;
394
395 bool reload();
396 void clear();
397
398 bool loadFromOtb(const std::string& file);
399
400 const ItemType& operator[](size_t id) const {
401 return getItemType(id);
402 }
403 const ItemType& getItemType(size_t id) const;
404 ItemType& getItemType(size_t id);
405 const ItemType& getItemIdByClientId(uint16_t spriteId) const;
406
407 uint16_t getItemIdByName(const std::string& name);
408
409 uint32_t majorVersion = 0;
410 uint32_t minorVersion = 0;
411 uint32_t buildNumber = 0;
412
413 bool loadFromXml();
414 void parseItemNode(const pugi::xml_node& itemNode, uint16_t id);
415
416 void buildInventoryList();
417 const InventoryVector& getInventory() const {
418 return inventory;
419 }
420
421 size_t size() const {
422 return items.size();
423 }
424
425 NameMap nameToItems;
426
427 private:
428 std::vector<ItemType> items;
429 InventoryVector inventory;
430 class ClientIdToServerIdMap
431 {
432 public:
433 ClientIdToServerIdMap() {
434 vec.reserve(30000);
435 }
436
437 void emplace(uint16_t clientId, uint16_t serverId) {
438 if (clientId >= vec.size()) {
439 vec.resize(clientId + 1, 0);
440 }
441 if (vec[clientId] == 0) {
442 vec[clientId] = serverId;
443 }
444 }
445
446 uint16_t getServerId(uint16_t clientId) const {
447 uint16_t serverId = 0;
448 if (clientId < vec.size()) {
449 serverId = vec[clientId];
450 }
451 return serverId;
452 }
453
454 void clear() {
455 vec.clear();
456 }
457 private:
458 std::vector<uint16_t> vec;
459 } clientIdToServerIdMap;
460};
461#endif
462