2 * Copyright (C) 2005-2013 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "ObjectMgr.h"
22 #include "ProgressBar.h"
25 #include "SharedDefines.h"
27 #include "DBCStores.h"
28 #include "SQLStorages.h"
30 static eConfigFloatValues
const qualityToRate
[MAX_ITEM_QUALITY
] =
32 CONFIG_FLOAT_RATE_DROP_ITEM_POOR
, // ITEM_QUALITY_POOR
33 CONFIG_FLOAT_RATE_DROP_ITEM_NORMAL
, // ITEM_QUALITY_NORMAL
34 CONFIG_FLOAT_RATE_DROP_ITEM_UNCOMMON
, // ITEM_QUALITY_UNCOMMON
35 CONFIG_FLOAT_RATE_DROP_ITEM_RARE
, // ITEM_QUALITY_RARE
36 CONFIG_FLOAT_RATE_DROP_ITEM_EPIC
, // ITEM_QUALITY_EPIC
37 CONFIG_FLOAT_RATE_DROP_ITEM_LEGENDARY
, // ITEM_QUALITY_LEGENDARY
38 CONFIG_FLOAT_RATE_DROP_ITEM_ARTIFACT
, // ITEM_QUALITY_ARTIFACT
41 LootStore
LootTemplates_Creature("creature_loot_template", "creature entry", true);
42 LootStore
LootTemplates_Disenchant("disenchant_loot_template", "item disenchant id", true);
43 LootStore
LootTemplates_Fishing("fishing_loot_template", "area id", true);
44 LootStore
LootTemplates_Gameobject("gameobject_loot_template", "gameobject lootid", true);
45 LootStore
LootTemplates_Item("item_loot_template", "item entry with ITEM_FLAG_LOOTABLE", true);
46 LootStore
LootTemplates_Mail("mail_loot_template", "mail template id", false);
47 LootStore
LootTemplates_Milling("milling_loot_template", "item entry (herb)", true);
48 LootStore
LootTemplates_Pickpocketing("pickpocketing_loot_template", "creature pickpocket lootid", true);
49 LootStore
LootTemplates_Prospecting("prospecting_loot_template", "item entry (ore)", true);
50 LootStore
LootTemplates_Reference("reference_loot_template", "reference id", false);
51 LootStore
LootTemplates_Skinning("skinning_loot_template", "creature skinning id", true);
52 LootStore
LootTemplates_Spell("spell_loot_template", "spell id (random item creating)", false);
54 class LootTemplate::LootGroup
// A set of loot definitions for items (refs are not allowed)
57 void AddEntry(LootStoreItem
& item
); // Adds an entry to the group (at loading stage)
58 bool HasQuestDrop() const; // True if group includes at least 1 quest drop entry
59 bool HasQuestDropForPlayer(Player
const* player
) const;
60 // The same for active quests of the player
61 void Process(Loot
& loot
) const; // Rolls an item from the group (if any) and adds the item to the loot
62 float RawTotalChance() const; // Overall chance for the group (without equal chanced items)
63 float TotalChance() const; // Overall chance for the group
65 void Verify(LootStore
const& lootstore
, uint32 id
, uint32 group_id
) const;
66 void CollectLootIds(LootIdSet
& set
) const;
67 void CheckLootRefs(LootIdSet
* ref_set
) const;
69 LootStoreItemList ExplicitlyChanced
; // Entries with chances defined in DB
70 LootStoreItemList EqualChanced
; // Zero chances - every entry takes the same chance
72 LootStoreItem
const* Roll() const; // Rolls an item from the group, returns NULL if all miss their chances
75 // Remove all data and free all memory
76 void LootStore::Clear()
78 for (LootTemplateMap::const_iterator itr
= m_LootTemplates
.begin(); itr
!= m_LootTemplates
.end(); ++itr
)
80 m_LootTemplates
.clear();
83 // Checks validity of the loot store
84 // Actual checks are done within LootTemplate::Verify() which is called for every template
85 void LootStore::Verify() const
87 for (LootTemplateMap::const_iterator i
= m_LootTemplates
.begin(); i
!= m_LootTemplates
.end(); ++i
)
88 i
->second
->Verify(*this, i
->first
);
91 // Loads a *_loot_template DB table into loot store
92 // All checks of the loaded template are called from here, no error reports at loot generation required
93 void LootStore::LoadLootTable()
95 LootTemplateMap::const_iterator tab
;
98 // Clearing store (for reloading case)
101 sLog
.outString("%s :", GetName());
104 QueryResult
* result
= WorldDatabase
.PQuery("SELECT entry, item, ChanceOrQuestChance, groupid, mincountOrRef, maxcount, condition_id FROM %s", GetName());
108 BarGoLink
bar(result
->GetRowCount());
112 Field
* fields
= result
->Fetch();
115 uint32 entry
= fields
[0].GetUInt32();
116 uint32 item
= abs(fields
[1].GetInt32());
117 uint8 type
= fields
[1].GetInt32() > 0 ? LOOT_ITEM_TYPE_ITEM
: LOOT_ITEM_TYPE_CURRENCY
;
118 float chanceOrQuestChance
= fields
[2].GetFloat();
119 uint8 group
= fields
[3].GetUInt8();
120 int32 mincountOrRef
= fields
[4].GetInt32();
121 uint32 maxcount
= fields
[5].GetUInt32();
122 uint16 conditionId
= fields
[6].GetUInt16();
124 if (type
== LOOT_ITEM_TYPE_ITEM
&& maxcount
> std::numeric_limits
<uint8
>::max())
126 sLog
.outErrorDb("Table '%s' entry %d item %d: maxcount value (%u) too large. must be less than %u - skipped", GetName(), entry
, item
, maxcount
, std::numeric_limits
<uint8
>::max());
127 continue; // error already printed to log/console.
130 if (mincountOrRef
< 0 && conditionId
)
132 sLog
.outErrorDb("Table '%s' entry %u mincountOrRef %i < 0 and not allowed has condition, skipped", GetName(), entry
, mincountOrRef
);
138 const PlayerCondition
* condition
= sConditionStorage
.LookupEntry
<PlayerCondition
>(conditionId
);
141 sLog
.outErrorDb("Table `%s` for entry %u, item %u has condition_id %u that does not exist in `conditions`, ignoring", GetName(), entry
, item
, conditionId
);
146 LootStoreItem storeitem
= LootStoreItem(item
, type
, chanceOrQuestChance
, group
, conditionId
, mincountOrRef
, maxcount
);
148 if (!storeitem
.IsValid(*this, entry
)) // Validity checks
151 // Looking for the template of the entry
152 // often entries are put together
153 if (m_LootTemplates
.empty() || tab
->first
!= entry
)
155 // Searching the template (in case template Id changed)
156 tab
= m_LootTemplates
.find(entry
);
157 if (tab
== m_LootTemplates
.end())
159 std::pair
< LootTemplateMap::iterator
, bool > pr
= m_LootTemplates
.insert(LootTemplateMap::value_type(entry
, new LootTemplate
));
163 // else is empty - template Id and iter are the same
164 // finally iter refers to already existing or just created <entry, LootTemplate>
166 // Adds current row to the template
167 tab
->second
->AddEntry(storeitem
);
171 while (result
->NextRow());
175 Verify(); // Checks validity of the loot store
178 sLog
.outString(">> Loaded %u loot definitions (" SIZEFMTD
" templates)", count
, m_LootTemplates
.size());
183 sLog
.outErrorDb(">> Loaded 0 loot definitions. DB table `%s` is empty.", GetName());
187 bool LootStore::HaveQuestLootFor(uint32 loot_id
) const
189 LootTemplateMap::const_iterator itr
= m_LootTemplates
.find(loot_id
);
190 if (itr
== m_LootTemplates
.end())
193 // scan loot for quest items
194 return itr
->second
->HasQuestDrop(m_LootTemplates
);
197 bool LootStore::HaveQuestLootForPlayer(uint32 loot_id
, Player
* player
) const
199 LootTemplateMap::const_iterator tab
= m_LootTemplates
.find(loot_id
);
200 if (tab
!= m_LootTemplates
.end())
201 if (tab
->second
->HasQuestDropForPlayer(m_LootTemplates
, player
))
207 LootTemplate
const* LootStore::GetLootFor(uint32 loot_id
) const
209 LootTemplateMap::const_iterator tab
= m_LootTemplates
.find(loot_id
);
211 if (tab
== m_LootTemplates
.end())
217 void LootStore::LoadAndCollectLootIds(LootIdSet
& ids_set
)
221 for (LootTemplateMap::const_iterator tab
= m_LootTemplates
.begin(); tab
!= m_LootTemplates
.end(); ++tab
)
222 ids_set
.insert(tab
->first
);
225 void LootStore::CheckLootRefs(LootIdSet
* ref_set
) const
227 for (LootTemplateMap::const_iterator ltItr
= m_LootTemplates
.begin(); ltItr
!= m_LootTemplates
.end(); ++ltItr
)
228 ltItr
->second
->CheckLootRefs(ref_set
);
231 void LootStore::ReportUnusedIds(LootIdSet
const& ids_set
) const
233 // all still listed ids isn't referenced
234 for (LootIdSet::const_iterator itr
= ids_set
.begin(); itr
!= ids_set
.end(); ++itr
)
235 sLog
.outErrorDb("Table '%s' entry %d isn't %s and not referenced from loot, and then useless.", GetName(), *itr
, GetEntryName());
238 void LootStore::ReportNotExistedId(uint32 id
) const
240 sLog
.outErrorDb("Table '%s' entry %d (%s) not exist but used as loot id in DB.", GetName(), id
, GetEntryName());
244 // --------- LootStoreItem ---------
247 // Checks if the entry (quest, non-quest, reference) takes it's chance (at loot generation)
248 // RATE_DROP_ITEMS is no longer used for all types of entries
249 bool LootStoreItem::Roll(bool rate
) const
251 if (chance
>= 100.0f
)
254 if (mincountOrRef
< 0) // reference case
255 return roll_chance_f(chance
* (rate
? sWorld
.getConfig(CONFIG_FLOAT_RATE_DROP_ITEM_REFERENCED
) : 1.0f
));
257 if (type
== LOOT_ITEM_TYPE_ITEM
)
259 ItemPrototype
const* pProto
= ObjectMgr::GetItemPrototype(itemid
);
261 float qualityModifier
= pProto
&& rate
? sWorld
.getConfig(qualityToRate
[pProto
->Quality
]) : 1.0f
;
263 return roll_chance_f(chance
* qualityModifier
);
265 else if (type
== LOOT_ITEM_TYPE_CURRENCY
)
266 return roll_chance_f(chance
* (rate
? sWorld
.getConfig(CONFIG_FLOAT_RATE_DROP_CURRENCY
) : 1.0f
));
271 // Checks correctness of values
272 bool LootStoreItem::IsValid(LootStore
const& store
, uint32 entry
) const
274 if (group
>= 1 << 7) // it stored in 7 bit field
276 sLog
.outErrorDb("Table '%s' entry %d item %d: group (%u) must be less %u - skipped", store
.GetName(), entry
, itemid
, group
, 1 << 7);
280 if (group
&& type
== LOOT_ITEM_TYPE_CURRENCY
)
282 sLog
.outErrorDb("Table '%s' entry %d currency %d: group is set, but currencies must not have group - skipped", store
.GetName(), entry
, itemid
, group
, 1 << 7);
286 if (mincountOrRef
== 0)
288 sLog
.outErrorDb("Table '%s' entry %d item %d: wrong mincountOrRef (%d) - skipped", store
.GetName(), entry
, itemid
, mincountOrRef
);
292 if (mincountOrRef
> 0) // item (quest or non-quest) entry, maybe grouped
294 if (type
== LOOT_ITEM_TYPE_ITEM
)
296 ItemPrototype
const* proto
= ObjectMgr::GetItemPrototype(itemid
);
299 sLog
.outErrorDb("Table '%s' entry %d item %d: item entry not listed in `item_template` - skipped", store
.GetName(), entry
, itemid
);
303 else if (type
== LOOT_ITEM_TYPE_CURRENCY
)
305 CurrencyTypesEntry
const* currency
= sCurrencyTypesStore
.LookupEntry(itemid
);
308 sLog
.outErrorDb("Table '%s' entry %d: currency entry %u not exists - skipped", store
.GetName(), entry
, itemid
);
314 sLog
.outErrorDb("Table '%s' entry %d: has unknown item %u with type %u - skipped", store
.GetName(), entry
, itemid
, type
);
318 if (chance
== 0 && group
== 0) // Zero chance is allowed for grouped entries only
320 sLog
.outErrorDb("Table '%s' entry %d item %d: equal-chanced grouped entry, but group not defined - skipped", store
.GetName(), entry
, itemid
);
324 if (chance
!= 0 && chance
< 0.000001f
) // loot with low chance
326 sLog
.outErrorDb("Table '%s' entry %d item %d: low chance (%f) - skipped", store
.GetName(), entry
, itemid
, chance
);
330 if (maxcount
< mincountOrRef
) // wrong max count
332 sLog
.outErrorDb("Table '%s' entry %d item %d: max count (%u) less that min count (%i) - skipped", store
.GetName(), entry
, itemid
, uint32(maxcount
), mincountOrRef
);
336 else // mincountOrRef < 0
340 sLog
.outErrorDb("Table '%s' entry %d item %d: negative chance is specified for a reference, skipped", store
.GetName(), entry
, itemid
);
343 else if (chance
== 0) // no chance for the reference
345 sLog
.outErrorDb("Table '%s' entry %d item %d: zero chance is specified for a reference, skipped", store
.GetName(), entry
, itemid
);
349 return true; // Referenced template existence is checked at whole store level
353 // --------- LootItem ---------
356 // Constructor, copies most fields from LootStoreItem and generates random count
357 LootItem::LootItem(LootStoreItem
const& li
)
361 conditionId
= li
.conditionId
;
362 currency
= type
== LOOT_ITEM_TYPE_CURRENCY
;
363 count
= urand(li
.mincountOrRef
, li
.maxcount
); // constructor called for mincountOrRef > 0 only
367 is_underthreshold
= 0;
375 randomPropertyId
= 0;
376 count
= uint32(count
* sWorld
.getConfig(CONFIG_FLOAT_RATE_DROP_CURRENCY_AMOUNT
));
380 ItemPrototype
const* proto
= ObjectMgr::GetItemPrototype(itemid
);
381 freeforall
= proto
&& (proto
->Flags
& ITEM_FLAG_PARTY_LOOT
);
382 needs_quest
= li
.needs_quest
;
383 randomSuffix
= GenerateEnchSuffixFactor(itemid
);
384 randomPropertyId
= Item::GenerateItemRandomPropertyId(itemid
);
388 LootItem::LootItem(uint32 itemid_
, uint8 type_
, uint32 count_
, uint32 randomSuffix_
, int32 randomPropertyId_
)
394 randomSuffix
= randomSuffix_
;
395 randomPropertyId
= randomPropertyId_
;
398 is_underthreshold
= 0;
400 currency
= type
== LOOT_ITEM_TYPE_CURRENCY
;
407 ItemPrototype
const* proto
= ObjectMgr::GetItemPrototype(itemid
);
408 freeforall
= proto
&& (proto
->Flags
& ITEM_FLAG_PARTY_LOOT
);
412 // Basic checks for player/item compatibility - if false no chance to see the item in the loot
413 bool LootItem::AllowedForPlayer(Player
const* player
) const
415 // DB conditions check
416 if (conditionId
&& !sObjectMgr
.IsPlayerMeetToCondition(player
, conditionId
))
419 if (type
== LOOT_ITEM_TYPE_ITEM
)
421 ItemPrototype
const* pProto
= ObjectMgr::GetItemPrototype(itemid
);
425 // not show loot for not own team
426 if ((pProto
->Flags2
& ITEM_FLAG2_HORDE_ONLY
) && player
->GetTeam() != HORDE
)
429 if ((pProto
->Flags2
& ITEM_FLAG2_ALLIANCE_ONLY
) && player
->GetTeam() != ALLIANCE
)
434 // Checking quests for quest-only drop (check only quests requirements in this case)
435 if (!player
->HasQuestForItem(itemid
))
440 // Not quest only drop (check quest starting items for already accepted non-repeatable quests)
441 if (pProto
->StartQuest
&& player
->GetQuestStatus(pProto
->StartQuest
) != QUEST_STATUS_NONE
&& !player
->HasQuestForItem(itemid
))
445 else if (type
== LOOT_ITEM_TYPE_CURRENCY
)
447 CurrencyTypesEntry
const * currency
= sCurrencyTypesStore
.LookupEntry(itemid
);
451 if (!player
->isGameMaster())
453 if (currency
->ID
== CURRENCY_CONQUEST_ARENA_META
|| currency
->ID
== CURRENCY_CONQUEST_BG_META
)
456 if (currency
->Category
== CURRENCY_CATEGORY_ARCHAEOLOGY
&& !player
->HasSkill(SKILL_ARCHAEOLOGY
))
464 LootSlotType
LootItem::GetSlotTypeForSharedLoot(PermissionTypes permission
, Player
* viewer
, bool condition_ok
/*= false*/) const
466 // ignore currencies, looted items, FFA (each player get own copy) and not allowed items
467 if (currency
|| is_looted
|| freeforall
|| (conditionId
&& !condition_ok
) || !AllowedForPlayer(viewer
))
468 return MAX_LOOT_SLOT_TYPE
;
473 return LOOT_SLOT_NORMAL
;
474 case GROUP_PERMISSION
:
475 return (is_blocked
|| is_underthreshold
) ? LOOT_SLOT_NORMAL
: LOOT_SLOT_VIEW
;
476 case MASTER_PERMISSION
:
477 return !is_underthreshold
? LOOT_SLOT_MASTER
: LOOT_SLOT_NORMAL
;
478 case OWNER_PERMISSION
:
479 return LOOT_SLOT_OWNER
;
481 return MAX_LOOT_SLOT_TYPE
;
486 // --------- Loot ---------
489 // Inserts the item into the loot (called by LootTemplate processors)
490 void Loot::AddItem(LootStoreItem
const& item
)
492 if (item
.needs_quest
) // Quest drop
494 if (m_questItems
.size() < MAX_NR_QUEST_ITEMS
)
495 m_questItems
.push_back(LootItem(item
));
497 else if (items
.size() < MAX_NR_LOOT_ITEMS
) // Non-quest drop
499 items
.push_back(LootItem(item
));
501 // non-conditional one-player only items are counted here,
502 // currencies are counter in FillCurrencyLoot,
503 // free for all items are counted in FillFFALoot(),
504 // non-ffa conditionals are counted in FillNonQuestNonFFANonCurrencyConditionalLoot()
505 if (!item
.conditionId
&& item
.type
== LOOT_ITEM_TYPE_ITEM
)
507 ItemPrototype
const* proto
= ObjectMgr::GetItemPrototype(item
.itemid
);
508 if (!proto
|| !(proto
->Flags
& ITEM_FLAG_PARTY_LOOT
))
514 // Calls processor of corresponding LootTemplate (which handles everything including references)
515 bool Loot::FillLoot(uint32 loot_id
, LootStore
const& store
, Player
* loot_owner
, bool personal
, bool noEmptyError
)
521 LootTemplate
const* tab
= store
.GetLootFor(loot_id
);
526 sLog
.outErrorDb("Table '%s' loot id #%u used but it doesn't have records.", store
.GetName(), loot_id
);
530 items
.reserve(MAX_NR_LOOT_ITEMS
);
531 m_questItems
.reserve(MAX_NR_QUEST_ITEMS
);
533 tab
->Process(*this, store
, store
.IsRatesAllowed()); // Processing is done there, callback via Loot::AddItem()
535 // Setting access rights for group loot case
536 Group
* pGroup
= loot_owner
->GetGroup();
537 if (!personal
&& pGroup
)
539 for (GroupReference
* itr
= pGroup
->GetFirstMember(); itr
!= NULL
; itr
= itr
->next())
540 if (Player
* pl
= itr
->getSource())
541 FillNotNormalLootFor(pl
);
543 // ... for personal loot
545 FillNotNormalLootFor(loot_owner
);
550 void Loot::FillNotNormalLootFor(Player
* pl
)
552 uint32 plguid
= pl
->GetGUIDLow();
554 QuestItemMap::const_iterator qmapitr
= m_playerCurrencies
.find(plguid
);
555 if (qmapitr
== m_playerCurrencies
.end())
556 FillCurrencyLoot(pl
);
558 qmapitr
= m_playerQuestItems
.find(plguid
);
559 if (qmapitr
== m_playerQuestItems
.end())
562 qmapitr
= m_playerFFAItems
.find(plguid
);
563 if (qmapitr
== m_playerFFAItems
.end())
566 qmapitr
= m_playerNonQuestNonFFANonCurrencyConditionalItems
.find(plguid
);
567 if (qmapitr
== m_playerNonQuestNonFFANonCurrencyConditionalItems
.end())
568 FillNonQuestNonFFANonCurrencyConditionalLoot(pl
);
571 QuestItemList
* Loot::FillCurrencyLoot(Player
* player
)
573 QuestItemList
* ql
= new QuestItemList();
575 for (uint8 i
= 0; i
< items
.size(); ++i
)
577 LootItem
& item
= items
[i
];
578 if (!item
.is_looted
&& item
.currency
&& item
.AllowedForPlayer(player
))
580 ql
->push_back(QuestItem(i
));
590 m_playerCurrencies
[player
->GetGUIDLow()] = ql
;
594 QuestItemList
* Loot::FillFFALoot(Player
* player
)
596 QuestItemList
* ql
= new QuestItemList();
598 for (uint8 i
= 0; i
< items
.size(); ++i
)
600 LootItem
& item
= items
[i
];
601 if (!item
.is_looted
&& item
.freeforall
&& item
.AllowedForPlayer(player
))
603 ql
->push_back(QuestItem(i
));
613 m_playerFFAItems
[player
->GetGUIDLow()] = ql
;
617 QuestItemList
* Loot::FillQuestLoot(Player
* player
)
619 if (items
.size() == MAX_NR_LOOT_ITEMS
) return NULL
;
620 QuestItemList
* ql
= new QuestItemList();
622 for (uint8 i
= 0; i
< m_questItems
.size(); ++i
)
624 LootItem
& item
= m_questItems
[i
];
625 if (!item
.is_looted
&& item
.AllowedForPlayer(player
))
627 ql
->push_back(QuestItem(i
));
629 // questitems get blocked when they first apper in a
630 // player's quest vector
632 // increase once if one looter only, looter-times if free for all
633 if (item
.freeforall
|| !item
.is_blocked
)
636 item
.is_blocked
= true;
638 if (items
.size() + ql
->size() == MAX_NR_LOOT_ITEMS
)
648 m_playerQuestItems
[player
->GetGUIDLow()] = ql
;
652 QuestItemList
* Loot::FillNonQuestNonFFANonCurrencyConditionalLoot(Player
* player
)
654 QuestItemList
* ql
= new QuestItemList();
656 for (uint8 i
= 0; i
< items
.size(); ++i
)
658 LootItem
& item
= items
[i
];
659 if (!item
.is_looted
&& !item
.freeforall
&& !item
.currency
&& item
.conditionId
&& item
.AllowedForPlayer(player
))
661 ql
->push_back(QuestItem(i
));
662 if (!item
.is_counted
)
665 item
.is_counted
= true;
675 m_playerNonQuestNonFFANonCurrencyConditionalItems
[player
->GetGUIDLow()] = ql
;
679 //===================================================
681 void Loot::NotifyItemRemoved(uint8 lootIndex
)
683 // notify all players that are looting this that the item was removed
684 // convert the index to the slot the player sees
685 GuidSet::iterator i_next
;
686 for (GuidSet::iterator i
= m_playersLooting
.begin(); i
!= m_playersLooting
.end(); i
= i_next
)
690 if (Player
* pl
= ObjectAccessor::FindPlayer(*i
))
691 pl
->SendNotifyLootItemRemoved(lootIndex
);
693 m_playersLooting
.erase(i
);
697 void Loot::NotifyMoneyRemoved()
699 // notify all players that are looting this that the money was removed
700 GuidSet::iterator i_next
;
701 for (GuidSet::iterator i
= m_playersLooting
.begin(); i
!= m_playersLooting
.end(); i
= i_next
)
705 if (Player
* pl
= ObjectAccessor::FindPlayer(*i
))
706 pl
->SendNotifyLootMoneyRemoved();
708 m_playersLooting
.erase(i
);
712 void Loot::NotifyQuestItemRemoved(uint8 questIndex
)
714 // when a free for all questitem is looted
715 // all players will get notified of it being removed
716 // (other questitems can be looted by each group member)
717 // bit inefficient but isnt called often
719 GuidSet::iterator i_next
;
720 for (GuidSet::iterator i
= m_playersLooting
.begin(); i
!= m_playersLooting
.end(); i
= i_next
)
724 if (Player
* pl
= ObjectAccessor::FindPlayer(*i
))
726 QuestItemMap::const_iterator pq
= m_playerQuestItems
.find(pl
->GetGUIDLow());
727 if (pq
!= m_playerQuestItems
.end() && pq
->second
)
729 // find where/if the player has the given item in it's vector
730 QuestItemList
& pql
= *pq
->second
;
733 for (j
= 0; j
< pql
.size(); ++j
)
734 if (pql
[j
].index
== questIndex
)
738 pl
->SendNotifyLootItemRemoved(items
.size() + j
);
742 m_playersLooting
.erase(i
);
746 void Loot::generateMoneyLoot(uint32 minAmount
, uint32 maxAmount
)
750 if (maxAmount
<= minAmount
)
751 gold
= uint32(maxAmount
* sWorld
.getConfig(CONFIG_FLOAT_RATE_DROP_MONEY
));
752 else if ((maxAmount
- minAmount
) < 32700)
753 gold
= uint32(urand(minAmount
, maxAmount
) * sWorld
.getConfig(CONFIG_FLOAT_RATE_DROP_MONEY
));
755 gold
= uint32(urand(minAmount
>> 8, maxAmount
>> 8) * sWorld
.getConfig(CONFIG_FLOAT_RATE_DROP_MONEY
)) << 8;
759 LootItem
* Loot::LootItemInSlot(uint32 lootSlot
, Player
* player
, QuestItem
** qitem
, QuestItem
** ffaitem
, QuestItem
** conditem
, QuestItem
** currency
)
761 LootItem
* item
= NULL
;
762 bool is_looted
= true;
763 if (lootSlot
>= items
.size())
765 uint32 questSlot
= lootSlot
- items
.size();
766 QuestItemMap::const_iterator itr
= m_playerQuestItems
.find(player
->GetGUIDLow());
767 if (itr
!= m_playerQuestItems
.end() && questSlot
< itr
->second
->size())
769 QuestItem
* qitem2
= &itr
->second
->at(questSlot
);
772 item
= &m_questItems
[qitem2
->index
];
773 is_looted
= qitem2
->is_looted
;
778 item
= &items
[lootSlot
];
779 is_looted
= item
->is_looted
;
782 QuestItemMap::const_iterator itr
= m_playerCurrencies
.find(player
->GetGUIDLow());
783 if (itr
!= m_playerCurrencies
.end())
785 for (QuestItemList::const_iterator iter
= itr
->second
->begin(); iter
!= itr
->second
->end(); ++iter
)
787 if (iter
->index
== lootSlot
)
789 QuestItem
* currency2
= (QuestItem
*) & (*iter
);
791 *currency
= currency2
;
792 is_looted
= currency2
->is_looted
;
798 else if (item
->freeforall
)
800 QuestItemMap::const_iterator itr
= m_playerFFAItems
.find(player
->GetGUIDLow());
801 if (itr
!= m_playerFFAItems
.end())
803 for (QuestItemList::const_iterator iter
= itr
->second
->begin(); iter
!= itr
->second
->end(); ++iter
)
804 if (iter
->index
== lootSlot
)
806 QuestItem
* ffaitem2
= (QuestItem
*) & (*iter
);
809 is_looted
= ffaitem2
->is_looted
;
814 else if (item
->conditionId
)
816 QuestItemMap::const_iterator itr
= m_playerNonQuestNonFFANonCurrencyConditionalItems
.find(player
->GetGUIDLow());
817 if (itr
!= m_playerNonQuestNonFFANonCurrencyConditionalItems
.end())
819 for (QuestItemList::const_iterator iter
= itr
->second
->begin(); iter
!= itr
->second
->end(); ++iter
)
821 if (iter
->index
== lootSlot
)
823 QuestItem
* conditem2
= (QuestItem
*) & (*iter
);
825 *conditem
= conditem2
;
826 is_looted
= conditem2
->is_looted
;
840 uint32
Loot::GetMaxSlotInLootFor(Player
* player
) const
842 QuestItemMap::const_iterator itr
= m_playerQuestItems
.find(player
->GetGUIDLow());
843 return items
.size() + (itr
!= m_playerQuestItems
.end() ? itr
->second
->size() : 0);
846 ByteBuffer
& operator<<(ByteBuffer
& b
, LootItem
const& li
)
848 if (li
.type
== LOOT_ITEM_TYPE_ITEM
)
850 b
<< uint32(li
.itemid
);
851 b
<< uint32(li
.count
); // nr of items of this type
852 b
<< uint32(ObjectMgr::GetItemPrototype(li
.itemid
)->DisplayInfoID
);
853 b
<< uint32(li
.randomSuffix
);
854 b
<< uint32(li
.randomPropertyId
);
856 else if (li
.type
== LOOT_ITEM_TYPE_CURRENCY
)
858 b
<< uint32(li
.itemid
);
859 b
<< uint32(li
.count
);
861 // b << uint8(0); // slot type - will send after this function call
865 ByteBuffer
& operator<<(ByteBuffer
& b
, LootView
const& lv
)
867 if (lv
.permission
== NONE_PERMISSION
)
869 b
<< uint32(0); // gold
870 b
<< uint8(0); // item count
871 b
<< uint8(0); // currency count
877 uint8 itemsShown
= 0;
878 uint8 currenciesShown
= 0;
880 b
<< uint32(l
.gold
); // gold
882 size_t count_pos
= b
.wpos(); // pos of item count byte
883 b
<< uint8(0); // item count placeholder
884 size_t currency_count_pos
= b
.wpos(); // pos of currency count byte
885 b
<< uint8(0); // currency count placeholder
887 for (uint8 i
= 0; i
< l
.items
.size(); ++i
)
889 LootSlotType slot_type
= l
.items
[i
].GetSlotTypeForSharedLoot(lv
.permission
, lv
.viewer
);
890 if (slot_type
>= MAX_LOOT_SLOT_TYPE
)
893 b
<< uint8(i
) << l
.items
[i
];
894 b
<< uint8(slot_type
); // 0 - get 1 - look only 2 - master selection
898 QuestItemMap
const& lootPlayerNonQuestNonFFAConditionalItems
= l
.GetPlayerNonQuestNonFFANonCurrencyConditionalItems();
899 QuestItemMap::const_iterator nn_itr
= lootPlayerNonQuestNonFFAConditionalItems
.find(lv
.viewer
->GetGUIDLow());
900 if (nn_itr
!= lootPlayerNonQuestNonFFAConditionalItems
.end())
902 QuestItemList
* conditional_list
= nn_itr
->second
;
903 for (QuestItemList::const_iterator ci
= conditional_list
->begin() ; ci
!= conditional_list
->end(); ++ci
)
905 LootItem
& item
= l
.items
[ci
->index
];
907 LootSlotType slot_type
= item
.GetSlotTypeForSharedLoot(lv
.permission
, lv
.viewer
, !ci
->is_looted
);
908 if (slot_type
>= MAX_LOOT_SLOT_TYPE
)
911 b
<< uint8(ci
->index
) << item
;
912 b
<< uint8(slot_type
); // allow loot
917 // in next cases used same slot type for all items
918 LootSlotType slot_type
= lv
.permission
== OWNER_PERMISSION
? LOOT_SLOT_OWNER
: LOOT_SLOT_NORMAL
;
920 QuestItemMap
const& lootPlayerQuestItems
= l
.GetPlayerQuestItems();
921 QuestItemMap::const_iterator q_itr
= lootPlayerQuestItems
.find(lv
.viewer
->GetGUIDLow());
922 if (q_itr
!= lootPlayerQuestItems
.end())
924 QuestItemList
* q_list
= q_itr
->second
;
925 for (QuestItemList::const_iterator qi
= q_list
->begin() ; qi
!= q_list
->end(); ++qi
)
927 LootItem
& item
= l
.m_questItems
[qi
->index
];
928 if (!qi
->is_looted
&& !item
.is_looted
)
930 b
<< uint8(l
.items
.size() + (qi
- q_list
->begin()));
932 b
<< uint8(slot_type
); // allow loot
938 QuestItemMap
const& lootPlayerFFAItems
= l
.GetPlayerFFAItems();
939 QuestItemMap::const_iterator ffa_itr
= lootPlayerFFAItems
.find(lv
.viewer
->GetGUIDLow());
940 if (ffa_itr
!= lootPlayerFFAItems
.end())
942 QuestItemList
* ffa_list
= ffa_itr
->second
;
943 for (QuestItemList::const_iterator fi
= ffa_list
->begin() ; fi
!= ffa_list
->end(); ++fi
)
945 LootItem
& item
= l
.items
[fi
->index
];
946 if (!fi
->is_looted
&& !item
.is_looted
)
948 b
<< uint8(fi
->index
) << item
;
949 b
<< uint8(slot_type
); // allow loot
955 QuestItemMap
const& lootPlayerCurrencies
= l
.GetPlayerCurrencies();
956 QuestItemMap::const_iterator currency_itr
= lootPlayerCurrencies
.find(lv
.viewer
->GetGUIDLow());
957 if (currency_itr
!= lootPlayerCurrencies
.end())
959 QuestItemList
* currency_list
= currency_itr
->second
;
960 for (QuestItemList::const_iterator ci
= currency_list
->begin() ; ci
!= currency_list
->end(); ++ci
)
962 LootItem
& item
= l
.items
[ci
->index
];
963 if (!ci
->is_looted
&& !item
.is_looted
)
965 b
<< uint8(ci
->index
) << item
;
971 // update number of items and currencies shown
972 b
.put
<uint8
>(count_pos
, itemsShown
);
973 b
.put
<uint8
>(currency_count_pos
, currenciesShown
);
979 // --------- LootTemplate::LootGroup ---------
982 // Adds an entry to the group (at loading stage)
983 void LootTemplate::LootGroup::AddEntry(LootStoreItem
& item
)
985 if (item
.chance
!= 0)
986 ExplicitlyChanced
.push_back(item
);
988 EqualChanced
.push_back(item
);
991 // Rolls an item from the group, returns NULL if all miss their chances
992 LootStoreItem
const* LootTemplate::LootGroup::Roll() const
994 if (!ExplicitlyChanced
.empty()) // First explicitly chanced entries are checked
996 float Roll
= rand_chance_f();
998 for (uint32 i
= 0; i
< ExplicitlyChanced
.size(); ++i
) // check each explicitly chanced entry in the template and modify its chance based on quality.
1000 if (ExplicitlyChanced
[i
].chance
>= 100.0f
)
1001 return &ExplicitlyChanced
[i
];
1003 Roll
-= ExplicitlyChanced
[i
].chance
;
1005 return &ExplicitlyChanced
[i
];
1008 if (!EqualChanced
.empty()) // If nothing selected yet - an item is taken from equal-chanced part
1009 return &EqualChanced
[irand(0, EqualChanced
.size() - 1)];
1011 return NULL
; // Empty drop from the group
1014 // True if group includes at least 1 quest drop entry
1015 bool LootTemplate::LootGroup::HasQuestDrop() const
1017 for (LootStoreItemList::const_iterator i
= ExplicitlyChanced
.begin(); i
!= ExplicitlyChanced
.end(); ++i
)
1020 for (LootStoreItemList::const_iterator i
= EqualChanced
.begin(); i
!= EqualChanced
.end(); ++i
)
1026 // True if group includes at least 1 quest drop entry for active quests of the player
1027 bool LootTemplate::LootGroup::HasQuestDropForPlayer(Player
const* player
) const
1029 for (LootStoreItemList::const_iterator i
= ExplicitlyChanced
.begin(); i
!= ExplicitlyChanced
.end(); ++i
)
1030 if (player
->HasQuestForItem(i
->itemid
))
1032 for (LootStoreItemList::const_iterator i
= EqualChanced
.begin(); i
!= EqualChanced
.end(); ++i
)
1033 if (player
->HasQuestForItem(i
->itemid
))
1038 // Rolls an item from the group (if any takes its chance) and adds the item to the loot
1039 void LootTemplate::LootGroup::Process(Loot
& loot
) const
1041 LootStoreItem
const* item
= Roll();
1043 loot
.AddItem(*item
);
1046 // Overall chance for the group without equal chanced items
1047 float LootTemplate::LootGroup::RawTotalChance() const
1051 for (LootStoreItemList::const_iterator i
= ExplicitlyChanced
.begin(); i
!= ExplicitlyChanced
.end(); ++i
)
1052 if (!i
->needs_quest
)
1053 result
+= i
->chance
;
1058 // Overall chance for the group
1059 float LootTemplate::LootGroup::TotalChance() const
1061 float result
= RawTotalChance();
1063 if (!EqualChanced
.empty() && result
< 100.0f
)
1069 void LootTemplate::LootGroup::Verify(LootStore
const& lootstore
, uint32 id
, uint32 group_id
) const
1071 float chance
= RawTotalChance();
1072 if (chance
> 101.0f
) // TODO: replace with 100% when DBs will be ready
1074 sLog
.outErrorDb("Table '%s' entry %u group %d has total chance > 100%% (%f)", lootstore
.GetName(), id
, group_id
, chance
);
1077 if (chance
>= 100.0f
&& !EqualChanced
.empty())
1079 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
);
1083 void LootTemplate::LootGroup::CheckLootRefs(LootIdSet
* ref_set
) const
1085 for (LootStoreItemList::const_iterator ieItr
= ExplicitlyChanced
.begin(); ieItr
!= ExplicitlyChanced
.end(); ++ieItr
)
1087 if (ieItr
->mincountOrRef
< 0)
1089 if (!LootTemplates_Reference
.GetLootFor(-ieItr
->mincountOrRef
))
1090 LootTemplates_Reference
.ReportNotExistedId(-ieItr
->mincountOrRef
);
1092 ref_set
->erase(-ieItr
->mincountOrRef
);
1096 for (LootStoreItemList::const_iterator ieItr
= EqualChanced
.begin(); ieItr
!= EqualChanced
.end(); ++ieItr
)
1098 if (ieItr
->mincountOrRef
< 0)
1100 if (!LootTemplates_Reference
.GetLootFor(-ieItr
->mincountOrRef
))
1101 LootTemplates_Reference
.ReportNotExistedId(-ieItr
->mincountOrRef
);
1103 ref_set
->erase(-ieItr
->mincountOrRef
);
1109 // --------- LootTemplate ---------
1112 // Adds an entry to the group (at loading stage)
1113 void LootTemplate::AddEntry(LootStoreItem
& item
)
1115 if (item
.group
> 0 && item
.mincountOrRef
> 0) // Group
1117 if (item
.group
>= Groups
.size())
1118 Groups
.resize(item
.group
); // Adds new group the the loot template if needed
1119 Groups
[item
.group
- 1].AddEntry(item
); // Adds new entry to the group
1121 else // Non-grouped entries and references are stored together
1122 Entries
.push_back(item
);
1125 // Rolls for every item in the template and adds the rolled items the the loot
1126 void LootTemplate::Process(Loot
& loot
, LootStore
const& store
, bool rate
, uint8 groupId
) const
1128 if (groupId
) // Group reference uses own processing of the group
1130 if (groupId
> Groups
.size())
1131 return; // Error message already printed at loading stage
1133 Groups
[groupId
- 1].Process(loot
);
1137 // Rolling non-grouped items
1138 for (LootStoreItemList::const_iterator i
= Entries
.begin() ; i
!= Entries
.end() ; ++i
)
1141 continue; // Bad luck for the entry
1143 if (i
->mincountOrRef
< 0 && i
->type
== LOOT_ITEM_TYPE_ITEM
) // References processing
1145 LootTemplate
const* Referenced
= LootTemplates_Reference
.GetLootFor(-i
->mincountOrRef
);
1148 continue; // Error message already printed at loading stage
1150 for (uint32 loop
= 0; loop
< i
->maxcount
; ++loop
) // Ref multiplicator
1151 Referenced
->Process(loot
, store
, rate
, i
->group
);
1153 else // Plain entries (not a reference, not grouped)
1154 loot
.AddItem(*i
); // Chance is already checked, just add
1157 // Now processing groups
1158 for (LootGroups::const_iterator i
= Groups
.begin() ; i
!= Groups
.end() ; ++i
)
1162 // True if template includes at least 1 quest drop entry
1163 bool LootTemplate::HasQuestDrop(LootTemplateMap
const& store
, uint8 groupId
) const
1165 if (groupId
) // Group reference
1167 if (groupId
> Groups
.size())
1168 return false; // Error message [should be] already printed at loading stage
1169 return Groups
[groupId
- 1].HasQuestDrop();
1172 for (LootStoreItemList::const_iterator i
= Entries
.begin(); i
!= Entries
.end(); ++i
)
1174 if (i
->mincountOrRef
< 0) // References
1176 LootTemplateMap::const_iterator Referenced
= store
.find(-i
->mincountOrRef
);
1177 if (Referenced
== store
.end())
1178 continue; // Error message [should be] already printed at loading stage
1179 if (Referenced
->second
->HasQuestDrop(store
, i
->group
))
1182 else if (i
->needs_quest
)
1183 return true; // quest drop found
1186 // Now processing groups
1187 for (LootGroups::const_iterator i
= Groups
.begin() ; i
!= Groups
.end() ; ++i
)
1188 if (i
->HasQuestDrop())
1194 // True if template includes at least 1 quest drop for an active quest of the player
1195 bool LootTemplate::HasQuestDropForPlayer(LootTemplateMap
const& store
, Player
const* player
, uint8 groupId
) const
1197 if (groupId
) // Group reference
1199 if (groupId
> Groups
.size())
1200 return false; // Error message already printed at loading stage
1201 return Groups
[groupId
- 1].HasQuestDropForPlayer(player
);
1204 // Checking non-grouped entries
1205 for (LootStoreItemList::const_iterator i
= Entries
.begin() ; i
!= Entries
.end() ; ++i
)
1207 if (i
->mincountOrRef
< 0) // References processing
1209 LootTemplateMap::const_iterator Referenced
= store
.find(-i
->mincountOrRef
);
1210 if (Referenced
== store
.end())
1211 continue; // Error message already printed at loading stage
1212 if (Referenced
->second
->HasQuestDropForPlayer(store
, player
, i
->group
))
1215 else if (player
->HasQuestForItem(i
->itemid
))
1216 return true; // active quest drop found
1219 // Now checking groups
1220 for (LootGroups::const_iterator i
= Groups
.begin(); i
!= Groups
.end(); ++i
)
1221 if (i
->HasQuestDropForPlayer(player
))
1227 // Checks integrity of the template
1228 void LootTemplate::Verify(LootStore
const& lootstore
, uint32 id
) const
1230 // Checking group chances
1231 for (uint32 i
= 0; i
< Groups
.size(); ++i
)
1232 Groups
[i
].Verify(lootstore
, id
, i
+ 1);
1234 // TODO: References validity checks
1237 void LootTemplate::CheckLootRefs(LootIdSet
* ref_set
) const
1239 for (LootStoreItemList::const_iterator ieItr
= Entries
.begin(); ieItr
!= Entries
.end(); ++ieItr
)
1241 if (ieItr
->mincountOrRef
< 0)
1243 if (!LootTemplates_Reference
.GetLootFor(-ieItr
->mincountOrRef
))
1244 LootTemplates_Reference
.ReportNotExistedId(-ieItr
->mincountOrRef
);
1246 ref_set
->erase(-ieItr
->mincountOrRef
);
1250 for (LootGroups::const_iterator grItr
= Groups
.begin(); grItr
!= Groups
.end(); ++grItr
)
1251 grItr
->CheckLootRefs(ref_set
);
1254 void LoadLootTemplates_Creature()
1256 LootIdSet ids_set
, ids_setUsed
;
1257 LootTemplates_Creature
.LoadAndCollectLootIds(ids_set
);
1259 // remove real entries and check existence loot
1260 for (uint32 i
= 1; i
< sCreatureStorage
.GetMaxEntry(); ++i
)
1262 if (CreatureInfo
const* cInfo
= sCreatureStorage
.LookupEntry
<CreatureInfo
>(i
))
1264 if (uint32 lootid
= cInfo
->lootid
)
1266 if (ids_set
.find(lootid
) == ids_set
.end())
1267 LootTemplates_Creature
.ReportNotExistedId(lootid
);
1269 ids_setUsed
.insert(lootid
);
1273 for (LootIdSet::const_iterator itr
= ids_setUsed
.begin(); itr
!= ids_setUsed
.end(); ++itr
)
1274 ids_set
.erase(*itr
);
1276 // for alterac valley we've defined Player-loot inside creature_loot_template id=0
1277 // this hack is used, so that we won't need to create an extra table player_loot_template for just one case
1280 // output error for any still listed (not referenced from appropriate table) ids
1281 LootTemplates_Creature
.ReportUnusedIds(ids_set
);
1284 void LoadLootTemplates_Disenchant()
1286 LootIdSet ids_set
, ids_setUsed
;
1287 LootTemplates_Disenchant
.LoadAndCollectLootIds(ids_set
);
1289 // remove real entries and check existence loot
1290 for (uint32 i
= 1; i
< sItemStorage
.GetMaxEntry(); ++i
)
1292 if (ItemPrototype
const* proto
= sItemStorage
.LookupEntry
<ItemPrototype
>(i
))
1294 if (uint32 lootid
= proto
->DisenchantID
)
1296 if (ids_set
.find(lootid
) == ids_set
.end())
1297 LootTemplates_Disenchant
.ReportNotExistedId(lootid
);
1299 ids_setUsed
.insert(lootid
);
1303 for (LootIdSet::const_iterator itr
= ids_setUsed
.begin(); itr
!= ids_setUsed
.end(); ++itr
)
1304 ids_set
.erase(*itr
);
1305 // output error for any still listed (not referenced from appropriate table) ids
1306 LootTemplates_Disenchant
.ReportUnusedIds(ids_set
);
1309 void LoadLootTemplates_Fishing()
1312 LootTemplates_Fishing
.LoadAndCollectLootIds(ids_set
);
1314 // remove real entries and check existence loot
1315 for (uint32 i
= 1; i
< sAreaStore
.GetNumRows(); ++i
)
1317 if (AreaTableEntry
const* areaEntry
= sAreaStore
.LookupEntry(i
))
1318 if (ids_set
.find(areaEntry
->ID
) != ids_set
.end())
1319 ids_set
.erase(areaEntry
->ID
);
1322 // by default (look config options) fishing at fail provide junk loot, entry 0 use for store this loot
1325 // output error for any still listed (not referenced from appropriate table) ids
1326 LootTemplates_Fishing
.ReportUnusedIds(ids_set
);
1329 void LoadLootTemplates_Gameobject()
1331 LootIdSet ids_set
, ids_setUsed
;
1332 LootTemplates_Gameobject
.LoadAndCollectLootIds(ids_set
);
1334 // remove real entries and check existence loot
1335 for (uint32 i
= 1; i
< sGOStorage
.GetMaxEntry(); ++i
)
1337 if (GameObjectInfo
const* gInfo
= sGOStorage
.LookupEntry
<GameObjectInfo
>(i
))
1339 if (uint32 lootid
= gInfo
->GetLootId())
1341 if (ids_set
.find(lootid
) == ids_set
.end())
1342 LootTemplates_Gameobject
.ReportNotExistedId(lootid
);
1344 ids_setUsed
.insert(lootid
);
1348 for (LootIdSet::const_iterator itr
= ids_setUsed
.begin(); itr
!= ids_setUsed
.end(); ++itr
)
1349 ids_set
.erase(*itr
);
1351 // output error for any still listed (not referenced from appropriate table) ids
1352 LootTemplates_Gameobject
.ReportUnusedIds(ids_set
);
1355 void LoadLootTemplates_Item()
1358 LootTemplates_Item
.LoadAndCollectLootIds(ids_set
);
1360 // remove real entries and check existence loot
1361 for (uint32 i
= 1; i
< sItemStorage
.GetMaxEntry(); ++i
)
1363 if (ItemPrototype
const* proto
= sItemStorage
.LookupEntry
<ItemPrototype
>(i
))
1365 if (!(proto
->Flags
& ITEM_FLAG_LOOTABLE
))
1368 if (ids_set
.find(proto
->ItemId
) != ids_set
.end() || proto
->MaxMoneyLoot
> 0)
1369 ids_set
.erase(proto
->ItemId
);
1370 // wdb have wrong data cases, so skip by default
1371 else if (!sLog
.HasLogFilter(LOG_FILTER_DB_STRICTED_CHECK
))
1372 LootTemplates_Item
.ReportNotExistedId(proto
->ItemId
);
1376 // output error for any still listed (not referenced from appropriate table) ids
1377 LootTemplates_Item
.ReportUnusedIds(ids_set
);
1380 void LoadLootTemplates_Milling()
1383 LootTemplates_Milling
.LoadAndCollectLootIds(ids_set
);
1385 // remove real entries and check existence loot
1386 for (uint32 i
= 1; i
< sItemStorage
.GetMaxEntry(); ++i
)
1388 ItemPrototype
const* proto
= sItemStorage
.LookupEntry
<ItemPrototype
>(i
);
1392 if (!(proto
->Flags
& ITEM_FLAG_MILLABLE
))
1395 if (ids_set
.find(proto
->ItemId
) != ids_set
.end())
1396 ids_set
.erase(proto
->ItemId
);
1398 LootTemplates_Milling
.ReportNotExistedId(proto
->ItemId
);
1401 // output error for any still listed (not referenced from appropriate table) ids
1402 LootTemplates_Milling
.ReportUnusedIds(ids_set
);
1405 void LoadLootTemplates_Pickpocketing()
1407 LootIdSet ids_set
, ids_setUsed
;
1408 LootTemplates_Pickpocketing
.LoadAndCollectLootIds(ids_set
);
1410 // remove real entries and check existence loot
1411 for (uint32 i
= 1; i
< sCreatureStorage
.GetMaxEntry(); ++i
)
1413 if (CreatureInfo
const* cInfo
= sCreatureStorage
.LookupEntry
<CreatureInfo
>(i
))
1415 if (uint32 lootid
= cInfo
->pickpocketLootId
)
1417 if (ids_set
.find(lootid
) == ids_set
.end())
1418 LootTemplates_Pickpocketing
.ReportNotExistedId(lootid
);
1420 ids_setUsed
.insert(lootid
);
1424 for (LootIdSet::const_iterator itr
= ids_setUsed
.begin(); itr
!= ids_setUsed
.end(); ++itr
)
1425 ids_set
.erase(*itr
);
1427 // output error for any still listed (not referenced from appropriate table) ids
1428 LootTemplates_Pickpocketing
.ReportUnusedIds(ids_set
);
1431 void LoadLootTemplates_Prospecting()
1434 LootTemplates_Prospecting
.LoadAndCollectLootIds(ids_set
);
1436 // remove real entries and check existence loot
1437 for (uint32 i
= 1; i
< sItemStorage
.GetMaxEntry(); ++i
)
1439 ItemPrototype
const* proto
= sItemStorage
.LookupEntry
<ItemPrototype
>(i
);
1443 if (!(proto
->Flags
& ITEM_FLAG_PROSPECTABLE
))
1446 if (ids_set
.find(proto
->ItemId
) != ids_set
.end())
1447 ids_set
.erase(proto
->ItemId
);
1448 // else -- exist some cases that possible can be prospected but not expected have any result loot
1449 // LootTemplates_Prospecting.ReportNotExistedId(proto->ItemId);
1452 // output error for any still listed (not referenced from appropriate table) ids
1453 LootTemplates_Prospecting
.ReportUnusedIds(ids_set
);
1456 void LoadLootTemplates_Mail()
1459 LootTemplates_Mail
.LoadAndCollectLootIds(ids_set
);
1461 // remove real entries and check existence loot
1462 for (uint32 i
= 1; i
< sMailTemplateStore
.GetNumRows(); ++i
)
1463 if (sMailTemplateStore
.LookupEntry(i
))
1464 if (ids_set
.find(i
) != ids_set
.end())
1467 // output error for any still listed (not referenced from appropriate table) ids
1468 LootTemplates_Mail
.ReportUnusedIds(ids_set
);
1471 void LoadLootTemplates_Skinning()
1473 LootIdSet ids_set
, ids_setUsed
;
1474 LootTemplates_Skinning
.LoadAndCollectLootIds(ids_set
);
1476 // remove real entries and check existence loot
1477 for (uint32 i
= 1; i
< sCreatureStorage
.GetMaxEntry(); ++i
)
1479 if (CreatureInfo
const* cInfo
= sCreatureStorage
.LookupEntry
<CreatureInfo
>(i
))
1481 if (uint32 lootid
= cInfo
->SkinLootId
)
1483 if (ids_set
.find(lootid
) == ids_set
.end())
1484 LootTemplates_Skinning
.ReportNotExistedId(lootid
);
1486 ids_setUsed
.insert(lootid
);
1490 for (LootIdSet::const_iterator itr
= ids_setUsed
.begin(); itr
!= ids_setUsed
.end(); ++itr
)
1491 ids_set
.erase(*itr
);
1493 // output error for any still listed (not referenced from appropriate table) ids
1494 LootTemplates_Skinning
.ReportUnusedIds(ids_set
);
1497 void LoadLootTemplates_Spell()
1500 LootTemplates_Spell
.LoadAndCollectLootIds(ids_set
);
1502 // remove real entries and check existence loot
1503 for (uint32 spell_id
= 1; spell_id
< sSpellStore
.GetNumRows(); ++spell_id
)
1505 SpellEntry
const* spellInfo
= sSpellStore
.LookupEntry(spell_id
);
1510 if (!IsLootCraftingSpell(spellInfo
))
1513 if (ids_set
.find(spell_id
) == ids_set
.end())
1515 // not report about not trainable spells (optionally supported by DB)
1516 // ignore 61756 (Northrend Inscription Research (FAST QA VERSION) for example
1517 if (!spellInfo
->HasAttribute(SPELL_ATTR_NOT_SHAPESHIFT
) || spellInfo
->HasAttribute(SPELL_ATTR_TRADESPELL
))
1519 LootTemplates_Spell
.ReportNotExistedId(spell_id
);
1523 ids_set
.erase(spell_id
);
1526 // output error for any still listed (not referenced from appropriate table) ids
1527 LootTemplates_Spell
.ReportUnusedIds(ids_set
);
1530 void LoadLootTemplates_Reference()
1533 LootTemplates_Reference
.LoadAndCollectLootIds(ids_set
);
1535 // check references and remove used
1536 LootTemplates_Creature
.CheckLootRefs(&ids_set
);
1537 LootTemplates_Fishing
.CheckLootRefs(&ids_set
);
1538 LootTemplates_Gameobject
.CheckLootRefs(&ids_set
);
1539 LootTemplates_Item
.CheckLootRefs(&ids_set
);
1540 LootTemplates_Milling
.CheckLootRefs(&ids_set
);
1541 LootTemplates_Pickpocketing
.CheckLootRefs(&ids_set
);
1542 LootTemplates_Skinning
.CheckLootRefs(&ids_set
);
1543 LootTemplates_Disenchant
.CheckLootRefs(&ids_set
);
1544 LootTemplates_Prospecting
.CheckLootRefs(&ids_set
);
1545 LootTemplates_Mail
.CheckLootRefs(&ids_set
);
1546 LootTemplates_Reference
.CheckLootRefs(&ids_set
);
1548 // output error for any still listed ids (not referenced from any loot table)
1549 LootTemplates_Reference
.ReportUnusedIds(ids_set
);