*Backed out changeset 3be01fb200a5
--HG-- branch : trunk
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "LootMgr.h"
|
||||
#include "Log.h"
|
||||
#include "ObjectMgr.h"
|
||||
@@ -25,6 +26,7 @@
|
||||
#include "Util.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "SpellMgr.h"
|
||||
|
||||
static Rates const qualityToRate[MAX_ITEM_QUALITY] = {
|
||||
RATE_DROP_ITEM_POOR, // ITEM_QUALITY_POOR
|
||||
RATE_DROP_ITEM_NORMAL, // ITEM_QUALITY_NORMAL
|
||||
@@ -34,6 +36,7 @@ static Rates const qualityToRate[MAX_ITEM_QUALITY] = {
|
||||
RATE_DROP_ITEM_LEGENDARY, // ITEM_QUALITY_LEGENDARY
|
||||
RATE_DROP_ITEM_ARTIFACT, // ITEM_QUALITY_ARTIFACT
|
||||
};
|
||||
|
||||
LootStore LootTemplates_Creature( "creature_loot_template", "creature entry", true);
|
||||
LootStore LootTemplates_Disenchant( "disenchant_loot_template", "item disenchant id", true);
|
||||
LootStore LootTemplates_Fishing( "fishing_loot_template", "area id", true);
|
||||
@@ -47,6 +50,7 @@ LootStore LootTemplates_Reference( "reference_loot_template", "reference i
|
||||
LootStore LootTemplates_Skinning( "skinning_loot_template", "creature skinning id", true);
|
||||
LootStore LootTemplates_Spell( "spell_loot_template", "spell id (random item creating)",false);
|
||||
|
||||
|
||||
class LootTemplate::LootGroup // A set of loot definitions for items (refs are not allowed)
|
||||
{
|
||||
public:
|
||||
@@ -57,14 +61,17 @@ class LootTemplate::LootGroup // A set of loot def
|
||||
void Process(Loot& loot) const; // Rolls an item from the group (if any) and adds the item to the loot
|
||||
float RawTotalChance() const; // Overall chance for the group (without equal chanced items)
|
||||
float TotalChance() const; // Overall chance for the group
|
||||
|
||||
void Verify(LootStore const& lootstore, uint32 id, uint32 group_id) const;
|
||||
void CollectLootIds(LootIdSet& set) const;
|
||||
void CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const;
|
||||
private:
|
||||
LootStoreItemList ExplicitlyChanced; // Entries with chances defined in DB
|
||||
LootStoreItemList EqualChanced; // Zero chances - every entry takes the same chance
|
||||
|
||||
LootStoreItem const * Roll() const; // Rolls an item from the group, returns NULL if all miss their chances
|
||||
};
|
||||
|
||||
//Remove all data and free all memory
|
||||
void LootStore::Clear()
|
||||
{
|
||||
@@ -72,6 +79,7 @@ void LootStore::Clear()
|
||||
delete itr->second;
|
||||
m_LootTemplates.clear();
|
||||
}
|
||||
|
||||
// Checks validity of the loot store
|
||||
// Actual checks are done within LootTemplate::Verify() which is called for every template
|
||||
void LootStore::Verify() const
|
||||
@@ -79,24 +87,31 @@ void LootStore::Verify() const
|
||||
for (LootTemplateMap::const_iterator i = m_LootTemplates.begin(); i != m_LootTemplates.end(); ++i )
|
||||
i->second->Verify(*this, i->first);
|
||||
}
|
||||
|
||||
// Loads a *_loot_template DB table into loot store
|
||||
// All checks of the loaded template are called from here, no error reports at loot generation required
|
||||
void LootStore::LoadLootTable()
|
||||
{
|
||||
LootTemplateMap::const_iterator tab;
|
||||
uint32 count = 0;
|
||||
|
||||
// Clearing store (for reloading case)
|
||||
Clear();
|
||||
|
||||
sLog.outString( "%s :", GetName());
|
||||
|
||||
// 0 1 2 3 4 5 6 7 8
|
||||
QueryResult *result = WorldDatabase.PQuery("SELECT entry, item, ChanceOrQuestChance, groupid, mincountOrRef, maxcount, lootcondition, condition_value1, condition_value2 FROM %s",GetName());
|
||||
|
||||
if (result)
|
||||
{
|
||||
barGoLink bar(result->GetRowCount());
|
||||
|
||||
do
|
||||
{
|
||||
Field *fields = result->Fetch();
|
||||
bar.step();
|
||||
|
||||
uint32 entry = fields[0].GetUInt32();
|
||||
uint32 item = fields[1].GetUInt32();
|
||||
float chanceOrQuestChance = fields[2].GetFloat();
|
||||
@@ -106,22 +121,28 @@ void LootStore::LoadLootTable()
|
||||
ConditionType condition = (ConditionType)fields[6].GetUInt8();
|
||||
uint32 cond_value1 = fields[7].GetUInt32();
|
||||
uint32 cond_value2 = fields[8].GetUInt32();
|
||||
|
||||
if(maxcount > std::numeric_limits<uint8>::max())
|
||||
{
|
||||
sLog.outErrorDb("Table '%s' entry %d item %d: maxcount value (%u) to large. must be less %u - skipped", GetName(), entry, item, maxcount,std::numeric_limits<uint8>::max());
|
||||
continue; // error already printed to log/console.
|
||||
}
|
||||
|
||||
|
||||
if(!PlayerCondition::IsValid(condition,cond_value1, cond_value2))
|
||||
{
|
||||
sLog.outErrorDb("... in table '%s' entry %u item %u", GetName(), entry, item);
|
||||
continue; // error already printed to log/console.
|
||||
}
|
||||
|
||||
// (condition + cond_value1/2) are converted into single conditionId
|
||||
uint16 conditionId = objmgr.GetConditionId(condition, cond_value1, cond_value2);
|
||||
|
||||
LootStoreItem storeitem = LootStoreItem(item, chanceOrQuestChance, group, conditionId, mincountOrRef, maxcount);
|
||||
|
||||
if (!storeitem.IsValid(*this,entry)) // Validity checks
|
||||
continue;
|
||||
|
||||
// Looking for the template of the entry
|
||||
// often entries are put together
|
||||
if (m_LootTemplates.empty() || tab->first != entry)
|
||||
@@ -136,12 +157,17 @@ void LootStore::LoadLootTable()
|
||||
}
|
||||
// else is empty - template Id and iter are the same
|
||||
// finally iter refers to already existed or just created <entry, LootTemplate>
|
||||
|
||||
// Adds current row to the template
|
||||
tab->second->AddEntry(storeitem);
|
||||
++count;
|
||||
|
||||
} while (result->NextRow());
|
||||
|
||||
delete result;
|
||||
|
||||
Verify(); // Checks validity of the loot store
|
||||
|
||||
sLog.outString();
|
||||
sLog.outString( ">> Loaded %u loot definitions (%lu templates)", count, (unsigned long)m_LootTemplates.size());
|
||||
}
|
||||
@@ -151,65 +177,84 @@ void LootStore::LoadLootTable()
|
||||
sLog.outErrorDb( ">> Loaded 0 loot definitions. DB table `%s` is empty.",GetName() );
|
||||
}
|
||||
}
|
||||
|
||||
bool LootStore::HaveQuestLootFor(uint32 loot_id) const
|
||||
{
|
||||
LootTemplateMap::const_iterator itr = m_LootTemplates.find(loot_id);
|
||||
if(itr == m_LootTemplates.end())
|
||||
return false;
|
||||
|
||||
// scan loot for quest items
|
||||
return itr->second->HasQuestDrop(m_LootTemplates);
|
||||
}
|
||||
|
||||
bool LootStore::HaveQuestLootForPlayer(uint32 loot_id,Player* player) const
|
||||
{
|
||||
LootTemplateMap::const_iterator tab = m_LootTemplates.find(loot_id);
|
||||
if (tab != m_LootTemplates.end())
|
||||
if (tab->second->HasQuestDropForPlayer(m_LootTemplates, player))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
LootTemplate const* LootStore::GetLootFor(uint32 loot_id) const
|
||||
{
|
||||
LootTemplateMap::const_iterator tab = m_LootTemplates.find(loot_id);
|
||||
|
||||
if (tab == m_LootTemplates.end())
|
||||
return NULL;
|
||||
|
||||
return tab->second;
|
||||
}
|
||||
|
||||
void LootStore::LoadAndCollectLootIds(LootIdSet& ids_set)
|
||||
{
|
||||
LoadLootTable();
|
||||
|
||||
for(LootTemplateMap::const_iterator tab = m_LootTemplates.begin(); tab != m_LootTemplates.end(); ++tab)
|
||||
ids_set.insert(tab->first);
|
||||
}
|
||||
|
||||
void LootStore::CheckLootRefs(LootIdSet* ref_set) const
|
||||
{
|
||||
for(LootTemplateMap::const_iterator ltItr = m_LootTemplates.begin(); ltItr != m_LootTemplates.end(); ++ltItr)
|
||||
ltItr->second->CheckLootRefs(m_LootTemplates,ref_set);
|
||||
}
|
||||
|
||||
void LootStore::ReportUnusedIds(LootIdSet const& ids_set) const
|
||||
{
|
||||
// all still listed ids isn't referenced
|
||||
for(LootIdSet::const_iterator itr = ids_set.begin(); itr != ids_set.end(); ++itr)
|
||||
sLog.outErrorDb("Table '%s' entry %d isn't %s and not referenced from loot, and then useless.", GetName(), *itr,GetEntryName());
|
||||
}
|
||||
|
||||
void LootStore::ReportNotExistedId(uint32 id) const
|
||||
{
|
||||
sLog.outErrorDb("Table '%s' entry %d (%s) not exist but used as loot id in DB.", GetName(), id,GetEntryName());
|
||||
}
|
||||
|
||||
//
|
||||
// --------- LootStoreItem ---------
|
||||
//
|
||||
|
||||
// Checks if the entry (quest, non-quest, reference) takes it's chance (at loot generation)
|
||||
// RATE_DROP_ITEMS is no longer used for all types of entries
|
||||
bool LootStoreItem::Roll(bool rate) const
|
||||
{
|
||||
if(chance>=100.0f)
|
||||
return true;
|
||||
|
||||
if(mincountOrRef < 0) // reference case
|
||||
return roll_chance_f(chance* (rate ? sWorld.getRate(RATE_DROP_ITEM_REFERENCED) : 1.0f));
|
||||
|
||||
ItemPrototype const *pProto = objmgr.GetItemPrototype(itemid);
|
||||
|
||||
float qualityModifier = pProto && rate ? sWorld.getRate(qualityToRate[pProto->Quality]) : 1.0f;
|
||||
|
||||
return roll_chance_f(chance*qualityModifier);
|
||||
}
|
||||
|
||||
// Checks correctness of values
|
||||
bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const
|
||||
{
|
||||
@@ -218,11 +263,13 @@ bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const
|
||||
sLog.outErrorDb("Table '%s' entry %d item %d: group (%u) must be less %u - skipped", store.GetName(), entry, itemid, group, 1 << 7);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mincountOrRef == 0)
|
||||
{
|
||||
sLog.outErrorDb("Table '%s' entry %d item %d: wrong mincountOrRef (%d) - skipped", store.GetName(), entry, itemid, mincountOrRef);
|
||||
return false;
|
||||
}
|
||||
|
||||
if( mincountOrRef > 0 ) // item (quest or non-quest) entry, maybe grouped
|
||||
{
|
||||
ItemPrototype const *proto = objmgr.GetItemPrototype(itemid);
|
||||
@@ -231,22 +278,26 @@ bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const
|
||||
sLog.outErrorDb("Table '%s' entry %d item %d: item entry not listed in `item_template` - skipped", store.GetName(), entry, itemid);
|
||||
return false;
|
||||
}
|
||||
|
||||
if( chance == 0 && group == 0) // Zero chance is allowed for grouped entries only
|
||||
{
|
||||
sLog.outErrorDb("Table '%s' entry %d item %d: equal-chanced grouped entry, but group not defined - skipped", store.GetName(), entry, itemid);
|
||||
return false;
|
||||
}
|
||||
|
||||
if( chance != 0 && chance < 0.000001f ) // loot with low chance
|
||||
{
|
||||
sLog.outErrorDb("Table '%s' entry %d item %d: low chance (%f) - skipped",
|
||||
store.GetName(), entry, itemid, chance);
|
||||
return false;
|
||||
}
|
||||
|
||||
if( maxcount < mincountOrRef) // wrong max count
|
||||
{
|
||||
sLog.outErrorDb("Table '%s' entry %d item %d: max count (%u) less that min count (%i) - skipped", store.GetName(), entry, itemid, uint32(maxcount), mincountOrRef);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
else // mincountOrRef < 0
|
||||
{
|
||||
@@ -260,17 +311,22 @@ bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const
|
||||
}
|
||||
return true; // Referenced template existence is checked at whole store level
|
||||
}
|
||||
|
||||
//
|
||||
// --------- LootItem ---------
|
||||
//
|
||||
|
||||
// Constructor, copies most fields from LootStoreItem and generates random count
|
||||
LootItem::LootItem(LootStoreItem const& li)
|
||||
{
|
||||
itemid = li.itemid;
|
||||
conditionId = li.conditionId;
|
||||
|
||||
ItemPrototype const* proto = objmgr.GetItemPrototype(itemid);
|
||||
freeforall = proto && (proto->Flags & ITEM_FLAGS_PARTY_LOOT);
|
||||
|
||||
needs_quest = li.needs_quest;
|
||||
|
||||
count = urand(li.mincountOrRef, li.maxcount); // constructor called for mincountOrRef > 0 only
|
||||
randomSuffix = GenerateEnchSuffixFactor(itemid);
|
||||
randomPropertyId = Item::GenerateItemRandomPropertyId(itemid);
|
||||
@@ -279,12 +335,14 @@ LootItem::LootItem(LootStoreItem const& li)
|
||||
is_underthreshold = 0;
|
||||
is_counted = 0;
|
||||
}
|
||||
|
||||
// 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 ( !objmgr.IsPlayerMeetToCondition(player,conditionId) )
|
||||
return false;
|
||||
|
||||
if ( needs_quest )
|
||||
{
|
||||
// Checking quests for quest-only drop (check only quests requirements in this case)
|
||||
@@ -298,11 +356,14 @@ bool LootItem::AllowedForPlayer(Player const * player) const
|
||||
if (pProto && pProto->StartQuest && player->GetQuestStatus(pProto->StartQuest) != QUEST_STATUS_NONE && !player->HasQuestForItem(itemid))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// --------- Loot ---------
|
||||
//
|
||||
|
||||
// Inserts the item into the loot (called by LootTemplate processors)
|
||||
void Loot::AddItem(LootStoreItem const & item)
|
||||
{
|
||||
@@ -314,6 +375,7 @@ void Loot::AddItem(LootStoreItem const & item)
|
||||
else if (items.size() < MAX_NR_LOOT_ITEMS) // Non-quest drop
|
||||
{
|
||||
items.push_back(LootItem(item));
|
||||
|
||||
// non-conditional one-player only items are counted here,
|
||||
// free for all items are counted in FillFFALoot(),
|
||||
// non-ffa conditionals are counted in FillNonQuestNonFFAConditionalLoot()
|
||||
@@ -325,21 +387,27 @@ void Loot::AddItem(LootStoreItem const & item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calls processor of corresponding LootTemplate (which handles everything including references)
|
||||
void Loot::FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner, bool personal)
|
||||
{
|
||||
// Must be provided
|
||||
if(!loot_owner)
|
||||
return;
|
||||
|
||||
LootTemplate const* tab = store.GetLootFor(loot_id);
|
||||
|
||||
if (!tab)
|
||||
{
|
||||
sLog.outErrorDb("Table '%s' loot id #%u used but it doesn't have records.",store.GetName(),loot_id);
|
||||
return;
|
||||
}
|
||||
|
||||
items.reserve(MAX_NR_LOOT_ITEMS);
|
||||
quest_items.reserve(MAX_NR_QUEST_ITEMS);
|
||||
|
||||
tab->Process(*this, store,store.IsRatesAllowed ()); // Processing is done there, callback via Loot::AddItem()
|
||||
|
||||
// Setting access rights for group loot case
|
||||
Group * pGroup=loot_owner->GetGroup();
|
||||
if(!personal && pGroup)
|
||||
@@ -352,22 +420,28 @@ void Loot::FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner,
|
||||
else
|
||||
FillNotNormalLootFor(loot_owner);
|
||||
}
|
||||
|
||||
void Loot::FillNotNormalLootFor(Player* pl)
|
||||
{
|
||||
uint32 plguid = pl->GetGUIDLow();
|
||||
|
||||
QuestItemMap::const_iterator qmapitr = PlayerQuestItems.find(plguid);
|
||||
if (qmapitr == PlayerQuestItems.end())
|
||||
FillQuestLoot(pl);
|
||||
|
||||
qmapitr = PlayerFFAItems.find(plguid);
|
||||
if (qmapitr == PlayerFFAItems.end())
|
||||
FillFFALoot(pl);
|
||||
|
||||
qmapitr = PlayerNonQuestNonFFAConditionalItems.find(plguid);
|
||||
if (qmapitr == PlayerNonQuestNonFFAConditionalItems.end())
|
||||
FillNonQuestNonFFAConditionalLoot(pl);
|
||||
}
|
||||
|
||||
QuestItemList* Loot::FillFFALoot(Player* player)
|
||||
{
|
||||
QuestItemList *ql = new QuestItemList();
|
||||
|
||||
for(uint8 i = 0; i < items.size(); ++i)
|
||||
{
|
||||
LootItem &item = items[i];
|
||||
@@ -382,26 +456,32 @@ QuestItemList* Loot::FillFFALoot(Player* player)
|
||||
delete ql;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PlayerFFAItems[player->GetGUIDLow()] = 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) )
|
||||
{
|
||||
ql->push_back(QuestItem(i));
|
||||
|
||||
// questitems get blocked when they first apper 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;
|
||||
|
||||
item.is_blocked = true;
|
||||
|
||||
if (items.size() + ql->size() == MAX_NR_LOOT_ITEMS)
|
||||
break;
|
||||
}
|
||||
@@ -411,12 +491,15 @@ QuestItemList* Loot::FillQuestLoot(Player* player)
|
||||
delete ql;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PlayerQuestItems[player->GetGUIDLow()] = ql;
|
||||
return ql;
|
||||
}
|
||||
|
||||
QuestItemList* Loot::FillNonQuestNonFFAConditionalLoot(Player* player)
|
||||
{
|
||||
QuestItemList *ql = new QuestItemList();
|
||||
|
||||
for(uint8 i = 0; i < items.size(); ++i)
|
||||
{
|
||||
LootItem &item = items[i];
|
||||
@@ -435,10 +518,13 @@ QuestItemList* Loot::FillNonQuestNonFFAConditionalLoot(Player* player)
|
||||
delete ql;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PlayerNonQuestNonFFAConditionalItems[player->GetGUIDLow()] = ql;
|
||||
return ql;
|
||||
}
|
||||
|
||||
//===================================================
|
||||
|
||||
void Loot::NotifyItemRemoved(uint8 lootIndex)
|
||||
{
|
||||
// notify all players that are looting this that the item was removed
|
||||
@@ -454,6 +540,7 @@ void Loot::NotifyItemRemoved(uint8 lootIndex)
|
||||
PlayersLooting.erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
void Loot::NotifyMoneyRemoved()
|
||||
{
|
||||
// notify all players that are looting this that the money was removed
|
||||
@@ -468,12 +555,14 @@ void Loot::NotifyMoneyRemoved()
|
||||
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
|
||||
|
||||
std::set<uint64>::iterator i_next;
|
||||
for(std::set<uint64>::iterator i = PlayersLooting.begin(); i != PlayersLooting.end(); i = i_next)
|
||||
{
|
||||
@@ -486,10 +575,12 @@ void Loot::NotifyQuestItemRemoved(uint8 questIndex)
|
||||
{
|
||||
// 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())
|
||||
pl->SendNotifyLootItemRemoved(items.size()+j);
|
||||
}
|
||||
@@ -498,6 +589,7 @@ void Loot::NotifyQuestItemRemoved(uint8 questIndex)
|
||||
PlayersLooting.erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
void Loot::generateMoneyLoot( uint32 minAmount, uint32 maxAmount )
|
||||
{
|
||||
if (maxAmount > 0)
|
||||
@@ -510,6 +602,7 @@ void Loot::generateMoneyLoot( uint32 minAmount, uint32 maxAmount )
|
||||
gold = uint32(urand(minAmount >> 8, maxAmount >> 8) * sWorld.getRate(RATE_DROP_MONEY)) << 8;
|
||||
}
|
||||
}
|
||||
|
||||
LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem **qitem, QuestItem **ffaitem, QuestItem **conditem)
|
||||
{
|
||||
LootItem* item = NULL;
|
||||
@@ -566,15 +659,19 @@ LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem **qite
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(is_looted)
|
||||
return NULL;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
uint32 Loot::GetMaxSlotInLootFor(Player* player) const
|
||||
{
|
||||
QuestItemMap::const_iterator itr = PlayerQuestItems.find(player->GetGUIDLow());
|
||||
return items.size() + (itr != PlayerQuestItems.end() ? itr->second->size() : 0);
|
||||
}
|
||||
|
||||
ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li)
|
||||
{
|
||||
b << uint32(li.itemid);
|
||||
@@ -585,6 +682,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li)
|
||||
//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)
|
||||
@@ -593,12 +691,17 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
|
||||
b << uint8(0); // item count
|
||||
return b; // nothing output more
|
||||
}
|
||||
|
||||
Loot &l = lv.loot;
|
||||
|
||||
uint8 itemsShown = 0;
|
||||
|
||||
//gold
|
||||
b << uint32(l.gold);
|
||||
|
||||
size_t count_pos = b.wpos(); // pos of item count byte
|
||||
b << uint8(0); // item count placeholder
|
||||
|
||||
switch (lv.permission)
|
||||
{
|
||||
case GROUP_PERMISSION:
|
||||
@@ -610,6 +713,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
|
||||
if (!l.items[i].is_looted && !l.items[i].freeforall && !l.items[i].conditionId && l.items[i].AllowedForPlayer(lv.viewer))
|
||||
{
|
||||
uint8 slot_type = (l.items[i].is_blocked || l.items[i].is_underthreshold) ? 0 : 1;
|
||||
|
||||
b << uint8(i) << l.items[i]; //send the index and the item if it's not looted, and blocked or under threshold, free for all items will be sent later, only one-player loots here
|
||||
b << uint8(slot_type); // 0 - get 1 - look only
|
||||
++itemsShown;
|
||||
@@ -635,6 +739,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
|
||||
default:
|
||||
return b; // nothing output more
|
||||
}
|
||||
|
||||
QuestItemMap const& lootPlayerQuestItems = l.GetPlayerQuestItems();
|
||||
QuestItemMap::const_iterator q_itr = lootPlayerQuestItems.find(lv.viewer->GetGUIDLow());
|
||||
if (q_itr != lootPlayerQuestItems.end())
|
||||
@@ -652,6 +757,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QuestItemMap const& lootPlayerFFAItems = l.GetPlayerFFAItems();
|
||||
QuestItemMap::const_iterator ffa_itr = lootPlayerFFAItems.find(lv.viewer->GetGUIDLow());
|
||||
if (ffa_itr != lootPlayerFFAItems.end())
|
||||
@@ -668,6 +774,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QuestItemMap const& lootPlayerNonQuestNonFFAConditionalItems = l.GetPlayerNonQuestNonFFAConditionalItems();
|
||||
QuestItemMap::const_iterator nn_itr = lootPlayerNonQuestNonFFAConditionalItems.find(lv.viewer->GetGUIDLow());
|
||||
if (nn_itr != lootPlayerNonQuestNonFFAConditionalItems.end())
|
||||
@@ -684,13 +791,17 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//update number of items shown
|
||||
b.put<uint8>(count_pos,itemsShown);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
//
|
||||
// --------- LootTemplate::LootGroup ---------
|
||||
//
|
||||
|
||||
// Adds an entry to the group (at loading stage)
|
||||
void LootTemplate::LootGroup::AddEntry(LootStoreItem& item)
|
||||
{
|
||||
@@ -699,16 +810,19 @@ void LootTemplate::LootGroup::AddEntry(LootStoreItem& item)
|
||||
else
|
||||
EqualChanced.push_back(item);
|
||||
}
|
||||
|
||||
// Rolls an item from the group, returns NULL if all miss their chances
|
||||
LootStoreItem const * LootTemplate::LootGroup::Roll() const
|
||||
{
|
||||
if (!ExplicitlyChanced.empty()) // First explicitly chanced entries are checked
|
||||
{
|
||||
float Roll = rand_chance();
|
||||
|
||||
for (uint32 i=0; i<ExplicitlyChanced.size(); ++i) //check each explicitly chanced entry in the template and modify its chance based on quality.
|
||||
{
|
||||
if(ExplicitlyChanced[i].chance>=100.0f)
|
||||
return &ExplicitlyChanced[i];
|
||||
|
||||
Roll -= ExplicitlyChanced[i].chance;
|
||||
if (Roll < 0)
|
||||
return &ExplicitlyChanced[i];
|
||||
@@ -716,8 +830,10 @@ LootStoreItem const * LootTemplate::LootGroup::Roll() const
|
||||
}
|
||||
if (!EqualChanced.empty()) // If nothing selected yet - an item is taken from equal-chanced part
|
||||
return &EqualChanced[irand(0, EqualChanced.size()-1)];
|
||||
|
||||
return NULL; // Empty drop from the group
|
||||
}
|
||||
|
||||
// True if group includes at least 1 quest drop entry
|
||||
bool LootTemplate::LootGroup::HasQuestDrop() const
|
||||
{
|
||||
@@ -729,6 +845,7 @@ bool LootTemplate::LootGroup::HasQuestDrop() const
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// True if group includes at least 1 quest drop entry for active quests of the player
|
||||
bool LootTemplate::LootGroup::HasQuestDropForPlayer(Player const * player) const
|
||||
{
|
||||
@@ -740,6 +857,7 @@ bool LootTemplate::LootGroup::HasQuestDropForPlayer(Player const * player) const
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Rolls an item from the group (if any takes its chance) and adds the item to the loot
|
||||
void LootTemplate::LootGroup::Process(Loot& loot) const
|
||||
{
|
||||
@@ -747,23 +865,30 @@ void LootTemplate::LootGroup::Process(Loot& loot) const
|
||||
if (item != NULL)
|
||||
loot.AddItem(*item);
|
||||
}
|
||||
|
||||
// Overall chance for the group without equal chanced items
|
||||
float LootTemplate::LootGroup::RawTotalChance() const
|
||||
{
|
||||
float result = 0;
|
||||
|
||||
for (LootStoreItemList::const_iterator i=ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i)
|
||||
if ( !i->needs_quest )
|
||||
result += i->chance;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Overall chance for the group
|
||||
float LootTemplate::LootGroup::TotalChance() const
|
||||
{
|
||||
float result = RawTotalChance();
|
||||
|
||||
if (!EqualChanced.empty() && result < 100.0f)
|
||||
return 100.0f;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void LootTemplate::LootGroup::Verify(LootStore const& lootstore, uint32 id, uint32 group_id) const
|
||||
{
|
||||
float chance = RawTotalChance();
|
||||
@@ -771,11 +896,13 @@ void LootTemplate::LootGroup::Verify(LootStore const& lootstore, uint32 id, uint
|
||||
{
|
||||
sLog.outErrorDb("Table '%s' entry %u group %d has total chance > 100%% (%f)", lootstore.GetName(), id, group_id, chance);
|
||||
}
|
||||
|
||||
if(chance >= 100.0f && !EqualChanced.empty())
|
||||
{
|
||||
sLog.outErrorDb("Table '%s' entry %u group %d has items with chance=0%% but group total chance >= 100%% (%f)", lootstore.GetName(), id, group_id, chance);
|
||||
}
|
||||
}
|
||||
|
||||
void LootTemplate::LootGroup::CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const
|
||||
{
|
||||
for (LootStoreItemList::const_iterator ieItr=ExplicitlyChanced.begin(); ieItr != ExplicitlyChanced.end(); ++ieItr)
|
||||
@@ -788,6 +915,7 @@ void LootTemplate::LootGroup::CheckLootRefs(LootTemplateMap const& store, LootId
|
||||
ref_set->erase(-ieItr->mincountOrRef);
|
||||
}
|
||||
}
|
||||
|
||||
for (LootStoreItemList::const_iterator ieItr=EqualChanced.begin(); ieItr != EqualChanced.end(); ++ieItr)
|
||||
{
|
||||
if(ieItr->mincountOrRef < 0)
|
||||
@@ -799,9 +927,11 @@ void LootTemplate::LootGroup::CheckLootRefs(LootTemplateMap const& store, LootId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// --------- LootTemplate ---------
|
||||
//
|
||||
|
||||
// Adds an entry to the group (at loading stage)
|
||||
void LootTemplate::AddEntry(LootStoreItem& item)
|
||||
{
|
||||
@@ -814,6 +944,7 @@ void LootTemplate::AddEntry(LootStoreItem& item)
|
||||
else // Non-grouped entries and references are stored together
|
||||
Entries.push_back(item);
|
||||
}
|
||||
|
||||
// Rolls for every item in the template and adds the rolled items the the loot
|
||||
void LootTemplate::Process(Loot& loot, LootStore const& store, bool rate, uint8 groupId) const
|
||||
{
|
||||
@@ -821,29 +952,36 @@ void LootTemplate::Process(Loot& loot, LootStore const& store, bool rate, uint8
|
||||
{
|
||||
if (groupId > Groups.size())
|
||||
return; // Error message already printed at loading stage
|
||||
|
||||
Groups[groupId-1].Process(loot);
|
||||
return;
|
||||
}
|
||||
|
||||
// Rolling non-grouped items
|
||||
for (LootStoreItemList::const_iterator i = Entries.begin() ; i != Entries.end() ; ++i )
|
||||
{
|
||||
if (!i->Roll(rate))
|
||||
continue; // Bad luck for the entry
|
||||
|
||||
if (i->mincountOrRef < 0) // References processing
|
||||
{
|
||||
LootTemplate const* Referenced = LootTemplates_Reference.GetLootFor(-i->mincountOrRef);
|
||||
|
||||
if(!Referenced)
|
||||
continue; // Error message already printed at loading stage
|
||||
|
||||
for (uint32 loop=0; loop < i->maxcount; ++loop )// Ref multiplicator
|
||||
Referenced->Process(loot, store, rate, i->group);
|
||||
}
|
||||
else // Plain entries (not a reference, not grouped)
|
||||
loot.AddItem(*i); // Chance is already checked, just add
|
||||
}
|
||||
|
||||
// Now processing groups
|
||||
for (LootGroups::const_iterator i = Groups.begin( ) ; i != Groups.end( ) ; ++i )
|
||||
i->Process(loot);
|
||||
}
|
||||
|
||||
// True if template includes at least 1 quest drop entry
|
||||
bool LootTemplate::HasQuestDrop(LootTemplateMap const& store, uint8 groupId) const
|
||||
{
|
||||
@@ -853,6 +991,7 @@ bool LootTemplate::HasQuestDrop(LootTemplateMap const& store, uint8 groupId) con
|
||||
return false; // Error message [should be] already printed at loading stage
|
||||
return Groups[groupId-1].HasQuestDrop();
|
||||
}
|
||||
|
||||
for (LootStoreItemList::const_iterator i = Entries.begin(); i != Entries.end(); ++i )
|
||||
{
|
||||
if (i->mincountOrRef < 0) // References
|
||||
@@ -866,12 +1005,15 @@ bool LootTemplate::HasQuestDrop(LootTemplateMap const& store, uint8 groupId) con
|
||||
else if ( i->needs_quest )
|
||||
return true; // quest drop found
|
||||
}
|
||||
|
||||
// Now processing groups
|
||||
for (LootGroups::const_iterator i = Groups.begin() ; i != Groups.end() ; ++i )
|
||||
if (i->HasQuestDrop())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// True if template includes at least 1 quest drop for an active quest of the player
|
||||
bool LootTemplate::HasQuestDropForPlayer(LootTemplateMap const& store, Player const* player, uint8 groupId) const
|
||||
{
|
||||
@@ -881,6 +1023,7 @@ bool LootTemplate::HasQuestDropForPlayer(LootTemplateMap const& store, Player co
|
||||
return false; // Error message already printed at loading stage
|
||||
return Groups[groupId-1].HasQuestDropForPlayer(player);
|
||||
}
|
||||
|
||||
// Checking non-grouped entries
|
||||
for (LootStoreItemList::const_iterator i = Entries.begin() ; i != Entries.end() ; ++i )
|
||||
{
|
||||
@@ -895,20 +1038,25 @@ bool LootTemplate::HasQuestDropForPlayer(LootTemplateMap const& store, Player co
|
||||
else if ( player->HasQuestForItem(i->itemid) )
|
||||
return true; // active quest drop found
|
||||
}
|
||||
|
||||
// Now checking groups
|
||||
for (LootGroups::const_iterator i = Groups.begin(); i != Groups.end(); ++i )
|
||||
if (i->HasQuestDropForPlayer(player))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Checks integrity of the template
|
||||
void LootTemplate::Verify(LootStore const& lootstore, uint32 id) const
|
||||
{
|
||||
// Checking group chances
|
||||
for (uint32 i=0; i < Groups.size(); ++i)
|
||||
Groups[i].Verify(lootstore,id,i+1);
|
||||
|
||||
// TODO: References validity checks
|
||||
}
|
||||
|
||||
void LootTemplate::CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const
|
||||
{
|
||||
for(LootStoreItemList::const_iterator ieItr = Entries.begin(); ieItr != Entries.end(); ++ieItr)
|
||||
@@ -921,13 +1069,16 @@ void LootTemplate::CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_se
|
||||
ref_set->erase(-ieItr->mincountOrRef);
|
||||
}
|
||||
}
|
||||
|
||||
for(LootGroups::const_iterator grItr = Groups.begin(); grItr != Groups.end(); ++grItr)
|
||||
grItr->CheckLootRefs(store,ref_set);
|
||||
}
|
||||
|
||||
void LoadLootTemplates_Creature()
|
||||
{
|
||||
LootIdSet ids_set, ids_setUsed;
|
||||
LootTemplates_Creature.LoadAndCollectLootIds(ids_set);
|
||||
|
||||
// remove real entries and check existence loot
|
||||
for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i )
|
||||
{
|
||||
@@ -944,13 +1095,16 @@ void LoadLootTemplates_Creature()
|
||||
}
|
||||
for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr)
|
||||
ids_set.erase(*itr);
|
||||
|
||||
// output error for any still listed (not referenced from appropriate table) ids
|
||||
LootTemplates_Creature.ReportUnusedIds(ids_set);
|
||||
}
|
||||
|
||||
void LoadLootTemplates_Disenchant()
|
||||
{
|
||||
LootIdSet ids_set, ids_setUsed;
|
||||
LootTemplates_Disenchant.LoadAndCollectLootIds(ids_set);
|
||||
|
||||
// remove real entries and check existence loot
|
||||
for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i )
|
||||
{
|
||||
@@ -970,10 +1124,12 @@ void LoadLootTemplates_Disenchant()
|
||||
// output error for any still listed (not referenced from appropriate table) ids
|
||||
LootTemplates_Disenchant.ReportUnusedIds(ids_set);
|
||||
}
|
||||
|
||||
void LoadLootTemplates_Fishing()
|
||||
{
|
||||
LootIdSet ids_set;
|
||||
LootTemplates_Fishing.LoadAndCollectLootIds(ids_set);
|
||||
|
||||
// remove real entries and check existence loot
|
||||
for(uint32 i = 1; i < sAreaStore.GetNumRows(); ++i )
|
||||
{
|
||||
@@ -981,13 +1137,16 @@ void LoadLootTemplates_Fishing()
|
||||
if(ids_set.count(areaEntry->ID))
|
||||
ids_set.erase(areaEntry->ID);
|
||||
}
|
||||
|
||||
// output error for any still listed (not referenced from appropriate table) ids
|
||||
LootTemplates_Fishing.ReportUnusedIds(ids_set);
|
||||
}
|
||||
|
||||
void LoadLootTemplates_Gameobject()
|
||||
{
|
||||
LootIdSet ids_set, ids_setUsed;
|
||||
LootTemplates_Gameobject.LoadAndCollectLootIds(ids_set);
|
||||
|
||||
// remove real entries and check existence loot
|
||||
for(uint32 i = 1; i < sGOStorage.MaxEntry; ++i )
|
||||
{
|
||||
@@ -1004,43 +1163,54 @@ void LoadLootTemplates_Gameobject()
|
||||
}
|
||||
for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr)
|
||||
ids_set.erase(*itr);
|
||||
|
||||
// output error for any still listed (not referenced from appropriate table) ids
|
||||
LootTemplates_Gameobject.ReportUnusedIds(ids_set);
|
||||
}
|
||||
|
||||
void LoadLootTemplates_Item()
|
||||
{
|
||||
LootIdSet ids_set;
|
||||
LootTemplates_Item.LoadAndCollectLootIds(ids_set);
|
||||
|
||||
// remove real entries and check existence loot
|
||||
for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i )
|
||||
if(ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i))
|
||||
if(ids_set.count(proto->ItemId))
|
||||
ids_set.erase(proto->ItemId);
|
||||
|
||||
// output error for any still listed (not referenced from appropriate table) ids
|
||||
LootTemplates_Item.ReportUnusedIds(ids_set);
|
||||
}
|
||||
|
||||
void LoadLootTemplates_Milling()
|
||||
{
|
||||
LootIdSet ids_set;
|
||||
LootTemplates_Milling.LoadAndCollectLootIds(ids_set);
|
||||
|
||||
// remove real entries and check existence loot
|
||||
for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i )
|
||||
{
|
||||
ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i);
|
||||
if(!proto)
|
||||
continue;
|
||||
|
||||
if((proto->BagFamily & BAG_FAMILY_MASK_HERBS)==0)
|
||||
continue;
|
||||
|
||||
if(ids_set.count(proto->ItemId))
|
||||
ids_set.erase(proto->ItemId);
|
||||
}
|
||||
|
||||
// output error for any still listed (not referenced from appropriate table) ids
|
||||
LootTemplates_Milling.ReportUnusedIds(ids_set);
|
||||
}
|
||||
|
||||
void LoadLootTemplates_Pickpocketing()
|
||||
{
|
||||
LootIdSet ids_set, ids_setUsed;
|
||||
LootTemplates_Pickpocketing.LoadAndCollectLootIds(ids_set);
|
||||
|
||||
// remove real entries and check existence loot
|
||||
for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i )
|
||||
{
|
||||
@@ -1057,37 +1227,46 @@ void LoadLootTemplates_Pickpocketing()
|
||||
}
|
||||
for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr)
|
||||
ids_set.erase(*itr);
|
||||
|
||||
// output error for any still listed (not referenced from appropriate table) ids
|
||||
LootTemplates_Pickpocketing.ReportUnusedIds(ids_set);
|
||||
}
|
||||
|
||||
void LoadLootTemplates_Prospecting()
|
||||
{
|
||||
LootIdSet ids_set;
|
||||
LootTemplates_Prospecting.LoadAndCollectLootIds(ids_set);
|
||||
|
||||
// remove real entries and check existence loot
|
||||
for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i )
|
||||
{
|
||||
ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i);
|
||||
if(!proto)
|
||||
continue;
|
||||
|
||||
if((proto->BagFamily & BAG_FAMILY_MASK_MINING_SUPP)==0)
|
||||
continue;
|
||||
|
||||
if(ids_set.count(proto->ItemId))
|
||||
ids_set.erase(proto->ItemId);
|
||||
}
|
||||
|
||||
// output error for any still listed (not referenced from appropriate table) ids
|
||||
LootTemplates_Prospecting.ReportUnusedIds(ids_set);
|
||||
}
|
||||
|
||||
void LoadLootTemplates_QuestMail()
|
||||
{
|
||||
LootIdSet ids_set;
|
||||
LootTemplates_QuestMail.LoadAndCollectLootIds(ids_set);
|
||||
|
||||
// remove real entries and check existence loot
|
||||
ObjectMgr::QuestMap const& questMap = objmgr.GetQuestTemplates();
|
||||
for(ObjectMgr::QuestMap::const_iterator itr = questMap.begin(); itr != questMap.end(); ++itr )
|
||||
{
|
||||
if(!itr->second->GetRewMailTemplateId())
|
||||
continue;
|
||||
|
||||
if(ids_set.count(itr->first))
|
||||
ids_set.erase(itr->first);
|
||||
/* disabled reporting: some quest mails not include items
|
||||
@@ -1095,13 +1274,16 @@ void LoadLootTemplates_QuestMail()
|
||||
LootTemplates_QuestMail.ReportNotExistedId(itr->first);
|
||||
*/
|
||||
}
|
||||
|
||||
// output error for any still listed (not referenced from appropriate table) ids
|
||||
LootTemplates_QuestMail.ReportUnusedIds(ids_set);
|
||||
}
|
||||
|
||||
void LoadLootTemplates_Skinning()
|
||||
{
|
||||
LootIdSet ids_set, ids_setUsed;
|
||||
LootTemplates_Skinning.LoadAndCollectLootIds(ids_set);
|
||||
|
||||
// remove real entries and check existence loot
|
||||
for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i )
|
||||
{
|
||||
@@ -1118,22 +1300,27 @@ void LoadLootTemplates_Skinning()
|
||||
}
|
||||
for(LootIdSet::const_iterator itr = ids_setUsed.begin(); itr != ids_setUsed.end(); ++itr)
|
||||
ids_set.erase(*itr);
|
||||
|
||||
// output error for any still listed (not referenced from appropriate table) ids
|
||||
LootTemplates_Skinning.ReportUnusedIds(ids_set);
|
||||
}
|
||||
|
||||
void LoadLootTemplates_Spell()
|
||||
{
|
||||
LootIdSet ids_set;
|
||||
LootTemplates_Spell.LoadAndCollectLootIds(ids_set);
|
||||
|
||||
// remove real entries and check existence loot
|
||||
for(uint32 spell_id = 1; spell_id < sSpellStore.GetNumRows(); ++spell_id)
|
||||
{
|
||||
SpellEntry const* spellInfo = sSpellStore.LookupEntry (spell_id);
|
||||
if(!spellInfo)
|
||||
continue;
|
||||
|
||||
// possible cases
|
||||
if( !IsLootCraftingSpell(spellInfo))
|
||||
continue;
|
||||
|
||||
if(!ids_set.count(spell_id))
|
||||
{
|
||||
// not report about not trainable spells (optionally supported by DB)
|
||||
@@ -1146,13 +1333,16 @@ void LoadLootTemplates_Spell()
|
||||
else
|
||||
ids_set.erase(spell_id);
|
||||
}
|
||||
|
||||
// output error for any still listed (not referenced from appropriate table) ids
|
||||
LootTemplates_Spell.ReportUnusedIds(ids_set);
|
||||
}
|
||||
|
||||
void LoadLootTemplates_Reference()
|
||||
{
|
||||
LootIdSet ids_set;
|
||||
LootTemplates_Reference.LoadAndCollectLootIds(ids_set);
|
||||
|
||||
// check references and remove used
|
||||
LootTemplates_Creature.CheckLootRefs(&ids_set);
|
||||
LootTemplates_Fishing.CheckLootRefs(&ids_set);
|
||||
@@ -1165,6 +1355,7 @@ void LoadLootTemplates_Reference()
|
||||
LootTemplates_Prospecting.CheckLootRefs(&ids_set);
|
||||
LootTemplates_QuestMail.CheckLootRefs(&ids_set);
|
||||
LootTemplates_Reference.CheckLootRefs(&ids_set);
|
||||
|
||||
// output error for any still listed ids (not referenced from any loot table)
|
||||
LootTemplates_Reference.ReportUnusedIds(ids_set);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user