diff options
| author | Shauren <shauren.trinity@gmail.com> | 2017-05-18 23:52:58 +0200 |
|---|---|---|
| committer | Shauren <shauren.trinity@gmail.com> | 2017-05-18 23:53:25 +0200 |
| commit | c5d3dd90bea3889ef5fcd33c9ef0d59d7c544f8a (patch) | |
| tree | aa7fde6f924fc39da54908bd6eeeb0be422e5fc3 /src/server/game/Loot | |
| parent | 74456703146194de72424ec98c4ea76402077be6 (diff) | |
Core/Game: Include cleanup
* Mostly aimed at removing Log/DatabaseEnv includes from other headers
* Fix most packet headers including other packet headers - moved common structures such as ItemInstance to their own files
* Moved SAI function definitions to source files (massive or requiring many different dependencies)
Diffstat (limited to 'src/server/game/Loot')
| -rw-r--r-- | src/server/game/Loot/Loot.cpp | 828 | ||||
| -rw-r--r-- | src/server/game/Loot/Loot.h | 314 | ||||
| -rw-r--r-- | src/server/game/Loot/LootMgr.cpp | 782 | ||||
| -rw-r--r-- | src/server/game/Loot/LootMgr.h | 324 |
4 files changed, 1162 insertions, 1086 deletions
diff --git a/src/server/game/Loot/Loot.cpp b/src/server/game/Loot/Loot.cpp new file mode 100644 index 00000000000..406c616ccc0 --- /dev/null +++ b/src/server/game/Loot/Loot.cpp @@ -0,0 +1,828 @@ +/* + * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "Loot.h" +#include "DatabaseEnv.h" +#include "DB2Stores.h" +#include "Group.h" +#include "ItemTemplate.h" +#include "Log.h" +#include "LootMgr.h" +#include "LootPackets.h" +#include "ObjectAccessor.h" +#include "ObjectMgr.h" +#include "Player.h" +#include "Random.h" +#include "World.h" + +// +// --------- LootItem --------- +// + +// Constructor, copies most fields from LootStoreItem and generates random count +LootItem::LootItem(LootStoreItem const& li) +{ + itemid = li.itemid; + conditions = li.conditions; + + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemid); + freeforall = proto && (proto->GetFlags() & ITEM_FLAG_MULTI_DROP); + follow_loot_rules = proto && (proto->FlagsCu & ITEM_FLAGS_CU_FOLLOW_LOOT_RULES); + + needs_quest = li.needs_quest; + + randomSuffix = GenerateEnchSuffixFactor(itemid); + randomPropertyId = GenerateItemRandomPropertyId(itemid); + upgradeId = sDB2Manager.GetRulesetItemUpgrade(itemid); + context = 0; + count = 0; + is_looted = 0; + is_blocked = 0; + is_underthreshold = 0; + is_counted = 0; + canSave = true; +} + +// Basic checks for player/item compatibility - if false no chance to see the item in the loot +bool LootItem::AllowedForPlayer(Player const* player) const +{ + // DB conditions check + if (!sConditionMgr->IsObjectMeetToConditions(const_cast<Player*>(player), conditions)) + return false; + + ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(itemid); + if (!pProto) + return false; + + // not show loot for players without profession or those who already know the recipe + if ((pProto->GetFlags() & ITEM_FLAG_HIDE_UNUSABLE_RECIPE) && (!player->HasSkill(pProto->GetRequiredSkill()) || player->HasSpell(pProto->Effects[1]->SpellID))) + return false; + + // not show loot for not own team + if ((pProto->GetFlags2() & ITEM_FLAG2_FACTION_HORDE) && player->GetTeam() != HORDE) + return false; + + if ((pProto->GetFlags2() & ITEM_FLAG2_FACTION_ALLIANCE) && player->GetTeam() != ALLIANCE) + return false; + + // check quest requirements + if (!(pProto->FlagsCu & ITEM_FLAGS_CU_IGNORE_QUEST_STATUS) && ((needs_quest || (pProto->GetStartQuest() && player->GetQuestStatus(pProto->GetStartQuest()) != QUEST_STATUS_NONE)) && !player->HasQuestForItem(itemid))) + return false; + + // Don't show bind-when-picked-up unique items if player already has the maximum allowed quantity. + if (pProto->GetBonding() == BIND_ON_ACQUIRE && pProto->GetMaxCount() && player->GetItemCount(itemid, true) >= pProto->GetMaxCount()) + return false; + + return true; +} + +void LootItem::AddAllowedLooter(const Player* player) +{ + allowedGUIDs.insert(player->GetGUID()); +} + +// +// --------- Loot --------- +// + +Loot::Loot(uint32 _gold /*= 0*/) : gold(_gold), unlootedCount(0), roundRobinPlayer(), loot_type(LOOT_CORPSE), maxDuplicates(1), _difficultyBonusTreeMod(0) +{ +} + +Loot::~Loot() +{ + clear(); +} + +void Loot::DeleteLootItemFromContainerItemDB(uint32 itemID) +{ + // Deletes a single item associated with an openable item from the DB + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_ITEM); + stmt->setUInt64(0, containerID.GetCounter()); + stmt->setUInt32(1, itemID); + CharacterDatabase.Execute(stmt); + + // Mark the item looted to prevent resaving + for (LootItemList::iterator _itr = items.begin(); _itr != items.end(); ++_itr) + { + if (_itr->itemid != itemID) + continue; + + _itr->canSave = false; + break; + } +} + +void Loot::DeleteLootMoneyFromContainerItemDB() +{ + // Deletes money loot associated with an openable item from the DB + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_MONEY); + stmt->setUInt64(0, containerID.GetCounter()); + CharacterDatabase.Execute(stmt); +} + +void Loot::clear() +{ + for (QuestItemMap::const_iterator itr = PlayerQuestItems.begin(); itr != PlayerQuestItems.end(); ++itr) + delete itr->second; + PlayerQuestItems.clear(); + + for (QuestItemMap::const_iterator itr = PlayerFFAItems.begin(); itr != PlayerFFAItems.end(); ++itr) + delete itr->second; + PlayerFFAItems.clear(); + + for (QuestItemMap::const_iterator itr = PlayerNonQuestNonFFAConditionalItems.begin(); itr != PlayerNonQuestNonFFAConditionalItems.end(); ++itr) + delete itr->second; + PlayerNonQuestNonFFAConditionalItems.clear(); + + PlayersLooting.clear(); + items.clear(); + quest_items.clear(); + gold = 0; + unlootedCount = 0; + roundRobinPlayer.Clear(); + loot_type = LOOT_NONE; + i_LootValidatorRefManager.clearReferences(); + _difficultyBonusTreeMod = 0; +} + +void Loot::NotifyItemRemoved(uint8 lootIndex) +{ + // notify all players that are looting this that the item was removed + // convert the index to the slot the player sees + GuidSet::iterator i_next; + for (GuidSet::iterator i = PlayersLooting.begin(); i != PlayersLooting.end(); i = i_next) + { + i_next = i; + ++i_next; + if (Player* player = ObjectAccessor::FindPlayer(*i)) + player->SendNotifyLootItemRemoved(GetGUID(), lootIndex); + else + PlayersLooting.erase(i); + } +} + +void Loot::NotifyQuestItemRemoved(uint8 questIndex) +{ + // when a free for all questitem is looted + // all players will get notified of it being removed + // (other questitems can be looted by each group member) + // bit inefficient but isn't called often + + GuidSet::iterator i_next; + for (GuidSet::iterator i = PlayersLooting.begin(); i != PlayersLooting.end(); i = i_next) + { + i_next = i; + ++i_next; + if (Player* player = ObjectAccessor::FindPlayer(*i)) + { + QuestItemMap::const_iterator pq = PlayerQuestItems.find(player->GetGUID()); + if (pq != PlayerQuestItems.end() && pq->second) + { + // find where/if the player has the given item in it's vector + QuestItemList& pql = *pq->second; + + uint8 j; + for (j = 0; j < pql.size(); ++j) + if (pql[j].index == questIndex) + break; + + if (j < pql.size()) + player->SendNotifyLootItemRemoved(GetGUID(), items.size() + j); + } + } + else + PlayersLooting.erase(i); + } +} + +void Loot::NotifyMoneyRemoved() +{ + // notify all players that are looting this that the money was removed + GuidSet::iterator i_next; + for (GuidSet::iterator i = PlayersLooting.begin(); i != PlayersLooting.end(); i = i_next) + { + i_next = i; + ++i_next; + if (Player* player = ObjectAccessor::FindPlayer(*i)) + player->SendNotifyLootMoneyRemoved(GetGUID()); + else + PlayersLooting.erase(i); + } +} + +void Loot::generateMoneyLoot(uint32 minAmount, uint32 maxAmount) +{ + if (maxAmount > 0) + { + if (maxAmount <= minAmount) + gold = uint32(maxAmount * sWorld->getRate(RATE_DROP_MONEY)); + else if ((maxAmount - minAmount) < 32700) + gold = uint32(urand(minAmount, maxAmount) * sWorld->getRate(RATE_DROP_MONEY)); + else + gold = uint32(urand(minAmount >> 8, maxAmount >> 8) * sWorld->getRate(RATE_DROP_MONEY)) << 8; + } +} + +// Calls processor of corresponding LootTemplate (which handles everything including references) +bool Loot::FillLoot(uint32 lootId, LootStore const& store, Player* lootOwner, bool personal, bool noEmptyError, uint16 lootMode /*= LOOT_MODE_DEFAULT*/) +{ + // Must be provided + if (!lootOwner) + return false; + + LootTemplate const* tab = store.GetLootFor(lootId); + + if (!tab) + { + if (!noEmptyError) + TC_LOG_ERROR("sql.sql", "Table '%s' loot id #%u used but it doesn't have records.", store.GetName(), lootId); + return false; + } + + _difficultyBonusTreeMod = lootOwner->GetMap()->GetDifficultyLootBonusTreeMod(); + + items.reserve(MAX_NR_LOOT_ITEMS); + quest_items.reserve(MAX_NR_QUEST_ITEMS); + + tab->Process(*this, store.IsRatesAllowed(), lootMode); // Processing is done there, callback via Loot::AddItem() + + // Setting access rights for group loot case + Group* group = lootOwner->GetGroup(); + if (!personal && group) + { + roundRobinPlayer = lootOwner->GetGUID(); + + for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next()) + if (Player* player = itr->GetSource()) // should actually be looted object instead of lootOwner but looter has to be really close so doesnt really matter + FillNotNormalLootFor(player, player->IsAtGroupRewardDistance(lootOwner)); + + for (uint8 i = 0; i < items.size(); ++i) + { + if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(items[i].itemid)) + if (proto->GetQuality() < uint32(group->GetLootThreshold())) + items[i].is_underthreshold = true; + } + } + // ... for personal loot + else + FillNotNormalLootFor(lootOwner, true); + + return true; +} + +// Inserts the item into the loot (called by LootTemplate processors) +void Loot::AddItem(LootStoreItem const& item) +{ + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item.itemid); + if (!proto) + return; + + uint32 count = urand(item.mincount, item.maxcount); + uint32 stacks = count / proto->GetMaxStackSize() + ((count % proto->GetMaxStackSize()) ? 1 : 0); + + std::vector<LootItem>& lootItems = item.needs_quest ? quest_items : items; + uint32 limit = item.needs_quest ? MAX_NR_QUEST_ITEMS : MAX_NR_LOOT_ITEMS; + + for (uint32 i = 0; i < stacks && lootItems.size() < limit; ++i) + { + LootItem generatedLoot(item); + generatedLoot.context = _difficultyBonusTreeMod; + generatedLoot.count = std::min(count, proto->GetMaxStackSize()); + if (_difficultyBonusTreeMod) + { + std::set<uint32> bonusListIDs = sDB2Manager.GetItemBonusTree(generatedLoot.itemid, _difficultyBonusTreeMod); + generatedLoot.BonusListIDs.insert(generatedLoot.BonusListIDs.end(), bonusListIDs.begin(), bonusListIDs.end()); + } + + lootItems.push_back(generatedLoot); + count -= proto->GetMaxStackSize(); + + // non-conditional one-player only items are counted here, + // free for all items are counted in FillFFALoot(), + // non-ffa conditionals are counted in FillNonQuestNonFFAConditionalLoot() + if (!item.needs_quest && item.conditions.empty() && !(proto->GetFlags() & ITEM_FLAG_MULTI_DROP)) + ++unlootedCount; + } +} + +LootItem const* Loot::GetItemInSlot(uint32 lootSlot) const +{ + if (lootSlot < items.size()) + return &items[lootSlot]; + + lootSlot -= uint32(items.size()); + if (lootSlot < quest_items.size()) + return &quest_items[lootSlot]; + + return nullptr; +} + +LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem* *qitem, QuestItem* *ffaitem, QuestItem* *conditem) +{ + LootItem* item = NULL; + bool is_looted = true; + if (lootSlot >= items.size()) + { + uint32 questSlot = lootSlot - items.size(); + QuestItemMap::const_iterator itr = PlayerQuestItems.find(player->GetGUID()); + if (itr != PlayerQuestItems.end() && questSlot < itr->second->size()) + { + QuestItem* qitem2 = &itr->second->at(questSlot); + if (qitem) + *qitem = qitem2; + item = &quest_items[qitem2->index]; + is_looted = qitem2->is_looted; + } + } + else + { + item = &items[lootSlot]; + is_looted = item->is_looted; + if (item->freeforall) + { + QuestItemMap::const_iterator itr = PlayerFFAItems.find(player->GetGUID()); + if (itr != PlayerFFAItems.end()) + { + for (QuestItemList::const_iterator iter = itr->second->begin(); iter != itr->second->end(); ++iter) + if (iter->index == lootSlot) + { + QuestItem* ffaitem2 = (QuestItem*)&(*iter); + if (ffaitem) + *ffaitem = ffaitem2; + is_looted = ffaitem2->is_looted; + break; + } + } + } + else if (!item->conditions.empty()) + { + QuestItemMap::const_iterator itr = PlayerNonQuestNonFFAConditionalItems.find(player->GetGUID()); + if (itr != PlayerNonQuestNonFFAConditionalItems.end()) + { + for (QuestItemList::const_iterator iter = itr->second->begin(); iter != itr->second->end(); ++iter) + { + if (iter->index == lootSlot) + { + QuestItem* conditem2 = (QuestItem*)&(*iter); + if (conditem) + *conditem = conditem2; + is_looted = conditem2->is_looted; + break; + } + } + } + } + } + + if (is_looted) + return NULL; + + return item; +} + +uint32 Loot::GetMaxSlotInLootFor(Player* player) const +{ + QuestItemMap::const_iterator itr = PlayerQuestItems.find(player->GetGUID()); + return items.size() + (itr != PlayerQuestItems.end() ? itr->second->size() : 0); +} + +// return true if there is any item that is lootable for any player (not quest item, FFA or conditional) +bool Loot::hasItemForAll() const +{ + // Gold is always lootable + if (gold) + return true; + + for (LootItem const& item : items) + if (!item.is_looted && !item.freeforall && item.conditions.empty()) + return true; + return false; +} + +// return true if there is any FFA, quest or conditional item for the player. +bool Loot::hasItemFor(Player* player) const +{ + QuestItemMap const& lootPlayerQuestItems = GetPlayerQuestItems(); + QuestItemMap::const_iterator q_itr = lootPlayerQuestItems.find(player->GetGUID()); + if (q_itr != lootPlayerQuestItems.end()) + { + QuestItemList* q_list = q_itr->second; + for (QuestItemList::const_iterator qi = q_list->begin(); qi != q_list->end(); ++qi) + { + const LootItem &item = quest_items[qi->index]; + if (!qi->is_looted && !item.is_looted) + return true; + } + } + + QuestItemMap const& lootPlayerFFAItems = GetPlayerFFAItems(); + QuestItemMap::const_iterator ffa_itr = lootPlayerFFAItems.find(player->GetGUID()); + if (ffa_itr != lootPlayerFFAItems.end()) + { + QuestItemList* ffa_list = ffa_itr->second; + for (QuestItemList::const_iterator fi = ffa_list->begin(); fi != ffa_list->end(); ++fi) + { + const LootItem &item = items[fi->index]; + if (!fi->is_looted && !item.is_looted) + return true; + } + } + + QuestItemMap const& lootPlayerNonQuestNonFFAConditionalItems = GetPlayerNonQuestNonFFAConditionalItems(); + QuestItemMap::const_iterator nn_itr = lootPlayerNonQuestNonFFAConditionalItems.find(player->GetGUID()); + if (nn_itr != lootPlayerNonQuestNonFFAConditionalItems.end()) + { + QuestItemList* conditional_list = nn_itr->second; + for (QuestItemList::const_iterator ci = conditional_list->begin(); ci != conditional_list->end(); ++ci) + { + const LootItem &item = items[ci->index]; + if (!ci->is_looted && !item.is_looted) + return true; + } + } + + return false; +} + +// return true if there is any item over the group threshold (i.e. not underthreshold). +bool Loot::hasOverThresholdItem() const +{ + for (uint8 i = 0; i < items.size(); ++i) + { + if (!items[i].is_looted && !items[i].is_underthreshold && !items[i].freeforall) + return true; + } + + return false; +} + +void Loot::BuildLootResponse(WorldPackets::Loot::LootResponse& packet, Player* viewer, PermissionTypes permission) const +{ + if (permission == NONE_PERMISSION) + return; + + packet.Coins = gold; + + switch (permission) + { + case GROUP_PERMISSION: + case MASTER_PERMISSION: + case RESTRICTED_PERMISSION: + { + // if you are not the round-robin group looter, you can only see + // blocked rolled items and quest items, and !ffa items + for (uint8 i = 0; i < items.size(); ++i) + { + if (!items[i].is_looted && !items[i].freeforall && items[i].conditions.empty() && items[i].AllowedForPlayer(viewer)) + { + uint8 slot_type; + + if (items[i].is_blocked) // for ML & restricted is_blocked = !is_underthreshold + { + switch (permission) + { + case GROUP_PERMISSION: + slot_type = LOOT_SLOT_TYPE_ROLL_ONGOING; + break; + case MASTER_PERMISSION: + { + if (viewer->GetGroup() && viewer->GetGroup()->GetMasterLooterGuid() == viewer->GetGUID()) + slot_type = LOOT_SLOT_TYPE_MASTER; + else + slot_type = LOOT_SLOT_TYPE_LOCKED; + break; + } + case RESTRICTED_PERMISSION: + slot_type = LOOT_SLOT_TYPE_LOCKED; + break; + default: + continue; + } + } + else if (roundRobinPlayer.IsEmpty() || viewer->GetGUID() == roundRobinPlayer || !items[i].is_underthreshold) + { + // no round robin owner or he has released the loot + // or it IS the round robin group owner + // => item is lootable + slot_type = LOOT_SLOT_TYPE_ALLOW_LOOT; + } + else + // item shall not be displayed. + continue; + + WorldPackets::Loot::LootItemData lootItem; + lootItem.LootListID = i + 1; + lootItem.UIType = slot_type; + lootItem.Quantity = items[i].count; + lootItem.Loot.Initialize(items[i]); + packet.Items.push_back(lootItem); + } + } + break; + } + case ALL_PERMISSION: + case OWNER_PERMISSION: + { + for (uint8 i = 0; i < items.size(); ++i) + { + if (!items[i].is_looted && !items[i].freeforall && items[i].conditions.empty() && items[i].AllowedForPlayer(viewer)) + { + WorldPackets::Loot::LootItemData lootItem; + lootItem.LootListID = i + 1; + lootItem.UIType = permission == OWNER_PERMISSION ? LOOT_SLOT_TYPE_OWNER : LOOT_SLOT_TYPE_ALLOW_LOOT; + lootItem.Quantity = items[i].count; + lootItem.Loot.Initialize(items[i]); + packet.Items.push_back(lootItem); + } + } + break; + } + default: + return; + } + + LootSlotType slotType = permission == OWNER_PERMISSION ? LOOT_SLOT_TYPE_OWNER : LOOT_SLOT_TYPE_ALLOW_LOOT; + QuestItemMap const& lootPlayerQuestItems = GetPlayerQuestItems(); + QuestItemMap::const_iterator q_itr = lootPlayerQuestItems.find(viewer->GetGUID()); + if (q_itr != lootPlayerQuestItems.end()) + { + QuestItemList* q_list = q_itr->second; + for (QuestItemList::const_iterator qi = q_list->begin(); qi != q_list->end(); ++qi) + { + LootItem const& item = quest_items[qi->index]; + if (!qi->is_looted && !item.is_looted) + { + WorldPackets::Loot::LootItemData lootItem; + lootItem.LootListID = items.size() + qi->index + 1; + lootItem.Quantity = item.count; + lootItem.Loot.Initialize(item); + + if (item.follow_loot_rules) + { + switch (permission) + { + case MASTER_PERMISSION: + lootItem.UIType = LOOT_SLOT_TYPE_MASTER; + break; + case RESTRICTED_PERMISSION: + lootItem.UIType = item.is_blocked ? LOOT_SLOT_TYPE_LOCKED : LOOT_SLOT_TYPE_ALLOW_LOOT; + break; + case GROUP_PERMISSION: + if (!item.is_blocked) + lootItem.UIType = LOOT_SLOT_TYPE_ALLOW_LOOT; + else + lootItem.UIType = LOOT_SLOT_TYPE_ROLL_ONGOING; + break; + default: + lootItem.UIType = slotType; + break; + } + } + else + lootItem.UIType = slotType; + + packet.Items.push_back(lootItem); + } + } + } + + QuestItemMap const& lootPlayerFFAItems = GetPlayerFFAItems(); + QuestItemMap::const_iterator ffa_itr = lootPlayerFFAItems.find(viewer->GetGUID()); + if (ffa_itr != lootPlayerFFAItems.end()) + { + QuestItemList* ffa_list = ffa_itr->second; + for (QuestItemList::const_iterator fi = ffa_list->begin(); fi != ffa_list->end(); ++fi) + { + LootItem const& item = items[fi->index]; + if (!fi->is_looted && !item.is_looted) + { + WorldPackets::Loot::LootItemData lootItem; + lootItem.LootListID = items.size() + fi->index + 1; + lootItem.UIType = slotType; + lootItem.Quantity = item.count; + lootItem.Loot.Initialize(item); + packet.Items.push_back(lootItem); + } + } + } + + QuestItemMap const& lootPlayerNonQuestNonFFAConditionalItems = GetPlayerNonQuestNonFFAConditionalItems(); + QuestItemMap::const_iterator nn_itr = lootPlayerNonQuestNonFFAConditionalItems.find(viewer->GetGUID()); + if (nn_itr != lootPlayerNonQuestNonFFAConditionalItems.end()) + { + QuestItemList* conditional_list = nn_itr->second; + for (QuestItemList::const_iterator ci = conditional_list->begin(); ci != conditional_list->end(); ++ci) + { + LootItem const& item = items[ci->index]; + if (!ci->is_looted && !item.is_looted) + { + WorldPackets::Loot::LootItemData lootItem; + lootItem.LootListID = items.size() + ci->index + 1; + lootItem.Quantity = item.count; + lootItem.Loot.Initialize(item); + + if (item.follow_loot_rules) + { + switch (permission) + { + case MASTER_PERMISSION: + lootItem.UIType = LOOT_SLOT_TYPE_MASTER; + break; + case RESTRICTED_PERMISSION: + lootItem.UIType = item.is_blocked ? LOOT_SLOT_TYPE_LOCKED : LOOT_SLOT_TYPE_ALLOW_LOOT; + break; + case GROUP_PERMISSION: + if (!item.is_blocked) + lootItem.UIType = LOOT_SLOT_TYPE_ALLOW_LOOT; + else + lootItem.UIType = LOOT_SLOT_TYPE_ROLL_ONGOING; + break; + default: + lootItem.UIType = slotType; + break; + } + } + else + lootItem.UIType = slotType; + + packet.Items.push_back(lootItem); + } + } + } +} + +void Loot::FillNotNormalLootFor(Player* player, bool presentAtLooting) +{ + ObjectGuid plguid = player->GetGUID(); + + QuestItemMap::const_iterator qmapitr = PlayerQuestItems.find(plguid); + if (qmapitr == PlayerQuestItems.end()) + FillQuestLoot(player); + + qmapitr = PlayerFFAItems.find(plguid); + if (qmapitr == PlayerFFAItems.end()) + FillFFALoot(player); + + qmapitr = PlayerNonQuestNonFFAConditionalItems.find(plguid); + if (qmapitr == PlayerNonQuestNonFFAConditionalItems.end()) + FillNonQuestNonFFAConditionalLoot(player, presentAtLooting); + + // if not auto-processed player will have to come and pick it up manually + if (!presentAtLooting) + return; + + // Process currency items + uint32 max_slot = GetMaxSlotInLootFor(player); + LootItem const* item = NULL; + uint32 itemsSize = uint32(items.size()); + for (uint32 i = 0; i < max_slot; ++i) + { + if (i < items.size()) + item = &items[i]; + else + item = &quest_items[i - itemsSize]; + + if (!item->is_looted && item->freeforall && item->AllowedForPlayer(player)) + if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item->itemid)) + if (proto->IsCurrencyToken()) + player->StoreLootItem(i, this); + } +} + +QuestItemList* Loot::FillFFALoot(Player* player) +{ + QuestItemList* ql = new QuestItemList(); + + for (uint8 i = 0; i < items.size(); ++i) + { + LootItem &item = items[i]; + if (!item.is_looted && item.freeforall && item.AllowedForPlayer(player)) + { + ql->push_back(QuestItem(i)); + ++unlootedCount; + } + } + if (ql->empty()) + { + delete ql; + return NULL; + } + + PlayerFFAItems[player->GetGUID()] = ql; + return ql; +} + +QuestItemList* Loot::FillQuestLoot(Player* player) +{ + if (items.size() == MAX_NR_LOOT_ITEMS) + return NULL; + + QuestItemList* ql = new QuestItemList(); + + for (uint8 i = 0; i < quest_items.size(); ++i) + { + LootItem &item = quest_items[i]; + + if (!item.is_looted && (item.AllowedForPlayer(player) || (item.follow_loot_rules && player->GetGroup() && ((player->GetGroup()->GetLootMethod() == MASTER_LOOT && player->GetGroup()->GetMasterLooterGuid() == player->GetGUID()) || player->GetGroup()->GetLootMethod() != MASTER_LOOT)))) + { + ql->push_back(QuestItem(i)); + + // quest items get blocked when they first appear in a + // player's quest vector + // + // increase once if one looter only, looter-times if free for all + if (item.freeforall || !item.is_blocked) + ++unlootedCount; + if (!player->GetGroup() || (player->GetGroup()->GetLootMethod() != GROUP_LOOT)) + item.is_blocked = true; + + if (items.size() + ql->size() == MAX_NR_LOOT_ITEMS) + break; + } + } + if (ql->empty()) + { + delete ql; + return NULL; + } + + PlayerQuestItems[player->GetGUID()] = ql; + return ql; +} + +QuestItemList* Loot::FillNonQuestNonFFAConditionalLoot(Player* player, bool presentAtLooting) +{ + QuestItemList* ql = new QuestItemList(); + + for (uint8 i = 0; i < items.size(); ++i) + { + LootItem &item = items[i]; + if (!item.is_looted && !item.freeforall && (item.AllowedForPlayer(player) || (item.follow_loot_rules && player->GetGroup() && ((player->GetGroup()->GetLootMethod() == MASTER_LOOT && player->GetGroup()->GetMasterLooterGuid() == player->GetGUID()) || player->GetGroup()->GetLootMethod() != MASTER_LOOT)))) + { + if (presentAtLooting) + item.AddAllowedLooter(player); + if (!item.conditions.empty()) + { + ql->push_back(QuestItem(i)); + if (!item.is_counted) + { + ++unlootedCount; + item.is_counted = true; + } + } + } + } + if (ql->empty()) + { + delete ql; + return NULL; + } + + PlayerNonQuestNonFFAConditionalItems[player->GetGUID()] = ql; + return ql; +} + +// +// --------- AELootResult --------- +// + +void AELootResult::Add(Item* item, uint8 count, LootType lootType) +{ + auto itr = _byItem.find(item); + if (itr != _byItem.end()) + _byOrder[itr->second].count += count; + else + { + _byItem[item] = _byOrder.size(); + ResultValue value; + value.item = item; + value.count = count; + value.lootType = lootType; + _byOrder.push_back(value); + } +} + +AELootResult::OrderedStorage::const_iterator AELootResult::begin() const +{ + return _byOrder.begin(); +} + +AELootResult::OrderedStorage::const_iterator AELootResult::end() const +{ + return _byOrder.end(); +} diff --git a/src/server/game/Loot/Loot.h b/src/server/game/Loot/Loot.h new file mode 100644 index 00000000000..64268ff64b8 --- /dev/null +++ b/src/server/game/Loot/Loot.h @@ -0,0 +1,314 @@ +/* + * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef Loot_h__ +#define Loot_h__ + +#include "Define.h" +#include "ConditionMgr.h" +#include "ItemEnchantmentMgr.h" +#include "ObjectGuid.h" +#include "RefManager.h" +#include "SharedDefines.h" +#include <unordered_map> +#include <vector> + +class Item; +class LootStore; +class Player; +struct Loot; +struct LootStoreItem; + +namespace WorldPackets +{ + namespace Loot + { + class LootResponse; + } +} + +enum RollType +{ + ROLL_PASS = 0, + ROLL_NEED = 1, + ROLL_GREED = 2, + ROLL_DISENCHANT = 3, + MAX_ROLL_TYPE = 4 +}; + +enum RollMask +{ + ROLL_FLAG_TYPE_PASS = 0x01, + ROLL_FLAG_TYPE_NEED = 0x02, + ROLL_FLAG_TYPE_GREED = 0x04, + ROLL_FLAG_TYPE_DISENCHANT = 0x08, + + ROLL_ALL_TYPE_NO_DISENCHANT = 0x07, + ROLL_ALL_TYPE_MASK = 0x0F +}; + +#define MAX_NR_LOOT_ITEMS 16 +// note: the client cannot show more than 16 items total +#define MAX_NR_QUEST_ITEMS 32 +// unrelated to the number of quest items shown, just for reserve + +enum LootMethod : uint8 +{ + FREE_FOR_ALL = 0, + MASTER_LOOT = 2, + GROUP_LOOT = 3, + PERSONAL_LOOT = 5 +}; + +enum PermissionTypes +{ + ALL_PERMISSION = 0, + GROUP_PERMISSION = 1, + MASTER_PERMISSION = 2, + RESTRICTED_PERMISSION = 3, + OWNER_PERMISSION = 5, + NONE_PERMISSION = 6 +}; + +enum LootType : uint8 +{ + LOOT_NONE = 0, + + LOOT_CORPSE = 1, + LOOT_PICKPOCKETING = 2, + LOOT_FISHING = 3, + LOOT_DISENCHANTING = 4, + // ignored always by client + LOOT_SKINNING = 6, + LOOT_PROSPECTING = 7, + LOOT_MILLING = 8, + + 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 +}; + +enum LootError +{ + 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 +}; + +struct TC_GAME_API LootItem +{ + uint32 itemid; + uint32 randomSuffix; + ItemRandomEnchantmentId randomPropertyId; + int32 upgradeId; + std::vector<int32> BonusListIDs; + uint8 context; + ConditionContainer conditions; // additional loot condition + GuidSet allowedGUIDs; + uint8 count : 8; + bool is_looted : 1; + bool is_blocked : 1; + bool freeforall : 1; // free for all + bool is_underthreshold : 1; + bool is_counted : 1; + bool needs_quest : 1; // quest drop + bool follow_loot_rules : 1; + bool canSave; + + // Constructor, copies most fields from LootStoreItem, generates random count and random suffixes/properties + // Should be called for non-reference LootStoreItem entries only (reference = 0) + explicit LootItem(LootStoreItem const& li); + + // Empty constructor for creating an empty LootItem to be filled in with DB data + LootItem() : itemid(0), randomSuffix(0), randomPropertyId(), upgradeId(0), context(0), count(0), is_looted(false), is_blocked(false), + freeforall(false), is_underthreshold(false), is_counted(false), needs_quest(false), follow_loot_rules(false), + canSave(true){ }; + + // Basic checks for player/item compatibility - if false no chance to see the item in the loot + bool AllowedForPlayer(Player const* player) const; + void AddAllowedLooter(Player const* player); + GuidSet const& GetAllowedLooters() const { return allowedGUIDs; } +}; + +struct QuestItem +{ + uint8 index; // position in quest_items; + bool is_looted; + + QuestItem() + : index(0), is_looted(false) { } + + QuestItem(uint8 _index, bool _islooted = false) + : index(_index), is_looted(_islooted) { } +}; + +typedef std::vector<QuestItem> QuestItemList; +typedef std::vector<LootItem> LootItemList; +typedef std::unordered_map<ObjectGuid, QuestItemList*> QuestItemMap; + +//===================================================== + +class LootValidatorRef : public Reference<Loot, LootValidatorRef> +{ +public: + LootValidatorRef() { } + void targetObjectDestroyLink() override { } + void sourceObjectDestroyLink() override { } +}; + +//===================================================== + +class LootValidatorRefManager : public RefManager<Loot, LootValidatorRef> +{ +public: + typedef LinkedListHead::Iterator<LootValidatorRef> iterator; + + LootValidatorRef* getFirst() { return (LootValidatorRef*)RefManager<Loot, LootValidatorRef>::getFirst(); } + LootValidatorRef* getLast() { return (LootValidatorRef*)RefManager<Loot, LootValidatorRef>::getLast(); } + + iterator begin() { return iterator(getFirst()); } + iterator end() { return iterator(NULL); } + iterator rbegin() { return iterator(getLast()); } + iterator rend() { return iterator(NULL); } +}; + +//===================================================== + +struct TC_GAME_API Loot +{ + QuestItemMap const& GetPlayerQuestItems() const { return PlayerQuestItems; } + QuestItemMap const& GetPlayerFFAItems() const { return PlayerFFAItems; } + QuestItemMap const& GetPlayerNonQuestNonFFAConditionalItems() const { return PlayerNonQuestNonFFAConditionalItems; } + + std::vector<LootItem> items; + std::vector<LootItem> quest_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 + uint8 maxDuplicates; // Max amount of items with the same entry that can drop (default is 1; on 25 man raid mode 3) + + // GUID of container that holds this loot (item_instance.entry) + // Only set for inventory items that can be right-click looted + ObjectGuid containerID; + + Loot(uint32 _gold = 0); + ~Loot(); + + ObjectGuid const& GetGUID() const { return _GUID; } + void SetGUID(ObjectGuid const& guid) { _GUID = guid; } + + // For deleting items at loot removal since there is no backward interface to the Item() + void DeleteLootItemFromContainerItemDB(uint32 itemID); + void DeleteLootMoneyFromContainerItemDB(); + + // if loot becomes invalid this reference is used to inform the listener + void addLootValidatorRef(LootValidatorRef* pLootValidatorRef) + { + i_LootValidatorRefManager.insertFirst(pLootValidatorRef); + } + + void clear(); + + bool empty() const { return items.empty() && gold == 0; } + bool isLooted() const { return gold == 0 && unlootedCount == 0; } + + void NotifyItemRemoved(uint8 lootIndex); + void NotifyQuestItemRemoved(uint8 questIndex); + void NotifyMoneyRemoved(); + void AddLooter(ObjectGuid GUID) { PlayersLooting.insert(GUID); } + void RemoveLooter(ObjectGuid GUID) { PlayersLooting.erase(GUID); } + + 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); + + // Inserts the item into the loot (called by LootTemplate processors) + void AddItem(LootStoreItem const & item); + + LootItem const* GetItemInSlot(uint32 lootSlot) const; + LootItem* LootItemInSlot(uint32 lootslot, Player* player, QuestItem** qitem = NULL, QuestItem** ffaitem = NULL, QuestItem** conditem = NULL); + uint32 GetMaxSlotInLootFor(Player* player) const; + bool hasItemForAll() const; + bool hasItemFor(Player* player) const; + bool hasOverThresholdItem() const; + + // Builds data for SMSG_LOOT_RESPONSE + void BuildLootResponse(WorldPackets::Loot::LootResponse& packet, Player* viewer, PermissionTypes permission = ALL_PERMISSION) const; + +private: + + void FillNotNormalLootFor(Player* player, bool presentAtLooting); + QuestItemList* FillFFALoot(Player* player); + QuestItemList* FillQuestLoot(Player* player); + QuestItemList* FillNonQuestNonFFAConditionalLoot(Player* player, bool presentAtLooting); + + GuidSet PlayersLooting; + QuestItemMap PlayerQuestItems; + QuestItemMap PlayerFFAItems; + QuestItemMap PlayerNonQuestNonFFAConditionalItems; + + // All rolls are registered here. They need to know, when the loot is not valid anymore + LootValidatorRefManager i_LootValidatorRefManager; + + // Loot GUID + ObjectGuid _GUID; + uint8 _difficultyBonusTreeMod; +}; + +class TC_GAME_API AELootResult +{ +public: + struct ResultValue + { + Item* item; + uint8 count; + LootType lootType; + }; + + typedef std::vector<ResultValue> OrderedStorage; + + void Add(Item* item, uint8 count, LootType lootType); + + OrderedStorage::const_iterator begin() const; + OrderedStorage::const_iterator end() const; + + OrderedStorage _byOrder; + std::unordered_map<Item*, OrderedStorage::size_type> _byItem; +}; + +#endif // Loot_h__ diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index 6b984054fb2..eab712e7ed6 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -17,17 +17,18 @@ */ #include "LootMgr.h" +#include "Containers.h" +#include "DatabaseEnv.h" +#include "DB2Stores.h" +#include "ItemTemplate.h" #include "Log.h" +#include "Loot.h" #include "ObjectMgr.h" -#include "World.h" -#include "Util.h" -#include "SharedDefines.h" -#include "SpellMgr.h" -#include "SpellInfo.h" -#include "Group.h" #include "Player.h" -#include "Containers.h" -#include "LootPackets.h" +#include "Random.h" +#include "SpellInfo.h" +#include "SpellMgr.h" +#include "World.h" static Rates const qualityToRate[MAX_ITEM_QUALITY] = { @@ -92,7 +93,6 @@ class LootTemplate::LootGroup // A set of loot def float TotalChance() const; // Overall chance for the group void Verify(LootStore const& lootstore, uint32 id, uint8 group_id) const; - void CollectLootIds(LootIdSet& set) const; void CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const; LootStoreItemList* GetExplicitlyChancedItemList() { return &ExplicitlyChanced; } LootStoreItemList* GetEqualChancedItemList() { return &EqualChanced; } @@ -349,742 +349,6 @@ bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const } // -// --------- LootItem --------- -// - -// Constructor, copies most fields from LootStoreItem and generates random count -LootItem::LootItem(LootStoreItem const& li) -{ - itemid = li.itemid; - conditions = li.conditions; - - ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemid); - freeforall = proto && (proto->GetFlags() & ITEM_FLAG_MULTI_DROP); - follow_loot_rules = proto && (proto->FlagsCu & ITEM_FLAGS_CU_FOLLOW_LOOT_RULES); - - needs_quest = li.needs_quest; - - randomSuffix = GenerateEnchSuffixFactor(itemid); - randomPropertyId = Item::GenerateItemRandomPropertyId(itemid); - upgradeId = sDB2Manager.GetRulesetItemUpgrade(itemid); - context = 0; - count = 0; - is_looted = 0; - is_blocked = 0; - is_underthreshold = 0; - is_counted = 0; - canSave = true; -} - -// Basic checks for player/item compatibility - if false no chance to see the item in the loot -bool LootItem::AllowedForPlayer(Player const* player) const -{ - // DB conditions check - if (!sConditionMgr->IsObjectMeetToConditions(const_cast<Player*>(player), conditions)) - return false; - - ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(itemid); - if (!pProto) - return false; - - // not show loot for players without profession or those who already know the recipe - if ((pProto->GetFlags() & ITEM_FLAG_HIDE_UNUSABLE_RECIPE) && (!player->HasSkill(pProto->GetRequiredSkill()) || player->HasSpell(pProto->Effects[1]->SpellID))) - return false; - - // not show loot for not own team - if ((pProto->GetFlags2() & ITEM_FLAG2_FACTION_HORDE) && player->GetTeam() != HORDE) - return false; - - if ((pProto->GetFlags2() & ITEM_FLAG2_FACTION_ALLIANCE) && player->GetTeam() != ALLIANCE) - return false; - - // check quest requirements - if (!(pProto->FlagsCu & ITEM_FLAGS_CU_IGNORE_QUEST_STATUS) && ((needs_quest || (pProto->GetStartQuest() && player->GetQuestStatus(pProto->GetStartQuest()) != QUEST_STATUS_NONE)) && !player->HasQuestForItem(itemid))) - return false; - - // Don't show bind-when-picked-up unique items if player already has the maximum allowed quantity. - if (pProto->GetBonding() == BIND_ON_ACQUIRE && pProto->GetMaxCount() && player->GetItemCount(itemid, true) >= pProto->GetMaxCount()) - return false; - - return true; -} - -void LootItem::AddAllowedLooter(const Player* player) -{ - allowedGUIDs.insert(player->GetGUID()); -} - -// -// --------- Loot --------- -// - -// Inserts the item into the loot (called by LootTemplate processors) -void Loot::AddItem(LootStoreItem const& item) -{ - ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item.itemid); - if (!proto) - return; - - uint32 count = urand(item.mincount, item.maxcount); - uint32 stacks = count / proto->GetMaxStackSize() + ((count % proto->GetMaxStackSize()) ? 1 : 0); - - std::vector<LootItem>& lootItems = item.needs_quest ? quest_items : items; - uint32 limit = item.needs_quest ? MAX_NR_QUEST_ITEMS : MAX_NR_LOOT_ITEMS; - - for (uint32 i = 0; i < stacks && lootItems.size() < limit; ++i) - { - LootItem generatedLoot(item); - generatedLoot.context = _difficultyBonusTreeMod; - generatedLoot.count = std::min(count, proto->GetMaxStackSize()); - if (_difficultyBonusTreeMod) - { - std::set<uint32> bonusListIDs = sDB2Manager.GetItemBonusTree(generatedLoot.itemid, _difficultyBonusTreeMod); - generatedLoot.BonusListIDs.insert(generatedLoot.BonusListIDs.end(), bonusListIDs.begin(), bonusListIDs.end()); - } - - lootItems.push_back(generatedLoot); - count -= proto->GetMaxStackSize(); - - // non-conditional one-player only items are counted here, - // free for all items are counted in FillFFALoot(), - // non-ffa conditionals are counted in FillNonQuestNonFFAConditionalLoot() - if (!item.needs_quest && item.conditions.empty() && !(proto->GetFlags() & ITEM_FLAG_MULTI_DROP)) - ++unlootedCount; - } -} - -LootItem const* Loot::GetItemInSlot(uint32 lootSlot) const -{ - if (lootSlot < items.size()) - return &items[lootSlot]; - - lootSlot -= uint32(items.size()); - if (lootSlot < quest_items.size()) - return &quest_items[lootSlot]; - - return nullptr; -} - -// Calls processor of corresponding LootTemplate (which handles everything including references) -bool Loot::FillLoot(uint32 lootId, LootStore const& store, Player* lootOwner, bool personal, bool noEmptyError, uint16 lootMode /*= LOOT_MODE_DEFAULT*/) -{ - // Must be provided - if (!lootOwner) - return false; - - LootTemplate const* tab = store.GetLootFor(lootId); - - if (!tab) - { - if (!noEmptyError) - TC_LOG_ERROR("sql.sql", "Table '%s' loot id #%u used but it doesn't have records.", store.GetName(), lootId); - return false; - } - - _difficultyBonusTreeMod = lootOwner->GetMap()->GetDifficultyLootBonusTreeMod(); - - items.reserve(MAX_NR_LOOT_ITEMS); - quest_items.reserve(MAX_NR_QUEST_ITEMS); - - tab->Process(*this, store.IsRatesAllowed(), lootMode); // Processing is done there, callback via Loot::AddItem() - - // Setting access rights for group loot case - Group* group = lootOwner->GetGroup(); - if (!personal && group) - { - roundRobinPlayer = lootOwner->GetGUID(); - - for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next()) - if (Player* player = itr->GetSource()) // should actually be looted object instead of lootOwner but looter has to be really close so doesnt really matter - FillNotNormalLootFor(player, player->IsAtGroupRewardDistance(lootOwner)); - - for (uint8 i = 0; i < items.size(); ++i) - { - if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(items[i].itemid)) - if (proto->GetQuality() < uint32(group->GetLootThreshold())) - items[i].is_underthreshold = true; - } - } - // ... for personal loot - else - FillNotNormalLootFor(lootOwner, true); - - return true; -} - -void Loot::FillNotNormalLootFor(Player* player, bool presentAtLooting) -{ - ObjectGuid::LowType plguid = player->GetGUID().GetCounter(); - - QuestItemMap::const_iterator qmapitr = PlayerQuestItems.find(plguid); - if (qmapitr == PlayerQuestItems.end()) - FillQuestLoot(player); - - qmapitr = PlayerFFAItems.find(plguid); - if (qmapitr == PlayerFFAItems.end()) - FillFFALoot(player); - - qmapitr = PlayerNonQuestNonFFAConditionalItems.find(plguid); - if (qmapitr == PlayerNonQuestNonFFAConditionalItems.end()) - FillNonQuestNonFFAConditionalLoot(player, presentAtLooting); - - // if not auto-processed player will have to come and pick it up manually - if (!presentAtLooting) - return; - - // Process currency items - uint32 max_slot = GetMaxSlotInLootFor(player); - LootItem const* item = NULL; - uint32 itemsSize = uint32(items.size()); - for (uint32 i = 0; i < max_slot; ++i) - { - if (i < items.size()) - item = &items[i]; - else - item = &quest_items[i-itemsSize]; - - if (!item->is_looted && item->freeforall && item->AllowedForPlayer(player)) - if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item->itemid)) - if (proto->IsCurrencyToken()) - player->StoreLootItem(i, this); - } -} - -QuestItemList* Loot::FillFFALoot(Player* player) -{ - QuestItemList* ql = new QuestItemList(); - - for (uint8 i = 0; i < items.size(); ++i) - { - LootItem &item = items[i]; - if (!item.is_looted && item.freeforall && item.AllowedForPlayer(player)) - { - ql->push_back(QuestItem(i)); - ++unlootedCount; - } - } - if (ql->empty()) - { - delete ql; - return NULL; - } - - PlayerFFAItems[player->GetGUID().GetCounter()] = ql; - return ql; -} - -QuestItemList* Loot::FillQuestLoot(Player* player) -{ - if (items.size() == MAX_NR_LOOT_ITEMS) - return NULL; - - QuestItemList* ql = new QuestItemList(); - - for (uint8 i = 0; i < quest_items.size(); ++i) - { - LootItem &item = quest_items[i]; - - if (!item.is_looted && (item.AllowedForPlayer(player) || (item.follow_loot_rules && player->GetGroup() && ((player->GetGroup()->GetLootMethod() == MASTER_LOOT && player->GetGroup()->GetMasterLooterGuid() == player->GetGUID()) || player->GetGroup()->GetLootMethod() != MASTER_LOOT)))) - { - ql->push_back(QuestItem(i)); - - // quest items get blocked when they first appear in a - // player's quest vector - // - // increase once if one looter only, looter-times if free for all - if (item.freeforall || !item.is_blocked) - ++unlootedCount; - if (!player->GetGroup() || (player->GetGroup()->GetLootMethod() != GROUP_LOOT)) - item.is_blocked = true; - - if (items.size() + ql->size() == MAX_NR_LOOT_ITEMS) - break; - } - } - if (ql->empty()) - { - delete ql; - return NULL; - } - - PlayerQuestItems[player->GetGUID().GetCounter()] = ql; - return ql; -} - -QuestItemList* Loot::FillNonQuestNonFFAConditionalLoot(Player* player, bool presentAtLooting) -{ - QuestItemList* ql = new QuestItemList(); - - for (uint8 i = 0; i < items.size(); ++i) - { - LootItem &item = items[i]; - if (!item.is_looted && !item.freeforall && (item.AllowedForPlayer(player) || (item.follow_loot_rules && player->GetGroup() && ((player->GetGroup()->GetLootMethod() == MASTER_LOOT && player->GetGroup()->GetMasterLooterGuid() == player->GetGUID()) || player->GetGroup()->GetLootMethod() != MASTER_LOOT)))) - { - if (presentAtLooting) - item.AddAllowedLooter(player); - if (!item.conditions.empty()) - { - ql->push_back(QuestItem(i)); - if (!item.is_counted) - { - ++unlootedCount; - item.is_counted = true; - } - } - } - } - if (ql->empty()) - { - delete ql; - return NULL; - } - - PlayerNonQuestNonFFAConditionalItems[player->GetGUID().GetCounter()] = ql; - return ql; -} - -//=================================================== - -void Loot::NotifyItemRemoved(uint8 lootIndex) -{ - // notify all players that are looting this that the item was removed - // convert the index to the slot the player sees - GuidSet::iterator i_next; - for (GuidSet::iterator i = PlayersLooting.begin(); i != PlayersLooting.end(); i = i_next) - { - i_next = i; - ++i_next; - if (Player* player = ObjectAccessor::FindPlayer(*i)) - player->SendNotifyLootItemRemoved(GetGUID(), lootIndex); - else - PlayersLooting.erase(i); - } -} - -void Loot::NotifyMoneyRemoved() -{ - // notify all players that are looting this that the money was removed - GuidSet::iterator i_next; - for (GuidSet::iterator i = PlayersLooting.begin(); i != PlayersLooting.end(); i = i_next) - { - i_next = i; - ++i_next; - if (Player* player = ObjectAccessor::FindPlayer(*i)) - player->SendNotifyLootMoneyRemoved(GetGUID()); - else - PlayersLooting.erase(i); - } -} - -void Loot::NotifyQuestItemRemoved(uint8 questIndex) -{ - // when a free for all questitem is looted - // all players will get notified of it being removed - // (other questitems can be looted by each group member) - // bit inefficient but isn't called often - - GuidSet::iterator i_next; - for (GuidSet::iterator i = PlayersLooting.begin(); i != PlayersLooting.end(); i = i_next) - { - i_next = i; - ++i_next; - if (Player* player = ObjectAccessor::FindPlayer(*i)) - { - QuestItemMap::const_iterator pq = PlayerQuestItems.find(player->GetGUID().GetCounter()); - if (pq != PlayerQuestItems.end() && pq->second) - { - // find where/if the player has the given item in it's vector - QuestItemList& pql = *pq->second; - - uint8 j; - for (j = 0; j < pql.size(); ++j) - if (pql[j].index == questIndex) - break; - - if (j < pql.size()) - player->SendNotifyLootItemRemoved(GetGUID(), items.size()+j); - } - } - else - PlayersLooting.erase(i); - } -} - -void Loot::generateMoneyLoot(uint32 minAmount, uint32 maxAmount) -{ - if (maxAmount > 0) - { - if (maxAmount <= minAmount) - gold = uint32(maxAmount * sWorld->getRate(RATE_DROP_MONEY)); - else if ((maxAmount - minAmount) < 32700) - gold = uint32(urand(minAmount, maxAmount) * sWorld->getRate(RATE_DROP_MONEY)); - else - gold = uint32(urand(minAmount >> 8, maxAmount >> 8) * sWorld->getRate(RATE_DROP_MONEY)) << 8; - } -} - -void Loot::DeleteLootItemFromContainerItemDB(uint32 itemID) -{ - // Deletes a single item associated with an openable item from the DB - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_ITEM); - stmt->setUInt64(0, containerID.GetCounter()); - stmt->setUInt32(1, itemID); - CharacterDatabase.Execute(stmt); - - // Mark the item looted to prevent resaving - for (LootItemList::iterator _itr = items.begin(); _itr != items.end(); ++_itr) - { - if (_itr->itemid != itemID) - continue; - - _itr->canSave = false; - break; - } -} - -void Loot::DeleteLootMoneyFromContainerItemDB() -{ - // Deletes money loot associated with an openable item from the DB - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEMCONTAINER_MONEY); - stmt->setUInt64(0, containerID.GetCounter()); - CharacterDatabase.Execute(stmt); -} - -LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem* *qitem, QuestItem* *ffaitem, QuestItem* *conditem) -{ - LootItem* item = NULL; - bool is_looted = true; - if (lootSlot >= items.size()) - { - uint32 questSlot = lootSlot - items.size(); - QuestItemMap::const_iterator itr = PlayerQuestItems.find(player->GetGUID().GetCounter()); - if (itr != PlayerQuestItems.end() && questSlot < itr->second->size()) - { - QuestItem* qitem2 = &itr->second->at(questSlot); - if (qitem) - *qitem = qitem2; - item = &quest_items[qitem2->index]; - is_looted = qitem2->is_looted; - } - } - else - { - item = &items[lootSlot]; - is_looted = item->is_looted; - if (item->freeforall) - { - QuestItemMap::const_iterator itr = PlayerFFAItems.find(player->GetGUID().GetCounter()); - if (itr != PlayerFFAItems.end()) - { - for (QuestItemList::const_iterator iter=itr->second->begin(); iter!= itr->second->end(); ++iter) - if (iter->index == lootSlot) - { - QuestItem* ffaitem2 = (QuestItem*)&(*iter); - if (ffaitem) - *ffaitem = ffaitem2; - is_looted = ffaitem2->is_looted; - break; - } - } - } - else if (!item->conditions.empty()) - { - QuestItemMap::const_iterator itr = PlayerNonQuestNonFFAConditionalItems.find(player->GetGUID().GetCounter()); - if (itr != PlayerNonQuestNonFFAConditionalItems.end()) - { - for (QuestItemList::const_iterator iter=itr->second->begin(); iter!= itr->second->end(); ++iter) - { - if (iter->index == lootSlot) - { - QuestItem* conditem2 = (QuestItem*)&(*iter); - if (conditem) - *conditem = conditem2; - is_looted = conditem2->is_looted; - break; - } - } - } - } - } - - if (is_looted) - return NULL; - - return item; -} - -uint32 Loot::GetMaxSlotInLootFor(Player* player) const -{ - QuestItemMap::const_iterator itr = PlayerQuestItems.find(player->GetGUID().GetCounter()); - return items.size() + (itr != PlayerQuestItems.end() ? itr->second->size() : 0); -} - -// return true if there is any item that is lootable for any player (not quest item, FFA or conditional) -bool Loot::hasItemForAll() const -{ - // Gold is always lootable - if (gold) - return true; - - for (LootItem const& item : items) - if (!item.is_looted && !item.freeforall && item.conditions.empty()) - return true; - return false; -} - -// return true if there is any FFA, quest or conditional item for the player. -bool Loot::hasItemFor(Player* player) const -{ - QuestItemMap const& lootPlayerQuestItems = GetPlayerQuestItems(); - QuestItemMap::const_iterator q_itr = lootPlayerQuestItems.find(player->GetGUID().GetCounter()); - if (q_itr != lootPlayerQuestItems.end()) - { - QuestItemList* q_list = q_itr->second; - for (QuestItemList::const_iterator qi = q_list->begin(); qi != q_list->end(); ++qi) - { - const LootItem &item = quest_items[qi->index]; - if (!qi->is_looted && !item.is_looted) - return true; - } - } - - QuestItemMap const& lootPlayerFFAItems = GetPlayerFFAItems(); - QuestItemMap::const_iterator ffa_itr = lootPlayerFFAItems.find(player->GetGUID().GetCounter()); - if (ffa_itr != lootPlayerFFAItems.end()) - { - QuestItemList* ffa_list = ffa_itr->second; - for (QuestItemList::const_iterator fi = ffa_list->begin(); fi != ffa_list->end(); ++fi) - { - const LootItem &item = items[fi->index]; - if (!fi->is_looted && !item.is_looted) - return true; - } - } - - QuestItemMap const& lootPlayerNonQuestNonFFAConditionalItems = GetPlayerNonQuestNonFFAConditionalItems(); - QuestItemMap::const_iterator nn_itr = lootPlayerNonQuestNonFFAConditionalItems.find(player->GetGUID().GetCounter()); - if (nn_itr != lootPlayerNonQuestNonFFAConditionalItems.end()) - { - QuestItemList* conditional_list = nn_itr->second; - for (QuestItemList::const_iterator ci = conditional_list->begin(); ci != conditional_list->end(); ++ci) - { - const LootItem &item = items[ci->index]; - if (!ci->is_looted && !item.is_looted) - return true; - } - } - - return false; -} - -// return true if there is any item over the group threshold (i.e. not underthreshold). -bool Loot::hasOverThresholdItem() const -{ - for (uint8 i = 0; i < items.size(); ++i) - { - if (!items[i].is_looted && !items[i].is_underthreshold && !items[i].freeforall) - return true; - } - - return false; -} - -void Loot::BuildLootResponse(WorldPackets::Loot::LootResponse& packet, Player* viewer, PermissionTypes permission) const -{ - if (permission == NONE_PERMISSION) - return; - - packet.Coins = gold; - - switch (permission) - { - case GROUP_PERMISSION: - case MASTER_PERMISSION: - case RESTRICTED_PERMISSION: - { - // if you are not the round-robin group looter, you can only see - // blocked rolled items and quest items, and !ffa items - for (uint8 i = 0; i < items.size(); ++i) - { - if (!items[i].is_looted && !items[i].freeforall && items[i].conditions.empty() && items[i].AllowedForPlayer(viewer)) - { - uint8 slot_type; - - if (items[i].is_blocked) // for ML & restricted is_blocked = !is_underthreshold - { - switch (permission) - { - case GROUP_PERMISSION: - slot_type = LOOT_SLOT_TYPE_ROLL_ONGOING; - break; - case MASTER_PERMISSION: - { - if (viewer->GetGroup() && viewer->GetGroup()->GetMasterLooterGuid() == viewer->GetGUID()) - slot_type = LOOT_SLOT_TYPE_MASTER; - else - slot_type = LOOT_SLOT_TYPE_LOCKED; - break; - } - case RESTRICTED_PERMISSION: - slot_type = LOOT_SLOT_TYPE_LOCKED; - break; - default: - continue; - } - } - else if (roundRobinPlayer.IsEmpty() || viewer->GetGUID() == roundRobinPlayer || !items[i].is_underthreshold) - { - // no round robin owner or he has released the loot - // or it IS the round robin group owner - // => item is lootable - slot_type = LOOT_SLOT_TYPE_ALLOW_LOOT; - } - else - // item shall not be displayed. - continue; - - WorldPackets::Loot::LootItemData lootItem; - lootItem.LootListID = i + 1; - lootItem.UIType = slot_type; - lootItem.Quantity = items[i].count; - lootItem.Loot.Initialize(items[i]); - packet.Items.push_back(lootItem); - } - } - break; - } - case ALL_PERMISSION: - case OWNER_PERMISSION: - { - for (uint8 i = 0; i < items.size(); ++i) - { - if (!items[i].is_looted && !items[i].freeforall && items[i].conditions.empty() && items[i].AllowedForPlayer(viewer)) - { - WorldPackets::Loot::LootItemData lootItem; - lootItem.LootListID = i + 1; - lootItem.UIType = permission == OWNER_PERMISSION ? LOOT_SLOT_TYPE_OWNER : LOOT_SLOT_TYPE_ALLOW_LOOT; - lootItem.Quantity = items[i].count; - lootItem.Loot.Initialize(items[i]); - packet.Items.push_back(lootItem); - } - } - break; - } - default: - return; - } - - LootSlotType slotType = permission == OWNER_PERMISSION ? LOOT_SLOT_TYPE_OWNER : LOOT_SLOT_TYPE_ALLOW_LOOT; - QuestItemMap const& lootPlayerQuestItems = GetPlayerQuestItems(); - QuestItemMap::const_iterator q_itr = lootPlayerQuestItems.find(viewer->GetGUID().GetCounter()); - if (q_itr != lootPlayerQuestItems.end()) - { - QuestItemList* q_list = q_itr->second; - for (QuestItemList::const_iterator qi = q_list->begin(); qi != q_list->end(); ++qi) - { - LootItem const& item = quest_items[qi->index]; - if (!qi->is_looted && !item.is_looted) - { - WorldPackets::Loot::LootItemData lootItem; - lootItem.LootListID = items.size() + qi->index + 1; - lootItem.Quantity = item.count; - lootItem.Loot.Initialize(item); - - if (item.follow_loot_rules) - { - switch (permission) - { - case MASTER_PERMISSION: - lootItem.UIType = LOOT_SLOT_TYPE_MASTER; - break; - case RESTRICTED_PERMISSION: - lootItem.UIType = item.is_blocked ? LOOT_SLOT_TYPE_LOCKED : LOOT_SLOT_TYPE_ALLOW_LOOT; - break; - case GROUP_PERMISSION: - if (!item.is_blocked) - lootItem.UIType = LOOT_SLOT_TYPE_ALLOW_LOOT; - else - lootItem.UIType = LOOT_SLOT_TYPE_ROLL_ONGOING; - break; - default: - lootItem.UIType = slotType; - break; - } - } - else - lootItem.UIType = slotType; - - packet.Items.push_back(lootItem); - } - } - } - - QuestItemMap const& lootPlayerFFAItems = GetPlayerFFAItems(); - QuestItemMap::const_iterator ffa_itr = lootPlayerFFAItems.find(viewer->GetGUID().GetCounter()); - if (ffa_itr != lootPlayerFFAItems.end()) - { - QuestItemList* ffa_list = ffa_itr->second; - for (QuestItemList::const_iterator fi = ffa_list->begin(); fi != ffa_list->end(); ++fi) - { - LootItem const& item = items[fi->index]; - if (!fi->is_looted && !item.is_looted) - { - WorldPackets::Loot::LootItemData lootItem; - lootItem.LootListID = items.size() + fi->index + 1; - lootItem.UIType = slotType; - lootItem.Quantity = item.count; - lootItem.Loot.Initialize(item); - packet.Items.push_back(lootItem); - } - } - } - - QuestItemMap const& lootPlayerNonQuestNonFFAConditionalItems = GetPlayerNonQuestNonFFAConditionalItems(); - QuestItemMap::const_iterator nn_itr = lootPlayerNonQuestNonFFAConditionalItems.find(viewer->GetGUID().GetCounter()); - if (nn_itr != lootPlayerNonQuestNonFFAConditionalItems.end()) - { - QuestItemList* conditional_list = nn_itr->second; - for (QuestItemList::const_iterator ci = conditional_list->begin(); ci != conditional_list->end(); ++ci) - { - LootItem const& item = items[ci->index]; - if (!ci->is_looted && !item.is_looted) - { - WorldPackets::Loot::LootItemData lootItem; - lootItem.LootListID = items.size() + ci->index + 1; - lootItem.Quantity = item.count; - lootItem.Loot.Initialize(item); - - if (item.follow_loot_rules) - { - switch (permission) - { - case MASTER_PERMISSION: - lootItem.UIType = LOOT_SLOT_TYPE_MASTER; - break; - case RESTRICTED_PERMISSION: - lootItem.UIType = item.is_blocked ? LOOT_SLOT_TYPE_LOCKED : LOOT_SLOT_TYPE_ALLOW_LOOT; - break; - case GROUP_PERMISSION: - if (!item.is_blocked) - lootItem.UIType = LOOT_SLOT_TYPE_ALLOW_LOOT; - else - lootItem.UIType = LOOT_SLOT_TYPE_ROLL_ONGOING; - break; - default: - lootItem.UIType = slotType; - break; - } - } - else - lootItem.UIType = slotType; - - packet.Items.push_back(lootItem); - } - } - } -} - -// // --------- LootTemplate::LootGroup --------- // @@ -1843,9 +1107,7 @@ void LoadLootTemplates_Spell() // not report about not trainable spells (optionally supported by DB) // ignore 61756 (Northrend Inscription Research (FAST QA VERSION) for example if (!spellInfo->HasAttribute(SPELL_ATTR0_NOT_SHAPESHIFT) || (spellInfo->HasAttribute(SPELL_ATTR0_TRADESPELL))) - { LootTemplates_Spell.ReportNonExistingId(spell_id, "Spell", spellInfo->Id); - } } else lootIdSet.erase(spell_id); @@ -1904,29 +1166,3 @@ void LoadLootTables() LoadLootTemplates_Reference(); } - -void AELootResult::Add(Item* item, uint8 count, LootType lootType) -{ - auto itr = _byItem.find(item); - if (itr != _byItem.end()) - _byOrder[itr->second].count += count; - else - { - _byItem[item] = _byOrder.size(); - ResultValue value; - value.item = item; - value.count = count; - value.lootType = lootType; - _byOrder.push_back(value); - } -} - -AELootResult::OrderedStorage::const_iterator AELootResult::begin() const -{ - return _byOrder.begin(); -} - -AELootResult::OrderedStorage::const_iterator AELootResult::end() const -{ - return _byOrder.end(); -} diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h index eeecb40440c..d3356d4a629 100644 --- a/src/server/game/Loot/LootMgr.h +++ b/src/server/game/Loot/LootMgr.h @@ -19,117 +19,20 @@ #ifndef TRINITY_LOOTMGR_H #define TRINITY_LOOTMGR_H -#include "ItemEnchantmentMgr.h" -#include "ByteBuffer.h" -#include "RefManager.h" -#include "SharedDefines.h" +#include "Define.h" #include "ConditionMgr.h" #include "ObjectGuid.h" -#include <map> -#include <vector> +#include "SharedDefines.h" #include <list> +#include <set> +#include <unordered_map> +#include <vector> -class Item; - -namespace WorldPackets -{ - namespace Loot - { - class LootResponse; - } -} - -enum RollType -{ - ROLL_PASS = 0, - ROLL_NEED = 1, - ROLL_GREED = 2, - ROLL_DISENCHANT = 3, - MAX_ROLL_TYPE = 4 -}; - -enum RollMask -{ - ROLL_FLAG_TYPE_PASS = 0x01, - ROLL_FLAG_TYPE_NEED = 0x02, - ROLL_FLAG_TYPE_GREED = 0x04, - ROLL_FLAG_TYPE_DISENCHANT = 0x08, - - ROLL_ALL_TYPE_NO_DISENCHANT = 0x07, - ROLL_ALL_TYPE_MASK = 0x0F -}; - -#define MAX_NR_LOOT_ITEMS 16 -// note: the client cannot show more than 16 items total -#define MAX_NR_QUEST_ITEMS 32 -// unrelated to the number of quest items shown, just for reserve - -enum LootMethod : uint8 -{ - FREE_FOR_ALL = 0, - MASTER_LOOT = 2, - GROUP_LOOT = 3, - PERSONAL_LOOT = 5 -}; - -enum PermissionTypes -{ - ALL_PERMISSION = 0, - GROUP_PERMISSION = 1, - MASTER_PERMISSION = 2, - RESTRICTED_PERMISSION = 3, - OWNER_PERMISSION = 5, - NONE_PERMISSION = 6 -}; - -enum LootType : uint8 -{ - LOOT_NONE = 0, - - LOOT_CORPSE = 1, - LOOT_PICKPOCKETING = 2, - LOOT_FISHING = 3, - LOOT_DISENCHANTING = 4, - // ignored always by client - LOOT_SKINNING = 6, - LOOT_PROSPECTING = 7, - LOOT_MILLING = 8, - - 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 -}; - -enum LootError -{ - 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 -}; - -class Player; class LootStore; +class LootTemplate; +class Player; +struct Loot; +struct LootItem; struct TC_GAME_API LootStoreItem { @@ -137,8 +40,8 @@ struct TC_GAME_API LootStoreItem uint32 reference; // referenced TemplateleId float chance; // chance to drop for both quest and non-quest items, chance to be used for refs uint16 lootmode; - bool needs_quest : 1; // quest drop (quest is required for item to drop) - uint8 groupid : 7; + bool needs_quest; // quest drop (quest is required for item to drop) + uint8 groupid; uint8 mincount; // mincount for drop items uint8 maxcount; // max drop count for the item mincount or Ref multiplicator ConditionContainer conditions; // additional loot condition @@ -154,59 +57,6 @@ struct TC_GAME_API LootStoreItem bool IsValid(LootStore const& store, uint32 entry) const; // Checks correctness of values }; -struct TC_GAME_API LootItem -{ - uint32 itemid; - uint32 randomSuffix; - ItemRandomEnchantmentId randomPropertyId; - int32 upgradeId; - std::vector<int32> BonusListIDs; - uint8 context; - ConditionContainer conditions; // additional loot condition - GuidSet allowedGUIDs; - uint8 count : 8; - bool is_looted : 1; - bool is_blocked : 1; - bool freeforall : 1; // free for all - bool is_underthreshold : 1; - bool is_counted : 1; - bool needs_quest : 1; // quest drop - bool follow_loot_rules : 1; - bool canSave; - - // Constructor, copies most fields from LootStoreItem, generates random count and random suffixes/properties - // Should be called for non-reference LootStoreItem entries only (reference = 0) - explicit LootItem(LootStoreItem const& li); - - // Empty constructor for creating an empty LootItem to be filled in with DB data - LootItem() : itemid(0), randomSuffix(0), randomPropertyId(), upgradeId(0), context(0), count(0), is_looted(false), is_blocked(false), - freeforall(false), is_underthreshold(false), is_counted(false), needs_quest(false), follow_loot_rules(false), - canSave(true){ }; - - // Basic checks for player/item compatibility - if false no chance to see the item in the loot - bool AllowedForPlayer(Player const* player) const; - void AddAllowedLooter(Player const* player); - GuidSet const& GetAllowedLooters() const { return allowedGUIDs; } -}; - -struct QuestItem -{ - uint8 index; // position in quest_items; - bool is_looted; - - QuestItem() - : index(0), is_looted(false) { } - - QuestItem(uint8 _index, bool _islooted = false) - : index(_index), is_looted(_islooted) { } -}; - -struct Loot; -class LootTemplate; - -typedef std::vector<QuestItem> QuestItemList; -typedef std::vector<LootItem> LootItemList; -typedef std::map<ObjectGuid::LowType, QuestItemList*> QuestItemMap; typedef std::list<LootStoreItem*> LootStoreItemList; typedef std::unordered_map<uint32, LootTemplate*> LootTemplateMap; @@ -287,158 +137,6 @@ class TC_GAME_API LootTemplate //===================================================== -class LootValidatorRef : public Reference<Loot, LootValidatorRef> -{ - public: - LootValidatorRef() { } - void targetObjectDestroyLink() override { } - void sourceObjectDestroyLink() override { } -}; - -//===================================================== - -class LootValidatorRefManager : public RefManager<Loot, LootValidatorRef> -{ - public: - typedef LinkedListHead::Iterator< LootValidatorRef > iterator; - - LootValidatorRef* getFirst() { return (LootValidatorRef*)RefManager<Loot, LootValidatorRef>::getFirst(); } - LootValidatorRef* getLast() { return (LootValidatorRef*)RefManager<Loot, LootValidatorRef>::getLast(); } - - iterator begin() { return iterator(getFirst()); } - iterator end() { return iterator(NULL); } - iterator rbegin() { return iterator(getLast()); } - iterator rend() { return iterator(NULL); } -}; - -//===================================================== - -struct TC_GAME_API Loot -{ - QuestItemMap const& GetPlayerQuestItems() const { return PlayerQuestItems; } - QuestItemMap const& GetPlayerFFAItems() const { return PlayerFFAItems; } - QuestItemMap const& GetPlayerNonQuestNonFFAConditionalItems() const { return PlayerNonQuestNonFFAConditionalItems; } - - std::vector<LootItem> items; - std::vector<LootItem> quest_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 - uint8 maxDuplicates; // Max amount of items with the same entry that can drop (default is 1; on 25 man raid mode 3) - - // GUID of container that holds this loot (item_instance.entry) - // Only set for inventory items that can be right-click looted - ObjectGuid containerID; - - Loot(uint32 _gold = 0) : gold(_gold), unlootedCount(0), roundRobinPlayer(), loot_type(LOOT_CORPSE), maxDuplicates(1), _difficultyBonusTreeMod(0){ } - ~Loot() { clear(); } - - ObjectGuid const& GetGUID() const { return _GUID; } - void SetGUID(ObjectGuid const& guid) { _GUID = guid; } - - // For deleting items at loot removal since there is no backward interface to the Item() - void DeleteLootItemFromContainerItemDB(uint32 itemID); - void DeleteLootMoneyFromContainerItemDB(); - - // if loot becomes invalid this reference is used to inform the listener - void addLootValidatorRef(LootValidatorRef* pLootValidatorRef) - { - i_LootValidatorRefManager.insertFirst(pLootValidatorRef); - } - - // void clear(); - void clear() - { - for (QuestItemMap::const_iterator itr = PlayerQuestItems.begin(); itr != PlayerQuestItems.end(); ++itr) - delete itr->second; - PlayerQuestItems.clear(); - - for (QuestItemMap::const_iterator itr = PlayerFFAItems.begin(); itr != PlayerFFAItems.end(); ++itr) - delete itr->second; - PlayerFFAItems.clear(); - - for (QuestItemMap::const_iterator itr = PlayerNonQuestNonFFAConditionalItems.begin(); itr != PlayerNonQuestNonFFAConditionalItems.end(); ++itr) - delete itr->second; - PlayerNonQuestNonFFAConditionalItems.clear(); - - PlayersLooting.clear(); - items.clear(); - quest_items.clear(); - gold = 0; - unlootedCount = 0; - roundRobinPlayer.Clear(); - loot_type = LOOT_NONE; - i_LootValidatorRefManager.clearReferences(); - _difficultyBonusTreeMod = 0; - } - - bool empty() const { return items.empty() && gold == 0; } - bool isLooted() const { return gold == 0 && unlootedCount == 0; } - - void NotifyItemRemoved(uint8 lootIndex); - void NotifyQuestItemRemoved(uint8 questIndex); - void NotifyMoneyRemoved(); - void AddLooter(ObjectGuid GUID) { PlayersLooting.insert(GUID); } - void RemoveLooter(ObjectGuid GUID) { PlayersLooting.erase(GUID); } - - 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); - - // Inserts the item into the loot (called by LootTemplate processors) - void AddItem(LootStoreItem const & item); - - LootItem const* GetItemInSlot(uint32 lootSlot) const; - LootItem* LootItemInSlot(uint32 lootslot, Player* player, QuestItem** qitem = NULL, QuestItem** ffaitem = NULL, QuestItem** conditem = NULL); - uint32 GetMaxSlotInLootFor(Player* player) const; - bool hasItemForAll() const; - bool hasItemFor(Player* player) const; - bool hasOverThresholdItem() const; - - // Builds data for SMSG_LOOT_RESPONSE - void BuildLootResponse(WorldPackets::Loot::LootResponse& packet, Player* viewer, PermissionTypes permission = ALL_PERMISSION) const; - -private: - - void FillNotNormalLootFor(Player* player, bool presentAtLooting); - QuestItemList* FillFFALoot(Player* player); - QuestItemList* FillQuestLoot(Player* player); - QuestItemList* FillNonQuestNonFFAConditionalLoot(Player* player, bool presentAtLooting); - - GuidSet PlayersLooting; - QuestItemMap PlayerQuestItems; - QuestItemMap PlayerFFAItems; - QuestItemMap PlayerNonQuestNonFFAConditionalItems; - - // All rolls are registered here. They need to know, when the loot is not valid anymore - LootValidatorRefManager i_LootValidatorRefManager; - - // Loot GUID - ObjectGuid _GUID; - uint8 _difficultyBonusTreeMod; -}; - -class TC_GAME_API AELootResult -{ -public: - struct ResultValue - { - Item* item; - uint8 count; - LootType lootType; - }; - - typedef std::vector<ResultValue> OrderedStorage; - - void Add(Item* item, uint8 count, LootType lootType); - - OrderedStorage::const_iterator begin() const; - OrderedStorage::const_iterator end() const; - - OrderedStorage _byOrder; - std::unordered_map<Item*, OrderedStorage::size_type> _byItem; -}; - TC_GAME_API extern LootStore LootTemplates_Creature; TC_GAME_API extern LootStore LootTemplates_Fishing; TC_GAME_API extern LootStore LootTemplates_Gameobject; |
