mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-22 18:15:31 +01:00
808 lines
29 KiB
C++
808 lines
29 KiB
C++
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#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*>(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<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.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<uint8>(count_pos, itemsShown);
|
|
|
|
return b;
|
|
}
|