/* * Copyright (C) 2008-2018 TrinityCore * * 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 . */ #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 "Map.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), 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; 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), _itemContext(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 (NotNormalLootItemMap::const_iterator itr = PlayerQuestItems.begin(); itr != PlayerQuestItems.end(); ++itr) delete itr->second; PlayerQuestItems.clear(); for (NotNormalLootItemMap::const_iterator itr = PlayerFFAItems.begin(); itr != PlayerFFAItems.end(); ++itr) delete itr->second; PlayerFFAItems.clear(); for (NotNormalLootItemMap::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(); _itemContext = 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)) { NotNormalLootItemMap::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 NotNormalLootItemList& 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; } _itemContext = lootOwner->GetMap()->GetDifficultyLootItemContext(); 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& 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 = _itemContext; generatedLoot.count = std::min(count, proto->GetMaxStackSize()); if (_itemContext) { std::set bonusListIDs = sDB2Manager.GetItemBonusTree(generatedLoot.itemid, _itemContext); 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, NotNormalLootItem* *qitem, NotNormalLootItem* *ffaitem, NotNormalLootItem* *conditem) { LootItem* item = NULL; bool is_looted = true; if (lootSlot >= items.size()) { uint32 questSlot = lootSlot - items.size(); NotNormalLootItemMap::const_iterator itr = PlayerQuestItems.find(player->GetGUID()); if (itr != PlayerQuestItems.end() && questSlot < itr->second->size()) { NotNormalLootItem* qitem2 = &(*itr->second)[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) { NotNormalLootItemMap::const_iterator itr = PlayerFFAItems.find(player->GetGUID()); if (itr != PlayerFFAItems.end()) { for (NotNormalLootItemList::const_iterator iter = itr->second->begin(); iter != itr->second->end(); ++iter) if (iter->index == lootSlot) { NotNormalLootItem* ffaitem2 = (NotNormalLootItem*)&(*iter); if (ffaitem) *ffaitem = ffaitem2; is_looted = ffaitem2->is_looted; break; } } } else if (!item->conditions.empty()) { NotNormalLootItemMap::const_iterator itr = PlayerNonQuestNonFFAConditionalItems.find(player->GetGUID()); if (itr != PlayerNonQuestNonFFAConditionalItems.end()) { for (NotNormalLootItemList::const_iterator iter = itr->second->begin(); iter != itr->second->end(); ++iter) { if (iter->index == lootSlot) { NotNormalLootItem* conditem2 = (NotNormalLootItem*)&(*iter); if (conditem) *conditem = conditem2; is_looted = conditem2->is_looted; break; } } } } } if (is_looted) return NULL; return item; } uint32 Loot::GetMaxSlotInLootFor(Player* player) const { NotNormalLootItemMap::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 { NotNormalLootItemMap const& lootPlayerQuestItems = GetPlayerQuestItems(); NotNormalLootItemMap::const_iterator q_itr = lootPlayerQuestItems.find(player->GetGUID()); if (q_itr != lootPlayerQuestItems.end()) { NotNormalLootItemList* q_list = q_itr->second; for (NotNormalLootItemList::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; } } NotNormalLootItemMap const& lootPlayerFFAItems = GetPlayerFFAItems(); NotNormalLootItemMap::const_iterator ffa_itr = lootPlayerFFAItems.find(player->GetGUID()); if (ffa_itr != lootPlayerFFAItems.end()) { NotNormalLootItemList* ffa_list = ffa_itr->second; for (NotNormalLootItemList::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; } } NotNormalLootItemMap const& lootPlayerNonQuestNonFFAConditionalItems = GetPlayerNonQuestNonFFAConditionalItems(); NotNormalLootItemMap::const_iterator nn_itr = lootPlayerNonQuestNonFFAConditionalItems.find(player->GetGUID()); if (nn_itr != lootPlayerNonQuestNonFFAConditionalItems.end()) { NotNormalLootItemList* conditional_list = nn_itr->second; for (NotNormalLootItemList::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; NotNormalLootItemMap const& lootPlayerQuestItems = GetPlayerQuestItems(); NotNormalLootItemMap::const_iterator q_itr = lootPlayerQuestItems.find(viewer->GetGUID()); if (q_itr != lootPlayerQuestItems.end()) { NotNormalLootItemList const& q_list = *q_itr->second; for (std::size_t i = 0; i < q_list.size(); ++i) { NotNormalLootItem const& qi = q_list[i]; LootItem const& item = quest_items[qi.index]; if (!qi.is_looted && !item.is_looted) { WorldPackets::Loot::LootItemData lootItem; lootItem.LootListID = items.size() + i + 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); } } } NotNormalLootItemMap const& lootPlayerFFAItems = GetPlayerFFAItems(); NotNormalLootItemMap::const_iterator ffa_itr = lootPlayerFFAItems.find(viewer->GetGUID()); if (ffa_itr != lootPlayerFFAItems.end()) { NotNormalLootItemList* ffa_list = ffa_itr->second; for (NotNormalLootItemList::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 = fi->index + 1; lootItem.UIType = slotType; lootItem.Quantity = item.count; lootItem.Loot.Initialize(item); packet.Items.push_back(lootItem); } } } NotNormalLootItemMap const& lootPlayerNonQuestNonFFAConditionalItems = GetPlayerNonQuestNonFFAConditionalItems(); NotNormalLootItemMap::const_iterator nn_itr = lootPlayerNonQuestNonFFAConditionalItems.find(viewer->GetGUID()); if (nn_itr != lootPlayerNonQuestNonFFAConditionalItems.end()) { NotNormalLootItemList* conditional_list = nn_itr->second; for (NotNormalLootItemList::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 = ci->index + 1; lootItem.Quantity = item.count; lootItem.Loot.Initialize(item); 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; } packet.Items.push_back(lootItem); } } } } void Loot::FillNotNormalLootFor(Player* player, bool presentAtLooting) { ObjectGuid plguid = player->GetGUID(); NotNormalLootItemMap::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); } } NotNormalLootItemList* Loot::FillFFALoot(Player* player) { NotNormalLootItemList* ql = new NotNormalLootItemList(); for (uint8 i = 0; i < items.size(); ++i) { LootItem &item = items[i]; if (!item.is_looted && item.freeforall && item.AllowedForPlayer(player)) { ql->push_back(NotNormalLootItem(i)); ++unlootedCount; } } if (ql->empty()) { delete ql; return NULL; } PlayerFFAItems[player->GetGUID()] = ql; return ql; } NotNormalLootItemList* Loot::FillQuestLoot(Player* player) { if (items.size() == MAX_NR_LOOT_ITEMS) return NULL; NotNormalLootItemList* ql = new NotNormalLootItemList(); 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(NotNormalLootItem(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; } NotNormalLootItemList* Loot::FillNonQuestNonFFAConditionalLoot(Player* player, bool presentAtLooting) { NotNormalLootItemList* ql = new NotNormalLootItemList(); for (uint8 i = 0; i < items.size(); ++i) { LootItem &item = items[i]; if (!item.is_looted && !item.freeforall && (item.AllowedForPlayer(player))) { if (presentAtLooting) item.AddAllowedLooter(player); if (!item.conditions.empty()) { ql->push_back(NotNormalLootItem(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(); }