/* * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information * * 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, see . */ #ifndef Loot_h__ #define Loot_h__ #include "ConditionMgr.h" #include "DBCEnums.h" #include "Define.h" #include "Duration.h" #include "ItemEnchantmentMgr.h" #include "LootItemType.h" #include "ObjectGuid.h" #include "Optional.h" #include "SharedDefines.h" #include #include #include constexpr Minutes LOOT_ROLL_TIMEOUT = 1min; class Group; class Item; class LootStore; class Map; class Player; struct ItemDisenchantLootEntry; struct Loot; struct LootStoreItem; namespace WorldPackets { namespace Loot { struct LootItemData; class LootResponse; } } enum RollType { ROLL_PASS = 0, ROLL_NEED = 1, ROLL_GREED = 2, ROLL_DISENCHANT = 3, ROLL_TRANSMOG = 4, MAX_ROLL_TYPE = 5 }; enum class RollVote { Pass = 0, Need = 1, Greed = 2, Disenchant = 3, NotEmitedYet = 4, NotValid = 5 }; enum RollMask { ROLL_FLAG_TYPE_PASS = 0x01, ROLL_FLAG_TYPE_NEED = 0x02, ROLL_FLAG_TYPE_GREED = 0x04, ROLL_FLAG_TYPE_DISENCHANT = 0x08, ROLL_FLAG_TYPE_TRANSMOG = 0x10, ROLL_ALL_TYPE_NO_DISENCHANT = 0x07, ROLL_ALL_TYPE_MASK = 0x0F }; #define MAX_NR_LOOT_ITEMS 18 enum LootMethod : uint8 { FREE_FOR_ALL = 0, ROUND_ROBIN = 1, MASTER_LOOT = 2, GROUP_LOOT = 3, NEED_BEFORE_GREED = 4, PERSONAL_LOOT = 5 }; enum LootType : uint8 { LOOT_NONE = 0, LOOT_CORPSE = 1, LOOT_PICKPOCKETING = 2, LOOT_FISHING = 3, LOOT_DISENCHANTING = 4, LOOT_ITEM = 5, LOOT_SKINNING = 6, LOOT_GATHERING_NODE = 8, LOOT_CHEST = 9, LOOT_CORPSE_PERSONAL = 14, LOOT_FISHINGHOLE = 20, // unsupported by client, sending LOOT_FISHING instead LOOT_INSIGNIA = 21, // unsupported by client, sending LOOT_CORPSE instead LOOT_FISHING_JUNK = 22, // unsupported by client, sending LOOT_FISHING instead LOOT_PROSPECTING = 23, LOOT_MILLING = 24, }; constexpr LootType GetLootTypeForClient(LootType lootType) { switch (lootType) { case LOOT_PROSPECTING: case LOOT_MILLING: return LOOT_DISENCHANTING; case LOOT_INSIGNIA: return LOOT_SKINNING; case LOOT_FISHINGHOLE: case LOOT_FISHING_JUNK: return LOOT_FISHING; default: break; } return lootType; } enum LootError : uint8 { LOOT_ERROR_DIDNT_KILL = 0, // You don't have permission to loot that corpse. LOOT_ERROR_TOO_FAR = 4, // You are too far away to loot that corpse. LOOT_ERROR_BAD_FACING = 5, // You must be facing the corpse to loot it. LOOT_ERROR_LOCKED = 6, // Someone is already looting that corpse. LOOT_ERROR_NOTSTANDING = 8, // You need to be standing up to loot something! LOOT_ERROR_STUNNED = 9, // You can't loot anything while stunned! LOOT_ERROR_PLAYER_NOT_FOUND = 10, // Player not found LOOT_ERROR_PLAY_TIME_EXCEEDED = 11, // Maximum play time exceeded LOOT_ERROR_MASTER_INV_FULL = 12, // That player's inventory is full LOOT_ERROR_MASTER_UNIQUE_ITEM = 13, // Player has too many of that item already LOOT_ERROR_MASTER_OTHER = 14, // Can't assign item to that player LOOT_ERROR_ALREADY_PICKPOCKETED = 15, // Your target has already had its pockets picked LOOT_ERROR_NOT_WHILE_SHAPESHIFTED = 16, // You can't do that while shapeshifted. LOOT_ERROR_NO_LOOT = 17 // There is no loot. }; // type of Loot Item in Loot View enum LootSlotType { LOOT_SLOT_TYPE_ALLOW_LOOT = 0, // player can loot the item. LOOT_SLOT_TYPE_ROLL_ONGOING = 1, // roll is ongoing. player cannot loot. LOOT_SLOT_TYPE_MASTER = 3, // item can only be distributed by group loot master. LOOT_SLOT_TYPE_LOCKED = 2, // item is shown in red. player cannot loot. LOOT_SLOT_TYPE_OWNER = 4 // ignore binding confirmation and etc, for single player looting }; enum class LootRollIneligibilityReason : uint32 { None = 0, UnusableByClass = 1, // Your class may not roll need on this item. MaxUniqueItemCount = 2, // You already have the maximum amount of this item. CannotBeDisenchanted = 3, // This item may not be disenchanted. EnchantingSkillTooLow = 4, // You do not have an Enchanter of skill %d in your group. NeedDisabled = 5, // Need rolls are disabled for this item. OwnBetterItem = 6 // You already have a powerful version of this item. }; struct TC_GAME_API LootItem { uint32 itemid = 0; uint32 LootListId = 0; ItemRandomBonusListId randomBonusListId = 0; std::vector BonusListIDs; ItemContext context = ItemContext::NONE; ConditionsReference conditions; // additional loot condition GuidSet allowedGUIDs; ObjectGuid rollWinnerGUID; // Stores the guid of person who won loot, if his bags are full only he can see the item in loot list! uint32 count = 0; LootItemType type = LootItemType::Item; bool is_looted : 1 = false; bool is_blocked : 1 = false; bool freeforall : 1 = false; // free for all bool is_underthreshold : 1 = false; bool is_counted : 1 = false; bool needs_quest : 1 = false; // quest drop bool follow_loot_rules : 1 = false; // Constructor, copies most fields from LootStoreItem, generates random count and random suffixes/properties // Should be called for non-reference LootStoreItem entries only explicit LootItem(LootStoreItem const& li); // Empty constructor for creating an empty LootItem to be filled in with DB data LootItem() = default; LootItem(LootItem const&); LootItem(LootItem&&) noexcept; LootItem& operator=(LootItem const&); LootItem& operator=(LootItem&&) noexcept; ~LootItem(); // Basic checks for player/item compatibility - if false no chance to see the item in the loot - used only for loot generation bool AllowedForPlayer(Player const* player, Loot const* loot) const; static bool AllowedForPlayer(Player const* player, LootStoreItem const& lootStoreItem, bool strictUsabilityCheck); static bool ItemAllowedForPlayer(Player const* player, Loot const* loot, uint32 itemid, bool needs_quest, bool follow_loot_rules, bool strictUsabilityCheck, ConditionsReference const& conditions); static bool CurrencyAllowedForPlayer(Player const* player, uint32 currencyId, bool needs_quest, ConditionsReference const& conditions); static bool TrackingQuestAllowedForPlayer(Player const* player, uint32 questId, ConditionsReference const& conditions); void AddAllowedLooter(Player const* player); GuidSet const& GetAllowedLooters() const { return allowedGUIDs; } bool HasAllowedLooter(ObjectGuid const& looter) const; Optional GetUiTypeForPlayer(Player const* player, Loot const& loot) const; }; struct NotNormalLootItem { uint8 LootListId; bool is_looted; NotNormalLootItem() : LootListId(0), is_looted(false) { } NotNormalLootItem(uint8 _index, bool _islooted = false) : LootListId(_index), is_looted(_islooted) { } }; typedef std::vector NotNormalLootItemList; typedef std::vector LootItemList; typedef std::unordered_map> NotNormalLootItemMap; //===================================================== struct PlayerRollVote { PlayerRollVote() : Vote(RollVote::NotValid), RollNumber(0) { } RollVote Vote; uint8 RollNumber; }; class LootRoll { public: using RollVoteMap = std::unordered_map; LootRoll() : m_map(nullptr), m_isStarted(false), m_lootItem(nullptr), m_loot(nullptr), m_voteMask(), m_endTime(TimePoint::min()) { } ~LootRoll(); LootRoll(LootRoll const&) = delete; LootRoll(LootRoll&&) = delete; LootRoll& operator=(LootRoll const&) = delete; LootRoll& operator=(LootRoll&&) = delete; bool TryToStart(Map* map, Loot& loot, uint32 lootListId, uint16 enchantingSkill); bool PlayerVote(Player* player, RollVote vote); bool UpdateRoll(); bool IsLootItem(ObjectGuid const& lootObject, uint32 lootListId) const; private: void SendStartRoll(); void SendAllPassed(); void SendRoll(ObjectGuid const& targetGuid, int32 rollNumber, RollVote rollType, Optional const& rollWinner); void SendLootRollWon(ObjectGuid const& targetGuid, int32 rollNumber, RollVote rollType); void FillPacket(WorldPackets::Loot::LootItemData& lootItem) const; void Finish(RollVoteMap::const_iterator winnerItr); bool AllPlayerVoted(RollVoteMap::const_iterator& winnerItr); Optional GetItemDisenchantLootId() const; Optional GetItemDisenchantSkillRequired() const; Map* m_map; RollVoteMap m_rollVoteMap; bool m_isStarted; LootItem* m_lootItem; Loot* m_loot; RollMask m_voteMask; TimePoint m_endTime; }; struct TC_GAME_API Loot { NotNormalLootItemMap const& GetPlayerFFAItems() const { return PlayerFFAItems; } std::vector items; uint32 gold; uint8 unlootedCount; ObjectGuid roundRobinPlayer; // GUID of the player having the Round-Robin ownership for the loot. If 0, round robin owner has released. LootType loot_type; // required for achievement system explicit Loot(Map* map, ObjectGuid owner, LootType type, Group const* group); ~Loot(); Loot(Loot const&) = delete; Loot(Loot&&) = delete; Loot& operator=(Loot const&) = delete; Loot& operator=(Loot&&) = delete; ObjectGuid const& GetGUID() const { return _guid; } ObjectGuid const& GetOwnerGUID() const { return _owner; } ItemContext GetItemContext() const { return _itemContext; } void SetItemContext(ItemContext context) { _itemContext = context; } LootMethod GetLootMethod() const { return _lootMethod; } ObjectGuid const& GetLootMasterGUID() const { return _lootMaster; } uint32 GetDungeonEncounterId() const { return _dungeonEncounterId; } void SetDungeonEncounterId(uint32 dungeonEncounterId) { _dungeonEncounterId = dungeonEncounterId; } bool isLooted() const { return gold == 0 && unlootedCount == 0; } bool IsChanged() const { return _changed; } void NotifyLootList(Map const* map) const; void NotifyItemRemoved(uint8 lootListId, Map const* map); void NotifyMoneyRemoved(Map const* map); void OnLootOpened(Map* map, Player* looter); void AddLooter(ObjectGuid GUID) { PlayersLooting.insert(GUID); } void RemoveLooter(ObjectGuid GUID) { PlayersLooting.erase(GUID); } bool HasAllowedLooter(ObjectGuid const& looter) const; void generateMoneyLoot(uint32 minAmount, uint32 maxAmount); bool FillLoot(uint32 lootId, LootStore const& store, Player* lootOwner, bool personal, bool noEmptyError = false, uint16 lootMode = LOOT_MODE_DEFAULT, ItemContext context = ItemContext::NONE); void FillNotNormalLootFor(Player* player); // count unlooted items // Inserts the item into the loot (called by LootTemplate processors) void AddItem(LootStoreItem const& item); bool AutoStore(Player* player, uint8 bag, uint8 slot, bool broadcast = false, bool createdByPlayer = false); void AutoStoreTrackingQuests(Player* player, NotNormalLootItemList& ffaItems); void LootMoney(); LootItem const* GetItemInSlot(uint32 lootListId) const; LootItem* LootItemInSlot(uint32 lootListId, Player const* player, NotNormalLootItem** ffaItem = nullptr); bool hasItemForAll() const; bool hasItemFor(Player const* player) const; bool hasOverThresholdItem() const; // Builds data for SMSG_LOOT_RESPONSE void BuildLootResponse(WorldPackets::Loot::LootResponse& packet, Player const* viewer) const; void Update(); private: GuidSet PlayersLooting; NotNormalLootItemMap PlayerFFAItems; // Loot GUID ObjectGuid _guid; ObjectGuid _owner; // The WorldObject that holds this loot ItemContext _itemContext; LootMethod _lootMethod; std::unordered_map _rolls; // used if an item is under rolling ObjectGuid _lootMaster; GuidUnorderedSet _allowedLooters; bool _wasOpened; // true if at least one player received the loot content bool _changed; uint32 _dungeonEncounterId; }; class TC_GAME_API AELootResult { public: struct ResultValue { Item* item; uint8 count; LootType lootType; uint32 dungeonEncounterId; }; typedef std::vector OrderedStorage; void Add(Item* item, uint8 count, LootType lootType, uint32 dungeonEncounterId); OrderedStorage::const_iterator begin() const; OrderedStorage::const_iterator end() const; OrderedStorage _byOrder; std::unordered_map _byItem; }; #endif // Loot_h__