Updated Copyright year to 2013
[getmangos.git] / src / game / LootMgr.cpp
blob19ab3c9cfd6caa7848022ed4e3cff58fac424dde
1 /*
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
19 #include "LootMgr.h"
20 #include "Log.h"
21 #include "ObjectMgr.h"
22 #include "ProgressBar.h"
23 #include "World.h"
24 #include "Util.h"
25 #include "SharedDefines.h"
26 #include "SpellMgr.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)
56 public:
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;
68 private:
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)
79 delete itr->second;
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;
96 uint32 count = 0;
98 // Clearing store (for reloading case)
99 Clear();
101 sLog.outString("%s :", GetName());
103 // 0 1 2 3 4 5 6
104 QueryResult* result = WorldDatabase.PQuery("SELECT entry, item, ChanceOrQuestChance, groupid, mincountOrRef, maxcount, condition_id FROM %s", GetName());
106 if (result)
108 BarGoLink bar(result->GetRowCount());
112 Field* fields = result->Fetch();
113 bar.step();
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);
133 continue;
136 if (conditionId)
138 const PlayerCondition* condition = sConditionStorage.LookupEntry<PlayerCondition>(conditionId);
139 if (!condition)
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);
142 conditionId = 0;
146 LootStoreItem storeitem = LootStoreItem(item, type, chanceOrQuestChance, group, conditionId, mincountOrRef, maxcount);
148 if (!storeitem.IsValid(*this, entry)) // Validity checks
149 continue;
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));
160 tab = pr.first;
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);
168 ++count;
171 while (result->NextRow());
173 delete result;
175 Verify(); // Checks validity of the loot store
177 sLog.outString();
178 sLog.outString(">> Loaded %u loot definitions (" SIZEFMTD " templates)", count, m_LootTemplates.size());
180 else
182 sLog.outString();
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())
191 return false;
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))
202 return true;
204 return false;
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())
212 return NULL;
214 return tab->second;
217 void LootStore::LoadAndCollectLootIds(LootIdSet& ids_set)
219 LoadLootTable();
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)
252 return true;
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));
268 return false;
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);
277 return false;
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);
283 return false;
286 if (mincountOrRef == 0)
288 sLog.outErrorDb("Table '%s' entry %d item %d: wrong mincountOrRef (%d) - skipped", store.GetName(), entry, itemid, mincountOrRef);
289 return false;
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);
297 if (!proto)
299 sLog.outErrorDb("Table '%s' entry %d item %d: item entry not listed in `item_template` - skipped", store.GetName(), entry, itemid);
300 return false;
303 else if (type == LOOT_ITEM_TYPE_CURRENCY)
305 CurrencyTypesEntry const* currency = sCurrencyTypesStore.LookupEntry(itemid);
306 if (!currency)
308 sLog.outErrorDb("Table '%s' entry %d: currency entry %u not exists - skipped", store.GetName(), entry, itemid);
309 return false;
312 else
314 sLog.outErrorDb("Table '%s' entry %d: has unknown item %u with type %u - skipped", store.GetName(), entry, itemid, type);
315 return false;
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);
321 return false;
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);
327 return false;
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);
333 return false;
336 else // mincountOrRef < 0
338 if (needs_quest)
340 sLog.outErrorDb("Table '%s' entry %d item %d: negative chance is specified for a reference, skipped", store.GetName(), entry, itemid);
341 return false;
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);
346 return false;
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)
359 itemid = li.itemid;
360 type = li.type;
361 conditionId = li.conditionId;
362 currency = type == LOOT_ITEM_TYPE_CURRENCY;
363 count = urand(li.mincountOrRef, li.maxcount); // constructor called for mincountOrRef > 0 only
365 is_looted = 0;
366 is_blocked = 0;
367 is_underthreshold = 0;
368 is_counted = 0;
370 if (currency)
372 freeforall = false;
373 needs_quest = false;
374 randomSuffix = 0;
375 randomPropertyId = 0;
376 count = uint32(count * sWorld.getConfig(CONFIG_FLOAT_RATE_DROP_CURRENCY_AMOUNT));
378 else
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_)
390 itemid = itemid_;
391 type = type_;
392 conditionId = 0;
393 count = count_;
394 randomSuffix = randomSuffix_;
395 randomPropertyId = randomPropertyId_;
396 is_looted = 0;
397 is_blocked = 0;
398 is_underthreshold = 0;
399 is_counted = 0;
400 currency = type == LOOT_ITEM_TYPE_CURRENCY;
401 needs_quest = false;
403 if (currency)
404 freeforall = false;
405 else
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))
417 return false;
419 if (type == LOOT_ITEM_TYPE_ITEM)
421 ItemPrototype const* pProto = ObjectMgr::GetItemPrototype(itemid);
422 if (!pProto)
423 return false;
425 // not show loot for not own team
426 if ((pProto->Flags2 & ITEM_FLAG2_HORDE_ONLY) && player->GetTeam() != HORDE)
427 return false;
429 if ((pProto->Flags2 & ITEM_FLAG2_ALLIANCE_ONLY) && player->GetTeam() != ALLIANCE)
430 return false;
432 if (needs_quest)
434 // Checking quests for quest-only drop (check only quests requirements in this case)
435 if (!player->HasQuestForItem(itemid))
436 return false;
438 else
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))
442 return false;
445 else if (type == LOOT_ITEM_TYPE_CURRENCY)
447 CurrencyTypesEntry const * currency = sCurrencyTypesStore.LookupEntry(itemid);
448 if (!itemid)
449 return false;
451 if (!player->isGameMaster())
453 if (currency->ID == CURRENCY_CONQUEST_ARENA_META || currency->ID == CURRENCY_CONQUEST_BG_META)
454 return false;
456 if (currency->Category == CURRENCY_CATEGORY_ARCHAEOLOGY && !player->HasSkill(SKILL_ARCHAEOLOGY))
457 return false;
461 return true;
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;
470 switch (permission)
472 case ALL_PERMISSION:
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;
480 default:
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))
509 ++unlootedCount;
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)
517 // Must be provided
518 if (!loot_owner)
519 return false;
521 LootTemplate const* tab = store.GetLootFor(loot_id);
523 if (!tab)
525 if (!noEmptyError)
526 sLog.outErrorDb("Table '%s' loot id #%u used but it doesn't have records.", store.GetName(), loot_id);
527 return false;
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
544 else
545 FillNotNormalLootFor(loot_owner);
547 return true;
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())
560 FillQuestLoot(pl);
562 qmapitr = m_playerFFAItems.find(plguid);
563 if (qmapitr == m_playerFFAItems.end())
564 FillFFALoot(pl);
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));
581 ++unlootedCount;
584 if (ql->empty())
586 delete ql;
587 return NULL;
590 m_playerCurrencies[player->GetGUIDLow()] = ql;
591 return 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));
604 ++unlootedCount;
607 if (ql->empty())
609 delete ql;
610 return NULL;
613 m_playerFFAItems[player->GetGUIDLow()] = ql;
614 return 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)
634 ++unlootedCount;
636 item.is_blocked = true;
638 if (items.size() + ql->size() == MAX_NR_LOOT_ITEMS)
639 break;
642 if (ql->empty())
644 delete ql;
645 return NULL;
648 m_playerQuestItems[player->GetGUIDLow()] = ql;
649 return 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)
664 ++unlootedCount;
665 item.is_counted = true;
669 if (ql->empty())
671 delete ql;
672 return NULL;
675 m_playerNonQuestNonFFANonCurrencyConditionalItems[player->GetGUIDLow()] = ql;
676 return 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)
688 i_next = i;
689 ++i_next;
690 if (Player* pl = ObjectAccessor::FindPlayer(*i))
691 pl->SendNotifyLootItemRemoved(lootIndex);
692 else
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)
703 i_next = i;
704 ++i_next;
705 if (Player* pl = ObjectAccessor::FindPlayer(*i))
706 pl->SendNotifyLootMoneyRemoved();
707 else
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)
722 i_next = i;
723 ++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;
732 uint8 j;
733 for (j = 0; j < pql.size(); ++j)
734 if (pql[j].index == questIndex)
735 break;
737 if (j < pql.size())
738 pl->SendNotifyLootItemRemoved(items.size() + j);
741 else
742 m_playersLooting.erase(i);
746 void Loot::generateMoneyLoot(uint32 minAmount, uint32 maxAmount)
748 if (maxAmount > 0)
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));
754 else
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);
770 if (qitem)
771 *qitem = qitem2;
772 item = &m_questItems[qitem2->index];
773 is_looted = qitem2->is_looted;
776 else
778 item = &items[lootSlot];
779 is_looted = item->is_looted;
780 if (item->currency)
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);
790 if (currency)
791 *currency = currency2;
792 is_looted = currency2->is_looted;
793 break;
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);
807 if (ffaitem)
808 *ffaitem = ffaitem2;
809 is_looted = ffaitem2->is_looted;
810 break;
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);
824 if (conditem)
825 *conditem = conditem2;
826 is_looted = conditem2->is_looted;
827 break;
834 if (is_looted)
835 return NULL;
837 return item;
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
862 return b;
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
872 return b;
875 Loot& l = lv.loot;
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)
891 continue;
893 b << uint8(i) << l.items[i];
894 b << uint8(slot_type); // 0 - get 1 - look only 2 - master selection
895 ++itemsShown;
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)
909 continue;
911 b << uint8(ci->index) << item;
912 b << uint8(slot_type); // allow loot
913 ++itemsShown;
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()));
931 b << item;
932 b << uint8(slot_type); // allow loot
933 ++itemsShown;
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
950 ++itemsShown;
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;
966 ++currenciesShown;
971 // update number of items and currencies shown
972 b.put<uint8>(count_pos, itemsShown);
973 b.put<uint8>(currency_count_pos, currenciesShown);
975 return b;
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);
987 else
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;
1004 if (Roll < 0)
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)
1018 if (i->needs_quest)
1019 return true;
1020 for (LootStoreItemList::const_iterator i = EqualChanced.begin(); i != EqualChanced.end(); ++i)
1021 if (i->needs_quest)
1022 return true;
1023 return false;
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))
1031 return true;
1032 for (LootStoreItemList::const_iterator i = EqualChanced.begin(); i != EqualChanced.end(); ++i)
1033 if (player->HasQuestForItem(i->itemid))
1034 return true;
1035 return false;
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();
1042 if (item != NULL)
1043 loot.AddItem(*item);
1046 // Overall chance for the group without equal chanced items
1047 float LootTemplate::LootGroup::RawTotalChance() const
1049 float result = 0;
1051 for (LootStoreItemList::const_iterator i = ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i)
1052 if (!i->needs_quest)
1053 result += i->chance;
1055 return result;
1058 // Overall chance for the group
1059 float LootTemplate::LootGroup::TotalChance() const
1061 float result = RawTotalChance();
1063 if (!EqualChanced.empty() && result < 100.0f)
1064 return 100.0f;
1066 return result;
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);
1091 else if (ref_set)
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);
1102 else if (ref_set)
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);
1134 return;
1137 // Rolling non-grouped items
1138 for (LootStoreItemList::const_iterator i = Entries.begin() ; i != Entries.end() ; ++i)
1140 if (!i->Roll(rate))
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);
1147 if (!Referenced)
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)
1159 i->Process(loot);
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))
1180 return true;
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())
1189 return true;
1191 return false;
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))
1213 return true;
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))
1222 return true;
1224 return false;
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);
1245 else if (ref_set)
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);
1268 else
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
1278 ids_set.erase(0);
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);
1298 else
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()
1311 LootIdSet ids_set;
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
1323 ids_set.erase(0);
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);
1343 else
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()
1357 LootIdSet ids_set;
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))
1366 continue;
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()
1382 LootIdSet ids_set;
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);
1389 if (!proto)
1390 continue;
1392 if (!(proto->Flags & ITEM_FLAG_MILLABLE))
1393 continue;
1395 if (ids_set.find(proto->ItemId) != ids_set.end())
1396 ids_set.erase(proto->ItemId);
1397 else
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);
1419 else
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()
1433 LootIdSet ids_set;
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);
1440 if (!proto)
1441 continue;
1443 if (!(proto->Flags & ITEM_FLAG_PROSPECTABLE))
1444 continue;
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()
1458 LootIdSet ids_set;
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())
1465 ids_set.erase(i);
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);
1485 else
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()
1499 LootIdSet ids_set;
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);
1506 if (!spellInfo)
1507 continue;
1509 // possible cases
1510 if (!IsLootCraftingSpell(spellInfo))
1511 continue;
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);
1522 else
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()
1532 LootIdSet ids_set;
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);