/* * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include "Loot.h" #include "Group.h" #include "ItemEnchantmentMgr.h" #include "ItemTemplate.h" #include "Log.h" #include "LootMgr.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->HasFlag(ITEM_FLAG_MULTI_DROP); follow_loot_rules = proto && (proto->HasFlag(ITEM_FLAGS_CU_FOLLOW_LOOT_RULES)); needs_quest = li.needs_quest; randomSuffix = GenerateEnchSuffixFactor(itemid); randomPropertyId = GenerateItemRandomPropertyId(itemid); count = 0; is_looted = 0; is_blocked = 0; is_underthreshold = 0; is_counted = 0; rollWinnerGUID = ObjectGuid::Empty; } // Basic checks for player/item compatibility - if false no chance to see the item in the loot bool LootItem::AllowedForPlayer(Player const* player, bool isGivenByMasterLooter) 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 not own team if (pProto->HasFlag(ITEM_FLAG2_FACTION_HORDE) && player->GetTeam() != HORDE) return false; if (pProto->HasFlag(ITEM_FLAG2_FACTION_ALLIANCE) && player->GetTeam() != ALLIANCE) return false; // Master looter can see all items even if the character can't loot them if (!isGivenByMasterLooter && player->GetGroup() && player->GetGroup()->GetMasterLooterGuid() == player->GetGUID()) { return true; } // Don't allow loot for players without profession or those who already know the recipe if (pProto->HasFlag(ITEM_FLAG_HIDE_UNUSABLE_RECIPE) && (!player->HasSkill(pProto->RequiredSkill) || player->HasSpell(pProto->Spells[1].SpellId))) return false; // Don't allow to loot soulbound recipes that the player has already learned if (pProto->Class == ITEM_CLASS_RECIPE && pProto->Bonding == BIND_WHEN_PICKED_UP && pProto->Spells[1].SpellId != 0 && player->HasSpell(pProto->Spells[1].SpellId)) return false; // check quest requirements if (!pProto->HasFlag(ITEM_FLAGS_CU_IGNORE_QUEST_STATUS) && ((needs_quest || (pProto->StartQuest && player->GetQuestStatus(pProto->StartQuest) != QUEST_STATUS_NONE)) && !player->HasQuestForItem(itemid))) return false; return true; } void LootItem::AddAllowedLooter(Player const* player) { allowedGUIDs.insert(player->GetGUID()); } // // --------- Loot --------- // Loot::Loot(uint32 _gold /*= 0*/) : gold(_gold), unlootedCount(0), roundRobinPlayer(), loot_type(LOOT_NONE), maxDuplicates(1), containerID(0) { } Loot::~Loot() { clear(); } 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(); } // 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.count = std::min(count, proto->GetMaxStackSize()); lootItems.push_back(generatedLoot); count -= proto->GetMaxStackSize(); // In some cases, a dropped item should be visible/lootable only for some players in group bool canSeeItemInLootWindow = false; if (Player* player = ObjectAccessor::FindPlayer(lootOwnerGUID)) { if (Group* group = player->GetGroup()) { for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) if (Player* member = itr->GetSource()) if (generatedLoot.AllowedForPlayer(member)) canSeeItemInLootWindow = true; } else if (generatedLoot.AllowedForPlayer(player)) canSeeItemInLootWindow = true; } if (!canSeeItemInLootWindow) continue; // 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->HasFlag(ITEM_FLAG_MULTI_DROP)) ++unlootedCount; } } // 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; lootOwnerGUID = lootOwner->GetGUID(); 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; } 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 != nullptr; 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 if (player->IsInMap(lootOwner)) FillNotNormalLootFor(player, player->IsAtGroupRewardDistance(lootOwner)); for (uint8 i = 0; i < items.size(); ++i) { if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(items[i].itemid)) if (proto->Quality < 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 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 = nullptr; 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 nullptr; } PlayerFFAItems[player->GetGUID()] = ql; return ql; } NotNormalLootItemList* Loot::FillQuestLoot(Player* player) { if (items.size() == MAX_NR_LOOT_ITEMS) return nullptr; 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 && player->GetGroup()->GetLootMethod() != ROUND_ROBIN)) item.is_blocked = true; if (items.size() + ql->size() == MAX_NR_LOOT_ITEMS) break; } } if (ql->empty()) { delete ql; return nullptr; } 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 nullptr; } PlayerNonQuestNonFFAConditionalItems[player->GetGUID()] = 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(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(); 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(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; } } LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, NotNormalLootItem* *qitem, NotNormalLootItem* *ffaitem, NotNormalLootItem* *conditem) { LootItem* item = nullptr; 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->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) { 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 nullptr; 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) { LootItem const& 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) { LootItem const& 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) { LootItem const& 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; } ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li) { b << uint32(li.itemid); b << uint32(li.count); // nr of items of this type b << uint32(ASSERT_NOTNULL(sObjectMgr->GetItemTemplate(li.itemid))->DisplayInfoID); b << uint32(li.randomSuffix); b << uint32(li.randomPropertyId); //b << uint8(0); // slot type - will send after this function call return b; } ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) { if (lv.permission == NONE_PERMISSION) { b << uint32(0); // gold b << uint8(0); // item count return b; } Loot &l = lv.loot; uint8 itemsShown = 0; b << uint32(l.gold); //gold size_t count_pos = b.wpos(); // pos of item count byte b << uint8(0); // item count placeholder switch (lv.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 < l.items.size(); ++i) { if (!l.items[i].is_looted && !l.items[i].freeforall && l.items[i].conditions.empty() && l.items[i].AllowedForPlayer(lv.viewer)) { uint8 slot_type; if (l.items[i].is_blocked) // for ML & restricted is_blocked = !is_underthreshold { switch (lv.permission) { case GROUP_PERMISSION: slot_type = LOOT_SLOT_TYPE_ROLL_ONGOING; break; case MASTER_PERMISSION: { if (lv.viewer->GetGroup() && lv.viewer->GetGroup()->GetMasterLooterGuid() == lv.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 (!l.items[i].rollWinnerGUID.IsEmpty()) { if (l.items[i].rollWinnerGUID == lv.viewer->GetGUID()) slot_type = LOOT_SLOT_TYPE_OWNER; else continue; } else if (l.roundRobinPlayer.IsEmpty() || lv.viewer->GetGUID() == l.roundRobinPlayer || !l.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; b << uint8(i) << l.items[i]; b << uint8(slot_type); ++itemsShown; } } break; } case ROUND_ROBIN_PERMISSION: { for (uint8 i = 0; i < l.items.size(); ++i) { if (!l.items[i].is_looted && !l.items[i].freeforall && l.items[i].conditions.empty() && l.items[i].AllowedForPlayer(lv.viewer)) { if (!l.roundRobinPlayer.IsEmpty() && lv.viewer->GetGUID() != l.roundRobinPlayer) // item shall not be displayed. continue; b << uint8(i) << l.items[i]; b << uint8(LOOT_SLOT_TYPE_ALLOW_LOOT); ++itemsShown; } } break; } case ALL_PERMISSION: case OWNER_PERMISSION: { uint8 slot_type = lv.permission == OWNER_PERMISSION ? LOOT_SLOT_TYPE_OWNER : LOOT_SLOT_TYPE_ALLOW_LOOT; for (uint8 i = 0; i < l.items.size(); ++i) { if (!l.items[i].is_looted && !l.items[i].freeforall && l.items[i].conditions.empty() && l.items[i].AllowedForPlayer(lv.viewer)) { b << uint8(i) << l.items[i]; b << uint8(slot_type); ++itemsShown; } } break; } default: return b; } LootSlotType slotType = lv.permission == OWNER_PERMISSION ? LOOT_SLOT_TYPE_OWNER : LOOT_SLOT_TYPE_ALLOW_LOOT; NotNormalLootItemMap const& lootPlayerQuestItems = l.GetPlayerQuestItems(); NotNormalLootItemMap::const_iterator q_itr = lootPlayerQuestItems.find(lv.viewer->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) { LootItem &item = l.quest_items[qi->index]; if (!qi->is_looted && !item.is_looted) { b << uint8(l.items.size() + (qi - q_list->begin())); b << item; if (item.follow_loot_rules) { switch (lv.permission) { case MASTER_PERMISSION: b << uint8(LOOT_SLOT_TYPE_MASTER); break; case RESTRICTED_PERMISSION: b << (item.is_blocked ? uint8(LOOT_SLOT_TYPE_LOCKED) : uint8(slotType)); break; case GROUP_PERMISSION: case ROUND_ROBIN_PERMISSION: if (!item.is_blocked) b << uint8(LOOT_SLOT_TYPE_ALLOW_LOOT); else b << uint8(LOOT_SLOT_TYPE_ROLL_ONGOING); break; default: b << uint8(slotType); break; } } else b << uint8(slotType); ++itemsShown; } } } NotNormalLootItemMap const& lootPlayerFFAItems = l.GetPlayerFFAItems(); NotNormalLootItemMap::const_iterator ffa_itr = lootPlayerFFAItems.find(lv.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 &item = l.items[fi->index]; if (!fi->is_looted && !item.is_looted) { b << uint8(fi->index); b << item; b << uint8(slotType); ++itemsShown; } } } NotNormalLootItemMap const& lootPlayerNonQuestNonFFAConditionalItems = l.GetPlayerNonQuestNonFFAConditionalItems(); NotNormalLootItemMap::const_iterator nn_itr = lootPlayerNonQuestNonFFAConditionalItems.find(lv.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 &item = l.items[ci->index]; if (!ci->is_looted && !item.is_looted) { b << uint8(ci->index); b << item; switch (lv.permission) { case MASTER_PERMISSION: b << uint8(LOOT_SLOT_TYPE_MASTER); break; case RESTRICTED_PERMISSION: b << (item.is_blocked ? uint8(LOOT_SLOT_TYPE_LOCKED) : uint8(slotType)); break; case GROUP_PERMISSION: case ROUND_ROBIN_PERMISSION: if (!item.is_blocked) b << uint8(LOOT_SLOT_TYPE_ALLOW_LOOT); else b << uint8(LOOT_SLOT_TYPE_ROLL_ONGOING); break; default: b << uint8(slotType); break; } ++itemsShown; } } } //update number of items shown b.put(count_pos, itemsShown); return b; }