aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Loot
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2017-05-18 23:52:58 +0200
committerShauren <shauren.trinity@gmail.com>2017-05-18 23:53:25 +0200
commitc5d3dd90bea3889ef5fcd33c9ef0d59d7c544f8a (patch)
treeaa7fde6f924fc39da54908bd6eeeb0be422e5fc3 /src/server/game/Loot
parent74456703146194de72424ec98c4ea76402077be6 (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.cpp828
-rw-r--r--src/server/game/Loot/Loot.h314
-rw-r--r--src/server/game/Loot/LootMgr.cpp782
-rw-r--r--src/server/game/Loot/LootMgr.h324
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;